From a514d80cf9ba5b06b98ed3a80277708285d7734b Mon Sep 17 00:00:00 2001 From: Kosuke Arisawa Date: Mon, 14 May 2018 09:49:29 +0900 Subject: [PATCH 1/3] Add cli typing game --- kadai3/arisawa/cli-typing/main.go | 101 ++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 kadai3/arisawa/cli-typing/main.go diff --git a/kadai3/arisawa/cli-typing/main.go b/kadai3/arisawa/cli-typing/main.go new file mode 100644 index 0000000..867a5ae --- /dev/null +++ b/kadai3/arisawa/cli-typing/main.go @@ -0,0 +1,101 @@ +package main + +import ( + "bufio" + "fmt" + "io" + "math/rand" + "os" + "reflect" + "time" + "context" + + "github.com/dmgk/faker" + "github.com/fatih/color" +) + +var ( + fakerMethods = []string{"Adjective", "Noun", "Verb", "IngVerb"} + goodMessage = "✓ GOOD!" + badMessage = "✗ BAD!" + timeout = 60 * time.Second + wordCount = 0 + goodCount = 0 + badCount = 0 + streaks = 0 +) + +func main() { + fmt.Print("press enter to start a minute typing! are you ready?") + bufio.NewScanner(os.Stdin).Scan() + + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + typing(ctx) + os.Exit(0) +} + +func input(r io.Reader) <-chan string { + ch := make(chan string) + + go func() { + s := bufio.NewScanner(r) + for s.Scan() { + ch <- s.Text() + } + close(ch) + }() + return ch +} + +func typing(ctx context.Context) { + ch := input(os.Stdin) + for { + w := word() + fmt.Println("> " + w) + select { + case v, ok := <-ch: + if ok { + output(w, v) + } else { + return + } + case <-ctx.Done(): + fmt.Printf("\n :\n :\ntimeup! %v words correct out of %v.\n", goodCount, wordCount) + return + } + } +} + +func word() string { + fh := faker.Hacker() + methodName := fakerMethods[rand.Intn(len(fakerMethods))] + rvm := reflect.ValueOf(fh).MethodByName(methodName) + rv := rvm.Call([]reflect.Value{}) + if w, ok := rv[0].Interface().(string); ok { + return w + } + // このエラーは main まで持っていくべきだろうか。ここで os.Exit するのは違和感がある + fmt.Printf("word generation error. fakerMethod: %v\n", methodName) + os.Exit(1) + return "" +} + +func output(w, v string) { + wordCount += 1 + if w == v { + goodCount += 1 + streaks += 1 + if streaks > 1 { + color.Green("%v %v streaks!", goodMessage, streaks) + } else { + color.Green("%v", goodMessage) + } + } else { + badCount += 1 + streaks = 0 + color.Red("%v", badMessage) + } + fmt.Println("") +} From 57c3eaa29326f4b507a46f924c38834174f06c60 Mon Sep 17 00:00:00 2001 From: Kosuke Arisawa Date: Mon, 14 May 2018 10:16:17 +0900 Subject: [PATCH 2/3] Add README --- kadai3/arisawa/README.md | 43 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 kadai3/arisawa/README.md diff --git a/kadai3/arisawa/README.md b/kadai3/arisawa/README.md new file mode 100644 index 0000000..48f642b --- /dev/null +++ b/kadai3/arisawa/README.md @@ -0,0 +1,43 @@ +# 課題3 + +## 課題3-1 + +タイピングゲームを作ろう。 + +* 標準出力に英単語を出す(出すものは自由) +* 標準入力から1行受け取る +* 制限時間内に何問解けたか表示する + +### 実装 + +時間がなくて main.go にベタッと書いてしまいました。 + +* 単語は `github.com/dmgk/faker` の `FakeHacker` 内のものを使っている + * いい感じの単語を出すメソッドを調べて、そのメソッドがランダムで選ばれるようにした +* 入力を受ける `input()` 関数は第三回で書いた内容とほぼ変わらず +* タイムアウトは `context.WithTimeout` を使った +* 入力とタイムアウトのチャネルの受け取りは `for` と `select` を使って実装している + * `select` 内で `break` を使うと `select` にしか作用しなかったので `typing()` 関数にした + +* 個人的に気を使った点 + * いちユーザーとしてはやる気になる単語を出題したかった + * ので、いい感じのパッケージ見つかってよかった + * CLIだと結構見づらいので `github.com/fatih/color` を使って文字に色をつけてみた + * UX向上のため、連続して正解したら `N streaks!` って出すようにした + +### TODO + +* パッケージ分離 +* テスト +* `$HOME/.cli-typing/records.json` にいい記録を保持していきたい +* 単語だけじゃなくてフレーズもやると面白いかもしれない + +## 課題3-2 + +分割ダウンローダーを実装しよう。 + +* Rangeアクセスを用いる +* いくつかのゴルーチンでダウンロードしてマージする +* エラー処理を工夫する + * golang.org/x/sync/errgourpパッケージなどを使ってみる +* キャンセルが発生した場合の実装を行う From 566be6648fe54894a18540da5998c47a6a9fca3b Mon Sep 17 00:00:00 2001 From: Kosuke Arisawa Date: Mon, 14 May 2018 10:52:14 +0900 Subject: [PATCH 3/3] =?UTF-8?q?=E6=B0=97=E3=81=AB=E3=81=AA=E3=81=A3?= =?UTF-8?q?=E3=81=A6=E3=82=8B=E7=82=B9=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kadai3/arisawa/README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/kadai3/arisawa/README.md b/kadai3/arisawa/README.md index 48f642b..d4c914c 100644 --- a/kadai3/arisawa/README.md +++ b/kadai3/arisawa/README.md @@ -14,7 +14,7 @@ * 単語は `github.com/dmgk/faker` の `FakeHacker` 内のものを使っている * いい感じの単語を出すメソッドを調べて、そのメソッドがランダムで選ばれるようにした -* 入力を受ける `input()` 関数は第三回で書いた内容とほぼ変わらず +* 入力を受ける `input()` 関数は第三回の講習で書いた内容とほぼ変わらず * タイムアウトは `context.WithTimeout` を使った * 入力とタイムアウトのチャネルの受け取りは `for` と `select` を使って実装している * `select` 内で `break` を使うと `select` にしか作用しなかったので `typing()` 関数にした @@ -25,6 +25,14 @@ * CLIだと結構見づらいので `github.com/fatih/color` を使って文字に色をつけてみた * UX向上のため、連続して正解したら `N streaks!` って出すようにした +* 気になってる点 + * main.go 内とはいえ、main関数外で `os.Exit()` するのはどうなんだろうか + * `word()` 関数で使ってる + * パッケージ化したらちゃんと書くんだけども、一旦やっつけで書いている中で作法として気になった + * 第三回の講習に `context.WithTimeout()` を足したくらいで、あとはUXのための追加実装 + * golang の勉強できてなくないかw + * コードはシンプルなのでいいのかもしれないが + ### TODO * パッケージ分離