mirror of
https://github.com/AmanTahiliani/gophercises.git
synced 2026-08-07 19:56:17 -04:00
Different solution for Gophercise 1
This commit is contained in:
BIN
1. Quiz Game Sol2/students/hackeryarn/hackeryarn
Executable file
BIN
1. Quiz Game Sol2/students/hackeryarn/hackeryarn
Executable file
Binary file not shown.
120
1. Quiz Game Sol2/students/hackeryarn/main.go
Normal file
120
1. Quiz Game Sol2/students/hackeryarn/main.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
quiz "github.com/gophercises/quiz/students/hackeryarn/myquiz"
|
||||
"github.com/gophercises/quiz/students/hackeryarn/problem"
|
||||
)
|
||||
|
||||
const (
|
||||
// FileFlag is used to set a file for the questions
|
||||
FileFlag = "file"
|
||||
// FileFlagValue is the value used when no FileFlag is provided
|
||||
FileFlagValue = "problems.csv"
|
||||
// FileFlagUsage is the help string for the FileFlag
|
||||
FileFlagUsage = "Questions file"
|
||||
|
||||
// TimerFlag is used for setting a timer for the quiz
|
||||
TimerFlag = "timer"
|
||||
// TimerFlagValue is the value used when no TimerFlag is provided
|
||||
TimerFlagValue = 30
|
||||
// TimerFlagUsage is the help string for the TimerFlag
|
||||
TimerFlagUsage = "Amount of seconds the quiz will allow"
|
||||
)
|
||||
|
||||
// Flagger configures the flags used
|
||||
type Flagger interface {
|
||||
StringVar(p *string, name, value, usage string)
|
||||
IntVar(p *int, name string, value int, usage string)
|
||||
}
|
||||
|
||||
type quizFlagger struct{}
|
||||
|
||||
func (q *quizFlagger) StringVar(p *string, name, value, usage string) {
|
||||
flag.StringVar(p, name, value, usage)
|
||||
}
|
||||
|
||||
func (q *quizFlagger) IntVar(p *int, name string, value int, usage string) {
|
||||
flag.IntVar(p, name, value, usage)
|
||||
}
|
||||
|
||||
// Timer is used to start a timer
|
||||
type Timer interface {
|
||||
NewTimer(d time.Duration) *time.Timer
|
||||
}
|
||||
|
||||
type quizTimer struct{}
|
||||
|
||||
func (q quizTimer) NewTimer(d time.Duration) *time.Timer {
|
||||
return time.NewTimer(d)
|
||||
}
|
||||
|
||||
// ReadCSV parses the CSV file into a Problem struct
|
||||
func ReadCSV(reader io.Reader) quiz.Quiz {
|
||||
csvReader := csv.NewReader(reader)
|
||||
|
||||
problems := []problem.Problem{}
|
||||
for {
|
||||
record, err := csvReader.Read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
log.Fatalln("Error reading CSV:", err)
|
||||
}
|
||||
|
||||
problems = append(problems, problem.New(record))
|
||||
}
|
||||
|
||||
return quiz.New(problems)
|
||||
}
|
||||
|
||||
// TimerSeconds is the amount of time allowed for the quiz
|
||||
var TimerSeconds int
|
||||
var file string
|
||||
|
||||
// ConfigFlags sets all the flags used by the application
|
||||
func ConfigFlags(f Flagger) {
|
||||
f.StringVar(&file, FileFlag, FileFlagValue, FileFlagUsage)
|
||||
f.IntVar(&TimerSeconds, TimerFlag, TimerFlagValue, TimerFlagUsage)
|
||||
}
|
||||
|
||||
// StartTimer begins a timer once the user provides input
|
||||
func StartTimer(w io.Writer, r io.Reader, timer Timer) *time.Timer {
|
||||
fmt.Fprint(w, "Ready to start?")
|
||||
fmt.Fscanln(r)
|
||||
|
||||
return timer.NewTimer(time.Second * time.Duration(TimerSeconds))
|
||||
}
|
||||
|
||||
func init() {
|
||||
flagger := &quizFlagger{}
|
||||
ConfigFlags(flagger)
|
||||
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
func main() {
|
||||
file, err := os.Open(file)
|
||||
if err != nil {
|
||||
log.Fatalln("Could not open file", err)
|
||||
}
|
||||
|
||||
quiz := ReadCSV(file)
|
||||
|
||||
timer := StartTimer(os.Stdout, os.Stdin, quizTimer{})
|
||||
go func() {
|
||||
<-timer.C
|
||||
fmt.Println("")
|
||||
quiz.PrintResults(os.Stdout)
|
||||
os.Exit(0)
|
||||
}()
|
||||
|
||||
quiz.Run(os.Stdout, os.Stdin)
|
||||
}
|
||||
40
1. Quiz Game Sol2/students/hackeryarn/myquiz/myquiz.go
Normal file
40
1. Quiz Game Sol2/students/hackeryarn/myquiz/myquiz.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package quiz
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/gophercises/quiz/students/hackeryarn/problem"
|
||||
)
|
||||
|
||||
// Quiz represents the quiz to be given to the user
|
||||
type Quiz struct {
|
||||
problems []problem.Problem
|
||||
rightAnswers int
|
||||
}
|
||||
|
||||
// Run runs the quiz for all the problems keeping track of correct answers
|
||||
func (q *Quiz) Run(w io.Writer, r io.Reader) {
|
||||
for _, problem := range q.problems {
|
||||
problem.AskQuestion(w)
|
||||
correct := problem.CheckAnswer(r)
|
||||
if correct {
|
||||
q.rightAnswers++
|
||||
}
|
||||
}
|
||||
|
||||
q.PrintResults(w)
|
||||
}
|
||||
|
||||
// PrintResults outputs the results of the quiz
|
||||
func (q Quiz) PrintResults(w io.Writer) {
|
||||
fmt.Fprintf(w, "You got %d questions right!\n", q.rightAnswers)
|
||||
}
|
||||
|
||||
// New creates a new quiz from the supplied slice of problems
|
||||
func New(problems []problem.Problem) Quiz {
|
||||
return Quiz{
|
||||
problems: problems,
|
||||
rightAnswers: 0,
|
||||
}
|
||||
}
|
||||
66
1. Quiz Game Sol2/students/hackeryarn/myquiz/myquiz_test.go
Normal file
66
1. Quiz Game Sol2/students/hackeryarn/myquiz/myquiz_test.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package quiz
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gophercises/quiz/students/hackeryarn/problem"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
problems := sampleProblems()
|
||||
|
||||
want := Quiz{problems: problems, rightAnswers: 0}
|
||||
got := New(problems)
|
||||
|
||||
if !reflect.DeepEqual(want, got) {
|
||||
t.Errorf("expeted to create quiz %v got %v", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRun(t *testing.T) {
|
||||
t.Run("it runs the quiz", func(t *testing.T) {
|
||||
buffer := &bytes.Buffer{}
|
||||
quiz := createQuiz()
|
||||
runQuiz(buffer, &quiz)
|
||||
|
||||
expectedResults := 2
|
||||
results := quiz.rightAnswers
|
||||
|
||||
if expectedResults != results {
|
||||
t.Errorf("expected right answers of %v, got %v",
|
||||
expectedResults, results)
|
||||
}
|
||||
|
||||
expectedOutput := "7+3: 1+1: You got 2 questions right!\n"
|
||||
|
||||
if buffer.String() != expectedOutput {
|
||||
t.Errorf("expected full output %v, got %v",
|
||||
expectedOutput, buffer)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func sampleProblems() []problem.Problem {
|
||||
record1 := []string{"7+3", "10"}
|
||||
record2 := []string{"1+1", "2"}
|
||||
|
||||
return []problem.Problem{
|
||||
problem.New(record1),
|
||||
problem.New(record2),
|
||||
}
|
||||
}
|
||||
|
||||
func createQuiz() Quiz {
|
||||
problems := sampleProblems()
|
||||
return New(problems)
|
||||
}
|
||||
|
||||
func runQuiz(buffer io.Writer, quiz *Quiz) {
|
||||
answers := strings.NewReader("10\n2\n")
|
||||
quiz.Run(buffer, answers)
|
||||
}
|
||||
49
1. Quiz Game Sol2/students/hackeryarn/problem/problem.go
Normal file
49
1. Quiz Game Sol2/students/hackeryarn/problem/problem.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package problem
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Problem represents a single question answer pair
|
||||
type Problem struct {
|
||||
question string
|
||||
answer string
|
||||
}
|
||||
|
||||
// CheckAnswer checks the answer against the provided input
|
||||
func (p Problem) CheckAnswer(r io.Reader) bool {
|
||||
answer := readAnswer(r)
|
||||
|
||||
if answer != p.answer {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func readAnswer(r io.Reader) (answer string) {
|
||||
_, err := fmt.Fscanln(r, &answer)
|
||||
if err != nil {
|
||||
log.Fatalln("Error reading in answer", err)
|
||||
}
|
||||
|
||||
return strings.TrimSpace(answer)
|
||||
}
|
||||
|
||||
// AskQuestion prints out the question
|
||||
func (p Problem) AskQuestion(w io.Writer) {
|
||||
_, err := fmt.Fprintf(w, "%s: ", p.question)
|
||||
if err != nil {
|
||||
log.Fatalln("Could not ask the question", err)
|
||||
}
|
||||
}
|
||||
|
||||
// New creates a Problem from a provided CSV record
|
||||
func New(record []string) Problem {
|
||||
return Problem{
|
||||
question: record[0],
|
||||
answer: record[1],
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package problem
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
record := []string{"question", "answer"}
|
||||
|
||||
want := Problem{"question", "answer"}
|
||||
got := New(record)
|
||||
|
||||
if got != want {
|
||||
t.Errorf("expected to create problem %v got %v", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckAnswer(t *testing.T) {
|
||||
problem := createProblem()
|
||||
|
||||
t.Run("it checks the correct answer", func(t *testing.T) {
|
||||
answer := getAnswer(problem, "10\n")
|
||||
|
||||
checkAnswer(t, answer, true)
|
||||
})
|
||||
|
||||
t.Run("it checks incorrect answer", func(t *testing.T) {
|
||||
answer := getAnswer(problem, "2\n")
|
||||
|
||||
checkAnswer(t, answer, false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAskQuestion(t *testing.T) {
|
||||
problem := createProblem()
|
||||
|
||||
t.Run("it asks the question", func(t *testing.T) {
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
|
||||
problem.AskQuestion(buffer)
|
||||
|
||||
want := "7+3: "
|
||||
got := buffer.String()
|
||||
|
||||
if want != got {
|
||||
t.Errorf("Expected question %s, got %s", want, got)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func createProblem() Problem {
|
||||
record := []string{"7+3", "10"}
|
||||
return New(record)
|
||||
}
|
||||
|
||||
func getAnswer(problem Problem, input string) bool {
|
||||
r := bytes.NewBufferString(input)
|
||||
|
||||
return problem.CheckAnswer(r)
|
||||
}
|
||||
|
||||
func checkAnswer(t *testing.T, got, want bool) {
|
||||
if want != got {
|
||||
t.Errorf("Expected to return %v got %v", want, got)
|
||||
}
|
||||
}
|
||||
12
1. Quiz Game Sol2/students/hackeryarn/problems.csv
Normal file
12
1. Quiz Game Sol2/students/hackeryarn/problems.csv
Normal file
@@ -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
|
||||
|
136
1. Quiz Game Sol2/students/hackeryarn/quiz_test.go
Normal file
136
1. Quiz Game Sol2/students/hackeryarn/quiz_test.go
Normal file
@@ -0,0 +1,136 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
quiz "github.com/gophercises/quiz/students/hackeryarn/myquiz"
|
||||
"github.com/gophercises/quiz/students/hackeryarn/problem"
|
||||
)
|
||||
|
||||
type flaggerMock struct {
|
||||
stringVarCalls int
|
||||
intVarCalls int
|
||||
varNames []string
|
||||
varUsages []string
|
||||
varStringValues []string
|
||||
varIntValues []int
|
||||
}
|
||||
|
||||
func (f *flaggerMock) StringVar(p *string, name, value, usage string) {
|
||||
f.stringVarCalls++
|
||||
f.varNames = append(f.varNames, name)
|
||||
f.varStringValues = append(f.varStringValues, value)
|
||||
f.varUsages = append(f.varUsages, usage)
|
||||
}
|
||||
|
||||
func (f *flaggerMock) IntVar(p *int, name string, value int, usage string) {
|
||||
f.intVarCalls++
|
||||
f.varNames = append(f.varNames, name)
|
||||
f.varIntValues = append(f.varIntValues, value)
|
||||
f.varUsages = append(f.varUsages, usage)
|
||||
}
|
||||
|
||||
type timerMock struct {
|
||||
duration int
|
||||
}
|
||||
|
||||
func (t *timerMock) NewTimer(d time.Duration) *time.Timer {
|
||||
t.duration = int(d.Seconds())
|
||||
return time.NewTimer(1 * time.Millisecond)
|
||||
}
|
||||
|
||||
func TestReadCSV(t *testing.T) {
|
||||
input := "7+3,10\n1+1,2"
|
||||
reader := bytes.NewBufferString(input)
|
||||
|
||||
record1 := []string{"7+3", "10"}
|
||||
record2 := []string{"1+1", "2"}
|
||||
problems := []problem.Problem{
|
||||
problem.New(record1),
|
||||
problem.New(record2),
|
||||
}
|
||||
|
||||
want := quiz.New(problems)
|
||||
got := ReadCSV(reader)
|
||||
|
||||
if !reflect.DeepEqual(want, got) {
|
||||
t.Errorf("it should read in %v got %v", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigFlags(t *testing.T) {
|
||||
flagger := &flaggerMock{}
|
||||
|
||||
ConfigFlags(flagger)
|
||||
|
||||
assertStringCalls(t, flagger)
|
||||
assertIntCalls(t, flagger)
|
||||
assertFlags(t, flagger)
|
||||
}
|
||||
|
||||
func TestStartTimer(t *testing.T) {
|
||||
timer := &timerMock{}
|
||||
w := &bytes.Buffer{}
|
||||
r := bytes.NewBufferString("\n")
|
||||
TimerSeconds := 30
|
||||
|
||||
StartTimer(w, r, timer)
|
||||
|
||||
if timer.duration != TimerSeconds {
|
||||
t.Errorf("it should set timer for %d seconds, set for %d",
|
||||
TimerSeconds, timer.duration)
|
||||
}
|
||||
|
||||
if w.String() != "Ready to start?" {
|
||||
t.Errorf("it should ask user if the user is ready, got %s", w.String())
|
||||
}
|
||||
}
|
||||
|
||||
func assertStringCalls(t *testing.T, flagger *flaggerMock) {
|
||||
t.Helper()
|
||||
if flagger.stringVarCalls != 1 {
|
||||
t.Errorf("it should call StringVar %d times, called %d",
|
||||
1, flagger.stringVarCalls)
|
||||
}
|
||||
}
|
||||
|
||||
func assertIntCalls(t *testing.T, flagger *flaggerMock) {
|
||||
t.Helper()
|
||||
if flagger.intVarCalls != 1 {
|
||||
t.Errorf("it should call IntVar %d times, called %d",
|
||||
1, flagger.intVarCalls)
|
||||
}
|
||||
}
|
||||
|
||||
func assertFlags(t *testing.T, flagger *flaggerMock) {
|
||||
t.Helper()
|
||||
|
||||
expectedNames := []string{FileFlag, TimerFlag}
|
||||
expectedUsages := []string{FileFlagUsage, TimerFlagUsage}
|
||||
expectedStringValues := []string{FileFlagValue}
|
||||
expectedIntValues := []int{TimerFlagValue}
|
||||
|
||||
if !reflect.DeepEqual(expectedNames, flagger.varNames) {
|
||||
t.Errorf("it should setup flag names to be %v, got %v",
|
||||
expectedNames, flagger.varNames)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expectedUsages, flagger.varUsages) {
|
||||
t.Errorf("it should setup flag usages to be %v, got %v",
|
||||
expectedUsages, flagger.varUsages)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expectedStringValues, flagger.varStringValues) {
|
||||
t.Errorf("it should setup string values to be %v, got %v",
|
||||
expectedStringValues, flagger.varStringValues)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expectedIntValues, flagger.varIntValues) {
|
||||
t.Errorf("it should setup int values to be %v, got %v",
|
||||
expectedIntValues, flagger.varIntValues)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user