Distributed Game of Life

As decided by roughly a 75% majority vote1 after multiple rounds of elimination votes before that of those in class on March 28, our final project will be a distributed version of the Game of Life (GoL).

This page is a work-in-progress. Its content is correct, but incomplete, and will be added to as the project draws closer.

1 System Architecture

2 Getting started

We have a separate page describing how to get a minimal version of GoL implemented in just a few lines of code.

We have some initial files and automated tests:2 Last updated 2024-04-30 14:49 CDT; repeat these commands if you last ran them before then.

git fetch release
git merge release/project --allow-unrelated-histories -m "Merging initial files"

3 Your Task

  1. Combine the two functions from MP93 We recommend using your MP9 solution because you wrote and understand it, but we also have an example solution you can used instead if you want. into one. This will be used in the POST /tick handler, the description of which specifies what parts of the MP9 functions you need to keep.
  2. Add a Flask wrapper around that. The remainder of this section defines the Flask API you should implement.

3.1 Server States

Your flask app will be stateful; in particular, it will have the following states:

Waiting User Edit Simulating POST /config POST /tick GET /pause GET /stop GET /stop POST /tick GET /change/x/y/c/ POST /inform
State machine of your GoL server, with all API endpoints you need to implement marked as edges.
Waiting

Has not been told its board size by the middleware.

In this state, handle the following API endpoints:

Route New state Side effect
POST /inform Waiting PUT info about server to middleware
POST /config User Edit initialize a GoL tile
User Edit

Accepts single-cell edits from a user interface we provide.

In this state, handle the following API endpoints:

Route New state Side effect
GET /stop Waiting discard the GoL tile
GET /change/<x>/<y>/<c>/ User Edit change one cell in the tile
POST /tick Simulating advance the simulation 1 tick
Simulating

Accepts time-step requests from the middleware.

In this state, handle the following API endpoints:

Route New state Side effect
GET /stop Waiting discard the GoL tile
GET /pause User Edit (none)
POST /tick Simulating advance the simulation 1 tick

In all states, GET /ping responds with what state it is in, and tile contents if that is applicable for its state.

3.2 API Endpoints

3.2.1 GET /

Response
The server UI frontend HTML we provide (link to appear)

3.2.2 GET /ping

Response
A JSON object with field state indicating which state (Waiting, User Edit, or Simualting) the server is in. If the state is not Waiting, also field tile with the current cell contents of the tile as a string in the same format as POST /tick.

3.2.3 POST /inform

Request
JSON object with one field, url, containing a URL
Action

Send a PUT request to that URL with JSON object body containing (at least) two entries: your NetID in the author field and the base URL of your server in the url field.

{
    "author": "yournetid",
    "url": "http://sp24-cs340-###.cs.illinois.edu:#####"
}

If you are develping on your own machine instead of your VM, use localhost or 127.0.0.1 instead of your VM name, but don’t forget to change it back before the in-class project check-off.

States
Only valid in Waiting state; return a 409 Conflict status code if received in any other state.

3.2.4 POST /config

Request
JSON object with two fields, width and height, giving the number of cells in this tile as positive integers.
Action
Initialize whatever internal state is needed to simulate a GoL tile of that size.
States

Only valid in Waiting state; return a 409 Conflict status code if received in any other state.

Moves the server into the User Edit state.

3.2.5 GET /change/x/y/c/

Action

Set cell (x,y)(x,y) of the tile to single-character cell code c.

Accept the following cell codes:

  • space – clear/empty/kill the cell
  • octothorpe – fill the cell with a color of your choice
  • 0 through o – fill the cell with that color

If c does not match one of those values or if (x,y)(x,y) is outside the bounds of your tile, return a 403 Forbidden status code.

Response
The new board, in the same format as POST /tick
States
Only valid in User Edit state; return a 409 Conflict status code if received in any other state.

3.2.6 GET /stop

Action
Discard the internal state used to track the current tile.
States

Only valid in User Edit or Simulating state; return a 409 Conflict status code if received in any other state.

