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
12 changes: 12 additions & 0 deletions students/viveksyngh/problems.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
5+5,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
94 changes: 94 additions & 0 deletions students/viveksyngh/quiz.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package main

import (
"encoding/csv"
"os"
"fmt"
"strings"
"log"
"flag"
"time"
"math/rand"
)

type Question struct {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a good idea to document an exported struct. You can use golint tool to tell you about these actions:

golint students/viveksyngh

question string
answer string
}

func getQuestions(filePath string) ([]Question) {
file, err := os.Open(filePath)
if(err != nil){
log.Fatal("Failed to open file.")
}
reader := csv.NewReader(file)
questionList, err := reader.ReadAll()
if(err != nil) {
log.Fatal("Failed to parse CSV file.")
}
questions := make([]Question, 0)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now you have successfully loaded your questionList, you have the required length. You can do this instead:

questions := make([]Question, len(questionList))
for i, question := range questionList {
	if len(question) != 2 {
		continue
	}
	questions[i] = Question{
		strings.TrimSpace(question[0]),
		strings.TrimSpace(question[1]),
	}
}

This way you don't allocate new array every time append is called. Also always check the input coming from outside world.


for _, question := range questionList {
questions = append(questions, Question{strings.TrimSpace(question[0]),
strings.TrimSpace(question[1])})
}
return questions
}

func Quiz(questions []Question, timer *time.Timer) (score int){
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need for named return, it's another allocation. If you simply name your function score it informs the reader what they expect.


for i, question := range questions {
fmt.Printf("Problem #%d %s : ", i + 1, question.question)
answerChannel := make(chan string)
go func() {
var userAnswer string
fmt.Scanln(&userAnswer)
answerChannel <- userAnswer
}()

select {
case <-timer.C:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very clever use of timers.

fmt.Println("\nTimeout")
return score
case userAnswer := <- answerChannel:
if(strings.TrimSpace(userAnswer) == question.answer) {
score += 1
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can simply do: score++

}
}
}
return score
}

func randomize(questions []Question) []Question{
n := len(questions)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can do this too:

rand.Seed(time.Now().UnixNano())
for i := range questions {
	j := rand.Intn(i + 1)
	questions[i], questions[j] = questions[j], questions[i]
}

or

rand.Seed(time.Now().UnixNano())
rand.Shuffle(len(questions), func(i, j int) {
	questions[i], questions[j] = questions[j], questions[i]
})

It's a good idea to seed your random generator otherwise the shuffle returns the same results every time you run the program, which defeats its purpose.

for i := n-1; i>0; i-- {
j := rand.Intn(i)
temp := questions[i]
questions[i] = questions[j]
questions[j] = temp
}
return questions
}

var csvPath string
var timeout int
var shuffle bool

func init() {
flag.StringVar(&csvPath, "csv", "problems.csv", "a CSV file in format of 'question,answer'")
flag.IntVar(&timeout, "limit", 30, "The time limit of the quiz in seconds")
flag.BoolVar(&shuffle, "shuffle", false, "Shuffle the questions (default 'false')")
}

func main() {
flag.Parse()
fmt.Print("Hit Enter to start the timer:")
questions := getQuestions(csvPath)
if(shuffle) {
questions = randomize(questions)
}
fmt.Scanln()
timer := time.NewTimer(time.Second * time.Duration(timeout))
score := Quiz(questions, timer)
fmt.Printf("Your scored %d out of %d\n", score, len(questions))
}