Python - Conway's Game Of Life - Wont update to next cycle - python

I'm new to python, coming from Java and C#. I have a working implementation in Java, but when I tried writing it in Python the grid will not update to the next cycle. I don't think the issues is with the deepcopy. Could it be reassignment of array elements?
import copy
liveSymbol = "X"
deadSymbol = "-"
class Cell:
def __init__(self, live, symbol, row, column):
self.live = live
self.symbol = symbol
self.row = row
self.column = column
def __repr__(self):
return repr(self.symbol)
class Model:
def __init__(self, rows, columns):
self.grid = []
self.nextGrid = []
self.rows = rows
self.columns = columns
self.initGrids()
self.addPatternToGrid()
def initGrids(self):
for i in range(self.rows):
self.grid.append([])
self.nextGrid.append([])
for j in range(self.columns):
cell = Cell(False, deadSymbol, i, j)
self.grid[i].append(cell)
self.nextGrid[i].append(cell)
def addPatternToGrid(self):
self.grid[1][2] = Cell(True, liveSymbol, 1, 2)
self.grid[2][2] = Cell(True, liveSymbol, 2, 2)
self.grid[3][2] = Cell(True, liveSymbol, 3, 2)
def printGrid(self):
print("GRID")
print("\n______________________", end=" ")
for i in range(self.rows):
print("")
for j in range(self.columns):
print(self.grid[i][j], end=" ")
def printNextGrid(self):
print("Next Grid")
print("\n______________________", end=" ")
for i in range(self.rows):
print("")
for j in range(self.columns):
print(self.grid[i][j], end=" ")
def start(self):
for i in range(self.rows):
for j in range(self.columns):
willLive = self.aliveNextCycle(self.grid[i][j])
symbol = self.getSymbol(willLive)
self.nextGrid[i][j] = Cell(willLive, symbol, i, j)
print("Before update grid to next cycle")
self.printGrid()
self.printNextGrid()
self.updateGridToNextCycle()
print("After update grid to next cycle")
self.printGrid()
self.printNextGrid()
def getSymbol(self, isLive):
return liveSymbol if True else deadSymbol
def aliveNextCycle(self, cell):
liveNeighbors = self.countTheLiveNeighbors(cell)
if cell.live:
live = not (liveNeighbors < 2 or liveNeighbors > 3)
return not live
else:
lives = liveNeighbors==3
return lives
def updateGridToNextCycle(self):
# for i in range(self.rows):
# for j in range(self.columns):
# self.grid[i][j] = self.nextGrid[i][j]
self.grid = copy.deepcopy(self.nextGrid)
def countTheLiveNeighbors(self, cell):
count = 0
# Directly Left
if cell.column != 0:
if self.grid[cell.row][cell.column - 1].live:
count += 1
# Upper Left
if cell.row != 0 and cell.column != 0:
if self.grid[cell.row - 1][cell.column - 1].live:
count += 1
# Directly above
if cell.row != 0:
if self.grid[cell.row - 1][cell.column].live:
count += 1
# Above right
if cell.row != 0 and cell.column != self.columns - 1:
if self.grid[cell.row - 1][cell.column + 1].live:
count += 1
# Directly right
if cell.column != self.columns - 1:
if self.grid[cell.row][cell.column + 1].live:
count += 1
# Right under
if cell.row != self.rows - 1 and cell.column != self.columns - 1:
if self.grid[cell.row + 1][cell.column + 1].live:
count += 1
# Directly beneath
if cell.row != self.rows - 1:
if self.grid[cell.row + 1][cell.column].live:
count += 1
# Down left
if cell.row != self.rows - 1 and cell.column != 0:
if self.grid[cell.row + 1][cell.column - 1].live:
count += 1
return count
model = Model(5, 5)
model.start()
Below is the output
Before update grid to next cycle
GRID
______________________
'-' '-' '-' '-' '-'
'-' '-' 'X' '-' '-'
'-' '-' 'X' '-' '-'
'-' '-' 'X' '-' '-'
'-' '-' '-' '-' '-' Next Grid
______________________
'-' '-' '-' '-' '-'
'-' '-' 'X' '-' '-'
'-' '-' 'X' '-' '-'
'-' '-' 'X' '-' '-'
'-' '-' '-' '-' '-' After update grid to next cycle
GRID
______________________
'X' 'X' 'X' 'X' 'X'
'X' 'X' 'X' 'X' 'X'
'X' 'X' 'X' 'X' 'X'
'X' 'X' 'X' 'X' 'X'
'X' 'X' 'X' 'X' 'X' Next Grid
______________________
'X' 'X' 'X' 'X' 'X'
'X' 'X' 'X' 'X' 'X'
'X' 'X' 'X' 'X' 'X'
'X' 'X' 'X' 'X' 'X'
'X' 'X' 'X' 'X' 'X'

