Conway's Game of Life: Updating from second to third generation - python-3.x

As a challenge to myself I tried to write Conway's game of life simulator. While everthing works fine from Input values to printing out the First generation and Second, I have trouble when dealing with further generations.
Here is my code so far:
class Game_setup:
def __init__(self, cells):
self.boundry = 20
self.cells = cells
self.x_cord = []
self.y_cord = []
self.cord_pairs = []
self.coordinates = []
self.dead_pairs = []
def inital_values(self):
coordinates = []
for x in range(int(self.boundry)):
for y in range(int(self.boundry)):
coordinates.append([x, y])
self.coordinates = coordinates
return coordinates
def intial_cells(self):
cord_pairs = []
with open(self.cells, 'r', encoding="utf-8") as file:
for line in file:
row = line.split()
cord_pairs.append([int(row[0]),int(row[1])])
x = []
y = []
for number_of_coordinates in range(len(cord_pairs)):
x.append(cord_pairs[number_of_coordinates][0])
y.append(cord_pairs[number_of_coordinates][1])
self.x_cord = x
self.y_cord = y
self.cord_pairs = cord_pairs
return cord_pairs
def neighbours(self, n):
neighbours = 0
x_coordinate = self.cord_pairs[n][0]
y_coordinate = self.cord_pairs[n][1]
if [x_coordinate,y_coordinate+1] in self.cord_pairs:
neighbours += 1
if [x_coordinate,y_coordinate-1] in self.cord_pairs:
neighbours += 1
if [x_coordinate+1,y_coordinate] in self.cord_pairs:
neighbours += 1
if [x_coordinate+1,y_coordinate-1] in self.cord_pairs:
neighbours += 1
if [x_coordinate+1,y_coordinate+1] in self.cord_pairs:
neighbours += 1
if [x_coordinate-1,y_coordinate] in self.cord_pairs:
neighbours += 1
if [x_coordinate-1,y_coordinate-1] in self.cord_pairs:
neighbours += 1
if [x_coordinate-1,y_coordinate+1] in self.cord_pairs:
neighbours += 1
return neighbours
def from_dead_to_alive(self,pair):
x_coordinate = pair[0]
y_coordinate = pair[1]
neighbours = 0
if [x_coordinate,y_coordinate+1] in self.cord_pairs and [x_coordinate,y_coordinate] not in self.cord_pairs:
neighbours += 1
if [x_coordinate,y_coordinate-1] in self.cord_pairs and [x_coordinate,y_coordinate] not in self.cord_pairs:
neighbours += 1
if [x_coordinate+1,y_coordinate] in self.cord_pairs and [x_coordinate,y_coordinate] not in self.cord_pairs:
neighbours += 1
if [x_coordinate+1,y_coordinate-1] in self.cord_pairs and [x_coordinate,y_coordinate] not in self.cord_pairs:
neighbours += 1
if [x_coordinate+1,y_coordinate+1] in self.cord_pairs and [x_coordinate,y_coordinate] not in self.cord_pairs:
neighbours += 1
if [x_coordinate-1,y_coordinate] in self.cord_pairs and [x_coordinate,y_coordinate] not in self.cord_pairs:
neighbours += 1
if [x_coordinate-1,y_coordinate-1] in self.cord_pairs and [x_coordinate,y_coordinate] not in self.cord_pairs:
neighbours += 1
if [x_coordinate-1,y_coordinate+1] in self.cord_pairs and [x_coordinate,y_coordinate] not in self.cord_pairs:
neighbours += 1
if neighbours == 3:
self.dead_pairs.append([x_coordinate,y_coordinate])
return neighbours
def evaluate_initial_position(self,y_coordinate): # n är y koordinaterna som itereras över
coordinates_to_print = []
if y_coordinate in self.y_cord:
x_in_y = [x_coordinate for x_coordinate, y_values in enumerate(self.y_cord) if y_values == y_coordinate]
for items in range(len(x_in_y)):
coordinates_to_print.append(self.x_cord[x_in_y[items]])
for number_of_rows in range(self.boundry):
board_rows = ''.join('X' if item in coordinates_to_print else '-' for item in list(range(self.boundry)))
return print(board_rows)
def nxt_gen_cell_status(self):
status = {}
for lenght_initial_values in range(len(life.intial_cells())):
if life.neighbours(lenght_initial_values) == 3 or life.neighbours(lenght_initial_values) == 2:
status[tuple(self.cord_pairs[lenght_initial_values])] = "Alive"
elif life.neighbours(lenght_initial_values) < 2 or life.neighbours(lenght_initial_values) > 3:
status[tuple(self.cord_pairs[lenght_initial_values])] = "Dead"
for lenght_dead_cells in range(len(self.dead_pairs)):
status[tuple(self.dead_pairs[lenght_dead_cells])] = "Alive"
return status
def new_cells(self,status):
del self.cord_pairs[:]
for alive_cell in range(len(list(status.keys()))):
kord = list(status.keys())[alive_cell]
if status[kord] == "Alive":
self.cord_pairs.append(list(kord))
return self.cord_pairs
def set_board(self):
x = []
y = []
for new_coordinate in range(len(self.cord_pairs)):
x.append(self.cord_pairs[new_coordinate][0])
y.append(self.cord_pairs[new_coordinate][1])
self.x_cord = x
self.y_cord = y
return self.cord_pairs, self.y_cord
cells = 'www.csc.kth.se/~lk/P/glidare.txt'
life = Game_setup(cells)
def main():
cell_status_ditction = {}
life.intial_cells()
generation = input("How many generations would you like to see?" + "\n")
i = 0
while i < int(generation):
for boundry in range(10):
life.evaluate_initial_position(boundry)
for next_cells in range(len(life.inital_values())):
life.from_dead_to_alive(life.inital_values()[next_cells])
cell_status = life.nxt_gen_cell_status()
cell_status_ditction.update(cell_status)
life.new_cells(cell_status_ditction)
life.set_board()
cell_status_ditction.clear()
print("\n" + "\n")
i += 1
main()
Things to note:
I use a file as a input values [Website for download can be find here: www.csc.kth.se/~lk/P/glidare.txt]
I have just picked an arbitrary number as my boundry input
Everthing works fine except updating new cells between second and third generation – therefore I suspect there must be something wrong with how I wrote my method set_board
The initial position is just the famous glider
If I run this three generations this is the result:
--------------------
---X----------------
-X-X----------------
--XX----------------
--------------------
--------------------
--------------------
--------------------
--------------------
--------------------
--------------------
--X-----------------
---XX---------------
--XX----------------
--------------------
--------------------
--------------------
--------------------
--------------------
--------------------
--------------------
--X-----------------
---XX---------------
--XX----------------
--------------------
--------------------
--------------------
--------------------
--------------------
--------------------
As one can notice the third one is not updating correctly. Is there a fix to this? And should I update my board correctly?
Any help on how I might fix or make my code better is much appreciated. I quite the novice coder so please be gentle; I know this is not the best nor most optimal code.

