From 6c7a0616b68f6a2f9bc22c77a70ff1c04e65cf9e Mon Sep 17 00:00:00 2001 From: Daigo Hamasaki Date: Sat, 15 Jun 2019 14:06:43 +0900 Subject: [PATCH 01/14] Add basic implementation --- kadai3-1/lfcd85/.gitignore | 1 + kadai3-1/lfcd85/Makefile | 12 ++ kadai3-1/lfcd85/bin/.gitkeep | 0 kadai3-1/lfcd85/cmd/main.go | 25 +++ kadai3-1/lfcd85/go.mod | 3 + .../lfcd85/testdata/go_standard_library.txt | 154 ++++++++++++++++++ kadai3-1/lfcd85/typinggame/typinggame.go | 10 ++ kadai3-1/lfcd85/typinggame/typinggame_test.go | 13 ++ 8 files changed, 218 insertions(+) create mode 100644 kadai3-1/lfcd85/.gitignore create mode 100644 kadai3-1/lfcd85/Makefile create mode 100644 kadai3-1/lfcd85/bin/.gitkeep create mode 100644 kadai3-1/lfcd85/cmd/main.go create mode 100644 kadai3-1/lfcd85/go.mod create mode 100644 kadai3-1/lfcd85/testdata/go_standard_library.txt create mode 100644 kadai3-1/lfcd85/typinggame/typinggame.go create mode 100644 kadai3-1/lfcd85/typinggame/typinggame_test.go diff --git a/kadai3-1/lfcd85/.gitignore b/kadai3-1/lfcd85/.gitignore new file mode 100644 index 0000000..36f971e --- /dev/null +++ b/kadai3-1/lfcd85/.gitignore @@ -0,0 +1 @@ +bin/* diff --git a/kadai3-1/lfcd85/Makefile b/kadai3-1/lfcd85/Makefile new file mode 100644 index 0000000..76995b9 --- /dev/null +++ b/kadai3-1/lfcd85/Makefile @@ -0,0 +1,12 @@ +build: cmd/*.go typinggame/*.go + GO111MODULE=on go build -o bin/typinggame cmd/main.go + +fmt: + go fmt ./... + go vet ./... + +check: + GO111MODULE=on go test ./... -v + +coverage: + GO111MODULE=on go test ./... -cover diff --git a/kadai3-1/lfcd85/bin/.gitkeep b/kadai3-1/lfcd85/bin/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/kadai3-1/lfcd85/cmd/main.go b/kadai3-1/lfcd85/cmd/main.go new file mode 100644 index 0000000..9da1c92 --- /dev/null +++ b/kadai3-1/lfcd85/cmd/main.go @@ -0,0 +1,25 @@ +package main + +import ( + "fmt" + + "github.com/gopherdojo/dojo5/kadai3-1/lfcd85/typinggame" +) + +func main() { + // create words from input file (abstracted by io.Reader if possible) + + // start time counting by time.After or context.WithTimeout + + // output word to stdout + // get one line from stdin + // judge whether two words are the same + // if so, add count of correct answers + + // when time limit has come, show the count of correct answers + + if err := typinggame.Execute(); err != nil { + fmt.Println("error:", err) + return + } +} diff --git a/kadai3-1/lfcd85/go.mod b/kadai3-1/lfcd85/go.mod new file mode 100644 index 0000000..699a07f --- /dev/null +++ b/kadai3-1/lfcd85/go.mod @@ -0,0 +1,3 @@ +module github.com/gopherdojo/dojo5/kadai3-1/lfcd85 + +go 1.12 diff --git a/kadai3-1/lfcd85/testdata/go_standard_library.txt b/kadai3-1/lfcd85/testdata/go_standard_library.txt new file mode 100644 index 0000000..46f3462 --- /dev/null +++ b/kadai3-1/lfcd85/testdata/go_standard_library.txt @@ -0,0 +1,154 @@ +archive +archive/tar +archive/zip +bufio +builtin +bytes +compress +compress/bzip2 +compress/flate +compress/gzip +compress/lzw +compress/zlib +container +container/heap +container/list +container/ring +context +crypto +crypto/aes +crypto/cipher +crypto/des +crypto/dsa +crypto/ecdsa +crypto/elliptic +crypto/hmac +crypto/md5 +crypto/rand +crypto/rc4 +crypto/rsa +crypto/sha1 +crypto/sha256 +crypto/sha512 +crypto/subtle +crypto/tls +crypto/x509 +crypto/x509/pkix +database +database/sql +database/sql/driver +debug +debug/dwarf +debug/elf +debug/gosym +debug/macho +debug/pe +debug/plan9obj +encoding +encoding/ascii85 +encoding/asn1 +encoding/base32 +encoding/base64 +encoding/binary +encoding/csv +encoding/gob +encoding/hex +encoding/json +encoding/pem +encoding/xml +errors +expvar +flag +fmt +go +go/ast +go/build +go/constant +go/doc +go/format +go/importer +go/parser +go/printer +go/scanner +go/token +go/types +hash +hash/adler32 +hash/crc32 +hash/crc64 +hash/fnv +html +html/template +image +image/color +image/color/palette +image/draw +image/gif +image/jpeg +image/png +index +index/suffixarray +io +io/ioutil +log +log/syslog +math +math/big +math/bits +math/cmplx +math/rand +mime +mime/multipart +mime/quotedprintable +net +net/http +net/http/cgi +net/http/cookiejar +net/http/fcgi +net/http/httptest +net/http/httptrace +net/http/httputil +net/http/pprof +net/mail +net/rpc +net/rpc/jsonrpc +net/smtp +net/textproto +net/url +os +os/exec +os/signal +os/user +path +path/filepath +plugin +reflect +regexp +regexp/syntax +runtime +runtime/cgo +runtime/debug +runtime/msan +runtime/pprof +runtime/race +runtime/trace +sort +strconv +strings +sync +sync/atomic +syscall +syscall/js +testing +testing/iotest +testing/quick +text +text/scanner +text/tabwriter +text/template +text/template/parse +time +unicode +unicode/utf16 +unicode/utf8 +unsafe diff --git a/kadai3-1/lfcd85/typinggame/typinggame.go b/kadai3-1/lfcd85/typinggame/typinggame.go new file mode 100644 index 0000000..054a492 --- /dev/null +++ b/kadai3-1/lfcd85/typinggame/typinggame.go @@ -0,0 +1,10 @@ +package typinggame + +import ( + "fmt" +) + +func Execute() error { + fmt.Println("hello, typing game") + return nil +} diff --git a/kadai3-1/lfcd85/typinggame/typinggame_test.go b/kadai3-1/lfcd85/typinggame/typinggame_test.go new file mode 100644 index 0000000..78865cc --- /dev/null +++ b/kadai3-1/lfcd85/typinggame/typinggame_test.go @@ -0,0 +1,13 @@ +package typinggame_test + +import ( + "testing" + + "github.com/gopherdojo/dojo5/kadai3-1/lfcd85/typinggame" +) + +func TestExecute(t *testing.T) { + if err := typinggame.Execute(); err != nil { + t.Errorf("error: %v", err) + } +} From 55849c772c1742bae46f21e6f37e9b13b6e6986f Mon Sep 17 00:00:00 2001 From: Daigo Hamasaki Date: Sat, 15 Jun 2019 16:53:04 +0900 Subject: [PATCH 02/14] Add `words` package and its test --- kadai3-1/lfcd85/Makefile | 2 +- kadai3-1/lfcd85/go.mod | 2 ++ kadai3-1/lfcd85/go.sum | 4 +++ kadai3-1/lfcd85/testdata/abc.txt | 3 +++ kadai3-1/lfcd85/words/words.go | 34 ++++++++++++++++++++++++ kadai3-1/lfcd85/words/words_test.go | 40 +++++++++++++++++++++++++++++ 6 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 kadai3-1/lfcd85/go.sum create mode 100644 kadai3-1/lfcd85/testdata/abc.txt create mode 100644 kadai3-1/lfcd85/words/words.go create mode 100644 kadai3-1/lfcd85/words/words_test.go diff --git a/kadai3-1/lfcd85/Makefile b/kadai3-1/lfcd85/Makefile index 76995b9..996b188 100644 --- a/kadai3-1/lfcd85/Makefile +++ b/kadai3-1/lfcd85/Makefile @@ -1,4 +1,4 @@ -build: cmd/*.go typinggame/*.go +build: cmd/*.go typinggame/*.go words/*.go GO111MODULE=on go build -o bin/typinggame cmd/main.go fmt: diff --git a/kadai3-1/lfcd85/go.mod b/kadai3-1/lfcd85/go.mod index 699a07f..d2f0782 100644 --- a/kadai3-1/lfcd85/go.mod +++ b/kadai3-1/lfcd85/go.mod @@ -1,3 +1,5 @@ module github.com/gopherdojo/dojo5/kadai3-1/lfcd85 go 1.12 + +require github.com/hashicorp/go-multierror v1.0.0 diff --git a/kadai3-1/lfcd85/go.sum b/kadai3-1/lfcd85/go.sum new file mode 100644 index 0000000..d2f1330 --- /dev/null +++ b/kadai3-1/lfcd85/go.sum @@ -0,0 +1,4 @@ +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= diff --git a/kadai3-1/lfcd85/testdata/abc.txt b/kadai3-1/lfcd85/testdata/abc.txt new file mode 100644 index 0000000..de98044 --- /dev/null +++ b/kadai3-1/lfcd85/testdata/abc.txt @@ -0,0 +1,3 @@ +a +b +c diff --git a/kadai3-1/lfcd85/words/words.go b/kadai3-1/lfcd85/words/words.go new file mode 100644 index 0000000..4c8a92e --- /dev/null +++ b/kadai3-1/lfcd85/words/words.go @@ -0,0 +1,34 @@ +package words + +import ( + "bufio" + "os" + + "github.com/gopherdojo/dojo5/kadai3-1/lfcd85/typinggame" + "github.com/hashicorp/go-multierror" +) + +func Import(path string) (typinggame.Words, error) { + var words typinggame.Words + var result error + + f, err := os.Open(path) + if err != nil { + result = multierror.Append(result, err) + } + defer func() { + if err := f.Close(); err != nil { + result = multierror.Append(result, err) + } + }() + + s := bufio.NewScanner(f) + for s.Scan() { + words = append(words, s.Text()) + } + if err := s.Err(); err != nil { + result = multierror.Append(result, err) + } + + return words, result +} diff --git a/kadai3-1/lfcd85/words/words_test.go b/kadai3-1/lfcd85/words/words_test.go new file mode 100644 index 0000000..52f4fc8 --- /dev/null +++ b/kadai3-1/lfcd85/words/words_test.go @@ -0,0 +1,40 @@ +package words_test + +import ( + "testing" + + "github.com/gopherdojo/dojo5/kadai3-1/lfcd85/words" +) + +func TestImport(t *testing.T) { + cases := []struct { + path string + firstWord string + lastWord string + length int + }{ + {"../testdata/abc.txt", "a", "c", 3}, + {"../testdata/go_standard_library.txt", "archive", "unsafe", 154}, + } + + for _, c := range cases { + c := c + t.Run(c.path, func(t *testing.T) { + t.Parallel() + + words, err := words.Import(c.path) + if err != nil { + t.Errorf("failed to Import %v: %v", c.path, err) + } + if words[0] != c.firstWord { + t.Errorf("the first item of imported words is %v; it should be %v", words[0], c.firstWord) + } + if words[len(words)-1] != c.lastWord { + t.Errorf("the first item of imported words is %v; it should be %v", words[0], c.lastWord) + } + if len(words) != c.length { + t.Errorf("the length of imported words is %v; it should be %v", len(words), c.length) + } + }) + } +} From 8039b12b8cec56c169059196d6f405614d426b43 Mon Sep 17 00:00:00 2001 From: Daigo Hamasaki Date: Sat, 15 Jun 2019 16:54:28 +0900 Subject: [PATCH 03/14] Connect word importer to typing game --- kadai3-1/lfcd85/cmd/main.go | 24 ++++++++++--------- kadai3-1/lfcd85/typinggame/typinggame.go | 13 ++++++++-- kadai3-1/lfcd85/typinggame/typinggame_test.go | 10 ++++++-- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/kadai3-1/lfcd85/cmd/main.go b/kadai3-1/lfcd85/cmd/main.go index 9da1c92..a81e59f 100644 --- a/kadai3-1/lfcd85/cmd/main.go +++ b/kadai3-1/lfcd85/cmd/main.go @@ -2,23 +2,25 @@ package main import ( "fmt" + "time" "github.com/gopherdojo/dojo5/kadai3-1/lfcd85/typinggame" + "github.com/gopherdojo/dojo5/kadai3-1/lfcd85/words" ) func main() { - // create words from input file (abstracted by io.Reader if possible) - - // start time counting by time.After or context.WithTimeout - - // output word to stdout - // get one line from stdin - // judge whether two words are the same - // if so, add count of correct answers - - // when time limit has come, show the count of correct answers + path := "./testdata/go_standard_library.txt" // FIXME: move to options + words, err := words.Import(path) + if err != nil { + fmt.Println("error:", err) + return + } + g := typinggame.Game{ + Words: words, + TimeLimit: 30 * time.Second, + } - if err := typinggame.Execute(); err != nil { + if err := typinggame.Execute(g); err != nil { fmt.Println("error:", err) return } diff --git a/kadai3-1/lfcd85/typinggame/typinggame.go b/kadai3-1/lfcd85/typinggame/typinggame.go index 054a492..25ab7dd 100644 --- a/kadai3-1/lfcd85/typinggame/typinggame.go +++ b/kadai3-1/lfcd85/typinggame/typinggame.go @@ -2,9 +2,18 @@ package typinggame import ( "fmt" + "time" ) -func Execute() error { - fmt.Println("hello, typing game") +type Words []string + +type Game struct { + Words Words + TimeLimit time.Duration +} + +func Execute(g Game) error { + fmt.Println(g.Words) + fmt.Println(g.TimeLimit) return nil } diff --git a/kadai3-1/lfcd85/typinggame/typinggame_test.go b/kadai3-1/lfcd85/typinggame/typinggame_test.go index 78865cc..8647cb2 100644 --- a/kadai3-1/lfcd85/typinggame/typinggame_test.go +++ b/kadai3-1/lfcd85/typinggame/typinggame_test.go @@ -2,12 +2,18 @@ package typinggame_test import ( "testing" + "time" "github.com/gopherdojo/dojo5/kadai3-1/lfcd85/typinggame" ) func TestExecute(t *testing.T) { - if err := typinggame.Execute(); err != nil { - t.Errorf("error: %v", err) + g := typinggame.Game{ + typinggame.Words{"hoge", "fuga", "piyo"}, + 30 * time.Second, + } + + if err := typinggame.Execute(g); err != nil { + t.Errorf("failed to execute new game: %v", err) } } From b385b355580c2119ff68cf0d32718942f529eb56 Mon Sep 17 00:00:00 2001 From: Daigo Hamasaki Date: Sat, 15 Jun 2019 19:05:39 +0900 Subject: [PATCH 04/14] Enable to play the game --- kadai3-1/lfcd85/typinggame/typinggame.go | 51 ++++++++++++++++++- kadai3-1/lfcd85/typinggame/typinggame_test.go | 2 +- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/kadai3-1/lfcd85/typinggame/typinggame.go b/kadai3-1/lfcd85/typinggame/typinggame.go index 25ab7dd..f98dfd7 100644 --- a/kadai3-1/lfcd85/typinggame/typinggame.go +++ b/kadai3-1/lfcd85/typinggame/typinggame.go @@ -1,7 +1,12 @@ package typinggame import ( + "bufio" + "context" "fmt" + "io" + "math/rand" + "os" "time" ) @@ -13,7 +18,49 @@ type Game struct { } func Execute(g Game) error { - fmt.Println(g.Words) - fmt.Println(g.TimeLimit) + g.run(os.Stdin, os.Stdout) return nil } + +func (g *Game) run(r io.Reader, w io.Writer) { + ch := input(r) + bc := context.Background() + ctx, cancel := context.WithTimeout(bc, g.TimeLimit) + defer cancel() + + fmt.Fprintln(w, "Let's type the standard package names! (Time limit:", g.TimeLimit, ")") + + var score int + rand.Seed(time.Now().UnixNano()) + word := g.Words[rand.Intn(len(g.Words))] +LOOP: + for { + fmt.Fprintln(w, ">", word) + select { + case input := <-ch: + if input == word { + score++ + fmt.Fprintln(w, "ok! current score:", score) + word = g.Words[rand.Intn(len(g.Words))] + } else { + fmt.Fprintln(w, "ng") + } + case <-ctx.Done(): + fmt.Fprintln(w) + fmt.Fprintln(w, g.TimeLimit, "has passed: you correctly typed", score, "packages!") + break LOOP + } + } +} + +func input(r io.Reader) <-chan string { + ch := make(chan string) + + go func() { + s := bufio.NewScanner(r) + for s.Scan() { + ch <- s.Text() + } + }() + return ch +} diff --git a/kadai3-1/lfcd85/typinggame/typinggame_test.go b/kadai3-1/lfcd85/typinggame/typinggame_test.go index 8647cb2..630fccd 100644 --- a/kadai3-1/lfcd85/typinggame/typinggame_test.go +++ b/kadai3-1/lfcd85/typinggame/typinggame_test.go @@ -10,7 +10,7 @@ import ( func TestExecute(t *testing.T) { g := typinggame.Game{ typinggame.Words{"hoge", "fuga", "piyo"}, - 30 * time.Second, + 1 * time.Second, } if err := typinggame.Execute(g); err != nil { From 403837817ed5ec8a26c8d6b5e21dcbb26b19547f Mon Sep 17 00:00:00 2001 From: Daigo Hamasaki Date: Tue, 18 Jun 2019 00:14:47 +0900 Subject: [PATCH 05/14] Add test for Game.run() --- kadai3-1/lfcd85/typinggame/export_test.go | 3 ++ kadai3-1/lfcd85/typinggame/typinggame.go | 11 ++--- kadai3-1/lfcd85/typinggame/typinggame_test.go | 49 ++++++++++++++++++- 3 files changed, 56 insertions(+), 7 deletions(-) create mode 100644 kadai3-1/lfcd85/typinggame/export_test.go diff --git a/kadai3-1/lfcd85/typinggame/export_test.go b/kadai3-1/lfcd85/typinggame/export_test.go new file mode 100644 index 0000000..52c4547 --- /dev/null +++ b/kadai3-1/lfcd85/typinggame/export_test.go @@ -0,0 +1,3 @@ +package typinggame + +var ExportGameRun = (*Game).run diff --git a/kadai3-1/lfcd85/typinggame/typinggame.go b/kadai3-1/lfcd85/typinggame/typinggame.go index f98dfd7..1efab29 100644 --- a/kadai3-1/lfcd85/typinggame/typinggame.go +++ b/kadai3-1/lfcd85/typinggame/typinggame.go @@ -18,12 +18,11 @@ type Game struct { } func Execute(g Game) error { - g.run(os.Stdin, os.Stdout) + g.run(inputChannel(os.Stdin), os.Stdout) return nil } -func (g *Game) run(r io.Reader, w io.Writer) { - ch := input(r) +func (g *Game) run(ch <-chan string, w io.Writer) { bc := context.Background() ctx, cancel := context.WithTimeout(bc, g.TimeLimit) defer cancel() @@ -40,10 +39,10 @@ LOOP: case input := <-ch: if input == word { score++ - fmt.Fprintln(w, "ok! current score:", score) + fmt.Fprintln(w, input, "... OK! current score:", score) word = g.Words[rand.Intn(len(g.Words))] } else { - fmt.Fprintln(w, "ng") + fmt.Fprintln(w, input, "... NG: try again.") } case <-ctx.Done(): fmt.Fprintln(w) @@ -53,7 +52,7 @@ LOOP: } } -func input(r io.Reader) <-chan string { +func inputChannel(r io.Reader) <-chan string { ch := make(chan string) go func() { diff --git a/kadai3-1/lfcd85/typinggame/typinggame_test.go b/kadai3-1/lfcd85/typinggame/typinggame_test.go index 630fccd..7e5f2d7 100644 --- a/kadai3-1/lfcd85/typinggame/typinggame_test.go +++ b/kadai3-1/lfcd85/typinggame/typinggame_test.go @@ -1,6 +1,8 @@ package typinggame_test import ( + "bytes" + "regexp" "testing" "time" @@ -9,7 +11,7 @@ import ( func TestExecute(t *testing.T) { g := typinggame.Game{ - typinggame.Words{"hoge", "fuga", "piyo"}, + typinggame.Words{"hoge"}, 1 * time.Second, } @@ -17,3 +19,48 @@ func TestExecute(t *testing.T) { t.Errorf("failed to execute new game: %v", err) } } + +func TestGame_run(t *testing.T) { + ch := make(chan string) + go func() { + time.Sleep(100 * time.Millisecond) + ch <- "hoga" + time.Sleep(100 * time.Millisecond) + ch <- "hoge" + }() + + g := typinggame.Game{ + typinggame.Words{"hoge"}, + 1 * time.Second, + } + + var output bytes.Buffer + typinggame.ExportGameRun(&g, ch, &output) + + cases := []struct { + output string + expected bool + }{ + {"hoga ... NG", true}, + {"hoga ... OK", false}, + {"hoge ... OK", true}, + {"hoge ... NG", false}, + } + + for _, c := range cases { + c := c + t.Run(c.output, func(t *testing.T) { + t.Parallel() + + actual := regexp.MustCompile(c.output).MatchString(output.String()) + if actual != c.expected { + switch c.expected { + case true: + t.Errorf("%v should be outputted but actually was not", c.output) + case false: + t.Errorf("%v should not be outputted but actyally was", c.output) + } + } + }) + } +} From 97c1e790a36aed53e788d3150344ffcac77be83b Mon Sep 17 00:00:00 2001 From: Daigo Hamasaki Date: Tue, 18 Jun 2019 00:47:36 +0900 Subject: [PATCH 06/14] Set time limit as a command argument --- kadai3-1/lfcd85/cmd/main.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/kadai3-1/lfcd85/cmd/main.go b/kadai3-1/lfcd85/cmd/main.go index a81e59f..e2cc217 100644 --- a/kadai3-1/lfcd85/cmd/main.go +++ b/kadai3-1/lfcd85/cmd/main.go @@ -1,6 +1,7 @@ package main import ( + "flag" "fmt" "time" @@ -10,6 +11,9 @@ import ( func main() { path := "./testdata/go_standard_library.txt" // FIXME: move to options + timeLimit := flag.Int("t", 30, "Time limit of the game (secs)") + flag.Parse() + words, err := words.Import(path) if err != nil { fmt.Println("error:", err) @@ -17,7 +21,7 @@ func main() { } g := typinggame.Game{ Words: words, - TimeLimit: 30 * time.Second, + TimeLimit: time.Duration(*timeLimit) * time.Second, } if err := typinggame.Execute(g); err != nil { From 1607fd482f9b915203f4a47187a755ae4edb8355 Mon Sep 17 00:00:00 2001 From: Daigo Hamasaki Date: Tue, 18 Jun 2019 00:47:56 +0900 Subject: [PATCH 07/14] Set file path as a const --- kadai3-1/lfcd85/cmd/main.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kadai3-1/lfcd85/cmd/main.go b/kadai3-1/lfcd85/cmd/main.go index e2cc217..4533ca0 100644 --- a/kadai3-1/lfcd85/cmd/main.go +++ b/kadai3-1/lfcd85/cmd/main.go @@ -9,8 +9,9 @@ import ( "github.com/gopherdojo/dojo5/kadai3-1/lfcd85/words" ) +const path = "./testdata/go_standard_library.txt" + func main() { - path := "./testdata/go_standard_library.txt" // FIXME: move to options timeLimit := flag.Int("t", 30, "Time limit of the game (secs)") flag.Parse() From fc9963efa3a94c08e831616a9c9396966f2c7b74 Mon Sep 17 00:00:00 2001 From: Daigo Hamasaki Date: Tue, 18 Jun 2019 01:00:35 +0900 Subject: [PATCH 08/14] Add test for game score --- kadai3-1/lfcd85/typinggame/typinggame.go | 2 +- kadai3-1/lfcd85/typinggame/typinggame_test.go | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/kadai3-1/lfcd85/typinggame/typinggame.go b/kadai3-1/lfcd85/typinggame/typinggame.go index 1efab29..01f4095 100644 --- a/kadai3-1/lfcd85/typinggame/typinggame.go +++ b/kadai3-1/lfcd85/typinggame/typinggame.go @@ -46,7 +46,7 @@ LOOP: } case <-ctx.Done(): fmt.Fprintln(w) - fmt.Fprintln(w, g.TimeLimit, "has passed: you correctly typed", score, "packages!") + fmt.Fprintln(w, g.TimeLimit, "has passed: you correctly typed", score, "package(s)!") break LOOP } } diff --git a/kadai3-1/lfcd85/typinggame/typinggame_test.go b/kadai3-1/lfcd85/typinggame/typinggame_test.go index 7e5f2d7..83a8c38 100644 --- a/kadai3-1/lfcd85/typinggame/typinggame_test.go +++ b/kadai3-1/lfcd85/typinggame/typinggame_test.go @@ -21,6 +21,11 @@ func TestExecute(t *testing.T) { } func TestGame_run(t *testing.T) { + g := typinggame.Game{ + typinggame.Words{"hoge"}, + 1 * time.Second, + } + ch := make(chan string) go func() { time.Sleep(100 * time.Millisecond) @@ -29,11 +34,6 @@ func TestGame_run(t *testing.T) { ch <- "hoge" }() - g := typinggame.Game{ - typinggame.Words{"hoge"}, - 1 * time.Second, - } - var output bytes.Buffer typinggame.ExportGameRun(&g, ch, &output) @@ -45,6 +45,7 @@ func TestGame_run(t *testing.T) { {"hoga ... OK", false}, {"hoge ... OK", true}, {"hoge ... NG", false}, + {"you correctly typed 1 package", true}, } for _, c := range cases { From 01aef3e5d03ac3ecff2eebae8560aa092bbd7e08 Mon Sep 17 00:00:00 2001 From: Daigo Hamasaki Date: Tue, 18 Jun 2019 08:36:37 +0900 Subject: [PATCH 09/14] Add README --- kadai3-1/lfcd85/README.md | 41 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 kadai3-1/lfcd85/README.md diff --git a/kadai3-1/lfcd85/README.md b/kadai3-1/lfcd85/README.md new file mode 100644 index 0000000..eb1beba --- /dev/null +++ b/kadai3-1/lfcd85/README.md @@ -0,0 +1,41 @@ +# Typing Game + +An implementation for the typing game, kadai3-1 of Gopherdojo #5. The standard library packages for Golang are the words for typing. + +Gopher道場 #5 課題3-1 `タイピングゲームを作ろう` の実装です。Go言語の標準ライブラリのパッケージ名がタイピング対象の単語になっています。 + +## Installation + +```bash +$ make build +``` + +## Usage + +The game starts after executing the command below. Let's type the shown package name. Your score will be shown after reaching to the time limit and finishing the game. + +下記のコマンドを実行するとゲームが開始します。表示されたパッケージ名をタイプしてください。制限時間に達するとゲームが終了し、スコアが表示されます。 + +```bash +$ bin/typinggame +Let's type the standard package names! ( Time limit: 30s ) +> hash/fnv +hash/fnv +hash/fnv ... OK! current score: 1 + +> debug +d +d ... NG: try again. +> debug + +30s has passed: you correctly typed 1 package(s)! +``` + +The time limit can be set by `-t` option. Default value is 30 sec. + +制限時間は `-t` オプションで変更可能です。デフォルトは30秒です。 + +```bash +$ bin/typinggame -t 10 +Let's type the standard package names! ( Time limit: 10s ) +``` From 1e3a6d7aa148cb556fecc26da1805dec9d9900af Mon Sep 17 00:00:00 2001 From: Daigo Hamasaki Date: Tue, 18 Jun 2019 08:36:57 +0900 Subject: [PATCH 10/14] Modify text --- kadai3-1/lfcd85/cmd/main.go | 2 +- kadai3-1/lfcd85/typinggame/typinggame.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kadai3-1/lfcd85/cmd/main.go b/kadai3-1/lfcd85/cmd/main.go index 4533ca0..4fca9ca 100644 --- a/kadai3-1/lfcd85/cmd/main.go +++ b/kadai3-1/lfcd85/cmd/main.go @@ -12,7 +12,7 @@ import ( const path = "./testdata/go_standard_library.txt" func main() { - timeLimit := flag.Int("t", 30, "Time limit of the game (secs)") + timeLimit := flag.Int("t", 30, "Time limit of the game (sec)") flag.Parse() words, err := words.Import(path) diff --git a/kadai3-1/lfcd85/typinggame/typinggame.go b/kadai3-1/lfcd85/typinggame/typinggame.go index 01f4095..a06c28c 100644 --- a/kadai3-1/lfcd85/typinggame/typinggame.go +++ b/kadai3-1/lfcd85/typinggame/typinggame.go @@ -27,7 +27,7 @@ func (g *Game) run(ch <-chan string, w io.Writer) { ctx, cancel := context.WithTimeout(bc, g.TimeLimit) defer cancel() - fmt.Fprintln(w, "Let's type the standard package names! (Time limit:", g.TimeLimit, ")") + fmt.Fprintln(w, "Let's type the standard package names! ( Time limit:", g.TimeLimit, ")") var score int rand.Seed(time.Now().UnixNano()) From cea0f8e56f375c163e518d786d740625d627c41b Mon Sep 17 00:00:00 2001 From: Daigo Hamasaki Date: Tue, 18 Jun 2019 08:46:36 +0900 Subject: [PATCH 11/14] Add comments --- kadai3-1/lfcd85/typinggame/typinggame.go | 3 +++ kadai3-1/lfcd85/words/words.go | 1 + 2 files changed, 4 insertions(+) diff --git a/kadai3-1/lfcd85/typinggame/typinggame.go b/kadai3-1/lfcd85/typinggame/typinggame.go index a06c28c..e22a90b 100644 --- a/kadai3-1/lfcd85/typinggame/typinggame.go +++ b/kadai3-1/lfcd85/typinggame/typinggame.go @@ -10,13 +10,16 @@ import ( "time" ) +// Words stores a slice of words which is used for the game. type Words []string +// Game struct holds the words and the time limits of the game. type Game struct { Words Words TimeLimit time.Duration } +// Execute starts the game using standard input and output. func Execute(g Game) error { g.run(inputChannel(os.Stdin), os.Stdout) return nil diff --git a/kadai3-1/lfcd85/words/words.go b/kadai3-1/lfcd85/words/words.go index 4c8a92e..9809bb6 100644 --- a/kadai3-1/lfcd85/words/words.go +++ b/kadai3-1/lfcd85/words/words.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/go-multierror" ) +// Import reads the text file and returns the words for the typing game. func Import(path string) (typinggame.Words, error) { var words typinggame.Words var result error From 401c36289685db341f351dad929b9b6cda8351d1 Mon Sep 17 00:00:00 2001 From: Daigo Hamasaki Date: Tue, 18 Jun 2019 08:54:59 +0900 Subject: [PATCH 12/14] Extract initGame() in typinggame_test --- kadai3-1/lfcd85/typinggame/typinggame_test.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/kadai3-1/lfcd85/typinggame/typinggame_test.go b/kadai3-1/lfcd85/typinggame/typinggame_test.go index 83a8c38..9a03ccf 100644 --- a/kadai3-1/lfcd85/typinggame/typinggame_test.go +++ b/kadai3-1/lfcd85/typinggame/typinggame_test.go @@ -9,11 +9,15 @@ import ( "github.com/gopherdojo/dojo5/kadai3-1/lfcd85/typinggame" ) -func TestExecute(t *testing.T) { - g := typinggame.Game{ +func initGame() typinggame.Game { + return typinggame.Game{ typinggame.Words{"hoge"}, 1 * time.Second, } +} + +func TestExecute(t *testing.T) { + g := initGame() if err := typinggame.Execute(g); err != nil { t.Errorf("failed to execute new game: %v", err) @@ -21,10 +25,7 @@ func TestExecute(t *testing.T) { } func TestGame_run(t *testing.T) { - g := typinggame.Game{ - typinggame.Words{"hoge"}, - 1 * time.Second, - } + g := initGame() ch := make(chan string) go func() { From f947f819f09757ae75dc9a34ed7be2b9d4a1fbac Mon Sep 17 00:00:00 2001 From: Daigo Hamasaki Date: Tue, 18 Jun 2019 14:52:09 +0900 Subject: [PATCH 13/14] Add error handling to fmt.Fprintln() --- kadai3-1/lfcd85/typinggame/typinggame.go | 31 +++++++++++++++++------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/kadai3-1/lfcd85/typinggame/typinggame.go b/kadai3-1/lfcd85/typinggame/typinggame.go index e22a90b..68eb0ec 100644 --- a/kadai3-1/lfcd85/typinggame/typinggame.go +++ b/kadai3-1/lfcd85/typinggame/typinggame.go @@ -8,6 +8,8 @@ import ( "math/rand" "os" "time" + + "github.com/hashicorp/go-multierror" ) // Words stores a slice of words which is used for the game. @@ -21,38 +23,42 @@ type Game struct { // Execute starts the game using standard input and output. func Execute(g Game) error { - g.run(inputChannel(os.Stdin), os.Stdout) - return nil + err := g.run(inputChannel(os.Stdin), os.Stdout) + return err } -func (g *Game) run(ch <-chan string, w io.Writer) { +func (g *Game) run(ch <-chan string, w io.Writer) error { + var result error + bc := context.Background() ctx, cancel := context.WithTimeout(bc, g.TimeLimit) defer cancel() - fmt.Fprintln(w, "Let's type the standard package names! ( Time limit:", g.TimeLimit, ")") + result = printWithMultiErr(w, result, "Let's type the standard package names! ( Time limit:", g.TimeLimit, ")") var score int rand.Seed(time.Now().UnixNano()) word := g.Words[rand.Intn(len(g.Words))] LOOP: for { - fmt.Fprintln(w, ">", word) + result = printWithMultiErr(w, result, ">", word) select { case input := <-ch: if input == word { score++ - fmt.Fprintln(w, input, "... OK! current score:", score) + result = printWithMultiErr(w, result, input, "... OK! current score:", score) word = g.Words[rand.Intn(len(g.Words))] } else { - fmt.Fprintln(w, input, "... NG: try again.") + result = printWithMultiErr(w, result, input, "... NG: try again.") } case <-ctx.Done(): - fmt.Fprintln(w) - fmt.Fprintln(w, g.TimeLimit, "has passed: you correctly typed", score, "package(s)!") + result = printWithMultiErr(w, result) + result = printWithMultiErr(w, result, g.TimeLimit, "has passed: you correctly typed", score, "package(s)!") break LOOP } } + + return result } func inputChannel(r io.Reader) <-chan string { @@ -66,3 +72,10 @@ func inputChannel(r io.Reader) <-chan string { }() return ch } + +func printWithMultiErr(w io.Writer, result error, a ...interface{}) error { + if _, err := fmt.Fprintln(w, a...); err != nil { + result = multierror.Append(result, err) + } + return result +} From 6e3d8b09e17c1dc9d4643bc5b3eb24890275e798 Mon Sep 17 00:00:00 2001 From: Daigo Hamasaki Date: Wed, 19 Jun 2019 08:05:55 +0900 Subject: [PATCH 14/14] Fix .gitignore --- kadai3-1/lfcd85/.gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kadai3-1/lfcd85/.gitignore b/kadai3-1/lfcd85/.gitignore index 36f971e..3b01975 100644 --- a/kadai3-1/lfcd85/.gitignore +++ b/kadai3-1/lfcd85/.gitignore @@ -1 +1 @@ -bin/* +bin/typinggame