Ruby Code: Rendering Alphabetic and Numeric Games

Published Dec 01, 2017Last updated Apr 21, 2018
Ruby Code: Rendering Alphabetic and Numeric Games

Introduction
This article describes a computer program designed using Ruby and object-oriented principles. All design phases, as well as relevant coding, are examined. Each user screen is presented and followed by the Ruby implementation behind the user interface.

In addition to screen samples, the Ruby code used to interact with the screen options is available, along with the code for processing user input.

User Interface
The first page is usually the starting point from which the user selects the interaction options. An arbitrary scenario would be a guessing game where a random value is generated by the computer and the user enters a value within a set of numbers until the random number is uncovered. Another scenario would be where a random letter is selected by the computer and the user enters a letter until the computer and user choices coincide. (See Figure 1 below).

The intermediate screen for this example follows the main screen by allowing the user to enter the number or letter. This screen handles the entry and display of information (See Figure 2 below). When the user enters an incorrect number, the program eliminates that option with an asterisk. When the user guesses correctly, the program indicates the encrypted value with a question mark.

Finally, the last page does not appear on the screen. Instead, it tracks the total number of game sequences completed by the user. That information is then stored in a computer file, which resembles a computer screen layout, except that it is recorded inside a log file (See Figure 4, 5 below).

Numerical Analysis
The user begins the process by selecting the type of game to play. By pressing the “1” key followed by the “Enter” key, the program will display the option selected on a separate line (See Figure 1 below).

The next screen displays a line containing all digits between 1 and 9 separated by the “|” character. This screen requires the user to enter a numeral until a number matching the computer generated number is entered (See Figure 2 below). The user continues entering digits until that entry matches the computer number. Once the random number is revealed through the cycle of guessing, it is then marked in the appropriate position on the line.

At that point, the user has the option to play another game by entering “Y” or quit the entire program by choosing “N” followed by the "Enter" key. By selecting the option to quit the program, a subroutine is invoked to store the game activity into a log file (See Figure 4, 5 below).

Data Validation
The program incorporates data validation in order to ensure the proper choices are entered. At the start of session, the program checks to ensure that the only two choices, numeral or textual option, are chosen. The program notifies the user if a choice outside the range is entered.

Once the proper option has been selected, another cycle of verification is conducted. In the numeric version, the program ensures that only a number between 1 and 9, inclusive, are provided by the user. In the alphabetic version, the program accepts only letters of the alphabet, that is, A through Z.

Usage Report
For recordkeeping purposes, the program stores all user interaction in a log file (See Figure 4, 5 below). In essence, the program maintains the input internally and eventually writes that information to a log file for retrieval. The log file records interaction from a program sequence and finalizes it with a current date and time, as retrieved from the computer time clock.

As discussed in the section on user data validation, the program similarly performs a verification step after attempting to open, and thereby create, the log file. If the program fails to successfully create the computer file, it merely returns control back to the main routine — without compiling the usage report and without interrupting normal program execution.

play.jpg

In Closing
The game algorithm discussed in this segment seeks to apply the object-oriented programming discipline in the design of a fully operational game system. The object-oriented model offers a more entity-based conceptualization of computer automation underlying the game features.

The benefit is a highly organized, efficient, and pragmatic software scheme. The end result is a more rigorous, more organic approach to program development and, ultimately, software engineering concepts that meet or exceed the broad demands of modern computing, particularly in the gaming industry.

Appendix A. Ruby Code for Entire Game System (295 Lines)

#!/usr/bin/ruby

class DisplayC

  def firstDisplay
    puts "\e[H\e[2J"
    puts "[Main Page]"
    puts ""
    puts "\t1) Numeral Game"
    puts "\t2) Textual Game"
    puts "\t0) Exit"
    puts ""
    print "Please choose an option: "
  end

  def taskM(trayM)
    puts "\e[H\e[2J"
    puts "[Activity Page]"
    puts ""
    last = trayM.length - 1
    for indx in 1..last
      print "|#{trayM[indx]}"
    end
    puts "|\t[User = * Goal = ?]"
    puts ""
    if last == 9
      print "Please enter a number (1-9): "
    elsif last == 26
      print "Please enter a letter (A-Z): "
    end
  end

  def closeM(optionMext)
    if optionMext != "0"
      until optionMext.upcase == "Y" || optionMext.upcase == "N"
        print "\n\nDo you want to continue? (Y/N) "
        optionMext = gets.chomp
        if optionMext.upcase != "Y" && optionMext.upcase != "N"
          print "<Invalid entry.  Try again.>"
          sleep 1
        end
      end
    end
    if optionMext.upcase == "N" || optionMext == "0"
      puts "\e[H\e[2J"
      puts "\nHave a good day."
    sleep 1
    end
    return optionMext
  end

  def reportM(targetM)
    if targetM != ""
      print "\n\nThe secret key was #{targetM.upcase}."
      sleep 2
    end
  end
end

