Python game of life generates wrong structures - python

I'm trying to program the game of Life in python. I keep getting the wrong structure generated after generation and pinpointed my mistake to the fuction bellow.
What I want the program to do:
It should iterate through an array to determine if the cell is going to be alive or dead in next generation and places them in an array "cells"
INPUT: (.-dead cell x- Alive cell)
...
xxx
...
OUTPUT
.x.
.x.
.x.
What is happening now:
The program iterates through cells and changes them. Then it uses the new values instead of the original ones to determine if the next cell is alive.
INPUT:
...
xxx
...
OUTPUT
.xx
x.x
...
Additional info:
Both arrays have a frame of permanently dead cells around.
def nextGen(matrice):
cells = matrice
size = np.shape(matrice)
for i in range(1,size[0]-1):
for j in range(1,size[1]-1):
if matrice[i][j] > 0:
alive = True
else:
alive = False
neighbours = 0
neighbours += matrice[i-1,j+1]
neighbours += matrice[i-1,j]
neighbours += matrice[i-1,j -1]
neighbours += matrice[i,j+1]
neighbours += matrice[i,j -1]
neighbours += matrice[i+1,j+1]
neighbours += matrice[i+1,j]
neighbours += matrice[i+1,j -1]
if alive:
if neighbours != 2 and neighbours !=3:
cells[i,j] = 0
#print("Cell has died", i, j, "Neighbours: ", neighbours)
else:
if neighbours == 3:
cells[i,j] = 1
#print("Cell was born", i, j,"Neighbours: ", neighbours)
neighbours = 0
return cells

The problem was as #user2357112 suggested that cells = matrice doesn't make a copy.
To fix that I needed to change it to cells = np.copy(matrice) .

Related

What is the best way to generate random ships in an array in Python?

Recently, I've been working on a battleship game for my CS2 class. The focus of this project is to create a board game using arrays, and I decided to create a battleship. I have most of the code, but I cannot figure out how to get ships to get randomly generated on a 10x10 array without the ships being...
A. The wrong length
B. Looping around the array
Below is the function I currently have.
def createShips(board):
shipLen = [5,4,3,3,2]
shipAvalible = 5
directionposibilities = ["vertical", "horizontal"]
j = 0
for i in range(shipAvalible):
boatMade = False
#REGULAR VAR STATMENTS
direction = random.choice(directionposibilities)
col = randint(0,9)
row = randint(0,9)
while boatMade == False:
if direction == "vertical":
buildCount = 0
if col + int(shipLen[i]) <= 11:
colission = False
for i in range(0, int(shipLen[i])):
buildCount += 1
if board[int(row-i)][int(col)-1] == "X":
if colission:
pass
else:
colission = True
if colission:
col = randint(0,9)
row = randint(0,9)
else:
for j in range(buildCount):
board[int(row-j)][int(col)-1] = "X"
boatMade = True
else:
col = randint(0,9)
row = randint(0,9)
if direction == "horizontal":
if col + int(shipLen[i]) <= 10:
colission = False
buildCount = 0
for i in range(0, int(shipLen[i])):
buildCount += 1
if board[int(row)][int(col)+i-1] == "X":
if colission:
pass
else:
colission = True
if colission:
col = randint(0,9)
row = randint(0,9)
else:
for j in range(buildCount):
board[int(row)][int(col)+j-1] = "X"
boatMade = True
else:
col = randint(0,9)
row = randint(0,9)
shipAvalible = shipAvalible - 1
return(board)
board = [["■"] * 10 for x in range(10)]
print(createShips(board))
If you have any idea why this may not work please let me know!
P.S. I am using another function that prints the array nicely, if you would like that for convenience, it is seen below:
def showBoard(board):
print(" A B C D E F G H I J")
print(" ------------------------------")
rownumb = 1
for r in board:
if rownumb == 10:
space = ""
else:
space = " "
print("%d|%s|" % (rownumb, space + "|".join(r)))
rownumb += 1
Edit: #Eumel was a bit faster and said it better here
First of all, you have a inner and outer loop with the variable i, which might not actually interfere but it makes it harder to read the code. Consider renaming one of the variables.
When you create a vertical ship, you check the col rather than the row for index error, and does so against a value of 11 instead of your board size 10.
In the vertical case you build the ship "backwards" from the selected position, even though you checked that your board could fit the ship in the positive direction (And you place it in a column to the left). These negative values makes it such that your ships can wrap around.
In the horizontal case you are indexing a bit strange again where board[int(row)][int(col)+i-1] this gives that when i=0 you place your ship an index further to the left than intended, again causing the ship to wrap when col=0.
Further more, you could remove a few redundant if statements and duplicate lines of code, by moving:
direction = choice(directionposibilities)
col = randint(0, 9)
row = randint(0, 9)
Inside the while loop.
Ok lets go over this:
length check
if col + int(shipLen[i]) <= 9: since arrays start at 0 in python the maximum index for a 10x10 board is 9, therefore you need to chek against 9 instead of 11
loop variables
for k in range(0, int(shipLen[i])): using the same variable name for the outer and inner loop (while working in this specific case) is really bad form
collision check
if board[row+k][col] == "X": you want to build the ships "forwards" so you use row+k, you can also forego int conversions when you only use integers to begin with
collsion check 2
if colission: pass else: colission = True This whole block can be shortened to collision = True break since you dont need to keep checking for multiple collisions
buildcount
for j in range(buildCount): unless you get a collision (then you wouldnt be in this branch) buildCount is always the same as shipLen[i] so it can be completly removed from your code
boat building
board[row+j][col] = "X": same as before we build forwards
honorable mentions
shipAvailable = len(shipLen) you could also remove this part completly and iterate over your ships directly in your out for loop
Instead of radoomising your direction row and col on every break condition you could randomize it once at the start of your while loop
j = 0 unlike C you dont have to define your variables before using them in a loop, this statement does nothing

