mirror of
https://github.com/AmanTahiliani/gophercises.git
synced 2026-08-08 04:06:16 -04:00
113 lines
2.6 KiB
Go
113 lines
2.6 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/csv"
|
|
"flag"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"time"
|
|
)
|
|
|
|
var (
|
|
filename string
|
|
timelimit time.Duration
|
|
)
|
|
|
|
func init() {
|
|
flag.StringVar(&filename, "in", "problems.csv", "filename where problems are read from")
|
|
flag.DurationVar(&timelimit, "time", 30*time.Second, "time to allot for quiz")
|
|
}
|
|
|
|
|
|
// problem represents a prompt and the correct response
|
|
type problem struct {
|
|
prompt string
|
|
response string
|
|
}
|
|
|
|
// printResults prints a prompt to the user explaining the results achieved.
|
|
func printResults(nWrong, nTotal int) {
|
|
nRight := nTotal - nWrong
|
|
fmt.Printf("You answered %d out of %d questions correctly.\n", nRight, nTotal)
|
|
}
|
|
|
|
// readFile reads from the file, sending back a slice of problems.
|
|
func readFile() []problem {
|
|
file, err := os.Open(filename)
|
|
if err != nil {
|
|
log.Fatal("Error opening file: %s", err.Error())
|
|
}
|
|
|
|
r := csv.NewReader(file)
|
|
records, err := r.ReadAll()
|
|
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// Construct a slice of problems for later consumption
|
|
result := make([]problem, len(records))
|
|
var i int
|
|
for idx, rec := range records {
|
|
if len(rec) < 2 {
|
|
fmt.Println("Ignoring faulty or missing record from line %d", idx)
|
|
continue
|
|
}
|
|
result[i] = problem{rec[0], rec[1]}
|
|
i++
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func main() {
|
|
flag.Parse()
|
|
|
|
problems := readFile()
|
|
|
|
var nWrong, nDone int
|
|
// timeout is used to signal that the timer has elapsed
|
|
timeout := make(chan bool, 2)
|
|
timer := time.AfterFunc(timelimit, func() {
|
|
timeout <- true // signal that a timeout occurred
|
|
})
|
|
|
|
startTime := time.Now()
|
|
// Ask each question and get a response
|
|
for _, prob := range problems {
|
|
fmt.Printf("%s\n > ", prob.prompt)
|
|
|
|
// A separate goroutine must be used to accept input from each question. At first I tried to just read from
|
|
// Stdin on the main thread. That was a mistake. If you want to be able to interrupt the prompt when the time
|
|
// expires, you need to have your main thread waiting on a response from either the timer or the answer thread.
|
|
//
|
|
// Lesson is to treat user input as an asynchronous message!
|
|
answerCh := make(chan string)
|
|
go func() {
|
|
var answer string
|
|
fmt.Scanf("%s\n", &answer)
|
|
answerCh <- answer
|
|
}()
|
|
|
|
select {
|
|
case <-timeout:
|
|
fmt.Println("\nTimes up!")
|
|
nWrong += len(problems) - nDone // All remaining problems are marked wrong
|
|
printResults(nWrong, len(problems))
|
|
os.Exit(0)
|
|
case response := <-answerCh:
|
|
nDone++
|
|
if prob.response != response {
|
|
nWrong++
|
|
}
|
|
}
|
|
}
|
|
|
|
if timer.Stop() {
|
|
fmt.Printf("You completed the quiz with %s remaining\n", (timelimit - time.Since(startTime)).String())
|
|
|
|
printResults(nWrong, len(problems))
|
|
}
|
|
}
|