ZeroBot-Plugin/plugin_wordle/wordle.go
Kanri eaad8beb7b
增加插件 猜单词 (#129)
*  增加插件 猜单词

* Update wordle.go

* Update wordle.go

* Update wordle.go

* Update wordle.go

Co-authored-by: 源文雨 <41315874+fumiama@users.noreply.github.com>
2022-02-18 19:08:02 +08:00

142 lines
3.7 KiB
Go

// Package wordle 猜单词
package wordle
import (
"bytes"
"errors"
"image/color"
"image/png"
"math/rand"
"sort"
"strings"
"time"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/control/order"
"github.com/FloatTech/zbputils/ctxext"
"github.com/fogleman/gg"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
var errLengthNotEnough = errors.New("length not enough")
var errUnknownWord = errors.New("unknown word")
var errTimesRunOut = errors.New("times run out")
const (
match = iota
exist
notexist
undone
)
var colormap = map[int]color.RGBA{
0: {125, 166, 108, 255},
1: {199, 183, 96, 255},
2: {123, 123, 123, 255},
3: {219, 219, 219, 255},
}
func init() {
sort.Strings(words)
}
func init() {
control.Register("wordle", order.AcquirePrio(), &control.Options{
DisableOnDefault: false,
Help: "猜单词\n" +
"- 开始猜单词",
}).OnFullMatch("猜单词").SetBlock(true).Limit(ctxext.LimitByUser).
Handle(func(ctx *zero.Ctx) {
game := wordle(words)
_, img, _ := game("")
ctx.SendChain(message.ImageBytes(img), message.Text("请发送单词"))
// 没有图片就索取
next := zero.NewFutureEvent("message", 999, false, zero.RegexRule(`^[A-Z]{5}$|^[a-z]{5}$`))
recv, cancel := next.Repeat()
defer cancel()
for {
select {
case <-time.After(time.Second * 120):
return
case e := <-recv:
win, img, err := game(e.Message.String())
if err == errLengthNotEnough {
ctx.SendChain(message.ImageBytes(img), message.Text("单词长度错误"))
}
if err == errUnknownWord {
ctx.SendChain(message.ImageBytes(img), message.Text("不存在这样的单词"))
}
if win {
ctx.SendChain(message.ImageBytes(img), message.Text("你赢了"))
return
}
if err == errTimesRunOut {
ctx.SendChain(message.ImageBytes(img), message.Text("你输了"))
return
}
ctx.SendChain(message.ImageBytes(img))
}
}
})
}
func wordle(words []string) func(string) (bool, []byte, error) {
rand.Seed(time.Now().UnixMilli())
index := rand.Intn(len(words))
onhand := words[index]
record := make([]string, 0, len(onhand)+1)
return func(s string) (win bool, image []byte, err error) {
if s != "" {
s = strings.ToLower(s)
if onhand == s {
win = true
} else {
if len(s) != len(onhand) {
err = errLengthNotEnough
return
}
i := sort.SearchStrings(words, s)
if i >= len(words) || words[i] != s {
err = errUnknownWord
return
}
}
if len(record) >= cap(record) {
err = errTimesRunOut
}
record = append(record, s)
}
var side = 20
ctx := gg.NewContext((side+2)*5+26, (side+2)*6+26)
ctx.SetColor(color.RGBA{255, 255, 255, 255})
ctx.Clear()
for i := 0; i < len(onhand)+1; i++ {
for j := 0; j < len(onhand); j++ {
if len(record) > i {
ctx.DrawRectangle(float64(10+j*(side+4)), float64(10+i*(side+4)), float64(side), float64(side))
switch {
case record[i][j] == onhand[j]:
ctx.SetColor(colormap[match])
case strings.IndexByte(onhand, record[i][j]) != -1:
ctx.SetColor(colormap[exist])
default:
ctx.SetColor(colormap[notexist])
}
ctx.Fill()
ctx.SetColor(color.RGBA{255, 255, 255, 255})
ctx.DrawString(strings.ToUpper(string(record[i][j])), float64(10+j*(side+4)+7), float64(10+i*(side+4)+15))
} else {
ctx.DrawRectangle(float64(10+j*(side+4)+1), float64(10+i*(side+4)+1), float64(side-2), float64(side-2))
ctx.SetLineWidth(1)
ctx.SetColor(colormap[undone])
ctx.Stroke()
}
}
}
buf := bytes.NewBuffer(make([]byte, 0))
_ = png.Encode(buf, ctx.Image())
return win, buf.Bytes(), err
}
}