diff --git a/kadai3/arisawa/README.md b/kadai3/arisawa/README.md new file mode 100644 index 0000000..d4c914c --- /dev/null +++ b/kadai3/arisawa/README.md @@ -0,0 +1,51 @@ +# 課題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!` って出すようにした + +* 気になってる点 + * main.go 内とはいえ、main関数外で `os.Exit()` するのはどうなんだろうか + * `word()` 関数で使ってる + * パッケージ化したらちゃんと書くんだけども、一旦やっつけで書いている中で作法として気になった + * 第三回の講習に `context.WithTimeout()` を足したくらいで、あとはUXのための追加実装 + * golang の勉強できてなくないかw + * コードはシンプルなのでいいのかもしれないが + +### TODO + +* パッケージ分離 +* テスト +* `$HOME/.cli-typing/records.json` にいい記録を保持していきたい +* 単語だけじゃなくてフレーズもやると面白いかもしれない + +## 課題3-2 + +分割ダウンローダーを実装しよう。 + +* Rangeアクセスを用いる +* いくつかのゴルーチンでダウンロードしてマージする +* エラー処理を工夫する + * golang.org/x/sync/errgourpパッケージなどを使ってみる +* キャンセルが発生した場合の実装を行う 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("") +}