Project P032

Vending machine

Six products with stock and prices; coins of 5 to 200 cents go in, and change comes out with the fewest coins the machine actually holds - found by dynamic programming over a limited coin box, where taking the biggest coin first can fail. If it cannot make the change the sale is refused and the coins come back. Staff restock products and refill the coin box.

2 modules · 2 recorded sessionstext-menu UI in the terminalupdated 2026-10-06

Every screen below was recorded under CPython. When this page was built, the EML interpreter replayed each session from the same input and printed the same bytes.

About

Six products in slots A1 to B3, each with a price and up to 10 in stock. Coins of 5, 10, 20, 50, 100 and 200 cents go in one at a time; once enough is in, the product comes out with the change. The change is made from the coins the machine actually holds, with as few coins as possible - and when those coins cannot make it, the sale is refused and the coins put in come back. Staff can restock a product and refill the coin box.

  • main.eml - the menu, the products on screen, buying, restocking, refilling and the machine status
  • change.eml - the coin values, amounts shown as money, and the fewest coins that make an amount from a limited coin box

How each part works:

  • The coins put in join the box before the change is worked out, so the change can use them.
  • With an endless supply of every coin, taking the biggest coin that fits always gives the fewest coins for these six values. With the coins a machine really holds it can fail: for 0.60 from a box with one 50, one 5 and four 20s it takes the 50, then has only the one 5 for the 10 left and gets stuck, although 20 + 20 + 20 works. So the change is found with the table of the corpus case coin-change-dp - the fewest coins for every amount up to the change - built one coin value at a time, each used at most as many times as the box holds it. On a tie the bigger coins are used.
  • When no combination of the coins in the box makes the change, nothing is sold: the coins put in are handed back as they went in.
  • A sale takes one from the stock and adds the price to the takings; the status shows every coin in the box, the money it holds and what was sold.

What is checked: a slot from A1 to B3, in either case; only the six coin values (anything else is refused and asked for again); 0 or an empty answer while paying gives back the coins put in; at most 10 of a product and at most 50 of each coin; a count as a whole number in the room left. An empty answer cancels.

Sessions: sessions/basic.in buys a Cola with a 200 coin - the 0.60 change is 20 + 20 + 20, where the biggest coin first gets stuck - and a Gum with 50 and 20 (0.05 back); a Juice paid with two 100s is refused, because the box has no 5 or 10 for the 0.25 change, and the coins come back; after refilling 5s and 10s the same Juice goes through (20 + 5 back); then it pays a Water exactly, buys a Chocolate with 200 (20 + 20 + 5 back), shows the status and restocks the Cola. sessions/bad-input.in gives menu choices 0 and x, an unknown slot, coins of 25 and abc, 0 with nothing put in and with 20 put in, buys the last two Chocolates and asks for a third, restocks with 11 and 0 before 10, restocks a full slot, cancels a restock and a refill with an empty answer, refills with a coin of 3 and with 50 coins of 200 where 49 fit, then tries the full 200s once more.

Built on the verified corpus cases vending-machine-simulator (a product list, coins adding up to a balance, and a sale with change or a shortfall) and coin-change-dp (the fewest coins for an amount from a table, where the biggest coin first is not always right).

Recorded sessions

What the screen shows while someone uses the program. Each typed line appears after its prompt, the way a terminal shows it.

bad-input

interpreter: byte-equal
== Vending machine ==

  A1  Water        0.90  5 left
  A2  Cola         1.40  5 left
  A3  Juice        1.75  3 left
  B1  Chips        1.20  4 left
  B2  Chocolate    1.55  2 left
  B3  Gum          0.65  6 left
1) buy  2) restock a product  3) refill coins  4) machine status  5) quit
choice> 0
Pick a number from 1 to 5.

  A1  Water        0.90  5 left
  A2  Cola         1.40  5 left
  A3  Juice        1.75  3 left
  B1  Chips        1.20  4 left
  B2  Chocolate    1.55  2 left
  B3  Gum          0.65  6 left
1) buy  2) restock a product  3) refill coins  4) machine status  5) quit
choice> x
Pick a number from 1 to 5.

  A1  Water        0.90  5 left
  A2  Cola         1.40  5 left
  A3  Juice        1.75  3 left
  B1  Chips        1.20  4 left
  B2  Chocolate    1.55  2 left
  B3  Gum          0.65  6 left
