In this article, I am going to go through how we might create a terminal based quiz application where we ingest a CSV with quiz questions and answers, accept an answer from a user (and confirm if it’s correct) while doing a countdown to make sure they don”t exceed a specified time limit.
In the main function, I have made a call to the extract() and the quizme() functions. Let’s first look at extract.
So, what are we doing here. Well, we’re creating a function called extract, which has a return type of slice of strings. So, the idea is, each row of the CSV will become an entry in the slice.
We then define where the quiz file is and open it using the ioutil package. As you’ll know if you’ve worked with this package before, it comes out as a byte slice, so we convert that to be a string value.
Finally, we split the contents at each new line. The output of the split function is a slice of strings. We then return that.
func extract() []string {
//define filepath
filepath := "quiz.csv"
//open the file
openfile, _ := ioutil.ReadFile(filepath)
//cast byte slice as string
contents := string(openfile)
s := strings.Split(contents, "\n")
return s
}
Now, back to the main function, we pass the returned value (s) to the quiz me function. So, in that function, as you can see below, we ingest s (which is a slice of strings). We then create a couple of variables, which we’ll increment in the loop: the first, is the question number, so we can tell the user how many questions they answered and the other is a counter for how many of those questions they got correct.
We then create a new timer. That timer has a duration set of 3 (which is 3 seconds, for testing purposes) and we multiply that by time.Second to make it an actual second value.
In the loop, we split the string. Why? Because each line of the CSV like like this: 4+5, 9, where 4+5 is the question the user needs to answer and 9 is the correct outcome. So, by splitting at the comma, the first split is the question and the second split is the answer.
Of course, this is a slice of strings, so we need to convert the answer to integer, so we can make a comparison against the user entered answer.
Next, we create a select case statement where we say ‘If the channel has reported the timer is out of time’, then we exit the process, otherwise we take the user input and compare that against the correct answer. In both situations, we increment the question count and the correct count.
func quizme(s []string) {
question_number := 0
correct := 0
timer := time.NewTimer(time.Duration(3) * time.Second)
for _, value := range s {
//extract the question and answer
fmt.Println("loop start")
question := strings.Split(value, ",")[0]
answer := strings.Split(value, ",")[1]
//convert string to int
answer2, _ := strconv.Atoi(answer)
//SELECT CASE STATEMENT
select {
//If the channel has reported timer is out of time then return
case <- timer.C:
fmt.Println("you got this many questions correct")
fmt.Println(correct)
fmt.Println(question_number)
return
default :
//Show the user the question
fmt.Print("Question: " + question)
//create variable i (short for input)
var i int
_, err := fmt.Scanln(&i) //request user input in terminal
//check whether user input is correct or not
if i == answer2 {
correct = correct + 1
question_number = question_number + 1
fmt.Println("CORRECT")
} else {
fmt.Println("INCORRECT", err)
question_number = question_number + 1
}
}
}
fmt.Println("you got this many questions correct")
fmt.Println(correct)
fmt.Println(question_number)
}
Full GO Code:
package main import "fmt" import "io/ioutil" import "strings" import "strconv" import "time" //https://tour.golang.org/concurrency/5 func main () { s := extract() quizme(s) } func extract() []string { //define filepath filepath := "quiz.csv" //open the file openfile, _ := ioutil.ReadFile(filepath) //cast byte slice as string contents := string(openfile) s := strings.Split(contents, "\n") return s } func quizme(s []string) { question_number := 0 correct := 0 timer := time.NewTimer(time.Duration(3) * time.Second) for _, value := range s { //extract the question and answer fmt.Println("loop start") question := strings.Split(value, ",")[0] answer := strings.Split(value, ",")[1] //convert string to int answer2, _ := strconv.Atoi(answer) //SELECT CASE STATEMENT select { //If the channel has reported timer is out of time then return case <- timer.C: fmt.Println("you got this many questions correct") fmt.Println(correct) fmt.Println(question_number) return default : //Show the user the question fmt.Print("Question: " + question) //create variable i (short for input) var i int _, err := fmt.Scanln(&i) //request user input in terminal //check whether user input is correct or not if i == answer2 { correct = correct + 1 question_number = question_number + 1 fmt.Println("CORRECT") } else { fmt.Println("INCORRECT", err) question_number = question_number + 1 } } } fmt.Println("you got this many questions correct") fmt.Println(correct) fmt.Println(question_number) }
The Python code is, as you would expect, a lot less verbose. We simply define the starting time (which is now), we say that we have a limit of 10 and then with each loop say ‘has the limit been exceeded?’.
If it hasn’t, we read the columns in, ask the user for an answer and verify that the answer is correct.
import csv import time starting_time = time.time() time_limit = 10 with open('quiz.csv') as csv_file: csv_reader = csv.reader(csv_file, delimiter=',') q_count = 0 score = 0 for row in csv_reader: if time.time() - starting_time < time_limit: question = row[0] answer = row[1] print('Question' + str(q_count) + ": " + question) user_answer = input('What is your answer?') if user_answer == answer: score = score + 1 q_count = q_count + 1 print('your final score was: ' + str(score)) print('you answered ' + str(q_count) + " questions")