8 puzzle breadth first search - python

I moved this from code review because folks over there suggest this should be posted here
I'm trying to do 8-puzzle using pycharm, I will first generate a 3x3 list and then shuffle it to get the initial board state example:
[1 , 2 , 3 ]
[4 , 5 , 6 ]
[7 , 8 , 9 ]
then I'll do some random moves on it to get the goal board state:
[ 3 , 2 , 1 ]
[ 6 , 5 , 4 ]
[ 9 , 8 , 7 ]
when I set the size to be 3(3x3) it never complete the search, and when I set the size to be 2 it solves but sometimes it will exhaust the frontier list and returns nothing, which should not be possible.
EDIT
I changed the
return Solution (start_node, numExplored, memoryRequired)
to
return Solution (fr_node, numExplored, memoryRequired)
and it always return an solution for 2x2 , 3x3 is still very slow though
I have these code in Python:
def breadthFirstSearch(problem):
"""Search the shallowest nodes in the search tree first."""
frontier = util.Queue()
explored = []
numExplored = 0
memoryRequired = 0
# generate start node
start_node = Node(problem.getStartState(), None, 0, None)
# return immediately if start node is the goal state
if problem.isGoalState(start_node.state):
return Solution(start_node, 0, 0)
# push start node into frontier
frontier.push (start_node)
# while frontier list is not empty
while not frontier.isEmpty():
# get the first-in frontier node from frontier list
fr_node = frontier.pop()
explored.append(fr_node)
# get successor nodes
successors = problem.getSuccessors(fr_node.state)
numExplored += 1
# append into explored list
# if len(successors) >0:
# explored.append(fr_node)
# else:
# explored.append(fr_node)
# for each successor node
for sc_state, sc_action, sc_cost in successors:
# generate successor node
sc_node = Node(sc_state, sc_action, sc_cost, fr_node)
# check for goal state immediately after generate
if problem.isGoalState(sc_node.state): # if found solution
return Solution (sc_node, numExplored, memoryRequired)
# insert successor node into frontier list (if not found in explored and frontier)
if sc_node not in explored and sc_node not in frontier.list:
frontier.push(sc_node)
memoryRequired = max ([memoryRequired, len(frontier.list)+len(explored)])
print ('Frontier size = %d, Explored size = %d, Total size = %d ,Successor Length= %d' %(len(frontier.list), len(explored), len(frontier.list)+len(explored), len(successors)))
return Solution (start_node, numExplored, memoryRequired)
Node is defined as:
class Node:
def __init__(self, state, action, cost, parent):
self.state = state
self.cost = cost
self.action = action
self.parent = parent
if parent is None:
self.totalCost = cost
self.depth = 0
else:
self.totalCost = parent.totalCost + cost
self.depth = parent.depth + 1
def __eq__(self, other):
return hasattr(other,'state') and self.state == other.state
and then there's the file that defines NPuzzle and Puzzle state.
class ActionType:
UP = 'up'
DOWN = 'down'
LEFT = 'left'
RIGHT = 'right'
Actions = [ActionType.UP, ActionType.DOWN, ActionType.LEFT,
ActionType.RIGHT]
class PuzzleState (SearchState):
def __init__ (self, n, board = None):
self.n = n
self.board = [[j for j in range(n*i,n*i+n)] for i in range(n)]
self.blankCellPos = self.getBlankCellPos()
def __eq__ (self, other):
if self.board == other.board:
return True
else:
return False
def deepCopy(self):
state = copy.deepcopy(self) #PuzzleState(self.n)
return state
def getBlankCellPos (self):
for i in range(self.n):
for j in range(self.n):
if self.board[i][j] == 0:
return (i,j)
def isValidAction (self, action):
(r,c)= self.blankCellPos
if action == ActionType.UP:
if r - 1 < 0:
return False
else:
return True
elif action == ActionType.DOWN:
if r + 1 > self.n-1:
return False
else:
return True
elif action == ActionType.LEFT:
if c - 1 < 0:
return False
else:
return True
elif action == ActionType.RIGHT:
if c + 1 > self.n-1:
return False
else:
return True
def getValidActions (self):
ValidActions = []
for action in Actions:
if(self.isValidAction(action) == True):
ValidActions.append(action)
return ValidActions
def move (self, action):
(r,c) = self.blankCellPos
if action == ActionType.UP:
self.blankCellPos = (r-1,c)
self.board[r][c], self.board[r-1][c] = self.board[r-1][c], self.board[r][c]
elif action == ActionType.DOWN:
self.blankCellPos = (r+1,c)
self.board[r][c], self.board[r+1][c] = self.board[r+1][c], self.board[r][c]
elif action == ActionType.LEFT:
self.blankCellPos = (r, c-1)
self.board[r][c], self.board[r][c-1] = self.board[r][c-1], self.board[r][c]
elif action == ActionType.RIGHT:
self.blankCellPos = (r, c+1)
self.board[r][c], self.board[r][c+1] = self.board[r][c+1], self.board[r][c]
def randomMove (self, numMoves):
#(r,c) = self.board.blankCellPos
ValidActions = []
actions = []
tmp = -1
for num in range(numMoves):
ValidActions = self.getValidActions()
tmp = random.choice(ValidActions)
#remove loopy choices#
if num > 0:
if actions[-1] == ActionType.UP and tmp == ActionType.DOWN:
ValidActions.remove(ActionType.DOWN)
tmp = random.choice(ValidActions)
elif actions[-1] == ActionType.DOWN and tmp == ActionType.UP:
ValidActions.remove(ActionType.UP)
tmp = random.choice(ValidActions)
elif actions[-1] == ActionType.LEFT and tmp == ActionType.RIGHT:
ValidActions.remove(ActionType.RIGHT)
tmp = random.choice(ValidActions)
elif actions[-1] == ActionType.RIGHT and tmp == ActionType.LEFT:
ValidActions.remove(ActionType.LEFT)
tmp = random.choice(ValidActions)
actions.append(tmp)
self.move(tmp)
def randomInitialize (self):
random.shuffle(self.board)
for ii, sublist in enumerate(self.board):
random.shuffle(self.board[ii])
self.blankCellPos = self.getBlankCellPos()
def display (self):
print( ' ',)
#for c in range (self.n):
print (self.board)
#print (' %d' %(c),)
class NPuzzle (SearchProblem):
def __init__ (self, n, startState = None, goalState = None):
self.n = PuzzleState(n)
self.startState = self.n.deepCopy()
self.goalState = self.n.deepCopy()
def randomStartState (self):
self.startState.randomInitialize()
def randomGoalState(self, numMoves):
self.goalState.randomMove(numMoves)
def setStartState (self, startState):
self.startState = startState
def setGoalState (self, goalState):
self.goalState = goalState
def getStartState (self):
return self.startState
def getGoalState (self):
return self.goalState
def isGoalState(self, state):
if state == self.goalState:
return True
else:
return False
def getSuccessors (self, state):
successorlist = []
validActions = state.getValidActions()
for action in validActions:
#if not deep copy what will happen?
successor = state.deepCopy()
successor.move(action)
self.cost = 1
successorlist.append([successor, action, self.cost])
return successorlist
According to what I read and what my lecturer told me , the machine should punch out something around thousands of searches every second but when I run it it only appear to be very fast at the first few thousand nodes and then slows down terribly after that.
I have no idea what's wrong. Any hint will be greatly appreciated.

