acpc_table_manager 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/.gitignore +10 -0
 - data/.travis.yml +4 -0
 - data/Gemfile +4 -0
 - data/LICENSE.txt +21 -0
 - data/README.md +58 -0
 - data/Rakefile +10 -0
 - data/acpc_table_manager.gemspec +55 -0
 - data/bin/console +16 -0
 - data/bin/setup +7 -0
 - data/exe/acpc_table_manager +63 -0
 - data/lib/acpc_table_manager.rb +17 -0
 - data/lib/acpc_table_manager/config.rb +180 -0
 - data/lib/acpc_table_manager/dealer.rb +57 -0
 - data/lib/acpc_table_manager/match.rb +350 -0
 - data/lib/acpc_table_manager/match_slice.rb +196 -0
 - data/lib/acpc_table_manager/match_view.rb +203 -0
 - data/lib/acpc_table_manager/monkey_patches.rb +19 -0
 - data/lib/acpc_table_manager/opponents.rb +39 -0
 - data/lib/acpc_table_manager/param_retrieval.rb +32 -0
 - data/lib/acpc_table_manager/proxy.rb +276 -0
 - data/lib/acpc_table_manager/simple_logging.rb +54 -0
 - data/lib/acpc_table_manager/table_manager.rb +260 -0
 - data/lib/acpc_table_manager/table_queue.rb +379 -0
 - data/lib/acpc_table_manager/utils.rb +34 -0
 - data/lib/acpc_table_manager/version.rb +3 -0
 - metadata +311 -0
 
| 
         @@ -0,0 +1,203 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'delegate'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'timeout'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            require 'acpc_poker_types/match_state'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'acpc_poker_types/game_definition'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'acpc_poker_types/hand'
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            require 'contextual_exceptions'
         
     | 
| 
      
 9 
     | 
    
         
            +
            using ContextualExceptions::ClassRefinement
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            require_relative 'match'
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            module AcpcTableManager
         
     | 
| 
      
 14 
     | 
    
         
            +
            class MatchView < SimpleDelegator
         
     | 
| 
      
 15 
     | 
    
         
            +
              include AcpcPokerTypes
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
              exceptions :unable_to_find_next_slice
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              attr_reader :match, :slice_index, :messages_to_display
         
     | 
| 
      
 20 
     | 
    
         
            +
              attr_writer :messages_to_display
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              def self.chip_contributions_in_previous_rounds(player, round)
         
     | 
| 
      
 23 
     | 
    
         
            +
                if round > 0
         
     | 
| 
      
 24 
     | 
    
         
            +
                  player['chip_contributions'][0..round-1].inject(:+)
         
     | 
| 
      
 25 
     | 
    
         
            +
                else
         
     | 
| 
      
 26 
     | 
    
         
            +
                  0
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
              DEFAULT_WAIT_FOR_SLICE_TIMEOUT = 0 # seconds
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
              def initialize(match_id, slice_index = nil, load_previous_messages: false, timeout: DEFAULT_WAIT_FOR_SLICE_TIMEOUT)
         
     | 
| 
      
 33 
     | 
    
         
            +
                @match = Match.find(match_id)
         
     | 
| 
      
 34 
     | 
    
         
            +
                super @match
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                @messages_to_display = []
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                @slice_index = slice_index || @match.last_slice_viewed
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                raise StandardError.new("Illegal slice index: #{@slice_index}") unless @slice_index >= 0
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                unless @slice_index < @match.slices.length
         
     | 
| 
      
 43 
     | 
    
         
            +
                  if timeout > 0
         
     | 
| 
      
 44 
     | 
    
         
            +
                    Timeout.timeout(timeout, UnableToFindNextSlice) do
         
     | 
| 
      
 45 
     | 
    
         
            +
                      while @slice_index >= @match.slices.length do
         
     | 
| 
      
 46 
     | 
    
         
            +
                        sleep 0.5
         
     | 
| 
      
 47 
     | 
    
         
            +
                        @match = Match.find(match_id)
         
     | 
| 
      
 48 
     | 
    
         
            +
                      end
         
     | 
| 
      
 49 
     | 
    
         
            +
                    end
         
     | 
| 
      
 50 
     | 
    
         
            +
                    super @match
         
     | 
| 
      
 51 
     | 
    
         
            +
                  else
         
     | 
| 
      
 52 
     | 
    
         
            +
                    raise UnableToFindNextSlice
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                @messages_to_display = slice.messages
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                @loaded_previous_messages_ = false
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                load_previous_messages! if load_previous_messages
         
     | 
| 
      
 61 
     | 
    
         
            +
              end
         
     | 
| 
      
 62 
     | 
    
         
            +
              def user_contributions_in_previous_rounds
         
     | 
| 
      
 63 
     | 
    
         
            +
                self.class.chip_contributions_in_previous_rounds(user, state.round)
         
     | 
| 
      
 64 
     | 
    
         
            +
              end
         
     | 
| 
      
 65 
     | 
    
         
            +
              def state() @state ||= MatchState.parse slice.state_string end
         
     | 
| 
      
 66 
     | 
    
         
            +
              def slice() slices[@slice_index] end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
              # zero indexed
         
     | 
| 
      
 69 
     | 
    
         
            +
              def users_seat() @users_seat ||= @match.seat - 1 end
         
     | 
