spy_alley_application 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.
Files changed (148) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +7 -0
  5. data/Gemfile +4 -0
  6. data/Gemfile.lock +92 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +39 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/lib/spy_alley_application.rb +6 -0
  13. data/lib/spy_alley_application/actions/buy_equipment.rb +31 -0
  14. data/lib/spy_alley_application/actions/choose_new_spy_identity.rb +26 -0
  15. data/lib/spy_alley_application/actions/choose_space_to_move.rb +21 -0
  16. data/lib/spy_alley_application/actions/confiscate_materials.rb +58 -0
  17. data/lib/spy_alley_application/actions/generate_new_game.rb +70 -0
  18. data/lib/spy_alley_application/actions/make_accusation.rb +40 -0
  19. data/lib/spy_alley_application/actions/pass.rb +21 -0
  20. data/lib/spy_alley_application/actions/roll_die.rb +25 -0
  21. data/lib/spy_alley_application/actions/use_move_card.rb +28 -0
  22. data/lib/spy_alley_application/execute_action.rb +20 -0
  23. data/lib/spy_alley_application/injection_container.rb +596 -0
  24. data/lib/spy_alley_application/models/acquisition_method/by_confiscation.rb +22 -0
  25. data/lib/spy_alley_application/models/acquisition_method/by_free_gift.rb +20 -0
  26. data/lib/spy_alley_application/models/acquisition_method/by_passing_start.rb +20 -0
  27. data/lib/spy_alley_application/models/acquisition_method/by_purchase.rb +21 -0
  28. data/lib/spy_alley_application/models/acquisition_method/by_selling_top_secret_information.rb +21 -0
  29. data/lib/spy_alley_application/models/board_spaces/black_market.rb +24 -0
  30. data/lib/spy_alley_application/models/board_spaces/border_crossing.rb +24 -0
  31. data/lib/spy_alley_application/models/board_spaces/buy_equipment.rb +26 -0
  32. data/lib/spy_alley_application/models/board_spaces/buy_password.rb +26 -0
  33. data/lib/spy_alley_application/models/board_spaces/confiscate_materials.rb +24 -0
  34. data/lib/spy_alley_application/models/board_spaces/draw_free_gift.rb +24 -0
  35. data/lib/spy_alley_application/models/board_spaces/draw_move_card.rb +24 -0
  36. data/lib/spy_alley_application/models/board_spaces/embassy.rb +26 -0
  37. data/lib/spy_alley_application/models/board_spaces/move_back.rb +25 -0
  38. data/lib/spy_alley_application/models/board_spaces/sold_top_secret_information.rb +27 -0
  39. data/lib/spy_alley_application/models/board_spaces/spy_alley_entrance.rb +38 -0
  40. data/lib/spy_alley_application/models/board_spaces/spy_eliminator.rb +24 -0
  41. data/lib/spy_alley_application/models/board_spaces/start.rb +36 -0
  42. data/lib/spy_alley_application/models/board_spaces/take_another_turn.rb +24 -0
  43. data/lib/spy_alley_application/models/equipment.rb +42 -0
  44. data/lib/spy_alley_application/models/game_board.rb +23 -0
  45. data/lib/spy_alley_application/models/game_board/black_market_option_state.rb +59 -0
  46. data/lib/spy_alley_application/models/game_board/buy_equipment_option_state.rb +52 -0
  47. data/lib/spy_alley_application/models/game_board/buy_password_option_state.rb +39 -0
  48. data/lib/spy_alley_application/models/game_board/confiscate_materials_option_state.rb +68 -0
  49. data/lib/spy_alley_application/models/game_board/eliminate_player.rb +91 -0
  50. data/lib/spy_alley_application/models/game_board/embassy_victory.rb +23 -0
  51. data/lib/spy_alley_application/models/game_board/equipment_bought.rb +42 -0
  52. data/lib/spy_alley_application/models/game_board/equipment_confiscated.rb +67 -0
  53. data/lib/spy_alley_application/models/game_board/free_gift_drawn.rb +39 -0
  54. data/lib/spy_alley_application/models/game_board/money_gained_or_lost.rb +22 -0
  55. data/lib/spy_alley_application/models/game_board/move_card_drawn.rb +31 -0
  56. data/lib/spy_alley_application/models/game_board/move_card_used.rb +30 -0
  57. data/lib/spy_alley_application/models/game_board/move_options.rb +23 -0
  58. data/lib/spy_alley_application/models/game_board/new_spy_identity_chosen.rb +37 -0
  59. data/lib/spy_alley_application/models/game_board/next_game_state.rb +70 -0
  60. data/lib/spy_alley_application/models/game_board/player_moved.rb +24 -0
  61. data/lib/spy_alley_application/models/game_board/spy_eliminator_options.rb +41 -0
  62. data/lib/spy_alley_application/models/game_state/buy_equipment.rb +26 -0
  63. data/lib/spy_alley_application/models/game_state/choose_new_spy_identity.rb +29 -0
  64. data/lib/spy_alley_application/models/game_state/confiscate_materials.rb +29 -0
  65. data/lib/spy_alley_application/models/game_state/game_over.rb +27 -0
  66. data/lib/spy_alley_application/models/game_state/move_option.rb +25 -0
  67. data/lib/spy_alley_application/models/game_state/spy_eliminator.rb +25 -0
  68. data/lib/spy_alley_application/models/game_state/start_of_turn.rb +23 -0
  69. data/lib/spy_alley_application/models/game_state/victory_reason/by_elimination.rb +22 -0
  70. data/lib/spy_alley_application/models/game_state/victory_reason/by_embassy.rb +22 -0
  71. data/lib/spy_alley_application/models/move_card.rb +26 -0
  72. data/lib/spy_alley_application/models/player.rb +37 -0
  73. data/lib/spy_alley_application/models/sell_top_secret_information.rb +24 -0
  74. data/lib/spy_alley_application/models/validator/buying_equipment.rb +35 -0
  75. data/lib/spy_alley_application/models/validator/choosing_new_spy_identity.rb +33 -0
  76. data/lib/spy_alley_application/models/validator/confiscating_materials.rb +50 -0
  77. data/lib/spy_alley_application/models/validator/making_accusation.rb +31 -0
  78. data/lib/spy_alley_application/models/validator/moving.rb +30 -0
  79. data/lib/spy_alley_application/models/validator/passing.rb +24 -0
  80. data/lib/spy_alley_application/models/validator/rolling_die.rb +29 -0
  81. data/lib/spy_alley_application/models/validator/using_move_card.rb +31 -0
  82. data/lib/spy_alley_application/models/wild_card.rb +25 -0
  83. data/lib/spy_alley_application/new_game.rb +18 -0
  84. data/lib/spy_alley_application/new_game/assign_seats.rb +21 -0
  85. data/lib/spy_alley_application/new_game/assign_spy_identities.rb +24 -0
  86. data/lib/spy_alley_application/results/nodes/buy_equipment_option_node.rb +23 -0
  87. data/lib/spy_alley_application/results/nodes/choose_new_spy_identity_option_node.rb +23 -0
  88. data/lib/spy_alley_application/results/nodes/confiscate_materials_option_node.rb +22 -0
  89. data/lib/spy_alley_application/results/nodes/die_rolled_node.rb +21 -0
  90. data/lib/spy_alley_application/results/nodes/eliminated_player_node.rb +22 -0
  91. data/lib/spy_alley_application/results/nodes/equipment_gained_node.rb +28 -0
  92. data/lib/spy_alley_application/results/nodes/game_over_node.rb +24 -0
  93. data/lib/spy_alley_application/results/nodes/make_accusation_option_node.rb +20 -0
  94. data/lib/spy_alley_application/results/nodes/money_gained_node.rb +25 -0
  95. data/lib/spy_alley_application/results/nodes/money_lost_node.rb +21 -0
  96. data/lib/spy_alley_application/results/nodes/move_back_node.rb +21 -0
  97. data/lib/spy_alley_application/results/nodes/move_card_drawn_node.rb +22 -0
  98. data/lib/spy_alley_application/results/nodes/move_card_used_node.rb +22 -0
  99. data/lib/spy_alley_application/results/nodes/move_option_node.rb +20 -0
  100. data/lib/spy_alley_application/results/nodes/new_spy_identity_chosen_node.rb +21 -0
  101. data/lib/spy_alley_application/results/nodes/next_player_node.rb +20 -0
  102. data/lib/spy_alley_application/results/nodes/pass_option_node.rb +16 -0
  103. data/lib/spy_alley_application/results/nodes/player_movement_node.rb +21 -0
  104. data/lib/spy_alley_application/results/nodes/player_passed_node.rb +19 -0
  105. data/lib/spy_alley_application/results/nodes/reached_embassy_with_all_equipment_node.rb +20 -0
  106. data/lib/spy_alley_application/results/nodes/result_game_board_node.rb +21 -0
  107. data/lib/spy_alley_application/results/nodes/roll_die_option_node.rb +16 -0
  108. data/lib/spy_alley_application/results/nodes/use_move_card_option_node.rb +20 -0
  109. data/lib/spy_alley_application/results/nodes/wild_card_gained_node.rb +27 -0
  110. data/lib/spy_alley_application/results/process_buy_equipment_options.rb +25 -0
  111. data/lib/spy_alley_application/results/process_eliminating_player.rb +36 -0
  112. data/lib/spy_alley_application/results/process_landing_on_space.rb +232 -0
  113. data/lib/spy_alley_application/results/process_move_options.rb +65 -0
  114. data/lib/spy_alley_application/results/process_next_turn_options.rb +87 -0
  115. data/lib/spy_alley_application/results/process_passing_spaces.rb +68 -0
  116. data/lib/spy_alley_application/results/process_proceeding_to_next_state.rb +23 -0
  117. data/lib/spy_alley_application/types/array_of_equipment.rb +7 -0
  118. data/lib/spy_alley_application/types/array_of_free_gifts.rb +7 -0
  119. data/lib/spy_alley_application/types/array_of_move_cards.rb +6 -0
  120. data/lib/spy_alley_application/types/array_of_players.rb +6 -0
  121. data/lib/spy_alley_application/types/board_space.rb +4 -0
  122. data/lib/spy_alley_application/types/coercible_integer_one_to_six.rb +9 -0
  123. data/lib/spy_alley_application/types/equipment.rb +32 -0
  124. data/lib/spy_alley_application/types/equipment_type.rb +9 -0
  125. data/lib/spy_alley_application/types/free_gift.rb +22 -0
  126. data/lib/spy_alley_application/types/game_board.rb +52 -0
  127. data/lib/spy_alley_application/types/game_state.rb +19 -0
  128. data/lib/spy_alley_application/types/move_card.rb +21 -0
  129. data/lib/spy_alley_application/types/nationality.rb +5 -0
  130. data/lib/spy_alley_application/types/player.rb +123 -0
  131. data/lib/spy_alley_application/types/validation_builder.rb +21 -0
  132. data/lib/spy_alley_application/types/validator/non_admin.rb +21 -0
  133. data/lib/spy_alley_application/types/wild_card.rb +14 -0
  134. data/lib/spy_alley_application/validator/builder.rb +119 -0
  135. data/lib/spy_alley_application/validator/buy_equipment.rb +39 -0
  136. data/lib/spy_alley_application/validator/choose_new_spy_identity.rb +24 -0
  137. data/lib/spy_alley_application/validator/confiscate_materials.rb +22 -0
  138. data/lib/spy_alley_application/validator/confiscate_materials/full_validator.rb +39 -0
  139. data/lib/spy_alley_application/validator/confiscate_materials/validate_target_player_id.rb +24 -0
  140. data/lib/spy_alley_application/validator/make_accusation.rb +24 -0
  141. data/lib/spy_alley_application/validator/move.rb +24 -0
  142. data/lib/spy_alley_application/validator/new_game.rb +35 -0
  143. data/lib/spy_alley_application/validator/new_game_builder.rb +32 -0
  144. data/lib/spy_alley_application/validator/no_options.rb +7 -0
  145. data/lib/spy_alley_application/validator/use_move_card.rb +23 -0
  146. data/lib/spy_alley_application/version.rb +3 -0
  147. data/spy_alley_application.gemspec +40 -0
  148. metadata +343 -0
