texas-holdem 0.0.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: eda1eff093ca7de35fa918d0c108fe9ce5b9eed1
4
+ data.tar.gz: 35a40aa0f3020019dab30d9b7e44a4d15c4f40f3
5
+ SHA512:
6
+ metadata.gz: 1bbc1ea749be628c35f2209fb06c546fd3623b417a54014548b8db44d0b961664d6a1b1cce41fdd9daa16b8d564e8902f371f404693a5603089f6c323806c4a7
7
+ data.tar.gz: b4486b6717c5582cb6237e90380bb26f2a9acf9477ad684f1899889b7b5978c99b9b358fd26900c0a30fc4f7e98451765b660a6d8b5abb24a9cbb7fd97cd130f
data/.gitignore ADDED
@@ -0,0 +1,36 @@
1
+ .idea
2
+ *.gem
3
+ *.rbc
4
+ /.config
5
+ /coverage/
6
+ /InstalledFiles
7
+ /pkg/
8
+ /spec/reports/
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ ## Specific to RubyMotion:
14
+ .dat*
15
+ .repl_history
16
+ build/
17
+
18
+ ## Documentation cache and generated files:
19
+ /.yardoc/
20
+ /_yardoc/
21
+ /doc/
22
+ /rdoc/
23
+
24
+ ## Environment normalisation:
25
+ /.bundle/
26
+ /vendor/bundle
27
+ /lib/bundler/man/
28
+
29
+ # for a library or gem, you might want to ignore these files since the code is
30
+ # intended to run in multiple environments; otherwise, check them in:
31
+ # Gemfile.lock
32
+ # .ruby-version
33
+ # .ruby-gemset
34
+
35
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
36
+ .rvmrc
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --color
2
+ --require spec_helper
3
+ --require combinations/shared_combination_examples
4
+ --format documentation
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "https://rubygems.org"
2
+ gemspec
data/README.md ADDED
@@ -0,0 +1,16 @@
1
+ # texas-holdem
2
+
3
+ Gem that provides basic classes for Texas Holdem Poker game
4
+
5
+ ##Installation
6
+
7
+ `gem install texas-holdem`
8
+
9
+ ##Usage
10
+
11
+ To play a game just use gem's binary: `texas-holdem`
12
+
13
+ TODO
14
+
15
+ Add more combinations
16
+ Add betting rounds
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require 'rspec/core/rake_task'
2
+
3
+ RSpec::Core::RakeTask.new(:spec)
4
+
5
+ task :test => :spec
data/bin/texas-holdem ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ require 'texas-holdem'
3
+
4
+ loop do
5
+ TexasHoldem::Game.new(2, TexasHoldem::Deck.new)
6
+ puts 'Want to play another game? y/n'
7
+ break if (gets.chomp).downcase == 'n'
8
+ end
@@ -0,0 +1,13 @@
1
+ require 'texas-holdem/card'
2
+ require 'texas-holdem/deck'
3
+ require 'texas-holdem/game'
4
+ require 'texas-holdem/player_hand'
5
+ require 'texas-holdem/combinations/combination'
6
+ require 'texas-holdem/combinations/highest_card'
7
+ require 'texas-holdem/combinations/pair'
8
+ require 'texas-holdem/combinations/two_pairs'
9
+ require 'texas-holdem/combinations/three_of_a_kind'
10
+ require 'texas-holdem/combinations/straight'
11
+ require 'texas-holdem/combinations/flush'
12
+ require 'texas-holdem/combinations/ranks'
13
+
@@ -0,0 +1,58 @@
1
+ module TexasHoldem
2
+ class Card
3
+ include Comparable
4
+ NUM_RANKS = 2..10
5
+ FACE_CARD_RANKS = { J: 11, Q: 12, K: 13, A: 14 }
6
+ SUITS = {c: '♣', s: '♠', h: '♥', d: '♦'}
7
+ ACE_HIGH = FACE_CARD_RANKS[:A]
8
+ ACE_LOW = NUM_RANKS.min - 1
9
+
10
+ attr_reader :suit, :rank
11
+
12
+ def self.build_all
13
+ all = []
14
+ SUITS.each do |suit, icon|
15
+ NUM_RANKS.to_a.tap {|num_ranks| num_ranks.concat(FACE_CARD_RANKS.keys) }.each do |rank|
16
+ all << new(rank.to_s + suit.to_s)
17
+ end
18
+ end
19
+ all
20
+ end
21
+
22
+ def initialize(card_as_string)
23
+ @suit = parse_suit(card_as_string)
24
+ @rank = parse_rank(card_as_string)
25
+ end
26
+
27
+ def is_face_card?
28
+ FACE_CARD_RANKS.has_key? rank.to_sym
29
+ end
30
+
31
+ def to_i
32
+ is_face_card? ? FACE_CARD_RANKS[rank.to_sym] : rank.to_i
33
+ end
34
+
35
+ def to_s
36
+ rank + SUITS[suit.to_sym]
37
+ end
38
+
39
+ def <=>(other_card)
40
+ to_i <=> other_card.to_i
41
+ end
42
+
43
+ private
44
+
45
+ def parse_suit(card_as_string)
46
+ raw_suit = card_as_string[-1]
47
+ return raw_suit if SUITS[raw_suit.to_sym]
48
+ raise ArgumentError, "Incorrect suit #{raw_suit}. Use only #{SUITS.keys}"
49
+ end
50
+
51
+ def parse_rank(card_as_string)
52
+ raw_rank = card_as_string[0..-2]
53
+ return raw_rank if NUM_RANKS.include?(raw_rank.to_i)
54
+ return raw_rank if FACE_CARD_RANKS.include?(raw_rank.to_sym)
55
+ raise ArgumentError.new "Incorrect rank #{raw_rank}. Use only #{NUM_RANKS} or #{FACE_CARD_RANKS.keys}"
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,86 @@
1
+ module TexasHoldem
2
+ module Combinations
3
+
4
+ class Combination
5
+ include Comparable
6
+ MAX_COMBINATION_LENGTH = 5
7
+ attr_reader :cards, :cards_by_rank, :cards_by_suit, :combination_cards, :kicker_cards
8
+
9
+ def initialize(cards)
10
+ @cards = cards
11
+ @cards_by_rank = get_cards_by_rank
12
+ @cards_by_suit = get_cards_by_suit
13
+ @combination_cards = get_combination_cards(cards)
14
+ @kicker_cards = get_kicker_cards
15
+ end
16
+
17
+ def has_combination?
18
+ combination_cards.length > 0
19
+ end
20
+
21
+ def <=>(other)
22
+ compared_by_rank = RANKS[self.class] <=> RANKS[other.class]
23
+ return compared_by_rank unless compared_by_rank == 0
24
+ compared_by_same_rank = compare_same_rank(other)
25
+ return compared_by_same_rank unless compared_by_same_rank == 0
26
+ compare_kickers(other)
27
+ end
28
+
29
+ def to_s
30
+ "#{combination_cards.map(&:to_s)}, kickers: #{kicker_cards.map(&:to_s)}"
31
+ end
32
+
33
+ private
34
+
35
+ def get_cards_by_rank
36
+ cards_by_rank = {}
37
+ cards.each do |card|
38
+ cards_by_rank[card.to_i] ||= []
39
+ cards_by_rank[card.to_i] << card
40
+ end
41
+ cards_by_rank
42
+ end
43
+
44
+ def get_cards_by_suit
45
+ cards_by_suit = {}
46
+ cards.each do |card|
47
+ cards_by_suit[card.suit] ||= []
48
+ cards_by_suit[card.suit] << card
49
+ end
50
+ cards_by_suit
51
+ end
52
+
53
+ def get_kicker_cards
54
+ cards.reject do |card|
55
+ combination_cards.include?(card)
56
+ end.sort{ |c1, c2| c2 <=> c1 }.take(MAX_COMBINATION_LENGTH - combination_cards.length)
57
+ end
58
+
59
+ #Template method
60
+ #every ancestor of Combination class is supposed to implement this method
61
+ #return -1,0 or 1 as a result of comparing two combinations of same class
62
+ def compare_same_rank(other)
63
+ raise NotImplementedError 'Provide an implementation of #compare_same_rank method in ancestors'
64
+ end
65
+
66
+ #Template method
67
+ #every ancestor of Combination class is supposed to implement this method
68
+ #returns an array containing all cards needed to build a combination
69
+ #if there is no way to build a combination return an empty array
70
+ def get_combination_cards(cards)
71
+ raise NotImplementedError 'Provide an implementation of #get_combination_cards method in ancestors'
72
+ end
73
+
74
+ #compares kickers of two combinations of same rank
75
+ #assuming kicker cards are stored in sorted array
76
+ def compare_kickers(other)
77
+ kicker_cards.each_with_index do |kicker, i|
78
+ other_kicker = other.kicker_cards[i]
79
+ compared_by_kicker = kicker <=> other_kicker
80
+ return compared_by_kicker unless compared_by_kicker == 0
81
+ end
82
+ 0
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,23 @@
1
+ module TexasHoldem
2
+ module Combinations
3
+ class Flush < Combination
4
+
5
+ def get_combination_cards(cards)
6
+ same_suit = cards_by_suit.find do |suit, cards_of_same_suit|
7
+ cards_of_same_suit.length >= 5
8
+ end
9
+ same_suit ? same_suit[1].sort.last(5) : []
10
+ end
11
+
12
+ def compare_same_rank(other)
13
+ combination_cards.each_with_index do |highest_card, i|
14
+ other_highest_card = other.combination_cards[i]
15
+ compared_by_high = highest_card <=> other_highest_card
16
+ return compared_by_high unless compared_by_high == 0
17
+ end
18
+ 0
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,13 @@
1
+ module TexasHoldem
2
+ module Combinations
3
+ class HighestCard < Combination
4
+ def get_combination_cards(cards)
5
+ cards.empty? ? [] : [cards.max]
6
+ end
7
+
8
+ def compare_same_rank(other)
9
+ combination_cards[0] <=> other.combination_cards[0]
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ module TexasHoldem
2
+ module Combinations
3
+ class Pair < Combination
4
+
5
+ def get_combination_cards(cards)
6
+ cards_by_rank.each do |rank, cards_of_same_rank|
7
+ return cards_of_same_rank if cards_of_same_rank.length == 2
8
+ end
9
+ []
10
+ end
11
+
12
+ def compare_same_rank(other)
13
+ combination_cards[0] <=> other.combination_cards[0]
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,13 @@
1
+ module TexasHoldem
2
+ module Combinations
3
+ #Order matters. Highest combinations should go first
4
+ RANKS = {
5
+ Flush => 6,
6
+ Straight => 5,
7
+ ThreeOfAKind => 4,
8
+ TwoPairs => 3,
9
+ Pair => 2,
10
+ HighestCard => 1
11
+ }
12
+ end
13
+ end
@@ -0,0 +1,39 @@
1
+ module TexasHoldem
2
+ module Combinations
3
+ class Straight < Combination
4
+
5
+ def get_combination_cards(cards)
6
+ ranks = add_ace_low(cards_by_rank.keys).sort
7
+ return [] if ranks.length < 5
8
+ straight = get_all_sequences(ranks).find {|seq| seq.length >= 5}
9
+ return [] unless straight
10
+ straight.last(5).map do |rank|
11
+ rank = Card::ACE_HIGH if rank == Card::ACE_LOW
12
+ cards_by_rank[rank][0]
13
+ end
14
+ end
15
+
16
+ def compare_same_rank(other)
17
+ combination_cards.max <=> other.combination_cards.max
18
+ end
19
+
20
+ def add_ace_low(ranks)
21
+ ranks << Card::ACE_LOW if ranks.index(Card::ACE_HIGH)
22
+ ranks
23
+ end
24
+
25
+ def get_all_sequences(ranks)
26
+ sequences, current_sequence = [], []
27
+ ranks.each do |rank|
28
+ if current_sequence.empty? || rank - current_sequence.last == 1
29
+ current_sequence << rank
30
+ else
31
+ sequences << current_sequence
32
+ current_sequence = [rank]
33
+ end
34
+ end
35
+ sequences << current_sequence
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,18 @@
1
+ module TexasHoldem
2
+ module Combinations
3
+ class ThreeOfAKind < Combination
4
+
5
+ def get_combination_cards(cards)
6
+ cards_by_rank.each do |rank, cards_of_same_rank|
7
+ return cards_of_same_rank if cards_of_same_rank.length == 3
8
+ end
9
+ []
10
+ end
11
+
12
+ def compare_same_rank(other)
13
+ combination_cards[0] <=> other.combination_cards[0]
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,25 @@
1
+ module TexasHoldem
2
+ module Combinations
3
+ class TwoPairs < Combination
4
+
5
+ def get_combination_cards(cards)
6
+ pairs = []
7
+ cards_by_rank.each do |rank, cards_of_same_rank|
8
+ pairs << cards_of_same_rank if cards_of_same_rank.length == 2
9
+ end
10
+ if pairs.length == 2
11
+ pairs.flatten
12
+ elsif pairs.length > 2
13
+ pairs.sort_by { |pair| pair[0] }.last(2).flatten
14
+ else
15
+ []
16
+ end
17
+ end
18
+
19
+ def compare_same_rank(other)
20
+ combination_cards.max <=> other.combination_cards.max
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,14 @@
1
+ module TexasHoldem
2
+ class Deck
3
+ attr_reader :cards
4
+
5
+ def initialize
6
+ @cards = Card.build_all
7
+ @cards.shuffle!
8
+ end
9
+
10
+ def deal(n)
11
+ cards.pop n
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,22 @@
1
+ module TexasHoldem
2
+ class Game
3
+
4
+ attr_reader :players, :common_cards
5
+
6
+ def initialize(players_count, deck)
7
+ @players = []
8
+ @common_cards = deck.deal(5)
9
+ players_count.times do |i|
10
+ player_cards = deck.deal(2)
11
+ puts "Player #{i} gets #{player_cards.map(&:to_s)}"
12
+ players << PlayerHand.new(player_cards.concat(@common_cards))
13
+ end
14
+ puts "Cards on table: #{@common_cards.map(&:to_s)}"
15
+ puts "The winner is player #{@players.index(winner)} with combination #{winner.best_combination}"
16
+ end
17
+
18
+ def winner
19
+ players.max
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,28 @@
1
+ module TexasHoldem
2
+ class PlayerHand
3
+ include Comparable
4
+ attr_reader :best_combination, :cards
5
+
6
+ def initialize(cards)
7
+ @cards = cards
8
+ @best_combination = get_best_combination
9
+ end
10
+
11
+ def add_cards(cards)
12
+ @cards.push(*cards)
13
+ @best_combination = get_best_combination
14
+ end
15
+
16
+ def <=>(other)
17
+ best_combination <=> other.best_combination
18
+ end
19
+
20
+ private
21
+
22
+ def get_best_combination
23
+ Combinations::RANKS.keys.map do |combination_class|
24
+ combination_class.new(cards)
25
+ end.find(&:has_combination?)
26
+ end
27
+ end
28
+ end
data/spec/card_spec.rb ADDED
@@ -0,0 +1,71 @@
1
+ require 'rspec'
2
+
3
+ describe TexasHoldem::Card do
4
+
5
+ it 'builds a set 52 cards' do
6
+ expect(TexasHoldem::Card.build_all.length).to eq(52)
7
+ end
8
+
9
+ it 'includes Comparable' do
10
+ expect(TexasHoldem::Card.ancestors).to include(Comparable)
11
+ end
12
+
13
+ describe '#initialize' do
14
+ context 'when arguments are valid' do
15
+ before :each do
16
+ @card1 = TexasHoldem::Card.new('10c')
17
+ @card2 = TexasHoldem::Card.new('2h')
18
+ @card3 = TexasHoldem::Card.new('Jd')
19
+ end
20
+
21
+ it 'parses suit' do
22
+ expect(@card1.suit).to eq('c')
23
+ expect(@card2.suit).to eq('h')
24
+ expect(@card3.suit).to eq('d')
25
+ end
26
+
27
+ it 'parses rank' do
28
+ expect(@card1.rank).to eq('10')
29
+ expect(@card2.rank).to eq('2')
30
+ expect(@card3.rank).to eq('J')
31
+ end
32
+ end
33
+
34
+ context 'when arguments are invalid' do
35
+ it 'returns an error if suit is invalid' do
36
+ expect { TexasHoldem::Card.new('Qk') }.to raise_error ArgumentError
37
+ end
38
+
39
+ it 'returns an error if rank is invalid' do
40
+ expect { TexasHoldem::Card.new('Ih') }.to raise_error ArgumentError
41
+ end
42
+ end
43
+ end
44
+
45
+ describe '#is_face_card?' do
46
+ it 'returns true' do
47
+ expect(TexasHoldem::Card.new('As').is_face_card?).to eq(true)
48
+ end
49
+
50
+ it 'returns false' do
51
+ expect(TexasHoldem::Card.new('5d').is_face_card?).to eq(false)
52
+ end
53
+ end
54
+
55
+ describe '#to_s' do
56
+ it 'returns string representation of card' do
57
+ expect(TexasHoldem::Card.new('Qs').to_s).to eq('Q♠')
58
+ expect(TexasHoldem::Card.new('Ad').to_s).to eq('A♦')
59
+ expect(TexasHoldem::Card.new('10c').to_s).to eq('10♣')
60
+ expect(TexasHoldem::Card.new('9h').to_s).to eq('9♥')
61
+ end
62
+ end
63
+
64
+ describe '#<=>' do
65
+ it 'compares two cards' do
66
+ expect(TexasHoldem::Card.new('Qs')).to eq TexasHoldem::Card.new('Qc')
67
+ expect(TexasHoldem::Card.new('Qs')).to be > TexasHoldem::Card.new('2d')
68
+ expect(TexasHoldem::Card.new('Qs')).to be < TexasHoldem::Card.new('Kh')
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,23 @@
1
+ require 'rspec'
2
+
3
+ describe TexasHoldem::Combinations::Combination do
4
+
5
+ describe '#<=>' do
6
+
7
+ let(:highest1) { TexasHoldem::Combinations::HighestCard.new([Card.new('As'), Card.new('Jh'), Card.new('3c')]) }
8
+ let(:highest2) { TexasHoldem::Combinations::HighestCard.new([Card.new('Ah'), Card.new('2d'), Card.new('Js')]) }
9
+ let(:highest3) { TexasHoldem::Combinations::HighestCard.new([Card.new('3d'), Card.new('Js'), Card.new('Ac')]) }
10
+ let(:pair){ TexasHoldem::Combinations::Pair.new([Card.new('As'), Card.new('Ac')]) }
11
+
12
+ it 'compares combinations by rank' do
13
+ #todo add more compares
14
+ expect(pair).to be > highest1
15
+ end
16
+
17
+ it 'compares combinations by kickers' do
18
+ expect(highest1).to be > highest2
19
+ expect(highest1).to eq(highest3)
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,46 @@
1
+ require 'rspec'
2
+
3
+ describe TexasHoldem::Combinations::Flush do
4
+
5
+ include_examples 'initialising with empty array'
6
+
7
+ context 'when combination is present' do
8
+ include_examples 'setting combination info' do
9
+ let(:cards) {[ Card.new('Jh'), Card.new('Ah'), Card.new('Qh'), Card.new('3c'),
10
+ Card.new('4h'), Card.new('Js'), Card.new('6h') ]}
11
+ let(:kicker_cards) {[]}
12
+ let(:combination_cards) {['4♥', '6♥', 'J♥', 'Q♥', 'A♥']}
13
+ end
14
+ end
15
+
16
+ context 'when there is no flush' do
17
+ include_examples 'setting combination info' do
18
+ let(:cards) {[Card.new('Jh'), Card.new('Ah'), Card.new('6h'), Card.new('Qh'), Card.new('Qd')]}
19
+ let(:kicker_cards) {['A♥', 'Q♦', 'Q♥', 'J♥', '6♥']}
20
+ let(:combination_cards) {[]}
21
+ end
22
+ end
23
+
24
+ context 'when there is more than 5 cards in flush' do
25
+ include_examples 'setting combination info' do
26
+ let(:cards) {[ Card.new('Jh'), Card.new('Ah'), Card.new('Qh'), Card.new('10h'),
27
+ Card.new('4h'), Card.new('Js'), Card.new('6h') ]}
28
+ let(:kicker_cards) {[]}
29
+ let(:combination_cards) {['6♥', '10♥', 'J♥', 'Q♥', 'A♥']}
30
+ end
31
+ end
32
+
33
+ include_examples '#has_combination?' do
34
+ let(:cards_with_combination) {[ Card.new('Ah'), Card.new('Qh'), Card.new('10h'), Card.new('9h'), Card.new('2h') ]}
35
+ let(:cards_without_combination) {[Card.new('Jh'), Card.new('Ad'), Card.new('6s')]}
36
+ end
37
+
38
+
39
+ include_examples 'comparing' do
40
+ let(:strongest_combination) { [ Card.new('Kh'), Card.new('Qh'), Card.new('Jh'), Card.new('4h'), Card.new('5h') ] }
41
+ let(:middle_combination1) { [ Card.new('Kh'), Card.new('Qh'), Card.new('10h'), Card.new('9h'), Card.new('2h') ] }
42
+ let(:middle_combination2) { [ Card.new('2d'), Card.new('9d'), Card.new('10d'), Card.new('Qd'), Card.new('Kd') ] }
43
+ let(:weakest_combination) { [ Card.new('Kh'), Card.new('Qh'), Card.new('9h'), Card.new('5h'), Card.new('2h') ] }
44
+ end
45
+
46
+ end
@@ -0,0 +1,27 @@
1
+ require 'rspec'
2
+
3
+ describe TexasHoldem::Combinations::HighestCard do
4
+
5
+ include_examples 'initialising with empty array'
6
+
7
+ context 'when combination is present' do
8
+ include_examples 'setting combination info' do
9
+ let(:cards) {[ Card.new('Jh'), Card.new('Ad'), Card.new('Qc') ]}
10
+ let(:combination_cards) {['A♦']}
11
+ let(:kicker_cards) {['Q♣','J♥']}
12
+ end
13
+ end
14
+
15
+ include_examples '#has_combination?' do
16
+ let(:cards_with_combination) {[ Card.new('Ad'), Card.new('As'), Card.new('6h') ]}
17
+ let(:cards_without_combination) {[]}
18
+ end
19
+
20
+ include_examples 'comparing' do
21
+ let(:strongest_combination) { [Card.new('As'), Card.new('Qc')] }
22
+ let(:middle_combination1) { [Card.new('2s'), Card.new('10d')] }
23
+ let(:middle_combination2) { [Card.new('2h'), Card.new('10h')] }
24
+ let(:weakest_combination) { [Card.new('2s'), Card.new('3c')] }
25
+ end
26
+
27
+ end
@@ -0,0 +1,36 @@
1
+ require 'rspec'
2
+
3
+ describe TexasHoldem::Combinations::Pair do
4
+
5
+ include_examples 'initialising with empty array'
6
+
7
+ context 'when combination is present' do
8
+ include_examples 'setting combination info' do
9
+ let(:cards) {[ Card.new('Jh'), Card.new('Ad'), Card.new('Qs'), Card.new('3c'),
10
+ Card.new('4d'), Card.new('Js'), Card.new('6h') ]}
11
+ let(:kicker_cards) {['A♦', 'Q♠', '6♥']}
12
+ let(:combination_cards) {['J♥', 'J♠']}
13
+ end
14
+ end
15
+
16
+ context 'when there are no pairs' do
17
+ include_examples 'setting combination info' do
18
+ let(:cards) {[Card.new('Jh'), Card.new('Ad'), Card.new('6s')]}
19
+ let(:kicker_cards) {['A♦', 'J♥', '6♠']}
20
+ let(:combination_cards) {[]}
21
+ end
22
+ end
23
+
24
+ include_examples '#has_combination?' do
25
+ let(:cards_with_combination) {[ Card.new('Ad'), Card.new('As'), Card.new('6h') ]}
26
+ let(:cards_without_combination) {[Card.new('Jh'), Card.new('Ad'), Card.new('6s')]}
27
+ end
28
+
29
+ include_examples 'comparing' do
30
+ let(:strongest_combination) { [Card.new('As'), Card.new('Ac')] }
31
+ let(:middle_combination1) { [Card.new('2s'), Card.new('2c'), Card.new('10d')] }
32
+ let(:middle_combination2) { [Card.new('2h'), Card.new('2d'), Card.new('10h')] }
33
+ let(:weakest_combination) { [Card.new('2s'), Card.new('2c'), Card.new('3c')] }
34
+ end
35
+
36
+ end
@@ -0,0 +1,47 @@
1
+ require 'rspec'
2
+
3
+ RSpec.shared_examples 'setting combination info' do
4
+ describe 'setting combination an kicker cards' do
5
+ it 'sets combination cards' do
6
+ comb_cards = described_class.new(cards).combination_cards.map(&:to_s)
7
+ expect(comb_cards). to eq(combination_cards)
8
+ end
9
+
10
+ it 'sets kicker cards' do
11
+ kick_cards = described_class.new(cards).kicker_cards.map(&:to_s)
12
+ expect(kick_cards). to eq(kicker_cards)
13
+ end
14
+ end
15
+ end
16
+
17
+ RSpec.shared_examples 'initialising with empty array' do
18
+ describe 'initialising with empty array of cards' do
19
+ it 'sets combination cards to an empty array' do
20
+ expect(described_class.new([]).combination_cards).to be_empty
21
+ end
22
+ end
23
+
24
+ end
25
+
26
+ RSpec.shared_examples '#has_combination?' do
27
+ describe '#has_combination?' do
28
+ it 'returns true when combination is present' do
29
+ expect(described_class.new(cards_with_combination)).to have_combination
30
+ end
31
+
32
+ it 'returns false when there is no combination' do
33
+ expect(described_class.new(cards_without_combination)).to_not have_combination
34
+ end
35
+ end
36
+ end
37
+
38
+ RSpec.shared_examples 'comparing' do
39
+ describe 'comparing combination of same type' do
40
+ it 'compares combinations' do
41
+ expect(described_class.new(strongest_combination)).to be > described_class.new(weakest_combination)
42
+ expect(described_class.new(middle_combination1)).to be < described_class.new(strongest_combination)
43
+ expect(described_class.new(middle_combination1)).to eq described_class.new(middle_combination2)
44
+ end
45
+ end
46
+ end
47
+
@@ -0,0 +1,56 @@
1
+ require 'rspec'
2
+
3
+ describe TexasHoldem::Combinations::Straight do
4
+
5
+ include_examples 'initialising with empty array'
6
+
7
+ context 'when ace is used as low' do
8
+ include_examples 'setting combination info' do
9
+ let(:cards) {[ Card.new('2h'), Card.new('Ad'), Card.new('3s'), Card.new('5c'),
10
+ Card.new('4d'), Card.new('Js'), Card.new('Qh') ]}
11
+ let(:kicker_cards) {[]}
12
+ let(:combination_cards) {['A♦', '2♥', '3♠', '4♦', '5♣']}
13
+ end
14
+ end
15
+
16
+ context 'when there is a straight combination' do
17
+ include_examples 'setting combination info' do
18
+ let(:cards) {[ Card.new('Jh'), Card.new('Ad'), Card.new('Qs'), Card.new('Kc'),
19
+ Card.new('10d'), Card.new('Js'), Card.new('6h') ]}
20
+ let(:kicker_cards) {[]}
21
+ let(:combination_cards) {['10♦', 'J♥', 'Q♠', 'K♣', 'A♦']}
22
+ end
23
+ end
24
+
25
+ context 'when there is a straight with more than 5 cards' do
26
+ include_examples 'setting combination info' do
27
+ let(:cards) {[ Card.new('4h'), Card.new('6d'), Card.new('9s'), Card.new('5c'),
28
+ Card.new('7d'), Card.new('10s'), Card.new('8h') ]}
29
+ let(:kicker_cards) {[]}
30
+ let(:combination_cards) {['6♦', '7♦', '8♥', '9♠', '10♠']}
31
+ end
32
+ end
33
+
34
+ context 'when there is no straight' do
35
+ include_examples 'setting combination info' do
36
+ let(:cards) {[ Card.new('4h'), Card.new('6d'), Card.new('9s'), Card.new('5c'),
37
+ Card.new('7d'), Card.new('10s'), Card.new('Jh') ]}
38
+ let(:kicker_cards) {['J♥', '10♠', '9♠', '7♦', '6♦']}
39
+ let(:combination_cards) {[]}
40
+ end
41
+ end
42
+
43
+ include_examples '#has_combination?' do
44
+ let(:cards_with_combination) {[ Card.new('3h'), Card.new('4h'), Card.new('5d'), Card.new('7d'), Card.new('6c') ]}
45
+ let(:cards_without_combination) {[ Card.new('4h'), Card.new('6d'), Card.new('9s'), Card.new('5c'),
46
+ Card.new('7d'), Card.new('10s'), Card.new('Jh') ]}
47
+ end
48
+
49
+ include_examples 'comparing' do
50
+ let(:strongest_combination) { [Card.new('Qs'), Card.new('Kc'), Card.new('Js'), Card.new('10c'), Card.new('Ac')] }
51
+ let(:middle_combination1) { [Card.new('5s'), Card.new('6c'), Card.new('7d'), Card.new('8s'), Card.new('9s')] }
52
+ let(:middle_combination2) { [ Card.new('9s'), Card.new('8s'), Card.new('7d'), Card.new('6c'), Card.new('5s')] }
53
+ let(:weakest_combination) { [Card.new('2s'), Card.new('3c'), Card.new('4c'), Card.new('5d'), Card.new('6d')] }
54
+ end
55
+
56
+ end
@@ -0,0 +1,36 @@
1
+ require 'rspec'
2
+
3
+ describe TexasHoldem::Combinations::ThreeOfAKind do
4
+
5
+ include_examples 'initialising with empty array'
6
+
7
+ context 'when combination is present' do
8
+ include_examples 'setting combination info' do
9
+ let(:cards) {[ Card.new('Jh'), Card.new('Ad'), Card.new('Qs'), Card.new('Jc'),
10
+ Card.new('3d'), Card.new('Js'), Card.new('6h') ]}
11
+ let(:kicker_cards) {['A♦', 'Q♠']}
12
+ let(:combination_cards) {['J♥', 'J♣', 'J♠']}
13
+ end
14
+ end
15
+
16
+ context 'when there are no two pairs' do
17
+ include_examples 'setting combination info' do
18
+ let(:cards) {[Card.new('Jh'), Card.new('Jd'), Card.new('Ad'), Card.new('As'), Card.new('2s')]}
19
+ let(:kicker_cards) {['A♦', 'A♠', 'J♥', 'J♦', '2♠']}
20
+ let(:combination_cards) {[]}
21
+ end
22
+ end
23
+
24
+ include_examples '#has_combination?' do
25
+ let(:cards_with_combination) {[ Card.new('Ad'), Card.new('As'), Card.new('Ah'), Card.new('6s') ]}
26
+ let(:cards_without_combination) {[Card.new('Jh'), Card.new('Ad'), Card.new('6s')]}
27
+ end
28
+
29
+ include_examples 'comparing' do
30
+ let(:strongest_combination) { [Card.new('As'), Card.new('Ac'), Card.new('As'), Card.new('Kc')] }
31
+ let(:middle_combination1) { [Card.new('10s'), Card.new('2c'), Card.new('10d'), Card.new('10s')] }
32
+ let(:middle_combination2) { [Card.new('10h'), Card.new('2d'), Card.new('10h'), Card.new('10c')] }
33
+ let(:weakest_combination) { [Card.new('2s'), Card.new('2c'), Card.new('3c'), Card.new('2d')] }
34
+ end
35
+
36
+ end
@@ -0,0 +1,49 @@
1
+ require 'rspec'
2
+
3
+ # ["Q♦", "Q♠"]
4
+ # Cards on table: ["7♣", "A♥", "A♠", "8♥", "7♥"]
5
+ #[Card.new('Qd'),Card.new('Qs'),Card.new('7c'),Card.new('Ah'),Card.new('As'),Card.new('8h'),Card.new('7h')]
6
+
7
+ describe TexasHoldem::Combinations::TwoPairs do
8
+
9
+ include_examples 'initialising with empty array'
10
+
11
+ context 'when combination is present' do
12
+ include_examples 'setting combination info' do
13
+ let(:cards) {[ Card.new('Jh'), Card.new('Ad'), Card.new('Qs'), Card.new('3c'),
14
+ Card.new('3d'), Card.new('Js'), Card.new('6h') ]}
15
+ let(:kicker_cards) {['A♦']}
16
+ let(:combination_cards) {['J♥', 'J♠', '3♣', '3♦']}
17
+ end
18
+ end
19
+
20
+ context 'when there are no two pairs' do
21
+ include_examples 'setting combination info' do
22
+ let(:cards) {[Card.new('Jh'), Card.new('Jd'), Card.new('Ad'), Card.new('6s')]}
23
+ let(:kicker_cards) {['A♦', 'J♥', 'J♦', '6♠']}
24
+ let(:combination_cards) {[]}
25
+ end
26
+ end
27
+
28
+ context 'when there are three pairs' do
29
+ include_examples 'setting combination info' do
30
+ let(:cards) { [Card.new('Qd'), Card.new('Qs'), Card.new('7c'), Card.new('Ah'),
31
+ Card.new('As'), Card.new('8h'), Card.new('7h')] }
32
+ let(:kicker_cards) {['8♥']}
33
+ let(:combination_cards) {['Q♦', 'Q♠', 'A♥', 'A♠']}
34
+ end
35
+ end
36
+
37
+ include_examples '#has_combination?' do
38
+ let(:cards_with_combination) {[ Card.new('Ad'), Card.new('As'), Card.new('6h'), Card.new('6s') ]}
39
+ let(:cards_without_combination) {[Card.new('Jh'), Card.new('Ad'), Card.new('6s')]}
40
+ end
41
+
42
+ include_examples 'comparing' do
43
+ let(:strongest_combination) { [Card.new('Qs'), Card.new('Qc'), Card.new('2s'), Card.new('2c')] }
44
+ let(:middle_combination1) { [Card.new('2s'), Card.new('2c'), Card.new('10d'), Card.new('10s')] }
45
+ let(:middle_combination2) { [Card.new('5h'), Card.new('5d'), Card.new('10h'), Card.new('10c')] }
46
+ let(:weakest_combination) { [Card.new('2s'), Card.new('2c'), Card.new('3c'), Card.new('3d')] }
47
+ end
48
+
49
+ end
@@ -0,0 +1,44 @@
1
+ require 'rspec'
2
+
3
+ describe TexasHoldem::PlayerHand do
4
+
5
+ describe '#initialize' do
6
+ it 'should set best combination' do
7
+ hand = described_class.new([Card.new('As'), Card.new('2c'), Card.new('8d')])
8
+ expect(hand.best_combination).to be_instance_of(TexasHoldem::Combinations::HighestCard)
9
+ end
10
+
11
+ it 'should set best combination to nil' do
12
+ hand = described_class.new([])
13
+ expect(hand.best_combination).to be_nil
14
+ end
15
+ end
16
+
17
+ describe '#add_cards' do
18
+ let(:hand) {described_class.new([])}
19
+
20
+ it 'should add one card' do
21
+ hand.add_cards(Card.new('As'))
22
+ expect(hand.cards).to eq([Card.new('As')])
23
+ end
24
+
25
+ it 'should add array of cards' do
26
+ hand.add_cards([Card.new('As'), Card.new('2s')])
27
+ expect(hand.cards).to eq([Card.new('As'), Card.new('2s')])
28
+ end
29
+
30
+ it 'should update best combination' do
31
+ hand = described_class.new([Card.new('As')])
32
+ hand.add_cards([Card.new('As'), Card.new('2s')])
33
+ expect(hand.best_combination).to be_instance_of(TexasHoldem::Combinations::Pair)
34
+ end
35
+ end
36
+
37
+ describe '#<=>' do
38
+ it 'compares two hands by best combination' do
39
+ hand1 = described_class.new([Card.new('As')])
40
+ hand2 = described_class.new([Card.new('2s'), Card.new('2h')])
41
+ expect(hand2).to be > hand1
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,2 @@
1
+ require 'texas-holdem'
2
+ Card = TexasHoldem::Card
@@ -0,0 +1,17 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = 'texas-holdem'
3
+ spec.version = '0.0.0'
4
+ spec.date = '2015-06-12'
5
+ spec.summary = 'Texas holdem'
6
+ spec.description = 'Texas holdem'
7
+ spec.authors = ['Andrey Chernyshev']
8
+ spec.email = 'libpual@yandex.ru'
9
+ spec.license = 'MIT'
10
+ spec.files = `git ls-files -z`.split("\x0")
11
+ spec.test_files = spec.files.grep(%r{^(spec)/})
12
+ spec.require_paths = ['lib']
13
+ spec.executables = ['texas-holdem']
14
+
15
+ spec.add_development_dependency 'rake'
16
+ spec.add_development_dependency 'rspec'
17
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: texas-holdem
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Andrey Chernyshev
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-06-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Texas holdem
42
+ email: libpual@yandex.ru
43
+ executables:
44
+ - texas-holdem
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".gitignore"
49
+ - ".rspec"
50
+ - Gemfile
51
+ - README.md
52
+ - Rakefile
53
+ - bin/texas-holdem
54
+ - lib/texas-holdem.rb
55
+ - lib/texas-holdem/card.rb
56
+ - lib/texas-holdem/combinations/combination.rb
57
+ - lib/texas-holdem/combinations/flush.rb
58
+ - lib/texas-holdem/combinations/highest_card.rb
59
+ - lib/texas-holdem/combinations/pair.rb
60
+ - lib/texas-holdem/combinations/ranks.rb
61
+ - lib/texas-holdem/combinations/straight.rb
62
+ - lib/texas-holdem/combinations/three_of_a_kind.rb
63
+ - lib/texas-holdem/combinations/two_pairs.rb
64
+ - lib/texas-holdem/deck.rb
65
+ - lib/texas-holdem/game.rb
66
+ - lib/texas-holdem/player_hand.rb
67
+ - spec/card_spec.rb
68
+ - spec/combinations/combination_spec.rb
69
+ - spec/combinations/flush_spec.rb
70
+ - spec/combinations/highest_card_spec.rb
71
+ - spec/combinations/pair_spec.rb
72
+ - spec/combinations/shared_combination_examples.rb
73
+ - spec/combinations/straight_spec.rb
74
+ - spec/combinations/three_of_a_kind_spec.rb
75
+ - spec/combinations/two_pairs_spec.rb
76
+ - spec/player_hand_spec.rb
77
+ - spec/spec_helper.rb
78
+ - texas-holdem.gemspec
79
+ homepage:
80
+ licenses:
81
+ - MIT
82
+ metadata: {}
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ requirements: []
98
+ rubyforge_project:
99
+ rubygems_version: 2.2.1
100
+ signing_key:
101
+ specification_version: 4
102
+ summary: Texas holdem
103
+ test_files:
104
+ - spec/card_spec.rb
105
+ - spec/combinations/combination_spec.rb
106
+ - spec/combinations/flush_spec.rb
107
+ - spec/combinations/highest_card_spec.rb
108
+ - spec/combinations/pair_spec.rb
109
+ - spec/combinations/shared_combination_examples.rb
110
+ - spec/combinations/straight_spec.rb
111
+ - spec/combinations/three_of_a_kind_spec.rb
112
+ - spec/combinations/two_pairs_spec.rb
113
+ - spec/player_hand_spec.rb
114
+ - spec/spec_helper.rb