class ScoreC
  def results(eventM)
    timeM = Time.new

    if timeM.zone == "UTC"
      timeM = Time.new(timeM.year,timeM.month,timeM.day,timeM.hour - 4,timeM.sec,"")
    end
    
    timeFormat = timeM.strftime("%p")
    timeFormat = timeFormat.downcase
    timeRecord = timeM.strftime("On %A of %B %d, %Y at %I:%M:%S#{timeFormat}")

    fileStatus = File.open('Outcome.rpt', 'w')
    if fileStatus
      fileStatus.puts ""
      fileStatus.puts "[Results Page]"
      for indx in 1..2
        if indx == 1
          eventM.setNumeral
        elsif indx == 2
          eventM.setTextual
        end
        if eventM.solution(indx) == true
          lenText = eventM.collectWin("").length
          if eventM.collectWin("")[lenText - 1] == ","
            eventM.collectWin("").chop!
          end
          ave = eventM.getArry.pop
          ave = (eventM.getArry[2] + eventM.getArry[3]) / eventM.getArry[1]
          eventM.getArry.push(ave)
          fileStatus.puts ""
          if indx == 1
            fileStatus.puts "\tNumeral Game"
          elsif indx == 2
            fileStatus.puts "\tTextual Game"
          end
          fileStatus.puts ""
          fileStatus.puts "\t\tThe total number of completed games was #{eventM.getArry[1]}."
          fileStatus.puts "\t\tThe lowest number of guesses was #{eventM.getArry[2]}."
          fileStatus.puts "\t\tThe highest number of guesses was #{eventM.getArry[3]}."
          fileStatus.puts "\t\tThe average number of guesses was #{eventM.getArry[4]}."
          fileStatus.puts "\t\tThe solution set was (#{eventM.collectWin("")})."
        end
      end
      fileStatus.puts ""
      fileStatus.puts "Time Log: #{timeRecord}"
      fileStatus.close
    end
  end
end

class TrackC
  def initialize
    @side = 0
    @recordNum = ""
    @recordTex = ""
    @vitalsNum = Array.new(4,0)
    @vitalsTex = Array.new(4,0)
  end
  def setNumeral
    @side = 1
    @trayTrack = {0=>"0",1=>"1",2=>"2",3=>"3",4=>"4",5=>"5",6=>"6",7=>"7",8=>"8",9=>"9"}
  end
  def setTextual
    @side = 2
    @trayTrack = {0=>"0",1=>"A",2=>"B",3=>"C",4=>"D",5=>"E",6=>"F",7=>"G",8=>"H",9=>"I",10=>"J",11=>"K",12=>"L",13=>"M",14=>"N",15=>"O",16=>"P",17=>"Q",18=>"R",19=>"S",20=>"T",21=>"U",22=>"V",23=>"W",24=>"X",25=>"Y",26=>"Z"}
  end
  def trayLen
    return @trayTrack.length
  end
  def trayValid(op)
    return @trayTrack.has_value?(op)
  end
  def getTray
    return @trayTrack
  end
  def getArry
    if @side == 1
      return @vitalsNum
    elsif @side == 2
      return @vitalsTex
    end
  end
  def setVitals(indx,val)
    if @side == 1
      @vitalsNum[indx] = val
    elsif @side == 2
      @vitalsTex[indx] = val
    end
  end
  def addVitals(indx,val)
    if @side == 1
      sum = @vitalsNum.pop
      sum = sum + val
      @vitalsNum.push(sum)
    elsif @side == 2
      sum = @vitalsNum.pop
      sum = sum + val
      @vitalsNum.push(sum)
    end
  end
  def collectWin(val)
    if @side == 1
      if val != ""
        @recordNum.concat(val)
      end
      return @recordNum
    elsif @side == 2
      if val != ""
        @recordTex.concat(val)
      end
      return @recordTex
    end
  end
  def solution(type)
    if type == 1
      return @recordNum.length > 0
    elsif type == 2
      return @recordTex.length > 0
    end
  end
end

class FieldC
  def mainField(sessionM,eventM)
    retain = true
    attempts = 0

    if eventM.trayLen == 10
      target = rand(8)
    elsif eventM.trayLen == 27
      target = rand(25)
    end
    target += 1

    while retain == true
      sessionM.taskM(eventM.getTray)
      optionM = gets.chomp
      until eventM.trayValid(optionM.upcase)
        print "<Invalid entry.  Try again.>"
        sleep 1
        sessionM.taskM(eventM.getTray)
        optionM = gets.chomp
      end
      attempts += 1
      opt = eventM.getTray.key(optionM.upcase)
      if opt == target
        let = eventM.getTray[opt]
        eventM.collectWin(let)
        eventM.getTray[opt] = "?"
        if eventM.getArry[1] == 1
          eventM.setVitals(2,attempts)
          eventM.setVitals(3,attempts)
          eventM.setVitals(4,attempts)
        else
          if attempts < eventM.getArry[2]
            eventM.setVitals(2,attempts)
          elsif attempts > eventM.getArry[3]
            eventM.setVitals(3,attempts)
          end
          eventM.addVitals(4,attempts)
        end
        sessionM.taskM(eventM.getTray)
        sessionM.reportM(optionM)
        answerField = sessionM.closeM(opt.to_s)
        if answerField.upcase == "N"
          retain = false
          rewindM = false
        else
          retain = false
          rewindM = true
          eventM.collectWin(",")
        end
      elsif
        eventM.getTray[opt] = "*"
      end
    end
    return rewindM
  end
end

class BenchC
  def initialize
    @rewind = true

    @session = DisplayC.new
    @action = FieldC.new
    @archive = ScoreC.new
    @event = TrackC.new

    mainBench
  end

  def mainBench
    while @rewind == true
      option = ""
      until option == "1" || option == "2" || option == "0"
        @session.firstDisplay
        option = gets.chomp
        if option != "1" && option != "2" && option != "0"
          puts "<Invalid entry.  Try again.>"
          sleep 1
        end
      end
      puts "\nYou entered #{option}."
      sleep 1
      if option == "1"
        @event.setNumeral
        @event.getArry[1] += 1
        @rewind = @action.mainField(@session,@event)
      elsif option == "2"
        @event.setTextual
        @event.getArry[1] += 1
        @rewind = @action.mainField(@session,@event)
      else
        @rewind = @session.closeM(option)
      end
    end
    @archive.results(@event)
  end
end

#
# Game Start

arena = BenchC.new
Discover and read more posts from Luis O. Freire
get started