// vi:ts=4:sts=4:sw=4:noet:tw=72 package modules import ( "bufio" "flokatirc/util" "fmt" "math/rand" "os" "regexp" "strings" "time" "github.com/sorcix/irc" "code.dnix.de/an/xlog" ) type quizQuestion struct { category, question, answer, regexp, level string } var ( quizCtrlCh = make(chan string, 1024) quizAnswerCh = make(chan *irc.Message, 1024) quizQuestions []quizQuestion quizQuestionValues = map[string]int{"extreme": 5, "hard": 4, "normal": 3, "easy": 2, "baby": 1} ) func init() { MsgFuncs["quiz"] = quizHandleMessage RunFuncs["quiz"] = quiz rand.Seed(time.Now().Unix()) } func quizHandleMessage(m *irc.Message) { tok := strings.Split(m.Trailing, " ") if len(tok) < 1 { return } switch tok[0] { case "!quiz", "!quizstart": quizCtrlCh <- "start" break case "!quizstop": quizCtrlCh <- "stop" break default: quizAnswerCh <- m break } } func quiz() { time.Sleep(5 * time.Second) SayCh <- fmt.Sprintf("%s\nquiz mod test. !quizstart to start, !quizstop to end.", "*") for { time.Sleep(1 * time.Millisecond) select { case s := <-quizCtrlCh: if s == "start" { quizRun() } default: break } } } func quizRun() { quizQuestions = quizLoadQuestions("questions.txt") ranklist := make(map[string]int) SayCh <- fmt.Sprintf("%s\nQuiz gestartet.", "*") for { solved, cont, solver, gain := quizAskQuestion() if !cont { SayCh <- fmt.Sprintf("%s\nQuiz beendet.", "*") return } else { if solved { if _, exists := ranklist[solver]; exists { ranklist[solver] += gain } else { ranklist[solver] = gain } if ranklist[solver] > 1000 { bold := byte(0x02) solver = string(bold) + solver + string(bold) SayCh <- fmt.Sprintf("%s\n%s gewinnt!", "*", solver) quizPrintRanklist(ranklist) SayCh <- fmt.Sprintf("%s\nQuiz beendet.", "*") return } } } quizPrintRanklist(ranklist) } } func quizPrintRanklist(ranklist map[string]int) { if len(ranklist) == 0 { return } ranklistTmp := make(map[string]int, 0) for k, v := range ranklist { ranklistTmp[k] = v } SayCh <- fmt.Sprintf("%s\nAktueller Punktestand:", "*") for { maxk := "" maxv := -1 if len(ranklistTmp) == 0 { break } for k, v := range ranklistTmp { if v > maxv { maxv = v maxk = k } } delete(ranklistTmp, maxk) SayCh <- fmt.Sprintf("%s\n%s: %d", "*", maxk, maxv) } } func quizAskQuestion() (bool, bool, string, int) { q := quizQuestions[rand.Intn(len(quizQuestions))] SayCh <- fmt.Sprintf("%s\nEine Frage aus der Kategorie \"%s\" (%s):", "*", q.category, q.level) SayCh <- fmt.Sprintf("%s\n>>> %s <<<", "*", q.question) solved, cont, solver, gain := quizWaitForAnswer(q) if !solved { SayCh <- fmt.Sprintf("%s\nDie richtige Antwort wäre gewesen:", "*") SayCh <- fmt.Sprintf("%s\n%s [%s]", "*", q.answer, q.regexp) } time.Sleep(5 * time.Second) return solved, cont, solver, gain } func quizWaitForAnswer(q quizQuestion) (bool, bool, string, int) { i := 0 haveAnswer := false timer := time.Now().Unix() timeHintCh := make(chan bool) go func(chan bool) { for i := 0; i < 4; i++ { time.Sleep(15 * time.Second) timeHintCh <- true } }(timeHintCh) for { select { case m := <-quizAnswerCh: haveAnswer = true points := 10 - ((time.Now().Unix() - timer) / 10) if points < 1 { points = 1 } re, err := regexp.Compile(q.regexp) if err != nil { xlog.Error(err.Error()) return false, false, "", 0 } if re.MatchString(strings.ToLower(util.ReplaceUmlauts(m.Trailing))) { bold := byte(0x02) solver := strings.Split(m.Prefix.String(), "!")[0] solver = string(bold) + solver + string(bold) value := quizQuestionValues[q.level] gain := int(points) * value SayCh <- fmt.Sprintf("%s\n%s hat die Frage korrekt beantwortet und erhält dafür %d (%d * %d) Punkte.", "*", solver, gain, points, value) return true, true, solver, gain } break case s := <-quizCtrlCh: if s == "stop" { return false, false, "", 0 } break case <-timeHintCh: i++ if i > 3 { if haveAnswer { SayCh <- fmt.Sprintf("%s\nNiemand konnte die Frage korrekt beantworten.", "*") return false, true, "", 0 } else { SayCh <- fmt.Sprintf("%s\nNiemand hat auf die Frage geantwortet.", "*") return false, false, "", 0 } } else { quizGiveHint(q, i) } } } } func quizGiveHint(q quizQuestion, n int) { var hint string haveHint := false for { hint = "" for i := 0; i < len(q.answer); i++ { if string(q.answer[i]) == " " { hint += " " } else { if rand.Intn(10) < 2 { haveHint = true hint += string(q.answer[i]) } else { hint += "-" } } } if haveHint { break } } SayCh <- fmt.Sprintf("%s\nTipp: %s", "*", hint) } func quizLoadQuestions(path string) []quizQuestion { file, err := os.Open(path) if err != nil { xlog.Fatal(err.Error()) } defer file.Close() questions := make([]quizQuestion, 0) q := quizQuestion{"", "", "", "", ""} scanner := bufio.NewScanner(file) for scanner.Scan() { line := scanner.Text() if len(line) == 0 || line[0] == '#' || line[0] == '\n' { if q.category != "" { questions = append(questions, q) q = quizQuestion{"", "", "", "", "normal"} } } else { tok := strings.Split(line, ":: ") switch tok[0] { case "Category": q.category = tok[1] break case "Question": q.question = tok[1] break case "Answer": q.answer = strings.Replace(util.ReplaceUmlauts(tok[1]), "#", "", -1) if q.regexp == "" { regexp := strings.Split(strings.ToLower(util.ReplaceUmlauts(tok[1])), "#") if len(regexp) > 1 { q.regexp = regexp[1] } else { q.regexp = regexp[0] } } break case "Regexp": q.regexp = util.ReplaceUmlauts(strings.ToLower(tok[1])) break case "Level": q.level = tok[1] break } } } if err := scanner.Err(); err != nil { xlog.Fatal(err.Error()) } return questions }