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.
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 statuschange.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