Reputation:
We've been tasked with writing a program which simulates a 1-dimensional Game of Life. We are supposed to create a spider pattern. I've gotten this to work, however there needs to be an obscene amount of spaces in the first string initialisation for it to print the entire figure, as each line loses 2 spaces. What should the range in my for-loop be for it to print the entire figure, regardless of the initialisation?
Note: We are not supposed to use arrays, matrices or any other clever methods or approaches. I know of them, but they are only expecting if-statements, while/for -loops, and variables (primarily).
I've written this code:
def main():
a=" ****** "
print(a)
empty=False
while (empty==False):
b=""
for i in range(len(a)-2):
alive_cells=0
if (i==0):
if(a[i+1]=="*"):
alive_cells+=1
if(a[i+2]=="*"):
alive_cells+=1
elif (i==1):
if(a[i-1]=="*"):
alive_cells+=1
if(a[i+1]=="*"):
alive_cells+=1
if(a[i+2]=="*"):
alive_cells+=1
else:
if(a[i-2]=="*"):
alive_cells+=1
if(a[i-1]=="*"):
alive_cells+=1
if(a[i+1]=="*"):
alive_cells+=1
if(a[i+2]=="*"):
alive_cells+=1
if (a[i]==" " and (alive_cells==1 or alive_cells==4 or alive_cells==0)):
b+=" "
elif(a[i]==" " and (alive_cells==2 or alive_cells==3)):
b+="*"
elif(a[i]=="*" and (alive_cells==0 or alive_cells==1 or alive_cells==3)):
b+=" "
elif(a[i]=="*" and (alive_cells==2 or alive_cells==4)):
b+="*"
if (b.isspace()==True):
empty=True
break
a=b
print(a)
main()
Which prints the following output:
******
** ** **
* * ** * *
********
** ****
* *
*
It's supposed to print this:
******
** ** **
* * ** * *
********
** **** **
* * * *
* *
Upvotes: 1
Views: 178
Reputation: 13067
Given the following rules:
## ----------------------
## All cells change state simultaneously from one "generation" to the next
## based on their current state, and the current state of their four
## surrounding neighbors (2 to their left and 2 to their right) according
## to the following rules:
## Rule 1:
## a Dead cell lives if it has either two or three living neighbors
## Rule 2:
## a Live cell dies if if has zero, one, or three living neighbors.
## ----------------------
The tricky part here is to calculate the correct indexes to iterate over when counting neighbors. You don't want to count negative indexes, the current index or indexes that would exceed the length of the list.
How might we get such a list of valid indexes to search when our neighbors are cells within 2 of us.
let's take a look at:
gen0 = " ****** "
for cell_index, value in enumerate(gen0):
search_indexes = [
i for i in range(
max(0, cell_index-2), # not out of bounds low
min(len(gen0), cell_index+3) # not out of bounds high
)
if i != cell_index # not ourselves
]
print(cell_index, search_indexes)
So for each cell in our input (gen0), we get back a list of cells to search for neighbors. We then just need to count up how many of those are "living" and then apply our rules.
Note that this gives us the indexes of ourselves, out two nearest neighbors but not out of bounds neighbors
def next_generate(gen0):
gen0 = list(gen0)
gen1 = gen0.copy()
for cell_index, value in enumerate(gen0):
living_neighbors = sum(
gen0[i] == "*" and i != cell_index # live and not "us"
for i in range(
max(0, cell_index-2), # not out of bounds low
min(len(gen0), cell_index+3) # not out of bounds high
)
)
if value == " " and living_neighbors in [2, 3]:
gen1[cell_index] = "*" # Rule 1
elif value == "*" and living_neighbors in [0, 1, 3]:
gen1[cell_index] = " " # Rule 2
return "".join(gen1)
def main(gen0):
while gen0.strip():
print(gen0)
gen0 = next_generate(gen0)
main(" ****** ")
Should result in:
******
** ** **
* * ** * *
********
** **** **
* * * *
* *
Upvotes: 0
Reputation: 56855
The problem is failing to check the remaining two indices and condition them as you did with the left side.
Better yet, bake the conditions into the index checks directly to avoid redundancy:
def main():
a = " ****** "
while not a.isspace():
print(a)
b = ""
for i in range(len(a)):
alive = 0
if i > 1 and a[i - 2] == "*":
alive += 1
if i > 0 and a[i - 1] == "*":
alive += 1
if i < len(a) - 1 and a[i + 1] == "*":
alive += 1
if i < len(a) - 2 and a[i + 2] == "*":
alive += 1
if a[i] == " " and (alive < 2 or alive == 4):
b += " "
elif a[i] == "*" and (alive < 2 or alive == 3):
b += " "
else:
b += "*"
a = b
if __name__ == "__main__":
main()
Always format your code with Black so it's easier to read and debug, both for yourself and for others.
Upvotes: 0
Reputation: 3409
Here's another version with guard tests, and compacting the tests for adding ' ' or '*' to b:
def main():
a=" ****** "
n = len(a)
print(a)
while not a.isspace():
b=""
for i in range(n):
alive_cells = 0
if i-2 >= 0 and a[i-2] == "*":
alive_cells+=1
if i-1 >= 0 and a[i-1] == "*":
alive_cells+=1
if i+1 < n and a[i+1] == "*":
alive_cells+=1
if i+2 < n and a[i+2]=="*":
alive_cells+=1
if (
(a[i]==" " and (alive_cells==2 or alive_cells==3)) or
(a[i]=="*" and (alive_cells==2 or alive_cells==4))
):
b+="*"
else:
b+=" "
a=b
print(a)
Upvotes: 0
Reputation: 27588
Instead of adding those extra spaces at the start, I'd just append three spaces each round. Then you also don't need the extra edge case distinction:
def main():
a=" ******"
print(a)
empty=False
while (empty==False):
b=""
a += " "
for i in range(len(a)-2):
alive_cells=0
if(a[i-2]=="*"):
alive_cells+=1
if(a[i-1]=="*"):
alive_cells+=1
if(a[i+1]=="*"):
alive_cells+=1
if(a[i+2]=="*"):
alive_cells+=1
if (a[i]==" " and (alive_cells==1 or alive_cells==4 or alive_cells==0)):
b+=" "
elif(a[i]==" " and (alive_cells==2 or alive_cells==3)):
b+="*"
elif(a[i]=="*" and (alive_cells==0 or alive_cells==1 or alive_cells==3)):
b+=" "
elif(a[i]=="*" and (alive_cells==2 or alive_cells==4)):
b+="*"
if (b.isspace()==True):
empty=True
break
a=b
print(a)
main()
Output (Attempt This Online!):
******
** ** **
* * ** * *
********
** **** **
* * * *
* *
Upvotes: 1