概要
初めまして。
Golang初心者です。
Gopher道場の課題の「タイピングゲームを作ろう」にて、標準入力を行う際に、挙動がどうなっているかわからないため、どなたかに教えていただきたく、質問させていただきました。
自分が考えたゲームの流れ
ゲーム性としてはおかしいですが、以下の感じで考えました。
名前入力 -> 制限時間入力 -> カウントダウン開始 -> タイピングゲーム
コード
以下ざっくりと現状作ったコードになります。
まだ、上記のタイピングゲームの部分は完成していません。
go
1package main 2 3import ( 4 "bufio" 5 "fmt" 6 "io" 7 "os" 8 "strconv" 9 "time" 10) 11 12// TypingGameを扱う情報 13type GameInfo struct { 14 timeLimit time.Duration 15 score int 16 name string 17} 18 19func main() { 20 // 初期化 21 g := GameInfo{time.Duration(10 * time.Second), 0, "defalut",} 22 stdin := os.Stdin 23 24 // 名前入力 25 done := makeDoneChan() 26 fmt.Print("名前を入力してね!(何も入力しない場合は defalut になります)\n > ") 27 name := input(stdin, done) 28 res := <-name 29 if res != "" { 30 g.name = res 31 } 32 fmt.Println(g.name, "さんようこそ!") 33 // サブルーチンを終了させる 34 close(done) 35 36 // タイムリミットの設定 37 fmt.Print("タイムリミットを設定してね!(正しい値が入力されない場合は 10秒 になります) \n > ") 38 done = makeDoneChan() 39 timeLimit := input(stdin, done) 40 num := <-timeLimit 41 // サブルーチンを終了させる 42 close(done) 43 if v, err := strconv.Atoi(num); err == nil && v > 0 { 44 g.timeLimit = time.Duration(v) 45 } 46 fmt.Printf("タイムリミットは %v です!\n", g.timeLimit) 47 48 49 // カウントダウン開始 50 countdown() 51 52 // ゲーム開始 53 54} 55// goroutine終了用のチャネルを返す 56func makeDoneChan() (chan struct{}) { 57 return make(chan struct{}) 58} 59 60// 3秒間カウントダウンを行うコード 61func countdown() { 62 for i := 3; i > 0; i-- { 63 fmt.Printf("%d ", i) 64 time.Sleep(time.Second) 65 } 66 fmt.Println("Go!") 67} 68 69// func game() { 70// words, err := questions() 71// if err != nil { 72// fmt.Println("エラーが起きたため、ゲームを終了します。") 73// os.Exit(1) 74// } 75 76// // words 77// } 78 79// 自分で作成したtextファイルを読み込む 80func questions() ([]string, error) { 81 var words []string 82 f, err := os.Open("question.txt") 83 if err != nil { 84 return nil, fmt.Errorf("%v", err) 85 } 86 s := bufio.NewScanner(f) 87 for s.Scan() { 88 words = append(words, s.Text()) 89 } 90 return words, nil 91} 92 93func input(r io.Reader, done chan struct{}) <-chan string { 94 ch := make(chan string) 95 go func() { 96 s := bufio.NewScanner(r) 97 defer close(ch) 98 for s.Scan() { 99 select { 100 case <-done: 101 return 102 case ch <- s.Text(): 103 } 104 } 105 }() 106 return ch 107} 108
実行結果とわからない箇所
パターン1と2から、タイムリミット入力時には一度改行しないと、欲しい入力結果を得ることができません。
なぜ、このようなことが起こるのかを教えていただければ幸いです。
よろしくお願い致します。
パターン1
bash
1名前を入力してね!(何も入力しない場合は defalut になります) 2 > nanashi 3nanashi さんようこそ! 4タイムリミットを設定してね!(正しい値が入力されない場合は 10秒 になります) 5 > 20 6 7タイムリミットは 10s です! 83 2 1 Go!
パターン2
bash
1名前を入力してね!(何も入力しない場合は defalut になります) 2 > nanashi 3nanashi さんようこそ! 4タイムリミットを設定してね!(正しい値が入力されない場合は 10秒 になります) 5 > 620 7タイムリミットは 20s です! 83 2 1 Go!
最後に
go始めたばかりなんで、かなりコードが汚いと思いますが、その部分は見逃していただきたいです。
また、きたないと思う部分も同時に指摘していただけると助かります。
> なぜ、このようなことが起こるのか
input 関数内にある goroutine で、
case ch <- s.Text():
としていますが、この直後に for ループに戻って s.Scan() が実行されるので入力待ちになっています。
そして、その時点で何かを入力すると done が受信されて channel が閉じます(goroutine が終了)。
この場合、読み込むのは一行なので channel に入力内容を送信したら return する必要があります(goroutine を終了させる)。
case ch <- s.Text():
return
s.Scanの挙動がわからなかったのですが、melianさんのコメントを見て、スッキリしました!
ありがとうございます!
解決できたので、ぜひベストアンサーに選ばせてください!
ありがとうございます。回答に転記しました。
回答1件
あなたの回答
tips
プレビュー