{
  "id": "P018",
  "slug": "parking-lot",
  "key": "P018-parking-lot",
  "title": "Parking lot",
  "summary": "Run an eight-space parking lot: cars come in and go out at typed times, each exit gets a ticket that shows how its fee was worked out - free up to 10 minutes, 3.00 for the first hour, 1.00 for each half hour after, at most 15.00 per 24 hours - and each day has a report of its takings, from a text menu.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P018 - Parking lot\n\nAn eight-space parking lot run from a text menu. Cars come in and go out at\ntimes the attendant types; each car takes the lowest free space. Each exit\ngets a ticket that shows the stay and how its fee was worked out, and each day\nhas a report of the cars that went out and the takings.\n\n- `main.eml` - the menu and its questions, the lot itself (a class with its\n  spaces), the tickets and the day report\n- `fees.eml` - the fee rules and the lines that explain a fee\n- `text.eml` - trimming, capitals, reading and writing times, stays, counts\n  and money\n\nThere is no clock: a time is typed as HH:MM and may not be earlier than the\nlast time typed that day, and \"next day\" starts the following day at 00:00,\nso a car can stay overnight or for days. A time is kept as minutes since day\n1, 00:00, so a stay is a subtraction.\n\nThe fee rules: a stay of 10 minutes or less is free. Otherwise every full 24\nhours costs the day's maximum, 15.00, and what is left costs 3.00 for the\nfirst hour (or part of it) and 1.00 for each half hour (or part of one)\nafter that - but never more than 15.00. So 2 h 35 min costs 7.00, 12 h 40 min\nreaches the 15.00 maximum, and from 12:00 one day to 14:30 the next costs\n15.00 for the full day and 6.00 for the 2 h 30 min after it. Money is whole\ncents throughout.\n\nWhat is checked: a plate has 2 to 10 letters, digits or dashes (typed in any\ncase, kept in capitals) and is not already parked; a car going out must be\nparked; a time is HH:MM from 00:00 to 23:59 and does not go back; a car cannot\ncome in when the lot is full. Anything else asks again; nothing cancels.\n\nSessions: `sessions/basic.in` parks four cars and takes them out - one free\nafter 7 minutes, one for 7.00, one that reaches the day's maximum - shows the\nspaces and the day's report, then lets one car stay overnight into day 2 and\ngoes out at 21.00; `sessions/bad-input.in` takes a car out of an empty lot,\ntypes plates that are empty, too short, too long or have a space, times in the\nwrong shape, out of range or earlier than the clock, parks a car twice, takes\nout a car that is not there, stays exactly 10 minutes (free), fills all eight\nspaces, tries a ninth car, and keeps one car until 07:59 the next day - 23 h\n59 min, which stops at the 15.00 maximum.\n\nBuilt on the verified corpus case `parking-lot-tracker` (a lot as a class\nwhose spaces are taken from the lowest free one, refusing a car when full).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P018 parking lot: an eight-space lot. Cars come in and go out at times the\n# attendant types; the clock only moves forward, and \"next day\" starts the\n# following day at 00:00, so a car can stay overnight or longer. Each exit\n# gets a ticket showing how its fee was worked out, and each day has a report\n# of its takings. A time is kept as minutes since day 1, 00:00.\nimport fees\nimport text\n\n8 => space_count\n\nclass ParkingLot:\n    # Spaces numbered from 1; each holds None or [plate, time in].\n    def __init__(self, size):\n        [] => self.spaces\n        for i in [1:size]:\n            self.spaces + [None] => self.spaces\n\n    def free_space(self):\n        # The lowest free space number, or 0 if the lot is full.\n        for i in [0:len(self.spaces) - 1]:\n            if self.spaces[i] == None:\n                return i + 1\n        return 0\n\n    def find(self, plate):\n        # The space number of plate, or 0 if it is not parked here.\n        for i in [0:len(self.spaces) - 1]:\n            if self.spaces[i] != None and self.spaces[i][0] == plate:\n                return i + 1\n        return 0\n\n    def taken(self):\n        0 => n\n        for s in self.spaces:\n            if s != None:\n                n + 1 => n\n        return n\n\n    def park(self, plate, t):\n        self.free_space() => n\n        if n == 0:\n            raise ValueError(\"the lot is full\")\n        [plate, t] => self.spaces[n - 1]\n        return n\n\n    def leave(self, n):\n        self.spaces[n - 1] => car\n        None => self.spaces[n - 1]\n        return car\n\ndef day_of(t):\n    return int((t - t % 1440) / 1440) + 1\n\ndef stamp(t):\n    return \"day \" + str(day_of(t)) + \" \" + text.clock(t % 1440)\n\ndef ask_plate():\n    # A plate in capitals, asked again until a good one is typed; \"\" cancels.\n    while True:\n        text.upper(text.trim(input(\"plate> \"))) => plate\n        if plate == \"\":\n            return \"\"\n        True => good\n        for c in plate:\n            if not (c in \"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-\"):\n                False => good\n        if len(plate) < 2 or len(plate) > 10:\n            \"A plate has 2 to 10 characters.\" ^0\n        elif not good:\n            \"A plate has only letters, digits and dashes.\" ^0\n        else:\n            return plate\n\ndef ask_time(now):\n    # A time today, not before now, as minutes since day 1; -1 cancels.\n    while True:\n        text.trim(input(\"time (HH:MM)> \")) => answer\n        if answer == \"\":\n            return 0 - 1\n        text.parse_time(answer) => m\n        (day_of(now) - 1) * 1440 + m => t\n        if m == 0 - 1:\n            \"Type the time as HH:MM, like 08:30, or nothing to cancel.\" ^0\n        elif t < now:\n            (\"It is already \" + text.clock(now % 1440) + \" on day \" + str(day_of(now)) + \"; times only move forward.\") ^0\n        else:\n            return t\n\nParkingLot(space_count) => lot\n0 => now\n[] => log\nTrue => running\nwhile running:\n    \"\" ^0\n    (\"== Parking lot == day \" + str(day_of(now)) + \", \" + text.clock(now % 1440)) ^0\n    \"1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\" ^0\n    text.trim(input(\"choice> \")) => choice\n    if choice == \"1\":\n        \"\" ^0\n        (\"-- spaces: \" + str(lot.taken()) + \" of \" + str(space_count) + \" taken --\") ^0\n        for i in [0:space_count - 1]:\n            lot.spaces[i] => car\n            if car == None:\n                (\"  \" + (\"%2d\" % (i + 1)) + \"  free\") ^0\n            else:\n                (\"  \" + (\"%2d\" % (i + 1)) + \"  \" + (\"%-12s\" % car[0]) + \"in since \" + stamp(car[1])) ^0\n    elif choice == \"2\":\n        if lot.free_space() == 0:\n            \"The lot is full.\" ^0\n        else:\n            ask_plate() => plate\n            0 - 1 => t\n            if plate != \"\" and lot.find(plate) > 0:\n                (plate + \" is already parked in space \" + str(lot.find(plate)) + \".\") ^0\n            else:\n                if plate != \"\":\n                    ask_time(now) => t\n                if t == 0 - 1:\n                    \"Cancelled.\" ^0\n                else:\n                    t => now\n                    lot.park(plate, t) => n\n                    (plate + \" parks in space \" + str(n) + \" at \" + stamp(t) + \".\") ^0\n    elif choice == \"3\":\n        if lot.taken() == 0:\n            \"No cars are parked.\" ^0\n        else:\n            ask_plate() => plate\n            0 - 1 => t\n            if plate != \"\" and lot.find(plate) == 0:\n                (\"No car \" + plate + \" is parked here.\") ^0\n            else:\n                if plate != \"\":\n                    ask_time(now) => t\n                if t == 0 - 1:\n                    \"Cancelled.\" ^0\n                else:\n                    t => now\n                    lot.find(plate) => n\n                    lot.leave(n) => car\n                    t - car[1] => minutes\n                    fees.fee(minutes) => cents\n                    log + [[plate, n, car[1], t, cents]] => log\n                    \"\" ^0\n                    (plate + \" leaves space \" + str(n) + \".\") ^0\n                    (\"  in    \" + stamp(car[1])) ^0\n                    (\"  out   \" + stamp(t)) ^0\n                    (\"  stay  \" + text.duration(minutes)) ^0\n                    for line in fees.explain(minutes):\n                        (\"  \" + line) ^0\n                    (\"  fee   \" + text.money(cents)) ^0\n    elif choice == \"4\":\n        day_of(now) * 1440 => now\n        (\"Day \" + str(day_of(now)) + \" begins; it is 00:00. \" + text.count(lot.taken(), \"car\") + \" still parked.\") ^0\n    elif choice == \"5\":\n        0 => takings\n        \"\" ^0\n        (\"-- day \" + str(day_of(now)) + \" --\") ^0\n        0 => outs\n        for e in log:\n            if day_of(e[3]) == day_of(now):\n                outs + 1 => outs\n                takings + e[4] => takings\n                text.clock(e[3] % 1440) => out_time\n                if day_of(e[2]) != day_of(e[3]):\n                    stamp(e[3]) => out_time\n                (\"  \" + (\"%-12s\" % e[0]) + (\"%-28s\" % (stamp(e[2]) + \" - \" + out_time)) + (\"%8s\" % text.money(e[4]))) ^0\n        if outs == 0:\n            \"  no cars went out today\" ^0\n        (\"  \" + text.count(outs, \"car\") + \" out, takings \" + text.money(takings) + \"; \" + str(lot.taken()) + \" still parked\") ^0\n    elif choice == \"6\":\n        False => running\n    else:\n        \"Pick a number from 1 to 6.\" ^0\n\"Bye.\" ^0\n",
      "python": "import fees\nimport text\nspace_count = 8\n\nclass ParkingLot:\n    def __init__(self, size):\n        self.spaces = []\n        for i in range(1, size+1):\n            self.spaces = self.spaces + [None]\n    def free_space(self):\n        for i in range(0, len(self.spaces)):\n            if self.spaces[i] == None:\n                return i + 1\n        return 0\n    def find(self, plate):\n        for i in range(0, len(self.spaces)):\n            if self.spaces[i] != None and self.spaces[i][0] == plate:\n                return i + 1\n        return 0\n    def taken(self):\n        n = 0\n        for s in self.spaces:\n            if s != None:\n                n = n + 1\n        return n\n    def park(self, plate, t):\n        n = self.free_space()\n        if n == 0:\n            raise ValueError(\"the lot is full\")\n        self.spaces[n - 1] = [plate, t]\n        return n\n    def leave(self, n):\n        car = self.spaces[n - 1]\n        self.spaces[n - 1] = None\n        return car\n\ndef day_of(t):\n    return int((t - t % 1440) / 1440) + 1\n\ndef stamp(t):\n    return \"day \" + str(day_of(t)) + \" \" + text.clock(t % 1440)\n\ndef ask_plate():\n    while True:\n        plate = text.upper(text.trim(input(\"plate> \")))\n        if plate == \"\":\n            return \"\"\n        good = True\n        for c in plate:\n            if not c in \"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-\":\n                good = False\n        if len(plate) < 2 or len(plate) > 10:\n            print(\"A plate has 2 to 10 characters.\")\n        elif not good:\n            print(\"A plate has only letters, digits and dashes.\")\n        else:\n            return plate\n\ndef ask_time(now):\n    while True:\n        answer = text.trim(input(\"time (HH:MM)> \"))\n        if answer == \"\":\n            return 0 - 1\n        m = text.parse_time(answer)\n        t = (day_of(now) - 1) * 1440 + m\n        if m == 0 - 1:\n            print(\"Type the time as HH:MM, like 08:30, or nothing to cancel.\")\n        elif t < now:\n            print(\"It is already \" + text.clock(now % 1440) + \" on day \" + str(day_of(now)) + \"; times only move forward.\")\n        else:\n            return t\n\nlot = ParkingLot(space_count)\nnow = 0\nlog = []\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"== Parking lot == day \" + str(day_of(now)) + \", \" + text.clock(now % 1440))\n    print(\"1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\")\n    choice = text.trim(input(\"choice> \"))\n    if choice == \"1\":\n        print(\"\")\n        print(\"-- spaces: \" + str(lot.taken()) + \" of \" + str(space_count) + \" taken --\")\n        for i in range(0, space_count):\n            car = lot.spaces[i]\n            if car == None:\n                print(\"  \" + \"%2d\" % (i + 1) + \"  free\")\n            else:\n                print(\"  \" + \"%2d\" % (i + 1) + \"  \" + \"%-12s\" % car[0] + \"in since \" + stamp(car[1]))\n    elif choice == \"2\":\n        if lot.free_space() == 0:\n            print(\"The lot is full.\")\n        else:\n            plate = ask_plate()\n            t = 0 - 1\n            if plate != \"\" and lot.find(plate) > 0:\n                print(plate + \" is already parked in space \" + str(lot.find(plate)) + \".\")\n            else:\n                if plate != \"\":\n                    t = ask_time(now)\n                if t == 0 - 1:\n                    print(\"Cancelled.\")\n                else:\n                    now = t\n                    n = lot.park(plate, t)\n                    print(plate + \" parks in space \" + str(n) + \" at \" + stamp(t) + \".\")\n    elif choice == \"3\":\n        if lot.taken() == 0:\n            print(\"No cars are parked.\")\n        else:\n            plate = ask_plate()\n            t = 0 - 1\n            if plate != \"\" and lot.find(plate) == 0:\n                print(\"No car \" + plate + \" is parked here.\")\n            else:\n                if plate != \"\":\n                    t = ask_time(now)\n                if t == 0 - 1:\n                    print(\"Cancelled.\")\n                else:\n                    now = t\n                    n = lot.find(plate)\n                    car = lot.leave(n)\n                    minutes = t - car[1]\n                    cents = fees.fee(minutes)\n                    log = log + [[plate, n, car[1], t, cents]]\n                    print(\"\")\n                    print(plate + \" leaves space \" + str(n) + \".\")\n                    print(\"  in    \" + stamp(car[1]))\n                    print(\"  out   \" + stamp(t))\n                    print(\"  stay  \" + text.duration(minutes))\n                    for line in fees.explain(minutes):\n                        print(\"  \" + line)\n                    print(\"  fee   \" + text.money(cents))\n    elif choice == \"4\":\n        now = day_of(now) * 1440\n        print(\"Day \" + str(day_of(now)) + \" begins; it is 00:00. \" + text.count(lot.taken(), \"car\") + \" still parked.\")\n    elif choice == \"5\":\n        takings = 0\n        print(\"\")\n        print(\"-- day \" + str(day_of(now)) + \" --\")\n        outs = 0\n        for e in log:\n            if day_of(e[3]) == day_of(now):\n                outs = outs + 1\n                takings = takings + e[4]\n                out_time = text.clock(e[3] % 1440)\n                if day_of(e[2]) != day_of(e[3]):\n                    out_time = stamp(e[3])\n                print(\"  \" + \"%-12s\" % e[0] + \"%-28s\" % (stamp(e[2]) + \" - \" + out_time) + \"%8s\" % text.money(e[4]))\n        if outs == 0:\n            print(\"  no cars went out today\")\n        print(\"  \" + text.count(outs, \"car\") + \" out, takings \" + text.money(takings) + \"; \" + str(lot.taken()) + \" still parked\")\n    elif choice == \"6\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 6.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "fees.eml",
      "eml": "# P018 parking lot - the fee rules. A stay is a whole number of minutes and\n# money is whole cents. Up to 10 minutes is free; otherwise every full 24\n# hours costs the day's maximum, and what is left costs 3.00 for the first\n# hour (or part of it) and 1.00 for each half hour (or part of one) after\n# that, but never more than the day's maximum.\nimport text\n\n10 => grace_minutes\n300 => first_hour\n100 => per_half_hour\n1500 => day_cap\n1440 => day_minutes\n\ndef part_of_day(minutes):\n    # The fee for less than 24 hours: [cents, half hours after the first\n    # hour, whether the day's maximum was reached].\n    if minutes == 0:\n        return [0, 0, False]\n    0 => halves\n    if minutes > 60:\n        minutes - 60 => after\n        int((after + 29 - (after + 29) % 30) / 30) => halves\n    first_hour + halves * per_half_hour => cents\n    if cents > day_cap:\n        return [day_cap, halves, True]\n    return [cents, halves, False]\n\ndef fee(minutes):\n    # The fee for a stay of minutes, in cents.\n    if minutes <= grace_minutes:\n        return 0\n    int((minutes - minutes % day_minutes) / day_minutes) => days\n    return days * day_cap + part_of_day(minutes % day_minutes)[0]\n\ndef explain(minutes):\n    # The lines that show how fee(minutes) was worked out.\n    if minutes <= grace_minutes:\n        return [\"free: \" + str(grace_minutes) + \" minutes or less\"]\n    [] => out\n    int((minutes - minutes % day_minutes) / day_minutes) => days\n    if days > 0:\n        out + [text.count(days, \"full day\") + \" at \" + text.money(day_cap) + \": \" + text.money(days * day_cap)] => out\n    part_of_day(minutes % day_minutes) => p\n    if minutes % day_minutes > 0:\n        \"first hour \" + text.money(first_hour) => line\n        if p[1] > 0:\n            line + \", then \" + text.count(p[1], \"half hour\") + \" at \" + text.money(per_half_hour) + \": \" + text.money(p[1] * per_half_hour) => line\n        if p[2]:\n            line + \" - \" + text.money(day_cap) + \" at most: \" + text.money(day_cap) => line\n        out + [line] => out\n    return out\n",
      "python": "import text\ngrace_minutes = 10\nfirst_hour = 300\nper_half_hour = 100\nday_cap = 1500\nday_minutes = 1440\n\ndef part_of_day(minutes):\n    if minutes == 0:\n        return [0, 0, False]\n    halves = 0\n    if minutes > 60:\n        after = minutes - 60\n        halves = int((after + 29 - (after + 29) % 30) / 30)\n    cents = first_hour + halves * per_half_hour\n    if cents > day_cap:\n        return [day_cap, halves, True]\n    return [cents, halves, False]\n\ndef fee(minutes):\n    if minutes <= grace_minutes:\n        return 0\n    days = int((minutes - minutes % day_minutes) / day_minutes)\n    return days * day_cap + part_of_day(minutes % day_minutes)[0]\n\ndef explain(minutes):\n    if minutes <= grace_minutes:\n        return [\"free: \" + str(grace_minutes) + \" minutes or less\"]\n    out = []\n    days = int((minutes - minutes % day_minutes) / day_minutes)\n    if days > 0:\n        out = out + [text.count(days, \"full day\") + \" at \" + text.money(day_cap) + \": \" + text.money(days * day_cap)]\n    p = part_of_day(minutes % day_minutes)\n    if minutes % day_minutes > 0:\n        line = \"first hour \" + text.money(first_hour)\n        if p[1] > 0:\n            line = line + \", then \" + text.count(p[1], \"half hour\") + \" at \" + text.money(per_half_hour) + \": \" + text.money(p[1] * per_half_hour)\n        if p[2]:\n            line = line + \" - \" + text.money(day_cap) + \" at most: \" + text.money(day_cap)\n        out = out + [line]\n    return out\n"
    },
    {
      "name": "text.eml",
      "eml": "# P018 parking lot - reading what is typed and writing times and money. The\n# interpreter that checks every session does not run string methods yet, so\n# the text handling is written out here.\n\ndef trim(s):\n    # s without the spaces at either end.\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 upper(s):\n    # s with a-z turned into A-Z; everything else as it was.\n    \"abcdefghijklmnopqrstuvwxyz\" => lower_letters\n    \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\" => upper_letters\n    \"\" => out\n    for c in s:\n        c => d\n        for i in [0:25]:\n            if lower_letters[i] == c:\n                upper_letters[i] => d\n        out + d => out\n    return out\n\ndef clock(t):\n    # Minutes since midnight as HH:MM.\n    return (\"%02d\" % int((t - t % 60) / 60)) + \":\" + (\"%02d\" % (t % 60))\n\ndef parse_time(s):\n    # Minutes since midnight for HH:MM (00:00 to 23:59), or -1.\n    if len(s) != 5 or s[2] != \":\":\n        return 0 - 1\n    for i in [0, 1, 3, 4]:\n        if not (s[i] in \"0123456789\"):\n            return 0 - 1\n    int(s[0:2]) => h\n    int(s[3:5]) => m\n    if h > 23 or m > 59:\n        return 0 - 1\n    return h * 60 + m\n\ndef duration(minutes):\n    # A stay as \"2 h 35 min\", \"45 min\" or \"1 day 2 h 30 min\".\n    int((minutes - minutes % 1440) / 1440) => days\n    minutes % 1440 => rest\n    int((rest - rest % 60) / 60) => hours\n    rest % 60 => mins\n    \"\" => out\n    if days > 0:\n        count(days, \"day\") + \" \" => out\n    if hours > 0 or days > 0:\n        out + str(hours) + \" h \" => out\n    return out + str(mins) + \" min\"\n\ndef count(n, word):\n    if n == 1:\n        return \"1 \" + word\n    return str(n) + \" \" + word + \"s\"\n\ndef money(c):\n    # Whole cents as 12.50.\n    str(c) => s\n    while len(s) < 3:\n        \"0\" + s => s\n    return s[0:len(s) - 2] + \".\" + s[len(s) - 2:len(s)]\n",
      "python": "def 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 upper(s):\n    lower_letters = \"abcdefghijklmnopqrstuvwxyz\"\n    upper_letters = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n    out = \"\"\n    for c in s:\n        d = c\n        for i in range(0, 26):\n            if lower_letters[i] == c:\n                d = upper_letters[i]\n        out = out + d\n    return out\n\ndef clock(t):\n    return \"%02d\" % int((t - t % 60) / 60) + \":\" + \"%02d\" % (t % 60)\n\ndef parse_time(s):\n    if len(s) != 5 or s[2] != \":\":\n        return 0 - 1\n    for i in [0, 1, 3, 4]:\n        if not s[i] in \"0123456789\":\n            return 0 - 1\n    h = int(s[0:2])\n    m = int(s[3:5])\n    if h > 23 or m > 59:\n        return 0 - 1\n    return h * 60 + m\n\ndef duration(minutes):\n    days = int((minutes - minutes % 1440) / 1440)\n    rest = minutes % 1440\n    hours = int((rest - rest % 60) / 60)\n    mins = rest % 60\n    out = \"\"\n    if days > 0:\n        out = count(days, \"day\") + \" \"\n    if hours > 0 or days > 0:\n        out = out + str(hours) + \" h \"\n    return out + str(mins) + \" min\"\n\ndef count(n, word):\n    if n == 1:\n        return \"1 \" + word\n    return str(n) + \" \" + word + \"s\"\n\ndef money(c):\n    s = str(c)\n    while len(s) < 3:\n        s = \"0\" + s\n    return s[0:len(s) - 2] + \".\" + s[len(s) - 2:len(s)]\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "7\n3\n5\n2\n\n2\nA\nTOO-LONG-PLATE\nAB 12\nab-12\n8:30\n25:00\n07:60\n07:30\n2\nab-12\n3\nZZ-99\n3\nAB-12\n07:00\n07:40\n2\nC1\n08:00\n2\nC2\n08:00\n2\nC3\n08:05\n2\nC4\n08:10\n2\nC5\n08:15\n2\nC6\n08:20\n2\nC7\n08:25\n2\nC8\n08:30\n2\n1\n4\n3\nC1\n07:59\n3\nC2\n\n6\n",
      "screen": "\n== Parking lot == day 1, 00:00\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 7\nPick a number from 1 to 6.\n\n== Parking lot == day 1, 00:00\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 3\nNo cars are parked.\n\n== Parking lot == day 1, 00:00\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 5\n\n-- day 1 --\n  no cars went out today\n  0 cars out, takings 0.00; 0 still parked\n\n== Parking lot == day 1, 00:00\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 2\nplate> \nCancelled.\n\n== Parking lot == day 1, 00:00\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 2\nplate> A\nA plate has 2 to 10 characters.\nplate> TOO-LONG-PLATE\nA plate has 2 to 10 characters.\nplate> AB 12\nA plate has only letters, digits and dashes.\nplate> ab-12\ntime (HH:MM)> 8:30\nType the time as HH:MM, like 08:30, or nothing to cancel.\ntime (HH:MM)> 25:00\nType the time as HH:MM, like 08:30, or nothing to cancel.\ntime (HH:MM)> 07:60\nType the time as HH:MM, like 08:30, or nothing to cancel.\ntime (HH:MM)> 07:30\nAB-12 parks in space 1 at day 1 07:30.\n\n== Parking lot == day 1, 07:30\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 2\nplate> ab-12\nAB-12 is already parked in space 1.\n\n== Parking lot == day 1, 07:30\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 3\nplate> ZZ-99\nNo car ZZ-99 is parked here.\n\n== Parking lot == day 1, 07:30\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 3\nplate> AB-12\ntime (HH:MM)> 07:00\nIt is already 07:30 on day 1; times only move forward.\ntime (HH:MM)> 07:40\n\nAB-12 leaves space 1.\n  in    day 1 07:30\n  out   day 1 07:40\n  stay  10 min\n  free: 10 minutes or less\n  fee   0.00\n\n== Parking lot == day 1, 07:40\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 2\nplate> C1\ntime (HH:MM)> 08:00\nC1 parks in space 1 at day 1 08:00.\n\n== Parking lot == day 1, 08:00\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 2\nplate> C2\ntime (HH:MM)> 08:00\nC2 parks in space 2 at day 1 08:00.\n\n== Parking lot == day 1, 08:00\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 2\nplate> C3\ntime (HH:MM)> 08:05\nC3 parks in space 3 at day 1 08:05.\n\n== Parking lot == day 1, 08:05\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 2\nplate> C4\ntime (HH:MM)> 08:10\nC4 parks in space 4 at day 1 08:10.\n\n== Parking lot == day 1, 08:10\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 2\nplate> C5\ntime (HH:MM)> 08:15\nC5 parks in space 5 at day 1 08:15.\n\n== Parking lot == day 1, 08:15\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 2\nplate> C6\ntime (HH:MM)> 08:20\nC6 parks in space 6 at day 1 08:20.\n\n== Parking lot == day 1, 08:20\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 2\nplate> C7\ntime (HH:MM)> 08:25\nC7 parks in space 7 at day 1 08:25.\n\n== Parking lot == day 1, 08:25\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 2\nplate> C8\ntime (HH:MM)> 08:30\nC8 parks in space 8 at day 1 08:30.\n\n== Parking lot == day 1, 08:30\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 2\nThe lot is full.\n\n== Parking lot == day 1, 08:30\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 1\n\n-- spaces: 8 of 8 taken --\n   1  C1          in since day 1 08:00\n   2  C2          in since day 1 08:00\n   3  C3          in since day 1 08:05\n   4  C4          in since day 1 08:10\n   5  C5          in since day 1 08:15\n   6  C6          in since day 1 08:20\n   7  C7          in since day 1 08:25\n   8  C8          in since day 1 08:30\n\n== Parking lot == day 1, 08:30\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 4\nDay 2 begins; it is 00:00. 8 cars still parked.\n\n== Parking lot == day 2, 00:00\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 3\nplate> C1\ntime (HH:MM)> 07:59\n\nC1 leaves space 1.\n  in    day 1 08:00\n  out   day 2 07:59\n  stay  23 h 59 min\n  first hour 3.00, then 46 half hours at 1.00: 46.00 - 15.00 at most: 15.00\n  fee   15.00\n\n== Parking lot == day 2, 07:59\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 3\nplate> C2\ntime (HH:MM)> \nCancelled.\n\n== Parking lot == day 2, 07:59\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 6\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "1\n2\nABC-123\n08:30\n2\nXYZ-789\n08:45\n3\nXYZ-789\n08:52\n2\nKLM-456\n09:00\n3\nABC-123\n11:05\n2\ndef-321\n12:00\n1\n3\nKLM-456\n21:40\n5\n4\n3\nDEF-321\n14:30\n5\n6\n",
      "screen": "\n== Parking lot == day 1, 00:00\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 1\n\n-- spaces: 0 of 8 taken --\n   1  free\n   2  free\n   3  free\n   4  free\n   5  free\n   6  free\n   7  free\n   8  free\n\n== Parking lot == day 1, 00:00\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 2\nplate> ABC-123\ntime (HH:MM)> 08:30\nABC-123 parks in space 1 at day 1 08:30.\n\n== Parking lot == day 1, 08:30\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 2\nplate> XYZ-789\ntime (HH:MM)> 08:45\nXYZ-789 parks in space 2 at day 1 08:45.\n\n== Parking lot == day 1, 08:45\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 3\nplate> XYZ-789\ntime (HH:MM)> 08:52\n\nXYZ-789 leaves space 2.\n  in    day 1 08:45\n  out   day 1 08:52\n  stay  7 min\n  free: 10 minutes or less\n  fee   0.00\n\n== Parking lot == day 1, 08:52\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 2\nplate> KLM-456\ntime (HH:MM)> 09:00\nKLM-456 parks in space 2 at day 1 09:00.\n\n== Parking lot == day 1, 09:00\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 3\nplate> ABC-123\ntime (HH:MM)> 11:05\n\nABC-123 leaves space 1.\n  in    day 1 08:30\n  out   day 1 11:05\n  stay  2 h 35 min\n  first hour 3.00, then 4 half hours at 1.00: 4.00\n  fee   7.00\n\n== Parking lot == day 1, 11:05\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 2\nplate> def-321\ntime (HH:MM)> 12:00\nDEF-321 parks in space 1 at day 1 12:00.\n\n== Parking lot == day 1, 12:00\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 1\n\n-- spaces: 2 of 8 taken --\n   1  DEF-321     in since day 1 12:00\n   2  KLM-456     in since day 1 09:00\n   3  free\n   4  free\n   5  free\n   6  free\n   7  free\n   8  free\n\n== Parking lot == day 1, 12:00\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 3\nplate> KLM-456\ntime (HH:MM)> 21:40\n\nKLM-456 leaves space 2.\n  in    day 1 09:00\n  out   day 1 21:40\n  stay  12 h 40 min\n  first hour 3.00, then 24 half hours at 1.00: 24.00 - 15.00 at most: 15.00\n  fee   15.00\n\n== Parking lot == day 1, 21:40\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 5\n\n-- day 1 --\n  XYZ-789     day 1 08:45 - 08:52             0.00\n  ABC-123     day 1 08:30 - 11:05             7.00\n  KLM-456     day 1 09:00 - 21:40            15.00\n  3 cars out, takings 22.00; 1 still parked\n\n== Parking lot == day 1, 21:40\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 4\nDay 2 begins; it is 00:00. 1 car still parked.\n\n== Parking lot == day 2, 00:00\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 3\nplate> DEF-321\ntime (HH:MM)> 14:30\n\nDEF-321 leaves space 1.\n  in    day 1 12:00\n  out   day 2 14:30\n  stay  1 day 2 h 30 min\n  1 full day at 15.00: 15.00\n  first hour 3.00, then 3 half hours at 1.00: 3.00\n  fee   21.00\n\n== Parking lot == day 2, 14:30\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 5\n\n-- day 2 --\n  DEF-321     day 1 12:00 - day 2 14:30      21.00\n  1 car out, takings 21.00; 0 still parked\n\n== Parking lot == day 2, 14:30\n1) spaces  2) car in  3) car out  4) next day  5) day report  6) quit\nchoice> 6\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "parking-lot-tracker",
      "caseId": "032-parking-lot-tracker",
      "title": "Case corpus: a self-authored parking lot tracker"
    }
  ],
  "updated": "2026-10-03"
}
