Oshuma-gambler 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,10 @@
1
+ === 0.0.2 / 2008-06-09
2
+
3
+ * Basic gambling framework!
4
+
5
+ === 0.0.1 / 2008-06-05
6
+
7
+ * 1 major enhancement
8
+
9
+ * Birthday!
10
+
data/Manifest.txt ADDED
@@ -0,0 +1,31 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README
4
+ README.rdoc
5
+ README.txt
6
+ Rakefile
7
+ bin/gambler_client
8
+ gambler.gemspec
9
+ lib/gambler.rb
10
+ lib/gambler/card.rb
11
+ lib/gambler/deck.rb
12
+ lib/gambler/exceptions.rb
13
+ lib/gambler/game.rb
14
+ lib/gambler/game/basic_game.rb
15
+ lib/gambler/game/blackjack.rb
16
+ lib/gambler/player.rb
17
+ tasks/ditz.rake
18
+ tasks/docs.rake
19
+ tasks/git.rake
20
+ tasks/override_rake_task.rb
21
+ tasks/rcov.rake
22
+ tasks/site.rake
23
+ tasks/util.rb
24
+ test/game/test_basic_game.rb
25
+ test/game/test_blackjack.rb
26
+ test/helper.rb
27
+ test/suite.rb
28
+ test/test_card.rb
29
+ test/test_deck.rb
30
+ test/test_gambler.rb
31
+ test/test_player.rb
data/README ADDED
@@ -0,0 +1 @@
1
+ README.txt
data/README.rdoc ADDED
@@ -0,0 +1 @@
1
+ README.txt
data/README.txt ADDED
@@ -0,0 +1,46 @@
1
+ = Gambler
2
+
3
+ Github:: http://github.com/Oshuma/gambler/
4
+ RubyForge:: http://gambler.rubyforge.org/
5
+
6
+ == DESCRIPTION:
7
+
8
+ Gambler is a Ruby library which can be included into other classes/modules.
9
+ It provides an object oriented interface for common gambling games such as
10
+ Blackjack, Poker, etc.
11
+
12
+ == SYNOPSIS:
13
+
14
+ Ruby library to satisfy yet another human addiction.
15
+
16
+
17
+ == REQUIREMENTS:
18
+
19
+ Pure Ruby, so far.
20
+
21
+ == INSTALL:
22
+
23
+ # From RubyForge:
24
+ $ gem install gambler
25
+
26
+ # From Github:
27
+ $ gem sources -a http://gems.github.com/ (You only need to do this once.)
28
+ $ gem install Oshuma-gambler
29
+
30
+ == LICENSE:
31
+
32
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
33
+ Version 2, December 2004
34
+ http://sam.zoy.org/wtfpl/
35
+
36
+ Copyright (C) 2008 Dale Campbell <dale@save-state.net>
37
+
38
+ Everyone is permitted to copy and distribute verbatim or modified
39
+ copies of this license document, and changing it is allowed as long
40
+ as the name is changed.
41
+
42
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
43
+
44
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
45
+
46
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
data/Rakefile ADDED
@@ -0,0 +1,47 @@
1
+ require 'rubygems'
2
+ require 'hoe'
3
+ require './lib/gambler'
4
+ require './tasks/override_rake_task'
5
+ require './tasks/util'
6
+
7
+ GAMBLER_ROOT = File.dirname(__FILE__) unless defined? GAMBLER_ROOT
8
+
9
+ Hoe.new('Gambler', Gambler::VERSION) do |g|
10
+ g.developer('Dale Campbell', 'dale@save-state.net')
11
+ g.name = 'gambler'
12
+ g.version = Gambler::VERSION
13
+ g.changes = g.paragraphs_of('History.txt', 0..1).join("\n\n")
14
+
15
+ url = g.paragraphs_of('README.txt', 1).first.split(/\n/)[1].split[1].strip
16
+ g.url = url
17
+
18
+ g.summary = 'Ruby library to satisfy yet another human addiction.'
19
+ g.description = g.paragraphs_of('README.txt', 3..3).join("\n\n")
20
+ end
21
+
22
+ # Remove un-needed tasks.
23
+ remove_task 'audit'
24
+ remove_task 'generate_key'
25
+ remove_task 'multi'
26
+ remove_task 'post_blog'
27
+
28
+ Dir["#{GAMBLER_ROOT}/tasks/**/*.rake"].sort.each { |task| load task }
29
+
30
+ task :default => [ :test, :rcov ]
31
+
32
+ desc 'Clean up dynamically generated files'
33
+ task :cleanup do
34
+ %w{
35
+ docs:clear
36
+ issues:report:clear
37
+ rcov:clear
38
+ site:clear_local
39
+ }.each do |clean|
40
+ Rake::Task[clean].invoke
41
+ end
42
+ end
43
+
44
+ desc 'Open a console with Gambler loaded'
45
+ task :console do
46
+ sh "irb -rubygems -r ./lib/gambler.rb"
47
+ end
@@ -0,0 +1,214 @@
1
+ #!/usr/bin/env ruby -w
2
+
3
+ GAMBLER_ROOT = File.join(File.dirname(__FILE__), '..')
4
+ $LOAD_PATH.unshift File.join(GAMBLER_ROOT, 'lib')
5
+ require 'gambler'
6
+
7
+ module Gambler
8
+ # A simple Client which allows you to play around with Gambler.
9
+ # Most of this class can be ignored, as it just sets up the gaming
10
+ # environment. The method to pay attention to is +play_blackjack+.
11
+ # This is the method that actually creates and deals a Blackjack game.
12
+ class Client
13
+ WIDTH = 50
14
+
15
+ $player_quits = false
16
+
17
+ attr_accessor :bots
18
+ attr_accessor :player
19
+
20
+ def initialize
21
+ @player = setup_player
22
+ @bots = Array.new
23
+ end
24
+
25
+ # This is the main client menu.
26
+ def menu
27
+ display_header
28
+ until $player_quits do
29
+ display_player_stats
30
+ display_game_menu
31
+ choice = STDIN.gets.chomp
32
+
33
+ case choice
34
+ when '1':
35
+ play_blackjack
36
+ when '2':
37
+ puts 'This game is not implemented yet.'
38
+ when '3':
39
+ puts 'This game is not implemented yet.'
40
+ when /a/i:
41
+ add_ai_player
42
+ when /p/i:
43
+ setup_player
44
+ when /d/i:
45
+ debug_console
46
+ when /q/i:
47
+ $player_quits = true
48
+ exit
49
+ else
50
+ puts 'Invalid choice, dumbass.'
51
+ end
52
+ end
53
+ end # of menu
54
+
55
+ private
56
+
57
+ # Add an AI Player as an opponent.
58
+ def add_ai_player
59
+ @bots << setup_player(:bot => true)
60
+ end
61
+
62
+ # Simple debugging console.
63
+ def debug_console
64
+ stop_debug = false
65
+ code = Array.new
66
+
67
+ puts # spacer
68
+ puts '== DEBUG CONSOLE =='
69
+ puts 'Commands:'
70
+ puts ' /run - Quit debugger and run the code.'
71
+ puts ' /show - Show the code currently in the buffer.'
72
+ puts ' /quit - Quit debugger without running the code.'
73
+
74
+ until stop_debug do
75
+ print "eval:#{code.size + 1}> "
76
+ line = STDIN.gets.chomp
77
+
78
+ case line
79
+ when '/run':
80
+ run_code(code)
81
+ stop_debug = true
82
+ when '/show':
83
+ show_code_buffer(code)
84
+ when '/quit':
85
+ stop_debug = true
86
+ else # not a command, must be some Ruby code.
87
+ code << line
88
+ end
89
+ end
90
+ end # of debug_console
91
+
92
+ # Client header.
93
+ def display_header
94
+ puts # spacer
95
+ puts ''.center(WIDTH, '=')
96
+ puts " Gambler v#{Gambler::VERSION} ".center(WIDTH, '=')
97
+ puts ''.center(WIDTH, '=')
98
+ puts '----------------------------------------'.center(WIDTH)
99
+ puts '| Feeding yet another human addiction. |'.center(WIDTH)
100
+ puts '----------------------------------------'.center(WIDTH)
101
+ end # of display_header
102
+
103
+ # Pretty print the player's stats.
104
+ def display_player_stats
105
+ puts '- Players -'.center(WIDTH)
106
+ seperator = "\t"
107
+ stat_width = WIDTH - 5
108
+ stats = String.new
109
+ bot_stats = String.new
110
+
111
+ stats << "Player: #{@player.name}".ljust(WIDTH/2)
112
+ stats << seperator
113
+ stats << "Chips: #{@player.chips}"
114
+ puts stats.center(stat_width)
115
+
116
+ if @bots.empty?
117
+ puts 'No AI opponents.'.center(WIDTH)
118
+ else
119
+ @bots.each do |bot|
120
+ bot_stats = "AI: #{bot.name}".ljust(WIDTH/2)
121
+ bot_stats << seperator
122
+ bot_stats << "Chips: #{bot.chips}"
123
+ puts bot_stats.center(stat_width)
124
+ end
125
+ end
126
+ end # of display_player_stats
127
+
128
+ # Client game menu.
129
+ def display_game_menu
130
+ puts '-----------------------'.center(WIDTH)
131
+ puts '| 1) Blackjack |'.center(WIDTH)
132
+ puts '| 2) Stud Poker |'.center(WIDTH)
133
+ puts "| 3) Texas Hold 'em |".center(WIDTH)
134
+ puts '| A) Add AI Player |'.center(WIDTH)
135
+ puts '| D) Debug Console |'.center(WIDTH)
136
+ puts '| P) Setup Player |'.center(WIDTH)
137
+ puts '| Q) Quit |'.center(WIDTH)
138
+ puts '-----------------------'.center(WIDTH)
139
+ puts
140
+ print 'Game: '
141
+ end # of display_menu
142
+
143
+ # Play a quick 1vs1 game of Blackjack.
144
+ def play_blackjack
145
+ begin
146
+ # Create an array and add the player and bots.
147
+ players = Array.new
148
+ players << @player
149
+ players << @bots
150
+ players.flatten!
151
+
152
+ @game = Gambler::Game::Blackjack.new(:players => players)
153
+ rescue Gambler::Exceptions::InvalidPlayerSize
154
+ puts 'Need at least 2 players for blackjack.'
155
+ until @bots.size >= 1
156
+ @bots << setup_player(:bot => true)
157
+ end
158
+ retry
159
+ end # of begin ZOMG!!1
160
+ end # of play_blackjack
161
+
162
+ # Takes an array of +code+ lines and <tt>eval</tt>'s them.
163
+ def run_code(code)
164
+ puts # spacer
165
+ begin
166
+ puts " BEGIN DEBUG ".center(WIDTH, '=')
167
+ eval(code.join("\n")) # Need to join, since +code+ is an Array.
168
+ puts " END DEBUG ".center(WIDTH, '=')
169
+ rescue Exception => error
170
+ puts " DEBUG FAILED ".center(WIDTH, '=')
171
+ puts error
172
+ end
173
+ puts # spacer
174
+ end
175
+
176
+ # Setup a new Player instance.
177
+ def setup_player(options = {})
178
+ default_chips = 100
179
+ if options[:bot]
180
+ who = 'AI player'
181
+ default_name = 'Kenny'
182
+ else
183
+ who = 'your player'
184
+ default_name = 'Human'
185
+ end
186
+
187
+ puts "Setting up #{who}."
188
+ print "Name [#{default_name}]: "
189
+ name = STDIN.gets.chomp
190
+ name = default_name if name.empty?
191
+
192
+ print "Chips [#{default_chips}]: "
193
+ chips = STDIN.gets.chomp.to_i
194
+ chips = default_chips if chips.zero?
195
+
196
+ return Gambler::Player.new(name, :chips => chips)
197
+ end # of setup_player
198
+
199
+ # Takes an array of +code+ lines and prints them.
200
+ def show_code_buffer(code)
201
+ return (puts "Buffer empty.") if code.size.zero?
202
+ puts "== BUFFER ==\n"
203
+ code.each_with_index do |buf, line_num|
204
+ print "#{line_num + 1}: ".rjust(5)
205
+ puts buf
206
+ end
207
+ end
208
+
209
+ end # of Client
210
+
211
+ end # of Gambler
212
+
213
+ # Let's do this!
214
+ Gambler::Client.new.menu
@@ -0,0 +1,163 @@
1
+ module Gambler
2
+
3
+ # Object representing an individual Card.
4
+ #
5
+ # All of the following lines create a king of diamonds:
6
+ # # Array
7
+ # Card.new ['K', 'd']
8
+ #
9
+ # # Hash
10
+ # Card.new :face => 'K', :suit => 'd'
11
+ #
12
+ # # String
13
+ # Card.new 'Kd'
14
+ class Card
15
+ SUITS = %w(c d h s)
16
+ SUIT_NAMES = {
17
+ 'c' => 'Clubs',
18
+ 'd' => 'Diamonds',
19
+ 'h' => 'Hearts',
20
+ 's' => 'Spades'
21
+ }
22
+
23
+ FACES = %w(A K Q J T 9 8 7 6 5 4 3 2)
24
+ FACE_NAMES = {
25
+ 'A' => 'Ace',
26
+ 'K' => 'King',
27
+ 'Q' => 'Queen',
28
+ 'J' => 'Jack',
29
+ 'T' => 'Ten',
30
+ '9' => 'Nine',
31
+ '8' => 'Eight',
32
+ '7' => 'Seven',
33
+ '6' => 'Six',
34
+ '5' => 'Five',
35
+ '4' => 'Four',
36
+ '3' => 'Three',
37
+ '2' => 'Two'
38
+ }
39
+ FACE_VALUES = {
40
+ 'A' => 14,
41
+ 'K' => 13,
42
+ 'Q' => 12,
43
+ 'J' => 11,
44
+ 'T' => 10,
45
+ '9' => 9,
46
+ '8' => 8,
47
+ '7' => 7,
48
+ '6' => 6,
49
+ '5' => 5,
50
+ '4' => 4,
51
+ '3' => 3,
52
+ '2' => 2,
53
+ 'L' => 1 # Magic low Ace.
54
+ }
55
+
56
+ attr_reader :face, :suit
57
+
58
+ # Creates a new Card object, based on the class of +args+.
59
+ def initialize(args)
60
+ face, suit = case args
61
+ when Array:
62
+ build_from_array(args)
63
+ when Hash:
64
+ build_from_hash(args)
65
+ when String:
66
+ build_from_string(args)
67
+ end
68
+ raise Exceptions::InvalidCardType unless SUITS.include?(suit) && FACES.include?(face)
69
+ @face, @suit = face, suit
70
+ end
71
+
72
+ # Class methods.
73
+ class << self
74
+ # Iterator for all Cards; yields one Card in +block+ or returns an Array of all Cards.
75
+ def all(&block)
76
+ cards = Array.new
77
+ each_suit do |suit|
78
+ each_face do |face|
79
+ card = self.new(:face => face, :suit => suit)
80
+ block_given? ? (yield card) : cards << card
81
+ end
82
+ end
83
+ return cards unless block_given?
84
+ end
85
+
86
+ # Iterator for the FACES.
87
+ def each_face(&block)
88
+ FACES.each { |face| yield face }
89
+ end # of each_face
90
+
91
+ # Iterator for the SUITS.
92
+ def each_suit(&block)
93
+ SUITS.each { |suit| yield suit }
94
+ end # of each_suit
95
+
96
+ # Build methods for each SUIT which will return an array of all Cards in that SUIT.
97
+ # This will allow for things like:
98
+ # Card.diamonds.sort_by { |card| card.face_value }.each {|card| puts card}
99
+ class_eval do
100
+ Card.each_suit do |index|
101
+ suit_name = SUIT_NAMES[index].downcase
102
+ define_method(suit_name) do
103
+ cards = Array.new
104
+ all do |card|
105
+ cards << card if card.suit == index
106
+ end
107
+ return cards
108
+ end
109
+ end # of Card.each_suit
110
+ end # of class_eval
111
+ end # of class methods
112
+
113
+ # Return the numerical face value of a Card.
114
+ def face_value
115
+ FACE_VALUES[@face]
116
+ end
117
+
118
+ # Print a human readable description of a Card instance.
119
+ # Card.new('Kd').to_s # => 'Kd'
120
+ def to_s
121
+ @face + @suit
122
+ end
123
+
124
+ # Print a (pretty) human readable description of a Card instance.
125
+ # Card.new('Kd').to_pretty_s # => 'King of Diamonds'
126
+ def to_pretty_s
127
+ face = FACE_NAMES[@face]
128
+ suit = SUIT_NAMES[@suit]
129
+ return "#{face} of #{suit}"
130
+ end
131
+
132
+ private
133
+
134
+ # Build a Card object with the given +array+.
135
+ # Example:
136
+ # build_from_array ['K', 'd']
137
+ def build_from_array(array)
138
+ raise Exceptions::InvalidCardType unless array.size == 2
139
+ face = array.shift
140
+ suit = array.shift
141
+ return face, suit
142
+ end
143
+
144
+ # Build a Card object with the given +hash+.
145
+ # Example:
146
+ # build_from_hash(:face => 'K', :suit => 'd')
147
+ def build_from_hash(hash)
148
+ raise Exceptions::InvalidCardType unless hash.include?(:face) && hash.include?(:suit)
149
+ return hash[:face], hash[:suit]
150
+ end
151
+
152
+ # Build a Card object with the given +string+.
153
+ # Example:
154
+ # build_from_string('Kd')
155
+ def build_from_string(string)
156
+ card = string.split('')
157
+ raise Exceptions::InvalidCardType unless card.size == 2
158
+ face = card.shift # First letter.
159
+ suit = card.shift # Second letter.
160
+ return face, suit
161
+ end
162
+ end # of Card
163
+ end # of Gambler
@@ -0,0 +1,46 @@
1
+ module Gambler
2
+
3
+ # Handles a collection of Gambler::Card objects as a single Deck.
4
+ # +options+ can be:
5
+ # * <tt>cards</tt>: An array of Cards this Deck will contain.
6
+ #
7
+ # Examples:
8
+ #
9
+ # @deck = Deck.new
10
+ class Deck
11
+ attr_reader :cards, :shuffled
12
+
13
+ def initialize(options = {})
14
+ options[:cards] ||= Card.all
15
+ options[:cards].each do |card|
16
+ raise Exceptions::InvalidDeck unless card.is_a? Card
17
+ end
18
+
19
+ @cards = options[:cards]
20
+ @shuffled = false
21
+ end
22
+
23
+ # Deals one Card to the given +player+.
24
+ # Example:
25
+ # @deck.deal_to(@player)
26
+ def deal_to(player)
27
+ card = @cards.first
28
+ raise Exceptions::DeckEmpty if card.nil?
29
+ player.hand << card
30
+ @cards.delete(card)
31
+ end
32
+
33
+ # Shuffles the Deck; changes <tt>@cards</tt> to reflect the shuffle.
34
+ # Example:
35
+ # @deck.shuffle!
36
+ def shuffle!
37
+ @cards.sort! {rand}
38
+ @shuffled = true
39
+ end
40
+
41
+ # Size of the current Deck (number of Cards remaining).
42
+ def size
43
+ @cards.size
44
+ end
45
+ end # of Deck
46
+ end # of Gambler
@@ -0,0 +1,26 @@
1
+ module Gambler
2
+
3
+ module Exceptions
4
+
5
+ # Gambler
6
+ class MustOverrideMethod < Exception; end
7
+
8
+ # Card
9
+ class InvalidCardType < Exception; end
10
+
11
+ # Deck
12
+ class DeckEmpty < Exception; end
13
+ class InvalidDeck < Exception; end
14
+
15
+ # Game
16
+ class InvalidPlayers < Exception; end
17
+ class InvalidPlayerSize < Exception; end
18
+ class NoPlayers < Exception; end
19
+
20
+ # Player
21
+ class NoPlayerName < Exception; end
22
+ class InvalidChipCount < Exception; end
23
+
24
+ end # of Exceptions
25
+
26
+ end # of Gambler
@@ -0,0 +1,31 @@
1
+ module Gambler
2
+
3
+ module Game
4
+
5
+ # The Game of Blackjack.
6
+ class Blackjack < Gambler::Game::BasicGame
7
+ INITIAL_CARDS = 2
8
+
9
+ def initialize(options = {})
10
+ raise Exceptions::InvalidPlayerSize unless options[:players].size >= 2
11
+ super(options)
12
+ @players.each { |player| player.empty_hand! }
13
+ deal_initial_hands
14
+ end
15
+
16
+ private
17
+
18
+ # Deal out the initial cards to each Player.
19
+ def deal_initial_hands
20
+ INITIAL_CARDS.times do
21
+ @players.each do |player|
22
+ @deck.deal_to player
23
+ end
24
+ end
25
+ end
26
+
27
+ end # of Blackjack
28
+
29
+ end # of Game
30
+
31
+ end # of Gambler
@@ -0,0 +1,9 @@
1
+ require 'gambler/game/basic_game'
2
+ require 'gambler/game/blackjack'
3
+
4
+ module Gambler
5
+
6
+ module Game
7
+ end # of Game
8
+
9
+ end # of Gambler
@@ -0,0 +1,64 @@
1
+ module Gambler
2
+
3
+ # Main Player class in which all others should inherit (if neccessary).
4
+ # The Player's +name+ is required.
5
+ #
6
+ # +options+ can contain:
7
+ # * <tt>chips</tt>: Amount of chips the new Player will have. Defaults to 100.
8
+ # * <tt>hand</tt>: An array of Cards the new Player will be holding. Defaults to +nil+.
9
+ #
10
+ # Example:
11
+ # Player.new('Dale')
12
+ # Player.new('Kenny', :chips => 1_000_000)
13
+ # Player.new( 'Cheaty McGee', # has blackjack from the start!
14
+ # :hand => [Card.new('Ad'), Card.new('Kd')] )
15
+ class Player
16
+ # Initial chip count if none is specified.
17
+ CHIP_STACK = 100
18
+
19
+ attr_accessor :name, :chips, :hand
20
+
21
+ def initialize(name, options = {})
22
+ raise Exceptions::NoPlayerName unless name
23
+ @name = name
24
+ @hand = options[:hand] || Array.new
25
+
26
+ @chips = options[:chips] || CHIP_STACK
27
+ raise Exceptions::InvalidChipCount unless @chips.is_a? Fixnum
28
+ end
29
+
30
+ # Removes all the Cards from the Player's hand.
31
+ def empty_hand!
32
+ @hand = Array.new
33
+ end
34
+
35
+ # Pretty object inspection.
36
+ def inspect
37
+ %Q{#<Gambler::Player '#{@name}'>}
38
+ end
39
+
40
+ # Pretty printing of a Player.
41
+ def to_s
42
+ "#{@name} ($#{@chips})"
43
+ end
44
+
45
+ # Allows a Player to view their +hand+ in various formats.
46
+ def view_hand(options = {})
47
+ format = options[:format] || :array
48
+ pretty = options[:pretty] || false
49
+ seperator = options[:seperator] || (pretty ? ', ' : ' ')
50
+
51
+ method = (pretty ? :to_pretty_s : :to_s)
52
+
53
+ case format
54
+ when :array
55
+ cards = Array.new
56
+ @hand.each { |card| cards << card.send(method) }
57
+ when :string
58
+ cards = @hand.collect { |card| card.send(method) }.join(seperator)
59
+ end
60
+ return cards
61
+ end # of view_hand
62
+
63
+ end # of Player
64
+ end # of Gambler