| 
      
 70 
     | 
    
         
            +
              def betting_sequence() slice.betting_sequence end
         
     | 
| 
      
 71 
     | 
    
         
            +
              def pot_at_start_of_round() slice.pot_at_start_of_round end
         
     | 
| 
      
 72 
     | 
    
         
            +
              def hand_ended?() slice.hand_ended? end
         
     | 
| 
      
 73 
     | 
    
         
            +
              def match_ended?() slice.match_ended? end
         
     | 
| 
      
 74 
     | 
    
         
            +
              def users_turn_to_act?() slice.users_turn_to_act? end
         
     | 
| 
      
 75 
     | 
    
         
            +
              def legal_actions
         
     | 
| 
      
 76 
     | 
    
         
            +
                slice.legal_actions.map do |action|
         
     | 
| 
      
 77 
     | 
    
         
            +
                  AcpcPokerTypes::PokerAction.new(action)
         
     | 
| 
      
 78 
     | 
    
         
            +
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
              end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
              # @return [Array<Hash>] Player information ordered by seat.
         
     | 
| 
      
 82 
     | 
    
         
            +
              # Each player hash should contain
         
     | 
| 
      
 83 
     | 
    
         
            +
              # values for the following keys:
         
     | 
| 
      
 84 
     | 
    
         
            +
              # 'name',
         
     | 
| 
      
 85 
     | 
    
         
            +
              # 'seat'
         
     | 
| 
      
 86 
     | 
    
         
            +
              # 'chip_stack'
         
     | 
| 
      
 87 
     | 
    
         
            +
              # 'chip_contributions'
         
     | 
| 
      
 88 
     | 
    
         
            +
              # 'chip_balance'
         
     | 
| 
      
 89 
     | 
    
         
            +
              # 'hole_cards'
         
     | 
| 
      
 90 
     | 
    
         
            +
              def players
         
     | 
| 
      
 91 
     | 
    
         
            +
                return @players if @players
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                @players = slice.players
         
     | 
| 
      
 94 
     | 
    
         
            +
              end
         
     | 
| 
      
 95 
     | 
    
         
            +
              def user
         
     | 
| 
      
 96 
     | 
    
         
            +
                @user ||= players[users_seat]
         
     | 
| 
      
 97 
     | 
    
         
            +
              end
         
     | 
| 
      
 98 
     | 
    
         
            +
              def opponents
         
     | 
| 
      
 99 
     | 
    
         
            +
                @opponents ||= compute_opponents
         
     | 
| 
      
 100 
     | 
    
         
            +
              end
         
     | 
| 
      
 101 
     | 
    
         
            +
              def opponents_sorted_by_position_from_user
         
     | 
| 
      
 102 
     | 
    
         
            +
                @opponents_sorted_by_position_from_user ||= opponents.sort_by do |opp|
         
     | 
| 
      
 103 
     | 
    
         
            +
                  Seat.new(
         
     | 
| 
      
 104 
     | 
    
         
            +
                    opp['seat'],
         
     | 
| 
      
 105 
     | 
    
         
            +
                    players.length
         
     | 
| 
      
 106 
     | 
    
         
            +
                  ).seats_from(
         
     | 
| 
      
 107 
     | 
    
         
            +
                    users_seat
         
     | 
| 
      
 108 
     | 
    
         
            +
                  )
         
     | 
| 
      
 109 
     | 
    
         
            +
                end
         
     | 
| 
      
 110 
     | 
    
         
            +
              end
         
     | 
| 
      
 111 
     | 
    
         
            +
              def amount_for_next_player_to_call
         
     | 
| 
      
 112 
     | 
    
         
            +
                @amount_for_next_player_to_call ||= slice.amount_to_call
         
     | 
| 
      
 113 
     | 
    
         
            +
              end
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
              # Over round
         
     | 
| 
      
 116 
     | 
    
         
            +
              def chip_contribution_for_next_player_after_calling
         
     | 
| 
      
 117 
     | 
    
         
            +
                @chip_contribution_for_next_player_after_calling ||= slice.chip_contribution_after_calling
         
     | 
| 
      
 118 
     | 
    
         
            +
              end
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
              # Over round
         
     | 
| 
      
 121 
     | 
    
         
            +
              def minimum_wager_to
         
     | 
| 
      
 122 
     | 
    
         
            +
                @minimum_wager_to ||= slice.minimum_wager_to
         
     | 
| 
      
 123 
     | 
    
         
            +
              end
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
              # Over round
         
     | 
| 
      
 126 
     | 
    
         
            +
              def pot_after_call
         
     | 
| 
      
 127 
     | 
    
         
            +
                @pot_after_call ||= slice.pot_after_call
         
     | 
| 
      
 128 
     | 
    
         
            +
              end
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
              # Over round
         
     | 
| 
      
 131 
     | 
    
         
            +
              def pot_fraction_wager_to(fraction=1)
         
     | 
