001 package aima.search.online; 002 003 import java.util.ArrayList; 004 import java.util.Hashtable; 005 import java.util.List; 006 007 import aima.basic.Agent; 008 import aima.basic.Percept; 009 import aima.search.framework.Problem; 010 import aima.search.framework.Successor; 011 012 /** 013 * Artificial Intelligence A Modern Approach (2nd Edition): Figure 4.23, page 014 * 128.<br> 015 * <code> 016 * function LRTA*AGENT(s') returns an action 017 * inputs: s', a percept that identifies the current state 018 * static: result, a table, indexed by action and state, initially empty 019 * H, a table of cost estimates indexed by state, initially empty 020 * s, a, the previous state and action, initially null 021 * 022 * if GOAL-TEST(s') then return stop 023 * if s' is new state (not in H) then H[s'] <- h(s') 024 * unless s is null 025 * result[a, s] <- s' 026 * H[s] <- min LRTA*-COST(s, b, result[b, s], H) 027 * b (element of) ACTIONS(s) 028 * a <- an action b in ACTIONS(s') that minimizes LRTA*-COST(s', b, result[b, s'], H) 029 * s <- s' 030 * return a 031 * 032 * 033 * function LRTA*-COST(s, a, s', H) returns a cost estimate 034 * if s' is undefined then return h(s) 035 * else return c(s, a, s') + H[s'] 036 * </code> 037 * 038 * Figure 4.23 LRTA*-AGENT selects an action according to the value of 039 * neighboring states, which are updated as the agent moves about the state 040 * space.<br> 041 * Note: This algorithm fails to exit if the goal does not exist (e.g. A<->B Goal=X), 042 * this could be an issue with the implementation. Comments welcome. 043 */ 044 045 /** 046 * @author Ciaran O'Reilly 047 * 048 */ 049 public class LRTAStarAgent extends Agent { 050 051 private Problem problem; 052 // static: result, a table, indexed by action and state, initially empty 053 private final Hashtable<ActionState, Percept> result = new Hashtable<ActionState, Percept>(); 054 // H, a table of cost estimates indexed by state, initially empty 055 private final Hashtable<Percept, Double> H = new Hashtable<Percept, Double>(); 056 // s, a, the previous state and action, initially null 057 private Percept s = null; 058 private Object a = null; 059 060 public LRTAStarAgent(Problem problem) { 061 setProblem(problem); 062 } 063 064 public Problem getProblem() { 065 return problem; 066 } 067 068 public void setProblem(Problem problem) { 069 this.problem = problem; 070 init(); 071 } 072 073 // function LRTA*AGENT(s') returns an action 074 // inputs: s', a percept that identifies the current state 075 @Override 076 public String execute(Percept sComma) { 077 078 // if GOAL-TEST(s') then return stop 079 if (!goalTest(sComma)) { 080 // if s' is new state (not in H) then H[s'] <- h(s') 081 if (!H.containsKey(sComma)) { 082 H.put(sComma, getProblem().getHeuristicFunction() 083 .getHeuristicValue(sComma)); 084 } 085 // unless s is null 086 if (null != s) { 087 // result[a, s] <- s' 088 result.put(new ActionState(a, s), sComma); 089 090 // H[s] <- min LRTA*-COST(s, b, result[b, s], H) 091 // b (element of) ACTIONS(s) 092 double min = Double.MAX_VALUE; 093 for (Object b : actions(s)) { 094 double cost = lrtaCost(s, b, result.get(new ActionState(b, 095 s))); 096 if (cost < min) { 097 min = cost; 098 } 099 } 100 H.put(s, min); 101 } 102 // a <- an action b in ACTIONS(s') that minimizes LRTA*-COST(s', b, 103 // result[b, s'], H) 104 double min = Double.MAX_VALUE; 105 // Just in case no actions 106 a = Agent.NO_OP; 107 for (Object b : actions(sComma)) { 108 double cost = lrtaCost(sComma, b, result.get(new ActionState(b, 109 sComma))); 110 if (cost < min) { 111 min = cost; 112 a = b; 113 } 114 } 115 } else { 116 a = Agent.NO_OP; 117 } 118 119 // s <- s' 120 s = sComma; 121 122 if (Agent.NO_OP.equals(a)) { 123 // I'm either at the Goal or can't get to it, 124 // which in either case I'm finished so just die. 125 die(); 126 } 127 // return a 128 return a.toString(); 129 } 130 131 // 132 // PRIVATE METHODS 133 // 134 private void init() { 135 live(); 136 result.clear(); 137 H.clear(); 138 s = null; 139 a = null; 140 } 141 142 private boolean goalTest(Percept state) { 143 return getProblem().isGoalState(state); 144 } 145 146 // function LRTA*-COST(s, a, s', H) returns a cost estimate 147 private double lrtaCost(Percept s, Object action, Percept sComma) { 148 // if s' is undefined then return h(s) 149 if (null == sComma) { 150 return getProblem().getHeuristicFunction().getHeuristicValue(s); 151 } 152 // else return c(s, a, s') + H[s'] 153 return getProblem().getStepCostFunction().calculateStepCost(s, sComma, 154 action.toString()) 155 + H.get(sComma); 156 } 157 158 private List<Object> actions(Percept state) { 159 List<Object> actions = new ArrayList<Object>(); 160 161 List<Successor> successors = getProblem().getSuccessorFunction() 162 .getSuccessors(state); 163 164 for (Successor s : successors) { 165 actions.add(s.getAction()); 166 } 167 168 return actions; 169 } 170 }