texas-holdem 0.0.0

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