Implemented stochastic grammar. Work in progress.
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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:
|
||||
|
||||
44
src/dk/itu/mario/level/grammar/AndClause.java
Normal file
44
src/dk/itu/mario/level/grammar/AndClause.java
Normal 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;
|
||||
}
|
||||
}
|
||||
10
src/dk/itu/mario/level/grammar/Clause.java
Normal file
10
src/dk/itu/mario/level/grammar/Clause.java
Normal 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();
|
||||
}
|
||||
@@ -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,40 +31,43 @@ 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();
|
||||
|
||||
ParseNode rootNode = new ParseNode(TYPE.FLAT,1.0);
|
||||
Variable startRuleLHS = getStart();
|
||||
|
||||
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 parseNode;
|
||||
}
|
||||
|
||||
public ProductionRule getRule(Variable var) {
|
||||
return ruleMap.get(var);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
59
src/dk/itu/mario/level/grammar/OrClause.java
Normal file
59
src/dk/itu/mario/level/grammar/OrClause.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
@@ -22,6 +30,29 @@ public class Variable implements Comparable<Variable> {
|
||||
public int compareTo(Variable o) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user