class Typeking::GameController

Attributes

currentUid[RW]

attr_accessor used for testing purposes. App will still run if all variables changed to attr_reader

currentUserData[RW]

attr_accessor used for testing purposes. App will still run if all variables changed to attr_reader

userData[RW]

attr_accessor used for testing purposes. App will still run if all variables changed to attr_reader

userHashes[RW]

attr_accessor used for testing purposes. App will still run if all variables changed to attr_reader

Public Class Methods

new() click to toggle source
# File lib/typeking/game_controller.rb, line 12
def initialize()
@userData = [] #database of user hashes
@userHashes = {} #stores indexes for all users
@currentUserData = {} #stores current stats
@currentUid = 0 #current user id
setup_application
end

Public Instance Methods

attempt_login(username) click to toggle source
# File lib/typeking/game_controller.rb, line 92
def attempt_login(username)
    begin
        @currentUid = @userHashes[username] #update current user id to the username from the userhashes directory
        @currentUserData = @userData[currentUid] #user data hash becomes hash from database
        return "Hello #{@currentUserData[:name]}!" #user_login will puts this to terminal
    rescue
        return "You have not registered yet, please register first." #user_login will puts this to terminal
    end 
end
attempt_registration(username) click to toggle source
# File lib/typeking/game_controller.rb, line 69
def attempt_registration(username)
    if @userHashes[username] #check if user exists in @userHashes
        return "You already have an account, please login" #user_register will puts this to the terminal
    else
        user = User.new(username) #creates new user
        newHash = user.user_details #creates a new hash of user values
        @userhashes = @userHashes.merge!(newHash) #adds user to hash
        @userData << {:name => username, :high_score => 0, :accuracy => 0, :worst_character => "None!"}
        return "Thank you for registering, please login and have fun!" #user_register will puts this to the terminal
    end
end
begin_typing(wordCount) click to toggle source
# File lib/typeking/game_controller.rb, line 158
def begin_typing(wordCount)
    wordsCorrect = [] #creates array of words that the user types correctly
    wordsIncorrect = [] #creates array of words that the user types incorrectly
    startingTime = Process.clock_gettime(Process::CLOCK_MONOTONIC) #gets current time and saves it in variable
    wordsLeft = wordCount.to_i #words left to type are the words passed in argument from previous def
    while wordsLeft > 0 #loops while there are words to type
        newWord = RandomWord.nouns.next #starts with a random noun
        while newWord.size > 8 #makes sure no words are over 8 characters long, generates a new word if the new word is longer than 8 characters
            random = rand(2) #generators a random number from 0-1
            if random == 1
                newWord = RandomWord.adjs.next #if random number is '1', next word is an adjective
            else
                newWord = RandomWord.nouns.next #if random number is not '1', next word is a noun
            end
        end
        puts newWord #prints word for user to type
        input = gets.chomp
        if newWord == input #checks if user has typed the word correctly
            wordsCorrect << newWord.colorize(:green) #changes colour of word to green and pushes it to correct words array
        else
            wordsIncorrect << newWord.colorize(:red) #changes colour of word to red and pushes it to incorrect words array
        end
        wordsLeft -= 1 #user has typed another word
    end
    finishTime = Process.clock_gettime(Process::CLOCK_MONOTONIC) #gets current time and stores it in variable
    elapsedTime = (finishTime - startingTime) #gets time elapsed by subtracting starting time from the finished time
    calculate_results(elapsedTime, wordCount, wordsCorrect, wordsIncorrect) #calculate results using elapsed time, how many words the user typed and words that the user got correct/incorrect
end
calculate_results(elapsedTime, totalWordCount, wordsCorrect, wordsIncorrect) click to toggle source
# File lib/typeking/game_controller.rb, line 187
def calculate_results(elapsedTime, totalWordCount, wordsCorrect, wordsIncorrect)
    puts "Your final time to type #{totalWordCount} words was #{elapsedTime.round} seconds" #tells the user how long it took them to type the designated words
    puts "Words typed correctly: #{wordsCorrect.join(" ")}" #shows all correctly typed words seperated by a space
    puts "Words typed incorrectly: #{wordsIncorrect.join(" ")}" #shows all incorrectly typed words seperated by a space
    puts "Calculating..."
    puts calculate_speed(elapsedTime, totalWordCount, wordsCorrect) #returns wpm and accuracy
    puts count_worst_letters(wordsIncorrect) #returns array of top 3 incorrect letters typed
    home_screen #return to home screen
