From 109c2d099adb336e0a9903146cd4fd89f1e6ac48 Mon Sep 17 00:00:00 2001 From: Woody Folsom Date: Sat, 17 Mar 2012 18:08:56 -0400 Subject: [PATCH] Rete-based rule system (Drools) correctly fires the appropriate actions when Player's Profile meets certain criteria, based on the rules in rules/LevelTunerRules.drl. --- rules/BasicAccountRule.drl | 7 -- rules/LevelTunerRules.drl | 17 +++++ src/dk/itu/mario/engine/ParsedArgs.java | 4 +- src/dk/itu/mario/level/GrammarTuner.java | 54 ++++++++++++++ .../level/{matcher => }/LevelArchetype.java | 2 +- src/dk/itu/mario/level/PCGLevel.java | 5 +- src/dk/itu/mario/level/PlayerProfile.java | 70 +++++++++++++++++++ .../itu/mario/level/grammar/GrammarTuner.java | 10 --- .../level/grammar/LevelGrammarFactory.java | 4 +- .../mario/level/matcher/ArchetypeMatcher.java | 3 +- .../mario/level/matcher/PlayerProfile.java | 29 -------- .../mario/level/matcher/ProfileMatcher.java | 7 +- src/org/drools/examples/simple/Account.java | 19 ----- .../drools/examples/simple/DroolsExample.java | 65 ----------------- 14 files changed, 153 insertions(+), 143 deletions(-) delete mode 100644 rules/BasicAccountRule.drl create mode 100644 rules/LevelTunerRules.drl create mode 100644 src/dk/itu/mario/level/GrammarTuner.java rename src/dk/itu/mario/level/{matcher => }/LevelArchetype.java (97%) create mode 100644 src/dk/itu/mario/level/PlayerProfile.java delete mode 100644 src/dk/itu/mario/level/grammar/GrammarTuner.java delete mode 100644 src/dk/itu/mario/level/matcher/PlayerProfile.java delete mode 100644 src/org/drools/examples/simple/Account.java delete mode 100644 src/org/drools/examples/simple/DroolsExample.java diff --git a/rules/BasicAccountRule.drl b/rules/BasicAccountRule.drl deleted file mode 100644 index 9ea1bc0..0000000 --- a/rules/BasicAccountRule.drl +++ /dev/null @@ -1,7 +0,0 @@ -package org.drools.examples.simple; -rule "Account balance is less than 100" - when - $account : Account( balance < 100 ) // condition - then - System.out.println("Account number: " + $account.getId() + " is below 100"); // consequence -end \ No newline at end of file diff --git a/rules/LevelTunerRules.drl b/rules/LevelTunerRules.drl new file mode 100644 index 0000000..3ce180a --- /dev/null +++ b/rules/LevelTunerRules.drl @@ -0,0 +1,17 @@ +package dk.itu.mario.level; + +rule "NoviceJumper" + when + playerProfile : PlayerProfile( jumpSkill <= 20 ) // condition + then + System.out.println("PlayerProfile indicates NoviceJumper. Disabling Pipe challenge."); // consequence + playerProfile.setDisabled(LevelComponent.TYPE.PIPE_JUMP); +end + +rule "BeginnerJumper" + when + playerProfile : PlayerProfile( jumpSkill > 20 ) // condition + then + System.out.println("PlayerProfile indicates Beginner (or better) Jumper. Pipe challenge enabled!"); // consequence + playerProfile.setEnabled(LevelComponent.TYPE.PIPE_JUMP); +end \ No newline at end of file diff --git a/src/dk/itu/mario/engine/ParsedArgs.java b/src/dk/itu/mario/engine/ParsedArgs.java index 5de6ea8..b58aaae 100644 --- a/src/dk/itu/mario/engine/ParsedArgs.java +++ b/src/dk/itu/mario/engine/ParsedArgs.java @@ -13,7 +13,7 @@ public class ParsedArgs { private boolean custom = false; private boolean videoCaptureEnabled = false; private int levelRandSeed = (int) (Math.random() * Integer.MAX_VALUE); - private String generatorClass = "MyLevel"; + private String generatorClass = "PCGLevel"; private String videoFileName = "LevelGenerator"; public LevelGenerator createLevelGenerator() { @@ -22,7 +22,7 @@ public class ParsedArgs { } if ("MyLevel".equals(generatorClass)) { return new MyLevelGenerator(); - } else if ("MyNewLevel".equals(generatorClass)) { + } else if ("PCGLevel".equals(generatorClass)) { return new PCGLevelGenerator(); } else if ("CustomizedLevel".equalsIgnoreCase(generatorClass)) { return new CustomizedLevelGenerator(); diff --git a/src/dk/itu/mario/level/GrammarTuner.java b/src/dk/itu/mario/level/GrammarTuner.java new file mode 100644 index 0000000..d2b1e33 --- /dev/null +++ b/src/dk/itu/mario/level/GrammarTuner.java @@ -0,0 +1,54 @@ +package dk.itu.mario.level; + +import java.io.File; +import org.drools.KnowledgeBase; +import org.drools.KnowledgeBaseFactory; +import org.drools.builder.KnowledgeBuilder; +import org.drools.builder.KnowledgeBuilderFactory; +import org.drools.builder.ResourceType; +import org.drools.io.ResourceFactory; +import org.drools.runtime.StatefulKnowledgeSession; + +import dk.itu.mario.level.PlayerProfile; +import dk.itu.mario.level.grammar.LevelGrammar; + +public class GrammarTuner { + /** + * @param args + */ + public static void tune(LevelGrammar levelGrammar, PlayerProfile playerProfile, LevelArchetype archetype) { + KnowledgeBase knowledgeBase = createKnowledgeBase(); + + try { + StatefulKnowledgeSession session = knowledgeBase + .newStatefulKnowledgeSession(); + session.insert(playerProfile); + session.fireAllRules(); + } catch (RuntimeException rte) { + System.out + .println("Unable to initialize StatefulKnowledgeSession for Rete grammar tuning - all challenges will be enabled! Exception: "); + rte.printStackTrace(System.out); + } + } + + private static KnowledgeBase createKnowledgeBase() { + KnowledgeBuilder builder = KnowledgeBuilderFactory + .newKnowledgeBuilder(); + + File accountRules = new File("rules/LevelTunerRules.drl"); + builder.add(ResourceFactory.newFileResource(accountRules), + ResourceType.DRL); + + if (builder.hasErrors()) { + throw new RuntimeException(builder.getErrors().toString()); + } + + KnowledgeBase knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase(); + + // Add to Knowledge Base packages (rules from the drl file). + knowledgeBase.addKnowledgePackages(builder.getKnowledgePackages()); + + return knowledgeBase; + } + +} \ No newline at end of file diff --git a/src/dk/itu/mario/level/matcher/LevelArchetype.java b/src/dk/itu/mario/level/LevelArchetype.java similarity index 97% rename from src/dk/itu/mario/level/matcher/LevelArchetype.java rename to src/dk/itu/mario/level/LevelArchetype.java index 7aebcfb..ea0415d 100644 --- a/src/dk/itu/mario/level/matcher/LevelArchetype.java +++ b/src/dk/itu/mario/level/LevelArchetype.java @@ -1,4 +1,4 @@ -package dk.itu.mario.level.matcher; +package dk.itu.mario.level; import dk.itu.mario.MarioInterface.LevelInterface; diff --git a/src/dk/itu/mario/level/PCGLevel.java b/src/dk/itu/mario/level/PCGLevel.java index 4ae60a6..ea0a09c 100644 --- a/src/dk/itu/mario/level/PCGLevel.java +++ b/src/dk/itu/mario/level/PCGLevel.java @@ -9,13 +9,10 @@ import dk.itu.mario.MarioInterface.LevelInterface; import dk.itu.mario.engine.DataRecorder; import dk.itu.mario.engine.sprites.Enemy; import dk.itu.mario.engine.sprites.SpriteTemplate; -import dk.itu.mario.level.grammar.GrammarTuner; import dk.itu.mario.level.grammar.LevelGrammar; import dk.itu.mario.level.grammar.LevelGrammarFactory; import dk.itu.mario.level.grammar.LevelParseTree; import dk.itu.mario.level.matcher.ArchetypeMatcher; -import dk.itu.mario.level.matcher.LevelArchetype; -import dk.itu.mario.level.matcher.PlayerProfile; import dk.itu.mario.level.matcher.ProfileMatcher; public class PCGLevel extends Level { @@ -81,7 +78,7 @@ public class PCGLevel extends Level { System.out .println("Tuning grammar for PlayerProfile & LevelArchetype using RETE"); - grammar = GrammarTuner.tune(grammar, profile, archetype); + GrammarTuner.tune(grammar, profile, archetype); System.out.println("Creating level."); create(seed, profile, archetype, grammar); diff --git a/src/dk/itu/mario/level/PlayerProfile.java b/src/dk/itu/mario/level/PlayerProfile.java new file mode 100644 index 0000000..cefa4d5 --- /dev/null +++ b/src/dk/itu/mario/level/PlayerProfile.java @@ -0,0 +1,70 @@ +package dk.itu.mario.level; + +import java.util.HashSet; +import java.util.Set; + +public class PlayerProfile { + //From Bartle/Yee models of player psychology: achiever, killer, explorer, manipulator + public enum TYPE { RUNNER, SHOOTER, JUMPER, BUMPER} + //Dreyfus model of skill acquisition: + public enum SKILL_LEVEL { NOVICE, BEGINNER, COMPETENT, PROFICIENT, EXPERT} + + private Set enabledComponents = new HashSet(); + + private SKILL_LEVEL skillLevel; + private TYPE type; + + public PlayerProfile(SKILL_LEVEL skillLevel, TYPE type) { + this.skillLevel = skillLevel; + this .type = type; + } + + public SKILL_LEVEL getSkillLevel() { + return skillLevel; + } + + public int getJumpSkill() { + switch (type) { + case JUMPER : + switch (skillLevel) { + case NOVICE : + return 20; + case BEGINNER : + return 40; + case COMPETENT : + return 60; + case PROFICIENT : + return 80; + case EXPERT : + return 100; + default : + return 0; + } + default : + return 0; + } + } + + public TYPE getType() { + return type; + } + + public void setDisabled(LevelComponent.TYPE type) { + if (enabledComponents.contains(type)) { + System.out.println("Component type disabled: " + type); + enabledComponents.remove(type); + } + } + + public void setEnabled(LevelComponent.TYPE type) { + if (!enabledComponents.contains(type)) { + System.out.println("Component type enabled: " + type); + enabledComponents.add(type); + } + } + + @Override + public String toString() { + return skillLevel + " " + type; + } +} diff --git a/src/dk/itu/mario/level/grammar/GrammarTuner.java b/src/dk/itu/mario/level/grammar/GrammarTuner.java deleted file mode 100644 index 0eb2618..0000000 --- a/src/dk/itu/mario/level/grammar/GrammarTuner.java +++ /dev/null @@ -1,10 +0,0 @@ -package dk.itu.mario.level.grammar; - -import dk.itu.mario.level.matcher.LevelArchetype; -import dk.itu.mario.level.matcher.PlayerProfile; - -public class GrammarTuner { - public static LevelGrammar tune(LevelGrammar grammar, PlayerProfile profile, LevelArchetype archetype) { - return grammar; - } -} diff --git a/src/dk/itu/mario/level/grammar/LevelGrammarFactory.java b/src/dk/itu/mario/level/grammar/LevelGrammarFactory.java index 287a884..9e3dd42 100644 --- a/src/dk/itu/mario/level/grammar/LevelGrammarFactory.java +++ b/src/dk/itu/mario/level/grammar/LevelGrammarFactory.java @@ -1,7 +1,7 @@ package dk.itu.mario.level.grammar; -import dk.itu.mario.level.matcher.LevelArchetype; -import dk.itu.mario.level.matcher.PlayerProfile; +import dk.itu.mario.level.LevelArchetype; +import dk.itu.mario.level.PlayerProfile; public class LevelGrammarFactory { diff --git a/src/dk/itu/mario/level/matcher/ArchetypeMatcher.java b/src/dk/itu/mario/level/matcher/ArchetypeMatcher.java index d0c3b94..2193535 100644 --- a/src/dk/itu/mario/level/matcher/ArchetypeMatcher.java +++ b/src/dk/itu/mario/level/matcher/ArchetypeMatcher.java @@ -2,7 +2,8 @@ package dk.itu.mario.level.matcher; import dk.itu.mario.MarioInterface.GamePlay; import dk.itu.mario.engine.DataRecorder; -import dk.itu.mario.level.matcher.LevelArchetype.TYPE; +import dk.itu.mario.level.LevelArchetype; +import dk.itu.mario.level.LevelArchetype.TYPE; public class ArchetypeMatcher { public static LevelArchetype getMatchingArchetype(GamePlay playerMetrics, DataRecorder detailedInfo) { diff --git a/src/dk/itu/mario/level/matcher/PlayerProfile.java b/src/dk/itu/mario/level/matcher/PlayerProfile.java deleted file mode 100644 index 8b04c4e..0000000 --- a/src/dk/itu/mario/level/matcher/PlayerProfile.java +++ /dev/null @@ -1,29 +0,0 @@ -package dk.itu.mario.level.matcher; - -public class PlayerProfile { - //From Bartle/Yee models of player psychology: achiever, killer, explorer, manipulator - public enum TYPE { RUNNER, SHOOTER, JUMPER, BUMPER} - //Dreyfus model of skill acquisition: - public enum SKILL_LEVEL { NOVICE, BEGINNER, COMPETENT, PROFICIENT, EXPERT} - - private SKILL_LEVEL skillLevel; - private TYPE type; - - public PlayerProfile(SKILL_LEVEL skillLevel, TYPE type) { - this.skillLevel = skillLevel; - this .type = type; - } - - public SKILL_LEVEL getSkillLevel() { - return skillLevel; - } - - public TYPE getType() { - return type; - } - - @Override - public String toString() { - return skillLevel + " " + type; - } -} diff --git a/src/dk/itu/mario/level/matcher/ProfileMatcher.java b/src/dk/itu/mario/level/matcher/ProfileMatcher.java index b260656..6f7a5e2 100644 --- a/src/dk/itu/mario/level/matcher/ProfileMatcher.java +++ b/src/dk/itu/mario/level/matcher/ProfileMatcher.java @@ -2,12 +2,13 @@ package dk.itu.mario.level.matcher; import dk.itu.mario.MarioInterface.GamePlay; import dk.itu.mario.engine.DataRecorder; -import dk.itu.mario.level.matcher.PlayerProfile.SKILL_LEVEL; -import dk.itu.mario.level.matcher.PlayerProfile.TYPE; +import dk.itu.mario.level.PlayerProfile; +import dk.itu.mario.level.PlayerProfile.SKILL_LEVEL; +import dk.itu.mario.level.PlayerProfile.TYPE; public class ProfileMatcher { public static PlayerProfile getMatchingProfile(GamePlay playerMetrics, DataRecorder detailedInfo) { System.out.println("Selecting PlayerProfile based on GamePlay metrics, DataRecorder logs."); - return new PlayerProfile(SKILL_LEVEL.NOVICE, TYPE.RUNNER); + return new PlayerProfile(SKILL_LEVEL.BEGINNER, TYPE.JUMPER); } } diff --git a/src/org/drools/examples/simple/Account.java b/src/org/drools/examples/simple/Account.java deleted file mode 100644 index 210c5b5..0000000 --- a/src/org/drools/examples/simple/Account.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.drools.examples.simple; - -public class Account { - private int balance; - private String Id; - public int getBalance() { - return balance; - } - public void setBalance(int balance) { - this.balance = balance; - } - public String getId() { - return Id; - } - public void setId(String id) { - Id = id; - } - -} diff --git a/src/org/drools/examples/simple/DroolsExample.java b/src/org/drools/examples/simple/DroolsExample.java deleted file mode 100644 index 8722524..0000000 --- a/src/org/drools/examples/simple/DroolsExample.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.drools.examples.simple; - -import java.io.File; -import org.drools.KnowledgeBase; -import org.drools.KnowledgeBaseFactory; -import org.drools.builder.KnowledgeBuilder; -import org.drools.builder.KnowledgeBuilderFactory; -import org.drools.builder.ResourceType; -import org.drools.io.ResourceFactory; -import org.drools.runtime.StatefulKnowledgeSession; -import org.drools.runtime.StatelessKnowledgeSession; - -public class DroolsExample { - /** - * @param args - */ - public static void main(String[] args) { - //Create KnowledgeBase... - KnowledgeBase knowledgeBase = createKnowledgeBase(); - //Create a statefull session - StatefulKnowledgeSession session = knowledgeBase.newStatefulKnowledgeSession(); - - //Create Facts - two ban accounts - Account account = new Account(); - account.setBalance(10); - account.setId("N1"); - - Account account2 = new Account(); - account2.setBalance(120); - account2.setId("N2"); - - //Insert the bank account facts into a State full session - session.insert(account); - session.insert(account2); - - //Note that at this point, afetr fact insertation the Agenda has one activation ready to be fired. - //Account1 is less than 100. - - //Only now we will fire the rules which are already in the agenda - session.fireAllRules(); - - //A message of N1 is less than 100 will be printed to stdout. - } - - /** - * Create new knowledge base - */ - private static KnowledgeBase createKnowledgeBase() { - KnowledgeBuilder builder = KnowledgeBuilderFactory.newKnowledgeBuilder(); - //Add drl file into builder - File accountRules = new File("rules/BasicAccountRule.drl"); //Where the account rule is. - builder.add(ResourceFactory.newFileResource(accountRules), ResourceType.DRL); - if (builder.hasErrors()) { - throw new RuntimeException(builder.getErrors().toString()); - } - - KnowledgeBase knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase(); - - //Add to Knowledge Base packages from the builder which are actually the rules from the drl file. - knowledgeBase.addKnowledgePackages(builder.getKnowledgePackages()); - - return knowledgeBase; - } - -} \ No newline at end of file