From bde00cfb2355643d08e013dcc63689734b935665 Mon Sep 17 00:00:00 2001 From: Kunwar Raj Singh Date: Wed, 21 Mar 2018 18:24:01 +0530 Subject: [PATCH 1/4] define plan_route, plan_shot and refactor code --- logic.py | 117 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 72 insertions(+), 45 deletions(-) diff --git a/logic.py b/logic.py index 96190a1ba..d444f2b7d 100644 --- a/logic.py +++ b/logic.py @@ -36,6 +36,7 @@ isnumber, issequence, Expr, expr, subexpressions ) import agents +from search import astar_search, PlanRoute import itertools import random @@ -763,7 +764,7 @@ def location(x, y, time = None): def implies(lhs, rhs): return Expr('==>', lhs, rhs) -def implies_and_implies(lhs, rhs): +def equiv(lhs, rhs): return Expr('<=>', lhs, rhs) # Helper Function @@ -811,8 +812,8 @@ def __init__(self,dimrow): pits_in.append(pit(x, y - 1)) wumpus_in.append(wumpus(x, y - 1)) - self.tell(implies_and_implies(breeze(x, y), new_disjunction(pits_in))) - self.tell(implies_and_implies(stench(x, y), new_disjunction(wumpus_in))) + self.tell(equiv(breeze(x, y), new_disjunction(pits_in))) + self.tell(equiv(stench(x, y), new_disjunction(wumpus_in))) ## Rule that describes existence of at least one Wumpus @@ -837,8 +838,8 @@ def __init__(self,dimrow): self.tell(location(1, 1, 0)) for i in range(1, dimrow+1): for j in range(1, dimrow + 1): - self.tell(implies(location(i, j, 0), implies_and_implies(percept_breeze(0), breeze(i, j)))) - self.tell(implies(location(i, j, 0), implies_and_implies(percept_stench(0), stench(i, j)))) + self.tell(implies(location(i, j, 0), equiv(percept_breeze(0), breeze(i, j)))) + self.tell(implies(location(i, j, 0), equiv(percept_stench(0), stench(i, j)))) if i != 1 or j != 1: self.tell(~location(i, j, 0)) @@ -903,13 +904,13 @@ def add_temporal_sentences(self, time): ## current location rules for i in range(1, self.dimrow+1): for j in range(1, self.dimrow+1): - self.tell(implies(location(i, j, time), implies_and_implies(percept_breeze(time), breeze(i, j)))) - self.tell(implies(location(i, j, time), implies_and_implies(percept_stench(time), stench(i, j)))) + self.tell(implies(location(i, j, time), equiv(percept_breeze(time), breeze(i, j)))) + self.tell(implies(location(i, j, time), equiv(percept_stench(time), stench(i, j)))) s = list() s.append( - implies_and_implies( + equiv( location(i, j, time), location(i, j, time) & ~move_forward(time) | percept_bump(time))) if i != 1: @@ -929,7 +930,7 @@ def add_temporal_sentences(self, time): ## add sentence about safety of location i,j self.tell( - implies_and_implies(ok_to_move(i, j, time), ~pit(i, j) & ~wumpus(i, j) & wumpus_alive(time)) + equiv(ok_to_move(i, j, time), ~pit(i, j) & ~wumpus(i, j) & wumpus_alive(time)) ) ## Rules about current orientation @@ -937,64 +938,71 @@ def add_temporal_sentences(self, time): a = facing_north(t) & turn_right(t) b = facing_south(t) & turn_left(t) c = facing_east(t) & ~turn_left(t) & ~turn_right(t) - s = implies_and_implies(facing_east(time), a | b | c) + s = equiv(facing_east(time), a | b | c) self.tell(s) a = facing_north(t) & turn_left(t) b = facing_south(t) & turn_right(t) c = facing_west(t) & ~turn_left(t) & ~turn_right(t) - s = implies_and_implies(facing_west(time), a | b | c) + s = equiv(facing_west(time), a | b | c) self.tell(s) a = facing_east(t) & turn_left(t) b = facing_west(t) & turn_right(t) c = facing_north(t) & ~turn_left(t) & ~turn_right(t) - s = implies_and_implies(facing_north(time), a | b | c) + s = equiv(facing_north(time), a | b | c) self.tell(s) a = facing_west(t) & turn_left(t) b = facing_east(t) & turn_right(t) c = facing_south(t) & ~turn_left(t) & ~turn_right(t) - s = implies_and_implies(facing_south(time), a | b | c) + s = equiv(facing_south(time), a | b | c) self.tell(s) ## Rules about last action - self.tell(implies_and_implies(move_forward(t), ~turn_right(t) & ~turn_left(t))) + self.tell(equiv(move_forward(t), ~turn_right(t) & ~turn_left(t))) ##Rule about the arrow - self.tell(implies_and_implies(have_arrow(time), have_arrow(t) & ~shoot(t))) + self.tell(equiv(have_arrow(time), have_arrow(t) & ~shoot(t))) ##Rule about Wumpus (dead or alive) - self.tell(implies_and_implies(wumpus_alive(time), wumpus_alive(t) & ~percept_scream(time))) + self.tell(equiv(wumpus_alive(time), wumpus_alive(t) & ~percept_scream(time))) def ask_if_true(self, query): return pl_resolution(self, query) - - + + # ______________________________________________________________________________ class WumpusPosition(): - def __init__(self, X, Y, orientation): - self.X = X - self.Y = Y + def __init__(self, x, y, orientation): + self.X = x + self.Y = y self.orientation = orientation def get_location(self): return self.X, self.Y + def set_location(self, x, y): + self.X = x + self.Y = y + def get_orientation(self): return self.orientation - def equals(self, wumpus_position): - if wumpus_position.get_location() == self.get_location() and \ - wumpus_position.get_orientation()==self.get_orientation(): + def set_orientation(self, orientation): + self.orientation = orientation + + def __eq__(self, other): + if other.get_location() == self.get_location() and \ + other.get_orientation()==self.get_orientation(): return True else: return False - + # ______________________________________________________________________________ @@ -1041,9 +1049,8 @@ def execute(self, percept): goals = list() goals.append([1, 1]) self.plan.append('Grab') - actions = plan_route(self.current_position,goals,safe_points) - for action in actions: - self.plan.append(action) + actions = self.plan_route(self.current_position,goals,safe_points) + self.plan.extend(actions) self.plan.append('Climb') if len(self.plan) == 0: @@ -1059,9 +1066,8 @@ def execute(self, percept): if u not in unvisited_and_safe and s == u: unvisited_and_safe.append(u) - temp = plan_route(self.current_position,unvisited_and_safe,safe_points) - for t in temp: - self.plan.append(t) + temp = self.plan_route(self.current_position,unvisited_and_safe,safe_points) + self.plan.extend(temp) if len(self.plan) == 0 and self.kb.ask_if_true(have_arrow(self.t)): possible_wumpus = list() @@ -1070,9 +1076,8 @@ def execute(self, percept): if not self.kb.ask_if_true(wumpus(i, j)): possible_wumpus.append([i, j]) - temp = plan_shot(self.current_position, possible_wumpus, safe_points) - for t in temp: - self.plan.append(t) + temp = self.plan_shot(self.current_position, possible_wumpus, safe_points) + self.plan.extend(temp) if len(self.plan) == 0: not_unsafe = list() @@ -1080,16 +1085,14 @@ def execute(self, percept): for j in range(1, self.dimrow+1): if not self.kb.ask_if_true(ok_to_move(i, j, self.t)): not_unsafe.append([i, j]) - temp = plan_route(self.current_position, not_unsafe, safe_points) - for t in temp: - self.plan.append(t) + temp = self.plan_route(self.current_position, not_unsafe, safe_points) + self.plan.extend(temp) if len(self.plan) == 0: start = list() start.append([1, 1]) - temp = plan_route(self.current_position, start, safe_points) - for t in temp: - self.plan.append(t) + temp = self.plan_route(self.current_position, start, safe_points) + self.plan.extend(temp) self.plan.append('Climb') action = self.plan[0] @@ -1100,12 +1103,36 @@ def execute(self, percept): return action -def plan_route(current, goals, allowed): - raise NotImplementedError + def plan_route(self, current, goals, allowed): + problem = PlanRoute(current,goals,allowed, self.dimrow) + return astar_search(problem) + - -def plan_shot(current, goals, allowed): - raise NotImplementedError + def plan_shot(self, current, goals, allowed): + shooting_positions = set() + + for loc in goals: + x = loc[0] + y = loc[1] + for i in range(1, self.dimrow+1): + if i < x: + shooting_positions.add(WumpusPosition(i, y, 'EAST')) + if i > x: + shooting_positions.add(WumpusPosition(i, y, 'WEST')) + if i < y: + shooting_positions.add(WumpusPosition(x, i, 'NORTH')) + if i > y: + shooting_positions.add(WumpusPosition(x, i, 'SOUTH')) + + # Can't have a shooting position from any of the rooms the Wumpus could reside + for loc in goals: + for orientation in ['EAST', 'WEST', 'NORTH', 'SOUTH']: + shooting_positions.remove(WumpusPosition(loc[0], loc[1], orientation)) + + actions = list() + actions.extend(self.plan_route(current, shooting_positions, allowed)) + actions.append('Shoot') + return actions # ______________________________________________________________________________ From 3ec7768b8aa734def2e7ba4de9a1b5e3496de5c9 Mon Sep 17 00:00:00 2001 From: Kunwar Raj Singh Date: Wed, 21 Mar 2018 18:27:49 +0530 Subject: [PATCH 2/4] minor changes --- logic.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/logic.py b/logic.py index d444f2b7d..a44e96366 100644 --- a/logic.py +++ b/logic.py @@ -1104,7 +1104,7 @@ def execute(self, percept): def plan_route(self, current, goals, allowed): - problem = PlanRoute(current,goals,allowed, self.dimrow) + problem = PlanRoute(current, goals, allowed, self.dimrow) return astar_search(problem) @@ -1125,8 +1125,9 @@ def plan_shot(self, current, goals, allowed): shooting_positions.add(WumpusPosition(x, i, 'SOUTH')) # Can't have a shooting position from any of the rooms the Wumpus could reside - for loc in goals: - for orientation in ['EAST', 'WEST', 'NORTH', 'SOUTH']: + orientations = ['EAST', 'WEST', 'NORTH', 'SOUTH'] + for loc in goals: + for orientation in orientations: shooting_positions.remove(WumpusPosition(loc[0], loc[1], orientation)) actions = list() From 13396f96fb5a79bc6a6e2633b1ffe94651cd5d78 Mon Sep 17 00:00:00 2001 From: Kunwar Raj Singh Date: Wed, 21 Mar 2018 18:35:42 +0530 Subject: [PATCH 3/4] Added PlanRoute Problem class --- search.py | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/search.py b/search.py index 7296429af..d82d9e709 100644 --- a/search.py +++ b/search.py @@ -481,6 +481,110 @@ def h(self, node): return sum(s != g for (s, g) in zip(node.state, self.goal)) +# ______________________________________________________________________________ + + +class PlanRoute(Problem): + """ The problem of moving the Hybrid Wumpus Agent from one place to other """ + + def __init__(self, initial, goal, allowed, dimrow): + """ Define goal state and initialize a problem """ + + self.dimrow = dimrow + self.goal = goal + self.allowed = allowed + Problem.__init__(self, initial, goal) + + def actions(self, state): + """ Return the actions that can be executed in the given state. + The result would be a list, since there are only three possible actions + in any given state of the environment """ + + possible_actions = ['Forward', 'TurnLeft', 'TurnRight'] + x, y = state.get_location() + orientation = state.get_orientation() + + # Prevent Bumps + if x == 1 and orientation == 'LEFT': + if 'Forward' in possible_actions: + possible_actions.remove('Forward') + if y == 1 and orientation == 'DOWN': + if 'Forward' in possible_actions: + possible_actions.remove('Forward') + if x == self.dimrow and orientation == 'RIGHT': + if 'Forward' in possible_actions: + possible_actions.remove('Forward') + if y == self.dimrow and orientation == 'UP': + if 'Forward' in possible_actions: + possible_actions.remove('Forward') + + return possible_actions + + def result(self, state, action): + """ Given state and action, return a new state that is the result of the action. + Action is assumed to be a valid action in the state """ + x, y = state.get_location() + proposed_loc = list() + + # Move Forward + if action == 'Forward': + if state.get_orientation() == 'UP': + proposed_loc = [x, y + 1] + elif state.get_orientation() == 'DOWN': + proposed_loc = [x, y - 1] + elif state.get_orientation() == 'LEFT': + proposed_loc = [x - 1, y] + elif state.get_orientation() == 'RIGHT': + proposed_loc = [x + 1, y] + else: + raise Exception('InvalidOrientation') + + # Rotate counter-clockwise + elif action == 'TurnLeft': + if state.get_orientation() == 'UP': + state.set_orientation('LEFT') + elif state.get_orientation() == 'DOWN': + state.set_orientation('RIGHT') + elif state.get_orientation() == 'LEFT': + state.set_orientation('DOWN') + elif state.get_orientation() == 'RIGHT': + state.set_orientation('UP') + else: + raise Exception('InvalidOrientation') + + # Rotate clockwise + elif action == 'TurnRight': + if state.get_orientation() == 'UP': + state.set_orientation('RIGHT') + elif state.get_orientation() == 'DOWN': + state.set_orientation('LEFT') + elif state.get_orientation() == 'LEFT': + state.set_orientation('UP') + elif state.get_orientation() == 'RIGHT': + state.set_orientation('DOWN') + else: + raise Exception('InvalidOrientation') + + if proposed_loc in self.allowed: + state.set_location(proposed_loc[0], [proposed_loc[1]]) + + return state + + def goal_test(self, state): + """ Given a state, return True if state is a goal state or False, otherwise """ + + return state.get_location() == tuple(self.goal) + + def h(self, node): + """ Return the heuristic value for a given state.""" + + # Manhattan Heuristic Function + x1, y1 = node.state.get_location() + x2, y2 = self.goal + + return abs(x2 - x1) + abs(y2 - y1) + + # ______________________________________________________________________________ # Other search algorithms From a07a6cdb52d682b612c7b0cfb08018e53cc1ab30 Mon Sep 17 00:00:00 2001 From: Kunwar Raj Singh Date: Wed, 21 Mar 2018 18:38:12 +0530 Subject: [PATCH 4/4] update plan_route return list of actions ( node.solution() ) instead of node itself in plan_route --- logic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logic.py b/logic.py index a44e96366..dfa70d0db 100644 --- a/logic.py +++ b/logic.py @@ -1105,7 +1105,7 @@ def execute(self, percept): def plan_route(self, current, goals, allowed): problem = PlanRoute(current, goals, allowed, self.dimrow) - return astar_search(problem) + return astar_search(problem).solution() def plan_shot(self, current, goals, allowed):