All Downloads are FREE. Search and download functionalities are using the official Maven repository.

aima.core.environment.wumpusworld.WumpusKnowledgeBase Maven / Gradle / Ivy

Go to download

AIMA-Java Core Algorithms from the book Artificial Intelligence a Modern Approach 3rd Ed.

The newest version!
package aima.core.environment.wumpusworld;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import aima.core.agent.Action;
import aima.core.environment.wumpusworld.action.Climb;
import aima.core.environment.wumpusworld.action.Forward;
import aima.core.environment.wumpusworld.action.Grab;
import aima.core.environment.wumpusworld.action.Shoot;
import aima.core.environment.wumpusworld.action.TurnLeft;
import aima.core.environment.wumpusworld.action.TurnRight;
import aima.core.logic.propositional.inference.DPLL;
import aima.core.logic.propositional.inference.OptimizedDPLL;
import aima.core.logic.propositional.kb.KnowledgeBase;
import aima.core.logic.propositional.parsing.ast.ComplexSentence;
import aima.core.logic.propositional.parsing.ast.Connective;
import aima.core.logic.propositional.parsing.ast.PropositionSymbol;
import aima.core.logic.propositional.parsing.ast.Sentence;

/**
 * A Knowledge base tailored to the Wumpus World environment.
 * 
 * @author Ciaran O'Reilly
 * @author Federico Baron
 * @author Alessandro Daniele
 */
public class WumpusKnowledgeBase extends KnowledgeBase {
	public static final String LOCATION          = "L";
	public static final String BREEZE            = "B";
	public static final String STENCH            = "S";
	public static final String PIT               = "P";
	public static final String WUMPUS            = "W";
	public static final String WUMPUS_ALIVE      = "WumpusAlive";
	public static final String HAVE_ARROW        = "HaveArrow";
	public static final String FACING_NORTH      = AgentPosition.Orientation.FACING_NORTH.toString();
	public static final String FACING_SOUTH      = AgentPosition.Orientation.FACING_SOUTH.toString();
	public static final String FACING_EAST       = AgentPosition.Orientation.FACING_EAST.toString();
	public static final String FACING_WEST       = AgentPosition.Orientation.FACING_WEST.toString();
	public static final String PERCEPT_STENCH    = "Stench";
	public static final String PERCEPT_BREEZE    = "Breeze";
	public static final String PERCEPT_GLITTER   = "Glitter";
	public static final String PERCEPT_BUMP      = "Bump";
	public static final String PERCEPT_SCREAM    = "Scream";
	public static final String ACTION_FORWARD    = Forward.FORWARD_ACTION_NAME;
	public static final String ACTION_SHOOT      = Shoot.SHOOT_ACTION_NAME;
	public static final String ACTION_TURN_LEFT  = TurnLeft.TURN_LEFT_ACTION_NAME;
	public static final String ACTION_TURN_RIGHT = TurnRight.TURN_RIGHT_ACTION_NAME;
	public static final String OK_TO_MOVE_INTO   = "OK";
	//
	private int  caveXDimension;
	private int  caveYDimension;
	private DPLL dpll;

