Now I have read the other stackoverflow Game of Life questions and also Googled voraciously.I know what to do for my Python implementation of the Game Of Life.I want to keep track of the active cells in the grid.The problem is I'm stuck at how should I code it.
Here's what I thought up but I was kinda at my wit's end beyond that:
Maintain a ActiveCell list consisting of cell co-ordinates tuples which are active
dead or alive.
When computing next generation , just iterate over the ActiveCell list,compute cell
state and check whether state changes or not.
If state changes , add all of the present cells neighbours to the list
If not , remove that cell from the list
Now the problem is : (" . "--> other cell)
B C D
. A .
. . .
If A satisfies 3) then it adds B,C,D
then if B also returns true for 3) ,which means it will add A,C again
(Duplication)
I considered using OrderedSet or something to take care of the order and avoid duplication.But still these I hit these issues.I just need a direction.
don't know if it will help you, but here's a quick sketch of Game of Life, with activecells dictionary:
from itertools import product
def show(board):
for row in board:
print " ".join(row)
def init(N):
board = []
for x in range(N):
board.append([])
for y in range(N):
board[x].append(".");
return board
def create_plane(board):
board[2][0] = "x"
board[2][1] = "x"
board[2][2] = "x"
board[1][2] = "x"
board[0][1] = "x"
def neighbors(i, j, N):
g1 = {x for x in product([1, 0, -1], repeat=2) if x != (0, 0)}
g2 = {(i + di, j + dj) for di, dj in g1}
return [(x, y) for x, y in g2 if x >= 0 and x < N and y >= 0 and y < N]
def live(board):
N = len(board)
acells = {}
for i in range(N):
for j in range(N):
if board[i][j] == "x":
for (x, y) in neighbors(i, j, N):
if (x, y) not in acells: acells[(x, y)] = board[x][y]
while True:
print "-" * 2 * N, len(acells), "cells to check"
show(board)
raw_input("Press any key...")
for c in acells.keys():
a = len([x for x in neighbors(c[0], c[1], N) if board[x[0]][x[1]] == "x"])
cur = board[c[0]][c[1]]
if a == 0:
del acells[c] # if no live cells around, remove from active
elif cur == "x" and a not in (2, 3):
acells[c] = "." # if alive and not 2 or 3 neighbors - dead
elif cur == "." and a == 3:
acells[c] = "x" # if dead and 3 neighbors - alive
for x in neighbors(c[0], c[1], N): # add all neighbors of new born
if x not in acells: acells[x] = board[x[0]][x[1]]
for c in acells:
board[c[0]][c[1]] = acells[c]
N = 7
board = init(N)
create_plane(board)
live(board)
You have two lists, I'll name them currentState, and newChanges. Here will be the workflow:
Iterate over currentState, figuring out which are newly born cells, and which ones are going to die. Do NOT add these changes to your currentState. If there is a cell to be born or a death, add it to the newChanges list. When you are finished with this step, currentState should look exactly the same as it did at the beginning.
Once you have finished all calculations in step 1 for every cell, then iterate over newChanges. For each pair in newChanges, change it in currentState from dead to alive or vice versa.
Example:
currentState has {0,0} {0,1} {0,2}. (Three dots in a line)
newChanges is calculated to be {0,0} {-1,1} {1,1} {0,2} (The two end dots die, and the spot above and below the middle are born)
currentState recieves the changes, and becomes {-1,1} {0,1} {1 ,1}, and newChanges is cleared.
Did you consider using an ordered dictionary and just set the values to None?
You didn't state that you have a restriction to implement the game in a specific way. So, the main question is: how big a grid do you want to be able to handle?
For example, if you are starting with a small fixed-size grid, the simplest representation is just a [[bool]] or [[int]] containing whether each cell is alive or dead. So, each round, you can make a new grid from the old one, e.g. assuming that all cells outside the grid are dead. Example:
[
[False, True, False],
[True, False, True],
[False, True, False],
]
If you want a very large dynamic-sized grid, there's the HashLife algorithm, which is much faster, but more complicated.
I implemented Game of Life in Python for fun and what I did was having board dict, with tuples of coordinates. Value is a state of cell. You can look at the code here https://raw.github.com/tdi/pycello/master/pycello.py. I know this is not very fast implementation and the project is abandoned due to lack of time.
board = {}
board[(x,y)] = value
Related
I have a list of strings:
['oXoXXoo', 'oXXoooo', 'oooXooo']
These are moves of a puzzle where one peg jumps over an adjacent peg. The first item in the list is the starting state and the final item is the solved board.
I am trying to display the moves needed to solve the board in the format:
[ (4, L), (1, R) ]
where peg at index [4] jumps left to get to the second board state and peg at index [1] jumps right to solve the puzzle. Basically I need to find specific differences between each list item and return a tuple list based on them. My current pseudocode idea is:
find where oXX became Xoo
path.add((index of the o+2, L))
find where XXo became ooX
path.add((index of the X+2, R))
I have also considered turning the strings into a list and doing something with .difference but im not sure where to go from there. Any suggests into how I can compare strings or lists in python welcome!
Something like this would probably work if I understood your problem correctly:
l = ['oXoXXoo', 'oXXoooo', 'oooXooo']
path = []
for i in range(len(l) - 1):
before = l[i]
after = l[i+1]
string_length = len(before)
for j in range(string_length):
if before[j] != after[j] and before[j] == "o":
# It means that the peg went LEFT! (it jumped from j+2 to j)
path.append((j+2,"L"))
break
if before[j] != after[j] and before[j] == "X":
# It means that the peg went RIGHT! (it jumped from j to j+2)
path.append((j,"R"))
break
for p in path:
print(p)
Output:
(4,L)
(1,R)
It's sufficient to check the first elements that differ in two consecutive strings, then we can both infer if the peg went LEFT or RIGHT and the original peg position.
There is a considerably simpler implementation though it's based on the same observation as ИванКарамазов's answer below. Just to give some inspiration (and for my own pleasure of optimizing stuff):
states = ['oXoXXoo', 'oXXoooo', 'oooXooo']
moves = []
for i, state1 in enumerate(states[:-1]):
state2 = states[i+1]
pos, char = next((i,a) for i, (a,b) in enumerate(zip(state1, state2)) if a != b)
moves.append((pos, 'R') if char == 'X' else (pos + 2, 'L'))
# result
[(4, 'L'), (1, 'R')]
Explanation
There are only two possible options – L or R
There is only one action allowed per move
There are only the symbols o and X
That means you only need to get
the position of the first character char that's different between the two states and
the value of char (o or X) in either of the states.
(i,a) for i, (a,b) in enumerate(zip(state, state2)) if a != b) is a generator that yields elements from the two states in sync plus the current index (enumerate) if the characters are different. By next we only iterate up to the first match, which makes it quite efficient.
If char is o, we know we are going from oXX to Xoo (= L), in which case we need to add 2 positions to the right to get the origin of X in the first state. If the char is X, the move was R and we just return the pos.
IIUC, you want a function that takes two strings, s1 and s2, and describes the move used to get from s1 to s2 - or in other words
describe_move('XXoXXoo', 'oXXXXoo') should return (0, 'R')
You could write such a function by checking for the position of ('o', 'X') and identifying the move as coming from 2 places off on either side -
def describe_move(s1, s2):
move = list(zip(s1, s2))
peg_final_index = move.index(('o', 'X'))
peg_initial_index = (peg_final_index + 2) if move[peg_final_index + 2] == ('X', 'o') else (peg_final_index - 2)
direction = 'L' if peg_initial_index > peg_final_index else 'R'
return peg_initial_index, direction
s1 = 'oXoXXoo'
s2 = 'oXXoooo'
describe_move(s1, s2)
# (4, 'L')
describe_move(s2, 'oooXooo')
# (1, 'R')
describe_move('XXoXXoo', 'oXXXXoo')
# (0, 'R')
so I've got a list of questions as a dictionary, e.g
{"Question1": 3, "Question2": 5 ... }
That means the "Question1" has 3 points, the second one has 5, etc.
I'm trying to create all subset of question that have between a certain number of questions and points.
I've tried something like
questions = {"Q1":1, "Q2":2, "Q3": 1, "Q4" : 3, "Q5" : 1, "Q6" : 2}
u = 3 #
v = 5 # between u and v questions
x = 5 #
y = 10 #between x and y points
solution = []
n = 0
def main(n_):
global n
n = n_
global solution
solution = []
finalSolution = []
for x in questions.keys():
solution.append("_")
finalSolution.extend(Backtracking(0))
return finalSolution
def Backtracking(k):
finalSolution = []
for c in questions.keys():
solution[k] = c
print ("candidate: ", solution)
if not reject(k):
print ("not rejected: ", solution)
if accept(k):
finalSolution.append(list(solution))
else:
finalSolution.extend(Backtracking(k+1))
return finalSolution
def reject(k):
if solution[k] in solution: #if the question already exists
return True
if k > v: #too many questions
return True
points = 0
for x in solution:
if x in questions.keys():
points = points + questions[x]
if points > y: #too many points
return True
return False
def accept(k):
points = 0
for x in solution:
if x in questions.keys():
points = points + questions[x]
if points in range (x, y+1) and k in range (u, v+1):
return True
return False
print(main(len(questions.keys())))
but it's not trying all possibilities, only putting all the questions on the first index..
I have no idea what I'm doing wrong.
There are three problems with your code.
The first issue is that the first check in your reject function is always True. You can fix that in a variety of ways (you commented that you're now using solution.count(solution[k]) != 1).
The second issue is that your accept function uses the variable name x for what it intends to be two different things (a question from solution in the for loop and the global x that is the minimum number of points). That doesn't work, and you'll get a TypeError when trying to pass it to range. A simple fix is to rename the loop variable (I suggest q since it's a key into questions). Checking if a value is in a range is also a bit awkward. It's usually much nicer to use chained comparisons: if x <= points <= y and u <= k <= v
The third issue is that you're not backtracking at all. The backtracking step needs to reset the global solution list to the same state it had before Backtracking was called. You can do this at the end of the function, just before you return, using solution[k] = "_" (you commented that you've added this line, but I think you put it in the wrong place).
Anyway, here's a fixed version of your functions:
def Backtracking(k):
finalSolution = []
for c in questions.keys():
solution[k] = c
print ("candidate: ", solution)
if not reject(k):
print ("not rejected: ", solution)
if accept(k):
finalSolution.append(list(solution))
else:
finalSolution.extend(Backtracking(k+1))
solution[k] = "_" # backtracking step here!
return finalSolution
def reject(k):
if solution.count(solution[k]) != 1: # fix this condition
return True
if k > v:
return True
points = 0
for q in solution:
if q in questions:
points = points + questions[q]
if points > y: #too many points
return True
return False
def accept(k):
points = 0
for q in solution: # change this loop variable (also done above, for symmetry)
if q in questions:
points = points + questions[q]
if x <= points <= y and u <= k <= v: # chained comparisons are much nicer than range
return True
return False
There are still things that could probably be improved in there. I think having solution be a fixed-size global list with dummy values is especially unpythonic (a dynamically growing list that you pass as an argument would be much more natural). I'd also suggest using sum to add up the points rather than using an explicit loop of your own.
So Basically I am, trying to code the horizontal win condition for a connect 4 Game In python I figured out the way to do this for 4 Connecting Chips In python would be
for x in range(cols - 3):
for y in range(rows):
if board[x][y] == color and board[x+1][y] == color and board[x+2][y] ==color and board[x+3]==color:
return True
But The Problem is I need something similar to this to work a winning connecting chips as determined by the user it could be four or what ever amount the user wants.
for x in range(col - 3):
for y in range(row):
for z in range (0, required_connected_chips):
if board_values[x + z][y] == color:
required _Connecting_chips is the winning connecting chips as determined by the user after writing this i am basically stuck on what to do next and would take any suggestions or feedback on how to solve my problem and i am sorry if i got my indentation wrong on some of the code.
you could add a boolean to save the output of board_values[x + z][y] == color:. If this keeps True until the end of your loop return True. Like:
b = True
for z in range (0, required_connected_chips):
b &= board_values[x + z][y] == color
if b:
return True
Edit: for not checking every Field u might check b in the loops after setting it so it would look like:
b = True
for z in range (0, required_connected_chips):
if not board_values[x + z][y] == color:
b = False
break
if b:
return True
Edit2: removed redundant "&" in second code example
Edit3: changed the second code example a bit further to a way it feels bether for me
I'm writing a Python script which is supposed to allow human and computer players to play Tic Tac Toe. To represent the board, I'm using a 3x3 Numpy array with 1 and 0 for the marks of the players (instead of "X" and "O"). I've written the following function to determine the winner:
import numpy as np
class Board():
def __init__(self, grid = np.ones((3,3))*np.nan):
self.grid = grid
def winner(self):
rows = [self.grid[i,:] for i in range(3)]
cols = [self.grid[:,j] for j in range(3)]
diag = [np.array([self.grid[i,i] for i in range(3)])]
cross_diag = [np.array([self.grid[2-i,i] for i in range(3)])]
lanes = np.concatenate((rows, cols, diag, cross_diag))
if any([np.array_equal(lane, np.ones(3)) for lane in lanes]):
return 1
elif any([np.array_equal(lane, np.zeros(3)) for lane in lanes]):
return 0
So for example, if I execute
board = Board()
board.grid = np.diag(np.ones(3))
print board.winner()
I get the result 1. What bothers me slightly is the repetition of the any statements. I would think there would be a more concise, DRY way of coding this. (I was thinking of a switch/case as in MATLAB but this doesn't exist in Python). Any suggestions?
Another option is to check the sum of lanes.
s = np.sum(lanes, axis=1)
if 3 in s:
return 1
elif 0 in s:
return 0
I have made a loop instead, and return only once, to conform with PEP8 and to be honest to my personal coding standards :)
enumerate in the correct order will yield 0,zeromatrix then 1,onematrix
rval = None
for i,m in enumerate([np.zeros(3),np.ones(3)]):
if any([np.array_equal(lane, m) for lane in lanes]):
rval = i; break
return rval
I found out one way, by using a Lambda function:
any_lane = lambda x: any([np.array_equal(lane, x) for lane in lanes])
if any_lane(np.ones(3)):
return 1
elif any_lane(np.zeros(3)):
return 0
This adds an extra line to the code but makes it more legible overall, I reckon.
This can be done in two lines, starting from the board (grid): simple sums along columns, rows and the two main diagonals gives you a value of 0 or 3 depending on who is winning (or some intermediate values only if nobody is winning). You can thus calculate something like:
# Score along each column, row and both main diagonals:
scores = (grid.sum(axis=0).tolist() + grid.sum(axis=1).tolist()
+[grid.trace(), np.flipud(grid).trace()])
# If there is no winner, None is declared the winner:
print "Winner:", 1 if 3 in scores else 0 if 0 in scores else None
where flipud() transforms the diagonal into the anti-diagonal (the diagonal at 90° from the main diagonal) by flipping the array horizontally, so that a simple trace() gives the total value along the anti-diagonal.
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
I have read in a matrix of numbers and I am trying to work with each cell and perform tests on each one. If the number is != 0 I want to use it so if it is 0 I am currently incrementing x and y to find a non zero number.
In the end I will just be first looking across the top row and then if they are all 0 start looking down the first column and this works fine as long as I am referring to just one (row or column).
Why am I receiving this error? Am I thinking incorrectly about how the cells are set up or is my code wrong?
The matrix looks like this:
0,2,4,1,6,0,0
2,0,0,0,5,0,0
4,0,0,0,0,5,0
1,0,0,0,1,1,0
6,5,0,1,0,5,5
0,0,5,1,5,0,0
0,0,0,0,5,0,0
When I start trying to do this:
y = y + 5
x = x + 5
node = Alist[x][y]
I receieve an error saying:
node = Alist[x][y]
IndexError: list index out of range
If I was to just write:
y = y + 5
node = Alist[x][y]
print node
It would work completely fine but when I introduce both x and y I start to get list index out of range problems. In my mind it should now read:
node = Alist[5][5]
Which is 0 if you follow the matrix
def create_matrix(file):
with open('network.txt') as f:
Alist = []
for line in f:
part = []
for x in line.split(','):
part.append(int(x))
Alist.append(part)
return Alist
#used to set the start node, used once
def start_node(Alist):
x=0
y=0
#point node to pos [0][0] of Alist
node = Alist[x][y]
#test if node == 0
while node == 0:
y = y + 5
x = x + 5
node = Alist[x][y]
#create a list to hold co-ordinates
if node != 0:
#return node for processing by check_neighbours
return node, x, y
#def current_node(Alist, x, y)
#test neighbours to see if they can be used
def check_neighbours(node, Alist, i, j):
#print out values of x and y
print "x = %d" %i
print "y = %d" % j
print "node in check_neighbours is " + str(node)
#running of code begins here
def main():
file = ("F:/media/KINGSTON/Networking/network.txt")
Alist = create_matrix(file)
node, x, y = start_node(Alist)
check_neighbours(node, Alist, x, y)
main()
It's the second iteration round the loop that's failing, because it sets x and y to 10, where you only have 7 items in each dimension.
Your loop while node == 0: moves down the diagonal of the matrix five steps at a time, falling off the end. It would fall off the end even if it went one at a time, because that diagonal is all zeros.
Perhaps you meant if node == 0?
I don't know what the code is actually trying to do, but you need to rethink the algorithm. :-)
If I follow your code in start_node you are
a) Not performing any kind of bounds checking
and
b) Jumping down the diagonal elements of your matrix, which are all zero, until you go out of bounds, as your error tells you. Presumably when you are incrementing only one of your indices you are lucky in that you hit a non-zero element before going out of bounds
In this code, start_node returns the first non-zero "node".
I used mat instead of Alist as I feel it is more Pythonic.
Comments note the changes and tips.
def create_matrix(fname): # don't use built-ins as variable names!!
with open(fname) as f: # you take filename in arguments, but don't use it..
# why not use in-built functions and list comprehensions ?
mat = [map(int,line.split(',')) for line in f]
return mat
#used to set the start node, used once
def start_node(mat):
# for each row in matrix (index)..
for i1 in range(len(mat)):
# for each cell in row (index)..
for i2 in range(len(mat[i1])):
# mat[i1][i2] is the node/cell..
if mat[i1][i2] != 0:
# return node, x, y :)
return mat[i1][i2], i2, i1
# test neighbours to see if they can be used
# same as yours
def check_neighbours(node, Alist, i, j):
#print out values of x and y
print "x = %d" % (i,)
print "y = %d" % (j,)
print "node (in check_neighbours) is " + str(node)
#running of code begins here
def main():
fname = "F:/media/KINGSTON/Networking/network.txt"
mat = create_matrix(fname)
node, x, y = start_node(mat)
check_neighbours(node, mat, x, y)
main()
Explanation:
mat = [map(int,line.split(',')) for line in f]
This is a list comprehension. map is an in-built method.
The for loops in start_node...
for i1 in range(len(mat)): # 1
for i2 in range(len(mat[i1])): # 2
len(mat) is the number of rows in your matrix.
range(len(mat)) gives us the indexes of each row.
len(mat[i1]) gives us the length of the current row.
range(len(mat[i1])) gives us the indexes of each column in that row.
In your code, you were incrementing both x and y (here i2 and i1 respectively) so were moving diagonally.