diff --git a/kadai3-1/int128/README.md b/kadai3-1/int128/README.md new file mode 100644 index 0000000..f42f64b --- /dev/null +++ b/kadai3-1/int128/README.md @@ -0,0 +1,58 @@ +# kadai3-1 + +``` +% go run main.go +Are you ready? Type words in 30 seconds! +-- +door +>> door +apple +>> apple +farm +>> farm +nose +>> nose +town +>> town +stomach +>> stomach +cup +>> cup +band +>> band +watch +>> watch +pipe +>> pipe +pin +>> pin +train +>> train +coat +>> coat +plate +>> plate +oven +>> oven +brush +>> b +Time up! You got 15 point(s). +``` + + +## 課題3-1 + +> タイピングゲームを作ろう +> +> - 標準出力に英単語を出す(出すものは自由) +> - 標準入力から1行受け取る +> - 制限時間内に何問解けたか表示する + +## 課題3-2 + +> 分割ダウンロードを行う +> +> - Rangeアクセスを用いる +> - いくつかのゴルーチンでダウンロードしてマージする +> - エラー処理を工夫する: golang.org/x/sync/errgourpパッケージなどを使ってみる +> - キャンセルが発生した場合の実装を行う diff --git a/kadai3-1/int128/game/game.go b/kadai3-1/int128/game/game.go new file mode 100644 index 0000000..bcd77c2 --- /dev/null +++ b/kadai3-1/int128/game/game.go @@ -0,0 +1,73 @@ +package game + +import ( + "bufio" + "context" + "fmt" + "io" + "os" + "time" +) + +// Game represents a typing game. +type Game struct { + Vocabulary Vocabulary + Timeout time.Duration + Reader io.Reader +} + +// Score represents a game score. +type Score struct { + CorrectWords int + TotalWords int + GiveUp bool +} + +// New returns a new game. +func New(vocabulary Vocabulary, timeout time.Duration) *Game { + return &Game{vocabulary, timeout, os.Stdin} +} + +// Start starts a game and waits for timeout or cancel (ctrl+D). +// If timeout or cancel occurred, this returns the score. +// If any error occurred, this returns the error. +func (g *Game) Start(ctx context.Context) (*Score, error) { + ctx, cancel := context.WithTimeout(ctx, g.Timeout) + defer cancel() + errCh := make(chan error) + defer close(errCh) + score := &Score{} + + go g.scanLines(score, cancel, errCh) + + select { + case <-ctx.Done(): + if err := ctx.Err(); err != context.DeadlineExceeded && err != context.Canceled { + return nil, err + } + return score, nil + case err := <-errCh: + return nil, err + } +} + +func (g *Game) scanLines(score *Score, cancel func(), errCh chan<- error) { + s := bufio.NewScanner(g.Reader) + for { + expected := g.Vocabulary.NextWord() + fmt.Printf("%s\n>> ", expected) + if !s.Scan() { + score.GiveUp = true + cancel() + return + } + if err := s.Err(); err != nil { + errCh <- err + return + } + if expected == s.Text() { + score.CorrectWords++ + } + score.TotalWords++ + } +} diff --git a/kadai3-1/int128/game/game_test.go b/kadai3-1/int128/game/game_test.go new file mode 100644 index 0000000..137fedb --- /dev/null +++ b/kadai3-1/int128/game/game_test.go @@ -0,0 +1,30 @@ +package game + +import ( + "strings" + "testing" + "time" +) + +func TestGameScanLines(t *testing.T) { + v := Vocabulary([]string{"foo"}) + r := strings.NewReader("foo\nfoo\nbar") + g := &Game{v, time.Second, r} + s := &Score{} + c := 0 + cancel := func() { c++ } + g.scanLines(s, cancel, make(chan error)) + + if c != 1 { + t.Errorf("cancel should be called once but %d", c) + } + if s.GiveUp != true { + t.Errorf("s.GiveUp wants true but %v", s.GiveUp) + } + if s.CorrectWords != 2 { + t.Errorf("s.CorrectWords wants 2 but %d", s.CorrectWords) + } + if s.TotalWords != 3 { + t.Errorf("s.TotalWords wants 3 but %d", s.TotalWords) + } +} diff --git a/kadai3-1/int128/game/vocabulary.go b/kadai3-1/int128/game/vocabulary.go new file mode 100644 index 0000000..9406484 --- /dev/null +++ b/kadai3-1/int128/game/vocabulary.go @@ -0,0 +1,17 @@ +package game + +import "math/rand" + +// Vocabulary is a set of words. +type Vocabulary []string + +// NextWord returns a random word. +func (w Vocabulary) NextWord() string { + index := rand.Intn(len(w)) + return w[index] +} + +// Things in https://en.wiktionary.org/wiki/Appendix:Basic_English_word_list +var Things = Vocabulary([]string{ + "angle", "ant", "apple", "arch", "arm", "army", "baby", "bag", "ball", "band", "basin", "basket", "bath", "bed", "bee", "bell", "berry", "bird", "blade", "board", "boat", "bone", "book", "boot", "bottle", "box", "boy", "brain", "brake", "branch", "brick", "bridge", "brush", "bucket", "bulb", "button", "cake", "camera", "card", "cart", "carriage", "cat", "chain", "cheese", "chest", "chin", "church", "circle", "clock", "cloud", "coat", "collar", "comb", "cord", "cow", "cup", "curtain", "cushion", "dog", "door", "drain", "drawer", "dress", "drop", "ear", "egg", "engine", "eye", "face", "farm", "feather", "finger", "fish", "flag", "floor", "fly", "foot", "fork", "fowl", "frame", "garden", "girl", "glove", "goat", "gun", "hair", "hammer", "hand", "hat", "head", "heart", "hook", "horn", "horse", "hospital", "house", "island", "jewel", "kettle", "key", "knee", "knife", "knot", "leaf", "leg", "library", "line", "lip", "lock", "map", "match", "monkey", "moon", "mouth", "muscle", "nail", "neck", "needle", "nerve", "net", "nose", "nut", "office", "orange", "oven", "parcel", "pen", "pencil", "picture", "pig", "pin", "pipe", "plane", "plate", "plough", "pocket", "pot", "potato", "prison", "pump", "rail", "rat", "receipt", "ring", "rod", "roof", "root", "sail", "school", "scissors", "screw", "seed", "sheep", "shelf", "ship", "shirt", "shoe", "skin", "skirt", "snake", "sock", "spade", "sponge", "spoon", "spring", "square", "stamp", "star", "station", "stem", "stick", "stocking", "stomach", "store", "street", "sun", "table", "tail", "thread", "throat", "thumb", "ticket", "toe", "tongue", "tooth", "town", "train", "tray", "tree", "trousers", "umbrella", "wall", "watch", "wheel", "whip", "whistle", "window", "wing", "wire", "worm", +}) diff --git a/kadai3-1/int128/game/vocabulary_test.go b/kadai3-1/int128/game/vocabulary_test.go new file mode 100644 index 0000000..9f49352 --- /dev/null +++ b/kadai3-1/int128/game/vocabulary_test.go @@ -0,0 +1,11 @@ +package game + +import ( + "fmt" +) + +func ExampleVocabulary_NextWord() { + v := Vocabulary([]string{"foo"}) + fmt.Print(v.NextWord()) + // Out: foo +} diff --git a/kadai3-1/int128/main.go b/kadai3-1/int128/main.go new file mode 100644 index 0000000..4a81d19 --- /dev/null +++ b/kadai3-1/int128/main.go @@ -0,0 +1,29 @@ +package main + +import ( + "context" + "fmt" + "log" + "math/rand" + "os" + "time" + + "github.com/gopherdojo/dojo2/kadai3-1/int128/game" +) + +func main() { + rand.Seed(time.Now().UnixNano()) + g := game.New(game.Things, 30*time.Second) + fmt.Fprintf(os.Stderr, "Are you ready? Type words in 30 seconds!\n--\n") + + ctx := context.Background() + score, err := g.Start(ctx) + switch { + case err != nil: + log.Fatal(err) + case score.GiveUp: + fmt.Fprintf(os.Stderr, "\nGive up? You got %d point(s).\n", score.CorrectWords) + default: + fmt.Fprintf(os.Stderr, "\nTime up! You got %d point(s).\n", score.CorrectWords) + } +}