Skip to content

Add kadai3-1 #41

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
51 changes: 51 additions & 0 deletions kadai3/arisawa/README.md
Original file line number Diff line number Diff line change
@@ -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パッケージなどを使ってみる
* キャンセルが発生した場合の実装を行う
101 changes: 101 additions & 0 deletions kadai3/arisawa/cli-typing/main.go
Original file line number Diff line number Diff line change
@@ -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("")
}