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)  endenddef 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    endenddef 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  endenddef 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    endenddef 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   endenddef 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)  endelse  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)  endendenddef totalscore(num)if num == 1$h2c = 0$h3c = 0elsif num == 2$h3c = 0end  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  endenddef 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..    endenddef 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)  endenddef dealonecard(hand)  hand.insert(-1, $deck.shift(1)).flatten!enddef dealtwocards(hand)  hand.insert(0, $deck.shift(2)).flatten!enddef 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...  endenddef 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)  endenddef 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 chipsenddef 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)enddef 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)  endenddef 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)  endenddef valueofcards(hand)total = 0  for x in hand    total += x[0..0].to_i  end  return totalendgetnumberofhands
Hot 18 game (modified Blackjack)
optimization;ruby;beginner;game;playing cards
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_playerswhile @deck.size > 14 do  placebets!  deal!  @players.each do |player|    player.play!(@deck)  end  @dealer.play!(@deck)  reviewendfinalWith 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  endendThe 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    ...endThen, elsewhere, you can initialize all of the players like this:ordinals = ['First', 'Second', 'Third']init_chips = 150_000# Initialize playersn_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.newWith 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)   numendYour 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}  endendYou 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  endend