end
calculate_speed(elapsedTime, totalWordCount, wordsCorrect) click to toggle source
# File lib/typeking/game_controller.rb, line 219
def calculate_speed(elapsedTime, totalWordCount, wordsCorrect)
    charactersTyped = 0 #checks how many characters were typed
    wordsCorrect.each do |word|
        word = word.green.uncolorize #uncolorizes words so that word size will revert to normal
        charactersTyped += word.size #adds characters of words to character count
    end
    timeMultiplier = 60 / elapsedTime  #creates a multiplier to set typing rate to words per minute
    wordsNormalized = charactersTyped * timeMultiplier #converts characters typed to characters per minute
    wpm = ((wordsNormalized / 3)).round #wpm = characters typed per minute divided by average characters in words. Average is lower to be more accurate because user has to use the enter button and is not typing sentences
    accuracy = (wordsCorrect.size.to_f / totalWordCount.to_f) * 100 #accuracy = correct words / total words
    save_data(wpm, accuracy.to_i) unless wpm < @currentUserData[:high_score]#saves data if user has reached high score
    return "You type #{wpm.to_s.colorize(:green)} word(s) per minute with #{accuracy.to_i}% accuracy!" #returns wpm and accuracy, outputted by calculate_results
end
count_worst_letters(incorrectWords) click to toggle source
# File lib/typeking/game_controller.rb, line 197
def count_worst_letters(incorrectWords)
    letter_count = {} #create new array of letters incorrectly typed
    incorrectWords.each do |word| #loop through each word
        word = word.red.uncolorize #uncolorize to remove colorize characters
        letterArr = word.split("") #split word into array of letters
        letterArr.each do |letter| #loop through each letter
            if !letter_count[letter] #if letter value is nil, add it to hash
                letter_count[letter] = 1
            else
                letter_count[letter] += 1 #if letter exists, increment
            end
        end
    end
    if letter_count.empty? #check if hash is empty, meaning that they got everything correct
        return "Great job, you got all the words correct!" #calculate_results will puts this to application
    else
        final_letters = letter_count.sort_by {|char, c| c}.reverse #sort hash by descending order
        @currentUserData[:worst_character] = letter_count.max_by{|k, v| v} #change worst character in database to be the key with the highest value
        return "Here are some characters that you may want to practice: #{final_letters[0]}, #{final_letters[1]}, #{final_letters[2]}" #calculate_results will puts this to terminal
    end
end
display_leaders(leaderCount,leaderboardArr) click to toggle source
# File lib/typeking/game_controller.rb, line 103
def display_leaders(leaderCount,leaderboardArr)
    leaders = 0 #current number of users displayed on leaderboard
    while leaders < leaderCount #loop while displayed leaders is less than actual users
        leaderHash = leaderboardArr[leaders] #creates new hash using leaders as the index as the highest score would start from index 0
        puts "#{leaders+1}. #{leaderHash[:name]} - WPM: #{leaderHash[:high_score]}, Accuracy: #{leaderHash[:accuracy]}%, Worst Character: #{leaderHash[:worst_character]}" #uses hash to output leaderboards
        leaders += 1 #increment leaders
    end
end
help() click to toggle source
# File lib/typeking/game_controller.rb, line 55
def help
    puts "1. Register"
    puts "2. Login"
    puts "3. Play"
    puts "4. Check your stats"
end
home_screen() click to toggle source
# File lib/typeking/game_controller.rb, line 127
def home_screen
    entryPrompt = TTY::Prompt.new #creates new prompt
    entryChoices = {Play: 1, Stats: 2, "Log out": 3} #options to choose from
    choice = entryPrompt.select("Hello #{@currentUserData[:name]}, what would you like to do?", entryChoices) #receives choice input
    if choice == 1 #play game selected
        start_game #starts game
    elsif choice == 2 #stats selected
        puts show_stats #show user stats
        home_screen #restart method
    else
        start_screen #logs out
    end
