Try to implement to A*
Co-authored-by: Sebastian Piotrowski <sebpio@st.amu.edu.pl> Co-authored-by: Marcin Matoga <marmat35@st.amu.edu.pl> Co-authored-by: Ladislaus3III <Ladislaus3III@users.noreply.github.com>
This commit is contained in:
parent
0e0a59b6ab
commit
520efb84ea
335
astar.py
Normal file
335
astar.py
Normal file
@ -0,0 +1,335 @@
|
|||||||
|
import heapq
|
||||||
|
from os import path
|
||||||
|
from settings import *
|
||||||
|
|
||||||
|
|
||||||
|
class Problem:
|
||||||
|
def __init__(self, initial, goal):
|
||||||
|
self.initial = initial
|
||||||
|
self.goal = goal
|
||||||
|
|
||||||
|
def actions(self, state):
|
||||||
|
moves = []
|
||||||
|
if self.turn_left(state):
|
||||||
|
moves.append('Left')
|
||||||
|
if self.turn_right(state):
|
||||||
|
moves.append('Right')
|
||||||
|
if self.move_forward(state):
|
||||||
|
moves.append('Forward')
|
||||||
|
|
||||||
|
# print(moves)
|
||||||
|
return moves
|
||||||
|
|
||||||
|
def turn_left(self, state):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def turn_right(self, state):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def move_forward(self, state):
|
||||||
|
|
||||||
|
a_row = 0
|
||||||
|
a_column = 0
|
||||||
|
|
||||||
|
for row in range(MAP_SIZE):
|
||||||
|
for column, pos in enumerate(state[row]):
|
||||||
|
if pos == ">":
|
||||||
|
a_row = row
|
||||||
|
a_column = column
|
||||||
|
|
||||||
|
if a_column == MAP_SIZE-1:
|
||||||
|
return False
|
||||||
|
elif state[a_row][a_column+1] == '.':
|
||||||
|
return True
|
||||||
|
elif state[a_row][a_column+1] == 'p':
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
if pos == "<":
|
||||||
|
a_row = row
|
||||||
|
a_column = column
|
||||||
|
|
||||||
|
if a_column == 0:
|
||||||
|
return False
|
||||||
|
elif state[a_row][a_column-1] == '.':
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
if pos == "v":
|
||||||
|
a_row = row
|
||||||
|
a_column = column
|
||||||
|
|
||||||
|
if a_row == MAP_SIZE-1:
|
||||||
|
return False
|
||||||
|
elif state[a_row+1][a_column] == '.':
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
if pos == "^":
|
||||||
|
a_row = row
|
||||||
|
a_column = column
|
||||||
|
|
||||||
|
if row == 0:
|
||||||
|
return False
|
||||||
|
elif state[a_row-1][a_column] == '.':
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def turn_me_or_move(self, state, do_it):
|
||||||
|
|
||||||
|
temp_map = [list(item) for item in state]
|
||||||
|
|
||||||
|
# print(temp_map)
|
||||||
|
|
||||||
|
#a_row = 0
|
||||||
|
#a_column = 0
|
||||||
|
|
||||||
|
for row in range(MAP_SIZE):
|
||||||
|
for column, pos in enumerate(temp_map[row]):
|
||||||
|
if pos == ">":
|
||||||
|
a_row = row
|
||||||
|
a_column = column
|
||||||
|
#print("a_row:" + str(a_row))
|
||||||
|
#print("a_column" + str(a_column))
|
||||||
|
|
||||||
|
if(do_it == 'Left'):
|
||||||
|
temp_map[a_row][a_column] = "^"
|
||||||
|
if(do_it == 'Right'):
|
||||||
|
temp_map[a_row][a_column] = 'v'
|
||||||
|
if(do_it == 'Forward'):
|
||||||
|
temp_map[a_row][a_column] = '.'
|
||||||
|
temp_map[a_row][a_column+1] = '>'
|
||||||
|
return temp_map
|
||||||
|
|
||||||
|
if pos == "<":
|
||||||
|
a_row = row
|
||||||
|
a_column = column
|
||||||
|
if(do_it == 'Left'):
|
||||||
|
temp_map[a_row][a_column] = 'v'
|
||||||
|
if(do_it == 'Right'):
|
||||||
|
temp_map[a_row][a_column] = '^'
|
||||||
|
if(do_it == 'Forward'):
|
||||||
|
temp_map[a_row][a_column] = '.'
|
||||||
|
temp_map[a_row][a_column-1] = '<'
|
||||||
|
return temp_map
|
||||||
|
if pos == "v":
|
||||||
|
a_row = row
|
||||||
|
a_column = column
|
||||||
|
if(do_it == 'Left'):
|
||||||
|
temp_map[a_row][a_column] = '>'
|
||||||
|
if(do_it == 'Right'):
|
||||||
|
temp_map[a_row][a_column] = '<'
|
||||||
|
if(do_it == 'Forward'):
|
||||||
|
temp_map[a_row][a_column] = '.'
|
||||||
|
temp_map[a_row+1][a_column] = 'v'
|
||||||
|
return temp_map
|
||||||
|
if pos == "^":
|
||||||
|
a_row = row
|
||||||
|
a_column = column
|
||||||
|
if(do_it == 'Left'):
|
||||||
|
temp_map[a_row][a_column] = '<'
|
||||||
|
if(do_it == 'Right'):
|
||||||
|
temp_map[a_row][a_column] = '>'
|
||||||
|
if(do_it == 'Forward'):
|
||||||
|
temp_map[a_row][a_column] = '.'
|
||||||
|
temp_map[a_row-1][a_column] = '^'
|
||||||
|
return temp_map
|
||||||
|
return temp_map
|
||||||
|
|
||||||
|
def result(self, state, action):
|
||||||
|
new_state = []
|
||||||
|
|
||||||
|
if action == 'Left':
|
||||||
|
new_state = self.turn_me_or_move(state, 'Left')
|
||||||
|
elif action == 'Right':
|
||||||
|
new_state = self.turn_me_or_move(state, 'Right')
|
||||||
|
elif action == 'Forward':
|
||||||
|
new_state = self.turn_me_or_move(state, 'Forward')
|
||||||
|
|
||||||
|
super_new_state = tuple(map(tuple, new_state))
|
||||||
|
|
||||||
|
return super_new_state
|
||||||
|
|
||||||
|
def goal_test(self, state):
|
||||||
|
if self.goal == state:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def path_cost(self, c, state1, action, state2, in_puddle1, in_puddle2):
|
||||||
|
|
||||||
|
|
||||||
|
return c+1
|
||||||
|
|
||||||
|
# funkcja heurystyki
|
||||||
|
def h(self, node):
|
||||||
|
node_row = node.row
|
||||||
|
node_column = node.column
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Node:
|
||||||
|
def __init__(self, state, parent=None, action=None, path_cost=0):
|
||||||
|
"""Create a search tree Node, derived from a parent by an action."""
|
||||||
|
self.state = state
|
||||||
|
self.parent = parent
|
||||||
|
self.action = action
|
||||||
|
self.path_cost = path_cost
|
||||||
|
|
||||||
|
self.in_puddle = False
|
||||||
|
|
||||||
|
#self.row = row
|
||||||
|
#self.column = column
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<Node {}>".format(self.state)
|
||||||
|
|
||||||
|
def expand(self, problem):
|
||||||
|
"""List the nodes reachable in one step from this node."""
|
||||||
|
return [self.child_node(problem, action)
|
||||||
|
for action in problem.actions(self.state)]
|
||||||
|
|
||||||
|
def child_node(self, problem, action):
|
||||||
|
next_state = problem.result(self.state, action)
|
||||||
|
next_node = Node(next_state, self, action, problem.path_cost(self.path_cost, self.state, action, next_state, in_puddle))
|
||||||
|
return next_node
|
||||||
|
|
||||||
|
def where_am_i(self):
|
||||||
|
temp_map = [list(item) for item in state]
|
||||||
|
|
||||||
|
for row in range(MAP_SIZE):
|
||||||
|
for column, pos in enumerate(temp_map[row]):
|
||||||
|
if pos == ">" or pos == "<" or pos == "^" or pos == "v":
|
||||||
|
self.row = row
|
||||||
|
self.column = column
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return isinstance(other, Node) and self.state == other.state
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
# We use the hash value of the state
|
||||||
|
# stored in the node instead of the node
|
||||||
|
# object itself to quickly search a node
|
||||||
|
# with the same state in a Hash Table
|
||||||
|
return hash(self.state)
|
||||||
|
|
||||||
|
|
||||||
|
class PriorityQueue:
|
||||||
|
"""A Queue in which the minimum (or maximum) element (as determined by f and
|
||||||
|
order) is returned first.
|
||||||
|
If order is 'min', the item with minimum f(x) is
|
||||||
|
returned first; if order is 'max', then it is the item with maximum f(x).
|
||||||
|
Also supports dict-like lookup."""
|
||||||
|
|
||||||
|
def __init__(self, order='min', f=lambda x: x):
|
||||||
|
self.heap = []
|
||||||
|
if order == 'min':
|
||||||
|
self.f = f
|
||||||
|
elif order == 'max': # now item with max f(x)
|
||||||
|
self.f = lambda x: -f(x) # will be popped first
|
||||||
|
else:
|
||||||
|
raise ValueError("Order must be either 'min' or 'max'.")
|
||||||
|
|
||||||
|
def append(self, item):
|
||||||
|
"""Insert item at its correct position."""
|
||||||
|
heapq.heappush(self.heap, (self.f(item), item))
|
||||||
|
|
||||||
|
def extend(self, items):
|
||||||
|
"""Insert each item in items at its correct position."""
|
||||||
|
for item in items:
|
||||||
|
self.append(item)
|
||||||
|
|
||||||
|
def pop(self):
|
||||||
|
"""Pop and return the item (with min or max f(x) value)
|
||||||
|
depending on the order."""
|
||||||
|
if self.heap:
|
||||||
|
return heapq.heappop(self.heap)[1]
|
||||||
|
else:
|
||||||
|
raise Exception('Trying to pop from empty PriorityQueue.')
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
"""Return current capacity of PriorityQueue."""
|
||||||
|
return len(self.heap)
|
||||||
|
|
||||||
|
def __contains__(self, key):
|
||||||
|
"""Return True if the key is in PriorityQueue."""
|
||||||
|
return any([item == key for _, item in self.heap])
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
"""Returns the first value associated with key in PriorityQueue.
|
||||||
|
Raises KeyError if key is not present."""
|
||||||
|
for value, item in self.heap:
|
||||||
|
if item == key:
|
||||||
|
return value
|
||||||
|
raise KeyError(str(key) + " is not in the priority queue")
|
||||||
|
|
||||||
|
def __delitem__(self, key):
|
||||||
|
"""Delete the first occurrence of key."""
|
||||||
|
try:
|
||||||
|
del self.heap[[item == key for _, item in self.heap].index(True)]
|
||||||
|
except ValueError:
|
||||||
|
raise KeyError(str(key) + " is not in the priority queue")
|
||||||
|
heapq.heapify(self.heap)
|
||||||
|
|
||||||
|
|
||||||
|
class Astar:
|
||||||
|
@staticmethod
|
||||||
|
def best_first_graph_search(problem, f, display=False):
|
||||||
|
"""Search the nodes with the lowest f scores first.
|
||||||
|
You specify the function f(node) that you want to minimize; for example,
|
||||||
|
if f is a heuristic estimate to the goal, then we have greedy best
|
||||||
|
first search; if f is node.depth then we have breadth-first search.
|
||||||
|
There is a subtlety: the line "f = memoize(f, 'f')" means that the f
|
||||||
|
values will be cached on the nodes as they are computed. So after doing
|
||||||
|
a best first search you can examine the f values of the path returned."""
|
||||||
|
#f = memoize(f, 'f')
|
||||||
|
node = Node(problem.initial)
|
||||||
|
# PriorityQueue ma przechowywac g+h
|
||||||
|
frontier = PriorityQueue('min', f)
|
||||||
|
frontier.append(node)
|
||||||
|
explored = set()
|
||||||
|
while frontier:
|
||||||
|
node = frontier.pop()
|
||||||
|
if problem.goal_test(node.state):
|
||||||
|
if display:
|
||||||
|
print(len(explored), "paths have been expanded and",
|
||||||
|
len(frontier), "paths remain in the frontier")
|
||||||
|
return node
|
||||||
|
explored.add(node.state)
|
||||||
|
for child in node.expand(problem):
|
||||||
|
if child.state not in explored and child not in frontier:
|
||||||
|
frontier.append(child)
|
||||||
|
elif child in frontier:
|
||||||
|
if f(child) < frontier[child]:
|
||||||
|
del frontier[child]
|
||||||
|
frontier.append(child)
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def loadMap(map_name=''):
|
||||||
|
maze = []
|
||||||
|
map_folder = path.dirname(__file__)
|
||||||
|
with open(path.join(map_folder, map_name), 'rt') as f:
|
||||||
|
for line in f:
|
||||||
|
maze.append(line.rstrip('\n'))
|
||||||
|
|
||||||
|
#print(maze)
|
||||||
|
return maze
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def run():
|
||||||
|
initial_map = tuple(map(tuple, Astar.loadMap('map.txt')))
|
||||||
|
goal_map = tuple(map(tuple, Astar.loadMap('goal_map.txt')))
|
||||||
|
problem = Problem(initial_map, goal_map)
|
||||||
|
|
||||||
|
#BFS.print_node_state(initial_map)
|
||||||
|
#BFS.print_node_state(goal_map)
|
||||||
|
|
||||||
|
result = Astar.breadth_first_graph_search(problem)
|
||||||
|
print(result)
|
||||||
|
return result
|
||||||
|
#print(BFS.print_node_state(result))
|
542
astar2.py
Normal file
542
astar2.py
Normal file
@ -0,0 +1,542 @@
|
|||||||
|
from os import path
|
||||||
|
import heapq
|
||||||
|
|
||||||
|
from settings import *
|
||||||
|
from sprites import Direction
|
||||||
|
|
||||||
|
|
||||||
|
class PlanRoute():
|
||||||
|
""" The problem of moving the Hybrid Wumpus Agent from one place to other """
|
||||||
|
|
||||||
|
def __init__(self, initial, goal, allowed, puddles=None, dimrow=None):
|
||||||
|
""" Define goal state and initialize a problem """
|
||||||
|
self.initial = initial
|
||||||
|
self.goal = goal
|
||||||
|
self.dimrow = dimrow
|
||||||
|
self.goal = goal
|
||||||
|
self.allowed = allowed
|
||||||
|
self.puddles = puddles
|
||||||
|
|
||||||
|
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', 'Left', 'Right']
|
||||||
|
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()
|
||||||
|
proposed_loc = []
|
||||||
|
|
||||||
|
# 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 == 'Left':
|
||||||
|
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 == 'Right':
|
||||||
|
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 tuple(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() == self.goal.get_location()
|
||||||
|
|
||||||
|
def path_cost(self, c, state1, action, state2):
|
||||||
|
"""Return the cost of a solution path that arrives at state2 from
|
||||||
|
state1 via action, assuming cost c to get up to state1. If the problem
|
||||||
|
is such that the path doesn't matter, this function will only look at
|
||||||
|
state2. If the path does matter, it will consider c and maybe state1
|
||||||
|
and action. The default method costs 1 for every step in the path."""
|
||||||
|
|
||||||
|
if action == "Forward" or action == "Left" or action == "Right":
|
||||||
|
|
||||||
|
|
||||||
|
x1, y1 = state1.get_location()
|
||||||
|
location1 = tuple([x1, y1])
|
||||||
|
x2, y2 = state2.get_location()
|
||||||
|
location2 = tuple([x1, y1])
|
||||||
|
|
||||||
|
|
||||||
|
if location2 in self.puddles:
|
||||||
|
return c + 1
|
||||||
|
if location1 == location2 and state1 in self.puddles:
|
||||||
|
return c + 1
|
||||||
|
return c
|
||||||
|
|
||||||
|
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.get_location()
|
||||||
|
|
||||||
|
return abs(x2 - x1) + abs(y2 - y1)
|
||||||
|
|
||||||
|
|
||||||
|
class Node:
|
||||||
|
def __init__(self, state, parent=None, action=None, path_cost=0):
|
||||||
|
"""Create a search tree Node, derived from a parent by an action."""
|
||||||
|
self.state = state #AgentPosition?
|
||||||
|
self.parent = parent
|
||||||
|
self.action = action
|
||||||
|
self.path_cost = path_cost
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<Node {}>".format(self.state)
|
||||||
|
|
||||||
|
def solution(self):
|
||||||
|
"""Return the sequence of actions to go from the root to this node."""
|
||||||
|
return [node.action for node in self.path()[1:]]
|
||||||
|
|
||||||
|
|
||||||
|
def expand(self, problem):
|
||||||
|
"""List the nodes reachable in one step from this node."""
|
||||||
|
return [self.child_node(problem, action)
|
||||||
|
for action in problem.actions(self.state)]
|
||||||
|
|
||||||
|
def child_node(self, problem, action):
|
||||||
|
|
||||||
|
next_state = problem.result(self.state, action)
|
||||||
|
next_node = Node(next_state, self, action, problem.path_cost(
|
||||||
|
self.path_cost, self.state, action, next_state))
|
||||||
|
print(problem.path_cost(
|
||||||
|
self.path_cost, self.state, action, next_state))
|
||||||
|
return next_node
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return isinstance(other, Node) and self.state == other.state
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
# We use the hash value of the state
|
||||||
|
# stored in the node instead of the node
|
||||||
|
# object itself to quickly search a node
|
||||||
|
# with the same state in a Hash Table
|
||||||
|
return hash(self.state)
|
||||||
|
|
||||||
|
def path(self):
|
||||||
|
"""Return a list of nodes forming the path from the root to this node."""
|
||||||
|
node, path_back = self, []
|
||||||
|
while node:
|
||||||
|
path_back.append(node)
|
||||||
|
node = node.parent
|
||||||
|
return list(reversed(path_back))
|
||||||
|
|
||||||
|
|
||||||
|
class AgentPosition:
|
||||||
|
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 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
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash((self.X, self.Y, self.orientation))
|
||||||
|
|
||||||
|
|
||||||
|
class SweeperAgent:
|
||||||
|
def __init__(self, dimensions=None):
|
||||||
|
self.dimrow = dimensions
|
||||||
|
self.current_position = None
|
||||||
|
self.orientation = ""
|
||||||
|
self.initial = set()
|
||||||
|
self.goal = set()
|
||||||
|
self.allowed_points = set()
|
||||||
|
self.puddle_points = set()
|
||||||
|
|
||||||
|
|
||||||
|
def where_am_i(self):
|
||||||
|
temp_map = [list(item) for item in SweeperAgent.loadMap("map.txt")]
|
||||||
|
|
||||||
|
for row in range(MAP_SIZE):
|
||||||
|
for column, pos in enumerate(temp_map[row]):
|
||||||
|
if pos == ">" or pos == "<" or pos == "^" or pos == "v":
|
||||||
|
self.row = row
|
||||||
|
self.column = column
|
||||||
|
|
||||||
|
return row, column
|
||||||
|
|
||||||
|
# add orientation
|
||||||
|
|
||||||
|
def where_to_go(self):
|
||||||
|
temp_map = [list(item) for item in SweeperAgent.loadMap("goal_map.txt")]
|
||||||
|
|
||||||
|
for row in range(MAP_SIZE):
|
||||||
|
for column, pos in enumerate(temp_map[row]):
|
||||||
|
if pos == ">" or pos == "<" or pos == "^" or pos == "v":
|
||||||
|
self.row = row
|
||||||
|
self.column = column
|
||||||
|
|
||||||
|
return row, column
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_allowed(allowed_points):
|
||||||
|
temp_map = [list(item) for item in SweeperAgent.loadMap('map.txt')]
|
||||||
|
|
||||||
|
a_row = 0
|
||||||
|
a_column = 0
|
||||||
|
|
||||||
|
for row in range(MAP_SIZE):
|
||||||
|
for column, pos in enumerate(temp_map[row]):
|
||||||
|
if pos == "." or pos == 'p' or pos == '>' or pos == '<' or pos == 'v' or pos == '^':
|
||||||
|
a_row = row
|
||||||
|
a_column = column
|
||||||
|
location = tuple([a_row, a_column])
|
||||||
|
allowed_points.add(location)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_puddles(puddle_points):
|
||||||
|
temp_map = [list(item) for item in SweeperAgent.loadMap('map.txt')]
|
||||||
|
|
||||||
|
a_row = 0
|
||||||
|
a_column = 0
|
||||||
|
|
||||||
|
for row in range(MAP_SIZE):
|
||||||
|
for column, pos in enumerate(temp_map[row]):
|
||||||
|
if pos == "p" :
|
||||||
|
a_row = row
|
||||||
|
a_column = column
|
||||||
|
location = tuple([a_row, a_column])
|
||||||
|
puddle_points.add(location)
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_goal():
|
||||||
|
temp_map = [list(item) for item in SweeperAgent.loadMap('goal_map.txt')]
|
||||||
|
|
||||||
|
a_row = 0
|
||||||
|
a_column = 0
|
||||||
|
|
||||||
|
for row in range(MAP_SIZE):
|
||||||
|
for column, pos in enumerate(temp_map[row]):
|
||||||
|
if pos == '>' or pos == '<' or pos == 'v' or pos == '^':
|
||||||
|
a_row = row
|
||||||
|
a_column = column
|
||||||
|
return a_row, a_column
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_initial(initial):
|
||||||
|
temp_map = [list(item) for item in SweeperAgent.loadMap('map.txt')]
|
||||||
|
|
||||||
|
a_row = 0
|
||||||
|
a_column = 0
|
||||||
|
|
||||||
|
for row in range(MAP_SIZE):
|
||||||
|
for column, pos in enumerate(temp_map[row]):
|
||||||
|
if pos == '>' or pos == '<' or pos == 'v' or pos == '^':
|
||||||
|
a_row = row
|
||||||
|
a_column = column
|
||||||
|
location = tuple([a_row, a_column])
|
||||||
|
initial.add(location)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_orientation():
|
||||||
|
temp_map = [list(item) for item in SweeperAgent.loadMap('map.txt')]
|
||||||
|
|
||||||
|
orientation = ""
|
||||||
|
|
||||||
|
for row in range(MAP_SIZE):
|
||||||
|
for column, pos in enumerate(temp_map[row]):
|
||||||
|
if pos == ">":
|
||||||
|
orientation = "RIGHT"
|
||||||
|
if pos == "<":
|
||||||
|
orientation = "LEFT"
|
||||||
|
if pos == "^":
|
||||||
|
orientation = "UP"
|
||||||
|
if pos == "v":
|
||||||
|
orientation = "DOWN"
|
||||||
|
|
||||||
|
return orientation
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_goal_orientation():
|
||||||
|
temp_map = [list(item) for item in SweeperAgent.loadMap('goal_map.txt')]
|
||||||
|
|
||||||
|
orientation = ""
|
||||||
|
|
||||||
|
for row in range(MAP_SIZE):
|
||||||
|
for column, pos in enumerate(temp_map[row]):
|
||||||
|
if pos == ">":
|
||||||
|
orientation = "RIGHT"
|
||||||
|
if pos == "<":
|
||||||
|
orientation = "LEFT"
|
||||||
|
if pos == "^":
|
||||||
|
orientation = "UP"
|
||||||
|
if pos == "v":
|
||||||
|
orientation = "DOWN"
|
||||||
|
|
||||||
|
return orientation
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def run(self):
|
||||||
|
self.orientation = SweeperAgent.set_orientation()
|
||||||
|
goal_orientation = SweeperAgent.set_goal_orientation()
|
||||||
|
#SweeperAgent.set_initial(self.initial)
|
||||||
|
#SweeperAgent.set_goal(self.goal)
|
||||||
|
SweeperAgent.set_allowed(self.allowed_points)
|
||||||
|
SweeperAgent.set_puddles(self.puddle_points)
|
||||||
|
|
||||||
|
x, y = self.where_am_i()
|
||||||
|
x1, y1 = SweeperAgent.get_goal()
|
||||||
|
|
||||||
|
agent_position = AgentPosition(x, y, self.orientation)
|
||||||
|
goal_position = AgentPosition(x1, y1, goal_orientation)
|
||||||
|
|
||||||
|
self.plan_route(agent_position, goal_position, self.allowed_points, self.puddle_points)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"""print("allowed: ")
|
||||||
|
print("(row, column)")
|
||||||
|
print(sorted(self.allowed_points))
|
||||||
|
print("puddles:")
|
||||||
|
print(sorted(self.puddle_points))
|
||||||
|
print("initial:")
|
||||||
|
print(self.initial)
|
||||||
|
print("goal:")
|
||||||
|
print(self.goal)
|
||||||
|
print("orientation:")
|
||||||
|
print(self.orientation)"""
|
||||||
|
|
||||||
|
|
||||||
|
def plan_route(self, current, goals, allowed, puddles):
|
||||||
|
problem = PlanRoute(current, goals, allowed, puddles)
|
||||||
|
return SweeperAgent.astar_search(problem, problem.h).solution()
|
||||||
|
#return SweeperAgent.astar_search(problem, problem.h)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"""TODO"""
|
||||||
|
# liczenie kosztów
|
||||||
|
#
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def loadMap(map_name=''):
|
||||||
|
maze = []
|
||||||
|
map_folder = path.dirname(__file__)
|
||||||
|
with open(path.join(map_folder, map_name), 'rt') as f:
|
||||||
|
for line in f:
|
||||||
|
maze.append(line.rstrip('\n'))
|
||||||
|
|
||||||
|
# print(maze)
|
||||||
|
return maze
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def astar_search(problem, h=None):
|
||||||
|
"""A* search is best-first graph search with f(n) = g(n)+h(n).
|
||||||
|
You need to specify the h function when you call astar_search, or
|
||||||
|
else in your Problem subclass."""
|
||||||
|
# h = memoize(h or problem.h, 'h')
|
||||||
|
return SweeperAgent.best_first_graph_search(problem, lambda n: n.path_cost + h(n))
|
||||||
|
#return best_first_graph_search(problem)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
#def best_first_graph_search(problem, f, display=False):
|
||||||
|
def best_first_graph_search(problem, f, display=True):
|
||||||
|
"""Search the nodes with the lowest f scores first.
|
||||||
|
You specify the function f(node) that you want to minimize; for example,
|
||||||
|
if f is a heuristic estimate to the goal, then we have greedy best
|
||||||
|
first search; if f is node.depth then we have breadth-first search.
|
||||||
|
There is a subtlety: the line "f = memoize(f, 'f')" means that the f
|
||||||
|
values will be cached on the nodes as they are computed. So after doing
|
||||||
|
a best first search you can examine the f values of the path returned."""
|
||||||
|
# f = memoize(f, 'f')
|
||||||
|
|
||||||
|
"""TODO"""
|
||||||
|
# Zaimplementować klasę Node dla Astar
|
||||||
|
|
||||||
|
history = []
|
||||||
|
|
||||||
|
node = Node(problem.initial)
|
||||||
|
frontier = PriorityQueue('min', f)
|
||||||
|
frontier.append(node)
|
||||||
|
explored = set()
|
||||||
|
while frontier:
|
||||||
|
node = frontier.pop()
|
||||||
|
if problem.goal_test(node.state):
|
||||||
|
if display:
|
||||||
|
print(len(explored), "paths have been expanded and", len(frontier), "paths remain in the frontier")
|
||||||
|
return node
|
||||||
|
explored.add(node.state)
|
||||||
|
for child in node.expand(problem):
|
||||||
|
if child.state not in explored and child not in frontier:
|
||||||
|
frontier.append(child)
|
||||||
|
elif child in frontier:
|
||||||
|
if f(child) < frontier[child]:
|
||||||
|
del frontier[child]
|
||||||
|
frontier.append(child)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class PriorityQueue:
|
||||||
|
"""A Queue in which the minimum (or maximum) element (as determined by f and
|
||||||
|
order) is returned first.
|
||||||
|
If order is 'min', the item with minimum f(x) is
|
||||||
|
returned first; if order is 'max', then it is the item with maximum f(x).
|
||||||
|
Also supports dict-like lookup."""
|
||||||
|
|
||||||
|
def __init__(self, order='min', f=lambda x: x):
|
||||||
|
self.heap = []
|
||||||
|
if order == 'min':
|
||||||
|
self.f = f
|
||||||
|
elif order == 'max': # now item with max f(x)
|
||||||
|
self.f = lambda x: -f(x) # will be popped first
|
||||||
|
else:
|
||||||
|
raise ValueError("Order must be either 'min' or 'max'.")
|
||||||
|
|
||||||
|
def append(self, item):
|
||||||
|
"""Insert item at its correct position."""
|
||||||
|
heapq.heappush(self.heap, (self.f(item), item))
|
||||||
|
|
||||||
|
def extend(self, items):
|
||||||
|
"""Insert each item in items at its correct position."""
|
||||||
|
for item in items:
|
||||||
|
self.append(item)
|
||||||
|
|
||||||
|
def pop(self):
|
||||||
|
"""Pop and return the item (with min or max f(x) value)
|
||||||
|
depending on the order."""
|
||||||
|
if self.heap:
|
||||||
|
return heapq.heappop(self.heap)[1]
|
||||||
|
else:
|
||||||
|
raise Exception('Trying to pop from empty PriorityQueue.')
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
"""Return current capacity of PriorityQueue."""
|
||||||
|
return len(self.heap)
|
||||||
|
|
||||||
|
def __contains__(self, key):
|
||||||
|
"""Return True if the key is in PriorityQueue."""
|
||||||
|
return any([item == key for _, item in self.heap])
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
"""Returns the first value associated with key in PriorityQueue.
|
||||||
|
Raises KeyError if key is not present."""
|
||||||
|
for value, item in self.heap:
|
||||||
|
if item == key:
|
||||||
|
return value
|
||||||
|
raise KeyError(str(key) + " is not in the priority queue")
|
||||||
|
|
||||||
|
def __delitem__(self, key):
|
||||||
|
"""Delete the first occurrence of key."""
|
||||||
|
try:
|
||||||
|
del self.heap[[item == key for _, item in self.heap].index(True)]
|
||||||
|
except ValueError:
|
||||||
|
raise KeyError(str(key) + " is not in the priority queue")
|
||||||
|
heapq.heapify(self.heap)
|
||||||
|
|
||||||
|
|
||||||
|
class Test:
|
||||||
|
@staticmethod
|
||||||
|
def run():
|
||||||
|
|
||||||
|
allowed_points = set()
|
||||||
|
puddle_points = set()
|
||||||
|
|
||||||
|
initial = set()
|
||||||
|
goal = set()
|
||||||
|
|
||||||
|
orientation = SweeperAgent.set_orientation()
|
||||||
|
|
||||||
|
SweeperAgent.set_initial(initial)
|
||||||
|
SweeperAgent.set_goal(goal)
|
||||||
|
|
||||||
|
SweeperAgent.set_allowed(allowed_points)
|
||||||
|
SweeperAgent.set_puddles(puddle_points)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
print("allowed: ")
|
||||||
|
print("(row, column)")
|
||||||
|
print(sorted(allowed_points))
|
||||||
|
print("puddles:")
|
||||||
|
print(sorted(puddle_points))
|
||||||
|
print("initial:")
|
||||||
|
print(initial)
|
||||||
|
print("goal:")
|
||||||
|
print(goal)
|
||||||
|
print("orientation:")
|
||||||
|
print(orientation)
|
98
astar2_document.txt
Normal file
98
astar2_document.txt
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
|
||||||
|
|
||||||
|
class PlanRoute
|
||||||
|
|
||||||
|
actions(state) przyjmuje mape? state
|
||||||
|
zwraca możliwe akcje agenta
|
||||||
|
|
||||||
|
result(state, action) mapa? state, action(string)
|
||||||
|
|
||||||
|
tworzy listę możliwych ruchów w liście proposed_loc
|
||||||
|
sprawdza, czy współrzędne w proposed_loc znajdują się w zbiorze allowed
|
||||||
|
|
||||||
|
ustawia kierunek agenta oraz nowe położenie na mapie
|
||||||
|
zwraca state
|
||||||
|
|
||||||
|
goal_test(state) przyjmuje state
|
||||||
|
sprawdza czy stan docelowy zgadza się ze stanem obecnym
|
||||||
|
zwraca wartość True lub False
|
||||||
|
|
||||||
|
path_cost(c, state1, action, state2)
|
||||||
|
ocenia nowy koszt po zmianie stanu
|
||||||
|
zwraca nowy koszt
|
||||||
|
|
||||||
|
def h(node) funkcja heurystyki, przyjmuje objekt klasy node
|
||||||
|
|
||||||
|
przypisuje zmiennym x i y wartość współrzędnych
|
||||||
|
za pomocą funkcji get_location()
|
||||||
|
|
||||||
|
mamy xy dla stanu obecnego
|
||||||
|
mamy xy dla stanu docelowego
|
||||||
|
|
||||||
|
zwraca ogległość
|
||||||
|
|
||||||
|
|
||||||
|
class Node
|
||||||
|
|
||||||
|
expand(problem) przyjmuje klasę problem
|
||||||
|
|
||||||
|
tworzy węzły potomne na podstawie możliwych akcji
|
||||||
|
|
||||||
|
zwraca listę węzłów potomnych
|
||||||
|
|
||||||
|
child_node(problem, action) przyjmuje problem oraz akcję
|
||||||
|
tworzy węzeł potomny na podstawie otzymanej akcji
|
||||||
|
|
||||||
|
zwraca węzeł potomny
|
||||||
|
|
||||||
|
|
||||||
|
class AgentPosition
|
||||||
|
|
||||||
|
get_location
|
||||||
|
zwraca dwie zmienne: X i Y
|
||||||
|
|
||||||
|
set_location
|
||||||
|
ustawia zmienne: X i Y
|
||||||
|
|
||||||
|
get_orientation:
|
||||||
|
zwraca kierunek
|
||||||
|
|
||||||
|
set_orientation:
|
||||||
|
ustawia kierunek
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class SweeperAgent - najbardziej tajemnicza klasa, bo do końca nie wiadomo co ma robić
|
||||||
|
|
||||||
|
where_am_i() - niby ma zwracać położenie agenta na planszy
|
||||||
|
|
||||||
|
set_allowed(allowed_points)
|
||||||
|
ustawia listę dostępnych miejsc na mapie
|
||||||
|
|
||||||
|
set_puddles(puddle_points)
|
||||||
|
ustawia listę obecnych kałuż
|
||||||
|
|
||||||
|
set_goal(goal)
|
||||||
|
ustawia punkt docelowy
|
||||||
|
|
||||||
|
set_initial(initial)
|
||||||
|
ustawia punkt początkowy
|
||||||
|
|
||||||
|
set_orientation()
|
||||||
|
ustawia kierunek
|
||||||
|
|
||||||
|
plan_route() - czarna magia A* (∩ ͝ ° ͜ʖ͡° )⊃━☆゚
|
||||||
|
|
||||||
|
load_map() - ładowanie mapy
|
||||||
|
|
||||||
|
astar_search() a*
|
||||||
|
|
||||||
|
|
||||||
|
class PriorityQueue - jakaś kolejka priorytetowa
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
12
goal_map.txt
12
goal_map.txt
@ -1,7 +1,7 @@
|
|||||||
.......
|
.......
|
||||||
###....
|
###.ppp
|
||||||
.......
|
....p..
|
||||||
.......
|
....p.p
|
||||||
...###.
|
...###p
|
||||||
...#^#.
|
...#^#p
|
||||||
.......
|
......p
|
BIN
images/puddle.bmp
Normal file
BIN
images/puddle.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
BIN
images/robot2.bmp
Normal file
BIN
images/robot2.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
13
main.py
13
main.py
@ -7,7 +7,7 @@ from grid import *
|
|||||||
from settings import *
|
from settings import *
|
||||||
from sprites import *
|
from sprites import *
|
||||||
from graphsearch import *
|
from graphsearch import *
|
||||||
|
from astar2 import *
|
||||||
|
|
||||||
|
|
||||||
class Game:
|
class Game:
|
||||||
@ -32,6 +32,7 @@ class Game:
|
|||||||
self.all_sprites = pg.sprite.Group()
|
self.all_sprites = pg.sprite.Group()
|
||||||
self.walls = pg.sprite.Group()
|
self.walls = pg.sprite.Group()
|
||||||
self.mines = pg.sprite.Group()
|
self.mines = pg.sprite.Group()
|
||||||
|
self.puddles = pg.sprite.Group()
|
||||||
for row, tiles in enumerate(self.map_data):
|
for row, tiles in enumerate(self.map_data):
|
||||||
for col, tile in enumerate(tiles):
|
for col, tile in enumerate(tiles):
|
||||||
if tile == '2':
|
if tile == '2':
|
||||||
@ -42,6 +43,8 @@ class Game:
|
|||||||
Grenade(self, col, row)
|
Grenade(self, col, row)
|
||||||
if tile == "#":
|
if tile == "#":
|
||||||
Wall(self, col, row)
|
Wall(self, col, row)
|
||||||
|
if tile == 'p':
|
||||||
|
Puddle(self, col, row)
|
||||||
if tile == '>':
|
if tile == '>':
|
||||||
self.player = Player(self, col, row, Direction.Right.name)
|
self.player = Player(self, col, row, Direction.Right.name)
|
||||||
if tile == '^':
|
if tile == '^':
|
||||||
@ -49,7 +52,7 @@ class Game:
|
|||||||
if tile == '<':
|
if tile == '<':
|
||||||
self.player = Player(self, col, row, Direction.Left.name)
|
self.player = Player(self, col, row, Direction.Left.name)
|
||||||
if tile == 'v':
|
if tile == 'v':
|
||||||
self.player = Player(self, col, row. Direction.Down.name)
|
self.player = Player(self, col, row, Direction.Down.name)
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
@ -105,6 +108,12 @@ class Game:
|
|||||||
player_moves = BFS.run()
|
player_moves = BFS.run()
|
||||||
self.graph_move(player_moves)
|
self.graph_move(player_moves)
|
||||||
self.wentyl_bezpieczenstwa = 1
|
self.wentyl_bezpieczenstwa = 1
|
||||||
|
if event.key == pg.K_F4 and self.wentyl_bezpieczenstwa == 0:
|
||||||
|
print("test1")
|
||||||
|
agent = SweeperAgent()
|
||||||
|
SweeperAgent.run(agent)
|
||||||
|
# Test.run()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def graph_move(self, moves):
|
def graph_move(self, moves):
|
||||||
|
12
map.txt
12
map.txt
@ -1,7 +1,7 @@
|
|||||||
.>.....
|
.>.....
|
||||||
###....
|
###.ppp
|
||||||
.......
|
....p..
|
||||||
.......
|
....p.p
|
||||||
...###.
|
...###p
|
||||||
...#.#.
|
...#.#p
|
||||||
.......
|
......p
|
19
sprites.py
19
sprites.py
@ -11,8 +11,8 @@ class Player(pg.sprite.Sprite):
|
|||||||
pg.sprite.Sprite.__init__(self, self.groups)
|
pg.sprite.Sprite.__init__(self, self.groups)
|
||||||
self.game = game
|
self.game = game
|
||||||
#self.image = pg.Surface((TILESIZE, TILESIZE))
|
#self.image = pg.Surface((TILESIZE, TILESIZE))
|
||||||
self.image = pg.image.load('images/robot.bmp')
|
self.image = pg.image.load('images/robot2.bmp')
|
||||||
self.baseImage = pg.image.load('images/robot.bmp')
|
self.baseImage = pg.image.load('images/robot2.bmp')
|
||||||
#self.image.fill(YELLOW)
|
#self.image.fill(YELLOW)
|
||||||
self.image = pg.transform.scale(self.image, (TILESIZE, TILESIZE))
|
self.image = pg.transform.scale(self.image, (TILESIZE, TILESIZE))
|
||||||
self.baseImage = pg.transform.scale(self.image, (TILESIZE, TILESIZE))
|
self.baseImage = pg.transform.scale(self.image, (TILESIZE, TILESIZE))
|
||||||
@ -272,4 +272,19 @@ class Wall(pg.sprite.Sprite):
|
|||||||
self.rect.x = self.x * TILESIZE
|
self.rect.x = self.x * TILESIZE
|
||||||
self.rect.y = self.y * TILESIZE
|
self.rect.y = self.y * TILESIZE
|
||||||
|
|
||||||
|
class Puddle(pg.sprite.Sprite):
|
||||||
|
def __init__(self, game, x, y):
|
||||||
|
self.groups = game.all_sprites, game.puddles
|
||||||
|
pg.sprite.Sprite.__init__(self, self.groups)
|
||||||
|
self.game = game
|
||||||
|
self.image = pg.image.load('images/puddle.bmp')
|
||||||
|
self.image = pg.transform.scale(self.image, (TILESIZE, TILESIZE))
|
||||||
|
self.rect = self.image.get_rect()
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
self.rect.x = self.x * TILESIZE
|
||||||
|
self.rect.y = self.y * TILESIZE
|
||||||
|
|
||||||
|
|
270
todo.txt
Normal file
270
todo.txt
Normal file
@ -0,0 +1,270 @@
|
|||||||
|
todo:
|
||||||
|
|
||||||
|
________________
|
||||||
|
|'-.--._ _________:
|
||||||
|
| / | __ __\
|
||||||
|
| | _ | [\_\= [\_\
|
||||||
|
| |.' '. \.........|
|
||||||
|
| ( <) ||: :|_
|
||||||
|
\ '._.' | :.....: |_(o
|
||||||
|
'-\_ \ .------./
|
||||||
|
_ \ ||.---.|| _
|
||||||
|
/ \ '-._|/\n~~\n' | \
|
||||||
|
(| []=.--[===[()]===[) |
|
||||||
|
<\_/ \_______/ _.' /_/
|
||||||
|
/// (_/_/
|
||||||
|
|\\ [\\
|
||||||
|
||:| | I|
|
||||||
|
|::| | I|
|
||||||
|
||:| | I|
|
||||||
|
||:| : \:
|
||||||
|
|\:| \I|
|
||||||
|
:/\: ([])
|
||||||
|
([]) [|
|
||||||
|
|| |\_
|
||||||
|
_/_\_ [ -'-.__
|
||||||
|
<] \> \_____.>
|
||||||
|
\__/
|
||||||
|
|
||||||
|
|
||||||
|
__...------------._
|
||||||
|
,-' `-.
|
||||||
|
,-' `.
|
||||||
|
,' ,-`.
|
||||||
|
; `-' `.
|
||||||
|
; .-. \
|
||||||
|
; .-. `-' \
|
||||||
|
; `-' \
|
||||||
|
; `.
|
||||||
|
; :
|
||||||
|
; |
|
||||||
|
; ;
|
||||||
|
; ___ ;
|
||||||
|
; ,-;-','.`.__ |
|
||||||
|
_..; ,-' ;`,'.`,'.--`. |
|
||||||
|
///; ,-' `. ,-' ;` ;`,','_.--=: /
|
||||||
|
|'': ,' : ;` ;,;,,-'_.-._`. ,' IT'S A TRAP!!!
|
||||||
|
' : ;_.-. `. :' ;;;'.ee. \| /
|
||||||
|
\.' _..-'/8o. `. : :! ' ':8888) || /
|
||||||
|
||`-'' \\88o\ : : :! : :`""' ;;/
|
||||||
|
|| \"88o\; `. \ `. `. ;,'
|
||||||
|
/) ___ `."'/(--.._ `. `.`. `-..-' ;--.
|
||||||
|
\(.="""""==.. `'-' `.| `-`-..__.-' `. `.
|
||||||
|
| `"==.__ ) ) ;
|
||||||
|
| || `"=== ' .' .'
|
||||||
|
/\,,|||| | | \ .' .'
|
||||||
|
| |||'|' |'|' \| .' _.' \
|
||||||
|
| |\' | | || || .' .' \
|
||||||
|
' | \ ' |' . ``-- `| || .' .' \
|
||||||
|
' | ' | . ``-.._ | ; .' .' `.
|
||||||
|
_.--,;`. . -- ...._,' .' .' `.__
|
||||||
|
,' ,'; `. . --..__..--'.' .' __/_\
|
||||||
|
,' ; ; | . --..__.._.' .' ,' `.
|
||||||
|
/ ; : ; . -.. _.' _.' / `
|
||||||
|
/ : `-._ | . _.--' _.' |
|
||||||
|
/ `. `--....--'' _.' |
|
||||||
|
`._ _..-' |
|
||||||
|
`-..____...-'' |
|
||||||
|
|
|
||||||
|
|
|
||||||
|
|
||||||
|
.-.
|
||||||
|
|_:_|
|
||||||
|
/(_Y_)\
|
||||||
|
. ( \/M\/ )
|
||||||
|
'. _.'-/'-'\-'._
|
||||||
|
': _/.--'[[[[]'--.\_
|
||||||
|
': /_' : |::"| : '.\
|
||||||
|
': // ./ |oUU| \.' :\
|
||||||
|
': _:'..' \_|___|_/ : :|
|
||||||
|
':. .' |_[___]_| :.':\
|
||||||
|
[::\ | : | | : ; : \
|
||||||
|
'-' \/'.| |.' \ .;.' |
|
||||||
|
|\_ \ '-' : |
|
||||||
|
| \ \ .: : | |
|
||||||
|
| \ | '. : \ |
|
||||||
|
/ \ :. .; |
|
||||||
|
/ | | :__/ : \\
|
||||||
|
| | | \: | \ | ||
|
||||||
|
/ \ : : |: / |__| /|
|
||||||
|
| : : :_/_| /'._\ '--|_\
|
||||||
|
/___.-/_|-' \ \
|
||||||
|
'-'
|
||||||
|
|
||||||
|
|
||||||
|
.-.__ \ .-. ___ __
|
||||||
|
|_| '--.-.-( \/\;;\_\.-._______.-.
|
||||||
|
(-)___ \ \ .-\ \;;\( \ \ \
|
||||||
|
Y '---._\_((Q)) \;;\\ .-\ __(_)
|
||||||
|
I __'-' / .--.((Q))---' \,
|
||||||
|
I ___.-: \| | \'-'_ \
|
||||||
|
A .-' \ .-.\ \ \ \ '--.__ '\
|
||||||
|
| |____.----((Q))\ \__|--\_ \ '
|
||||||
|
( ) '-' \_ : \-' '--.___\
|
||||||
|
Y \ \ \ \(_)
|
||||||
|
I \ \ \ \,
|
||||||
|
I \ \ \ \
|
||||||
|
A \ \ \ '\
|
||||||
|
| \ \__| '
|
||||||
|
\_:. \
|
||||||
|
\ \ \
|
||||||
|
\ \ \
|
||||||
|
\_\_|
|
||||||
|
|
||||||
|
.==.
|
||||||
|
()''()-.
|
||||||
|
.---. ;--; /
|
||||||
|
.'_:___". _..'. __'.
|
||||||
|
|__ --==|'-''' \'...;
|
||||||
|
[ ] :[| |---\
|
||||||
|
|__| I=[| .' '.
|
||||||
|
/ / ____| : '._
|
||||||
|
|-/.____.' | : :
|
||||||
|
snd /___\ /___\ '-'._----'
|
||||||
|
|
||||||
|
|
||||||
|
___ |\________/)
|
||||||
|
[_,_]) \ / \|
|
||||||
|
/|=T=|] / __ __\
|
||||||
|
|\ " // |_ 9 p ]\
|
||||||
|
||'-'/--. / /\ =| \|\ \
|
||||||
|
/|| <\/> |\ | '._, @ @)<_)
|
||||||
|
| |\ | | \.__/(_;_)
|
||||||
|
| . H | | : '='|
|
||||||
|
| | _H__/ _| : |
|
||||||
|
\ '.__ \ / ; ';
|
||||||
|
__'-._(_}==.' : ;
|
||||||
|
(___| /-' | :. :
|
||||||
|
[.-' \ | \ \ ; :
|
||||||
|
.-' | | | | ":
|
||||||
|
/ |==| \ \ / \_
|
||||||
|
/ [ | '._\_ -._ \
|
||||||
|
/ \__) __.- \ \ )\\
|
||||||
|
/ | /.' >>)
|
||||||
|
| \ |\ |
|
||||||
|
| .' '-. | \ /
|
||||||
|
| / / / / /
|
||||||
|
snd | /
|
||||||
|
|
||||||
|
|
||||||
|
._,.
|
||||||
|
"..-..pf.
|
||||||
|
-L ..#'
|
||||||
|
.+_L ."]#
|
||||||
|
,'j' .+.j` -'.__..,.,p.
|
||||||
|
_~ #..<..0. .J-.``..._f.
|
||||||
|
.7..#_.. _f. .....-..,`4'
|
||||||
|
;` ,#j. T' .. ..J....,'.j`
|
||||||
|
.` .."^.,-0.,,,,yMMMMM,. ,-.J...+`.j@
|
||||||
|
.'.`...' .yMMMMM0M@^=`""g.. .'..J..".'.jH
|
||||||
|
j' .'1` q'^)@@#"^".`"='BNg_...,]_)'...0-
|
||||||
|
.T ...I. j" .'..+,_.'3#MMM0MggCBf....F.
|
||||||
|
j/.+'.{..+ `^~'-^~~""""'"""?'"``'1`
|
||||||
|
.... .y.} `.._-:`_...jf
|
||||||
|
g-. .Lg' ..,..'-....,'.
|
||||||
|
.'. .Y^ .....',].._f
|
||||||
|
......-f. .-,,.,.-:--&`
|
||||||
|
.`...'..`_J`
|
||||||
|
.~......'#'
|
||||||
|
'..,,.,_]`
|
||||||
|
.L..`..``.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
,ooo888888888888888oooo,
|
||||||
|
o8888YYYYYY77iiiiooo8888888o
|
||||||
|
8888YYYY77iiYY8888888888888888
|
||||||
|
[88YYY77iiY88888888888888888888]
|
||||||
|
88YY7iYY888888888888888888888888
|
||||||
|
[88YYi 88888888888888888888888888]
|
||||||
|
i88Yo8888888888888888888888888888i
|
||||||
|
i] ^^^88888888^^^ o [i
|
||||||
|
oi8 i o8o i 8io
|
||||||
|
,77788o ^^ ,oooo8888888ooo, ^ o88777,
|
||||||
|
7777788888888888888888888888888888877777
|
||||||
|
77777888888888888888888888888888877777
|
||||||
|
77777788888888^7777777^8888888777777
|
||||||
|
,oooo888 ooo 88888778888^7777ooooo7777^8887788888 ,o88^^^^888oo
|
||||||
|
o8888777788[];78 88888888888888888888888888888888888887 7;8^ 888888888oo^88
|
||||||
|
o888888iii788 ]; o 78888887788788888^;;^888878877888887 o7;[]88888888888888o
|
||||||
|
88888877 ii78[]8;7o 7888878^ ^8788^;;;;;;^878^ ^878877 o7;8 ]878888888888888
|
||||||
|
[88888888887888 87;7oo 777888o8888^;ii;;ii;^888o87777 oo7;7[]8778888888888888
|
||||||
|
88888888888888[]87;777oooooooooooooo888888oooooooooooo77;78]88877i78888888888
|
||||||
|
o88888888888888 877;7877788777iiiiiii;;;;;iiiiiiiii77877i;78] 88877i;788888888
|
||||||
|
88^;iiii^88888 o87;78888888888888888888888888888888888887;778] 88877ii;7788888
|
||||||
|
;;;iiiii7iiii^ 87;;888888888888888888888888888888888888887;778] 888777ii;78888
|
||||||
|
;iiiii7iiiii7iiii77;i88888888888888888888i7888888888888888877;77i 888877777ii78
|
||||||
|
iiiiiiiiiii7iiii7iii;;;i7778888888888888ii7788888888888777i;;;;iiii 88888888888
|
||||||
|
i;iiiiiiiiiiii7iiiiiiiiiiiiiiiiiiiiiiiiii8877iiiiiiiiiiiiiiiiiii877 88888
|
||||||
|
ii;;iiiiiiiiiiiiii;;;ii^^^;;;ii77777788888888888887777iii;; 77777 78
|
||||||
|
77iii;;iiiiiiiiii;;;ii;;;;;;;;;^^^^8888888888888888888777ii;; ii7 ;i78
|
||||||
|
^ii;8iiiiiiii ';;;;ii;;;;;;;;;;;;;;;;;;^^oo ooooo^^^88888888;;i7 7;788
|
||||||
|
o ^;;^^88888^ 'i;;;;;;;;;;;;;;;;;;;;;;;;;;;^^^88oo^^^^888ii7 7;i788
|
||||||
|
88ooooooooo ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 788oo^;; 7;i888
|
||||||
|
887ii8788888 ;;;;;;;ii;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;^87 7;788
|
||||||
|
887i8788888^ ;;;;;;;ii;;;;;;;oo;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;,,, ;;888
|
||||||
|
87787888888 ;;;;;;;ii;;;;;;;888888oo;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;,,;i788
|
||||||
|
87i8788888^ ';;;ii;;;;;;;8888878777ii8ooo;;;;;;;;;;;;;;;;;;;;;;;;;;i788 7
|
||||||
|
77i8788888 ioo;;;;;;oo^^ooooo ^7i88^ooooo;;;;;;;;;;;;;;;;;;;;i7888 78
|
||||||
|
7i87788888o 7;ii788887i7;7;788888ooooo7888888ooo;;;;;;;;;;;;;;oo ^^^ 78
|
||||||
|
i; 7888888^ 8888^o;ii778877;7;7888887;;7;7788878;878;; ;;;;;;;i78888o ^
|
||||||
|
i8 788888 [88888^^ ooo ^^^^^;;77888^^^^;;7787^^^^ ^^;;;; iiii;i78888888
|
||||||
|
^8 7888^ [87888 87 ^877i;i8ooooooo8778oooooo888877ii; iiiiiiii788888888
|
||||||
|
^^^ [7i888 87;; ^8i;;i7888888888888888887888888 i7iiiiiii88888^^
|
||||||
|
87;88 o87;;;;o 87i;;;78888788888888888888^^ o 8ii7iiiiii;;
|
||||||
|
87;i8 877;77888o ^877;;;i7888888888888^^ 7888 78iii7iii7iiii
|
||||||
|
^87; 877;778888887o 877;;88888888888^ 7ii7888 788oiiiiiiiii
|
||||||
|
^ 877;7 7888888887 877i;;8888887ii 87i78888 7888888888
|
||||||
|
[87;;7 78888888887 87i;;888887i 87ii78888 7888888888]
|
||||||
|
877;7 7788888888887 887i;887i^ 87ii788888 78888888888
|
||||||
|
87;i8 788888888888887 887ii;;^ 87ii7888888 78888888888
|
||||||
|
[87;i8 7888888888888887 ^^^^ 87ii77888888 78888888888
|
||||||
|
87;;78 7888888888888887ii 87i78888888 778888888888
|
||||||
|
87;788 7888888888888887i] 87i78888888 788888888888
|
||||||
|
[87;88 778888888888888887 7ii78888888 788888888888
|
||||||
|
87;;88 78888888888888887] ii778888888 78888888888]
|
||||||
|
7;;788 7888888888888888] i7888888888 78888888888'
|
||||||
|
7;;788 7888888888888888 'i788888888 78888888888
|
||||||
|
7;i788 788888888888888] 788888888 77888888888]
|
||||||
|
'7;788 778888888888888] [788888888 78888888888'
|
||||||
|
';77888 78888888888888 8888888888 7888888888]
|
||||||
|
778888 78888888888888 8888888888 7888888888]
|
||||||
|
78888 7888888888888] [8888888888 7888888888
|
||||||
|
7888 788888888888] 88888888888 788888888]
|
||||||
|
778 78888888888] ]888888888 778888888]
|
||||||
|
oooooo ^88888^ ^88888^^^^^^^^8888]
|
||||||
|
87;78888ooooooo8o ,oooooo oo888oooooo
|
||||||
|
[877;i77888888888] [;78887i8888878i7888;
|
||||||
|
^877;;ii7888ii788 ;i777;7788887787;778;
|
||||||
|
^87777;;;iiii777 ;77^^^^^^^^^^^^^^^^;;
|
||||||
|
^^^^^^^^^ii7] ^ o88888888877iiioo
|
||||||
|
77777o [88777777iiiiii;;778
|
||||||
|
77777iii 8877iiiii;;;77888888]
|
||||||
|
77iiii;8 [77ii;778 788888888888
|
||||||
|
7iii;;88 iii;78888 778888888888
|
||||||
|
77i;78888] ;;;;i88888 78888888888
|
||||||
|
,7;78888888 [;;i788888 7888888888]
|
||||||
|
i;788888888 ;i7888888 7888888888
|
||||||
|
;788888888] i77888888 788888888]
|
||||||
|
';88888888' [77888888 788888888]
|
||||||
|
[[8ooo88] 78888888 788888888
|
||||||
|
[88888] 78888888 788888888
|
||||||
|
^^^ [7888888 77888888]
|
||||||
|
88888888 7888887
|
||||||
|
77888888 7888887
|
||||||
|
;i88888 788888i
|
||||||
|
,;;78888 788877i7
|
||||||
|
,7;;i;777777i7i;;7
|
||||||
|
87778^^^ ^^^^87778
|
||||||
|
^^^^ o777777o ^^^
|
||||||
|
o77777iiiiii7777o
|
||||||
|
7777iiii88888iii777
|
||||||
|
;;;i7778888888877ii;;
|
||||||
|
Imperial Stormtrooper [i77888888^^^^8888877i]
|
||||||
|
(Standard Shock Trooper) 77888^oooo8888oooo^8887]
|
||||||
|
[788888888888888888888888]
|
||||||
|
88888888888888888888888888
|
||||||
|
]8888888^iiiiiiiii^888888]
|
||||||
|
iiiiiiiiiiiiiiiiiiiiii
|
||||||
|
^^^^^^^^^^^^^
|
||||||
|
|
Loading…
Reference in New Issue
Block a user