(Traversal) How to solve this question in python?

Write a function traverse() that takes in a list tb of n strings each containing n lower case characters
(a-z).
tb represents a square table with n rows and n columns. The function returns a string st generated by the procedure below that traverses the grid starting from the top left cell and ending at the bottom right cell.
At every step, the procedure moves either horizontally to the right or vertically down, depending on which of the two cells has a \smaller" letter (i.e., a letter that appears earlier in the alphabetical order).
The letter in the visited cell is then added to st. In case of ties, either direction can be taken.
When the right or bottom edges of the table are reached, there is obviously only a unique next cell to move to. As an example, traverse(["veus", "oxde", "oxlx", "hwuj"]) returns "veudexj"
so the table would look like this:
v o o h
e x x w
u d l u
s e x j
I am new in python and I wrote this code ... but it only prints "veuexj" I would say the problem is in this line if new_list[a - 1][b - 1] == new_list[a - 1][-2]: which force the parameter to skip the 'd' character. #And I don't know how to solve it.
def traverse(tb_list):
new_list = tb_list.copy()
st = new_list[0][0]
parameter = ''
handler = 1
for a in range(1, len(new_list)):
for b in range(handler, len(new_list[a])):
if new_list[a - 1][b - 1] == new_list[a - 1][-2]:
parameter = new_list[a][b]
elif new_list[a - 1][b - 1] > min(new_list[a - 1][b], new_list[a][b - 1]):
parameter = min(new_list[a - 1][b], new_list[a][b - 1])
elif new_list[a - 1][b - 1] < min(new_list[a - 1][b], new_list[a][b - 1]):
parameter = min(new_list[a - 1][b], new_list[a][b - 1])
st += parameter
handler = b
return st
print(traverse(["veus", "oxde", "oxlx", "hwuj"]))
You can try something like this (explanation added as comments):
def traverse(tb_list):
lst = tb_list.copy() #Takes a copy of tb_list
lst = list(zip(*[list(elem) for elem in lst])) #Transposes the list
final = lst[0][0] #Sets final as the first element of the list
index = [0,0] #Sets index to 0,0
while True:
x = index[0] #The x coordinate is the first element of the list
y = index[1] #The y coordinate is the second element of the list
if x == len(lst) - 1: #Checks if the program has reached the right most point of the table
if y == len(list(zip(*lst))) - 1: #Checks if program has reached the bottommost point of the table
return final #If both the conditions are True, it returns final (the final string)
else:
final += lst[x][y+1] #If the program has reached the right most corner, but not the bottommost, then the program moves one step down
index = [x, y+1] #Sets the index to the new coordinates
elif y == len(list(zip(*lst))) - 1: #Does the same thing in the previous if condition button in an opposite way (checks if program has reached bottommost corner first, rightmost corner next)
if x == len(lst) - 1:
return final
else:
final += lst[x + 1][y] #If the program has reached the bottommost corner, but not the rightmost, then the program moves one step right
index = [x + 1, y]
else: #If both conditions are false (rightmost and bottommost)
if lst[x+1][y] < lst[x][y+1]: #Checks if right value is lesser than the bottom value
final += lst[x+1][y] #If True, then it moves one step right and adds the alphabet at that index to final
index = [x+1,y] #Sets the index to the new coords
else: #If the previous if condition is False (bottom val > right val)
final += lst[x][y+1] #Moves one step down and adds the alphabet at that index to final
index = [x,y+1] #Sets the index to the new coords
lst = ["veus", "oxde", "oxlx", "hwuj"]
print(traverse(lst))
Output:
veudexj
I have added the explanation as comments, so take your time to go through it. If you are still not clear with any part of the code, feel free to ask me. Any suggestions to optimize/shorten my code are most welcome.

