Things used in this project
|Software apps and online services:|
The universe of the Game of Life is an infinite two-dimensional orthogonal grid of square cells, each of which is in one of two possible states, alive or dead, or "populated" or "unpopulated". Every cell interacts with its eight neighbours, which are the cells that are horizontally, vertically, or diagonally adjacent. At each step The universe of the Game of Life is an infinite two-dimensional orthogonal grid of square cells, each of which is in one of two possible states, alive or dead, or "populated" or "unpopulated". Every cell interacts with its eight neighbours, which are the cells that are horizontally, vertically, or diagonally adjacent. At each step in time, the following transitions occur:
Any live cell with fewer than two live neighbours dies, as if caused by underpopulation.
Any live cell with two or three live neighbours lives on to the next generation.
Any live cell with more than three live neighbours dies, as if by overpopulation.
Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
The initial pattern constitutes the seed of the system. The first generation is created by applying the above rules simultaneously to every cell in the seed—births and deaths occur simultaneously, and the discrete moment at which this happens is sometimes called a tick (in other words, each generation is a pure function of the preceding one). The rules continue to be applied repeatedly to create further generations.
Extracted from the Wikipedia page about Conway's Game of Life.
In this implementation the universe is a toroidal 5x5 grid, this means the top 5 cells are adiacent with the bottom 5 cells, the same for the left and right cells.
With a so small space available some patterns acts in a strange way, we start exploring it.
Download the hex file below and upload it to the mico:bit.
If you don't know how to upload the hex file you can find a tutorial here, the detailed instructions are in step 3.
Go to this BBC micro:bit page, press "Create Code" and choose "MicroPython", now you are in the Python Editor.
Remove the existing code and copy / paste the code below. Name your project (top right).
Follow the "Instructions" on the right to compile and uploat the script to your micro:bit.
The script has 2 modes: "CONFIG" and "RUN".
In the CONFIG mode you can set up the start configuration, using the B button you can RUN it and watch the universe evolve, at any time you can modify the universe and watch what's happen.
- Tilt: move the cursor.
- Button A: enable or disable the cell under the cursor.
- Button B: switch to RUN mode.
- Button B: switch to CONFIG mode.
Some patterns to try
Blinker: an oscillator with period 2.
Toad: another oscillator with period 2.
Glider: a spaceship.
Line and dot: an oscillator with period 4.
The code explained
At rows 3 an 5 there are the 2 "draw" functions: draw_universe and draw_cursor. These functions sets the led luminosity at various levels, 0 for an empty cell, 9 for a populated cell, 4 for and empty cell with the cursor on it and 6 for a populated cell with a cursor on it.
The core is the function evolve (row 15), in this function there are all the rules defined on top of this page.
At rows 33 and 39 there are 2 support function for evolve: cell_state and count_neighbours. In count_neighbours at row 43 there's the trick for the toroidal universe: the module operator (%). The same trick is in the row 73 and following.
At row 59 there's the main loop, it is basically divided in 2 parts: the RUN mode and the CONFIG mode.
The interesting thing here is the accelerometer managing at row 71. The accelerometer values (x, y, z) goes from -1024 to +1024, when it is flat the x and the y values are both (more on less) 0. In this case a grace zone of 200 is used to avoid unwanted cursor movements.
from microbit import * def draw_cursor(universe, mode, x, y): if mode == "CONFIG": if cell_state(universe, x, y) == 0: display.set_pixel(x, y, 4) else: display.set_pixel(x, y, 6) def draw_universe( universe ): for y in range(0, 5): for x in range(0, 5): display.set_pixel(x, y, universe[x + y * 5]) def evolve( universe ): next_universe =  for y in range(0, 5): for x in range(0, 5): cell_neighbours = count_neighbours(universe, x, y) cell_is_alive = cell_state(universe, x, y) == 1 if cell_is_alive and cell_neighbours < 2: next_universe.append(0) elif cell_is_alive and (cell_neighbours == 2 or cell_neighbours == 3): next_universe.append(9) elif cell_is_alive and cell_neighbours > 3: next_universe.append(0) elif not cell_is_alive and cell_neighbours == 3: next_universe.append(9) else: next_universe.append(0) return next_universe def cell_state(universe, x, y): state = 1 if universe[x + 5 * y] == 0: state = 0 return state def count_neighbours(universe, x, y): neighbours = -cell_state(universe, x, y) for dy in [-1, 0, 1]: for dx in [-1, 0, 1]: neighbours += cell_state(universe, (x + dx) % 5, (y + dy) % 5) return neighbours current_universe = [ 0, 0 ,0 ,0 ,0, 0, 0, 9, 0, 0, 0, 0, 9, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0,] cursor_x = 2 cursor_y = 2 mode = "CONFIG" display.scroll(mode) while True: if mode == "RUN": current_universe = evolve( current_universe ) if button_b.is_pressed(): mode = "CONFIG" display.scroll(mode) if mode == "CONFIG": accelerometer_xyz = accelerometer.get_values() if accelerometer_xyz < -200: cursor_x = (cursor_x - 1) % 5 if accelerometer_xyz > 200: cursor_x = (cursor_x + 1) % 5 if accelerometer_xyz < -200: cursor_y = (cursor_y - 1) % 5 if accelerometer_xyz > 200: cursor_y = (cursor_y + 1) % 5 if button_a.is_pressed(): if cell_state(current_universe, cursor_x, cursor_y) == 0: current_universe[cursor_x + 5 * cursor_y] = 9 else: current_universe[cursor_x + 5 * cursor_y] = 0 if button_b.is_pressed(): mode = "RUN" display.scroll(mode) draw_universe( current_universe ) draw_cursor(current_universe, mode, cursor_x, cursor_y) sleep(1000)
Did you replicate this project? Share it!I made one
Love this project? Think it could be improved? Tell us what you think!