aima.core.environment.wumpusworld.HybridWumpusAgent Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aima-core Show documentation
Show all versions of aima-core Show documentation
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.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import aima.core.agent.Action;
import aima.core.agent.Percept;
import aima.core.agent.impl.AbstractAgent;
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.search.framework.SearchAgent;
import aima.core.search.framework.SearchForActions;
import aima.core.search.framework.evalfunc.HeuristicFunction;
import aima.core.search.framework.problem.GoalTest;
import aima.core.search.framework.problem.Problem;
import aima.core.search.framework.qsearch.GraphSearch;
import aima.core.search.informed.AStarSearch;
import aima.core.util.SetOps;
/**
* Artificial Intelligence A Modern Approach (3rd Edition): page 270.
*
*
*
*
* function HYBRID-WUMPUS-AGENT(percept) returns an action
* inputs: percept, a list, [stench, breeze, glitter, bump, scream]
* persistent: KB, a knowledge base, initially the atemporal "wumpus physics"
* t, a counter, initially 0, indicating time
* plan, an action sequence, initially empty
*
* TELL(KB, MAKE-PERCEPT-SENTENCE(percept, t))
* TELL the KB the temporal "physics" sentences for time t
* safe <- {[x, y] : ASK(KB, OKtx,y) = true}
* if ASK(KB, Glittert) = true then
* plan <- [Grab] + PLAN-ROUTE(current, {[1,1]}, safe) + [Climb]
* if plan is empty then
* unvisited <- {[x, y] : ASK(KB, Lt'x,y) = false for all t' ≤ t}
* plan <- PLAN-ROUTE(current, unvisited ∩ safe, safe)
* if plan is empty and ASK(KB, HaveArrowt) = true then
* possible_wumpus <- {[x, y] : ASK(KB, ~Wx,y) = false}
* plan <- PLAN-SHOT(current, possible_wumpus, safe)
* if plan is empty then //no choice but to take a risk
* not_unsafe <- {[x, y] : ASK(KB, ~OKtx,y) = false}
* plan <- PLAN-ROUTE(current, unvisited ∩ not_unsafe, safe)
* if plan is empty then
* plan <- PLAN-ROUTE(current, {[1,1]}, safe) + [Climb]
* action <- POP(plan)
* TELL(KB, MAKE-ACTION-SENTENCE(action, t))
* t <- t+1
* return action
*
* --------------------------------------------------------------------------------
*
* function PLAN-ROUTE(current, goals, allowed) returns an action sequence
* inputs: current, the agent's current position
* goals, a set of squares; try to plan a route to one of them
* allowed, a set of squares that can form part of the route
*
* problem <- ROUTE-PROBLEM(current, goals, allowed)
* return A*-GRAPH-SEARCH(problem)
*
*
*
* Figure 7.20 A hybrid agent program for the wumpus world. It uses a
* propositional knowledge base to infer the state of the world, and a
* combination of problem-solving search and domain-specific code to decide what
* actions to take
*
* @author Federico Baron
* @author Alessandro Daniele
* @author Ciaran O'Reilly
*/
public class HybridWumpusAgent extends AbstractAgent {
// persistent: KB, a knowledge base, initially the atemporal
// "wumpus physics"
private WumpusKnowledgeBase kb = null;
// t, a counter, initially 0, indicating time
private int t = 0;
// plan, an action sequence, initially empty
private Queue plan = new LinkedList(); // FIFOQueue
/**
* function HYBRID-WUMPUS-AGENT(percept) returns an action
*
* @param percept
* a list, [stench, breeze, glitter, bump, scream]
*
* @return an action the agent should take.
*/
@Override
public Action execute(Percept percept) {
// TELL(KB, MAKE-PERCEPT-SENTENCE(percept, t))
kb.makePerceptSentence((AgentPercept) percept, t);
// TELL the KB the temporal "physics" sentences for time t
kb.tellTemporalPhysicsSentences(t);
AgentPosition current = kb.askCurrentPosition(t);
// safe <- {[x, y] : ASK(KB, OKtx,y) = true}
Set safe = kb.askSafeRooms(t);
// if ASK(KB, Glittert) = true then
if (kb.askGlitter(t)) {
// plan <- [Grab] + PLAN-ROUTE(current, {[1,1]}, safe) + [Climb]
Set goals = new LinkedHashSet();
goals.add(new Room(1, 1));
plan.add(new Grab());
plan.addAll(planRoute(current, goals, safe));
plan.add(new Climb());
}
// if plan is empty then
// unvisited <- {[x, y] : ASK(KB, Lt'x,y) = false
// for all t' ≤ t}
Set unvisited = kb.askUnvisitedRooms(t);
if (plan.isEmpty()) {
// plan <- PLAN-ROUTE(current, unvisited ∩ safe, safe)
plan.addAll(planRoute(current, SetOps.intersection(unvisited, safe), safe));
}
// if plan is empty and ASK(KB, HaveArrowt) = true then
if (plan.isEmpty() && kb.askHaveArrow(t)) {
// possible_wumpus <- {[x, y] : ASK(KB, ~Wx,y) = false}
Set possibleWumpus = kb.askPossibleWumpusRooms(t);
// plan <- PLAN-SHOT(current, possible_wumpus, safe)
plan.addAll(planShot(current, possibleWumpus, safe));
}
// if plan is empty then //no choice but to take a risk
if (plan.isEmpty()) {
// not_unsafe <- {[x, y] : ASK(KB, ~OKtx,y) =
// false}
Set notUnsafe = kb.askNotUnsafeRooms(t);
// plan <- PLAN-ROUTE(current, unvisited ∩ not_unsafe, safe)
plan.addAll(planRoute(current, SetOps.intersection(unvisited, notUnsafe), safe));
}
// if plan is empty then
if (plan.isEmpty()) {
// plan PLAN-ROUTE(current, {[1,1]}, safe) + [Climb]
Set start = new LinkedHashSet();
start.add(new Room(1, 1));
plan.addAll(planRoute(current, start, safe));
plan.add(new Climb());
}
// action <- POP(plan)
Action action = plan.remove();
// TELL(KB, MAKE-ACTION-SENTENCE(action, t))
kb.makeActionSentence(action, t);
// t <- t+1
t = t + 1;
// return action
return action;
}
/**
* Returns a sequence of actions using A* Search.
*
* @param current
* the agent's current position
* @param goals
* a set of squares; try to plan a route to one of them
* @param allowed
* a set of squares that can form part of the route
*
* @return the best sequence of actions that the agent have to do to reach a
* goal from the current position.
*/
public List planRoute(AgentPosition current, Set goals, Set allowed) {
// Every square represent 4 possible positions for the agent, it could
// be in different orientations. For every square in allowed and goals
// sets we add 4 squares.
Set allowedPositions = new LinkedHashSet();
for (Room allowedRoom : allowed) {
int x = allowedRoom.getX();
int y = allowedRoom.getY();
allowedPositions.add(new AgentPosition(x, y, AgentPosition.Orientation.FACING_WEST));
allowedPositions.add(new AgentPosition(x, y, AgentPosition.Orientation.FACING_EAST));
allowedPositions.add(new AgentPosition(x, y, AgentPosition.Orientation.FACING_NORTH));
allowedPositions.add(new AgentPosition(x, y, AgentPosition.Orientation.FACING_SOUTH));
}
final Set goalPositions = new LinkedHashSet();
for (Room goalRoom : goals) {
int x = goalRoom.getX();
int y = goalRoom.getY();
goalPositions.add(new AgentPosition(x, y, AgentPosition.Orientation.FACING_WEST));
goalPositions.add(new AgentPosition(x, y, AgentPosition.Orientation.FACING_EAST));
goalPositions.add(new AgentPosition(x, y, AgentPosition.Orientation.FACING_NORTH));
goalPositions.add(new AgentPosition(x, y, AgentPosition.Orientation.FACING_SOUTH));
}
WumpusCave cave = new WumpusCave(kb.getCaveXDimension(), kb.getCaveYDimension(), allowedPositions);
GoalTest goalTest = new GoalTest() {
@Override
public boolean isGoalState(Object state) {
if (goalPositions.contains(state)) {
return true;
} else {
return false;
}
}
};
Problem problem = new Problem(current, WumpusFunctionFactory.getActionsFunction(cave),
WumpusFunctionFactory.getResultFunction(), goalTest);
HeuristicFunction hf = new ManhattanHeuristicFunction(goals);
SearchForActions search = new AStarSearch(new GraphSearch(), hf);
SearchAgent agent = null;
List actions = null;
try {
agent = new SearchAgent(problem, search);
actions = agent.getActions();
// Search agent can return a NoOp if already at goal,
// in the context of this agent we will just return
// no actions.
if (actions.size() == 1 && actions.get(0).isNoOp()) {
actions = new ArrayList();
}
} catch (Exception e) {
e.printStackTrace();
}
return actions;
}
/**
*
* @param current
* the agent's current position
* @param possibleWumpus
* a set of squares where we don't know that there isn't the
* wumpus.
* @param allowed
* a set of squares that can form part of the route
*
* @return the sequence of actions to reach the nearest square that is in
* line with a possible wumpus position. The last action is a shot.
*/
public List planShot(AgentPosition current, Set possibleWumpus, Set allowed) {
Set shootingPositions = new LinkedHashSet();
for (Room p : possibleWumpus) {
int x = p.getX();
int y = p.getY();
for (int i = 1; i <= kb.getCaveXDimension(); i++) {
if (i < x) {
shootingPositions.add(new AgentPosition(i, y, AgentPosition.Orientation.FACING_EAST));
}
if (i > x) {
shootingPositions.add(new AgentPosition(i, y, AgentPosition.Orientation.FACING_WEST));
}
if (i < y) {
shootingPositions.add(new AgentPosition(x, i, AgentPosition.Orientation.FACING_NORTH));
}
if (i > y) {
shootingPositions.add(new AgentPosition(x, i, AgentPosition.Orientation.FACING_SOUTH));
}
}
}
// Can't have a shooting position from any of the rooms the wumpus could
// reside
for (Room p : possibleWumpus) {
for (AgentPosition.Orientation orientation : AgentPosition.Orientation.values()) {
shootingPositions.remove(new AgentPosition(p.getX(), p.getY(), orientation));
}
}
Iterator it = shootingPositions.iterator();
Set shootingPositionsArray = new LinkedHashSet();
while (it.hasNext()) {
AgentPosition tmp = it.next();
shootingPositionsArray.add(new Room(tmp.getX(), tmp.getY()));
}
List actions = planRoute(current, shootingPositionsArray, allowed);
AgentPosition newPos = current;
if (actions.size() > 0) {
newPos = ((Forward) actions.get(actions.size() - 1)).getToPosition();
}
while (!shootingPositions.contains(newPos)) {
TurnLeft tLeft = new TurnLeft(newPos.getOrientation());
newPos = new AgentPosition(newPos.getX(), newPos.getY(), tLeft.getToOrientation());
actions.add(tLeft);
}
actions.add(new Shoot());
return actions;
}
//
// SUPPORTING CODE
//
public HybridWumpusAgent() {
// i.e. default is a 4x4 world as depicted in figure 7.2
this(4);
}
public HybridWumpusAgent(int caveXandYDimensions) {
kb = new WumpusKnowledgeBase(caveXandYDimensions);
}
}