| 
      
 132 
     | 
    
         
            +
                return 0 if hand_ended?
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
                [
         
     | 
| 
      
 135 
     | 
    
         
            +
                  [
         
     | 
| 
      
 136 
     | 
    
         
            +
                    (
         
     | 
| 
      
 137 
     | 
    
         
            +
                      fraction * pot_after_call +
         
     | 
| 
      
 138 
     | 
    
         
            +
                      chip_contribution_for_next_player_after_calling
         
     | 
| 
      
 139 
     | 
    
         
            +
                    ),
         
     | 
| 
      
 140 
     | 
    
         
            +
                    minimum_wager_to
         
     | 
| 
      
 141 
     | 
    
         
            +
                  ].max,
         
     | 
| 
      
 142 
     | 
    
         
            +
                  all_in
         
     | 
| 
      
 143 
     | 
    
         
            +
                ].min.floor
         
     | 
| 
      
 144 
     | 
    
         
            +
              end
         
     | 
| 
      
 145 
     | 
    
         
            +
             
     | 
| 
      
 146 
     | 
    
         
            +
              # Over round
         
     | 
| 
      
 147 
     | 
    
         
            +
              def all_in
         
     | 
| 
      
 148 
     | 
    
         
            +
                @all_in ||= slice.all_in
         
     | 
| 
      
 149 
     | 
    
         
            +
              end
         
     | 
| 
      
 150 
     | 
    
         
            +
             
     | 
| 
      
 151 
     | 
    
         
            +
              def betting_type_label
         
     | 
| 
      
 152 
     | 
    
         
            +
                if @betting_type_label.nil?
         
     | 
| 
      
 153 
     | 
    
         
            +
                  @betting_type_label = if no_limit?
         
     | 
| 
      
 154 
     | 
    
         
            +
                    'nolimit'
         
     | 
| 
      
 155 
     | 
    
         
            +
                  else
         
     | 
| 
      
 156 
     | 
    
         
            +
                   'limit'
         
     | 
| 
      
 157 
     | 
    
         
            +
                  end
         
     | 
| 
      
 158 
     | 
    
         
            +
                end
         
     | 
| 
      
 159 
     | 
    
         
            +
             
     | 
| 
      
 160 
     | 
    
         
            +
                @betting_type_label
         
     | 
| 
      
 161 
     | 
    
         
            +
              end
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
              def load_previous_messages!(index = @slice_index)
         
     | 
| 
      
 164 
     | 
    
         
            +
                @messages_to_display = slices[0...index].inject([]) do |messages, s|
         
     | 
| 
      
 165 
     | 
    
         
            +
                  messages += s.messages
         
     | 
| 
      
 166 
     | 
    
         
            +
                end + @messages_to_display
         
     | 
| 
      
 167 
     | 
    
         
            +
                @loaded_previous_messages_ = true
         
     | 
| 
      
 168 
     | 
    
         
            +
                self
         
     | 
| 
      
 169 
     | 
    
         
            +
              end
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
              def loaded_previous_messages?
         
     | 
| 
      
 172 
     | 
    
         
            +
                @loaded_previous_messages_
         
     | 
| 
      
 173 
     | 
    
         
            +
              end
         
     | 
| 
      
 174 
     | 
    
         
            +
             
     | 
| 
      
 175 
     | 
    
         
            +
              private
         
     | 
| 
      
 176 
     | 
    
         
            +
             
     | 
| 
      
 177 
     | 
    
         
            +
              def compute_opponents
         
     | 
| 
      
 178 
     | 
    
         
            +
                opp = players.dup
         
     | 
| 
      
 179 
     | 
    
         
            +
                opp.delete_at(users_seat)
         
     | 
| 
      
 180 
     | 
    
         
            +
                opp
         
     | 
| 
      
 181 
     | 
    
         
            +
              end
         
     | 
| 
      
 182 
     | 
    
         
            +
             
     | 
| 
      
 183 
     | 
    
         
            +
              def next_slice_without_updating_messages!(max_retries = 0)
         
     | 
| 
      
 184 
     | 
    
         
            +
                @slice_index += 1
         
     | 
| 
      
 185 
     | 
    
         
            +
                retries = 0
         
     | 
| 
      
 186 
     | 
    
         
            +
                if @slice_index >= slices.length && max_retries < 1
         
     | 
| 
      
 187 
     | 
    
         
            +
                  @slice_index -= 1
         
     | 
| 
      
 188 
     | 
    
         
            +
                  raise UnableToFindNextSlice.new("Unable to find next match slice after #{retries} retries")
         
     | 
| 
      
 189 
     | 
    
         
            +
                end
         
     | 
| 
      
 190 
     | 
    
         
            +
                while @slice_index >= slices.length do
         
     | 
| 
      
 191 
     | 
    
         
            +
                  sleep(0.1)
         
     | 
| 
      
 192 
     | 
    
         
            +
                  @match = Match.find(@match.id)
         
     | 
| 
      
 193 
     | 
    
         
            +
                  __setobj__ @match
         
     | 
| 
      
 194 
     | 
    
         
            +
                  if retries >= max_retries
         
     | 
| 
      
 195 
     | 
    
         
            +
                    @slice_index -= 1
         
     | 
| 
      
 196 
     | 
    
         
            +
                    raise UnableToFindNextSlice.new("Unable to find next match slice after #{retries} retries")
         
     | 
| 
      
 197 
     | 
    
         
            +
                  end
         
     | 
| 
      
 198 
     | 
    
         
            +
                  retries += 1
         
     | 
| 
      
 199 
     | 
    
         
            +
                end
         
     | 
| 
      
 200 
     | 
    
         
            +
                self
         
     | 
| 
      
 201 
     | 
    
         
            +
              end
         
     | 