@@ -0,0 +1,36 @@
1
+ # frozen string_literal: true
2
+
3
+ require 'dry-struct'
4
+ require 'spy_alley_application/types/board_space'
5
+
6
+ module SpyAlleyApplication
7
+ module Models
8
+ module BoardSpaces
9
+ class Start < Dry::Struct
10
+ @@can_handle_start = ::Types.Interface(:handle_start)
11
+ def self.new(id:, next_space: nil, &block)
12
+ o = Start.allocate
13
+ next_space ||= yield(o)
14
+ o.send(:initialize, **{id: id, next_space: next_space})
15
+ o
16
+ end
17
+
18
+ def initialize(id:, next_space: nil, &block)
19
+ @id = ::Types::Coercible::Integer.call(id)
20
+ @next_space = SpyAlleyApplication::Types::BoardSpace.call(next_space)
21
+ end
22
+
23
+ def id;@id;end
24
+ def next_space;@next_space;end
25
+ def in_spy_alley?;false;end
26
+ def to_h;{id: id};end
27
+
28
+ def accept(visitor, **args)
29
+ @@can_handle_start.(visitor)
30
+ visitor.handle_start(self, args)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+
@@ -0,0 +1,24 @@
1
+ # frozen string_literal: true
2
+
3
+ require 'dry-struct'
4
+ require 'spy_alley_application/types/board_space'
5
+
6
+ module SpyAlleyApplication
7
+ module Models
8
+ module BoardSpaces
9
+ class TakeAnotherTurn < Dry::Struct
10
+ @@can_handle_take_another_turn = ::Types.Interface(:handle_take_another_turn)
11
+ attribute :id, ::Types::Coercible::Integer
12
+ attribute :next_space, ::SpyAlleyApplication::Types::BoardSpace
13
+
14
+ def accept(visitor, **args)
15
+ @@can_handle_take_another_turn.(visitor)
16
+ visitor.handle_take_another_turn(self, args)
17
+ end
18
+
19
+ def in_spy_alley?;false;end
20
+ end
21
+ end
22
+ end
23
+ end
24
+
@@ -0,0 +1,42 @@
1
+ # frozen string literal: true
2
+
3
+ require 'dry-struct'
4
+ require 'spy_alley_application/types/equipment_type'
5
+ require 'spy_alley_application/types/nationality'
6
+
7
+ module SpyAlleyApplication
8
+ module Models
9
+ class Equipment < Dry::Struct
10
+ @@can_handle_equipment = ::Types.Interface(:handle_equipment)
11
+
12
+ attribute :nationality, SpyAlleyApplication::Types::Nationality
13
+ attribute :type, SpyAlleyApplication::Types::EquipmentType
14
+
15
+ def accept(visitor, **args)
16
+ @@can_handle_equipment.(visitor)
17
+ visitor.handle_equipment(self, args)
18
+ end
19
+
20
+ def wild_card?
21
+ false
22
+ end
23
+
24
+ def to_h
25
+ self
26
+ end
27
+
28
+ def to_s
29
+ "#{nationality} #{type}"
30
+ end
31
+ alias_method :transform_values, :to_s
32
+
33
+ def <=>(other)
34
+ return nil if !other.is_a?(self.class)
35
+ result = self.nationality <=> other.nationality
36
+ result = self.type <=> other.type if result.eql?(0)
37
+ result
38
+ end
39
+ end
40
+ end
41
+ end
42
+
@@ -0,0 +1,23 @@
1
+ # frozen string_literal: true
2
+
3
+ require 'dry-struct'
4
+ require 'spy_alley_application/types/array_of_players'
5
+ require 'spy_alley_application/types/array_of_free_gifts'
6
+ require 'spy_alley_application/types/array_of_move_cards'
7
+ require 'spy_alley_application/types/game_state'
8
+
9
+ module SpyAlleyApplication
10
+ module Models
11
+ class GameBoard < Dry::Struct
12
+ attribute :players, Types::ArrayOfPlayers
13
+ attribute :move_card_pile, Types::ArrayOfMoveCards
14
+ attribute :free_gift_pile, Types::ArrayOfFreeGifts
15
+ attribute :game_state, Types::GameState
16
+
17
+ def current_player
18
+ players.find{|p| p.seat.eql?(game_state.seat)}
19
+ end
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,59 @@
1
+ # frozen string_literal: true
2
+
3
+ require 'dry-initializer'
4
+ require 'dry-struct'
5
+ require 'spy_alley_application/models/game_board'
6
+ require 'spy_alley_application/types/equipment'
7
+ require 'spy_alley_application/types/equipment_type'
8
+ require 'spy_alley_application/types/game_state'
9
+ require 'spy_alley_application/types/nationality'
10
+
11
+ module SpyAlleyApplication
12
+ module Models
13
+ class GameBoard < Dry::Struct
14
+ class BlackMarketOptionState
15
+ include Dry::Initializer.define -> do
16
+ option :next_game_state, type: ::Types::Callable, reader: :private
17
+ option :equipment_cost,
18
+ default: ->{{password: 1, disguise: 5, codebook: 15, key: 30}},
19
+ reader: :private,
20
+ type: ::Types::Hash.schema(
21
+ password: ::Types::CoercibleNonnegativeInteger,
22
+ disguise: ::Types::CoercibleNonnegativeInteger,
23
+ codebook: ::Types::CoercibleNonnegativeInteger,
24
+ key: ::Types::CoercibleNonnegativeInteger)
25
+ end
26
+
27
+ def call(game_board:)
28
+ player = game_board.players.find{|p| p.seat.eql?(game_board.game_state.seat)}
29
+ affordable_equipment = SpyAlleyApplication::Types::EquipmentType.values.select do |t|
30
+ player.money >= equipment_cost[t.to_sym]
31
+ end
32
+
33
+ equipment = affordable_equipment.map do |equipment_type|
34
+ SpyAlleyApplication::Types::Nationality.values.map do |nationality|
35
+ SpyAlleyApplication::Types::Equipment.call("#{nationality} #{equipment_type}")
36
+ end
37
+ end.flatten.sort.freeze
38
+
39
+ options = equipment - player.equipment
40
+
41
+ # only give buy option if player both has enough money and does not already all equipment
42
+ if options.empty?
43
+ return next_game_state.(game_board: game_board)
44
+ end
45
+ SpyAlleyApplication::Models::GameBoard::new(
46
+ players: game_board.players,
47
+ move_card_pile: game_board.move_card_pile,
48
+ free_gift_pile: game_board.free_gift_pile,
49
+ game_state: SpyAlleyApplication::Types::GameState.call(
50
+ name: 'buy_equipment',
51
+ seat: game_board.game_state.seat,
52
+ options: options,
53
+ limit: 1))
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+
@@ -0,0 +1,52 @@
1
+ # frozen string_literal: true
2
+
3
+ require 'dry-initializer'
4
+ require 'dry-struct'
5
+ require 'spy_alley_application/models/game_board'
6
+ require 'spy_alley_application/types/equipment'
7
+ require 'spy_alley_application/types/game_state'
8
+ require 'spy_alley_application/types/nationality'
9
+
10
+ module SpyAlleyApplication
11
+ module Models
12
+ class GameBoard < Dry::Struct
13
+ class BuyEquipmentOptionState
14
+ include Dry::Initializer.define -> do
15
+ option :next_game_state, type: ::Types::Callable, reader: :private
16
+ option :equipment_cost,
17
+ default: ->{{disguise: 5, codebook: 15, key: 30}},
18
+ reader: :private,
19
+ type: ::Types::Hash.schema(
20
+ disguise: ::Types::CoercibleNonnegativeInteger,
21
+ codebook: ::Types::CoercibleNonnegativeInteger,
22
+ key: ::Types::CoercibleNonnegativeInteger)
23
+ end
24
+
25
+ def call(game_board:, equipment_type:)
26
+ player = game_board.current_player
27
+ unit_cost = equipment_cost[equipment_type.to_sym]
28
+ equipment = SpyAlleyApplication::Types::Nationality.values.map do |nationality|
29
+ SpyAlleyApplication::Types::Equipment.call("#{nationality} #{equipment_type}")
30
+ end
31
+ options = (equipment - player.equipment).sort.freeze
32
+ limit = [player.money / unit_cost, options.length].min
33
+
34
+ # only give buy option if player both has enough money and does not already all equipment
35
+ if limit.eql?(0)
36
+ return next_game_state.(game_board: game_board)
37
+ end
38
+ SpyAlleyApplication::Models::GameBoard::new(
39
+ players: game_board.players,
40
+ move_card_pile: game_board.move_card_pile,
41
+ free_gift_pile: game_board.free_gift_pile,
42
+ game_state: SpyAlleyApplication::Types::GameState.call(
43
+ name: 'buy_equipment',
44
+ seat: game_board.game_state.seat,
45
+ options: options,
46
+ limit: limit))
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+
@@ -0,0 +1,39 @@
1
+ # frozen string_literal: true
2
+
3
+ require 'dry-struct'
4
+ require 'spy_alley_application/models/game_board'
5
+ require 'spy_alley_application/types/equipment'
6
+ require 'spy_alley_application/types/game_state'
7
+
8
+ module SpyAlleyApplication
9
+ module Models
10
+ class GameBoard < Dry::Struct
11
+ class BuyPasswordOptionState
12
+ include Dry::Initializer.define -> do
13
+ option :next_game_state, type: ::Types::Callable, reader: :private
14
+ option :password_cost, type: ::Types::CoercibleNonnegativeInteger, reader: :private,
15
+ default: ->{1}
16
+ end
17
+
18
+ def call(game_board:, nationality:)
19
+ equipment_to_add = SpyAlleyApplication::Types::Equipment.call("#{nationality} password")
20
+ player = game_board.players.find{|p| p.seat.eql?(game_board.game_state.seat)}
21
+ # only give buy option if player both has money and does not already have the password
22
+ if player.money < password_cost || player.equipment.any?{|e| e.eql?(equipment_to_add)}
23
+ return next_game_state.(game_board: game_board)
24
+ end
25
+ SpyAlleyApplication::Models::GameBoard::new(
26
+ players: game_board.players,
27
+ move_card_pile: game_board.move_card_pile,
28
+ free_gift_pile: game_board.free_gift_pile,
29
+ game_state: SpyAlleyApplication::Types::GameState.call(
30
+ name: 'buy_equipment',
31
+ seat: game_board.game_state.seat,
32
+ options: [equipment_to_add],
33
+ limit: 1))
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+
@@ -0,0 +1,68 @@
1
+ # frozen string_literal: true
2
+
3
+ require 'dry-initializer'
4
+ require 'dry-struct'
5
+ require 'spy_alley_application/models/game_board'
6
+ require 'spy_alley_application/types/equipment'
7
+ require 'spy_alley_application/types/equipment_type'
8
+ require 'spy_alley_application/types/game_state'
9
+ require 'spy_alley_application/types/nationality'
10
+
11
+ module SpyAlleyApplication
12
+ module Models
13
+ class GameBoard < Dry::Struct
14
+ class ConfiscateMaterialsOptionState
15
+ include Dry::Initializer.define -> do
16
+ option :next_game_state, type: ::Types::Callable, reader: :private
17
+ option :equipment_cost,
18
+ default: ->{{password: 5, disguise: 5, codebook: 10, key: 25, wild_card: 50}},
19
+ reader: :private,
20
+ type: ::Types::Hash.schema(
21
+ password: ::Types::CoercibleNonnegativeInteger,
22
+ disguise: ::Types::CoercibleNonnegativeInteger,
23
+ codebook: ::Types::CoercibleNonnegativeInteger,
24
+ key: ::Types::CoercibleNonnegativeInteger,
25
+ wild_card: ::Types::CoercibleNonnegativeInteger)
26
+ end
27
+
28
+ def call(game_board:)
29
+ player = game_board.players.find{|p| p.seat.eql?(game_board.game_state.seat)}
30
+ other_players = game_board.players.reject{|p| p.seat.eql?(game_board.game_state.seat)}
31
+
32
+ affordable_types = SpyAlleyApplication::Types::EquipmentType.values.select do |t|
33
+ player.money >= equipment_cost[t.to_sym]
34
+ end
35
+ can_afford_wild_card = (player.money > equipment_cost[:wild_card])
36
+
37
+ all_affordable_equipment = affordable_types.map do |equipment_type|
38
+ SpyAlleyApplication::Types::Nationality.values.map do |nationality|
39
+ SpyAlleyApplication::Types::Equipment.call("#{nationality} #{equipment_type}")
40
+ end
41
+ end.flatten.sort
42
+
43
+ targets = other_players.map do |p|
44
+ equipment = all_affordable_equipment.select{|e| p.equipment.include?(e)}
45
+ equipment.push('wild card') if p.wild_cards > 0 && can_afford_wild_card
46
+ [p.seat, equipment.freeze]
47
+ end.reject{|seat, equipment| equipment.empty?}.map do |seat, equipment|
48
+ {seat: seat, equipment: equipment}
49
+ end.freeze
50
+
51
+ # only give confiscation option if there are targets
52
+ if targets.empty?
53
+ return next_game_state.(game_board: game_board)
54
+ end
55
+ SpyAlleyApplication::Models::GameBoard::new(
56
+ players: game_board.players,
57
+ move_card_pile: game_board.move_card_pile,
58
+ free_gift_pile: game_board.free_gift_pile,
59
+ game_state: SpyAlleyApplication::Types::GameState.call(
60
+ name: 'confiscate_materials',
61
+ seat: game_board.game_state.seat,
62
+ targets: targets))
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+
@@ -0,0 +1,91 @@
1
+ # frozen string_literal: true
2
+
3
+ require 'dry-initializer'
4
+ require 'dry-struct'
5
+ require 'spy_alley_application/models/game_board'
6
+ require 'spy_alley_application/types/equipment'
7
+ require 'spy_alley_application/types/game_state'
8
+
9
+ module SpyAlleyApplication
10
+ module Models
11
+ class GameBoard < Dry::Struct
12
+ class EliminatePlayer
13
+ def call(game_board:, eliminating_player:, eliminated_player:)
14
+ unaffected_players = game_board
15
+ .players
16
+ .reject{|player| [eliminating_player, eliminated_player].include?(player)}
17
+
18
+ game_state = get_game_state(unaffected_players, eliminating_player)
19
+
20
+ eliminating_player = get_eliminating_player(eliminating_player, eliminated_player)
21
+
22
+ eliminated_player = get_eliminated_player(eliminated_player)
23
+
24
+ players = unaffected_players
25
+ .push(eliminating_player)
26
+ .push(eliminated_player)
27
+ .sort{|p, q| p.seat <=> q.seat}
28
+ .freeze
29
+
30
+ get_new_game_board(game_board, players, game_state)
31
+ end
32
+
33
+ private
34
+ def get_game_state(unaffected_players, eliminating_player)
35
+ # If none of the other players are in the game, then the game must be over
36
+ if unaffected_players.none?(&:active?)
37
+ {name: 'game_over', reason: {name: 'by_elimination'}, seat: eliminating_player.seat}
38
+ else
39
+ {
40
+ name: 'choose_new_spy_identity',
41
+ seat: eliminating_player.seat,
42
+ options: [eliminating_player.spy_identity, eliminated_player.spy_identity].freeze,
43
+ parent: game_board.game_state
44
+ }
45
+ end
46
+ end
47
+
48
+ def get_eliminating_player(eliminating_player, eliminated_player)
49
+ new_equipment = eliminating_player.equipment.map(&:to_s)
50
+ new_equipment = new_equipment + (eliminated_player.equipment.map(&:to_s))
51
+ new_equipment = new_equipment.uniq.sort
52
+ new_move_cards = eliminating_player.move_cards.map(&:value)
53
+ new_move_cards = new_move_cards + (eliminated_player.move_cards.map(&:value))
54
+ new_move_cards = new_move_cards.sort
55
+ new_money = eliminating_player.money + eliminated_player.money
56
+ new_wild_cards = eliminating_player.wild_cards + eliminated_player.wild_cards
57
+
58
+ new_values = eliminating_player.to_h
59
+ new_values[:equipment] = new_equipment
60
+ new_values[:money] = new_money
61
+ new_values[:move_cards] = new_move_cards
62
+ new_values[:wild_cards] = new_wild_cards
63
+
64
+ SpyAlleyApplication::Types::Player.call(new_values)
65
+ end
66
+
67
+ def get_eliminated_player(eliminated_player)
68
+ SpyAlleyApplication::Types::Player.call(
69
+ id: eliminated_player.id,
70
+ seat: eliminated_player.seat,
71
+ location: eliminated_player.location,
72
+ spy_identity: eliminated_player.spy_identity,
73
+ money: 0,
74
+ move_cards: [].freeze,
75
+ equipment: [].freeze,
76
+ wild_cards: 0,
77
+ active?: false)
78
+ end
79
+
80
+ def get_new_game_board(game_board, players, game_state)
81
+ SpyAlleyApplication::Models::GameBoard::new(
82
+ players: players,
83
+ move_card_pile: game_board.move_card_pile,
84
+ free_gift_pile: game_board.free_gift_pile,
85
+ game_state: game_state)
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+