8
\$\begingroup\$

I coded a simulator of the Game Of Life by Conway. I made using Kivy, so that it has a graphical interface.

Any suggestion on how to improve its pythonism or performance would be highly appreciated.

This it my .kv file:

<MenuGrid>:
 id: actions
 cols: 1
 rows: 1
 BoxLayout:
 size_hint_y: None
 height: 30
 spacing: 10
 canvas:
 Color:
 rgba: 1, 1, 1, 1
 Rectangle:
 pos: self.pos
 size: self.size
 Button:
 text: str(actions.clear_stop)
 on_press: actions.clear_action()
 Button:
 text: str(actions.start_pause)
 on_press: actions.start_action()

And this is my .py file:

from kivy.config import Config
Config.set('graphics', 'width', 903)
Config.set('graphics', 'height', 933)
from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.togglebutton import ToggleButton
from kivy.clock import Clock
from kivy.properties import StringProperty
class MenuGrid(GridLayout):
 active = False # Defines whether the game is running, or paused.
 start_pause = StringProperty('Start')
 clear_stop = StringProperty('Clear')
 clock = None
 def start_action(self):
 # If game is paused, run it, making an iteration every half a second
 # Change the labels to Pause and Stop
 if self.active is False:
 for instance in CustomButton.cells:
 instance.disabled = True
 self.clock = Clock.schedule_interval(iteration, 0.5)
 self.active = True
 self.start_pause = 'Pause'
 self.clear_stop = 'Stop'
 # Else, unschedule the game and change the labels to Start and Clear
 elif self.active is True:
 self.clock.cancel()
 for instance in CustomButton.cells:
 instance.disabled = False
 self.active = False
 self.start_pause = 'Start'
 self.clear_stop = 'Clear'
 def clear_action(self):
 # Stop the game if running, and change all instances to their normal
 # state and set them to False, so that they are not considered as
 # living cells in the next game.
 if self.active is False:
 for instance in CustomButton.cells:
 instance.state = 'normal'
 instance.phase = False
 elif self.active is True:
 self.start_action()
 for instance in CustomButton.cells:
 instance.state = 'normal'
 instance.phase = False
class CustomButton(ToggleButton):
 # Class for cells. Give them an id and a False phase.
 # The phase tells the program if the cell is alive or dead.
 # Store them in a list
 cells = []
 def __init__(self, index, **kwargs):
 super(CustomButton, self).__init__(**kwargs)
 self.id = str(index)
 self.phase = False
 CustomButton.cells.append(self)
 def __str__(self):
 return 'Index: {}'.format(self.id)
 def __int__(self):
 return int(self.id)
def iteration(dt):
 # Get the list of cells.
 # Create temporary list
 cells = CustomButton.cells
 next_iteration = []
 # For each cell, count its living neighbours by trying to access them and
 # checking their phase
 for i, instance in enumerate(cells):
 neighbours = [-51, -50, -49, -1, 1, 49, 50, 51]
 living_neighbours = 0
 for x in neighbours:
 try:
 if cells[i + x].phase is True:
 living_neighbours += 1
 except IndexError:
 pass
 if living_neighbours == 4:
 break
 # Apply the game's rules
 # 1: Any live cell with fewer than two live neighbours dies,
 # as if caused by underpopulation.
 # 2: Any live cell with two or three live neighbours lives on to
 # the next generation.
 # 3: Any live cell with more than three live neighbours dies,
 # as if by overpopulation.
 # 4: Any dead cell with exactly three live neighbours becomes a
 # live cell, as if by reproduction.
 if instance.phase is True:
 if living_neighbours < 2:
 next_iteration.append(False)
 elif living_neighbours > 3:
 next_iteration.append(False)
 else:
 next_iteration.append(True)
 else:
 if living_neighbours == 3:
 next_iteration.append(True)
 else:
 next_iteration.append(False)
 # Change the state of the buttons according to their phases
 for i, instance in enumerate(cells):
 instance.phase = next_iteration[i]
 if next_iteration[i] is True:
 instance.state = 'down'
 else:
 instance.state = 'normal'
def change_phase(instance):
 # If a cell is clicked, turn it to its opposite phase.
 # True - False
 # Alive - Dead
 if instance.phase is True:
 instance.phase = False
 else:
 instance.phase = True
class GameOfLifeApp(App):
 # Build the game
 # Create a 50x50 grid, and assign a button to each cell.
 # Bind the button to the change_phase() function
 def build(self):
 layout_base = GridLayout(cols=1, row_force_default=True,
 row_default_height=30)
 layout_base.add_widget(MenuGrid())
 layout = GridLayout(cols=50, spacing=3, padding=3)
 for i in range(0, 2500):
 button = CustomButton(i, size_hint=[None, None], width=15,
 height=15)
 button.bind(on_press=change_phase)
 layout.add_widget(button)
 layout_base.add_widget(layout)
 return layout_base
# Run the game
if __name__ == '__main__':
 GameOfLifeApp().run()

In the comments I explain what everything is supposed to do, but if you have doubts, please tell me and I'll try to be clearer.

Peilonrayz
44.4k7 gold badges80 silver badges157 bronze badges
asked May 11, 2017 at 22:05
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

You do not need to check if variable is True/None/False like this if instance.phase is True. You can write just if instance.phase (check if True), or if not instance.phase. (check if False).

next thing, you can write this function in one line using "Conditional expressions" - http://book.pythontips.com/en/latest/ternary_operators.html

def change_phase(instance):
 # If a cell is clicked, turn it to its opposite phase.
 # True - False
 # Alive - Dead
 if instance.phase is True:
 instance.phase = False
 else:
 instance.phase = True

one liner:

def change_phase_2(instance):
 # If a cell is clicked, turn it to its opposite phase.
 # True - False
 # Alive - Dead
 instance.phase = not instance.phase

you can also read about Generators here http://book.pythontips.com/en/latest/generators.html .

answered May 15, 2017 at 13:11
\$\endgroup\$
0

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.