| 
      
 202 
     | 
    
         
            +
            end
         
     | 
| 
      
 203 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,19 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module AcpcTableManager
         
     | 
| 
      
 2 
     | 
    
         
            +
              module MonkeyPatches
         
     | 
| 
      
 3 
     | 
    
         
            +
                module ConversionToEnglish
         
     | 
| 
      
 4 
     | 
    
         
            +
                  def to_english
         
     | 
| 
      
 5 
     | 
    
         
            +
                    gsub '_', ' '
         
     | 
| 
      
 6 
     | 
    
         
            +
                  end
         
     | 
| 
      
 7 
     | 
    
         
            +
                end
         
     | 
| 
      
 8 
     | 
    
         
            +
                module StringToEnglishExtension
         
     | 
| 
      
 9 
     | 
    
         
            +
                  refine String do
         
     | 
| 
      
 10 
     | 
    
         
            +
                    include ConversionToEnglish
         
     | 
| 
      
 11 
     | 
    
         
            +
                  end
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
                module SymbolToEnglishExtension
         
     | 
| 
      
 14 
     | 
    
         
            +
                  refine Symbol do
         
     | 
| 
      
 15 
     | 
    
         
            +
                    include ConversionToEnglish
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,39 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'timeout'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'process_runner'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative 'config'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require_relative 'simple_logging'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module AcpcTableManager
         
     | 
| 
      
 7 
     | 
    
         
            +
            module Opponents
         
     | 
| 
      
 8 
     | 
    
         
            +
              extend SimpleLogging
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              @logger = nil
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              # @return [Array<Integer>] PIDs of the opponents started
         
     | 
| 
      
 13 
     | 
    
         
            +
              def self.start(*bot_start_commands)
         
     | 
| 
      
 14 
     | 
    
         
            +
                @logger ||= ::AcpcTableManager.new_log 'opponents.log'
         
     | 
| 
      
 15 
     | 
    
         
            +
                log __method__, num_opponents: bot_start_commands.length
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                bot_start_commands.map do |bot_start_command|
         
     | 
| 
      
 18 
     | 
    
         
            +
                  log(
         
     | 
| 
      
 19 
     | 
    
         
            +
                    __method__,
         
     | 
| 
      
 20 
     | 
    
         
            +
                    {
         
     | 
| 
      
 21 
     | 
    
         
            +
                      bot_start_command_parameters: bot_start_command,
         
     | 
| 
      
 22 
     | 
    
         
            +
                      command_to_be_run: bot_start_command.join(' ')
         
     | 
| 
      
 23 
     | 
    
         
            +
                    }
         
     | 
| 
      
 24 
     | 
    
         
            +
                  )
         
     | 
| 
      
 25 
     | 
    
         
            +
                  pid = Timeout::timeout(3) do
         
     | 
| 
      
 26 
     | 
    
         
            +
                    ProcessRunner.go(bot_start_command)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
                  log(
         
     | 
| 
      
 29 
     | 
    
         
            +
                    __method__,
         
     | 
| 
      
 30 
     | 
    
         
            +
                    {
         
     | 
| 
      
 31 
     | 
    
         
            +
                      bot_started?: true,
         
     | 
| 
      
 32 
     | 
    
         
            +
                      pid: pid
         
     | 
| 
      
 33 
     | 
    
         
            +
                    }
         
     | 
| 
      
 34 
     | 
    
         
            +
                  )
         
     | 
| 
      
 35 
     | 
    
         
            +
                  pid
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
            end
         
     | 
| 
      
 39 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,32 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require_relative 'monkey_patches'
         
     | 
| 
      
 2 
     | 
    
         
            +
            using AcpcTableManager::MonkeyPatches::StringToEnglishExtension
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            require_relative 'config'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module AcpcTableManager
         
     | 
| 
      
 7 
     | 
    
         
            +
              module ParamRetrieval
         
     | 
| 
      
 8 
     | 
    
         
            +
                protected
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                # @param [Hash<String, Object>] params Parameter hash
         
     | 
| 
      
 11 
     | 
    
         
            +
                # @param parameter_key The key of the parameter to be retrieved.
         
     | 
| 
      
 12 
     | 
    
         
            +
                # @raise
         
     | 
| 
      
 13 
     | 
    
         
            +
                def retrieve_parameter_or_raise_exception(
         
     | 
| 
      
 14 
     | 
    
         
            +
                  params,
         
     | 
| 
      
 15 
     | 
    
         
            +
                  parameter_key
         
     | 
| 
      
 16 
     | 
    
         
            +
                )
         
     | 
| 
      
 17 
     | 
    
         
            +
                  raise StandardError.new("nil params hash given") unless params
         
     | 
| 
      
 18 
     | 
    
         
            +
                  retrieved_param = params[parameter_key]
         
     | 
| 
      
 19 
     | 
    
         
            +
                  unless retrieved_param
         
     | 
| 
      
 20 
     | 
    
         
            +
                    raise StandardError.new("No #{parameter_key.to_english} provided")
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
                  retrieved_param
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                # @param [Hash<String, Object>] params Parameter hash
         
     | 
| 
      
 26 
     | 
    
         
            +
                # @raise (see #param)
         
     | 
| 
      
 27 
     | 
    
         
            +
                def retrieve_match_id_or_raise_exception(params)
         
     | 