Moves the server into the Waiting state.

3.2.7 GET /pause

States

Only valid in Simulating state; return a 409 Conflict status code if received in any other state.

Moves the server into the User Edit.

3.2.8 POST /tick

Request

A string containing one character for each cell surrounding the tile, clockwise from the top-left corner. This is the same format as MP9’s life_ring function with characters like life_alphabet. In particular,

  • Space (' ' U+0020) means empty/kill this cell.
  • Octothorpe ('#' U+0023) means fill/birth this cell (with any color you want).
  • Quotation mark ('"' U+0022) means unspecified; we recommend handling this the same way you did in MP9, but if you’d rather treat this the same as a space character that is OK too.
  • Anything between Zero ('0' U+0030) and Small O ('o' U+006F) means full cell with a color, where the color is
    • Two bits each of red, green, and blue,
    • Stored in six bits, red in the high-order blue in the low-order, as rrggbb,
    • Offset by 0x30 and converted to a character.
  • Anything else represents an error on the part of the owner of that tile; how you handle it is up to you.
Non-JSON request bodies

In Flask, the raw bytes of a request are in request.data. You’ll probably want to decode that to a string using request.data.decode('ascii')

Character code-points in Python

The build-in function chr(number) converts a codepoint (like 0x51) to its corresponding one-character string ('Q').

The build-in function ord(s) converts a one-character string (like 'Q') to the codepoint of its character (0x51).

Color of 'b'

The character 'b' has code-point 98. Removing the 0x30 offset gives 50, or 0b110010. That means bright red (0b11, as high as 2 bits can go), no green (0b00, as low as 2 bits can go), and brightish blue (0b10, high but not as high as possible) for this color:  

The colors are:
 
