diff --git a/kadai3/onoyama/README.md b/kadai3/onoyama/README.md new file mode 100644 index 0000000..07bd3b2 --- /dev/null +++ b/kadai3/onoyama/README.md @@ -0,0 +1,51 @@ +# 解説 (Gopher道場 課題 3-1) + +## コマンド +./Questions +(上記コマンドを実行後、画面の指示に従ってください) + +## ライブラリ + +### PACKAGE DOCUMENTATION + +package utils + import "./utils" + +### FUNCTIONS + +func CheckAnswer(question Question, answer string) bool + 回答があっているか確認して結果を表示 + +func InputChannel(r io.Reader) <-chan string + 入力用のチャネル + +func LoadQuestions(path string) []Question + JSONファイルから問題をロード + +func ShowResult(result Result) + 正解の合計を表示 + +func TimeoutChannel(timeout int) <-chan bool + タイムアウト用のチャネル + +### TYPES + +type Question struct { +  Question string ``json:"question"`` +  Answer string ``json:"answer"`` +} + 質問格納用struct + +func GetQuestion(questions []Question) Question + 問題の配列からランダムに一問抽出 + +func PopQuestion(questions []Question, q_count int) Question + 問題の表示 + +type Result struct { +  Count int +  Correct int +} + 回答結果の格納用struct + + diff --git a/kadai3/onoyama/json/questions.json b/kadai3/onoyama/json/questions.json new file mode 100644 index 0000000..9afbc36 --- /dev/null +++ b/kadai3/onoyama/json/questions.json @@ -0,0 +1,5 @@ +[ + {"question": "test1", "answer": "テスト1"}, + {"question": "test2", "answer": "テスト2"}, + {"question": "test3", "answer": "テスト3"} +] diff --git a/kadai3/onoyama/json/words.json b/kadai3/onoyama/json/words.json new file mode 100644 index 0000000..64b4f69 --- /dev/null +++ b/kadai3/onoyama/json/words.json @@ -0,0 +1 @@ +[{"question": "ability", "answer": "能力"}, {"question": "able", "answer": "することができる"}, {"question": "about", "answer": "について"}, {"question": "above", "answer": "の上に"}, {"question": "absolute", "answer": "絶対的な"}, {"question": "absolutely", "answer": "完全に"}, {"question": "accept", "answer": "受け入れる"}, {"question": "acceptable", "answer": "許容できる"}, {"question": "account", "answer": "口座"}, {"question": "across", "answer": "横切って"}, {"question": "act", "answer": "行動する"}, {"question": "action", "answer": "行動"}, {"question": "actual", "answer": "実際の"}, {"question": "actually", "answer": "実際は"}, {"question": "add", "answer": "加える"}, {"question": "addition", "answer": "追加"}, {"question": "additional", "answer": "追加の"}, {"question": "address", "answer": "住所"}, {"question": "admit", "answer": "認める"}, {"question": "advertise", "answer": "宣伝する"}, {"question": "advertisement", "answer": "広告"}, {"question": "afford", "answer": "する余裕がある"}, {"question": "afraid", "answer": "怖がって"}, {"question": "after", "answer": "後で"}, {"question": "afternoon", "answer": "午後"}, {"question": "again", "answer": "再び"}, {"question": "against", "answer": "に反対して"}, {"question": "age", "answer": "年齢"}, {"question": "ago", "answer": "前に"}, {"question": "agree", "answer": "同意する"}, {"question": "agreement", "answer": "合意"}, {"question": "ahead", "answer": "前方に"}, {"question": "air", "answer": "空気"}, {"question": "all", "answer": "すべて"}, {"question": "allow", "answer": "許可する"}, {"question": "almost", "answer": "ほとんど"}, {"question": "alone", "answer": "独りで"}, {"question": "along", "answer": "に沿って"}, {"question": "already", "answer": "すでに"}, {"question": "alright", "answer": "申し分ない"}, {"question": "also", "answer": "もまた"}, {"question": "although", "answer": "だけれども"}, {"question": "always", "answer": "いつも"}, {"question": "amaze", "answer": "びっくりさせる"}, {"question": "among", "answer": "の間に"}, {"question": "amount", "answer": "量"}, {"question": "and", "answer": "と"}, {"question": "angry", "answer": "腹を立てている"}, {"question": "animal", "answer": "動物"}, {"question": "another", "answer": "もう一つの"}, {"question": "answer", "answer": "答え"}, {"question": "any", "answer": "何か"}, {"question": "anybody", "answer": "誰か"}, {"question": "anymore", "answer": "もはやこれ以上しない"}, {"question": "anyone", "answer": "誰か"}, {"question": "anything", "answer": "何か"}, {"question": "anyway", "answer": "とにかく"}, {"question": "anywhere", "answer": "どこかへ"}, {"question": "apart", "answer": "離れて"}, {"question": "apparent", "answer": "明白な"}, {"question": "apparently", "answer": "明白に"}, {"question": "appear", "answer": "現れる"}, {"question": "appearance", "answer": "外観"}, {"question": "area", "answer": "地域;場所"}, {"question": "arm", "answer": "腕"}, {"question": "around", "answer": "の周りに"}, {"question": "arrival", "answer": "到着"}, {"question": "arrive", "answer": "到着する"}, {"question": "art", "answer": "芸術"}, {"question": "artist", "answer": "芸術家"}, {"question": "as", "answer": "同じぐらい"}, {"question": "ashamed", "answer": "恥じている"}, {"question": "ask", "answer": "尋ねる"}, {"question": "at", "answer": "の所で"}, {"question": "autumn", "answer": "秋"}, {"question": "aware", "answer": "気づいている"}, {"question": "awareness", "answer": "認識"}, {"question": "away", "answer": "離れて"}, {"question": "awful", "answer": "恐ろしい"}, {"question": "baby", "answer": "赤ん坊"}, {"question": "back", "answer": "もとに戻って"}, {"question": "bad", "answer": "悪い"}, {"question": "badly", "answer": "悪く"}, {"question": "bag", "answer": "かばん"}, {"question": "ball", "answer": "ボール"}, {"question": "bank", "answer": "銀行"}, {"question": "bar", "answer": "妨げる"}, {"question": "basic", "answer": "基本的な"}, {"question": "basically", "answer": "基本的には"}, {"question": "bath", "answer": "浴槽"}, {"question": "be", "answer": "である"}, {"question": "beach", "answer": "砂浜"}, {"question": "bear", "answer": "熊"}, {"question": "beat", "answer": "打ち負かす"}, {"question": "beautiful", "answer": "美しい"}, {"question": "beauty", "answer": "美人"}, {"question": "because", "answer": "だから"}, {"question": "become", "answer": "になる"}, {"question": "bed", "answer": "ベッド"}, {"question": "before", "answer": "以前に"}, {"question": "begin", "answer": "始まる"}, {"question": "behind", "answer": "の後ろに"}, {"question": "believe", "answer": "信じる"}, {"question": "below", "answer": "下に"}, {"question": "beneath", "answer": "の下に"}, {"question": "beside", "answer": "の近くに"}, {"question": "besides", "answer": "のほか"}, {"question": "bet", "answer": "賭ける"}, {"question": "between", "answer": "の間に"}, {"question": "beyond", "answer": "の向こうに"}, {"question": "big", "answer": "大きい"}, {"question": "bill", "answer": "請求書"}, {"question": "billion", "answer": "十億"}, {"question": "bird", "answer": "鳥"}, {"question": "birth", "answer": "誕生"}, {"question": "bit", "answer": "少し"}, {"question": "black", "answer": "黒い"}, {"question": "blood", "answer": "血"}, {"question": "bloody", "answer": "血がついた"}, {"question": "blow", "answer": "吹き飛ばす"}, {"question": "blue", "answer": "青い"}, {"question": "boat", "answer": "ボート"}, {"question": "body", "answer": "体"}, {"question": "bone", "answer": "骨"}, {"question": "book", "answer": "本"}, {"question": "both", "answer": "両方"}, {"question": "bother", "answer": "悩ませる"}, {"question": "bottle", "answer": "ボトル"}, {"question": "bottom", "answer": "底"}, {"question": "box", "answer": "箱"}, {"question": "boy", "answer": "男の子"}, {"question": "bread", "answer": "パン"}, {"question": "break", "answer": "壊す"}, {"question": "breakfast", "answer": "朝食"}, {"question": "breath", "answer": "息"}, {"question": "bright", "answer": "明るい;"}, {"question": "bring", "answer": "持ってくる"}, {"question": "brother", "answer": "兄弟"}, {"question": "brown", "answer": "茶色の"}, {"question": "build", "answer": "建てる"}, {"question": "burn", "answer": "燃える"}, {"question": "bus", "answer": "バス"}, {"question": "business", "answer": "会社"}, {"question": "busy", "answer": "忙しい"}, {"question": "but", "answer": "でも"}, {"question": "buy", "answer": "買う"}, {"question": "buyer", "answer": "買い手"}, {"question": "by", "answer": "の近くに"}, {"question": "cake", "answer": "ケーキ"}, {"question": "camp", "answer": "キャンプ"}, {"question": "can", "answer": "することができる"}, {"question": "car", "answer": "車"}, {"question": "card", "answer": "カード"}, {"question": "care", "answer": "世話をする"}, {"question": "careful", "answer": "注意深い"}, {"question": "carefully", "answer": "注意深く"}, {"question": "carry", "answer": "運ぶ"}, {"question": "case", "answer": "場合"}, {"question": "cat", "answer": "猫"}, {"question": "catch", "answer": "捕まえる"}, {"question": "cause", "answer": "の原因となる"}, {"question": "center", "answer": "中心"}, {"question": "central", "answer": "中央の"}, {"question": "certain", "answer": "確信している"}, {"question": "certainly", "answer": "確かに"}, {"question": "chair", "answer": "椅子"}, {"question": "chance", "answer": "機会"}, {"question": "change", "answer": "変える"}, {"question": "charge", "answer": "請求する"}, {"question": "cheap", "answer": "安い"}, {"question": "check", "answer": "確認する"}, {"question": "chicken", "answer": "ニワトリ"}, {"question": "child", "answer": "子ども"}, {"question": "childhood", "answer": "子供時代"}, {"question": "chip", "answer": "削り取る"}, {"question": "choice", "answer": "選択"}, {"question": "choose", "answer": "選ぶ"}, {"question": "church", "answer": "教会"}, {"question": "city", "answer": "市"}, {"question": "class", "answer": "階級"}, {"question": "clean", "answer": "きれいな"}, {"question": "clear", "answer": "はっきりした"}, {"question": "clearly", "answer": "はっきりと"}, {"question": "climb", "answer": "登る"}, {"question": "clock", "answer": "時計"}, {"question": "close", "answer": "近い"}, {"question": "closely", "answer": "密接に"}, {"question": "clothes", "answer": "衣服"}, {"question": "clothing", "answer": "衣類"}, {"question": "club", "answer": "クラブ"}, {"question": "coat", "answer": "上着"}, {"question": "coffee", "answer": "コーヒー"}, {"question": "cold", "answer": "冷たい"}, {"question": "collect", "answer": "集める"}, {"question": "collection", "answer": "収集物"}, {"question": "college", "answer": "大学"}, {"question": "color", "answer": "色"}, {"question": "come", "answer": "行く"}, {"question": "comfort", "answer": "快適さ"}] diff --git a/kadai3/onoyama/main.go b/kadai3/onoyama/main.go new file mode 100644 index 0000000..5b9a8cd --- /dev/null +++ b/kadai3/onoyama/main.go @@ -0,0 +1,47 @@ +package main + +import ( + "./utils" + "fmt" + "os" +) + +// 問題と解答のリスト +var json_path = "./json/words.json" + +// タイムアウト設定 +var timeout_seconds = 60 + +func main() { + var question utils.Question + questions := utils.LoadQuestions(json_path) + questioning := false + results := utils.Result{0, 0} + + input_ch := utils.InputChannel(os.Stdin) + timeout_ch := utils.TimeoutChannel(timeout_seconds) + + fmt.Printf("制限時間は、%d秒です。\n", timeout_seconds) + + for { + select { + // 回答があっているかの確認 + case answer := <-input_ch: + if result := utils.CheckAnswer(question, answer); result == true { + results.Correct++ + } + questioning = false + case <-timeout_ch: + // タイムアウトで結果を表示 + utils.ShowResult(results) + os.Exit(1) + default: + //質問がされていない場合は問題を出力 + if !questioning { + results.Count++ + question = utils.PopQuestion(questions, results.Count) + questioning = true + } + } + } +} diff --git a/kadai3/onoyama/utils/input_utils.go b/kadai3/onoyama/utils/input_utils.go new file mode 100644 index 0000000..2a7d5b1 --- /dev/null +++ b/kadai3/onoyama/utils/input_utils.go @@ -0,0 +1,31 @@ +package utils + +import ( + "bufio" + "io" + "time" +) + +// 入力用のチャネル +func InputChannel(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 TimeoutChannel(timeout int) <-chan bool { + ch := make(chan bool, 1) + go func() { + time.Sleep(time.Duration(timeout) * time.Second) + ch <- true + close(ch) + }() + return ch +} diff --git a/kadai3/onoyama/utils/question_utils.go b/kadai3/onoyama/utils/question_utils.go new file mode 100644 index 0000000..1b0f65e --- /dev/null +++ b/kadai3/onoyama/utils/question_utils.go @@ -0,0 +1,67 @@ +package utils + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "math/rand" + "time" +) + +// 質問格納用struct +type Question struct { + Question string `json:"question"` + Answer string `json:"answer"` +} + +// 回答結果の格納用struct +type Result struct { + Count int + Correct int +} + +// JSONファイルから問題をロード +func LoadQuestions(path string) []Question { + bytes, err := ioutil.ReadFile(path) + if err != nil { + log.Fatal(err) + } + var questions []Question + if err := json.Unmarshal(bytes, &questions); err != nil { + log.Fatal(err) + } + return questions +} + +// 問題の配列からランダムに一問抽出 +func GetQuestion(questions []Question) Question { + rand.Seed(time.Now().UnixNano()) + rand_index := rand.Intn(len(questions)) + return questions[rand_index] +} + +// 問題の表示 +func PopQuestion(questions []Question, q_count int) Question { + question := GetQuestion(questions) + fmt.Printf("Q%d \"%s\" は日本語で? : ", q_count, question.Question) + return question +} + +// 回答があっているか確認して結果を表示 +func CheckAnswer(question Question, answer string) bool { + correct := false + if answer == question.Answer { + fmt.Printf("正解です!\n") + correct = true + } else { + fmt.Printf("不正解です。 正解は、\"%s\" です。\n", question.Answer) + } + return correct +} + +// 正解の合計を表示 +func ShowResult(result Result) { + fmt.Printf("\n!!! タイムオーバーです。!!!\n") + fmt.Printf("問題%d問中、正解は%d問でした。\n", result.Count, result.Correct) +} diff --git a/kadai3/onoyama/utils/question_utils_test.go b/kadai3/onoyama/utils/question_utils_test.go new file mode 100644 index 0000000..3bf3140 --- /dev/null +++ b/kadai3/onoyama/utils/question_utils_test.go @@ -0,0 +1,142 @@ +package utils + +import ( + "bytes" + "fmt" + "io" + "os" + "testing" +) + +// テスト問題用のJSONファイル +var test_json_path = "../json/questions.json" +var questions []Question + +// テスト用問題の解答 +var expect_questions = []Question{ + Question{"test1", "テスト1"}, + Question{"test2", "テスト2"}, + Question{"test3", "テスト3"}, +} + +// 問題のロードのテスト +func TestLoadQuestions(t *testing.T) { + questions = LoadQuestions(test_json_path) + expect_len := 3 + if actual_len := len(questions); expect_len != actual_len { + t.Error(`expect="%s" actual="%s"`, expect_len, actual_len) + } + for i, question := range questions { + if expect_questions[i] != question { + t.Error(`expect="%s" actual="%s"`, expect_questions[i], question) + } + } +} + +// 問題の解答が正しいかテスト +func TestGetQuestion(t *testing.T) { + question := GetQuestion(questions) + is_question := false + for _, expect := range expect_questions { + if question.Question == expect.Question { + if question.Answer != expect.Answer { + t.Error(`expect="%s" actual="%s"`, expect.Answer, question.Answer) + } + is_question = true + } + } + if !is_question { + t.Error(`Invalid question : "%v"`, question) + } +} + +// 問題の表示が正しいかテスト +func TestPopQuestion(t *testing.T) { + out := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + expect_question := PopQuestion(questions, 5) + expect := fmt.Sprintf("Q%d \"%s\" は日本語で? : ", 5, expect_question.Question) + outC := make(chan string) + go func() { + var buf bytes.Buffer + io.Copy(&buf, r) + outC <- buf.String() + }() + w.Close() + os.Stdout = out + actual := <-outC + + if actual != expect { + t.Errorf(`expect="%s" actual="%s"`, expect, actual) + } +} + +// 回答が正解の場合の表示のテスト +func TestCheckAnswer1(t *testing.T) { + question := GetQuestion(questions) + expect := fmt.Sprintf("正解です!\n") + out := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + CheckAnswer(question, question.Answer) + outC := make(chan string) + go func() { + var buf bytes.Buffer + io.Copy(&buf, r) + outC <- buf.String() + }() + w.Close() + os.Stdout = out + actual := <-outC + + if actual != expect { + t.Errorf(`expect="%s" actual="%s"`, expect, actual) + } +} + +// 回答が不正解の場合のテスト +func TestCheckAnswer2(t *testing.T) { + question := GetQuestion(questions) + expect := fmt.Sprintf("不正解です。 正解は、\"%s\" です。\n", question.Answer) + out := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + CheckAnswer(question, "xxxxxxxxxxxx") + outC := make(chan string) + go func() { + var buf bytes.Buffer + io.Copy(&buf, r) + outC <- buf.String() + }() + w.Close() + os.Stdout = out + actual := <-outC + + if actual != expect { + t.Errorf(`expect="%s" actual="%s"`, expect, actual) + } +} + +// タイムアウト後の結果表示のテスト +func TestShowResult(t *testing.T) { + expect_result := Result{10, 5} + expect := fmt.Sprintf("\n!!! タイムオーバーです。!!!\n問題%d問中、正解は%d問でした。\n", expect_result.Count, expect_result.Correct) + out := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + ShowResult(expect_result) + outC := make(chan string) + go func() { + var buf bytes.Buffer + io.Copy(&buf, r) + outC <- buf.String() + }() + w.Close() + os.Stdout = out + actual := <-outC + + if actual != expect { + t.Errorf(`expect="%s" actual="%s"`, expect, actual) + } +}