| 
      
 28 
     | 
    
         
            +
                  AcpcTableManager.raise_if_uninitialized
         
     | 
| 
      
 29 
     | 
    
         
            +
                  retrieve_parameter_or_raise_exception params, AcpcTableManager.config.match_id_key
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
              end
         
     | 
| 
      
 32 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,276 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require_relative 'config'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require_relative 'match'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative 'match_slice'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            require 'acpc_poker_player_proxy'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'acpc_poker_types'
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            require_relative 'simple_logging'
         
     | 
| 
      
 9 
     | 
    
         
            +
            using SimpleLogging::MessageFormatting
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            require 'contextual_exceptions'
         
     | 
| 
      
 12 
     | 
    
         
            +
            using ContextualExceptions::ClassRefinement
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            module AcpcTableManager
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            class Proxy
         
     | 
| 
      
 17 
     | 
    
         
            +
              include SimpleLogging
         
     | 
| 
      
 18 
     | 
    
         
            +
              include AcpcPokerTypes
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              exceptions :unable_to_create_match_slice
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              def self.start(match)
         
     | 
| 
      
 23 
     | 
    
         
            +
                game_definition = GameDefinition.parse_file(match.game_definition_file_name)
         
     | 
| 
      
 24 
     | 
    
         
            +
                match.game_def_hash = game_definition.to_h
         
     | 
| 
      
 25 
     | 
    
         
            +
                match.save!
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                proxy = new(
         
     | 
| 
      
 28 
     | 
    
         
            +
                  match.id,
         
     | 
| 
      
 29 
     | 
    
         
            +
                  AcpcDealer::ConnectionInformation.new(
         
     | 
| 
      
 30 
     | 
    
         
            +
                    match.port_numbers[match.seat - 1],
         
     | 
| 
      
 31 
     | 
    
         
            +
                    ::AcpcTableManager.config.dealer_host
         
     | 
| 
      
 32 
     | 
    
         
            +
                  ),
         
     | 
| 
      
 33 
     | 
    
         
            +
                  match.seat - 1,
         
     | 
| 
      
 34 
     | 
    
         
            +
                  game_definition,
         
     | 
| 
      
 35 
     | 
    
         
            +
                  match.player_names.join(' '),
         
     | 
| 
      
 36 
     | 
    
         
            +
                  match.number_of_hands
         
     | 
| 
      
 37 
     | 
    
         
            +
                ) do |players_at_the_table|
         
     | 
| 
      
 38 
     | 
    
         
            +
                  yield players_at_the_table if block_given?
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
              end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
              # @todo Reduce the # of params
         
     | 
| 
      
 43 
     | 
    
         
            +
              #
         
     | 
| 
      
 44 
     | 
    
         
            +
              # @param [String] match_id The ID of the match in which this player is participating.
         
     | 
| 
      
 45 
     | 
    
         
            +
              # @param [DealerInformation] dealer_information Information about the dealer to which this bot should connect.
         
     | 
| 
      
 46 
     | 
    
         
            +
              # @param [GameDefinition, #to_s] game_definition A game definition; either a +GameDefinition+ or the name of the file containing a game definition.
         
     | 
| 
      
 47 
     | 
    
         
            +
              # @param [String] player_names The names of the players in this match.
         
     | 
| 
      
 48 
     | 
    
         
            +
              # @param [Integer] number_of_hands The number of hands in this match.
         
     | 
| 
      
 49 
     | 
    
         
            +
              def initialize(
         
     | 
| 
      
 50 
     | 
    
         
            +
                match_id,
         
     | 
| 
      
 51 
     | 
    
         
            +
                dealer_information,
         
     | 
| 
      
 52 
     | 
    
         
            +
                users_seat,
         
     | 
| 
      
 53 
     | 
    
         
            +
                game_definition,
         
     | 
| 
      
 54 
     | 
    
         
            +
                player_names='user p2',
         
     | 
| 
      
 55 
     | 
    
         
            +
                number_of_hands=1
         
     | 
| 
      
 56 
     | 
    
         
            +
              )
         
     | 
| 
      
 57 
     | 
    
         
            +
                @logger = AcpcTableManager.new_log File.join('proxies', "#{match_id}.#{users_seat}.log")
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                log __method__, {
         
     | 
| 
      
 60 
     | 
    
         
            +
                  dealer_information: dealer_information,
         
     | 
| 
      
 61 
     | 
    
         
            +
                  users_seat: users_seat,
         
     | 
| 
      
 62 
     | 
    
         
            +
                  game_definition: game_definition,
         
     | 
| 
      
 63 
     | 
    
         
            +
                  player_names: player_names,
         
     | 
| 
      
 64 
     | 
    
         
            +
                  number_of_hands: number_of_hands
         
     | 
| 
      
 65 
     | 
    
         
            +
                }
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                @match_id = match_id
         
     | 
| 
      
 68 
     | 
    
         
            +
                @player_proxy = AcpcPokerPlayerProxy::PlayerProxy.new(
         
     | 
| 
      
 69 
     | 
    
         
            +
                  dealer_information,
         
     | 
| 
      
 70 
     | 
    
         
            +
                  game_definition,
         
     | 
| 
      
 71 
     | 
    
         
            +
                  users_seat
         
     | 
| 
      
 72 
     | 
    
         
            +
                ) do |players_at_the_table|
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                  if players_at_the_table.match_state
         
     | 
