console-poker 1.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
+ SHA256:
3
+ metadata.gz: 2e70a169233864f5e2a074a20109adc0136b6a6a9e81d6877b90e37233390238
4
+ data.tar.gz: 16c18c8cee66bfe98d0a186faa7830f8c6dd0564f1ac0b0c4957fb489f0141e3
5
+ SHA512:
6
+ metadata.gz: 6c47146c022d7b5fdfb414b70c73d4355f38e5797be22c8dd485784d0be1c3a12e05d2e8b80972ec0aea9ffa2064894a3a449a66fe31baa578fc0046167e095e
7
+ data.tar.gz: 7444d0f39a2e31863afe4a42a96c66911d3424176431138b6f0adda77aa2a4b685cccc03f7dbc6160ce1f69f52d9fd4819a302363fb7c285305320ec3d2eed32
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ ruby '3.1.2'
4
+
5
+ source 'https://rubygems.org'
6
+
7
+ gem 'factory_bot'
8
+ gem 'pry'
9
+ gem 'rake'
10
+ gem 'rspec'
11
+ gem 'rubocop', require: false
12
+ gem 'rubocop-rake'
13
+ gem 'rubocop-rspec'
14
+ gem 'simplecov', require: false
data/Gemfile.lock ADDED
@@ -0,0 +1,89 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ activesupport (7.0.4)
5
+ concurrent-ruby (~> 1.0, >= 1.0.2)
6
+ i18n (>= 1.6, < 2)
7
+ minitest (>= 5.1)
8
+ tzinfo (~> 2.0)
9
+ ast (2.4.2)
10
+ coderay (1.1.3)
11
+ concurrent-ruby (1.1.10)
12
+ diff-lcs (1.5.0)
13
+ docile (1.4.0)
14
+ factory_bot (6.2.1)
15
+ activesupport (>= 5.0.0)
16
+ i18n (1.12.0)
17
+ concurrent-ruby (~> 1.0)
18
+ json (2.6.2)
19
+ method_source (1.0.0)
20
+ minitest (5.16.3)
21
+ parallel (1.22.1)
22
+ parser (3.1.2.1)
23
+ ast (~> 2.4.1)
24
+ pry (0.14.1)
25
+ coderay (~> 1.1)
26
+ method_source (~> 1.0)
27
+ rainbow (3.1.1)
28
+ rake (13.0.6)
29
+ regexp_parser (2.5.0)
30
+ rexml (3.2.5)
31
+ rspec (3.11.0)
32
+ rspec-core (~> 3.11.0)
33
+ rspec-expectations (~> 3.11.0)
34
+ rspec-mocks (~> 3.11.0)
35
+ rspec-core (3.11.0)
36
+ rspec-support (~> 3.11.0)
37
+ rspec-expectations (3.11.0)
38
+ diff-lcs (>= 1.2.0, < 2.0)
39
+ rspec-support (~> 3.11.0)
40
+ rspec-mocks (3.11.1)
41
+ diff-lcs (>= 1.2.0, < 2.0)
42
+ rspec-support (~> 3.11.0)
43
+ rspec-support (3.11.0)
44
+ rubocop (1.36.0)
45
+ json (~> 2.3)
46
+ parallel (~> 1.10)
47
+ parser (>= 3.1.2.1)
48
+ rainbow (>= 2.2.2, < 4.0)
49
+ regexp_parser (>= 1.8, < 3.0)
50
+ rexml (>= 3.2.5, < 4.0)
51
+ rubocop-ast (>= 1.20.1, < 2.0)
52
+ ruby-progressbar (~> 1.7)
53
+ unicode-display_width (>= 1.4.0, < 3.0)
54
+ rubocop-ast (1.21.0)
55
+ parser (>= 3.1.1.0)
56
+ rubocop-rake (0.6.0)
57
+ rubocop (~> 1.0)
58
+ rubocop-rspec (2.12.1)
59
+ rubocop (~> 1.31)
60
+ ruby-progressbar (1.11.0)
61
+ simplecov (0.21.2)
62
+ docile (~> 1.1)
63
+ simplecov-html (~> 0.11)
64
+ simplecov_json_formatter (~> 0.1)
65
+ simplecov-html (0.12.3)
66
+ simplecov_json_formatter (0.1.4)
67
+ tzinfo (2.0.5)
68
+ concurrent-ruby (~> 1.0)
69
+ unicode-display_width (2.2.0)
70
+
71
+ PLATFORMS
72
+ arm64-darwin-21
73
+ x86_64-linux
74
+
75
+ DEPENDENCIES
76
+ factory_bot
77
+ pry
78
+ rake
79
+ rspec
80
+ rubocop
81
+ rubocop-rake
82
+ rubocop-rspec
83
+ simplecov
84
+
85
+ RUBY VERSION
86
+ ruby 3.1.2p20
87
+
88
+ BUNDLED WITH
89
+ 2.3.15
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Greg Donald
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # console-poker-ruby
2
+
3
+ ![Poker](https://raw.githubusercontent.com/gdonald/console-poker-ruby/main/ss1.png)
4
+
5
+ ![Poker](https://raw.githubusercontent.com/gdonald/console-poker-ruby/main/ss2.png)
6
+
7
+ ## Getting Started
8
+
9
+ Install:
10
+
11
+ gem install console-poker
12
+
13
+ Run:
14
+
15
+ console-poker
16
+
17
+ ## Status
18
+
19
+ [![Ruby](https://github.com/gdonald/console-poker-ruby/workflows/Ruby/badge.svg)](https://github.com/gdonald/console-poker-ruby/actions)
20
+ [![Downloads](https://ruby-gem-downloads-badge.herokuapp.com/console-poker?color=blue&type=total&total_label=)](https://rubygems.org/gems/console-poker)
21
+
22
+ ## License
23
+
24
+ [![GitHub](https://img.shields.io/github/license/gdonald/console-poker-ruby?color=aa0000)](https://github.com/gdonald/console-poker-ruby/blob/main/LICENSE)
25
+
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rspec/core/rake_task'
4
+ task default: :spec
5
+ RSpec::Core::RakeTask.new
data/bin/console-poker ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../lib/poker'
5
+ Poker.new.run
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rake'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.required_ruby_version = '>= 3.1'
7
+ spec.name = 'console-poker'
8
+ spec.version = '1.0.0'
9
+ spec.summary = 'Console Poker'
10
+ spec.description = 'Poker for your console, full version.'
11
+ spec.author = 'Greg Donald'
12
+ spec.email = 'gdonald@gmail.com'
13
+ spec.bindir = 'bin'
14
+ spec.executables << 'console-poker'
15
+ spec.files = FileList['lib/**/*.rb',
16
+ 'bin/*',
17
+ '[A-Z]*',
18
+ 'spec/**/*.rb'].to_a
19
+ spec.homepage = 'https://github.com/gdonald/console-poker-ruby'
20
+ spec.metadata = {
21
+ 'source_code_uri' => 'https://github.com/gdonald/console-poker-ruby',
22
+ 'rubygems_mfa_required' => 'true'
23
+ }
24
+ spec.license = 'MIT'
25
+ spec.post_install_message = "\nType `console-poker` to run!\n\n"
26
+ end
data/lib/poker/card.rb ADDED
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Card
4
+ attr_reader :poker, :value, :suit
5
+
6
+ def initialize(poker, value, suit)
7
+ @poker = poker
8
+ @value = value
9
+ @suit = suit
10
+ end
11
+
12
+ def to_s
13
+ return Card.faces[value][suit] if poker.face_type == 1
14
+
15
+ Card.faces2[value][suit]
16
+ end
17
+
18
+ def self.faces
19
+ [%w[🂡 🂱 🃁 🃑], %w[🂢 🂲 🃂 🃒], %w[🂣 🂳 🃃 🃓], %w[🂤 🂴 🃄 🃔],
20
+ %w[🂥 🂵 🃅 🃕], %w[🂦 🂶 🃆 🃖], %w[🂧 🂷 🃇 🃗], %w[🂨 🂸 🃈 🃘],
21
+ %w[🂩 🂹 🃉 🃙], %w[🂪 🂺 🃊 🃚], %w[🂫 🂻 🃋 🃛], %w[🂭 🂽 🃍 🃝],
22
+ %w[🂮 🂾 🃎 🃞], %w[🂠]]
23
+ end
24
+
25
+ def self.faces2
26
+ [%w[A♠ A♥ A♣ A♦], %w[2♠ 2♥ 2♣ 2♦],
27
+ %w[3♠ 3♥ 3♣ 3♦], %w[4♠ 4♥ 4♣ 4♦],
28
+ %w[5♠ 5♥ 5♣ 5♦], %w[6♠ 6♥ 6♣ 6♦],
29
+ %w[7♠ 7♥ 7♣ 7♦], %w[8♠ 8♥ 8♣ 8♦],
30
+ %w[9♠ 9♥ 9♣ 9♦], %w[T♠ T♥ T♣ T♦],
31
+ %w[J♠ J♥ J♣ J♦], %w[Q♠ Q♥ Q♣ Q♦],
32
+ %w[K♠ K♥ K♣ K♦], %w[??]]
33
+ end
34
+ end
data/lib/poker/deck.rb ADDED
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'card'
4
+
5
+ DECKS = {
6
+ 1 => :regular,
7
+ 2 => :aces,
8
+ 3 => :jacks,
9
+ 4 => :aces_jacks,
10
+ 5 => :sevens,
11
+ 6 => :eights
12
+ }.freeze
13
+
14
+ class Deck
15
+ TOTAL_CARDS = 52
16
+
17
+ attr_accessor :poker, :cards
18
+
19
+ def initialize(poker)
20
+ @poker = poker
21
+ @cards = []
22
+ end
23
+
24
+ def shuffle
25
+ 7.times { cards.shuffle! }
26
+ end
27
+
28
+ def new_regular
29
+ self.cards = []
30
+ 4.times do |suit_value|
31
+ 13.times do |value|
32
+ cards << Card.new(poker, value, suit_value)
33
+ end
34
+ end
35
+ shuffle
36
+ end
37
+
38
+ def new_irregular(values = [])
39
+ self.cards = []
40
+ while cards.count < TOTAL_CARDS
41
+ 4.times do |suit_value|
42
+ next if cards.count >= TOTAL_CARDS
43
+
44
+ values.each do |value|
45
+ cards << Card.new(poker, value, suit_value)
46
+ end
47
+ end
48
+ end
49
+ shuffle
50
+ end
51
+
52
+ def new_aces
53
+ new_irregular([0])
54
+ end
55
+
56
+ def new_jacks
57
+ new_irregular([10])
58
+ end
59
+
60
+ def new_aces_jacks
61
+ new_irregular([0, 10])
62
+ end
63
+
64
+ def new_sevens
65
+ new_irregular([6])
66
+ end
67
+
68
+ def new_eights
69
+ new_irregular([7])
70
+ end
71
+
72
+ def next_card
73
+ cards.shift
74
+ end
75
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Format
4
+ def self.money(value)
5
+ format_str = '%.2f'
6
+ format(format_str, value)
7
+ end
8
+ end
data/lib/poker/hand.rb ADDED
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'hand_ranker'
4
+
5
+ class Hand
6
+ PAYOUT = {
7
+ royal_flush: 800,
8
+ straight_flush: 50,
9
+ four_of_a_kind: 25,
10
+ full_house: 9,
11
+ flush: 6,
12
+ straight: 4,
13
+ three_of_a_kind: 3,
14
+ two_pair: 2,
15
+ one_pair: 1
16
+ }.freeze
17
+
18
+ attr_accessor :poker, :bet, :result, :cards, :rank, :current_card, :discards
19
+
20
+ def initialize(poker, bet)
21
+ @poker = poker
22
+ @bet = bet
23
+ @result = nil
24
+ @cards = []
25
+ @discards = []
26
+ @current_card = 0
27
+ @rank = :unknown
28
+ end
29
+
30
+ def deal_card
31
+ cards << poker.deck.next_card
32
+ end
33
+
34
+ def pay
35
+ return if result
36
+
37
+ HandRanker.new(self)
38
+
39
+ self.result = rank == :unknown ? -bet : PAYOUT[rank] * bet
40
+ poker.money += result
41
+ end
42
+
43
+ def draw
44
+ out = String.new(' ')
45
+ out << draw_cards
46
+ out << draw_money
47
+ out << draw_card_selector
48
+ out << "\n\n"
49
+ out
50
+ end
51
+
52
+ def draw_card_selector
53
+ space = poker.face_type == 2 ? ' ' : ' '
54
+
55
+ out = String.new("\n ")
56
+ 5.times do |i|
57
+ out << (current_card == i ? '⇑' : ' ')
58
+ out << space
59
+ end
60
+ out
61
+ end
62
+
63
+ def draw_money
64
+ out = String.new('')
65
+ out << (result.negative? ? '-' : '+') if result
66
+ out << '$' << Format.money((result || bet).abs / 100.0)
67
+ out << ' '
68
+ out
69
+ end
70
+
71
+ def draw_cards
72
+ faces = poker.face_type == 2 ? Card.faces2 : Card.faces
73
+ card_back = faces[13][0]
74
+
75
+ out = String.new('')
76
+ 5.times do |i|
77
+ out << (discards.include?(i) ? card_back : cards[i].to_s)
78
+ out << ' '
79
+ end
80
+ out << rank_display
81
+ out
82
+ end
83
+
84
+ def rank_display
85
+ if rank == :unknown
86
+ ' '
87
+ else
88
+ rank_name = rank.to_s.split(/ |_/).map(&:capitalize).join(' ')
89
+ " ⇒ #{rank_name} "
90
+ end
91
+ end
92
+
93
+ def ask_hand_action
94
+ puts ' (K) Keep (D) Discard (N) Next (P) Prev (X) Draw'
95
+ c = Poker.getc($stdin)
96
+ case c
97
+ when 'k'
98
+ discards.delete(current_card)
99
+ self.current_card = current_card.succ
100
+ clear_draw_hand_actions
101
+ when 'd'
102
+ discards << current_card unless discards.include?(current_card)
103
+ self.current_card = current_card.succ
104
+ clear_draw_hand_actions
105
+ when 'n'
106
+ self.current_card = current_card.succ
107
+ clear_draw_hand_actions
108
+ when 'p'
109
+ self.current_card = current_card.pred
110
+ clear_draw_hand_actions
111
+ when 'x'
112
+ self.current_card = nil
113
+ replace_discards
114
+ pay
115
+ else
116
+ clear_draw_hand_actions
117
+ end
118
+ end
119
+
120
+ def clear_draw_hand_actions
121
+ normalize_current_card
122
+ poker.clear
123
+ poker.draw_hand
124
+ ask_hand_action
125
+ end
126
+
127
+ def normalize_current_card
128
+ self.current_card = 0 if current_card.negative?
129
+ self.current_card = 4 if current_card > 4
130
+ end
131
+
132
+ def replace_discards
133
+ 5.times do |i|
134
+ next unless discards.include?(i)
135
+
136
+ cards[i] = poker.deck.next_card
137
+ end
138
+
139
+ discards.clear
140
+ end
141
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ class HandRanker
4
+ attr_reader :hand, :vals
5
+
6
+ def initialize(hand)
7
+ @hand = hand
8
+ @vals = hand.cards.map(&:value).compact.sort
9
+ raise ArgumentError, 'hand must have exactly five Cards' unless vals.count == 5
10
+
11
+ hand.rank = rank_hand
12
+ end
13
+
14
+ private
15
+
16
+ def rank_hand
17
+ if royal_flush?
18
+ :royal_flush
19
+ elsif straight_flush?
20
+ :straight_flush
21
+ elsif four_of_a_kind?
22
+ :four_of_a_kind
23
+ elsif full_house?
24
+ :full_house
25
+ elsif flush?
26
+ :flush
27
+ elsif straight?
28
+ :straight
29
+ elsif three_of_a_kind?
30
+ :three_of_a_kind
31
+ elsif two_pair?
32
+ :two_pair
33
+ elsif one_pair?
34
+ :one_pair
35
+ else
36
+ :unknown
37
+ end
38
+ end
39
+
40
+ def royal_flush?
41
+ flush? && vals == [0, 9, 10, 11, 12]
42
+ end
43
+
44
+ def straight_flush?
45
+ flush? && straight?
46
+ end
47
+
48
+ def four_of_a_kind?
49
+ uniq_count_one?(vals, 0, 1, 2, 3) || uniq_count_one?(vals, 1, 2, 3, 4)
50
+ end
51
+
52
+ def full_house?
53
+ (uniq_count_one?(vals, 0, 1, 2) && uniq_count_one?(vals, 3, 4)) ||
54
+ (uniq_count_one?(vals, 0, 1) && uniq_count_one?(vals, 2, 3, 4))
55
+ end
56
+
57
+ def flush?
58
+ hand.cards.map(&:suit).uniq.count == 1
59
+ end
60
+
61
+ def straight?
62
+ vals == [vals[0], vals[0] + 1, vals[0] + 2, vals[0] + 3, vals[0] + 4]
63
+ end
64
+
65
+ def three_of_a_kind?
66
+ uniq_count_one?(vals, 0, 1, 2) ||
67
+ uniq_count_one?(vals, 1, 2, 3) ||
68
+ uniq_count_one?(vals, 2, 3, 4)
69
+ end
70
+
71
+ def two_pair?
72
+ (uniq_count_one?(vals, 0, 1) && uniq_count_one?(vals, 2, 3)) ||
73
+ (uniq_count_one?(vals, 0, 1) && uniq_count_one?(vals, 3, 4)) ||
74
+ (uniq_count_one?(vals, 1, 2) && uniq_count_one?(vals, 3, 4))
75
+ end
76
+
77
+ def one_pair?
78
+ uniq_count_one?(vals, 0, 1) ||
79
+ uniq_count_one?(vals, 1, 2) ||
80
+ uniq_count_one?(vals, 2, 3) ||
81
+ uniq_count_one?(vals, 3, 4)
82
+ end
83
+
84
+ def uniq_count_one?(array, *args)
85
+ array.values_at(*args).uniq.count == 1
86
+ end
87
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Menus
4
+ def draw_bet_options
5
+ puts ' (D) Deal Hand (B) Change Bet (O) Options (Q) Quit'
6
+ c = Poker.getc($stdin)
7
+ case c
8
+ when 'd'
9
+ deal_new_hand
10
+ when 'b'
11
+ new_bet($stdin)
12
+ when 'o'
13
+ clear_draw_hand_game_options
14
+ when 'q'
15
+ self.quitting = true
16
+ else
17
+ clear_draw_hand_bet_options
18
+ end
19
+ end
20
+
21
+ def new_face_type
22
+ puts ' (1) 🂡 (2) A♠'
23
+ loop do
24
+ c = Poker.getc($stdin).to_i
25
+ case c
26
+ when (1..2)
27
+ self.face_type = c
28
+ save_game
29
+ else
30
+ clear_draw_hand_new_face_type
31
+ end
32
+ break if (1..2).include?(c)
33
+ end
34
+ end
35
+
36
+ def new_deck_type
37
+ puts ' (1) Regular (2) Aces (3) Jacks (4) Aces & Jacks (5) Sevens (6) Eights'
38
+ loop do
39
+ c = Poker.getc($stdin).to_i
40
+ case c
41
+ when (1..6)
42
+ self.deck_type = c
43
+ deck.send("new_#{DECKS[c]}")
44
+ save_game
45
+ else
46
+ clear_draw_hand_new_deck_type
47
+ end
48
+ break if (1..6).include?(c)
49
+ end
50
+ end
51
+
52
+ def draw_game_options
53
+ puts ' (T) Deck Type (F) Face Type (B) Back'
54
+ loop do
55
+ c = Poker.getc($stdin)
56
+ case c
57
+ when 't'
58
+ clear_draw_hand_new_deck_type
59
+ clear_draw_hand_bet_options
60
+ when 'f'
61
+ clear_draw_hand_new_face_type
62
+ clear_draw_hand_bet_options
63
+ when 'b'
64
+ clear_draw_hand_bet_options
65
+ else
66
+ clear_draw_hand_game_options
67
+ end
68
+ break if %w[t b f].include?(c)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Utils
4
+ def load_game
5
+ return unless File.readable?(SAVE_FILE)
6
+
7
+ a = File.read(SAVE_FILE).split('|')
8
+ self.deck_type = a[0].to_i
9
+ self.face_type = a[1].to_i
10
+ self.money = a[2].to_i
11
+ self.current_bet = a[3].to_i
12
+ end
13
+
14
+ def save_game
15
+ File.open(SAVE_FILE, 'w') do |file|
16
+ file.puts "#{deck_type}|#{face_type}|#{money}|#{current_bet}"
17
+ end
18
+ end
19
+
20
+ def clear_draw_hand
21
+ clear
22
+ draw_hand
23
+ end
24
+
25
+ def clear_draw_hand_new_deck_type
26
+ clear_draw_hand
27
+ new_deck_type
28
+ end
29
+
30
+ def clear_draw_hand_new_face_type
31
+ clear_draw_hand
32
+ new_face_type
33
+ end
34
+
35
+ def clear_draw_hand_bet_options
36
+ clear_draw_hand
37
+ draw_bet_options
38
+ end
39
+
40
+ def clear_draw_hand_game_options
41
+ clear_draw_hand
42
+ draw_game_options
43
+ end
44
+ end