	public WumpusKnowledgeBase(int caveXandYDimensions) {
		this(new OptimizedDPLL(), caveXandYDimensions);
	}
	/**
	 * Create a Knowledge Base that contains the atemporal "wumpus physics" and
	 * temporal rules with time zero.
	 * 
	 * @param dpll
	 *        the dpll implementation to use for answering 'ask' queries.
	 * @param caveXandYDimensions
	 *            x and y dimensions of the wumpus world's cave.
	 * 
	 */
	public WumpusKnowledgeBase(DPLL dpll, int caveXandYDimensions) {
		super();
		
		this.dpll = dpll;

		this.caveXDimension = caveXandYDimensions;
		this.caveYDimension = caveXandYDimensions;

		//
		// 7.7.1 - The current state of the World
		// The agent knows that the starting square contains no pit
		tell(new ComplexSentence(Connective.NOT, newSymbol(PIT, 1, 1)));
		// and no wumpus.
		tell(new ComplexSentence(Connective.NOT, newSymbol(WUMPUS, 1, 1)));

		// Atemporal rules about breeze and stench
		// For each square, the agent knows that the square is breezy
		// if and only if a neighboring square has a pit; and a square
		// is smelly if and only if a neighboring square has a wumpus.
		for (int y = 1; y <= caveYDimension; y++) {
			for (int x = 1; x <= caveXDimension; x++) {
				
				List pitsIn  = new ArrayList();
				List wumpsIn = new ArrayList();
				
				if (x > 1) { // West room exists
					pitsIn.add(newSymbol(PIT, x-1, y));
					wumpsIn.add(newSymbol(WUMPUS, x-1, y));
				}
				if (y < caveYDimension) { // North room exists
					pitsIn.add(newSymbol(PIT, x, y+1));
					wumpsIn.add(newSymbol(WUMPUS, x, y+1));
				}
				if (x < caveXDimension) { // East room exists
					pitsIn.add(newSymbol(PIT, x+1, y));
					wumpsIn.add(newSymbol(WUMPUS, x+1, y));
				}
				if (y > 1) { // South room exists
					pitsIn.add(newSymbol(PIT, x, y-1));
					wumpsIn.add(newSymbol(WUMPUS, x, y-1));
				}
			
				tell(new ComplexSentence(newSymbol(BREEZE, x, y), Connective.BICONDITIONAL, Sentence.newDisjunction(pitsIn)));
				tell(new ComplexSentence(newSymbol(STENCH, x, y), Connective.BICONDITIONAL, Sentence.newDisjunction(wumpsIn)));
			}
		}

		// The agent also knows there is exactly one wumpus. This is represented
		// in two parts. First, we have to say that there is at least one wumpus
		List wumpsAtLeast = new ArrayList();
		for (int x = 1; x <= caveXDimension; x++) {
			for (int y = 1; y <= caveYDimension; y++) {
				wumpsAtLeast.add(newSymbol(WUMPUS, x, y));
			}
		}
		tell(Sentence.newDisjunction(wumpsAtLeast));

		// Then, we have to say that there is at most one wumpus.
		// For each pair of locations, we add a sentence saying
		// that at least one of them must be wumpus-free.
		int numRooms = (caveXDimension*caveYDimension);
		for (int i = 0; i < numRooms; i++) {
			for (int j = i+1; j < numRooms; j++) {
				tell(new ComplexSentence(Connective.OR,
						new ComplexSentence(Connective.NOT, newSymbol(WUMPUS, (i / caveXDimension)+1, (i % caveYDimension)+1)),
						new ComplexSentence(Connective.NOT, newSymbol(WUMPUS, (j / caveXDimension)+1, (j % caveYDimension)+1))));
			}
		}
	}
	
	public AgentPosition askCurrentPosition(int t) {
		int locX = -1, locY = -1;
		for (int x = 1; x <= getCaveXDimension() && locX == -1; x++) {
			for (int y = 1; y <= getCaveYDimension() && locY == -1; y++) {
				if (ask(newSymbol(LOCATION, t, x, y))) {
					locX = x;
					locY = y;
				}
			}
		}
		if (locX == -1 || locY == -1) {
			throw new IllegalStateException("Inconsistent KB, unable to determine current room position.");
		}
		AgentPosition current = null;
		if (ask(newSymbol(FACING_NORTH, t))) {
			current = new AgentPosition(locX, locY, AgentPosition.Orientation.FACING_NORTH);
		}
		else if (ask(newSymbol(FACING_SOUTH, t))) {
			current = new AgentPosition(locX, locY, AgentPosition.Orientation.FACING_SOUTH);
		}
		else if (ask(newSymbol(FACING_EAST, t))) {
			current = new AgentPosition(locX, locY, AgentPosition.Orientation.FACING_EAST);
		}
		else if (ask(newSymbol(FACING_WEST, t))) {
			current = new AgentPosition(locX, locY, AgentPosition.Orientation.FACING_WEST);
		}
		else {
			throw new IllegalStateException("Inconsistent KB, unable to determine current room orientation.");
		}
		
		return current;
	}
	