| 
      
 75 
     | 
    
         
            +
                    update_database! players_at_the_table
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                    yield players_at_the_table if block_given?
         
     | 
| 
      
 78 
     | 
    
         
            +
                  else
         
     | 
| 
      
 79 
     | 
    
         
            +
                    log __method__, {before_first_match_state: true}
         
     | 
| 
      
 80 
     | 
    
         
            +
                  end
         
     | 
| 
      
 81 
     | 
    
         
            +
                end
         
     | 
| 
      
 82 
     | 
    
         
            +
              end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
              # Player action interface
         
     | 
| 
      
 85 
     | 
    
         
            +
              # @see PlayerProxy#play!
         
     | 
| 
      
 86 
     | 
    
         
            +
              def play!(action)
         
     | 
| 
      
 87 
     | 
    
         
            +
                log __method__, action: action
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                action = PokerAction.new(action) unless action.is_a?(PokerAction)
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                @player_proxy.play! action do |players_at_the_table|
         
     | 
| 
      
 92 
     | 
    
         
            +
                  update_database! players_at_the_table
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                  yield players_at_the_table if block_given?
         
     | 
| 
      
 95 
     | 
    
         
            +
                end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                log(
         
     | 
| 
      
 98 
     | 
    
         
            +
                  __method__,
         
     | 
| 
      
 99 
     | 
    
         
            +
                  {
         
     | 
| 
      
 100 
     | 
    
         
            +
                    users_turn_to_act?: @player_proxy.users_turn_to_act?,
         
     | 
| 
      
 101 
     | 
    
         
            +
                    match_ended?: @player_proxy.match_ended?
         
     | 
| 
      
 102 
     | 
    
         
            +
                  }
         
     | 
| 
      
 103 
     | 
    
         
            +
                )
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                self
         
     | 
| 
      
 106 
     | 
    
         
            +
              end
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
              # @see PlayerProxy#match_ended?
         
     | 
| 
      
 109 
     | 
    
         
            +
              def match_ended?
         
     | 
| 
      
 110 
     | 
    
         
            +
                return false if @player_proxy.nil?
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                @match ||= Match.find(@match_id)
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                @player_proxy.match_ended? ||
         
     | 
| 
      
 115 
     | 
    
         
            +
                (
         
     | 
| 
      
 116 
     | 
    
         
            +
                  @player_proxy.hand_ended? &&
         
     | 
| 
      
 117 
     | 
    
         
            +
                  @player_proxy.match_state.hand_number >= @match.number_of_hands - 1
         
     | 
| 
      
 118 
     | 
    
         
            +
                )
         
     | 
| 
      
 119 
     | 
    
         
            +
              end
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
              private
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
              def update_database!(players_at_the_table)
         
     | 
| 
      
 124 
     | 
    
         
            +
                @match = Match.find(@match_id)
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
                begin
         
     | 
| 
      
 127 
     | 
    
         
            +
                  MatchSlice.from_players_at_the_table!(
         
     | 
| 
      
 128 
     | 
    
         
            +
                    players_at_the_table,
         
     | 
| 
      
 129 
     | 
    
         
            +
                    match_ended?,
         
     | 
| 
      
 130 
     | 
    
         
            +
                    @match
         
     | 
| 
      
 131 
     | 
    
         
            +
                  )
         
     | 
| 
      
 132 
     | 
    
         
            +
             
     | 
| 
      
 133 
     | 
    
         
            +
                  new_slice = @match.slices.last
         
     | 
| 
      
 134 
     | 
    
         
            +
                  new_slice.messages = []
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
                  ms = players_at_the_table.match_state
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
                  log(
         
     | 
| 
      
 139 
     | 
    
         
            +
                    __method__,
         
     | 
| 
      
 140 
     | 
    
         
            +
                    {
         
     | 
| 
      
 141 
     | 
    
         
            +
                      first_state_of_first_round?: ms.first_state_of_first_round?
         
     | 
| 
      
 142 
     | 
    
         
            +
                    }
         
     | 
| 
      
 143 
     | 
    
         
            +
                  )
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
                  if ms.first_state_of_first_round?
         
     | 
| 
      
 146 
     | 
    
         
            +
                    new_slice.messages << hand_dealt_description(
         
     | 
| 
      
 147 
     | 
    
         
            +
                      @match.player_names,
         
     | 
| 
      
 148 
     | 
    
         
            +
                      ms.hand_number + 1,
         
     | 
| 
      
 149 
     | 
    
         
            +
                      players_at_the_table.game_def,
         
     | 
| 
      
 150 
     | 
    
         
            +
                      @match.number_of_hands
         
     | 
| 
      
 151 
     | 
    
         
            +
                    )
         
     | 
| 
      
 152 
     | 
    
         
            +
                  end
         
     | 
| 
      
 153 
     | 
    
         
            +
             
     | 
| 
      
 154 
     | 
    
         
            +
                  last_action = ms.betting_sequence(
         
     | 
| 
      
 155 
     | 
    
         
            +
                    players_at_the_table.game_def
         
     | 
| 
      
 156 
     | 
    
         
            +
                  ).flatten.last
         
     | 
| 
      
 157 
     | 
    
         
            +
             
     | 
