blackjack 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+