love_letter_application 0.1.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 (83) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +7 -0
  5. data/Gemfile +5 -0
  6. data/Gemfile.lock +91 -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/love_letter_application.rb +7 -0
  13. data/lib/love_letter_application/action_creator.rb +16 -0
  14. data/lib/love_letter_application/actions/clown.rb +45 -0
  15. data/lib/love_letter_application/actions/general.rb +29 -0
  16. data/lib/love_letter_application/actions/knight.rb +31 -0
  17. data/lib/love_letter_application/actions/marquess.rb +28 -0
  18. data/lib/love_letter_application/actions/play_card.rb +33 -0
  19. data/lib/love_letter_application/actions/priestess.rb +36 -0
  20. data/lib/love_letter_application/actions/princess.rb +32 -0
  21. data/lib/love_letter_application/actions/soldier.rb +47 -0
  22. data/lib/love_letter_application/actions/wizard.rb +31 -0
  23. data/lib/love_letter_application/execute_action.rb +21 -0
  24. data/lib/love_letter_application/love_letter_imports.rb +641 -0
  25. data/lib/love_letter_application/models/card.rb +23 -0
  26. data/lib/love_letter_application/models/effects/discard_and_draw.rb +51 -0
  27. data/lib/love_letter_application/models/effects/eliminate_player.rb +43 -0
  28. data/lib/love_letter_application/models/effects/make_player_not_targetable.rb +44 -0
  29. data/lib/love_letter_application/models/effects/next_player_draw_card.rb +45 -0
  30. data/lib/love_letter_application/models/effects/play_card.rb +48 -0
  31. data/lib/love_letter_application/models/effects/round_complete.rb +29 -0
  32. data/lib/love_letter_application/models/effects/switch_hands.rb +50 -0
  33. data/lib/love_letter_application/models/game_board.rb +28 -0
  34. data/lib/love_letter_application/models/player.rb +36 -0
  35. data/lib/love_letter_application/results/eliminate_player.rb +43 -0
  36. data/lib/love_letter_application/results/nodes/card_played_node.rb +15 -0
  37. data/lib/love_letter_application/results/nodes/card_viewed_node.rb +21 -0
  38. data/lib/love_letter_application/results/nodes/discard_card_node.rb +20 -0
  39. data/lib/love_letter_application/results/nodes/drawn_card_node.rb +20 -0
  40. data/lib/love_letter_application/results/nodes/eliminated_player_node.rb +19 -0
  41. data/lib/love_letter_application/results/nodes/hands_switched_node.rb +22 -0
  42. data/lib/love_letter_application/results/nodes/knight_drawn_node.rb +15 -0
  43. data/lib/love_letter_application/results/nodes/knight_victory_node.rb +20 -0
  44. data/lib/love_letter_application/results/nodes/log_node.rb +20 -0
  45. data/lib/love_letter_application/results/nodes/next_player_node.rb +19 -0
  46. data/lib/love_letter_application/results/nodes/play_card_with_no_args_option_node.rb +19 -0
  47. data/lib/love_letter_application/results/nodes/play_card_with_player_id_and_card_id_option_node.rb +21 -0
  48. data/lib/love_letter_application/results/nodes/play_card_with_player_id_option_node.rb +20 -0
  49. data/lib/love_letter_application/results/nodes/player_not_targetable_node.rb +14 -0
  50. data/lib/love_letter_application/results/nodes/player_victory_node.rb +20 -0
  51. data/lib/love_letter_application/results/nodes/players_and_scores_node.rb +44 -0
  52. data/lib/love_letter_application/results/nodes/princess_discarded_node.rb +19 -0
  53. data/lib/love_letter_application/results/nodes/result_game_board_node.rb +19 -0
  54. data/lib/love_letter_application/results/process_correct_guess.rb +22 -0
  55. data/lib/love_letter_application/results/process_draw_after_discard.rb +26 -0
  56. data/lib/love_letter_application/results/process_incorrect_guess.rb +20 -0
  57. data/lib/love_letter_application/results/process_knight_showdown.rb +50 -0
  58. data/lib/love_letter_application/results/process_next_player_turn.rb +57 -0
  59. data/lib/love_letter_application/results/process_next_turn_options.rb +53 -0
  60. data/lib/love_letter_application/results/process_princess_discarded.rb +23 -0
  61. data/lib/love_letter_application/results/process_resolve_wizard.rb +36 -0
  62. data/lib/love_letter_application/results/process_round_complete_by_depleted_deck.rb +50 -0
  63. data/lib/love_letter_application/results/process_round_complete_by_elimination.rb +26 -0
  64. data/lib/love_letter_application/results/process_switch_hands.rb +51 -0
  65. data/lib/love_letter_application/results/yield_to_block.rb +12 -0
  66. data/lib/love_letter_application/types/card.rb +20 -0
  67. data/lib/love_letter_application/types/game_board.rb +59 -0
  68. data/lib/love_letter_application/types/player.rb +25 -0
  69. data/lib/love_letter_application/validator/base.rb +19 -0
  70. data/lib/love_letter_application/validator/builder.rb +50 -0
  71. data/lib/love_letter_application/validator/get_legal_card_ids.rb +23 -0
  72. data/lib/love_letter_application/validator/get_legal_card_ids/marquess.rb +18 -0
  73. data/lib/love_letter_application/validator/play_card.rb +21 -0
  74. data/lib/love_letter_application/validator/play_card/builder.rb +23 -0
  75. data/lib/love_letter_application/validator/play_card/no_options.rb +19 -0
  76. data/lib/love_letter_application/validator/play_card/select_target_player.rb +30 -0
  77. data/lib/love_letter_application/validator/play_card/select_target_player/builder.rb +44 -0
  78. data/lib/love_letter_application/validator/play_card/soldier.rb +39 -0
  79. data/lib/love_letter_application/validator/play_card/soldier/builder.rb +49 -0
  80. data/lib/love_letter_application/validator/validate_card_id.rb +23 -0
  81. data/lib/love_letter_application/version.rb +3 -0
  82. data/love_letter_application.gemspec +43 -0
  83. metadata +278 -0
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry-struct'
4
+
5
+ module LoveLetterApplication
6
+ module Models
7
+ class Card
8
+ include Dry::Initializer.define -> do
9
+ option :id, ::Types::Coercible::Integer
10
+ option :rank, ::Types::Coercible::Integer
11
+ option :targetable?, as: :targetable, type: ::Types::Strict::Bool, reader: false
12
+ end
13
+ def targetable?;@targetable;end
14
+ def inspect
15
+ "#<#{self.class} id=#{id}, rank=#{rank} targetable?=#{targetable?}>"
16
+ end
17
+ def to_h
18
+ {id: id, rank: rank, targetable?: targetable?}
19
+ end
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry-initializer'
4
+ require 'love_letter_application/models/game_board'
5
+ require 'love_letter_application/models/player'
6
+
7
+ module LoveLetterApplication
8
+ module Models
9
+ class Effects
10
+ class DiscardAndDraw
11
+ include Dry::Initializer.define -> do
12
+ option :null_card, type: LoveLetterApplication::Types::Card
13
+ end
14
+
15
+ def call(game_board:, player_id:)
16
+ draw_pile = game_board.draw_pile
17
+ sac = game_board.set_aside_card
18
+ player_replacement = get_replacement(game_board.players, player_id, draw_pile, sac)
19
+ players = game_board
20
+ .players
21
+ .reject{|player| player.id.to_i.eql?(player_id.to_i)}
22
+ .push(player_replacement)
23
+ .freeze
24
+
25
+ draw_pile = game_board.draw_pile
26
+ draw_pile = game_board.draw_pile[1..-1].map(&:itself).freeze unless draw_pile.empty?
27
+
28
+ LoveLetterApplication::Models::GameBoard::new(
29
+ players: players,
30
+ draw_pile: draw_pile,
31
+ set_aside_card: (game_board.draw_pile.empty? ? null_card : game_board.set_aside_card),
32
+ current_player_id: game_board.current_player_id.to_i,
33
+ game_state: game_board.game_state)
34
+ end
35
+
36
+ private
37
+ def get_replacement(players, player_id, draw_pile, set_aside_card)
38
+ player = players.find{|player| player.id.eql?(player_id.to_i)}
39
+ LoveLetterApplication::Models::Player::new(
40
+ id: player.id.to_i,
41
+ seat: player.seat.to_i,
42
+ played_cards: (player.played_cards + player.hand).freeze,
43
+ hand: [draw_pile.first || set_aside_card].freeze,
44
+ active?: player.active?,
45
+ targetable?: player.targetable?)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'love_letter_application/models/game_board'
4
+ require 'love_letter_application/models/player'
5
+
6
+ module LoveLetterApplication
7
+ module Models
8
+ class Effects
9
+ class EliminatePlayer
10
+ def call(game_board:, player_id:)
11
+ eliminated_player_replacement = get_replacement(game_board.players, player_id)
12
+
13
+ players = game_board
14
+ .players
15
+ .reject{|player| player.id.to_i.eql?(player_id.to_i)}
16
+ .push(eliminated_player_replacement)
17
+ .freeze
18
+
19
+ LoveLetterApplication::Models::GameBoard::new(
20
+ players: players,
21
+ draw_pile: game_board.draw_pile.map(&:itself).freeze,
22
+ set_aside_card: game_board.set_aside_card,
23
+ current_player_id: game_board.current_player_id.to_i,
24
+ game_state: game_board.game_state)
25
+ end
26
+
27
+ private
28
+ def get_replacement(players, player_id)
29
+ eliminated_player = players
30
+ .find{|player| player.id.to_i.eql?(player_id.to_i)}
31
+ LoveLetterApplication::Models::Player::new(
32
+ id: eliminated_player.id.to_i,
33
+ seat: eliminated_player.id.to_i,
34
+ played_cards: (eliminated_player.played_cards + eliminated_player.hand).freeze,
35
+ hand: [].freeze,
36
+ active?: false,
37
+ targetable?: false)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry-initializer'
4
+ require 'love_letter_application/models/game_board'
5
+ require 'love_letter_application/models/player'
6
+
7
+ module LoveLetterApplication
8
+ module Models
9
+ class Effects
10
+ class MakePlayerNotTargetable
11
+ def call(game_board:, player_id:)
12
+ player_replacement = get_replacement(game_board.players, player_id)
13
+
14
+ players = game_board
15
+ .players
16
+ .reject{|player| player.id.to_i.eql?(player_id.to_i)}
17
+ .push(player_replacement)
18
+
19
+ LoveLetterApplication::Models::GameBoard::new(
20
+ players: players,
21
+ draw_pile: game_board.draw_pile,
22
+ set_aside_card: game_board.set_aside_card,
23
+ current_player_id: game_board.current_player_id,
24
+ game_state: game_board.game_state)
25
+ end
26
+
27
+ private
28
+ def get_replacement(players, player_id)
29
+ modified_player = players
30
+ .find{|player| player.id.to_i.eql?(player_id.to_i)}
31
+
32
+ LoveLetterApplication::Models::Player::new(
33
+ id: modified_player.id.to_i,
34
+ seat: modified_player.id.to_i,
35
+ played_cards: modified_player.played_cards,
36
+ hand: modified_player.hand,
37
+ active?: modified_player.active?,
38
+ targetable?: false)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry-initializer'
4
+ require 'love_letter_application/models/game_board'
5
+ require 'love_letter_application/types/player'
6
+ require 'love_letter_application/types/game_board'
7
+
8
+ module LoveLetterApplication
9
+ module Models
10
+ class Effects
11
+ class NextPlayerDrawCard
12
+ def call(game_board:, next_player_id:)
13
+ draw_pile = game_board.draw_pile
14
+ player_replacement = get_replacement(game_board.players, next_player_id, draw_pile)
15
+ players = game_board
16
+ .players
17
+ .reject{|player| player.id.to_i.eql?(next_player_id.to_i)}
18
+ .push(player_replacement)
19
+ .freeze
20
+
21
+ draw_pile = game_board.draw_pile[1..-1].map(&:itself).freeze
22
+
23
+ LoveLetterApplication::Models::GameBoard::new(
24
+ players: players,
25
+ draw_pile: draw_pile,
26
+ set_aside_card: game_board.set_aside_card,
27
+ current_player_id: next_player_id,
28
+ game_state: LoveLetterApplication::Types::GameStateEnum.call('card_drawn'))
29
+ end
30
+
31
+ private
32
+ def get_replacement(players, player_id, draw_pile)
33
+ next_player = players.find{|player| player.id.eql?(player_id.to_i)}
34
+ LoveLetterApplication::Models::Player::new(
35
+ id: next_player.id.to_i,
36
+ seat: next_player.seat.to_i,
37
+ played_cards: next_player.played_cards.map(&:itself).freeze,
38
+ hand: next_player.hand.map(&:itself).push(draw_pile.first).freeze,
39
+ active?: next_player.active?,
40
+ targetable?: true) # limitations on being targeted expire when player's turn again
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'love_letter_application/models/game_board'
4
+ require 'love_letter_application/models/player'
5
+
6
+ module LoveLetterApplication
7
+ module Models
8
+ class Effects
9
+ class PlayCard
10
+ def call(game_board:, card_id:)
11
+ player_id = game_board.current_player_id.to_i
12
+ player_replacement = get_replacement(game_board.players, player_id, card_id)
13
+
14
+ players = game_board
15
+ .players
16
+ .reject{|player| player.id.to_i.eql?(player_id.to_i)}
17
+ .push(player_replacement)
18
+
19
+ LoveLetterApplication::Models::GameBoard::new(
20
+ players: players,
21
+ draw_pile: game_board.draw_pile,
22
+ set_aside_card: game_board.set_aside_card,
23
+ current_player_id: game_board.current_player_id,
24
+ game_state: game_board.game_state)
25
+ end
26
+
27
+ private
28
+ def get_replacement(players, player_id, card_id)
29
+ modified_player = players
30
+ .find{|player| player.id.to_i.eql?(player_id.to_i)}
31
+ played_card = modified_player.hand.find{|card| card.id.to_i.eql?(card_id.to_i)}
32
+ new_hand = modified_player.hand.map(&:itself)
33
+ new_hand.slice!(new_hand.index{|card| card.id.to_i.eql?(card_id.to_i)}).freeze
34
+ new_played_cards = ([played_card] + modified_player.played_cards.map(&:itself)).freeze
35
+
36
+ LoveLetterApplication::Models::Player::new(
37
+ id: modified_player.id.to_i,
38
+ seat: modified_player.id.to_i,
39
+ played_cards: new_played_cards,
40
+ hand: new_hand,
41
+ active?: modified_player.active?,
42
+ targetable?: modified_player.targetable?)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'love_letter_application/models/game_board'
4
+ require 'love_letter_application/types/game_board'
5
+
6
+ module LoveLetterApplication
7
+ module Models
8
+ class Effects
9
+ class RoundComplete
10
+ def call(game_board:, victorious_player_id:)
11
+ victorious_player_id = ::Types::Array::of(::Types::Strict::Integer)
12
+ .call(victorious_player_id)
13
+ next_player_id = game_board.players
14
+ .select{|player| victorious_player_id.include?(player.id.to_i)}
15
+ .max{|p, q| p.id.to_i <=> q.id.to_i}
16
+ .id
17
+
18
+ LoveLetterApplication::Models::GameBoard::new(
19
+ players: game_board.players,
20
+ draw_pile: game_board.draw_pile,
21
+ set_aside_card: game_board.set_aside_card,
22
+ current_player_id: next_player_id,
23
+ game_state: LoveLetterApplication::Types::GameStateEnum.call('round_complete'))
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'love_letter_application/models/game_board'
4
+ require 'love_letter_application/models/player'
5
+
6
+ module LoveLetterApplication
7
+ module Models
8
+ class Effects
9
+ class SwitchHands
10
+ def call(game_board:, player_id:, target_player_id:)
11
+ player_replacement = get_replacement(game_board.players, player_id, target_player_id)
12
+ target_replacement = get_replacement(game_board.players, target_player_id, player_id)
13
+
14
+ players = game_board
15
+ .players
16
+ .reject{|player| [player_id.to_i, target_player_id.to_i].include?(player.id.to_i)}
17
+ .push(player_replacement)
18
+ .push(target_replacement)
19
+ .freeze
20
+
21
+ LoveLetterApplication::Models::GameBoard::new(
22
+ players: players,
23
+ draw_pile: game_board.draw_pile.map(&:itself).freeze,
24
+ set_aside_card: game_board.set_aside_card,
25
+ current_player_id: game_board.current_player_id.to_i,
26
+ game_state: game_board.game_state)
27
+ end
28
+
29
+ private
30
+ def get_replacement(players, player_id, target_player_id)
31
+ player_to_replace = players
32
+ .find{|player| player.id.to_i.eql?(player_id.to_i)}
33
+ hand_to_replace_with = players
34
+ .find{|player| player.id.to_i.eql?(target_player_id.to_i)}
35
+ .hand
36
+ .map(&:itself)
37
+ .freeze
38
+ LoveLetterApplication::Models::Player::new(
39
+ id: player_to_replace.id.to_i,
40
+ seat: player_to_replace.id.to_i,
41
+ played_cards: player_to_replace.played_cards.map(&:itself).freeze,
42
+ hand: hand_to_replace_with,
43
+ active?: player_to_replace.active?,
44
+ targetable?: player_to_replace.targetable?)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry-struct'
4
+ require 'love_letter_application/types/game_board'
5
+ require 'love_letter_application/types/card'
6
+ require 'love_letter_application/types/player'
7
+
8
+ module LoveLetterApplication
9
+ module Models
10
+ class GameBoard < Dry::Struct
11
+ attribute :players, ::Types::Array::of(LoveLetterApplication::Types::Player)
12
+ attribute :draw_pile, ::Types::Array::of(LoveLetterApplication::Types::Card)
13
+ attribute :set_aside_card, LoveLetterApplication::Types::Card
14
+ attribute :current_player_id, ::Types::Coercible::Integer
15
+ attribute :game_state, LoveLetterApplication::Types::GameStateEnum
16
+ def to_h
17
+ {
18
+ players: players.map(&:to_h),
19
+ draw_pile: draw_pile.map(&:to_h),
20
+ set_aside_card: set_aside_card.to_h,
21
+ current_player_id: current_player_id,
22
+ game_state: game_state
23
+ }
24
+ end
25
+ end
26
+ end
27
+ end
28
+
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry-initializer'
4
+ require 'love_letter_application/types/card'
5
+
6
+ module LoveLetterApplication
7
+ module Models
8
+ class Player
9
+ include Dry::Initializer.define -> do
10
+ option :id, type: ::Types::Coercible::Integer
11
+ option :seat, type: ::Types::Coercible::Integer
12
+ option :played_cards, type: ::Types::Array::of(LoveLetterApplication::Types::Card)
13
+ option :hand, type: ::Types::Array::of(LoveLetterApplication::Types::Card)
14
+ option :active?, as: :active, type: ::Types::Strict::Bool, reader: false
15
+ option :targetable?, as: :targetable, type: ::Types::Strict::Bool, reader: false
16
+ end
17
+ def active?;@active;end
18
+ def targetable?;@targetable;end
19
+ def inspect
20
+ "#<#{self.class} id=#{id}, seat=#{seat} played_cards=#{played_cards} hand=#{hand} " +
21
+ "targetable?=#{targetable?} active?=#{active?}>"
22
+ end
23
+ def to_h
24
+ {
25
+ id: id,
26
+ seat: seat,
27
+ played_cards: played_cards.map(&:to_h),
28
+ hand: hand.map(&:to_h),
29
+ targetable?: targetable?,
30
+ active?: active?
31
+ }
32
+ end
33
+ end
34
+ end
35
+ end
36
+
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry-initializer'
4
+
5
+ module LoveLetterApplication
6
+ module Results
7
+ class EliminatePlayer
8
+ extend Dry::Initializer
9
+ option :process_round_complete_by_elimination, type: ::Types::Callable
10
+ option :eliminate_player_from_game_board, ::Types::Callable
11
+ option :process_next_player_turn, type: ::Types::Callable
12
+ option :get_eliminated_player_node, type: ::Types::Callable
13
+
14
+ def call(target_player_id:, game_board:, change_orders:)
15
+ change_orders = change_orders
16
+ .push(get_eliminated_player_node.(player_id: target_player_id))
17
+ game_board = eliminate_player_from_game_board.(
18
+ game_board: game_board,
19
+ player_id: target_player_id)
20
+ if one_player_remaining?(game_board)
21
+ process_round_complete_by_elimination.(
22
+ game_board: game_board,
23
+ change_orders: change_orders,
24
+ player: game_board
25
+ .players
26
+ .find(&:active?))
27
+ else
28
+ process_next_player_turn.(game_board: game_board, change_orders: change_orders)
29
+ end
30
+ end
31
+
32
+ private
33
+ def one_player_remaining?(game_board)
34
+ game_board
35
+ .players
36
+ .select(&:active?)
37
+ .length
38
+ .eql?(1)
39
+ end
40
+ end
41
+ end
42
+ end
43
+