| 
      
 158 
     | 
    
         
            +
                  log(
         
     | 
| 
      
 159 
     | 
    
         
            +
                    __method__,
         
     | 
| 
      
 160 
     | 
    
         
            +
                    {
         
     | 
| 
      
 161 
     | 
    
         
            +
                      last_action: last_action
         
     | 
| 
      
 162 
     | 
    
         
            +
                    }
         
     | 
| 
      
 163 
     | 
    
         
            +
                  )
         
     | 
| 
      
 164 
     | 
    
         
            +
             
     | 
| 
      
 165 
     | 
    
         
            +
                  if last_action
         
     | 
| 
      
 166 
     | 
    
         
            +
                    last_actor = @match.player_names[
         
     | 
| 
      
 167 
     | 
    
         
            +
                      @match.slices[-2].seat_next_to_act
         
     | 
| 
      
 168 
     | 
    
         
            +
                    ]
         
     | 
| 
      
 169 
     | 
    
         
            +
             
     | 
| 
      
 170 
     | 
    
         
            +
                    log(
         
     | 
| 
      
 171 
     | 
    
         
            +
                      __method__,
         
     | 
| 
      
 172 
     | 
    
         
            +
                      {
         
     | 
| 
      
 173 
     | 
    
         
            +
                        last_actor: last_actor
         
     | 
| 
      
 174 
     | 
    
         
            +
                      }
         
     | 
| 
      
 175 
     | 
    
         
            +
                    )
         
     | 
| 
      
 176 
     | 
    
         
            +
             
     | 
| 
      
 177 
     | 
    
         
            +
                    case last_action.to_acpc_character
         
     | 
| 
      
 178 
     | 
    
         
            +
                    when PokerAction::CHECK
         
     | 
| 
      
 179 
     | 
    
         
            +
                      new_slice.messages << check_description(
         
     | 
| 
      
 180 
     | 
    
         
            +
                        last_actor
         
     | 
| 
      
 181 
     | 
    
         
            +
                      )
         
     | 
| 
      
 182 
     | 
    
         
            +
                    when PokerAction::CALL
         
     | 
| 
      
 183 
     | 
    
         
            +
                      new_slice.messages << call_description(
         
     | 
| 
      
 184 
     | 
    
         
            +
                        last_actor,
         
     | 
| 
      
 185 
     | 
    
         
            +
                        last_action
         
     | 
| 
      
 186 
     | 
    
         
            +
                      )
         
     | 
| 
      
 187 
     | 
    
         
            +
                    when PokerAction::BET
         
     | 
| 
      
 188 
     | 
    
         
            +
                      new_slice.messages << bet_description(
         
     | 
| 
      
 189 
     | 
    
         
            +
                        last_actor,
         
     | 
| 
      
 190 
     | 
    
         
            +
                        last_action
         
     | 
| 
      
 191 
     | 
    
         
            +
                      )
         
     | 
| 
      
 192 
     | 
    
         
            +
                    when PokerAction::RAISE
         
     | 
| 
      
 193 
     | 
    
         
            +
                      new_slice.messages << if @match.no_limit?
         
     | 
| 
      
 194 
     | 
    
         
            +
                        no_limit_raise_description(
         
     | 
| 
      
 195 
     | 
    
         
            +
                          last_actor,
         
     | 
| 
      
 196 
     | 
    
         
            +
                          last_action,
         
     | 
| 
      
 197 
     | 
    
         
            +
                          @match.slices[-2].amount_to_call
         
     | 
| 
      
 198 
     | 
    
         
            +
                        )
         
     | 
| 
      
 199 
     | 
    
         
            +
                      else
         
     | 
| 
      
 200 
     | 
    
         
            +
                        limit_raise_description(
         
     | 
| 
      
 201 
     | 
    
         
            +
                          last_actor,
         
     | 
| 
      
 202 
     | 
    
         
            +
                          last_action,
         
     | 
| 
      
 203 
     | 
    
         
            +
                          ms.players(players_at_the_table.game_def).num_wagers(ms.round) - 1,
         
     | 
| 
      
 204 
     | 
    
         
            +
                          players_at_the_table.game_def.max_number_of_wagers[ms.round]
         
     | 
| 
      
 205 
     | 
    
         
            +
                        )
         
     | 
| 
      
 206 
     | 
    
         
            +
                      end
         
     | 
| 
      
 207 
     | 
    
         
            +
                    when PokerAction::FOLD
         
     | 
| 
      
 208 
     | 
    
         
            +
                      new_slice.messages << fold_description(
         
     | 
| 
      
 209 
     | 
    
         
            +
                        last_actor
         
     | 
| 
      
 210 
     | 
    
         
            +
                      )
         
     | 
| 
      
 211 
     | 
    
         
            +
                    end
         
     | 
| 
      
 212 
     | 
    
         
            +
                  end
         
     | 
| 
      
 213 
     | 
    
         
            +
             
     | 
| 
      
 214 
     | 
    
         
            +
                  log(
         
     | 
| 
      
 215 
     | 
    
         
            +
                    __method__,
         
     | 
| 
      
 216 
     | 
    
         
            +
                    {
         
     | 
| 
      
 217 
     | 
    
         
            +
                      hand_ended?: players_at_the_table.hand_ended?
         
     | 
| 
      
 218 
     | 
    
         
            +
                    }
         
     | 
| 
      
 219 
     | 
    
         
            +
                  )
         
     | 