Related

Error in code while converting infix form to prefix form using stack class

class Stack:
def __init__(self):
self.stack=[]
self.top = None
def empty(self):
return not self.stack
def push(self, item):
self.stack.append(item)
self.top = item
def pop(self):
if self.empty():
a = self.stack.pop()
return a
else:
print("Empty stack")
def printall(self):
print(self.stack)
def clear(self):
self.stack = []
self.top = None
ysj = Stack()
jy = []
list = input().split()
leng = len(list)
num = 0
for i in range(0, leng):
if list[i] == '+' or list[i] == '-':
while ysj.top is not None:
jy.append(ysj.pop())
num = num - 1
ysj.push(list[i])
num = num + 1
elif list[i] == '/' or list[i] == '*':
while ysj.top == '/' or ysj.top == '*':
jy.append(ysj.pop())
num = num - 1
ysj.push(list[i])
num = num + 1
else:
jy.append(int(list[i]))
for i in range(1, num + 1):
jy.append(ysj.pop())
for i in range(0, len(jy)):
print(jy[i], end=" ")
I'm trying to convert the equation from infix form to postfix form using Stack class.
This code works when I type in
1 + 2 * 3
but doesn't work well when I type in
1 * 2 + 3 or 1 * 2 * 3 or 1 + 2 + 3
I think there's something wrong with my while loop but I can't figure out what part is wrong. Please help me.
You forgot to insert None to top when the list gets empty, and you are trying to remove a item when the list is empty:
class Stack:
def __init__(self):
self.stack=[]
self.top = None
def empty(self):
return not self.stack
def push(self, item):
self.stack.append(item)
self.top = item
def pop(self):
if not self.empty(): # remove item only if the stack is not empty
a = self.stack.pop()
# after removing a item, check if the stack is empty
if self.empty(): # if it is, set the top as None
self.top = None
return a
else:
print("Empty stack")

Iterative Deepening Search for K-puzzle