What it looks like is happening is that your board isn't getting updated with the new state.
I don't know python, so I'm not going to try to line-by-line debug this code (or write python code myself), but generally speaking, a Game of Life simulator should look something like this (I've left some stuff abstracted away, like board's underlying representation, because those are implementation details that are going to vary, and are specific to your code):
#definition of board.get_neighbor_count(cell)
neighbors = 0
for(row in [cell.row - 1, cell.row + 1])
for(column in [cell.column - 1, cell.column + 1])
#We skip the center cell.
if(row == 0 && column == 0) continue
#We don't want to scan cells like [-1,0] or [20,0], which would be out of
#bounds. I don't know how python handles OOB accesses, but I imagine the program
#would crash instead of behaving gracefully
if(!board.in_bounds(row, column)) continue
if(board.get_cell(row, column).alive) neighbors++
return neighbors
#==============================================================
#definition of board.get_next_generation()
Game_Board new_board(rows = board.rows, columns = board.columns, ruleset = board.ruleset)
for(row in [0, board.rows-1])
for(column in [0, board.columns-1])
cell = board.get_cell(row, column)
neighbors = board.get_neighbor_count(cell)
if(cell.alive && board.ruleset.survives(neighbors))
new_board.put(cell)
else if(cell.dead && board.ruleset.births(neighbors))
new_board.put(cell)
return new_board
#==============================================================
#definition of main()
#Should create an empty board size 20x20. I'm assuming they'll be addressed
#[0...rows-1],[0...columns-1]
Game_Board board{rows = 20, columns = 20, ruleset = 23S/3B}
#I'm putting values here for a Gosper Glider, but if your implementation is correct
#one could put literally anything here
board.put_starting_values({10,10}, {10,11}, {10,12}, {11,12}, {12,11})
board.print_state()
num_of_generations = 50
while(num_of_generations > 0)
#Note that I'm assigning a new value to `board` here! This is deliberate and
#intended to convey explicit semantics, in that `board` should now refer to the
#newly generated state, not the preexisting state. If, for whatever reason,
#you need to maintain the original state, either store each successive generation
#in an array or just the original state in a separate variable.
board = board.get_next_generation()
board.print_state()
num_of_generations--
print("Done!")
What you need to do is reduce your main() down to just this logic flow here, and figure out where the error is, as this is the complete logic of a (very simple) GOL simulator. Writing it the way I've written it should make the error very obvious. As it stands, your code contains a lot of cruft, where functions like from_dead_to_alive is trying to duplicate the work that neighbors does (a function itself which is overly complicated). Meanwhile, you've got an entire separate entity called status, responsible for tracking the results of evaluations on these cells, that you shouldn't be depending on and is cluttering your code due to your reliance on it.

