ZeroBot-Plugin/plugin_wordle/wordle.go
2022-02-18 20:15:07 +08:00

161 lines
4.0 KiB
Go

// Package wordle 猜单词
package wordle
import (
"errors"
"image/color"
"math/rand"
"strings"
"time"
"github.com/FloatTech/zbputils/binary"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/control/order"
"github.com/FloatTech/zbputils/ctxext"
"github.com/FloatTech/zbputils/file"
"github.com/FloatTech/zbputils/img/writer"
"github.com/fogleman/gg"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
var (
errLengthNotEnough = errors.New("length not enough")
errUnknownWord = errors.New("unknown word")
errTimesRunOut = errors.New("times run out")
)
const (
match = iota
exist
notexist
undone
)
var colors = [...]color.RGBA{
{125, 166, 108, 255},
{199, 183, 96, 255},
{123, 123, 123, 255},
{219, 219, 219, 255},
}
var words []wordpack
func init() {
en := control.Register("wordle", order.AcquirePrio(), &control.Options{
DisableOnDefault: false,
Help: "猜单词\n" +
"- 开始猜单词",
PublicDataFolder: "Wordle",
})
go func() {
data, err := file.GetLazyData(en.DataFolder()+"words.bin", true, true)
if err != nil {
panic(err)
}
words = loadwords(data)
}()
en.OnFullMatch("猜单词").SetBlock(true).Limit(ctxext.LimitByUser).
Handle(func(ctx *zero.Ctx) {
game := newWordleGame()
_, img, _ := game("")
ctx.SendChain(message.Image("base64://"+binary.BytesToString(img)), message.Text("请发送单词"))
// 没有图片就索取
next := zero.NewFutureEvent("message", 999, false, zero.RegexRule(`^[A-Z]{5}$|^[a-z]{5}$`))
recv, cancel := next.Repeat()
var msg message.Message
defer func() {
cancel()
ctx.Send(msg)
}()
for {
select {
case <-time.After(time.Second * 120):
return
case e := <-recv:
win, img, err := game(e.Message.String())
msg = []message.MessageSegment{message.Image("base64://" + binary.BytesToString(img))}
switch err {
case nil:
if win {
msg = append(msg, message.Text("你赢了"))
return
}
case errLengthNotEnough:
msg = append(msg, message.Text("单词长度错误"))
case errUnknownWord:
msg = append(msg, message.Text("不存在这样的单词"))
case errTimesRunOut:
msg = append(msg, message.Text("你输了"))
return
}
ctx.Send(msg)
}
}
})
}
func newWordleGame() func(string) (bool, []byte, error) {
onhandpack := words[rand.Intn(len(words))]
onhand := onhandpack.String()
record := make([]string, 0, len(onhand)+1)
return func(s string) (win bool, base64Image []byte, err error) {
if s != "" {
s = strings.ToLower(s)
sp := pack(s)
if onhandpack == sp {
win = true
} else {
if len(s) != len(onhand) {
err = errLengthNotEnough
return
}
i := 0
for ; i < len(words); i++ {
if words[i] == sp {
break
}
}
if i >= len(words) || words[i] != sp {
err = errUnknownWord
return
}
}
if len(record) >= cap(record) {
err = errTimesRunOut
return
}
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(colors[match])
case strings.IndexByte(onhand, record[i][j]) != -1:
ctx.SetColor(colors[exist])
default:
ctx.SetColor(colors[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(colors[undone])
ctx.Stroke()
}
}
}
base64Image, err = writer.ToBase64(ctx.Image())
return
}
}