{
  "id": "P037",
  "slug": "pixel-paint",
  "key": "P037-pixel-paint",
  "title": "Pixel paint",
  "summary": "A 12 x 24 canvas of characters: paint single cells, Bresenham lines and boxes with the pen's color, fill an area with the paint bucket, switch pens (one of them erases), and undo as far back as you like. The canvas is a dictionary keyed by (row, column) tuples.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P037 - Pixel paint\n\nA canvas of 12 rows and 24 columns, drawn in characters. Paint single cells,\nlines and boxes with the pen's color, fill an area with the paint bucket,\nchange the pen - one of the pens paints the background back, like an eraser -\nand undo as far back as you like. The canvas is shown after every change.\n\n- `main.eml` - the menu, the questions and their checks, the pen, and the\n  undo history\n- `canvas.eml` - the canvas, painting cells with a record of what changed,\n  undoing such a record, and the canvas on screen\n- `shapes.eml` - which cells a line, a box or a fill covers\n\nHow each part works:\n\n- The canvas is a dictionary keyed by (row, column) tuples, as in the corpus\n  case `grid-coordinate-map`: a cell never painted is not in it and shows the\n  background. Painting returns the changes - each cell whose color really\n  changed, with its old and new color - and undo puts the old colors back,\n  last change first. A step that changes nothing is not recorded.\n- A line is Bresenham's: whole numbers only, an error term that tracks how\n  far the drawn cells are from the true line, one step at a time along the\n  longer direction and, when the error says so, along the shorter one too.\n  Every cell it draws lies within half a cell of the true line, and both\n  ends are drawn.\n- A fill spreads from the cell picked over every cell of the same color that\n  can be reached by moving up, down, left and right - so a diagonal line is\n  a wall it does not cross. The corpus case `flood-fill` recurses into the\n  four neighbours; here the cells still to visit wait on a list, and a cell\n  is marked when it joins the list, so none is taken twice.\n- A box is the outline of the rectangle with the two corners given; with\n  both corners in one row or column it is a line, with one corner twice a\n  single cell.\n\nWhat is checked: a cell as a row from 1 to 12 and a column from 1 to 24,\ntyped as `3 5` or `3,5`; a pen of exactly one of `# o + * ~ .`. An empty\nanswer cancels.\n\nSessions: `sessions/basic.in` draws a box and a diagonal line across it,\nfills the part above the line with o and the part below with + (33 cells\neach - the line keeps them apart), draws a long line and a short diagonal\nthat crosses it with *, undoes both, erases a corner of the box with the .\npen and fills the whole background with ~ (163 cells). `sessions/bad-input.in`\ngives menu choices 0 and x, an undo with nothing to undo, cells 0 5, 13 1,\n1 25, a b and 3, empty answers at each question, pens x and ##, a cell\npainted twice, a box with one corner twice and a box in one row, a fill on\ncells already that color, an erasing fill, and undoes everything, one step\npast the start.\n\nBuilt on the verified corpus cases `flood-fill` (the paint bucket: spread to\nthe four neighbours while they hold the color being replaced) and\n`grid-coordinate-map` (a sparse grid in a dictionary keyed by row and column\ntuples).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P037 pixel paint: a 12 x 24 canvas of characters. Paint single cells,\n# lines and boxes with the pen's color, fill an area with the paint bucket,\n# change the pen, and undo as far back as you like.\nimport canvas\nimport shapes\n\ndef trim(s):\n    0 => i\n    len(s) => j\n    while i < j and s[i] == \" \":\n        i + 1 => i\n    while j > i and s[j - 1] == \" \":\n        j - 1 => j\n    return s[i:j]\n\ndef whole_number(s):\n    # The value of 1 to 3 digits, otherwise -1.\n    if s == \"\" or len(s) > 3:\n        return -1\n    0 => n\n    for c in s:\n        if not (c in \"0123456789\"):\n            return -1\n        n * 10 + int(c) => n\n    return n\n\ndef ask_cell(prompt):\n    # A cell typed as \"row column\" or \"row,column\", counting from 1, as a\n    # (row, column) tuple counting from 0; [] when the answer is empty.\n    while True:\n        trim(input(prompt)) => answer\n        if answer == \"\":\n            return []\n        [] => parts\n        \"\" => word\n        for c in answer + \" \":\n            if c == \" \" or c == \",\":\n                if word != \"\":\n                    parts + [word] => parts\n                \"\" => word\n            else:\n                word + c => word\n        if len(parts) == 2:\n            whole_number(parts[0]) => r\n            whole_number(parts[1]) => c\n            if r >= 1 and r <= canvas.rows and c >= 1 and c <= canvas.cols:\n                return (r - 1, c - 1)\n        (\"Type a row from 1 to \" + str(canvas.rows) + \" and a column from 1 to \" + str(canvas.cols) + \", like 3 5.\") ^0\n\ndef cells_text(n):\n    if n == 1:\n        return \"1 cell\"\n    return str(n) + \" cells\"\n\ndef done(cv, pen, history, name, cells, changes):\n    # Records a drawing step for undo and shows the result. Returns history.\n    if len(changes) == 0:\n        (\"Nothing changed: \" + cells_text(len(cells)) + \", all already \" + pen + \".\") ^0\n        return history\n    (name + \": \" + cells_text(len(cells)) + \", \" + str(len(changes)) + \" changed.\") ^0\n    canvas.show(cv, pen)\n    return history + [[name, changes]]\n\ndef draw(cv, pen, history, kind):\n    if kind == \"dot\":\n        ask_cell(\"cell> \") => a\n        if len(a) == 0:\n            \"Cancelled.\" ^0\n            return history\n        [a] => cells\n    elif kind == \"fill\":\n        ask_cell(\"start> \") => a\n        if len(a) == 0:\n            \"Cancelled.\" ^0\n            return history\n        shapes.area(cv, a) => cells\n    else:\n        ask_cell(\"from> \") => a\n        if len(a) == 0:\n            \"Cancelled.\" ^0\n            return history\n        ask_cell(\"to> \") => b\n        if len(b) == 0:\n            \"Cancelled.\" ^0\n            return history\n        if kind == \"line\":\n            shapes.line(a[0], a[1], b[0], b[1]) => cells\n        else:\n            shapes.box(a[0], a[1], b[0], b[1]) => cells\n    canvas.paint(cv, cells, pen) => changes\n    return done(cv, pen, history, kind, cells, changes)\n\ndef choose_pen(pen):\n    while True:\n        trim(input(\"pen (# o + * ~, or . to erase)> \")) => answer\n        if answer == \"\":\n            \"Cancelled.\" ^0\n            return pen\n        if len(answer) == 1 and answer in canvas.colors:\n            if answer == canvas.background:\n                \"The pen is now . - it paints the background back, like an eraser.\" ^0\n            else:\n                (\"The pen is now \" + answer + \".\") ^0\n            return answer\n        \"Type one character: # o + * ~, or . to erase.\" ^0\n\n\"== Pixel paint ==\" ^0\n\"A 12 x 24 canvas. Rows and columns count from 1; type a cell as 3 5.\" ^0\n{} => cv\n\"#\" => pen\n[] => history\nTrue => running\nwhile running:\n    \"\" ^0\n    \"1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\" ^0\n    trim(input(\"choice> \")) => choice\n    if choice == \"1\":\n        draw(cv, pen, history, \"dot\") => history\n    elif choice == \"2\":\n        draw(cv, pen, history, \"line\") => history\n    elif choice == \"3\":\n        draw(cv, pen, history, \"box\") => history\n    elif choice == \"4\":\n        draw(cv, pen, history, \"fill\") => history\n    elif choice == \"5\":\n        choose_pen(pen) => pen\n    elif choice == \"6\":\n        if len(history) == 0:\n            \"Nothing to undo.\" ^0\n        else:\n            history[len(history) - 1] => last\n            canvas.undo(cv, last[1])\n            history[0:len(history) - 1] => history\n            (\"Undid the \" + last[0] + \": \" + cells_text(len(last[1])) + \" put back.\") ^0\n            canvas.show(cv, pen)\n    elif choice == \"7\":\n        canvas.show(cv, pen)\n    elif choice == \"8\":\n        False => running\n    else:\n        \"Pick a number from 1 to 8.\" ^0\n\"Bye.\" ^0\n",
      "python": "import canvas\nimport shapes\n\ndef trim(s):\n    i = 0\n    j = len(s)\n    while i < j and s[i] == \" \":\n        i = i + 1\n    while j > i and s[j - 1] == \" \":\n        j = j - 1\n    return s[i:j]\n\ndef whole_number(s):\n    if s == \"\" or len(s) > 3:\n        return -1\n    n = 0\n    for c in s:\n        if not c in \"0123456789\":\n            return -1\n        n = n * 10 + int(c)\n    return n\n\ndef ask_cell(prompt):\n    while True:\n        answer = trim(input(prompt))\n        if answer == \"\":\n            return []\n        parts = []\n        word = \"\"\n        for c in answer + \" \":\n            if c == \" \" or c == \",\":\n                if word != \"\":\n                    parts = parts + [word]\n                word = \"\"\n            else:\n                word = word + c\n        if len(parts) == 2:\n            r = whole_number(parts[0])\n            c = whole_number(parts[1])\n            if r >= 1 and r <= canvas.rows and c >= 1 and c <= canvas.cols:\n                return (r - 1, c - 1)\n        print(\"Type a row from 1 to \" + str(canvas.rows) + \" and a column from 1 to \" + str(canvas.cols) + \", like 3 5.\")\n\ndef cells_text(n):\n    if n == 1:\n        return \"1 cell\"\n    return str(n) + \" cells\"\n\ndef done(cv, pen, history, name, cells, changes):\n    if len(changes) == 0:\n        print(\"Nothing changed: \" + cells_text(len(cells)) + \", all already \" + pen + \".\")\n        return history\n    print(name + \": \" + cells_text(len(cells)) + \", \" + str(len(changes)) + \" changed.\")\n    canvas.show(cv, pen)\n    return history + [[name, changes]]\n\ndef draw(cv, pen, history, kind):\n    if kind == \"dot\":\n        a = ask_cell(\"cell> \")\n        if len(a) == 0:\n            print(\"Cancelled.\")\n            return history\n        cells = [a]\n    elif kind == \"fill\":\n        a = ask_cell(\"start> \")\n        if len(a) == 0:\n            print(\"Cancelled.\")\n            return history\n        cells = shapes.area(cv, a)\n    else:\n        a = ask_cell(\"from> \")\n        if len(a) == 0:\n            print(\"Cancelled.\")\n            return history\n        b = ask_cell(\"to> \")\n        if len(b) == 0:\n            print(\"Cancelled.\")\n            return history\n        if kind == \"line\":\n            cells = shapes.line(a[0], a[1], b[0], b[1])\n        else:\n            cells = shapes.box(a[0], a[1], b[0], b[1])\n    changes = canvas.paint(cv, cells, pen)\n    return done(cv, pen, history, kind, cells, changes)\n\ndef choose_pen(pen):\n    while True:\n        answer = trim(input(\"pen (# o + * ~, or . to erase)> \"))\n        if answer == \"\":\n            print(\"Cancelled.\")\n            return pen\n        if len(answer) == 1 and answer in canvas.colors:\n            if answer == canvas.background:\n                print(\"The pen is now . - it paints the background back, like an eraser.\")\n            else:\n                print(\"The pen is now \" + answer + \".\")\n            return answer\n        print(\"Type one character: # o + * ~, or . to erase.\")\n\nprint(\"== Pixel paint ==\")\nprint(\"A 12 x 24 canvas. Rows and columns count from 1; type a cell as 3 5.\")\ncv = {}\npen = \"#\"\nhistory = []\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\")\n    choice = trim(input(\"choice> \"))\n    if choice == \"1\":\n        history = draw(cv, pen, history, \"dot\")\n    elif choice == \"2\":\n        history = draw(cv, pen, history, \"line\")\n    elif choice == \"3\":\n        history = draw(cv, pen, history, \"box\")\n    elif choice == \"4\":\n        history = draw(cv, pen, history, \"fill\")\n    elif choice == \"5\":\n        pen = choose_pen(pen)\n    elif choice == \"6\":\n        if len(history) == 0:\n            print(\"Nothing to undo.\")\n        else:\n            last = history[len(history) - 1]\n            canvas.undo(cv, last[1])\n            history = history[0:len(history) - 1]\n            print(\"Undid the \" + last[0] + \": \" + cells_text(len(last[1])) + \" put back.\")\n            canvas.show(cv, pen)\n    elif choice == \"7\":\n        canvas.show(cv, pen)\n    elif choice == \"8\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 8.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "canvas.eml",
      "eml": "# P037 pixel paint - the canvas. 12 rows and 24 columns, counted from 0\n# inside the program. The canvas is a dictionary keyed by (row, column)\n# tuples, as in the corpus case grid-coordinate-map: a cell that was never\n# painted is simply not in it and shows the background \".\".\n\n12 => rows\n24 => cols\n\".\" => background\n\"#o+*~.\" => colors\n\ndef color_at(canvas, cell):\n    if cell in canvas:\n        return canvas[cell]\n    return background\n\ndef inside(r, c):\n    return r >= 0 and r < rows and c >= 0 and c < cols\n\ndef paint(canvas, cells, color):\n    # Paints the cells and returns the changes [cell, old, new] - only the\n    # cells whose color really changed - so that they can be undone.\n    [] => changes\n    for cell in cells:\n        color_at(canvas, cell) => old\n        if old != color:\n            changes + [[cell, old, color]] => changes\n            color => canvas[cell]\n    return changes\n\ndef undo(canvas, changes):\n    # Puts back the old colors, last change first.\n    len(changes) - 1 => i\n    while i >= 0:\n        changes[i][1] => canvas[changes[i][0]]\n        i - 1 => i\n\ndef painted(canvas):\n    # How many cells hold something other than the background.\n    0 => n\n    for cell in canvas:\n        if canvas[cell] != background:\n            n + 1 => n\n    return n\n\ndef show(canvas, pen):\n    painted(canvas) => n\n    \"cells\" => word\n    if n == 1:\n        \"cell\" => word\n    (\"Pen \" + pen + \", \" + str(n) + \" \" + word + \" painted\") ^0\n    \"    \" => header\n    for c in [1:cols]:\n        header + str(c % 10) => header\n    header ^0\n    for r in [0:rows - 1]:\n        str(r + 1) => label\n        while len(label) < 3:\n            \" \" + label => label\n        label + \" \" => line\n        for c in [0:cols - 1]:\n            line + color_at(canvas, (r, c)) => line\n        line ^0\n",
      "python": "rows = 12\ncols = 24\nbackground = \".\"\ncolors = \"#o+*~.\"\n\ndef color_at(canvas, cell):\n    if cell in canvas:\n        return canvas[cell]\n    return background\n\ndef inside(r, c):\n    return r >= 0 and r < rows and c >= 0 and c < cols\n\ndef paint(canvas, cells, color):\n    changes = []\n    for cell in cells:\n        old = color_at(canvas, cell)\n        if old != color:\n            changes = changes + [[cell, old, color]]\n            canvas[cell] = color\n    return changes\n\ndef undo(canvas, changes):\n    i = len(changes) - 1\n    while i >= 0:\n        canvas[changes[i][0]] = changes[i][1]\n        i = i - 1\n\ndef painted(canvas):\n    n = 0\n    for cell in canvas:\n        if canvas[cell] != background:\n            n = n + 1\n    return n\n\ndef show(canvas, pen):\n    n = painted(canvas)\n    word = \"cells\"\n    if n == 1:\n        word = \"cell\"\n    print(\"Pen \" + pen + \", \" + str(n) + \" \" + word + \" painted\")\n    header = \"    \"\n    for c in range(1, cols+1):\n        header = header + str(c % 10)\n    print(header)\n    for r in range(0, rows):\n        label = str(r + 1)\n        while len(label) < 3:\n            label = \" \" + label\n        line = label + \" \"\n        for c in range(0, cols):\n            line = line + color_at(canvas, (r, c))\n        print(line)\n"
    },
    {
      "name": "shapes.eml",
      "eml": "# P037 pixel paint - which cells a shape covers: a line, a box, and the area\n# a fill spreads over. Cells are (row, column) tuples.\nimport canvas\n\ndef step_toward(a, b):\n    if a < b:\n        return 1\n    if a > b:\n        return -1\n    return 0\n\ndef line(r0, c0, r1, c1):\n    # Bresenham's line from (r0, c0) to (r1, c1), with whole numbers only:\n    # err tracks how far the drawn cells are from the true line, and each\n    # step moves along the longer direction and, when err says so, along the\n    # shorter one too. Both ends are included.\n    abs(c1 - c0) => dc\n    abs(r1 - r0) => dr\n    step_toward(c0, c1) => sc\n    step_toward(r0, r1) => sr\n    dc - dr => err\n    [] => cells\n    True => going\n    while going:\n        cells + [(r0, c0)] => cells\n        if r0 == r1 and c0 == c1:\n            False => going\n        else:\n            2 * err => e2\n            if e2 > 0 - dr:\n                err - dr => err\n                c0 + sc => c0\n            if e2 < dc:\n                err + dc => err\n                r0 + sr => r0\n    return cells\n\ndef box(r0, c0, r1, c1):\n    # The outline of the rectangle with these two corners.\n    min(r0, r1) => top\n    max(r0, r1) => bottom\n    min(c0, c1) => left\n    max(c0, c1) => right\n    [] => cells\n    for c in [left:right]:\n        cells + [(top, c)] => cells\n        if bottom != top:\n            cells + [(bottom, c)] => cells\n    for r in [top + 1:bottom - 1]:\n        cells + [(r, left)] => cells\n        if right != left:\n            cells + [(r, right)] => cells\n    return cells\n\ndef area(cv, start):\n    # The cells a fill from start reaches: every cell of start's color that\n    # can be reached through up, down, left and right moves over that color.\n    # The corpus case flood-fill recurses into the four neighbours; this\n    # keeps the cells still to visit on a list instead, and marks a cell when\n    # it is put on the list, so none is taken twice.\n    canvas.color_at(cv, start) => target\n    {} => seen\n    True => seen[start]\n    [start] => todo\n    [] => cells\n    while len(todo) > 0:\n        todo[len(todo) - 1] => cell\n        todo[0:len(todo) - 1] => todo\n        cells + [cell] => cells\n        cell[0] => r\n        cell[1] => c\n        for d in [(r - 1, c), (r + 1, c), (r, c - 1), (r, c + 1)]:\n            if canvas.inside(d[0], d[1]) and not (d in seen) and canvas.color_at(cv, d) == target:\n                True => seen[d]\n                todo + [d] => todo\n    return cells\n",
      "python": "import canvas\n\ndef step_toward(a, b):\n    if a < b:\n        return 1\n    if a > b:\n        return -1\n    return 0\n\ndef line(r0, c0, r1, c1):\n    dc = abs(c1 - c0)\n    dr = abs(r1 - r0)\n    sc = step_toward(c0, c1)\n    sr = step_toward(r0, r1)\n    err = dc - dr\n    cells = []\n    going = True\n    while going:\n        cells = cells + [(r0, c0)]\n        if r0 == r1 and c0 == c1:\n            going = False\n        else:\n            e2 = 2 * err\n            if e2 > 0 - dr:\n                err = err - dr\n                c0 = c0 + sc\n            if e2 < dc:\n                err = err + dc\n                r0 = r0 + sr\n    return cells\n\ndef box(r0, c0, r1, c1):\n    top = min(r0, r1)\n    bottom = max(r0, r1)\n    left = min(c0, c1)\n    right = max(c0, c1)\n    cells = []\n    for c in range(left, right+1):\n        cells = cells + [(top, c)]\n        if bottom != top:\n            cells = cells + [(bottom, c)]\n    for r in range(top + 1, bottom):\n        cells = cells + [(r, left)]\n        if right != left:\n            cells = cells + [(r, right)]\n    return cells\n\ndef area(cv, start):\n    target = canvas.color_at(cv, start)\n    seen = {}\n    seen[start] = True\n    todo = [start]\n    cells = []\n    while len(todo) > 0:\n        cell = todo[len(todo) - 1]\n        todo = todo[0:len(todo) - 1]\n        cells = cells + [cell]\n        r = cell[0]\n        c = cell[1]\n        for d in [(r - 1, c), (r + 1, c), (r, c - 1), (r, c + 1)]:\n            if canvas.inside(d[0], d[1]) and not d in seen and canvas.color_at(cv, d) == target:\n                seen[d] = True\n                todo = todo + [d]\n    return cells\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "0\nx\n6\n1\n0 5\n13 1\n1 25\na b\n3\n\n2\n1 1\n\n3\n\n4\n\n5\nx\n##\n\n1\n1 1\n1\n1,1\n3\n5 5\n5 5\n3\n7 9\n7 3\n4\n7 5\n5\n.\n4\n1 1\n6\n6\n6\n6\n6\n7\n8\n",
      "screen": "== Pixel paint ==\nA 12 x 24 canvas. Rows and columns count from 1; type a cell as 3 5.\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 0\nPick a number from 1 to 8.\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> x\nPick a number from 1 to 8.\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 6\nNothing to undo.\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 1\ncell> 0 5\nType a row from 1 to 12 and a column from 1 to 24, like 3 5.\ncell> 13 1\nType a row from 1 to 12 and a column from 1 to 24, like 3 5.\ncell> 1 25\nType a row from 1 to 12 and a column from 1 to 24, like 3 5.\ncell> a b\nType a row from 1 to 12 and a column from 1 to 24, like 3 5.\ncell> 3\nType a row from 1 to 12 and a column from 1 to 24, like 3 5.\ncell> \nCancelled.\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 2\nfrom> 1 1\nto> \nCancelled.\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 3\nfrom> \nCancelled.\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 4\nstart> \nCancelled.\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 5\npen (# o + * ~, or . to erase)> x\nType one character: # o + * ~, or . to erase.\npen (# o + * ~, or . to erase)> ##\nType one character: # o + * ~, or . to erase.\npen (# o + * ~, or . to erase)> \nCancelled.\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 1\ncell> 1 1\ndot: 1 cell, 1 changed.\nPen #, 1 cell painted\n    123456789012345678901234\n  1 #.......................\n  2 ........................\n  3 ........................\n  4 ........................\n  5 ........................\n  6 ........................\n  7 ........................\n  8 ........................\n  9 ........................\n 10 ........................\n 11 ........................\n 12 ........................\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 1\ncell> 1,1\nNothing changed: 1 cell, all already #.\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 3\nfrom> 5 5\nto> 5 5\nbox: 1 cell, 1 changed.\nPen #, 2 cells painted\n    123456789012345678901234\n  1 #.......................\n  2 ........................\n  3 ........................\n  4 ........................\n  5 ....#...................\n  6 ........................\n  7 ........................\n  8 ........................\n  9 ........................\n 10 ........................\n 11 ........................\n 12 ........................\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 3\nfrom> 7 9\nto> 7 3\nbox: 7 cells, 7 changed.\nPen #, 9 cells painted\n    123456789012345678901234\n  1 #.......................\n  2 ........................\n  3 ........................\n  4 ........................\n  5 ....#...................\n  6 ........................\n  7 ..#######...............\n  8 ........................\n  9 ........................\n 10 ........................\n 11 ........................\n 12 ........................\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 4\nstart> 7 5\nNothing changed: 7 cells, all already #.\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 5\npen (# o + * ~, or . to erase)> .\nThe pen is now . - it paints the background back, like an eraser.\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 4\nstart> 1 1\nfill: 1 cell, 1 changed.\nPen ., 8 cells painted\n    123456789012345678901234\n  1 ........................\n  2 ........................\n  3 ........................\n  4 ........................\n  5 ....#...................\n  6 ........................\n  7 ..#######...............\n  8 ........................\n  9 ........................\n 10 ........................\n 11 ........................\n 12 ........................\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 6\nUndid the fill: 1 cell put back.\nPen ., 9 cells painted\n    123456789012345678901234\n  1 #.......................\n  2 ........................\n  3 ........................\n  4 ........................\n  5 ....#...................\n  6 ........................\n  7 ..#######...............\n  8 ........................\n  9 ........................\n 10 ........................\n 11 ........................\n 12 ........................\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 6\nUndid the box: 7 cells put back.\nPen ., 2 cells painted\n    123456789012345678901234\n  1 #.......................\n  2 ........................\n  3 ........................\n  4 ........................\n  5 ....#...................\n  6 ........................\n  7 ........................\n  8 ........................\n  9 ........................\n 10 ........................\n 11 ........................\n 12 ........................\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 6\nUndid the box: 1 cell put back.\nPen ., 1 cell painted\n    123456789012345678901234\n  1 #.......................\n  2 ........................\n  3 ........................\n  4 ........................\n  5 ........................\n  6 ........................\n  7 ........................\n  8 ........................\n  9 ........................\n 10 ........................\n 11 ........................\n 12 ........................\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 6\nUndid the dot: 1 cell put back.\nPen ., 0 cells painted\n    123456789012345678901234\n  1 ........................\n  2 ........................\n  3 ........................\n  4 ........................\n  5 ........................\n  6 ........................\n  7 ........................\n  8 ........................\n  9 ........................\n 10 ........................\n 11 ........................\n 12 ........................\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 6\nNothing to undo.\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 7\nPen ., 0 cells painted\n    123456789012345678901234\n  1 ........................\n  2 ........................\n  3 ........................\n  4 ........................\n  5 ........................\n  6 ........................\n  7 ........................\n  8 ........................\n  9 ........................\n 10 ........................\n 11 ........................\n 12 ........................\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 8\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "3\n2 3\n8 20\n2\n3 5\n7 18\n5\no\n4\n4 10\n5\n+\n4\n3 4\n5\n*\n2\n10 2\n10 23\n2\n9 24\n12 21\n6\n6\n5\n.\n1\n2 3\n5\n~\n4\n1 1\n8\n",
      "screen": "== Pixel paint ==\nA 12 x 24 canvas. Rows and columns count from 1; type a cell as 3 5.\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 3\nfrom> 2 3\nto> 8 20\nbox: 46 cells, 46 changed.\nPen #, 46 cells painted\n    123456789012345678901234\n  1 ........................\n  2 ..##################....\n  3 ..#................#....\n  4 ..#................#....\n  5 ..#................#....\n  6 ..#................#....\n  7 ..#................#....\n  8 ..##################....\n  9 ........................\n 10 ........................\n 11 ........................\n 12 ........................\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 2\nfrom> 3 5\nto> 7 18\nline: 14 cells, 14 changed.\nPen #, 60 cells painted\n    123456789012345678901234\n  1 ........................\n  2 ..##################....\n  3 ..#.##.............#....\n  4 ..#...###..........#....\n  5 ..#......####......#....\n  6 ..#..........###...#....\n  7 ..#.............##.#....\n  8 ..##################....\n  9 ........................\n 10 ........................\n 11 ........................\n 12 ........................\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 5\npen (# o + * ~, or . to erase)> o\nThe pen is now o.\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 4\nstart> 4 10\nfill: 33 cells, 33 changed.\nPen o, 93 cells painted\n    123456789012345678901234\n  1 ........................\n  2 ..##################....\n  3 ..#.##ooooooooooooo#....\n  4 ..#...###oooooooooo#....\n  5 ..#......####oooooo#....\n  6 ..#..........###ooo#....\n  7 ..#.............##o#....\n  8 ..##################....\n  9 ........................\n 10 ........................\n 11 ........................\n 12 ........................\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 5\npen (# o + * ~, or . to erase)> +\nThe pen is now +.\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 4\nstart> 3 4\nfill: 33 cells, 33 changed.\nPen +, 126 cells painted\n    123456789012345678901234\n  1 ........................\n  2 ..##################....\n  3 ..#+##ooooooooooooo#....\n  4 ..#+++###oooooooooo#....\n  5 ..#++++++####oooooo#....\n  6 ..#++++++++++###ooo#....\n  7 ..#+++++++++++++##o#....\n  8 ..##################....\n  9 ........................\n 10 ........................\n 11 ........................\n 12 ........................\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 5\npen (# o + * ~, or . to erase)> *\nThe pen is now *.\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 2\nfrom> 10 2\nto> 10 23\nline: 22 cells, 22 changed.\nPen *, 148 cells painted\n    123456789012345678901234\n  1 ........................\n  2 ..##################....\n  3 ..#+##ooooooooooooo#....\n  4 ..#+++###oooooooooo#....\n  5 ..#++++++####oooooo#....\n  6 ..#++++++++++###ooo#....\n  7 ..#+++++++++++++##o#....\n  8 ..##################....\n  9 ........................\n 10 .**********************.\n 11 ........................\n 12 ........................\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 2\nfrom> 9 24\nto> 12 21\nline: 4 cells, 3 changed.\nPen *, 151 cells painted\n    123456789012345678901234\n  1 ........................\n  2 ..##################....\n  3 ..#+##ooooooooooooo#....\n  4 ..#+++###oooooooooo#....\n  5 ..#++++++####oooooo#....\n  6 ..#++++++++++###ooo#....\n  7 ..#+++++++++++++##o#....\n  8 ..##################....\n  9 .......................*\n 10 .**********************.\n 11 .....................*..\n 12 ....................*...\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 6\nUndid the line: 3 cells put back.\nPen *, 148 cells painted\n    123456789012345678901234\n  1 ........................\n  2 ..##################....\n  3 ..#+##ooooooooooooo#....\n  4 ..#+++###oooooooooo#....\n  5 ..#++++++####oooooo#....\n  6 ..#++++++++++###ooo#....\n  7 ..#+++++++++++++##o#....\n  8 ..##################....\n  9 ........................\n 10 .**********************.\n 11 ........................\n 12 ........................\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 6\nUndid the line: 22 cells put back.\nPen *, 126 cells painted\n    123456789012345678901234\n  1 ........................\n  2 ..##################....\n  3 ..#+##ooooooooooooo#....\n  4 ..#+++###oooooooooo#....\n  5 ..#++++++####oooooo#....\n  6 ..#++++++++++###ooo#....\n  7 ..#+++++++++++++##o#....\n  8 ..##################....\n  9 ........................\n 10 ........................\n 11 ........................\n 12 ........................\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 5\npen (# o + * ~, or . to erase)> .\nThe pen is now . - it paints the background back, like an eraser.\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 1\ncell> 2 3\ndot: 1 cell, 1 changed.\nPen ., 125 cells painted\n    123456789012345678901234\n  1 ........................\n  2 ...#################....\n  3 ..#+##ooooooooooooo#....\n  4 ..#+++###oooooooooo#....\n  5 ..#++++++####oooooo#....\n  6 ..#++++++++++###ooo#....\n  7 ..#+++++++++++++##o#....\n  8 ..##################....\n  9 ........................\n 10 ........................\n 11 ........................\n 12 ........................\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 5\npen (# o + * ~, or . to erase)> ~\nThe pen is now ~.\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 4\nstart> 1 1\nfill: 163 cells, 163 changed.\nPen ~, 288 cells painted\n    123456789012345678901234\n  1 ~~~~~~~~~~~~~~~~~~~~~~~~\n  2 ~~~#################~~~~\n  3 ~~#+##ooooooooooooo#~~~~\n  4 ~~#+++###oooooooooo#~~~~\n  5 ~~#++++++####oooooo#~~~~\n  6 ~~#++++++++++###ooo#~~~~\n  7 ~~#+++++++++++++##o#~~~~\n  8 ~~##################~~~~\n  9 ~~~~~~~~~~~~~~~~~~~~~~~~\n 10 ~~~~~~~~~~~~~~~~~~~~~~~~\n 11 ~~~~~~~~~~~~~~~~~~~~~~~~\n 12 ~~~~~~~~~~~~~~~~~~~~~~~~\n\n1) dot  2) line  3) box  4) fill  5) pen  6) undo  7) show  8) quit\nchoice> 8\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "flood-fill",
      "caseId": "099-flood-fill",
      "title": "Flood fill"
    },
    {
      "slug": "grid-coordinate-map",
      "caseId": "172-grid-coordinate-map",
      "title": "A grid keyed by (row, col)"
    }
  ],
  "updated": "2026-10-10"
}
