blackjack 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.
Files changed (2) hide show
  1. data/lib/cardtable.rb +363 -0
  2. metadata +67 -0
@@ -0,0 +1,363 @@
1
+ class Cardtable < ActiveRecord::Base
2
+
3
+ belongs_to :game
4
+ belongs_to :player_record, :class_name => "User", :foreign_key => 'player_id'
5
+ belongs_to :dealer_record, :class_name => "User", :foreign_key => 'dealer_id'
6
+
7
+ serialize :player
8
+ serialize :dealer
9
+ serialize :deck
10
+
11
+
12
+ def startup(thisplayer)
13
+ # Setup its dealer, player and game associations
14
+
15
+ # Find the dealer record and associate it with this cardtable object
16
+ self.dealer_record = User.find(26)
17
+ # Update the number of game sessions for the dealer
18
+ self.dealer_record.num_sessions += 1
19
+ self.dealer_record.save
20
+
21
+ # Associate the cardtable player with the logged in user
22
+ self.player_record = thisplayer
23
+ # Update the user information regarding sessions
24
+ self.player_record.num_sessions += 1
25
+ self.player_record.save
26
+
27
+ # Set up a new game object and initialise it.
28
+ newgame
29
+
30
+ # Create a new shuffled desk of cards - we shuffle in case our logic always arranges
31
+ # the cards in the deck the same way.
32
+ self.deck = Deck.new
33
+ shuffle
34
+
35
+ # Create a new dealer object
36
+ self.dealer = Player.new
37
+
38
+ # Create a new player object
39
+ self.player = Player.new
40
+
41
+ # Set the bet to be 5 initially
42
+ self.bet = 5
43
+
44
+ end
45
+
46
+ def newgame
47
+
48
+ # Create a new game object/record but don't save it just yet
49
+ # as the player may not actually decide to play a game in this session
50
+ @game = self.build_game
51
+ self.game = @game
52
+
53
+ # Initialize the game stats for this game
54
+ # We do this here for statistical purposes as it is legal for a session
55
+ # that has no games played
56
+ self.game.user = self.player_record
57
+ self.game.session_num = self.player_record.num_sessions
58
+ self.game.dealer_session_num = self.dealer_record.num_sessions
59
+ self.game.game_num, self.game.dealer_game_num = 0, 0
60
+ self.game.win = false
61
+ self.game.amount = 0
62
+ self.game.num_cards, self.game.dealer_num_cards = 0, 0
63
+ self.game.card_count, self.game.dealer_count = 0, 0
64
+ end
65
+
66
+ def setBet(amount)
67
+ # Set the bet on the cardtable
68
+ self.bet = amount
69
+ end
70
+
71
+ def deal
72
+ # This is the beginning of a new game
73
+
74
+ # Clear out any old game data to reset
75
+ if self.game.game_num > 0
76
+
77
+ # We need to call endGame to save the stats from teh previous game. This
78
+ # is necessary as the player could have hit 'stand' and the lost
79
+ # immediately to the dealer's 2 cards. In this case, the game will not
80
+ # have been saved.
81
+ endGame
82
+
83
+ # Reset the game stuff so it can be used later in the method
84
+ self.game.win = false
85
+ self.game.num_cards, self.game.dealer_num_cards = 0, 0
86
+ self.game.card_count, self.game.dealer_count = 0, 0
87
+
88
+ # Reset the player and dealer objects
89
+ self.player.reset
90
+
91
+ # If we don't make the second dealer card faceup, then this could be
92
+ # dealt to a player - not sure this is true really as cards are dealt from the deck
93
+ self.dealer.hand[1].faceup = true
94
+ self.dealer.reset
95
+
96
+ # Create a new game object and initialise it
97
+ newgame
98
+ end
99
+
100
+
101
+ # Deal 2 cards to both the dealer and the player
102
+ self.deck.deal(self.player)
103
+ self.deck.deal(self.player)
104
+
105
+ self.deck.deal(self.dealer)
106
+ self.deck.deal(self.dealer)
107
+ # The second card dealt to the dealer is face down
108
+ self.dealer.hand[1].faceup = false
109
+
110
+ # Update the number of games played by the player and dealer
111
+ self.player_record.games_played += 1
112
+ self.dealer_record.games_played += 1
113
+
114
+ # We subtract the bet from the player and add to the dealer
115
+ # This is in case the player exits the game before it completes
116
+ # When the game completes naturally, we will take this into consideration
117
+ # Think of this as the dealer holding the pot while the game is playing
118
+ self.player_record.balance -= self.bet
119
+ self.dealer_record.balance += self.bet
120
+
121
+ # Similarly, we assume that the player will lost and the dealer will win
122
+ # in case the game terminates unexpectedly
123
+ # Again, we will take this into consideration if the game ends normally
124
+ self.player_record.games_lost += 1
125
+ self.dealer_record.games_won += 1
126
+
127
+ # Update the games per session average for the player and dealer
128
+ avg = self.player_record.games_played / self.player_record.num_sessions
129
+ if avg < 1
130
+ self.player_record.games_per_session_avg = 1
131
+ else
132
+ self.player_record.games_per_session_avg = avg.floor
133
+ end
134
+ avg = self.dealer_record.games_played / self.dealer_record.num_sessions
135
+ if avg < 1
136
+ self.dealer_record.games_per_session_avg = 1
137
+ else
138
+ self.dealer_record.games_per_session_avg = avg.floor
139
+ end
140
+
141
+ self.player_record.save
142
+ self.dealer_record.save
143
+
144
+ # Update the game stats
145
+ self.game.game_num = self.player_record.games_played
146
+ self.game.dealer_game_num = self.dealer_record.games_played
147
+
148
+ self.game.amount = self.bet
149
+
150
+ # We will only update the players cards so that a 0 number of cards
151
+ # for the dealer signifies that the dealer won before playing his hand
152
+ self.game.num_cards = 2
153
+ self.game.card_count = self.player.score.handScore
154
+
155
+ self.game.save
156
+
157
+ end
158
+
159
+ def hit(thisPlayer, numCards, cardCount)
160
+ # This is where the player gets another card
161
+
162
+ # Deal another card to the player
163
+ self.deck.deal(thisPlayer)
164
+
165
+ # Update the stats and save
166
+ if thisPlayer === self.player
167
+ self.game.num_cards += 1
168
+ self.game.card_count = thisPlayer.score.handScore
169
+ else
170
+ self.game.dealer_num_cards += 1
171
+ self.game.dealer_count = thisPlayer.score.handScore
172
+ end
173
+ self.game.save
174
+
175
+ # If the player is bust then we don't need to do anything - the stats have
176
+ # already been saved and the view will take care of starting a new game
177
+
178
+ # Try to rub salt into the player's wound by showing the
179
+ # dealer's hidden card if the player has bust.
180
+ if self.game.num_cards == 0
181
+ # It's not the dealer who has been hit with a card but the player
182
+ if self.game.card_count > 21
183
+ self.dealer.hand[1].faceup = true
184
+ endGame
185
+ end
186
+ elsif self.game.num_cards > 0
187
+
188
+ # if the dealer isn't winning yet...
189
+ if self.game.dealer_count < self.game.card_count
190
+ # Save the game as usual and wait for the next card to be dealt
191
+ self.game.save
192
+ elsif self.game.dealer_count > 21
193
+ # The dealer has bust
194
+ # Should we tidy up the stats to show that the player has won
195
+ # right here or leave that to 'endGame'?
196
+ endGame
197
+ else
198
+ # The dealer has beaten the player
199
+ endGame
200
+ end
201
+ end
202
+ end
203
+
204
+ def stand
205
+ # This is where the player chooses to stand
206
+
207
+ # Give control to the dealer and play its hand
208
+ self.dealer.hand[1].faceup = true
209
+
210
+ self.game.dealer_num_cards = 2
211
+ self.game.dealer_count = self.dealer.score.handScore
212
+ self.game.save
213
+ end
214
+
215
+ def double
216
+ # This is where the player chooses to double their initial bet
217
+
218
+ # Double the bet
219
+ self.setBet(self.bet * 2)
220
+ self.game.amount = self.bet
221
+
222
+ # Deal one more card only
223
+ hit(self.player, self.game.num_cards, self.game.card_count)
224
+
225
+ # If they're not bust, give control to the dealer and play its hand
226
+ if self.player.score.handScore < 22
227
+ stand
228
+ end
229
+ end
230
+
231
+ def shuffle
232
+ # Shuffle the deck
233
+ self.deck.shuffle
234
+ end
235
+
236
+ def endGame
237
+
238
+ # Call this function when we've determined that a game has finished.
239
+ # This will save the stats.
240
+
241
+ if self.game.dealer_count > 21
242
+ # If the dealer has gone bust, then the player has won. This is the
243
+ # only situation where the player wins. We have to reverse the stats
244
+ # where we assumed that the player would lose before saving the stats
245
+
246
+ # We add twice the bet to the player and subtract from the dealer
247
+ # This is because we already subtracted the bet from the player and
248
+ # added to the dealer at teh start of this game
249
+ self.player_record.balance += self.bet * 2
250
+ self.dealer_record.balance -= self.bet * 2
251
+
252
+ # Similarly, we assumed that the player lost and the dealer won
253
+ # So, we change this accordingly
254
+ self.player_record.games_lost -= 1
255
+ self.player_record.games_won += 1
256
+ self.dealer_record.games_won -= 1
257
+ self.dealer_record.games_lost += 1
258
+
259
+ self.game.win = true
260
+ end
261
+
262
+ # Save the game stats, and for the dealer and player
263
+ self.game.save
264
+ self.player_record.save
265
+ self.dealer_record.save
266
+
267
+ end
268
+
269
+ end
270
+
271
+ class Player
272
+
273
+ attr_accessor :hand, :isawinner, :score
274
+
275
+ def initialize
276
+ # Initially, each player has an empty hand of cards and they are assumed
277
+ # to be the loser of the game. This is in case the player kills the game
278
+ # before the end and that player is assumed to have lost.
279
+ self.hand = Array.new
280
+ self.isawinner = false
281
+ self.score = Array.new
282
+ end
283
+
284
+ def reset
285
+ # Blow away any old data and star again
286
+ self.hand.clear
287
+ self.score.clear
288
+ Player.new
289
+ end
290
+
291
+ end
292
+
293
+ class Card
294
+ RANKS = %w(2 3 4 5 6 7 8 9 10 J Q K A)
295
+ SCORES = %w(2 3 4 5 6 7 8 9 10 10 10 10 11)
296
+ SUITS = %w(Spades Clubs Hearts Diamonds)
297
+
298
+ attr_accessor :rank, :score, :suit, :image, :faceup
299
+
300
+ def initialize(index)
301
+ # Init the rank, suit, score and image of each card based on the
302
+ # argument passed in
303
+ self.rank = RANKS[index % 13]
304
+ self.score = SCORES[index % 13]
305
+ self.suit = SUITS[index % 4]
306
+ self.image = "/images/" + self.rank + self.suit + ".png"
307
+
308
+ # By default, each card is assumed to be dealt face up. When we start
309
+ # playing, only the second card of the dealer will be dealt face down.
310
+ # When it is the dealer's turn to play, this card will be set to be face up
311
+ self.faceup = true
312
+ end
313
+
314
+ end
315
+
316
+ class Deck
317
+
318
+ attr_accessor :cards, :nextcard
319
+
320
+ def initialize
321
+ # Create an array of 52 shuffled cards to be the deck
322
+ self.cards = (0..51).to_a.shuffle.collect { |index| Card.new(index) }
323
+
324
+ # Set the first card to be dealt as the card at index 0 in the deck
325
+ self.nextcard = 0
326
+ end
327
+
328
+ def deal(player)
329
+ # Add the next card in the deck to the player's hand
330
+ # We need to copy the card object from the deck before appending to the hand
331
+ # as it will be referenced otherwise. This can cause the deck card to be 'faceup = false'
332
+ # when we set this attribute for the dealer's second card. This will mean that when the
333
+ # deck loops around on itself, that card will be dealt facedown which is wrong!
334
+ player.hand << self.cards[self.nextcard].dup
335
+ player.score << self.cards[self.nextcard].score.to_i
336
+
337
+ # And then increment the position in the deck of the next card to be dealt
338
+ self.nextcard = (self.nextcard + 1) % 52
339
+ end
340
+
341
+ def shuffle
342
+ # Shuffle the cards in the deck and set the next card to be dealt
343
+ # as the card at index 0
344
+ self.cards = self.cards.shuffle
345
+ self.nextcard = 0
346
+ end
347
+
348
+ end
349
+
350
+ class Array
351
+ # Extend the Array class to create a handScore method
352
+ # It sorts the array so that the Aces are at the end. Aces are initially
353
+ # assumed to have a score of 11. Basically, if the score of the hand
354
+ # is > 21 and we're working with an Ace, make the ace 1 not 11. Also, if
355
+ # we encounter an Ace and we're not at the end of the hand, we take that
356
+ # into consideration by subtracting the value of the remaining Aces. Only
357
+ # really for the unlikely event we get a 10 an Ace and another Ace
358
+
359
+ def handScore
360
+ sort.each_with_index.inject(0){|sum, (current, i)| sum+current > 21-(length-(i+1)) && current==11 ? sum+1 : sum+current}
361
+
362
+ end
363
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: blackjack
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Tony O'Keeffe
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-12-17 00:00:00 +00:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: Gem creation as part of WAF project
23
+ email: tony.okeeffe@student.ncirl.ie
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - lib/cardtable.rb
32
+ has_rdoc: true
33
+ homepage:
34
+ licenses: []
35
+
36
+ post_install_message:
37
+ rdoc_options: []
38
+
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ hash: 3
47
+ segments:
48
+ - 0
49
+ version: "0"
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ hash: 3
56
+ segments:
57
+ - 0
58
+ version: "0"
59
+ requirements: []
60
+
61
+ rubyforge_project:
62
+ rubygems_version: 1.3.7
63
+ signing_key:
64
+ specification_version: 3
65
+ summary: Simple solo player blackjack game
66
+ test_files: []
67
+