Comparing Beginner’s RPS Code to Advanced RPS Code

Table of Contents

Beginner’s Code

Beginner’s Code Pros

Beginner’s Code Cons

Beginner’s Analysis

It is common for beginners to use many repetitive statements when coding a simple Ruby app.

In this case, using many if/elsif/else statements is typical beginner code formatting when programming a rock-paper-scissors game.

The beginner’s code might also show redundancy by programming more than what needs to be coded (this situation is different for advanced Ruby programmers).

Most beginners’ code lacks modularization and a solid code structure; thus making it very unorganized.

While beginners’ code might look simple and easy to understand, it is not always the most efficient.

In this particular case, the advanced code is longer and more confusing, though it has more modularization and professionalism attached to it (in some cases, it will actually run faster than the beginner’s code).

What Beginner’s Code Looks Like

% ruby beginner-rps.rb
options = ["rock", "paper", "scissors"]
computer_choice = options[rand(options.length)] 
puts "What's your choice?"
puts "rock, paper, or scissors"
user_input = gets.chomp.downcase # read user input and convert to lower case
loop do 
  if (user_input == "rock" || user_input == "paper" || user_input == "scissors")
    if (user_input == computer_choice)
      puts "We got the same, let's keep playing!"
    elsif (user_input == "rock" && computer_choice == "scissors")
      puts "computer choice is: " + computer_choice + " , you win! :)"
    elsif (user_input == "rock" && computer_choice == "paper")
      puts "computer choice is: " + computer_choice + " ,computer wins :("
    elsif (user_input == "paper" && computer_choice == "scissors")
      puts "computer choice is: " + computer_choice + " ,computer wins :("
    elsif (user_input == "paper" && computer_choice == "rock")
      puts "computer choice is: " + computer_choice + " , you win! :)"
    elsif (user_input == "scissors" && computer_choice == "rock")
      puts "computer choice is: " + computer_choice + " ,computer wins :("
    elsif (user_input == "scissors" && computer_choice == "paper")
      puts "computer choice is: " + computer_choice + " , you win! :)"
    else
      puts "Invalid choice, enter Rock, Paper, or Scissors"
      puts "What's your choice?"
      user_input = gets.chomp.downcase
    end
  end
  break
end

Advanced Code

Advanced Code Pros

Advanced Code Cons

Advanced Analysis

An advanced coder’s code could be longer or shorter than a beginner’s code.

Advanced code usually has seldom repetition and more subtle programming.

Advanced coders tend to shy away from many if, elsif, & else statements. This is primarily due to the way an advanced coder thinks. One might replace multiple elsifs with a case statement –adding more structure and organization to the code–.

Subtlety is usually painted all over advanced ruby code. While this does make the code harder to understand, it certainly adds more structure professionalism to the code.

What Advanced Code Looks Like

% ruby advanced-rps.rb
#!/usr/bin/env ruby

=begin
|====================================|
| Req Ruby Ver | Req Ruby Gems Ver   |
|--------------|---------------------|
| >= v2.0.0    | >= v2.6.0           |
|====================================|
=end

