Topic: How Could I Improve This Code?

I'm in the process of learning Ruby...slowly.  I started by "learning" Rails, but I hit a roadblock, not knowing how to do anything past the basic stuff I read about or saw in other people's code.  So, I set out to learn Ruby first, before I dive into Rails.  I hope it's ok for me to post this here.

I looked around for programs to write, and saw an exercise to write a Hangman game (it didn't include a solution).  This is what I came up with.

#!/usr/bin/env ruby

class Hangman
  # Read word list to get list of possible words
  WORD_LIST = File.read('wordlist.txt').split("\n")
 
  def initialize
    # Set guesses and correct guesses to empty arrays
    @guesses = []
    @correct = []
    # Set incorrect to 0 so it can count up on wrong answers
    @incorrect = 0
    # Sets the word to guess using a random number up to the number of words in the file
    @word = WORD_LIST[rand(WORD_LIST.length)]
    # If the word is not a valid character (ie. space, hyphen, etc.) automatically add it to the @correct array
    for letter in @word.split("").uniq
      unless letter =~ /\w/
        @correct << letter
      end
    end
    display
  end
 
  def display
    hangee
    print_word
    ask_for_letter
  end
 
  def guess(letter)
    # Makes the letter upper-case for easier readability and so no conflicts with lower-case
    letter.upcase!
    # Takes the first letter if more than one are submitted
    if letter.length > 1 && letter != 'QUIT'
      letter = letter.split("").first
      puts "That was not a valid letter.  Assuming you meant #{letter}."
    elsif letter.empty?
      puts "You can not enter a blank space."
      display
    elsif letter == 'QUIT'
      exit
    end
    unless @guesses.include?(letter)
      # Adds the guess to the @guesses array
      @guesses << letter
      # If the guess is a letter in the word
      if @word.include?(letter)
        @correct << letter
        puts "Good guess.  Keep going!"
        display
      # If the guess isn't a letter in the word
      else
        puts "Bad guess.  Guess again."
        @incorrect += 1
        display
      end
    # If the guess has already been made
    else
      puts "You have already guessed that letter!"
      puts "You have guessed: " + @guesses.join(" ")
      display
    end
  end
 
  def ask_for_letter
    # If you've made 7 incorrect guesses already
    if @incorrect >= 7
      puts "\nSorry, you guessed 7 wrong letters."
      puts "The word was #{@word}"
      try_again
    # Checks to see if the correct guesses matches the letters in the word
    elsif @correct.sort == @word.split("").sort.uniq
      puts "\nYou won!!  Congratulations!!"
      try_again
    else
      print "\nGuess a letter: "
      letter = gets.chomp
      guess(letter)
    end
  end
 
  def print_word
    # Prints the word out, using _ for unguessed letters and letters for correctly guessed letters
    for letter in @word.split("")
      if @correct.include?(letter)
        print "#{letter} "
      else
        print "_ "
      end
    end
    puts ""
  end
 
  def try_again
    print "Try again (y/n)? "
    try_again = gets.chomp
    if try_again == "n"
      puts "Bye!"
      exit
    else
      # Restarts the game
      initialize
    end
  end
 
  def hangee
    case @incorrect
    when 0
      guy = "
      \t|---
      \t|
      \t|
      \t|
      \t|
      \t|_____"
    when 1
      guy = "
      \t|---
      \t|  O
      \t|
      \t|
      \t|_____"
    when 2
      guy = "
      \t|---
      \t|  O
      \t|  |
      \t|
      \t|_____"
    when 3
      guy = "
      \t|---
      \t|  O
      \t|  |/
      \t|
      \t|
      \t|_____"
    when 4
      guy = "
      \t|---
      \t|  O
      \t| \\|/
      \t|
      \t|
      \t|_____"
    when 5
      guy = "
      \t|---
      \t|  O
      \t| \\|/
      \t| /
      \t|
      \t|_____"
    when 6
      guy = "
      \t|---
      \t|  O
      \t| \\|/
      \t| /\\
      \t|
      \t|_____"
    when 7
      guy = "
      \t|---
      \t|  |
      \t|  O
      \t| \\|/
      \t| /\\
      \t|_____"
    end
    puts guy + "\n\n"
  end
end

puts "Welcome to hangman. You get seven chances to guess the mystery word. Type quit to exit."
Hangman.new


This is my first Ruby program, and I was hoping to learn a bit from it (which I think I did, because I didn't look at any references when writing it).  I was also hoping you guys could help me improve a bit!

Thanks!

Last edited by Firanide (2007-04-16 12:54:19)

Re: How Could I Improve This Code?

This is really good as is.  You have good reuse, and encapsulation.  I like the way you did the hangee method.

While I think it would be overkill for this application, you could make it more Object Oriented.  To do this, make an object for everything, Word, Guess, Display, etc.  Then add methods that make sense like, Guess.correct?   , Word.contains_guess?, etc. 

This should increase your reuse, and make your code more beautiful and easy to read.

Re: How Could I Improve This Code?

Thanks for your reply.

I've tried to implement what you said, but...well...I have no idea how to do it really.  I understand most of what you're saying, but I'm just wondering...how could I pass values like my @correct and @guesses arrays around between classes without storing them as constants?

Again, thanks. big_smile