I am trying to implement iterative deepening search for the k - puzzle. I have managed to find the goal node. However, I am unable to backtrack from the goal node to the start node to find the optimal moves. I think it has something to do with repeated states in IDS. Currently, I am keeping track of all visited states in the IDS algorithm.
This is the current implementation of my algorithm. In the code below, moves_dict stores each node's previous state and move to get to current state.
import os
import sys
from itertools import chain
from collections import deque
# Iterative Deepening Search (IDS)
class Node:
def __init__(self, state, empty_pos = None, depth = 0):
self.state = state
self.depth = depth
self.actions = ["UP", "DOWN", "LEFT", "RIGHT"]
if empty_pos is None:
self.empty_pos = self.find_empty_pos(self.state)
else:
self.empty_pos = empty_pos
def find_empty_pos(self, state):
for x in range(n):
for y in range(n):
if state[x][y] == 0:
return (x, y)
def find_empty_pos(self, state):
for x in range(n):
for y in range(n):
if state[x][y] == 0:
return (x, y)
def do_move(self, move):
if move == "UP":
return self.up()
if move == "DOWN":
return self.down()
if move == "LEFT":
return self.left()
if move == "RIGHT":
return self.right()
def swap(self, state, (x1, y1), (x2, y2)):
temp = state[x1][y1]
state[x1][y1] = state[x2][y2]
state[x2][y2] = temp
def down(self):
empty = self.empty_pos
if (empty[0] != 0):
t = [row[:] for row in self.state]
pos = (empty[0] - 1, empty[1])
self.swap(t, pos, empty)
return t, pos
else:
return self.state, empty
def up(self):
empty = self.empty_pos
if (empty[0] != n - 1):
t = [row[:] for row in self.state]
pos = (empty[0] + 1 , empty[1])
self.swap(t, pos, empty)
return t, pos
else:
return self.state, empty
def right(self):
empty = self.empty_pos
if (empty[1] != 0):
t = [row[:] for row in self.state]
pos = (empty[0] , empty[1] - 1)
self.swap(t, pos, empty)
return t, pos
else:
return self.state, empty
def left(self):
empty = self.empty_pos
if (empty[1] != n - 1):
t = [row[:] for row in self.state]
pos = (empty[0] , empty[1] + 1)
self.swap(t, pos, empty)
return t, pos
else:
return self.state, empty
class Puzzle(object):
def __init__(self, init_state, goal_state):
self.init_state = init_state
self.state = init_state
self.goal_state = goal_state
self.total_nodes = 1
self.total_visited = 0
self.max_frontier = 0
self.depth = 0
self.visited = {}
self.frontier_node = []
self.move_dict = {}
def is_goal_state(self, node):
return node.state == self.goal_state
def is_solvable(self):
flat_list = list(chain.from_iterable(self.init_state))
num_inversions = 0
for i in range(max_num):
current = flat_list[i]
for j in range(i + 1, max_num + 1):
next = flat_list[j]
if current > next and next != 0:
num_inversions += 1
if n % 2 != 0 and num_inversions % 2 == 0:
return True
elif n % 2 == 0:
row_with_blank = n - flat_list.index(0) // n
return (row_with_blank % 2 == 0) == (num_inversions % 2 != 0)
else:
return False
def succ(self, node, frontier):
succs = deque()
node_str = str(node.state)
self.visited[node_str] = node.depth
self.total_visited += 1
frontier -= 1
for m in node.actions:
transition, t_empty = node.do_move(m)
transition_str = str(transition)
transition_depth = node.depth + 1
if transition_str not in self.visited or transition_depth < self.visited[transition_str]:
self.total_nodes += 1
transition_depth = node.depth + 1
transition_str = str(transition)
self.move_dict[transition_str] = (node_str, m)
succs.append(Node(transition, t_empty, transition_depth))
frontier += 1
return succs , frontier
def depth_limited(self, node, depth, frontier):
if self.is_goal_state(node):
return node
if node.depth >= depth:
return None
succs, frontier = self.succ(node, frontier)
self.max_frontier = max(self.max_frontier, frontier)
while succs:
result = self.depth_limited(succs.popleft(), depth, frontier)
if result is not None:
return result
return None
def solve(self):
if not self.is_solvable():
return ["UNSOLVABLE"]
goal_node = None
while goal_node is None:
goal_node = self.depth_limited(Node(self.init_state), self.depth, 1)
if goal_node is not None:
break
# reset statistics
self.visited = {}
self.total_nodes = 1
self.move_dict = {}
self.depth += 1
print self.depth
print "out"
print goal_node.state
solution = deque()
init_str = str(self.init_state)
current_str = str(goal_node.state)
while current_str != init_str:
current_str, move = self.move_dict[current_str]
solution.appendleft(move)
print "Total number of nodes generated: " + str(self.total_nodes)
print "Total number of nodes explored: " + str(self.total_visited)
print "Maximum number of nodes in frontier: " + str(self.max_frontier)
print "Solution depth: " + str(self.depth)
return solution
I have been cracking my head for awhile now. I use a hashMap that maps the state string to its depth and when adds the node whenever the same state appears in a shallower depth
EDIT
Optimal solution depth for this test case is 22.
init state: [[1,8,3],[5,2,4],[0,7,6]]
Goal state: [[1,2,3],[4,5,6],[7,8,0]]
im not going to implement your k puzzle but consider the following datastruct
d = {'A':{'B':{'Z':7,'Q':9},'R':{'T':0}},'D':{'G':1}}
def find_node(search_space,target,path_so_far=None):
if not path_so_far: # empty path to start
path_so_far = []
for key,value in search_space.items():
if value == target:
# found the value return the path
return path_so_far+[key]
else:
# pass the path so far down to the next step of the search space
result = find_node(search_space[key],target, path_so_far+[key])
if result:
print("Found Path:",result)
return result