class PlayRockPaperScissorsGame 

  module RockPaperScissors
    VERSION = "2.9.1" 
  end
  
  # import the colorize gem
  require "colorized_string"
  ColorizedString.colors # import colors; ex: red, green, blue from colorize gem
  ColorizedString.modes  # import modes; ex: bold, italic, underline from colorize gem

  module Constants 
    NTRY_TO_SYM = { # define entry to symbol (key to value)
      'p' => :PAPER   , 'paper'    => :PAPER   ,
      'r' => :ROCK    , 'rock'     => :ROCK    ,
      's' => :SCISSORS, 'scissors' => :SCISSORS
    } 
    VALID_ENTRIES    = NTRY_TO_SYM.keys 
    COMPUTER_CHOICES = NTRY_TO_SYM.values
    WINNERS = [ 
      # format: player choice, computer choice
      [:SCISSORS, :PAPER   ], 
      [:PAPER   , :ROCK    ], 
      [:ROCK    , :SCISSORS]
    ] 
    LOSERS = WINNERS.map { |player_choice,computer_choice| [computer_choice,player_choice] } # this will take the original WINNERS array and flip the symbols, thus returning a loss for the user/player
    INIT_STRINGS = [
      ColorizedString["You are about to enter a rock-paper-scissors best of 3 match."].colorize(:green), 
      ColorizedString["Press the return/enter key to continue..."].colorize(:green)                    , 
      ""
    ]
  end

  protected_methods :Constants 

  class << self # define a self calling method within the parent class
    def continue(str1,str2,str3)
      puts  str1 
      print str2
      gets  # press enter or return to continue
      puts  str3
    end 
  end 

  continue(Constants::INIT_STRINGS[0], Constants::INIT_STRINGS[1], Constants::INIT_STRINGS[2]) 

  def initialize # initialize variables
    @player_score = @computer_score = @ties = 0 
  end 

  def play(winning_score) 
    while @player_score < winning_score && @computer_score < winning_score
      puts ColorizedString["Player score: #{@player_score}, "].colorize(:blue) + 
           ColorizedString["Computer score: #{@computer_score}, Ties: #{@ties}."].colorize(:blue) 
      player = PrivateMethods.player_choice 
      computer = Constants::COMPUTER_CHOICES.sample # chooses a "random" option
      puts ColorizedString["\nPlayer chooses #{player.to_s.downcase}."].colorize(:blue) 
      puts ColorizedString["Computer chooses #{computer.to_s.downcase}."].colorize(:blue)
      case PrivateMethods.player_outcome [player, computer] 
      when :WIN
        puts ColorizedString["#{player.to_s.capitalize} beats #{computer.to_s.downcase}, player wins the round."].colorize(:red) 
        @player_score += 1 # @player_score = @player_score + 1
      when :LOSE
        puts ColorizedString["#{computer.to_s.capitalize} beats #{player.to_s.downcase}, computer wins the round."].colorize(:red)
        @computer_score += 1 
      else 
        puts ColorizedString["Tie, choose again"].colorize(:red) 
        @ties += 1
      end
    end
    puts ColorizedString["\nFinal score: player: #{@player_score}, "].colorize(:blue) +
         ColorizedString["computer: #{@computer_score} (ties: #{@ties})."].colorize(:blue)
    case PrivateMethods.final_outcome(@player_score, @computer_score)
    when :WIN 
      puts ColorizedString["Player wins!"].colorize(:red) 
    when :LOSE
      puts ColorizedString["Computer wins!"].colorize(:red)
    else 
      puts ColorizedString["It's a tie!"].colorize(:red) 
    end 
    gets
  end 

  module PrivateMethods 
    class << self 
      def player_choice
        loop do 
          print ColorizedString["Choose: Rock (r), Paper (p), or Scissors (s): "].colorize(:green)
          choice = gets.chomp.downcase 
          if Constants::NTRY_TO_SYM.key?(choice)
            return Constants::NTRY_TO_SYM[choice] 
          elsif choice != Constants::VALID_ENTRIES
            puts ColorizedString["That entry is invalid. Please re-enter."].colorize(:red) 
          else
            return nil
          end
        end 
      end 

      def player_outcome(plays)
        # plays = [player_choice, computer_choice]
        return :WIN  if Constants::WINNERS.include?(plays) 
        return :LOSE if Constants::LOSERS.include?(plays)
        return :TIE  if !:WIN | !:LOSE 
      end

      def final_outcome(pl,co)
        return :WIN  if pl > co 
        return :LOSE if pl < co
        # there will never be a tie for the final outcome due to the code in the play() method
      end 
    end
  end
  
  private_methods :PrivateMethods 

end 

PlayRockPaperScissorsGame.new.play(2) # call the play method and pass in 3 (0, 1, 2) for the winning score