I see several problems here.
First, your printNextGrid() function is exactly the same as printGrid(); it prints data from self.grid instead of self.nextGrid. Your use of copy.deepcopy is perfectly fine.
Second, you're embedding too much information in the Cell object. Each cell only needs to track its own state; it doesn't need to also separately store the symbol used for its current state and its location. (Truthfully, it's not clear this needs to be an object at all; I'd strongly recommend that you store True and False in the grid instead of objects. This will make your code much simpler.)
Critically, though, your implementations of getSymbol() and aliveNextCycle() are incorrect. First:
def getSymbol(self, isLive):
return liveSymbol if True else deadSymbol
^^^^^^^
True is always true. The condition you want here is isLive.
Second:
def aliveNextCycle(self, cell):
liveNeighbors = self.countTheLiveNeighbors(cell)
if cell.live:
live = not (liveNeighbors < 2 or liveNeighbors > 3)
return not live
else:
lives = liveNeighbors==3
return lives
The logic for the cell.live case is excessively convoluted, and ends up inverting the conditions for survival, causing incorrect behavior. A much simpler (and correct) implementation would be:
def aliveNextCycle(self, cell):
liveNeighbors = self.countTheLiveNeighbors(cell)
if liveNeighbors == 3: return True
if liveNeighbors == 2: return cell.live
return False

Related

If a certain letter is included in a string then its coordinate will increase or decrease

The program should ask what its first coordinates are. Then movement is asked and its answer should be with
r,R,l,L,u,U,d,D.
Coordinate are input with a comma (x,y), and then again if 'u','d','l','r' are input then the x or y axis will increase or decrease.
'u' and 'd' for the y-axis; 'r' and 'l' for the x-axis.
def program_for_grid(x,y,where):
if where == 'r' or where == 'R':
# move x upp +
for r in range(len(where)):
r == 'r' or r == 'R'
x + 1
elif where == 'l' or where == 'L':
# move x down -
for l in range(len(where)):
l == 'l' or l == 'L'
x - 1
elif where == 'u' or where == 'U':
# move y up +
for u in range(len(where)):
u == 'u' or u == 'U'
y + 1
elif where == 'd' or where == 'D':
# move y down -
for d in range(len(where)):
d == 'd' or d == 'D'
y - 1
x_axis,y_axis = input('Initial position: ').split(',')
int_x_axix = int(x_axis)
int_y_axix = int(x_axis)
movement = input('Movement: ')
grid = program_for_grid(int_x_axix,int_y_axix,movement)
print(grid)
Something like this?
def delta(movement):
"""
Parse the string for movement instructions and
return resulting relative coordinates
"""
x = 0
y = 0
for letter in movement.lower():
if letter == "d": y -= 1
elif letter == "u": y += 1
elif letter == "l": x -= 1
elif letter == "r": x += 1
else:
print("warning: skipping undefined", letter)
return x, y
x_axis,y_axis = input('Initial position: ').split(',')
int_x_axix = int(x_axis)
int_y_axix = int(y_axis)
while True:
movement = input('Movement: ')
xdelta, ydelta = delta(movement)
int_x_axix += xdelta
int_y_axix += ydelta
print(f"resulting position: ({int_x_axix},{int_y_axix})")

