forked from gophercises/quiz
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Implemented the quiz. Most of the quiz is completed. Randomization was not directly implemented. Instead the map data structure was used with the range operator, which results in random access of the map elements (the ordering of Go maps is not defined for integers?). I'll try to implement a better random sequence of questions later. * Implemented random question generation switched by command line flag -random. This completely implements the quiz according to the specification.
- Loading branch information
1 parent
6378856
commit 4c5cce0
Showing
2 changed files
with
154 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
5+5,10 | ||
7+3,10 | ||
8+3,11 | ||
8+6,14 | ||
1+1,2 | ||
1+2,3 | ||
3+1,4 | ||
1+4,5 | ||
2+3,5 | ||
5+1,6 | ||
3+3,6 | ||
2+4,6 | ||
5+2,7 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
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) | ||
} |