Skip to content

Commit

Permalink
Added solution using timer.AfterFunc and a duration flag. (gophercise…
Browse files Browse the repository at this point in the history
  • Loading branch information
kalexmills authored and joncalhoun committed Jul 22, 2019
1 parent b09f555 commit d06f1d1
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 0 deletions.
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

0 comments on commit d06f1d1

Please sign in to comment.