Files
gophercises/1. Quiz Game Sol2/students/latentgenius/quiz.go

142 lines
3.1 KiB
Go

package main
import (
"bufio"
"encoding/csv"
"flag"
"fmt"
"io"
"log"
"math/rand"
"os"
"path/filepath"
"strings"
"sync"
"time"
)
var (
flagFilePath string
flagRandom bool
flagTime int
wg sync.WaitGroup
)
func init() {
flag.StringVar(&flagFilePath, "file", "questions.csv", "path/to/csv_file")
flag.BoolVar(&flagRandom, "random", true, "randomize order of questions")
flag.IntVar(&flagTime, "time", 10, "test duration")
flag.Parse()
}
func main() {
// this program will progress as follows
// read a csv filepath and a time limit from flags
// prompt for a key press
// on key press, start the quiz as follows
//
// while time has not elapsed:
// print a random question to the screen
// prompt the user for an answer
// store the answer in a container
// normalize answers so they compare correctly
// output total questions answered correctly and how many questions there
// were.
csvPath, err := filepath.Abs(flagFilePath)
if err != nil {
log.Fatalln("Unable to parse path" + csvPath)
}
file, err := os.Open(csvPath)
if err != nil {
log.Fatalln(err)
}
defer file.Close()
csvReader := csv.NewReader(file)
csvData, err := csvReader.ReadAll()
if err != nil {
log.Fatalln(err)
}
var totalQuestions = len(csvData)
questions := make(map[int]string, totalQuestions)
answers := make(map[int]string, totalQuestions)
responses := make(map[int]string, totalQuestions)
for i, data := range csvData {
questions[i] = data[0]
answers[i] = data[1]
}
respondTo := make(chan string)
// block until user presses enter
fmt.Println("Press [Enter] to start test.")
bufio.NewScanner(os.Stdout).Scan()
if flagRandom {
// seed the random number generator with the current time
rand.Seed(time.Now().UTC().UnixNano())
}
// randPool should contain random indexes into the questions map
randPool := rand.Perm(totalQuestions)
wg.Add(1)
timeUp := time.After(time.Second * time.Duration(flagTime))
go func() {
label:
for i := 0; i < totalQuestions; i++ {
index := randPool[i]
go askQuestion(os.Stdout, os.Stdin, questions[index], respondTo)
select {
case <-timeUp:
fmt.Fprintln(os.Stderr, "\nTime up!")
break label
case ans, ok := <-respondTo:
if ok {
responses[index] = ans
} else {
break label
}
}
}
wg.Done()
}()
wg.Wait()
correct := 0
for i := 0; i < totalQuestions; i++ {
if checkAnswer(answers[i], responses[i]) {
correct++
}
}
summary(correct, totalQuestions)
}
func askQuestion(w io.Writer, r io.Reader, question string, replyTo chan string) {
reader := bufio.NewReader(r)
fmt.Fprintln(w, "Question: "+question)
fmt.Fprint(w, "Answer: ")
answer, err := reader.ReadString('\n')
if err != nil {
close(replyTo)
if err == io.EOF {
return
}
log.Fatalln(err)
}
replyTo <- answer
}
func checkAnswer(ans string, expected string) bool {
if strings.EqualFold(strings.TrimSpace(ans), strings.TrimSpace(expected)) {
return true
}
return false
}
func summary(correct, totalQuestions int) {
fmt.Fprintf(os.Stdout, "You answered %d questions correctly (%d / %d)\n", correct,
correct, totalQuestions)
}