{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Conway's Game of Life\n", "This notebook demostrates how you can develop interactive notebooks with lgo by developping [Conway's Game of Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "// Board defines the interface of game-of-life board.\n", "// This interface exists to remove the direct dependency between Board implementation and renderer.\n", "type Board interface {\n", " Generation() int\n", " Get(x, y int) bool\n", " Set(x, y int)\n", " Size() (int, int)\n", " Next()\n", "}" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "type boardImpl struct {\n", " cur [][]bool\n", " buf [][]bool\n", " generation int\n", "}\n", "\n", "func NewBoard(w, h int) *boardImpl {\n", " if w < 3 || h < 3 {\n", " panic(\"too small\")\n", " }\n", " cur := make([][]bool, w)\n", " buf := make([][]bool, w)\n", " for i := 0; i < w; i++ {\n", " cur[i] = make([]bool, h)\n", " buf[i] = make([]bool, h)\n", " }\n", " return &boardImpl{\n", " cur: cur,\n", " buf: buf,\n", " }\n", "}\n", "\n", "func (b *boardImpl) Set(x, y int) {\n", " if x >= 1 && y >= 1 && x < len(b.cur)-1 && y < len(b.cur[0])-1 {\n", " b.cur[x][y] = true\n", " }\n", "}\n", "\n", "func (b *boardImpl) Get(x, y int) bool {\n", " return b.cur[x][y]\n", "}\n", "\n", "func (b *boardImpl) Size() (int, int) {\n", " return len(b.cur), len(b.cur[0])\n", "}\n", "\n", "func (b *boardImpl) Generation() int {\n", " return b.generation\n", "}\n", "\n", "func (b *boardImpl) nextPixel(x, y int) bool {\n", " c := 0\n", " for i := x-1; i < x+2; i++ {\n", " for j := y-1; j < y+2; j++ {\n", " if (i != x || j != y) && b.cur[i][j] {\n", " c += 1\n", " }\n", " }\n", " }\n", " if !b.cur[x][y] {\n", " // dead\n", " return c == 3\n", " }\n", " if c == 2 || c == 3 {\n", " return true\n", " }\n", " return false\n", "}\n", "\n", "func (b *boardImpl) Next() {\n", " w := len(b.cur)\n", " h := len(b.cur[0])\n", " for i := 0; i < w; i++ {\n", " for j := 0; j < h; j++ {\n", " if i == 0 || i == w-1 || j == 0 || j == h-1 {\n", " // border\n", " b.buf[i][j] = false\n", " continue\n", " }\n", " b.buf[i][j] = b.nextPixel(i, j)\n", " }\n", " }\n", " tmp := b.cur\n", " b.cur = b.buf\n", " b.buf = tmp\n", " b.generation++\n", "}" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import (\n", " \"bytes\"\n", " \"encoding/base64\"\n", " \"fmt\"\n", " \"math/rand\"\n", " \"time\"\n", " \"os\"\n", ")\n", "\n", "// Canvas renders the content of GameOfLife to HTML Canvas.\n", "type Canvas struct {\n", " id string\n", " jsid string\n", " width int\n", " height int\n", " board Board\n", " labelID string\n", " svgID string\n", "}\n", "\n", "func NewCanvas(board Board, width, height int) *Canvas {\n", " return &Canvas{\n", " id: fmt.Sprintf(\"canvas%d\", rand.Int63()),\n", " width: width,\n", " height: height,\n", " board: board,\n", " }\n", "}\n", "\n", "func (c *Canvas) renderSVG() {\n", " board := c.board\n", " w, h := board.Size()\n", " cw := 100/float64(w)\n", " ch := 100/float64(h)\n", " var buf bytes.Buffer\n", " buf.WriteString(``)\n", " for x := 0; x < w; x++ {\n", " for y := 0; y < h; y++ {\n", " if !board.Get(x, y) {\n", " continue\n", " }\n", " buf.WriteString(fmt.Sprintf(\n", " ``,\n", " float64(x)*cw, float64(y)*ch, cw, ch))\n", " }\n", " }\n", " buf.WriteString(``)\n", " _ctx.Display.Text(fmt.Sprintf(\"Generation: %d\", board.Generation()), &c.labelID)\n", " _ctx.Display.HTML(fmt.Sprintf(\n", " ``,\n", " c.width, c.height,\n", " base64.StdEncoding.EncodeToString(buf.Bytes())), &c.svgID)\n", "}\n", "\n", "func (c *Canvas) DisplayAnimation(step int, interval time.Duration) {\n", " if interval < 10 * time.Millisecond {\n", " fmt.Fprintf(os.Stderr, \"interval is too small: %v\", interval)\n", " return\n", " }\n", " c.renderSVG()\n", " prev := time.Now()\n", " for i := 0; step < 0 || i < step; i++ {\n", " c.board.Next()\n", " time.Sleep(interval-time.Now().Sub(prev))\n", " prev = time.Now()\n", " c.renderSVG()\n", " }\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Oscillators" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": false }, "outputs": [ { "data": { "text/plain": [ "Generation: 20" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "{\n", " g := NewBoard(20, 10)\n", " c := NewCanvas(g, 400, 200)\n", " \n", " var x, y int\n", " x, y = 1, 1\n", " g.Set(x, y+1)\n", " g.Set(x+1, y+1)\n", " g.Set(x+2, y+1)\n", " \n", " x, y = 5, 1\n", " g.Set(x+1, y+1)\n", " g.Set(x+2, y+1)\n", " g.Set(x+3, y+1)\n", " g.Set(x, y+2)\n", " g.Set(x+1, y+2)\n", " g.Set(x+2, y+2)\n", " \n", " x, y = 11, 1\n", " g.Set(x, y)\n", " g.Set(x+1, y)\n", " g.Set(x, y+1)\n", " g.Set(x+1, y+1)\n", " g.Set(x+2, y+2)\n", " g.Set(x+3, y+2)\n", " g.Set(x+2, y+3)\n", " g.Set(x+3, y+3)\n", " \n", " c.DisplayAnimation(20, 250*time.Millisecond)\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Gliders" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Generation: 300" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "func leftRotate(g *boardImpl) *boardImpl {\n", " w, h := g.Size()\n", " n := NewBoard(h, w)\n", " n.generation = g.generation\n", " for i := 0; i < w; i++ {\n", " for j := 0; j < h; j++ {\n", " n.cur[j][h-1-i] = g.cur[i][j]\n", " }\n", " }\n", " return n\n", "}\n", "\n", "func addGlider(g Board, x, y int) {\n", " g.Set(x, y+2)\n", " g.Set(x+1, y)\n", " g.Set(x+1, y+2)\n", " g.Set(x+2, y+1)\n", " g.Set(x+2, y+2)\n", "}\n", "\n", "{ \n", " g := NewBoard(160, 160)\n", " for r := 0; r < 4; r++ {\n", " max := 7\n", " for i := 0; i < max; i++ {\n", " for j := 0; j < max; j++ {\n", " if i + j >= max {\n", " continue\n", " }\n", " addGlider(g, i*10+5, j*10+5)\n", " } \n", " }\n", " g = leftRotate(g)\n", " }\n", " c := NewCanvas(g, 480, 480)\n", " c.DisplayAnimation(300, 100*time.Millisecond)\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Random" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Generation: 500" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import (\n", " \"math/rand\"\n", ")\n", "\n", "{ \n", " w, h := 200, 200\n", " g := NewBoard(w, h)\n", " c := NewCanvas(g, 480, 480)\n", " for i := 1; i < w; i++ {\n", " for j := 1; j < h; j++ {\n", " if rand.Int()%2!=0 {\n", " g.Set(i, j)\n", " }\n", " }\n", " }\n", " \n", " for i := 0; i < 10; i++ {\n", " for j := 0; j < 10; j++ {\n", " addGlider(g, i*8, j*9)\n", " }\n", " }\n", " c.DisplayAnimation(500, 100*time.Millisecond)\n", "}" ] } ], "metadata": { "kernelspec": { "display_name": "Go (lgo)", "language": "go", "name": "lgo" }, "language_info": { "file_extension": "", "mimetype": "", "name": "go", "version": "" } }, "nbformat": 4, "nbformat_minor": 2 }