Class: Typeking::GameController

Inherits:
Object
  • Object
show all
Defined in:
lib/typeking/game_controller.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeGameController

Returns a new instance of GameController.



12
13
14
15
16
17
18
# 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

Instance Attribute Details

#currentUidObject

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



10
11
12
# File 'lib/typeking/game_controller.rb', line 10

def currentUid
  @currentUid
end

#currentUserDataObject

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



10
11
12
# File 'lib/typeking/game_controller.rb', line 10

def currentUserData
  @currentUserData
end

#userDataObject

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



10
11
12
# File 'lib/typeking/game_controller.rb', line 10

def userData
  @userData
end

#userHashesObject

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



10
11
12
# File 'lib/typeking/game_controller.rb', line 10

def userHashes
  @userHashes
end

Instance Method Details

#attempt_login(username) ⇒ Object



92
93
94
95
96
97
98
99
100
# File 'lib/typeking/game_controller.rb', line 92

def (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) ⇒ Object



69
70
71
72
73
74
75
76
77
78
79
# 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) ⇒ Object



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# 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) ⇒ Object



187
188
189
190
191
192
193
194
195
# 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) ⇒ Object



219
220
221
222
223
224
225
226
227
228
229
230
231
# 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) ⇒ Object



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# 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) ⇒ Object



103
104
105
106
107
108
109
110
# 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

#helpObject



55
56
57
58
59
60
# 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_screenObject



127
128
129
130
131
132
133
134
135
136
137
138
139
# 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) ⇒ Object



233
234
235
236
237
# 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_applicationObject



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 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_leaderboardsObject



112
113
114
115
116
117
118
119
120
# 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) #calls method based on number of users
    start_screen #return to starting screen
end

#show_statsObject



122
123
124
125
# 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_gameObject



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# 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_screenObject



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/typeking/game_controller.rb', line 37

def start_screen
    startChoices = {: 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
        
    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_loginObject



81
82
83
84
85
86
87
88
89
90
# File 'lib/typeking/game_controller.rb', line 81

def 
    loginPrompt = TTY::Prompt.new #creates new prompt
    username = loginPrompt.ask("What is your name?", default: "Anonymous") #receive prompt input
    puts((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_registerObject



62
63
64
65
66
67
# 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