love_letter_application 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+