how can i get the grid representation of the integer

I need to create a method set_from_integer(self, integer_encoding) that sets the colours of all points of the board according to the given integer_encoding which does not change the size of the board. To convert an integer n to ternary representation, as required by set_from_integer(), get the last digit by computing n mod 3. You get the next digit by computing [n/3]mod 3 and so on. For example, to get the last two digits of the ternary representation of 7473:
#This is the problematic part
def set_from_integer(self, integer_encoding):
n = int(integer_encoding)
list1 = []
while n != 0:
rem = n % 3
list1.append(rem)
n = n // 3
list1.reverse()
for i in list1:
print(i, end="")
So for now the above is what i came up with to get the ternary base of the decimal number but i need to convert this into a grid of symbols according to the given :
(empty = 0, white = 1, black = 2)
My entire code is as follows:
def load_board(filename):
result = " "
with open(filename) as f:
print(f)
for index, line in enumerate(f):
if index == 0:
result += ' '+' '.join([chr(alphabets + 65) for alphabets in range(len(line) - 1)]) + '\n' # the alphabetical column heading
result += f"{19 - index:2d}"
result += ' ' + ' '.join(line.strip()) + '\n'
return result
def save_board(filename, board):
with open(filename, "wt") as f:
f.write(board)
from string import ascii_uppercase as letters
class Board:
#Dictionary created for the colours and the respected symbols
points = {'E': '.', 'B': '#', 'W': 'O'}
#Constructor
def __init__(self,size=19,from_strings=None):
assert 2 <= size <= 26, "Illegal board size: must be between 2 and 26."
if from_strings != None:
if type(from_strings) != list:
raise AssertionError("input is not a list")
if len(from_strings) != size:
raise AssertionError("length of input list does not match size")
for i in range(size):
if type(from_strings[i]) != str:
raise AssertionError("row " + str(i) + " is not a string")
if len(from_strings[i]) != size:
raise AssertionError("length of row " + str(i) + " does not match size")
for j in range(size):
if from_strings[i][j] not in ".#O":
raise AssertionError("invalid character in row " + str(i))
self.size = size
self.grid = [['E'] * size for _ in range(size)]
self.from_strings = [] if from_strings is None else from_strings
def get_size(self): #Returns the size of the grid created by the constructor
return self.size
def __str__(self): # creating the grid
padding = ' ' # Creating a variable with a space assigned so that it acts as a padding to the rows that have a single digit
heading = ' ' + ' '.join(letters[:self.size]) # Alphabetical heading is created
lines = [heading] # adding the alphabetical heading into a list named lines to which the rows will be added later
for r, row in enumerate(self.grid):
if len(self.grid) < 10: # for the grid with a size less than 10 to add the space to the start of the row for the single digits to be aligned
if (self.from_strings):
line = " " + f'{self.size - r} ' + ' '.join(self.from_strings[r])
else:
line = " " + f'{self.size - r} ' + ' '.join(self.points[x] for x in row)
lines.append(line)
else: # for the grids that are larger than 9
if r > 9: # for rows 1 to 9 the single digits are aligned according to the first digit from the right of the two digit rows
if (self.from_strings):
line = f'{self.size - r} ' + ' '.join(self.from_strings[r])
else:
line = f'{self.size - r} ' + ' '.join(self.points[x] for x in row)
line = padding + line # adding the space using the variable padding to the row created
lines.append(line) # adding the row to the list of rows
else: # for the rows 10 onwards - as there is no requirement to add a padding it is not added here
if (self.from_strings):
line = f'{self.size - r} ' + ' '.join(self.from_strings[r])
else:
line = f'{self.size - r} ' + ' '.join(self.points[x] for x in row) # creation of the row
lines.append(line) # adding the newly created row to the list of rows
return '\n'.join(lines)
def _to_row_and_column(self, coords):
# destructure coordinates like "B2" to "B" and 2
alpha, num = coords
colnum = ord(alpha) - ord('A') + 1
rownum = self.size - int(num) + 1
assert 1 <= rownum <= self.size,"row out of range"
assert 1 <= colnum <= self.size,'column out of range'
return rownum, colnum
def set_colour(self, coords, colour_name):
rownum, colnum = self._to_row_and_column(coords)
assert len(coords)==2 or len(coords)==3, "invalid coordinates"
assert colour_name in self.points,"invalid colour name"
self.grid[rownum - 1][colnum - 1] = colour_name
def get_colour(self, coords):
rownum, colnum = self._to_row_and_column(coords)
return self.grid[rownum - 1][colnum - 1]
def to_strings(self):
padding = ' '
lines = []
for r, row in enumerate(self.grid):
if self.from_strings:
lines.append(''.join(self.from_strings[r]))
else:
lines.append(''.join(self.points[x] for x in row))
return lines
def to_integer(self):
digit_colour=""
for line in self.to_strings():
for character in line:
if character=='.':
character=0
elif character=='O':
character=1
elif character=='#':
character=2
character=str(character)
digit_colour+=character
return int(digit_colour,3)
# return ''.join(self.to_int[x] for line in self.grid for x in line)
def set_from_integer(self, integer_encoding):
n = int(integer_encoding)
list1 = []
while n != 0:
rem = n % 3
list1.append(rem)
n = n // 3
list1.reverse()
for i in list1:
print(i, end="")
print(type(list1))
print(list1)
for i in range(self.size):
for j in range(self.size):
if list1[i * self.size + j] == "0":
self.grid[i][j] = "."
elif list1[i * self.size + j] == "1":
self.grid[i][j] = "#"
elif list1[i * self.size + j] == "2":
self.grid[i][j] = "O"
c = Board(3)
c.set_from_integer(14676)
print(c)
d = Board(5)
d.set_from_integer(1)
print(d)
and the expected output is shown below:
A B C
3 # . #
2 . O .
1 O # .
A B C D E
5 . . . . .
4 . . . . .
3 . . . . .
2 . . . . .
1 . . . . O