	// safe <- {[x, y] : ASK(KB, OKtx,y) = true}
	public Set askSafeRooms(int t) {
		Set safe = new LinkedHashSet();
		for (int x = 1; x <= getCaveXDimension(); x++) {
			for (int y = 1; y <= getCaveYDimension(); y++) {
				if (ask(newSymbol(OK_TO_MOVE_INTO, t, x, y))) {
					safe.add(new Room(x, y));
				}
			}
		}
		return safe;
	}
	
	public boolean askGlitter(int t) {
		return ask(newSymbol(PERCEPT_GLITTER, t));
	}
	
	// unvisited <- {[x, y] : ASK(KB, Lt'x,y) = false for all t' ≤ t}
	public Set askUnvisitedRooms(int t) {
		Set unvisited = new LinkedHashSet();
		
		for (int x = 1; x <= getCaveXDimension(); x++) {
			for (int y = 1; y <= getCaveYDimension(); y++) {
				for (int tPrime = 0; tPrime <= t; tPrime++) {					
					if (ask(newSymbol(LOCATION, tPrime, x, y))) {
						break; // i.e. is not false for all t' <= t
					}
					if (tPrime == t) {
						unvisited.add(new Room(x, y)); // i.e. is false for all t' <= t
					}
				}
			}
		}
		
		return unvisited;
	}
	
	public boolean askHaveArrow(int t) {
		return ask(newSymbol(HAVE_ARROW, t));
	}
	
	// possible_wumpus <- {[x, y] : ASK(KB, ~Wx,y) = false}
	public Set askPossibleWumpusRooms(int t) {
		Set possible = new LinkedHashSet();
		
		for (int x = 1; x <= getCaveXDimension(); x++) {
			for (int y = 1; y <= getCaveYDimension(); y++) {
				if (!ask(new ComplexSentence(Connective.NOT, newSymbol(WUMPUS, x, y)))) {
					possible.add(new Room(x, y));
				}
			}
		}
		
		return possible;
	}
	
	// not_unsafe <- {[x, y] : ASK(KB, ~OKtx,y) = false}
	public Set askNotUnsafeRooms(int t) {
		Set notUnsafe = new LinkedHashSet();
		
		for (int x = 1; x <= getCaveXDimension(); x++) {
			for (int y = 1; y <= getCaveYDimension(); y++) {
				if (!ask(new ComplexSentence(Connective.NOT, newSymbol(OK_TO_MOVE_INTO, t, x, y)))) {
					notUnsafe.add(new Room(x, y));
				}
			}
		}
		
		return notUnsafe;
	}
	
	public boolean askOK(int t, int x, int y) {
		return ask(newSymbol(OK_TO_MOVE_INTO, t, x, y));
	}
	
	public boolean ask(Sentence query) {		
		return dpll.isEntailed(this, query);
	}

	public int getCaveXDimension() {
		return caveXDimension;
	}

	public void setCaveXDimension(int caveXDimension) {
		this.caveXDimension = caveXDimension;
	}

	public int getCaveYDimension() {
		return caveYDimension;
	}

	public void setCaveYDimension(int caveYDimension) {
		this.caveYDimension = caveYDimension;
	}

