{
  "id": "P041",
  "slug": "maze",
  "key": "P041-maze",
  "title": "Maze",
  "summary": "A random maze from a seed - the same seed always makes the same maze - built by a random depth-first walk with a few extra walls knocked down so that there is more than one way. Walk it from S to G, or show a shortest way found by breadth-first search beside the way the corpus case's backtracking finds.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P041 - Maze\n\nA random maze of 3 to 10 rows and 3 to 15 columns, made from a seed - the\nsame seed always makes the same maze. Walk it from S (top left) to G (bottom\nright), or show a shortest way through it beside the way the corpus case's\nbacktracking finds, which need not be as short.\n\n- `main.eml` - the menu, the questions and their checks, walking, and the\n  maze on screen\n- `maze.eml` - making a maze, the two ways through it, and drawing it\n- `rng.eml` - the random generator of P008 (number guessing)\n\nHow each part works:\n\n- A maze is made by a random depth-first walk: from the current cell, step\n  to a random neighbour not yet visited and knock down the wall between;\n  when there is none, step back. That knocks down the walls of a spanning\n  tree, so every cell can be reached along exactly one way. Then about one\n  wall in eight of those left inside is knocked down as well, so that some\n  places can be reached in more than one way - otherwise every way through\n  would be the same way.\n- The shortest way comes from a breadth-first search from S: it reaches\n  cells in order of distance, so the first time it reaches G, the way there\n  is a shortest one.\n- The backtracking way is the corpus case `maze-solver-backtracking`: try\n  down, right, up and left in that order, mark the cell, and unmark it when\n  the branch dead-ends. It finds a way, not the shortest: on the maze from\n  seed 335 in the basic session it takes 36 moves where 16 are enough.\n- Moves are typed as letters - u, d, l, r - several at a time; a move into a\n  wall, or a letter that is not a move, stops there.\n- The random numbers come from the generator written in EML for P008, not\n  from Python's random module, so a session gives the same maze in CPython\n  and in the EML interpreter.\n\nWhat is checked: 3 to 10 rows and 3 to 15 columns; a seed of up to 9 digits;\nmoves as u, d, l and r in either case. An empty answer cancels.\n\nSessions: `sessions/basic.in` walks the starting maze (seed 2026) with a\ndetour of two moves and a stop at a wall, reaches G in 18 moves where the\nshortest way takes 16, and shows that way; then makes a 6 x 10 maze from\nseed 335, where the backtracking way takes 36 moves and the shortest 16.\n`sessions/bad-input.in` gives menu choices 0 and x, 2, 11 and x rows, 16 and\n2 columns, seeds abc and 1234567890, then a 3 x 3 maze from seed 7: a move\nz, a move into the outer wall, an empty walk, the goal in 4 moves, a walk\nfrom the goal, and both ways (6 moves against 4).\n\nBuilt on the verified corpus cases `maze-solver-backtracking` (a way\nthrough a maze by marking and unmarking cells, not necessarily the shortest)\nand `graph-bfs-traversal` (breadth-first traversal with a queue).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P041 maze: a random maze from a seed - the same seed always makes the same\n# maze. Walk it from S to G, or show a shortest way (breadth-first search)\n# and the way the corpus case's backtracking finds, which need not be as\n# short.\nimport maze\nimport rng\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 number(s):\n    # The value of 1 to 9 digits, otherwise -1.\n    if s == \"\" or len(s) > 9:\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 moves_text(n):\n    if n == 1:\n        return \"1 move\"\n    return str(n) + \" moves\"\n\ndef show(state, way):\n    for line in maze.drawn(state[0], way, state[2]):\n        line ^0\n\ndef make(rows, cols, seed):\n    maze.generate(rows, cols, rng.Rng(seed)) => m\n    (\"A maze of \" + str(rows) + \" x \" + str(cols) + \" cells from seed \" + str(seed) + \".\") ^0\n    return [m, seed, [0, 0], 0]\n\ndef ask(prompt, low, high):\n    while True:\n        trim(input(prompt + \" (\" + str(low) + \" to \" + str(high) + \")> \")) => answer\n        if answer == \"\":\n            return -1\n        number(answer) => n\n        if n >= low and n <= high:\n            return n\n        (\"Type a number from \" + str(low) + \" to \" + str(high) + \".\") ^0\n\ndef new_maze(state):\n    ask(\"rows\", 3, 10) => rows\n    if rows == -1:\n        \"Cancelled.\" ^0\n        return state\n    ask(\"columns\", 3, 15) => cols\n    if cols == -1:\n        \"Cancelled.\" ^0\n        return state\n    while True:\n        trim(input(\"seed (a whole number)> \")) => answer\n        if answer == \"\":\n            \"Cancelled.\" ^0\n            return state\n        number(answer) => seed\n        if seed >= 0:\n            make(rows, cols, seed) => state\n            show(state, [])\n            return state\n        \"A seed is a whole number of up to 9 digits.\" ^0\n\ndef walk(state):\n    state[0] => m\n    if state[2][0] == m[0] - 1 and state[2][1] == m[1] - 1:\n        \"You have reached the goal already - choose a new maze to walk again.\" ^0\n        return state\n    trim(input(\"moves (u, d, l, r - like rrdd)> \")) => answer\n    if answer == \"\":\n        \"Cancelled.\" ^0\n        return state\n    state[2][0] => r\n    state[2][1] => c\n    state[3] => made\n    for ch in answer:\n        -1 => d\n        if ch == \"u\" or ch == \"U\":\n            0 => d\n        elif ch == \"r\" or ch == \"R\":\n            1 => d\n        elif ch == \"d\" or ch == \"D\":\n            2 => d\n        elif ch == \"l\" or ch == \"L\":\n            3 => d\n        if d == -1:\n            (\"Stopped at \" + ch + \": moves are u, d, l and r.\") ^0\n            return finish(state, r, c, made)\n        if not maze.open_between(m, r, c, d):\n            (\"Stopped: a wall \" + maze.names[d] + \" from here.\") ^0\n            return finish(state, r, c, made)\n        r + maze.dr[d] => r\n        c + maze.dc[d] => c\n        made + 1 => made\n        if r == m[0] - 1 and c == m[1] - 1:\n            return finish(state, r, c, made)\n    return finish(state, r, c, made)\n\ndef finish(state, r, c, made):\n    [state[0], state[1], [r, c], made] => state\n    show(state, [])\n    state[0] => m\n    if r == m[0] - 1 and c == m[1] - 1:\n        len(maze.shortest(m)) - 1 => best\n        (\"You reached the goal in \" + moves_text(made) + \"; the shortest way takes \" + moves_text(best) + \".\") ^0\n    else:\n        (moves_text(made) + \" so far.\") ^0\n    return state\n\n\"== Maze ==\" ^0\n\"Walk from S to G. The same seed always makes the same maze.\" ^0\nmake(6, 10, 2026) => state\nshow(state, [])\nTrue => running\nwhile running:\n    \"\" ^0\n    \"1) new maze  2) walk  3) shortest way  4) backtracking way  5) show  6) quit\" ^0\n    trim(input(\"choice> \")) => choice\n    if choice == \"1\":\n        new_maze(state) => state\n    elif choice == \"2\":\n        walk(state) => state\n    elif choice == \"3\":\n        maze.shortest(state[0]) => way\n        show(state, way)\n        (\"A shortest way: \" + moves_text(len(way) - 1) + \", found by breadth-first search.\") ^0\n    elif choice == \"4\":\n        maze.backtracking(state[0]) => way\n        show(state, way)\n        len(maze.shortest(state[0])) - 1 => best\n        (\"The backtracking way: \" + moves_text(len(way) - 1) + \", trying down, right, up, left in that order; the shortest takes \" + moves_text(best) + \".\") ^0\n    elif choice == \"5\":\n        show(state, [])\n    elif choice == \"6\":\n        False => running\n    else:\n        \"Pick a number from 1 to 6.\" ^0\n\"Bye.\" ^0\n",
      "python": "import maze\nimport rng\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 number(s):\n    if s == \"\" or len(s) > 9:\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 moves_text(n):\n    if n == 1:\n        return \"1 move\"\n    return str(n) + \" moves\"\n\ndef show(state, way):\n    for line in maze.drawn(state[0], way, state[2]):\n        print(line)\n\ndef make(rows, cols, seed):\n    m = maze.generate(rows, cols, rng.Rng(seed))\n    print(\"A maze of \" + str(rows) + \" x \" + str(cols) + \" cells from seed \" + str(seed) + \".\")\n    return [m, seed, [0, 0], 0]\n\ndef ask(prompt, low, high):\n    while True:\n        answer = trim(input(prompt + \" (\" + str(low) + \" to \" + str(high) + \")> \"))\n        if answer == \"\":\n            return -1\n        n = number(answer)\n        if n >= low and n <= high:\n            return n\n        print(\"Type a number from \" + str(low) + \" to \" + str(high) + \".\")\n\ndef new_maze(state):\n    rows = ask(\"rows\", 3, 10)\n    if rows == -1:\n        print(\"Cancelled.\")\n        return state\n    cols = ask(\"columns\", 3, 15)\n    if cols == -1:\n        print(\"Cancelled.\")\n        return state\n    while True:\n        answer = trim(input(\"seed (a whole number)> \"))\n        if answer == \"\":\n            print(\"Cancelled.\")\n            return state\n        seed = number(answer)\n        if seed >= 0:\n            state = make(rows, cols, seed)\n            show(state, [])\n            return state\n        print(\"A seed is a whole number of up to 9 digits.\")\n\ndef walk(state):\n    m = state[0]\n    if state[2][0] == m[0] - 1 and state[2][1] == m[1] - 1:\n        print(\"You have reached the goal already - choose a new maze to walk again.\")\n        return state\n    answer = trim(input(\"moves (u, d, l, r - like rrdd)> \"))\n    if answer == \"\":\n        print(\"Cancelled.\")\n        return state\n    r = state[2][0]\n    c = state[2][1]\n    made = state[3]\n    for ch in answer:\n        d = -1\n        if ch == \"u\" or ch == \"U\":\n            d = 0\n        elif ch == \"r\" or ch == \"R\":\n            d = 1\n        elif ch == \"d\" or ch == \"D\":\n            d = 2\n        elif ch == \"l\" or ch == \"L\":\n            d = 3\n        if d == -1:\n            print(\"Stopped at \" + ch + \": moves are u, d, l and r.\")\n            return finish(state, r, c, made)\n        if not maze.open_between(m, r, c, d):\n            print(\"Stopped: a wall \" + maze.names[d] + \" from here.\")\n            return finish(state, r, c, made)\n        r = r + maze.dr[d]\n        c = c + maze.dc[d]\n        made = made + 1\n        if r == m[0] - 1 and c == m[1] - 1:\n            return finish(state, r, c, made)\n    return finish(state, r, c, made)\n\ndef finish(state, r, c, made):\n    state = [state[0], state[1], [r, c], made]\n    show(state, [])\n    m = state[0]\n    if r == m[0] - 1 and c == m[1] - 1:\n        best = len(maze.shortest(m)) - 1\n        print(\"You reached the goal in \" + moves_text(made) + \"; the shortest way takes \" + moves_text(best) + \".\")\n    else:\n        print(moves_text(made) + \" so far.\")\n    return state\n\nprint(\"== Maze ==\")\nprint(\"Walk from S to G. The same seed always makes the same maze.\")\nstate = make(6, 10, 2026)\nshow(state, [])\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"1) new maze  2) walk  3) shortest way  4) backtracking way  5) show  6) quit\")\n    choice = trim(input(\"choice> \"))\n    if choice == \"1\":\n        state = new_maze(state)\n    elif choice == \"2\":\n        state = walk(state)\n    elif choice == \"3\":\n        way = maze.shortest(state[0])\n        show(state, way)\n        print(\"A shortest way: \" + moves_text(len(way) - 1) + \", found by breadth-first search.\")\n    elif choice == \"4\":\n        way = maze.backtracking(state[0])\n        show(state, way)\n        best = len(maze.shortest(state[0])) - 1\n        print(\"The backtracking way: \" + moves_text(len(way) - 1) + \", trying down, right, up, left in that order; the shortest takes \" + moves_text(best) + \".\")\n    elif choice == \"5\":\n        show(state, [])\n    elif choice == \"6\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 6.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "maze.eml",
      "eml": "# P041 maze - making a maze and finding ways through it. A maze is\n# [rows, cols, right, down]: right[r][c] is True when a wall stands between\n# cell (r, c) and the cell to its right, down[r][c] when one stands between\n# it and the cell below. The way in is the top-left cell, the way out the\n# bottom-right one.\n\n# up, right, down, left\n[-1, 0, 1, 0] => dr\n[0, 1, 0, -1] => dc\n[\"up\", \"right\", \"down\", \"left\"] => names\n\ndef walls(rows, cols):\n    [] => grid\n    for r in [1:rows]:\n        grid + [[True] * cols] => grid\n    return grid\n\ndef open_between(m, r, c, d):\n    # Can one step from (r, c) in direction d (0 up, 1 right, 2 down, 3 left)?\n    r + dr[d] => y\n    c + dc[d] => x\n    if y < 0 or y >= m[0] or x < 0 or x >= m[1]:\n        return False\n    if d == 0:\n        return not m[3][y][x]\n    if d == 1:\n        return not m[2][r][c]\n    if d == 2:\n        return not m[3][r][c]\n    return not m[2][y][x]\n\ndef knock(m, r, c, d):\n    # Takes down the wall on side d of cell (r, c).\n    if d == 0:\n        False => m[3][r - 1][c]\n    elif d == 1:\n        False => m[2][r][c]\n    elif d == 2:\n        False => m[3][r][c]\n    else:\n        False => m[2][r][c - 1]\n\ndef generate(rows, cols, rng):\n    # A random depth-first walk knocks down the walls of a spanning tree, so\n    # every cell is reachable along exactly one way; then about one wall in\n    # eight of those left inside is knocked down too, so that some places\n    # can be reached in more than one way.\n    [rows, cols, walls(rows, cols), walls(rows, cols)] => m\n    [] => seen\n    for r in [1:rows]:\n        seen + [[False] * cols] => seen\n    True => seen[0][0]\n    [[0, 0]] => stack\n    while len(stack) > 0:\n        stack[len(stack) - 1] => cell\n        [] => choices\n        for d in [0:3]:\n            cell[0] + dr[d] => y\n            cell[1] + dc[d] => x\n            if y >= 0 and y < rows and x >= 0 and x < cols and not seen[y][x]:\n                choices + [d] => choices\n        if len(choices) == 0:\n            stack[0:len(stack) - 1] => stack\n        else:\n            choices[rng.below(len(choices))] => d\n            knock(m, cell[0], cell[1], d)\n            cell[0] + dr[d] => y\n            cell[1] + dc[d] => x\n            True => seen[y][x]\n            stack + [[y, x]] => stack\n    [] => standing\n    for r in [0:rows - 1]:\n        for c in [0:cols - 1]:\n            if c < cols - 1 and m[2][r][c]:\n                standing + [[r, c, 1]] => standing\n            if r < rows - 1 and m[3][r][c]:\n                standing + [[r, c, 2]] => standing\n    int(rows * cols / 8) => extra\n    while extra > 0 and len(standing) > 0:\n        rng.below(len(standing)) => k\n        standing[k] => w\n        knock(m, w[0], w[1], w[2])\n        standing[0:k] + standing[k + 1:len(standing)] => standing\n        extra - 1 => extra\n    return m\n\ndef shortest(m):\n    # Breadth-first search from the way in: cells are reached in order of\n    # distance, so the first time the way out is reached, the way there is\n    # a shortest one. Returns the cells from the way in to the way out.\n    m[0] => rows\n    m[1] => cols\n    [] => before\n    for r in [1:rows]:\n        before + [[-1] * cols] => before\n    0 => before[0][0]\n    [[0, 0]] => queue\n    0 => head\n    while head < len(queue):\n        queue[head] => cell\n        head + 1 => head\n        for d in [0:3]:\n            if open_between(m, cell[0], cell[1], d):\n                cell[0] + dr[d] => y\n                cell[1] + dc[d] => x\n                if before[y][x] == -1:\n                    cell[0] * cols + cell[1] => before[y][x]\n                    queue + [[y, x]] => queue\n    [[rows - 1, cols - 1]] => way\n    while way[0][0] != 0 or way[0][1] != 0:\n        before[way[0][0]][way[0][1]] => p\n        [[int(p / cols), p % cols]] + way => way\n    return way\n\ndef wander(m, marks, r, c, path):\n    # The corpus case maze-solver-backtracking: try down, right, up, left in\n    # that order, mark the cell, and unmark it when the branch dead-ends.\n    # It finds a way, not necessarily the shortest.\n    path + [[r, c]] => here\n    if r == m[0] - 1 and c == m[1] - 1:\n        return here\n    True => marks[r][c]\n    for d in [2, 1, 0, 3]:\n        if open_between(m, r, c, d) and not marks[r + dr[d]][c + dc[d]]:\n            wander(m, marks, r + dr[d], c + dc[d], here) => found\n            if len(found) > 0:\n                return found\n    False => marks[r][c]\n    return []\n\ndef backtracking(m):\n    [] => marks\n    for r in [1:m[0]]:\n        marks + [[False] * m[1]] => marks\n    return wander(m, marks, 0, 0, [])\n\ndef drawn(m, way, at):\n    # The maze as lines of text: walls #, the way in S, the way out G, the\n    # walker @, and a way through as dots, on its cells and between them.\n    m[0] => rows\n    m[1] => cols\n    [] => on\n    for r in [1:rows]:\n        on + [[False] * cols] => on\n    for cell in way:\n        True => on[cell[0]][cell[1]]\n    [] => lines\n    \"#\" * (3 * cols + 1) => top\n    lines + [top] => lines\n    for r in [0:rows - 1]:\n        \"#\" => line\n        \"#\" => under\n        for c in [0:cols - 1]:\n            \"  \" => body\n            if on[r][c]:\n                \"..\" => body\n            if r == 0 and c == 0:\n                \"S \" => body\n            if r == rows - 1 and c == cols - 1:\n                \"G \" => body\n            if r == at[0] and c == at[1]:\n                \"@ \" => body\n            line + body => line\n            if c == cols - 1 or m[2][r][c]:\n                line + \"#\" => line\n            elif on[r][c] and on[r][c + 1] and next_to(way, r, c, r, c + 1):\n                line + \".\" => line\n            else:\n                line + \" \" => line\n            if r == rows - 1 or m[3][r][c]:\n                under + \"###\" => under\n            elif on[r][c] and on[r + 1][c] and next_to(way, r, c, r + 1, c):\n                under + \"..#\" => under\n            else:\n                under + \"  #\" => under\n        lines + [line, under] => lines\n    return lines\n\ndef next_to(way, r1, c1, r2, c2):\n    # Are the two cells one step apart along the way?\n    for i in [0:len(way) - 2]:\n        if way[i][0] == r1 and way[i][1] == c1 and way[i + 1][0] == r2 and way[i + 1][1] == c2:\n            return True\n        if way[i][0] == r2 and way[i][1] == c2 and way[i + 1][0] == r1 and way[i + 1][1] == c1:\n            return True\n    return False\n",
      "python": "dr = [-1, 0, 1, 0]\ndc = [0, 1, 0, -1]\nnames = [\"up\", \"right\", \"down\", \"left\"]\n\ndef walls(rows, cols):\n    grid = []\n    for r in range(1, rows+1):\n        grid = grid + [[True] * cols]\n    return grid\n\ndef open_between(m, r, c, d):\n    y = r + dr[d]\n    x = c + dc[d]\n    if y < 0 or y >= m[0] or x < 0 or x >= m[1]:\n        return False\n    if d == 0:\n        return not m[3][y][x]\n    if d == 1:\n        return not m[2][r][c]\n    if d == 2:\n        return not m[3][r][c]\n    return not m[2][y][x]\n\ndef knock(m, r, c, d):\n    if d == 0:\n        m[3][r - 1][c] = False\n    elif d == 1:\n        m[2][r][c] = False\n    elif d == 2:\n        m[3][r][c] = False\n    else:\n        m[2][r][c - 1] = False\n\ndef generate(rows, cols, rng):\n    m = [rows, cols, walls(rows, cols), walls(rows, cols)]\n    seen = []\n    for r in range(1, rows+1):\n        seen = seen + [[False] * cols]\n    seen[0][0] = True\n    stack = [[0, 0]]\n    while len(stack) > 0:\n        cell = stack[len(stack) - 1]\n        choices = []\n        for d in range(0, 4):\n            y = cell[0] + dr[d]\n            x = cell[1] + dc[d]\n            if y >= 0 and y < rows and x >= 0 and x < cols and not seen[y][x]:\n                choices = choices + [d]\n        if len(choices) == 0:\n            stack = stack[0:len(stack) - 1]\n        else:\n            d = choices[rng.below(len(choices))]\n            knock(m, cell[0], cell[1], d)\n            y = cell[0] + dr[d]\n            x = cell[1] + dc[d]\n            seen[y][x] = True\n            stack = stack + [[y, x]]\n    standing = []\n    for r in range(0, rows):\n        for c in range(0, cols):\n            if c < cols - 1 and m[2][r][c]:\n                standing = standing + [[r, c, 1]]\n            if r < rows - 1 and m[3][r][c]:\n                standing = standing + [[r, c, 2]]\n    extra = int(rows * cols / 8)\n    while extra > 0 and len(standing) > 0:\n        k = rng.below(len(standing))\n        w = standing[k]\n        knock(m, w[0], w[1], w[2])\n        standing = standing[0:k] + standing[k + 1:len(standing)]\n        extra = extra - 1\n    return m\n\ndef shortest(m):\n    rows = m[0]\n    cols = m[1]\n    before = []\n    for r in range(1, rows+1):\n        before = before + [[-1] * cols]\n    before[0][0] = 0\n    queue = [[0, 0]]\n    head = 0\n    while head < len(queue):\n        cell = queue[head]\n        head = head + 1\n        for d in range(0, 4):\n            if open_between(m, cell[0], cell[1], d):\n                y = cell[0] + dr[d]\n                x = cell[1] + dc[d]\n                if before[y][x] == -1:\n                    before[y][x] = cell[0] * cols + cell[1]\n                    queue = queue + [[y, x]]\n    way = [[rows - 1, cols - 1]]\n    while way[0][0] != 0 or way[0][1] != 0:\n        p = before[way[0][0]][way[0][1]]\n        way = [[int(p / cols), p % cols]] + way\n    return way\n\ndef wander(m, marks, r, c, path):\n    here = path + [[r, c]]\n    if r == m[0] - 1 and c == m[1] - 1:\n        return here\n    marks[r][c] = True\n    for d in [2, 1, 0, 3]:\n        if open_between(m, r, c, d) and not marks[r + dr[d]][c + dc[d]]:\n            found = wander(m, marks, r + dr[d], c + dc[d], here)\n            if len(found) > 0:\n                return found\n    marks[r][c] = False\n    return []\n\ndef backtracking(m):\n    marks = []\n    for r in range(1, m[0]+1):\n        marks = marks + [[False] * m[1]]\n    return wander(m, marks, 0, 0, [])\n\ndef drawn(m, way, at):\n    rows = m[0]\n    cols = m[1]\n    on = []\n    for r in range(1, rows+1):\n        on = on + [[False] * cols]\n    for cell in way:\n        on[cell[0]][cell[1]] = True\n    lines = []\n    top = \"#\" * (3 * cols + 1)\n    lines = lines + [top]\n    for r in range(0, rows):\n        line = \"#\"\n        under = \"#\"\n        for c in range(0, cols):\n            body = \"  \"\n            if on[r][c]:\n                body = \"..\"\n            if r == 0 and c == 0:\n                body = \"S \"\n            if r == rows - 1 and c == cols - 1:\n                body = \"G \"\n            if r == at[0] and c == at[1]:\n                body = \"@ \"\n            line = line + body\n            if c == cols - 1 or m[2][r][c]:\n                line = line + \"#\"\n            elif on[r][c] and on[r][c + 1] and next_to(way, r, c, r, c + 1):\n                line = line + \".\"\n            else:\n                line = line + \" \"\n            if r == rows - 1 or m[3][r][c]:\n                under = under + \"###\"\n            elif on[r][c] and on[r + 1][c] and next_to(way, r, c, r + 1, c):\n                under = under + \"..#\"\n            else:\n                under = under + \"  #\"\n        lines = lines + [line, under]\n    return lines\n\ndef next_to(way, r1, c1, r2, c2):\n    for i in range(0, len(way) - 2+1):\n        if way[i][0] == r1 and way[i][1] == c1 and way[i + 1][0] == r2 and way[i + 1][1] == c2:\n            return True\n        if way[i][0] == r2 and way[i][1] == c2 and way[i + 1][0] == r1 and way[i + 1][1] == c1:\n            return True\n    return False\n"
    },
    {
      "name": "rng.eml",
      "eml": "# P041 maze - random numbers written in EML: the linear congruential\n# generator of P008 (number guessing), with the constants of the C\n# standard's example rand(). Python's random module is not used, so a seed\n# gives the same maze on every machine, and the interpreter can check a\n# whole session byte for byte.\n\nclass Rng:\n    def __init__(self, seed):\n        seed % 2147483648 => self.state\n\n    def step(self):\n        (1103515245 * self.state + 12345) % 2147483648 => self.state\n        return self.state\n\n    def below(self, n):\n        # A number from 0 to n - 1, taken from the high bits of the state: the\n        # low bits of this generator repeat with short periods. Dividing by\n        # 65536 is exact in a float for a state below 2^31.\n        return int(self.step() / 65536) % n\n",
      "python": "class Rng:\n    def __init__(self, seed):\n        self.state = seed % 2147483648\n    def step(self):\n        self.state = (1103515245 * self.state + 12345) % 2147483648\n        return self.state\n    def below(self, n):\n        return int(self.step() / 65536) % n\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "0\nx\n1\n2\n11\nx\n\n1\n3\n16\n2\n\n1\n3\n3\nabc\n1234567890\n\n1\n3\n3\n7\n2\nz\n2\nu\n2\n\n2\nrrdd\n2\n4\n3\n5\n6\n",
      "screen": "== Maze ==\nWalk from S to G. The same seed always makes the same maze.\nA maze of 6 x 10 cells from seed 2026.\n###############################\n#@                      #     #\n#  ####  #  ####  ####  #  #  #\n#  #     #  #        #     #  #\n#  #  ####  #  #  ##########  #\n#     #     #  #  #           #\n#  ####  ####  #  #  #  #######\n#  #     #        #  #        #\n#  #  #  #  #  ####  #  ####  #\n#           #  #     #  #     #\n#######  #  ####  #######  #  #\n#        #                 #G #\n###############################\n\n1) new maze  2) walk  3) shortest way  4) backtracking way  5) show  6) quit\nchoice> 0\nPick a number from 1 to 6.\n\n1) new maze  2) walk  3) shortest way  4) backtracking way  5) show  6) quit\nchoice> x\nPick a number from 1 to 6.\n\n1) new maze  2) walk  3) shortest way  4) backtracking way  5) show  6) quit\nchoice> 1\nrows (3 to 10)> 2\nType a number from 3 to 10.\nrows (3 to 10)> 11\nType a number from 3 to 10.\nrows (3 to 10)> x\nType a number from 3 to 10.\nrows (3 to 10)> \nCancelled.\n\n1) new maze  2) walk  3) shortest way  4) backtracking way  5) show  6) quit\nchoice> 1\nrows (3 to 10)> 3\ncolumns (3 to 15)> 16\nType a number from 3 to 15.\ncolumns (3 to 15)> 2\nType a number from 3 to 15.\ncolumns (3 to 15)> \nCancelled.\n\n1) new maze  2) walk  3) shortest way  4) backtracking way  5) show  6) quit\nchoice> 1\nrows (3 to 10)> 3\ncolumns (3 to 15)> 3\nseed (a whole number)> abc\nA seed is a whole number of up to 9 digits.\nseed (a whole number)> 1234567890\nA seed is a whole number of up to 9 digits.\nseed (a whole number)> \nCancelled.\n\n1) new maze  2) walk  3) shortest way  4) backtracking way  5) show  6) quit\nchoice> 1\nrows (3 to 10)> 3\ncolumns (3 to 15)> 3\nseed (a whole number)> 7\nA maze of 3 x 3 cells from seed 7.\n##########\n#@       #\n####  #  #\n#     #  #\n#  ####  #\n#      G #\n##########\n\n1) new maze  2) walk  3) shortest way  4) backtracking way  5) show  6) quit\nchoice> 2\nmoves (u, d, l, r - like rrdd)> z\nStopped at z: moves are u, d, l and r.\n##########\n#@       #\n####  #  #\n#     #  #\n#  ####  #\n#      G #\n##########\n0 moves so far.\n\n1) new maze  2) walk  3) shortest way  4) backtracking way  5) show  6) quit\nchoice> 2\nmoves (u, d, l, r - like rrdd)> u\nStopped: a wall up from here.\n##########\n#@       #\n####  #  #\n#     #  #\n#  ####  #\n#      G #\n##########\n0 moves so far.\n\n1) new maze  2) walk  3) shortest way  4) backtracking way  5) show  6) quit\nchoice> 2\nmoves (u, d, l, r - like rrdd)> \nCancelled.\n\n1) new maze  2) walk  3) shortest way  4) backtracking way  5) show  6) quit\nchoice> 2\nmoves (u, d, l, r - like rrdd)> rrdd\n##########\n#S       #\n####  #  #\n#     #  #\n#  ####  #\n#      @ #\n##########\nYou reached the goal in 4 moves; the shortest way takes 4 moves.\n\n1) new maze  2) walk  3) shortest way  4) backtracking way  5) show  6) quit\nchoice> 2\nYou have reached the goal already - choose a new maze to walk again.\n\n1) new maze  2) walk  3) shortest way  4) backtracking way  5) show  6) quit\nchoice> 4\n##########\n#S ...   #\n####..#  #\n#.....#  #\n#..####  #\n#......@ #\n##########\nThe backtracking way: 6 moves, trying down, right, up, left in that order; the shortest takes 4 moves.\n\n1) new maze  2) walk  3) shortest way  4) backtracking way  5) show  6) quit\nchoice> 3\n##########\n#S ......#\n####  #..#\n#     #..#\n#  ####..#\n#      @ #\n##########\nA shortest way: 4 moves, found by breadth-first search.\n\n1) new maze  2) walk  3) shortest way  4) backtracking way  5) show  6) quit\nchoice> 5\n##########\n#S       #\n####  #  #\n#     #  #\n#  ####  #\n#      @ #\n##########\n\n1) new maze  2) walk  3) shortest way  4) backtracking way  5) show  6) quit\nchoice> 6\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "2\nrl\n2\nddddrrrr\n2\ndrrrrrurd\n3\n1\n6\n10\n335\n4\n3\n6\n",
      "screen": "== Maze ==\nWalk from S to G. The same seed always makes the same maze.\nA maze of 6 x 10 cells from seed 2026.\n###############################\n#@                      #     #\n#  ####  #  ####  ####  #  #  #\n#  #     #  #        #     #  #\n#  #  ####  #  #  ##########  #\n#     #     #  #  #           #\n#  ####  ####  #  #  #  #######\n#  #     #        #  #        #\n#  #  #  #  #  ####  #  ####  #\n#           #  #     #  #     #\n#######  #  ####  #######  #  #\n#        #                 #G #\n###############################\n\n1) new maze  2) walk  3) shortest way  4) backtracking way  5) show  6) quit\nchoice> 2\nmoves (u, d, l, r - like rrdd)> rl\n###############################\n#@                      #     #\n#  ####  #  ####  ####  #  #  #\n#  #     #  #        #     #  #\n#  #  ####  #  #  ##########  #\n#     #     #  #  #           #\n#  ####  ####  #  #  #  #######\n#  #     #        #  #        #\n#  #  #  #  #  ####  #  ####  #\n#           #  #     #  #     #\n#######  #  ####  #######  #  #\n#        #                 #G #\n###############################\n2 moves so far.\n\n1) new maze  2) walk  3) shortest way  4) backtracking way  5) show  6) quit\nchoice> 2\nmoves (u, d, l, r - like rrdd)> ddddrrrr\nStopped: a wall right from here.\n###############################\n#S                      #     #\n#  ####  #  ####  ####  #  #  #\n#  #     #  #        #     #  #\n#  #  ####  #  #  ##########  #\n#     #     #  #  #           #\n#  ####  ####  #  #  #  #######\n#  #     #        #  #        #\n#  #  #  #  #  ####  #  ####  #\n#         @ #  #     #  #     #\n#######  #  ####  #######  #  #\n#        #                 #G #\n###############################\n9 moves so far.\n\n1) new maze  2) walk  3) shortest way  4) backtracking way  5) show  6) quit\nchoice> 2\nmoves (u, d, l, r - like rrdd)> drrrrrurd\n###############################\n#S                      #     #\n#  ####  #  ####  ####  #  #  #\n#  #     #  #        #     #  #\n#  #  ####  #  #  ##########  #\n#     #     #  #  #           #\n#  ####  ####  #  #  #  #######\n#  #     #        #  #        #\n#  #  #  #  #  ####  #  ####  #\n#           #  #     #  #     #\n#######  #  ####  #######  #  #\n#        #                 #@ #\n###############################\nYou reached the goal in 18 moves; the shortest way takes 16 moves.\n\n1) new maze  2) walk  3) shortest way  4) backtracking way  5) show  6) quit\nchoice> 3\n###############################\n#S                      #     #\n#..####  #  ####  ####  #  #  #\n#..#     #  #        #     #  #\n#..#  ####  #  #  ##########  #\n#..   #     #  #  #           #\n#..####  ####  #  #  #  #######\n#..#     #        #  #        #\n#..#  #  #  #  ####  #  ####  #\n#...........#  #     #  #.....#\n#######  #..####  #######..#..#\n#        #.................#@ #\n###############################\nA shortest way: 16 moves, found by breadth-first search.\n\n1) new maze  2) walk  3) shortest way  4) backtracking way  5) show  6) quit\nchoice> 1\nrows (3 to 10)> 6\ncolumns (3 to 15)> 10\nseed (a whole number)> 335\nA maze of 6 x 10 cells from seed 335.\n###############################\n#@                            #\n#  #######  #######  ####  #  #\n#        #     #     #     #  #\n#######  ####  #  ####  ####  #\n#     #              #        #\n#  #  #  #  #######  #  #######\n#  #     #        #  #        #\n#  ##########  #  #  #######  #\n#  #           #  #     #  #  #\n#  #  ####  ####  ####  #  #  #\n#                       #   G #\n###############################\n\n1) new maze  2) walk  3) shortest way  4) backtracking way  5) show  6) quit\nchoice> 4\n###############################\n#@                 ........   #\n#..#######  #######..####..#  #\n#........#     #.....#.....#  #\n#######..####  #..####..####  #\n#.....#..       .....#..      #\n#..#..#..#  #######..#..#######\n#..#.....#        #..#........#\n#..##########  #  #..#######..#\n#..#           #  #.....#  #..#\n#..#  ####  ####  ####..#  #..#\n#.......................#   G #\n###############################\nThe backtracking way: 36 moves, trying down, right, up, left in that order; the shortest takes 16 moves.\n\n1) new maze  2) walk  3) shortest way  4) backtracking way  5) show  6) quit\nchoice> 3\n###############################\n#@ ........................   #\n#  #######  #######  ####..#  #\n#        #     #     #.....#  #\n#######  ####  #  ####..####  #\n#     #              #..      #\n#  #  #  #  #######  #..#######\n#  #     #        #  #........#\n#  ##########  #  #  #######..#\n#  #           #  #     #  #..#\n#  #  ####  ####  ####  #  #..#\n#                       #   G #\n###############################\nA shortest way: 16 moves, found by breadth-first search.\n\n1) new maze  2) walk  3) shortest way  4) backtracking way  5) show  6) quit\nchoice> 6\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "maze-solver-backtracking",
      "caseId": "116-maze-solver-backtracking",
      "title": "Maze solver (backtracking)"
    },
    {
      "slug": "graph-bfs-traversal",
      "caseId": "085-graph-bfs-traversal",
      "title": "Graph BFS traversal"
    }
  ],
  "updated": "2026-10-10"
}