my pop( ) function does not delete the top element of a list, but rather a random one

I have created the following pop function:
def pop(arr):
if not isempty(arr):
topelement = arr[len(arr)-1]
arr.remove(topelement)
return topelement
And it worked correctly until I used it in order to reverse order of a stack of numbers and operators:
"3 6 2 + * 14 3 4 + + /"
into
"/ + + 4 3 14 * + 2 6 3".
In first iteration of while loop shown below it pushed "/" operator to the auxiliary stack and deleted it from the top of entry, which is OK - but in the second iteration it deleted "+" sign from the middle of the entry instead of the plus sign with the highest index.
def evaluation(eq):
entryunsplit = errpostfix(eq)
entry = entryunsplit.split()
lengthofentry = len(entry)
stack = []
while len(stack) != lengthofentry:
push(stack,pop(entry))
Could someone please explain me why didn't it delete the last element and how can I avoid that error?
I am putting the whole code below in case some other element turns out to be significant.
stack1 = []
max_size = 30
def push(arr,a):
if len(arr) < max_size:
arr.append(a)
def isempty(arr):
if len(arr) == 0:
return True
else:
return False
def top(arr):
if not isempty(arr):
return arr[len(arr)-1]
def pop(arr):
if not isempty(arr):
topelement = arr[len(arr)-1]
arr.remove(topelement)
return topelement
def errpostfix(eq):
entry = eq.split()
opstack = []
exit = []
priorities = { "+": 1, "-": 1, "*": 0, "/": 0 }
for token in entry:
if token == "(":
push(opstack,token)
elif token == "*" or token == "/" or token == "+" or token == "-":
if top(opstack) == "*" or top(opstack) == "/" or top(opstack) == "+" or top(opstack) == "-":
while not isempty(opstack) and priorities[top(opstack)] >= priorities[token]:
push(exit,pop(opstack))
isempty(opstack)
push(opstack,token)
elif token == ")":
while top(opstack) != "(":
push(exit,pop(opstack))
pop(opstack)
else:
push(exit,token)
while not isempty(opstack):
push(exit, pop(opstack))
output = " ".join(exit)
return output
def isop(ch):
if ch == "+" or ch == "-" or ch == "*" or ch == "/":
return True
else:
return False
def evaluation(eq):
entryunsplit = "3 6 2 + * 14 3 4 + + /"
entry = entryunsplit.split()
lengthofentry = len(entry)
stack = []
while len(stack) != lengthofentry:
push(stack,pop(entry))
The remove() operation on a list will delete the first occurrence of an item in that list, is not "random" (check the docs). If there are several repeated elements, the first one encountered will be the one that gets deleted. To delete the last element, simply use the built-in pop() method:
def pop(arr):
if not isempty(arr):
topelement = arr[-1]
arr.pop()
return topelement