end
save_data(wpm, accuracy) click to toggle source
# File lib/typeking/game_controller.rb, line 233
def save_data(wpm, accuracy)
    @currentUserData[:high_score] = wpm #updates highest wpm
    @currentUserData[:accuracy] = accuracy #updates accuracy
    @userData[@currentUid] = @currentUserData #updates database with current user data
end
setup_application() click to toggle source
# File lib/typeking/game_controller.rb, line 20
def setup_application
    system("clear") #clears terminal
    artOpts = Artii::Base.new :font => 'slant' #create artii options using Artii Ruby gem
    puts artOpts.asciify('Type King').colorize(:blue) #apply artii style to string to output to the application
    if ARGV[0] == "-h" #command line argument that calls help function
        gameController.help
    else
        ARGV.each do |i|
            attempt_registration(i.chomp) #command line argument that registers each user
        end 
    end
    while ARGV.size > 0 #loop while there are elements in ARGV
        ARGV.shift #delete ARGV so that application can get input later using 'gets'
    end
    start_screen
end
show_leaderboards() click to toggle source
# File lib/typeking/game_controller.rb, line 112
def show_leaderboards
    leaderboardArr = @userData.dup #creates a new duplicate array of all user data
    puts "No entries yet!" unless leaderboardArr.count > 0 #output when no users in data
    leaderboardArr.sort_by!{|w| w[:high_score]} #sort by ascending order
    leaderboardArr = leaderboardArr.reverse #reverse order to descending
    leaderCount = leaderboardArr.count #checks how many users are in the array
    display_leaders(leaderCount, leaderboardArr) #calls method based on number of users
    start_screen #return to starting screen
end
show_stats() click to toggle source
# File lib/typeking/game_controller.rb, line 122
def show_stats
    @currentUserData = @userData[@currentUid] #double check that the stats shown are the highest, this line is used for testing
    return "Name: #{@currentUserData[:name]}, WPM: #{@currentUserData[:high_score]}, Accuracy: #{@currentUserData[:accuracy]}, Least accurate letter: #{@currentUserData[:worst_character]}" #home_screen will puts this to application
end
start_game() click to toggle source
# File lib/typeking/game_controller.rb, line 141
def start_game
    wordsPrompt = TTY::Prompt.new #creates new prompt
    words = wordsPrompt.ask("How many words do you want to type? (enter a number between 5 and 500)", default: 60) #asks user for number of words they would like to type. If no input, defaults to 30
    if words.to_i < 5 || words.to_i > 499 #checks if entry is in range
        puts "Invalid entry, please enter a number larger than 5 and smaller than 500"
        start_game #restarts method
    else
        countdown = 3 #starts countdown to game
        while countdown > 0  #loops while countown is higher than 0
            puts countdown
            countdown -= 1
            sleep 1 #waits 1 second
        end
        begin_typing(words) #starts games with chosen number of words
    end
end
start_screen() click to toggle source
# File lib/typeking/game_controller.rb, line 37
def start_screen
    startChoices = {Login: 1, "New User": 2, Leaderboards: 3, Help: 4, Exit: 5} #Options for starting screen
    startPrompt = TTY::Prompt.new #creates new prompt
    choice = startPrompt.select("Welcome to Type King", startChoices) #receive input for prompt
    if choice == 1 #user picked Login
        user_login
    elsif choice == 2 #user picked New User
        user_register
    elsif choice == 3 #user picked Leaderboards
        show_leaderboards
    elsif choice == 4
        help
        start_screen
    else #user picked exit
        puts "Thank you for playing!"   
    end
end
user_login() click to toggle source
# File lib/typeking/game_controller.rb, line 81
def user_login
    loginPrompt = TTY::Prompt.new #creates new prompt
    username = loginPrompt.ask("What is your name?", default: "Anonymous") #receive prompt input
    puts(attempt_login(username)) #attempt to login
    if @userHashes[username]
        home_screen #if user exists go to home screen
    else
        start_screen #otherwise go back to start screen
    end
end
user_register() click to toggle source
# File lib/typeking/game_controller.rb, line 62
def user_register
    registerPrompt = TTY::Prompt.new #create new prompt
    username = registerPrompt.ask("What is your name?", default: "Anonymous") #ask for user name, default is 'anonymous'
    puts attempt_registration(username) #calls attempt_registration method with username to check if user exists
    start_screen #return to start screen so user can login
end