Related

Nodes at given distance in binary tree (Amazon SDE-2)

Given a binary tree, a target node in the binary tree, and an integer value k, find all the nodes that are at distance k from the given target node. No parent pointers are available.
link to the problem on GFG: LINK
Example 1:
Input :
20
/ \
8 22
/ \
4 12
/ \
10 14
Target Node = 8
K = 2
Output: 10 14 22
Explanation: The three nodes at distance 2
from node 8 are 10, 14, 22.
My code
from collections import defaultdict
class solver:
def __init__(self):
self.vertList = defaultdict(list)
def addEdge(self,u,v):
self.vertList[u].append(v)
def makeGraph(self,root):
visited = set()
queue = []
queue.append(root)
while len(queue) > 0:
curr = queue.pop(0)
visited.add(curr)
if curr.left is not None and curr.left not in visited:
self.vertList[curr.data].append(curr.left.data)
self.vertList[curr.left.data].append(curr.data)
queue.append(curr.left)
if curr.right is not None and curr.right not in visited:
self.vertList[curr.data].append(curr.right.data)
self.vertList[curr.right.data].append(curr.data)
queue.append(curr.right)
def KDistanceNodes(self,root,target,k):
self.makeGraph(root)
dist = {}
for v in self.vertList:
dist[v] = 0
visited2 = set()
queue2 = []
queue2.append(target)
while len(queue2) > 0:
curr = queue2.pop(0)
visited2.add(curr)
for nbr in self.vertList[curr]:
if nbr not in visited2:
visited2.add(nbr)
queue2.append(nbr)
dist[nbr] = dist[curr] + 1
ans = []
for v in dist:
if dist[v] == k:
ans.append(str(v))
return ans
#{
# Driver Code Starts
#Initial Template for Python 3
from collections import deque
# Tree Node
class Node:
def __init__(self, val):
self.right = None
self.data = val
self.left = None
# Function to Build Tree
def buildTree(s):
# Corner Case
if (len(s) == 0 or s[0] == "N"):
return None
# Creating list of strings from input
# string after spliting by space
ip = list(map(str, s.split()))
# Create the root of the tree
root = Node(int(ip[0]))
size = 0
q = deque()
# Push the root to the queue
q.append(root)
size = size + 1
# Starting from the second element
i = 1
while (size > 0 and i < len(ip)):
# Get and remove the front of the queue
currNode = q[0]
q.popleft()
size = size - 1
# Get the current node's value from the string
currVal = ip[i]
# If the left child is not null
if (currVal != "N"):
# Create the left child for the current node
currNode.left = Node(int(currVal))
# Push it to the queue
q.append(currNode.left)
size = size + 1
# For the right child
i = i + 1
if (i >= len(ip)):
break
currVal = ip[i]
# If the right child is not null
if (currVal != "N"):
# Create the right child for the current node
currNode.right = Node(int(currVal))
# Push it to the queue
q.append(currNode.right)
size = size + 1
i = i + 1
return root
if __name__ == "__main__":
x = solver()
t = int(input())
for _ in range(t):
line = input()
target=int(input())
k=int(input())
root = buildTree(line)
res = x.KDistanceNodes(root,target,k)
for i in res:
print(i, end=' ')
print()
Input:
1 N 2 N 3 N 4 5
target = 5
k = 4
Its Correct output is:
1
And Your Code's output is:
[]
My logic:
-> First convert the tree into a undirected graph using BFS / level order traversal
-> Traverse the graph using BFS and calculate the distance
-> Return the nodes at k distance from the target
What I think:
First of all in the given test case the tree representation seems to be in level order however, in the failing test case it doesn't look like level order or maybe my logic is wrong?
Input Format:
Custom input should have 3 lines. First line contains a string representing the tree as described below. Second line contains the data value of the target node. Third line contains the value of K.
The values in the string are in the order of level order traversal of the tree where, numbers denote node values, and a character “N” denotes NULL child.
For the above tree, the string will be: 1 2 3 N N 4 6 N 5 N N 7 N
The mistake is that the logic that is done in "init" should be done for every use-case (reinitializing self.vertList), not just once at the beginning.
Change:
def __init__(self):
self.vertList = defaultdict(list)
to:
def __init__(self):
pass
and:
def KDistanceNodes(self,root,target,k):
self.makeGraph(root)
dist = {}
...
to:
def KDistanceNodes(self, root, target, k):
self.vertList = defaultdict(list) # <-- move it here
self.makeGraph(root)
dist = {}
...
The original code kept accumulating the nodes and "remembered" the previous use-cases.
Also pay attention that you should return ans sorted, meaning that you should not append the numbers as strings so that you'll be able to sort them, change: ans.append(str(v)) to: ans.append(v) and return sorted(ans).