Recursive Path finding error

I'm working on an exercise where given a set of connections between two points (ie. 12 is a connection between 1 and 2 ect.). I decided to tackle the approach recursively in order to have it systematically check every path and return when it finds one that hits every node and starts and ends with one.
However upon debugging this it seems that as I pass down the adjMatrix further into the recursion it's also editing the upper levels and causing it not to search any further as it goes back up the tree. I think it has something to when I set newMatrix = adjMatrix, but I'm not exactly sure.
def checkio(teleports_string):
#return any route from 1 to 1 over all points
firstnode, secondnode, size = 0, 0, 8
#Makes the adjacency matrix
adjMatrix = [[0 for i in range(size)] for j in range(size)]
for x in teleports_string:
#Assigns Variables
if firstnode == 0 and x != ",":
#print("Node1:" + x)
firstnode = x
elif secondnode == 0 and x != ",":
#print("Node2:" + x)
secondnode = x
#Marks connections
if firstnode != 0 and secondnode != 0:
adjMatrix[int(firstnode) - 1][int(secondnode) - 1] = 1
adjMatrix[int(secondnode) - 1][int(firstnode) - 1] = 1
firstnode, secondnode = 0, 0
print(adjMatrix)
return findPath(adjMatrix, 1, "1")
def findPath(adjMatrix, currentnode, currentpath):
if isFinished(currentpath):
return currentpath
for x in range(0, 8):
if adjMatrix[currentnode - 1][x] == 1:
print(currentpath + "+" + str(x+1))
newMatrix = adjMatrix
newMatrix[currentnode - 1][x] = 0
newMatrix[x][currentnode - 1] = 0
temp = currentpath
temp += str(x+1)
newpath = findPath(newMatrix, x+1,temp)
print(newpath)
if isFinished(newpath):
print ("Returning: " + newpath)
return newpath
return ""
def isFinished(currentpath):
#Checks if node 1 is hit at least twice and each other node is hit at least once
if currentpath == "":
return False
for i in range(1, 9):
if i == 1 and currentpath.count(str(i)) < 2:
return False
elif currentpath.count(str(i)) < 1:
return False
#Checks if it starts and ends with 1
if not currentpath.startswith(str(1)) or not currentpath.endswith(str(1)):
return False
return True
#This part is using only for self-testing
if __name__ == "__main__":
def check_solution(func, teleports_str):
route = func(teleports_str)
teleports_map = [tuple(sorted([int(x), int(y)])) for x, y in teleports_str.split(",")]
if route[0] != '1' or route[-1] != '1':
print("The path must start and end at 1")
return False
ch_route = route[0]
for i in range(len(route) - 1):
teleport = tuple(sorted([int(route[i]), int(route[i + 1])]))
if not teleport in teleports_map:
print("No way from {0} to {1}".format(route[i], route[i + 1]))
return False
teleports_map.remove(teleport)
ch_route += route[i + 1]
for s in range(1, 9):
if not str(s) in ch_route:
print("You forgot about {0}".format(s))
return False
return True
assert check_solution(checkio, "13,14,23,25,34,35,47,56,58,76,68"), "Fourth"
The line
newMatrix = adjMatrix
merely creates another reference to your list. You'll need to actually create a new list object. As this is a matrix, do so for the contents:
newMatrix = [row[:] for row in adjMatrix]
This creates a new list of copies of your nested lists.