Depth First Search Python Game Solver

I have a basic maze game in python "*" are walls, "W" water buckets which allow you to travel through fire "F" (if you have none you lose), "A" is the player and " " is air and "X" and "Y" are start and endpoints. There are also numbers from 1 - 9 which have 1 other matching number so the player can teleport. Example map:
*X**********
* 1 W F*
* FFFF 1 *
**********Y*
So I have a graph (dictionary) where I go through each character in the maze and find their neighbours like {"0,0": [None, "0,1", None, "0,1"]}
{"node": "LeftNeighbour", "RightNeighbour", "NeighbourAbove", "NeighbourBelow"}
and I am using coordinates "x,y" to denote neighbours.
Then the DFS code is ...
visited = []
def DFS_recursive(graph, node, nodeToFind):
global visited
for neighbour in graph[node]:
if neighbour == nodeToFind:
visited.append(neighbour)
break
if neighbour not in visited and neighbour != None:
# get x and y by breaking up the string
x = ""
y = ""
yFlag = False
for char in neighbour:
if char == ",":
yFlag = True
elif yFlag == False:
x += str(char)
else:
y += str(char)
x, y = int(x), int(y)
if grid[y][x].display == "*": # if the neighbour's display property is a wall, append the neighbour but don't call the function again ...
visited.append(neighbour)
else:
visited.append(neighbour)
DFS_recursive(graph, neighbour, nodeToFind)
So this works until I add teleport pad code.
if neighbour == (number from 1 - 9):
find the coordinates of the other teleport pad and make the coordinates the new node
DFS_recursive(graph, newNode, nodeToFind)
And yes I haven't added in the "nodeToFind" bit yet, couldn't quite get that to work so I added a separate function to deal with that. Probably should remove the parameter but anyway that's not the point.
So 1. Not sure if this is the right way to go about it (am new to programming)
and 2. No idea how to handle the teleport pads.
Thanks in advance!

Python: List not searching diagonally

