Skip to content
This repository was archived by the owner on Oct 29, 2021. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 45 additions & 31 deletions lib/texas_holdem/deck.rb
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
Copy link
Copy Markdown

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

Copy link
Copy Markdown
Member Author

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.

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
1 change: 1 addition & 0 deletions lib/texas_holdem/exceptions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
class NotEnoughCreditError < StandardError; end
45 changes: 27 additions & 18 deletions lib/texas_holdem/game.rb
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
112 changes: 39 additions & 73 deletions lib/texas_holdem/hand.rb
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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should 0.0125 be an option stored somewhere?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The 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
end

That 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
91 changes: 52 additions & 39 deletions lib/texas_holdem/player.rb
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