python maze recurrsion not recurssing

I'm trying to solve a maze recursively and I can get it to go where I want it to (check to make sure it can go a way and then to mark it as been there) but for some reason when it runs into a dead end, it does not recursively go back to an open place to check another path. Is there anything wrong with the way I'm using recursion?
class maze(object):
def __init__(self, maze):
self.maze = maze
self.maze2d = []
n = 0
i = 0
for row in self.maze:
self.maze2d.append([])
print(row)
for col in row:
self.maze2d[i].append(col)
i += 1
print(self.maze2d)
def find_start(self):
x = 0
y = 0
for row in self.maze2d:
for index in row:
if index == "S":
self.maze2d[x][y] = index
return x,y
y += 1
x += 1
y = 0
return -1
def mazeSolver(self,x,y,path):
if self.maze2d[x][y] == "E":
return True
else:
if self.maze2d[x] != 0:
if self.maze2d[x+1][y] == ".":
self.maze2d[x+1][y] = "_"
self.mazeSolver(x+1,y,path + "S")
if self.maze2d[x] < len(self.maze2d):
if self.maze2d[x-1][y] == ".":
self.maze2d[x-1][y] = "_"
self.mazeSolver(x-1,y,path + "N")
if y < len(self.maze2d[x]):
if self.maze2d[x][y+1] == ".":
self.maze2d[x][y+1] = "_"
self.mazeSolver(x,y+1,path + "E")
if self.maze2d[y] != 0:
if self.maze2d[x][y-y] == ".":
self.maze2d[x][y-1] = "_"
self.mazeSolver(x,y-1,path + "W")
and where I'm calling the function and the maze itself:
from eachstep import *
maze1 = []
maze1.append("S..*..")
maze1.append("*...*.")
maze1.append("..*..E")
var1 = maze(maze1)
x,y = var1.find_start()
var1.mazeSolver(x,y,"")
I changed your mazeSolver function with this. And I print the path at the end:
def mazeSolver(self,x,y,path):
if self.maze2d[x][y] == '.':
self.maze2d[x][y] = "_"
if self.maze2d[x][y] == "E":
print path
return True
else:
if x < len(self.maze2d)-1:
if self.maze2d[x+1][y] in ['.','E']:
self.mazeSolver(x+1,y,path + "S")
if x > 0:
if self.maze2d[x-1][y] in ['.','E']:
self.mazeSolver(x-1,y,path + "N")
if y < len(var1.maze2d[x])-1:
if self.maze2d[x][y+1] in ['.','E']:
self.mazeSolver(x,y+1,path + "E")
if y > 0:
if self.maze2d[x][y-y] in ['.','E']:
self.mazeSolver(x,y-1,path + "W")
>>> var1.mazeSolver(x,y,"")
ESEESEE
>>>> var1.maze2d
[['S', '_', '_', '*', '.', '.'],
['*', '_', '_', '_', '*', '.'],
['_', '_', '*', '_', '_', 'E']]
Your code will never reach the end, since it only visits locations with the value ., and the end has the value E.

Categories