vvm 0.1.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 255e957bd23e4b4b099fd3cfa5eb3f215eaa94b6032bfcf23ec3479e0a4866a7
4
- data.tar.gz: e4c83101f4f8daea662b07aa5c2ae7677814b6375d38824b6f8cf43fcc47271c
3
+ metadata.gz: '0742129dc37e28bf46ea49679313f24eb670cbf949f5db63c433a6dfd0848349'
4
+ data.tar.gz: 28be2e9c01eee6566c07a3f8618c06601b3534bffa097de8514da72df1b4e856
5
5
  SHA512:
6
- metadata.gz: 76f07cb86f35e4425c37ff8f92c46a9b03b3d498eda1e4407484b837479dcd240edfb37ad1835ed7aa4794d28b0278bce206844e2b7dfd9ab7d587fb74de1318
7
- data.tar.gz: 0301b3b61d82540cc78b89520c81882447edd71befa47bf3c35d53c4b46a3c7bc5b59e2ab98a2839dc73739abafc49146028740022cbcea0d80cd16c3e9a188c
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
- formatted_coins = machine.dispense.filter { _1.qty.positive? }.map { "#{_1.qty} x $#{_1.value}" }.join(', ')
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
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vvm
4
+ class CLI
5
+ module Command
6
+ class Exit < Base
7
+ self.command_name = :exit
8
+
9
+ def call
10
+ exit
11
+ end
12
+ end
13
+ end
14
+ end
15
+ 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
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'YAML'
4
- require 'forwardable'
5
-
6
3
  module Vvm
7
4
  class Machine
8
5
  extend Forwardable
@@ -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 # rubocop:disable Metrics/AbcSize
19
- machine.coins.each_with_object([]) do |coin, acc|
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
- acc.push(coin_to_return)
29
- coin.qty -= qty_to_return
25
+ machine.balance -= result.sum { _1.qty * _1.value }
30
26
 
31
- machine.balance = (machine.balance - (coin.value * qty_to_return)).round(2)
32
- end
27
+ result
33
28
  end
34
29
 
35
30
  private
data/lib/vvm/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Vvm
4
- VERSION = '0.1.1'
4
+ VERSION = '0.3.0'
5
5
  end
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.1.1
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-10-20 00:00:00.000000000 Z
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