| 
      
 220 
     | 
    
         
            +
             
     | 
| 
      
 221 
     | 
    
         
            +
                  if players_at_the_table.hand_ended?
         
     | 
| 
      
 222 
     | 
    
         
            +
                    log(
         
     | 
| 
      
 223 
     | 
    
         
            +
                      __method__,
         
     | 
| 
      
 224 
     | 
    
         
            +
                      {
         
     | 
| 
      
 225 
     | 
    
         
            +
                        reached_showdown?: ms.reached_showdown?
         
     | 
| 
      
 226 
     | 
    
         
            +
                      }
         
     | 
| 
      
 227 
     | 
    
         
            +
                    )
         
     | 
| 
      
 228 
     | 
    
         
            +
             
     | 
| 
      
 229 
     | 
    
         
            +
                    if ms.reached_showdown?
         
     | 
| 
      
 230 
     | 
    
         
            +
                      players_at_the_table.players.each_with_index do |player, i|
         
     | 
| 
      
 231 
     | 
    
         
            +
                        hd = PileOfCards.new(
         
     | 
| 
      
 232 
     | 
    
         
            +
                          player.hand +
         
     | 
| 
      
 233 
     | 
    
         
            +
                          ms.community_cards.flatten
         
     | 
| 
      
 234 
     | 
    
         
            +
                        ).to_poker_hand_description
         
     | 
| 
      
 235 
     | 
    
         
            +
                        new_slice.messages << "#{@match.player_names[i]} shows #{hd}"
         
     | 
| 
      
 236 
     | 
    
         
            +
                      end
         
     | 
| 
      
 237 
     | 
    
         
            +
                    end
         
     | 
| 
      
 238 
     | 
    
         
            +
                    winning_players = new_slice.players.select do |player|
         
     | 
| 
      
 239 
     | 
    
         
            +
                      player['winnings'] > 0
         
     | 
| 
      
 240 
     | 
    
         
            +
                    end
         
     | 
| 
      
 241 
     | 
    
         
            +
                    if winning_players.length > 1
         
     | 
| 
      
 242 
     | 
    
         
            +
                      new_slice.messages << split_pot_description(
         
     | 
| 
      
 243 
     | 
    
         
            +
                        winning_players.map { |player| player['name'] },
         
     | 
| 
      
 244 
     | 
    
         
            +
                        ms.pot(players_at_the_table.game_def)
         
     | 
| 
      
 245 
     | 
    
         
            +
                      )
         
     | 
| 
      
 246 
     | 
    
         
            +
                    else
         
     | 
| 
      
 247 
     | 
    
         
            +
                      winnings = winning_players.first['winnings']
         
     | 
| 
      
 248 
     | 
    
         
            +
                      if winnings.to_i == winnings
         
     | 
| 
      
 249 
     | 
    
         
            +
                        winnings = winnings.to_i
         
     | 
| 
      
 250 
     | 
    
         
            +
                      end
         
     | 
| 
      
 251 
     | 
    
         
            +
                      chip_balance = winning_players.first['chip_balance']
         
     | 
| 
      
 252 
     | 
    
         
            +
                      if chip_balance.to_i == chip_balance
         
     | 
| 
      
 253 
     | 
    
         
            +
                        chip_balance = chip_balance.to_i
         
     | 
| 
      
 254 
     | 
    
         
            +
                      end
         
     | 
| 
      
 255 
     | 
    
         
            +
             
     | 
| 
      
 256 
     | 
    
         
            +
                      new_slice.messages << hand_win_description(
         
     | 
| 
      
 257 
     | 
    
         
            +
                        winning_players.first['name'],
         
     | 
| 
      
 258 
     | 
    
         
            +
                        winnings,
         
     | 
| 
      
 259 
     | 
    
         
            +
                        chip_balance - winnings
         
     | 
| 
      
 260 
     | 
    
         
            +
                      )
         
     | 
| 
      
 261 
     | 
    
         
            +
                    end
         
     | 
| 
      
 262 
     | 
    
         
            +
                  end
         
     | 
| 
      
 263 
     | 
    
         
            +
             
     | 
| 
      
 264 
     | 
    
         
            +
                  new_slice.save!
         
     | 
| 
      
 265 
     | 
    
         
            +
             
     | 
| 
      
 266 
     | 
    
         
            +
                  # Since creating a new slice doesn't "update" the match for some reason
         
     | 
| 
      
 267 
     | 
    
         
            +
                  @match.update_attribute(:updated_at, Time.now)
         
     | 
| 
      
 268 
     | 
    
         
            +
                  @match.save!
         
     | 
| 
      
 269 
     | 
    
         
            +
                rescue => e
         
     | 
| 
      
 270 
     | 
    
         
            +
                  raise UnableToCreateMatchSlice.with_context('Unable to create match slice', e)
         
     | 
| 
      
 271 
     | 
    
         
            +
                end
         
     | 
| 
      
 272 
     | 
    
         
            +
             
     | 
| 
      
 273 
     | 
    
         
            +
                self
         
     | 
| 
      
 274 
     | 
    
         
            +
              end
         
     | 
| 
      
 275 
     | 
    
         
            +
            end
         
     | 
| 
      
 276 
     | 
    
         
            +
            end
         
     |