I am implementing a Python version of the game Othello / Reversi. However, my algorithm seems to be having trouble when searching in the southwest direction.
Here are some important functions to understand how my current code works:
def _new_game_board(self)->[[str]]:
board = []
for row in range(self.rows):
board.append([])
for col in range(self.columns):
board[-1].append(0)
return board
def _is_valid_position(self, turn:list)->bool:
'''return true if the turn is a valid row and column'''
row = int(turn[0]) - 1
column = int(turn[1]) - 1
if row >= 0:
if row < self.rows:
if column >= 0:
if column < self.columns:
return True
else:
return False
def _is_on_board(self, row:int, col:int)->bool:
'''returns true is coordinate is on the board'''
if row >=0:
if row < self.rows:
if col >=0:
if col < self.columns:
return True
def _searchNorthEast(self)->None:
'''Search the board NorthEast'''
print("NorthEast")
row = self.move_row
column = self.move_column
should_be_flipped = list()
row += 1
column -= 1
if self._is_on_board(row, column):
print("column searching NorthEast on board")
if self.board[row][column] == self._opponent:
should_be_flipped.append([row, column])
while True:
row += 1
column -= 1
if self._is_on_board(row, column):
if self.board[row][column] == self._opponent:
should_be_flipped.append([row, column])
continue
elif self.board[row][column] == self.turn:
self._to_be_flipped.extend(should_be_flipped)
break
else:
break
else:
self._to_be_flipped.extend(should_be_flipped)
else:
pass
def _searchSouthWest(self)->None:
'''Search the board SouthWest'''
print("in SouthWest")
row = self.move_row
column = self.move_column
should_be_flipped = list()
row -= 1
column += 1
if self._is_on_board(row, column):
print("column searching SouthWest on board")
if self.board[row][column] == self._opponent:
should_be_flipped.append([row, column])
while True:
row -= 1
column += 1
if self._is_on_board(row, column):
if self.board[row][column] == self._opponent:
should_be_flipped.append([row, column])
continue
elif self.board[row][column] == self.turn:
self._to_be_flipped.extend(should_be_flipped)
break
else:
break
else:
self._to_be_flipped.extend(should_be_flipped)
else:
pass
def _move_is_valid(self, turn:list)->bool:
'''Verify move is valid'''
self._to_be_flipped = list()
self._opponent = self._get_opposite(self.turn)
if self._is_valid_position(turn):
self.move_row = int(turn[0]) - 1
self.move_column = int(turn[1]) - 1
self._searchRight()
self._searchLeft()
self._searchUp()
self._searchDown()
self._searchNorthWest()
self._searchNorthEast
self._searchSouthEast()
self._searchSouthWest()
if len(self._to_be_flipped) > 0:
return True
else:
return False
Now let's say the current board looks like the following:
. . . .
W W W .
. B B .
. B . .
Turn: B
and the player makes a move to row 1 column 4, it says invalid because it does not detect the white piece in row 2 column 3 to be flipped. All my other functions are written the same way. I can get it to work in every other direction except in this case.
Any ideas why it is not detecting the piece in this diagonal direction?
Don't Repeat Yourself. The _search* methods are extremely redundant which makes it difficult to see that the signs in
row -= 1
column += 1
are correct. Since you've given only two directions (NE, SW) and no documentation of the board orientation, I cannot tell if the signs agree with the board layout or even agree with themselves.
The _search* methods are also too long and should be divided into multiple functions, but that's a secondary concern.
I agree with msw about not repeating stuff. It is tempting to go ahead and do what you can once you see it, but generalizing will save you the headaches of debugging.
Here is some pseudocode that should give the general idea. I might not be able to finagle working with your code, but hopefully this shows how to reduce repetitive code. Note it doesn't matter if -1 is up or down. The board class is simply a 2x2 array of (open square/player 1's piece/player 2's piece,) along with whose turn it is to move.
# x_delta and y_delta are -1/0/1 each based on which of the up to 8 adjacent squares you are checking. Player_num is the player number.
def search_valid(x_candidate, y_candidate, x_delta, y_delta, board, player_num):
y_near = y_candidate + y_delta
x_near = x_candidate + x_delta
if x_near < 0 or x_near >= board_width:
return False
if y_near < 0 or y_near >= board_width:
return False # let's make sure we don't go off the board and throw an exception
if board.pieces[x_candidate+x_delta][y_candidate+y_delta] == 0:
return False #empty square
if board.pieces[x_candidate+x_delta][y_candidate+y_delta] == player_num:
return False #same color piece
return True #if you wanted to detect how many flips, you could use a while loop
Now a succinct function can loop this search_valid to see whether a move is legal or not e.g.
def check_valid_move(x_candidate, y_candidate, board, player_num):
for dx in [-1, 0, 1]:
for dy in [-1, 0, 1]:
if not x and not y:
continue # this is not a move. Maybe we don't strictly need this, since board.pieces[x_candidate+x_delta][y_candidate+y_delta] == player_num anyway, but it seems like good form.
if search_valid(x_candidate, y_candidate, dx, dy, board, player_num):
return True
return False
A similar function could actually flip all the opposing pieces, but that's a bit trickier. You'd need a while function inside the for loops. But you would not have to rewrite code for each direction.

