-
Notifications
You must be signed in to change notification settings - Fork 3
Rails integration #2
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,35 +1,49 @@ | ||
| class TexasHoldem::Deck | ||
| FACES, SUITS = "AKQJT98765432", "cdhs" | ||
|
|
||
| attr_reader :cards | ||
|
|
||
| def initialize | ||
| build | ||
| shuffle | ||
| end | ||
|
|
||
| def next_card | ||
| @cards.pop | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def build | ||
| @cards = [] | ||
|
|
||
| FACES.each_byte do |face| | ||
| SUITS.each_byte {|suit| @cards.push(face.chr + suit.chr) } | ||
| module TexasHoldem | ||
| module Deck | ||
| def acts_as_holdem_deck options | ||
| return if included_modules.include? InstanceMethods | ||
|
|
||
| cattr_accessor :faces, :suits | ||
|
|
||
| self.faces = "AKQJT98765432" | ||
| self.suits = "cdhs" | ||
|
|
||
| include InstanceMethods | ||
| extend ClassMethods | ||
| end | ||
| end | ||
|
|
||
| def shuffle | ||
| 3.times do | ||
| shuf = [] | ||
| @cards.each do |card| | ||
| loc = rand(shuf.size + 1) | ||
| shuf.insert(loc, card) | ||
|
|
||
| module InstanceMethods | ||
| # TODO: change @cards to the cards setter provided by ActiveRecord and convert to hash for proper serialization | ||
| def build | ||
| @cards = [] | ||
|
|
||
| self.faces.each_byte do |face| | ||
| self.suits.each_byte { |suit| @cards << ( face.chr + suit.chr ) } | ||
| end | ||
| end | ||
|
|
||
| def shuffle #not convinced by this but will keep it as is and test against the Array#shuffle method | ||
| 3.times do | ||
| shuf = [] | ||
| @cards.each do |card| | ||
| loc = rand( shuf.size + 1 ) | ||
| shuf.insert( loc, card ) | ||
| end | ||
|
|
||
| @cards = shuf.reverse | ||
| end | ||
| end | ||
| end | ||
|
|
||
| module ClassMethods | ||
| extend ActiveSupport::Concenr | ||
|
|
||
| setup do | ||
| validates :cards, :presence => true | ||
| serialize :cards | ||
| end | ||
| @cards = shuf.reverse | ||
| end | ||
| end | ||
| end | ||
| end | ||
|
|
||
| ActiveRecord::Base.send :extend, TexasHoldem::Deck | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| class NotEnoughCreditError < StandardError; end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,20 +1,29 @@ | ||
| class TexasHoldem::Game | ||
| SMALL_BLIND_PERCENTAGE = 0.0125 | ||
| attr_reader :players, :entrance_fee | ||
|
|
||
| def initialize(players,entrance_fee) | ||
| @players, @entrance_fee = players, entrance_fee | ||
| end | ||
|
|
||
| def finished? | ||
| players.size == 1 | ||
| end | ||
|
|
||
| def winner | ||
| players.first if finished? | ||
| end | ||
|
|
||
| def small_blind | ||
| @entrance_fee * SMALL_BLIND_PERCENTAGE | ||
| module TexasHoldem | ||
| module Game | ||
| def acts_as_holdem_game options | ||
| return if included_modules.include? InstanceMethods | ||
|
|
||
| cattr_accessor :player_class | ||
|
|
||
| self.player_class = options[:player_class] | ||
|
|
||
| include InstanceMethods | ||
| extend ClassMethods | ||
| end | ||
|
|
||
| module InstanceMethods | ||
| end | ||
|
|
||
| module ClassMethods | ||
| extend ActiveSupport::Concern | ||
|
|
||
| included do | ||
| has_many :hands, :source => self.hand_class | ||
| has_many :players, :through => :hands | ||
| belongs_to :winner, :source => self.player_class, :foreign_key => :winner_id | ||
| end | ||
| end | ||
| end | ||
| end | ||
|
|
||
| ActiveRecord::Base.send :extend, TexasHoldem::Game |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,78 +1,44 @@ | ||
| require 'enumerated_attribute' | ||
| module TexasHoldem | ||
| module Hand | ||
| def acts_as_holdem_hand options | ||
| return if included_modules.include? InstanceMethods | ||
|
|
||
| class TexasHoldem::Hand | ||
| enum_attr :round, %w( ^pocket flop turn river showdown ) | ||
| attr_reader :players, :community_cards | ||
| attr_accessor :pot | ||
|
|
||
| def initialize(players, small_blind_amount=1) | ||
| @players = players | ||
| @deck = TexasHoldem::Deck.new | ||
| @community_cards = [] | ||
| @small_blind_amount = small_blind_amount | ||
| @pot = 0 # Should this be an Observer? | ||
| end | ||
|
|
||
| def dealer | ||
| @players.first | ||
| end | ||
|
|
||
| def minimum_bet | ||
| @small_blind_amount * 2 | ||
| end | ||
|
|
||
| def small_blind | ||
| return dealer if players_remaining? 2 | ||
| @players[2] | ||
| end | ||
|
|
||
| def big_blind | ||
| return @players.last if players_remaining? 2 | ||
| @players[1] | ||
| end | ||
|
|
||
| def winner | ||
| return unless finished? | ||
| winning_player = @players.first | ||
| winning_player.take_winnings @pot | ||
| winning_player | ||
| end | ||
|
|
||
| def finished? | ||
| players_remaining? 1 | ||
| end | ||
|
|
||
| def fold(player) | ||
| @players.delete player | ||
| end | ||
|
|
||
| def deal | ||
| case round | ||
| when :pocket : deal_pocket_cards && deduct_blinds | ||
| when :flop : deal_community_cards 3 | ||
| when :turn : deal_community_cards 1 | ||
| when :river : deal_community_cards 1 | ||
| cattr_accessor :small_blind_percentage, :player_class, :game_class | ||
|
|
||
| self.small_blind_percentage = options[:small_blind_percentage] || 0.0125 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should 0.0125 be an option stored somewhere?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not really it's just a fallback for something the developer can pass as a parameter, if the developer doesn't set the percentage 0.0125 kicks in, but it's meant to be overridden by the developer. The developer should have a model looking like this: class Hand < ActiveRecord::Base
acts_as_holdem_hand :small_blind_percentage => 0.5, :player_class => User, :game_class => Game
endThat number is only there so that we know how much will the small blind get to bet after we have set the big blind to whatever we like, it's a static number so no need for it to be stored anywhere else besides the code. |
||
| self.player_class = options[:player_class] | ||
| self.game_class = options[:game_class] | ||
|
|
||
| include InstanceMethods | ||
| extend ClassMethods | ||
| end | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def players_remaining?(number) | ||
| @players.size == number | ||
| end | ||
|
|
||
| def deal_pocket_cards | ||
| @players.each do |player| | ||
| 2.times { player.cards << @deck.next_card } | ||
|
|
||
| module InstanceMethods | ||
| def small_blind | ||
| small_blind_percentage * entrance_fee | ||
| end | ||
|
|
||
| def minimum_bet | ||
| small_blind * 2 | ||
| end | ||
| end | ||
|
|
||
| module ClassMethods | ||
| extend ActiveSupport::Concern | ||
|
|
||
| included do | ||
| belongs_to :game, :source => self.game_class | ||
| belongs_to :dealer, :source => self.player_class, :foreign_key => :dealer_id | ||
|
|
||
| has_one :poker_hand # must define this model within gem | ||
| has_many :players, :through => :poker_hand | ||
|
|
||
| symbolize :round, :in => [ :pocket, :flop, :turn, :river, :showdown ], :methods => true, :scopes => true | ||
|
|
||
| validates :entrance_fee, :presence => true, :numericality => { :greater_than => 0 } | ||
| end | ||
| end | ||
| end | ||
|
|
||
| def deal_community_cards(number) | ||
| number.times { @community_cards << @deck.next_card } | ||
| end | ||
|
|
||
| def deduct_blinds | ||
| @pot += small_blind.bet(@small_blind_amount) | ||
| @pot += big_blind.bet(@small_blind_amount * 2) | ||
| end | ||
| end | ||
|
|
||
| ActiveRecord::Base.send :extend, TexasHoldem::Hand | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,42 +1,55 @@ | ||
| class NotEnoughCashError < StandardError | ||
| end | ||
| module TexasHoldem | ||
| module Player | ||
| def acts_as_holdem_player options={} | ||
| return if included_modules.include? IntanceMethods | ||
|
|
||
| require 'observer' | ||
| cattr_accessor :initial_credit, :game_class | ||
|
|
||
| class TexasHoldem::Player | ||
| attr_reader :name, :cards, :cash | ||
| include Observable | ||
|
|
||
| def initialize(name,cash) | ||
| @name, @cash = name, cash | ||
| @cards = [] | ||
| end | ||
|
|
||
| def bet(amount) | ||
| # TODO: add remaining cash instead of raising an error | ||
| raise NotEnoughCashError unless enough_cash_for?(amount) | ||
| @cash -= amount | ||
| changed true | ||
| notify_observers :bet => { :player => self, :amount => amount } | ||
| amount | ||
| end | ||
|
|
||
| def check | ||
| bet 0 | ||
| end | ||
|
|
||
| def fold | ||
| changed true | ||
| notify_observers :fold => true | ||
| end | ||
|
|
||
| def take_winnings(amount) | ||
| @cash += amount | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def enough_cash_for?(amount) | ||
| @cash > amount | ||
| self.initial_credit = options[:initial_credit] || 100 | ||
| self.game_class = options[:game_class] | ||
|
|
||
| include InstanceMethods | ||
| extend ClassMethods | ||
| end | ||
|
|
||
| module InstaceMethods | ||
| def bet amount | ||
| raise NotEnoughCreditsError unless self.credits >= amount | ||
| self.update_attributes!( :credits => self.credits - amount ) | ||
| end | ||
|
|
||
| def fold | ||
| end | ||
|
|
||
| def take_winnings share | ||
| self.update_attributes!( :credits => self.credits + share ) | ||
| end | ||
|
|
||
| def check | ||
| bet 0 | ||
| end | ||
|
|
||
| def add_initial_credit | ||
| self.credit = self.initial_credit | ||
| end | ||
| end | ||
|
|
||
| module ClassMethods | ||
| extend ActiveSupport::Concern | ||
|
|
||
| included do | ||
| validates :credit, :presence => true, :numericality => { :greater_than_or_equal_to => 0 } | ||
| after_create :add_initial_credit | ||
|
|
||
| has_many :poker_hands # must define this model within gem | ||
| has_many :games, :source => self.game_class, :foreign_key => :winner_id | ||
|
|
||
| attr_protected :credit | ||
| scope :lesser_credit, order( 'credit' ) | ||
| end | ||
|
|
||
| end | ||
| end | ||
| end | ||
| end | ||
|
|
||
| ActiveRecord::Base.send :extend, TexasHoldem::Player |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should do the Array#shuffle method. All sorts of cleverness going on here, shuffle 3 times, random location, then reverse the deck lol
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, the same feeling I have, but when I re-factored I decided to test first to better understand what the original developer was trying to accomplish.