acpc_poker_types 2.0.0 → 3.0.2

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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/acpc_poker_types.gemspec +2 -2
  3. data/lib/acpc_poker_types/acpc_dealer_data/action_messages.rb +1 -3
  4. data/lib/acpc_poker_types/acpc_dealer_data/poker_match_data.rb +1 -1
  5. data/lib/acpc_poker_types/board_cards.rb +38 -37
  6. data/lib/acpc_poker_types/card.rb +40 -54
  7. data/lib/acpc_poker_types/chip_stack.rb +25 -25
  8. data/lib/acpc_poker_types/game_definition.rb +286 -284
  9. data/lib/acpc_poker_types/hand.rb +19 -17
  10. data/lib/acpc_poker_types/match_state.rb +208 -235
  11. data/lib/acpc_poker_types/pile_of_cards.rb +6 -4
  12. data/lib/acpc_poker_types/player.rb +138 -133
  13. data/lib/acpc_poker_types/poker_action.rb +80 -90
  14. data/lib/acpc_poker_types/rank.rb +51 -49
  15. data/lib/acpc_poker_types/suit.rb +45 -43
  16. data/lib/acpc_poker_types/version.rb +1 -1
  17. data/spec/card_spec.rb +0 -5
  18. data/spec/coverage/index.html +1 -1
  19. data/spec/match_state_spec.rb +8 -10
  20. data/spec/player_spec.rb +12 -19
  21. data/spec/poker_action_spec.rb +105 -171
  22. data/spec/support/spec_helper.rb +0 -1
  23. metadata +121 -219
  24. data/spec/coverage/assets/0.5.3/app.js +0 -88
  25. data/spec/coverage/assets/0.5.3/fancybox/blank.gif +0 -0
  26. data/spec/coverage/assets/0.5.3/fancybox/fancy_close.png +0 -0
  27. data/spec/coverage/assets/0.5.3/fancybox/fancy_loading.png +0 -0
  28. data/spec/coverage/assets/0.5.3/fancybox/fancy_nav_left.png +0 -0
  29. data/spec/coverage/assets/0.5.3/fancybox/fancy_nav_right.png +0 -0
  30. data/spec/coverage/assets/0.5.3/fancybox/fancy_shadow_e.png +0 -0
  31. data/spec/coverage/assets/0.5.3/fancybox/fancy_shadow_n.png +0 -0
  32. data/spec/coverage/assets/0.5.3/fancybox/fancy_shadow_ne.png +0 -0
  33. data/spec/coverage/assets/0.5.3/fancybox/fancy_shadow_nw.png +0 -0
  34. data/spec/coverage/assets/0.5.3/fancybox/fancy_shadow_s.png +0 -0
  35. data/spec/coverage/assets/0.5.3/fancybox/fancy_shadow_se.png +0 -0
  36. data/spec/coverage/assets/0.5.3/fancybox/fancy_shadow_sw.png +0 -0
  37. data/spec/coverage/assets/0.5.3/fancybox/fancy_shadow_w.png +0 -0
  38. data/spec/coverage/assets/0.5.3/fancybox/fancy_title_left.png +0 -0
  39. data/spec/coverage/assets/0.5.3/fancybox/fancy_title_main.png +0 -0
  40. data/spec/coverage/assets/0.5.3/fancybox/fancy_title_over.png +0 -0
  41. data/spec/coverage/assets/0.5.3/fancybox/fancy_title_right.png +0 -0
  42. data/spec/coverage/assets/0.5.3/fancybox/fancybox-x.png +0 -0
  43. data/spec/coverage/assets/0.5.3/fancybox/fancybox-y.png +0 -0
  44. data/spec/coverage/assets/0.5.3/fancybox/fancybox.png +0 -0
  45. data/spec/coverage/assets/0.5.3/fancybox/jquery.fancybox-1.3.1.css +0 -363
  46. data/spec/coverage/assets/0.5.3/fancybox/jquery.fancybox-1.3.1.pack.js +0 -44
  47. data/spec/coverage/assets/0.5.3/favicon_green.png +0 -0
  48. data/spec/coverage/assets/0.5.3/favicon_red.png +0 -0
  49. data/spec/coverage/assets/0.5.3/favicon_yellow.png +0 -0
  50. data/spec/coverage/assets/0.5.3/highlight.css +0 -129
  51. data/spec/coverage/assets/0.5.3/highlight.pack.js +0 -1
  52. data/spec/coverage/assets/0.5.3/jquery-1.6.2.min.js +0 -18
  53. data/spec/coverage/assets/0.5.3/jquery.dataTables.min.js +0 -152
  54. data/spec/coverage/assets/0.5.3/jquery.timeago.js +0 -141
  55. data/spec/coverage/assets/0.5.3/jquery.url.js +0 -174
  56. data/spec/coverage/assets/0.5.3/loading.gif +0 -0
  57. data/spec/coverage/assets/0.5.3/magnify.png +0 -0
  58. data/spec/coverage/assets/0.5.3/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  59. data/spec/coverage/assets/0.5.3/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  60. data/spec/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  61. data/spec/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  62. data/spec/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  63. data/spec/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  64. data/spec/coverage/assets/0.5.3/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  65. data/spec/coverage/assets/0.5.3/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  66. data/spec/coverage/assets/0.5.3/smoothness/images/ui-icons_222222_256x240.png +0 -0
  67. data/spec/coverage/assets/0.5.3/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  68. data/spec/coverage/assets/0.5.3/smoothness/images/ui-icons_454545_256x240.png +0 -0
  69. data/spec/coverage/assets/0.5.3/smoothness/images/ui-icons_888888_256x240.png +0 -0
  70. data/spec/coverage/assets/0.5.3/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  71. data/spec/coverage/assets/0.5.3/smoothness/jquery-ui-1.8.4.custom.css +0 -295
  72. data/spec/coverage/assets/0.5.3/stylesheet.css +0 -383
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cf1293107d6832328e4a3d10d28970b1aff99bd6
4
- data.tar.gz: ab16e7afe3e9eb9a21856dab774ff7184123d2ac
3
+ metadata.gz: 4486cbb027fcef016f2c31f6cb33360b618a17e0
4
+ data.tar.gz: c06bc66f83a1609b25e7a8f30b577cb99c121454
5
5
  SHA512:
