{
  "id": "P030",
  "slug": "room-booking",
  "key": "P030-room-booking",
  "title": "Room booking",
  "summary": "Books three meeting rooms of 4, 8 and 12 seats over a working week. A booking that overlaps another in the same room is refused, naming the booking it clashes with and the rooms that are free instead - back-to-back bookings do not clash; each day has a schedule, free rooms can be looked up, and bookings can be cancelled.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P030 - Room booking\n\nBooks three meeting rooms - Oak (4 seats), Pine (8) and Cedar (12) - from\nMonday to Friday, 08:00 to 20:00. A booking that overlaps another in the same\nroom is refused: the program names the booking it clashes with and the rooms\nthat are free at that time for that many people. Each day has a schedule,\nfree rooms can be looked up, and bookings can be cancelled by number.\n\n- `main.eml` - the menu, the questions and their checks, and what the screen\n  shows\n- `bookings.eml` - the rooms, the overlap test, clashes, free rooms, a room's\n  day in order, and cancelling\n- `hours.eml` - weekdays, reading and writing times\n\nHow each part works:\n\n- A booking runs from its start up to, not including, its end. Two bookings\n  overlap when each starts before the other ends - the predicate of the\n  corpus case `interval-overlap-detector`. So 09:00-09:45 and 09:45-10:30 do\n  not clash (back-to-back is the most common case in a real calendar), while\n  11:59-12:01 clashes with 08:00-12:00.\n- Before a booking is made, the clashes are looked for in that room on that\n  day; if there are any, the booking is refused with each clashing booking\n  and the list of rooms with enough seats and no clash.\n- A day's schedule lists each room's bookings, earliest first, with numbers;\n  a room with none is free all day.\n- Booking numbers count up from 1 and are never reused, so a cancelled\n  number stays gone.\n\nWhat is checked: a room by name in any case; a weekday by its name or its\nfirst three letters; a start from 08:00 and an end after it, no later than\n20:00, at most 4 hours later; 1 to 12 people, and no more than the room's\nseats; a title of 1 to 30 characters; a booking number that exists (with or\nwithout `#`). An empty answer cancels.\n\nSessions: `sessions/basic.in` books a stand-up and a back-to-back design\nreview in Oak, is refused 10:00-11:00 in Oak (clash with the review; Pine and\nCedar are free), books Cedar and Pine in overlapping hours, shows Monday,\nfinds no room for 6 people on Monday 13:00-14:00 and two on Tuesday, cancels\nthe review and then books 10:00-11:00 in Oak; `sessions/bad-input.in` types\nmenu choices that are not 1 to 5, Saturday, a cancel with no bookings, a room\nthat does not exist, `funday`, starts before opening and at 25:00, ends\nbefore the start and after closing, too many people for Oak, 0 people, a\nbooking longer than 4 hours, 13 people, a title of 31 characters, a\ntwo-minute booking inside another, a booking number that is not a number and\none that does not exist.\n\nBuilt on the verified corpus case `interval-overlap-detector` (half-open\nintervals: touching is not overlapping).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P030 room booking: three meeting rooms over a working week. A booking that\n# overlaps another in the same room is refused, with the booking it clashes\n# with and the rooms that are free instead; each day has a schedule, and\n# bookings can be cancelled.\nimport bookings\nimport hours\n\n8 * 60 => opening\n20 * 60 => closing\n4 * 60 => longest\n\ndef listed(items):\n    \"\" => out\n    0 => i\n    while i < len(items):\n        if i > 0 and i == len(items) - 1:\n            out + \" and \" => out\n        elif i > 0:\n            out + \", \" => out\n        out + items[i] => out\n        i + 1 => i\n    return out\n\ndef head_count(n):\n    if n == 1:\n        return \"1 person\"\n    return str(n) + \" people\"\n\ndef whole_number(s):\n    # 1 to 3 digits, with an optional # in front; -1 for anything else.\n    hours.trim(s) => s\n    if s != \"\" and s[0] == \"#\":\n        s[1:len(s)] => s\n    if s == \"\" or len(s) > 3:\n        return 0 - 1\n    0 => n\n    for c in s:\n        if not (c in \"0123456789\"):\n            return 0 - 1\n        n * 10 + int(c) => n\n    return n\n\ndef ask_room():\n    while True:\n        hours.trim(input(\"room (Oak, Pine or Cedar)> \")) => answer\n        if answer == \"\":\n            return 0 - 1\n        bookings.room_of(answer) => r\n        if r >= 0:\n            return r\n        \"There are three rooms: Oak, Pine and Cedar.\" ^0\n\ndef ask_day():\n    while True:\n        input(\"day (Mon to Fri)> \") => answer\n        if hours.trim(answer) == \"\":\n            return 0 - 1\n        hours.day_of(answer) => d\n        if d >= 0:\n            return d\n        \"Type a weekday, Mon to Fri.\" ^0\n\ndef ask_times():\n    # [start, end] inside opening hours, at most 4 hours long; [] cancels.\n    0 - 1 => start\n    while start == 0 - 1:\n        input(\"from (HH:MM)> \") => answer\n        if hours.trim(answer) == \"\":\n            return []\n        hours.minute_of(answer) => start\n        if start == 0 - 1 or start < opening or start >= closing:\n            \"The rooms are open from 08:00 to 20:00; type a start such as 09:30.\" ^0\n            0 - 1 => start\n    0 - 1 => end\n    while end == 0 - 1:\n        input(\"to (HH:MM)> \") => answer\n        if hours.trim(answer) == \"\":\n            return []\n        hours.minute_of(answer) => end\n        if end == 0 - 1 or end <= start or end > closing:\n            (\"Type an end after \" + hours.clock(start) + \" and no later than 20:00.\") ^0\n            0 - 1 => end\n        elif end - start > longest:\n            \"A booking can last at most 4 hours.\" ^0\n            0 - 1 => end\n    return [start, end]\n\ndef ask_people(most):\n    while True:\n        input(\"people> \") => answer\n        if hours.trim(answer) == \"\":\n            return 0 - 1\n        whole_number(answer) => n\n        if n >= 1 and n <= most:\n            return n\n        (\"Type a number of people from 1 to \" + str(most) + \".\") ^0\n\ndef room_name(r):\n    return bookings.rooms[r] + \" (\" + str(bookings.seats[r]) + \" seats)\"\n\ndef describe(b):\n    return \"#\" + str(b[0]) + \": \" + bookings.rooms[b[1]] + \", \" + hours.days[b[2]] + \" \" + hours.span(b[3], b[4]) + \", \" + b[6]\n\ndef say_free(found, people):\n    [] => names\n    for r in found:\n        names + [room_name(r)] => names\n    if len(names) == 0:\n        (\"No room is free then for \" + head_count(people) + \".\") ^0\n    else:\n        (\"Free then for \" + head_count(people) + \": \" + listed(names) + \".\") ^0\n\ndef book(state):\n    # state is [bookings, next number]; returns the new state.\n    ask_room() => r\n    if r == 0 - 1:\n        \"Cancelled.\" ^0\n        return state\n    ask_day() => d\n    if d == 0 - 1:\n        \"Cancelled.\" ^0\n        return state\n    ask_times() => t\n    if len(t) == 0:\n        \"Cancelled.\" ^0\n        return state\n    ask_people(12) => people\n    while people > bookings.seats[r]:\n        (bookings.rooms[r] + \" has \" + str(bookings.seats[r]) + \" seats.\") ^0\n        ask_people(12) => people\n    if people == 0 - 1:\n        \"Cancelled.\" ^0\n        return state\n    bookings.clashes(state[0], r, d, t[0], t[1]) => taken\n    if len(taken) > 0:\n        (bookings.rooms[r] + \" is booked then:\") ^0\n        for b in taken:\n            (\"  #\" + str(b[0]) + \" \" + hours.span(b[3], b[4]) + \" \" + b[6]) ^0\n        say_free(bookings.free_rooms(state[0], d, t[0], t[1], people), people)\n        return state\n    \"\" => title\n    while title == \"\":\n        hours.trim(input(\"title> \")) => title\n        if title == \"\":\n            \"Cancelled.\" ^0\n            return state\n        if len(title) > 30:\n            \"A title can have at most 30 characters.\" ^0\n            \"\" => title\n    [state[1], r, d, t[0], t[1], people, title] => b\n    (\"Booked \" + describe(b) + \" (\" + head_count(people) + \").\") ^0\n    return [state[0] + [b], state[1] + 1]\n\ndef schedule(state):\n    ask_day() => d\n    if d == 0 - 1:\n        \"Cancelled.\" ^0\n        return\n    (hours.days[d] + \":\") ^0\n    0 => width\n    for r in [0:len(bookings.rooms) - 1]:\n        if len(room_name(r)) > width:\n            len(room_name(r)) => width\n    for r in [0:len(bookings.rooms) - 1]:\n        room_name(r) => label\n        while len(label) < width:\n            label + \" \" => label\n        bookings.in_order(state[0], r, d) => day_bookings\n        if len(day_bookings) == 0:\n            (\"  \" + label + \"  free all day\") ^0\n        else:\n            for b in day_bookings:\n                (\"  \" + label + \"  \" + hours.span(b[3], b[4]) + \"  #\" + str(b[0]) + \" \" + b[6] + \" (\" + head_count(b[5]) + \")\") ^0\n                \" \" * width => label\n\ndef cancel(state):\n    if len(state[0]) == 0:\n        \"There are no bookings.\" ^0\n        return state\n    while True:\n        input(\"booking number> \") => answer\n        if hours.trim(answer) == \"\":\n            \"Cancelled.\" ^0\n            return state\n        whole_number(answer) => n\n        None => found\n        for b in state[0]:\n            if b[0] == n:\n                b => found\n        if n == 0 - 1:\n            \"Type a booking number, such as 3.\" ^0\n        elif found == None:\n            (\"There is no booking #\" + str(n) + \".\") ^0\n        else:\n            (\"Cancelled \" + describe(found) + \".\") ^0\n            return [bookings.without(state[0], n), state[1]]\n\ndef free(state):\n    ask_day() => d\n    if d == 0 - 1:\n        \"Cancelled.\" ^0\n        return\n    ask_times() => t\n    if len(t) == 0:\n        \"Cancelled.\" ^0\n        return\n    ask_people(12) => people\n    if people == 0 - 1:\n        \"Cancelled.\" ^0\n        return\n    bookings.free_rooms(state[0], d, t[0], t[1], people) => found\n    [] => names\n    for r in found:\n        names + [room_name(r)] => names\n    \"on \" + hours.days[d] + \" \" + hours.span(t[0], t[1]) + \" for \" + head_count(people) => when\n    if len(names) == 0:\n        (\"No room is free \" + when + \".\") ^0\n    else:\n        (\"Free \" + when + \": \" + listed(names) + \".\") ^0\n\n\"== Room booking ==\" ^0\n\"Rooms: Oak (4 seats), Pine (8) and Cedar (12); Mon to Fri, 08:00 to 20:00.\" ^0\n[[], 1] => state\nTrue => running\nwhile running:\n    \"\" ^0\n    \"1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit\" ^0\n    hours.trim(input(\"choice> \")) => choice\n    if choice == \"1\":\n        book(state) => state\n    elif choice == \"2\":\n        schedule(state)\n    elif choice == \"3\":\n        cancel(state) => state\n    elif choice == \"4\":\n        free(state)\n    elif choice == \"5\":\n        False => running\n    else:\n        \"Pick a number from 1 to 5.\" ^0\n\"Bye.\" ^0\n",
      "python": "import bookings\nimport hours\nopening = 8 * 60\nclosing = 20 * 60\nlongest = 4 * 60\n\ndef listed(items):\n    out = \"\"\n    i = 0\n    while i < len(items):\n        if i > 0 and i == len(items) - 1:\n            out = out + \" and \"\n        elif i > 0:\n            out = out + \", \"\n        out = out + items[i]\n        i = i + 1\n    return out\n\ndef head_count(n):\n    if n == 1:\n        return \"1 person\"\n    return str(n) + \" people\"\n\ndef whole_number(s):\n    s = hours.trim(s)\n    if s != \"\" and s[0] == \"#\":\n        s = s[1:len(s)]\n    if s == \"\" or len(s) > 3:\n        return 0 - 1\n    n = 0\n    for c in s:\n        if not c in \"0123456789\":\n            return 0 - 1\n        n = n * 10 + int(c)\n    return n\n\ndef ask_room():\n    while True:\n        answer = hours.trim(input(\"room (Oak, Pine or Cedar)> \"))\n        if answer == \"\":\n            return 0 - 1\n        r = bookings.room_of(answer)\n        if r >= 0:\n            return r\n        print(\"There are three rooms: Oak, Pine and Cedar.\")\n\ndef ask_day():\n    while True:\n        answer = input(\"day (Mon to Fri)> \")\n        if hours.trim(answer) == \"\":\n            return 0 - 1\n        d = hours.day_of(answer)\n        if d >= 0:\n            return d\n        print(\"Type a weekday, Mon to Fri.\")\n\ndef ask_times():\n    start = 0 - 1\n    while start == 0 - 1:\n        answer = input(\"from (HH:MM)> \")\n        if hours.trim(answer) == \"\":\n            return []\n        start = hours.minute_of(answer)\n        if start == 0 - 1 or start < opening or start >= closing:\n            print(\"The rooms are open from 08:00 to 20:00; type a start such as 09:30.\")\n            start = 0 - 1\n    end = 0 - 1\n    while end == 0 - 1:\n        answer = input(\"to (HH:MM)> \")\n        if hours.trim(answer) == \"\":\n            return []\n        end = hours.minute_of(answer)\n        if end == 0 - 1 or end <= start or end > closing:\n            print(\"Type an end after \" + hours.clock(start) + \" and no later than 20:00.\")\n            end = 0 - 1\n        elif end - start > longest:\n            print(\"A booking can last at most 4 hours.\")\n            end = 0 - 1\n    return [start, end]\n\ndef ask_people(most):\n    while True:\n        answer = input(\"people> \")\n        if hours.trim(answer) == \"\":\n            return 0 - 1\n        n = whole_number(answer)\n        if n >= 1 and n <= most:\n            return n\n        print(\"Type a number of people from 1 to \" + str(most) + \".\")\n\ndef room_name(r):\n    return bookings.rooms[r] + \" (\" + str(bookings.seats[r]) + \" seats)\"\n\ndef describe(b):\n    return \"#\" + str(b[0]) + \": \" + bookings.rooms[b[1]] + \", \" + hours.days[b[2]] + \" \" + hours.span(b[3], b[4]) + \", \" + b[6]\n\ndef say_free(found, people):\n    names = []\n    for r in found:\n        names = names + [room_name(r)]\n    if len(names) == 0:\n        print(\"No room is free then for \" + head_count(people) + \".\")\n    else:\n        print(\"Free then for \" + head_count(people) + \": \" + listed(names) + \".\")\n\ndef book(state):\n    r = ask_room()\n    if r == 0 - 1:\n        print(\"Cancelled.\")\n        return state\n    d = ask_day()\n    if d == 0 - 1:\n        print(\"Cancelled.\")\n        return state\n    t = ask_times()\n    if len(t) == 0:\n        print(\"Cancelled.\")\n        return state\n    people = ask_people(12)\n    while people > bookings.seats[r]:\n        print(bookings.rooms[r] + \" has \" + str(bookings.seats[r]) + \" seats.\")\n        people = ask_people(12)\n    if people == 0 - 1:\n        print(\"Cancelled.\")\n        return state\n    taken = bookings.clashes(state[0], r, d, t[0], t[1])\n    if len(taken) > 0:\n        print(bookings.rooms[r] + \" is booked then:\")\n        for b in taken:\n            print(\"  #\" + str(b[0]) + \" \" + hours.span(b[3], b[4]) + \" \" + b[6])\n        say_free(bookings.free_rooms(state[0], d, t[0], t[1], people), people)\n        return state\n    title = \"\"\n    while title == \"\":\n        title = hours.trim(input(\"title> \"))\n        if title == \"\":\n            print(\"Cancelled.\")\n            return state\n        if len(title) > 30:\n            print(\"A title can have at most 30 characters.\")\n            title = \"\"\n    b = [state[1], r, d, t[0], t[1], people, title]\n    print(\"Booked \" + describe(b) + \" (\" + head_count(people) + \").\")\n    return [state[0] + [b], state[1] + 1]\n\ndef schedule(state):\n    d = ask_day()\n    if d == 0 - 1:\n        print(\"Cancelled.\")\n        return\n    print(hours.days[d] + \":\")\n    width = 0\n    for r in range(0, len(bookings.rooms)):\n        if len(room_name(r)) > width:\n            width = len(room_name(r))\n    for r in range(0, len(bookings.rooms)):\n        label = room_name(r)\n        while len(label) < width:\n            label = label + \" \"\n        day_bookings = bookings.in_order(state[0], r, d)\n        if len(day_bookings) == 0:\n            print(\"  \" + label + \"  free all day\")\n        else:\n            for b in day_bookings:\n                print(\"  \" + label + \"  \" + hours.span(b[3], b[4]) + \"  #\" + str(b[0]) + \" \" + b[6] + \" (\" + head_count(b[5]) + \")\")\n                label = \" \" * width\n\ndef cancel(state):\n    if len(state[0]) == 0:\n        print(\"There are no bookings.\")\n        return state\n    while True:\n        answer = input(\"booking number> \")\n        if hours.trim(answer) == \"\":\n            print(\"Cancelled.\")\n            return state\n        n = whole_number(answer)\n        found = None\n        for b in state[0]:\n            if b[0] == n:\n                found = b\n        if n == 0 - 1:\n            print(\"Type a booking number, such as 3.\")\n        elif found == None:\n            print(\"There is no booking #\" + str(n) + \".\")\n        else:\n            print(\"Cancelled \" + describe(found) + \".\")\n            return [bookings.without(state[0], n), state[1]]\n\ndef free(state):\n    d = ask_day()\n    if d == 0 - 1:\n        print(\"Cancelled.\")\n        return\n    t = ask_times()\n    if len(t) == 0:\n        print(\"Cancelled.\")\n        return\n    people = ask_people(12)\n    if people == 0 - 1:\n        print(\"Cancelled.\")\n        return\n    found = bookings.free_rooms(state[0], d, t[0], t[1], people)\n    names = []\n    for r in found:\n        names = names + [room_name(r)]\n    when = \"on \" + hours.days[d] + \" \" + hours.span(t[0], t[1]) + \" for \" + head_count(people)\n    if len(names) == 0:\n        print(\"No room is free \" + when + \".\")\n    else:\n        print(\"Free \" + when + \": \" + listed(names) + \".\")\n\nprint(\"== Room booking ==\")\nprint(\"Rooms: Oak (4 seats), Pine (8) and Cedar (12); Mon to Fri, 08:00 to 20:00.\")\nstate = [[], 1]\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit\")\n    choice = hours.trim(input(\"choice> \"))\n    if choice == \"1\":\n        state = book(state)\n    elif choice == \"2\":\n        schedule(state)\n    elif choice == \"3\":\n        state = cancel(state)\n    elif choice == \"4\":\n        free(state)\n    elif choice == \"5\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 5.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "bookings.eml",
      "eml": "# P030 room booking - rooms, bookings and conflicts. A booking is\n# [number, room, day, start, end, people, title]; room and day are indexes,\n# start and end are minutes after midnight.\n\n[\"Oak\", \"Pine\", \"Cedar\"] => rooms\n[4, 8, 12] => seats\n\ndef overlaps(a_start, a_end, b_start, b_end):\n    # Half-open times [start, end): a booking that ends at 10:00 and one that\n    # starts at 10:00 do not overlap - the predicate of the corpus case\n    # interval-overlap-detector.\n    return a_start < b_end and b_start < a_end\n\ndef clashes(bookings, room, day, start, end):\n    # The bookings of that room on that day that overlap [start, end), in\n    # the order they were made.\n    [] => out\n    for b in bookings:\n        if b[1] == room and b[2] == day and overlaps(start, end, b[3], b[4]):\n            out + [b] => out\n    return out\n\ndef free_rooms(bookings, day, start, end, people):\n    # The indexes of the rooms with enough seats and nothing booked in\n    # [start, end) that day.\n    [] => out\n    for r in [0:len(rooms) - 1]:\n        if seats[r] >= people and len(clashes(bookings, r, day, start, end)) == 0:\n            out + [r] => out\n    return out\n\ndef in_order(bookings, room, day):\n    # That room's bookings on that day, earliest first (insertion sort).\n    [] => out\n    for b in bookings:\n        if b[1] == room and b[2] == day:\n            len(out) => j\n            while j > 0 and out[j - 1][3] > b[3]:\n                j - 1 => j\n            out[0:j] + [b] + out[j:len(out)] => out\n    return out\n\ndef without(bookings, number):\n    # The bookings with that number left out.\n    [] => out\n    for b in bookings:\n        if b[0] != number:\n            out + [b] => out\n    return out\n\ndef room_of(name):\n    # The index of a room by name, in any case; -1 if there is none.\n    for r in [0:len(rooms) - 1]:\n        if lower(rooms[r]) == lower(name):\n            return r\n    return 0 - 1\n\ndef lower(s):\n    \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\" => upper\n    \"abcdefghijklmnopqrstuvwxyz\" => small\n    \"\" => out\n    for c in s:\n        0 => k\n        while k < 26 and upper[k] != c:\n            k + 1 => k\n        if k < 26:\n            out + small[k] => out\n        else:\n            out + c => out\n    return out\n",
      "python": "rooms = [\"Oak\", \"Pine\", \"Cedar\"]\nseats = [4, 8, 12]\n\ndef overlaps(a_start, a_end, b_start, b_end):\n    return a_start < b_end and b_start < a_end\n\ndef clashes(bookings, room, day, start, end):\n    out = []\n    for b in bookings:\n        if b[1] == room and b[2] == day and overlaps(start, end, b[3], b[4]):\n            out = out + [b]\n    return out\n\ndef free_rooms(bookings, day, start, end, people):\n    out = []\n    for r in range(0, len(rooms)):\n        if seats[r] >= people and len(clashes(bookings, r, day, start, end)) == 0:\n            out = out + [r]\n    return out\n\ndef in_order(bookings, room, day):\n    out = []\n    for b in bookings:\n        if b[1] == room and b[2] == day:\n            j = len(out)\n            while j > 0 and out[j - 1][3] > b[3]:\n                j = j - 1\n            out = out[0:j] + [b] + out[j:len(out)]\n    return out\n\ndef without(bookings, number):\n    out = []\n    for b in bookings:\n        if b[0] != number:\n            out = out + [b]\n    return out\n\ndef room_of(name):\n    for r in range(0, len(rooms)):\n        if lower(rooms[r]) == lower(name):\n            return r\n    return 0 - 1\n\ndef lower(s):\n    upper = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n    small = \"abcdefghijklmnopqrstuvwxyz\"\n    out = \"\"\n    for c in s:\n        k = 0\n        while k < 26 and upper[k] != c:\n            k = k + 1\n        if k < 26:\n            out = out + small[k]\n        else:\n            out = out + c\n    return out\n"
    },
    {
      "name": "hours.eml",
      "eml": "# P030 room booking - days and times. A time is kept as minutes after\n# midnight; the interpreter that checks every session does not run string\n# methods yet, so the text handling is written out here.\n\n[\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\"] => days\n\"0123456789\" => digits\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 lower(s):\n    \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\" => upper\n    \"abcdefghijklmnopqrstuvwxyz\" => small\n    \"\" => out\n    for c in s:\n        0 => k\n        while k < 26 and upper[k] != c:\n            k + 1 => k\n        if k < 26:\n            out + small[k] => out\n        else:\n            out + c => out\n    return out\n\ndef day_of(s):\n    # The index of a weekday from its name or its first three letters, in\n    # any case (\"mon\", \"Monday\"); -1 otherwise.\n    lower(trim(s)) => s\n    [\"monday\", \"tuesday\", \"wednesday\", \"thursday\", \"friday\"] => names\n    for i in [0:4]:\n        if s == names[i] or (len(s) == 3 and s == names[i][0:3]):\n            return i\n    return 0 - 1\n\ndef minute_of(s):\n    # Minutes after midnight for \"9:30\" or \"09:30\" (00:00 to 23:59), else -1.\n    trim(s) => s\n    0 => colon\n    while colon < len(s) and s[colon] != \":\":\n        colon + 1 => colon\n    if colon == 0 or colon > 2 or colon + 3 != len(s):\n        return 0 - 1\n    0 => h\n    for c in s[0:colon]:\n        if not (c in digits):\n            return 0 - 1\n        h * 10 + int(c) => h\n    if not (s[colon + 1] in digits and s[colon + 2] in digits):\n        return 0 - 1\n    int(s[colon + 1]) * 10 + int(s[colon + 2]) => m\n    if h > 23 or m > 59:\n        return 0 - 1\n    return h * 60 + m\n\ndef clock(minute):\n    # 570 as \"09:30\".\n    int((minute - minute % 60) / 60) => h\n    minute % 60 => m\n    \"\" => out\n    if h < 10:\n        \"0\" => out\n    out + str(h) + \":\" => out\n    if m < 10:\n        out + \"0\" => out\n    return out + str(m)\n\ndef span(start, end):\n    return clock(start) + \"-\" + clock(end)\n",
      "python": "days = [\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\"]\ndigits = \"0123456789\"\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 lower(s):\n    upper = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n    small = \"abcdefghijklmnopqrstuvwxyz\"\n    out = \"\"\n    for c in s:\n        k = 0\n        while k < 26 and upper[k] != c:\n            k = k + 1\n        if k < 26:\n            out = out + small[k]\n        else:\n            out = out + c\n    return out\n\ndef day_of(s):\n    s = lower(trim(s))\n    names = [\"monday\", \"tuesday\", \"wednesday\", \"thursday\", \"friday\"]\n    for i in range(0, 5):\n        if s == names[i] or len(s) == 3 and s == names[i][0:3]:\n            return i\n    return 0 - 1\n\ndef minute_of(s):\n    s = trim(s)\n    colon = 0\n    while colon < len(s) and s[colon] != \":\":\n        colon = colon + 1\n    if colon == 0 or colon > 2 or colon + 3 != len(s):\n        return 0 - 1\n    h = 0\n    for c in s[0:colon]:\n        if not c in digits:\n            return 0 - 1\n        h = h * 10 + int(c)\n    if not (s[colon + 1] in digits and s[colon + 2] in digits):\n        return 0 - 1\n    m = int(s[colon + 1]) * 10 + int(s[colon + 2])\n    if h > 23 or m > 59:\n        return 0 - 1\n    return h * 60 + m\n\ndef clock(minute):\n    h = int((minute - minute % 60) / 60)\n    m = minute % 60\n    out = \"\"\n    if h < 10:\n        out = \"0\"\n    out = out + str(h) + \":\"\n    if m < 10:\n        out = out + \"0\"\n    return out + str(m)\n\ndef span(start, end):\n    return clock(start) + \"-\" + clock(end)\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "0\nx\n2\nSat\n\n3\n1\nMaple\n\n1\nOak\nfunday\nTue\n7:30\n25:00\n19:45\n19:30\n20:15\n20:00\n5\n0\n4\n\n1\nPine\nWed\n08:00\n12:30\n12:00\n13\n8\nA title that is thirty-one long\nWorkshop\n1\npine\nwed\n11:59\n12:01\n2\n3\nabc\n#7\n\n3\n#1\n4\nWed\n08:00\n12:00\n12\n4\n\n5\n",
      "screen": "== Room booking ==\nRooms: Oak (4 seats), Pine (8) and Cedar (12); Mon to Fri, 08:00 to 20:00.\n\n1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit\nchoice> 0\nPick a number from 1 to 5.\n\n1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit\nchoice> x\nPick a number from 1 to 5.\n\n1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit\nchoice> 2\nday (Mon to Fri)> Sat\nType a weekday, Mon to Fri.\nday (Mon to Fri)> \nCancelled.\n\n1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit\nchoice> 3\nThere are no bookings.\n\n1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit\nchoice> 1\nroom (Oak, Pine or Cedar)> Maple\nThere are three rooms: Oak, Pine and Cedar.\nroom (Oak, Pine or Cedar)> \nCancelled.\n\n1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit\nchoice> 1\nroom (Oak, Pine or Cedar)> Oak\nday (Mon to Fri)> funday\nType a weekday, Mon to Fri.\nday (Mon to Fri)> Tue\nfrom (HH:MM)> 7:30\nThe rooms are open from 08:00 to 20:00; type a start such as 09:30.\nfrom (HH:MM)> 25:00\nThe rooms are open from 08:00 to 20:00; type a start such as 09:30.\nfrom (HH:MM)> 19:45\nto (HH:MM)> 19:30\nType an end after 19:45 and no later than 20:00.\nto (HH:MM)> 20:15\nType an end after 19:45 and no later than 20:00.\nto (HH:MM)> 20:00\npeople> 5\nOak has 4 seats.\npeople> 0\nType a number of people from 1 to 12.\npeople> 4\ntitle> \nCancelled.\n\n1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit\nchoice> 1\nroom (Oak, Pine or Cedar)> Pine\nday (Mon to Fri)> Wed\nfrom (HH:MM)> 08:00\nto (HH:MM)> 12:30\nA booking can last at most 4 hours.\nto (HH:MM)> 12:00\npeople> 13\nType a number of people from 1 to 12.\npeople> 8\ntitle> A title that is thirty-one long\nA title can have at most 30 characters.\ntitle> Workshop\nBooked #1: Pine, Wed 08:00-12:00, Workshop (8 people).\n\n1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit\nchoice> 1\nroom (Oak, Pine or Cedar)> pine\nday (Mon to Fri)> wed\nfrom (HH:MM)> 11:59\nto (HH:MM)> 12:01\npeople> 2\nPine is booked then:\n  #1 08:00-12:00 Workshop\nFree then for 2 people: Oak (4 seats) and Cedar (12 seats).\n\n1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit\nchoice> 3\nbooking number> abc\nType a booking number, such as 3.\nbooking number> #7\nThere is no booking #7.\nbooking number> \nCancelled.\n\n1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit\nchoice> 3\nbooking number> #1\nCancelled #1: Pine, Wed 08:00-12:00, Workshop.\n\n1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit\nchoice> 4\nday (Mon to Fri)> Wed\nfrom (HH:MM)> 08:00\nto (HH:MM)> 12:00\npeople> 12\nFree on Wed 08:00-12:00 for 12 people: Cedar (12 seats).\n\n1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit\nchoice> 4\nday (Mon to Fri)> \nCancelled.\n\n1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit\nchoice> 5\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "1\nOak\nmon\n09:00\n09:45\n3\nStandup\n1\nOak\nMonday\n09:45\n10:30\n4\nDesign review\n1\noak\nMon\n10:00\n11:00\n3\n1\nCedar\nMon\n13:00\n14:00\n12\nAll hands\n1\nPine\nMon\n13:30\n14:30\n6\nInterview\n2\nMon\n4\nMon\n13:00\n14:00\n6\n4\nTue\n14:00\n15:30\n6\n3\n2\n1\nOak\nMon\n10:00\n11:00\n3\nPlanning\n2\nmon\n5\n",
      "screen": "== Room booking ==\nRooms: Oak (4 seats), Pine (8) and Cedar (12); Mon to Fri, 08:00 to 20:00.\n\n1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit\nchoice> 1\nroom (Oak, Pine or Cedar)> Oak\nday (Mon to Fri)> mon\nfrom (HH:MM)> 09:00\nto (HH:MM)> 09:45\npeople> 3\ntitle> Standup\nBooked #1: Oak, Mon 09:00-09:45, Standup (3 people).\n\n1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit\nchoice> 1\nroom (Oak, Pine or Cedar)> Oak\nday (Mon to Fri)> Monday\nfrom (HH:MM)> 09:45\nto (HH:MM)> 10:30\npeople> 4\ntitle> Design review\nBooked #2: Oak, Mon 09:45-10:30, Design review (4 people).\n\n1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit\nchoice> 1\nroom (Oak, Pine or Cedar)> oak\nday (Mon to Fri)> Mon\nfrom (HH:MM)> 10:00\nto (HH:MM)> 11:00\npeople> 3\nOak is booked then:\n  #2 09:45-10:30 Design review\nFree then for 3 people: Pine (8 seats) and Cedar (12 seats).\n\n1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit\nchoice> 1\nroom (Oak, Pine or Cedar)> Cedar\nday (Mon to Fri)> Mon\nfrom (HH:MM)> 13:00\nto (HH:MM)> 14:00\npeople> 12\ntitle> All hands\nBooked #3: Cedar, Mon 13:00-14:00, All hands (12 people).\n\n1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit\nchoice> 1\nroom (Oak, Pine or Cedar)> Pine\nday (Mon to Fri)> Mon\nfrom (HH:MM)> 13:30\nto (HH:MM)> 14:30\npeople> 6\ntitle> Interview\nBooked #4: Pine, Mon 13:30-14:30, Interview (6 people).\n\n1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit\nchoice> 2\nday (Mon to Fri)> Mon\nMon:\n  Oak (4 seats)     09:00-09:45  #1 Standup (3 people)\n                    09:45-10:30  #2 Design review (4 people)\n  Pine (8 seats)    13:30-14:30  #4 Interview (6 people)\n  Cedar (12 seats)  13:00-14:00  #3 All hands (12 people)\n\n1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit\nchoice> 4\nday (Mon to Fri)> Mon\nfrom (HH:MM)> 13:00\nto (HH:MM)> 14:00\npeople> 6\nNo room is free on Mon 13:00-14:00 for 6 people.\n\n1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit\nchoice> 4\nday (Mon to Fri)> Tue\nfrom (HH:MM)> 14:00\nto (HH:MM)> 15:30\npeople> 6\nFree on Tue 14:00-15:30 for 6 people: Pine (8 seats) and Cedar (12 seats).\n\n1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit\nchoice> 3\nbooking number> 2\nCancelled #2: Oak, Mon 09:45-10:30, Design review.\n\n1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit\nchoice> 1\nroom (Oak, Pine or Cedar)> Oak\nday (Mon to Fri)> Mon\nfrom (HH:MM)> 10:00\nto (HH:MM)> 11:00\npeople> 3\ntitle> Planning\nBooked #5: Oak, Mon 10:00-11:00, Planning (3 people).\n\n1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit\nchoice> 2\nday (Mon to Fri)> mon\nMon:\n  Oak (4 seats)     09:00-09:45  #1 Standup (3 people)\n                    10:00-11:00  #5 Planning (3 people)\n  Pine (8 seats)    13:30-14:30  #4 Interview (6 people)\n  Cedar (12 seats)  13:00-14:00  #3 All hands (12 people)\n\n1) book a room  2) day schedule  3) cancel a booking  4) free rooms  5) quit\nchoice> 5\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "interval-overlap-detector",
      "caseId": "205-interval-overlap-detector",
      "title": "Back-to-back is not an overlap"
    }
  ],
  "updated": "2026-10-06"
}
