Add game-of-life for WebAssembly

I have considered multiple approaches for the data - as utf8 string or an array of offsets / lengths for the rows, but I found a flat array together with the number of columns and rows the easiest to work with. However, this adds the problem of border detection. I have added a note of this to the instructions, but I’m not sure if this makes it too easy to solve this.

For the assembly tracks, my intention is that minesweeper uses a string with newlines for the input/output (see MIPS, WASM), and that game-of-life uses an array of words, with each word representing a row in binary. The MIPS and Web Assembly tracks would use 32 bit words.

For example,

        "matrix": [
          [0, 1, 0],
          [1, 0, 0],
          [1, 1, 0]
        ]

would be represented as [0x2, 0x4, 0x6].

x86-64-assembly / arm64-assembly: void tick(uint64_t *buffer, const uint64_t *matrix, size_t row_count, size_t column_count);
MIPS: number of rows, number of columns, address of input words, address of output words
WASM: We usually pass in and out an offset and a byte length. For this exercise we can also pass in the number of rows and the number of columns. (In this case the input byte length and the number of rows are not both necessary, but there is a downside to omitting either of them.) (export "tick") (param $offset i32) (param $length i32) (param $rowCount i32) (param $columnCount i32) (result i32 i32) We also add an instructions.append.md (minesweeper, all-your-base, knapsack) describing how the exercise uses linear memory. These are usually short, like for minesweeper.

instructions.append.md would specify the maximum number of columns as 32/64.

My intention was also to generate an extra 5-row, 32/64-column test case, so that at least one test case has high bits set in the input and output. (My intention was that the input and output would contain examples of live cells with 0…8 live neighbors, and examples of dead cells with 0…8 live neighbors.)

I acknowledge that representing each row as a word is a departure from other tracks, but our goal is to help students gain fluency with assembly, and this would be the idiomatic way to represent the input and output in contexts where assembly is used.

1 Like

Thank you for your answer.

In production, using WASM is usually about performance, so it is more idiomatic to select a data format that is as simple and performant in terms of serialization as possible for functions that are called frequently from the JS side.

In actual applications, a game-of-life matrix could be well over 64 columns wide. While we could compensate for this by doing run-length encoding for the data (using 31 bits of u32 for the cells and set the high bit to indicate another dword as part of the same row), but that would needlessly increase the complexity of both the serialization and the task.

There’s also prior art for the data format that is used within WASM: Tutorial - Rust and WebAssembly

5-row test cases:

[0xec6efb48, 0xbeb23898, 0xed06beb6, 0x91205a96, 0x93710c2c]

[0xe1a9452f9072d77a, 0x25150d7d533f22c, 0x9c20fdcb0fadc212, 0x55941c3f54993610, 0x3ddd9f17d265087a]