0
1
2
3
4
5
6
7
8
9
:
;
<
=
>
?
@
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z
[
\
]
^
_
`
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o

Suppose you have a 4-cells-wide 3-cells-tall tile.

  • The tile to the NW of your tile’s SW corner value is 0
  • The tile to the N of your tile’s S edge is (left-to-right) ABCD
  • The tile to the NE of your tile’s SW corner is 1
  • The tile to the E of your tile’s W edge is (top-to-bottom) EFG
  • The tile to the SE of your tile’s NW corner is 2
  • The tile to the S of your tile’s N edge is (left-to-right) HIJK
  • The tile to the SW of your tile’s NE corner is 3
  • The tile to the W of your tile’s E edge is (top-to-bottom) LMN
0 A B C D 1 E F G 2 H I J K 3 L M N
The board and neighbor values outlined above

The payload that will be sent from the middleware will be the 18-character string 0ABCD1EFG2KJIH3NML

Action
Set the cells surrounding your tile to the values given in the request, then simulate one step of the GoL. You may handle colors in any way you wish, but should handle them: always using the same color is not sufficient.
Response
A string containing one character per cell in your board, with newlines after each row, in the same format as MP9’s life_alphabet function.

Suppose you have a 4-cells-wide 3-cells-tall tile. You’d return a 15-character string:

  1. 4 characters representing the top row of your tile, left-to-right
  2. a newline character
  3. 4 characters representing the middle row of your tile, left-to-right
  4. a newline character
  5. 4 characters representing the bottom row of your tile, left-to-right
  6. a newline character
C ABBC A

The above tile would be returned as " C\nABBC\nA \n" and displayed if printed as

   C
ABBC
A   
States

Only valid in User Edit or Simulating state; return a 409 Conflict status code if received in any other state.

Moves the server into the Simulating state (if it was not already in that state).

4 Testing your Code

4.1 Automated tests

Test your code with

python3 -m pytest

As of 2024-04-30 14:49 CDT4 If you downloaded the starter code before then, see Getting started for the commands to run to update the tests to the latest version., there are 10 tests covering most of the functionality of this project. However, they are cases that will arise in a live simulation that they do not test, so make sure to also run the local and VM tests listed below.

The automated tests only work with localhost URLs, not VM-specific URLs. This is needed because the VMs do not allow github to connect to them. After passing the tests, switch to the VM-specific URLs for the VM tests.

Submit your code with the usual commands:

git add -A
git commit -m "MP submission"
git push

Run the github pretest:

We install pytest, flask, and requests on the autograder. If you want to install more packages, you can call pip from inside your code as, e.g.

import subprocess
subprocess.run(['pip', 'install', 'numpy'])
import numpy as np

4.2 Local tests

We provide a fully-functional middleware implementation and a web-based user interface for both the middleware and your tile server. To test your code, you’ll need to run both the middleware (python3 middleware.py) and your tile server (python3 tileserver.py) a the same time in different terminals, and then visit both in separate browser tabs or windows.

A basic test might run as follows:

  1. Visit the middleware UI at http://localhost:34034
    1. copy the listed URL (i.e. http://localhost:34034/addTS).
  2. Visit the tile server UI at http://localhost:5000
    1. paste the URL into the provided box and click the button.
    2. a pop-up should appear with the message OK (if it doesn’t, check the terminals to see what went wrong).
  3. Visit the middleware UI at http://localhost:34034
    1. you should see your tile servers listed under Registered servers:.
    2. Fill in the board set-up; for example, try 11×8 cell tiles with 1×1 tiles on the board.
    3. Click the Configure board button.
    4. A gray board should show up.
  4. Visit the tile server UI at http://localhost:5000
    1. A tile image should be visible with a menu of colors below it.
    2. Pick a color and click in the image to set a starting tile configuration.
  5. Visit the middleware UI at http://localhost:34034
    1. Put 1 in the Seconds between ticks field and then click outside that field.
    2. Watch the simulation run.

Various inputs on the middleware UI page change the tile server’s state (visible on the tile server pages):

To conserve resources, updates to both UI pages are only made while those browser pages are focused. A running simulation does not stop when the page is out of focus, but the middleware page does not display the changes when it is not focused.

Additional tests:

4.3 VM tests

Make sure your code works on your VM, with your VM’s URL in both your code and in the browser. This is how it will be tested in class on May 6. On May 6 you will not run your own middleware – the one we provided doesn’t scale to large numbers of users very well – but you will run your own tile servers. If your tile server works as designed, you should be able to run it once before class and leave it running throughout class: there should be no need to re-enter the VM and change things unless your code crashes.

5 Prep for in-class check-off

  1. Pass the VM tests

  2. The day of the check-off, run your code on the vm with the following:

    nohup python3 tileserver.py 1>>ts_output.log 2>&1 </dev/null &
    echo $! >> ts_pid

    The components of this pair of commands are:

    • nohup – run a program such that it can keep running after you log out
    • python3 tileserver.py – run your app. If your app needs a different command to run it, feel free to replace this accordingly
    • 1>>ts_output.log 2>&1 – append any output to the file ts_output.log. You can view it by opening that file, cating it, using tail -f ts_output.log to watch for changes live, etc.
    • </dev/null – detach the program from user input, so that later things we type won’t go to it
    • & – run it in the background
    • echo $! >> ts_pid store it’s process ID in a file. Thereafter if we want to stop it we’d run kill $(cat ts_pid)
  3. Verify that you see the tile UI when visiting your VM’s URL and port in a browser

  4. Attend class, where we will give you various middleware URLs to notify and tasks to perform. Some of those tasks will include

    • Draw a glider5 The getting started examples are gliders in the center of your tile; then we run. This will verify tile-to-tile hand-off works.

    • Draw something that shows off how you use color.

    • A test where there are roughly 2× tiles on the board as there are tile servers, to ensure '"' borders and handled sanely.

    • Fill your tile with some involved shape (such as soup) and then we’ll make the middleware go faster and faster until tile servers start dropping out. We don’t have any particular speed requirement, but will give kudos to the fastest implementations and ask their authors to describe their approach.

    • Others to be revealed the day-of

6 Scoring

The project is worth 15% of the overall grade. This will be divided as follows: