{
  "id": "P093",
  "slug": "route-planner",
  "key": "P093-route-planner",
  "title": "Route planner",
  "summary": "A map of towns and two-way roads in whole kilometres: the shortest route by Dijkstra's method with a binary heap, every town's distance in the order the towns were settled, a route through a chosen town against the shortest, and roads closed, opened or changed.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P093 - Route planner\n\nA map of towns and two-way roads in whole kilometres. The program finds\nthe shortest route between two towns by Dijkstra's method, lists every\ntown's distance in the order the towns were settled, compares a route\nthrough a chosen town with the shortest, and lets roads be closed, opened\nor changed. It starts with eight towns: six on the textbook example map,\nand two more off on their own.\n\n- `main.eml` - the menu, the map, the routes and the road changes\n- `routes.eml` - the roads, the binary heap, Dijkstra's method and the\n  route read back\n\nHow each part works:\n\n- Dijkstra's method: start with the first town at distance 0. Always\n  settle the nearest town not yet settled - with no negative roads, no\n  later route can bring it nearer - then see whether going through it\n  shortens the way to each neighbour. The waiting towns sit in a binary\n  heap, as in the corpus case `binary-heap`, nearest on top. A town can\n  be in the heap more than once; old entries are skipped once it is\n  settled.\n- The route is read back from each town's previous town. As in the corpus\n  case `route-path-builder`, each stop is added by making a new list, so\n  no list already handed out changes.\n- A route through a town is the shortest route to it followed by the\n  shortest route on from it.\n- On the starting map, Avon to Eton is 20 km by Cove and Ford - the\n  answer the textbook example gives.\n\nWhat is checked: menu choices 1 to 7; towns that are on the map; a road\nbetween two different towns, of 1 to 999 km; closing only a road that\nexists. An empty answer cancels.\n\nSessions: `sessions/basic.in` gives:\n- the map; Avon to Eton (20 km); every distance from Avon, with Glen and\n  Hale out of reach;\n- Avon to Eton through Dale (26 km against 20); Bree to Ford (12 km);\n- Cove - Ford closed, which makes Avon to Eton 23 km by Ford;\n- a road Eton - Glen of 4 km, after which Avon reaches Hale in 32 km, and\n  every distance from Hale.\n\n`sessions/bad-input.in` gives:\n- menu choices 0 and x;\n- a town that does not exist; closing a road that does not exist; a road\n  from Avon to Avon; 0, 1000 and x km;\n- Avon through Glen to Bree, which cannot be done.\n\nBuilt on the verified corpus cases `binary-heap` (the nearest town always\non top) and `route-path-builder` (a route extended by making a new one).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P093 route planner: a map of towns and roads, the shortest route between\n# two towns by Dijkstra's method, every town's distance with the order they\n# were settled in, a route through a chosen town, and roads closed or\n# opened.\nimport routes\n\ndef plain(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 lpad(s, n):\n    while len(s) < n:\n        \" \" + s => s\n    return s\n\ndef pad(s, n):\n    while len(s) < n:\n        s + \" \" => s\n    return s\n\ndef ask_town(m, prompt):\n    while True:\n        plain(input(prompt)) => s\n        if s == \"\":\n            return -1\n        routes.index_of(m[0], s) => i\n        if i >= 0:\n            return i\n        (\"No town called \" + s + \" is on the map.\") ^0\n\ndef chain(m, path):\n    \"\" => s\n    for i in path:\n        if s != \"\":\n            s + \" - \" => s\n        s + m[0][i] => s\n    return s\n\ndef show(m):\n    for i in [0:len(m[0]) - 1]:\n        \"\" => s\n        for r in m[1][i]:\n            if s != \"\":\n                s + \", \" => s\n            s + m[0][r[0]] + \" \" + str(r[1]) => s\n        if s == \"\":\n            \"no roads\" => s\n        (\"  \" + pad(m[0][i], 6) + s) ^0\n    \"(kilometres; every road goes both ways)\" ^0\n\ndef shortest(m):\n    ask_town(m, \"from> \") => a\n    if a == -1:\n        \"Cancelled.\" ^0\n        return\n    ask_town(m, \"to> \") => b\n    if b == -1:\n        \"Cancelled.\" ^0\n        return\n    routes.route(m, a, b) => r\n    if r[0] == -1:\n        (\"No road links \" + m[0][a] + \" to \" + m[0][b] + \".\") ^0\n        return\n    (\"  \" + chain(m, r[1])) ^0\n    (str(r[0]) + \" km - no route is shorter.\") ^0\n\ndef distances(m):\n    ask_town(m, \"from> \") => a\n    if a == -1:\n        \"Cancelled.\" ^0\n        return\n    routes.dijkstra(m, a) => r\n    \"  town   km  by way of\" ^0\n    for i in r[2]:\n        \"-\" => via\n        if r[1][i] >= 0:\n            m[0][r[1][i]] => via\n        (\"  \" + pad(m[0][i], 6) + lpad(str(r[0][i]), 3) + \"  \" + via) ^0\n    [] => far\n    for i in [0:len(m[0]) - 1]:\n        if r[0][i] == -1:\n            far + [m[0][i]] => far\n    if len(far) > 0:\n        \"\" => s\n        for t in far:\n            if s != \"\":\n                s + \", \" => s\n            s + t => s\n        (\"  cannot be reached: \" + s) ^0\n    \"Towns are listed in the order they were settled: always the nearest one not yet settled, whose distance can then no longer shrink.\" ^0\n\ndef via(m):\n    ask_town(m, \"from> \") => a\n    if a == -1:\n        \"Cancelled.\" ^0\n        return\n    ask_town(m, \"through> \") => v\n    if v == -1:\n        \"Cancelled.\" ^0\n        return\n    ask_town(m, \"to> \") => b\n    if b == -1:\n        \"Cancelled.\" ^0\n        return\n    routes.route(m, a, v) => r1\n    routes.route(m, v, b) => r2\n    if r1[0] == -1 or r2[0] == -1:\n        (\"There is no way from \" + m[0][a] + \" through \" + m[0][v] + \" to \" + m[0][b] + \".\") ^0\n        return\n    r1[1] + r2[1][1:len(r2[1])] => path\n    (\"  \" + chain(m, path)) ^0\n    routes.route(m, a, b) => direct\n    (str(r1[0] + r2[0]) + \" km through \" + m[0][v] + \", against \" + str(direct[0]) + \" km by the shortest route.\") ^0\n\ndef close(m):\n    ask_town(m, \"one end> \") => a\n    if a == -1:\n        \"Cancelled.\" ^0\n        return m\n    ask_town(m, \"other end> \") => b\n    if b == -1:\n        \"Cancelled.\" ^0\n        return m\n    if routes.road(m, a, b) == -1:\n        (\"There is no road between \" + m[0][a] + \" and \" + m[0][b] + \".\") ^0\n        return m\n    (\"The road \" + m[0][a] + \" - \" + m[0][b] + \" is closed.\") ^0\n    return routes.set_road(m, a, b, 0)\n\ndef open_road(m):\n    ask_town(m, \"one end> \") => a\n    if a == -1:\n        \"Cancelled.\" ^0\n        return m\n    ask_town(m, \"other end> \") => b\n    if b == -1:\n        \"Cancelled.\" ^0\n        return m\n    if a == b:\n        \"A road needs two different towns.\" ^0\n        return m\n    while True:\n        plain(input(\"kilometres (1 to 999)> \")) => s\n        if s == \"\":\n            \"Cancelled.\" ^0\n            return m\n        0 => km\n        True => ok\n        if len(s) > 3:\n            False => ok\n        for c in s:\n            if c >= \"0\" and c <= \"9\":\n                km * 10 + int(c) => km\n            else:\n                False => ok\n        if ok and km >= 1:\n            (\"The road \" + m[0][a] + \" - \" + m[0][b] + \" is now \" + str(km) + \" km.\") ^0\n            return routes.set_road(m, a, b, km)\n        \"Type a whole number from 1 to 999.\" ^0\n\n[\"Avon\", \"Bree\", \"Cove\", \"Dale\", \"Eton\", \"Ford\", \"Glen\", \"Hale\"] => towns\n[[], [], [], [], [], [], [], []] => roads\n[towns, roads] => m\nfor r in [[0, 1, 7], [0, 2, 9], [0, 5, 14], [1, 2, 10], [1, 3, 15], [2, 3, 11], [2, 5, 2], [3, 4, 6], [4, 5, 9], [6, 7, 5]]:\n    routes.set_road(m, r[0], r[1], r[2]) => m\n\n\"== Route planner ==\" ^0\n\"Towns and roads: the shortest route by Dijkstra's method.\" ^0\nTrue => running\nwhile running:\n    \"\" ^0\n    \"1) map  2) shortest route  3) distances from a town  4) route through a town  5) close a road  6) open or change a road  7) quit\" ^0\n    plain(input(\"choice> \")) => choice\n    if choice == \"1\":\n        show(m)\n    elif choice == \"2\":\n        shortest(m)\n    elif choice == \"3\":\n        distances(m)\n    elif choice == \"4\":\n        via(m)\n    elif choice == \"5\":\n        close(m) => m\n    elif choice == \"6\":\n        open_road(m) => m\n    elif choice == \"7\":\n        False => running\n    else:\n        \"Pick a number from 1 to 7.\" ^0\n\"Bye.\" ^0\n",
      "python": "import routes\n\ndef plain(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 lpad(s, n):\n    while len(s) < n:\n        s = \" \" + s\n    return s\n\ndef pad(s, n):\n    while len(s) < n:\n        s = s + \" \"\n    return s\n\ndef ask_town(m, prompt):\n    while True:\n        s = plain(input(prompt))\n        if s == \"\":\n            return -1\n        i = routes.index_of(m[0], s)\n        if i >= 0:\n            return i\n        print(\"No town called \" + s + \" is on the map.\")\n\ndef chain(m, path):\n    s = \"\"\n    for i in path:\n        if s != \"\":\n            s = s + \" - \"\n        s = s + m[0][i]\n    return s\n\ndef show(m):\n    for i in range(0, len(m[0])):\n        s = \"\"\n        for r in m[1][i]:\n            if s != \"\":\n                s = s + \", \"\n            s = s + m[0][r[0]] + \" \" + str(r[1])\n        if s == \"\":\n            s = \"no roads\"\n        print(\"  \" + pad(m[0][i], 6) + s)\n    print(\"(kilometres; every road goes both ways)\")\n\ndef shortest(m):\n    a = ask_town(m, \"from> \")\n    if a == -1:\n        print(\"Cancelled.\")\n        return\n    b = ask_town(m, \"to> \")\n    if b == -1:\n        print(\"Cancelled.\")\n        return\n    r = routes.route(m, a, b)\n    if r[0] == -1:\n        print(\"No road links \" + m[0][a] + \" to \" + m[0][b] + \".\")\n        return\n    print(\"  \" + chain(m, r[1]))\n    print(str(r[0]) + \" km - no route is shorter.\")\n\ndef distances(m):\n    a = ask_town(m, \"from> \")\n    if a == -1:\n        print(\"Cancelled.\")\n        return\n    r = routes.dijkstra(m, a)\n    print(\"  town   km  by way of\")\n    for i in r[2]:\n        via = \"-\"\n        if r[1][i] >= 0:\n            via = m[0][r[1][i]]\n        print(\"  \" + pad(m[0][i], 6) + lpad(str(r[0][i]), 3) + \"  \" + via)\n    far = []\n    for i in range(0, len(m[0])):\n        if r[0][i] == -1:\n            far = far + [m[0][i]]\n    if len(far) > 0:\n        s = \"\"\n        for t in far:\n            if s != \"\":\n                s = s + \", \"\n            s = s + t\n        print(\"  cannot be reached: \" + s)\n    print(\"Towns are listed in the order they were settled: always the nearest one not yet settled, whose distance can then no longer shrink.\")\n\ndef via(m):\n    a = ask_town(m, \"from> \")\n    if a == -1:\n        print(\"Cancelled.\")\n        return\n    v = ask_town(m, \"through> \")\n    if v == -1:\n        print(\"Cancelled.\")\n        return\n    b = ask_town(m, \"to> \")\n    if b == -1:\n        print(\"Cancelled.\")\n        return\n    r1 = routes.route(m, a, v)\n    r2 = routes.route(m, v, b)\n    if r1[0] == -1 or r2[0] == -1:\n        print(\"There is no way from \" + m[0][a] + \" through \" + m[0][v] + \" to \" + m[0][b] + \".\")\n        return\n    path = r1[1] + r2[1][1:len(r2[1])]\n    print(\"  \" + chain(m, path))\n    direct = routes.route(m, a, b)\n    print(str(r1[0] + r2[0]) + \" km through \" + m[0][v] + \", against \" + str(direct[0]) + \" km by the shortest route.\")\n\ndef close(m):\n    a = ask_town(m, \"one end> \")\n    if a == -1:\n        print(\"Cancelled.\")\n        return m\n    b = ask_town(m, \"other end> \")\n    if b == -1:\n        print(\"Cancelled.\")\n        return m\n    if routes.road(m, a, b) == -1:\n        print(\"There is no road between \" + m[0][a] + \" and \" + m[0][b] + \".\")\n        return m\n    print(\"The road \" + m[0][a] + \" - \" + m[0][b] + \" is closed.\")\n    return routes.set_road(m, a, b, 0)\n\ndef open_road(m):\n    a = ask_town(m, \"one end> \")\n    if a == -1:\n        print(\"Cancelled.\")\n        return m\n    b = ask_town(m, \"other end> \")\n    if b == -1:\n        print(\"Cancelled.\")\n        return m\n    if a == b:\n        print(\"A road needs two different towns.\")\n        return m\n    while True:\n        s = plain(input(\"kilometres (1 to 999)> \"))\n        if s == \"\":\n            print(\"Cancelled.\")\n            return m\n        km = 0\n        ok = True\n        if len(s) > 3:\n            ok = False\n        for c in s:\n            if c >= \"0\" and c <= \"9\":\n                km = km * 10 + int(c)\n            else:\n                ok = False\n        if ok and km >= 1:\n            print(\"The road \" + m[0][a] + \" - \" + m[0][b] + \" is now \" + str(km) + \" km.\")\n            return routes.set_road(m, a, b, km)\n        print(\"Type a whole number from 1 to 999.\")\n\ntowns = [\"Avon\", \"Bree\", \"Cove\", \"Dale\", \"Eton\", \"Ford\", \"Glen\", \"Hale\"]\nroads = [[], [], [], [], [], [], [], []]\nm = [towns, roads]\nfor r in [[0, 1, 7], [0, 2, 9], [0, 5, 14], [1, 2, 10], [1, 3, 15], [2, 3, 11], [2, 5, 2], [3, 4, 6], [4, 5, 9], [6, 7, 5]]:\n    m = routes.set_road(m, r[0], r[1], r[2])\nprint(\"== Route planner ==\")\nprint(\"Towns and roads: the shortest route by Dijkstra's method.\")\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"1) map  2) shortest route  3) distances from a town  4) route through a town  5) close a road  6) open or change a road  7) quit\")\n    choice = plain(input(\"choice> \"))\n    if choice == \"1\":\n        show(m)\n    elif choice == \"2\":\n        shortest(m)\n    elif choice == \"3\":\n        distances(m)\n    elif choice == \"4\":\n        via(m)\n    elif choice == \"5\":\n        m = close(m)\n    elif choice == \"6\":\n        m = open_road(m)\n    elif choice == \"7\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 7.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "routes.eml",
      "eml": "# P093 route planner - towns and the roads between them (both ways, whole\n# kilometres), and the shortest route by Dijkstra's method with a binary\n# heap. A map is [towns, roads], roads[i] the list of [town, km] from i.\n\ndef index_of(towns, name):\n    for i in [0:len(towns) - 1]:\n        if towns[i] == name:\n            return i\n    return -1\n\ndef road(m, a, b):\n    # The length of the road a-b, or -1 if there is none.\n    for r in m[1][a]:\n        if r[0] == b:\n            return r[1]\n    return -1\n\ndef set_road(m, a, b, km):\n    # Add, change (km > 0) or close (km = 0) the road a-b, both ways.\n    m[1] => roads\n    for e in [[a, b], [b, a]]:\n        [] => out\n        for r in roads[e[0]]:\n            if r[0] != e[1]:\n                out + [r] => out\n        if km > 0:\n            out + [[e[1], km]] => out\n        out => roads[e[0]]\n    return [m[0], roads]\n\ndef before(x, y):\n    # Heap order: shorter distance first, then the lower town number.\n    return x[0] < y[0] or (x[0] == y[0] and x[1] < y[1])\n\ndef push(heap, x):\n    # The corpus case binary-heap: add at the end and swap up.\n    heap + [x] => heap\n    len(heap) - 1 => i\n    while i > 0:\n        int((i - 1) / 2) => p\n        if before(heap[i], heap[p]):\n            heap[p] => t\n            heap[i] => heap[p]\n            t => heap[i]\n            p => i\n        else:\n            0 => i\n    return heap\n\ndef pop(heap):\n    heap[0] => top\n    heap[len(heap) - 1] => last\n    heap[0:len(heap) - 1] => heap\n    if len(heap) > 0:\n        last => heap[0]\n        0 => i\n        True => going\n        while going:\n            2 * i + 1 => l\n            i => s\n            if l < len(heap) and before(heap[l], heap[s]):\n                l => s\n            if l + 1 < len(heap) and before(heap[l + 1], heap[s]):\n                l + 1 => s\n            if s == i:\n                False => going\n            else:\n                heap[s] => t\n                heap[i] => heap[s]\n                t => heap[i]\n                s => i\n    return [top, heap]\n\ndef dijkstra(m, start):\n    # Shortest distances from start: always settle the nearest town not yet\n    # settled, then see whether going through it shortens the way to its\n    # neighbours. Old heap entries for a town already settled are skipped.\n    # Returns [dist (-1 unreachable), previous town, settling order].\n    len(m[0]) => n\n    [-1] * n => dist\n    [-1] * n => prev\n    [0] * n => done\n    [] => order\n    0 => dist[start]\n    push([], [0, start]) => heap\n    while len(heap) > 0:\n        pop(heap) => r\n        r[0] => top\n        r[1] => heap\n        top[1] => u\n        if done[u] == 0:\n            1 => done[u]\n            order + [u] => order\n            for e in m[1][u]:\n                e[0] => v\n                dist[u] + e[1] => d\n                if done[v] == 0 and (dist[v] == -1 or d < dist[v] or (d == dist[v] and u < prev[v])):\n                    d => dist[v]\n                    u => prev[v]\n                    push(heap, [d, v]) => heap\n    return [dist, prev, order]\n\ndef route(m, a, b):\n    # [km, towns along the way] or [-1, []]. The path is built by adding a\n    # stop to a new list each time, as in the corpus case route-path-builder,\n    # so no list already handed out changes.\n    dijkstra(m, a) => r\n    if r[0][b] == -1:\n        return [-1, []]\n    [b] => path\n    b => x\n    while x != a:\n        r[1][x] => x\n        [x] + path => path\n    return [r[0][b], path]\n",
      "python": "def index_of(towns, name):\n    for i in range(0, len(towns)):\n        if towns[i] == name:\n            return i\n    return -1\n\ndef road(m, a, b):\n    for r in m[1][a]:\n        if r[0] == b:\n            return r[1]\n    return -1\n\ndef set_road(m, a, b, km):\n    roads = m[1]\n    for e in [[a, b], [b, a]]:\n        out = []\n        for r in roads[e[0]]:\n            if r[0] != e[1]:\n                out = out + [r]\n        if km > 0:\n            out = out + [[e[1], km]]\n        roads[e[0]] = out\n    return [m[0], roads]\n\ndef before(x, y):\n    return x[0] < y[0] or x[0] == y[0] and x[1] < y[1]\n\ndef push(heap, x):\n    heap = heap + [x]\n    i = len(heap) - 1\n    while i > 0:\n        p = int((i - 1) / 2)\n        if before(heap[i], heap[p]):\n            t = heap[p]\n            heap[p] = heap[i]\n            heap[i] = t\n            i = p\n        else:\n            i = 0\n    return heap\n\ndef pop(heap):\n    top = heap[0]\n    last = heap[len(heap) - 1]\n    heap = heap[0:len(heap) - 1]\n    if len(heap) > 0:\n        heap[0] = last\n        i = 0\n        going = True\n        while going:\n            l = 2 * i + 1\n            s = i\n            if l < len(heap) and before(heap[l], heap[s]):\n                s = l\n            if l + 1 < len(heap) and before(heap[l + 1], heap[s]):\n                s = l + 1\n            if s == i:\n                going = False\n            else:\n                t = heap[s]\n                heap[s] = heap[i]\n                heap[i] = t\n                i = s\n    return [top, heap]\n\ndef dijkstra(m, start):\n    n = len(m[0])\n    dist = [-1] * n\n    prev = [-1] * n\n    done = [0] * n\n    order = []\n    dist[start] = 0\n    heap = push([], [0, start])\n    while len(heap) > 0:\n        r = pop(heap)\n        top = r[0]\n        heap = r[1]\n        u = top[1]\n        if done[u] == 0:\n            done[u] = 1\n            order = order + [u]\n            for e in m[1][u]:\n                v = e[0]\n                d = dist[u] + e[1]\n                if done[v] == 0 and (dist[v] == -1 or d < dist[v] or d == dist[v] and u < prev[v]):\n                    dist[v] = d\n                    prev[v] = u\n                    heap = push(heap, [d, v])\n    return [dist, prev, order]\n\ndef route(m, a, b):\n    r = dijkstra(m, a)\n    if r[0][b] == -1:\n        return [-1, []]\n    path = [b]\n    x = b\n    while x != a:\n        x = r[1][x]\n        path = [x] + path\n    return [r[0][b], path]\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "0\nx\n2\nZed\n\n5\nAvon\nEton\n6\nAvon\nAvon\n6\nAvon\nBree\n0\n1000\nx\n\n4\nAvon\nGlen\nBree\n7\n",
      "screen": "== Route planner ==\nTowns and roads: the shortest route by Dijkstra's method.\n\n1) map  2) shortest route  3) distances from a town  4) route through a town  5) close a road  6) open or change a road  7) quit\nchoice> 0\nPick a number from 1 to 7.\n\n1) map  2) shortest route  3) distances from a town  4) route through a town  5) close a road  6) open or change a road  7) quit\nchoice> x\nPick a number from 1 to 7.\n\n1) map  2) shortest route  3) distances from a town  4) route through a town  5) close a road  6) open or change a road  7) quit\nchoice> 2\nfrom> Zed\nNo town called Zed is on the map.\nfrom> \nCancelled.\n\n1) map  2) shortest route  3) distances from a town  4) route through a town  5) close a road  6) open or change a road  7) quit\nchoice> 5\none end> Avon\nother end> Eton\nThere is no road between Avon and Eton.\n\n1) map  2) shortest route  3) distances from a town  4) route through a town  5) close a road  6) open or change a road  7) quit\nchoice> 6\none end> Avon\nother end> Avon\nA road needs two different towns.\n\n1) map  2) shortest route  3) distances from a town  4) route through a town  5) close a road  6) open or change a road  7) quit\nchoice> 6\none end> Avon\nother end> Bree\nkilometres (1 to 999)> 0\nType a whole number from 1 to 999.\nkilometres (1 to 999)> 1000\nType a whole number from 1 to 999.\nkilometres (1 to 999)> x\nType a whole number from 1 to 999.\nkilometres (1 to 999)> \nCancelled.\n\n1) map  2) shortest route  3) distances from a town  4) route through a town  5) close a road  6) open or change a road  7) quit\nchoice> 4\nfrom> Avon\nthrough> Glen\nto> Bree\nThere is no way from Avon through Glen to Bree.\n\n1) map  2) shortest route  3) distances from a town  4) route through a town  5) close a road  6) open or change a road  7) quit\nchoice> 7\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "1\n2\nAvon\nEton\n3\nAvon\n4\nAvon\nDale\nEton\n2\nBree\nFord\n5\nCove\nFord\n2\nAvon\nEton\n6\nEton\nGlen\n4\n2\nAvon\nHale\n3\nHale\n7\n",
      "screen": "== Route planner ==\nTowns and roads: the shortest route by Dijkstra's method.\n\n1) map  2) shortest route  3) distances from a town  4) route through a town  5) close a road  6) open or change a road  7) quit\nchoice> 1\n  Avon  Bree 7, Cove 9, Ford 14\n  Bree  Avon 7, Cove 10, Dale 15\n  Cove  Avon 9, Bree 10, Dale 11, Ford 2\n  Dale  Bree 15, Cove 11, Eton 6\n  Eton  Dale 6, Ford 9\n  Ford  Avon 14, Cove 2, Eton 9\n  Glen  Hale 5\n  Hale  Glen 5\n(kilometres; every road goes both ways)\n\n1) map  2) shortest route  3) distances from a town  4) route through a town  5) close a road  6) open or change a road  7) quit\nchoice> 2\nfrom> Avon\nto> Eton\n  Avon - Cove - Ford - Eton\n20 km - no route is shorter.\n\n1) map  2) shortest route  3) distances from a town  4) route through a town  5) close a road  6) open or change a road  7) quit\nchoice> 3\nfrom> Avon\n  town   km  by way of\n  Avon    0  -\n  Bree    7  Avon\n  Cove    9  Avon\n  Ford   11  Cove\n  Dale   20  Cove\n  Eton   20  Ford\n  cannot be reached: Glen, Hale\nTowns are listed in the order they were settled: always the nearest one not yet settled, whose distance can then no longer shrink.\n\n1) map  2) shortest route  3) distances from a town  4) route through a town  5) close a road  6) open or change a road  7) quit\nchoice> 4\nfrom> Avon\nthrough> Dale\nto> Eton\n  Avon - Cove - Dale - Eton\n26 km through Dale, against 20 km by the shortest route.\n\n1) map  2) shortest route  3) distances from a town  4) route through a town  5) close a road  6) open or change a road  7) quit\nchoice> 2\nfrom> Bree\nto> Ford\n  Bree - Cove - Ford\n12 km - no route is shorter.\n\n1) map  2) shortest route  3) distances from a town  4) route through a town  5) close a road  6) open or change a road  7) quit\nchoice> 5\none end> Cove\nother end> Ford\nThe road Cove - Ford is closed.\n\n1) map  2) shortest route  3) distances from a town  4) route through a town  5) close a road  6) open or change a road  7) quit\nchoice> 2\nfrom> Avon\nto> Eton\n  Avon - Ford - Eton\n23 km - no route is shorter.\n\n1) map  2) shortest route  3) distances from a town  4) route through a town  5) close a road  6) open or change a road  7) quit\nchoice> 6\none end> Eton\nother end> Glen\nkilometres (1 to 999)> 4\nThe road Eton - Glen is now 4 km.\n\n1) map  2) shortest route  3) distances from a town  4) route through a town  5) close a road  6) open or change a road  7) quit\nchoice> 2\nfrom> Avon\nto> Hale\n  Avon - Ford - Eton - Glen - Hale\n32 km - no route is shorter.\n\n1) map  2) shortest route  3) distances from a town  4) route through a town  5) close a road  6) open or change a road  7) quit\nchoice> 3\nfrom> Hale\n  town   km  by way of\n  Hale    0  -\n  Glen    5  Hale\n  Eton    9  Glen\n  Dale   15  Eton\n  Ford   18  Eton\n  Cove   26  Dale\n  Bree   30  Dale\n  Avon   32  Ford\nTowns are listed in the order they were settled: always the nearest one not yet settled, whose distance can then no longer shrink.\n\n1) map  2) shortest route  3) distances from a town  4) route through a town  5) close a road  6) open or change a road  7) quit\nchoice> 7\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "binary-heap",
      "caseId": "096-binary-heap",
      "title": "Binary heap (min-heap priority queue)"
    },
    {
      "slug": "route-path-builder",
      "caseId": "182-route-path-builder",
      "title": "Extending a path without mutating it"
    }
  ],
  "updated": "2026-10-15"
}
