euler_poker 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/bin/euler_poker +11 -0
- data/lib/euler_poker.rb +8 -0
- data/lib/euler_poker/card.rb +34 -0
- data/lib/euler_poker/cli.rb +27 -0
- data/lib/euler_poker/hand.rb +82 -0
- data/lib/euler_poker/hand_factory.rb +41 -0
- data/lib/euler_poker/hands/flush.rb +13 -0
- data/lib/euler_poker/hands/four_of_a_kind.rb +19 -0
- data/lib/euler_poker/hands/full_house.rb +29 -0
- data/lib/euler_poker/hands/high_card.rb +13 -0
- data/lib/euler_poker/hands/one_pair.rb +27 -0
- data/lib/euler_poker/hands/straight.rb +13 -0
- data/lib/euler_poker/hands/straight_flush.rb +13 -0
- data/lib/euler_poker/hands/three_of_a_kind.rb +27 -0
- data/lib/euler_poker/hands/two_pair.rb +41 -0
- data/lib/euler_poker/round.rb +17 -0
- metadata +60 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 006bcaa584aea2730f6321f36e756b4ac45aedc7
|
4
|
+
data.tar.gz: d528d36fc097becdec5f17cb918537fb6f7582ef
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e076703a9c5bbdf0332d187d6d310dd934b5a1575e6d92d88e01b1c5dd48a140eb29e44d8e3f368dba8ca5acb90cc3c332216be3955685f9a800f43795002605
|
7
|
+
data.tar.gz: 80c2b1989aa4463c28617791bdbe22bd2f6edc508f60d9101e24af2453abbdc841e99cb51ce26c12ac035ddfd64272beb57498853c1d1f4fa4e0e1245bc3b75a
|
data/bin/euler_poker
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Adds this gem's lib path the the Ruby load path.
|
4
|
+
# Borrowed from https://stackoverflow.com/a/5294358/918507
|
5
|
+
lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
|
6
|
+
$LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
|
7
|
+
|
8
|
+
require 'euler_poker'
|
9
|
+
|
10
|
+
file = ARGV[0]
|
11
|
+
EulerPoker::CLI.new(file)
|
data/lib/euler_poker.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
module EulerPoker
|
2
|
+
RANKS = {
|
3
|
+
'2' => 2,
|
4
|
+
'3' => 3,
|
5
|
+
'4' => 4,
|
6
|
+
'5' => 5,
|
7
|
+
'6' => 6,
|
8
|
+
'7' => 7,
|
9
|
+
'8' => 8,
|
10
|
+
'9' => 9,
|
11
|
+
'T' => 10,
|
12
|
+
'J' => 11,
|
13
|
+
'Q' => 12,
|
14
|
+
'K' => 13,
|
15
|
+
'A' => 14
|
16
|
+
}
|
17
|
+
|
18
|
+
SUITS = {
|
19
|
+
'S' => :spade,
|
20
|
+
'H' => :heart,
|
21
|
+
'D' => :diamond,
|
22
|
+
'C' => :club
|
23
|
+
}
|
24
|
+
|
25
|
+
class Card
|
26
|
+
attr_reader :value, :rank, :suit
|
27
|
+
|
28
|
+
def initialize(card)
|
29
|
+
@value = card
|
30
|
+
@rank = RANKS[card[0]]
|
31
|
+
@suit = SUITS[card[1]]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module EulerPoker
|
2
|
+
class CLI
|
3
|
+
attr_reader :results
|
4
|
+
|
5
|
+
def initialize(file)
|
6
|
+
@file = File.open(file, 'r')
|
7
|
+
print_results
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def print_results
|
13
|
+
hands = 0
|
14
|
+
results = []
|
15
|
+
|
16
|
+
@file.each_line do |line|
|
17
|
+
round = EulerPoker::Round.new(line)
|
18
|
+
|
19
|
+
results << round.winner
|
20
|
+
hands += 1
|
21
|
+
end
|
22
|
+
@file.close
|
23
|
+
|
24
|
+
puts "Player 1 won #{results.count(:first)} out of #{hands} hands."
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module EulerPoker
|
2
|
+
class Hand
|
3
|
+
include Comparable
|
4
|
+
|
5
|
+
def initialize(cards)
|
6
|
+
@cards = cards
|
7
|
+
end
|
8
|
+
|
9
|
+
def <=>(other)
|
10
|
+
ranking <=> other.ranking
|
11
|
+
end
|
12
|
+
|
13
|
+
def ranking
|
14
|
+
RANKED_HANDS.reverse.index(self.class)
|
15
|
+
end
|
16
|
+
|
17
|
+
def ranks
|
18
|
+
@cards.map(&:rank)
|
19
|
+
end
|
20
|
+
|
21
|
+
def suits
|
22
|
+
@cards.map(&:suit)
|
23
|
+
end
|
24
|
+
|
25
|
+
def descending_ranks
|
26
|
+
ranks.sort.reverse
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def rank_counts
|
32
|
+
ranks.reduce(Hash.new(0)) do |counts, rank|
|
33
|
+
counts[rank] += 1
|
34
|
+
counts
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def straight_flush?
|
39
|
+
return true if straight? && flush?
|
40
|
+
end
|
41
|
+
|
42
|
+
def four_of_a_kind?
|
43
|
+
rank_counts.values.any? { |count| count == 4 }
|
44
|
+
end
|
45
|
+
|
46
|
+
def full_house?
|
47
|
+
rank_counts.values.include?(3) && rank_counts.values.include?(2)
|
48
|
+
end
|
49
|
+
|
50
|
+
def flush?
|
51
|
+
suits.uniq.length == 1
|
52
|
+
end
|
53
|
+
|
54
|
+
def straight?
|
55
|
+
# Can't be a straight unless all cards are different ranks
|
56
|
+
return false unless ranks.uniq.count == 5
|
57
|
+
|
58
|
+
sorted_cards = ranks.sort
|
59
|
+
smallest = sorted_cards.first
|
60
|
+
largest = sorted_cards.last
|
61
|
+
|
62
|
+
# Since these are unique and sorted, if the difference between
|
63
|
+
# largest and smallest is 4, then these are consecutive cards
|
64
|
+
largest - smallest == 4
|
65
|
+
end
|
66
|
+
|
67
|
+
def three_of_a_kind?
|
68
|
+
rank_counts.values.any? { |count| count == 3 }
|
69
|
+
end
|
70
|
+
|
71
|
+
def two_pair?
|
72
|
+
rank_counts
|
73
|
+
.values
|
74
|
+
.select { |count| count == 2 }
|
75
|
+
.count == 2
|
76
|
+
end
|
77
|
+
|
78
|
+
def one_pair?
|
79
|
+
rank_counts.values.any? { |count| count == 2 }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
Dir[File.dirname(__FILE__) + '/hands/*.rb'].each { |file| require file }
|
2
|
+
|
3
|
+
module EulerPoker
|
4
|
+
RANKED_HANDS = [
|
5
|
+
StraightFlush,
|
6
|
+
FourOfAKind,
|
7
|
+
FullHouse,
|
8
|
+
Flush,
|
9
|
+
Straight,
|
10
|
+
ThreeOfAKind,
|
11
|
+
TwoPair,
|
12
|
+
OnePair,
|
13
|
+
HighCard
|
14
|
+
]
|
15
|
+
|
16
|
+
class HandFactory
|
17
|
+
def self.round(round)
|
18
|
+
cards = parse_cards(round)
|
19
|
+
first = build_hand(cards.first(5))
|
20
|
+
second = build_hand(cards.last(5))
|
21
|
+
[first, second]
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.hand(hand)
|
25
|
+
cards = parse_cards(hand)
|
26
|
+
build_hand(cards)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.parse_cards(cards_string)
|
30
|
+
cards_string
|
31
|
+
.split
|
32
|
+
.map { |card_string| Card.new(card_string) }
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.build_hand(cards)
|
36
|
+
RANKED_HANDS
|
37
|
+
.map { |h| h.new(cards) }
|
38
|
+
.find{ |h| h.valid? }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module EulerPoker
|
2
|
+
class FourOfAKind < Hand
|
3
|
+
def <=>(other)
|
4
|
+
return super unless super == 0
|
5
|
+
|
6
|
+
return quartet_rank <=> other.quartet_rank
|
7
|
+
end
|
8
|
+
|
9
|
+
def quartet_rank
|
10
|
+
rank_counts
|
11
|
+
.find { |rank, count| count == 4 }
|
12
|
+
.first
|
13
|
+
end
|
14
|
+
|
15
|
+
def valid?
|
16
|
+
four_of_a_kind?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module EulerPoker
|
2
|
+
class FullHouse < Hand
|
3
|
+
def <=>(other)
|
4
|
+
return super unless super == 0
|
5
|
+
|
6
|
+
if triplet_rank == other.triplet_rank
|
7
|
+
return pair_rank <=> other.pair_rank
|
8
|
+
else
|
9
|
+
return triplet_rank <=> other.triplet_rank
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def triplet_rank
|
14
|
+
rank_counts
|
15
|
+
.find { |rank, count| count == 3 }
|
16
|
+
.first
|
17
|
+
end
|
18
|
+
|
19
|
+
def pair_rank
|
20
|
+
rank_counts
|
21
|
+
.find { |rank, count| count == 2 }
|
22
|
+
.first
|
23
|
+
end
|
24
|
+
|
25
|
+
def valid?
|
26
|
+
full_house?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module EulerPoker
|
2
|
+
class OnePair < Hand
|
3
|
+
def <=>(other)
|
4
|
+
return super unless super == 0
|
5
|
+
|
6
|
+
if pair_rank != other.pair_rank
|
7
|
+
pair_rank <=> other.pair_rank
|
8
|
+
else
|
9
|
+
ranked_extra_cards <=> other.ranked_extra_cards
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def pair_rank
|
14
|
+
rank_counts
|
15
|
+
.find { |rank, count| count == 2 }
|
16
|
+
.first
|
17
|
+
end
|
18
|
+
|
19
|
+
def ranked_extra_cards
|
20
|
+
descending_ranks.reject { |rank| rank == pair_rank }
|
21
|
+
end
|
22
|
+
|
23
|
+
def valid?
|
24
|
+
one_pair?
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module EulerPoker
|
2
|
+
class ThreeOfAKind < Hand
|
3
|
+
def <=>(other)
|
4
|
+
return super unless super == 0
|
5
|
+
|
6
|
+
if triplet_rank == other.triplet_rank
|
7
|
+
return ranked_extra_cards <=> other.ranked_extra_cards
|
8
|
+
else
|
9
|
+
return triplet_rank <=> other.triplet_rank
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def triplet_rank
|
14
|
+
rank_counts
|
15
|
+
.find { |rank, count| count == 3 }
|
16
|
+
.first
|
17
|
+
end
|
18
|
+
|
19
|
+
def ranked_extra_cards
|
20
|
+
descending_ranks.reject { |rank| rank == triplet_rank }
|
21
|
+
end
|
22
|
+
|
23
|
+
def valid?
|
24
|
+
three_of_a_kind?
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module EulerPoker
|
2
|
+
class TwoPair < Hand
|
3
|
+
def <=>(other)
|
4
|
+
return super unless super == 0
|
5
|
+
|
6
|
+
if high_pair_rank != other.high_pair_rank
|
7
|
+
high_pair_rank <=> other.high_pair_rank
|
8
|
+
elsif low_pair_rank != other.low_pair_rank
|
9
|
+
low_pair_rank <=> other.low_pair_rank
|
10
|
+
else
|
11
|
+
extra_card <=> other.extra_card
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def high_pair_rank
|
16
|
+
pair_ranks.max
|
17
|
+
end
|
18
|
+
|
19
|
+
def low_pair_rank
|
20
|
+
pair_ranks.min
|
21
|
+
end
|
22
|
+
|
23
|
+
def extra_card
|
24
|
+
rank_counts
|
25
|
+
.find { |rank, count| count == 1 }
|
26
|
+
.first
|
27
|
+
end
|
28
|
+
|
29
|
+
def valid?
|
30
|
+
two_pair?
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def pair_ranks
|
36
|
+
rank_counts
|
37
|
+
.select { |rank, count| count == 2 }
|
38
|
+
.keys
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
metadata
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: euler_poker
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Richard Jones
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-06-26 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A solution to problem 54 of Project Euler
|
14
|
+
email: rico@toasterlovin.com
|
15
|
+
executables:
|
16
|
+
- euler_poker
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- bin/euler_poker
|
21
|
+
- lib/euler_poker.rb
|
22
|
+
- lib/euler_poker/card.rb
|
23
|
+
- lib/euler_poker/cli.rb
|
24
|
+
- lib/euler_poker/hand.rb
|
25
|
+
- lib/euler_poker/hand_factory.rb
|
26
|
+
- lib/euler_poker/hands/flush.rb
|
27
|
+
- lib/euler_poker/hands/four_of_a_kind.rb
|
28
|
+
- lib/euler_poker/hands/full_house.rb
|
29
|
+
- lib/euler_poker/hands/high_card.rb
|
30
|
+
- lib/euler_poker/hands/one_pair.rb
|
31
|
+
- lib/euler_poker/hands/straight.rb
|
32
|
+
- lib/euler_poker/hands/straight_flush.rb
|
33
|
+
- lib/euler_poker/hands/three_of_a_kind.rb
|
34
|
+
- lib/euler_poker/hands/two_pair.rb
|
35
|
+
- lib/euler_poker/round.rb
|
36
|
+
homepage: https://github.com/toasterlovin/euler_poker
|
37
|
+
licenses:
|
38
|
+
- MIT
|
39
|
+
metadata: {}
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options: []
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
requirements: []
|
55
|
+
rubyforge_project:
|
56
|
+
rubygems_version: 2.6.12
|
57
|
+
signing_key:
|
58
|
+
specification_version: 4
|
59
|
+
summary: Euler Poker
|
60
|
+
test_files: []
|