	/**
	 * Add to KB sentences that describe the action a
	 * 
	 * @param a
	 *            action that must be added to KB
	 * @param time
	 *            current time
	 */
	public void makeActionSentence(Action a, int t) {
		if (a instanceof Climb) {
			tell(newSymbol(Climb.CLIMB_ACTION_NAME, t));
		}
		else {
			tell(new ComplexSentence(Connective.NOT, newSymbol(Climb.CLIMB_ACTION_NAME, t)));
		}
		if (a instanceof Forward) {
			tell(newSymbol(Forward.FORWARD_ACTION_NAME, t));
		}
		else {
			tell(new ComplexSentence(Connective.NOT, newSymbol(Forward.FORWARD_ACTION_NAME, t)));
		}
		if (a instanceof Grab) {
			tell(newSymbol(Grab.GRAB_ACTION_NAME, t));
		}
		else {
			tell(new ComplexSentence(Connective.NOT, newSymbol(Grab.GRAB_ACTION_NAME, t)));
		}
		if (a instanceof Shoot) {
			tell(newSymbol(Shoot.SHOOT_ACTION_NAME, t));
		}
		else {
			tell(new ComplexSentence(Connective.NOT, newSymbol(Shoot.SHOOT_ACTION_NAME, t)));
		}
		if (a instanceof TurnLeft) {
			tell(newSymbol(TurnLeft.TURN_LEFT_ACTION_NAME, t));
		}
		else {
			tell(new ComplexSentence(Connective.NOT, newSymbol(TurnLeft.TURN_LEFT_ACTION_NAME, t)));
		}
		if (a instanceof TurnRight) {
			tell(newSymbol(TurnRight.TURN_RIGHT_ACTION_NAME, t));
		}
		else {
			tell(new ComplexSentence(Connective.NOT, newSymbol(TurnRight.TURN_RIGHT_ACTION_NAME, t)));
		}
	}

	/**
	 * Add to KB sentences that describe the perception p
	 * (only about the current time).
	 * 
	 * @param p
	 *            perception that must be added to KB
	 * @param time
	 *            current time
	 */
	public void makePerceptSentence(AgentPercept p, int time) {
		if (p.isStench()) {
			tell(newSymbol(PERCEPT_STENCH, time));
		} else {
			tell(new ComplexSentence(Connective.NOT, newSymbol(PERCEPT_STENCH, time)));
		}

		if (p.isBreeze()) {
			tell(newSymbol(PERCEPT_BREEZE, time));
		} else {
			tell(new ComplexSentence(Connective.NOT, newSymbol(PERCEPT_BREEZE, time)));
		}

		if (p.isGlitter()) {
			tell(newSymbol(PERCEPT_GLITTER, time));
		} else {
			tell(new ComplexSentence(Connective.NOT, newSymbol(PERCEPT_GLITTER, time)));
		}

		if (p.isBump()) {
			tell(newSymbol(PERCEPT_BUMP, time));
		} else {
			tell(new ComplexSentence(Connective.NOT, newSymbol(PERCEPT_BUMP, time)));
		}

		if (p.isScream()) {
			tell(newSymbol(PERCEPT_SCREAM, time));
		} else {
			tell(new ComplexSentence(Connective.NOT, newSymbol(PERCEPT_SCREAM, time)));
		}
	}

