Added problem 3, display board
authorNeil Smith <neil.git@njae.me.uk>
Fri, 19 May 2017 17:36:07 +0000 (18:36 +0100)
committerNeil Smith <neil.git@njae.me.uk>
Fri, 19 May 2017 17:36:07 +0000 (18:36 +0100)
03-display-board/03-pixels.txt [new file with mode: 0644]
03-display-board/display-board-creation.ipynb [new file with mode: 0644]
03-display-board/display-board-solution.ipynb [new file with mode: 0644]
03-display-board/jantar-mantar.pbm [new file with mode: 0644]

diff --git a/03-display-board/03-pixels.txt b/03-display-board/03-pixels.txt
new file mode 100644 (file)
index 0000000..c77033e
--- /dev/null
@@ -0,0 +1,255 @@
+top 42 42
+top 35 35
+top 39 39
+top 25 25
+top 22 22
+top 28 28
+rotate column 39 1
+rotate column 42 1
+rotate column 25 1
+rotate column 22 1
+rotate column 35 1
+rotate column 28 1
+left 2 2
+rotate row 2 3
+left 2 2
+rotate row 2 4
+left 2 2
+rotate row 2 7
+left 2 3
+rotate row 2 3
+rotate row 3 41
+left 2 3
+rotate row 2 3
+rotate row 3 20
+top 4 4
+top 42 42
+top 18 18
+top 34 34
+top 25 25
+top 7 7
+top 66 66
+top 45 45
+top 59 59
+top 54 55
+top 40 40
+top 62 62
+top 13 14
+top 48 48
+top 21 21
+rotate column 48 1
+rotate column 18 1
+rotate column 14 1
+rotate column 45 1
+rotate column 42 2
+rotate column 55 1
+rotate column 34 1
+rotate column 66 1
+rotate column 59 1
+rotate column 13 3
+rotate column 7 1
+rotate column 25 1
+rotate column 40 1
+rotate column 21 1
+rotate column 54 3
+rotate column 62 1
+rotate column 4 1
+top 62 63
+top 59 59
+top 42 48
+top 40 40
+top 66 66
+top 18 18
+top 10 11
+top 21 22
+top 33 34
+top 30 30
+top 25 25
+top 2 7
+top 51 52
+top 54 55
+top 36 38
+top 13 14
+rotate column 44 1
+rotate column 6 4
+rotate column 18 1
+rotate column 62 1
+rotate column 14 1
+rotate column 45 2
+rotate column 38 1
+rotate column 51 1
+rotate column 5 2
+rotate column 46 2
+rotate column 48 1
+rotate column 63 2
+rotate column 66 2
+rotate column 42 1
+rotate column 54 1
+rotate column 33 1
+rotate column 7 1
+rotate column 34 2
+rotate column 11 2
+rotate column 30 1
+rotate column 36 1
+rotate column 21 1
+rotate column 52 2
+rotate column 2 2
+rotate column 40 2
+rotate column 13 1
+rotate column 3 1
+rotate column 37 3
+rotate column 10 1
+rotate column 55 1
+rotate column 4 2
+rotate column 59 1
+rotate column 47 4
+rotate column 43 2
+rotate column 25 2
+rotate column 22 2
+left 2 6
+rotate row 5 3
+rotate row 6 37
+rotate row 4 2
+rotate row 2 6
+rotate row 3 2
+top 19 24
+top 70 71
+top 9 13
+top 38 42
+top 2 7
+top 36 36
+top 44 48
+top 29 30
+top 26 27
+top 67 68
+top 56 57
+top 50 54
+top 60 65
+top 15 16
+rotate column 56 5
+rotate column 27 1
+rotate column 64 2
+rotate column 21 2
+rotate column 57 1
+rotate column 71 4
+rotate column 10 4
+rotate column 16 1
+rotate column 60 6
+rotate column 15 5
+rotate column 63 2
+rotate column 19 6
+rotate column 22 2
+rotate column 68 1
+rotate column 65 1
+rotate column 24 1
+rotate column 26 1
+rotate column 48 1
+rotate column 4 2
+rotate column 50 1
+rotate column 46 2
+rotate column 9 1
+rotate column 36 1
+rotate column 7 1
+rotate column 2 7
+rotate column 62 2
+rotate column 20 2
+rotate column 6 2
+rotate column 12 4
+rotate column 30 4
+rotate column 29 5
+rotate column 53 4
+rotate column 51 4
+rotate column 41 1
+rotate column 54 1
+rotate column 67 1
+rotate column 45 2
+rotate column 44 2
+rotate column 38 1
+rotate column 39 3
+rotate column 5 2
+rotate column 70 5
+rotate column 61 2
+rotate column 3 2
+rotate column 52 5
+rotate column 47 2
+rotate column 42 1
+rotate column 13 1
+rotate column 23 2
+rotate column 11 5
+rotate column 40 1
+top 2 7
+top 44 48
+top 67 71
+top 56 65
+top 9 13
+top 26 30
+top 36 42
+top 50 54
+top 15 24
+rotate column 68 2
+rotate column 54 2
+rotate column 56 2
+rotate column 27 2
+rotate column 24 2
+rotate column 12 2
+rotate column 44 2
+rotate column 67 6
+rotate column 23 2
+rotate column 6 2
+rotate column 10 2
+rotate column 5 2
+rotate column 30 2
+rotate column 15 2
+rotate column 2 0
+rotate column 57 1
+rotate column 29 2
+rotate column 45 0
+rotate column 65 2
+rotate column 47 2
+rotate column 63 2
+rotate column 22 2
+rotate column 26 6
+rotate column 48 2
+rotate column 38 1
+rotate column 46 2
+rotate column 11 2
+rotate column 17 2
+rotate column 13 2
+rotate column 51 2
+rotate column 21 2
+rotate column 70 2
+rotate column 52 2
+rotate column 19 0
+rotate column 20 2
+rotate column 4 0
+rotate column 59 2
+rotate column 62 2
+rotate column 69 2
+rotate column 64 2
+rotate column 7 2
+rotate column 28 2
+rotate column 58 2
+rotate column 41 1
+rotate column 50 2
+rotate column 9 2
+rotate column 60 0
+rotate column 42 1
+rotate column 3 0
+rotate column 40 2
+rotate column 37 1
+rotate column 36 1
+rotate column 61 2
+rotate column 71 2
+rotate column 53 2
+rotate column 18 2
+rotate column 39 0
+rotate column 16 1
+left 1 8
+rotate row 3 6
+rotate row 6 6
+rotate row 7 3
+rotate row 2 6
+rotate row 8 4
+rotate row 1 3
+rotate row 5 6
+rotate row 4 6
\ No newline at end of file
diff --git a/03-display-board/display-board-creation.ipynb b/03-display-board/display-board-creation.ipynb
new file mode 100644 (file)
index 0000000..c770246
--- /dev/null
@@ -0,0 +1,1676 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 52,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "import itertools\n",
+    "import time\n",
+    "import re\n",
+    "from IPython.display import clear_output\n",
+    "import random"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "WIDTH = 50\n",
+    "HEIGHT = 8"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def new_grid(w=WIDTH, h=HEIGHT):\n",
+    "    return ['.' * w for r in range(1, h+1)]"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 56,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def print_grid(grid, md=False, suppress_dots=False):\n",
+    "    if md:\n",
+    "        print('```')\n",
+    "    for row in grid:\n",
+    "        if suppress_dots:\n",
+    "            print(re.sub(r'\\.', ' ', row))\n",
+    "        else:\n",
+    "            print(row)        \n",
+    "    if md:\n",
+    "        print('```')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def top(grid, l, r):\n",
+    "    new_segment = ''\n",
+    "    for i in range(l-1, r):\n",
+    "        if grid[0][i] == '.':\n",
+    "            new_segment += '*'\n",
+    "        else:\n",
+    "            new_segment += '.'\n",
+    "    grid[0] = grid[0][:l-1] + new_segment + grid[0][r:]\n",
+    "    return grid"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def left(grid, t, b):\n",
+    "    for i in range(t-1, b):\n",
+    "        if grid[i][0] == '.':\n",
+    "            grid[i] = '*' + grid[i][1:]\n",
+    "        else:\n",
+    "            grid[i] = '.' + grid[i][1:]\n",
+    "    return grid"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def rotate_column(grid, c, raw_n):\n",
+    "    n = raw_n % len(grid)\n",
+    "    col = [row[c-1] for row in grid]\n",
+    "    new_col = col[-n:] + col[:-n]\n",
+    "    for i in range(len(grid)):\n",
+    "        grid[i] = grid[i][:c-1] + new_col[i] + grid[i][c:]\n",
+    "    return grid"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def rotate_row(grid, r, raw_n):\n",
+    "    n = raw_n % len(grid[0])\n",
+    "    grid[r-1] = grid[r-1][-n:] + grid[r-1][:-n]\n",
+    "    return grid"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "command_dispatch = {'left': left, 'top': top,\n",
+    "                   'rotate row': rotate_row,\n",
+    "                   'rotate column': rotate_column}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def parse(command):\n",
+    "    cmd, a, b = command.rsplit(maxsplit=2)\n",
+    "    return cmd, int(a), int(b)  "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 59,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def interpret(commands, grid=None, w=WIDTH, h=HEIGHT, \n",
+    "              uninterpret=False,\n",
+    "              show_each_step=False, md=False, overprint=False,\n",
+    "              suppress_dots=False):\n",
+    "    if grid is None:\n",
+    "        grid = new_grid(w, h)\n",
+    "    if uninterpret:\n",
+    "        ordered_commands = reversed(commands)\n",
+    "    else:\n",
+    "        ordered_commands = commands\n",
+    "    for c in ordered_commands:\n",
+    "        cmd, a, b = parse(c)\n",
+    "        if uninterpret and cmd in uncommand_dispatch:\n",
+    "            uncommand_dispatch[cmd](grid, a, b)\n",
+    "        elif not uninterpret and cmd in command_dispatch:\n",
+    "            command_dispatch[cmd](grid, a, b)\n",
+    "        else:\n",
+    "            raise ValueError('Unknown command')\n",
+    "        if show_each_step:\n",
+    "            if overprint:\n",
+    "                time.sleep(0.25)\n",
+    "            if md: \n",
+    "                print('`{}`'.format(c))\n",
+    "            else:\n",
+    "                print(c)\n",
+    "            print_grid(grid, md=md, suppress_dots=suppress_dots)\n",
+    "            print()\n",
+    "            if overprint:\n",
+    "                clear_output(wait=True)\n",
+    "    if show_each_step: \n",
+    "        print('Final')\n",
+    "        print_grid(grid, md=md, suppress_dots=suppress_dots)\n",
+    "    return grid\n",
+    "    \n",
+    "    \n",
+    "# for i in range(10):\n",
+    "#     time.sleep(0.25)\n",
+    "#     print(i)\n",
+    "#     clear_output(wait=True)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "For instance, with a smaller grid that is 10 pixels wide and 4 tall, this is what a sample sequence of instructions would do.\n",
+    "\n",
+    "* `toggle 1 6` turns on the first six pixels on the top row.\n",
+    "```\n",
+    "******....\n",
+    "..........\n",
+    "..........\n",
+    "..........\n",
+    "```\n",
+    "\n",
+    "* `rotate column 2 3` moves the lit pixel on the second column to the bottom row.\n",
+    "```\n",
+    "*.****....\n",
+    "..........\n",
+    "..........\n",
+    ".*........\n",
+    "```\n",
+    "\n",
+    "* `toggle 3 10` turns off the pixels in columns 4, 5, and 6, and turns on the pixels in columns 7 to 10.\n",
+    "\n",
+    "```\n",
+    "*.....****\n",
+    "..........\n",
+    "..........\n",
+    ".*........\n",
+    "```\n",
+    "\n",
+    "* `rotate column 8 1` moves the one lit pixel in column 8 down one row.\n",
+    "```\n",
+    "*.....*.**\n",
+    ".......*..\n",
+    "..........\n",
+    ".*........\n",
+    "```\n",
+    "\n",
+    "* `rotate row 2 6` moves that pixel off the right edge of the display, to it wraps around to appear in column 4.\n",
+    "```\n",
+    "*.....*.**\n",
+    "...*......\n",
+    "..........\n",
+    ".*........\n",
+    "```\n",
+    "\n",
+    "* `left 1 3` toggles the pixels in rows 1, 2, and 3 of the first column. The top left pixel (previously on) turns off, while the pixels in rows 2 and 3 come on.\n",
+    "\n",
+    "```\n",
+    "......*.**\n",
+    "*..*......\n",
+    "*.........\n",
+    ".*........\n",
+    "```"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "`top 1 6`\n",
+      "```\n",
+      "******....\n",
+      "..........\n",
+      "..........\n",
+      "..........\n",
+      "```\n",
+      "\n",
+      "`rotate column 2 3`\n",
+      "```\n",
+      "*.****....\n",
+      "..........\n",
+      "..........\n",
+      ".*........\n",
+      "```\n",
+      "\n",
+      "`top 3 10`\n",
+      "```\n",
+      "*.....****\n",
+      "..........\n",
+      "..........\n",
+      ".*........\n",
+      "```\n",
+      "\n",
+      "`rotate column 8 1`\n",
+      "```\n",
+      "*.....*.**\n",
+      ".......*..\n",
+      "..........\n",
+      ".*........\n",
+      "```\n",
+      "\n",
+      "`rotate row 2 6`\n",
+      "```\n",
+      "*.....*.**\n",
+      "...*......\n",
+      "..........\n",
+      ".*........\n",
+      "```\n",
+      "\n",
+      "`left 1 3`\n",
+      "```\n",
+      "......*.**\n",
+      "*..*......\n",
+      "*.........\n",
+      ".*........\n",
+      "```\n",
+      "\n",
+      "Final\n",
+      "```\n",
+      "......*.**\n",
+      "*..*......\n",
+      "*.........\n",
+      ".*........\n",
+      "```\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "['......*.**', '*..*......', '*.........', '.*........']"
+      ]
+     },
+     "execution_count": 12,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "cmds = '''\n",
+    "top 1 6\n",
+    "rotate column 2 3\n",
+    "top 3 10\n",
+    "rotate column 8 1\n",
+    "rotate row 2 6\n",
+    "left 1 3\n",
+    "'''.split('\\n')[1:-1]\n",
+    "interpret(cmds, w=10, h=4, show_each_step=True, md=True)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def unrotate_column(grid, c, raw_n):\n",
+    "    return rotate_column(grid, c, (-1 * raw_n) % len(grid))\n",
+    "\n",
+    "def unrotate_row(grid, r, raw_n):\n",
+    "    return rotate_row(grid, r, (-1 * raw_n) % len(grid[0]))\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 14,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "uncommand_dispatch = {'left': left, 'top': top,\n",
+    "                      'rotate row': unrotate_row,\n",
+    "                      'rotate column': unrotate_column}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 15,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "['..................................................',\n",
+       " '..*****..****....***...*...*..*****...***...****..',\n",
+       " '....*....*...*..*...*..**..*....*....*...*..*...*.',\n",
+       " '....*....*..*...*...*..*.*.*....*....*...*..*..*..',\n",
+       " '....*....****...*****..*.*.*....*....*...*..****..',\n",
+       " '....*....*...*..*...*..*..**....*....*...*..*...*.',\n",
+       " '....*....*...*..*...*..*...*....*.....***...*...*.',\n",
+       " '..................................................']"
+      ]
+     },
+     "execution_count": 15,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "trantor_grid = '''\n",
+    "..................................................\n",
+    "..*****..****....***...*...*..*****...***...****..\n",
+    "....*....*...*..*...*..**..*....*....*...*..*...*.\n",
+    "....*....*..*...*...*..*.*.*....*....*...*..*..*..\n",
+    "....*....****...*****..*.*.*....*....*...*..****..\n",
+    "....*....*...*..*...*..*..**....*....*...*..*...*.\n",
+    "....*....*...*..*...*..*...*....*.....***...*...*.\n",
+    "..................................................\n",
+    "'''.split('\\n')[1:-1]\n",
+    "trantor_grid"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 16,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def tg():\n",
+    "    return '''\n",
+    "..................................................\n",
+    "..*****..****....***...*...*..*****...***...****..\n",
+    "....*....*...*..*...*..**..*....*....*...*..*...*.\n",
+    "....*....*..*...*...*..*.*.*....*....*...*..*..*..\n",
+    "....*....****...*****..*.*.*....*....*...*..****..\n",
+    "....*....*...*..*...*..*..**....*....*...*..*...*.\n",
+    "....*....*...*..*...*..*...*....*.....***...*...*.\n",
+    "..................................................\n",
+    "'''.split('\\n')[1:-1]"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 43,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "....................................................................................................\n",
+      "....................................................................................................\n",
+      "....................................................................................................\n",
+      "....................................................................................................\n",
+      "....................................................................................................\n",
+      "....................................................................................................\n",
+      "....................................................................................................\n",
+      "....................................................................................................\n"
+     ]
+    }
+   ],
+   "source": [
+    "print_grid(new_grid(100, 8))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 45,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def jantar_mantar():\n",
+    "    return '''\n",
+    "..*****.............................................................................................\n",
+    ".....*..............................................................................................\n",
+    ".....*..............................................................................................\n",
+    ".....*..............................................................................................\n",
+    ".....*..............................................................................................\n",
+    ".....*..............................................................................................\n",
+    ".*...*..............................................................................................\n",
+    "..***...............................................................................................\n",
+    "'''.split('\\n')[1:-1]"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 47,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def jantar_mantar():\n",
+    "    return '''\n",
+    "...****..............*...................*.....*..............*.................\n",
+    "......*..............*...................***..**..............*.................\n",
+    "......*.*****.*****.****.*****..****.....*.*.***.*****.*****.****.*****..****...\n",
+    "......*.....*.*...*..*.......*..*........*..**.*.....*.*...*..*.......*..*......\n",
+    "......*.*****.*...*..*...*****..*........*..*..*.*****.*...*..*...*****..*......\n",
+    "......*.*...*.*...*..*...*...*..*........*.....*.*...*.*...*..*...*...*..*......\n",
+    "...*..*.*..**.*...*..**..*..**..*........*.....*.*..**.*...*..**..*..**..*......\n",
+    "....**...**.*.*...*...**..**.*..*........*.....*..**.*.*...*...**..**.*..*......\n",
+    "'''.split('\\n')[1:-1]"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 17,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "left 3 7\n",
+      "..................................................\n",
+      "..*****..****....***...*...*..*****...***...****..\n",
+      "*...*....*...*..*...*..**..*....*....*...*..*...*.\n",
+      "*...*....*..*...*...*..*.*.*....*....*...*..*..*..\n",
+      "*...*....****...*****..*.*.*....*....*...*..****..\n",
+      "*...*....*...*..*...*..*..**....*....*...*..*...*.\n",
+      "*...*....*...*..*...*..*...*....*.....***...*...*.\n",
+      "..................................................\n",
+      "\n",
+      "rotate column 1 1\n",
+      "..................................................\n",
+      "*.*****..****....***...*...*..*****...***...****..\n",
+      "*...*....*...*..*...*..**..*....*....*...*..*...*.\n",
+      "*...*....*..*...*...*..*.*.*....*....*...*..*..*..\n",
+      "*...*....****...*****..*.*.*....*....*...*..****..\n",
+      "*...*....*...*..*...*..*..**....*....*...*..*...*.\n",
+      "....*....*...*..*...*..*...*....*.....***...*...*.\n",
+      "..................................................\n",
+      "\n",
+      "left 3 7\n",
+      "..................................................\n",
+      "*.*****..****....***...*...*..*****...***...****..\n",
+      "....*....*...*..*...*..**..*....*....*...*..*...*.\n",
+      "....*....*..*...*...*..*.*.*....*....*...*..*..*..\n",
+      "....*....****...*****..*.*.*....*....*...*..****..\n",
+      "....*....*...*..*...*..*..**....*....*...*..*...*.\n",
+      "*...*....*...*..*...*..*...*....*.....***...*...*.\n",
+      "..................................................\n",
+      "\n",
+      "rotate column 3 1\n",
+      "..*...............................................\n",
+      "*..****..****....***...*...*..*****...***...****..\n",
+      "....*....*...*..*...*..**..*....*....*...*..*...*.\n",
+      "....*....*..*...*...*..*.*.*....*....*...*..*..*..\n",
+      "....*....****...*****..*.*.*....*....*...*..****..\n",
+      "....*....*...*..*...*..*..**....*....*...*..*...*.\n",
+      "*...*....*...*..*...*..*...*....*.....***...*...*.\n",
+      "..................................................\n",
+      "\n",
+      "Final\n",
+      "..*...............................................\n",
+      "*..****..****....***...*...*..*****...***...****..\n",
+      "....*....*...*..*...*..**..*....*....*...*..*...*.\n",
+      "....*....*..*...*...*..*.*.*....*....*...*..*..*..\n",
+      "....*....****...*****..*.*.*....*....*...*..****..\n",
+      "....*....*...*..*...*..*..**....*....*...*..*...*.\n",
+      "*...*....*...*..*...*..*...*....*.....***...*...*.\n",
+      "..................................................\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "['..*...............................................',\n",
+       " '*..****..****....***...*...*..*****...***...****..',\n",
+       " '....*....*...*..*...*..**..*....*....*...*..*...*.',\n",
+       " '....*....*..*...*...*..*.*.*....*....*...*..*..*..',\n",
+       " '....*....****...*****..*.*.*....*....*...*..****..',\n",
+       " '....*....*...*..*...*..*..**....*....*...*..*...*.',\n",
+       " '*...*....*...*..*...*..*...*....*.....***...*...*.',\n",
+       " '..................................................']"
+      ]
+     },
+     "execution_count": 17,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "trantor_grid = '''\n",
+    "..................................................\n",
+    "..*****..****....***...*...*..*****...***...****..\n",
+    "....*....*...*..*...*..**..*....*....*...*..*...*.\n",
+    "....*....*..*...*...*..*.*.*....*....*...*..*..*..\n",
+    "....*....****...*****..*.*.*....*....*...*..****..\n",
+    "....*....*...*..*...*..*..**....*....*...*..*...*.\n",
+    "....*....*...*..*...*..*...*....*.....***...*...*.\n",
+    "..................................................\n",
+    "'''.split('\\n')[1:-1]\n",
+    "\n",
+    "cmds = '''\n",
+    "rotate column 3 1\n",
+    "left 3 7\n",
+    "rotate column 1 1\n",
+    "left 3 7\n",
+    "'''.split('\\n')[1:-1]\n",
+    "interpret(cmds, trantor_grid, show_each_step=True, uninterpret=True)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 18,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def transpose(grid):\n",
+    "    return list(zip(*grid))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 19,
+   "metadata": {
+    "scrolled": true
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "[('.', '*', '.', '.', '.', '.', '*', '.'),\n",
+       " ('.', '.', '.', '.', '.', '.', '.', '.'),\n",
+       " ('*', '.', '.', '.', '.', '.', '.', '.'),\n",
+       " ('.', '*', '.', '.', '.', '.', '.', '.'),\n",
+       " ('.', '*', '*', '*', '*', '*', '*', '.'),\n",
+       " ('.', '*', '.', '.', '.', '.', '.', '.'),\n",
+       " ('.', '*', '.', '.', '.', '.', '.', '.'),\n",
+       " ('.', '.', '.', '.', '.', '.', '.', '.'),\n",
+       " ('.', '.', '.', '.', '.', '.', '.', '.'),\n",
+       " ('.', '*', '*', '*', '*', '*', '*', '.'),\n",
+       " ('.', '*', '.', '.', '*', '.', '.', '.'),\n",
+       " ('.', '*', '.', '.', '*', '.', '.', '.'),\n",
+       " ('.', '*', '.', '*', '*', '.', '.', '.'),\n",
+       " ('.', '.', '*', '.', '.', '*', '*', '.'),\n",
+       " ('.', '.', '.', '.', '.', '.', '.', '.'),\n",
+       " ('.', '.', '.', '.', '.', '.', '.', '.'),\n",
+       " ('.', '.', '*', '*', '*', '*', '*', '.'),\n",
+       " ('.', '*', '.', '.', '*', '.', '.', '.'),\n",
+       " ('.', '*', '.', '.', '*', '.', '.', '.'),\n",
+       " ('.', '*', '.', '.', '*', '.', '.', '.'),\n",
+       " ('.', '.', '*', '*', '*', '*', '*', '.'),\n",
+       " ('.', '.', '.', '.', '.', '.', '.', '.'),\n",
+       " ('.', '.', '.', '.', '.', '.', '.', '.'),\n",
+       " ('.', '*', '*', '*', '*', '*', '*', '.'),\n",
+       " ('.', '.', '*', '.', '.', '.', '.', '.'),\n",
+       " ('.', '.', '.', '*', '*', '.', '.', '.'),\n",
+       " ('.', '.', '.', '.', '.', '*', '.', '.'),\n",
+       " ('.', '*', '*', '*', '*', '*', '*', '.'),\n",
+       " ('.', '.', '.', '.', '.', '.', '.', '.'),\n",
+       " ('.', '.', '.', '.', '.', '.', '.', '.'),\n",
+       " ('.', '*', '.', '.', '.', '.', '.', '.'),\n",
+       " ('.', '*', '.', '.', '.', '.', '.', '.'),\n",
+       " ('.', '*', '*', '*', '*', '*', '*', '.'),\n",
+       " ('.', '*', '.', '.', '.', '.', '.', '.'),\n",
+       " ('.', '*', '.', '.', '.', '.', '.', '.'),\n",
+       " ('.', '.', '.', '.', '.', '.', '.', '.'),\n",
+       " ('.', '.', '.', '.', '.', '.', '.', '.'),\n",
+       " ('.', '.', '*', '*', '*', '*', '.', '.'),\n",
+       " ('.', '*', '.', '.', '.', '.', '*', '.'),\n",
+       " ('.', '*', '.', '.', '.', '.', '*', '.'),\n",
+       " ('.', '*', '.', '.', '.', '.', '*', '.'),\n",
+       " ('.', '.', '*', '*', '*', '*', '.', '.'),\n",
+       " ('.', '.', '.', '.', '.', '.', '.', '.'),\n",
+       " ('.', '.', '.', '.', '.', '.', '.', '.'),\n",
+       " ('.', '*', '*', '*', '*', '*', '*', '.'),\n",
+       " ('.', '*', '.', '.', '*', '.', '.', '.'),\n",
+       " ('.', '*', '.', '.', '*', '.', '.', '.'),\n",
+       " ('.', '*', '.', '*', '*', '.', '.', '.'),\n",
+       " ('.', '.', '*', '.', '.', '*', '*', '.'),\n",
+       " ('.', '.', '.', '.', '.', '.', '.', '.')]"
+      ]
+     },
+     "execution_count": 19,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "transpose(trantor_grid)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 20,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def count_empty_rows(grid, col):\n",
+    "    return len(list(itertools.takewhile(lambda c: c == '.', transpose(grid)[col-1])))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 21,
+   "metadata": {
+    "scrolled": true
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "[1,\n",
+       " 8,\n",
+       " 0,\n",
+       " 1,\n",
+       " 1,\n",
+       " 1,\n",
+       " 1,\n",
+       " 8,\n",
+       " 8,\n",
+       " 1,\n",
+       " 1,\n",
+       " 1,\n",
+       " 1,\n",
+       " 2,\n",
+       " 8,\n",
+       " 8,\n",
+       " 2,\n",
+       " 1,\n",
+       " 1,\n",
+       " 1,\n",
+       " 2,\n",
+       " 8,\n",
+       " 8,\n",
+       " 1,\n",
+       " 2,\n",
+       " 3,\n",
+       " 5,\n",
+       " 1,\n",
+       " 8,\n",
+       " 8,\n",
+       " 1,\n",
+       " 1,\n",
+       " 1,\n",
+       " 1,\n",
+       " 1,\n",
+       " 8,\n",
+       " 8,\n",
+       " 2,\n",
+       " 1,\n",
+       " 1,\n",
+       " 1,\n",
+       " 2,\n",
+       " 8,\n",
+       " 8,\n",
+       " 1,\n",
+       " 1,\n",
+       " 1,\n",
+       " 1,\n",
+       " 2,\n",
+       " 8]"
+      ]
+     },
+     "execution_count": 21,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "[count_empty_rows(trantor_grid, i) for i in range(1, WIDTH+1)]"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 22,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def lift_cols(grid, randomise=False):\n",
+    "    commands = []\n",
+    "    h = len(grid)\n",
+    "    for i in range(1, len(grid[0])+1):\n",
+    "        n = count_empty_rows(grid, i)\n",
+    "        if n != h:\n",
+    "            commands.append('rotate column {} {}'.format(i, n))\n",
+    "    if randomise:\n",
+    "        random.shuffle(commands)\n",
+    "    return commands"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 23,
+   "metadata": {
+    "scrolled": true
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "['rotate column 3 1',\n",
+       " 'rotate column 4 1',\n",
+       " 'rotate column 5 1',\n",
+       " 'rotate column 6 1',\n",
+       " 'rotate column 7 1',\n",
+       " 'rotate column 10 1',\n",
+       " 'rotate column 11 1',\n",
+       " 'rotate column 12 1',\n",
+       " 'rotate column 13 1',\n",
+       " 'rotate column 14 2',\n",
+       " 'rotate column 17 2',\n",
+       " 'rotate column 18 1',\n",
+       " 'rotate column 19 1',\n",
+       " 'rotate column 20 1',\n",
+       " 'rotate column 21 2',\n",
+       " 'rotate column 24 1',\n",
+       " 'rotate column 25 2',\n",
+       " 'rotate column 26 3',\n",
+       " 'rotate column 27 5',\n",
+       " 'rotate column 28 1',\n",
+       " 'rotate column 31 1',\n",
+       " 'rotate column 32 1',\n",
+       " 'rotate column 33 1',\n",
+       " 'rotate column 34 1',\n",
+       " 'rotate column 35 1',\n",
+       " 'rotate column 38 2',\n",
+       " 'rotate column 39 1',\n",
+       " 'rotate column 40 1',\n",
+       " 'rotate column 41 1',\n",
+       " 'rotate column 42 2',\n",
+       " 'rotate column 45 1',\n",
+       " 'rotate column 46 1',\n",
+       " 'rotate column 47 1',\n",
+       " 'rotate column 48 1',\n",
+       " 'rotate column 49 2']"
+      ]
+     },
+     "execution_count": 23,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "lift_cols(tg())"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 24,
+   "metadata": {
+    "scrolled": true
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "['rotate column 19 1',\n",
+       " 'rotate column 46 1',\n",
+       " 'rotate column 10 1',\n",
+       " 'rotate column 25 2',\n",
+       " 'rotate column 39 1',\n",
+       " 'rotate column 3 1',\n",
+       " 'rotate column 4 1',\n",
+       " 'rotate column 41 1',\n",
+       " 'rotate column 47 1',\n",
+       " 'rotate column 14 2',\n",
+       " 'rotate column 21 2',\n",
+       " 'rotate column 48 1',\n",
+       " 'rotate column 34 1',\n",
+       " 'rotate column 49 2',\n",
+       " 'rotate column 28 1',\n",
+       " 'rotate column 32 1',\n",
+       " 'rotate column 18 1',\n",
+       " 'rotate column 45 1',\n",
+       " 'rotate column 12 1',\n",
+       " 'rotate column 38 2',\n",
+       " 'rotate column 24 1',\n",
+       " 'rotate column 40 1',\n",
+       " 'rotate column 17 2',\n",
+       " 'rotate column 7 1',\n",
+       " 'rotate column 31 1',\n",
+       " 'rotate column 5 1',\n",
+       " 'rotate column 26 3',\n",
+       " 'rotate column 6 1',\n",
+       " 'rotate column 35 1',\n",
+       " 'rotate column 20 1',\n",
+       " 'rotate column 42 2',\n",
+       " 'rotate column 11 1',\n",
+       " 'rotate column 33 1',\n",
+       " 'rotate column 13 1',\n",
+       " 'rotate column 27 5']"
+      ]
+     },
+     "execution_count": 24,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "lift_cols(tg(), randomise=True)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 25,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def count_empty_cols(grid, row):\n",
+    "    return len(list(itertools.takewhile(lambda r: r == '.', grid[row-1])))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 26,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "[2, 0, 4, 4, 4, 4, 0, 50]"
+      ]
+     },
+     "execution_count": 26,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "[count_empty_cols(trantor_grid, i) for i in range(1, HEIGHT+1)]"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 27,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def slide_rows(grid, randomise=False):\n",
+    "    commands = []\n",
+    "    w = len(grid[0])\n",
+    "    for i in range(1, len(grid)+1):\n",
+    "        n = count_empty_cols(grid, i)\n",
+    "        if n != w:\n",
+    "            commands.append('rotate row {} {}'.format(i, n))\n",
+    "    if randomise:\n",
+    "        random.shuffle(commands)\n",
+    "    return commands"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 28,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def untop(grid, randomise=False):\n",
+    "    groups = [(k, len(list(g))) for k, g in itertools.groupby(grid[0])]\n",
+    "    commands = []\n",
+    "    col = 1\n",
+    "    for c, l in groups:\n",
+    "        if c == '*':\n",
+    "            commands.append('top {} {}'.format(col, col + l - 1))\n",
+    "        col += l\n",
+    "    if randomise:\n",
+    "        random.shuffle(commands)\n",
+    "    return commands"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 29,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def unleft(grid, randomise=False):\n",
+    "    groups = [(k, len(list(g))) for k, g in itertools.groupby(transpose(grid)[0])]\n",
+    "    commands = []\n",
+    "    row = 1\n",
+    "    for c, l in groups:\n",
+    "        if c == '*':\n",
+    "            commands.append('left {} {}'.format(row, row + l - 1))\n",
+    "        row += l\n",
+    "    if randomise:\n",
+    "        random.shuffle(commands)\n",
+    "    return commands"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 30,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "['***********.******.*****.*..***********.******....',\n",
+       " '*....*...*..*...*..*...*....*....*...*..*...*.....',\n",
+       " '*....*..*...*...*..*.*.*....*....*...*..*..*......',\n",
+       " '*....*.**...*..**..*.*.*....*....*...*..*.**......',\n",
+       " '*....*...*..*...*..*...*....*........*..*...*.....',\n",
+       " '*........*......*...........*.......*.......*.....',\n",
+       " '..................................................',\n",
+       " '..................................................']"
+      ]
+     },
+     "execution_count": 30,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "trantor_grid = tg() \n",
+    "\n",
+    "cmds = []\n",
+    "\n",
+    "c = slide_rows(trantor_grid)\n",
+    "cmds = c + cmds\n",
+    "\n",
+    "interpret(c, trantor_grid, uninterpret=True)\n",
+    "\n",
+    "c = lift_cols(trantor_grid)\n",
+    "cmds = c + cmds\n",
+    "\n",
+    "interpret(c, trantor_grid, uninterpret=True)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 31,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "['top 1 11', 'top 13 18', 'top 20 24', 'top 26 26', 'top 29 39', 'top 41 46']"
+      ]
+     },
+     "execution_count": 31,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "g = interpret(cmds, tg(), uninterpret=True)\n",
+    "untop(g)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 32,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "..................................................\n",
+      ".....*...*..*...*..*...*....*....*...*..*...*.....\n",
+      ".....*..*...*...*..*.*.*....*....*...*..*..*......\n",
+      ".....*.**...*..**..*.*.*....*....*...*..*.**......\n",
+      ".....*...*..*...*..*...*....*........*..*...*.....\n",
+      ".........*......*...........*.......*.......*.....\n",
+      "..................................................\n",
+      "..................................................\n"
+     ]
+    }
+   ],
+   "source": [
+    "g = tg() \n",
+    "\n",
+    "cmds = []\n",
+    "\n",
+    "c = slide_rows(g, randomise=True)\n",
+    "interpret(c, g, uninterpret=True)\n",
+    "cmds = c + cmds\n",
+    "\n",
+    "c = lift_cols(g, randomise=True)\n",
+    "interpret(c, g, uninterpret=True)\n",
+    "cmds = c + cmds\n",
+    "\n",
+    "c = untop(g, randomise=True)\n",
+    "interpret(c, g, uninterpret=True)\n",
+    "cmds = c + cmds\n",
+    "\n",
+    "c = unleft(g, randomise=True)\n",
+    "interpret(c, g, uninterpret=True)\n",
+    "cmds = c + cmds\n",
+    "\n",
+    "print_grid(g)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 33,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "..................................................\n",
+      "..................................................\n",
+      "..................................................\n",
+      "..................................................\n",
+      "..................................................\n",
+      "..................................................\n",
+      "..................................................\n",
+      "..................................................\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "127"
+      ]
+     },
+     "execution_count": 33,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "g = tg() \n",
+    "cmds = []\n",
+    "\n",
+    "while '*' in ''.join(g):\n",
+    "    if random.choice([True, False]):\n",
+    "        c = slide_rows(g, randomise=True)\n",
+    "        interpret(c, g, uninterpret=True)\n",
+    "        cmds = c + cmds\n",
+    "    \n",
+    "        c = unleft(g, randomise=True)\n",
+    "        interpret(c, g, uninterpret=True)\n",
+    "        cmds = c + cmds\n",
+    "        \n",
+    "    else:\n",
+    "        c = lift_cols(g, randomise=True)\n",
+    "        interpret(c, g, uninterpret=True)\n",
+    "        cmds = c + cmds\n",
+    "    \n",
+    "        c = untop(g, randomise=True)\n",
+    "        interpret(c, g, uninterpret=True)\n",
+    "        cmds = c + cmds\n",
+    "    \n",
+    "print_grid(g)\n",
+    "len(cmds)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 34,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "['..................................................',\n",
+       " '..................................................',\n",
+       " '..................................................',\n",
+       " '..................................................',\n",
+       " '..................................................',\n",
+       " '..................................................',\n",
+       " '..................................................',\n",
+       " '..................................................']"
+      ]
+     },
+     "execution_count": 34,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "g = tg()\n",
+    "interpret(cmds, g, uninterpret=True)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 35,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "['..................................................',\n",
+       " '..*****..****....***...*...*..*****...***...****..',\n",
+       " '....*....*...*..*...*..**..*....*....*...*..*...*.',\n",
+       " '....*....*..*...*...*..*.*.*....*....*...*..*..*..',\n",
+       " '....*....****...*****..*.*.*....*....*...*..****..',\n",
+       " '....*....*...*..*...*..*..**....*....*...*..*...*.',\n",
+       " '....*....*...*..*...*..*...*....*.....***...*...*.',\n",
+       " '..................................................']"
+      ]
+     },
+     "execution_count": 35,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "interpret(cmds)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 36,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "['top 25 25',\n",
+       " 'top 2 2',\n",
+       " 'rotate column 2 2',\n",
+       " 'rotate column 25 2',\n",
+       " 'top 18 18',\n",
+       " 'top 13 13',\n",
+       " 'top 2 2',\n",
+       " 'top 4 8',\n",
+       " 'top 22 22',\n",
+       " 'top 25 25',\n",
+       " 'rotate column 7 2',\n",
+       " 'rotate column 5 2',\n",
+       " 'rotate column 4 2',\n",
+       " 'rotate column 6 1',\n",
+       " 'rotate column 8 2',\n",
+       " 'rotate column 13 2',\n",
+       " 'rotate column 22 1',\n",
+       " 'rotate column 2 2',\n",
+       " 'rotate column 18 2',\n",
+       " 'rotate column 25 1',\n",
+       " 'top 33 35',\n",
+       " 'top 2 10',\n",
+       " 'top 25 26',\n",
+       " 'top 18 19',\n",
+       " 'top 29 30',\n",
+       " 'top 21 22',\n",
+       " 'top 12 16',\n",
+       " 'top 37 37',\n",
+       " 'rotate column 19 1',\n",
+       " 'rotate column 9 3',\n",
+       " 'rotate column 25 1',\n",
+       " 'rotate column 3 5',\n",
+       " 'rotate column 33 3',\n",
+       " 'rotate column 5 1',\n",
+       " 'rotate column 2 1',\n",
+       " 'rotate column 12 3',\n",
+       " 'rotate column 21 3',\n",
+       " 'rotate column 14 3',\n",
+       " 'rotate column 22 1',\n",
+       " 'rotate column 6 2',\n",
+       " 'rotate column 26 3',\n",
+       " 'rotate column 10 1',\n",
+       " 'rotate column 35 3',\n",
+       " 'rotate column 16 3',\n",
+       " 'rotate column 18 2',\n",
+       " 'rotate column 30 3',\n",
+       " 'rotate column 13 2',\n",
+       " 'rotate column 15 1',\n",
+       " 'rotate column 37 3',\n",
+       " 'rotate column 8 2',\n",
+       " 'rotate column 4 2',\n",
+       " 'rotate column 29 4',\n",
+       " 'rotate column 7 3',\n",
+       " 'rotate column 34 3',\n",
+       " 'left 2 6',\n",
+       " 'rotate row 5 4',\n",
+       " 'rotate row 6 15',\n",
+       " 'rotate row 2 3',\n",
+       " 'rotate row 4 1',\n",
+       " 'rotate row 3 4',\n",
+       " 'left 2 6',\n",
+       " 'rotate row 4 1',\n",
+       " 'rotate row 2 4',\n",
+       " 'rotate row 3 4',\n",
+       " 'rotate row 6 7',\n",
+       " 'rotate row 5 3',\n",
+       " 'left 2 6',\n",
+       " 'rotate row 2 7',\n",
+       " 'rotate row 4 1',\n",
+       " 'rotate row 6 7',\n",
+       " 'rotate row 3 3',\n",
+       " 'rotate row 5 4',\n",
+       " 'top 42 45',\n",
+       " 'top 21 21',\n",
+       " 'top 24 25',\n",
+       " 'top 28 33',\n",
+       " 'top 36 40',\n",
+       " 'top 2 5',\n",
+       " 'top 15 19',\n",
+       " 'top 7 12',\n",
+       " 'rotate column 38 1',\n",
+       " 'rotate column 33 2',\n",
+       " 'rotate column 25 1',\n",
+       " 'rotate column 3 1',\n",
+       " 'rotate column 40 2',\n",
+       " 'rotate column 31 1',\n",
+       " 'rotate column 16 1',\n",
+       " 'rotate column 42 1',\n",
+       " 'rotate column 32 1',\n",
+       " 'rotate column 19 2',\n",
+       " 'rotate column 37 1',\n",
+       " 'rotate column 10 1',\n",
+       " 'rotate column 21 1',\n",
+       " 'rotate column 18 5',\n",
+       " 'rotate column 30 1',\n",
+       " 'rotate column 44 1',\n",
+       " 'rotate column 8 1',\n",
+       " 'rotate column 4 1',\n",
+       " 'rotate column 5 2',\n",
+       " 'rotate column 36 1',\n",
+       " 'rotate column 24 2',\n",
+       " 'rotate column 29 1',\n",
+       " 'rotate column 2 1',\n",
+       " 'rotate column 15 1',\n",
+       " 'rotate column 12 2',\n",
+       " 'rotate column 9 1',\n",
+       " 'rotate column 43 1',\n",
+       " 'rotate column 45 1',\n",
+       " 'rotate column 17 1',\n",
+       " 'rotate column 11 4',\n",
+       " 'rotate column 28 1',\n",
+       " 'rotate column 7 1',\n",
+       " 'rotate column 39 3',\n",
+       " 'left 2 7',\n",
+       " 'rotate row 2 1',\n",
+       " 'rotate row 6 5',\n",
+       " 'rotate row 5 5',\n",
+       " 'rotate row 3 5',\n",
+       " 'rotate row 4 5',\n",
+       " 'rotate row 7 5',\n",
+       " 'left 2 7',\n",
+       " 'rotate row 4 4',\n",
+       " 'rotate row 2 2',\n",
+       " 'rotate row 3 4',\n",
+       " 'rotate row 5 4',\n",
+       " 'rotate row 7 4',\n",
+       " 'rotate row 6 4']"
+      ]
+     },
+     "execution_count": 36,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "cmds"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 37,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Final\n",
+      "..................................................\n",
+      "..*****..****....***...*...*..*****...***...****..\n",
+      "....*....*...*..*...*..**..*....*....*...*..*...*.\n",
+      "....*....*..*...*...*..*.*.*....*....*...*..*..*..\n",
+      "....*....****...*****..*.*.*....*....*...*..****..\n",
+      "....*....*...*..*...*..*..**....*....*...*..*...*.\n",
+      "....*....*...*..*...*..*...*....*.....***...*...*.\n",
+      "..................................................\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "['..................................................',\n",
+       " '..*****..****....***...*...*..*****...***...****..',\n",
+       " '....*....*...*..*...*..**..*....*....*...*..*...*.',\n",
+       " '....*....*..*...*...*..*.*.*....*....*...*..*..*..',\n",
+       " '....*....****...*****..*.*.*....*....*...*..****..',\n",
+       " '....*....*...*..*...*..*..**....*....*...*..*...*.',\n",
+       " '....*....*...*..*...*..*...*....*.....***...*...*.',\n",
+       " '..................................................']"
+      ]
+     },
+     "execution_count": 37,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "g = new_grid()\n",
+    "interpret(cmds, g, show_each_step=True, overprint=True)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 38,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Final\n",
+      "..................................................\n",
+      "..................................................\n",
+      "..................................................\n",
+      "..................................................\n",
+      "..................................................\n",
+      "..................................................\n",
+      "..................................................\n",
+      "..................................................\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "['..................................................',\n",
+       " '..................................................',\n",
+       " '..................................................',\n",
+       " '..................................................',\n",
+       " '..................................................',\n",
+       " '..................................................',\n",
+       " '..................................................',\n",
+       " '..................................................']"
+      ]
+     },
+     "execution_count": 38,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "g = tg()\n",
+    "interpret(cmds, g, show_each_step=True, overprint=True, uninterpret=True)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 39,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "98"
+      ]
+     },
+     "execution_count": 39,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "sum(1 for c in ''.join(tg()) if c == '*')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 41,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "2007"
+      ]
+     },
+     "execution_count": 41,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "open('03-pixels.txt', 'w').write('\\n'.join(cmds))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 48,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "................................................................................\n",
+      "................................................................................\n",
+      "................................................................................\n",
+      "................................................................................\n",
+      "................................................................................\n",
+      "................................................................................\n",
+      "................................................................................\n",
+      "................................................................................\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "255"
+      ]
+     },
+     "execution_count": 48,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "g = jantar_mantar() \n",
+    "cmds = []\n",
+    "\n",
+    "while '*' in ''.join(g):\n",
+    "    if random.choice([True, False]):\n",
+    "        c = slide_rows(g, randomise=True)\n",
+    "        interpret(c, g, uninterpret=True)\n",
+    "        cmds = c + cmds\n",
+    "    \n",
+    "        c = unleft(g, randomise=True)\n",
+    "        interpret(c, g, uninterpret=True)\n",
+    "        cmds = c + cmds\n",
+    "        \n",
+    "    else:\n",
+    "        c = lift_cols(g, randomise=True)\n",
+    "        interpret(c, g, uninterpret=True)\n",
+    "        cmds = c + cmds\n",
+    "    \n",
+    "        c = untop(g, randomise=True)\n",
+    "        interpret(c, g, uninterpret=True)\n",
+    "        cmds = c + cmds\n",
+    "    \n",
+    "print_grid(g)\n",
+    "len(cmds)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 61,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Final\n",
+      "...****..............*...................*.....*..............*.................\n",
+      "......*..............*...................***..**..............*.................\n",
+      "......*.*****.*****.****.*****..****.....*.*.***.*****.*****.****.*****..****...\n",
+      "......*.....*.*...*..*.......*..*........*..**.*.....*.*...*..*.......*..*......\n",
+      "......*.*****.*...*..*...*****..*........*..*..*.*****.*...*..*...*****..*......\n",
+      "......*.*...*.*...*..*...*...*..*........*.....*.*...*.*...*..*...*...*..*......\n",
+      "...*..*.*..**.*...*..**..*..**..*........*.....*.*..**.*...*..**..*..**..*......\n",
+      "....**...**.*.*...*...**..**.*..*........*.....*..**.*.*...*...**..**.*..*......\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "['...****..............*...................*.....*..............*.................',\n",
+       " '......*..............*...................***..**..............*.................',\n",
+       " '......*.*****.*****.****.*****..****.....*.*.***.*****.*****.****.*****..****...',\n",
+       " '......*.....*.*...*..*.......*..*........*..**.*.....*.*...*..*.......*..*......',\n",
+       " '......*.*****.*...*..*...*****..*........*..*..*.*****.*...*..*...*****..*......',\n",
+       " '......*.*...*.*...*..*...*...*..*........*.....*.*...*.*...*..*...*...*..*......',\n",
+       " '...*..*.*..**.*...*..**..*..**..*........*.....*.*..**.*...*..**..*..**..*......',\n",
+       " '....**...**.*.*...*...**..**.*..*........*.....*..**.*.*...*...**..**.*..*......']"
+      ]
+     },
+     "execution_count": 61,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "g = new_grid(w=80)\n",
+    "interpret(cmds, g, show_each_step=True, overprint=True)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 50,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "80"
+      ]
+     },
+     "execution_count": 50,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "len(jantar_mantar()[0])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 51,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "4123"
+      ]
+     },
+     "execution_count": 51,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "open('03-pixels.txt', 'w').write('\\n'.join(cmds))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 57,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "   ****              *                   *     *              *                 \n",
+      "      *              *                   ***  **              *                 \n",
+      "      * ***** ***** **** *****  ****     * * *** ***** ***** **** *****  ****   \n",
+      "      *     * *   *  *       *  *        *  ** *     * *   *  *       *  *      \n",
+      "      * ***** *   *  *   *****  *        *  *  * ***** *   *  *   *****  *      \n",
+      "      * *   * *   *  *   *   *  *        *     * *   * *   *  *   *   *  *      \n",
+      "   *  * *  ** *   *  **  *  **  *        *     * *  ** *   *  **  *  **  *      \n",
+      "    **   ** * *   *   **  ** *  *        *     *  ** * *   *   **  ** *  *      \n"
+     ]
+    }
+   ],
+   "source": [
+    "print_grid(jantar_mantar(), suppress_dots=True)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.5.2+"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/03-display-board/display-board-solution.ipynb b/03-display-board/display-board-solution.ipynb
new file mode 100644 (file)
index 0000000..271eb68
--- /dev/null
@@ -0,0 +1,401 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Laser display boards\n",
+    "\n",
+    "You need to navigate your way through Heathwick Airport to find the correct gate. The bad news is that all the display boards have gone down. The good news is that the terminal staff are handing out the machine-code instructions to generate the messages on the board. \n",
+    "\n",
+    "Given your l33t haxor skilz, it will be no problem to recreate the messages on the display boards.\n",
+    "\n",
+    "The board is grid, 80 pixels wide and 8 pixels tall, with row 1 being the top row and column 1 being the left column. The pixels are changed with these commands:\n",
+    "\n",
+    "* `top A B` switches the state of the pixels in the topmost row from columns A to B inclusive. If a pixel was lit, it becomes dark; if it was dark, it becomes lit.\n",
+    "* `left A B` is similar, but works on the left edge, toggling the state of pixles in the leftmost column in rows A to B inclusive. \n",
+    "* `rotate column A B` rotates column A by B spaces down. Pixels that are moved beyond the bottom edge \"wrap around\" to the top edge.\n",
+    "* `rotate row A B` rotates row A by B spaces to the right. Pixels that are moved beyond the right edge \"wrap around\" to the left edge.\n",
+    "\n",
+    "You can assume all numbers are integers, the row and column values are always valid, and `A` $\\le$ `B` in the `left` and `toggle` commands."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "For instance, with a smaller grid that is 10 pixels wide and 4 tall, this is what a sample sequence of instructions would do.\n",
+    "\n",
+    "* `toggle 1 6` turns on the first six pixels on the top row.\n",
+    "```\n",
+    "******....\n",
+    "..........\n",
+    "..........\n",
+    "..........\n",
+    "```\n",
+    "\n",
+    "* `rotate column 2 3` moves the lit pixel on the second column to the bottom row.\n",
+    "```\n",
+    "*.****....\n",
+    "..........\n",
+    "..........\n",
+    ".*........\n",
+    "```\n",
+    "\n",
+    "* `toggle 3 10` turns off the pixels in columns 4, 5, and 6, and turns on the pixels in columns 7 to 10.\n",
+    "\n",
+    "```\n",
+    "*.....****\n",
+    "..........\n",
+    "..........\n",
+    ".*........\n",
+    "```\n",
+    "\n",
+    "* `rotate column 8 1` moves the one lit pixel in column 8 down one row.\n",
+    "```\n",
+    "*.....*.**\n",
+    ".......*..\n",
+    "..........\n",
+    ".*........\n",
+    "```\n",
+    "\n",
+    "* `rotate row 2 6` moves that pixel off the right edge of the display, to it wraps around to appear in column 4.\n",
+    "```\n",
+    "*.....*.**\n",
+    "...*......\n",
+    "..........\n",
+    ".*........\n",
+    "```\n",
+    "\n",
+    "* `left 1 3` toggles the pixels in rows 1, 2, and 3 of the first column. The top left pixel (previously on) turns off, while the pixels in rows 2 and 3 come on.\n",
+    "```\n",
+    "......*.**\n",
+    "*..*......\n",
+    "*.........\n",
+    ".*........\n",
+    "```"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Part 1\n",
+    "You're standing in front of gate 9¾. You have [the instructions](03-pixels.txt). How many pixels would be lit on the board, if it was working?"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "import time\n",
+    "import re\n",
+    "from IPython.display import clear_output"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "WIDTH = 80\n",
+    "HEIGHT = 8"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def new_grid(w=WIDTH, h=HEIGHT):\n",
+    "    return ['.' * w for r in range(1, h+1)]"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def print_grid(grid, md=False, suppress_dots=False):\n",
+    "    if md:\n",
+    "        print('```')\n",
+    "    for row in grid:\n",
+    "        if suppress_dots:\n",
+    "            print(re.sub(r'\\.', ' ', row))\n",
+    "        else:\n",
+    "            print(row)\n",
+    "    if md:\n",
+    "        print('```')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def top(grid, l, r):\n",
+    "    new_segment = ''\n",
+    "    for i in range(l-1, r):\n",
+    "        if grid[0][i] == '.':\n",
+    "            new_segment += '*'\n",
+    "        else:\n",
+    "            new_segment += '.'\n",
+    "    grid[0] = grid[0][:l-1] + new_segment + grid[0][r:]\n",
+    "    return grid"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def left(grid, t, b):\n",
+    "    for i in range(t-1, b):\n",
+    "        if grid[i][0] == '.':\n",
+    "            grid[i] = '*' + grid[i][1:]\n",
+    "        else:\n",
+    "            grid[i] = '.' + grid[i][1:]\n",
+    "    return grid"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def rotate_column(grid, c, raw_n):\n",
+    "    n = raw_n % len(grid)\n",
+    "    col = [row[c-1] for row in grid]\n",
+    "    new_col = col[-n:] + col[:-n]\n",
+    "    for i in range(len(grid)):\n",
+    "        grid[i] = grid[i][:c-1] + new_col[i] + grid[i][c:]\n",
+    "    return grid"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def rotate_row(grid, r, raw_n):\n",
+    "    n = raw_n % len(grid[0])\n",
+    "    grid[r-1] = grid[r-1][-n:] + grid[r-1][:-n]\n",
+    "    return grid"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "command_dispatch = {'left': left, 'top': top,\n",
+    "                   'rotate row': rotate_row,\n",
+    "                   'rotate column': rotate_column}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def parse(command):\n",
+    "    cmd, a, b = command.rsplit(maxsplit=2)\n",
+    "    return cmd, int(a), int(b)  "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def interpret(commands, grid=None, w=WIDTH, h=HEIGHT, \n",
+    "              show_each_step=False, md=False, overprint=False):\n",
+    "    if grid is None:\n",
+    "        grid = new_grid(w, h)\n",
+    "    for c in commands:\n",
+    "        cmd, a, b = parse(c)\n",
+    "        if cmd in command_dispatch:\n",
+    "            command_dispatch[cmd](grid, a, b)\n",
+    "        else:\n",
+    "            raise ValueError('Unknown command')\n",
+    "        if show_each_step:\n",
+    "            if overprint:\n",
+    "                time.sleep(0.25)\n",
+    "            if md: \n",
+    "                print('`{}`'.format(c))\n",
+    "            else:\n",
+    "                print(c)\n",
+    "            print_grid(grid, md=md)\n",
+    "            print()\n",
+    "            if overprint:\n",
+    "                clear_output(wait=True)\n",
+    "    if show_each_step: \n",
+    "        print('Final')\n",
+    "        print_grid(grid, md=md)\n",
+    "    return grid"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 12,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "255"
+      ]
+     },
+     "execution_count": 12,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "cmds = [c.strip() for c in open('03-pixels.txt').readlines()]\n",
+    "len(cmds)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "188"
+      ]
+     },
+     "execution_count": 13,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "g = interpret(cmds)\n",
+    "sum(1 for c in ''.join(g) if c == '*')"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Part 2\n",
+    "Where does the flight go from gate 9¾?"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 14,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "...****..............*...................*.....*..............*.................\n",
+      "......*..............*...................***..**..............*.................\n",
+      "......*.*****.*****.****.*****..****.....*.*.***.*****.*****.****.*****..****...\n",
+      "......*.....*.*...*..*.......*..*........*..**.*.....*.*...*..*.......*..*......\n",
+      "......*.*****.*...*..*...*****..*........*..*..*.*****.*...*..*...*****..*......\n",
+      "......*.*...*.*...*..*...*...*..*........*.....*.*...*.*...*..*...*...*..*......\n",
+      "...*..*.*..**.*...*..**..*..**..*........*.....*.*..**.*...*..**..*..**..*......\n",
+      "....**...**.*.*...*...**..**.*..*........*.....*..**.*.*...*...**..**.*..*......\n"
+     ]
+    }
+   ],
+   "source": [
+    "print_grid(g)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 15,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "   ****              *                   *     *              *                 \n",
+      "      *              *                   ***  **              *                 \n",
+      "      * ***** ***** **** *****  ****     * * *** ***** ***** **** *****  ****   \n",
+      "      *     * *   *  *       *  *        *  ** *     * *   *  *       *  *      \n",
+      "      * ***** *   *  *   *****  *        *  *  * ***** *   *  *   *****  *      \n",
+      "      * *   * *   *  *   *   *  *        *     * *   * *   *  *   *   *  *      \n",
+      "   *  * *  ** *   *  **  *  **  *        *     * *  ** *   *  **  *  **  *      \n",
+      "    **   ** * *   *   **  ** *  *        *     *  ** * *   *   **  ** *  *      \n"
+     ]
+    }
+   ],
+   "source": [
+    "print_grid(g, suppress_dots=True)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.5.2+"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/03-display-board/jantar-mantar.pbm b/03-display-board/jantar-mantar.pbm
new file mode 100644 (file)
index 0000000..a718ab8
--- /dev/null
@@ -0,0 +1,12 @@
+P1
+# CREATOR: GIMP PNM Filter Version 1.1
+72 8
+1111000000000000001000000000000000001000001000000000000001000000000000
+0000010000000000000010000000000000000011100110000000000000010000000000
+0000000101111101111101111011111001111000101011101111101111101111011111
+0011110001000001010001001000000010010000001001101000001010001001000000
+0100100000010111110100010010001111100100000010010010111110100010010001
+1111001000000101000101000100100010001001000000100000101000101000100100
+0100010010001001010011010001001100100110010000001000001010011010001001
+1001001100100001100011010100010001100110100100000010000010011010100010
+0011001101001000
\ No newline at end of file