Now with file-based level grammar.
This commit is contained in:
20
grammars/overland.grm
Normal file
20
grammars/overland.grm
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#VAR name = LevelComponent.TYPE
|
||||||
|
VAR S = LEVEL
|
||||||
|
VAR LAND_SEGMENT = LEVEL_SEGMENT
|
||||||
|
VAR LO_HI = LO_HI
|
||||||
|
VAR HI_LO = HI_LO
|
||||||
|
VAR LO_PATH = LO_PATH
|
||||||
|
VAR HI_PATH = HI_PATH
|
||||||
|
VAR lo_path = FLAT_LO
|
||||||
|
VAR hi_path = FLAT_HI
|
||||||
|
|
||||||
|
#RULE name -> {probabilities}, (clause) [+,|] (clause)...
|
||||||
|
RULE S -> LAND_SEGMENT + LAND_SEGMENT
|
||||||
|
RULE LAND_SEGMENT -> {0.25,0.65,0.10}, (LO_HI + HI_LO) | (LO_PATH) | (LAND_SEGMENT + LAND_SEGMENT)
|
||||||
|
RULE LO_HI -> LO_PATH + HI_PATH
|
||||||
|
RULE HI_LO -> HI_PATH + LO_PATH
|
||||||
|
RULE HI_PATH -> {0.25,0.75}, (HI_PATH + HI_PATH) | (hi_path)
|
||||||
|
RULE LO_PATH -> {0.25,0.75}, (LO_PATH + LO_PATH) | (lo_path)
|
||||||
|
|
||||||
|
#START variable name
|
||||||
|
START = S
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package dk.itu.mario.level;
|
package dk.itu.mario.level;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
@@ -76,9 +77,16 @@ public class PCGLevel extends Level {
|
|||||||
System.out.println("LevelArchetype: " + archetype);
|
System.out.println("LevelArchetype: " + archetype);
|
||||||
|
|
||||||
System.out.println("Creating level grammar");
|
System.out.println("Creating level grammar");
|
||||||
LevelGrammar grammar = LevelGrammarFactory.createGrammar(profile,
|
LevelGrammar grammar;
|
||||||
archetype);
|
try {
|
||||||
|
String grammarFileName = "grammars/overland.grm";
|
||||||
|
grammar = LevelGrammarFactory.createGrammar(new File(grammarFileName));
|
||||||
|
System.out.println("Read grammar from file: " + grammarFileName);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
System.out.println("Failed to parse grammar file due to exception: " + ex.getMessage());
|
||||||
|
System.out.println("Defaulting to basic overland grammar.");
|
||||||
|
grammar = LevelGrammarFactory.createGrammar();
|
||||||
|
}
|
||||||
System.out
|
System.out
|
||||||
.println("Tuning grammar for PlayerProfile & LevelArchetype using RETE");
|
.println("Tuning grammar for PlayerProfile & LevelArchetype using RETE");
|
||||||
GrammarTuner.tune(grammar, profile, archetype);
|
GrammarTuner.tune(grammar, profile, archetype);
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ import java.util.List;
|
|||||||
public class AndClause implements Clause {
|
public class AndClause implements Clause {
|
||||||
List<Clause> subClauses = new ArrayList<Clause>();
|
List<Clause> subClauses = new ArrayList<Clause>();
|
||||||
|
|
||||||
|
public AndClause(List<Clause> subClauses) {
|
||||||
|
this.subClauses.addAll(subClauses);
|
||||||
|
}
|
||||||
|
|
||||||
public AndClause(Clause... subClauses) {
|
public AndClause(Clause... subClauses) {
|
||||||
for (Clause clause : subClauses) {
|
for (Clause clause : subClauses) {
|
||||||
this.subClauses.add(clause);
|
this.subClauses.add(clause);
|
||||||
@@ -50,7 +54,7 @@ public class AndClause implements Clause {
|
|||||||
sb.append("(");
|
sb.append("(");
|
||||||
for (int i = 0; i < subClauses.size() - 1; i ++) {
|
for (int i = 0; i < subClauses.size() - 1; i ++) {
|
||||||
sb.append(subClauses.get(i).toString());
|
sb.append(subClauses.get(i).toString());
|
||||||
sb.append(" AND ");
|
sb.append(" + ");
|
||||||
}
|
}
|
||||||
sb.append(subClauses.get(subClauses.size()-1).toString());
|
sb.append(subClauses.get(subClauses.size()-1).toString());
|
||||||
sb.append(")");
|
sb.append(")");
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package dk.itu.mario.level.grammar;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
@@ -67,6 +68,15 @@ public class LevelGrammar {
|
|||||||
}
|
}
|
||||||
return parseNode;
|
return parseNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Variable getVariable(String varName) {
|
||||||
|
for (Variable var : variables) {
|
||||||
|
if (varName.equals(var.getValue())) {
|
||||||
|
return var;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public ProductionRule getRule(Variable var) {
|
public ProductionRule getRule(Variable var) {
|
||||||
return ruleMap.get(var);
|
return ruleMap.get(var);
|
||||||
@@ -76,7 +86,37 @@ public class LevelGrammar {
|
|||||||
return start;
|
return start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setStart(String startVar) {
|
||||||
|
for (Variable var : variables) {
|
||||||
|
if (startVar.equals(var.getValue())) {
|
||||||
|
start = var;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void setStart(Variable start) {
|
public void setStart(Variable start) {
|
||||||
this.start = start;
|
this.start = start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("Variables:\n");
|
||||||
|
for (Variable var : variables) {
|
||||||
|
sb.append("VAR ");
|
||||||
|
sb.append(var.getValue());
|
||||||
|
sb.append(" = ");
|
||||||
|
sb.append(var.getType());
|
||||||
|
sb.append("\n");
|
||||||
|
}
|
||||||
|
sb.append("\n");
|
||||||
|
for (Entry<Variable,ProductionRule> entry : ruleMap.entrySet()) {
|
||||||
|
//could as easily have used LHS instead of getKey().getValue()
|
||||||
|
sb.append("RULE " + entry.getKey().getValue() + " -> " + entry.getValue().getRHS());
|
||||||
|
sb.append("\n");
|
||||||
|
}
|
||||||
|
sb.append("\n");
|
||||||
|
sb.append("START = " + start.getValue());
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,128 @@
|
|||||||
package dk.itu.mario.level.grammar;
|
package dk.itu.mario.level.grammar;
|
||||||
|
|
||||||
import dk.itu.mario.level.LevelArchetype;
|
import java.io.BufferedReader;
|
||||||
import dk.itu.mario.level.LevelComponent;
|
import java.io.File;
|
||||||
import dk.itu.mario.level.PlayerProfile;
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import dk.itu.mario.level.LevelComponent;
|
||||||
|
|
||||||
public class LevelGrammarFactory {
|
public class LevelGrammarFactory {
|
||||||
public static LevelGrammar createGrammar(PlayerProfile playerProfile, LevelArchetype archetype) {
|
public static LevelGrammar createGrammar(File grammarFile) throws FileNotFoundException, IOException {
|
||||||
|
FileInputStream fis = new FileInputStream(grammarFile);
|
||||||
|
LevelGrammar levelGrammar = new LevelGrammar();
|
||||||
|
try {
|
||||||
|
InputStreamReader reader = new InputStreamReader(fis);
|
||||||
|
BufferedReader buf = new BufferedReader(reader);
|
||||||
|
String line;
|
||||||
|
while ((line = buf.readLine()) != null) {
|
||||||
|
line = line.trim(); // trim off newline/whitespace
|
||||||
|
if (line.length() == 0 || line.startsWith("#")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
System.out.println("Read: " + line);
|
||||||
|
//split on whitespace
|
||||||
|
String[] fields;
|
||||||
|
if (line.startsWith("VAR")) {
|
||||||
|
fields = line.split("\\s");
|
||||||
|
if ("=".equals(fields[2]) && fields.length == 4) {
|
||||||
|
levelGrammar.addVariable(new Variable(fields[1],LevelComponent.TYPE.valueOf(fields[3])));
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("Invalid VAR syntax: " + line);
|
||||||
|
}
|
||||||
|
} else if (line.startsWith("RULE")) {
|
||||||
|
fields = line.split("->");
|
||||||
|
String ruleName = fields[0].split("\\s")[1];
|
||||||
|
System.out.println("Rule name: " + ruleName);
|
||||||
|
Variable lhs = levelGrammar.getVariable(ruleName);
|
||||||
|
if (lhs == null) {
|
||||||
|
throw new RuntimeException("LHS variable not found: " + ruleName);
|
||||||
|
}
|
||||||
|
Clause rhs = getClause(fields[1].trim(),levelGrammar);
|
||||||
|
levelGrammar.addProductionRule(new ProductionRule(lhs,rhs));
|
||||||
|
} else if (line.startsWith("START")) {
|
||||||
|
fields = line.split("\\s");
|
||||||
|
if ("=".equals(fields[1]) && fields.length == 3) {
|
||||||
|
levelGrammar.setStart(fields[2]);
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("Invalid START syntax: " + line);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("Unable to parse grammar file " + grammarFile.getName() + " due to invalid line: " + line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return levelGrammar;
|
||||||
|
} finally {
|
||||||
|
fis.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Clause getClause(String clause, LevelGrammar grammar) {
|
||||||
|
if (!clause.contains("|") && !clause.contains("+")) {
|
||||||
|
return grammar.getVariable(clause);
|
||||||
|
}
|
||||||
|
int lBraceIndex = clause.indexOf("{");
|
||||||
|
boolean isOrClause = false;
|
||||||
|
int rBraceIndex = 0;
|
||||||
|
|
||||||
|
double[] chances = {};
|
||||||
|
|
||||||
|
if (lBraceIndex != -1) {
|
||||||
|
isOrClause = true;
|
||||||
|
rBraceIndex = clause.indexOf("}");
|
||||||
|
System.out.println("Read OR-clause probabilities from: " + lBraceIndex + " to " + rBraceIndex);
|
||||||
|
String[] doubleFields = clause.substring(lBraceIndex+1,rBraceIndex).split(",");
|
||||||
|
chances = new double[doubleFields.length];
|
||||||
|
for (int i = 0; i < doubleFields.length; i++) {
|
||||||
|
chances[i] = Double.valueOf(doubleFields[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String remainder = clause.substring(rBraceIndex);
|
||||||
|
List<String> rhsClauseStrings = new ArrayList<String>();
|
||||||
|
if (remainder.contains("(")) {
|
||||||
|
int nextLeftIndex = -1;
|
||||||
|
do {
|
||||||
|
nextLeftIndex = remainder.indexOf("(", nextLeftIndex+1);
|
||||||
|
if (nextLeftIndex != -1) {
|
||||||
|
int nextRightIndex = remainder.indexOf(")", nextLeftIndex+1);
|
||||||
|
if (nextRightIndex == -1) {
|
||||||
|
throw new RuntimeException("Unmatched left '(' in clause");
|
||||||
|
}
|
||||||
|
rhsClauseStrings.add(remainder.substring(nextLeftIndex+1,nextRightIndex));
|
||||||
|
nextLeftIndex = nextRightIndex;
|
||||||
|
}
|
||||||
|
} while (nextLeftIndex != -1);
|
||||||
|
} else {
|
||||||
|
isOrClause = remainder.contains("|");
|
||||||
|
if (isOrClause) {
|
||||||
|
for (String subclause : remainder.split("\\|")) {
|
||||||
|
rhsClauseStrings.add(subclause.trim());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (String subclause : remainder.split("\\+")) {
|
||||||
|
rhsClauseStrings.add(subclause.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Clause> rhsClauses = new ArrayList<Clause>();
|
||||||
|
|
||||||
|
for (String subclause : rhsClauseStrings) {
|
||||||
|
rhsClauses.add(getClause(subclause,grammar));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isOrClause) {
|
||||||
|
return new OrClause(chances, rhsClauses);
|
||||||
|
} else {
|
||||||
|
return new AndClause(rhsClauses);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static LevelGrammar createGrammar() {
|
||||||
LevelGrammar grammar = new LevelGrammar();
|
LevelGrammar grammar = new LevelGrammar();
|
||||||
|
|
||||||
Variable v_START = new Variable("S", LevelComponent.TYPE.LEVEL);
|
Variable v_START = new Variable("S", LevelComponent.TYPE.LEVEL);
|
||||||
|
|||||||
@@ -7,6 +7,14 @@ public class OrClause implements Clause {
|
|||||||
private double[] chances;
|
private double[] chances;
|
||||||
private List<Clause> subClauses = new ArrayList<Clause>();
|
private List<Clause> subClauses = new ArrayList<Clause>();
|
||||||
|
|
||||||
|
public OrClause(double[] chances, List<Clause> subClauses) {
|
||||||
|
if (chances.length != subClauses.size()) {
|
||||||
|
throw new IllegalArgumentException("List of probabilities does not match size of subclause list in OrClause constructor.");
|
||||||
|
}
|
||||||
|
this.chances = chances;
|
||||||
|
this.subClauses.addAll(subClauses);
|
||||||
|
}
|
||||||
|
|
||||||
public OrClause(double[] chances, Clause... subClauses) {
|
public OrClause(double[] chances, Clause... subClauses) {
|
||||||
this.chances = chances;
|
this.chances = chances;
|
||||||
double chanceTotal = 0.0;
|
double chanceTotal = 0.0;
|
||||||
@@ -66,7 +74,7 @@ public class OrClause implements Clause {
|
|||||||
sb.append("(");
|
sb.append("(");
|
||||||
for (int i = 0; i < subClauses.size() - 1; i ++) {
|
for (int i = 0; i < subClauses.size() - 1; i ++) {
|
||||||
sb.append(subClauses.get(i).toString());
|
sb.append(subClauses.get(i).toString());
|
||||||
sb.append(" OR ");
|
sb.append(" | ");
|
||||||
}
|
}
|
||||||
sb.append(subClauses.get(subClauses.size()-1).toString());
|
sb.append(subClauses.get(subClauses.size()-1).toString());
|
||||||
sb.append(")");
|
sb.append(")");
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ public class Variable implements Clause, Comparable<Variable> {
|
|||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isTerminal() {
|
public boolean isTerminal() {
|
||||||
return terminal;
|
return terminal;
|
||||||
}
|
}
|
||||||
|
|||||||
25
test/dk/itu/mario/level/grammar/LevelGrammarFactoryTest.java
Normal file
25
test/dk/itu/mario/level/grammar/LevelGrammarFactoryTest.java
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package dk.itu.mario.level.grammar;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class LevelGrammarFactoryTest {
|
||||||
|
@Test
|
||||||
|
public void testCreateGrammar() {
|
||||||
|
LevelGrammar levelGrammar = LevelGrammarFactory.createGrammar();
|
||||||
|
assertNotNull(levelGrammar);
|
||||||
|
System.out.println(levelGrammar.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadFile() throws Exception {
|
||||||
|
File grammarFile = new File("grammars/overland.grm");
|
||||||
|
LevelGrammar levelGrammar = LevelGrammarFactory.createGrammar(grammarFile);
|
||||||
|
assertNotNull(levelGrammar);
|
||||||
|
System.out.println("Read Grammar from file:");
|
||||||
|
System.out.println(levelGrammar.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user