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 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)
@@ -0,0 +1,8 @@
1
+ require 'euler_poker/hand'
2
+ require 'euler_poker/card'
3
+ require 'euler_poker/hand_factory'
4
+ require 'euler_poker/round'
5
+ require 'euler_poker/cli'
6
+
7
+ module EulerPoker
8
+ end
@@ -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,13 @@
1
+ module EulerPoker
2
+ class Flush < Hand
3
+ def <=>(other)
4
+ return super unless super == 0
5
+
6
+ descending_ranks <=> other.descending_ranks
7
+ end
8
+
9
+ def valid?
10
+ flush?
11
+ end
12
+ end
13
+ 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,13 @@
1
+ module EulerPoker
2
+ class HighCard < Hand
3
+ def <=>(other)
4
+ return super unless super == 0
5
+
6
+ descending_ranks <=> other.descending_ranks
7
+ end
8
+
9
+ def valid?
10
+ true
11
+ end
12
+ end
13
+ 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,13 @@
1
+ module EulerPoker
2
+ class Straight < Hand
3
+ def <=>(other)
4
+ return super unless super == 0
5
+
6
+ return ranks.max <=> other.ranks.max
7
+ end
8
+
9
+ def valid?
10
+ straight?
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module EulerPoker
2
+ class StraightFlush < Hand
3
+ def <=>(other)
4
+ return super unless super == 0
5
+
6
+ return ranks.max <=> other.ranks.max
7
+ end
8
+
9
+ def valid?
10
+ straight_flush?
11
+ end
12
+ end
13
+ 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
@@ -0,0 +1,17 @@
1
+ module EulerPoker
2
+ class Round
3
+ def initialize(round)
4
+ @first, @second = HandFactory.round(round)
5
+ end
6
+
7
+ def winner
8
+ if @first > @second
9
+ :first
10
+ elsif @first < @second
11
+ :second
12
+ else
13
+ :tie
14
+ end
15
+ end
16
+ end
17
+ 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: []