diff --git a/.merge_file_a08396 b/.merge_file_a08396 new file mode 100644 index 0000000..ed824c0 --- /dev/null +++ b/.merge_file_a08396 @@ -0,0 +1,17 @@ + +import java.util.ArrayList; +import java.util.Iterator; +/* + A clause contains a list of predicates +*/ +public class Clause implements Iterable { + + + ArrayList facts = new ArrayList<>(); + + @Override + public Iterator iterator() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + +} diff --git a/Wumpus/PerceptBoard.txt b/Wumpus/PerceptBoard.txt new file mode 100644 index 0000000..bfd47a4 --- /dev/null +++ b/Wumpus/PerceptBoard.txt @@ -0,0 +1,21 @@ +20 11 0 +0 0 0 0 0 0 0 1 16 5 2 0 0 0 1 0 4 0 1 18 +0 0 0 0 0 0 1 0 1 2 33 2 0 1 16 1 0 0 2 33 +0 0 0 0 4 1 16 1 0 1 18 3 1 0 1 0 0 0 0 2 +0 0 0 0 0 0 1 0 0 0 3 33 18 1 1 0 4 0 0 0 +0 0 1 0 0 0 0 0 0 4 0 2 1 1 16 1 0 0 0 0 +0 1 16 1 0 1 2 0 0 0 0 0 0 0 1 0 0 0 0 0 +0 1 1 0 1 18 33 2 0 2 0 0 0 0 0 0 2 0 0 1 +1 16 1 0 0 1 2 0 2 32 2 0 0 0 0 2 32 2 1 16 +0 1 0 0 0 0 0 0 0 2 0 0 0 0 0 0 2 0 0 1 +0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 +1 16 1 0 0 0 0 0 0 0 0 0 0 0 0 0 2 32 2 2 +0 1 4 0 2 0 0 0 0 2 0 0 0 0 0 0 0 2 2 32 +0 0 4 2 32 2 0 0 2 32 2 0 0 0 0 0 0 0 0 2 +4 0 0 0 2 0 0 0 4 2 0 1 4 0 0 0 0 4 0 0 +0 0 0 2 32 2 0 0 0 0 1 16 1 0 0 4 0 0 0 0 +0 2 0 0 2 2 0 1 0 0 0 1 0 0 0 0 0 4 0 0 +3 32 2 4 2 32 3 16 1 0 0 0 0 0 0 0 0 0 0 0 +16 3 0 0 0 2 1 1 0 0 0 0 2 0 0 0 8 0 2 0 +1 0 0 0 0 5 16 1 0 4 0 2 32 2 0 1 0 2 32 6 +0 0 0 0 0 4 1 0 4 0 0 0 2 0 1 16 1 0 2 0 diff --git a/Wumpus/clean.txt b/Wumpus/clean.txt new file mode 100644 index 0000000..055344e --- /dev/null +++ b/Wumpus/clean.txt @@ -0,0 +1,21 @@ +20 +0 0 0 0 0 0 0 0 H I 0 0 0 0 0 0 I 0 0 H +0 0 0 0 0 0 0 0 0 0 W 0 0 0 H 0 0 0 0 W +0 0 0 0 I 0 H 0 0 0 H 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 W H 0 0 0 I 0 0 0 +0 0 0 0 0 0 0 0 0 I 0 0 0 0 H 0 0 0 0 0 +0 0 H 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 H W 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 H 0 0 0 0 0 0 0 W 0 0 0 0 0 0 W 0 0 H +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 H 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 W 0 0 +0 0 I 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 W +0 0 I 0 W 0 0 0 0 W 0 0 0 0 0 0 0 0 0 0 +I 0 0 0 0 0 0 0 I 0 0 0 I 0 0 0 0 I 0 0 +0 0 0 0 W 0 0 0 0 0 0 H 0 0 0 I 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 I 0 0 +0 W 0 I 0 W 0 H 0 0 0 0 0 0 0 0 0 0 0 0 +H 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 I H 0 0 I 0 0 W 0 0 0 0 0 W I +0 0 0 0 0 I 0 0 I 0 0 0 0 0 0 H 0 0 0 0 diff --git a/Wumpus/src/Agent.java b/Wumpus/src/Agent.java new file mode 100644 index 0000000..1d2cfff --- /dev/null +++ b/Wumpus/src/Agent.java @@ -0,0 +1,104 @@ + +import java.util.Random; +/* + Agent super class, holds functions variables and constants relavent to both + logical and reactive exploreres + */ + +public class Agent { + + protected Location location; + protected int direction, arrowCount; + protected final byte BREEZE = 0b00000001, STENCH = 0b0000010, BUMP = 0b00000100, GLITTER = 0b00001000, DEATH_PIT = 0b00010000, DEATH_WUMPUS = 0b00100000, SCREAM = 0b01000000; + protected static final int GRAB = 1, MOVE = 2, TURN_LEFT = 3, TURN_RIGHT = 4, SHOOT = 5, QUIT = 6; + protected static final int NORTH = 0, EAST = 1, SOUTH = 2, WEST = 3; + protected byte percepts; + protected World world; + protected Random random = new Random(); + + public Agent(World world, int startingArrowCount, int startingX, int startingY, int startingDirection) { + this.world = world; + this.arrowCount = startingArrowCount; + this.location = new Location(startingX, startingY); + this.direction = startingDirection; + this.percepts = world.getPercepts(); + } + + public Location getForward() { + switch (direction) { + case NORTH: + return new Location(location.x, location.y + 1); + case EAST: + return new Location(location.x + 1, location.y); + case SOUTH: + return new Location(location.x, location.y - 1); + default: + return new Location(location.x - 1, location.y); + } + } + + public void turnRight() { + direction = (direction + 1) % 4; + world.action(TURN_RIGHT); + } + + public void turnLeft() { + direction = (direction + 3) % 4; + world.action(TURN_LEFT); + } + + public int getRight() { + return (direction + 1) % 4; + } + + public int getLeft() { + return (direction + 3) % 4; + } + + public void updateLocation() { + switch (direction) { + case NORTH: + if (location.y < World.size - 1) { + location.y += 1; + } + break; + case WEST: + if (location.x > 0) { + location.x -= 1; + } + break; + case SOUTH: + if (location.y > 0) { + location.y--; + } + break; + case EAST: + if (location.x < World.size - 1) { + location.x++; + } + } + } + + public enum State { + + SAFE, + UNSAFE, + EXPLORED, + UNEXPLORED; + } + + protected class Location { + + int x; + int y; + + public Location(int x, int y) { + this.x = x; + this.y = y; + } + + public boolean equals(Location location) { + return (this.x == location.x && this.y == location.y); + } + } +} diff --git a/Wumpus/src/AtomicTerm.java b/Wumpus/src/AtomicTerm.java deleted file mode 100644 index 783db97..0000000 --- a/Wumpus/src/AtomicTerm.java +++ /dev/null @@ -1,4 +0,0 @@ - -public interface AtomicTerm { - -} diff --git a/Wumpus/src/Clause.java b/Wumpus/src/Clause.java new file mode 100644 index 0000000..5a0dedc --- /dev/null +++ b/Wumpus/src/Clause.java @@ -0,0 +1,34 @@ + +import java.util.ArrayList; + +/* + A clause contains a list of predicates + */ +public class Clause { + + ArrayList facts = new ArrayList<>(); + + public Clause() { + } + + public Clause(Clause clause) { + for (Fact tempFact : clause.facts) { + facts.add(new Fact(tempFact)); + } + } + + public Clause(Fact fact) { + + facts.add(fact); + } + + public static void printClause(Clause clause) { + for (Fact fact : clause.facts) { + fact.printFact(); + if (fact != clause.facts.get(clause.facts.size() - 1)) { + System.out.print(" v "); + } + } + System.out.println(""); + } +} diff --git a/Wumpus/src/ComplexSentence.java b/Wumpus/src/ComplexSentence.java deleted file mode 100644 index cad08d4..0000000 --- a/Wumpus/src/ComplexSentence.java +++ /dev/null @@ -1,49 +0,0 @@ - -public class ComplexSentence implements Sentence { - - private final Connective connective; - private final Sentence[] sentences; - - public ComplexSentence(Connective connective, Sentence... sentences) { - - this.connective = connective; - this.sentences = new Sentence[sentences.length]; - System.arraycopy(sentences, 0, this.sentences, 0, sentences.length); - } - - public Connective getConnective() { - return this.connective; - } - - public Sentence getSentence(int index) { - return this.sentences[index]; - } - - @Override - public boolean equals(Object object) { - if (this == object) { - return true; - } else if ((object == null) || this.getClass() != object.getClass()) { - return false; - } else { - ComplexSentence sentence = (ComplexSentence) object; - if ((this.connective == sentence.connective)) { - for (int i = 0; i < sentences.length; i ++) { - if (!this.sentences[i].equals(sentence.sentences[i])) { - return false; - } - } - } - } - return true; - } - - @Override - public String toString() { - - //requires implementation - String stringSentence = ""; - - return stringSentence; - } -} diff --git a/Wumpus/src/Connective.java b/Wumpus/src/Connective.java deleted file mode 100644 index a99c1e6..0000000 --- a/Wumpus/src/Connective.java +++ /dev/null @@ -1,20 +0,0 @@ - -public enum Connective { - - NOT("~"), - AND("&"), - OR("|"), - IMPLICATION("=>"), - BICONDITIONAL("<=>"); - - private final String symbol; - - @Override - public String toString() { - return symbol; - } - - private Connective(String symbol) { - this.symbol = symbol; - } -} diff --git a/Wumpus/src/Driver.java b/Wumpus/src/Driver.java index c53c48a..954779c 100644 --- a/Wumpus/src/Driver.java +++ b/Wumpus/src/Driver.java @@ -6,27 +6,53 @@ public class Driver { public static void main(String[] args) throws IOException { - Driver driver = new Driver(); - Tester tester = new Tester(); - tester.testInferenceEngine(); - //driver.makeGame(); +// Driver driver = new Driver(); +// Tester tester = new Tester(); +// tester.testPathFinder(); + //Tester tester = new Tester(); + //tester.testPathFinder(); + //tester.testUnify(); + // tester.testInferenceEngine(); + makeGame(); +// World world = new World("PerceptBoard.txt"); +// world.startGame("LogicExplorer"); } - public void makeGame() throws IOException { - - BufferedReader dataIn = new BufferedReader(new InputStreamReader(System.in)); - int[] prob = new int[3]; - System.out.print("% chance of generating pit: "); - prob[0] = Integer.parseInt(dataIn.readLine()); - System.out.println(); - System.out.print("% chance of generating obstacle: "); - prob[1] = Integer.parseInt(dataIn.readLine()); - System.out.println(""); - System.out.print("% chance of generating wumpus: "); - prob[2] = Integer.parseInt(dataIn.readLine()); - System.out.println(""); - - WumpusGame game = new WumpusGame(5, prob); + public static void makeGame() throws IOException { + + boolean newBoard = true; + boolean newStart = true; + WumpusGame game; + if (newBoard) { + BufferedReader dataIn = new BufferedReader(new InputStreamReader(System.in)); + int[] prob = new int[3]; + System.out.println("Size of board: "); + + int size = 20;//Integer.parseInt(dataIn.readLine()); + System.out.print("% chance of generating pit: "); + prob[0] = 5;// Integer.parseInt(dataIn.readLine()); + System.out.println(); + System.out.print("% chance of generating obstacle: "); + prob[1] = 5;//Integer.parseInt(dataIn.readLine()); + System.out.println(""); + System.out.print("% chance of generating wumpus: "); + prob[2] = 5;//Integer.parseInt(dataIn.readLine()); + System.out.println(""); + game = new WumpusGame(size, prob); + } + if (newStart) { + game = new WumpusGame("clean.txt"); + } + World world = new World("PerceptBoard.txt"); + world.startGame("LogicExplorer"); + //Agent explorer = new ReactiveExplorer(world, world.getLocation(), world.direction,world.getPercepts(), world.arrowCount); + // World world = new World("PerceptBoard.txt"); + //world.startGame("LogicExplorer"); + // world.startGame("ReactiveExplorer"); + //Agent explorer = new LogicExplorer(world); + + //world = new World("PerceptBoard.txt"); + // Agent explorer = new ReactiveExplorer(world, world.getLocation(), world.direction, world.getPercepts(), world.arrowCount); } } diff --git a/Wumpus/src/Fact.java b/Wumpus/src/Fact.java index 8dbbe7c..f8d30f9 100644 --- a/Wumpus/src/Fact.java +++ b/Wumpus/src/Fact.java @@ -1,18 +1,53 @@ import java.util.ArrayList; -public class Fact { +/* + A Fact contains a set of variables and a predicate string, extends object variable +*/ - ArrayList variables = new ArrayList<>(); - String predicate; - boolean isEvaluation; - int operator; +public class Fact extends Variable { + + protected ArrayList variables = new ArrayList<>(); + protected String predicate; + protected boolean not; + + public Fact() { + + } + + public Fact(Fact fact) { + + not = fact.not; + for (Variable var : fact.variables) { + variables.add(new Variable(var)); + } + predicate = fact.predicate; + this.function = fact.function; + + } + + public Fact(String predicate, int var1Val, boolean var1Var, int var2Val, boolean var2Var, boolean not, IFunction var1Function, IFunction var2Function) { + + Variable var1 = new Variable(var1Val, var1Var, var1Function); + Variable var2 = new Variable(var2Val, var2Var, var2Function); + variables.add(var1); + variables.add(var2); + this.predicate = predicate; + + this.not = not; + } public void printFact() { + + if (not) { + System.out.print("!"); + } System.out.print(predicate + "("); for (Variable var : variables) { var.printVariable(); - System.out.print(", "); + if (var != variables.get(variables.size() - 1)) { + System.out.print(", "); + } } System.out.print(") "); } diff --git a/Wumpus/src/InferenceEngine.java b/Wumpus/src/InferenceEngine.java index d42b1b7..9c79297 100644 --- a/Wumpus/src/InferenceEngine.java +++ b/Wumpus/src/InferenceEngine.java @@ -5,163 +5,174 @@ public class InferenceEngine { KnowledgeBase kb; - public ArrayList convertToCNF(Rule rule) { - ArrayList cnfRules = new ArrayList<>(); - ArrayList toConvertRules = new ArrayList<>(); - toConvertRules.add(rule); - //Step 1: break iff into two implication cases - convertToCNFStepOne(toConvertRules); - - return cnfRules; - } + public InferenceEngine(KnowledgeBase kb) { - public void convertToCNFStepOne(ArrayList rules) { - //Convert every iff to two implies statements - while (!rules.isEmpty()) { - ArrayList allRules = getAllRules(rules.get(0)); - for (Rule rule : allRules) { - if (rule.connector == Rule.IFF) { //for each a iff b we change it to ((a=>b)&&(b=>a)) - Rule newRule1 = new Rule(); - newRule1.leftRule = rule.leftRule; - newRule1.rightRule = rule.rightRule; - newRule1.connector = Rule.IMPLIES; - Rule newRule2 = new Rule(); - newRule2.leftRule = rule.rightRule; - newRule2.rightRule = rule.leftRule; - newRule2.connector = Rule.IMPLIES; - rule.leftRule = newRule1; - rule.rightRule = newRule2; - rule.connector = Rule.AND; - } - } - } + this.kb = kb; } - public void convertToCNFStepTwo(ArrayList rules) { - //Convert every a=>b to !a V b - while (!rules.isEmpty()) { - ArrayList allRules = getAllRules(rules.get(0)); - for (Rule rule : allRules) { - if (rule.connector == Rule.IMPLIES) { - rule.leftRule.negated = !rule.leftRule.negated; - rule.connector = Rule.OR; - } - } - } - } + public boolean follows(Fact fact) { - public void convertToCNFStepThree(ArrayList rules) { - //Deal with negated quantifiers, ie !(EXISTS x, p) to (Vx, !p) or !(Vx, p) to (Exists x, !p) - while (!rules.isEmpty()) { - ArrayList allRules = getAllRules(rules.get(0)); - for (Rule rule : allRules) { - for (Quantifier quantifier : rule.quantifiers) { - if (quantifier.not) { //we have a negated quantifier, move inwards - quantifier.not = false; - quantifier.isExistential = !quantifier.isExistential; - rule.negated = !rule.negated; - } - } + Clause clause = new Clause(); + //Step 1: negate input fact + fact.not = !fact.not; - } + clause.facts.add(fact); + ArrayList tempClone = new ArrayList<>(kb.getClauses()); + ArrayList kbClausesClone = new ArrayList<>(); + for (Clause tempClause : tempClone) { + kbClausesClone.add(new Clause(tempClause)); } - } - public void convertToCNFStepFour(ArrayList rules) { - //Move negation inwards, ie !(A&&B) to (!A V !B) also !(A V B) to (!A && !B) - while (!rules.isEmpty()) { - ArrayList allRules = getAllRules(rules.get(0)); - for (Rule rule : allRules) {//Maybe we have to order outter terms before inner terms for this to work?? - if (rule.negated) { //We assume only connectors left are OR and AND - rule.negated = false; - rule.leftRule.negated = !rule.leftRule.negated; - rule.rightRule.negated = !rule.rightRule.negated; - if (rule.connector == Rule.OR) { - rule.connector = Rule.AND; - } else { - rule.connector = Rule.OR; - } + //shortcut, check for direct contradiction to original fact + for (Clause factClause : kbClausesClone) { + if (factClause.facts.size() == 1) { + Fact holder = factClause.facts.get(0); + if (holder.predicate.equals(fact.predicate) && holder.not != fact.not && Unifier.equalWithSubs(holder, fact, new ArrayList<>())) { + return true; } } } - } - public void convertToCNFStepFive(ArrayList rules) { - //Pair every quantifier to a unique variable - int uniqueVariableId = 0;//We will increment this for each varId used. - while (!rules.isEmpty()) { - ArrayList allRules = getAllRules(rules.get(0)); - - for (int i = allRules.size() - 1; i >= 0; i--) {//We want to work with most inner rules prior to the ones containing them - Rule rule = allRules.get(i); - for (Quantifier quantifier : rule.quantifiers) { - ArrayList subRules = getAllRules(rule); - for (Rule subRule : subRules) { - if (subRule.justFact) { - for (Variable var : subRule.fact.variables) { - if (var.variableId == quantifier.variableId) { - var.variableId = uniqueVariableId; + boolean keepGoing; + //Step 2: Run negated facts against all known facts + while (!kbClausesClone.isEmpty()) { + keepGoing = true; + for (Clause kbClause : kbClausesClone) { + for (Fact kbFact : kbClause.facts) { + + for (Fact followFact : clause.facts) { + if (kbFact.predicate.equals(followFact.predicate) && kbFact.not == !followFact.not) { + //extend clause with everything in kbClause, remove kbFact and followFact, start over + boolean skipOut = false; + for (int i = 0; i < kbFact.variables.size(); i++) { + Variable var1 = kbFact.variables.get(i); + Variable var2 = followFact.variables.get(i); + if (var1.value != var2.value) { + skipOut = true; + } + } + if (!skipOut) { + kbClause.facts.remove(kbFact);//maybe commment out this line + clause.facts.remove(followFact); + clause.facts.addAll(kbClause.facts); + + //before starting over check if clause is empty... + if (clause.facts.isEmpty()) { + return true; } + keepGoing = false; } } + if (!keepGoing) { + break; + } + } + if (!keepGoing) { + break; } - uniqueVariableId++; + } + if (!keepGoing) { + break; } } - } - } - - public void convertToCNFStepSix(ArrayList rules) { - //Move all quantifiers to 'top' rule - while (!rules.isEmpty()) { - ArrayList allRules = getAllRules(rules.get(0));//assume allRules.get(0) is topMost rule. - ArrayList allQuantifiers = new ArrayList<>(); - for (Rule rule : allRules) { - allQuantifiers.addAll(rule.quantifiers); - rule.quantifiers = new ArrayList<>(); + if (keepGoing) { + return false; } - allRules.get(0).quantifiers = allQuantifiers; } + return false; } - public void convertToCNFStepSeven(ArrayList rules) { - //Skolemize existentials... this will be a bitch... - while (!rules.isEmpty()) { - ArrayList allRules = getAllRules(rules.get(0)); - for (Rule rule : allRules) { + public void infer(Fact factStart) { - } + //look at each fact in each clause in kb.rules, if predicates match, and negations are opposite + Fact fact = new Fact(factStart); + ArrayList tempClone = new ArrayList<>(kb.rules); + tempClone.addAll(kb.getClauses()); + ArrayList kbClausesClone = new ArrayList<>(); + for (Clause tempClause : tempClone) { + kbClausesClone.add(new Clause(tempClause)); } - } - - public void convertToCNFStepEight(ArrayList rules) { - while (!rules.isEmpty()) { - ArrayList allRules = getAllRules(rules.get(0)); - for (Rule rule : allRules) { + for (Clause clause : kbClausesClone) { + for (Fact ruleFact : clause.facts) { + if (ruleFact.predicate.equals(fact.predicate) && ruleFact.not == !fact.not) { + ArrayList substitutions = Unifier.unify(fact, ruleFact); + if (!Unifier.equalWithSubs(fact, ruleFact, substitutions)) { + continue; + } + for (Substitute sub : substitutions) { + for (Variable var : fact.variables) { + if (var.isVariable && var.variableId == sub.varIdToSubstitute) { + var.isVariable = false; + var.value = sub.valToSubstituteWith; + if (var.function != null) { + var.value = var.function.process(var.value); + } + } + } + for (Fact tempFact : clause.facts) { + + for (Variable var : tempFact.variables) { + if (var.isVariable && var.variableId == sub.varIdToSubstitute) { + var.isVariable = false; + var.value = sub.valToSubstituteWith; + if (var.function != null) { + var.value = var.function.process(var.value); + } + } + } + } + } + clause.facts.remove(ruleFact); + System.out.println("Inferred:"); + Clause.printClause(clause); + kb.addToClauses(clause); + break; + } } } } - public void convertToCNFStepNine(ArrayList rules) { - while (!rules.isEmpty()) { - ArrayList allRules = getAllRules(rules.get(0)); - for (Rule rule : allRules) { - - } - } - } + public void infer(Clause clauseToCheck) { - public ArrayList getAllRules(Rule rule) { - ArrayList allRules = new ArrayList<>(); - if (rule.leftRule != null) { - allRules.addAll(getAllRules(rule.leftRule)); + ArrayList tempClone = new ArrayList<>(kb.rules); + tempClone.addAll(kb.getClauses()); + ArrayList kbClausesClone = new ArrayList<>(); + for (Clause tempClause : tempClone) { + kbClausesClone.add(new Clause(tempClause)); } - if (rule.rightRule != null) { - allRules.addAll(getAllRules(rule.rightRule)); + Clause addedClause = new Clause(clauseToCheck); + for (Clause ruleClause : kbClausesClone) { + if (ruleClause.facts.size() == 1) { + Fact ruleFact = new Fact(ruleClause.facts.get(0));//we use a copy + Clause clause = new Clause(addedClause);//copy the added clause, redo each time + for (Fact fact : clause.facts) { + if (fact.predicate.equals(ruleFact.predicate) && fact.not != ruleFact.not) { + ArrayList substitutions = Unifier.unify(fact, ruleFact); + for (Substitute sub : substitutions) { + for (Variable var : ruleFact.variables) { + if (var.isVariable && var.variableId == sub.varIdToSubstitute) { + var.isVariable = false; + var.value = sub.valToSubstituteWith; + } + } + for (Fact changeFact : clause.facts) { + for (Variable var : changeFact.variables) { + if (var.isVariable && var.variableId == sub.varIdToSubstitute) { + var.isVariable = false; + var.value = sub.valToSubstituteWith; + } + } + } + } + if (Unifier.equalWithSubs(fact, ruleFact, substitutions)) { + clause.facts.remove(fact); + kb.addToClauses(clause); + return; + } + } + } + } } - allRules.add(rule); - - return allRules; } } diff --git a/Wumpus/src/KnowledgeBase.java b/Wumpus/src/KnowledgeBase.java index ab9f733..8922c68 100644 --- a/Wumpus/src/KnowledgeBase.java +++ b/Wumpus/src/KnowledgeBase.java @@ -1,50 +1,255 @@ +import java.util.ArrayList; + public class KnowledgeBase { + protected final InferenceEngine inferenceEngine = new InferenceEngine(this); + protected final ArrayList clauses = new ArrayList<>(); + protected final ArrayList rules = new ArrayList<>(); + + public ArrayList getClauses() { + return clauses; + } + + public void addToClauses(Clause clause) { + if (clause.facts.isEmpty()) { + return; + } else { + for (Fact fact : clause.facts) { + for (Variable var : fact.variables) { + if (var.isVariable) { + return; + } + } + } + } + if (clause.facts.size() == 1) { + if (factInClauses(clause.facts.get(0))) { + return; + } + System.out.println("Added to kb Clause: "); + System.out.print("\t"); + Clause.printClause(clause); + clauses.add(clause); + inferenceEngine.infer(clause.facts.get(0)); + } else { + int beforeSize = clauses.size(); + inferenceEngine.infer(clause); + if (clauses.size() == beforeSize) { + clauses.add(clause);//We only need add the clause itself if we didn't infer anything, otherwise the inferred shortened clause which is more valuable got added + + System.out.println("Added to kb Clause: "); + System.out.print("\t"); + Clause.printClause(clause); + } + } + //clauses.add(clause); + + } + public void initializeRules() { - //Special predicate: Evaluate - //Percepts come in as Glitter(t) Stench(t) Breeze(t) Bump(t) - //Vx,y,a,b Adjacent(x,y,a,b) iff [a,b] in {(x-1,y),(x+1,y),(x,y-1),(x,y+1)} - //Vd,t Facing(d,t) => Facing(d,t+1) OR Action(Turnleft,t) OR Action(TurnRight,t) - //Vt,v Facing(d,t) AND Action(Turnleft,t)=>Facing((d-1)%4, t+1) - //Vt,v Facing(d,t) AND Action(TurnRight,t)=>Facing((d+1)%4,t+1) - //Vt,x Arrows(x,t) => Arrows(x,t+1) OR Action(SHOOT,t) - //Vt,x HasArrow(t) iff Arrows(x,t) && x>0 - //Vt,x Action(SHOOT,t) AND Arrows(x,t)AND HasArrow(t) => Arrows(x-1,t+1) - //Vt At(Agent,x,y,t) && Facing(NORTH,t)&&Action(SHOOT,t)=>(Exists(a) st Scream(t+1) AND WumpusDead(x+a,y - //Vt,x Action(x,t) => !(Action(y,t) AND True(EVALUATE(x,y,NOTEQUALS)) - //True(1) - //!True(0) - //percept rules: - //At(Agent, x, y, t) && Stench(x,y,t) => At(Stink,x,y,t) - //Stink(x,y,t) iff Exists(a,b) st Wumpus(a,b,t) ^ Adjacent(x,y,a,b) - //Wumpus(x,y,t) => Wumpus(x,y,t+1) XOR WumpusDead(x,y,t+1) p XOR q is (pVq)&&(!(p&&q)) - //WumpusDead(x,y,t)=>WumpusDead(x,y,t+1) - //Stench(x,y)=>Wumpus(x-1,y)ORWumpus(x+1,y)ORWumpus(x,y-1)ORWumpus(x,y+1) - initializeStenchRule(); + Clause stench = new Clause(); + Fact stenchXY = new Fact("Stench", 0, true, 1, true, true, null, null); + Fact wumpusxminy = new Fact("Wumpus", 0, true, 1, true, false, new MinusFunction(), null); + Fact wumpusxplusy = new Fact("Wumpus", 0, true, 1, true, false, new PlusFunction(), null); + Fact wumpusxymin = new Fact("Wumpus", 0, true, 1, true, false, null, new MinusFunction()); + Fact wumpusxyplus = new Fact("Wumpus", 0, true, 1, true, false, null, new PlusFunction()); + stench.facts.add(stenchXY); + stench.facts.add(wumpusxminy); + stench.facts.add(wumpusxplusy); + stench.facts.add(wumpusxymin); + stench.facts.add(wumpusxyplus); + rules.add(stench); + Clause stench1 = new Clause(); + Fact stenchXY1 = new Fact(stenchXY); + stenchXY1.not = false; + Fact wumpusxminy1 = new Fact(wumpusxminy); + wumpusxminy1.not = true; + stench1.facts.add(stenchXY1); + stench1.facts.add(wumpusxminy1); + rules.add(stench1); + + Clause stench2 = new Clause(); + Fact stenchXY2 = new Fact(stenchXY); + stenchXY2.not = false; + Fact wumpusxplusy1 = new Fact(wumpusxplusy); + wumpusxplusy1.not = true; + stench2.facts.add(stenchXY2); + stench2.facts.add(wumpusxplusy1); + rules.add(stench2); + + Clause stench3 = new Clause(); + Fact stenchXY3 = new Fact(stenchXY); + stenchXY3.not = false; + Fact wumpusxymin1 = new Fact(wumpusxymin); + wumpusxymin1.not = true; + stench3.facts.add(stenchXY3); + stench3.facts.add(wumpusxymin1); + rules.add(stench3); + + Clause stench4 = new Clause(); + Fact stenchXY4 = new Fact(stenchXY); + stenchXY4.not = false; + Fact wumpusxyplus1 = new Fact(wumpusxyplus); + wumpusxyplus1.not = true; + stench4.facts.add(stenchXY4); + stench4.facts.add(wumpusxyplus1); + rules.add(stench4); + + Clause breeze = new Clause(); + Fact breezeXY = new Fact("Breeze", 0, true, 1, true, true, null, null); + Fact pitxminy = new Fact("Pit", 0, true, 1, true, false, new MinusFunction(), null); + Fact pitxplusy = new Fact("Pit", 0, true, 1, true, false, new PlusFunction(), null); + Fact pitxymin = new Fact("Pit", 0, true, 1, true, false, null, new MinusFunction()); + Fact pitxyplus = new Fact("Pit", 0, true, 1, true, false, null, new PlusFunction()); + breeze.facts.add(breezeXY); + breeze.facts.add(pitxminy); + breeze.facts.add(pitxplusy); + breeze.facts.add(pitxymin); + breeze.facts.add(pitxyplus); + rules.add(breeze); + + Clause breeze1 = new Clause(); + Fact breezeXY1 = new Fact(breezeXY); + breezeXY1.not = false; + Fact pitxminy1 = new Fact(pitxminy); + pitxminy1.not = true; + breeze1.facts.add(breezeXY1); + breeze1.facts.add(pitxminy1); + rules.add(breeze1); + + Clause breeze2 = new Clause(); + Fact breezeXY2 = new Fact(breezeXY); + + breezeXY2.not = false; + Fact pitxplusy1 = new Fact(pitxplusy); + pitxplusy1.not = true; + breeze2.facts.add(breezeXY2); + breeze2.facts.add(pitxplusy1); + rules.add(breeze2); + + Clause breeze3 = new Clause(); + Fact breezeXY3 = new Fact(breezeXY); - //Breeze mimics this - //Glitter(x,y,t)=>Gold(x,y,t) - //Bump(x,y)=>Obsticle(x,y) - //action(Move - //(!Wumpus(x,y)&&!Pit(x,y))=>Safe(x,y) - //!Wumpus(-1,y)&&!Wumpus(x,-1)&&!Pit(-1,z)&&!Pit(-1,a)//no boarder things - //!Wumpus(x,size)&&!Wumpus(size,y)&&!PIT(z,size)&&!Pit(size,a) + breezeXY3.not = false; + Fact pitxymin1 = new Fact(pitxymin); + pitxymin1.not = true; + breeze3.facts.add(breezeXY3); + breeze3.facts.add(pitxymin1); + rules.add(breeze3); + + Clause breeze4 = new Clause(); + Fact breezeXY4 = new Fact(breezeXY); + breezeXY4.not = false; + Fact pitxyplus1 = new Fact(pitxyplus); + pitxyplus1.not = true; + breeze4.facts.add(breezeXY4); + breeze4.facts.add(pitxyplus1); + rules.add(breeze4); + + rules.add(new Clause(new Fact("Wumpus", -1, false, 1, true, true, null, null))); + rules.add(new Clause(new Fact("Wumpus", 0, true, -1, false, true, null, null))); + rules.add(new Clause(new Fact("Wumpus", 0, true, World.size, false, true, null, null))); + rules.add(new Clause(new Fact("Wumpus", World.size, false, 1, true, true, null, null))); + rules.add(new Clause(new Fact("Pit", -1, false, 1, true, true, null, null))); + rules.add(new Clause(new Fact("Pit", 0, true, -1, false, true, null, null))); + rules.add(new Clause(new Fact("Pit", 0, true, World.size, false, true, null, null))); + rules.add(new Clause(new Fact("Pit", World.size, false, 1, true, true, null, null))); + + Clause notWumpusOrNotPit = new Clause(); + Fact notWumpus = new Fact("Wumpus", 0, true, 1, true, true, null, null); + Fact notPit = new Fact("Pit", 0, true, 1, true, true, null, null); + notWumpusOrNotPit.facts.add(notWumpus); + notWumpusOrNotPit.facts.add(notPit); + rules.add(notWumpusOrNotPit); + Clause notWumpusOrNotObstacle = new Clause(); + Fact notObstacle = new Fact("Obstacle", 0, true, 1, true, true, null, null); + notWumpusOrNotObstacle.facts.add(new Fact(notWumpus)); + notWumpusOrNotObstacle.facts.add(notObstacle); + + rules.add(notWumpusOrNotObstacle); + Clause notPitOrNotObstacle = new Clause(); + notPitOrNotObstacle.facts.add(new Fact(notObstacle)); + notPitOrNotObstacle.facts.add(new Fact(notPit)); + rules.add(notPitOrNotObstacle); + + } + + public boolean ask(Fact fact) { + + return inferenceEngine.follows(fact); } - public boolean ask(String placeholder) { - return true; + public void tell(Clause clause) { + + addToClauses(clause); } - public void tell(String placeholder) { + public void tell(Fact fact) { + + //If told DeadWumpus delete the wumpus entry at that position and add !Wumpus(x,y) + if (fact.predicate.equals("DeadWumpus")) { + removeWumpusAndStench(fact); + + } + if (factInClauses(fact)) { + return; + } + + addToClauses(new Clause(fact)); + //and check if there is a stench at any adjacent position, remove those facts too } - private void initializeStenchRule() { - //Stench(x,y)=>Wumpus(x-1,y)OR(Wumpus(x+1,y)OR(Wumpus(x,y-1) OR Wumpus(x,y+1))) - Quantifier x = new Quantifier(); + private void removeWumpusAndStench(Fact fact) { - //ClauseFormConvertion from here... + for (int i = clauses.size() - 1; i >= 0; i--) { + if (clauses.get(i).facts.size() == 1) { + Fact toRemove = clauses.get(i).facts.get(0); + if (toRemove.predicate.equals("Wumpus")) { + + if (toRemove.variables.get(0).value == fact.variables.get(0).value && toRemove.variables.get(1).value == fact.variables.get(1).value) { + toRemove.not = true; + } + } else if (toRemove.predicate.equals("Stench")) { + if ((toRemove.variables.get(0).value == fact.variables.get(0).value && Math.abs(toRemove.variables.get(1).value - fact.variables.get(1).value) == 1) || (toRemove.variables.get(1).value == fact.variables.get(1).value && Math.abs(toRemove.variables.get(0).value - fact.variables.get(0).value) == 1)) { + clauses.remove(i); + } + } + } else { + for (Fact toRemoveFact : clauses.get(i).facts) { + if (toRemoveFact.predicate.equals("Wumpus")) { + if (toRemoveFact.variables.get(0).value == fact.variables.get(0).value && toRemoveFact.variables.get(1).value == fact.variables.get(1).value) { + if (toRemoveFact.not == fact.not) { + toRemoveFact.not = true; + break; + } + } + } + } + } + } } + private boolean factInClauses(Fact fact) { + + for (Clause clause : clauses) { + if (clause.facts.size() == 1) { + Fact clauseFact = clause.facts.get(0); + if (clauseFact.predicate.equals(fact.predicate)) { + for (int i = 0; i < fact.variables.size(); i++) { + + if (clauseFact.variables.get(i).value == fact.variables.get(i).value) { + if (i == fact.variables.size() - 1) { + return true; + } + } else { + break; + } + } + } + } + } + return false; + } } diff --git a/Wumpus/src/LogicExplorer.java b/Wumpus/src/LogicExplorer.java index c09620a..5ec071e 100644 --- a/Wumpus/src/LogicExplorer.java +++ b/Wumpus/src/LogicExplorer.java @@ -1,99 +1,448 @@ -public class LogicExplorer { +import java.util.ArrayList; + +public class LogicExplorer extends Agent { - private final World world; private final KnowledgeBase kb; - private int arrowCount; - - private final byte BREEZE = 0b00000001; - private final byte STENTCH = 0b0000010; - private final byte BUMP = 0b00000100; - private final byte GLITTER = 0b00001000; - private final byte DEATH_BY_WUMPUS = 0b00010000; - private final byte DEATH_BY_PIT = 0b00100000; - private final byte SCREAM = 0b01000000; - - public LogicExplorer(World world) { - this.world = world; + private final ArrayList frontier = new ArrayList<>(); + private final boolean[][] searchedPositions; + private Location safeSpace; + private Location wumpusSpace; + private final ArrayList moveHistory = new ArrayList<>(); + boolean notFirstMove = false; + + public LogicExplorer(World world, int startingArrows, int startingX, int startingY, int direction) { + super(world, startingArrows, startingX, startingY, direction); kb = new KnowledgeBase(); kb.initializeRules(); - this.arrowCount = world.arrowCount; + this.searchedPositions = new boolean[World.size][World.size]; + searchedPositions[location.x][location.y] = true; + percepts = world.getPercepts(); + processPercepts(); + run(); + } + + private void run() { + + while (true) { + if (!notFirstMove) { + percepts = world.getPercepts(); + processPercepts(); + } + decideNextAction(); + } + } + + @Override + public void updateLocation() { + + if (notFirstMove) { + super.updateLocation(); + } else { + notFirstMove = true; + } + searchedPositions[location.x][location.y] = true; + expandFrontier(); + } + + public void addToFrontier(Location loc) { + + if (!searchedPositions[loc.x][loc.y]) { + if (inFrontier(loc)) { + removeFromFrontier(loc); + } + frontier.add(loc); + } + } + + public void expandFrontier() { + + if (location.x > 0 && !searchedPositions[location.x - 1][location.y]) { + addToFrontier(new Location(location.x - 1, location.y)); + } + if (location.x < World.size - 1 && !searchedPositions[location.x + 1][location.y]) { + addToFrontier(new Location(location.x + 1, location.y)); + } + if (location.y > 0 && !searchedPositions[location.x][location.y - 1]) { + addToFrontier(new Location(location.x, location.y - 1)); + } + if (location.y < World.size - 1 && !searchedPositions[location.x][location.y + 1]) { + addToFrontier(new Location(location.x, location.y + 1)); + } } private void move(int action) { switch (action) { - case 2: - kb.tell(encodePercepts(world.action(action))); + case GRAB: + System.out.println("Explorer Action:Grabbing"); + world.action(GRAB); + System.out.println("Game failed to end after action(GRAB)."); + break; + case MOVE: + moveHistory.add(MOVE); + System.out.println("Explorer Action: Moving"); + percepts = (byte) world.action(MOVE); + if (getForward().x >= 0 && getForward().x < World.size && getForward().y >= 0 && getForward().y < World.size) { + searchedPositions[getForward().x][getForward().y] = true; + } + processPercepts(); break; - case 5: + case TURN_LEFT: + moveHistory.add(TURN_LEFT); + System.out.println("Explorer Action: Turning left"); + direction = (direction + 3) % 4; + world.action(TURN_LEFT); + break; + case TURN_RIGHT: + moveHistory.add(TURN_RIGHT); + System.out.println("Explorer Action: turning right"); + direction = (direction + 1) % 4; + world.action(TURN_RIGHT); + break; + case SHOOT: + System.out.println("Explorer Action: shooting"); arrowCount--; - world.action(action); + percepts = (byte) world.action(SHOOT); + if ((percepts & SCREAM) != 0) { + processPercepts(); + } break; - default: - world.action(action); + case QUIT: + System.out.println("no possible solution"); + world.action(QUIT); + System.out.println("Game failed to end after action(QUIT)."); break; + default: + System.out.println("Error processing movement."); } + System.out.println("(Agent) thinks location after move: " + location.x + ", " + location.y); } - private String encodePercepts(int percepts) { + public void removeFromFrontier(Location locToRemove) { + for (Location loc : frontier) { + if (loc.x == locToRemove.x && loc.y == locToRemove.y) { + frontier.remove(loc); + return; + } + } + } - String sentence = ""; + private void processPercepts() { //there might still be an issue with wumpus death since its a seperate percept + if ((percepts & DEATH_PIT) == DEATH_PIT || (percepts & DEATH_WUMPUS) != 0) { + System.out.println("Explorer died after moving"); + Clause clause = new Clause(new Fact("Wumpus", getForward().x, false, getForward().y, false, false, null, null)); + clause.facts.add(new Fact("Pit", getForward().x, false, getForward().y, false, false, null, null)); + kb.tell(clause); + if (kb.ask(new Fact("Wumpus", getForward().x, false, getForward().y, false, true, null, null))) { + removeFromFrontier(getForward()); //we want to keep it in frontier so we can kill wumpus + } + moveHistory.remove(moveHistory.size() - 1);//why die again? + } + if ((((percepts & BUMP) != BUMP) && (percepts & DEATH_PIT) != DEATH_PIT) && ((percepts & DEATH_WUMPUS) != DEATH_WUMPUS) && ((percepts & SCREAM) == 0)) { + updateLocation(); + kb.tell(new Fact("Wumpus", location.x, false, location.y, false, true, null, null)); + kb.tell(new Fact("Pit", location.x, false, location.y, false, true, null, null)); + kb.tell(new Fact("Obstacle", location.x, false, location.y, false, true, null, null)); + removeFromFrontier(location); + } + if ((percepts & BUMP) == BUMP) { //theres soemthing funky here, hes bumping when there arent obsticales + System.out.println("Explorer bumped after moving"); + moveHistory.remove(moveHistory.size() - 1);//why bump again? + removeFromFrontier(getForward()); + kb.tell(new Fact("Obstacle", getForward().x, false, getForward().y, false, false, null, null)); + } else if ((percepts & DEATH_PIT) == 0 && (percepts & DEATH_WUMPUS) == 0) {//you if you bump than the only inputted percept should've been bump + if ((percepts & STENCH) != 0) { + kb.tell(new Fact("Stench", location.x, false, location.y, false, false, null, null)); + } else { + kb.tell(new Fact("Stench", location.x, false, location.y, false, true, null, null)); + } + if ((percepts & BREEZE) != 0) { + kb.tell(new Fact("Breeze", location.x, false, location.y, false, false, null, null)); + } else { + kb.tell(new Fact("Breeze", location.x, false, location.y, false, true, null, null)); + } + if ((percepts & SCREAM) != 0) { + kb.tell(new Fact("DeadWumpus", getForward().x, false, getForward().y, false, false, null, null)); + } + } + } + + private boolean inFrontier(Location loc) { - //conversion logic goes here - return sentence; + for (Location front : frontier) { + if (front.x == loc.x && front.y == loc.y) { + return true; + } + } + return false; } - public void decideAction(byte percepts) { - - if ((percepts & GLITTER) != 0) {//maybe just kb.ask("Holding(Gold,Result(Grab,CurrentPosition))"): is better, no percept based logic within agent. - move(1); //grab gold and end game - } else if (kb.ask("Safe(Result(Move,CurrentPosition))&&!Explored(Result(Move,CurrentPosition))")) { - move(2); //move forward - } else if (kb.ask("Safe(Result(Move,Result(TurnLeft,CurrentPosition))&&!Explored(Result(Move,Result(TurnLeft,CurrentPosition))")) { - move(3); //turn left - move(2); //move forward - } else if (kb.ask("Safe(Result(Move,Result(TurnRight,CurrentPosition))&&!Explored(Result(Move,Result(TurnRight,CurrentPosition))")) { - move(4); //turn right - move(2); //move forward - } else if (kb.ask("EXIST(s), Safe(s) && !Explored(s)")) { - RHWTraversal("I am adjacent to a space that is unexplored and safe"); - move(2); - } else { - if (arrowCount > 0) { - if (kb.ask("EXIST(s), WUMPUS(s)")) { - RHWTraversal("Facing Wumpus"); - move(5); //shoot arrow - move(2); //move forward - } else { - //explore potentially unsafe space - //we should never reach this step since were only shooting at a Wumpus who's location we know? + private void decideNextAction() { + world.addMajorDecision(); + + if ((percepts & GLITTER) != 0) { + move(GRAB); + } + if (frontier.isEmpty()) { + System.out.println("Frontier is empty, quitting.."); + move(World.QUIT); + } + if (getForward().x >= 0 && getForward().x < World.size && getForward().y >= 0 && getForward().y < World.size) { + if (kb.ask(new Fact("Wumpus", getForward().x, false, getForward().y, false, true, null, null))) { + if (kb.ask(new Fact("Pit", getForward().x, false, getForward().y, false, true, null, null))) { + if (inFrontier(getForward())) { + removeFromFrontier(getForward()); + move(MOVE); + return; + } + + } + } + } + if (safeSpaceInFrontier()) { + if (!checkAdjacent(safeSpace.x, safeSpace.y, location.x, location.y)) { + goTo(safeSpace); + } + turnToSpace(safeSpace); + move(MOVE); + removeFromFrontier(safeSpace); + } else if (arrowCount > 0 && wumpusInFrontier()) { + if (!checkAdjacent(wumpusSpace.x, wumpusSpace.y, location.x, location.y)) { + goTo(wumpusSpace); + } + turnToSpace(wumpusSpace); + move(SHOOT); + move(MOVE); + removeFromFrontier(wumpusSpace); + } else if (!frontier.isEmpty()) { + Location goalLoc = frontier.get(frontier.size() - 1); + goTo(goalLoc); + turnToSpace(goalLoc); + move(MOVE); + removeFromFrontier(goalLoc); + if (kb.ask(new Fact("Wumpus", getForward().x, false, getForward().y, false, false, null, null))) { + frontier.add(getForward()); + } + } + } + + public void turnToSpace(Location loc) { + + if (loc.x < location.x) { + switch (direction) { + case WEST: + break; + case NORTH: + move(TURN_LEFT); + break; + case SOUTH: + move(TURN_RIGHT); + break; + case EAST: + move(TURN_RIGHT); + move(TURN_RIGHT); + break; + default: + break; + } + } else if (loc.x > location.x) { + switch (direction) { + case EAST: + return; + case SOUTH: + move(TURN_LEFT); + break; + case NORTH: + move(TURN_RIGHT); + break; + case WEST: + move(TURN_RIGHT); + move(TURN_RIGHT); + break; + default: + break; + } + } else if (loc.y < location.y) { + switch (direction) { + case SOUTH: + return; + case WEST: + move(TURN_LEFT); + break; + case EAST: + move(TURN_RIGHT); + break; + case NORTH: + move(TURN_RIGHT); + move(TURN_RIGHT); + break; + default: + break; + } + } else if (loc.y > location.y) { + switch (direction) { + case NORTH: + return; + case WEST: + move(TURN_RIGHT); + break; + case EAST: + move(TURN_LEFT); + break; + case SOUTH: + move(TURN_RIGHT); + move(TURN_RIGHT); + default: + break; + } + } + } + + private boolean wumpusInFrontier() { + for (Location loc : frontier) { + if (kb.ask(new Fact("Wumpus", loc.x, false, loc.y, false, false, null, null))) { + wumpusSpace = loc; + return true; + } + } + return false; + } + + private boolean safeSpaceInFrontier() { + for (int i = frontier.size() - 1; i >= 0; i--) { + Location loc = frontier.get(i); + if (kb.ask(new Fact("Wumpus", loc.x, false, loc.y, false, true, null, null))) { + if (kb.ask(new Fact("Pit", loc.x, false, loc.y, false, true, null, null))) { + //hopefully no obstacle is in frontier as it should be removed when found... + safeSpace = new Location(loc.x, loc.y); + return true; + } } + if (kb.ask(new Fact("Pit", loc.x, false, loc.y, false, false, null, null)) || kb.ask(new Fact("Obstacle", loc.x, false, loc.y, false, false, null, null))) { + //there is specifically a wumpus, pit, or obsticle at this position, don't navigate to it. + frontier.remove(i); + } } + return false; } - private void RHWTraversal(String stopCondition) { + private void goTo(Location target) { - do { - if (kb.ask("right is safe")) { - move(4); //turn right + if (!checkAdjacent(target.x, target.y, location.x, location.y)) { + System.out.println("Traversing to " + target.x + ", " + target.y); + ArrayList path = new ArrayList<>(); + path = searchForPath(location.x, location.y, target.x, target.y, path); + traversePath(path); + if (checkAdjacent(target.x, target.y, location.x, location.y)) { + System.out.println("Done traversing to " + target.x + ", " + target.y); } else { - if (kb.ask("forward is safe")) { - move(2); //go forward - } else { - move(3); //turn left - } + System.out.println("Failed to reach " + target.x + ", " + target.y); } - //if space to the right is safe --> turn right - //if space to right is unsafe && forward is safe --> go forward - //if space to right is unsafe && forward is unsafe --> turn left - } while (!kb.ask(stopCondition)); + } + } + + private ArrayList searchForPath(int curX, int curY, int goalX, int goalY, ArrayList path) { + + boolean[][] traversed = new boolean[World.size][World.size]; + searchNext(curX, curY, goalX, goalY, path, traversed); + return path; + } - //face stop condition - while (!kb.ask("am i facing the stop condition?")) { - move(3); //turn until facing stp condition + private boolean searchNext(int curX, int curY, int goalX, int goalY, ArrayList path, boolean[][] traversed) { + + Location curLoc = new Location(curX, curY); + if (!this.location.equals(curLoc)) { + path.add(curLoc); + } + traversed[curX][curY] = true; + boolean done = false; + if (checkAdjacent(curX, curY, goalX, goalY)) { + return true; + } + if (!done && isValid(curX, curY + 1) && !traversed[curX][curY + 1]) { //north is valid + done = searchNext(curX, curY + 1, goalX, goalY, path, traversed); + } + if (!done && isValid(curX + 1, curY) && !traversed[curX + 1][curY]) { //east is valid + done = searchNext(curX + 1, curY, goalX, goalY, path, traversed); + } + if (!done && isValid(curX, curY - 1) && !traversed[curX][curY - 1]) { //south is valid + done = searchNext(curX, curY - 1, goalX, goalY, path, traversed); + } + if (!done && isValid(curX - 1, curY) && !traversed[curX - 1][curY]) { //west is valid + done = searchNext(curX - 1, curY, goalX, goalY, path, traversed); + } + if (!done) { + if (path.size() > 0) { + path.remove(path.size() - 1); + } + } + return done; + } + + private void printPath(ArrayList path) { + if (!path.isEmpty()) { + + System.out.print("Path: "); + for (Location l : path) { + System.out.print("[" + l.x + ", " + l.y + "] "); + } + System.out.println(); + } else { + System.out.println("Path: [ ]"); + } + } + + private boolean isValid(int x, int y) { + + if (x >= 0 && y >= 0 && x < World.size && y < World.size) { + boolean wumpus = kb.ask(new Fact("Wumpus", x, false, y, false, true, null, null)); + boolean pit = kb.ask(new Fact("Pit", x, false, y, false, true, null, null)); + boolean bump = kb.ask(new Fact("Obstacle", x, false, y, false, true, null, null)); + return searchedPositions[x][y] && bump && pit && wumpus; + } else { + return false; + } + } + + private boolean checkAdjacent(int curX, int curY, int goalX, int goalY) { + + if (curX == goalX) { + if (Math.abs(curY - goalY) == 1) { + return true; + } + } else if (curY == goalY) { + if (Math.abs(curX - goalX) == 1) { + return true; + } + } + return false; + } + + private void traversePath(ArrayList path) { + + while (path.size() > 0) { + Location next = path.remove(0); + if (next.x > this.location.x) { + //go east + turnToSpace(next); + move(MOVE); + } else if (next.x < this.location.x) { + //go west + turnToSpace(next); + move(MOVE); + } else if (next.y > this.location.y) { + //go north + turnToSpace(next); + move(MOVE); + } else if (next.y < this.location.y) { + //go south + turnToSpace(next); + move(MOVE); + } else { + System.out.println("Error traversing path."); + } } - move(2); } } diff --git a/Wumpus/src/MinusFunction.java b/Wumpus/src/MinusFunction.java index c6e3469..766a28c 100644 --- a/Wumpus/src/MinusFunction.java +++ b/Wumpus/src/MinusFunction.java @@ -3,16 +3,17 @@ public class MinusFunction implements IFunction { @Override public int process(int value) { + return value - 1; } @Override public Variable processVariable(Variable variable) { + Variable processedVariable = new Variable(); processedVariable.function = variable.function; - processedVariable.isSkolemConstant = variable.isSkolemConstant; processedVariable.isVariable = variable.isVariable; - processedVariable.modifier = variable.modifier - 1;//This is the important one + processedVariable.modifier = variable.modifier - 1; processedVariable.value = variable.value; processedVariable.variableId = variable.variableId; return processedVariable; diff --git a/Wumpus/src/PlusFunction.java b/Wumpus/src/PlusFunction.java index 849f640..506c8df 100644 --- a/Wumpus/src/PlusFunction.java +++ b/Wumpus/src/PlusFunction.java @@ -3,16 +3,17 @@ public class PlusFunction implements IFunction { @Override public int process(int value) { + return value + 1; } @Override public Variable processVariable(Variable variable) { + Variable processedVariable = new Variable(); processedVariable.function = variable.function; - processedVariable.isSkolemConstant = variable.isSkolemConstant; processedVariable.isVariable = variable.isVariable; - processedVariable.modifier = variable.modifier + 1;//This is the important one + processedVariable.modifier = variable.modifier + 1; processedVariable.value = variable.value; processedVariable.variableId = variable.variableId; return processedVariable; diff --git a/Wumpus/src/Quantifier.java b/Wumpus/src/Quantifier.java index 668c436..49bb8fc 100644 --- a/Wumpus/src/Quantifier.java +++ b/Wumpus/src/Quantifier.java @@ -2,7 +2,8 @@ public class Quantifier { int variableId; - boolean isExistential; //if false, assumed universal... + char variableRep; + boolean isExistential; boolean not; public void printQuantifier() { diff --git a/Wumpus/src/ReactiveExplorer.java b/Wumpus/src/ReactiveExplorer.java index 94ef334..2322fa3 100644 --- a/Wumpus/src/ReactiveExplorer.java +++ b/Wumpus/src/ReactiveExplorer.java @@ -1,102 +1,191 @@ -import java.util.Random; - -public class ReactiveExplorer { - - private World world; - private int arrowCount; - private int[] previousSpace; - private boolean previousSafe; - private int percepts; - private int[] currentSpace; - private boolean currentSafe; - private int direction; - - private final byte BREEZE = 0b00000001; - private final byte STENTCH = 0b0000010; - private final byte BUMP = 0b00000100; - private final byte GLITTER = 0b00001000; - private final byte DEATH_BY_WUMPUS = 0b00010000; - private final byte DEATH_BY_PIT = 0b00100000; - private final byte SCREAM = 0b01000000; - - public ReactiveExplorer(World world) { - this.world = world; - this.arrowCount = world.arrowCount; - this.previousSpace = world.getLocation(); - this.currentSpace = previousSpace; - this.percepts = world.getPercepts(); - this.direction = world.direction; - if (((percepts & STENTCH) != STENTCH) && ((percepts & BREEZE) != BREEZE)) { - previousSafe = true; - currentSafe = true; - } - } +import java.util.ArrayList; - private void move(int action) { +public class ReactiveExplorer extends Agent { - if (action == 1) { - percepts = world.action(action); - if ((percepts & GLITTER) == GLITTER) { //found gold + private Location prevLocation; + private State safeMap[][]; + private static final int FORWARD = 0, LEFT = 1, BACK = 2, RIGHT = 3; - } else if ((percepts & DEATH_BY_PIT) == DEATH_BY_PIT || (percepts & DEATH_BY_WUMPUS) == DEATH_BY_WUMPUS) { - //got dicked - } - if ((percepts & BUMP) != BUMP) { - previousSafe = currentSafe; - previousSpace = currentSpace; - } - currentSpace = world.getLocation(); - currentSafe = ((percepts & STENTCH) != STENTCH) && ((percepts & BREEZE) != BREEZE); - } else { - world.action(action); + public ReactiveExplorer(World world, int arrows, int x, int y, int direction) { + + super(world, arrows, x, y, direction); + prevLocation = location; + percepts = world.getPercepts(); + safeMap = new State[World.size][World.size]; + safeMap[location.x][location.y] = State.SAFE; + run(); + } + + private void run() { + + int i = 0; + while (i < 50000) { + move(); + i++; } + world.action(QUIT); } - public void decideAction() { + private void move() { + + if ((percepts & GLITTER) == GLITTER) { + world.action(GRAB); + } + + System.out.println("Making new move"); - //select safe neighboring cell else select unsafe neighboring cell - if (((percepts & STENTCH) != 0) && ((percepts & BREEZE) != 0)) { //all adjacent spaces are safe - Random random = new Random(); - switch (random.nextInt(3)) { - case 1: //go forward - move(1); + if ((percepts & STENCH) != STENCH && (percepts & BREEZE) != BREEZE) { //all adjacent spaces are safe + updateSafe(); + //go in random direction + int rand = random.nextInt(3); + switch (rand) { + case FORWARD: //try to go forward + System.out.println("Safe space, action is : " + "Go forward"); + doAction(FORWARD, true); break; - case 2: //turn left and go forward - move(2); - move(1); + case LEFT: //try to go left + System.out.println("Safe space, action is : " + "Go left"); + doAction(LEFT, true); break; - case 3: //turn right and go forward - move(3); - move(1); + case 2: //try go right + + System.out.println("Safe space, action is : " + "Go right"); + doAction(RIGHT, true); break; - default: - System.out.println("Should not reach this line."); + default: //turn around + System.out.println("Safe space, action is : " + "Move backwards"); + turnRight(); + turnRight(); + percepts = world.action(MOVE); + processPercepts(); + break; + } + } else { //if forward is safe, go forward + + ArrayList safeMoves = new ArrayList(); + if (getSafe(FORWARD)) { + safeMoves.add(FORWARD); + } + if (getSafe(LEFT)) { + safeMoves.add(LEFT); + } + if (getSafe(RIGHT)) { + safeMoves.add(RIGHT); + } + if (safeMoves.size() > 0) { + int rand = random.nextInt(safeMoves.size()); + doAction(safeMoves.get(rand), true); + percepts = world.action(safeMoves.get(rand) + 1); + } else { + int rand = random.nextInt(3); + System.out.println("No safe action, action is : " + rand); + doAction(rand, true); } - } else { //neighboring cells may not be safe - //if previous cell was safe, return and pick new direction - if (previousSafe) { - move(3); - move(3); - move(1); - } else { //pick random move - Random random = new Random(); - switch (random.nextInt(3)) { - case 1: //go forward - move(1); + } + } + + public void doAction(int rand, boolean processPercepts){ + switch (rand) { + case FORWARD: + percepts = world.action(MOVE); break; - case 2: //turn left and go forward - move(2); - move(1); + case LEFT: + turnLeft(); + percepts = world.action(MOVE); break; - case 3: //turn right and go forward - move(3); - move(1); + case RIGHT: + turnRight(); + percepts = world.action(MOVE); + break; + case BACK: + turnRight(); + turnRight(); + percepts = world.action(MOVE); break; - default: - System.out.println("Should not reach this line."); } + if(processPercepts) + processPercepts(); + } + private void processPercepts() { + + if ((percepts & BUMP) == BUMP) { + Location forward = getForward(); + if (forward.x >= 0 && forward.x < World.size && forward.y >= 0 && forward.y < World.size) { + safeMap[forward.x][forward.y] = State.UNSAFE; } + } else if ((percepts & DEATH_WUMPUS) == DEATH_WUMPUS) { + killWumpus(); + } else if ((percepts & DEATH_PIT) == DEATH_PIT) { + Location forward = getForward(); + if (forward.x >= 0 && forward.x < World.size && forward.y >= 0 && forward.y < World.size) { + safeMap[forward.x][forward.y] = State.UNSAFE; + } + } else { + goForward(); + } + } + + private boolean getSafe(int direction) { + + try { + int trueDirection = (this.direction - direction + 4) % 4; + switch (trueDirection) { + case NORTH: + return (safeMap[location.x][location.y + 1] == State.SAFE); + case SOUTH: + return safeMap[location.x][location.y - 1] == State.SAFE; + case EAST: + return safeMap[location.x + 1][location.y] == State.SAFE; + case WEST: + return safeMap[location.x - 1][location.y] == State.SAFE; + } + System.out.println("Invalid direction mapping, getSafe(): " + direction + " " + trueDirection); + return true; + } catch (ArrayIndexOutOfBoundsException ex) { + return false; + } + } + + private void updateSafe() { + + try { + if (location.x < World.size - 1) { + safeMap[location.x + 1][location.y] = State.SAFE; + } + if (location.x > 0) { + safeMap[location.x - 1][location.y] = State.SAFE; + } + if (location.y < World.size - 1) { + safeMap[location.x][location.y + 1] = State.SAFE; + } + if (location.y > 0) { + safeMap[location.x][location.y - 1] = State.SAFE; + } + } catch (Exception e) { + System.out.println("Location: " + location.x + ", " + location.y); + throw e; + } + + } + + private void goForward() { + + Location forward = getForward(); + if (forward.x >= 0 && forward.x < World.size && forward.y >= 0 && forward.y < World.size) { + this.location = getForward(); + this.prevLocation = this.location; } + + } + + private void killWumpus() { + + System.out.println("Killing wumpus.."); + percepts = world.getPercepts(); + world.action(SHOOT); + world.action(MOVE); + this.prevLocation = this.location; + this.location = getForward(); } } diff --git a/Wumpus/src/Rule.java b/Wumpus/src/Rule.java index 813c4af..12201ea 100644 --- a/Wumpus/src/Rule.java +++ b/Wumpus/src/Rule.java @@ -3,31 +3,26 @@ public class Rule { - public static final int AND = 1; - public static final int OR = 2; - public static final int IMPLIES = 3; - public static final int IFF = 4; - - ArrayList quantifiers = new ArrayList<>(); - boolean negated; - //boolean leftRuleNot; - //boolean rightRuleNot; - Fact fact; - Rule leftRule; - Rule rightRule; - boolean justFact = false; - int connector; + private static final int AND = 1, OR = 2, IMPLIES = 3, IFF = 4; + + private final ArrayList quantifiers = new ArrayList<>(); + private Fact fact; + private Rule leftRule, rightRule; + private boolean justFact = false; + private int connector; public Rule() { - + } public Rule(Fact fact) { + this.fact = fact; justFact = true; } public void printRule() { + if (justFact) { fact.printFact(); return; @@ -56,4 +51,20 @@ public void printRule() { System.out.println(")"); } + + class Quantifier { + + private int variableId; + private boolean isExistential; + + public void printQuantifier() { + if (isExistential) { + System.out.print("EXIST("); + } else { + System.out.print("FORALL("); + } + System.out.print((char) (variableId + 97) + ") "); + } + } + } diff --git a/Wumpus/src/Sentence.java b/Wumpus/src/Sentence.java deleted file mode 100644 index 230f9b2..0000000 --- a/Wumpus/src/Sentence.java +++ /dev/null @@ -1,10 +0,0 @@ - -public interface Sentence { - - @Override - boolean equals(Object object); - - @Override - String toString(); - -} diff --git a/Wumpus/src/SkolemFunction.java b/Wumpus/src/SkolemFunction.java deleted file mode 100644 index ec64ab3..0000000 --- a/Wumpus/src/SkolemFunction.java +++ /dev/null @@ -1,20 +0,0 @@ - -public class SkolemFunction implements IFunction { - - Variable var; - - public SkolemFunction(Variable variable) { - var = variable; - } - - @Override - public int process(int value) { - return value;//idk yet - } - - @Override - public Variable processVariable(Variable variable) { - return var; - - } -} diff --git a/Wumpus/src/Space.java b/Wumpus/src/Space.java index a0c1402..a232e40 100644 --- a/Wumpus/src/Space.java +++ b/Wumpus/src/Space.java @@ -1,11 +1,7 @@ public class Space { - private boolean hasWumpus = false; - private boolean hasHole = false; - private boolean hasObstacle = false; - private boolean hasGold = false; - private boolean filled = false; + private boolean hasWumpus = false, hasHole = false, hasObstacle = false, hasGold = false, start, filled = false; public Space() { @@ -16,6 +12,7 @@ public boolean isHasWumpus() { } public void toggleWumpus() { + if (hasWumpus == true) { hasWumpus = false; filled = false; @@ -60,4 +57,11 @@ public void setFilled(boolean filled) { this.filled = filled; } + public boolean isStart() { + return start; + } + + public void setStart(boolean start) { + this.start = start; + } } diff --git a/Wumpus/src/Substitute.java b/Wumpus/src/Substitute.java new file mode 100644 index 0000000..65bdaca --- /dev/null +++ b/Wumpus/src/Substitute.java @@ -0,0 +1,6 @@ + +public class Substitute { + + int varIdToSubstitute; + int valToSubstituteWith; +} diff --git a/Wumpus/src/Tester.java b/Wumpus/src/Tester.java deleted file mode 100644 index 62a0e64..0000000 --- a/Wumpus/src/Tester.java +++ /dev/null @@ -1,43 +0,0 @@ - -public class Tester { - - public void testInferenceEngine() { - InferenceEngine engine = new InferenceEngine(); - - //FORALL x, y, Commutative(x,y) IFF Commutative(y,x) - Rule rule = new Rule(); - Quantifier x = new Quantifier(); - x.variableId = 0; - Quantifier y = new Quantifier(); - y.variableId = 1; - rule.quantifiers.add(x); - rule.quantifiers.add(y); - Rule leftRule = new Rule(); - Fact leftFact = new Fact(); - leftFact.predicate = "Commutative"; - Variable varX = new Variable(); - varX.isVariable = true; - varX.variableId = x.variableId; - Variable varY = new Variable(); - varY.isVariable = true; - varY.variableId = y.variableId; - leftFact.variables.add(varX); - leftFact.variables.add(varY); - leftRule.justFact = true; - leftRule.fact = leftFact; - Fact rightFact = new Fact(); - Rule rightRule = new Rule(); - rightFact.predicate = "Commutative"; - rightFact.variables.add(varY); - rightFact.variables.add(varX); - rightRule.justFact = true; - rightRule.fact = rightFact; - rule.leftRule = leftRule; - rule.rightRule = rightRule; - rule.connector = Rule.IFF; - - rule.printRule(); - //engine.convertToCNF(null); - - } -} diff --git a/Wumpus/src/Unifier.java b/Wumpus/src/Unifier.java index 81db585..f3086fd 100644 --- a/Wumpus/src/Unifier.java +++ b/Wumpus/src/Unifier.java @@ -1,55 +1,79 @@ -import java.util.HashMap; -import java.util.Map; +import java.util.ArrayList; public class Unifier { - public Unifier() { + public static ArrayList unify(Fact f1, Fact f2) { - } - - public static String Unify(String p, String q) { + ArrayList subs = new ArrayList<>(); + if (!f1.predicate.equals(f2.predicate)) { + return subs; + } - String substitution = ""; + Fact fact1 = new Fact(f1); + Fact fact2 = new Fact(f2); + //check if fact1 has a variable + for (int i = 0; i < fact1.variables.size(); i++) { + Variable tempVar = fact1.variables.get(i); + if (tempVar.isVariable) { + if (!fact2.variables.get(i).isVariable) { + Substitute sub = new Substitute(); + sub.varIdToSubstitute = tempVar.variableId; + sub.valToSubstituteWith = fact2.variables.get(i).value; + if (tempVar.function != null) { + int val = tempVar.function.process(0); + sub.valToSubstituteWith += -1 * val; + } + subs.add(sub); + } + } + } + for (int i = 0; i < fact2.variables.size(); i++) { + Variable tempVar = fact2.variables.get(i); + if (tempVar.isVariable) { + if (!fact1.variables.get(i).isVariable) { + Substitute sub = new Substitute(); + sub.varIdToSubstitute = tempVar.variableId; + sub.valToSubstituteWith = fact1.variables.get(i).value; + if (tempVar.function != null) { + int val = tempVar.function.process(0); + sub.valToSubstituteWith += -1 * val; + } + subs.add(sub); + } + } + } - return substitution; + if (equalWithSubs(fact1, fact2, subs)) { + return subs; + } else { + return new ArrayList<>(); + } } - public static Map unify(Sentence x, Sentence y) { - return (Map) unify(x, y, new HashMap<>()); - } + public static boolean equalWithSubs(Fact fact1, Fact fact2, ArrayList subs) { - private static Map unify(Sentence x, Sentence y, - Map theta) { - if (theta == null) { //return null in case of failure to find substitution - return null; - } else if (x.equals(y)) { //if sentences are equal theta contains the complete substitution - return theta; - } else if (x instanceof Variable) { - // else if VARIABLE?(x) then return UNIVY-VAR(x, y, theta) - return unifyVariables((Variable) x, (Variable) y, theta); - } else if (y instanceof Variable) { - // else if VARIABLE?(y) then return UNIFY-VAR(y, x, theta) - return unifyVariables((Variable) y, (Variable) x, theta); - // } else if () { //if x and y are compound - // return unify(x.getArguments(), y.getArguments(), unifyOperators(getOperator(x), y.getValue(), theta)); - } else { - return null; + substitute(subs, fact1); + substitute(subs, fact2); + for (int i = 0; i < fact1.variables.size(); i++) { + Variable var1 = fact1.variables.get(i); + Variable var2 = fact2.variables.get(i); + if (var1.value != var2.value) { + return false; + } } + return true; } - - private static Map unifyVariables(Variable x, Variable y, Map theta) { - - } - - private static Map unifyOperators(String x, String y, Map theta) { - - if (theta == null) { - return theta; - } else if (x.equals(y)) { - return theta; - } else { - return null; + + public static void substitute(ArrayList subs, Fact fact) { + + for (Substitute sub : subs) { + for (Variable var : fact.variables) { + if (var.isVariable && var.variableId == sub.varIdToSubstitute) { + var.isVariable = false; + var.value = sub.valToSubstituteWith; + } + } } } } diff --git a/Wumpus/src/Variable.java b/Wumpus/src/Variable.java index d085bc8..2bcf208 100644 --- a/Wumpus/src/Variable.java +++ b/Wumpus/src/Variable.java @@ -1,21 +1,39 @@ public class Variable { - boolean isVariable; - int value; - IFunction function; - int variableId; - int modifier; - boolean isSkolemConstant;//Skolem function would just be the function, a skolem constant will have this and it's varId making a unique identifier + protected boolean isVariable; + protected IFunction function; + protected int variableId, modifier, value = -1; + + public Variable(Variable var) { + + isVariable = var.isVariable; + value = var.value; + function = var.function; + variableId = var.variableId; + modifier = var.modifier; + } + + public Variable() { + } + + public Variable(int value, boolean isVariable, IFunction function) { + + this.isVariable = isVariable; + if (!isVariable) { + this.value = value; + } else { + this.variableId = value; + } + this.function = function; + } public void printVariable() { - + if (isVariable) { System.out.print((char) (variableId + 97)); + } else { + System.out.print(value); } } - - public int getValue() { - return value; - } } diff --git a/Wumpus/src/World.java b/Wumpus/src/World.java index d214fcd..776f729 100644 --- a/Wumpus/src/World.java +++ b/Wumpus/src/World.java @@ -1,124 +1,321 @@ -public class World { - - protected int arrowCount; - private int size; - private int x; - private int y; - protected int direction = 0; - private int[][] perceptMap; - - final int NORTH = 1; - final int EAST = 2; - final int SOUTH = 3; - final int WEST = 4; - private final int BREEZE = 1; - private final int STENTCH = 2; - private final int BUMP = 4; - private final int GLITTER = 8; - private final int DEATH_BY_WUMPUS = 16; - private final int DEATH_BY_PIT = 32; - private final int SCREAM = 64; - - final int GRAB = 1; - final int MOVE = 2; - final int TURN_LEFT = 3; - final int TURN_RIGHT = 4; - final int SHOOT = 5; - final int END = 6;//needed? +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; + +public final class World { + + public static final int NORTH = 0, EAST = 1, SOUTH = 2, WEST = 3; + public static final int GRAB = 1, MOVE = 2, TURN_LEFT = 3, TURN_RIGHT = 4, SHOOT = 5, QUIT = 6; + protected final byte BREEZE = 0b00000001, STENCH = 0b0000010, BUMP = 0b00000100, GLITTER = 0b00001000, DEATH_PIT = 0b00010000, DEATH_WUMPUS = 0b00100000, SCREAM = 0b01000000; + + private int majorDecisions = 0; + private int arrowCount, x, y, direction = 0, score = 0, numMoves = 0, pitDeaths = 0, wumpusDeaths = 0, killedWumpus = 0; + public static int size; + private byte[][] perceptMap; + private Agent explorer; public World(String fileName) { - x = 0; - y = 0; - //read in file + importMap(fileName); + for (byte[] cell : perceptMap) { + for (int j = 0; j < perceptMap.length; j++) { + if ((cell[j] & DEATH_WUMPUS) != 0) { + arrowCount++; + } + } + } + System.out.println("Starting world:"); + printWorld(); } - public int[] getLocation() { - int[] location = {x, y}; - return location; + public void startGame(String id) { + switch (id) { + case "LogicExplorer": + explorer = new LogicExplorer(this, arrowCount, x, y, direction); + break; + case "ReactiveExplorer": + explorer = new ReactiveExplorer(this, arrowCount, x, y, direction); + break; + } } - public int getPercepts() { + public void importMap(String fileName) { + + try { + FileReader in = new FileReader(fileName); + BufferedReader reader = new BufferedReader(in); + String next = reader.readLine(); + size = Integer.parseInt(next.substring(0, next.indexOf(" "))); + next = next.substring(next.indexOf(" ") + 1); + x = Integer.parseInt(next.substring(0, next.indexOf(" "))); + next = next.substring(next.indexOf(" ") + 1); + y = Integer.parseInt(next.substring(0, next.indexOf(" "))); + perceptMap = new byte[size][size]; + int i = 0; + while ((next = reader.readLine()) != null) { + int j = 0; + while (next.contains(" ") && !next.equals(" ")) { + perceptMap[i][j] = (byte) Integer.parseInt(next.substring(0, next.indexOf(" "))); + + next = next.substring(next.indexOf(" ") + 1, next.length()); + j++; + } + i++; + } + } catch (IOException | NumberFormatException e) { + System.out.println("Exception caught: " + e); + } + } + + public byte getPercepts() { return perceptMap[x][y]; } - public int action(int action) { + public void addMajorDecision() { + majorDecisions++; + } + + public void printStats() { + + System.out.println("Number of Actions: " + numMoves); + System.out.println("Number of major decisions: " + majorDecisions); + System.out.println("Final score: " + score); + System.out.println("Wumpus Deaths: " + wumpusDeaths); + System.out.println("Pit Deaths: " + pitDeaths); + System.out.println("Killed Wumpus's: " + killedWumpus); + System.out.println("Leftover arrows: " + arrowCount); + } + + public void printWorld() { + + for (int i = perceptMap.length - 1; i >= 0; i--) { + for (int j = 0; j < perceptMap.length; j++) { + if (x == j && y == i) { + System.out.print("\u001B[31m" + "A " + "\u001B[0m"); + } else { + if ((perceptMap[j][i] & DEATH_WUMPUS) == DEATH_WUMPUS) { + System.out.print("W "); + } else if ((perceptMap[j][i] & DEATH_PIT) == DEATH_PIT) { + System.out.print("P "); + } else if ((perceptMap[j][i] & GLITTER) == GLITTER) { + System.out.print("\u001B[32m" + "G " + "\u001B[0m"); + } else if ((perceptMap[j][i] & BUMP) == BUMP) { + System.out.print("B "); + } else if (perceptMap[j][i] > 9) { + System.out.print(perceptMap[j][i] + " "); + } else { + System.out.print(perceptMap[j][i] + " "); + } + } + } + System.out.println(""); + } + System.out.println(""); + } + + public void printAction(int action) { switch (action) { case GRAB: - if ((perceptMap[x][y] & GLITTER) != 0) - perceptMap[x][y] -= GLITTER; + System.out.println("GRAB"); break; case MOVE: - if (direction == NORTH) { - if (y + 1 < size) { - y = y + 1; - return perceptMap[x][y]; - } else return BUMP; - } else if (direction == EAST) { - if (x + 1 < size) { - x = x + 1; - return perceptMap[x][y]; - } else return BUMP; - } else if (direction == SOUTH) { - if (y - 1 > 0) { - y -= 1; - return perceptMap[x][y]; - } else return BUMP; - } else if (direction == WEST) { - if (x - 1 > 0) { - x -= 1; - return perceptMap[x][y]; - } else return BUMP; + System.out.println("MOVE"); + break; + case TURN_LEFT: + System.out.println("TURN_LEFT"); + break; + case TURN_RIGHT: + System.out.println("TURN_RIGHT"); + break; + case SHOOT: + System.out.println("SHOOT"); + break; + case QUIT: + System.out.println("QUIT"); + } + } + + public byte action(int action) { + + printWorld(); + System.out.print("World Action: "); + printAction(action); + System.out.println(""); + numMoves++; + switch (action) { + case GRAB: + score--; + if ((perceptMap[x][y] & GLITTER) != 0) { + perceptMap[x][y] -= GLITTER; + score += 1000; + System.out.println("Gold found!\n"); + printStats(); + System.exit(0); + } else { + System.out.println("Error: Gold not found"); + } + case MOVE: + score--; + switch (direction) { + case NORTH: + System.out.println("Moved north"); + if (y + 1 < size && (perceptMap[x][y + 1] & BUMP) == 0) { + if ((perceptMap[x][y + 1] & DEATH_WUMPUS) == DEATH_WUMPUS) { + score -= 1000; + wumpusDeaths++; + System.out.println("Death to wumpus: arrows remaining = " + arrowCount); + System.out.println("(World) thinks location after move: " + x + ", " + y); + return DEATH_WUMPUS; + } + if ((perceptMap[x][y + 1] & DEATH_PIT) == DEATH_PIT) { + score -= 1000; + pitDeaths++; + System.out.println("Death to pit."); + System.out.println("(World) thinks location after move: " + x + ", " + y); + return DEATH_PIT; + } + y = y + 1; + System.out.println("(World) thinks location after move: " + x + ", " + y); + return perceptMap[x][y]; + } else { + System.out.println("(World) thinks location after move: " + x + ", " + y); + return BUMP; + } + case EAST: + System.out.println("Moved east"); + if (x + 1 < size && (perceptMap[x + 1][y] & BUMP) == 0) { + if ((perceptMap[x + 1][y] & DEATH_WUMPUS) == DEATH_WUMPUS) { + score -= 1000; + wumpusDeaths++; + System.out.println("Death to wumpus: arrows remaining = " + arrowCount); + System.out.println("(World) thinks location after move: " + x + ", " + y); + return DEATH_WUMPUS; + } + if ((perceptMap[x + 1][y] & DEATH_PIT) == DEATH_PIT) { + score -= 1000; + pitDeaths++; + System.out.println("(World) thinks location after move: " + x + ", " + y); + return DEATH_PIT; + } + x = x + 1; + System.out.println("(World) thinks location after move: " + x + ", " + y); + return perceptMap[x][y]; + } else { + System.out.println("(World) thinks location after move: " + x + ", " + y); + return BUMP; + } + case SOUTH: + System.out.println("Moved south"); + if (y > 0 && (perceptMap[x][y - 1] & BUMP) == 0) { + if ((perceptMap[x][y - 1] & DEATH_WUMPUS) == DEATH_WUMPUS) { + score -= 1000; + wumpusDeaths++; + System.out.println("Death to wumpus: arrows remaining = " + arrowCount); + System.out.println("(World) thinks location after move: " + x + ", " + y); + return DEATH_WUMPUS; + } + if ((perceptMap[x][y - 1] & DEATH_PIT) == DEATH_PIT) { + score -= 1000; + pitDeaths++; + System.out.println("Death to pit."); + System.out.println("(World) thinks location after move: " + x + ", " + y); + return DEATH_PIT; + } + y -= 1; + System.out.println("(World) thinks location after move: " + x + ", " + y); + return perceptMap[x][y]; + } else { + System.out.println("(World) thinks location after move: " + x + ", " + y); + return BUMP; + } + case WEST: + System.out.println("Moved west"); + if (x > 0 && (perceptMap[x - 1][y] & BUMP) == 0) { + if ((perceptMap[x - 1][y] & DEATH_WUMPUS) == DEATH_WUMPUS) { + score -= 1000; + wumpusDeaths++; + System.out.println("Death to wumpus: arrows remaining = " + arrowCount); + System.out.println("(World) thinks location after move: " + x + ", " + y); + return DEATH_WUMPUS; + } + if ((perceptMap[x - 1][y] & DEATH_PIT) == DEATH_PIT) { + score -= 1000; + pitDeaths++; + System.out.println("Death to pit."); + System.out.println("(World) thinks location after move: " + x + ", " + y); + return DEATH_PIT; + } + x -= 1; + System.out.println("(World) thinks location after move: " + x + ", " + y); + return perceptMap[x][y]; + } else { + System.out.println("Bumped"); + System.out.println("(World) thinks location after move: " + x + ", " + y); + return BUMP; + } + default: + System.out.println("Defaulted"); + System.out.println(direction); + System.out.println("(World) thinks location after move: " + x + ", " + y); + break; } break; case TURN_LEFT: - direction = (direction + 3) % 4 + 1; + score--; + direction = (direction + 3) % 4; return perceptMap[x][y]; case TURN_RIGHT: - direction = (direction + 1) % 4 + 1; + score--; + direction = (direction + 1) % 4; return perceptMap[x][y]; case SHOOT: //shoot logic - if (arrowCount < 1) { - return -1; //out of arrows, which shouldn't be possible + if (arrowCount <= 0) { + System.out.println("Error: Out of arrows"); + return -1; } arrowCount--; + score -= 10; switch (direction) { - case 1: //shoot north - for (int i = y; i < perceptMap.length; i++) { - if (perceptMap[x][i] == 16) { //hits Wumpus + case NORTH: //shoot north + for (int i = y + 1; i < perceptMap.length; i++) { + if ((perceptMap[x][i] & DEATH_WUMPUS) != 0) { //hits Wumpus + removeWumpus(x, i); return SCREAM; - } else if (perceptMap[x][i] == 4) { //hits Obstacle + } else if ((perceptMap[x][i] & BUMP) != 0) { //hits Obstacle return perceptMap[x][y]; } } return perceptMap[x][y]; - case 2: //shoot east - for (int i = y; i < perceptMap.length; i++) { - if (perceptMap[i][y] == 16) { //hits Wumpus + case EAST: //shoot east + for (int i = x + 1; i < perceptMap.length; i++) { + if ((perceptMap[i][y] & DEATH_WUMPUS) != 0) { //hits Wumpus + removeWumpus(i, y); return SCREAM; - } else if (perceptMap[i][y] == 4) { //hits Obstacle + } else if ((perceptMap[i][y] & BUMP) != 0) { //hits Obstacle return perceptMap[x][y]; } } return perceptMap[x][y]; - case 3: //shoot south - for (int i = y; i > 0; i--) { - if (perceptMap[x][i] == 16) { //hits Wumpus + case SOUTH: //shoot south + for (int i = y - 1; i >= 0; i--) { + if ((perceptMap[x][i] & DEATH_WUMPUS) != 0) { //hits Wumpus + removeWumpus(x, i); return SCREAM; - } else if (perceptMap[x][i] == 4) { //hits Obstacle + } else if ((perceptMap[x][i] & BUMP) != 0) { //hits Obstacle return perceptMap[x][y]; } } return perceptMap[x][y]; - case 4: //shoot west - for (int i = y; i > 0; i--) { - if (perceptMap[i][y] == 16) { //hits Wumpus + case WEST: //shoot west + for (int i = x - 1; i >= 0; i--) { + if ((perceptMap[i][y] & DEATH_WUMPUS) != 0) { //hits Wumpus + removeWumpus(i, y); return SCREAM; - } else if (perceptMap[i][y] == 4) { //hits Obstacle + } else if ((perceptMap[i][y] & BUMP) != 0) { //hits Obstacle return perceptMap[x][y]; } } @@ -127,12 +324,67 @@ public int action(int action) { default: System.out.println("Error in shooting logic."); } + case QUIT: + System.out.println("Agent elected to end game."); + printStats(); + System.out.println("Gold was not found"); + System.exit(0); + break; + default: + System.out.println("Error with world movement, no case exists: " + action); + return -1; + } + return -1; + } + + private void removeWumpus(int x, int y) { + + System.out.println("Wumpus slain at " + x + ", " + y + "!!!"); - case END: - //needed? + //remove death_by_wumpus percepts from x, y + perceptMap[x][y] = (byte) (perceptMap[x][y] & ~DEATH_WUMPUS); + + //remove stench percepts form spaces adjacent to wumpus + if (x > 0) { + //remove stentch to left + perceptMap[x - 1][y] = (byte) (perceptMap[x - 1][y] & ~STENCH); } - System.out.println("Error shouldn't ever get here"); - return -1; + if (x < size - 1) { + //remove stentch to right + perceptMap[x + 1][y] = (byte) (perceptMap[x + 1][y] & ~STENCH); + } + if (y > 0) { + //remove stentch below + perceptMap[x][y - 1] = (byte) (perceptMap[x][y - 1] & ~STENCH); + } + if (y < size - 1) { + //remove stentch above + perceptMap[x][y + 1] = (byte) (perceptMap[x][y + 1] & ~STENCH); + } + remakeStenches(); + killedWumpus++; + score += 10; + } + + public void remakeStenches() { + for (int i = 0; i < perceptMap.length; i++) { + for (int j = 0; j < perceptMap.length; j++) { + if ((perceptMap[i][j] & DEATH_WUMPUS) != 0) { + if (i > 0) { + perceptMap[i - 1][j] |= STENCH; + } + if (i < size - 1) { + perceptMap[i + 1][j] |= STENCH; + } + if (j > 0) { + perceptMap[i][j - 1] |= STENCH; + } + if (j < size - 1) { + perceptMap[i][j + 1] |= STENCH; + } + } + } + } } -} \ No newline at end of file +} diff --git a/Wumpus/src/WumpusGame.java b/Wumpus/src/WumpusGame.java index 6bb463f..8a479b1 100644 --- a/Wumpus/src/WumpusGame.java +++ b/Wumpus/src/WumpusGame.java @@ -1,60 +1,126 @@ +import java.io.BufferedReader; import java.io.File; -import java.io.FileInputStream; +import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; import java.io.PrintStream; import java.io.PrintWriter; -import java.util.HashMap; import java.util.Random; public class WumpusGame { - private int boardSize; - private int wumpus; + private int boardSize, wumpus, startX, startY; private Space[][] board; - private Random random = new Random(); - private HashMap probabilityGeneration; + private final Random random = new Random(); private int[] prob; private byte[][] perceptBoard; - private final byte BREEZE = 0b00000001; - private final byte STENTCH = 0b0000010; - private final byte BUMP = 0b00000100; - private final byte GLITTER = 0b00001000; - private final byte DEATH_BY_WUMPUS = 0b00010000; - private final byte DEATH_BY_PIT = 0b00100000; - private final byte SCREAM = 0b01000000; - private PrintWriter out = new PrintWriter(new File("PerceptBoard.txt")); + private final byte BREEZE = 0b00000001, STENCH = 0b0000010, BUMP = 0b00000100, GLITTER = 0b00001000, DEATH = 0b00010000, DEATH_WUMPUS = 0b00100000, SCREAM = 0b01000000; + private final PrintWriter out = new PrintWriter(new File("PerceptBoard.txt")); + + public WumpusGame(String fileName) throws FileNotFoundException { + + newStart(fileName); + System.setOut(new PrintStream(new FileOutputStream("world.txt"))); + printBoards(); + System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out))); + } public WumpusGame(int boardSize, int[] prob) throws FileNotFoundException { + this.boardSize = boardSize; this.prob = prob; perceptBoard = new byte[boardSize][boardSize]; board = new Space[boardSize][boardSize]; setBoard(); initializeBoard(); - PrintStream out = new PrintStream(new FileOutputStream("world.txt")); - System.setOut(out); + PrintStream worldOut = new PrintStream(new FileOutputStream("world.txt")); + System.setOut(worldOut); + printBoards(); + System.setOut(new PrintStream(new FileOutputStream("clean.txt"))); printBoards(); + System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out))); + } - public void setBoard() { - for (int i = 0; i < board.length; i++) { - for (int j = 0; j < board[i].length; j++) { - board[i][j] = new Space(); + private void setBoard() { + + for (Space[] space : board) { + for (int j = 0; j < space.length; j++) { + space[j] = new Space(); + } + } + } + + private void newStart(String fileName) { + + try { + FileReader in = new FileReader(fileName); + BufferedReader reader = new BufferedReader(in); + String next = reader.readLine(); + int size = Integer.parseInt(next); + this.boardSize = size; + board = new Space[boardSize][boardSize]; + perceptBoard = new byte[boardSize][boardSize]; + setBoard(); + int i = 0; + while ((next = reader.readLine()) != null) {//((Integer) reader.read()).toString()).equals("-1")) { + int j = 0; + while (next.contains(" ") && !next.equals(" ")) { + String temp = next.substring(0, next.indexOf(" ")); + switch (temp) { + case "W": + placeWumpus(i, j); + break; + case "I": + placeObstacle(i, j); + break; + case "H": + placePit(i, j); + break; + default: + break; + } + + next = next.substring(next.indexOf(" ") + 1, next.length()); + j++; + } + i++; } + } catch (IOException | NumberFormatException ex) { + System.out.println("Error with world generation: " + ex); + } + + int x = random.nextInt(boardSize); + int y = random.nextInt(boardSize); + while (board[x][y].isFilled()) { + x = random.nextInt(boardSize); + y = random.nextInt(boardSize); } + placeGold(x, y); + + while (board[x][y].isFilled()) { + x = random.nextInt(boardSize); + y = random.nextInt(boardSize); + } + startX = x; + startY = y; + board[x][y].setStart(true); + } - public void initializeBoard() { - for (int i = 0; i < board.length; i++) { - for (int j = board[i].length - 1; j >= 0; j--) { + private void initializeBoard() { + + for (int i = board.length - 1; i >= 0; i--) { + for (int j = 0; j < board[i].length; j++) { chooseState(i, j); } } } - public void chooseState(int x, int y) { + private void chooseState(int x, int y) { if ((random.nextInt(100) + 1) <= prob[0]) { placePit(x, y); @@ -65,11 +131,13 @@ public void chooseState(int x, int y) { } } - public void placePercept(int x, int y, byte percept) { + private void placePercept(int x, int y, byte percept) { + perceptBoard[x][y] |= percept; } - public void placeAdjacentPercept(int x, int y, byte percept) { + private void placeAdjacentPercept(int x, int y, byte percept) { + if (x > 0) { placePercept(x - 1, y, percept); } @@ -84,40 +152,45 @@ public void placeAdjacentPercept(int x, int y, byte percept) { } } - public void placeObstacle(int x, int y) { + private void placeObstacle(int x, int y) { + board[x][y].setHasObstacle(true); placePercept(x, y, BUMP); } - public void placePit(int x, int y) { + private void placePit(int x, int y) { + board[x][y].setHasHole(true); placeAdjacentPercept(x, y, BREEZE); - placePercept(x, y, DEATH_BY_PIT); + placePercept(x, y, DEATH); } - public void placeGold(int x, int y) { + private void placeGold(int x, int y) { + board[x][y].setHasGold(true); placePercept(x, y, GLITTER); } - public void placeWumpus(int x, int y) { + private void placeWumpus(int x, int y) { + board[x][y].toggleWumpus(); - placeAdjacentPercept(x, y, STENTCH); - placePercept(x, y, DEATH_BY_WUMPUS); + placeAdjacentPercept(x, y, STENCH); + placePercept(x, y, DEATH_WUMPUS); wumpus++; } - public void checkBlockedStart() { - - } + private void printBoards() { - public void printBoards() { printBoard(); printPerceptBoard(); } - public void printPerceptBoard() { - out.println(boardSize); + private void printPerceptBoard() { + + out.print(boardSize + " "); + out.print(startX + " "); + out.print(startY + " "); + out.println(); for (int i = 0; i < boardSize; i++) { for (int j = 0; j < boardSize; j++) { out.print(perceptBoard[i][j] + " "); @@ -127,16 +200,21 @@ public void printPerceptBoard() { out.close(); } - public void printBoard() { + private void printBoard() { + System.out.println(boardSize); for (int i = 0; i < boardSize; i++) { - for (int j = 0; j < board[i].length; j++) { - if (board[i][j].isHasWumpus()) { + for (Space item : board[i]) { + if (item.isHasWumpus()) { System.out.print("W "); - } else if (board[i][j].isHasHole()) { + } else if (item.isHasHole()) { System.out.print("H "); - } else if (board[i][j].isHasObstacle()) { + } else if (item.isHasObstacle()) { System.out.print("I "); + } else if (item.isHasGold()) { + System.out.print("G "); + } else if (item.isStart()) { + System.out.print("S "); } else { System.out.print("0 "); } diff --git a/Wumpus/world.txt b/Wumpus/world.txt new file mode 100644 index 0000000..8adc1ea --- /dev/null +++ b/Wumpus/world.txt @@ -0,0 +1,21 @@ +20 +0 0 0 0 0 0 0 0 H I 0 0 0 0 0 0 I 0 0 H +0 0 0 0 0 0 0 0 0 0 W 0 0 0 H 0 0 0 0 W +0 0 0 0 I 0 H 0 0 0 H 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 W H 0 0 0 I 0 0 0 +0 0 0 0 0 0 0 0 0 I 0 0 0 0 H 0 0 0 0 0 +0 0 H 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 H W 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 H 0 0 0 0 0 0 0 W 0 0 0 0 0 0 W 0 0 H +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 H 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 W 0 0 +S 0 I 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 W +0 0 I 0 W 0 0 0 0 W 0 0 0 0 0 0 0 0 0 0 +I 0 0 0 0 0 0 0 I 0 0 0 I 0 0 0 0 I 0 0 +0 0 0 0 W 0 0 0 0 0 0 H 0 0 0 I 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 I 0 0 +0 W 0 I 0 W 0 H 0 0 0 0 0 0 0 0 0 0 0 0 +H 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 G 0 0 0 +0 0 0 0 0 I H 0 0 I 0 0 W 0 0 0 0 0 W I +0 0 0 0 0 I 0 0 I 0 0 0 0 0 0 H 0 0 0 0 diff --git a/world.txt b/world.txt deleted file mode 100644 index edd36fd..0000000 --- a/world.txt +++ /dev/null @@ -1,6 +0,0 @@ -5 -I 0 0 H 0 -0 0 0 0 0 -0 0 0 0 0 -0 I 0 0 0 -0 I 0 0 0