6
- metadata.gz: 4632797276b35e397949d8fe5ef274b8e7c5dda915e4ffc1ec0aed08d6971e1743794d0001137c7a7ec685eee3d66f6f850cf8b2b59e1d5ae136f8a92c3282bc
7
- data.tar.gz: afefe7d96e5f0a65adc415a3333ab89490de3b1224bc8c8e3e05891d17686fad12293e7b09c9453e42d750bbae3ce328da6b1058eb58e33b95cdd6857e921f02
6
+ metadata.gz: 7f10ec42bac803bd7a134d1fa3686ce45462fab47f56672050704c249fb48c3b4c0aa942ed911e8083805143992b0a39f41d3041e718aef984bcb8ca379794ce
7
+ data.tar.gz: fd175bdd332b8cdad9ad26baff8663364cfdadc8db1f65bc320864d5b409193c833c4045f21d21632e8f9b217423751ceaa86e92ffcbfd7a97cb67ab8c886c16
@@ -10,10 +10,10 @@ Gem::Specification.new do |s|
10
10
  s.summary = %q{ACPC Poker Types}
11
11
  s.description = %q{Poker classes and constants that conform to the standards of the Annual Computer Poker Competition.}
12
12
 
13
- s.add_dependency 'dmorrill10-utils'
13
+ s.add_dependency 'dmorrill10-utils', '~> 1.0'
14
14
  s.add_dependency 'acpc_dealer', '~> 0.0'
15
+ s.add_dependency 'celluloid', '~> 0.13'
15
16
 
16
- s.add_development_dependency 'celluloid', '~> 0.13'
17
17
  s.add_development_dependency 'awesome_print'
18
18
  s.add_development_dependency 'minitest'
19
19
  s.add_development_dependency 'turn'
@@ -41,12 +41,10 @@ module AcpcPokerTypes::AcpcDealerData
41
41
  end
42
42
  end
43
43
 
44
- CONCATONATED_ACTIONS = AcpcPokerTypes::PokerAction::LEGAL_ACPC_CHARACTERS.to_a.join('')
45
-
46
44
  # @param [String] from_message FROM message (message from player)
47
45
  def self.parse_from_message(from_message)
