Iterations
The pythonic way to iterate is to use the for
keyword, usually without bothering about indices. I suggest you have a look at the presentation : Ned Batchelder: Loop like a native: while, for, iterators, generators (PyCon US) (video on Youtube/text with slides).
In your case, most loops can be rewritten. For instance :
n = 0
while n < testrect[2]:
m = 0
while m < testrect[3]:
o = 0
while o < len(stillLifes):
testReaction(n + testrect[0], m + testrect[1], o)
o += 1
m += 1
n += 1
could be written :
for n in range(testrect[2])
for m in range(testrect[3]):
for o, _ in enumerate(stillLifes):
testReaction(n + testrect[0], m + testrect[1], o)
Also, the for
loop accepts an optional else
block after it which is called when the loop exited with no break
(just because the thing we are iterating over has been fully consumed). This can be useful to rewrite :
i = 0
while i < 301:
g.step()
for y in herschels:
if g.getcells(g.getrect()) == y:
break
i += 1
if i == 301:
g.show("No result found at x:" + str(xpos) + ", y:" + str(ypos) + ", still life " + str(sli))
in a more straightforward way (and without repeating 301
in 2 places) :
for i in range(300)
g.step()
for y in herschels:
if g.getcells(g.getrect()) == y:
break
else:
g.show("No result found at x:" + str(xpos) + ", y:" + str(ypos) + ", still life " + str(sli))
Disclaimer for the snipper above: I may have misread your original code so this might be wrong.
User input
At the moment, there are a few issues with the way you deal with user input.
- you have to provide
"HRF v0.1"
several times.
You could write a function to hide implementation details and remove duplicated logic :
def get_user_string(prompt, initial):
title = "HRF v0.1"
return g.getstring(prompt, initial, title)
You are calling lower()
on the same string multiple times. If only the lowercase version of the string is interesting to you, you could simple do : addAnother = get_user_string("Add another result desired? (y|n)", "n").lower()
.
You have logic to ask boolean input and check it in multiple places
Your program stops in case of invalid logic, it might be better to ask the user to re-input something.
In order to fix these issues, you could define something like :
def get_user_string(prompt, initial):
title = "HRF v0.1"
return g.getstring(prompt, initial, title)
def get_user_bool(prompt, initial):
while True:
string = get_user_string(prompt, initial).lower()
if string == "n":
return False:
elif string == "y":
return True
else:
golly.error("Invalid input.")
That you could use like this :
addAnother = get_user_bool("Add another result desired? (y|n)", "n")
if not addAnother:
break
or this :
if get_user_bool("Debug mode? (y|n)", "n"):
g.autoupdate(True)
This shows a better Separation of concerns.
Code organisation
Instead of dealing with global variables, it would be cleaner to try to find alternatives. In your case, using function argument and return values should make things clearer.
You could define :
def get_user_still_lifes():
stillLifes = []
while True:
stillLife = get_user_string("What still life to test for? (RLE)", "2o$2o!")
stillLifes.append(g.parse(stillLife))
if not get_user_bool("Add another still life? (y|n)", "n"):
break
return stillLifes
def get_user_desired_results():
herschels = []
while True:
herschelDesired = get_user_string("What result to find? (RLE)", "3o$obo$obo!")
herschels.append(g.parse(herschelDesired))
if not get_user_bool("Add another result desired? (y|n)", "n"):
break
return herschels
and use it like this :
stillLifes = get_user_still_lifes()
herschels = get_user_desired_results()
Also it is a good habit to put the part of your code actually doing things behind an if __name__ == "__main__"
guard.
Style
Python has a style guide called PEP 8. It is an interesting read and it is probably worth trying to follow it if you don't have a good reason not to. For instance, your code does not follow the naming convention. You'll find various tools to check your code compliancy to PEP 8.
Final (untested) code
The following code has not been tested, feel free to edit it to fix errors and typos I may have introduced.
import golly as g
from sys import exit
def get_user_string(prompt, initial):
title = "HRF v0.1"
return g.getstring(prompt, initial, title)
def get_user_bool(prompt, initial):
while True:
string = get_user_string(prompt, initial).lower()
if string == "n":
return False:
elif string == "y":
return True
else:
golly.error("Invalid input.")
def get_user_rectangle():
while True:
rect = g.getselrect();
if rect == []:
golly.error("Select the area where the still life is placeable!")
elif g.empty():
g.error("There is no pattern to add bait!")
else:
return rect
def get_user_still_lifes():
still_lifes = []
while True:
stillLife = get_user_string("What still life to test for? (RLE)", "2o$2o!")
still_lifes.append(g.parse(stillLife))
if not get_user_bool("Add another still life? (y|n)", "n"):
break
return still_lifes
def get_user_desired_results():
herschels = []
while True:
herschelDesired = get_user_string("What result to find? (RLE)", "3o$obo$obo!")
herschels.append(g.parse(herschelDesired))
if not get_user_bool("Add another result desired? (y|n)", "n"):
break
return herschels
def test_reaction(still_lifes, herschels, xpos, ypos, sli):
orig = g.getcells(g.getrect())
g.putcells(still_lifes[sli], xpos, ypos)
this = g.getcells(g.getrect())
oldgeneration = g.getgen()
for i in range(300)
g.step()
for y in herschels:
if g.getcells(g.getrect()) == y:
break
else:
g.show("No result found at x:" + str(xpos) + ", y:" + str(ypos) + ", still life " + str(sli))
g.clear(0)
g.clear(1)
if i == 301:
this = g.putcells(orig)
else:A
g.show("Found reaction!")
g.putcells(this)
g.setgen(oldgeneration)
if __name__ == "__main__":
testrect = get_user_rectangle()
still_lifes = get_user_still_lifes()
herschels = get_user_desired_results()
if get_user_bool("Debug mode? (y|n)", "n"):
g.autoupdate(True)
for n in range(testrect[2])
for m in range(testrect[3]):
for o, _ in enumerate(still_lifes):
test_reaction(still_lifes, herschels, n + testrect[0], m + testrect[1], o)