8
\$\begingroup\$

I've just completed my first Ruby game. It's a modified BlackJack type game, called Hot 18. I've used all methods for this Ruby game. It's a one-player game, but you can play with 1, 2, or 3 hands.

I would like to make it shorter and more efficient. I know it's kinda long, so I'm not asking you to go over everything.

If you notice any methods or if statements or something else I could shorten/improve on, that would be awesome.

Any other advice/opinions on how crappy or good my game is, is also welcome.

Lastly, since this is just a text version, where should I go from here? How can I make it visual? What program should I use? How to start that next step?

Here is a link where you can change the code/test the game/etc. http://labs.codecademy.com/BnL9#:workspace

$deck = []
suits = ["spades", "diamonds", "clubs", "hearts"]
$hand1 = []
$hand2 = []
$hand3 = []
$handD = []

for x in suits
  for y in 2..9
    w = y.to_s
    $deck.push(w+" of "+x)
  end
end

def getnumberofhands
  print "How many hands do you want to play? (1, 2, or 3) >>>"
  num = gets.chomp.to_i
    if num.between?(1,3)
      startingchips(num)
      $deck.shuffle!
      puts "\n                   ...shuffling deck...\n\n"
      cutdeck
      newround(num)
    else
      getnumberofhands
    end
end

def startingchips(num)
  if num == 1
    $h1c = 150_000
    $h2c = nil
    $h3c = nil
  elsif num == 2
    $h1c = 75_000
    $h2c = 75_000
    $h3c = nil
  elsif num == 3
    $h1c = 50_000
    $h2c = 50_000
    $h3c = 50_000
  end
end

def placebets(chippers)
    if chippers >= 1_000
      print "          Place your bet! (min: 1000, max: #{chippers}) >>>"
      betamt = gets.chomp.to_i
      if betamt.between?(1_000, chippers)
        return betamt
      else
        placebets(chippers)
      end
    else
      print "         You don't have enough chips to place a bet.\n"
      betamt = 0
    end
end

def cutdeck
  print "  Cut the deck. (type a number between 1 and 32) >>>"
  cut_the_deck_number = gets.chomp.to_i
    if cut_the_deck_number.between?(1, 32)
       puts "\n                   ...cutting the deck..."
       bottomcut = $deck.pop(cut_the_deck_number)
       topcut = $deck.pop($deck.length)
       $deck = bottomcut + topcut
    else
      cutdeck
   end
end

def newround(num)
if $deck.length > 14
  if num > 0
  puts "\nFirst Hand:"
  $h1bet = placebets($h1c)
  dealtwocards($hand1)
    if num > 1
    puts "\nSecond Hand:"
    $h2bet = placebets($h2c)
    dealtwocards($hand2)
      if num > 2
      puts "\nThird Hand:"
      $h3bet = placebets($h3c)
      dealtwocards($hand3)
      end
    end
  puts "\n                     ...dealing cards..."
  dealtwocards($handD)
  puts "______________________________________________________________"
  puts "           \nDealer is showing the |#{$handD[1]}|"
  puts "______________________________________________________________"
  aroundtablechoices(num)
  end
else
  puts "______________________________________________________________"
  puts "     ____________________________________________________"
  puts "          _______________________________________"
  puts "\n     Game Over - Not enough cards left in deck to continue."
  if num > 0
  puts "\nFirst Hand:"
  final(num, $h1c)
    if num > 1
    puts "\nSecond Hand:"
    final(num, $h2c)
      if num > 2
      puts "\nThird Hand:"
      final(num, $h3c)
      end
    end
  totalscore(num)
  end
end
end

def totalscore(num)
if num == 1
$h2c = 0
$h3c = 0
elsif num == 2
$h3c = 0
end
  if $h1c + $h2c + $h3c < 150_000
  xxx = 150_000 - $h1c - $h2c - $h3c
  puts "\nYour total score is -#{xxx} (Which is not good at all)"
  else
  xxx = $h1c + $h2c + $h3c - 150_000
    if xxx < 80_500
    puts "\nYour total score is +#{xxx} (Which is okay)"
    elsif xxx < 190_300 and xxx >= 80_500
    puts "\nYour total score is +#{xxx} (Which is pretty decent)"
    elsif xxx >= 190_300
    puts "\nYour total score is +#{xxx} (Which is a great score!)"
    end
  end
end