48
46
  if from_message.strip.match(
49
- /^FROM\s*(\d+)\s*at\s*[\d\.]+\s*(#{AcpcPokerTypes::MatchState::LABEL}\S+):([#{CONCATONATED_ACTIONS}]\s*\d*)$/
47
+ /^FROM\s*(\d+)\s*at\s*[\d\.]+\s*(#{AcpcPokerTypes::MatchState::LABEL}\S+):([#{AcpcPokerTypes::PokerAction::CONCATONATED_ACTIONS}]\s*\d*)$/
50
48
  )
51
49
  FromMessage.new(
52
50
  $1.to_i - 1,
@@ -330,7 +330,7 @@ module AcpcPokerTypes::AcpcDealerData
330
330
  # return nil unless current_hand
331
331
 
332
332
  # @match_def.game_def.min_wagers[current_hand.next_state.round]
333
- # ChipStack.new [@min_wager.to_i, action_with_context.amount_to_put_in_pot.to_i].max
333
+ # ChipStack.new [@min_wager.to_i, action_with_context.cost.to_i].max
334
334
  # end
335
335
 
336
336
  protected
@@ -1,50 +1,51 @@
1
-
2
- require 'dmorrill10-utils/class'
3
-
4
1
  require 'acpc_poker_types/pile_of_cards'
5
2
 
6
3
  # List of community board cards.
7
- class AcpcPokerTypes::BoardCards < AcpcPokerTypes::PileOfCards
8
-
9
- exceptions :too_many_board_cards
4
+ module AcpcPokerTypes
5
+ class BoardCards < Array
6
+ def initialize(board_cards=[[]])
7
+ super(board_cards.map { |elem| AcpcPokerTypes::PileOfCards.new elem })
8
+ end
10
9
 
11
- attr_reader :round
10
+ def round
11
+ self.length - 1
12
+ end
12
13
 
13
- def initialize() @round = nil; next_round! end
14
+ alias_method :array_push, :push
14
15
 
15
- def next_round!
16
- @round = if @round then @round + 1 else 0 end
17
- self[@round] = AcpcPokerTypes::PileOfCards.new
18
- self
19
- end
16
+ def next_round!
17
+ self.array_push AcpcPokerTypes::PileOfCards.new
18
+ self
19
+ end
20
20
 
21
- def push(new_element)
22
- self[@round].push new_element
23
- self
24
- end
21
+ def push(new_element)
22
+ self.last.push new_element
23
+ self
24
+ end
25
25
 
26
- alias_method :<<, :push
26
+ alias_method :<<, :push
27
27
 
28
- # @return [String] The string representation of these board cards.
29
- def to_s
30
- if all? { |pile_for_round| pile_for_round.empty? }
31
- ''
32
- else
33
- '/' + (map { |pile_for_round| pile_for_round.join }).join('/')
28
+ # @return [String] The string representation of these board cards.
29
+ def to_s
30
+ if all? { |pile_for_round| pile_for_round.empty? }
31
+ ''
32
+ else
33
+ '/' + (map { |pile_for_round| pile_for_round.join }).join('/')
34
+ end
34
35
  end
35
- end
36
36
 
37
- # @see #to_s
38
- alias_method :to_str, :to_s
39
-
40
- # @return [String] The string representation of these board cards.
41
- def to_acpc
42
- if all? { |pile_for_round| pile_for_round.empty? }
43
- ''
44
- else
45
- '/' + (map do |pile_for_round|
46
- (pile_for_round.map { |card| card.to_acpc }).join
47
- end).join('/')
37
+ # @see #to_s
38
+ alias_method :to_str, :to_s
39
+
40
+ # @return [String] The string representation of these board cards.
41
+ def to_acpc
42
+ if all? { |pile_for_round| pile_for_round.empty? }
43
+ ''
44
+ else
45
+ '/' + (map do |pile_for_round|
46
+ (pile_for_round.map { |card| card.to_acpc }).join
47
+ end).join('/')
48
+ end
48
49
  end
49
50
  end
50
- end
51
+ end
@@ -1,71 +1,57 @@
1
1
 
2
2
  require 'dmorrill10-utils'
3
3
 
4
- require File.expand_path('../rank', __FILE__)
5
- require File.expand_path('../suit', __FILE__)
6
-
7
- class AcpcPokerTypes::Card
8
- exceptions :parse_error
9
-
10
- # @param [String] acpc_string_of_cards A string of cards in ACPC format
11
- # @return [Array<AcpcPokerTypes::Card>]
12
- def self.cards(acpc_string_of_cards)
13
- all_ranks = AcpcPokerTypes::Rank::DOMAIN.map do |rank, rank_properties|
14
- rank_properties[:acpc_character]
15
- end.join
16
- all_suits = AcpcPokerTypes::Suit::DOMAIN.map do |suit, suit_properties|
17
- suit_properties[:acpc_character]
18
- end.join
19
-
20
- acpc_string_of_cards.scan(/[#{all_ranks}][#{all_suits}]/).inject([]) do |pile, acpc_card|
21
- pile.push << AcpcPokerTypes::Card.from_acpc(acpc_card)
4
+ require 'acpc_poker_types/rank'
5
+ require 'acpc_poker_types/suit'
6
+
7
+ module AcpcPokerTypes
8
+ class Card
9
+ CONCATONATED_RANKS = (AcpcPokerTypes::Rank::DOMAIN.map { |rank, properties| properties[:acpc_character] }).join
10
+ CONCATONATED_SUITS = (AcpcPokerTypes::Suit::DOMAIN.map { |suit, properties| properties[:acpc_character] }).join
11
+ CONCATONATED_CARDS = CONCATONATED_RANKS + CONCATONATED_SUITS
12
+
13
+ # @param [String] acpc_string_of_cards A string of cards in ACPC format
14
+ # @return [Array<AcpcPokerTypes::Card>]
15
+ def self.cards(acpc_string_of_cards)
16
+ acpc_string_of_cards.scan(/[#{CONCATONATED_RANKS}][#{CONCATONATED_SUITS}]/).map do |acpc_card|
17
+ AcpcPokerTypes::Card.from_acpc(acpc_card)
18
+ end
22
19
  end
23
- end
24
20
 
25
- # @return [Integer] The numeric ACPC representation of the card.
26
- def self.acpc_card_number(rank, suit)
27
- rank.to_i * AcpcPokerTypes::Suit::DOMAIN.length + suit.to_i
28
- end
29
-
30
- attr_reader :rank, :suit
21
+ # @return [Integer] The numeric ACPC representation of the card.
22
+ def self.acpc_card_number(rank, suit)
23
+ rank.to_i * AcpcPokerTypes::Suit::DOMAIN.length + suit.to_i
24
+ end
31
25
 
32
- # @return AcpcPokerTypes::Card
33
- def self.from_acpc(acpc_card)
34
- all_ranks = AcpcPokerTypes::Rank::DOMAIN.values.map do |card_rank|
35
- card_rank[:acpc_character]
36
- end.join
37
- all_suits = AcpcPokerTypes::Suit::DOMAIN.values.map do |card_suit|
38
- card_suit[:acpc_character]
39
- end.join
26
+ attr_reader :rank, :suit
40
27
 
41
- if acpc_card.match(/([#{all_ranks}])([#{all_suits}])/)
42
- rank = $1
43
- suit = $2
28
+ # @return AcpcPokerTypes::Card
29
+ def self.from_acpc(acpc_card)
30
+ rank = acpc_card[0]
31
+ suit = acpc_card[1]
44
32
 
45
33
  AcpcPokerTypes::Card.from_components rank, suit
46
- else
47
- raise ParseError, acpc_card
48
34
  end
49
- end
50
35
 
51
- alias_new :from_components
36
+ alias_new :from_components
52
37
 
53
- def initialize(rank, suit)
54
- @rank = AcpcPokerTypes::Rank.new rank
55
- @suit = AcpcPokerTypes::Suit.new suit
56
- end
38
+ def initialize(rank, suit)
39
+ @rank = AcpcPokerTypes::Rank.new rank
40
+ @suit = AcpcPokerTypes::Suit.new suit
41
+ end
57
42
 
58
- def to_i
59
- AcpcPokerTypes::Card.acpc_card_number(@rank, @suit)
60
- end
43
+ def to_i
44
+ AcpcPokerTypes::Card.acpc_card_number(@rank, @suit)
45
+ end
61
46
 
62
- def to_str
63
- @rank.to_s + @suit.to_s
64
- end
47
+ def to_str
48
+ @rank.to_s + @suit.to_s
49
+ end
65
50
 
66
- alias_method :to_s, :to_str
51
+ alias_method :to_s, :to_str
67
52
 
68
- def to_acpc
69
- @rank.to_acpc + @suit.to_acpc
53
+ def to_acpc
54
+ @rank.to_acpc + @suit.to_acpc
55
+ end
70
56
  end
71
- end
57
+ end
@@ -1,38 +1,38 @@
1
-
2
1
  require 'dmorrill10-utils/class'
3
-
4
2
  require 'delegate'
5
3
 
6
- class AcpcPokerTypes::ChipStack < DelegateClass(Rational)
7
- exceptions :illegal_number_of_chips
4
+ module AcpcPokerTypes
5
+ class ChipStack < DelegateClass(Rational)
6
+ exceptions :illegal_number_of_chips
8
7
 
9
- # @param [#to_i] number_of_chips The number of chips to be made into a stack.
10
- # @raise (see #assert_valid_value)
11
- def initialize(number_of_chips=0)
12
- @value = number_of_chips.to_i
8
+ # @param [#to_i] number_of_chips The number of chips to be made into a stack.
9
+ # @raise (see #assert_valid_value)
10
+ def initialize(number_of_chips=0)
11
+ @value = number_of_chips.to_i
13
12
 
14
- assert_valid_value
13
+ assert_valid_value
15
14
 
16
- super @value
17
- end
15
+ super @value
16
+ end
18
17
 
19
- def receive!(number_of_chips)
20
- @value += number_of_chips
18
+ def receive!(number_of_chips)
19
+ @value += number_of_chips
21
20
 
22
- assert_valid_value
21
+ assert_valid_value
23
22
 
24
- __setobj__ @value
23
+ __setobj__ @value
25
24
 
26
- self
27
- end
28
- def give!(number_of_chips)
29
- receive!(-number_of_chips)
30
- end
25
+ self
26
+ end
27
+ def give!(number_of_chips)
28
+ receive!(-number_of_chips)
29
+ end
31
30
 
32
- private
31
+ private
33
32
 
34
- # @raise IllegalNumberOfChips
35
- def assert_valid_value
36
- raise IllegalNumberOfChips if @value < 0
33
+ # @raise IllegalNumberOfChips
34
+ def assert_valid_value
35
+ raise IllegalNumberOfChips if @value < 0
36
+ end
37
37
  end
38
- end
38
+ end
@@ -8,332 +8,334 @@ require 'acpc_poker_types/suit'
8
8
  require 'acpc_poker_types/rank'
9
9
 
10
10
  # Class that parses and manages game definition information from a game definition file.
11
- class AcpcPokerTypes::GameDefinition
12
- exceptions :parse_error
13
-
14
- # @return [String] The string designating the betting type.
15
- attr_reader :betting_type
16
-
17
- # @return [Integer] The number of players.
18
- attr_reader :number_of_players
19
-
20
- # @return [Integer] The number of rounds.
21
- attr_reader :number_of_rounds
22
-
23
- # @return [Array] The number of board cards in each round.
24
- # @example The usual Texas hold'em sequence would look like this:
25
- # number_of_board_cards == [0, 3, 1, 1]
26
- attr_reader :number_of_board_cards
27
-
28
- # @return [Array] The minimum wager in each round.
29
- attr_reader :min_wagers
30
-
31
- # @return [Array] The position relative to the dealer that is first to act
32
- # in each round, indexed from 0.
33
- # @example The usual Texas hold'em sequence would look like this:
34
- # first_player_positions == [1, 0, 0, 0]
35
- attr_reader :first_player_positions
36
-
37
- # @return [Array] The maximum number of wagers in each round.
38
- attr_reader :max_number_of_wagers
39
-
40
- # @return [Array] The list containing the initial stack size for every player.
41
- attr_reader :chip_stacks
42
-
43
- # @return [Integer] The number of suits in the deck.
44
- attr_reader :number_of_suits
45
-
46
- # @return [Integer] The number of ranks in the deck.
47
- attr_reader :number_of_ranks
48
-
49
- # @return [Integer] The number of hole cards that each player is dealt.
50
- attr_reader :number_of_hole_cards
51
-
52
- # @return [Array] The list of blind sizes.
53
- attr_reader :blinds
54
-
55
- # @return [Integer] The maximum value of an eight bit unsigned integer (for consistency with the ACPC Dealer).
56
- UINT8_MAX = 2**8 - 1
57
-
58
- DEFAULT_MAX_NUMBER_OF_WAGERS = UINT8_MAX
59
-
60
- # @return [Integer] The maximum value of a 32 bit signed integer (for consistency with the ACPC Dealer).
61
- INT32_MAX = 2**31 - 1
62
-
63
- DEFAULT_CHIP_STACK = INT32_MAX
64
-
65
- # @return [Hash] Maximum game parameter values.
66
- MIN_VALUES = {
67
- :@number_of_rounds => 1,
68
- :@number_of_players => 2,
69
- :@number_of_hole_cards => 1,
70
- :@number_of_suits => 1,
71
- :@number_of_ranks => 2
72
- }
73
-
74
- MAX_VALUES = {
75
- :@number_of_suits => AcpcPokerTypes::Suit::DOMAIN.length,
76
- :@number_of_ranks => AcpcPokerTypes::Rank::DOMAIN.length
77
- }
78
-
79
- # @return [Hash] Betting types understood by this class.
80
- BETTING_TYPES = {
81
- :limit => 'limit',
82
- :nolimit => 'nolimit'
83
- }
84
-
85
- DEFINITIONS = {
86
- :@chip_stacks => 'stack',
87
- :@number_of_players => 'numPlayers',
88
- :@blinds => 'blind',
89
- :@raise_sizes => 'raiseSize',
90
- :@number_of_rounds => 'numRounds',
91
- :@first_player_positions => 'firstPlayer',
92
- :@max_number_of_wagers => 'maxRaises',
93
- :@number_of_suits => 'numSuits',
94
- :@number_of_ranks => 'numRanks',
95
- :@number_of_hole_cards => 'numHoleCards',
96
- :@number_of_board_cards => 'numBoardCards'
97
- }
98
-
99
- def self.default_first_player_positions(number_of_rounds)
100
- number_of_rounds.to_i.times.inject([]) do |list, i|
101
- list << 0
11
+ module AcpcPokerTypes
12
+ class GameDefinition
13
+ exceptions :parse_error
14
+
15
+ # @return [String] The string designating the betting type.
16
+ attr_reader :betting_type
17
+
18
+ # @return [Integer] The number of players.
19
+ attr_reader :number_of_players
20
+
21
+ # @return [Integer] The number of rounds.
22
+ attr_reader :number_of_rounds
23
+
24
+ # @return [Array] The number of board cards in each round.
25
+ # @example The usual Texas hold'em sequence would look like this:
26
+ # number_of_board_cards == [0, 3, 1, 1]
27
+ attr_reader :number_of_board_cards
28
+
29
+ # @return [Array] The minimum wager in each round.
30
+ attr_reader :min_wagers
31
+
32
+ # @return [Array] The position relative to the dealer that is first to act
33
+ # in each round, indexed from 0.
34
+ # @example The usual Texas hold'em sequence would look like this:
35
+ # first_player_positions == [1, 0, 0, 0]
36
+ attr_reader :first_player_positions
37
+
38
+ # @return [Array] The maximum number of wagers in each round.
39
+ attr_reader :max_number_of_wagers
40
+
41
+ # @return [Array] The list containing the initial stack size for every player.
42
+ attr_reader :chip_stacks
43
+
44
+ # @return [Integer] The number of suits in the deck.
45
+ attr_reader :number_of_suits
46
+
47
+ # @return [Integer] The number of ranks in the deck.
48
+ attr_reader :number_of_ranks
49
+
50
+ # @return [Integer] The number of hole cards that each player is dealt.
51
+ attr_reader :number_of_hole_cards
52
+
53
+ # @return [Array] The list of blind sizes.
54
+ attr_reader :blinds
55
+
56
+ # @return [Integer] The maximum value of an eight bit unsigned integer (for consistency with the ACPC Dealer).
57
+ UINT8_MAX = 2**8 - 1
58
+
59
+ DEFAULT_MAX_NUMBER_OF_WAGERS = UINT8_MAX
60
+
61
+ # @return [Integer] The maximum value of a 32 bit signed integer (for consistency with the ACPC Dealer).
62
+ INT32_MAX = 2**31 - 1
63
+
64
+ DEFAULT_CHIP_STACK = INT32_MAX
65
+
66
+ # @return [Hash] Maximum game parameter values.
67
+ MIN_VALUES = {
68
+ :@number_of_rounds => 1,
69
+ :@number_of_players => 2,
70
+ :@number_of_hole_cards => 1,
71
+ :@number_of_suits => 1,
72
+ :@number_of_ranks => 2
73
+ }
74
+
75
+ MAX_VALUES = {
76
+ :@number_of_suits => AcpcPokerTypes::Suit::DOMAIN.length,
77
+ :@number_of_ranks => AcpcPokerTypes::Rank::DOMAIN.length
78
+ }
79
+
80
+ # @return [Hash] Betting types understood by this class.
81
+ BETTING_TYPES = {
82
+ :limit => 'limit',
83
+ :nolimit => 'nolimit'
84
+ }
85
+
86
+ DEFINITIONS = {
87
+ :@chip_stacks => 'stack',
88
+ :@number_of_players => 'numPlayers',
89
+ :@blinds => 'blind',
90
+ :@raise_sizes => 'raiseSize',
91
+ :@number_of_rounds => 'numRounds',
92
+ :@first_player_positions => 'firstPlayer',
93
+ :@max_number_of_wagers => 'maxRaises',
94
+ :@number_of_suits => 'numSuits',
95
+ :@number_of_ranks => 'numRanks',
96
+ :@number_of_hole_cards => 'numHoleCards',
97
+ :@number_of_board_cards => 'numBoardCards'
98
+ }
99
+
100
+ def self.default_first_player_positions(number_of_rounds)
101
+ number_of_rounds.to_i.times.inject([]) do |list, i|
102
+ list << 0
103
+ end
102
104
  end
103
- end
104
105
 
105
- # @return [Array] The default maximum raise in each round.
106
- def self.default_max_number_of_wagers(number_of_rounds)
107
- number_of_rounds.to_i.times.inject([]) do |list, i|
108
- list << DEFAULT_MAX_NUMBER_OF_WAGERS
106
+ # @return [Array] The default maximum raise in each round.
107
+ def self.default_max_number_of_wagers(number_of_rounds)
108
+ number_of_rounds.to_i.times.inject([]) do |list, i|
109
+ list << DEFAULT_MAX_NUMBER_OF_WAGERS
110
+ end
109
111
  end
110
- end
111
112
 
112
- # @param [Integer] number_of_players The number of players that require stacks.
113
- # @return [Array<AcpcPokerTypes::ChipStack>] The default list of initial stacks for every player.
114
- def self.default_chip_stacks(number_of_players)
115
- number_of_players.to_i.times.inject([]) do |list, i|
116
- list << AcpcPokerTypes::ChipStack.new(DEFAULT_CHIP_STACK)
113
+ # @param [Integer] number_of_players The number of players that require stacks.
114
+ # @return [Array<AcpcPokerTypes::ChipStack>] The default list of initial stacks for every player.
115
+ def self.default_chip_stacks(number_of_players)
116
+ number_of_players.to_i.times.inject([]) do |list, i|
117
+ list << AcpcPokerTypes::ChipStack.new(DEFAULT_CHIP_STACK)
118
+ end
117
119
  end
118
- end
119
120
 
120
- # @param [Array] array The array to flatten into a single element.
121
- # @return +array+ if +array+ has more than one element, the single element in +array+ otherwise.
122
- def self.flatten_if_single_element_array(array)
123
- if 1 == array.length then array[0] else array end
124
- end
121
+ # @param [Array] array The array to flatten into a single element.
122
+ # @return +array+ if +array+ has more than one element, the single element in +array+ otherwise.
123
+ def self.flatten_if_single_element_array(array)
124
+ if 1 == array.length then array[0] else array end
125
+ end
125
126
 
126
- # Checks if the given line is a comment beginning with '#' or ';', or empty.
127
- #
128
- # @param [String] line
129
- # @return [Boolean] True if +line+ is a comment or empty, false otherwise.
130
- def self.line_is_comment_or_empty?(line)
131
- line.nil? || line.match(/^\s*[#;]/) || line.empty?
132
- end
127
+ # Checks if the given line is a comment beginning with '#' or ';', or empty.
128
+ #
129
+ # @param [String] line
130
+ # @return [Boolean] True if +line+ is a comment or empty, false otherwise.
131
+ def self.line_is_comment_or_empty?(line)
132
+ line.nil? || line.match(/^\s*[#;]/) || line.empty?
133
+ end
133
134
 
134
- # Checks a given line from a game definition file for a game
135
- # definition name and returns the given default value unless there is a match.
136
- #
137
- # @param [String, #match] line A line from a game definition file.
138
- # @param [String] definition_name The name of the game definition that is
139
- # being checked for in +line+.
140
- # @return The game definition value in +line+ if +line+ contains the game definition
141
- # referred to by +definition_name+, +nil+ otherwise.
142
- def self.check_game_def_line_for_definition(line, definition_name)
143
- if line.match(/^\s*#{definition_name}\s*=\s*([\d\s]+)/i)
144
- values = $1.chomp.split(/\s+/)
145
- (0..values.length-1).each do |i|
146
- values[i] = values[i].to_i
135
+ # Checks a given line from a game definition file for a game
136
+ # definition name and returns the given default value unless there is a match.
137
+ #
138
+ # @param [String, #match] line A line from a game definition file.
139
+ # @param [String] definition_name The name of the game definition that is
140
+ # being checked for in +line+.
141
+ # @return The game definition value in +line+ if +line+ contains the game definition
142
+ # referred to by +definition_name+, +nil+ otherwise.
143
+ def self.check_game_def_line_for_definition(line, definition_name)
144
+ if line.match(/^\s*#{definition_name}\s*=\s*([\d\s]+)/i)
145
+ values = $1.chomp.split(/\s+/)
146
+ (0..values.length-1).each do |i|
147
+ values[i] = values[i].to_i
148
+ end
149
+ flatten_if_single_element_array values
147
150
  end
148
- flatten_if_single_element_array values
149
151
  end
150
- end
151
152
 
152
- # Checks if the given line from a game definition file is informative or not
153
- # (in which case the line is either: a comment beginning with '#', empty, or
154
- # contains 'gamedef').
155
- #
156
- # @return [String] line The line to check.
157
- # @return [Boolean] +true+ if the line is not informative, +false+ otherwise.
158
- def self.game_def_line_not_informative?(line)
159
- line_is_comment_or_empty?(line) || line.match(/\s*gamedef\s*/i)
160
- end
153
+ # Checks if the given line from a game definition file is informative or not
154
+ # (in which case the line is either: a comment beginning with '#', empty, or
155
+ # contains 'gamedef').
156
+ #
157
+ # @return [String] line The line to check.
158
+ # @return [Boolean] +true+ if the line is not informative, +false+ otherwise.
159
+ def self.game_def_line_not_informative?(line)
160
+ line_is_comment_or_empty?(line) || line.match(/\s*gamedef\s*/i)
161
+ end
161
162
 
162
- # @param [String] game_definition_file_name The name of the game definition file that this instance should parse.
163
- def self.parse_file(game_definition_file_name)
164
- File.open(game_definition_file_name, 'r') { |file| parse file }
165
- end
163
+ # @param [String] game_definition_file_name The name of the game definition file that this instance should parse.
164
+ def self.parse_file(game_definition_file_name)
165
+ File.open(game_definition_file_name, 'r') { |file| parse file }
166
+ end
166
167
 
167
- alias_new :parse
168
+ alias_new :parse
168
169
 
169
- def initialize(definitions)
170
- initialize_members!
171
- parse_definitions! definitions
170
+ def initialize(definitions)
171
+ initialize_members!
172
+ parse_definitions! definitions
172
173
 
173
- @chip_stacks = AcpcPokerTypes::GameDefinition.default_chip_stacks(@number_of_players) if @chip_stacks.empty?
174
+ @chip_stacks = AcpcPokerTypes::GameDefinition.default_chip_stacks(@number_of_players) if @chip_stacks.empty?
174
175
 
175
- unless @first_player_positions.any? { |pos| pos <= 0 }
176
- @first_player_positions.map! { |position| position - 1 }
177
- end
178
-
179
- sanity_check_game_definitions!
180
- end
176
+ unless @first_player_positions.any? { |pos| pos <= 0 }
177
+ @first_player_positions.map! { |position| position - 1 }
178
+ end
181
179
 
182
- def to_s
183
- to_a.join("\n")
184
- end
180
+ sanity_check_game_definitions!
181
+ end
185
182
 
186
- alias_method :to_str, :to_s
187
-
188
- def to_a
189
- list_of_lines = []
190
- list_of_lines << BETTING_TYPES[@betting_type] if @betting_type
191
- list_of_lines << "stack = #{@chip_stacks.join(' ')}" unless @chip_stacks.empty?
192
- list_of_lines << "numPlayers = #{@number_of_players}" if @number_of_players
193
- list_of_lines << "blind = #{@blinds.join(' ')}" unless @blinds.empty?
194
- list_of_lines << "raiseSize = #{min_wagers.join(' ')}" unless min_wagers.empty?
195
- list_of_lines << "numRounds = #{@number_of_rounds}" if @number_of_rounds
196
- list_of_lines << "firstPlayer = #{(@first_player_positions.map{|p| p + 1}).join(' ')}" unless @first_player_positions.empty?
197
- list_of_lines << "maxRaises = #{@max_number_of_wagers.join(' ')}" unless @max_number_of_wagers.empty?
198
- list_of_lines << "numSuits = #{@number_of_suits}" if @number_of_suits
199
- list_of_lines << "numRanks = #{@number_of_ranks}" if @number_of_ranks
200
- list_of_lines << "numHoleCards = #{@number_of_hole_cards}" if @number_of_hole_cards
201
- list_of_lines << "numBoardCards = #{@number_of_board_cards.join(' ')}" unless @number_of_board_cards.empty?
202
- list_of_lines
203
- end
183
+ def to_s
184
+ to_a.join("\n")
185
+ end
204
186
 
205
- def ==(other_game_definition)
206
- Set.new(to_a) == Set.new(other_game_definition.to_a)
207
- end
187
+ alias_method :to_str, :to_s
188
+
189
+ def to_a
190
+ list_of_lines = []
191
+ list_of_lines << BETTING_TYPES[@betting_type] if @betting_type
192
+ list_of_lines << "stack = #{@chip_stacks.join(' ')}" unless @chip_stacks.empty?
193
+ list_of_lines << "numPlayers = #{@number_of_players}" if @number_of_players
194
+ list_of_lines << "blind = #{@blinds.join(' ')}" unless @blinds.empty?
195
+ list_of_lines << "raiseSize = #{min_wagers.join(' ')}" unless min_wagers.empty?
196
+ list_of_lines << "numRounds = #{@number_of_rounds}" if @number_of_rounds
197
+ list_of_lines << "firstPlayer = #{(@first_player_positions.map{|p| p + 1}).join(' ')}" unless @first_player_positions.empty?
198
+ list_of_lines << "maxRaises = #{@max_number_of_wagers.join(' ')}" unless @max_number_of_wagers.empty?
199
+ list_of_lines << "numSuits = #{@number_of_suits}" if @number_of_suits
200
+ list_of_lines << "numRanks = #{@number_of_ranks}" if @number_of_ranks
201
+ list_of_lines << "numHoleCards = #{@number_of_hole_cards}" if @number_of_hole_cards
202
+ list_of_lines << "numBoardCards = #{@number_of_board_cards.join(' ')}" unless @number_of_board_cards.empty?
203
+ list_of_lines
204
+ end
208
205
 
209
- def min_wagers
210
- if @raise_sizes
211
- @raise_sizes
212
- else
213
- @number_of_rounds.times.map { |i| @blinds.max }
206
+ def ==(other_game_definition)
207
+ Set.new(to_a) == Set.new(other_game_definition.to_a)
214
208
  end
215
- end
216
209
 
217
- private
218
-
219
- def initialize_members!
220
- @betting_type = BETTING_TYPES[:limit]
221
- @number_of_players = MIN_VALUES[:@number_of_players]
222
- @blinds = @number_of_players.times.inject([]) { |blinds, i| blinds << 0 }
223
- @number_of_rounds = MIN_VALUES[:@number_of_rounds]
224
- @number_of_board_cards = @number_of_rounds.times.inject([]) { |cards, i| cards << 0 }
225
- @first_player_positions = AcpcPokerTypes::GameDefinition.default_first_player_positions @number_of_rounds
226
- @max_number_of_wagers = AcpcPokerTypes::GameDefinition.default_max_number_of_wagers @number_of_rounds
227
- @chip_stacks = AcpcPokerTypes::GameDefinition.default_chip_stacks @number_of_players
228
- @number_of_suits = MIN_VALUES[:@number_of_suits]
229
- @number_of_ranks = MIN_VALUES[:@number_of_ranks]
230
- @number_of_hole_cards = MIN_VALUES[:@number_of_hole_cards]
231
-
232
- self
233
- end
210
+ def min_wagers
211
+ if @raise_sizes
212
+ @raise_sizes
213
+ else
214
+ @number_of_rounds.times.map { |i| @blinds.max }
215
+ end
216
+ end
234
217
 
235
- def set_defintion_if_present!(definition_symbol, line, definition_label_in_line)
236
- new_definition = AcpcPokerTypes::GameDefinition.check_game_def_line_for_definition(
237
- line,
238
- definition_label_in_line
239
- )
240
- if new_definition
241
- instance_variable_set(definition_symbol, new_definition)
242
- true
243
- else
244
- false
218
+ private
219
+
220
+ def initialize_members!
221
+ @betting_type = BETTING_TYPES[:limit]
222
+ @number_of_players = MIN_VALUES[:@number_of_players]
223
+ @blinds = @number_of_players.times.inject([]) { |blinds, i| blinds << 0 }
224
+ @number_of_rounds = MIN_VALUES[:@number_of_rounds]
225
+ @number_of_board_cards = @number_of_rounds.times.inject([]) { |cards, i| cards << 0 }
226
+ @first_player_positions = AcpcPokerTypes::GameDefinition.default_first_player_positions @number_of_rounds
227
+ @max_number_of_wagers = AcpcPokerTypes::GameDefinition.default_max_number_of_wagers @number_of_rounds
228
+ @chip_stacks = AcpcPokerTypes::GameDefinition.default_chip_stacks @number_of_players
229
+ @number_of_suits = MIN_VALUES[:@number_of_suits]
230
+ @number_of_ranks = MIN_VALUES[:@number_of_ranks]
231
+ @number_of_hole_cards = MIN_VALUES[:@number_of_hole_cards]
232
+
233
+ self
245
234
  end
246
- end
247
235
 
248
- def parse_definitions!(definitions)
249
- definitions.each do |line|
250
- break if line.match(/\bend\s*gamedef\b/i)
251
- next if (
252
- AcpcPokerTypes::GameDefinition.game_def_line_not_informative?(line) ||
253
- BETTING_TYPES.any? do |type_and_name|
254
- type = type_and_name.first
255
- name = type_and_name[1]
256
- if line.match(/\b#{name}\b/i)
257
- @betting_type = type
258
- true
259
- else
260
- false
261
- end
262
- end ||
263
- DEFINITIONS.any? do |symbol, string|
264
- set_defintion_if_present!(symbol, line, string)
265
- end
236
+ def set_defintion_if_present!(definition_symbol, line, definition_label_in_line)
237
+ new_definition = AcpcPokerTypes::GameDefinition.check_game_def_line_for_definition(
238
+ line,
239
+ definition_label_in_line
266
240
  )
241
+ if new_definition
242
+ instance_variable_set(definition_symbol, new_definition)
243
+ true
244
+ else
245
+ false
246
+ end
267
247
  end
268
248
 
269
- self
270
- end
249
+ def parse_definitions!(definitions)
250
+ definitions.each do |line|
251
+ break if line.match(/\bend\s*gamedef\b/i)
252
+ next if (
253
+ AcpcPokerTypes::GameDefinition.game_def_line_not_informative?(line) ||
254
+ BETTING_TYPES.any? do |type_and_name|
255
+ type = type_and_name.first
256
+ name = type_and_name[1]
257
+ if line.match(/\b#{name}\b/i)
258
+ @betting_type = type
259
+ true
260
+ else
261
+ false
262
+ end
263
+ end ||
264
+ DEFINITIONS.any? do |symbol, string|
265
+ set_defintion_if_present!(symbol, line, string)
266
+ end
267
+ )
268
+ end
271
269
 
272
- # @raise ParseError
273
- def sanity_check_game_definitions!
274
- adjust_definitions_if_necessary!
270
+ self
271
+ end
275
272
 
276
- raise ParseError, "list of player stacks not specified" unless @chip_stacks
277
- raise ParseError, "list of blinds not specified" unless @blinds
278
- raise ParseError, "raise size in each round not specified" unless min_wagers
279
- raise ParseError, "first player position in each round not specified" unless @first_player_positions
280
- raise ParseError, "maximum raise in each round not specified" unless @max_number_of_wagers
281
- raise ParseError, "number of board cards in each round not specified" unless @number_of_board_cards
273
+ # @raise ParseError
274
+ def sanity_check_game_definitions!
275
+ adjust_definitions_if_necessary!
276
+
277
+ raise ParseError, "list of player stacks not specified" unless @chip_stacks
278
+ raise ParseError, "list of blinds not specified" unless @blinds
279
+ raise ParseError, "raise size in each round not specified" unless min_wagers
280
+ raise ParseError, "first player position in each round not specified" unless @first_player_positions
281
+ raise ParseError, "maximum raise in each round not specified" unless @max_number_of_wagers
282
+ raise ParseError, "number of board cards in each round not specified" unless @number_of_board_cards
283
+
284
+ MIN_VALUES.each do |symbol, min_value|
285
+ if instance_variable_get(symbol) < min_value
286
+ raise(
287
+ ParseError,
288
+ "Invalid definition, #{DEFINITION[symbol]} must be greater than #{min_value} but was set to #{instance_variable_get(symbol)}"
289
+ )
290
+ end
291
+ end
282
292
 
283
- MIN_VALUES.each do |symbol, min_value|
284
- if instance_variable_get(symbol) < min_value
285
- raise(
286
- ParseError,
287
- "Invalid definition, #{DEFINITION[symbol]} must be greater than #{min_value} but was set to #{instance_variable_get(symbol)}"
288
- )
293
+ (0..@number_of_players-1).each do |i|
294
+ if @blinds[i] > @chip_stacks[i]
295
+ raise(
296
+ ParseError,
297
+ "Blind for player #{i+1} (#{@blinds[i]}) is greater than stack size (#{@chip_stacks[i]})"
298
+ )
299
+ end
289
300
  end
290
- end
291
301
 
292
- (0..@number_of_players-1).each do |i|
293
- if @blinds[i] > @chip_stacks[i]
294
- raise(
295
- ParseError,
296
- "Blind for player #{i+1} (#{@blinds[i]}) is greater than stack size (#{@chip_stacks[i]})"
297
- )
302
+ @number_of_rounds.times do |i|
303
+ unless @first_player_positions[i].seat_in_bounds? @number_of_players
304
+ raise(
305
+ ParseError,
306
+ "Invalid first player #{@first_player_positions[i]} on round #{i+1}"
307
+ )
308
+ end
298
309
  end
299
- end
300
310
 
301
- @number_of_rounds.times do |i|
302
- unless @first_player_positions[i].seat_in_bounds? @number_of_players
303
- raise(
304
- ParseError,
305
- "Invalid first player #{@first_player_positions[i]} on round #{i+1}"
306
- )
311
+ MAX_VALUES.each do |symbol, max_value|
312
+ if instance_variable_get(symbol) > max_value
313
+ raise(
314
+ ParseError,
315
+ "Invalid definition, #{DEFINITIONS[symbol]} must be less than #{max_value} but was set to #{instance_variable_get(symbol)}"
316
+ )
317
+ end
307
318
  end
308
- end
309
319
 
310
- MAX_VALUES.each do |symbol, max_value|
311
- if instance_variable_get(symbol) > max_value
320
+ number_of_cards_required = (@number_of_hole_cards * @number_of_players) + @number_of_board_cards.sum
321
+
322
+ if number_of_cards_required > (@number_of_suits * @number_of_ranks)
312
323
  raise(
313
324
  ParseError,
314
- "Invalid definition, #{DEFINITIONS[symbol]} must be less than #{max_value} but was set to #{instance_variable_get(symbol)}"
325
+ "Too many hole and board cards (#{number_of_cards_required}) for specified deck (#{(@number_of_suits * @number_of_ranks)})"
315
326
  )
316
327
  end
317
328
  end
318
329
 
319
- number_of_cards_required = (@number_of_hole_cards * @number_of_players) + @number_of_board_cards.sum
320
-
321
- if number_of_cards_required > (@number_of_suits * @number_of_ranks)
322
- raise(
323
- ParseError,
324
- "Too many hole and board cards (#{number_of_cards_required}) for specified deck (#{(@number_of_suits * @number_of_ranks)})"
325
- )
326
- end
327
- end
328
-
329
- def adjust_definitions_if_necessary!
330
- @number_of_players.times do |i|
331
- @blinds << 0 unless @blinds.length > i
332
- @chip_stacks << DEFAULT_CHIP_STACK unless @chip_stacks.length > i
333
- end
334
- @number_of_rounds.times do |i|
335
- @first_player_positions << 0 unless @first_player_positions.length > i
336
- @number_of_board_cards << 0 unless @number_of_board_cards.length > i
330
+ def adjust_definitions_if_necessary!
331
+ @number_of_players.times do |i|
332
+ @blinds << 0 unless @blinds.length > i
333
+ @chip_stacks << DEFAULT_CHIP_STACK unless @chip_stacks.length > i
334
+ end
335
+ @number_of_rounds.times do |i|
336
+ @first_player_positions << 0 unless @first_player_positions.length > i
337
+ @number_of_board_cards << 0 unless @number_of_board_cards.length > i
338
+ end
337
339
  end
338
340
  end
339
- end
341
+ end