So I am programming a checkers game, and the problem I am having is with creating several pieces in a loop. I have the class creation part of the code, which I won't post here, and the rest, which I'm posting, but I don't know how to change the variable the loop is about to use. If you can lend me a hand and clear this out, I would be thankful.
Sorry for posting my code as image, I'm new to this website ( and programming) and couldn't format so that the website would accept my post. I really hope it's ok for you guys to help me!
Thanks for the help!
Further clarification: I need to use a different "piece" creation everytime the loop runs. That means the first loop has to create piece1, then piece2, then piece3... and so forward
EDIT: Posting whole code. I know format is wrong, can't help it. So, hope somebody can fix it.
class Piece:
def __init__(self, kind, yposition, xposition):
self.color = kind
self.ypos = xposition
self.xpos = yposition
def getColor(self):
return self.getColor
def adjustY(self, change):
self.ypos = self.ypos + change
def adjustX(self, change):
self.xpos = self.xpos + change
def getY(self):
return self.ypos
def getX(self):
return self.xpos
def mover(self, direction):
self.direc = direction
if self.direc == "right" and self.color == "white":
for n in alist:
if n.getY == (self.getY - 1) and n.getX == (self.getX + 1):
pass
# NOT YET IMPLEMENTED
else:
self.adjustY(-1)
self.adjustX(+1)
elif self.direc == "left" and self.color == "white":
for n in alist:
if n.getY == (self.getY - 1) and n.getX == (self.getX - 1):
pass
# NOT YET IMPLEMENTED
else:
self.adjustY(-1)
self.adjustX(-1)
elif self.direc == "right" and self.color == "black":
for n in alist:
if n.getY == (self.getY + 1) and n.getX == (self.getX + 1):
pass
# NOT YET IMPLEMENTED
else:
self.adjustY(+1)
self.adjustX(+1)
else:
for n in alist:
if n.getY == (self.getY + 1) and n.getX == (self.getX - 1):
pass
# NOT YET IMPLEMENTED
else:
self.adjustY(+1)
self.adjustX(-1)
piece1 = 0
piece2 = 0
piece3 = 0
piece4 = 0
piece5 = 0
piece6 = 0
piece7 = 0
piece8 = 0
piece9 = 0
piece10 = 0
piece11 = 0
piece12 = 0
alistb1 = [piece1,piece2,piece3,piece4,piece5,piece6,piece7,piece8,piece9,piece10,piece11,piece12]
k = 2
for i in range(0,11):
if i >= 0 and i <5:
j = 8
m = 0
elif i >= 5 and i < 9:
j = 7
m = 1
else:
j = 6
m = 0
alistb1[i] = Piece("white",j,(m + 1 + i * k))
print(alistb1[i].getY())
# print(piece7.getY()) test reasons
PS: def mover is not ready yet.
You do not need to assign a variable for each piece. You are already using a list for your pieces. Instead of writing piece1, you can just write pieces[0]. (You do need to note that lists start with index 0.)
range has an exclusive right bound. This means that it is not included, your range ends with one less than that value. You want to use range(0,12).
In python, you can add to lists dynamically. You do not need to allocate enough spaces to fit your pieces. You can use the .append() method of lists.
One way to write your code now is this:
pieces = []
for i in range(0, 12): # 0-11
if i < 5:
pieces.append(Piece("white", 8, 1 + i*2))
elif i < 9:
pieces.append(Piece("white", 7, 2 + i*2))
else:
pieces.append(Piece("white", 6, 1 + i*2))
I took the liberty of simplifying your conditional statements (i will always be >= 0 and if i < 5 is false, then the inverse, i >= 5, is true, so you don't need to restate it in your elif) and getting rid of j, k, and m which are unnecessary variables and can be replaced with literals to save memory.
One more thing: your implementation of getColor will return the function object itself. I think you wanted to do:
def getColor():
return self.color
Use a dictionary and a for loop:
pieces = {}
# I'm assuming you want 12 pieces since your list has 12 pieces
for i in range(1,13): # range starts at m so, range(m,n) iterates from m up to n-1
# I would suggest using more descriptive variable names if you can, row or column for example
if i >= 0 and i <5:
j = 8
m = 0
elif i >= 5 and i < 9:
j = 7
m = 1
else:
j = 6
m = 0
pieces['piece{}'.format(i)] = Piece("white",j,(m + 1 + i * k))
This should do what you want unless I am misunderstanding you. Also this isn't C++ you don't need those get methods you can simply Piece.color to get the color attribute of a piece.
Use the dictionary to access the pieces, pieces['piece1'].whatever(). However for brevity's sake you don't need to pieces['piece{}.format(i)] you can just pieces[i] and the piece would be accessed pieces[1].whatever().
More info on dictionaries http://docs.python.org/3.3/tutorial/datastructures.html#dictionaries
What I have and it works with no errors:
class Piece:
def __init__(self, kind, yposition, xposition):
self.color = kind
self.ypos = xposition
self.xpos = yposition
def getColor(self):
return self.getColor
def adjustY(self, change):
self.ypos = self.ypos + change
def adjustX(self, change):
self.xpos = self.xpos + change
def getY(self):
return self.ypos
def getX(self):
return self.xpos
def mover(self, direction):
self.direc = direction
if self.direc == "right" and self.color == "white":
for n in alist:
if n.getY == (self.getY - 1) and n.getX == (self.getX + 1):
pass
# NOT YET IMPLEMENTED
else:
self.adjustY(-1)
self.adjustX(+1)
elif self.direc == "left" and self.color == "white":
for n in alist:
if n.getY == (self.getY - 1) and n.getX == (self.getX - 1):
pass
# NOT YET IMPLEMENTED
else:
self.adjustY(-1)
self.adjustX(-1)
elif self.direc == "right" and self.color == "black":
for n in alist:
if n.getY == (self.getY + 1) and n.getX == (self.getX + 1):
pass
# NOT YET IMPLEMENTED
else:
self.adjustY(+1)
self.adjustX(+1)
else:
for n in alist:
if n.getY == (self.getY + 1) and n.getX == (self.getX - 1):
pass
# NOT YET IMPLEMENTED
else:
self.adjustY(+1)
self.adjustX(-1)
k=2
pieces = {}
# I'm assuming you want 12 pieces since your list has 12 pieces
for i in range(1,13): # range starts at m so, range(m,n) iterates from m up to n-1
# I would suggest using more descriptive variable names if you can, row or column for example
if i >= 0 and i <5:
j = 8
m = 0
elif i >= 5 and i < 9:
j = 7
m = 1
else:
j = 6
m = 0
pieces['piece{}'.format(i)] = Piece("white",j,(m + 1 + i * k))
Output:
>>> pieces['piece1'].color
'white'
>>> pieces['piece3'].color
'white'
>>> pieces['piece3'].xpos
8
>>> for key in pieces:
print(key, pieces[key])
piece8 <__main__.Piece object at 0x000000000329A4E0>
piece9 <__main__.Piece object at 0x000000000329A550>
piece6 <__main__.Piece object at 0x000000000329A400>
piece7 <__main__.Piece object at 0x000000000329A470>
piece4 <__main__.Piece object at 0x000000000329A320>
piece5 <__main__.Piece object at 0x000000000329A390>
piece2 <__main__.Piece object at 0x0000000003287DA0>
piece3 <__main__.Piece object at 0x000000000329A2B0>
piece1 <__main__.Piece object at 0x00000000031D9CF8>
piece10 <__main__.Piece object at 0x000000000329A5C0>
piece11 <__main__.Piece object at 0x000000000329A630>
piece12 <__main__.Piece object at 0x000000000329A6A0>
>>> pieces['piece3'].mover('right')
Traceback (most recent call last):
File "<pyshell#13>", line 1, in <module>
pieces['piece3'].mover('right')
File "C:/Users/Hannah/Documents/thing.py", line 25, in mover
for n in alist:
NameError: global name 'alist' is not defined
>>> pieces['piece3'].xpos
8
>>> pieces['piece3'].adjustX(1)
>>> pieces['piece3'].xpos
9
Keep in mind dictionaries are unordered so the order they print in is arbitrary.
The traceback on mover is expected since I don't have alist in my version of the code. You will need to modify mover() to work with the dictionary. Some helpful ways to work with dicts:
>>> for n in pieces.values(): # iterates over the values in a dict
n.color
'white'
'white'
'white'
'white'
'white'
'white'
'white'
'white'
'white'
'white'
'white'
'white'
>>> for n in pieces.keys(): # iterates over the keys
print(n)
piece8
piece9
piece6
piece7
piece4
piece5
piece2
piece3
piece1
piece10
piece11
piece12
Related
I'm trying to solve the 15-Puzzle problem using IDA* algorithm and Manhattan heuristic.
I already implemented the algorithm from the pseudocode in this Wikipedia page (link).
Here's my code so far :
def IDA(initial_state, goal_state):
initial_node = Node(initial_state)
goal_node = Node(goal_state)
threshold = manhattan_heuristic(initial_state, goal_state)
path = [initial_node]
while 1:
tmp = search(path, goal_state, 0, threshold)
if tmp == True:
return path, threshold
elif tmp == float('inf'):
return False
else:
threshold = tmp
def search(path, goal_state, g, threshold):
node = path[-1]
f = g + manhattan_heuristic(node.state, goal_state)
if f > threshold:
return f
if np.array_equal(node.state, goal_state):
return True
minimum = float('inf')
for n in node.nextnodes():
if n not in path:
path.append(n)
tmp = search(path, goal_state, g + 1, threshold)
if tmp == True:
return True
if tmp < minimum:
minimum = tmp
path.pop()
return minimum
def manhattan_heuristic(state1, state2):
size = range(1, len(state1) ** 2)
distances = [count_distance(num, state1, state2) for num in size]
return sum(distances)
def count_distance(number, state1, state2):
position1 = np.where(state1 == number)
position2 = np.where(state2 == number)
return manhattan_distance(position1, position2)
def manhattan_distance(a, b):
return abs(b[0] - a[0]) + abs(b[1] - a[1])
class Node():
def __init__(self, state):
self.state = state
def nextnodes(self):
zero = np.where(self.state == 0)
y,x = zero
y = int(y)
x = int(x)
up = (y - 1, x)
down = (y + 1, x)
right = (y, x + 1)
left = (y, x - 1)
arr = []
for direction in (up, down, right, left):
if len(self.state) - 1 >= direction[0] >= 0 and len(self.state) - 1 >= direction[1] >= 0:
tmp = np.copy(self.state)
tmp[direction[0], direction[1]], tmp[zero] = tmp[zero], tmp[direction[0], direction[1]]
arr.append(Node(tmp))
return arr
I'm testing this code with a 3x3 Puzzle and here's the infinite loop! Due to the recursion I have some trouble testing my code...
I think the error might be here : tmp = search(path, goal_state, g + 1, threshold) (in the search function). I'm adding only one to the g cost value. It should be correct though, because I can only move a tile 1 place away.
Here's how to call the IDA() function:
initial_state = np.array([8 7 3],[4 1 2],[0 5 6])
goal_state = np.array([1 2 3],[8 0 4],[7 6 5])
IDA(initial_state, goal_state)
Can someone help me on this ?
There are couple of issues in your IDA* implementation. First, what is the purpose of the variable path? I found two purposes of path in your code:
Use as a flag/map to keep the board-states that is already been visited.
Use as a stack to manage recursion states.
But, it is not possible to do both of them by using a single data structure. So, the first modification that your code requires:
Fix-1: Pass current node as a parameter to the search method.
Fix-2: flag should be a data structure that can perform a not in query efficiently.
Now, fix-1 is easy as we can just pass the current visiting node as the parameter in the search method. For fix-2, we need to change the type of flag from list to set as:
list's average case complexity for x in s is: O(n)
set's
Average case complexity for x in s is: O(1)
Worst case complexity for x in s is: O(n)
You can check more details about performance for testing memberships: list vs sets for more details.
Now, to keep the Node information into a set, you need to implement __eq__ and __hash__ in your Node class. In the following, I have attached the modified code.
import timeit
import numpy as np
def IDA(initial_state, goal_state):
initial_node = Node(initial_state)
goal_node = Node(goal_state)
threshold = manhattan_heuristic(initial_state, goal_state)
#print("heuristic threshold: {}".format(threshold))
loop_counter = 0
while 1:
path = set([initial_node])
tmp = search(initial_node, goal_state, 0, threshold, path)
#print("tmp: {}".format(tmp))
if tmp == True:
return True, threshold
elif tmp == float('inf'):
return False, float('inf')
else:
threshold = tmp
def search(node, goal_state, g, threshold, path):
#print("node-state: {}".format(node.state))
f = g + manhattan_heuristic(node.state, goal_state)
if f > threshold:
return f
if np.array_equal(node.state, goal_state):
return True
minimum = float('inf')
for n in node.nextnodes():
if n not in path:
path.add(n)
tmp = search(n, goal_state, g + 1, threshold, path)
if tmp == True:
return True
if tmp < minimum:
minimum = tmp
return minimum
def manhattan_heuristic(state1, state2):
size = range(1, len(state1) ** 2)
distances = [count_distance(num, state1, state2) for num in size]
return sum(distances)
def count_distance(number, state1, state2):
position1 = np.where(state1 == number)
position2 = np.where(state2 == number)
return manhattan_distance(position1, position2)
def manhattan_distance(a, b):
return abs(b[0] - a[0]) + abs(b[1] - a[1])
class Node():
def __init__(self, state):
self.state = state
def __repr__(self):
return np.array_str(self.state.flatten())
def __hash__(self):
return hash(self.__repr__())
def __eq__(self, other):
return self.__hash__() == other.__hash__()
def nextnodes(self):
zero = np.where(self.state == 0)
y,x = zero
y = int(y)
x = int(x)
up = (y - 1, x)
down = (y + 1, x)
right = (y, x + 1)
left = (y, x - 1)
arr = []
for direction in (up, down, right, left):
if len(self.state) - 1 >= direction[0] >= 0 and len(self.state) - 1 >= direction[1] >= 0:
tmp = np.copy(self.state)
tmp[direction[0], direction[1]], tmp[zero] = tmp[zero], tmp[direction[0], direction[1]]
arr.append(Node(tmp))
return arr
initial_state = np.array([[8, 7, 3],[4, 1, 2],[0, 5, 6]])
goal_state = np.array([[1, 2, 3],[8, 0, 4],[7, 6, 5]])
start = timeit.default_timer()
is_found, th = IDA(initial_state, goal_state)
stop = timeit.default_timer()
print('Time: {} seconds'.format(stop - start))
if is_found is True:
print("Solution found with heuristic-upperbound: {}".format(th))
else:
print("Solution not found!")
Node: Please double check your Node.nextnodes() and manhattan_heuristic() methods as I did not pay much attention in those areas. You can check this GitHub repository for other algorithmic implementations (i.e., A*, IDS, DLS) to solve this problem.
References:
Python Wiki: Time Complexity
TechnoBeans: Performance for testing memberships: list vs tuples vs sets
GitHub: Puzzle Solver (by using problem solving techniques)
My tests of my implementations of Dijkstra and A-Star have revealed that my A-star implementation is approximately 2 times SLOWER. Usually equivalent implementations of Dijkstra and A-star should see A-star beating out Dijkstra. But that isn't the case here and so it has led me to question my implementation of A-star. So I want someone to tell me what I am doing wrong in my implementation of A-star.
Here is my code:
from copy import deepcopy
from math import inf, sqrt
import maze_builderV2 as mb
if __name__ == '__main__':
order = 10
space = ['X']+['_' for x in range(order)]+['X']
maze = [deepcopy(space) for x in range(order)]
maze.append(['X' for x in range(order+2)])
maze.insert(0, ['X' for x in range(order+2)])
finalpos = (order, order)
pos = (1, 1)
maze[pos[0]][pos[1]] = 'S' # Initializing a start position
maze[finalpos[0]][finalpos[1]] = 'O' # Initializing a end position
mb.mazebuilder(maze=maze)
def spit():
for x in maze:
print(x)
spit()
print()
mazemap = {}
def scan(): # Converts raw map/maze into a suitable datastructure.
for x in range(1, order+1):
for y in range(1, order+1):
mazemap[(x, y)] = {}
t = [(x-1, y), (x+1, y), (x, y-1), (x, y+1)]
for z in t:
if maze[z[0]][z[1]] == 'X':
pass
else:
mazemap[(x, y)][z] = [sqrt((pos[0]-z[0])**2+(pos[1]-z[1])**2),
sqrt((finalpos[0]-z[0])**2+(finalpos[1]-z[1])**2)] # Euclidean distance to destination (Heuristic)
scan()
unvisited = deepcopy(mazemap)
distances = {}
paths = {}
# Initialization of distances:
for node in unvisited:
if node == pos:
distances[node] = [0, sqrt((finalpos[0]-node[0])**2+(finalpos[1]-node[1])**2)]
else:
distances[node] = [inf, inf]
while unvisited != {}:
curnode = None
for node in unvisited:
if curnode == None:
curnode = node
elif (distances[node][0]+distances[node][1]) < (distances[curnode][0]+distances[curnode][1]):
curnode = node
else:
pass
for childnode, lengths in mazemap[curnode].items():
# Length to nearby childnode - G length, Euclidean (Heuristic) length from curnode to finalpos - H length
# G length + H length < Euclidean length to reach that childnode directly + Euclidean length to finalpos from that childnode = Better path found, update known distance and paths
if lengths[0] + lengths[1] < distances[childnode][0] + distances[childnode][1]:
distances[childnode] = [lengths[0], lengths[1]]
paths[childnode] = curnode
unvisited.pop(curnode)
def shortestroute(paths, start, end):
shortestpath = []
try:
def rec(start, end):
if end == start:
shortestpath.append(end)
return shortestpath[::-1]
else:
shortestpath.append(end)
return rec(start, paths[end])
return rec(start, end)
except KeyError:
return False
finalpath = shortestroute(paths, pos, finalpos)
if finalpath:
for x in finalpath:
if x == pos or x == finalpos:
pass
else:
maze[x[0]][x[1]] = 'W'
else:
print("This maze not solvable, Blyat!")
print()
spit()
For those who find my code too messy and can't bother to read the comments I added to help with the reading... Here is a gist of my code:
Creates a mazemap (all the coordinates and its connected neighbors along with their euclidean distances from that neighboring point to the start position (G Cost) as well as to the final position (H Cost)... in a dictionary)
start position is selected as the current node. All distances to other nodes is initialised as infinity.
For every node we compare the total path cost i.e is the G cost + H cost. The one with least total cost is selected as then next current node. Each time we select new current node, we add that node to a dictionary that keeps track of through which node it was reached, so that it is easier to backtrack and find our path.
Process continues until current node is the final position.
If anyone can help me out on this, that would be great!
EDIT: On account of people asking for the maze building algorithm, here it is:
# Maze generator - v2: Generates mazes that look like city streets (more or less...)
from copy import deepcopy
from random import randint, choice
if __name__ == "__main__":
order = 10
space = ['X']+['_' for x in range(order)]+['X']
maze = [deepcopy(space) for x in range(order)]
maze.append(['X' for x in range(order+2)])
maze.insert(0, ['X' for x in range(order+2)])
pos = (1, 1)
finalpos = (order, order)
maze[pos[0]][pos[1]] = 'S' # Initializing a start position
maze[finalpos[1]][finalpos[1]] = 'O' # Initializing a end position
def spit():
for x in maze:
print(x)
blocks = []
freespaces = [(x, y) for x in range(1, order+1) for y in range(1, order+1)]
def blockbuilder(kind):
param1 = param2 = 0
double = randint(0, 1)
if kind == 0:
param2 = randint(3, 5)
if double:
param1 = 2
else:
param1 = 1
else:
param1 = randint(3, 5)
if double:
param2 = 2
else:
param2 = 1
for a in range(blockstarter[0], blockstarter[0]+param2):
for b in range(blockstarter[1], blockstarter[1]+param1):
if (a+1, b) in blocks or (a-1, b) in blocks or (a, b+1) in blocks or (a, b-1) in blocks or (a, b) in blocks or (a+1, b+1) in blocks or (a-1, b+1) in blocks or (a+1, b-1) in blocks or (a-1, b-1) in blocks:
pass
else:
if a > order+1 or b > order+1:
pass
else:
if maze[a][b] == 'X':
blocks.append((a, b))
else:
spaces = [(a+1, b), (a-1, b), (a, b+1), (a, b-1)]
for c in spaces:
if maze[c[0]][c[1]] == 'X':
break
else:
maze[a][b] = 'X'
blocks.append((a, b))
for x in range(1, order+1):
for y in range(1, order+1):
if (x, y) in freespaces:
t = [(x+1, y), (x-1, y), (x, y+1), (x, y-1)]
i = 0
while i < len(t):
if maze[t[i][0]][t[i][1]] == 'X' or (t[i][0], t[i][1]) == pos or (t[i][0], t[i][1]) == finalpos:
del t[i]
else:
i += 1
if len(t) > 2:
blockstarter = t[randint(0, len(t)-1)]
kind = randint(0, 1) # 0 - vertical, 1 - horizontal
blockbuilder(kind)
else:
pass
# rch = choice(['d', 'u', 'r', 'l'])
b = 0
while b < len(blocks):
block = blocks[b]
t = {'d': (block[0]+2, block[1]), 'u': (block[0]-2, block[1]),
'r': (block[0], block[1]+2), 'l': (block[0], block[1]-2)}
rch = choice(['d', 'u', 'r', 'l'])
z = t[rch]
# if z[0] > order+1 or z[1] > order+1 or z[0] < 1 or z[1] < 1:
# Decreased chance of having non solvable maze being generated...
if z[0] > order-2 or z[1] > order-2 or z[0] < 2+2 or z[1] < 2+2:
pass
else:
if maze[z[0]][z[1]] == 'X':
if randint(0, 1):
set = None
if rch == 'u':
set = (z[0]+1, z[1])
elif rch == 'd':
set = (z[0]-1, z[1])
elif rch == 'r':
set = (z[0], z[1]-1)
elif rch == 'l':
set = (z[0], z[1]+1)
else:
pass
if maze[set[0]][set[1]] == '_':
# Checks so that no walls that block the entire way are formed
# Makes sure maze is solvable
sets, count = [
(set[0]+1, set[1]), (set[0]-1, set[1]), (set[0], set[1]+1), (set[0], set[1]-1)], 0
for blyat in sets:
while blyat[0] != 0 and blyat[1] != 0 and blyat[0] != order+1 and blyat[1] != order+1:
ch = [(blyat[0]+1, blyat[1]), (blyat[0]-1, blyat[1]),
(blyat[0], blyat[1]+1), (blyat[0], blyat[1]-1)]
suka = []
for i in ch:
if ch not in suka:
if maze[i[0]][i[1]] == 'X':
blyat = i
break
else:
pass
suka.append(ch)
else:
pass
else:
blyat = None
if blyat == None:
break
else:
pass
else:
count += 1
if count < 1:
maze[set[0]][set[1]] = 'X'
blocks.append(set)
else:
pass
else:
pass
else:
pass
b += 1
mazebuilder(maze, order)
spit()
Sorry for leaving this out!
Just at a quick glance, it looks like you don't have a closed set at all?? Your unvisited structure appears to contain every node in the map. This algorithm is not A* at all.
Once you fix that, make sure to change unvisited from a list to a priority queue also.
I am making a puzzle game with Python, it's supposed to be a 3x3 slide puzzle. I've made a class that stores name/id and x,y coordinates.
In the function up() I want to move the asterisk (*) up one row and in theory it should work but apparently not. I have a function that finds the class instance associated with two coordinates (find()) and it returns an instance of the class called symbol.
Layout of the slide puzzle:
1 2 3
4 5 6
7 8 *
When I try to assign the new values to the class instance above me (represented by the 6) nothing gets assigned. I would like to use the global keyword but that seems like such a hassle because then I would have to check every coordinate and then say what variable to change.
This is a puzzle game so it's really important that the player can move around without issues. I haven't tried anything else since I don't know how to proceed from this point.
class symbol:
def __init__(self, x, y, val):
self.x = x
self.y = y
self.val = val
s1 = symbol(1, 1, "1")
s2 = symbol(2, 1, "2")
s3 = symbol(3, 1, "3")
s4 = symbol(1, 2, "4")
s5 = symbol(2, 2, "5")
s6 = symbol(3, 2, "6")
s7 = symbol(1, 3, "7")
s8 = symbol(2, 3, "8")
s9 = symbol(3, 3, "*")
def getPos():
xPos = 0
yPos = 0
for element in symbols:
if element.val == "*":
xPos = element.x
yPos = element.y
else:
pass
return xPos, yPos
def find(x, y):
xPos = 0
yPos = 0
found = symbol(100, 100, "FIND")
for element in symbols:
if element.x == x and element.y == y:
found.x = x
found.y = y
found.val = element.val
else:
pass
return found
def up():
x, y = getPos()
if y > 1:
newBlock = find(x, y-1)
myBlock = find(x, y)
myBlock.val = newBlock.val
newBlock.val = "*"
clear()
draw()
print(f"myBlock: {myBlock.val} ({myBlock.x}, {myBlock.y}), newBlock: {newBlock.val} ({newBlock.x}, {newBlock.y})")
else:
pass
#cannot go higher
The problem is that your find function is making a new symbol and returning that. When up() modifies the result of find(), it's just changing some copy that lives outside of (what I assume is) your list of symbols.
One solution would be to just return the element from the list in find. i.e.
for element in symbols:
if element.x == x and element.y == y:
return element
return None
I'm currently working on making a Game of Life program (UNI related, beginner course), by using nested lists.
However I can't seem to get the update() method to work properly, I've no clue what's wrong. The generation of the first board is okay, but the update leaves only the cornercells alive, and the rest dead.
All methodcalls in this class originates from other .py files, which works well.
from random import randint
from cell import *
class Spillebrett:
def __init__(self, rows, columns):
self.genNumber = 0
self._rows = rows
self._columns = columns
self._grid = []
for i in range(self._rows):
self._grid.append([])
for j in range(self._columns):
self._grid[i].append(cell())
self.generate()
def drawBoard(self):
for i in self._grid:
print(" ".join(map(str, i)))
print()
#Method updates genNumber, checks if cells are alive or dead and updates the board accordingly
#Currently only yield board with corner-cells alive
def updateBoard(self):
self.genNumber += 1
toLive = []
toDie = []
for x, row in enumerate(self._grid):
for y, cell in enumerate(rad):
if cell.areAlive() is True:
counter = len(self.findNeighbour(x, y))
if counter < 2 or counter > 3:
toDie.append(cell)
elif counter == 2 or counter == 3:
toLive.append(cell)
elif cell.areAlive() is False:
counter = len(self.findNeighbour(x, y))
if counter == 3:
toLive.append(cell)
for i in toDie:
i.setDead()
for i in toLive:
i.setAlive()
return self.genNumber
#Code given by Uni
def generate(self):
for i in range(self._rows):
for j in range(self._columns):
rand = randint(0, 3)
if rand == 3:
self._grid[i][j].setAlive()
#Code given by Uni
def findNeighbour(self, row, column):
neighbourList = []
for i in range(-1, 2):
for j in range(-1, 2):
neighbourRow = rad + i
neighbourcolumn = column + j
if(neighbourRow == rad and neighbourcolumn == column) is not True:
if(neighbourRow < 0 or neighbourcolumn < 0 or neighbourRow >
self._rows - 1 or neighbourcolumn > self._columns - 1) is not True:
neighbourList.append(self._grid[neighbourRow][neighbourcolumn])
return neighbourList
def findAllAlive(self):
self._alive = 0
for i in range(self._rows):
for j in range(self._columns):
if self._grid[i][j].areAlive() is True:
self._alive += 1
return self._alive
my code below
I have a little knight's tour problem I'm trying to solve: find the smallest number of moves from point A to point B on an N*N chess board.
I created a board, and used a simple algorithm:
1. add point A to candidate list and start loop:
2. pop first element in candidate list and check it:
3. if end - return counter
4. else - add the candidate 's "sons" to end of candidate list
5. go to step 2 (counter is incremented after all previous level sons are popped)
This algorithm works as I expected (used it on a few test cases), but it was very slow:
The call f = Find_route(20, Tile(4,4), Tile(14,11)) (20 is the board dimensions, Tile(4,4) and Tile(14,11) are the start & end positions, respectively) checked 201590 (!!) tiles before reaching the answer.
I tried optimizing it by sorting the candidates list with sorted(tiles, key = lambda e : abs(e.x - end.x)+abs(e.y - end.y)) where tiles is the candidates list. That works for some of the cases but for some it is kind of useless.
helpful cases:
f = Find_route(20, Tile(1,4), Tile(1,10)) from 459 to 309 (~33% !!)
f = Find_route(20, Tile(7,0), Tile(1,11)) from 87738 to 79524 (~10% :( )
unhelpful cases:
f = Find_route(20, Tile(4,4), Tile(14,11)): from 201891 to 201590
f = Find_route(20, Tile(1,4), Tile(1,11)) from 2134 to 2111
I want eventually to have a list of near-end cases, from which the algorithm would know exactly what to do, (something like a 5 tiles radius), and I think that could help, but I am more interested in how to improve my optimize_list method. Any tips?
Code
class Tile(object):
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
tmp = '({0},{1})'.format(self.x, self.y)
return tmp
def __eq__(self, new):
return self.x == new.x and self.y == new.y
def get_horse_jumps(self, max_x , max_y):
l = [(1,2), (1,-2), (-1,2), (-1,-2), (2,1), (2,-1), (-2,1), (-2,-1)]
return [Tile(self.x + i[0], self.y + i[1]) for i in l if (self.x + i[0]) >= 0 and (self.y + i[1]) >= 0 and (self.x + i[0]) < max_x and (self.y + i[1]) < max_y]
class Board(object):
def __init__(self, n):
self.dimension = n
self.mat = [Tile(x,y) for y in range(n) for x in range(n)]
def show_board(self):
print('-'*20, 'board', '-'*20)
n = self.dimension
s = ''
for i in range(n):
for j in range(n):
s += self.mat[i*n + j].__str__()
s += '\n'
print(s,end = '')
print('-'*20, 'board', '-'*20)
class Find_route(Board):
def __init__(self, n, start, end):
super(Find_route, self).__init__(n)
#self.show_board()
self.start = start
self.end = end
def optimize_list(self, tiles, end):
return sorted(tiles, key = lambda e : abs(e.x - end.x)+abs(e.y - end.y))
def find_shortest_path(self, optimize = False):
counter = 0
sons = [self.start]
next_lvl = []
num_of_checked = 0
while True:
curr = sons.pop(0)
num_of_checked += 1
if curr == self.end:
print('checked: ', num_of_checked)
return counter
else: # check sons
next_lvl += curr.get_horse_jumps(self.dimension, self.dimension)
# sons <- next_lvl (optimize?)
# next_lvl <- []
if sons == []:
counter += 1
if optimize:
sons = self.optimize_list(next_lvl, self.end)
else:
sons = next_lvl
next_lvl = []
optimize = True
f = Find_route(20, Tile(7,0), Tile(1,11))
print(f.find_shortest_path(optimize))
print(f.find_shortest_path())
EDIT
I added another optimization level - optimize list at any insertion of new candidate tiles, and it seems to work like a charm, for some cases:
if optimize == 2:
if sons == []:
#counter += 1
sons = self.optimize_list(next_lvl, self.end)
else:
sons = self.optimize_list(sons + next_lvl, self.end)
else:
if sons == []:
counter += 1
if optimize == 1:
sons = self.optimize_list(next_lvl, self.end)
else:
sons = next_lvl
next_lvl = []
optimize = 2
f = Find_route(20, Tile(1,4), Tile(8,18)) # from 103761 to 8 ( optimal!!! )
print(f.find_shortest_path(optimize))
print(f.find_shortest_path())
I have a problem with calculating the number-of-jumps because I don't know when to increment the counter (maybe at each check?), but it seems to at least converge faster. Also, for other cases (e.g. f = Find_route(20, Tile(1,4), Tile(8,17))) it does not improve at all (not sure if it stops...)
Don't reinvent the wheel.
Build a graph with tiles as vertices. Connect tiles with an edge if a knight can get from one tile to another in one step.
Use a standard path finding algorithm. The breadth-first search looks like the best option in you're looking for a shortest path in an unweighted graph.