1) buy  2) restock a product  3) refill coins  4) machine status  5) quit
choice> 1
slot> C1
Type a slot from A1 to B3.
slot> 
Cancelled.

  A1  Water        0.90  5 left
  A2  Cola         1.40  5 left
  A3  Juice        1.75  3 left
  B1  Chips        1.20  4 left
  B2  Chocolate    1.55  2 left
  B3  Gum          0.65  6 left
1) buy  2) restock a product  3) refill coins  4) machine status  5) quit
choice> 1
slot> B3
Gum costs 0.65. Insert coins: 5, 10, 20, 50, 100 or 200 cents; 0 gives them back.
coin> 25
This machine takes coins of 5, 10, 20, 50, 100 and 200 cents.
coin> abc
This machine takes coins of 5, 10, 20, 50, 100 and 200 cents.
coin> 0
Nothing was put in.

  A1  Water        0.90  5 left
  A2  Cola         1.40  5 left
  A3  Juice        1.75  3 left
  B1  Chips        1.20  4 left
  B2  Chocolate    1.55  2 left
  B3  Gum          0.65  6 left
1) buy  2) restock a product  3) refill coins  4) machine status  5) quit
choice> 1
slot> B3
Gum costs 0.65. Insert coins: 5, 10, 20, 50, 100 or 200 cents; 0 gives them back.
coin> 20
0.20 in, 0.45 to go.
coin> 0
Returned: 20.

  A1  Water        0.90  5 left
  A2  Cola         1.40  5 left
  A3  Juice        1.75  3 left
  B1  Chips        1.20  4 left
  B2  Chocolate    1.55  2 left
  B3  Gum          0.65  6 left
1) buy  2) restock a product  3) refill coins  4) machine status  5) quit
choice> 1
slot> B2
Chocolate costs 1.55. Insert coins: 5, 10, 20, 50, 100 or 200 cents; 0 gives them back.
coin> 100
1.00 in, 0.55 to go.
coin> 50
1.50 in, 0.05 to go.
coin> 5
1.55 in.
Here is your Chocolate.

  A1  Water        0.90  5 left
  A2  Cola         1.40  5 left
  A3  Juice        1.75  3 left
  B1  Chips        1.20  4 left
  B2  Chocolate    1.55  1 left
  B3  Gum          0.65  6 left
1) buy  2) restock a product  3) refill coins  4) machine status  5) quit
choice> 1
slot> B2
Chocolate costs 1.55. Insert coins: 5, 10, 20, 50, 100 or 200 cents; 0 gives them back.
coin> 200
2.00 in.
Here is your Chocolate, with 0.45 in change: 20 + 20 + 5.

  A1  Water        0.90  5 left
  A2  Cola         1.40  5 left
  A3  Juice        1.75  3 left
  B1  Chips        1.20  4 left
  B2  Chocolate    1.55  sold out
  B3  Gum          0.65  6 left
1) buy  2) restock a product  3) refill coins  4) machine status  5) quit
choice> 1
slot> B2
B2 Chocolate is sold out.

  A1  Water        0.90  5 left
  A2  Cola         1.40  5 left
  A3  Juice        1.75  3 left
  B1  Chips        1.20  4 left
  B2  Chocolate    1.55  sold out
  B3  Gum          0.65  6 left
1) buy  2) restock a product  3) refill coins  4) machine status  5) quit
choice> 2
slot> B2
add how many (1 to 10)> 11
Type a number from 1 to 10.
add how many (1 to 10)> 0
Type a number from 1 to 10.
add how many (1 to 10)> 10
B2 Chocolate: 10 now.

  A1  Water        0.90  5 left
  A2  Cola         1.40  5 left
  A3  Juice        1.75  3 left
  B1  Chips        1.20  4 left
  B2  Chocolate    1.55  10 left
  B3  Gum          0.65  6 left
1) buy  2) restock a product  3) refill coins  4) machine status  5) quit
choice> 2
slot> b2
B2 Chocolate is full (10).

  A1  Water        0.90  5 left
  A2  Cola         1.40  5 left
  A3  Juice        1.75  3 left
  B1  Chips        1.20  4 left
  B2  Chocolate    1.55  10 left
  B3  Gum          0.65  6 left
1) buy  2) restock a product  3) refill coins  4) machine status  5) quit
choice> 2
slot> A1
add how many (1 to 5)> 
Cancelled.

  A1  Water        0.90  5 left
  A2  Cola         1.40  5 left
  A3  Juice        1.75  3 left
  B1  Chips        1.20  4 left
  B2  Chocolate    1.55  10 left
  B3  Gum          0.65  6 left
