Vending Machine Build Status

This is a Ruby gem which emulates a vending machine.

Provided with the name of a product and an array of coins (in string values), the machine will either return the correct product, or inform you of the relevant error.

Installing the machine

gem install vending_machine

The machine

The machine itself contains a hash of products, and its available change, in denominations of €2 to 1c inclusive.

For example

change = {
             "1c" => 100,
             "2c" => 100,
             "5c" => 100,
             "10c" => 100,
             "20c" => 100,
             "50c" => 100,
             "€1" => 100,
             "€2" => 100
           }
snickers = Product.new(name: "Snickers", price: "€2")
mars_bar = Product.new(name: "Mars Bar", price: "€1")
products = {
             "Snickers" => {
                product: snickers,
                available: 10
             },
             "mars bar" => {
                product: mars_bar,
                available: 5
               }
             }
machine = Machine.new(products: products, available_change: change)

machine.select_product(product: "Mars Bar", coins: ["20c", "50c", "10c", "20c"])
=> {:product=>"Mars Bar", :change=>["0"]}

If not enough money is given, the machine raises a MoreMoneyError and exits

machine.select_product(product: "Snickers", coins: ["20c", "50c", "10c", "20c"])
MoreMoneyError: MoreMoneyError

The machine can be refilled with both products and change

machine = Machine.new(products: [Product.new(name: "snickers", price: "€2")], available_change: available_change)
=> #<Machine:0x00007fb3ac086440 @available_change={1=>100, 2=>100, 5=>100, 10=>100, 20=>100, 50=>100, 100=>100, 200=>100}, @products=[#<Product:0x00007fb3ac0864e0 @name="snickers", @price="€2">]>
cash_refill = { "1c" => 100 }
machine.refill_cash(cash_refill)
=> {1=>100}
machine.available_change
=> {1=>200, 2=>100, 5=>100, 10=>100, 20=>100, 50=>100, 100=>100, 200=>100}

milky_bar = Product.new(name: "milky bar", price: "60c")
=> #<Product:0x00007fb81297fc30 @name="milky bar", @price="60c">
snickers = Product.new(name: "Snickers", price: "€2")
=> #<Product:0x00007fb8121e76a8 @name="Snickers", @price="€2">
machine.refill_products([{ product: milky_bar, available: 10 }, { product: snickers, available: 15 }])
=> [{:product=>#<Product:0x00007fb81297fc30 @name="milky bar", @price="60c">, :available=>10}, {:product=>#<Product:0x00007fb8121e76a8 @name="Snickers", @price="€2">, :available=>15}]
machine.products
=> {"Snickers"=>{:product=>#<Product:0x00007fb8121a54b0 @name="Snickers", @price="€2">, :available=>25},
 "Mars Bar"=>{:product=>#<Product:0x00007fb8121a53e8 @name="Mars Bar", @price="€1">, :available=>5},
 "milky bar"=>{:product=>#<Product:0x00007fb81297fc30 @name="milky bar", @price="60c">, :available=>20}}

How does it find the correct change

The algorithm behind finding the correct change is a simple greedy algorithm which constantly asks for the largest coin denomination that can be returned without the change going below 0.

For example

available_coins = [1, 3, 4], change = 2
2 - 4 = -2: less than 0, moving on
2 - 3 = -1: less than 0, moving on
2 - 1 = 1: greater than 0, adding 1 to change array and checking if 1 still works with new change of 1
1 - 1 = 0: greater than 0, adding 1 to change array and checking if 1 still works with new change of 0
0 - 1 = -1: less than 0, moving on and breaking out as we have come to the end of the array

If the exact change for a product's price is given and no change needs to be calculated, the machine simply returns the product and 0 change.

machine.select_product(product: "Snickers", coins: ["€2"])
=> {:product=>"Snickers", :change=>["0"]}

Running the machine locally

Simply run gem install vending_machine.

Then call a Ruby console of your choice (for example pry or irb)

Requiring the installed gem will then let you interact with it.

This machine comes preloaded with some change and products for testing.

~/vending_machine ❯❯❯ pry                                                                                                                                                                                                    
[1] pry(main)> require "vending_machine"
=> true
[2] pry(main)> machine = Machine.new(products: products, available_change: change)
=> #<Machine:0x00007f8867a59280
 @available_change={1=>100, 2=>100, 5=>100, 10=>100, 20=>100, 50=>100, 100=>100, 200=>100},
 @products={"Snickers"=>{:product=>#<Product:0x00007f8867a59460 @name="Snickers", @price="€2">, :available=>10}, "Mars Bar"=>{:product=>#<Product:0x00007f8867a59398 @name="Mars Bar", @price="€1">, :available=>5}}>

Tests

To run the tests simply cd into the root of the directory and run rake