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.
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