From 7d747765ef2c93612bc01b524723c5f562282e4b Mon Sep 17 00:00:00 2001 From: Woody Folsom Date: Sat, 17 Mar 2012 22:23:38 -0400 Subject: [PATCH] Implemented stochastic grammar. Work in progress. --- src/dk/itu/mario/level/LevelComponent.java | 2 +- src/dk/itu/mario/level/PCGLevel.java | 3 +- src/dk/itu/mario/level/grammar/AndClause.java | 44 +++++++++++++ src/dk/itu/mario/level/grammar/Clause.java | 10 +++ .../itu/mario/level/grammar/LevelGrammar.java | 61 +++++++++--------- .../level/grammar/LevelGrammarFactory.java | 62 ++++++++++++++++--- src/dk/itu/mario/level/grammar/OrClause.java | 59 ++++++++++++++++++ .../mario/level/grammar/ProductionRule.java | 17 +++-- src/dk/itu/mario/level/grammar/Variable.java | 39 ++++++++++-- 9 files changed, 244 insertions(+), 53 deletions(-) create mode 100644 src/dk/itu/mario/level/grammar/AndClause.java create mode 100644 src/dk/itu/mario/level/grammar/Clause.java create mode 100644 src/dk/itu/mario/level/grammar/OrClause.java diff --git a/src/dk/itu/mario/level/LevelComponent.java b/src/dk/itu/mario/level/LevelComponent.java index f68064d..0514f39 100644 --- a/src/dk/itu/mario/level/LevelComponent.java +++ b/src/dk/itu/mario/level/LevelComponent.java @@ -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 { diff --git a/src/dk/itu/mario/level/PCGLevel.java b/src/dk/itu/mario/level/PCGLevel.java index 77e16b1..bb8decb 100644 --- a/src/dk/itu/mario/level/PCGLevel.java +++ b/src/dk/itu/mario/level/PCGLevel.java @@ -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: diff --git a/src/dk/itu/mario/level/grammar/AndClause.java b/src/dk/itu/mario/level/grammar/AndClause.java new file mode 100644 index 0000000..b70cce9 --- /dev/null +++ b/src/dk/itu/mario/level/grammar/AndClause.java @@ -0,0 +1,44 @@ +package dk.itu.mario.level.grammar; + +import java.util.ArrayList; +import java.util.List; + +public class AndClause implements Clause { + List subClauses = new ArrayList(); + + 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; + } +} \ No newline at end of file diff --git a/src/dk/itu/mario/level/grammar/Clause.java b/src/dk/itu/mario/level/grammar/Clause.java new file mode 100644 index 0000000..54f747d --- /dev/null +++ b/src/dk/itu/mario/level/grammar/Clause.java @@ -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(); +} \ No newline at end of file diff --git a/src/dk/itu/mario/level/grammar/LevelGrammar.java b/src/dk/itu/mario/level/grammar/LevelGrammar.java index 8280622..e79d14e 100644 --- a/src/dk/itu/mario/level/grammar/LevelGrammar.java +++ b/src/dk/itu/mario/level/grammar/LevelGrammar.java @@ -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 ruleMap = new HashMap(); @@ -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 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 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); } diff --git a/src/dk/itu/mario/level/grammar/LevelGrammarFactory.java b/src/dk/itu/mario/level/grammar/LevelGrammarFactory.java index 9e3dd42..a73add9 100644 --- a/src/dk/itu/mario/level/grammar/LevelGrammarFactory.java +++ b/src/dk/itu/mario/level/grammar/LevelGrammarFactory.java @@ -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; } diff --git a/src/dk/itu/mario/level/grammar/OrClause.java b/src/dk/itu/mario/level/grammar/OrClause.java new file mode 100644 index 0000000..5a97c53 --- /dev/null +++ b/src/dk/itu/mario/level/grammar/OrClause.java @@ -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 subClauses = new ArrayList(); + + 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; + } +} \ No newline at end of file diff --git a/src/dk/itu/mario/level/grammar/ProductionRule.java b/src/dk/itu/mario/level/grammar/ProductionRule.java index 1ca65b1..53bac19 100644 --- a/src/dk/itu/mario/level/grammar/ProductionRule.java +++ b/src/dk/itu/mario/level/grammar/ProductionRule.java @@ -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 rhs; + private Clause rhs; - public ProductionRule(Variable lhs, Variable... rhs) { + public ProductionRule(Variable lhs, Clause... rhs) { this.lhs = lhs; - this.rhs = new ArrayList(); - 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 getRHS() { + public Clause getRHS() { return rhs; } } \ No newline at end of file diff --git a/src/dk/itu/mario/level/grammar/Variable.java b/src/dk/itu/mario/level/grammar/Variable.java index 41af9db..2034e16 100644 --- a/src/dk/itu/mario/level/grammar/Variable.java +++ b/src/dk/itu/mario/level/grammar/Variable.java @@ -1,12 +1,20 @@ package dk.itu.mario.level.grammar; -public class Variable implements Comparable { +import dk.itu.mario.level.LevelComponent; + +public class Variable implements Clause, Comparable { 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 { 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; + } +} \ No newline at end of file