Implemented stochastic grammar. Work in progress.

This commit is contained in:
Woody Folsom
2012-03-17 22:23:38 -04:00
parent 1790d48393
commit 7d747765ef
9 changed files with 244 additions and 53 deletions

View File

@@ -2,7 +2,7 @@ package dk.itu.mario.level;
public class LevelComponent {
public enum TYPE {
FLAT, PIPE_JUMP, PLATFORM_JUMP, MAZE
FLAT_HI, FLAT_LO, HI_LO, HI_PATH, LEVEL_SEGMENT, LEVEL, LO_HI, LO_PATH, PIPE_JUMP, PLATFORM_JUMP, MAZE
};
public enum MazeLevel {

View File

@@ -146,7 +146,8 @@ public class PCGLevel extends Level {
LevelComponent.TYPE lctype = lcomp.getType();
System.out.println("Building for: " + lcomp);
switch (lctype) {
case FLAT:
case FLAT_LO:
case FLAT_HI:
length += buildStraight(length, lcomp.getEnd(), true);
break;
case PIPE_JUMP:

View File

@@ -0,0 +1,44 @@
package dk.itu.mario.level.grammar;
import java.util.ArrayList;
import java.util.List;
public class AndClause implements Clause {
List<Clause> subClauses = new ArrayList<Clause>();
public AndClause(Clause... subClauses) {
for (Clause clause : subClauses) {
this.subClauses.add(clause);
}
}
@Override
public Clause makeRandomChoice() {
throw new UnsupportedOperationException();
}
@Override
public int getNumSubClauses() {
return subClauses.size();
}
@Override
public Clause getSubClause(int index) {
// TODO Auto-generated method stub
return subClauses.get(index);
}
@Override
public boolean isChoice() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean isTerminal() {
return false;
}
@Override
public boolean isVariable() {
return false;
}
}

View File

@@ -0,0 +1,10 @@
package dk.itu.mario.level.grammar;
public interface Clause {
public Clause getSubClause(int index);
public int getNumSubClauses();
public boolean isChoice();
public boolean isTerminal();
public boolean isVariable();
public Clause makeRandomChoice();
}

View File

@@ -7,9 +7,9 @@ import java.util.Random;
import java.util.Set;
import java.util.TreeSet;
import dk.itu.mario.level.LevelComponent;
import dk.itu.mario.level.LevelComponent.TYPE;
public class LevelGrammar {
private Variable start;
private Map<Variable, ProductionRule> ruleMap = new HashMap<Variable, ProductionRule>();
@@ -31,38 +31,41 @@ public class LevelGrammar {
ruleMap.put(rule.getLHS(), rule);
}
public String generateRandom(long randomSeed) {
System.out.println("Generating random level parameters using seed: "
+ randomSeed);
List<Variable> startRuleRHS = getRule(getStart()).getRHS();
StringBuilder sb = new StringBuilder();
for (Variable var : startRuleRHS) {
sb.append(var.toString());
}
return sb.toString();
}
public String generateRandom() {
System.out.println("Creating new random seed for LevelGrammar");
return generateRandom(new Random().nextLong());
}
//TODO: refactor to recursively add children to the current node based on production rule
public LevelParseTree generateRandomTree(long randomSeed, int width) {
System.out.println("Generating random level parameters using seed: "
+ randomSeed);
List<Variable> startRuleRHS = getRule(getStart()).getRHS();
Variable startRuleLHS = getStart();
ParseNode rootNode = new ParseNode(TYPE.FLAT,1.0);
ParseNode rootNode = new ParseNode(startRuleLHS.getType(), 1.0);
generateRecursive(rootNode, getRule(startRuleLHS).getRHS(), 1.0);
LevelParseTree parseTree = new LevelParseTree(rootNode, 0, width);
for (Variable var : startRuleRHS) {
if (var.isTerminal()) {
rootNode.addChild(new ParseNode(TYPE.FLAT,0.5));
return parseTree;
}
private ParseNode generateRecursive(ParseNode parseNode, Clause clause,
double relativeWidth) {
if (clause.isVariable()) {
if (clause.isTerminal()) {
parseNode.addChild(new ParseNode(((Variable) clause).getType(),
relativeWidth));
} else {
generateRecursive(parseNode, getRule((Variable) clause)
.getRHS(), relativeWidth);
}
} else {
if (clause.isChoice()) {
generateRecursive(parseNode, clause.makeRandomChoice(),
relativeWidth);
} else {
int numSubClauses = clause.getNumSubClauses();
for (int i = 0; i < numSubClauses; i++) {
generateRecursive(parseNode, clause.getSubClause(i),
relativeWidth / numSubClauses);
}
}
}
return parseTree;
return parseNode;
}
public ProductionRule getRule(Variable var) {

View File

@@ -1,6 +1,7 @@
package dk.itu.mario.level.grammar;
import dk.itu.mario.level.LevelArchetype;
import dk.itu.mario.level.LevelComponent;
import dk.itu.mario.level.PlayerProfile;
@@ -8,16 +9,61 @@ public class LevelGrammarFactory {
public static LevelGrammar createGrammar(PlayerProfile playerProfile, LevelArchetype archetype) {
LevelGrammar grammar = new LevelGrammar();
Variable v_S = new Variable("S");
Variable v_level_start = new Variable("level_start");
Variable v_level_end = new Variable("level_end");
Variable v_START = new Variable("S", LevelComponent.TYPE.LEVEL);
Variable v_LAND_SEGMENT = new Variable("HALF_LVL", LevelComponent.TYPE.LEVEL_SEGMENT);
Variable v_LO_HI = new Variable("LO_HI", LevelComponent.TYPE.LO_HI);
Variable v_HI_LO = new Variable("HI_LO", LevelComponent.TYPE.HI_LO);
Variable v_LO_PATH = new Variable("LO_PATH", LevelComponent.TYPE.LO_PATH);
Variable v_HI_PATH = new Variable("HI_PATH", LevelComponent.TYPE.HI_PATH);
Variable v_lo_path = new Variable("lo_path", LevelComponent.TYPE.LO_PATH);
Variable v_hi_path = new Variable("hi_path", LevelComponent.TYPE.HI_PATH);
grammar.addVariable(v_S);
grammar.addVariable(v_level_start);
grammar.addVariable(v_level_end);
grammar.addVariable(v_START);
grammar.addVariable(v_LAND_SEGMENT);
grammar.addVariable(v_LO_HI);
grammar.addVariable(v_HI_LO);
grammar.addVariable(v_LO_PATH);
grammar.addVariable(v_HI_PATH);
grammar.addProductionRule(new ProductionRule(v_S,v_level_start,v_level_end));
grammar.setStart(v_S);
grammar.addProductionRule(new ProductionRule(v_START,v_LAND_SEGMENT,v_LAND_SEGMENT));
grammar.addProductionRule(
new ProductionRule(v_LAND_SEGMENT,
new OrClause( new double[] {0.25,0.65,0.10},
new AndClause(v_LO_HI,v_HI_LO),
v_LO_PATH,
new AndClause(v_LAND_SEGMENT,v_LAND_SEGMENT)
)
)
);
grammar.addProductionRule(
new ProductionRule(v_LO_HI, v_LO_PATH, v_HI_PATH)
);
grammar.addProductionRule(
new ProductionRule(v_HI_LO, v_HI_PATH, v_LO_PATH)
);
grammar.addProductionRule(
new ProductionRule(v_HI_PATH,
new OrClause( new double[] {0.25,0.75},
new AndClause(v_HI_PATH,v_HI_PATH),
v_hi_path
)
)
);
grammar.addProductionRule(
new ProductionRule(v_LO_PATH,
new OrClause( new double[] {0.25,0.75},
new AndClause(v_LO_PATH,v_LO_PATH),
v_lo_path
)
)
);
grammar.setStart(v_START);
return grammar;
}

View File

@@ -0,0 +1,59 @@
package dk.itu.mario.level.grammar;
import java.util.ArrayList;
import java.util.List;
public class OrClause implements Clause {
private double[] chances;
private List<Clause> subClauses = new ArrayList<Clause>();
public OrClause(double[] chances, Clause... subClauses) {
this.chances = chances;
double chanceTotal = 0.0;
for (double chance : chances) {
chanceTotal += chance;
}
if (Math.abs(chanceTotal - 1.0) > 0.01) {
throw new IllegalArgumentException(
"Total or-clause chances must sum to 1.0.");
}
for (Clause clause : subClauses) {
this.subClauses.add(clause);
}
}
@Override
public boolean isChoice() {
return true;
}
@Override
public boolean isTerminal() {
return false;
}
@Override
public Clause makeRandomChoice() {
double rand = Math.random();
for (int i = 0; i < chances.length; i++) {
if (rand < chances[i]) {
return subClauses.get(i);
}
rand -= chances[i];
}
return subClauses.get(chances.length - 1);
}
@Override
public int getNumSubClauses() {
return subClauses.size();
}
public Clause getSubClause(int index) {
throw new UnsupportedOperationException();
}
@Override
public boolean isVariable() {
return false;
}
}

View File

@@ -1,18 +1,15 @@
package dk.itu.mario.level.grammar;
import java.util.ArrayList;
import java.util.List;
public class ProductionRule {
private Variable lhs;
private List<Variable> rhs;
private Clause rhs;
public ProductionRule(Variable lhs, Variable... rhs) {
public ProductionRule(Variable lhs, Clause... rhs) {
this.lhs = lhs;
this.rhs = new ArrayList<Variable>();
for (Variable var : rhs) {
this.rhs.add(var);
if (rhs.length == 1) {
this.rhs = rhs[0];
} else {
this.rhs = new AndClause(rhs);
}
}
@@ -20,7 +17,7 @@ public class ProductionRule {
return lhs;
}
public List<Variable> getRHS() {
public Clause getRHS() {
return rhs;
}
}

View File

@@ -1,12 +1,20 @@
package dk.itu.mario.level.grammar;
public class Variable implements Comparable<Variable> {
import dk.itu.mario.level.LevelComponent;
public class Variable implements Clause, Comparable<Variable> {
private boolean terminal;
private LevelComponent.TYPE type;
private String value;
public Variable(String value) {
public Variable(String value, LevelComponent.TYPE type) {
this.value = value;
this.terminal = (value.toLowerCase().equals(value));
this.type = type;
}
public LevelComponent.TYPE getType() {
return type;
}
public boolean isTerminal() {
@@ -23,5 +31,28 @@ public class Variable implements Comparable<Variable> {
return this.value.compareTo(o.value);
}
@Override
public Clause getSubClause(int index) {
throw new UnsupportedOperationException();
}
@Override
public int getNumSubClauses() {
throw new UnsupportedOperationException();
}
@Override
public boolean isChoice() {
return false;
}
@Override
public Clause makeRandomChoice() {
throw new UnsupportedOperationException();
}
@Override
public boolean isVariable() {
return true;
}
}