	/**
	 * TELL the KB the temporal "physics" sentences for time t
	 * 
	 * @param t
	 *            current time step.
	 */
	public void tellTemporalPhysicsSentences(int t) {		
		if (t == 0) {
			// temporal rules at time zero
			tell(newSymbol(LOCATION, 0, 1, 1));
			tell(newSymbol(FACING_EAST, 0));
			tell(newSymbol(HAVE_ARROW, 0));
			tell(newSymbol(WUMPUS_ALIVE, 0));
		}
		
		// We can connect stench and breeze percepts directly
		// to the properties of the squares where they are experienced
		// through the location fluent as follows. For any time step t
		// and any square [x,y], we assert
		for (int x = 1; x <= caveXDimension; x++) {
			for (int y = 1; y <= caveYDimension; y++) {
				tell(new ComplexSentence( 
						newSymbol(LOCATION, t, x, y), 
						Connective.IMPLICATION,
						new ComplexSentence(newSymbol(PERCEPT_BREEZE, t), Connective.BICONDITIONAL, newSymbol(BREEZE, x, y))));
				
				tell(new ComplexSentence( 
						newSymbol(LOCATION, t, x, y), 
						Connective.IMPLICATION,
						new ComplexSentence(newSymbol(PERCEPT_STENCH, t), Connective.BICONDITIONAL, newSymbol(STENCH, x, y))));
			}
		}
		
		//
		// Successor state axioms (dependent on location)	
		for (int x = 1; x <= caveXDimension; x++) {
			for (int y = 1; y <= caveYDimension; y++) {

				// Location
				List locDisjuncts = new ArrayList();
				locDisjuncts.add(new ComplexSentence(
										newSymbol(LOCATION, t, x, y),
										Connective.AND,
										new ComplexSentence(
												new ComplexSentence(Connective.NOT, newSymbol(ACTION_FORWARD, t)),
												Connective.OR,
												newSymbol(PERCEPT_BUMP, t+1))));
				if (x > 1) { // West room is possible
					locDisjuncts.add(new ComplexSentence(
											newSymbol(LOCATION, t, x-1, y),
											Connective.AND,
											new ComplexSentence(
													newSymbol(FACING_EAST, t),
													Connective.AND,
													newSymbol(ACTION_FORWARD, t))));
				}
				if (y < caveYDimension) { // North room is possible
					locDisjuncts.add(new ComplexSentence(
											newSymbol(LOCATION, t, x, y+1),
											Connective.AND,
											new ComplexSentence(
													newSymbol(FACING_SOUTH, t),
													Connective.AND,
													newSymbol(ACTION_FORWARD, t))));
				}
				if (x < caveXDimension) { // East room is possible	
					locDisjuncts.add(new ComplexSentence(
											newSymbol(LOCATION, t, x+1, y),
											Connective.AND,
											new ComplexSentence(
													newSymbol(FACING_WEST, t),
													Connective.AND,
													newSymbol(ACTION_FORWARD, t))));
				}
				if (y > 1) { // South room is possible
					locDisjuncts.add(new ComplexSentence(
											newSymbol(LOCATION, t, x, y-1),
											Connective.AND,
											new ComplexSentence(
													newSymbol(FACING_NORTH, t),
													Connective.AND,
													newSymbol(ACTION_FORWARD, t))));
				}
				
				tell(new ComplexSentence(
							newSymbol(LOCATION, t+1, x, y),
							Connective.BICONDITIONAL,
							Sentence.newDisjunction(locDisjuncts)));

				// The most important question for the agent is whether
				// a square is OK to move into, that is, the square contains
				// no pit nor live wumpus.
				tell(new ComplexSentence(
							newSymbol(OK_TO_MOVE_INTO, t, x, y),
							Connective.BICONDITIONAL,
							new ComplexSentence(
									new ComplexSentence(Connective.NOT, newSymbol(PIT, x, y)),
									Connective.AND,
									new ComplexSentence(Connective.NOT, 
											new ComplexSentence(
													newSymbol(WUMPUS, x, y),
													Connective.AND,
													newSymbol(WUMPUS_ALIVE, t))))));
			}
		} 
		
		//
		// Successor state axioms (independent of location)

		// Rules about current orientation
		// Facing North
		tell(new ComplexSentence(
					newSymbol(FACING_NORTH, t+1),
					Connective.BICONDITIONAL,
					Sentence.newDisjunction(
							new ComplexSentence(newSymbol(FACING_WEST, t), Connective.AND, newSymbol(ACTION_TURN_RIGHT, t)),
							new ComplexSentence(newSymbol(FACING_EAST, t), Connective.AND, newSymbol(ACTION_TURN_LEFT, t)),
							new ComplexSentence(newSymbol(FACING_NORTH, t),
									Connective.AND,
									new ComplexSentence(
												new ComplexSentence(Connective.NOT, newSymbol(ACTION_TURN_LEFT, t)),
												Connective.AND,
												new ComplexSentence(Connective.NOT, newSymbol(ACTION_TURN_RIGHT, t))))
							)));
		// Facing South
		tell(new ComplexSentence(
				newSymbol(FACING_SOUTH, t+1),
				Connective.BICONDITIONAL,
				Sentence.newDisjunction(
						new ComplexSentence(newSymbol(FACING_WEST, t), Connective.AND, newSymbol(ACTION_TURN_LEFT, t)),
						new ComplexSentence(newSymbol(FACING_EAST, t), Connective.AND, newSymbol(ACTION_TURN_RIGHT, t)),
						new ComplexSentence(newSymbol(FACING_SOUTH, t),
								Connective.AND,
								new ComplexSentence(
										new ComplexSentence(Connective.NOT, newSymbol(ACTION_TURN_LEFT, t)),
										Connective.AND,
										new ComplexSentence(Connective.NOT, newSymbol(ACTION_TURN_RIGHT, t))))
						)));		
		// Facing East
		tell(new ComplexSentence(
				newSymbol(FACING_EAST, t+1),
				Connective.BICONDITIONAL,
				Sentence.newDisjunction(
						new ComplexSentence(newSymbol(FACING_NORTH, t), Connective.AND, newSymbol(ACTION_TURN_RIGHT, t)),
						new ComplexSentence(newSymbol(FACING_SOUTH, t), Connective.AND, newSymbol(ACTION_TURN_LEFT, t)),
						new ComplexSentence(newSymbol(FACING_EAST, t),
								Connective.AND,
								new ComplexSentence(
										new ComplexSentence(Connective.NOT, newSymbol(ACTION_TURN_LEFT, t)),
										Connective.AND,
										new ComplexSentence(Connective.NOT, newSymbol(ACTION_TURN_RIGHT, t))))
						)));			
		// Facing West
		tell(new ComplexSentence(
				newSymbol(FACING_WEST, t+1),
				Connective.BICONDITIONAL,
				Sentence.newDisjunction(
						new ComplexSentence(newSymbol(FACING_NORTH, t), Connective.AND, newSymbol(ACTION_TURN_LEFT, t)),
						new ComplexSentence(newSymbol(FACING_SOUTH, t), Connective.AND, newSymbol(ACTION_TURN_RIGHT, t)),
						new ComplexSentence(newSymbol(FACING_WEST, t),
								Connective.AND,
								new ComplexSentence(
										new ComplexSentence(Connective.NOT, newSymbol(ACTION_TURN_LEFT, t)),
										Connective.AND,
										new ComplexSentence(Connective.NOT, newSymbol(ACTION_TURN_RIGHT, t))))
						)));
		
		// Rule about the arrow
		tell(new ComplexSentence(
					newSymbol(HAVE_ARROW, t+1),
					Connective.BICONDITIONAL,
					new ComplexSentence(
							newSymbol(HAVE_ARROW, t), 
							Connective.AND, 
							new ComplexSentence(Connective.NOT, newSymbol(ACTION_SHOOT, t)))));
		
		// Rule about wumpus (dead or alive)
		tell(new ComplexSentence(
				newSymbol(WUMPUS_ALIVE, t+1),
				Connective.BICONDITIONAL,
				new ComplexSentence(
						newSymbol(WUMPUS_ALIVE, t),
						Connective.AND,
						new ComplexSentence(Connective.NOT, newSymbol(PERCEPT_SCREAM, t+1)))));
	}
	
	@Override
	public String toString() {
		List sentences = getSentences(); 
		if (sentences.size() == 0) {
			return "";
		} else {
			boolean first = true;
			StringBuilder sb = new StringBuilder();
			for (Sentence s : sentences) {
				if (!first) {
					sb.append("\n");
				}
				sb.append(s.toString());
				first = false;
			}
			return sb.toString();
		}
	}

	public PropositionSymbol newSymbol(String prefix, int timeStep) {
		return new PropositionSymbol(prefix+"_"+timeStep);
	}
	
	public PropositionSymbol newSymbol(String prefix, int x, int y) {
		return new PropositionSymbol(prefix+"_"+x+"_"+y);
	}
	
	public PropositionSymbol newSymbol(String prefix, int timeStep, int x, int y) {
		return newSymbol(newSymbol(prefix, timeStep).toString(), x, y);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy