Lots of debugging and new unit tests, results in something which seems

to perform as specified.
This commit is contained in:
simon 2014-03-27 23:32:11 +00:00
parent c4fc2fcdd0
commit ef5c6e9dc6
2 changed files with 72 additions and 26 deletions

View file

@ -1,4 +1,5 @@
(ns vending.core) (ns vending.core
(:use clojure.set))
@ -55,7 +56,7 @@
(defn sum-coins (defn sum-coins
"takes a list in the form (:merk :merk :bawbee :plack :bodle) and returns "takes a list in the form (:merk :merk :bawbee :plack :bodle) and returns
a map {:merk 2 :plack 1 :bawbee 1 :bodle 1}. Optional second argument: an a wallet {:merk 2 :plack 1 :bawbee 1 :bodle 1}. Optional second argument: an
existing map in that general form." existing map in that general form."
([coins] ([coins]
(sum-coins coins {})) (sum-coins coins {}))
@ -83,7 +84,7 @@
(defn subtract-merk [list] (defn subtract-merk [list]
(subtract-denomination list 4)) (subtract-denomination list 4))
(defn in-make-change [amount merk plack bawbee bodle] (defn- in-make-change [amount merk plack bawbee bodle]
"Given this amount of change to make, and this number each of merks, placks, bawbees "Given this amount of change to make, and this number each of merks, placks, bawbees
and bodles, return a tuple (merk plack bodle bawbee) which indicates the number remaining and bodles, return a tuple (merk plack bodle bawbee) which indicates the number remaining
after making change, or nil if not possible" after making change, or nil if not possible"
@ -117,7 +118,7 @@
(defn make-change [amount coins] (defn make-change [amount coins]
"Given this amount of change to make, and this number each of merks, placks, bawbees "Given this amount of change to make, and this number each of merks, placks, bawbees
and bodles, return a map with keys (:merk :plack :bawbee :bodle) which indicates the and bodles, return a wallet (a map with keys (:merk :plack :bawbee :bodle)) which indicates the
number of each remaining after making change, or nil if not possible" number of each remaining after making change, or nil if not possible"
(to-coins (to-coins
(let [merk (:merk coins) (let [merk (:merk coins)
@ -131,10 +132,29 @@
(defn subtract-change [coin-stacks change] (defn subtract-change [coin-stacks change]
"Return a copy of these coin-stacks with this change removed" "Return a copy of these coin-stacks with this change removed"
{:merk (- (:merk coin-stacks) (:merk change)) (let [change-map (sum-coins change)]
:plack (- (:plack coin-stacks) (:plack change)) {:merk (- (:merk coin-stacks) (or (:merk change-map) 0))
:bawbee (- (:bawbee coin-stacks) (:bawbee change)) :plack (- (:plack coin-stacks) (or (:plack change-map) 0))
:bodle (- (:bodle coin-stacks) (:bodle change))}) :bawbee (- (:bawbee coin-stacks) (or (:bawbee change-map) 0))
:bodle (- (:bodle coin-stacks) (or (:bodle change-map) 0))}))
(defn in-op-maps [op kys map1 map2 dflt]
(cond (empty? kys) {}
true
(let [ky (first kys)]
(assoc (in-op-maps op (rest kys) map1 map2 dflt)
ky
(apply op (list (or (map1 ky) dflt) (or (map2 ky) dflt)))))))
(defn op-maps [op map1 map2 dflt]
"return a new map composed by applying this op to the values of
the keys of these maps, using this default when no value present"
(in-op-maps op (union (keys map1) (keys map2)) map1 map2 dflt))
(defn add-wallets [wallet1 wallet2]
(op-maps + wallet1 wallet2 0))
(defn subtract-change-machine [machine change] (defn subtract-change-machine [machine change]
"return a copy of this machine which has this amount of change removed from its coins" "return a copy of this machine which has this amount of change removed from its coins"
@ -144,36 +164,50 @@
(defn make-change-machine [machine change] (defn make-change-machine [machine change]
"given this machine and these numbers of coins to remove, return a copy of the machine "given this machine and these numbers of coins to remove, return a copy of the machine
with the coins removed" with the coins removed"
(let [tend (sum-coins (:tendered machine))] (cond (empty? change) machine
(cond (= change '(0 0 0 0)) machine) true (assoc (dissoc (subtract-change-machine machine change) :change ) :change change )))
true (assoc (dissoc (subtract-change-machine machine change) :change ) :change change )))
(defn remove-from-stock [machine item] (defn remove-from-stock [machine item]
"return a copy of this machine with one fewer of this item in stock" "return a copy of this machine with one fewer of this item in stock"
(update-in machine [:stock item] dec)) (update-in machine [:stock item] dec))
(defn deliver-item [machine item change] (defn deliver-item [machine item]
(make-change-machine "Remove an item matching this item from stock and add it to the output hopper"
(remove-from-stock (remove-from-stock
(assoc (dissoc machine :output) :output (cons item (:output machine))) (assoc (dissoc machine :output) :output (cons item (:output machine)))
item) item))
change))
(defn store-coins-machine [machine]
;; add the tendered coins to the coin stacks
(let [wallet (sum-coins (:tendered machine))]
(assoc
(dissoc (dissoc machine :tendered) :coins)
:coins (add-wallets wallet (:coins machine)))))
(defn get-item [machine item] (defn get-item [machine item]
(let [item-price (item item-prices) (let [item-price (item item-prices)
coins (:coins machine) coins (:coins machine)
tendered (coins-value (:tendered machine)) tendered (coins-value (:tendered machine))
change (make-change (- tendered item-price) coins)] change (make-change (- tendered item-price) coins)]
(print (list "hello" item-price coins tendered change )) ;; (print (list "hello" item-price coins tendered change ))
(cond (> 0 (item (:stock machine))) (message-machine (coin-return machine) (str "Sorry, " item " not in stock")) (cond (> 0 (item (:stock machine))) (message-machine (coin-return machine) (str "Sorry, " item " not in stock"))
(<= tendered item-price) (message-machine machine "Please insert more money") (<= tendered item-price) (message-machine machine "Please insert more money")
(= change '(0 0 0 0)) (message-machine (coin-return machine) "Sorry, I don't have enough change.") (= change '(0 0 0 0)) (message-machine (coin-return machine) "Sorry, I don't have enough change.")
true (message-machine (deliver-item machine item change) (str "Enjoy your " item))))) true (message-machine
(store-coins-machine
(make-change-machine
(deliver-item machine item) change))
(str "Enjoy your " item)))))
(defn get-caramel-wafer [machine] (defn get-caramel-wafer [machine]
(get-item machine :caramel-wafer)) (get-item machine :caramel-wafer))
(defn get-teacake [machine]
(get-item machine :teacake))
(defn get-snowball [machine]
(get-item machine :snowball))
;; (get-caramel-wafer (add-coin (add-coin (make-default-machine) :merk) :merk)) ;; (get-caramel-wafer (add-coin (add-coin (make-default-machine) :merk) :merk))

View file

@ -9,7 +9,7 @@
(some #(= elm %) seq)) (some #(= elm %) seq))
(deftest test-add-coin (deftest test-vending
(testing (testing
"Test adding coins to machine" "Test adding coins to machine"
(let [machine (make-default-machine) (let [machine (make-default-machine)
@ -47,22 +47,34 @@
(testing (testing
"making appropriate change" "making appropriate change"
(let [no-change (make-change 0 5 5 5 5) (let [wallet {:merk 5 :plack 5 :bawbee 5 :bodle 5}
ten-change (make-change 10 5 5 5 5) no-change (make-change 0 wallet)
all-change (make-change 140 5 5 5 5)] merk-change (make-change 100 wallet)
(is (= no-change '(0 0 0 0)) "one hundred units of change should be satisfied with one merk") ten-change (make-change 10 wallet)
(is (= all-change '(1 1 1 1)) "140 units of change should be satisfied with one coin of each denomination") all-change (make-change 140 wallet)]
(is (or (= ten-change '(0 0 1 0)) (= ten-change '(0 0 0 2))) "ten change could be satisfied with one bawbee or two bodles"))) (is (empty? no-change) "no change implies an empty coin list")
(is (= merk-change '(:merk)) "100 units of change should be satisfied with one mark")
(is (= (set all-change) (set (list :merk :plack :bawbee :bodle))) "140 units of change should be satisfied with one coin of each denomination")
(is (or (= (set ten-change) (set (list :bawbee))) (= (set ten-change) (set (list :bodle :bodle)))) "ten change could be satisfied with one bawbee or two bodles")))
(testing (testing
"removing change from machine" "removing change from machine"
(let [machine (make-default-machine) (let [machine (make-default-machine)
changed (make-change-machine machine '(1 1 1 1)) changed (make-change-machine machine '(:merk :plack :bawbee :bodle))
emptied (make-change-machine machine '(1 4 4 4))] emptied (make-change-machine machine '(:merk :plack :bawbee :bodle :plack :bawbee :bodle :plack :bawbee :bodle :plack :bawbee :bodle))]
(is (= (vals (:coins changed)) '(0 3 3 3)) "each coin slot should be decremented by one") (is (= (vals (:coins changed)) '(0 3 3 3)) "each coin slot should be decremented by one")
(is (= (vals (:coins emptied)) '(0 0 0 0)) "each coin slot should be empty (contain zero)") (is (= (vals (:coins emptied)) '(0 0 0 0)) "each coin slot should be empty (contain zero)")
)) ))
(testing
"full machine cycle"
(let [machine (get-caramel-wafer (add-coin (make-default-machine) :merk))]
(is (= (:message machine) "Enjoy your :caramel-wafer"))
(is (= (set (:change machine)) #{:plack :bawbee}))
(is (= (:merk (:coins machine)) 2))
(is (= (:output machine) '(:caramel-wafer)))))
) )
(make-change-machine (make-default-machine) '( 1 1 1 1)) (make-change-machine (make-default-machine) '( 1 1 1 1))
(set '(:a :b :c))