🎨 🔥 优化代码结构

This commit is contained in:
fumiama 2022-02-18 20:15:07 +08:00
parent eaad8beb7b
commit 3838d74cb8
4 changed files with 144 additions and 15972 deletions

2
data

@ -1 +1 @@
Subproject commit 431911b8755799e61f096c7853a05731743f3761 Subproject commit 349cde86290220b8fb3f2bc957d1be58b68c852c

77
plugin_wordle/pack.go Normal file
View File

@ -0,0 +1,77 @@
package wordle
import (
"strconv"
"unsafe"
goBinary "encoding/binary"
"github.com/FloatTech/zbputils/binary"
)
type wordpack [3]byte
func pack(word string) (w wordpack) {
if len(word) != 5 {
panic("word must be 5 letters")
}
r := []rune(word)
for i := range r {
if r[i] < 'k' { // 0-9
r[i] -= 'a' - '0'
} else {
r[i] -= 10
}
}
word = string(r)
n, err := strconv.ParseUint(word, 26, 32)
if err != nil {
panic(err)
}
wt := binary.SelectWriter()
wt.WriteUInt32LE(uint32(n))
copy(w[:], wt.Bytes())
binary.PutWriter(wt)
return
}
func (w wordpack) String() (word string) {
wt := binary.SelectWriter()
wt.Write(w[:])
wt.WriteByte(0)
n := goBinary.LittleEndian.Uint32(wt.Bytes())
binary.PutWriter(wt)
word = strconv.FormatUint(uint64(n), 26)
for len(word) < 5 {
word = "0" + word
}
r := []rune(word)
for i := range r {
if r[i] < 'a' { // 0-9
r[i] += 'a' - '0'
} else {
r[i] += 10
}
}
word = string(r)
return
}
func loadwords(data []byte) (wordpacks []wordpack) {
(*slice)(unsafe.Pointer(&wordpacks)).data = (*slice)(unsafe.Pointer(&data)).data
(*slice)(unsafe.Pointer(&wordpacks)).len = len(data) / 3
(*slice)(unsafe.Pointer(&wordpacks)).cap = (*slice)(unsafe.Pointer(&data)).cap / 3
return
}
// slice is the runtime representation of a slice.
// It cannot be used safely or portably and its representation may
// change in a later release.
//
// Unlike reflect.SliceHeader, its Data field is sufficient to guarantee the
// data it references will not be garbage collected.
type slice struct {
data unsafe.Pointer
len int
cap int
}

View File

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

File diff suppressed because it is too large Load Diff