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,23 @@
1
+ # frozen string_literal: true
2
+
3
+ require 'dry-initializer'
4
+ require 'spy_alley_application/types/game_board'
5
+
6
+ module SpyAlleyApplication
7
+ module Models
8
+ class GameBoard < Dry::Struct
9
+ class EmbassyVictory
10
+ def call(game_board:)
11
+ player = game_board.players.find{|p| p.seat.eql?(game_board.game_state.seat)}
12
+ other_players = game_board.players.reject{|p| p.seat.eql?(game_board.game_state.seat)}
13
+
14
+ game_state = {name: 'game_over', reason: {name: 'by_embassy'}, seat: player.seat}
15
+
16
+ SpyAlleyApplication::Types::GameBoard.call(
17
+ game_board.to_h.tap{|g| g[:game_state] = game_state})
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,42 @@
1
+ # frozen string_literal: true
2
+
3
+ require 'dry-initializer'
4
+ require 'dry-struct'
5
+ require 'spy_alley_application/types/equipment'
6
+ require 'spy_alley_application/types/game_board'
7
+
8
+ module SpyAlleyApplication
9
+ module Models
10
+ class GameBoard < Dry::Struct
11
+ class EquipmentBought
12
+ include Dry::Initializer.define -> do
13
+ option :equipment_cost,
14
+ default: ->{{password: 1, disguise: 5, codebook: 15, key: 30}},
15
+ type: ::Types::Hash.schema(
16
+ password: ::Types::CoercibleNonnegativeInteger,
17
+ disguise: ::Types::CoercibleNonnegativeInteger,
18
+ codebook: ::Types::CoercibleNonnegativeInteger,
19
+ key: ::Types::CoercibleNonnegativeInteger)
20
+ end
21
+
22
+ def call(game_board:, equipment_bought:)
23
+ player = game_board.current_player
24
+ total_cost = 0
25
+ equipment_bought = equipment_bought
26
+ .map{|equipment| SpyAlleyApplication::Types::Equipment.call(equipment)}
27
+ equipment_bought.each{|equipment| total_cost += equipment_cost[equipment.type.to_sym]}
28
+
29
+ unaffected_players = game_board.players.reject{|p| p.equal?(player)}
30
+ player = player.to_h.tap do |p|
31
+ p[:equipment] = (p[:equipment] + equipment_bought).sort
32
+ p[:money] = p[:money] - total_cost
33
+ end
34
+ players = unaffected_players.push(player).sort_by{|p| p[:seat]}
35
+ SpyAlleyApplication::Types::GameBoard.call(
36
+ game_board.to_h.tap{|g| g[:players] = players})
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+
@@ -0,0 +1,67 @@
1
+ # frozen string_literal: true
2
+
3
+ require 'dry-initializer'
4
+ require 'dry-struct'
5
+ require 'spy_alley_application/types/game_board'
6
+
7
+ module SpyAlleyApplication
8
+ module Models
9
+ class GameBoard < Dry::Struct
10
+ class EquipmentConfiscated
11
+ include Dry::Initializer.define -> do
12
+ option :equipment_cost,
13
+ default: ->{{password: 5, disguise: 5, codebook: 10, key: 25, wild_card: 50}},
14
+ reader: :private,
15
+ type: ::Types::Hash.schema(
16
+ password: ::Types::CoercibleNonnegativeInteger,
17
+ disguise: ::Types::CoercibleNonnegativeInteger,
18
+ codebook: ::Types::CoercibleNonnegativeInteger,
19
+ key: ::Types::CoercibleNonnegativeInteger,
20
+ wild_card: ::Types::CoercibleNonnegativeInteger)
21
+ end
22
+
23
+ def call(game_board:, target_player:, equipment:)
24
+ player = game_board.current_player
25
+ unaffected_players = game_board
26
+ .players
27
+ .reject{|player| [player, target_player].include?(player)}
28
+ wild_card = equipment.accept(self).eql?(:wild_card)
29
+ cost = equipment_cost[equipment.accept(self)]
30
+
31
+ player_equipment = player.equipment.map(&:itself)
32
+ player_equipment = (player_equipment + [equipment]).sort unless wild_card
33
+ target_equipment = target_player.equipment.map(&:itself)
34
+ target_equipment = (target_equipment - [equipment]).sort unless wild_card
35
+
36
+ player = player.to_h
37
+ player[:money] -= cost
38
+ player[:equipment] = player_equipment.freeze
39
+ player[:wild_cards] += 1 if wild_card
40
+
41
+ target = target_player.to_h
42
+ target[:money] += cost
43
+ target[:equipment] = target_equipment.freeze
44
+ target[:wild_cards] -= 1 if wild_card
45
+
46
+ players = unaffected_players
47
+ .push(player)
48
+ .push(target)
49
+ .sort_by{|p| p[:seat]}
50
+ .freeze
51
+
52
+ SpyAlleyApplication::Types::GameBoard.call(
53
+ game_board.to_h.tap{|g| g[:players] = players})
54
+ end
55
+
56
+ def handle_equipment(equipment, **args)
57
+ equipment.type.to_sym
58
+ end
59
+
60
+ def handle_wild_card(*args)
61
+ :wild_card
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+
@@ -0,0 +1,39 @@
1
+ # frozen string_literal: true
2
+
3
+ require 'dry-struct'
4
+ require 'spy_alley_application/types/game_board'
5
+
6
+ module SpyAlleyApplication
7
+ module Models
8
+ class GameBoard < Dry::Struct
9
+ class FreeGiftDrawn
10
+ def call(game_board:)
11
+ player = game_board.players.find{|p| p.seat.eql?(game_board.game_state.seat)}
12
+ unaffected_players = game_board.players.reject{|p| p.equal?(player)}
13
+ free_gift_pile = game_board.free_gift_pile
14
+ top_free_gift = free_gift_pile.first
15
+ if top_free_gift.wild_card?
16
+ player = player.to_h.tap{|p| p[:wild_cards] += 1}.to_h
17
+ free_gift_pile = free_gift_pile[1..-1]
18
+ else
19
+ player = add_equipment(player, top_free_gift)
20
+ free_gift_pile = free_gift_pile[1..-1].push(top_free_gift).freeze
21
+ end
22
+ players = unaffected_players.push(player).sort{|p, q| p[:seat] <=> q[:seat]}
23
+ SpyAlleyApplication::Types::GameBoard.call(
24
+ game_board.to_h.tap{|g| g[:players] = players; g[:free_gift_pile] = free_gift_pile})
25
+ end
26
+
27
+ private
28
+ def add_equipment(player, equipment)
29
+ if player.equipment.none?{|e| e.equal?(equipment)}
30
+ player.to_h.tap{|p| p[:equipment] = p[:equipment].push(equipment).sort}
31
+ else
32
+ player
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+
@@ -0,0 +1,22 @@
1
+ # frozen string_literal: true
2
+
3
+ require 'dry-struct'
4
+ require 'spy_alley_application/types/game_board'
5
+
6
+ module SpyAlleyApplication
7
+ module Models
8
+ class GameBoard < Dry::Struct
9
+ class MoneyGainedOrLost
10
+ def call(game_board:, money_adjustment:)
11
+ player = game_board.players.find{|p| p.seat.eql?(game_board.game_state.seat)}
12
+ unaffected_players = game_board.players.reject{|p| p.equal?(player)}
13
+ player = player.to_h.tap{|p| p[:money] = p[:money] + money_adjustment}
14
+ players = unaffected_players.push(player).sort{|p, q| p[:seat] <=> q[:seat]}
15
+ SpyAlleyApplication::Types::GameBoard.call(
16
+ game_board.to_h.tap{|g| g[:players] = players})
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+
@@ -0,0 +1,31 @@
1
+ # frozen string_literal: true
2
+
3
+ require 'dry-initializer'
4
+ require 'dry-struct'
5
+ require 'spy_alley_application/types/game_board'
6
+
7
+ module SpyAlleyApplication
8
+ module Models
9
+ class GameBoard < Dry::Struct
10
+ class MoveCardDrawn
11
+ def call(game_board:)
12
+ player = game_board.current_player
13
+ unaffected_players = game_board.players.reject{|p| p.equal?(player)}
14
+ move_card_pile = game_board.move_card_pile
15
+ # put the first card in the player's move card hand
16
+ player = player.to_h.tap do |p|
17
+ move_cards = (p[:move_cards] + [move_card_pile.first.value]).sort
18
+ p[:move_cards] = move_cards
19
+ end
20
+ # and remove it from the move card pile
21
+ move_card_pile = move_card_pile[1..-1].freeze
22
+ players = unaffected_players.push(player).sort{|p, q| p[:seat] <=> q[:seat]}
23
+
24
+ SpyAlleyApplication::Types::GameBoard.call(
25
+ game_board.to_h.tap{|g| g[:players] = players; g[:move_card_pile] = move_card_pile})
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+
@@ -0,0 +1,30 @@
1
+ # frozen string_literal: true
2
+
3
+ require 'dry-struct'
4
+ require 'spy_alley_application/types/game_board'
5
+
6
+ module SpyAlleyApplication
7
+ module Models
8
+ class GameBoard < Dry::Struct
9
+ class MoveCardUsed
10
+ def call(game_board:, move_card_used:)
11
+ player = game_board.current_player
12
+ unaffected_players = game_board.players.reject{|p| p.equal?(player)}
13
+ move_card_pile = game_board.move_card_pile
14
+ # remove one copyof the move_card_used from the player's move card hand
15
+ player = player.to_h.tap do |p|
16
+ p[:move_cards].delete_at(p[:move_cards].index(move_card_used))
17
+ p[:move_cards] = p[:move_cards].sort
18
+ end
19
+ # and place at the bottom (end) of the move card pile
20
+ move_card_pile = (move_card_pile + [move_card_used]).freeze
21
+ players = unaffected_players.push(player).sort{|p, q| p[:seat] <=> q[:seat]}
22
+
23
+ SpyAlleyApplication::Types::GameBoard.call(
24
+ game_board.to_h.tap{|g| g[:players] = players; g[:move_card_pile] = move_card_pile})
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+
@@ -0,0 +1,23 @@
1
+ # frozen string_literal: true
2
+
3
+ require 'dry-struct'
4
+ require 'spy_alley_application/types/game_board'
5
+
6
+ module SpyAlleyApplication
7
+ module Models
8
+ class GameBoard < Dry::Struct
9
+ class MoveOptions
10
+ def call(game_board:, options:)
11
+ game_state = {
12
+ name: 'move_option',
13
+ seat: game_board.game_state.seat,
14
+ options: options
15
+ }
16
+ SpyAlleyApplication::Types::GameBoard.call(
17
+ game_board.to_h.tap{|g| g[:game_state] = game_state})
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,37 @@
1
+ # frozen string_literal: true
2
+
3
+ require 'dry-initializer'
4
+ require 'dry-struct'
5
+ require 'spy_alley_application/types/game_board'
6
+
7
+ module SpyAlleyApplication
8
+ module Models
9
+ class GameBoard < Dry::Struct
10
+ class NewSpyIdentityChosen
11
+ def call(game_board:, new_spy_identity:)
12
+ player = game_board.current_player
13
+ if !player.spy_identity.eql?(new_spy_identity)
14
+ eliminated_player = game_board.players.find{|p| p.spy_identity.eql?(new_spy_identity)}
15
+ unaffected_players = game_board
16
+ .players
17
+ .reject{|p| [player, eliminated_player].include?(p)}
18
+
19
+ eliminated_player =
20
+ eliminated_player.to_h.tap{|p| p[:spy_identity] = player.spy_identity}
21
+ player = player.to_h.tap{|p| p[:spy_identity] = new_spy_identity}
22
+
23
+ players = unaffected_players
24
+ players.push(player)
25
+ players.push(eliminated_player)
26
+ players = players.sort_by{|p| p[:seat]}
27
+
28
+ game_board = SpyAlleyApplication::Types::GameBoard.call(
29
+ game_board.to_h.tap{|b| b[:players] = players})
30
+ end
31
+ game_board
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+
@@ -0,0 +1,70 @@
1
+ # frozen string_literal: true
2
+
3
+ require 'dry-struct'
4
+ require 'spy_alley_application/models/game_board'
5
+ require 'spy_alley_application/types/game_state'
6
+
7
+ module SpyAlleyApplication
8
+ module Models
9
+ class GameBoard < Dry::Struct
10
+ class NextGameState
11
+ def call(game_board:, target_player_id: nil)
12
+ args = {game_board: game_board}
13
+ args[:target_player_id] = target_player_id if target_player_id
14
+ SpyAlleyApplication::Models::GameBoard::new(
15
+ players: game_board.players,
16
+ move_card_pile: game_board.move_card_pile,
17
+ free_gift_pile: game_board.free_gift_pile,
18
+ game_state: game_board.game_state.accept(self, args))
19
+ end
20
+
21
+ def handle_start_of_turn(node, game_board:)
22
+ all_seats = game_board
23
+ .players
24
+ .select(&:active?)
25
+ .map(&:seat)
26
+ .reject{|seat| seat.eql?(node.seat)}
27
+
28
+ next_seat = all_seats.select{|seat| seat > node.seat}.min || all_seats.min
29
+
30
+ SpyAlleyApplication::Types::GameState.call(
31
+ name: 'start_of_turn',
32
+ seat: next_seat)
33
+ end
34
+ alias_method :handle_buy_equipment, :handle_start_of_turn
35
+ alias_method :handle_move_action, :handle_start_of_turn
36
+ alias_method :handle_confiscate_materials, :handle_start_of_turn
37
+
38
+ def handle_choose_new_spy_identity(node, game_board:)
39
+ node.parent.accept(self, game_board: game_board)
40
+ end
41
+
42
+ def handle_spy_eliminator(node, game_board:, target_player_id: nil)
43
+ targeted_player_id = game_board
44
+ .players
45
+ .map(&:id)
46
+ .select{|id| id.eql?(target_player_id)}
47
+
48
+ remaining_seats = game_board
49
+ .players
50
+ .select(&:active?)
51
+ .reject{|p| p.id.eql?(target_player_id)}
52
+ .map(&:seat)
53
+ .select{|seat| node.targetable_seats.include?(seat)}
54
+ .sort
55
+
56
+ if remaining_seats.empty?
57
+ node.parent.accept(self, game_board: game_board)
58
+ else
59
+ SpyAlleyApplication::Types::GameState.call(
60
+ name: 'spy_eliminator',
61
+ seat: node.seat,
62
+ targetable_seats: remaining_seats,
63
+ parent: node.parent)
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+
@@ -0,0 +1,24 @@
1
+ # frozen string_literal: true
2
+
3
+ require 'dry-struct'
4
+ require 'spy_alley_application/types/game_board'
5
+
6
+ module SpyAlleyApplication
7
+ module Models
8
+ class GameBoard < Dry::Struct
9
+ class PlayerMoved
10
+ def call(game_board:, new_location:)
11
+ player = game_board.players.find{|p| p.seat.eql?(game_board.game_state.seat)}
12
+ unaffected_players = game_board.players.reject{|p| p.equal?(player)}
13
+ player = player.to_h.tap{|p| p[:location] = new_location}
14
+ players = unaffected_players.push(player).sort{|p, q| p[:seat] <=> q[:seat]}
15
+ game_board = SpyAlleyApplication::Types::GameBoard.call(
16
+ game_board.to_h.tap{|g| g[:players] = players})
17
+ game_board = yield(game_board: game_board) if block_given?
18
+ game_board
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+
@@ -0,0 +1,41 @@
1
+ # frozen string_literal: true
2
+
3
+ require 'dry-initializer'
4
+ require 'dry-struct'
5
+ require 'spy_alley_application/types/game_board'
6
+
7
+ module SpyAlleyApplication
8
+ module Models
9
+ class GameBoard < Dry::Struct
10
+ class SpyEliminatorOptions
11
+ include Dry::Initializer.define -> do
12
+ option :next_game_state, type: ::Types::Callable, reader: :private
13
+ end
14
+
15
+ def call(game_board:)
16
+ player = game_board.players.find{|p| p.seat.eql?(game_board.game_state.seat)}
17
+ targetable_seats = game_board.players
18
+ .select(&:in_spy_alley?)
19
+ .reject{|p| p.equal?(player)}
20
+ .map(&:seat)
21
+ .sort
22
+ .freeze
23
+
24
+ if targetable_seats.empty?
25
+ return next_game_state.(game_board: game_board)
26
+ end
27
+
28
+ game_state = {
29
+ name: 'spy_eliminator',
30
+ seat: game_board.game_state.seat,
31
+ targetable_seats: targetable_seats,
32
+ parent: game_board.game_state
33
+ }
34
+ SpyAlleyApplication::Types::GameBoard.call(
35
+ game_board.to_h.tap{|g| g[:game_state] = game_state})
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+