tundengine 0.0.1
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.
- checksums.yaml +7 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +29 -0
- data/Rakefile +8 -0
- data/lib/tundengine.rb +67 -0
- data/lib/tundengine/algebraic_data_type.rb +33 -0
- data/lib/tundengine/card_percolator.rb +9 -0
- data/lib/tundengine/cards/card.rb +63 -0
- data/lib/tundengine/cards/null.rb +18 -0
- data/lib/tundengine/dealer.rb +52 -0
- data/lib/tundengine/deck.rb +41 -0
- data/lib/tundengine/declarations/base.rb +24 -0
- data/lib/tundengine/declarations/las_cuarenta.rb +15 -0
- data/lib/tundengine/declarations/las_veinte.rb +29 -0
- data/lib/tundengine/declarations/null.rb +18 -0
- data/lib/tundengine/declarations/tute.rb +23 -0
- data/lib/tundengine/declarations/void.rb +15 -0
- data/lib/tundengine/hand.rb +35 -0
- data/lib/tundengine/move.rb +13 -0
- data/lib/tundengine/null_move.rb +13 -0
- data/lib/tundengine/player/in_match.rb +29 -0
- data/lib/tundengine/player/in_round.rb +71 -0
- data/lib/tundengine/player/in_turn.rb +31 -0
- data/lib/tundengine/ranks/base.rb +31 -0
- data/lib/tundengine/ranks/cinco.rb +10 -0
- data/lib/tundengine/ranks/cuatro.rb +10 -0
- data/lib/tundengine/ranks/diez.rb +10 -0
- data/lib/tundengine/ranks/doce.rb +10 -0
- data/lib/tundengine/ranks/dos.rb +10 -0
- data/lib/tundengine/ranks/null.rb +10 -0
- data/lib/tundengine/ranks/once.rb +10 -0
- data/lib/tundengine/ranks/seis.rb +10 -0
- data/lib/tundengine/ranks/siete.rb +10 -0
- data/lib/tundengine/ranks/tres.rb +10 -0
- data/lib/tundengine/ranks/uno.rb +10 -0
- data/lib/tundengine/ranks/white.rb +9 -0
- data/lib/tundengine/round_analyzer.rb +67 -0
- data/lib/tundengine/stages/base.rb +74 -0
- data/lib/tundengine/stages/match.rb +80 -0
- data/lib/tundengine/stages/null.rb +13 -0
- data/lib/tundengine/stages/round.rb +85 -0
- data/lib/tundengine/stages/tournament.rb +72 -0
- data/lib/tundengine/stages/trick.rb +90 -0
- data/lib/tundengine/stages/turn.rb +44 -0
- data/lib/tundengine/strategies/automatic.rb +27 -0
- data/lib/tundengine/strategies/base.rb +8 -0
- data/lib/tundengine/strategies/manual.rb +33 -0
- data/lib/tundengine/strategies/random.rb +15 -0
- data/lib/tundengine/stringifiable_by_class.rb +9 -0
- data/lib/tundengine/suits/base.rb +19 -0
- data/lib/tundengine/suits/basto.rb +7 -0
- data/lib/tundengine/suits/copa.rb +7 -0
- data/lib/tundengine/suits/espada.rb +7 -0
- data/lib/tundengine/suits/null.rb +14 -0
- data/lib/tundengine/suits/oro.rb +7 -0
- data/lib/tundengine/tute_values/base.rb +17 -0
- data/lib/tundengine/tute_values/capotes.rb +22 -0
- data/lib/tundengine/tute_values/nothing.rb +13 -0
- data/lib/tundengine/tute_values/victory.rb +17 -0
- data/lib/tundengine/version.rb +17 -0
- data/test/test_card_strength.rb +58 -0
- data/test/test_helper.rb +35 -0
- data/test/test_playable_cards.rb +66 -0
- data/test/test_random_tournaments.rb +31 -0
- data/tundengine.gemspec +28 -0
- metadata +154 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
module Tundengine
|
2
|
+
module Strategies
|
3
|
+
class Automatic < Base
|
4
|
+
|
5
|
+
def play!(player_in_turn, card)
|
6
|
+
selected_card = card.self_or_yield do
|
7
|
+
play(player_in_turn)
|
8
|
+
end
|
9
|
+
|
10
|
+
player_in_turn.after_playing!(selected_card)
|
11
|
+
end
|
12
|
+
|
13
|
+
def declare!(player_in_round, declaration)
|
14
|
+
selected_declaration = declaration.self_or_yield do
|
15
|
+
declare(player_in_round)
|
16
|
+
end
|
17
|
+
|
18
|
+
player_in_round.after_declaring!(selected_declaration)
|
19
|
+
end
|
20
|
+
|
21
|
+
def on_winning_trick!(player_in_round)
|
22
|
+
player_in_round.declare!
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Tundengine
|
2
|
+
module Strategies
|
3
|
+
class Manual < Base
|
4
|
+
|
5
|
+
def play!(player_in_turn, card)
|
6
|
+
card.yield_self_or_lock!(player_in_turn.turn) do |c|
|
7
|
+
do_play!(player_in_turn, c)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def declare!(player_in_round, declaration)
|
12
|
+
declaration.yield_self_or_lock!(player_in_round.round) do |d|
|
13
|
+
do_declare!(player_in_round, d)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def on_winning_trick!(player_in_round)
|
18
|
+
# do nothing
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
def do_play!(player_in_turn, card)
|
24
|
+
player_in_turn.after_playing!(card)
|
25
|
+
end
|
26
|
+
|
27
|
+
def do_declare!(player_in_round, declaration)
|
28
|
+
player_in_round.after_declaring!(declaration)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Tundengine
|
2
|
+
module Strategies
|
3
|
+
class Random < Automatic
|
4
|
+
|
5
|
+
def play(player_in_turn)
|
6
|
+
player_in_turn.hand.playable_cards(player_in_turn.turn.trick).first.first
|
7
|
+
end
|
8
|
+
|
9
|
+
def declare(player_in_round)
|
10
|
+
Declarations::Void.instance
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Tundengine
|
2
|
+
module Suits
|
3
|
+
class Base
|
4
|
+
|
5
|
+
include Singleton
|
6
|
+
include StringifiableByClass
|
7
|
+
include CardPercolator
|
8
|
+
|
9
|
+
protected
|
10
|
+
|
11
|
+
def is_of_this_suit?(card)
|
12
|
+
card.suit == self
|
13
|
+
end
|
14
|
+
alias_method :passes_card_percolator?, :is_of_this_suit?
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Tundengine
|
2
|
+
module TuteValues
|
3
|
+
class Capotes < Base
|
4
|
+
|
5
|
+
include AlgebraicDataType
|
6
|
+
|
7
|
+
def initialize(points)
|
8
|
+
@points = points
|
9
|
+
super()
|
10
|
+
end
|
11
|
+
|
12
|
+
def points_for_loser(loser, max_points)
|
13
|
+
[@points, max_points].min
|
14
|
+
end
|
15
|
+
|
16
|
+
def identifier
|
17
|
+
[@points]
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'tundengine'
|
3
|
+
|
4
|
+
class TestCardStrength < Minitest::Test
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@all_suits = Tundengine::Deck::SUITS
|
8
|
+
@all_cards = Tundengine::Deck::CARDS
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_that_null_always_loses
|
12
|
+
|
13
|
+
null_card = Tundengine::Cards::Null.instance
|
14
|
+
|
15
|
+
@all_suits.each do |trump_suit|
|
16
|
+
@all_cards.each do |card|
|
17
|
+
assert_equal(true, card.beats?(null_card, trump_suit))
|
18
|
+
assert_equal(false, null_card.beats?(card, trump_suit))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_that_trump_always_wins_against_no_trump
|
25
|
+
|
26
|
+
@all_suits.each do |trump_suit|
|
27
|
+
trump_cards, no_trump_cards = trump_suit.percolate(@all_cards, :partition)
|
28
|
+
pairs = trump_cards.product(no_trump_cards)
|
29
|
+
|
30
|
+
pairs.each do |trump_card, no_trump_card|
|
31
|
+
assert_equal(true, trump_card.beats?(no_trump_card, trump_suit))
|
32
|
+
assert_equal(false, no_trump_card.beats?(trump_card, trump_suit))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_strength_with_no_trumps
|
39
|
+
|
40
|
+
@all_suits.each do |trump_suit|
|
41
|
+
_, no_trump_cards = trump_suit.percolate(@all_cards, :partition)
|
42
|
+
pairs = no_trump_cards.product(no_trump_cards)
|
43
|
+
same_suit, different_suits = pairs.partition { |c1, c2| c1.suit == c2.suit }
|
44
|
+
|
45
|
+
same_suit.each do |c1, c2|
|
46
|
+
assert_equal(c1.rank > c2.rank, c1.beats?(c2, trump_suit))
|
47
|
+
assert_equal(c1.rank < c2.rank, c2.beats?(c1, trump_suit))
|
48
|
+
end
|
49
|
+
|
50
|
+
different_suits.each do |c1, c2|
|
51
|
+
assert_equal(false, c1.beats?(c2, trump_suit))
|
52
|
+
assert_equal(false, c2.beats?(c1, trump_suit))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'tundengine'
|
2
|
+
|
3
|
+
module TestHelper
|
4
|
+
|
5
|
+
def setup!(strategies = :all_automatic)
|
6
|
+
strategy = Tundengine::Strategies::Random.new if strategies == :all_automatic
|
7
|
+
strategy = Tundengine::Strategies::Manual.new if strategies == :all_manual
|
8
|
+
|
9
|
+
@@all_ranks = Tundengine::Deck::RANKS
|
10
|
+
@@all_suits = Tundengine::Deck::SUITS
|
11
|
+
@@all_cards = Tundengine::Deck::CARDS
|
12
|
+
|
13
|
+
@@players = %w(Mati Mar Pablito Mary Euge).map do |name|
|
14
|
+
Tundengine::Player::InMatch.new(name, strategy)
|
15
|
+
end
|
16
|
+
|
17
|
+
@@tournament_options = {
|
18
|
+
max_matches: 1,
|
19
|
+
match_settings: {
|
20
|
+
max_points: 4,
|
21
|
+
tute_value: Tundengine::TuteValues::Nothing.instance,
|
22
|
+
losing_position: :second # the alternative is :not_first_or_last
|
23
|
+
}
|
24
|
+
}
|
25
|
+
|
26
|
+
@@new_tournament = Tundengine::Stages::Tournament.new(@@players, @@tournament_options)
|
27
|
+
@@new_match = @@new_tournament.current_match
|
28
|
+
@@new_round = @@new_match.current_round
|
29
|
+
@@new_trick = @@new_round.current_trick
|
30
|
+
@@new_turn = @@new_trick.current_turn
|
31
|
+
@@new_player = @@new_turn.player
|
32
|
+
@@new_hand = @@new_player.hand
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'tundengine'
|
3
|
+
require_relative './test_helper'
|
4
|
+
|
5
|
+
class TestPlayableCards < Minitest::Test
|
6
|
+
|
7
|
+
include TestHelper
|
8
|
+
|
9
|
+
def test_starting_first_trick
|
10
|
+
|
11
|
+
setup!
|
12
|
+
|
13
|
+
expected = [@@new_hand, true]
|
14
|
+
actual = @@new_hand.playable_cards(@@new_trick)
|
15
|
+
|
16
|
+
assert_equal expected, actual
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_starting_last_trick
|
20
|
+
|
21
|
+
setup!
|
22
|
+
|
23
|
+
@@all_cards.each do |card|
|
24
|
+
hand = Tundengine::Hand.new([card])
|
25
|
+
|
26
|
+
expected = [hand, true]
|
27
|
+
actual = hand.playable_cards(@@new_trick)
|
28
|
+
|
29
|
+
assert_equal expected, actual
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_in_trick_with_one_card
|
35
|
+
|
36
|
+
setup!(:all_manual)
|
37
|
+
|
38
|
+
trick = @@new_trick
|
39
|
+
|
40
|
+
hand = Tundengine::Hand.new([
|
41
|
+
Tundengine::Ranks::Cinco.de(Tundengine::Suits::Oro),
|
42
|
+
Tundengine::Ranks::Diez .de(Tundengine::Suits::Oro),
|
43
|
+
Tundengine::Ranks::Cinco.de(Tundengine::Suits::Copa),
|
44
|
+
Tundengine::Ranks::Diez .de(Tundengine::Suits::Copa),
|
45
|
+
Tundengine::Ranks::Cinco.de(Tundengine::Suits::Espada),
|
46
|
+
Tundengine::Ranks::Diez .de(Tundengine::Suits::Espada),
|
47
|
+
Tundengine::Ranks::Cinco.de(Tundengine::Suits::Basto),
|
48
|
+
Tundengine::Ranks::Diez. de(Tundengine::Suits::Basto)
|
49
|
+
])
|
50
|
+
|
51
|
+
@@new_hand << Tundengine::Ranks::Dos.de(Tundengine::Suits::Copa)
|
52
|
+
trick.play!( Tundengine::Ranks::Dos.de(Tundengine::Suits::Copa))
|
53
|
+
|
54
|
+
expected = [Tundengine::Hand.new([
|
55
|
+
Tundengine::Ranks::Cinco.de(Tundengine::Suits::Copa),
|
56
|
+
Tundengine::Ranks::Diez .de(Tundengine::Suits::Copa)
|
57
|
+
]), true]
|
58
|
+
|
59
|
+
actual = hand.playable_cards(trick)
|
60
|
+
|
61
|
+
assert_equal expected, actual
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'tundengine'
|
3
|
+
require_relative './test_helper'
|
4
|
+
|
5
|
+
class TestRandomTournaments < Minitest::Test
|
6
|
+
|
7
|
+
include TestHelper
|
8
|
+
|
9
|
+
def test_three_players
|
10
|
+
play_with 3
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_four_players
|
14
|
+
play_with 4
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_five_players
|
18
|
+
play_with 5
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
def play_with(number_of_players)
|
24
|
+
setup!
|
25
|
+
|
26
|
+
players = @@players.take number_of_players
|
27
|
+
tournament = Tundengine::Stages::Tournament.new(players, @@tournament_options)
|
28
|
+
tournament.play!
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|