1) buy  2) restock a product  3) refill coins  4) machine status  5) quit
choice> 3
coin (5, 10, 20, 50, 100 or 200)> 
Cancelled.

  A1  Water        0.90  5 left
  A2  Cola         1.40  5 left
  A3  Juice        1.75  3 left
  B1  Chips        1.20  4 left
  B2  Chocolate    1.55  10 left
  B3  Gum          0.65  6 left
1) buy  2) restock a product  3) refill coins  4) machine status  5) quit
choice> 3
coin (5, 10, 20, 50, 100 or 200)> 3
Type one of 5, 10, 20, 50, 100 and 200.
coin (5, 10, 20, 50, 100 or 200)> 200
add how many (1 to 49)> 50
Type a number from 1 to 49.
add how many (1 to 49)> 49
The box now has 50 of 200.

  A1  Water        0.90  5 left
  A2  Cola         1.40  5 left
  A3  Juice        1.75  3 left
  B1  Chips        1.20  4 left
  B2  Chocolate    1.55  10 left
  B3  Gum          0.65  6 left
1) buy  2) restock a product  3) refill coins  4) machine status  5) quit
choice> 3
coin (5, 10, 20, 50, 100 or 200)> 200
The box is full of 200s (50).

  A1  Water        0.90  5 left
  A2  Cola         1.40  5 left
  A3  Juice        1.75  3 left
  B1  Chips        1.20  4 left
  B2  Chocolate    1.55  10 left
  B3  Gum          0.65  6 left
1) buy  2) restock a product  3) refill coins  4) machine status  5) quit
choice> 4
Coin box: 5 x 1, 10 x 0, 20 x 2, 50 x 2, 100 x 3, 200 x 50 - 104.45 in all.
Sold 2 items for 3.10.

  A1  Water        0.90  5 left
  A2  Cola         1.40  5 left
  A3  Juice        1.75  3 left
  B1  Chips        1.20  4 left
  B2  Chocolate    1.55  10 left
  B3  Gum          0.65  6 left
1) buy  2) restock a product  3) refill coins  4) machine status  5) quit
choice> 5
Bye.
What was typed (45 lines)
0
x
1
C1

1
B3
25
abc
0
1
B3
20
0
1
B2
100
50
5
1
B2
200
1
B2
2
B2
11
0
10
2
b2
2
A1

3

3
3
200
50
49
3
200
4
5

basic

interpreter: byte-equal
== Vending machine ==

  A1  Water        0.90  5 left
  A2  Cola         1.40  5 left
  A3  Juice        1.75  3 left
  B1  Chips        1.20  4 left
  B2  Chocolate    1.55  2 left
  B3  Gum          0.65  6 left
1) buy  2) restock a product  3) refill coins  4) machine status  5) quit
choice> 1
slot> A2
Cola costs 1.40. Insert coins: 5, 10, 20, 50, 100 or 200 cents; 0 gives them back.
coin> 200
2.00 in.
Here is your Cola, with 0.60 in change: 20 + 20 + 20.

  A1  Water        0.90  5 left
  A2  Cola         1.40  4 left
  A3  Juice        1.75  3 left
  B1  Chips        1.20  4 left
  B2  Chocolate    1.55  2 left
  B3  Gum          0.65  6 left
1) buy  2) restock a product  3) refill coins  4) machine status  5) quit
choice> 1
slot> b3
Gum costs 0.65. Insert coins: 5, 10, 20, 50, 100 or 200 cents; 0 gives them back.
coin> 50
0.50 in, 0.15 to go.
coin> 20
0.70 in.
Here is your Gum, with 0.05 in change: 5.

  A1  Water        0.90  5 left
  A2  Cola         1.40  4 left
  A3  Juice        1.75  3 left
  B1  Chips        1.20  4 left
  B2  Chocolate    1.55  2 left
  B3  Gum          0.65  5 left
1) buy  2) restock a product  3) refill coins  4) machine status  5) quit
choice> 1
slot> A3
Juice costs 1.75. Insert coins: 5, 10, 20, 50, 100 or 200 cents; 0 gives them back.
coin> 100
1.00 in, 0.75 to go.
coin> 100
2.00 in.
Sorry - the machine cannot make 0.25 in change from the coins it holds. Returned: 100, 100.

  A1  Water        0.90  5 left
  A2  Cola         1.40  4 left
  A3  Juice        1.75  3 left
  B1  Chips        1.20  4 left
  B2  Chocolate    1.55  2 left
  B3  Gum          0.65  5 left
1) buy  2) restock a product  3) refill coins  4) machine status  5) quit
choice> 3
coin (5, 10, 20, 50, 100 or 200)> 5
add how many (1 to 50)> 10
The box now has 10 of 5.

  A1  Water        0.90  5 left
  A2  Cola         1.40  4 left
  A3  Juice        1.75  3 left
  B1  Chips        1.20  4 left
  B2  Chocolate    1.55  2 left
  B3  Gum          0.65  5 left
1) buy  2) restock a product  3) refill coins  4) machine status  5) quit
choice> 3
coin (5, 10, 20, 50, 100 or 200)> 10
add how many (1 to 50)> 20
The box now has 20 of 10.

  A1  Water        0.90  5 left
  A2  Cola         1.40  4 left
  A3  Juice        1.75  3 left
  B1  Chips        1.20  4 left
  B2  Chocolate    1.55  2 left
  B3  Gum          0.65  5 left
1) buy  2) restock a product  3) refill coins  4) machine status  5) quit
choice> 1
slot> A3
Juice costs 1.75. Insert coins: 5, 10, 20, 50, 100 or 200 cents; 0 gives them back.
coin> 100
1.00 in, 0.75 to go.
coin> 100
2.00 in.
Here is your Juice, with 0.25 in change: 20 + 5.

  A1  Water        0.90  5 left
  A2  Cola         1.40  4 left
  A3  Juice        1.75  2 left
  B1  Chips        1.20  4 left
  B2  Chocolate    1.55  2 left
  B3  Gum          0.65  5 left
1) buy  2) restock a product  3) refill coins  4) machine status  5) quit
choice> 1
slot> A1
Water costs 0.90. Insert coins: 5, 10, 20, 50, 100 or 200 cents; 0 gives them back.
coin> 50
0.50 in, 0.40 to go.
coin> 20
0.70 in, 0.20 to go.
coin> 20
0.90 in.
Here is your Water.

  A1  Water        0.90  4 left
  A2  Cola         1.40  4 left
  A3  Juice        1.75  2 left
  B1  Chips        1.20  4 left
  B2  Chocolate    1.55  2 left
  B3  Gum          0.65  5 left
1) buy  2) restock a product  3) refill coins  4) machine status  5) quit
choice> 1
slot> B2
Chocolate costs 1.55. Insert coins: 5, 10, 20, 50, 100 or 200 cents; 0 gives them back.
coin> 200
2.00 in.
Here is your Chocolate, with 0.45 in change: 20 + 20 + 5.

  A1  Water        0.90  4 left
  A2  Cola         1.40  4 left
  A3  Juice        1.75  2 left
  B1  Chips        1.20  4 left
  B2  Chocolate    1.55  1 left
  B3  Gum          0.65  5 left
1) buy  2) restock a product  3) refill coins  4) machine status  5) quit
choice> 4
Coin box: 5 x 8, 10 x 20, 20 x 1, 50 x 3, 100 x 4, 200 x 2 - 12.10 in all.
Sold 5 items for 6.25.

  A1  Water        0.90  4 left
  A2  Cola         1.40  4 left
  A3  Juice        1.75  2 left
  B1  Chips        1.20  4 left
  B2  Chocolate    1.55  1 left
  B3  Gum          0.65  5 left
1) buy  2) restock a product  3) refill coins  4) machine status  5) quit
choice> 2
slot> A2
add how many (1 to 6)> 5
A2 Cola: 9 now.

  A1  Water        0.90  4 left
  A2  Cola         1.40  9 left
  A3  Juice        1.75  2 left
  B1  Chips        1.20  4 left
  B2  Chocolate    1.55  1 left
  B3  Gum          0.65  5 left
1) buy  2) restock a product  3) refill coins  4) machine status  5) quit
choice> 5
Bye.
What was typed (34 lines)
1
A2
200
1
b3
50
20
1
A3
100
100
3
5
10
3
10
20
1
A3
100
100
1
A1
50
20
20
1
B2
200
4
2
A2
5
5

Modules

The program as written, entry module first. Each module transpiles to its own Python file, which is what eml project run executes.

main.eml(entry)

eml
# P032 vending machine: six products with stock, coins in, change out with the
# fewest coins the machine actually holds - and if it cannot make the change,
# the sale is refused and the coins come back. Staff can restock products and
# refill the coin box.
import change

["A1", "A2", "A3", "B1", "B2", "B3"] => codes
["Water", "Cola", "Juice", "Chips", "Chocolate", "Gum"] => names
[90, 140, 175, 120, 155, 65] => prices
10 => most_stock
50 => most_coins

def trim(s):
    0 => i
    len(s) => j
    while i < j and s[i] == " ":
        i + 1 => i
    while j > i and s[j - 1] == " ":
        j - 1 => j
    return s[i:j]

def upper(s):
    "abcdefghijklmnopqrstuvwxyz" => small
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ" => big
    "" => out
    for c in s:
        0 => k
        while k < 26 and small[k] != c:
            k + 1 => k
        if k < 26:
            out + big[k] => out
        else:
            out + c => out
    return out

def whole_number(s):
    trim(s) => s
    if s == "" or len(s) > 3:
        return 0 - 1
    0 => n
    for c in s:
        if not (c in "0123456789"):
            return 0 - 1
        n * 10 + int(c) => n
    return n

def ask_code():
    # A slot's index, or -1 when the answer is empty.
    while True:
        upper(trim(input("slot> "))) => answer
        if answer == "":
            return 0 - 1
        for i in [0:len(codes) - 1]:
            if codes[i] == answer:
                return i
        "Type a slot from A1 to B3." ^0

def show_products(stock):
    for i in [0:len(codes) - 1]:
        names[i] => name
        while len(name) < 10:
            name + " " => name
        change.money(prices[i]) => price
        while len(price) < 5:
            " " + price => price
        "sold out" => left
        if stock[i] > 0:
            str(stock[i]) + " left" => left
        ("  " + codes[i] + "  " + name + "  " + price + "  " + left) ^0

def buy(state):
    # state is [stock, coin box, items sold, takings]; returns the new state.
    state[0] => stock
    state[1] => box
    ask_code() => i
    if i == 0 - 1:
        "Cancelled." ^0
        return state
    if stock[i] == 0:
        (codes[i] + " " + names[i] + " is sold out.") ^0
        return state
    prices[i] => price
    (names[i] + " costs " + change.money(price) + ". Insert coins: 5, 10, 20, 50, 100 or 200 cents; 0 gives them back.") ^0
    [] => inserted
    0 => paid
    while paid < price:
        trim(input("coin> ")) => answer
        whole_number(answer) => v
        if answer == "" or v == 0:
            if len(inserted) == 0:
                "Nothing was put in." ^0
            else:
                ("Returned: " + listed_coins(inserted) + ".") ^0
            return state
        if change.coin_index(v) == 0 - 1:
            "This machine takes coins of 5, 10, 20, 50, 100 and 200 cents." ^0
        else:
            inserted + [v] => inserted
            paid + v => paid
            if paid < price:
                (change.money(paid) + " in, " + change.money(price - paid) + " to go.") ^0
            else:
                (change.money(paid) + " in.") ^0
    # the coins put in join the box, and the change can use them
    [] => after
    for k in [0:len(change.values) - 1]:
        box[k] => n
        for v in inserted:
            if v == change.values[k]:
                n + 1 => n
        after + [n] => after
    paid - price => owed
    [0, 0, 0, 0, 0, 0] => used
    if owed > 0:
        change.fewest(owed, after) => used
        if len(used) == 0:
            ("Sorry - the machine cannot make " + change.money(owed) + " in change from the coins it holds. Returned: " + listed_coins(inserted) + ".") ^0
            return state
        for k in [0:len(change.values) - 1]:
            after[k] - used[k] => after[k]
    stock[0:i] + [stock[i] - 1] + stock[i + 1:len(stock)] => stock
    if owed == 0:
        ("Here is your " + names[i] + ".") ^0
    else:
        ("Here is your " + names[i] + ", with " + change.money(owed) + " in change: " + change.coins_text(used) + ".") ^0
    return [stock, after, state[2] + 1, state[3] + price]

def listed_coins(coins):
    "" => out
    for v in coins:
        if out != "":
            out + ", " => out
        out + str(v) => out
    return out

def restock(state):
    state[0] => stock
    ask_code() => i
    if i == 0 - 1:
        "Cancelled." ^0
        return state
    most_stock - stock[i] => room
    if room == 0:
        (codes[i] + " " + names[i] + " is full (" + str(most_stock) + ").") ^0
        return state
    while True:
        input("add how many (1 to " + str(room) + ")> ") => answer
        if trim(answer) == "":
            "Cancelled." ^0
            return state
        whole_number(answer) => n
        if n >= 1 and n <= room:
            stock[0:i] + [stock[i] + n] + stock[i + 1:len(stock)] => stock
            (codes[i] + " " + names[i] + ": " + str(stock[i]) + " now.") ^0
            return [stock, state[1], state[2], state[3]]
        ("Type a number from 1 to " + str(room) + ".") ^0

def refill(state):
    state[1] => box
    0 - 1 => k
    while k == 0 - 1:
        trim(input("coin (5, 10, 20, 50, 100 or 200)> ")) => answer
        if answer == "":
            "Cancelled." ^0
            return state
        change.coin_index(whole_number(answer)) => k
        if k == 0 - 1:
            "Type one of 5, 10, 20, 50, 100 and 200." ^0
    most_coins - box[k] => room
    if room == 0:
        ("The box is full of " + str(change.values[k]) + "s (" + str(most_coins) + ").") ^0
        return state
    while True:
        input("add how many (1 to " + str(room) + ")> ") => answer
        if trim(answer) == "":
            "Cancelled." ^0
            return state
        whole_number(answer) => n
        if n >= 1 and n <= room:
            box[0:k] + [box[k] + n] + box[k + 1:len(box)] => box
            ("The box now has " + str(box[k]) + " of " + str(change.values[k]) + ".") ^0
            return [state[0], box, state[2], state[3]]
        ("Type a number from 1 to " + str(room) + ".") ^0

def status(state):
    state[1] => box
    "" => parts
    0 => total
    for k in [0:len(change.values) - 1]:
        if parts != "":
            parts + ", " => parts
        parts + str(change.values[k]) + " x " + str(box[k]) => parts
        total + change.values[k] * box[k] => total
    ("Coin box: " + parts + " - " + change.money(total) + " in all.") ^0
    if state[2] == 1:
        ("Sold 1 item for " + change.money(state[3]) + ".") ^0
    else:
        ("Sold " + str(state[2]) + " items for " + change.money(state[3]) + ".") ^0

"== Vending machine ==" ^0
[[5, 5, 3, 4, 2, 6], [1, 0, 4, 1, 2, 0], 0, 0] => state
True => running
while running:
    "" ^0
    show_products(state[0])
    "1) buy  2) restock a product  3) refill coins  4) machine status  5) quit" ^0
    trim(input("choice> ")) => choice
    if choice == "1":
        buy(state) => state
    elif choice == "2":
        restock(state) => state
    elif choice == "3":
        refill(state) => state
    elif choice == "4":
        status(state)
    elif choice == "5":
        False => running
    else:
        "Pick a number from 1 to 5." ^0
"Bye." ^0
Python projection (main.py)
import change
codes = ["A1", "A2", "A3", "B1", "B2", "B3"]
names = ["Water", "Cola", "Juice", "Chips", "Chocolate", "Gum"]
prices = [90, 140, 175, 120, 155, 65]
most_stock = 10
most_coins = 50

def trim(s):
    i = 0
    j = len(s)
    while i < j and s[i] == " ":
        i = i + 1
    while j > i and s[j - 1] == " ":
        j = j - 1
    return s[i:j]

def upper(s):
    small = "abcdefghijklmnopqrstuvwxyz"
    big = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    out = ""
    for c in s:
        k = 0
        while k < 26 and small[k] != c:
            k = k + 1
        if k < 26:
            out = out + big[k]
        else:
            out = out + c
    return out

def whole_number(s):
    s = trim(s)
    if s == "" or len(s) > 3:
        return 0 - 1
    n = 0
    for c in s:
        if not c in "0123456789":
            return 0 - 1
        n = n * 10 + int(c)
    return n

def ask_code():
    while True:
        answer = upper(trim(input("slot> ")))
        if answer == "":
            return 0 - 1
        for i in range(0, len(codes)):
            if codes[i] == answer:
                return i
        print("Type a slot from A1 to B3.")

def show_products(stock):
    for i in range(0, len(codes)):
        name = names[i]
        while len(name) < 10:
            name = name + " "
        price = change.money(prices[i])
        while len(price) < 5:
            price = " " + price
        left = "sold out"
        if stock[i] > 0:
            left = str(stock[i]) + " left"
        print("  " + codes[i] + "  " + name + "  " + price + "  " + left)

def buy(state):
    stock = state[0]
    box = state[1]
    i = ask_code()
    if i == 0 - 1:
        print("Cancelled.")
        return state
    if stock[i] == 0:
        print(codes[i] + " " + names[i] + " is sold out.")
        return state
    price = prices[i]
    print(names[i] + " costs " + change.money(price) + ". Insert coins: 5, 10, 20, 50, 100 or 200 cents; 0 gives them back.")
    inserted = []
    paid = 0
    while paid < price:
        answer = trim(input("coin> "))
        v = whole_number(answer)
        if answer == "" or v == 0:
            if len(inserted) == 0:
                print("Nothing was put in.")
            else:
                print("Returned: " + listed_coins(inserted) + ".")
            return state
        if change.coin_index(v) == 0 - 1:
            print("This machine takes coins of 5, 10, 20, 50, 100 and 200 cents.")
        else:
            inserted = inserted + [v]
            paid = paid + v
            if paid < price:
                print(change.money(paid) + " in, " + change.money(price - paid) + " to go.")
            else:
                print(change.money(paid) + " in.")
    after = []
    for k in range(0, len(change.values)):
        n = box[k]
        for v in inserted:
            if v == change.values[k]:
                n = n + 1
        after = after + [n]
    owed = paid - price
    used = [0, 0, 0, 0, 0, 0]
    if owed > 0:
        used = change.fewest(owed, after)
        if len(used) == 0:
            print("Sorry - the machine cannot make " + change.money(owed) + " in change from the coins it holds. Returned: " + listed_coins(inserted) + ".")
            return state
        for k in range(0, len(change.values)):
            after[k] = after[k] - used[k]
    stock = stock[0:i] + [stock[i] - 1] + stock[i + 1:len(stock)]
    if owed == 0:
        print("Here is your " + names[i] + ".")
    else:
        print("Here is your " + names[i] + ", with " + change.money(owed) + " in change: " + change.coins_text(used) + ".")
    return [stock, after, state[2] + 1, state[3] + price]

def listed_coins(coins):
    out = ""
    for v in coins:
        if out != "":
            out = out + ", "
        out = out + str(v)
    return out

def restock(state):
    stock = state[0]
    i = ask_code()
    if i == 0 - 1:
        print("Cancelled.")
        return state
    room = most_stock - stock[i]
    if room == 0:
        print(codes[i] + " " + names[i] + " is full (" + str(most_stock) + ").")
        return state
    while True:
        answer = input("add how many (1 to " + str(room) + ")> ")
        if trim(answer) == "":
            print("Cancelled.")
            return state
        n = whole_number(answer)
        if n >= 1 and n <= room:
            stock = stock[0:i] + [stock[i] + n] + stock[i + 1:len(stock)]
            print(codes[i] + " " + names[i] + ": " + str(stock[i]) + " now.")
            return [stock, state[1], state[2], state[3]]
        print("Type a number from 1 to " + str(room) + ".")

def refill(state):
    box = state[1]
    k = 0 - 1
    while k == 0 - 1:
        answer = trim(input("coin (5, 10, 20, 50, 100 or 200)> "))
        if answer == "":
            print("Cancelled.")
            return state
        k = change.coin_index(whole_number(answer))
        if k == 0 - 1:
            print("Type one of 5, 10, 20, 50, 100 and 200.")
    room = most_coins - box[k]
    if room == 0:
        print("The box is full of " + str(change.values[k]) + "s (" + str(most_coins) + ").")
        return state
    while True:
        answer = input("add how many (1 to " + str(room) + ")> ")
        if trim(answer) == "":
            print("Cancelled.")
            return state
        n = whole_number(answer)
        if n >= 1 and n <= room:
            box = box[0:k] + [box[k] + n] + box[k + 1:len(box)]
            print("The box now has " + str(box[k]) + " of " + str(change.values[k]) + ".")
            return [state[0], box, state[2], state[3]]
        print("Type a number from 1 to " + str(room) + ".")

def status(state):
    box = state[1]
    parts = ""
    total = 0
    for k in range(0, len(change.values)):
        if parts != "":
            parts = parts + ", "
        parts = parts + str(change.values[k]) + " x " + str(box[k])
        total = total + change.values[k] * box[k]
    print("Coin box: " + parts + " - " + change.money(total) + " in all.")
    if state[2] == 1:
        print("Sold 1 item for " + change.money(state[3]) + ".")
    else:
        print("Sold " + str(state[2]) + " items for " + change.money(state[3]) + ".")

print("== Vending machine ==")
state = [[5, 5, 3, 4, 2, 6], [1, 0, 4, 1, 2, 0], 0, 0]
running = True
while running:
    print("")
    show_products(state[0])
    print("1) buy  2) restock a product  3) refill coins  4) machine status  5) quit")
    choice = trim(input("choice> "))
    if choice == "1":
        state = buy(state)
    elif choice == "2":
        state = restock(state)
    elif choice == "3":
        state = refill(state)
    elif choice == "4":
        status(state)
    elif choice == "5":
        running = False
    else:
        print("Pick a number from 1 to 5.")
print("Bye.")

change.eml

eml
# P032 vending machine - coins and change. Amounts are whole cents.
#
# Change is made from the coins actually in the machine, so the number of
# each coin is limited. With limited coins "always take the biggest coin"
# can fail even where it never fails with an endless supply (60 cents from
# 50, 20, 20, 20 is 20 + 20 + 20, not 50 + something), so the fewest coins
# are found with the dynamic-programming table of the corpus case
# coin-change-dp, one coin value at a time, each used at most as many times
# as the machine holds it.

[5, 10, 20, 50, 100, 200] => values

def quotient(a, b):
    return int((a - a % b) / b)

def money(cents):
    # 140 as "1.40".
    str(cents % 100) => c
    if len(c) < 2:
        "0" + c => c
    return str(quotient(cents, 100)) + "." + c

def coin_index(cents):
    for i in [0:len(values) - 1]:
        if values[i] == cents:
            return i
    return 0 - 1

def fewest(amount, counts):
    # How many of each coin value make `amount` with the fewest coins, using
    # at most counts[i] of values[i]; [] when no combination makes it. On a
    # tie the bigger coins are used: the table takes as many of each value
    # as it can while staying optimal, from the biggest down.
    1000000 => none
    [0] + [none] * amount => best
    [] => took
    for i in [0:len(values) - 1]:
        values[i] => v
        [none] * (amount + 1) => now
        [0] * (amount + 1) => k_at
        for a in [0:amount]:
            quotient(a, v) => most
            if counts[i] < most:
                counts[i] => most
            for k in [0:most]:
                if best[a - k * v] + k <= now[a] and best[a - k * v] < none:
                    best[a - k * v] + k => now[a]
                    k => k_at[a]
        now => best
        took + [k_at] => took
    if best[amount] >= none:
        return []
    [0] * len(values) => out
    amount => a
    len(values) - 1 => i
    while i >= 0:
        took[i][a] => k
        k => out[i]
        a - k * values[i] => a
        i - 1 => i
    return out

def coins_text(used):
    # [0, 1, 0, 1, 0, 0] as "50 + 10", biggest first.
    "" => out
    len(values) - 1 => i
    while i >= 0:
        for n in [1:used[i]]:
            if out != "":
                out + " + " => out
            out + str(values[i]) => out
        i - 1 => i
    return out
Python projection (change.py)
values = [5, 10, 20, 50, 100, 200]

def quotient(a, b):
    return int((a - a % b) / b)

def money(cents):
    c = str(cents % 100)
    if len(c) < 2:
        c = "0" + c
    return str(quotient(cents, 100)) + "." + c

def coin_index(cents):
    for i in range(0, len(values)):
        if values[i] == cents:
            return i
    return 0 - 1

def fewest(amount, counts):
    none = 1000000
    best = [0] + [none] * amount
    took = []
    for i in range(0, len(values)):
        v = values[i]
        now = [none] * (amount + 1)
        k_at = [0] * (amount + 1)
        for a in range(0, amount+1):
            most = quotient(a, v)
            if counts[i] < most:
                most = counts[i]
            for k in range(0, most+1):
                if best[a - k * v] + k <= now[a] and best[a - k * v] < none:
                    now[a] = best[a - k * v] + k
                    k_at[a] = k
        best = now
        took = took + [k_at]
    if best[amount] >= none:
        return []
    out = [0] * len(values)
    a = amount
    i = len(values) - 1
    while i >= 0:
        k = took[i][a]
        out[i] = k
        a = a - k * values[i]
        i = i - 1
    return out

def coins_text(used):
    out = ""
    i = len(values) - 1
    while i >= 0:
        for n in range(1, used[i]+1):
            if out != "":
                out = out + " + "
            out = out + str(values[i])
        i = i - 1
    return out

Built on these corpus cases

ΣEML 2026

Efficient New Language — a high-density semantic overlay for humans and AI agents.

This site’s demos run on the real EML toolchain, in your browser.

© 2026 EveMissLab (一言諾科技有限公司) / Neo.K. All rights reserved.