Count the number of possible paths through a grid

There is a robot at the top-left corner of an N*M grid. The robot can move up, down, left and right, but cannot visit the same cell more than once in each traversal. How do I find the total number of ways the robot can reach the bottom-right corner?
(the robot does not need to visit every cell for a path to be valid)
I think there is a recursive solution to this but I can't get it somehow.
Here's what I've got so far:
def initialize(row, cols):
grid = [ [ 0 for c in range(cols) ] for r in range(rows) ]
pos_r, pos_c = 0, 0
grid[pos_r][pos_c] = 1 # set start cell as visited
dst_x, dst_y = len(grid)-1, len(grid[0])-1 # coords of bottom-right corner
print move_robot(grid, dst_x, dst_y, pos_r, pos_c)
def move_robot(grid, dst_x, dst_y, pos_r, pos_c, prev_r=None, prev_c=None):
num_ways = 0
if reached_dst(dst_x, dst_y, pos_r, pos_c):
undo_move(grid, pos_r, pos_c)
undo_move(grid, prev_r, prev_c)
return 1
else:
moves = get_moves(grid, pos_r, pos_c)
if len(moves) == 0:
undo_move(grid, prev_r, prev_c)
return 0
for move in moves:
prev_r = pos_r
prev_c = pos_c
pos_r = move[0]
pos_c = move[1]
update_grid(grid, pos_r, pos_c)
num_ways += move_robot(grid, dst_x, dst_y, pos_r, pos_c, prev_r, prev_c)
return num_ways
if __name__ == '__main__':
initialize(4, 4)
I left out some function definitions for brevity. Get_moves retrieves the all legal moves, checking whether each move would still be on the board and whether the cell has already been visited. Update_grid sets the specified cell to '1', which means visited. Undo_move does the opposite, setting the specified cell to '0'.
I get the right answer for the simplest possible case (2*2 grid), but for larger grids the output is always too low. What's wrong with my code, and is there a simpler way of doing this?
The recursion is pretty straight forward but one should be careful to create copies of the matrix while recursing in order to receive good results:
from copy import copy, deepcopy
def calc(i, j, mat):
if i < 0 or j < 0 or i >= len(mat) or j >= len(mat[0]):
return 0 # out of borders
elif mat[i][j] == 1:
return 0 # this cell has already been visited
elif i == len(mat)-1 and j == len(mat[0])-1:
return 1 # reached destination (last cell)
else:
mat[i][j] = 1 # mark as visited
# create copies of the matrix for the recursion calls
m1 = deepcopy(mat)
m2 = deepcopy(mat)
m3 = deepcopy(mat)
m4 = deepcopy(mat)
# return the sum of results of the calls to the cells:
# down + up + right + left
return calc(i+1, j, m1) + calc(i-1, j, m2) + calc(i, j+1, m3) + calc(i, j-1, m4)
def do_the_robot_thing(m, n):
# an un-visited cell will be marked with "0"
mat = [[0]*n for x in xrange(m)]
return calc(0, 0, mat)
print(do_the_robot_thing(3, 3))
OUTPUT:
12

Categories

Resources