acpc_poker_types 2.0.0 → 3.0.2

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