Files

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))
}
}