How to create an algorithm from pseudo code to uniform cost search?

As we know from daily experience diagonal moves are cheaper than a
horizontal + vertical moves, this becomes a problem of uneven step cost.
So, uniform cost search will be required to solve this problem.
To implement uniform cost search, you will need a Priority Queue for frontier.
To add a child to the frontier
frontier.insert(item)
To extract the item with minimum cost
Temp = frontier.extract_min()
To decrease the cost of an item
frontier.decrease_key(item)
To check if in an item is already in the frontier
frontier.is_in(item)
It returns true if item is in the frontier, returns false if not in the frontier.
Algorithm I used for this code :
import collections
import copy
grid_size = 5
source = (0, 0)
destination = (4, 3)
grid = [[0, 0, 0, 0, 0],
[0, 1, 0, 1, 0],
[0, 1, 0, 0, 0],
[0, 1, 1, 1, 0],
[0, 0, 0, 0, 0]]
class Location:
def __init__(self, p, cost=0):
self.point = p
self.parent = None
self.cost = cost
self.hash_value = self.hash_calc()
def hash_calc(self):
x, y = self.point
return str(x) + "_" + str(y)
def make_move(self, direction):
x, y = self.point
# up
if direction == 'l':
if y > 0 and grid[x][y-1] == 0:
self.point = x, y-1
self.cost = self.cost + 1
# down
elif direction == 'r':
if y < grid_size-1 and grid[x][y+1] == 0:
self.point = x, y+1
self.cost = self.cost + 1
# left
elif direction == 'u':
if x > 0 and grid[x-1][y] == 0:
self.point = x-1, y
self.cost = self.cost + 1
# right
elif direction == 'd':
if x < grid_size-1 and grid[x+1][y] == 0:
self.point = x+1, y
self.cost = self.cost + 1
# up-left
elif direction == 'ul':
if y>0 and x>0 and grid[x-1][y-1] == 0:
self.point = x-1, y-1
self.cost = self.cost + 1.5
# up-right
elif direction == 'dl':
if y>0 and x<grid_size-1 and grid[x+1][y-1] == 0:
self.point = x+1, y-1
self.cost = self.cost + 1.5
# down-left
elif direction == 'ur':
if y<grid_size-1 and x>0 and grid[x-1][y+1] == 0:
self.point = x-1, y+1
self.cost = self.cost + 1.5
# down-right
elif direction == 'dr':
if y<grid_size-1 and x<grid_size-1 and grid[x+1][y+1] == 0:
self.point = x+1, y+1
self.cost = self.cost + 1.5
self.hash_value = self.hash_calc()
print(self.point, self.cost)
def copy_location(self):
temp = Location(self.point, cost=self.cost)
temp.parent = self
return temp
def print_path(self):
ancestors = []
temp = self
while(temp.parent != None):
ancestors.append(temp.parent)
temp = temp.parent
n = len(ancestors)
for i in range(n):
temp = ancestors.pop()
print(temp.point, end='->')
print(self.point)
print('%d steps were required.\nTotal cost is %f' % (n, self.cost))
def goal_test(self):
return self.point == destination
def get_key(self):
return self.cost
class Priority_Queue:
def __init__(self):
self.capacity = 2
self.q = [None] * self.capacity
self.items = [None] * self.capacity
self.dict = {}
self.size = 0
def parent(self, i):
return int((i-1)/2)
def left(self, i):
return 2*i + 1
def right(self, i):
return 2*i + 2
def move(self, i, j):
self.q[i] = self.q[j]
self.items[i] = self.items[j]
self.dict[self.items[i].hash_value] = i
def swap(self, i, j):
self.q[i], self.q[j] = self.q[j], self.q[i]
self.items[i], self.items[j] = self.items[j], self.items[i]
self.dict[self.items[i].hash_value] = i
self.dict[self.items[j].hash_value] = j
def min_heapify(self, i):
left = self.left(i)
right = self.right(i)
smallest = i
if left < self.size and self.q[left] < self.q[i]:
smallest = left
if right < self.size and self.q[right] < self.q[smallest]:
smallest = right
if smallest != i:
self.swap(i, smallest)
self.min_heapify(smallest)
def insert(self, item):
self.size = self.size + 1
if self.size > self.capacity:
self.q = self.q + [None] * self.capacity
self.items = self.items + [None] * self.capacity
self.capacity = self.capacity * 2
i = self.size - 1
self.q[i] = item.get_key()
self.items[i] = item
self.dict[item.hash_value] = i
while i != 0 and self.q[self.parent(i)] > self.q[i]:
self.swap(i, self.parent(i))
i = self.parent(i)
def extract_min(self):
item = self.items[0]
self.move(0, self.size - 1)
self.dict.pop(item.hash_value)
self.size = self.size - 1
self.min_heapify(0)
return item
def decrease_key(self, item):
i = self.dict[item.hash_value]
if self.q[i] < item.get_key():
return
self.q[i] = item.get_key()
self.items[i] = item
while i != 0 and self.q[self.parent(i)] > self.q[i]:
self.swap(i, self.parent(i))
i = self.parent(i)
def get_min(self):
return self.items[0]
def is_in(self, item):
return item.hash_value in self.dict.keys()
# Make an initial State with source
initial_state = Location(source)
# Create an empty frontier
frontier = Priority_Queue()
frontier.insert(initial_state)
# Create an empty explored list
explored = set()
# initialize frontier with initial state
##########################################
####Solution would be here
##############Solution i Tried to solve
x = 0
children=()
Output = False
#While Output is False
while Output == False:
x = x+1
if frontier.size == 0:
print('No Solution Found !')
break
z = frontier.get_min()
explored.add(z)
#make moves
for d in ['u','d','l','r','ul','dl','ur','dr']:
children = z.copy_location()
children.make_move(d)
if children not in explored and children:
if children.goal_test() == True:
children.print_path()
Output = True
break
frontier.insert(children)
#if output is true
if Output == True:
print('Total %d states Explored !.' %(x))
break
i want to implement uniform cost search here. i tried to follow the pseudo code but i failed.
well this is my first time in stack overflow so if i made any mistake please forgive me !
i will be more careful from next time.

