Initial commit
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
games/*
|
||||||
|
.bashrc
|
||||||
|
chessbot.log
|
||||||
|
chessbot
|
||||||
3
README.md
Normal file
3
README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Required for `go build`
|
||||||
|
export CGO_CFLAGS_ALLOW='-l.*'
|
||||||
|
export CGO_LDFLAGS_ALLOW='-I.*'
|
||||||
140
chess.go
Normal file
140
chess.go
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"image/color"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/notnil/chess"
|
||||||
|
"github.com/notnil/chess/image"
|
||||||
|
"samhofi.us/x/keybase/v2/types/chat1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func chessCommand(m chat1.MsgSummary) {
|
||||||
|
defer log.PanicSafe()
|
||||||
|
defer saveConfig()
|
||||||
|
convID := string(m.ConvID)
|
||||||
|
parts := strings.Split(m.Content.Text.Body, " ")
|
||||||
|
if g, ok := config.Games[convID]; ok {
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if parts[1] == "show" {
|
||||||
|
go showBoard(m, &g)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
submitMove(m, &g)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(parts) < 3 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
coin := r.Intn(100)
|
||||||
|
log.LogInfo(fmt.Sprintf("Coin toss resulted in %+v\n", coin))
|
||||||
|
var game Game
|
||||||
|
if coin%2 == 0 {
|
||||||
|
if len(parts) == 4 && parts[3] == "heads" {
|
||||||
|
game.White = m.Sender.Username
|
||||||
|
game.Black = strings.Replace(parts[2], "@", "", -1)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
game.Black = m.Sender.Username
|
||||||
|
game.White = strings.Replace(parts[2], "@", "", -1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if len(parts) == 4 && parts[3] == "heads" {
|
||||||
|
game.Black = m.Sender.Username
|
||||||
|
game.White = strings.Replace(parts[2], "@", "", -1)
|
||||||
|
} else {
|
||||||
|
game.White = m.Sender.Username
|
||||||
|
game.Black = strings.Replace(parts[2], "@", "", -1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.LogDebug("There are currently %+v games.\n", len(config.Games))
|
||||||
|
g := chess.NewGame()
|
||||||
|
game.Game = g
|
||||||
|
game.ConvID = convID
|
||||||
|
game.Move = true
|
||||||
|
game.StartTime = time.Now()
|
||||||
|
config.Games[convID] = game
|
||||||
|
k.SendMessageByConvID(m.ConvID, fmt.Sprintf("I have created a new game for this conversation @%+v is playing as White, @%+v is playing as Black.", game.White, game.Black))
|
||||||
|
k.SendMessageByConvID(m.ConvID, fmt.Sprintf("Possible commands are `@chessbot show` to show the board, or `@chessbot forfeit`. Otherwise I will be expecting a move in Algebraic Notation."))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func showBoard(m chat1.MsgSummary, g *Game) {
|
||||||
|
defer log.PanicSafe()
|
||||||
|
// create file
|
||||||
|
f, err := os.Create(fmt.Sprintf("/home/chessbot/chessbot/games/%+v.svg", m.ConvID))
|
||||||
|
if err != nil {
|
||||||
|
log.LogError(fmt.Sprintf("Error in ShowBoard os.Create: %+v\n", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// create board position
|
||||||
|
fenStr := g.Game.Position().String()
|
||||||
|
pos := &chess.Position{}
|
||||||
|
if err := pos.UnmarshalText([]byte(fenStr)); err != nil {
|
||||||
|
log.LogError(fmt.Sprintf("Error in ShowBoard pos.UnmasrhalText: %+v\n", err))
|
||||||
|
}
|
||||||
|
if len(g.Game.Moves()) != 0 {
|
||||||
|
// write board SVG to file
|
||||||
|
yellow := color.RGBA{255, 255, 0, 1}
|
||||||
|
lastMove := g.Game.Moves()[len(g.Game.Moves())-1]
|
||||||
|
|
||||||
|
mark := image.MarkSquares(yellow, lastMove.S1(), lastMove.S2())
|
||||||
|
if err := image.SVG(f, pos.Board(), mark); err != nil {
|
||||||
|
log.LogError("Error in ShowBoard image.SVG Mark: %+v\n", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := image.SVG(f, pos.Board()); err != nil {
|
||||||
|
log.LogError("Error in ShowBoard image.SVG: %+v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
if svgToPNG(fmt.Sprintf("/home/chessbot/chessbot/games/%+v", m.ConvID)) {
|
||||||
|
k.UploadToConversation(m.ConvID, "", fmt.Sprintf("/home/chessbot/chessbot/games/%+v.png", m.ConvID))
|
||||||
|
} else {
|
||||||
|
k.SendMessageByConvID(m.ConvID, fmt.Sprintf("```%+v```", g.Game.Position().Board().Draw()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func submitMove(m chat1.MsgSummary, g *Game) {
|
||||||
|
defer log.PanicSafe()
|
||||||
|
parts := strings.Split(m.Content.Text.Body, " ")
|
||||||
|
if g.Move {
|
||||||
|
if m.Sender.Username != g.White {
|
||||||
|
k.SendMessageByConvID(m.ConvID, "Wait your turn.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if m.Sender.Username != g.Black {
|
||||||
|
k.SendMessageByConvID(m.ConvID, "Wait your turn.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if parts[1] == "forfeit" || parts[1] == "surrender" {
|
||||||
|
if g.Move {
|
||||||
|
g.Game.Resign(chess.White)
|
||||||
|
} else {
|
||||||
|
g.Game.Resign(chess.Black)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err := g.Game.MoveStr(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
k.SendMessageByConvID(m.ConvID, "There was an error with your move.")
|
||||||
|
log.LogDebug("%+v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.Move = !g.Move
|
||||||
|
|
||||||
|
if g.Game.Outcome() != chess.NoOutcome {
|
||||||
|
k.SendMessageByConvID(m.ConvID, fmt.Sprintf("Game completed in %+v. %s by %s.\n", time.Since(g.StartTime), g.Game.Outcome(), g.Game.Method()))
|
||||||
|
delete(config.Games, g.ConvID)
|
||||||
|
}
|
||||||
|
go showBoard(m, g)
|
||||||
|
}
|
||||||
30
config.go
Normal file
30
config.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func loadConfig() {
|
||||||
|
var c Config
|
||||||
|
confFile, _ := ioutil.ReadFile(configFile)
|
||||||
|
err := json.Unmarshal([]byte(confFile), &c)
|
||||||
|
if err != nil {
|
||||||
|
log.LogErrorType(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
config = c
|
||||||
|
log.LogInfo("Setup completed using config file.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveConfig() {
|
||||||
|
defer log.PanicSafe()
|
||||||
|
file, err := json.Marshal(config)
|
||||||
|
if err != nil {
|
||||||
|
log.LogErrorType(err)
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(configFile, file, 0600)
|
||||||
|
if err != nil {
|
||||||
|
log.LogErrorType(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
11
config.json.sample
Normal file
11
config.json.sample
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"Games": {},
|
||||||
|
"LogOpts": {
|
||||||
|
"OutFile": "",
|
||||||
|
"KBTeam": "",
|
||||||
|
"KBChann": "",
|
||||||
|
"Level": 5,
|
||||||
|
"ProgName": "chessbot",
|
||||||
|
"UseStdout": true
|
||||||
|
}
|
||||||
|
}
|
||||||
12
go.mod
Normal file
12
go.mod
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
module git.nightmare.haus/rudi/chessbot
|
||||||
|
|
||||||
|
go 1.15
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/bwmarrin/discordgo v0.22.0
|
||||||
|
github.com/llgcode/draw2d v0.0.0-20200930101115-bfaf5d914d1e
|
||||||
|
github.com/notnil/chess v1.2.0
|
||||||
|
github.com/rogpeppe/misc v0.0.0-20200516092017-43a33a8f4c44
|
||||||
|
github.com/rudi9719/loggy v0.0.0-20201030144506-35d9b032ab93
|
||||||
|
samhofi.us/x/keybase/v2 v2.0.6
|
||||||
|
)
|
||||||
29
go.sum
Normal file
29
go.sum
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
github.com/ajstarks/svgo v0.0.0-20200320125537-f189e35d30ca h1:kWzLcty5V2rzOqJM7Tp/MfSX0RMSI1x4IOLApEefYxA=
|
||||||
|
github.com/ajstarks/svgo v0.0.0-20200320125537-f189e35d30ca/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
||||||
|
github.com/bwmarrin/discordgo v0.22.0 h1:uBxY1HmlVCsW1IuaPjpCGT6A2DBwRn0nvOguQIxDdFM=
|
||||||
|
github.com/bwmarrin/discordgo v0.22.0/go.mod h1:c1WtWUGN6nREDmzIpyTp/iD3VYt4Fpx+bVyfBG7JE+M=
|
||||||
|
github.com/go-gl/gl v0.0.0-20180407155706-68e253793080/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk=
|
||||||
|
github.com/go-gl/glfw v0.0.0-20180426074136-46a8d530c326/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||||
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||||
|
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||||
|
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||||
|
github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
|
||||||
|
github.com/llgcode/draw2d v0.0.0-20200930101115-bfaf5d914d1e h1:YRRazju3DMGuZTSWEj0nE2SCRcK3DW/qdHQ4UQx7sgs=
|
||||||
|
github.com/llgcode/draw2d v0.0.0-20200930101115-bfaf5d914d1e/go.mod h1:mVa0dA29Db2S4LVqDYLlsePDzRJLDfdhVZiI15uY0FA=
|
||||||
|
github.com/llgcode/ps v0.0.0-20150911083025-f1443b32eedb/go.mod h1:1l8ky+Ew27CMX29uG+a2hNOKpeNYEQjjtiALiBlFQbY=
|
||||||
|
github.com/notnil/chess v1.2.0 h1:5We6movgg1yzqBa68xsi0gWBM9XMIRfiaNc0Kcq6guk=
|
||||||
|
github.com/notnil/chess v1.2.0/go.mod h1:cRuJUIBFq9Xki05TWHJxHYkC+fFpq45IWwk94DdlCrA=
|
||||||
|
github.com/rogpeppe/misc v0.0.0-20200516092017-43a33a8f4c44 h1:osBnWuJgXxjdv/zUO9sm32B1Peew1cPGGyonHrqXd5M=
|
||||||
|
github.com/rogpeppe/misc v0.0.0-20200516092017-43a33a8f4c44/go.mod h1:hT7K6J2LNwUaaq7rtwF5VMB9kQ9yf7HgYFNVOOf41OI=
|
||||||
|
github.com/rudi9719/loggy v0.0.0-20201030144506-35d9b032ab93 h1:GjCI3CXdGt2PysVpObVzmKqMgKf3RlsKLBnaSOqPpUk=
|
||||||
|
github.com/rudi9719/loggy v0.0.0-20201030144506-35d9b032ab93/go.mod h1:s1ANCN8bF6HwwTpJLR458MFVGua9oqKKDbph/2jptL4=
|
||||||
|
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 h1:y6ce7gCWtnH+m3dCjzQ1PCuwl28DDIc3VNnvY29DlIA=
|
||||||
|
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81 h1:00VmoueYNlNz/aHIilyyQz/MHSqGoWJzpFv/HW8xpzI=
|
||||||
|
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
|
||||||
|
samhofi.us/x/keybase v0.0.0-20200129212102-e05e93be9f3f h1:MHSEiuiRFrFi7BTw46lC22PMk3Fit8IvVRM4xANTt20=
|
||||||
|
samhofi.us/x/keybase v0.0.0-20200129212102-e05e93be9f3f/go.mod h1:fcva80IUFyWcHtV4bBSzgKg07K6Rvuvi3GtGCLNGkyE=
|
||||||
|
samhofi.us/x/keybase v0.0.0-20200409184719-3e5afcc7f988 h1:ZQDHB2AP5ouuMnANibyBJQBbxwi+tCMQHa4ttp15ac0=
|
||||||
|
samhofi.us/x/keybase/v2 v2.0.6 h1:gLluTcyjbwckQxSarF1ig2klL4Li7O/THdxsgo1dUvw=
|
||||||
|
samhofi.us/x/keybase/v2 v2.0.6/go.mod h1:lJivwhzMSV+WUg+XUbatszStjjFVcuLGl+xcQpqQ5GQ=
|
||||||
69
main.go
Executable file
69
main.go
Executable file
@ -0,0 +1,69 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/llgcode/draw2d/draw2dimg"
|
||||||
|
"github.com/rogpeppe/misc/svg"
|
||||||
|
"github.com/rudi9719/loggy"
|
||||||
|
"samhofi.us/x/keybase/v2"
|
||||||
|
"samhofi.us/x/keybase/v2/types/chat1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
k = keybase.NewKeybase()
|
||||||
|
|
||||||
|
configFile = "config.json"
|
||||||
|
config Config
|
||||||
|
log = loggy.NewLogger(config.LogOpts)
|
||||||
|
r = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
)
|
||||||
|
|
||||||
|
func printChat(m chat1.MsgSummary) {
|
||||||
|
defer log.PanicSafe()
|
||||||
|
if m.Sender.Username == k.Username {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(m.Content.Text.Body, "@chessbot") {
|
||||||
|
chessCommand(m)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.LogInfo("%s: %s\n", m.Sender.Username, m.Content.Text.Body)
|
||||||
|
}
|
||||||
|
func main() {
|
||||||
|
loadConfig()
|
||||||
|
log = loggy.NewLogger(config.LogOpts)
|
||||||
|
defer log.PanicSafe()
|
||||||
|
chat := printChat
|
||||||
|
err := log.LogErrorType
|
||||||
|
handlers := keybase.Handlers{
|
||||||
|
ChatHandler: &chat,
|
||||||
|
ErrorHandler: &err,
|
||||||
|
}
|
||||||
|
log.LogCritical("Starting Chessbot")
|
||||||
|
k.Run(handlers, &keybase.RunOptions{})
|
||||||
|
}
|
||||||
|
func svgToPNG(path string) bool {
|
||||||
|
defer log.PanicSafe()
|
||||||
|
file, err := os.Open(fmt.Sprintf("%+v.svg", path))
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
size := image.Point{512, 512}
|
||||||
|
dest, err := svg.Render(file, size)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
err = draw2dimg.SaveToPngFile(fmt.Sprintf("%+v.png", path), dest)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
|
||||||
|
}
|
||||||
23
types.go
Normal file
23
types.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/notnil/chess"
|
||||||
|
"github.com/rudi9719/loggy"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Game is just a struct of type Game
|
||||||
|
type Game struct {
|
||||||
|
Game *chess.Game
|
||||||
|
StartTime time.Time
|
||||||
|
ConvID string
|
||||||
|
White string
|
||||||
|
Black string
|
||||||
|
Move bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config struct for restarting game
|
||||||
|
type Config struct {
|
||||||
|
Games map[string]Game
|
||||||
|
LogOpts loggy.LogOpts
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user