{
  "id": "P040",
  "slug": "sudoku-helper",
  "key": "P040-sudoku-helper",
  "title": "Sudoku helper",
  "summary": "A 4 x 4 or 9 x 9 sudoku, built in or typed in: put numbers in and take them out with a warning the moment one clashes, check the board (including when it can no longer be completed), ask for a hint that names one sure step, or let a backtracking solver finish it and say whether the solution is the only one.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P040 - Sudoku helper\n\nA 4 x 4 or 9 x 9 sudoku: every row, column and box holds each number once.\nPick one of three built-in puzzles or type one in. Put numbers in and take\nthem out - the helper says at once when a number clashes - check the whole\nboard, which also tells when it can no longer be completed, ask for a hint\nthat names one sure step, or let the solver finish the board and say whether\nits solution is the only one.\n\n- `main.eml` - the menu, the puzzles, putting and clearing numbers with their\n  checks, and the board on screen\n- `board.eml` - the rules: a cell's candidates, the clashes, the two kinds of\n  sure step, and the solver\n- `puzzles.eml` - the built-in puzzles\n\nHow each part works:\n\n- A cell's candidates are the numbers not yet in its row, its column or its\n  box; the box corner is the cell rounded down to a multiple of the box size,\n  as in the corpus case `sudoku-solver-4x4`.\n- A hint looks first for an empty cell with only one candidate, then for a\n  number that has only one possible cell left in some row, column or box.\n  Those are the two sure steps; when there is neither, the hint says so, and\n  only trying numbers can go on.\n- The solver backtracks like the corpus case, with one difference: instead\n  of the first empty cell it fills the one with the fewest candidates (a cell\n  with one or none stops the look-around), so a forced cell is filled before\n  any guess and a dead end shows at once. It does not stop at the first\n  solution but looks for a second, so it can say whether the solution is the\n  only one, and it counts the numbers it placed on the way.\n- A number that clashes is kept but named, so that it can be seen and taken\n  out. Check lists every clash; when there is none, it runs the solver to\n  tell whether the board can still be completed - a wrong number need not\n  clash with anything yet.\n- The puzzle's own numbers cannot be changed. The third built-in puzzle was\n  generated for this project: it has exactly one solution, and the sure steps\n  run out after 15 of them, with 38 cells still empty.\n\nWhat is checked: a puzzle number from the list; a size of 4 or 9; rows of\nexactly 4 or 9 digits from 1 to the size, or dots (a typed puzzle whose\nnumbers clash is refused); a cell as row and column, and a number, each from\n1 to the size. An empty answer cancels.\n\nSessions: `sessions/basic.in` opens the easy 9 x 9 puzzle, asks for a hint\n(row 5, column 5 can only be 5) and puts it in; puts in a 5 that clashes in\nits row and its box, checks, and clears it; puts in a 1 that clashes with\nnothing but leaves the board impossible - check says so - and clears it; asks\nfor another hint and lets the solver finish (the only solution, without a\nsingle guess). Then the third puzzle: a hint, and the solver places 150\nnumbers to finish it; then the corpus case's 4 x 4. `sessions/bad-input.in`\ngives menu choices 0 and x, actions before any puzzle, puzzle numbers 0, 5\nand x, size 5, rows of 8 and 10 characters and one with letters; types in\nthe third puzzle as it stands when the sure steps run out, so that the hint\nfinds none; tries a cell without a number, row 10, the number 0, a puzzle\ncell to fill and to clear and an empty cell to clear; lets the solver finish\n(114 numbers placed, the only solution); and types a 4 x 4 with two 1s in a\nrow, which is refused, and one with a single number, which has more than one\nsolution.\n\nBuilt on the verified corpus case `sudoku-solver-4x4` (backtracking over\nrow, column and box rules, checked by an independent validator).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P040 sudoku helper: a 4 x 4 or 9 x 9 sudoku, built in or typed in. Put\n# numbers in and take them out - the helper says at once when a number\n# clashes - check the whole board, ask for a hint that names one sure step,\n# or let the solver finish it and say whether the solution is the only one.\nimport board\nimport puzzles\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 words(s):\n    [] => out\n    \"\" => word\n    for c in s + \" \":\n        if c == \" \" or c == \",\":\n            if word != \"\":\n                out + [word] => out\n            \"\" => word\n        else:\n            word + c => word\n    return out\n\ndef number(s):\n    if s == \"\" or len(s) > 2:\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 parsed(rows, n):\n    # Rows of digits and \".\" (or 0) as a grid; [] if a row does not fit.\n    [] => g\n    for row in rows:\n        [] => cells\n        for ch in row:\n            if ch == \".\" or ch == \"0\":\n                cells + [0] => cells\n            elif ch in \"123456789\" and int(ch) <= n:\n                cells + [int(ch)] => cells\n            elif ch != \" \":\n                return []\n        if len(cells) != n:\n            return []\n        g + [cells] => g\n    return g\n\ndef show(g):\n    len(g) => n\n    board.box_size(n) => b\n    \"    \" => header\n    for c in [1:n]:\n        header + str(c) => header\n        if c % b == 0 and c < n:\n            header + \"   \" => header\n        elif c < n:\n            header + \" \" => header\n    header ^0\n    (\"  +\" + (\"-\" * (2 * b + 1) + \"+\") * b) => rule\n    rule ^0\n    for r in [0:n - 1]:\n        str(r + 1) + \" |\" => line\n        for c in [0:n - 1]:\n            if g[r][c] == 0:\n                line + \" .\" => line\n            else:\n                line + \" \" + str(g[r][c]) => line\n            if c % b == b - 1:\n                line + \" |\" => line\n        line ^0\n        if r % b == b - 1:\n            rule ^0\n    (\"Empty cells: \" + str(board.empty_cells(g)) + \".\") ^0\n\ndef small_first(name):\n    # \"Row 5\" as \"row 5\" (the names start with R, C or B).\n    \"RCB\" => big\n    \"rcb\" => small\n    for i in [0:2]:\n        if name[0] == big[i]:\n            return small[i] + name[1:len(name)]\n    return name\n\ndef plural(v):\n    return str(v) + \"s\"\n\ndef clash_text(cf):\n    # [\"Row 3\", 5, cells] as \"Row 3 has two 5s (columns 2 and 7).\"\n    cf[0] => name\n    cf[2] => cells\n    [\"\", \"\", \"two\", \"three\", \"four\", \"five\", \"six\", \"seven\", \"eight\", \"nine\"] => counts\n    \"\" => where\n    if name[0:3] == \"Row\":\n        \"columns\" => kind\n    elif name[0:3] == \"Col\":\n        \"rows\" => kind\n    else:\n        \"cells\" => kind\n    for i in [0:len(cells) - 1]:\n        if i > 0 and i == len(cells) - 1:\n            where + \" and \" => where\n        elif i > 0:\n            where + \", \" => where\n        if kind == \"columns\":\n            where + str(cells[i][1] + 1) => where\n        elif kind == \"rows\":\n            where + str(cells[i][0] + 1) => where\n        else:\n            where + str(cells[i][0] + 1) + \"-\" + str(cells[i][1] + 1) => where\n    return name + \" has \" + counts[len(cells)] + \" \" + plural(cf[1]) + \" (\" + kind + \" \" + where + \").\"\n\ndef new_puzzle(state):\n    for i in [0:len(puzzles.puzzles) - 1]:\n        (\"  \" + str(i + 1) + \") \" + puzzles.puzzles[i][0]) ^0\n    (\"  \" + str(len(puzzles.puzzles) + 1) + \") type one in\") ^0\n    while True:\n        trim(input(\"puzzle> \")) => answer\n        if answer == \"\":\n            \"Cancelled.\" ^0\n            return state\n        number(answer) => k\n        if k >= 1 and k <= len(puzzles.puzzles):\n            parsed(puzzles.puzzles[k - 1][1], len(puzzles.puzzles[k - 1][1])) => g\n            (\"Puzzle: \" + puzzles.puzzles[k - 1][0] + \".\") ^0\n            show(g)\n            return [g, given_of(g)]\n        if k == len(puzzles.puzzles) + 1:\n            return typed(state)\n        (\"Type a number from 1 to \" + str(len(puzzles.puzzles) + 1) + \".\") ^0\n\ndef given_of(g):\n    [] => out\n    for row in g:\n        [] => marks\n        for v in row:\n            marks + [v != 0] => marks\n        out + [marks] => out\n    return out\n\ndef typed(state):\n    0 => n\n    while n == 0:\n        trim(input(\"size (4 or 9)> \")) => answer\n        if answer == \"\":\n            \"Cancelled.\" ^0\n            return state\n        if answer == \"4\" or answer == \"9\":\n            int(answer) => n\n        else:\n            \"Type 4 or 9.\" ^0\n    (\"Type each row as \" + str(n) + \" digits from 1 to \" + str(n) + \", with . for an empty cell.\") ^0\n    [] => rows\n    while len(rows) < n:\n        trim(input(\"row \" + str(len(rows) + 1) + \"> \")) => answer\n        if answer == \"\":\n            \"Cancelled.\" ^0\n            return state\n        if len(parsed([answer], n)) == 0:\n            \"1..4\" => example\n            if n == 9:\n                \"53..7....\" => example\n            (\"A row is \" + str(n) + \" digits from 1 to \" + str(n) + \" or dots, like \\\"\" + example + \"\\\".\") ^0\n        else:\n            rows + [answer] => rows\n    parsed(rows, n) => g\n    board.conflicts(g) => cf\n    if len(cf) > 0:\n        (\"Those numbers break the rules: \" + clash_text(cf[0]) + \" The puzzle was not taken.\") ^0\n        return state\n    \"Puzzle taken.\" ^0\n    show(g)\n    return [g, given_of(g)]\n\ndef put(state, clearing):\n    state[0] => g\n    state[1] => given\n    if len(g) == 0:\n        \"Choose a puzzle first.\" ^0\n        return state\n    len(g) => n\n    \"row column number\" => shape\n    if clearing:\n        \"row column\" => shape\n    while True:\n        trim(input(\"cell (\" + shape + \")> \")) => answer\n        if answer == \"\":\n            \"Cancelled.\" ^0\n            return state\n        words(answer) => ws\n        [] => xs\n        for w in ws:\n            xs + [number(w)] => xs\n        if clearing and len(xs) == 2:\n            xs + [0] => xs\n        if len(xs) != 3 or xs[0] < 1 or xs[0] > n or xs[1] < 1 or xs[1] > n or xs[2] < 0 or xs[2] > n or (xs[2] == 0 and not clearing):\n            \"3 4 2\" => example\n            if clearing:\n                \"3 4\" => example\n            (\"Type \" + shape + \", each from 1 to \" + str(n) + \", like \" + example + \".\") ^0\n        elif given[xs[0] - 1][xs[1] - 1]:\n            \"That cell is part of the puzzle.\" ^0\n        else:\n            xs[0] - 1 => r\n            xs[1] - 1 => c\n            if clearing:\n                if g[r][c] == 0:\n                    \"That cell is already empty.\" ^0\n                    return state\n                0 => g[r][c]\n                (\"Row \" + str(r + 1) + \", column \" + str(c + 1) + \" is empty again.\") ^0\n                return state\n            xs[2] => g[r][c]\n            (\"Row \" + str(r + 1) + \", column \" + str(c + 1) + \" is now \" + str(xs[2]) + \".\") ^0\n            for cf in board.conflicts(g):\n                False => mine\n                for cell in cf[2]:\n                    if cell[0] == r and cell[1] == c:\n                        True => mine\n                if mine and cf[1] == xs[2]:\n                    (\"It clashes: \" + clash_text(cf)) ^0\n            if board.empty_cells(g) == 0 and len(board.conflicts(g)) == 0:\n                \"The puzzle is complete - well done!\" ^0\n            return state\n\ndef check(g):\n    board.conflicts(g) => cf\n    if len(cf) > 0:\n        for x in cf:\n            clash_text(x) ^0\n        return 0\n    if board.empty_cells(g) == 0:\n        \"No clashes, and every cell is filled: the puzzle is solved.\" ^0\n        return 0\n    board.solve(g) => s\n    if s[0] == 0:\n        (\"No clashes, but the board can no longer be completed: some number put in is wrong. \" + str(board.empty_cells(g)) + \" cells are empty.\") ^0\n    else:\n        (\"No clashes so far; \" + str(board.empty_cells(g)) + \" cells to go.\") ^0\n    return 0\n\ndef hint(g):\n    if len(board.conflicts(g)) > 0:\n        \"Fix the clashes first - check lists them.\" ^0\n        return 0\n    if board.empty_cells(g) == 0:\n        \"Every cell is filled.\" ^0\n        return 0\n    board.naked_single(g) => ns\n    if len(ns) > 0 and ns[2] == 0:\n        (\"Row \" + str(ns[0] + 1) + \", column \" + str(ns[1] + 1) + \" has no possible number: something put in is wrong.\") ^0\n        return 0\n    if len(ns) > 0:\n        (\"Row \" + str(ns[0] + 1) + \", column \" + str(ns[1] + 1) + \" can only be \" + str(ns[2]) + \": its row, column and box hold every other number.\") ^0\n        return 0\n    board.hidden_single(g) => hs\n    if len(hs) > 0:\n        (\"In \" + small_first(hs[0]) + \", \" + str(hs[1]) + \" fits only at row \" + str(hs[2] + 1) + \", column \" + str(hs[3] + 1) + \".\") ^0\n        return 0\n    \"No single sure step: every empty cell has two or more candidates, and every number two or more places. The solver can try them.\" ^0\n    return 0\n\ndef solve(state):\n    state[0] => g\n    if len(board.conflicts(g)) > 0:\n        \"Fix the clashes first - check lists them.\" ^0\n        return state\n    board.solve(g) => s\n    if s[0] == 0:\n        (\"No solution from here: some number put in is wrong (\" + str(s[2]) + \" numbers tried).\") ^0\n        return state\n    (\"Solved, trying \" + str(s[2]) + \" numbers along the way.\") ^0\n    show(s[1])\n    if s[0] == 1:\n        \"This is the only solution.\" ^0\n    else:\n        \"There is more than one solution; this is the first one found.\" ^0\n    return [s[1], state[1]]\n\n\"== Sudoku helper ==\" ^0\n\"A 4 x 4 or 9 x 9 sudoku: every row, column and box holds each number once.\" ^0\n[[], []] => state\nTrue => running\nwhile running:\n    \"\" ^0\n    \"1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\" ^0\n    trim(input(\"choice> \")) => choice\n    if choice == \"1\":\n        new_puzzle(state) => state\n    elif choice == \"2\" or choice == \"3\":\n        put(state, choice == \"3\") => state\n    elif choice == \"8\":\n        False => running\n    elif choice == \"4\" or choice == \"5\" or choice == \"6\" or choice == \"7\":\n        if len(state[0]) == 0:\n            \"Choose a puzzle first.\" ^0\n        elif choice == \"4\":\n            check(state[0])\n        elif choice == \"5\":\n            hint(state[0])\n        elif choice == \"6\":\n            solve(state) => state\n        else:\n            show(state[0])\n    else:\n        \"Pick a number from 1 to 8.\" ^0\n\"Bye.\" ^0\n",
      "python": "import board\nimport puzzles\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 words(s):\n    out = []\n    word = \"\"\n    for c in s + \" \":\n        if c == \" \" or c == \",\":\n            if word != \"\":\n                out = out + [word]\n            word = \"\"\n        else:\n            word = word + c\n    return out\n\ndef number(s):\n    if s == \"\" or len(s) > 2:\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 parsed(rows, n):\n    g = []\n    for row in rows:\n        cells = []\n        for ch in row:\n            if ch == \".\" or ch == \"0\":\n                cells = cells + [0]\n            elif ch in \"123456789\" and int(ch) <= n:\n                cells = cells + [int(ch)]\n            elif ch != \" \":\n                return []\n        if len(cells) != n:\n            return []\n        g = g + [cells]\n    return g\n\ndef show(g):\n    n = len(g)\n    b = board.box_size(n)\n    header = \"    \"\n    for c in range(1, n+1):\n        header = header + str(c)\n        if c % b == 0 and c < n:\n            header = header + \"   \"\n        elif c < n:\n            header = header + \" \"\n    print(header)\n    rule = \"  +\" + (\"-\" * (2 * b + 1) + \"+\") * b\n    print(rule)\n    for r in range(0, n):\n        line = str(r + 1) + \" |\"\n        for c in range(0, n):\n            if g[r][c] == 0:\n                line = line + \" .\"\n            else:\n                line = line + \" \" + str(g[r][c])\n            if c % b == b - 1:\n                line = line + \" |\"\n        print(line)\n        if r % b == b - 1:\n            print(rule)\n    print(\"Empty cells: \" + str(board.empty_cells(g)) + \".\")\n\ndef small_first(name):\n    big = \"RCB\"\n    small = \"rcb\"\n    for i in range(0, 3):\n        if name[0] == big[i]:\n            return small[i] + name[1:len(name)]\n    return name\n\ndef plural(v):\n    return str(v) + \"s\"\n\ndef clash_text(cf):\n    name = cf[0]\n    cells = cf[2]\n    counts = [\"\", \"\", \"two\", \"three\", \"four\", \"five\", \"six\", \"seven\", \"eight\", \"nine\"]\n    where = \"\"\n    if name[0:3] == \"Row\":\n        kind = \"columns\"\n    elif name[0:3] == \"Col\":\n        kind = \"rows\"\n    else:\n        kind = \"cells\"\n    for i in range(0, len(cells)):\n        if i > 0 and i == len(cells) - 1:\n            where = where + \" and \"\n        elif i > 0:\n            where = where + \", \"\n        if kind == \"columns\":\n            where = where + str(cells[i][1] + 1)\n        elif kind == \"rows\":\n            where = where + str(cells[i][0] + 1)\n        else:\n            where = where + str(cells[i][0] + 1) + \"-\" + str(cells[i][1] + 1)\n    return name + \" has \" + counts[len(cells)] + \" \" + plural(cf[1]) + \" (\" + kind + \" \" + where + \").\"\n\ndef new_puzzle(state):\n    for i in range(0, len(puzzles.puzzles)):\n        print(\"  \" + str(i + 1) + \") \" + puzzles.puzzles[i][0])\n    print(\"  \" + str(len(puzzles.puzzles) + 1) + \") type one in\")\n    while True:\n        answer = trim(input(\"puzzle> \"))\n        if answer == \"\":\n            print(\"Cancelled.\")\n            return state\n        k = number(answer)\n        if k >= 1 and k <= len(puzzles.puzzles):\n            g = parsed(puzzles.puzzles[k - 1][1], len(puzzles.puzzles[k - 1][1]))\n            print(\"Puzzle: \" + puzzles.puzzles[k - 1][0] + \".\")\n            show(g)\n            return [g, given_of(g)]\n        if k == len(puzzles.puzzles) + 1:\n            return typed(state)\n        print(\"Type a number from 1 to \" + str(len(puzzles.puzzles) + 1) + \".\")\n\ndef given_of(g):\n    out = []\n    for row in g:\n        marks = []\n        for v in row:\n            marks = marks + [v != 0]\n        out = out + [marks]\n    return out\n\ndef typed(state):\n    n = 0\n    while n == 0:\n        answer = trim(input(\"size (4 or 9)> \"))\n        if answer == \"\":\n            print(\"Cancelled.\")\n            return state\n        if answer == \"4\" or answer == \"9\":\n            n = int(answer)\n        else:\n            print(\"Type 4 or 9.\")\n    print(\"Type each row as \" + str(n) + \" digits from 1 to \" + str(n) + \", with . for an empty cell.\")\n    rows = []\n    while len(rows) < n:\n        answer = trim(input(\"row \" + str(len(rows) + 1) + \"> \"))\n        if answer == \"\":\n            print(\"Cancelled.\")\n            return state\n        if len(parsed([answer], n)) == 0:\n            example = \"1..4\"\n            if n == 9:\n                example = \"53..7....\"\n            print(\"A row is \" + str(n) + \" digits from 1 to \" + str(n) + \" or dots, like \\\"\" + example + \"\\\".\")\n        else:\n            rows = rows + [answer]\n    g = parsed(rows, n)\n    cf = board.conflicts(g)\n    if len(cf) > 0:\n        print(\"Those numbers break the rules: \" + clash_text(cf[0]) + \" The puzzle was not taken.\")\n        return state\n    print(\"Puzzle taken.\")\n    show(g)\n    return [g, given_of(g)]\n\ndef put(state, clearing):\n    g = state[0]\n    given = state[1]\n    if len(g) == 0:\n        print(\"Choose a puzzle first.\")\n        return state\n    n = len(g)\n    shape = \"row column number\"\n    if clearing:\n        shape = \"row column\"\n    while True:\n        answer = trim(input(\"cell (\" + shape + \")> \"))\n        if answer == \"\":\n            print(\"Cancelled.\")\n            return state\n        ws = words(answer)\n        xs = []\n        for w in ws:\n            xs = xs + [number(w)]\n        if clearing and len(xs) == 2:\n            xs = xs + [0]\n        if len(xs) != 3 or xs[0] < 1 or xs[0] > n or xs[1] < 1 or xs[1] > n or xs[2] < 0 or xs[2] > n or xs[2] == 0 and not clearing:\n            example = \"3 4 2\"\n            if clearing:\n                example = \"3 4\"\n            print(\"Type \" + shape + \", each from 1 to \" + str(n) + \", like \" + example + \".\")\n        elif given[xs[0] - 1][xs[1] - 1]:\n            print(\"That cell is part of the puzzle.\")\n        else:\n            r = xs[0] - 1\n            c = xs[1] - 1\n            if clearing:\n                if g[r][c] == 0:\n                    print(\"That cell is already empty.\")\n                    return state\n                g[r][c] = 0\n                print(\"Row \" + str(r + 1) + \", column \" + str(c + 1) + \" is empty again.\")\n                return state\n            g[r][c] = xs[2]\n            print(\"Row \" + str(r + 1) + \", column \" + str(c + 1) + \" is now \" + str(xs[2]) + \".\")\n            for cf in board.conflicts(g):\n                mine = False\n                for cell in cf[2]:\n                    if cell[0] == r and cell[1] == c:\n                        mine = True\n                if mine and cf[1] == xs[2]:\n                    print(\"It clashes: \" + clash_text(cf))\n            if board.empty_cells(g) == 0 and len(board.conflicts(g)) == 0:\n                print(\"The puzzle is complete - well done!\")\n            return state\n\ndef check(g):\n    cf = board.conflicts(g)\n    if len(cf) > 0:\n        for x in cf:\n            print(clash_text(x))\n        return 0\n    if board.empty_cells(g) == 0:\n        print(\"No clashes, and every cell is filled: the puzzle is solved.\")\n        return 0\n    s = board.solve(g)\n    if s[0] == 0:\n        print(\"No clashes, but the board can no longer be completed: some number put in is wrong. \" + str(board.empty_cells(g)) + \" cells are empty.\")\n    else:\n        print(\"No clashes so far; \" + str(board.empty_cells(g)) + \" cells to go.\")\n    return 0\n\ndef hint(g):\n    if len(board.conflicts(g)) > 0:\n        print(\"Fix the clashes first - check lists them.\")\n        return 0\n    if board.empty_cells(g) == 0:\n        print(\"Every cell is filled.\")\n        return 0\n    ns = board.naked_single(g)\n    if len(ns) > 0 and ns[2] == 0:\n        print(\"Row \" + str(ns[0] + 1) + \", column \" + str(ns[1] + 1) + \" has no possible number: something put in is wrong.\")\n        return 0\n    if len(ns) > 0:\n        print(\"Row \" + str(ns[0] + 1) + \", column \" + str(ns[1] + 1) + \" can only be \" + str(ns[2]) + \": its row, column and box hold every other number.\")\n        return 0\n    hs = board.hidden_single(g)\n    if len(hs) > 0:\n        print(\"In \" + small_first(hs[0]) + \", \" + str(hs[1]) + \" fits only at row \" + str(hs[2] + 1) + \", column \" + str(hs[3] + 1) + \".\")\n        return 0\n    print(\"No single sure step: every empty cell has two or more candidates, and every number two or more places. The solver can try them.\")\n    return 0\n\ndef solve(state):\n    g = state[0]\n    if len(board.conflicts(g)) > 0:\n        print(\"Fix the clashes first - check lists them.\")\n        return state\n    s = board.solve(g)\n    if s[0] == 0:\n        print(\"No solution from here: some number put in is wrong (\" + str(s[2]) + \" numbers tried).\")\n        return state\n    print(\"Solved, trying \" + str(s[2]) + \" numbers along the way.\")\n    show(s[1])\n    if s[0] == 1:\n        print(\"This is the only solution.\")\n    else:\n        print(\"There is more than one solution; this is the first one found.\")\n    return [s[1], state[1]]\n\nprint(\"== Sudoku helper ==\")\nprint(\"A 4 x 4 or 9 x 9 sudoku: every row, column and box holds each number once.\")\nstate = [[], []]\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\")\n    choice = trim(input(\"choice> \"))\n    if choice == \"1\":\n        state = new_puzzle(state)\n    elif choice == \"2\" or choice == \"3\":\n        state = put(state, choice == \"3\")\n    elif choice == \"8\":\n        running = False\n    elif choice == \"4\" or choice == \"5\" or choice == \"6\" or choice == \"7\":\n        if len(state[0]) == 0:\n            print(\"Choose a puzzle first.\")\n        elif choice == \"4\":\n            check(state[0])\n        elif choice == \"5\":\n            hint(state[0])\n        elif choice == \"6\":\n            state = solve(state)\n        else:\n            show(state[0])\n    else:\n        print(\"Pick a number from 1 to 8.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "board.eml",
      "eml": "# P040 sudoku helper - the rules. A grid is a list of rows of numbers, 0 for\n# an empty cell; it is 4 x 4 (boxes of 2 x 2) or 9 x 9 (boxes of 3 x 3).\n\ndef box_size(n):\n    if n == 4:\n        return 2\n    return 3\n\ndef candidates(g, r, c):\n    # The numbers that may go in (r, c): not yet in its row, its column or\n    # its box. The box corner is the cell rounded down to a multiple of the\n    # box size, as in the corpus case sudoku-solver-4x4.\n    len(g) => n\n    box_size(n) => b\n    [False] * (n + 1) => used\n    for i in [0:n - 1]:\n        True => used[g[r][i]]\n        True => used[g[i][c]]\n    int(r / b) * b => r0\n    int(c / b) * b => c0\n    for i in [0:b - 1]:\n        for j in [0:b - 1]:\n            True => used[g[r0 + i][c0 + j]]\n    [] => out\n    for v in [1:n]:\n        if not used[v]:\n            out + [v] => out\n    return out\n\ndef units(n):\n    # Every row, column and box as [name, list of (row, column) cells].\n    box_size(n) => b\n    [] => out\n    for r in [0:n - 1]:\n        [] => cells\n        for c in [0:n - 1]:\n            cells + [[r, c]] => cells\n        out + [[\"Row \" + str(r + 1), cells]] => out\n    for c in [0:n - 1]:\n        [] => cells\n        for r in [0:n - 1]:\n            cells + [[r, c]] => cells\n        out + [[\"Column \" + str(c + 1), cells]] => out\n    for k in [0:n - 1]:\n        int(k / b) * b => r0\n        (k % b) * b => c0\n        [] => cells\n        for i in [0:b - 1]:\n            for j in [0:b - 1]:\n                cells + [[r0 + i, c0 + j]] => cells\n        out + [[\"Box \" + str(k + 1), cells]] => out\n    return out\n\ndef conflicts(g):\n    # Every number that appears more than once in a row, column or box, as\n    # [unit name, number, the cells holding it].\n    [] => out\n    for u in units(len(g)):\n        for v in [1:len(g)]:\n            [] => where\n            for cell in u[1]:\n                if g[cell[0]][cell[1]] == v:\n                    where + [cell] => where\n            if len(where) > 1:\n                out + [[u[0], v, where]] => out\n    return out\n\ndef empty_cells(g):\n    0 => n\n    for row in g:\n        for v in row:\n            if v == 0:\n                n + 1 => n\n    return n\n\ndef naked_single(g):\n    # The first empty cell, row by row, with exactly one candidate:\n    # [row, column, number]; [row, column, 0] for a cell with none; [] if\n    # every empty cell has two or more.\n    for r in [0:len(g) - 1]:\n        for c in [0:len(g) - 1]:\n            if g[r][c] == 0:\n                candidates(g, r, c) => cs\n                if len(cs) == 0:\n                    return [r, c, 0]\n                if len(cs) == 1:\n                    return [r, c, cs[0]]\n    return []\n\ndef hidden_single(g):\n    # A number that has only one possible cell left in some row, column or\n    # box: [unit name, number, row, column]; [] if there is none.\n    for u in units(len(g)):\n        for v in [1:len(g)]:\n            False => placed\n            [] => spots\n            for cell in u[1]:\n                g[cell[0]][cell[1]] => x\n                if x == v:\n                    True => placed\n                elif x == 0:\n                    for cand in candidates(g, cell[0], cell[1]):\n                        if cand == v:\n                            spots + [cell] => spots\n            if not placed and len(spots) == 1:\n                return [u[0], v, spots[0][0], spots[0][1]]\n    return []\n\ndef search(g, found, tries):\n    # Backtracking: fill the empty cell with the fewest candidates, try each\n    # in turn, undo on failure. Counts solutions up to 2 (found[0]), keeps\n    # the first in found[1], and counts the numbers placed in tries[0]. The\n    # scan stops early at a cell with one candidate or none: nothing beats it.\n    -1 => best_r\n    -1 => best_c\n    [] => best\n    False => settled\n    for r in [0:len(g) - 1]:\n        for c in [0:len(g) - 1]:\n            if not settled and g[r][c] == 0:\n                candidates(g, r, c) => cs\n                if best_r == -1 or len(cs) < len(best):\n                    r => best_r\n                    c => best_c\n                    cs => best\n                    if len(cs) <= 1:\n                        True => settled\n    if best_r == -1:\n        found[0] + 1 => found[0]\n        if found[0] == 1:\n            [] => copy\n            for row in g:\n                copy + [row[0:len(row)]] => copy\n            copy => found[1]\n        return 0\n    for v in best:\n        if found[0] < 2:\n            tries[0] + 1 => tries[0]\n            v => g[best_r][best_c]\n            search(g, found, tries)\n            0 => g[best_r][best_c]\n    return 0\n\ndef solve(g):\n    # [number of solutions found (0, 1 or 2 meaning \"more than one\"), the\n    # first solution or [], numbers placed while searching].\n    [] => work\n    for row in g:\n        work + [row[0:len(row)]] => work\n    [0, []] => found\n    [0] => tries\n    search(work, found, tries)\n    return [found[0], found[1], tries[0]]\n",
      "python": "def box_size(n):\n    if n == 4:\n        return 2\n    return 3\n\ndef candidates(g, r, c):\n    n = len(g)\n    b = box_size(n)\n    used = [False] * (n + 1)\n    for i in range(0, n):\n        used[g[r][i]] = True\n        used[g[i][c]] = True\n    r0 = int(r / b) * b\n    c0 = int(c / b) * b\n    for i in range(0, b):\n        for j in range(0, b):\n            used[g[r0 + i][c0 + j]] = True\n    out = []\n    for v in range(1, n+1):\n        if not used[v]:\n            out = out + [v]\n    return out\n\ndef units(n):\n    b = box_size(n)\n    out = []\n    for r in range(0, n):\n        cells = []\n        for c in range(0, n):\n            cells = cells + [[r, c]]\n        out = out + [[\"Row \" + str(r + 1), cells]]\n    for c in range(0, n):\n        cells = []\n        for r in range(0, n):\n            cells = cells + [[r, c]]\n        out = out + [[\"Column \" + str(c + 1), cells]]\n    for k in range(0, n):\n        r0 = int(k / b) * b\n        c0 = k % b * b\n        cells = []\n        for i in range(0, b):\n            for j in range(0, b):\n                cells = cells + [[r0 + i, c0 + j]]\n        out = out + [[\"Box \" + str(k + 1), cells]]\n    return out\n\ndef conflicts(g):\n    out = []\n    for u in units(len(g)):\n        for v in range(1, len(g)+1):\n            where = []\n            for cell in u[1]:\n                if g[cell[0]][cell[1]] == v:\n                    where = where + [cell]\n            if len(where) > 1:\n                out = out + [[u[0], v, where]]\n    return out\n\ndef empty_cells(g):\n    n = 0\n    for row in g:\n        for v in row:\n            if v == 0:\n                n = n + 1\n    return n\n\ndef naked_single(g):\n    for r in range(0, len(g)):\n        for c in range(0, len(g)):\n            if g[r][c] == 0:\n                cs = candidates(g, r, c)\n                if len(cs) == 0:\n                    return [r, c, 0]\n                if len(cs) == 1:\n                    return [r, c, cs[0]]\n    return []\n\ndef hidden_single(g):\n    for u in units(len(g)):\n        for v in range(1, len(g)+1):\n            placed = False\n            spots = []\n            for cell in u[1]:\n                x = g[cell[0]][cell[1]]\n                if x == v:\n                    placed = True\n                elif x == 0:\n                    for cand in candidates(g, cell[0], cell[1]):\n                        if cand == v:\n                            spots = spots + [cell]\n            if not placed and len(spots) == 1:\n                return [u[0], v, spots[0][0], spots[0][1]]\n    return []\n\ndef search(g, found, tries):\n    best_r = -1\n    best_c = -1\n    best = []\n    settled = False\n    for r in range(0, len(g)):\n        for c in range(0, len(g)):\n            if not settled and g[r][c] == 0:\n                cs = candidates(g, r, c)\n                if best_r == -1 or len(cs) < len(best):\n                    best_r = r\n                    best_c = c\n                    best = cs\n                    if len(cs) <= 1:\n                        settled = True\n    if best_r == -1:\n        found[0] = found[0] + 1\n        if found[0] == 1:\n            copy = []\n            for row in g:\n                copy = copy + [row[0:len(row)]]\n            found[1] = copy\n        return 0\n    for v in best:\n        if found[0] < 2:\n            tries[0] = tries[0] + 1\n            g[best_r][best_c] = v\n            search(g, found, tries)\n            g[best_r][best_c] = 0\n    return 0\n\ndef solve(g):\n    work = []\n    for row in g:\n        work = work + [row[0:len(row)]]\n    found = [0, []]\n    tries = [0]\n    search(work, found, tries)\n    return [found[0], found[1], tries[0]]\n"
    },
    {
      "name": "puzzles.eml",
      "eml": "# P040 sudoku helper - the built-in puzzles, one string per row, \".\" for an\n# empty cell. The third was generated for this project: it has one solution,\n# but the sure steps the hint knows run out before the end.\n\n[\n    [\"4 x 4, from the corpus case\", [\"1...\", \"..3.\", \".4..\", \"...2\"]],\n    [\"9 x 9, easy\", [\"53..7....\", \"6..195...\", \".98....6.\", \"8...6...3\", \"4..8.3..1\", \"7...2...6\", \".6....28.\", \"...419..5\", \"....8..79\"]],\n    [\"9 x 9, needs guessing\", [\"..53.4.7.\", \".3.......\", \"...2.1.83\", \"982.4..5.\", \"5....7...\", \".1.82..4.\", \"2..5..69.\", \"75......8\", \"..8......\"]],\n] => puzzles\n",
      "python": "puzzles = [[\"4 x 4, from the corpus case\", [\"1...\", \"..3.\", \".4..\", \"...2\"]], [\"9 x 9, easy\", [\"53..7....\", \"6..195...\", \".98....6.\", \"8...6...3\", \"4..8.3..1\", \"7...2...6\", \".6....28.\", \"...419..5\", \"....8..79\"]], [\"9 x 9, needs guessing\", [\"..53.4.7.\", \".3.......\", \"...2.1.83\", \"982.4..5.\", \"5....7...\", \".1.82..4.\", \"2..5..69.\", \"75......8\", \"..8......\"]]]\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "0\nx\n2\n4\n6\n1\n0\n5\nx\n\n1\n4\n5\n9\n12345678\n1234567890\nabc......\n.253.4179\n.3.....6.\n.7.2.1.83\n982.4..5.\n564..78..\n317825946\n24.5..69.\n75......8\n.98......\n5\n2\n1 1\n10 1 1\n1 1 0\n1 2 9\n\n3\n1 1\n3\n1 2\n\n6\n1\n4\n4\n11..\n....\n....\n....\n1\n4\n4\n1...\n....\n....\n....\n6\n8\n",
      "screen": "== Sudoku helper ==\nA 4 x 4 or 9 x 9 sudoku: every row, column and box holds each number once.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 0\nPick a number from 1 to 8.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> x\nPick a number from 1 to 8.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 2\nChoose a puzzle first.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 4\nChoose a puzzle first.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 6\nChoose a puzzle first.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 1\n  1) 4 x 4, from the corpus case\n  2) 9 x 9, easy\n  3) 9 x 9, needs guessing\n  4) type one in\npuzzle> 0\nType a number from 1 to 4.\npuzzle> 5\nType a number from 1 to 4.\npuzzle> x\nType a number from 1 to 4.\npuzzle> \nCancelled.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 1\n  1) 4 x 4, from the corpus case\n  2) 9 x 9, easy\n  3) 9 x 9, needs guessing\n  4) type one in\npuzzle> 4\nsize (4 or 9)> 5\nType 4 or 9.\nsize (4 or 9)> 9\nType each row as 9 digits from 1 to 9, with . for an empty cell.\nrow 1> 12345678\nA row is 9 digits from 1 to 9 or dots, like \"53..7....\".\nrow 1> 1234567890\nA row is 9 digits from 1 to 9 or dots, like \"53..7....\".\nrow 1> abc......\nA row is 9 digits from 1 to 9 or dots, like \"53..7....\".\nrow 1> .253.4179\nrow 2> .3.....6.\nrow 3> .7.2.1.83\nrow 4> 982.4..5.\nrow 5> 564..78..\nrow 6> 317825946\nrow 7> 24.5..69.\nrow 8> 75......8\nrow 9> .98......\nPuzzle taken.\n    1 2 3   4 5 6   7 8 9\n  +-------+-------+-------+\n1 | . 2 5 | 3 . 4 | 1 7 9 |\n2 | . 3 . | . . . | . 6 . |\n3 | . 7 . | 2 . 1 | . 8 3 |\n  +-------+-------+-------+\n4 | 9 8 2 | . 4 . | . 5 . |\n5 | 5 6 4 | . . 7 | 8 . . |\n6 | 3 1 7 | 8 2 5 | 9 4 6 |\n  +-------+-------+-------+\n7 | 2 4 . | 5 . . | 6 9 . |\n8 | 7 5 . | . . . | . . 8 |\n9 | . 9 8 | . . . | . . . |\n  +-------+-------+-------+\nEmpty cells: 38.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 5\nNo single sure step: every empty cell has two or more candidates, and every number two or more places. The solver can try them.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 2\ncell (row column number)> 1 1\nType row column number, each from 1 to 9, like 3 4 2.\ncell (row column number)> 10 1 1\nType row column number, each from 1 to 9, like 3 4 2.\ncell (row column number)> 1 1 0\nType row column number, each from 1 to 9, like 3 4 2.\ncell (row column number)> 1 2 9\nThat cell is part of the puzzle.\ncell (row column number)> \nCancelled.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 3\ncell (row column)> 1 1\nThat cell is already empty.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 3\ncell (row column)> 1 2\nThat cell is part of the puzzle.\ncell (row column)> \nCancelled.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 6\nSolved, trying 114 numbers along the way.\n    1 2 3   4 5 6   7 8 9\n  +-------+-------+-------+\n1 | 6 2 5 | 3 8 4 | 1 7 9 |\n2 | 8 3 1 | 7 5 9 | 2 6 4 |\n3 | 4 7 9 | 2 6 1 | 5 8 3 |\n  +-------+-------+-------+\n4 | 9 8 2 | 6 4 3 | 7 5 1 |\n5 | 5 6 4 | 1 9 7 | 8 3 2 |\n6 | 3 1 7 | 8 2 5 | 9 4 6 |\n  +-------+-------+-------+\n7 | 2 4 3 | 5 1 8 | 6 9 7 |\n8 | 7 5 6 | 9 3 2 | 4 1 8 |\n9 | 1 9 8 | 4 7 6 | 3 2 5 |\n  +-------+-------+-------+\nEmpty cells: 0.\nThis is the only solution.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 1\n  1) 4 x 4, from the corpus case\n  2) 9 x 9, easy\n  3) 9 x 9, needs guessing\n  4) type one in\npuzzle> 4\nsize (4 or 9)> 4\nType each row as 4 digits from 1 to 4, with . for an empty cell.\nrow 1> 11..\nrow 2> ....\nrow 3> ....\nrow 4> ....\nThose numbers break the rules: Row 1 has two 1s (columns 1 and 2). The puzzle was not taken.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 1\n  1) 4 x 4, from the corpus case\n  2) 9 x 9, easy\n  3) 9 x 9, needs guessing\n  4) type one in\npuzzle> 4\nsize (4 or 9)> 4\nType each row as 4 digits from 1 to 4, with . for an empty cell.\nrow 1> 1...\nrow 2> ....\nrow 3> ....\nrow 4> ....\nPuzzle taken.\n    1 2   3 4\n  +-----+-----+\n1 | 1 . | . . |\n2 | . . | . . |\n  +-----+-----+\n3 | . . | . . |\n4 | . . | . . |\n  +-----+-----+\nEmpty cells: 15.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 6\nSolved, trying 19 numbers along the way.\n    1 2   3 4\n  +-----+-----+\n1 | 1 2 | 3 4 |\n2 | 3 4 | 1 2 |\n  +-----+-----+\n3 | 2 1 | 4 3 |\n4 | 4 3 | 2 1 |\n  +-----+-----+\nEmpty cells: 0.\nThere is more than one solution; this is the first one found.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 8\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "1\n2\n5\n2\n5 5 5\n2\n1 3 5\n4\n3\n1 3\n2\n1,3,1\n4\n3\n1 3\n5\n6\n1\n3\n5\n6\n1\n1\n6\n8\n",
      "screen": "== Sudoku helper ==\nA 4 x 4 or 9 x 9 sudoku: every row, column and box holds each number once.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 1\n  1) 4 x 4, from the corpus case\n  2) 9 x 9, easy\n  3) 9 x 9, needs guessing\n  4) type one in\npuzzle> 2\nPuzzle: 9 x 9, easy.\n    1 2 3   4 5 6   7 8 9\n  +-------+-------+-------+\n1 | 5 3 . | . 7 . | . . . |\n2 | 6 . . | 1 9 5 | . . . |\n3 | . 9 8 | . . . | . 6 . |\n  +-------+-------+-------+\n4 | 8 . . | . 6 . | . . 3 |\n5 | 4 . . | 8 . 3 | . . 1 |\n6 | 7 . . | . 2 . | . . 6 |\n  +-------+-------+-------+\n7 | . 6 . | . . . | 2 8 . |\n8 | . . . | 4 1 9 | . . 5 |\n9 | . . . | . 8 . | . 7 9 |\n  +-------+-------+-------+\nEmpty cells: 51.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 5\nRow 5, column 5 can only be 5: its row, column and box hold every other number.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 2\ncell (row column number)> 5 5 5\nRow 5, column 5 is now 5.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 2\ncell (row column number)> 1 3 5\nRow 1, column 3 is now 5.\nIt clashes: Row 1 has two 5s (columns 1 and 3).\nIt clashes: Box 1 has two 5s (cells 1-1 and 1-3).\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 4\nRow 1 has two 5s (columns 1 and 3).\nBox 1 has two 5s (cells 1-1 and 1-3).\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 3\ncell (row column)> 1 3\nRow 1, column 3 is empty again.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 2\ncell (row column number)> 1,3,1\nRow 1, column 3 is now 1.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 4\nNo clashes, but the board can no longer be completed: some number put in is wrong. 49 cells are empty.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 3\ncell (row column)> 1 3\nRow 1, column 3 is empty again.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 5\nRow 5, column 2 can only be 2: its row, column and box hold every other number.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 6\nSolved, trying 50 numbers along the way.\n    1 2 3   4 5 6   7 8 9\n  +-------+-------+-------+\n1 | 5 3 4 | 6 7 8 | 9 1 2 |\n2 | 6 7 2 | 1 9 5 | 3 4 8 |\n3 | 1 9 8 | 3 4 2 | 5 6 7 |\n  +-------+-------+-------+\n4 | 8 5 9 | 7 6 1 | 4 2 3 |\n5 | 4 2 6 | 8 5 3 | 7 9 1 |\n6 | 7 1 3 | 9 2 4 | 8 5 6 |\n  +-------+-------+-------+\n7 | 9 6 1 | 5 3 7 | 2 8 4 |\n8 | 2 8 7 | 4 1 9 | 6 3 5 |\n9 | 3 4 5 | 2 8 6 | 1 7 9 |\n  +-------+-------+-------+\nEmpty cells: 0.\nThis is the only solution.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 1\n  1) 4 x 4, from the corpus case\n  2) 9 x 9, easy\n  3) 9 x 9, needs guessing\n  4) type one in\npuzzle> 3\nPuzzle: 9 x 9, needs guessing.\n    1 2 3   4 5 6   7 8 9\n  +-------+-------+-------+\n1 | . . 5 | 3 . 4 | . 7 . |\n2 | . 3 . | . . . | . . . |\n3 | . . . | 2 . 1 | . 8 3 |\n  +-------+-------+-------+\n4 | 9 8 2 | . 4 . | . 5 . |\n5 | 5 . . | . . 7 | . . . |\n6 | . 1 . | 8 2 . | . 4 . |\n  +-------+-------+-------+\n7 | 2 . . | 5 . . | 6 9 . |\n8 | 7 5 . | . . . | . . 8 |\n9 | . . 8 | . . . | . . . |\n  +-------+-------+-------+\nEmpty cells: 53.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 5\nRow 7, column 2 can only be 4: its row, column and box hold every other number.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 6\nSolved, trying 150 numbers along the way.\n    1 2 3   4 5 6   7 8 9\n  +-------+-------+-------+\n1 | 6 2 5 | 3 8 4 | 1 7 9 |\n2 | 8 3 1 | 7 5 9 | 2 6 4 |\n3 | 4 7 9 | 2 6 1 | 5 8 3 |\n  +-------+-------+-------+\n4 | 9 8 2 | 6 4 3 | 7 5 1 |\n5 | 5 6 4 | 1 9 7 | 8 3 2 |\n6 | 3 1 7 | 8 2 5 | 9 4 6 |\n  +-------+-------+-------+\n7 | 2 4 3 | 5 1 8 | 6 9 7 |\n8 | 7 5 6 | 9 3 2 | 4 1 8 |\n9 | 1 9 8 | 4 7 6 | 3 2 5 |\n  +-------+-------+-------+\nEmpty cells: 0.\nThis is the only solution.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 1\n  1) 4 x 4, from the corpus case\n  2) 9 x 9, easy\n  3) 9 x 9, needs guessing\n  4) type one in\npuzzle> 1\nPuzzle: 4 x 4, from the corpus case.\n    1 2   3 4\n  +-----+-----+\n1 | 1 . | . . |\n2 | . . | 3 . |\n  +-----+-----+\n3 | . 4 | . . |\n4 | . . | . 2 |\n  +-----+-----+\nEmpty cells: 12.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 6\nSolved, trying 12 numbers along the way.\n    1 2   3 4\n  +-----+-----+\n1 | 1 3 | 2 4 |\n2 | 4 2 | 3 1 |\n  +-----+-----+\n3 | 2 4 | 1 3 |\n4 | 3 1 | 4 2 |\n  +-----+-----+\nEmpty cells: 0.\nThis is the only solution.\n\n1) new puzzle  2) put a number  3) clear a cell  4) check  5) hint  6) solve  7) show  8) quit\nchoice> 8\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "sudoku-solver-4x4",
      "caseId": "124-sudoku-solver-4x4",
      "title": "Sudoku solver (4x4)"
    }
  ],
  "updated": "2026-10-10"
}
