pxt-calliope/docs/examples/gameofLife.md
Juri Wolf a93febb5b7
Map and clean deprecated functions (#175)
* add image and deprecated arrow functions

* update locales

* map basic.showArrow

* map arrow blocks

* map & remove arrow images

* remove arrow blocks

* update locales

* remove & patch:
rgbw -> rgb
button/pin pressed -> button/pin event
loudness -> soundLevel

* update ts mappings for arrows

* add wip ts patch rules

* update .blocks files

* use Click instead of Down as default in Documentation and tests

* patch test.blocks

* fix lowercase name tag

* update test.blocks

* update blocks test files

* update blocks test files

* format block files

* pass blocks file tests

* fix ts mapping

* fix color.defl value

closes https://github.com/microsoft/pxt-calliope/issues/136

* fix ts mappings

- add optional spacing at the end of rgbw()
- map up to v4.0.19

* add suggested changes

* replace innerText by textContent

Co-authored-by: JW <gitkraken@juriwolf.de>
Co-authored-by: Juri <info@juriwolf.de>
2022-04-26 10:28:42 -07:00

4.8 KiB

Game of Life

The Game of Life simulates life in a grid world (a two-dimensional block of cells). The cells in the grid have a state of "alive" or "dead". The game starts with a population of cells placed in a certain pattern on the grid. A simulation is run, and based on some simple rules for life and death, cells continue to live, die off, or reproduce.

Rules for Life

The rules for life in the grid are:

  1. A living cell with less than two live cells next to it will die. This is underpopulation, no social support.
  2. A living cell with two or three live cells next to it continues to live. This is a healthy population.
  3. A living cell with more than three live cells next to it will die. This is over overpopulation, scarce resources.
  4. A dead cell with three live cells next to it turns into a living cell. This is reproduction.

Depending on the pattern of living cells at the start of the game, some population simulations may survive longer than others.

Game of Life simulation in LEDs

Here's a program that simulates cell life in the LED matrix. Use button A for the next stage of life and button B to reset.

//https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life
let lifeChart: Image = null

//Use button A for the next iteration of game of life
input.onButtonEvent(Button.A, ButtonEvent.Click, () => {
    gameOfLife();
    show();
})

//Use button B for reseting to random initial seed state
input.onButtonEvent(Button.B, ButtonEvent.Click, () => {
    reset();
    show();
})

lifeChart = images.createImage(`
    . . . . .
    . . . . .
    . . . . .
    . . . . .
    . . . . .
    `)

//State holds the information about pixel is live or dead
//false means dead, true means live.
let state = [false, false, false, false, false,
    false, false, false, false, false,
    false, false, false, false, false,
    false, false, false, false, false,
    false, false, false, false, false]

//get & set on any array
function getState(arr: boolean[], x: number, y: number): boolean {
    return arr[x * 5 + y];
}
function setState(arr: boolean[], x: number, y: number, value: boolean): void {
    arr[x * 5 + y] = value;
}

//Generate random initial state.
function reset() {
    for (let x = 0; x < 5; x++) {
        for (let y = 0; y < 5; y++) {
            setState(state, x, y, Math.randomBoolean());
        }
    }
}

//Show the lifeChart based on the state
function show() {
    for (let x = 0; x < 5; x++) {
        for (let y = 0; y < 5; y++) {
            lifeChart.setPixel(x, y, getState(state, x, y));
        }
    }
    lifeChart.plotImage(0);
}

//Core function
function gameOfLife() {
    let result: boolean[] = [];
    let count = 0;

    for (let x = 0; x < 5; x++) {
        for (let y = 0; y < 5; y++) {
            count = 0;

            //Count the live cells in the next row
            if ((x + 1) < 5) {
                if (getState(state, x + 1, y)) {
                    count++;
                }
                if ((y + 1 < 5) && getState(state, x + 1, y + 1)) {
                    count++;
                }
                if ((y - 1 >= 0) && getState(state, x + 1, y - 1)) {
                    count++;
                }
            }

            //Count the live cells in the previous row
            if ((x - 1) >= 0) {
                if (getState(state, x - 1, y)) {
                    count++;
                }
                if ((y + 1 < 5) && getState(state, x - 1, y + 1)) {
                    count++;
                }
                if ((y - 1 >= 0) && getState(state, x - 1, y - 1)) {
                    count++;
                }
            }

            //Count the live cells in the current row exlcuding the current position.
            if ((y - 1 >= 0) && getState(state, x, y - 1)) {
                count++;
            }
            if ((y + 1 < 5) && getState(state, x, y + 1)) {
                count++;
            }

            // Toggle live / dead cells based on the neighbour count.
            // 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 dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
            // Any live cell with more than three live neighbours dies, as if by overpopulation.
            switch (count) {
                case 0: setState(result, x, y, false); break;
                case 1: setState(result, x, y, false); break;
                case 2: setState(result, x, y, getState(state, x, y)); break;
                case 3: setState(result, x, y, true); break;
                default: setState(result, x, y, false); break;
            }
        }
    }
    //Update the state
    state = result;
}
//Initial reset & show
reset();
show();