Dynamic Array Runtime Error - code working but not for large input values | How to resolve?

This is the code I wrote and seems like it's working but when I checked on the Hackerrank for testing with the huge test cases - it's giving me a runtime error.
How can I optimize this code?
def dynamicArray(n, queries):
lastAnswer = 0
seq = []
result = []
for k in range(0, n):
seq.append([])
for i in queries:
N_querytype = i[0] #it can be either 1 or 2
x = i[1]
y = i[2]
index = (x ^ lastAnswer) % n
if(N_querytype == 1):
seq[index].append(y)
elif(N_querytype == 2):
lastAnswer = seq[index][y]
result.append(lastAnswer)
return result
This is the test-case for which it is not running. Is there something I am missing?
Your answer was close but you misunderstood what to do in query 2
Find the value of element y % size in seq (where size is the size of seq) and assign it to
So using index you get the sequence which will be a list, but you are then asked to find the value in the list whic his indexed at position y % size where size = len(seq[index])
def dynamicArray(n, queries):
lastAnswer = 0
seq = []
result = []
for k in range(0, n):
seq.append([])
for i in queries:
N_querytype = i[0] #it can be either 1 or 2
x = i[1]
y = i[2]
index = (x ^ lastAnswer) % n
print("#", index, y)
if(N_querytype == 1):
seq[index].append(y)
elif(N_querytype == 2):
size = len(seq[index]) #Calculate the size of the sequnce at this index
lastAnswer = seq[index][y % size] #look up the value in this sequence at index y % size
result.append(lastAnswer)
return result

How could I set the staring and ending points randomly in a grid that generates random obstacles?

I built a grid that generates random obstacles for pathfinding algorithm, but with fixed starting and ending points as shown in my snippet below:
import random
import numpy as np
#grid format
# 0 = navigable space
# 1 = occupied space
x = [[random.uniform(0,1) for i in range(50)]for j in range(50)]
grid = np.array([[0 for i in range(len(x[0]))]for j in range(len(x))])
for i in range(len(x)):
for j in range(len(x[0])):
if x[i][j] <= 0.7:
grid[i][j] = 0
else:
grid[i][j] = 1
init = [5,5] #Start location
goal = [45,45] #Our goal
# clear starting and end point of potential obstacles
def clear_grid(grid, x, y):
if x != 0 and y != 0:
grid[x-1:x+2,y-1:y+2]=0
elif x == 0 and y != 0:
grid[x:x+2,y-1:y+2]=0
elif x != 0 and y == 0:
grid[x-1:x+2,y:y+2]=0
elif x ==0 and y == 0:
grid[x:x+2,y:y+2]=0
clear_grid(grid, init[0], init[1])
clear_grid(grid, goal[0], goal[1])
I need to generate also the starting and ending points randomly every time I run the code instead of making them fixed. How could I make it? Any assistance, please?.
Replace,
init = [5,5] #Start location
goal = [45,45] #Our goal
with,
init = np.random.randint(0, high = 49, size = 2)
goal = np.random.randint(0, high = 49, size = 2)
Assuming your grid goes from 0-49 on each axis. Personally I would add grid size variables, i_length & j_length
EDIT #1
i_length = 50
j_length = 50
x = [[random.uniform(0,1) for i in range(i_length)]for j in range(j_length)]
grid = np.array([[0 for i in range(i_length)]for j in range(j_length)])

MST challenge gives "time exceeded" error [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I am doing the BLINNET problem on Sphere Online Judge where I need to find the cost of a minimum spanning tree. I should follow a structure with Edge and Vertex instances. Vertices represent cities in this case.
I get a "time exceeded" error, and I feel like too many for loop iterations are at the cause, but that is the best I can do. I want to try the binary sort to see if it works with that, but that is not easy as it should be sorted using the key property in the City class.
Sample input
2
4
gdansk
2
2 1
3 3
bydgoszcz
3
1 1
3 1
4 4
torun
3
1 3
2 1
4 1
warszawa
2
2 4
3 1
3
ixowo
2
2 1
3 3
iyekowo
2
1 1
3 7
zetowo
2
1 3
2 7
Output for the Sample
3
4
My code
import sys
import heapq
class City:
def __init__(self, city_id):
self.city_id = city_id
self.key = float('inf')
self.parent = None
self.edge_list = list()
self.visited = False
#self.city_name = None
def is_not_visited(self):
if self.visited is False:
return True
return False
def add_neighbor(self, edge):
self.edge_list.append(edge)
def __lt__(self, other):
return self.key < other.key
class Edge:
def __init__(self, to_vertex, cost):
self.to_vertex = to_vertex
self.cost = cost
#
# def find_and_pop(queue):
# min = queue[0]
# index = 0
# for a in range(0, len(queue)):
# if queue[a].key < min.key:
# min = queue[a]
# index = a
# return queue.pop(index)
#
def MST(vertices_list):
queue = vertices_list
current = queue[0]
current.key = 0
#visited_list = list()
#heapq.heapify(queue)
total_weight = 0
while queue:
#current = find_and_pop(queue)
current = queue.pop(0)
for edge in current.edge_list:
if edge.to_vertex.is_not_visited():
if edge.cost < edge.to_vertex.key:
edge.to_vertex.key = edge.cost
edge.to_vertex.parent = current
total_weight = total_weight + current.key
current.visited = True
queue = sorted(queue, key=lambda x: x.city_id)
#heapq.heapify(queue)
#visited_list.append(current)
# total_weight = 0
# for x in visited_list:
# total_weight = total_weight + x.key
sys.stdout.write("{0}\n".format(total_weight))
class TestCase:
def __init__(self, vertices):
self.vertices = vertices
testcases = []
def main():
case_num = int(sys.stdin.readline())
#skip_line = sys.stdin.readline()
for n_case in range(0, case_num):
sys.stdin.readline()
vertices_list = list()
number_of_city = int(sys.stdin.readline())
#interate and make for the time of number of cities
for n_city in range(0, number_of_city):
city = City(n_city)
vertices_list.append(city)
for n_city in range(0, number_of_city):
c_name = sys.stdin.readline()
#vertices_list[n_city].city_name = c_name
num_neighbor = int(sys.stdin.readline())
for n_neigh in range(0, num_neighbor):
to_city_cost = sys.stdin.readline()
to_city_cost = to_city_cost.split(" ")
to_city = int(to_city_cost[0])
cost = int(to_city_cost[1])
edge = Edge(vertices_list[to_city-1], cost)
vertices_list[n_city].edge_list.append(edge)
testcase = TestCase(vertices_list)
testcases.append(testcase)
count = 0
for testcase in testcases:
MST(testcase.vertices)
# if count < case_num -1:
# print()
# count = count + 1
if __name__ == "__main__":
main()
The sorted call in your MST loop makes the solution inefficient. You have some commented-out code that relies on heapq, and that is indeed the way to avoid having to sort the queue each time you alter it. Anyway, I don't understand why you would sort the queue by city id. If anything, it should be sorted by key.
Although it could work with the key property as you did it, it seems more natural to me to add edges to the queue (heap) instead of vertices, so you have the edge cost as the basis for the heap property. Also, that queue should not have all the items from the start, but add them as they are selected during the algorithm. And, that corresponds more the the MST-building algorithm, which adds edge after edge, each time the one with the minimum cost.
If edges are pushed on a heap, they must be comparable. So __lt__ must be implemented on the Edge class like you did for the Vertex class.
class Edge:
# ... your code remains unchanged... Just add:
def __lt__(self, other):
return self.cost < other.cost
def MST(vertices_list):
# first edge in the queue is a virtual one with zero cost.
queue = [Edge(vertices_list[0], 0)] # heap of edges, ordered by cost
total_weight = 0
while queue:
mst_edge = heapq.heappop(queue) # pop both cost & vertex
current = mst_edge.to_vertex
if current.visited: continue
for edge in current.edge_list:
if not edge.to_vertex.visited:
heapq.heappush(queue, edge)
current.visited = True
total_weight += mst_edge.cost
sys.stdout.write("{0}\n".format(total_weight))

Unique Paths II

A robot is located at the top-left corner of a m x n grid.
The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid.
if some obstacles are added to the grids. How many unique paths would there be?
An obstacle and empty space is marked as 1 and 0 respectively in the grid.
class Solution:
"""
#param obstacleGrid: A list of lists of integers
#return: An integer
"""
def uniquePathsWithObstacles(self, obstacleGrid):
# write your code here
if not obstacleGrid:
return 0
m = len(obstacleGrid)
n = len(obstacleGrid[0])
li = [[0] * n] * m
for i in range(m):
for j in range(n):
if obstacleGrid[i][j] == 1:
li[i][j] = 0 ######## why do I have to add this line ???########
continue
elif i == 0 and j == 0:
li[i][j] = 1
elif i == 0:
li[i][j] = li[i][j - 1]
elif j == 0:
li[i][j] = li[i - 1][j]
else:
li[i][j] = li[i - 1][j] + li[i][j - 1]
return li[m - 1][n - 1]
The question is in the coding. I already set the matrix of m*n filling with zeros. Why should I assign the zero to the position one more time??? It seems that it won't work if I del that line. Can anyone tell me the reason why??? Thx!!!
The problem is this line:
li = [[0] * n] * m
The syntax [a] * n creates shallow copies, not deep copies of a.
Example:
n = m = 2
li[0][0] = 3
print(li) # prints [[3, 0], [3, 0]]
Link to question with discussion of possible solutions.

Resources