def final(num, chipso)
  if num == 1
  abcd = 150_000
  elsif num == 2
  abcd = 75_000
  elsif num == 3
  abcd = 50_000
  end
  puts "          Starting Chips: #{abcd}"
  puts "            Ending Chips: #{chipso}"
    if chipso >= abcd
    zyx = chipso - abcd
    puts "\n                  You won #{zyx} play chips!"
    else
    zyx = abcd - chipso
    puts "\n                  You lost #{zyx} play chips.."
    end
end

def aroundtablechoices(num)
  if num > 0
  puts "\nFirst hand is |#{$hand1[0]}|#{$hand1[1]}|\n"
  choices($hand1)
    if num > 1
    puts "_______________________________"
    puts "\nSecond hand is |#{$hand2[0]}|#{$hand2[1]}|\n"
    choices($hand2)
      if num > 2
      puts "_______________________________"
      puts "\nThird hand is |#{$hand3[0]}|#{$hand3[1]}|\n"
      choices($hand3)
      end
    end
  puts "_______________________________"
  puts "\nDealer flips over his second card which is the |#{$handD[0]}|"
  dealerchoices($handD, num)
  end
end

def dealonecard(hand)
  hand.insert(-1, $deck.shift(1)).flatten!
end

def dealtwocards(hand)
  hand.insert(0, $deck.shift(2)).flatten!
end

def choices(hand)
  x = valueofcards(hand)
  if x > 18
    puts "\n              ...you busted with #{x}..."
  elsif x < 18
    hitorstay(hand)
  else
    puts "\n               ...you hit hot 18..."
  end
end

def dealerchoices(hand, num)
  x = valueofcards(hand)
  if x > 18
    puts "\n            ...dealer busted with #{x}..."
    prereview(num)
  elsif x < 15
    dealonecard(hand)
    xxx = hand[-1]
    puts "\n               |#{xxx}|"
    dealerchoices(hand, num)
  else
    puts "\n               ...dealer stayed on #{x}..."
    prereview(num)
  end
end

def review(hand, chips, betamount)
  abc = valueofcards(hand)
  klm = valueofcards($handD)
    if abc == klm and abc < 19
      puts "            You tied (you and dealer both had #{abc})"
      puts "                 +0"
    elsif abc > 18
      puts "            You lost (you went over 18 with #{abc})"
      puts "                 -#{betamount}"
      chips = chips - betamount
    elsif abc < 19 and klm > 18
      puts "            You won (you had #{abc}, and dealer busted with #{klm})"
      puts "                +#{betamount}"
      chips = chips + betamount
    elsif abc < 19 and abc > klm
      puts "            You won (you had #{abc}, and dealer only had #{klm})"
      puts "                +#{betamount}"
      chips = chips + betamount
    elsif abc < 19 and klm < 19 and klm > abc
      puts "            You lost (you only had #{abc}, and dealer had #{klm})"
      puts "                 -#{betamount}"
      chips = chips - betamount
    end
    return chips
end

def prereview(num)
  if num > 0
  puts "_____________________________________________________________________"
  puts "\n            Recap of that round"
  puts "\nFirst Hand:"
  $h1c = review($hand1, $h1c, $h1bet)
    if num > 1
    puts "\nSecond Hand:"
    $h2c = review($hand2, $h2c, $h2bet)
      if num > 2
      puts "\nThird Hand:"
      $h3c = review($hand3, $h3c, $h3bet)
      end
    end
  end
  muckcards(num)
end

def muckcards(num)
  if num > 0
  $hand1.pop(10)
    if num > 1
    $hand2.pop(10)
      if num > 2
      $hand3.pop(10)
      end
    end
  $handD.pop(10)
  newround(num)
  end
end

def hitorstay(hand)
  x = valueofcards(hand)
  print "       You have #{x}, do you want to hit or stay?  >>>"
  y = gets.chomp.downcase
  if y == "h" or y == "hit"
    dealonecard(hand)
    xxx = hand[-1]
    puts "\n              |#{xxx}|"
    choices(hand)
  elsif y == "s" or y == "stay"
    puts "\n              ...you stayed on #{x}..."
  else
    hitorstay(hand)
  end
end

def valueofcards(hand)
total = 0
  for x in hand
    total += x[0..0].to_i
  end
  return total
end

