{
  "id": "P033",
  "slug": "elevator",
  "key": "P033-elevator",
  "title": "Elevator",
  "summary": "One lift over floors 0 to 9: people call it from a floor to go to another, time moves on in steps, and the lift keeps its direction while there is a reason to go on and turns round when there is none. Statistics show how long people waited, and the same calls can be run again by a lift that takes one passenger at a time, to compare.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P033 - Elevator\n\nOne lift serves floors 0 to 9. People call it from a floor to go to another;\ntime moves on in steps - one floor of travel, or one stop with the doors open -\nand the screen shows each move and each stop with who got out and who got in.\nThe lift follows the usual rule for a single car (collective control). The\nstatistics show how long people waited for the lift and how long their whole\ntrip took, and the same calls can be run again from the start by a lift that\ntakes one passenger at a time, in call order, to compare.\n\n- `main.eml` - the menu, the questions and their checks, stepping and running\n  on\n- `lift.eml` - the rules: one step of time with the direction rule or one\n  passenger at a time, and a whole run of a list of calls\n- `report.eml` - the steps as lines, the building, the statistics and the\n  comparison table\n\nHow each part works:\n\n- One step with the direction rule: riders whose floor this is get out; the\n  lift keeps its direction while there is a reason to go on - a rider going\n  further that way, a call from a floor further that way, or someone waiting\n  here who wants to go that way. With no reason ahead but one behind, it turns\n  round; with none at all, it goes idle, and an idle lift heads for the\n  earliest call. Whoever waits at the floor and wants to go the lift's way\n  gets in. A stop to let people out or in takes the step.\n- So nobody is carried past their floor, and the lift never leaves behind\n  someone who wants to go its way. Someone who wants the other way waits until\n  it comes back: in the basic session P4 waits at floor 7 to go down while the\n  lift goes past on its way up to 8 and 9, and gets in on the way back.\n- The corpus case `elevator-simulator` moves one floor per step towards each\n  request of a queue in turn. That is the one-at-a-time lift here, kept for\n  the comparison: the same calls, at the same times, are run from t=0 by both\n  lifts.\n- Times are whole steps: the wait runs from the call to getting in, the trip\n  from the call to getting out. Averages are worked out in whole numbers and\n  shown to one decimal place, halves rounded up; on a tie for the longest, the\n  lowest passenger number is named.\n\nWhat is checked: floors from 0 to 9; a destination other than the floor the\ncaller is on; at most 20 calls in a run; 1 to 20 steps at a time. An empty\nanswer cancels.\n\nSessions: `sessions/basic.in` has four people call at t=0 - from the ground\nfloor up to 8, from 3 up to 6, from 5 up to 9 and from 7 down to 2; after four\nsteps a fifth calls from floor 1, behind the lift. It shows the building,\nfinishes the run, and shows the statistics and the comparison: an average wait\nof 9.8 steps with the direction rule, against 19.6 one at a time.\n`sessions/bad-input.in` gives menu choices 0 and x; asks for statistics, the\ncomparison and the finish before any call; asks for 0, 21 and abc steps before\nthree idle ones; gives floors 10 and x and the same floor twice; then makes\ntwenty calls at once - two or three on most floors, both ways - and a\ntwenty-first that is refused; shows the building, finishes (an average wait of\n14.1 against 88.2 one at a time) and shows the statistics.\n\nBuilt on the verified corpus case `elevator-simulator` (a lift that moves one\nfloor per step towards each request of a queue in turn).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P033 elevator: one lift, floors 0 to 9. People call it from a floor to go\n# to another, time moves on in steps, and the lift follows the usual rule for\n# a single car (collective control). The statistics show how long people\n# waited, and the same calls can be run again by a lift that takes one\n# passenger at a time, in call order, to compare.\nimport lift\nimport report\n\n20 => most_calls\n20 => most_steps\n\ndef trim(s):\n    0 => i\n    len(s) => j\n    while i < j and s[i] == \" \":\n        i + 1 => i\n    while j > i and s[j - 1] == \" \":\n        j - 1 => j\n    return s[i:j]\n\ndef whole_number(s):\n    # The value of s if it is 1 to 3 digits, otherwise -1.\n    trim(s) => s\n    if s == \"\" or len(s) > 3:\n        return -1\n    0 => n\n    for c in s:\n        if not (c in \"0123456789\"):\n            return -1\n        n * 10 + int(c) => n\n    return n\n\ndef ask_floor(prompt):\n    # A floor, or -1 when the answer is empty.\n    while True:\n        trim(input(prompt)) => answer\n        if answer == \"\":\n            return -1\n        whole_number(answer) => f\n        if f >= lift.lowest and f <= lift.highest:\n            return f\n        (\"Type a floor from \" + str(lift.lowest) + \" to \" + str(lift.highest) + \".\") ^0\n\ndef ask_destination(start):\n    # A floor other than start, or -1 when the answer is empty.\n    while True:\n        ask_floor(\"to floor> \") => f\n        if f != start:\n            return f\n        \"That is the floor they are on.\" ^0\n\ndef call(state, calls):\n    # Returns [state, calls] with the new caller waiting.\n    if len(calls) == most_calls:\n        (\"That makes \" + str(most_calls) + \" calls, the most for one run.\") ^0\n        return [state, calls]\n    ask_floor(\"from floor> \") => a\n    if a == -1:\n        \"Cancelled.\" ^0\n        return [state, calls]\n    ask_destination(a) => b\n    if b == -1:\n        \"Cancelled.\" ^0\n        return [state, calls]\n    len(calls) + 1 => n\n    calls + [[n, a, b, state[0]]] => calls\n    state[3] + [[n, a, b, state[0], -1, -1]] => waiting\n    \"up\" => way\n    if b < a:\n        \"down\" => way\n    (\"P\" + str(n) + \" waits at floor \" + str(a) + \" to go \" + way + \" to \" + str(b) + \" (t=\" + str(state[0]) + \").\") ^0\n    return [[state[0], state[1], state[2], waiting, state[4], state[5]], calls]\n\ndef advance(state, n):\n    [] => events\n    for k in [1:n]:\n        lift.step(state, False) => r\n        r[0] => state\n        events + [r[1]] => events\n    for line in report.event_lines(events):\n        line ^0\n    return state\n\ndef steps(state):\n    while True:\n        trim(input(\"how many steps (1 to \" + str(most_steps) + \")> \")) => answer\n        if answer == \"\":\n            \"Cancelled.\" ^0\n            return state\n        whole_number(answer) => n\n        if n >= 1 and n <= most_steps:\n            return advance(state, n)\n        (\"Type a number from 1 to \" + str(most_steps) + \".\") ^0\n\ndef run_to_end(state):\n    if not lift.busy(state):\n        \"Nobody is waiting or riding.\" ^0\n        return state\n    [] => events\n    while lift.busy(state):\n        lift.step(state, False) => r\n        r[0] => state\n        events + [r[1]] => events\n    for line in report.event_lines(events):\n        line ^0\n    (\"Everyone has arrived; the lift is free at floor \" + str(state[1]) + \".\") ^0\n    return state\n\n\"== Elevator ==\" ^0\n\"One lift, floors 0 to 9. A step of time is one floor of travel or one stop\" ^0\n\"with the doors open; finish runs on until everyone has arrived.\" ^0\n[0, lift.lowest, 0, [], [], []] => state\n[] => calls\nTrue => running\nwhile running:\n    \"\" ^0\n    \"1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\" ^0\n    trim(input(\"t=\" + str(state[0]) + \" choice> \")) => choice\n    if choice == \"1\":\n        call(state, calls) => r\n        r[0] => state\n        r[1] => calls\n    elif choice == \"2\":\n        steps(state) => state\n    elif choice == \"3\":\n        run_to_end(state) => state\n    elif choice == \"4\":\n        report.building(state)\n    elif choice == \"5\":\n        report.statistics(state)\n    elif choice == \"6\":\n        report.compare(calls)\n    elif choice == \"7\":\n        False => running\n    else:\n        \"Pick a number from 1 to 7.\" ^0\n\"Bye.\" ^0\n",
      "python": "import lift\nimport report\nmost_calls = 20\nmost_steps = 20\n\ndef trim(s):\n    i = 0\n    j = len(s)\n    while i < j and s[i] == \" \":\n        i = i + 1\n    while j > i and s[j - 1] == \" \":\n        j = j - 1\n    return s[i:j]\n\ndef whole_number(s):\n    s = trim(s)\n    if s == \"\" or len(s) > 3:\n        return -1\n    n = 0\n    for c in s:\n        if not c in \"0123456789\":\n            return -1\n        n = n * 10 + int(c)\n    return n\n\ndef ask_floor(prompt):\n    while True:\n        answer = trim(input(prompt))\n        if answer == \"\":\n            return -1\n        f = whole_number(answer)\n        if f >= lift.lowest and f <= lift.highest:\n            return f\n        print(\"Type a floor from \" + str(lift.lowest) + \" to \" + str(lift.highest) + \".\")\n\ndef ask_destination(start):\n    while True:\n        f = ask_floor(\"to floor> \")\n        if f != start:\n            return f\n        print(\"That is the floor they are on.\")\n\ndef call(state, calls):\n    if len(calls) == most_calls:\n        print(\"That makes \" + str(most_calls) + \" calls, the most for one run.\")\n        return [state, calls]\n    a = ask_floor(\"from floor> \")\n    if a == -1:\n        print(\"Cancelled.\")\n        return [state, calls]\n    b = ask_destination(a)\n    if b == -1:\n        print(\"Cancelled.\")\n        return [state, calls]\n    n = len(calls) + 1\n    calls = calls + [[n, a, b, state[0]]]\n    waiting = state[3] + [[n, a, b, state[0], -1, -1]]\n    way = \"up\"\n    if b < a:\n        way = \"down\"\n    print(\"P\" + str(n) + \" waits at floor \" + str(a) + \" to go \" + way + \" to \" + str(b) + \" (t=\" + str(state[0]) + \").\")\n    return [[state[0], state[1], state[2], waiting, state[4], state[5]], calls]\n\ndef advance(state, n):\n    events = []\n    for k in range(1, n+1):\n        r = lift.step(state, False)\n        state = r[0]\n        events = events + [r[1]]\n    for line in report.event_lines(events):\n        print(line)\n    return state\n\ndef steps(state):\n    while True:\n        answer = trim(input(\"how many steps (1 to \" + str(most_steps) + \")> \"))\n        if answer == \"\":\n            print(\"Cancelled.\")\n            return state\n        n = whole_number(answer)\n        if n >= 1 and n <= most_steps:\n            return advance(state, n)\n        print(\"Type a number from 1 to \" + str(most_steps) + \".\")\n\ndef run_to_end(state):\n    if not lift.busy(state):\n        print(\"Nobody is waiting or riding.\")\n        return state\n    events = []\n    while lift.busy(state):\n        r = lift.step(state, False)\n        state = r[0]\n        events = events + [r[1]]\n    for line in report.event_lines(events):\n        print(line)\n    print(\"Everyone has arrived; the lift is free at floor \" + str(state[1]) + \".\")\n    return state\n\nprint(\"== Elevator ==\")\nprint(\"One lift, floors 0 to 9. A step of time is one floor of travel or one stop\")\nprint(\"with the doors open; finish runs on until everyone has arrived.\")\nstate = [0, lift.lowest, 0, [], [], []]\ncalls = []\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\")\n    choice = trim(input(\"t=\" + str(state[0]) + \" choice> \"))\n    if choice == \"1\":\n        r = call(state, calls)\n        state = r[0]\n        calls = r[1]\n    elif choice == \"2\":\n        state = steps(state)\n    elif choice == \"3\":\n        state = run_to_end(state)\n    elif choice == \"4\":\n        report.building(state)\n    elif choice == \"5\":\n        report.statistics(state)\n    elif choice == \"6\":\n        report.compare(calls)\n    elif choice == \"7\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 7.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "lift.eml",
      "eml": "# P033 elevator - the lift and its rules. Floors 0 to 9. One step of time is\n# one floor of travel, or one stop with the doors open.\n#\n# A passenger is [number, from, to, call time, boarding time, arrival time];\n# the last two are -1 until they happen. The state of a run is\n# [time, floor, direction, waiting, riding, arrived], where the direction is\n# 1 going up, -1 going down and 0 idle, and waiting is kept in call order.\n\n0 => lowest\n9 => highest\n\ndef sign(x):\n    if x > 0:\n        return 1\n    if x < 0:\n        return -1\n    return 0\n\ndef way(p):\n    # 1 for a passenger who wants to go up, -1 for one going down.\n    return sign(p[2] - p[1])\n\ndef reason(floor, d, waiting, riding):\n    # Is there a reason to go on in direction d from this floor: a rider going\n    # further that way, a call from a floor further that way, or someone\n    # waiting right here who wants to go that way?\n    for p in riding:\n        if sign(p[2] - floor) == d:\n            return True\n    for p in waiting:\n        if sign(p[1] - floor) == d:\n            return True\n        if p[1] == floor and way(p) == d:\n            return True\n    return False\n\ndef step(state, one_at_a_time):\n    # One step of time. Returns [the new state, what happened], where what\n    # happened is [kind, time, floor, direction, got out, got in] and kind is\n    # \"doors\", \"move\" or \"idle\".\n    state[0] => t\n    state[1] => floor\n    state[2] => d\n    state[3] => waiting\n    state[4] => riding\n    [] => out\n    [] => went_in\n    if one_at_a_time:\n        # One passenger at a time, in call order: fetch the first caller,\n        # take them where they are going, then the next.\n        if len(riding) > 0 and riding[0][2] == floor:\n            riding[0] => p\n            out + [[p[0], p[1], p[2], p[3], p[4], t]] => out\n            [] => riding\n        if len(riding) == 0 and len(waiting) > 0 and waiting[0][1] == floor:\n            waiting[0] => p\n            [[p[0], p[1], p[2], p[3], t, -1]] => went_in\n            went_in => riding\n            waiting[1:len(waiting)] => waiting\n        0 => d\n        if len(riding) > 0:\n            sign(riding[0][2] - floor) => d\n        elif len(waiting) > 0:\n            sign(waiting[0][1] - floor) => d\n    else:\n        # The direction rule: riders get out at their floor; the lift keeps\n        # its direction while there is a reason to go on, turns round when\n        # there is none that way but some the other way, and otherwise goes\n        # idle. An idle lift heads for the earliest call. Whoever waits here\n        # and wants to go the lift's way gets in.\n        [] => kept\n        for p in riding:\n            if p[2] == floor:\n                out + [[p[0], p[1], p[2], p[3], p[4], t]] => out\n            else:\n                kept + [p] => kept\n        kept => riding\n        if d != 0 and not reason(floor, d, waiting, riding):\n            if reason(floor, 0 - d, waiting, riding):\n                0 - d => d\n            else:\n                0 => d\n        if d == 0 and len(waiting) > 0:\n            waiting[0] => first\n            if first[1] == floor:\n                way(first) => d\n            else:\n                sign(first[1] - floor) => d\n        [] => left\n        for p in waiting:\n            if p[1] == floor and way(p) == d:\n                went_in + [[p[0], p[1], p[2], p[3], t, -1]] => went_in\n            else:\n                left + [p] => left\n        left => waiting\n        riding + went_in => riding\n    if len(out) > 0 or len(went_in) > 0:\n        \"doors\" => kind\n    elif d != 0:\n        floor + d => floor\n        \"move\" => kind\n    else:\n        \"idle\" => kind\n    return [[t + 1, floor, d, waiting, riding, state[5] + out], [kind, t, floor, d, out, went_in]]\n\ndef busy(state):\n    return len(state[3]) > 0 or len(state[4]) > 0\n\ndef run_all(calls, one_at_a_time):\n    # Runs the calls [number, from, to, call time] (in call order) from time 0\n    # with an idle lift at the ground floor, until everyone has arrived.\n    # Returns the final state.\n    [0, lowest, 0, [], [], []] => state\n    0 => k\n    while k < len(calls) or busy(state):\n        while k < len(calls) and calls[k][3] == state[0]:\n            calls[k] => c\n            [state[0], state[1], state[2], state[3] + [[c[0], c[1], c[2], c[3], -1, -1]], state[4], state[5]] => state\n            k + 1 => k\n        step(state, one_at_a_time)[0] => state\n    return state\n",
      "python": "lowest = 0\nhighest = 9\n\ndef sign(x):\n    if x > 0:\n        return 1\n    if x < 0:\n        return -1\n    return 0\n\ndef way(p):\n    return sign(p[2] - p[1])\n\ndef reason(floor, d, waiting, riding):\n    for p in riding:\n        if sign(p[2] - floor) == d:\n            return True\n    for p in waiting:\n        if sign(p[1] - floor) == d:\n            return True\n        if p[1] == floor and way(p) == d:\n            return True\n    return False\n\ndef step(state, one_at_a_time):\n    t = state[0]\n    floor = state[1]\n    d = state[2]\n    waiting = state[3]\n    riding = state[4]\n    out = []\n    went_in = []\n    if one_at_a_time:\n        if len(riding) > 0 and riding[0][2] == floor:\n            p = riding[0]\n            out = out + [[p[0], p[1], p[2], p[3], p[4], t]]\n            riding = []\n        if len(riding) == 0 and len(waiting) > 0 and waiting[0][1] == floor:\n            p = waiting[0]\n            went_in = [[p[0], p[1], p[2], p[3], t, -1]]\n            riding = went_in\n            waiting = waiting[1:len(waiting)]\n        d = 0\n        if len(riding) > 0:\n            d = sign(riding[0][2] - floor)\n        elif len(waiting) > 0:\n            d = sign(waiting[0][1] - floor)\n    else:\n        kept = []\n        for p in riding:\n            if p[2] == floor:\n                out = out + [[p[0], p[1], p[2], p[3], p[4], t]]\n            else:\n                kept = kept + [p]\n        riding = kept\n        if d != 0 and not reason(floor, d, waiting, riding):\n            if reason(floor, 0 - d, waiting, riding):\n                d = 0 - d\n            else:\n                d = 0\n        if d == 0 and len(waiting) > 0:\n            first = waiting[0]\n            if first[1] == floor:\n                d = way(first)\n            else:\n                d = sign(first[1] - floor)\n        left = []\n        for p in waiting:\n            if p[1] == floor and way(p) == d:\n                went_in = went_in + [[p[0], p[1], p[2], p[3], t, -1]]\n            else:\n                left = left + [p]\n        waiting = left\n        riding = riding + went_in\n    if len(out) > 0 or len(went_in) > 0:\n        kind = \"doors\"\n    elif d != 0:\n        floor = floor + d\n        kind = \"move\"\n    else:\n        kind = \"idle\"\n    return [[t + 1, floor, d, waiting, riding, state[5] + out], [kind, t, floor, d, out, went_in]]\n\ndef busy(state):\n    return len(state[3]) > 0 or len(state[4]) > 0\n\ndef run_all(calls, one_at_a_time):\n    state = [0, lowest, 0, [], [], []]\n    k = 0\n    while k < len(calls) or busy(state):\n        while k < len(calls) and calls[k][3] == state[0]:\n            c = calls[k]\n            state = [state[0], state[1], state[2], state[3] + [[c[0], c[1], c[2], c[3], -1, -1]], state[4], state[5]]\n            k = k + 1\n        state = step(state, one_at_a_time)[0]\n    return state\n"
    },
    {
      "name": "report.eml",
      "eml": "# P033 elevator - what the screen shows: the steps, the building, the\n# statistics and the comparison. Times are whole steps; averages are worked\n# out in whole numbers and shown to one decimal place, halves rounded up.\nimport lift\n\ndef quotient(a, b):\n    return int((a - a % b) / b)\n\ndef average(total, n):\n    # total / n to one decimal place: 11 / 5 is \"2.2\", 9 / 4 is \"2.3\".\n    quotient(total * 20 + n, n * 2) => tenths\n    return str(quotient(tenths, 10)) + \".\" + str(tenths % 10)\n\ndef going(d):\n    if d == 1:\n        return \"going up\"\n    if d == -1:\n        return \"going down\"\n    return \"idle\"\n\ndef who(p):\n    return \"P\" + str(p[0])\n\ndef doors_line(e):\n    (\"t=\" + str(e[1]) + \"  floor \" + str(e[2]) + \" -\") => line\n    if len(e[4]) > 0:\n        \"\" => part\n        for p in e[4]:\n            if part != \"\":\n                part + \", \" => part\n            part + who(p) + \" (waited \" + str(p[4] - p[3]) + \", rode \" + str(p[5] - p[4]) + \")\" => part\n        line + \" out: \" + part => line\n        if len(e[5]) > 0:\n            line + \";\" => line\n    if len(e[5]) > 0:\n        \"\" => part\n        for p in e[5]:\n            if part != \"\":\n                part + \", \" => part\n            part + who(p) + \" (to \" + str(p[2]) + \")\" => part\n        line + \" in: \" + part => line\n    return line\n\ndef event_lines(events):\n    # One line for each stop; a run of moves the same way, or of idle steps,\n    # becomes one line: \"t=3-6  up to 7\".\n    [] => lines\n    0 => i\n    while i < len(events):\n        events[i] => e\n        if e[0] == \"doors\":\n            lines + [doors_line(e)] => lines\n            i + 1 => i\n        else:\n            i => j\n            while j + 1 < len(events) and events[j + 1][0] == e[0] and events[j + 1][3] == e[3]:\n                j + 1 => j\n            \"t=\" + str(e[1]) => when\n            if j > i:\n                when + \"-\" + str(events[j][1]) => when\n            if e[0] == \"idle\":\n                lines + [when + \"  idle at floor \" + str(e[2])] => lines\n            elif e[3] == 1:\n                lines + [when + \"  up to \" + str(events[j][2])] => lines\n            else:\n                lines + [when + \"  down to \" + str(events[j][2])] => lines\n            j + 1 => i\n    return lines\n\ndef trip(p):\n    return who(p) + \" -> \" + str(p[2])\n\ndef building(state):\n    lift.highest => f\n    while f >= lift.lowest:\n        \"    \" => car\n        if state[1] == f:\n            str(len(state[4])) => n\n            if len(n) < 2:\n                \" \" + n => n\n            \"[\" + n + \"]\" => car\n        \"\" => calls\n        for p in state[3]:\n            if p[1] == f:\n                if calls != \"\":\n                    calls + \", \" => calls\n                calls + trip(p) => calls\n        (\" \" + str(f) + \" |\" + car + \"|\") => line\n        if calls != \"\":\n            line + \" \" + calls => line\n        line ^0\n        f - 1 => f\n    (\"t=\" + str(state[0]) + \": the lift is at floor \" + str(state[1]) + \", \" + going(state[2])) => line\n    if len(state[4]) == 0:\n        line + \", empty.\" => line\n    else:\n        \"\" => riders\n        for p in state[4]:\n            if riders != \"\":\n                riders + \", \" => riders\n            riders + trip(p) => riders\n        line + \", with \" + riders + \".\" => line\n    line ^0\n\ndef longest(ps, k, j):\n    # The passenger with the longest time from field k to field j: the\n    # lowest number on a tie. Returns [time, passenger].\n    -1 => best\n    [] => whose\n    for p in ps:\n        p[j] - p[k] => v\n        if v > best or (v == best and p[0] < whose[0]):\n            v => best\n            p => whose\n    return [best, whose]\n\ndef total(ps, k, j):\n    0 => s\n    for p in ps:\n        s + p[j] - p[k] => s\n    return s\n\ndef statistics(state):\n    state[5] => arrived\n    arrived + state[4] => picked\n    len(arrived) + len(state[4]) + len(state[3]) => calls\n    if calls == 0:\n        \"Nobody has called the lift yet.\" ^0\n        return 0\n    (\"Calls: \" + str(calls) + \". Arrived \" + str(len(arrived)) + \", riding \" + str(len(state[4])) + \", waiting \" + str(len(state[3])) + \".\") ^0\n    if len(picked) == 0:\n        \"Nobody has been picked up yet.\" ^0\n    else:\n        longest(picked, 3, 4) => w\n        (\"Wait for the lift: average \" + average(total(picked, 3, 4), len(picked)) + \", longest \" + str(w[0]) + \" (\" + who(w[1]) + \"), over the \" + str(len(picked)) + \" picked up.\") ^0\n    if len(arrived) > 0:\n        longest(arrived, 3, 5) => a\n        (\"Call to arrival: average \" + average(total(arrived, 3, 5), len(arrived)) + \", longest \" + str(a[0]) + \" (\" + who(a[1]) + \"), over the \" + str(len(arrived)) + \" arrived.\") ^0\n    for p in state[3]:\n        (who(p) + \" has waited \" + str(state[0] - p[3]) + \" so far at floor \" + str(p[1]) + \".\") ^0\n    return 0\n\ndef right(s, width):\n    while len(s) < width:\n        \" \" + s => s\n    return s\n\ndef row(label, final):\n    final[5] => ps\n    0 => last\n    for p in ps:\n        if p[5] > last:\n            p[5] => last\n    (label + right(average(total(ps, 3, 4), len(ps)), 14) + right(str(longest(ps, 3, 4)[0]), 14) + right(average(total(ps, 3, 5), len(ps)), 14) + right(\"t=\" + str(last), 14)) ^0\n\ndef compare(calls):\n    # The same calls at the same times, run from t=0 by each kind of lift.\n    if len(calls) == 0:\n        \"Nobody has called the lift yet.\" ^0\n        return 0\n    \"call\" => what\n    if len(calls) > 1:\n        \"calls\" => what\n    (\"The \" + str(len(calls)) + \" \" + what + \" again from t=0 (trip = from the call to arrival):\") ^0\n    \"                  average wait  longest wait  average trip  last arrival\" ^0\n    row(\"  direction rule\", lift.run_all(calls, False))\n    row(\"  one at a time \", lift.run_all(calls, True))\n    return 0\n",
      "python": "import lift\n\ndef quotient(a, b):\n    return int((a - a % b) / b)\n\ndef average(total, n):\n    tenths = quotient(total * 20 + n, n * 2)\n    return str(quotient(tenths, 10)) + \".\" + str(tenths % 10)\n\ndef going(d):\n    if d == 1:\n        return \"going up\"\n    if d == -1:\n        return \"going down\"\n    return \"idle\"\n\ndef who(p):\n    return \"P\" + str(p[0])\n\ndef doors_line(e):\n    line = \"t=\" + str(e[1]) + \"  floor \" + str(e[2]) + \" -\"\n    if len(e[4]) > 0:\n        part = \"\"\n        for p in e[4]:\n            if part != \"\":\n                part = part + \", \"\n            part = part + who(p) + \" (waited \" + str(p[4] - p[3]) + \", rode \" + str(p[5] - p[4]) + \")\"\n        line = line + \" out: \" + part\n        if len(e[5]) > 0:\n            line = line + \";\"\n    if len(e[5]) > 0:\n        part = \"\"\n        for p in e[5]:\n            if part != \"\":\n                part = part + \", \"\n            part = part + who(p) + \" (to \" + str(p[2]) + \")\"\n        line = line + \" in: \" + part\n    return line\n\ndef event_lines(events):\n    lines = []\n    i = 0\n    while i < len(events):\n        e = events[i]\n        if e[0] == \"doors\":\n            lines = lines + [doors_line(e)]\n            i = i + 1\n        else:\n            j = i\n            while j + 1 < len(events) and events[j + 1][0] == e[0] and events[j + 1][3] == e[3]:\n                j = j + 1\n            when = \"t=\" + str(e[1])\n            if j > i:\n                when = when + \"-\" + str(events[j][1])\n            if e[0] == \"idle\":\n                lines = lines + [when + \"  idle at floor \" + str(e[2])]\n            elif e[3] == 1:\n                lines = lines + [when + \"  up to \" + str(events[j][2])]\n            else:\n                lines = lines + [when + \"  down to \" + str(events[j][2])]\n            i = j + 1\n    return lines\n\ndef trip(p):\n    return who(p) + \" -> \" + str(p[2])\n\ndef building(state):\n    f = lift.highest\n    while f >= lift.lowest:\n        car = \"    \"\n        if state[1] == f:\n            n = str(len(state[4]))\n            if len(n) < 2:\n                n = \" \" + n\n            car = \"[\" + n + \"]\"\n        calls = \"\"\n        for p in state[3]:\n            if p[1] == f:\n                if calls != \"\":\n                    calls = calls + \", \"\n                calls = calls + trip(p)\n        line = \" \" + str(f) + \" |\" + car + \"|\"\n        if calls != \"\":\n            line = line + \" \" + calls\n        print(line)\n        f = f - 1\n    line = \"t=\" + str(state[0]) + \": the lift is at floor \" + str(state[1]) + \", \" + going(state[2])\n    if len(state[4]) == 0:\n        line = line + \", empty.\"\n    else:\n        riders = \"\"\n        for p in state[4]:\n            if riders != \"\":\n                riders = riders + \", \"\n            riders = riders + trip(p)\n        line = line + \", with \" + riders + \".\"\n    print(line)\n\ndef longest(ps, k, j):\n    best = -1\n    whose = []\n    for p in ps:\n        v = p[j] - p[k]\n        if v > best or v == best and p[0] < whose[0]:\n            best = v\n            whose = p\n    return [best, whose]\n\ndef total(ps, k, j):\n    s = 0\n    for p in ps:\n        s = s + p[j] - p[k]\n    return s\n\ndef statistics(state):\n    arrived = state[5]\n    picked = arrived + state[4]\n    calls = len(arrived) + len(state[4]) + len(state[3])\n    if calls == 0:\n        print(\"Nobody has called the lift yet.\")\n        return 0\n    print(\"Calls: \" + str(calls) + \". Arrived \" + str(len(arrived)) + \", riding \" + str(len(state[4])) + \", waiting \" + str(len(state[3])) + \".\")\n    if len(picked) == 0:\n        print(\"Nobody has been picked up yet.\")\n    else:\n        w = longest(picked, 3, 4)\n        print(\"Wait for the lift: average \" + average(total(picked, 3, 4), len(picked)) + \", longest \" + str(w[0]) + \" (\" + who(w[1]) + \"), over the \" + str(len(picked)) + \" picked up.\")\n    if len(arrived) > 0:\n        a = longest(arrived, 3, 5)\n        print(\"Call to arrival: average \" + average(total(arrived, 3, 5), len(arrived)) + \", longest \" + str(a[0]) + \" (\" + who(a[1]) + \"), over the \" + str(len(arrived)) + \" arrived.\")\n    for p in state[3]:\n        print(who(p) + \" has waited \" + str(state[0] - p[3]) + \" so far at floor \" + str(p[1]) + \".\")\n    return 0\n\ndef right(s, width):\n    while len(s) < width:\n        s = \" \" + s\n    return s\n\ndef row(label, final):\n    ps = final[5]\n    last = 0\n    for p in ps:\n        if p[5] > last:\n            last = p[5]\n    print(label + right(average(total(ps, 3, 4), len(ps)), 14) + right(str(longest(ps, 3, 4)[0]), 14) + right(average(total(ps, 3, 5), len(ps)), 14) + right(\"t=\" + str(last), 14))\n\ndef compare(calls):\n    if len(calls) == 0:\n        print(\"Nobody has called the lift yet.\")\n        return 0\n    what = \"call\"\n    if len(calls) > 1:\n        what = \"calls\"\n    print(\"The \" + str(len(calls)) + \" \" + what + \" again from t=0 (trip = from the call to arrival):\")\n    print(\"                  average wait  longest wait  average trip  last arrival\")\n    row(\"  direction rule\", lift.run_all(calls, False))\n    row(\"  one at a time \", lift.run_all(calls, True))\n    return 0\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "0\nx\n5\n6\n3\n2\n0\n21\nabc\n3\n1\n10\nx\n\n1\n4\n4\n\n1\n4\n4\n9\n1\n0\n9\n1\n9\n0\n1\n2\n7\n1\n7\n2\n1\n5\n6\n1\n6\n5\n1\n1\n8\n1\n8\n1\n1\n3\n4\n1\n4\n3\n1\n0\n1\n1\n9\n8\n1\n2\n3\n1\n7\n6\n1\n5\n0\n1\n4\n9\n1\n6\n9\n1\n3\n0\n1\n8\n2\n1\n4\n2\n\n3\n5\n6\n7\n",
      "screen": "== Elevator ==\nOne lift, floors 0 to 9. A step of time is one floor of travel or one stop\nwith the doors open; finish runs on until everyone has arrived.\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=0 choice> 0\nPick a number from 1 to 7.\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=0 choice> x\nPick a number from 1 to 7.\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=0 choice> 5\nNobody has called the lift yet.\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=0 choice> 6\nNobody has called the lift yet.\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=0 choice> 3\nNobody is waiting or riding.\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=0 choice> 2\nhow many steps (1 to 20)> 0\nType a number from 1 to 20.\nhow many steps (1 to 20)> 21\nType a number from 1 to 20.\nhow many steps (1 to 20)> abc\nType a number from 1 to 20.\nhow many steps (1 to 20)> 3\nt=0-2  idle at floor 0\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=3 choice> 1\nfrom floor> 10\nType a floor from 0 to 9.\nfrom floor> x\nType a floor from 0 to 9.\nfrom floor> \nCancelled.\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=3 choice> 1\nfrom floor> 4\nto floor> 4\nThat is the floor they are on.\nto floor> \nCancelled.\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=3 choice> 1\nfrom floor> 4\nto floor> 4\nThat is the floor they are on.\nto floor> 9\nP1 waits at floor 4 to go up to 9 (t=3).\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=3 choice> 1\nfrom floor> 0\nto floor> 9\nP2 waits at floor 0 to go up to 9 (t=3).\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=3 choice> 1\nfrom floor> 9\nto floor> 0\nP3 waits at floor 9 to go down to 0 (t=3).\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=3 choice> 1\nfrom floor> 2\nto floor> 7\nP4 waits at floor 2 to go up to 7 (t=3).\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=3 choice> 1\nfrom floor> 7\nto floor> 2\nP5 waits at floor 7 to go down to 2 (t=3).\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=3 choice> 1\nfrom floor> 5\nto floor> 6\nP6 waits at floor 5 to go up to 6 (t=3).\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=3 choice> 1\nfrom floor> 6\nto floor> 5\nP7 waits at floor 6 to go down to 5 (t=3).\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=3 choice> 1\nfrom floor> 1\nto floor> 8\nP8 waits at floor 1 to go up to 8 (t=3).\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=3 choice> 1\nfrom floor> 8\nto floor> 1\nP9 waits at floor 8 to go down to 1 (t=3).\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=3 choice> 1\nfrom floor> 3\nto floor> 4\nP10 waits at floor 3 to go up to 4 (t=3).\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=3 choice> 1\nfrom floor> 4\nto floor> 3\nP11 waits at floor 4 to go down to 3 (t=3).\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=3 choice> 1\nfrom floor> 0\nto floor> 1\nP12 waits at floor 0 to go up to 1 (t=3).\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=3 choice> 1\nfrom floor> 9\nto floor> 8\nP13 waits at floor 9 to go down to 8 (t=3).\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=3 choice> 1\nfrom floor> 2\nto floor> 3\nP14 waits at floor 2 to go up to 3 (t=3).\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=3 choice> 1\nfrom floor> 7\nto floor> 6\nP15 waits at floor 7 to go down to 6 (t=3).\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=3 choice> 1\nfrom floor> 5\nto floor> 0\nP16 waits at floor 5 to go down to 0 (t=3).\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=3 choice> 1\nfrom floor> 4\nto floor> 9\nP17 waits at floor 4 to go up to 9 (t=3).\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=3 choice> 1\nfrom floor> 6\nto floor> 9\nP18 waits at floor 6 to go up to 9 (t=3).\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=3 choice> 1\nfrom floor> 3\nto floor> 0\nP19 waits at floor 3 to go down to 0 (t=3).\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=3 choice> 1\nfrom floor> 8\nto floor> 2\nP20 waits at floor 8 to go down to 2 (t=3).\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=3 choice> 1\nThat makes 20 calls, the most for one run.\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=3 choice> 4\n 9 |    | P3 -> 0, P13 -> 8\n 8 |    | P9 -> 1, P20 -> 2\n 7 |    | P5 -> 2, P15 -> 6\n 6 |    | P7 -> 5, P18 -> 9\n 5 |    | P6 -> 6, P16 -> 0\n 4 |    | P1 -> 9, P11 -> 3, P17 -> 9\n 3 |    | P10 -> 4, P19 -> 0\n 2 |    | P4 -> 7, P14 -> 3\n 1 |    | P8 -> 8\n 0 |[ 0]| P2 -> 9, P12 -> 1\nt=3: the lift is at floor 0, idle, empty.\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=3 choice> 2\nhow many steps (1 to 20)> \nCancelled.\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=3 choice> 3\nt=3  floor 0 - in: P2 (to 9), P12 (to 1)\nt=4  up to 1\nt=5  floor 1 - out: P12 (waited 0, rode 2); in: P8 (to 8)\nt=6  up to 2\nt=7  floor 2 - in: P4 (to 7), P14 (to 3)\nt=8  up to 3\nt=9  floor 3 - out: P14 (waited 4, rode 2); in: P10 (to 4)\nt=10  up to 4\nt=11  floor 4 - out: P10 (waited 6, rode 2); in: P1 (to 9), P17 (to 9)\nt=12  up to 5\nt=13  floor 5 - in: P6 (to 6)\nt=14  up to 6\nt=15  floor 6 - out: P6 (waited 10, rode 2); in: P18 (to 9)\nt=16  up to 7\nt=17  floor 7 - out: P4 (waited 4, rode 10)\nt=18  up to 8\nt=19  floor 8 - out: P8 (waited 2, rode 14)\nt=20  up to 9\nt=21  floor 9 - out: P2 (waited 0, rode 18), P1 (waited 8, rode 10), P17 (waited 8, rode 10), P18 (waited 12, rode 6); in: P3 (to 0), P13 (to 8)\nt=22  down to 8\nt=23  floor 8 - out: P13 (waited 18, rode 2); in: P9 (to 1), P20 (to 2)\nt=24  down to 7\nt=25  floor 7 - in: P5 (to 2), P15 (to 6)\nt=26  down to 6\nt=27  floor 6 - out: P15 (waited 22, rode 2); in: P7 (to 5)\nt=28  down to 5\nt=29  floor 5 - out: P7 (waited 24, rode 2); in: P16 (to 0)\nt=30  down to 4\nt=31  floor 4 - in: P11 (to 3)\nt=32  down to 3\nt=33  floor 3 - out: P11 (waited 28, rode 2); in: P19 (to 0)\nt=34  down to 2\nt=35  floor 2 - out: P20 (waited 20, rode 12), P5 (waited 22, rode 10)\nt=36  down to 1\nt=37  floor 1 - out: P9 (waited 20, rode 14)\nt=38  down to 0\nt=39  floor 0 - out: P3 (waited 18, rode 18), P16 (waited 26, rode 10), P19 (waited 30, rode 6)\nEveryone has arrived; the lift is free at floor 0.\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=40 choice> 5\nCalls: 20. Arrived 20, riding 0, waiting 0.\nWait for the lift: average 14.1, longest 30 (P19), over the 20 picked up.\nCall to arrival: average 21.8, longest 36 (P3), over the 20 arrived.\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=40 choice> 6\nThe 20 calls again from t=0 (trip = from the call to arrival):\n                  average wait  longest wait  average trip  last arrival\n  direction rule          14.1            30          21.8          t=39\n  one at a time           88.2           171          93.0         t=181\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=40 choice> 7\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "1\n0\n8\n1\n3\n6\n1\n5\n9\n1\n7\n2\n2\n4\n1\n1\n4\n4\n3\n5\n6\n7\n",
      "screen": "== Elevator ==\nOne lift, floors 0 to 9. A step of time is one floor of travel or one stop\nwith the doors open; finish runs on until everyone has arrived.\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=0 choice> 1\nfrom floor> 0\nto floor> 8\nP1 waits at floor 0 to go up to 8 (t=0).\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=0 choice> 1\nfrom floor> 3\nto floor> 6\nP2 waits at floor 3 to go up to 6 (t=0).\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=0 choice> 1\nfrom floor> 5\nto floor> 9\nP3 waits at floor 5 to go up to 9 (t=0).\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=0 choice> 1\nfrom floor> 7\nto floor> 2\nP4 waits at floor 7 to go down to 2 (t=0).\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=0 choice> 2\nhow many steps (1 to 20)> 4\nt=0  floor 0 - in: P1 (to 8)\nt=1-3  up to 3\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=4 choice> 1\nfrom floor> 1\nto floor> 4\nP5 waits at floor 1 to go up to 4 (t=4).\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=4 choice> 4\n 9 |    |\n 8 |    |\n 7 |    | P4 -> 2\n 6 |    |\n 5 |    | P3 -> 9\n 4 |    |\n 3 |[ 1]| P2 -> 6\n 2 |    |\n 1 |    | P5 -> 4\n 0 |    |\nt=4: the lift is at floor 3, going up, with P1 -> 8.\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=4 choice> 3\nt=4  floor 3 - in: P2 (to 6)\nt=5-6  up to 5\nt=7  floor 5 - in: P3 (to 9)\nt=8  up to 6\nt=9  floor 6 - out: P2 (waited 4, rode 5)\nt=10-11  up to 8\nt=12  floor 8 - out: P1 (waited 0, rode 12)\nt=13  up to 9\nt=14  floor 9 - out: P3 (waited 7, rode 7)\nt=15-16  down to 7\nt=17  floor 7 - in: P4 (to 2)\nt=18-22  down to 2\nt=23  floor 2 - out: P4 (waited 17, rode 6)\nt=24  down to 1\nt=25  floor 1 - in: P5 (to 4)\nt=26-28  up to 4\nt=29  floor 4 - out: P5 (waited 21, rode 4)\nEveryone has arrived; the lift is free at floor 4.\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=30 choice> 5\nCalls: 5. Arrived 5, riding 0, waiting 0.\nWait for the lift: average 9.8, longest 21 (P5), over the 5 picked up.\nCall to arrival: average 16.6, longest 25 (P5), over the 5 arrived.\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=30 choice> 6\nThe 5 calls again from t=0 (trip = from the call to arrival):\n                  average wait  longest wait  average trip  last arrival\n  direction rule           9.8            21          16.6          t=29\n  one at a time           19.6            33          25.2          t=41\n\n1) call  2) steps  3) finish  4) building  5) statistics  6) compare  7) quit\nt=30 choice> 7\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "elevator-simulator",
      "caseId": "052-elevator-simulator",
      "title": "Elevator simulator"
    }
  ],
  "updated": "2026-10-10"
}
