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,25 @@
1
+ #frozen string_literal: true
2
+
3
+ require 'dry-initializer'
4
+ require 'love_letter_application/types/card'
5
+
6
+ module LoveLetterApplication
7
+ module Types
8
+ i = Class::new do
9
+ @@has_player_methods = ::Types.Interface(:id, :seat, :played_cards, :hand, :active?)
10
+
11
+ def call(player)
12
+ @@has_player_methods.(player)
13
+ ::Types::Coercible::Integer.call(player.id)
14
+ ::Types::Coercible::Integer.call(player.seat)
15
+ ::Types::Array::of(LoveLetterApplication::Types::Card).call(player.played_cards)
16
+ ::Types::Array::of(LoveLetterApplication::Types::Card).call(player.hand)
17
+ ::Types::Strict::Bool.call(player.active?)
18
+ player
19
+ end
20
+ end.new
21
+
22
+ LoveLetterApplication::Types::Player = ::Types::Constructor(Class){|value| i.call(value)}
23
+ end
24
+ end
25
+
@@ -0,0 +1,19 @@
1
+ #frozen string_literal: true
2
+
3
+ require 'dry-initializer'
4
+
5
+ module LoveLetterApplication
6
+ class Validator
7
+ class Base
8
+ extend Dry::Initializer
9
+ option :validate_input, type: Types.Interface(:call)
10
+ option :wrap_result, type: Types.Interface(:call)
11
+
12
+ def call(action_hash)
13
+ result = validate_input.(action_hash)
14
+ return result if result.failure?
15
+ wrap_result.(result)
16
+ end
17
+ end
18
+ end
19
+
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry-initializer'
4
+ require 'love_letter_application/validator/play_card'
5
+
6
+ module LoveLetterApplication
7
+ module Validator
8
+ class Builder
9
+ include Dry::Initializer.define -> do
10
+ option :all_card_validator_builders, type: ::Types::ArrayOfCallable
11
+ option :get_legal_card_ids, type: ::Types::Callable
12
+ end
13
+
14
+ def call(game_board:, last_action_id:)
15
+ current_player = game_board.players.find do |player|
16
+ player.id.to_i.eql?(game_board.current_player_id.to_i)
17
+ end
18
+
19
+ validate_player_action_and_user = GameValidator::Validator::Base::new(
20
+ legal_options: ['play_card'],
21
+ current_player_id: game_board.current_player_id,
22
+ last_action_id: last_action_id)
23
+
24
+ legal_card_ids = get_legal_card_ids.(card_list: current_player.hand)
25
+
26
+ validate_card_id = ValidateCardId::new(legal_card_ids: legal_card_ids)
27
+
28
+ card_validators = all_card_validator_builders
29
+ .map
30
+ .with_index{|v, k| [k, v]}
31
+ .to_h
32
+ .select{|k, v| legal_card_ids.include?(k)}
33
+ .map{|k, v| [k, v.call(game_board)]}
34
+ .to_h
35
+
36
+ play_card = PlayCard::new(
37
+ validate_card_id: validate_card_id,
38
+ full_validator_for: card_validators)
39
+
40
+ full_validator_for =
41
+ {['play_card', true] => play_card, ['play_card', false] => play_card}
42
+
43
+ validate_input = GameValidator::Validator::new(
44
+ validate_player_action_and_user: validate_player_action_and_user,
45
+ full_validator_for: full_validator_for)
46
+ end
47
+ end
48
+ end
49
+ end
50
+
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry-initializer'
4
+
5
+ module LoveLetterApplication
6
+ module Validator
7
+ class GetLegalCardIds
8
+ include Dry::Initializer.define -> do
9
+ option :validate_card_combo_for, type: ::Types::ArrayOfCallable
10
+ end
11
+
12
+ def call(card_list:)
13
+ legal_card_ids = ::Types::ArrayOfStrictInteger.call(card_list.map{|c| c.id.to_i})
14
+ card_list.uniq.each do |card|
15
+ legal_card_ids = legal_card_ids
16
+ .&(validate_card_combo_for[card.id.to_i].call(card_list: card_list))
17
+ end
18
+ legal_card_ids.sort
19
+ end
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LoveLetterApplication
4
+ module Validator
5
+ class GetLegalCardIds
6
+ class Marquess
7
+ def call(card_list:)
8
+ if card_list.map{|card| card.rank.to_i}.sum >= 12
9
+ card_list.map{|card| card.id.to_i}.select{|card_id| card_id.eql?(7)}
10
+ else
11
+ card_list.map{|card| card.id.to_i}
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+
@@ -0,0 +1,21 @@
1
+ #frozen string_literal: true
2
+
3
+ require 'dry-initializer'
4
+
5
+ module LoveLetterApplication
6
+ module Validator
7
+ class PlayCard
8
+ extend Dry::Initializer
9
+ option :validate_card_id, type: ::Types::Callable
10
+ option :full_validator_for, type: ::Types::Hash
11
+
12
+ def call(action_hash)
13
+ result = validate_card_id.(action_hash)
14
+ return result if result.failure?
15
+ validate = full_validator_for[result[:card_id]]
16
+ validate.(action_hash)
17
+ end
18
+ end
19
+ end
20
+ end
21
+
@@ -0,0 +1,23 @@
1
+ #frozen string_literal: true
2
+
3
+ require 'dry-initializer'
4
+ require 'game_validator/validator/validate_to_action'
5
+
6
+ module LoveLetterApplication
7
+ module Validator
8
+ class PlayCard
9
+ class Builder
10
+ extend Dry::Initializer
11
+ option :build_raw_validator, type: ::Types::Callable
12
+ option :wrap_result, type: ::Types::Callable
13
+
14
+ def call(model)
15
+ GameValidator::Validator::ValidateToAction::new(
16
+ validate: build_raw_validator.(model),
17
+ wrap: wrap_result)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,19 @@
1
+ #frozen string_literal: true
2
+
3
+ require 'dry-validation'
4
+
5
+ module LoveLetterApplication
6
+ module Validator
7
+ class PlayCard
8
+ class NoOptions < Dry::Validation::Contract
9
+ params{}
10
+
11
+ def accept(visitor, **args)
12
+ visitor = ::Types.Interface(:handle_no_options_validator).call(visitor)
13
+ visitor.handle_no_options_validator(self, args)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+
@@ -0,0 +1,30 @@
1
+ #frozen string_literal: true
2
+
3
+ require 'dry-validation'
4
+
5
+ module LoveLetterApplication
6
+ module Validator
7
+ class PlayCard
8
+ class SelectTargetPlayer < Dry::Validation::Contract
9
+ option :legal_target_player_ids, type: ::Types::Array.of(::Types::Coercible::Integer)
10
+
11
+ params do
12
+ required(:target_player_id).filled(:integer)
13
+ end
14
+
15
+ rule(:target_player_id) do
16
+ if !legal_target_player_ids.include?(values[:target_player_id])
17
+ key.failure({text: "target_player_id '#{values[:target_player_id]}' is not permitted",
18
+ status: 422})
19
+ end
20
+ end
21
+
22
+ def accept(visitor, **args)
23
+ visitor = ::Types.Interface(:handle_select_player_id_validator).call(visitor)
24
+ visitor.handle_select_player_id_validator(self, args)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+
@@ -0,0 +1,44 @@
1
+ #frozen string_literal: true
2
+
3
+ require 'dry-initializer'
4
+ require 'dry-validation'
5
+ require 'love_letter_application/validator/play_card/select_target_player'
6
+
7
+ module LoveLetterApplication
8
+ module Validator
9
+ class PlayCard
10
+ class SelectTargetPlayer < Dry::Validation::Contract
11
+ class Builder
12
+ extend Dry::Initializer
13
+ option :target_self?, as: :target_self, type: ::Types::Strict::Bool
14
+ option :no_options_validator, type: ::Types::Callable
15
+
16
+ def call(model)
17
+ legal_target_player_ids = get_legal_target_player_ids(model)
18
+ if legal_target_player_ids.empty?
19
+ no_options_validator
20
+ else
21
+ LoveLetterApplication::Validator::PlayCard::SelectTargetPlayer::new(
22
+ legal_target_player_ids: legal_target_player_ids)
23
+ end
24
+ end
25
+
26
+ private
27
+ def get_legal_target_player_ids(model)
28
+ legal_target_players = model.players
29
+ .select(&:active?) # must not be an eliminated player
30
+ .select(&:targetable?) # can't have immunity to targeting (e.g. with priestess)
31
+ if !target_self?
32
+ legal_target_players = legal_target_players
33
+ .reject{|p| p.id.to_i.eql?(model.current_player_id.to_i)} # can't target self
34
+ end
35
+ legal_target_players.map{|p| p.id.to_i}
36
+ end
37
+
38
+ def target_self?;@target_self;end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+
@@ -0,0 +1,39 @@
1
+ #frozen string_literal: true
2
+
3
+ require 'dry-validation'
4
+
5
+ module LoveLetterApplication
6
+ module Validator
7
+ class PlayCard
8
+ class Soldier < Dry::Validation::Contract
9
+ option :legal_target_player_ids, type: ::Types::Array.of(::Types::Coercible::Integer)
10
+ option :legal_target_card_ids, type: ::Types::Array.of(::Types::Coercible::Integer)
11
+
12
+ params do
13
+ required(:target_player_id).filled(:integer)
14
+ required(:target_card_id).filled(:integer)
15
+ end
16
+
17
+ rule(:target_player_id) do
18
+ if !legal_card_ids.include?(values[:target_player_id])
19
+ key.failure({text: "target_player_id '#{values[:target_player_id]}' is not permitted",
20
+ status: 422})
21
+ end
22
+ end
23
+
24
+ rule(:target_card_id) do
25
+ if !legal_card_ids.include?(values[:target_card_id])
26
+ key.failure({text: "target_card_id '#{values[:target_card_id]}' is not permitted",
27
+ status: 422})
28
+ end
29
+ end
30
+
31
+ def accept(visitor, **args)
32
+ visitor = ::Types.Interface(:handle_soldier_validator).call(visitor)
33
+ visitor.handle_soldier_validator(self, args)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+
@@ -0,0 +1,49 @@
1
+ #frozen string_literal: true
2
+
3
+ require 'dry-validation'
4
+ require 'dry-initializer'
5
+ require 'love_letter_application/validator/play_card/soldier'
6
+
7
+ module LoveLetterApplication
8
+ module Validator
9
+ class PlayCard
10
+ class Soldier < Dry::Validation::Contract
11
+ class Builder
12
+ include Dry::Initializer.define -> do
13
+ option :no_options_validator, type: ::Types::Callable
14
+ end
15
+
16
+ def call(model)
17
+ card_id = LoveLetterApplication::Actions::Soldier::id,
18
+ legal_target_player_ids = get_legal_target_player_ids(model)
19
+ if legal_target_player_ids.empty?
20
+ no_options_validator
21
+ else
22
+ LoveLetterApplication::Validator::PlayCard::Soldier::new(
23
+ legal_target_player_ids: legal_target_player_ids,
24
+ legal_target_card_ids: get_legal_target_card_ids(model))
25
+ end
26
+ end
27
+
28
+ private
29
+ def get_legal_target_player_ids(model)
30
+ model.players
31
+ .select(&:active?) # must not be an eliminated player
32
+ .select(&:targetable?) # can't have immunity to targeting (e.g. with priestess)
33
+ .reject{|p| p.id.to_i.eql?(model.current_player_id.to_i)} # can't target self
34
+ .map{|p| p.id.to_i}
35
+ end
36
+
37
+ def get_legal_target_card_ids(model)
38
+ legal_target_cards = [model.set_aside_card] + model.draw_pile
39
+ model.players.each{|p| legal_target_cards += p.played_cards + p.hand}
40
+ legal_target_cards.select(&:targetable?)
41
+ .map{|c| c.id.to_i}
42
+ .uniq
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+
@@ -0,0 +1,23 @@
1
+ #frozen string_literal: true
2
+
3
+ require 'dry-validation'
4
+
5
+ module LoveLetterApplication
6
+ module Validator
7
+ class ValidateCardId < Dry::Validation::Contract
8
+ option :legal_card_ids, type: ::Types::Array.of(::Types::Coercible::Integer)
9
+
10
+ params do
11
+ required(:player_action).filled(:string, eql?: 'play_card')
12
+ required(:card_id).filled(:integer)
13
+ end
14
+
15
+ rule(:card_id) do
16
+ if !legal_card_ids.include?(values[:card_id])
17
+ key.failure({text: "card_id '#{values[:card_id]}' is not permitted", status: 400})
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,3 @@
1
+ module LoveLetterApplication
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,43 @@
1
+ lib = File.expand_path("lib", __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "love_letter_application/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'love_letter_application'
7
+ spec.version = LoveLetterApplication::VERSION
8
+ spec.authors = ['Colin Horner']
9
+ spec.email = ['25807014+cmcolinh@users.noreply.github.com']
10
+
11
+ spec.summary = 'Love Letter application'
12
+ spec.description = 'Runs the application'
13
+ #spec.homepage = "TODO: Put your gem's website or public repo URL here."
14
+ spec.license = 'MIT'
15
+
16
+ #spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
17
+
18
+ #spec.metadata["homepage_uri"] = spec.homepage
19
+ #spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
20
+ #spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
25
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
+ end
27
+ spec.bindir = 'exe'
28
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
29
+ spec.require_paths = ['lib']
30
+
31
+ spec.add_development_dependency 'bundler', '~> 2.0'
32
+ spec.add_development_dependency 'change_orders'
33
+ spec.add_development_dependency 'dry-auto_inject'
34
+ spec.add_development_dependency 'dry-initializer'
35
+ spec.add_development_dependency 'dry-monads'
36
+ spec.add_development_dependency 'dry-struct'
37
+ spec.add_development_dependency 'dry-types'
38
+ spec.add_development_dependency 'dry-validation'
39
+ spec.add_development_dependency 'game_validator', '~> 0.6.0'
40
+ spec.add_development_dependency 'rake', '~> 10.0'
41
+ spec.add_development_dependency 'rspec', '~> 3.0'
42
+ end
43
+