vvm 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/vvm/cli/command/dispense.rb +3 -1
- data/lib/vvm/cli/command/exit.rb +15 -0
- data/lib/vvm/cli/command/show_main_menu.rb +2 -1
- data/lib/vvm/dispenser.rb +92 -0
- data/lib/vvm/machine.rb +0 -3
- data/lib/vvm/state/base.rb +8 -13
- data/lib/vvm/version.rb +1 -1
- data/lib/vvm.rb +4 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '0742129dc37e28bf46ea49679313f24eb670cbf949f5db63c433a6dfd0848349'
|
4
|
+
data.tar.gz: 28be2e9c01eee6566c07a3f8618c06601b3534bffa097de8514da72df1b4e856
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f341b440b96bf4669f8fb8af2c85ee696a662ba4f68fde5cee5ee3061ff912ebc405f334f2b0ec3f6b254c2ca8608a63febb5aad59ddaa2997adbd9ee9a8502c
|
7
|
+
data.tar.gz: 668038ac248fc9a3fd99085fecbe0960c97fd78a45622744291500bd49992713dddcebaa8cee308fb8cdd59b7ae0961d0cd44621a7ca3a5809b0db5fc89c59f6
|
@@ -9,7 +9,9 @@ module Vvm
|
|
9
9
|
def call
|
10
10
|
prompt.say("Balance: $#{machine.balance}")
|
11
11
|
|
12
|
-
|
12
|
+
coins = machine.dispense.filter { _1.qty.positive? }.map { "#{_1.qty} x $#{_1.value}" }
|
13
|
+
|
14
|
+
formatted_coins = coins.empty? ? '0' : coins.join(', ')
|
13
15
|
|
14
16
|
env.call(:confirm_dispense, { formatted_coins: formatted_coins })
|
15
17
|
end
|
@@ -12,7 +12,8 @@ module Vvm
|
|
12
12
|
choices = [
|
13
13
|
{ name: 'Insert a coin', value: :insert_coin, **maybe_out_of_stock },
|
14
14
|
{ name: 'Pick the product', value: :pick_product },
|
15
|
-
{ name: 'Dispense', value: :dispense, **maybe_empty_balance, **maybe_no_change }
|
15
|
+
{ name: 'Dispense', value: :dispense, **maybe_empty_balance, **maybe_no_change },
|
16
|
+
{ name: 'Exit', value: :exit }
|
16
17
|
]
|
17
18
|
|
18
19
|
result = prompt.select('Main menu:', choices)
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Vvm
|
4
|
+
class Dispenser
|
5
|
+
def initialize(amount, coins)
|
6
|
+
@amount = amount
|
7
|
+
@coins = coins
|
8
|
+
@min_coins = []
|
9
|
+
@min_qty = 0
|
10
|
+
@subset = coins
|
11
|
+
end
|
12
|
+
|
13
|
+
def call # rubocop:disable Metrics/AbcSize
|
14
|
+
return give_away if not_enough_change?
|
15
|
+
|
16
|
+
coins.each_with_object([]).with_index do |(_, memo), index|
|
17
|
+
self.subset = define_subset(index)
|
18
|
+
|
19
|
+
result = calculate(subset, amount)
|
20
|
+
|
21
|
+
memo << result if result.sum { _1.value * _1.qty } == amount
|
22
|
+
|
23
|
+
match_qty = calculate_min_qty(memo)
|
24
|
+
|
25
|
+
calibrate(memo, match_qty) if min_coins.empty? || match_qty < min_qty
|
26
|
+
end
|
27
|
+
|
28
|
+
min_coins
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
attr_reader :amount, :coins
|
34
|
+
attr_accessor :min_coins, :min_qty, :subset
|
35
|
+
|
36
|
+
def give_away
|
37
|
+
coins.map do |coin|
|
38
|
+
dup = coin.dup
|
39
|
+
coin.qty = 0
|
40
|
+
dup
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def not_enough_change?
|
45
|
+
amount > coins.sum { _1.value * _1.qty }
|
46
|
+
end
|
47
|
+
|
48
|
+
def define_subset(index)
|
49
|
+
coins.dup.tap do |list|
|
50
|
+
list.delete_at(index) unless index.zero?
|
51
|
+
list.each { |coin| coin.qty = initial_coins.detect { coin.value == _1.value }.qty }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def calculate_min_qty(coins_matrix)
|
56
|
+
coins_matrix.min_by { _1.sum(&:qty) }.sum(&:qty)
|
57
|
+
end
|
58
|
+
|
59
|
+
def calibrate(coins_matrix, match_qty)
|
60
|
+
self.min_coins = coins_matrix.detect { _1.sum(&:qty) == match_qty }
|
61
|
+
self.min_qty = match_qty
|
62
|
+
end
|
63
|
+
|
64
|
+
def calculate(coins, change, index = 0) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
65
|
+
coins.drop(index).each_with_object([]).with_index do |(coin, memo), idx|
|
66
|
+
remainder = change % coin.value
|
67
|
+
|
68
|
+
if coin.qty.positive? && coin.value <= change && remainder < change
|
69
|
+
required_qty = [coin.qty, (change - remainder) / coin.value].min
|
70
|
+
|
71
|
+
memo << Vvm::Model::Coin.new(coin.value, required_qty)
|
72
|
+
|
73
|
+
amount = required_qty * coin.value
|
74
|
+
change_left = change - amount
|
75
|
+
coin.qty -= 1
|
76
|
+
|
77
|
+
break memo if change_left.zero?
|
78
|
+
|
79
|
+
sub_result = calculate(coins, change_left, idx + 1)
|
80
|
+
|
81
|
+
break unless sub_result
|
82
|
+
|
83
|
+
return memo + sub_result
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def initial_coins
|
89
|
+
@initial_coins ||= coins.map(&:dup)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
data/lib/vvm/machine.rb
CHANGED
data/lib/vvm/state/base.rb
CHANGED
@@ -3,6 +3,10 @@
|
|
3
3
|
module Vvm
|
4
4
|
module State
|
5
5
|
class Base
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
def_delegators :machine, :coins, :balance
|
9
|
+
|
6
10
|
def initialize(machine)
|
7
11
|
@machine = machine
|
8
12
|
end
|
@@ -15,21 +19,12 @@ module Vvm
|
|
15
19
|
raise NotImplementedError
|
16
20
|
end
|
17
21
|
|
18
|
-
def dispense
|
19
|
-
|
20
|
-
next if coin.value > machine.balance
|
21
|
-
|
22
|
-
unbalansed_qty = (machine.balance / coin.value).floor
|
23
|
-
qty_to_return = unbalansed_qty > coin.qty ? coin.qty : unbalansed_qty
|
24
|
-
|
25
|
-
coin_to_return = coin.dup
|
26
|
-
coin_to_return.qty = qty_to_return
|
22
|
+
def dispense
|
23
|
+
result = Vvm::Dispenser.new(balance, coins).call
|
27
24
|
|
28
|
-
|
29
|
-
coin.qty -= qty_to_return
|
25
|
+
machine.balance -= result.sum { _1.qty * _1.value }
|
30
26
|
|
31
|
-
|
32
|
-
end
|
27
|
+
result
|
33
28
|
end
|
34
29
|
|
35
30
|
private
|
data/lib/vvm/version.rb
CHANGED
data/lib/vvm.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'tty-prompt'
|
4
|
+
require 'yaml'
|
5
|
+
require 'forwardable'
|
4
6
|
|
5
7
|
require_relative 'vvm/version'
|
6
8
|
require_relative 'vvm/cli'
|
7
9
|
require_relative 'vvm/machine'
|
10
|
+
require_relative 'vvm/dispenser'
|
8
11
|
require_relative 'vvm/model/coin'
|
9
12
|
require_relative 'vvm/model/product'
|
10
13
|
require_relative 'vvm/error'
|
@@ -18,3 +21,4 @@ require_relative 'vvm/cli/command/pick_product'
|
|
18
21
|
require_relative 'vvm/cli/command/dispense'
|
19
22
|
require_relative 'vvm/cli/command/confirm_dispense'
|
20
23
|
require_relative 'vvm/cli/command/confirm_product_pick'
|
24
|
+
require_relative 'vvm/cli/command/exit'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vvm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Bykov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-11-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: tty-prompt
|
@@ -41,10 +41,12 @@ files:
|
|
41
41
|
- lib/vvm/cli/command/confirm_dispense.rb
|
42
42
|
- lib/vvm/cli/command/confirm_product_pick.rb
|
43
43
|
- lib/vvm/cli/command/dispense.rb
|
44
|
+
- lib/vvm/cli/command/exit.rb
|
44
45
|
- lib/vvm/cli/command/insert_coin.rb
|
45
46
|
- lib/vvm/cli/command/pick_product.rb
|
46
47
|
- lib/vvm/cli/command/show_main_menu.rb
|
47
48
|
- lib/vvm/cli/environment.rb
|
49
|
+
- lib/vvm/dispenser.rb
|
48
50
|
- lib/vvm/error.rb
|
49
51
|
- lib/vvm/machine.rb
|
50
52
|
- lib/vvm/model/coin.rb
|