Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions students/kalexmills/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
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))
}
}
13 changes: 13 additions & 0 deletions students/kalexmills/problems.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
5+5,10
7+3,10
1+1,2
8+3,11
1+2,3
8+6,14
3+1,4
1+4,5
5+1,6
2+3,5
3+3,6
2+4,6
5+2,7