How do I make an elif command which occurs in a for loop only register for certain instances of an item within the list?

Below is part of my code. Basically what this does is when run it creates credit cards with a random number and a given currency code and credit card limit. The Credit Card is a class and the money it stores is also a class (Which I have not included here for brevity and because I think they are not relevant to my question.) What happens in my code, however, is that my cancel statement works out fine but if I have two credit cards in a list and I try to cancel the second one it will again print out NO_SUCH_CARD. The card gets deleted anyway if its in the list. My guess is that his happens because the for loop iterates through the list and it first detects a card whose number is different than the given one, which is why it prints no such card, but I have no idea how to fix this. Help would be appreciated.
PATH = 'saved_cards.txt'
creditcard_list = []
import decimal
import ast
import collections
import os
import random
def currency_dictionary():
'''Initialized at the start of the program. It finds and reads a currency.txt
file and stores those in a dictionary'''
final_dict = collections.defaultdict(list)
with open("currency.txt", 'r') as f:
currency_lines = f.readlines()
for item in currency_lines:
m = item.split(' ')
final_dict[m[0]] = [int(m[1]), decimal.Decimal(m[2])]
return final_dict
class Money():
def __init__(self, money_amount: decimal, currency_code: str):
self.m = decimal.Decimal(money_amount)
self.c = str(currency_code)
self.d = currency_dictionary()
def __repr__(self):
return 'Money({}, {})'.format(self.m, self.c)
def __eq__(self, other):
if type(other) != Money:
return False
elif self.c == other.c:
return self.m == other.m
elif self.c != other.c:
dictionary_key1 = self.d[self.c]
decimal_point1 = dictionary_key1[0]
conversion_factor1 = dictionary_key1[1]
x = self.m / conversion_factor1
dictionary_key2 = self.d[other.c]
decimal_point = dictionary_key2[0]
conversion_factor = dictionary_key2[1]
y = other.m / conversion_factor
return x == y
def __ne__(self, other):
if type(other) != Money:
return True
elif self.c == other.c:
return self.m != other.m
elif self.c != other.c:
dictionary_key1 = self.d[self.c]
decimal_point1 = dictionary_key1[0]
conversion_factor1 = dictionary_key1[1]
x = self.m / conversion_factor1
dictionary_key2 = self.d[other.c]
decimal_point = dictionary_key2[0]
conversion_factor = dictionary_key2[1]
y = other.m / conversion_factor
return x != y
def __add__(self, other):
if self.c == other.c:
return Money((self.m + other.m), self.c)
elif self.c != other.c:
dictionary_key1 = self.d[self.c]
decimal_point1 = dictionary_key1[0]
conversion_factor1 = dictionary_key1[1]
x = self.m / conversion_factor1
dictionary_key2 = self.d[other.c]
decimal_point = dictionary_key2[0]
conversion_factor2 = dictionary_key2[1]
y = other.m / conversion_factor2
total = x + y
return Money((total * conversion_factor1), self.c)
def __sub__(self, other):
if self.c == other.c:
return Money((self.m - other.m), self.c)
elif self.c != other.c:
dictionary_key1 = self.d[self.c]
decimal_point1 = dictionary_key1[0]
conversion_factor1 = dictionary_key1[1]
x = self.m / conversion_factor1
dictionary_key2 = self.d[other.c]
decimal_point = dictionary_key2[0]
conversion_factor2 = dictionary_key2[1]
y = other.m / conversion_factor2
total = x - y
return Money((total * conversion_factor1), self.c)
class Credit_Card():
def __init__(self, card_number, money_amount: Money, card_limit: int):
if type(money_amount) != Money or type(card_limit) != int:
raise TypeError('one of the types of the parameters entered is not valid')
self.number = card_number
self.amount = money_amount
self.limit = card_limit
def __repr__(self):
return 'Card#{}({}, {})'.format(self.number, self.amount, self.limit)
def user_interface():
boolean = True
while boolean:
temp_list = []
command = input()
if command.split()[0] == 'ISSUE':
if len(command.split()) == 3:
x = "%0.5d" % random.randint(0,99999)
currency_code = command.split()[1]
card_limit = int(command.split()[2])
if card_limit < 0:
print("NEGATIVE_LIMIT")
elif not currency_dictionary()[currency_code]:
print("NO_SUCH_CURRENCY")
else:
for card in creditcard_list:
temp_list.append(card.number)
if x not in temp_list and currency_dictionary()[currency_code]:
creditcard_list.append(Credit_Card(x, Money(0, currency_code), card_limit))
print('ISSUED', x)
print(creditcard_list)
else:
print("INVALID_ISSUE_COMMAND")
elif command.split()[0] == 'CANCEL':
templist2 = []
if len(command.split()) == 2:
card_number = command.split()[1]
for card in creditcard_list:
templist2.append(card)
for i, card in enumerate(templist2):
if card_number not in templist2[i].number:
print('NO_SUCH_CARD')
elif templist2[i].amount.m != 0:
print('NONZERO_BALANCE')
elif templist2[i].number == command.split()[1] and card.amount.m == 0:
del creditcard_list[i]
print('CANCELLED', card_number)
print(creditcard_list)
elif command.split()[0] == 'PURCHASE':
if len(command.split()) == 4:
card_number = command.split()[1]
currency_code = command.split()[2]
amount = int(command.split()[3])
if currency_code not in currency_dictionary().keys():
print('NO_SUCH_CURRENCY')
elif amount < 0:
print('NONPOSITIVE_AMOUNT')
else:
for i, card in enumerate(creditcard_list):
if card.number == card_number and 0 <= amount <= card.limit :
x = card.amount + Money(amount, currency_code)
creditcard_list[i] = Credit_Card(card.number, x, card.limit)
elif creditcard_list[i].number != card_number:
print('NO_SUCH_CARD')
elif amount > creditcard_list[i].limit:
print('OVER_LIMIT')
elif command.split(0) == 'PAYMENT':
print(creditcard_list)
if __name__ == '__main__':
user_interface()
My output for the cancel command basically looks like this, and I'm pretty sure once I figure this out I'll be able to deal with the rest. Bold is input, non-bold is output.
**ISSUE USD 5000**
ISSUED 50695
[Card#50695(Money(0, USD), 5000)]
**ISSUE RON 3000**
ISSUED 25282
[Card#50695(Money(0, USD), 5000), Card#25282(Money(0, RON), 3000)]
**CANCEL 25282**
[Card#50695(Money(0, USD), 5000), Card#25282(Money(0, RON), 3000)]
*NO_SUCH_CARD*
CANCELLED 25282
[Card#50695(Money(0, USD), 5000)]
Note that the lists are only printed out for me to keep track of what cards are currently in the main list, and I will eventually remove those print statements. I have italicized the output I'm having a problem with.
The problem appears to be that you're modifying creditcard_list at the same time you are iterating through it. You should first create a temporary copy of the list to iterate through while later removing items from the actual list.
Edit: Ahh, it's printing "NO_SUCH_CARD" on the first card in the list! Not on the second. So in your example, you loop through a list of two; it first visits card #50695, which is not equal to 2582, so it prints "NO_SUCH_CARD". Then it visits 25282, deletes it, and prints "CANCELED". It's doing exactly what you wrote it to do. Instead, just loop through, deleting the card if you find it and silently skipping any card that doesn't match. In the end, if the card is not found print "NO_SUCH_CARD"
Edit2: Here's an example:
found = False
for card in templist2:
if card_number == card.number:
if card.amount.m != 0:
print('NONZERO_BALANCE')
else:
del creditcard_list[i]
print('CANCELLED', card_number)
found = True
break
if not found:
print('NO_SUCH_CARD')

Python A* implementation

I am currently working on my Python game, in ika, which uses python 2.5
I decided to use A* pathfinding for the AI. However, I find it too slow for my needs (3-4 enemies can lag the game, but I would like to supply up to 4-5 without problems). I know, that such complex search like A* is not mean to be scripted in python, but I am pretty sure, that my pathfinder is also implemented in the wrong way.
My question is: How can I speed up this algorithm?
I wrote my own binary heap, and there are some try: except: lines inside some functions. Those lines can create large overhead? Are there better methods maintaining the open list?
I supplied the algorithm with graphics interface, for testing purposes (when the pathfinder finishes searching, it will write the number of iterations and seconds it takes to find the path, inside the ika.txt file. Also, Pressing A will do a complete search, and S does that step by step.)
Graphical version:
http://data.hu/get/6084681/A_star.rar
Also, here is a pastebin version:
http://pastebin.com/9N8ybX5F
Here is the main code I use for pathfinding:
import ika
import time
class Node:
def __init__(self,x,y,parent=None,g=0,h=0):
self.x = x
self.y = y
self.parent = parent
self.g = g
self.h = h
def cost(self):
return self.g + self.h
def equal(self,node):
if self.x == node.x and self.y == node.y:
return True
else:
return False
class Emerald_Pathfinder:
def __init__(self):
pass
def setup(self,start,goal):
self.start = start
self.goal = goal
self.openlist = [None,start] # Implemented as binary heap
self.closedlist = {} # Implemented as hash
self.onopenlist = {} # Hash, for searching the openlist
self.found = False
self.current = None
self.iterations = 0
def lowest_cost(self):
pass
def add_nodes(self,current):
nodes = []
x = current.x
y = current.y
self.add_node(x+1,y,current,10,nodes)
self.add_node(x-1,y,current,10,nodes)
self.add_node(x,y+1,current,10,nodes)
self.add_node(x,y-1,current,10,nodes)
# Dont cut across corners
up = map.is_obstacle((x,y-1),x,y-1)
down = map.is_obstacle((x,y+1),x,y+1)
left = map.is_obstacle((x-1,y),x-1,y)
right = map.is_obstacle((x+1,y),x+1,y)
if right == False and down == False:
self.add_node(x+1,y+1,current,14,nodes)
if left == False and up == False:
self.add_node(x-1,y-1,current,14,nodes)
if right == False and up == False:
self.add_node(x+1,y-1,current,14,nodes)
if left == False and down == False:
self.add_node(x-1,y+1,current,14,nodes)
return nodes
def heuristic(self,x1,y1,x2,y2):
return (abs(x1-x2)+abs(y1-y2))*10
def add_node(self,x,y,parent,cost,list):
# If not obstructed
if map.is_obstacle((x,y),x,y) == False:
g = parent.g + cost
h = self.heuristic(x,y,self.goal.x,self.goal.y)
node = Node(x,y,parent,g,h)
list.append(node)
def ignore(self,node,current):
# If its on the closed list, or open list, ignore
try:
if self.closedlist[(node.x,node.y)] == True:
return True
except:
pass
# If the node is on the openlist, do the following
try:
# If its on the open list
if self.onopenlist[(node.x,node.y)] != None:
# Get the id number of the item on the real open list
index = self.openlist.index(self.onopenlist[(node.x,node.y)])
# If one of the coordinates equal, its not diagonal.
if node.x == current.x or node.y == current.y:
cost = 10
else:
cost = 14
# Check, is this items G cost is higher, than the current G + cost
if self.openlist[index].g > (current.g + cost):
# If so, then, make the list items parent, the current node.
self.openlist[index].g = current.g + cost
self.openlist[index].parent = current
# Now resort the binary heap, in the right order.
self.resort_binary_heap(index)
# And ignore the node
return True
except:
pass
return False
def resort_binary_heap(self,index):
m = index
while m > 1:
if self.openlist[m/2].cost() > self.openlist[m].cost():
temp = self.openlist[m/2]
self.openlist[m/2] = self.openlist[m]
self.openlist[m] = temp
m = m / 2
else:
break
def heap_add(self,node):
self.openlist.append(node)
# Add item to the onopenlist.
self.onopenlist[(node.x,node.y)] = node
m = len(self.openlist)-1
while m > 1:
if self.openlist[m/2].cost() > self.openlist[m].cost():
temp = self.openlist[m/2]
self.openlist[m/2] = self.openlist[m]
self.openlist[m] = temp
m = m / 2
else:
break
def heap_remove(self):
if len(self.openlist) == 1:
return
first = self.openlist[1]
# Remove the first item from the onopenlist
self.onopenlist[(self.openlist[1].x,self.openlist[1].y)] = None
last = self.openlist.pop(len(self.openlist)-1)
if len(self.openlist) == 1:
return last
else:
self.openlist[1] = last
v = 1
while True:
u = v
# If there is two children
if (2*u)+1 < len(self.openlist):
if self.openlist[2*u].cost() <= self.openlist[u].cost():
v = 2*u
if self.openlist[(2*u)+1].cost() <= self.openlist[v].cost():
v = (2*u)+1
# If there is only one children
elif 2*u < len(self.openlist):
if self.openlist[2*u].cost() <= self.openlist[u].cost():
v = 2*u
# If at least one child is smaller, than parent, swap them
if u != v:
temp = self.openlist[u]
self.openlist[u] = self.openlist[v]
self.openlist[v] = temp
else:
break
return first
def iterate(self):
# If the open list is empty, exit the game
if len(self.openlist) == 1:
ika.Exit("no path found")
# Expand iteration by one
self.iterations += 1
# Make the current node the lowest cost
self.current = self.heap_remove()
# Add it to the closed list
self.closedlist[(self.current.x,self.current.y)] = True
# Are we there yet?
if self.current.equal(self.goal) == True:
# Target reached
self.goal = self.current
self.found = True
print self.iterations
else:
# Add the adjacent nodes, and check them
nodes_around = self.add_nodes(self.current)
for na in nodes_around:
if self.ignore(na,self.current) == False:
self.heap_add(na)
def iterateloop(self):
time1 = time.clock()
while 1:
# If the open list is empty, exit the game
if len(self.openlist) == 1:
ika.Exit("no path found")
# Expand iteration by one
self.iterations += 1
# Make the current node the lowest cost
self.current = self.heap_remove()
# Add it to the closed list
self.closedlist[(self.current.x,self.current.y)] = True
# Are we there yet?
if self.current.equal(self.goal) == True:
# Target reached
self.goal = self.current
self.found = True
print "Number of iterations"
print self.iterations
break
else:
# Add the adjacent nodes, and check them
nodes_around = self.add_nodes(self.current)
for na in nodes_around:
if self.ignore(na,self.current) == False:
self.heap_add(na)
time2 = time.clock()
time3 = time2-time1
print "Seconds to find path:"
print time3
class Map:
def __init__(self):
self.map_size_x = 20
self.map_size_y = 15
self.obstructed = {} # Library, containing x,y couples
self.start = [2*40,3*40]
self.unit = [16*40,8*40]
def is_obstacle(self,couple,x,y):
if (x >= self.map_size_x or x < 0) or (y >= self.map_size_y or y < 0):
return True
try:
if self.obstructed[(couple)] != None:
return True
except:
return False
def render_screen():
# Draw the Character
ika.Video.DrawRect(map.start[0],map.start[1],map.start[0]+40,map.start[1]+40,ika.RGB(40,200,10),1)
# Draw walls
for x in range(0,map.map_size_x):
for y in range(0,map.map_size_y):
if map.is_obstacle((x,y),x,y) == True:
ika.Video.DrawRect(x*40,y*40,(x*40)+40,(y*40)+40,ika.RGB(168,44,0),1)
# Draw openlist items
for node in path.openlist:
if node == None:
continue
x = node.x
y = node.y
ika.Video.DrawRect(x*40,y*40,(x*40)+40,(y*40)+40,ika.RGB(100,100,100,50),1)
# Draw closedlist items
for x in range(0,map.map_size_x):
for y in range(0,map.map_size_y):
try:
if path.closedlist[(x,y)] == True:
ika.Video.DrawRect(x*40,y*40,(x*40)+20,(y*40)+20,ika.RGB(0,0,255))
except:
pass
# Draw the current square
try:
ika.Video.DrawRect(path.current.x*40,path.current.y*40,(path.current.x*40)+40,(path.current.y*40)+40,ika.RGB(128,128,128), 1)
except:
pass
ika.Video.DrawRect(mouse_x.Position(),mouse_y.Position(),mouse_x.Position()+8,mouse_y.Position()+8,ika.RGB(128,128,128), 1)
# Draw the path, if reached
if path.found == True:
node = path.goal
while node.parent:
ika.Video.DrawRect(node.x*40,node.y*40,(node.x*40)+40,(node.y*40)+40,ika.RGB(40,200,200),1)
node = node.parent
# Draw the Target
ika.Video.DrawRect(map.unit[0],map.unit[1],map.unit[0]+40,map.unit[1]+40,ika.RGB(128,40,200),1)
def mainloop():
while 1:
render_screen()
if mouse_middle.Pressed():
# Iterate pathfinder
if path.found == False:
path.iterateloop()
elif mouse_right.Pressed():
# Iterate pathfinder by one
if path.found == False:
path.iterate()
elif ika.Input.keyboard["A"].Pressed():
# Iterate pathfinder
if path.found == False:
path.iterateloop()
elif ika.Input.keyboard["S"].Pressed():
# Iterate pathfinder by one
if path.found == False:
path.iterate()
elif mouse_left.Position():
# Add a square to the map, to be obstructed
if path.iterations == 0:
x = mouse_x.Position()
y = mouse_y.Position()
map.obstructed[(int(x/40),int(y/40))] = True
# Mouse preview
x = mouse_x.Position()
y = mouse_y.Position()
mx = int(x/40)*40
my = int(y/40)*40
ika.Video.DrawRect(mx,my,mx+40,my+40,ika.RGB(150,150,150,70),1)
ika.Video.ShowPage()
ika.Input.Update()
map = Map()
path = Emerald_Pathfinder()
path.setup(Node(map.start[0]/40,map.start[1]/40),Node(map.unit[0]/40,map.unit[1]/40))
mouse_middle = ika.Input.mouse.middle
mouse_right = ika.Input.mouse.right
mouse_left = ika.Input.mouse.left
mouse_x = ika.Input.mouse.x
mouse_y = ika.Input.mouse.y
# Initialize loop
mainloop()
I appreciate any help!
(sorry for any spelling mistakes, English is not my native language)
I think a proper implementation in python will be fast enough for your purposes. But the boost library has an astar implementation and python bindings. https://github.com/erwinvaneijk/bgl-python

Categories

Resources