game_of_life.๐ฅ - a late night Mojo hack
Let's start from yesterday
I'm not a huge fan of really long form podcasts, the main issue really being the signal to chatter ratio and the freely available time I have. Having said that, yesterday I was sent a link to a podcast episode where Chris Lattner was talking for hours about Mojo. I had several hours of train ride planned during the day, so I figured that it would be perfect to listen to the conversation. It took a while to get through even at 1.9x.
Perhaps it was too much coffee or the thoughts in my head, but I sat down after 11pm and tried to write some Mojo. Around 2am in the morning I had a very hacky version of Conway's Game of Life working. It's the kind of code I'll mostly likely throw away, but I figured I'd include it on the internet as a historical object of embarrassment for my future self.
Mojo ๐ฅ ?
Mojo is a new programming language primarily targeted 'for all AI developers'. Mojo aims to combine the ease of Python syntax (plus ecosystem) with the performance of systems programming languages such as Rust, while providing compile time metaprogramming capabilities. It is early days for Mojo and I'd urge the reader to visit the language manual to learn more about it.
I recently had the opportunity to explore the basics of Mojo with a fast.ai meetup group. Please feel free to view the slides and the video recordings here.
Game of Life
Back to the late night hacks, I'd recently played around with Conway's Game of Life and figured that this would be a pretty good small challenge to attempt in Mojo. With additional time, I could even try to render the game grid using Matplotlib and hence test the Python ecosystem interop.
The plan was set to implement a Gilder gun and watch it evolve. Something like this! (NOTE: the following is not the final result.)
game_of_life.๐ฅ
After referring to the various notebooks included in Modular playgroud, I was able to come up with some code that setup the Glider gun and started emitting gliders. This is pretty much my first throwaway Mojo code, I've basically copied over things from various notebooks "until it worked", so I will not bother walking through it. The reader is urged to not use any of this code. I'm only including it here for my own historical reference.
A Boolean Matrix (Batrix) from scratch*
from IO import print_no_newline
from DType import DType
from Pointer import DTypePointer
struct Batrix:
var data: DTypePointer[DType.bool]
var rows: Int
var cols: Int
fn __init__(inout self, rows: Int, cols: Int, val: Bool):
self.rows = rows
self.cols = cols
self.data = DTypePointer[DType.bool].alloc(rows * cols)
for r in range(self.rows):
for c in range(self.cols):
self[r, c] = False
@always_inline
fn __getitem__(self, row: Int, col: Int) -> SIMD[DType.bool, 1]:
return self.load[1](row, col)
@always_inline
fn load[nelts:Int](self, row: Int, col: Int) -> SIMD[DType.bool, nelts]:
return self.data.simd_load[nelts](row * self.cols + col)
@always_inline
fn __setitem__(self, row: Int, col: Int, val: SIMD[DType.bool, 1]):
return self.store[1](row, col, val)
@always_inline
fn store[nelts:Int](self, row: Int, col: Int, val: SIMD[DType.bool, nelts]):
self.data.simd_store[nelts](row * self.cols + col, val)
# to_numpy below returns PythonObject
# which requires self to be copyable
fn __copyinit__(inout self, other: Self):
self.data = other.data
self.rows = other.rows
self.cols = other.cols
def to_numpy(self) -> PythonObject:
let np = Python.import_module("numpy")
let numpy_array = np.zeros((self.rows, self.cols), np.bool)
for col in range(self.cols):
for row in range(self.rows):
numpy_array.itemset((row, col), self[row, col])
return numpy_array
fn dump(self):
for x in range(self.rows):
for y in range(self.cols):
if self[x,y] == True:
print_no_newline("โ")
else:
print_no_newline("-")
print("")
The GameOfLife board
struct Game:
var board: Batrix
var rows: Int
var cols: Int
fn __init__(inout self, rows: Int, cols: Int):
self.rows = rows
self.cols = cols
self.board = Batrix(rows, cols, False)
fn neighbour_count(self, row: Int, col: Int) -> Int:
var count = 0
for rOff in range(-1, 2):
for cOff in range(-1, 2):
if not ((rOff == 0) and (cOff == 0)):
if (self.board[row + rOff, col + cOff] == True):
count += 1
return count
fn replace_board(inout self, owned _board: Batrix):
self.board = _board
fn step(inout self):
let next_board = Batrix(self.rows, self.cols, False)
for row in range(self.rows):
for col in range(self.cols):
if self.board[row, col] == True:
if (self.neighbour_count(row, col) == 2) or (self.neighbour_count(row, col) == 3):
next_board[row, col] = True
else:
if self.neighbour_count(row, col) == 3:
next_board[row, col] = True
self.replace_board(next_board)
fn evolve(inout self, steps: Int):
for _ in range(steps):
self.step()
fn dump(self):
self.board.dump()
The initial setup for the Gosper Glider Gun
g = Game(40, 80)
# Setup a Glider gun
# https://en.wikipedia.org/wiki/Gun_(cellular_automaton)
g.board[5, 1] = True
g.board[5, 2] = True
g.board[6, 1] = True
g.board[6, 2] = True
g.board[5, 11] = True
g.board[6, 11] = True
g.board[7, 11] = True
g.board[4, 12] = True
g.board[8, 12] = True
g.board[3, 13] = True
g.board[9, 13] = True
g.board[3, 14] = True
g.board[9, 14] = True
g.board[6, 15] = True
g.board[4, 16] = True
g.board[8, 16] = True
g.board[5, 17] = True
g.board[6, 17] = True
g.board[7, 17] = True
g.board[6, 18] = True
g.board[3, 21] = True
g.board[4, 21] = True
g.board[5, 21] = True
g.board[3, 22] = True
g.board[4, 22] = True
g.board[5, 22] = True
g.board[2, 23] = True
g.board[6, 23] = True
g.board[1, 25] = True
g.board[2, 25] = True
g.board[6, 25] = True
g.board[7, 25] = True
g.board[3, 35] = True
g.board[4, 35] = True
g.board[3, 36] = True
g.board[4, 36] = True
g.evolve(150)
g.dump()
Game board initilization output
After the setup above, the following is the result of printing the game board. The Glider gun is ready to shoot gliders.
--------------------------------------------------------------------------------
-------------------------โ------------------------------------------------------
-----------------------โ-โ------------------------------------------------------
-------------โโ------โโ------------โโ-------------------------------------------
------------โ---โ----โโ------------โโ-------------------------------------------
-โโ--------โ-----โ---โโ---------------------------------------------------------
-โโ--------โ---โ-โโ----โ-โ------------------------------------------------------
-----------โ-----โ-------โ------------------------------------------------------
------------โ---โ---------------------------------------------------------------
-------------โโ-----------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
Evolve the board
After evolving the board through 150 generations
g.evolve(150)
g.dump()
the glider generators seem to be working well
-------------------------------------โโโ----------------------------------------
-------------------------โ---------โ---โ----------------------------------------
-----------------------โ-โ--------โโโโโ-----------------------------------------
-------------โโ------โโ----------โ----โ-----------------------------------------
------------โ---โ----โโ-----------โโโ-------------------------------------------
-โโ--------โ-----โ---โโ------------โ--------------------------------------------
-โโ--------โ---โ-โโ----โ-โ------------------------------------------------------
-----------โ-----โ-------โ------------------------------------------------------
------------โ---โ---------------------------------------------------------------
-------------โโ-----------------------------------------------------------------
------------------------โ-------------------------------------------------------
-------------------------โโ-----------------------------------------------------
------------------------โโ------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-------------------------------โ-โ----------------------------------------------
--------------------------------โโ----------------------------------------------
--------------------------------โ-----------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
---------------------------------------โ----------------------------------------
----------------------------------------โโ--------------------------------------
---------------------------------------โโ---------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
----------------------------------------------โ-โ-------------------------------
-----------------------------------------------โโ-------------------------------
-----------------------------------------------โ--------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
------------------------------------------------------โ-------------------------
-----------------------------------------------------โ-โ------------------------
Bonus section - Matplotlib integration
It was still only 2am, so I figured I could push 30 more minutes until I figured out how to use Python integration and used matplotlib to render the game board. Here's the result. Not bad, I'd say. Maybe I'll actually render a gif next time. This is good enough to throw away for today.
def plot_board(inout game: Game):
np = Python.import_module("numpy")
plt = Python.import_module("matplotlib.pyplot")
fig = plt.figure(1, [10, 10*game.board.cols//game.board.rows])
ax = fig.add_axes([0.0, 0.0, 1.0, 1.0], False, 1)
plt.imshow(game.board.to_numpy(), 'gray')
plt.axis("off")
plt.show()
plot_board(g)