{
"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",
" _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
}