getnumberofhands
\$\endgroup\$
  • 1
    \$\begingroup\$ where should I go from here? How can I make it visual? What program should I use? How to start that next step? These requests are off-topic, but the other review requests can still be fulfilled. \$\endgroup\$ – Jamal Dec 22 '13 at 4:53
  • 5
    \$\begingroup\$ Hot18 sounds more like the name of a website I can't visit at work. \$\endgroup\$ – Mathieu Guindon Dec 23 '13 at 1:55
12
\$\begingroup\$

There is some awkward recursion and mutual recursion being used for flow control:

  • getnumberofhands() calls itself if the input fails validation.
  • hitorstay() calls itself if the input fails validation.
  • newround() calls aroundtablechoices(), which calls dealerchoices(), which calls prereview(), which calls muckcards(), which calls newround().

To begin it all, you call getnumberofhands(), which calls newround(). Who would have guessed that getnumberofhands() actually encompasses the entire game? You can't get an overview of the program unless you trace every line of code!

Basically, you are using function calls as goto, and it's not accepted programming practice. After a few rounds of play, with some invalid input, the call stack is going to be much deeper than it ought to be if you had structured your code properly. Instead, the outline of your code should be:

initialize_players
while @deck.size > 14 do
  placebets!
  deal!

  @players.each do |player|
    player.play!(@deck)
  end
  @dealer.play!(@deck)
  review
end
final

With properly structured code, you can immediately get the big picture.


Your code would benefit immensely with object-oriented programming. In particular, you have many if-conditions to support the one-, two-, or three-player cases. What you want to do is define a Player class:

class Player
  attr_reader :name
  attr_accessor :chips, :hand

  def initialize(name, chips)
    @name = name
    @chips = chips
    @hand = []
  end

  def hand_total
    # Functional code, like this, is more concise than for-loops
    @hand.collect { |card| card.value }.inject(:+)
  end

  def to_s
    '|' + hand.join('|') + '|'
  end

  def play!(deck)
    puts "\n#{self.name} is #{self}"
    # Hit or stay loop
    ...
  end

  def new_hand!
    # Your muckcards() pops 10 cards off.  Why not call clear() instead?
    hand.clear
  end
end

The dealer would be a subclass of Player:

class Dealer < Player
  def initialize
    super('Dealer', 0)
  end

  def play!(deck)
    puts "\n#{self.name} flips over his second card which is the |#{hand[0]}|"
    # Hit or stay code
    ...
end

Then, elsewhere, you can initialize all of the players like this:

ordinals = ['First', 'Second', 'Third']
init_chips = 150_000

# Initialize players
n_players = _prompt_i(1, ordinals.size,
  "How many hands do you want to play? (1, 2, or 3) >>> ")
@players = (0...n_players).collect do |nth|
  Player.new("#{ordinals[nth]} Hand", init_chips / n_players)
end
@dealer = Dealer.new

With all the players in an array, you can easily do @players.each { |player| ... } everywhere, without always having to worry about how many players there are.

It's worthwhile to define a function to avoid code repetition:

def _prompt_i(min, max, prompt)
   begin
     print prompt
     num = gets.to_i
   end while ! num.between?(min, max)
   num
end

Your cards are "stringly typed" — you store them as strings of the form "2 of spades", then call .to_i when summing the hand. Better to define a proper class for them so that you can call .value instead:

class Card
  attr_reader :value, :suit

  def initialize(value, suit)
    @value = value
    @suit = suit
  end

  def to_s
    "#{@value} of #{@suit}"
  end
end

You should also group your deck manipulation code into a class (and tighten up the code for .initialize and .cut!).

class Hot18Deck
  @@suits = ["spades", "diamonds", "clubs", "hearts"]

  def initialize
    @cards = @@suits.collect do |suit|
      (2..9).collect { |value| Card.new(value, suit) }
    end.flatten
  end

  def size
    @cards.size
  end

  def shuffle!
    @cards.shuffle!
    self
  end

  def cut!(position=nil)
    position = rand(@cards.size) if position.nil?
    @cards.rotate!(position)
    self
  end

  def deal!(player, n_cards=1)
    cards = (1..n_cards).collect { @cards.pop }
    player.hand.concat(cards)
    cards
  end
end
\$\endgroup\$
  • 1
    \$\begingroup\$ LOL.. I was just writing an answer.. then read yours. You made all the points I was going to and more. I couldn't have said it better. Nice work \$\endgroup\$ – Nigel Thorne Dec 22 '13 at 7:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.