Files
web_ylsa/api_iris/service/api/chess.go
2025-07-11 16:54:11 +08:00

615 lines
18 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package api
import (
"errors"
"fmt"
"github.com/kataras/iris/v12"
"github.com/sirupsen/logrus"
"io"
"main/database"
"main/model"
"main/utils"
"math/rand"
"net/http"
"net/url"
"sort"
"strconv"
"strings"
"time"
)
var initChessPawns = model.ChessStatus{
Current: "",
IsEnd: false,
Status: "0,1,2,3,4,5,6,7,8;-1,-1,-1,-1,-1,-1,-1,-1,-1;-1,9,-1,-1,-1,-1,-1,10,-1;11,-1,12,-1,13,-1,14,-1,15;-1,-1,-1,-1,-1,-1,-1,-1,-1;-1,-1,-1,-1,-1,-1,-1,-1,-1;31,-1,30,-1,29,-1,28,-1,27;-1,26,-1,-1,-1,-1,-1,25,-1;-1,-1,-1,-1,-1,-1,-1,-1,-1;24,23,22,21,20,19,18,17,16",
Winner: "",
ChessId: "",
}
func addAiChessPlayer(ctx iris.Context) {
username := utils.GetLoginUser(ctx)
if username.Username == "" {
return
}
id := ctx.Params().Get("id")
status := getChessRoomStatus(id)
if status.Players != username.Username {
ctx.StatusCode(iris.StatusBadRequest)
ctx.SetErr(errors.New("房主方可添加机器人"))
return
}
status.Players += ",ai--" + id
status.Status = initChessPawns.Status
status.Current = username.Username
status.IsEnd = initChessPawns.IsEnd
status.Winner = initChessPawns.Winner
status.ChessId = initChessPawns.ChessId
db := database.GetInstance().GetMysqlDb()
if err := db.Save(&status).Error; err != nil {
logrus.Errorln("sql执行失败", err)
}
err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", status))
if utils.ErrHandle(ctx, err) {
return
}
}
func updateChessAiStep(ctx iris.Context) {
username := utils.GetLoginUser(ctx)
if username.Username == "" {
return
}
id := ctx.Params().Get("id")
status := getChessRoomStatus(id)
if players := strings.Split(status.Players, ","); players[0] != username.Username || players[1] != "ai--"+id {
ctx.StatusCode(iris.StatusBadRequest)
ctx.SetErr(errors.New("房间人员配置错误"))
return
}
if status.Current != "ai--"+id {
ctx.StatusCode(iris.StatusBadRequest)
ctx.SetErr(errors.New("当前非ai回合"))
return
}
stepList := strings.Split(getAiChessStep(chessPawnStatusToFen(chessPawnStrToSlice(status.Status)), "queryall", "b"), "|")
var tmpNum int
if strings.Index(stepList[0], "score:??") != -1 {
rand.NewSource(time.Now().Unix())
tmpNum = rand.Intn(len(stepList))
} else {
tmpNum = 0
}
stepTmp := strings.Split(stepList[tmpNum], ",")[0]
//fmt.Println(stepTmp, chessPawnStatusToFen(chessPawnStrToSlice(status.Status)))
if strings.Index(stepTmp, "move:") == -1 {
ctx.StatusCode(iris.StatusInternalServerError)
ctx.SetErr(errors.New("机器人无合适步骤,对局结束"))
return
}
step := strings.Split(stepTmp, "move:")[1]
tmp := "abcdefghi0123456789"
statusList := chessPawnStrToSlice(status.Status)
start0 := 8 - strings.IndexByte(tmp, step[0])
end0 := 8 - strings.IndexByte(tmp, step[2])
start1 := strings.IndexByte(tmp, step[1]) - 9
end1 := strings.IndexByte(tmp, step[3]) - 9
statusList[end1][end0] = statusList[start1][start0]
statusList[start1][start0] = "-1"
status.Status = chessPawnSliceToStr(statusList)
status.ChessId = statusList[end1][end0]
status.Current = username.Username
db := database.GetInstance().GetMysqlDb()
if err := db.Save(&status).Error; err != nil {
logrus.Errorln("sql执行失败", err)
}
err := ctx.JSON(utils.FormatRes(200, "", status))
if utils.ErrHandle(ctx, err) {
return
}
}
func testChess(ctx iris.Context) {
var res [][]string
db := database.GetInstance().GetMysqlDb()
var status []model.ChessStatus
if err := db.Find(&status).Error; err != nil {
logrus.Errorln("sql执行失败", err)
}
for _, s := range status {
stepTmp := getAiChessStep(chessPawnStatusToFen(chessPawnStrToSlice(s.Status)), "queryall", "b")
//fmt.Println(s.Status, chessPawnStatusToFen(chessPawnStrToSlice(s.Status)))
step := strings.Split(stepTmp, "move:")[1]
tmp := "abcdefghi0123456789"
//statusList := chessPawnStrToSlice(status.Status)
start0 := strings.IndexByte(tmp, step[0])
end0 := strings.IndexByte(tmp, step[2])
start1 := strings.IndexByte(tmp, step[1]) - 9
end1 := strings.IndexByte(tmp, step[3]) - 9
statusTmp := chessPawnStrToSlice(s.Status)
start := statusTmp[start1][start0]
end := statusTmp[end1][end0]
res = append(res, []string{start, end})
}
err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", res))
if utils.ErrHandle(ctx, err) {
return
}
}
func resetRoom(ctx iris.Context) {
username := utils.GetLoginUser(ctx)
if username.Username == "" {
return
}
id := ctx.Params().Get("id")
status := getChessRoomStatus(id)
if utils.DataIsNil(status) {
ctx.StatusCode(iris.StatusBadRequest)
ctx.SetErr(errors.New("房间号不存在"))
return
}
if strings.Split(status.Players, ",")[0] != username.Username {
ctx.StatusCode(iris.StatusBadRequest)
ctx.SetErr(errors.New("房主方可重置房间"))
return
}
db := database.GetInstance().GetMysqlDb()
status.Status = initChessPawns.Status
status.Current = username.Username
status.IsEnd = initChessPawns.IsEnd
status.Winner = initChessPawns.Winner
status.ChessId = initChessPawns.ChessId
if err := db.Save(&status).Error; err != nil {
logrus.Errorln("sql执行失败", err)
}
err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", status))
if utils.ErrHandle(ctx, err) {
return
}
}
// 新建房间
func newChessRoom(ctx iris.Context) {
creator := utils.GetLoginUser(ctx)
if creator.Username == "" {
return
}
db := database.GetInstance().GetMysqlDb()
status := model.ChessStatus{
Players: creator.Username,
Current: creator.Username,
IsEnd: initChessPawns.IsEnd,
Status: initChessPawns.Status,
Winner: initChessPawns.Winner,
ChessId: initChessPawns.ChessId,
}
if err := db.Create(&status).Error; err != nil {
logrus.Errorln("sql执行失败", err)
}
err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", status.ID))
if utils.ErrHandle(ctx, err) {
return
}
}
// 获取所有房间
func getChessRooms(ctx iris.Context) {
db := database.GetInstance().GetMysqlDb()
var rooms []model.ChessStatus
if err := db.Find(&rooms).Error; err != nil {
logrus.Errorln("sql执行失败", err)
}
err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", rooms))
if utils.ErrHandle(ctx, err) {
return
}
}
// 加入房间
func joinChessRoom(ctx iris.Context) {
username := utils.GetLoginUser(ctx)
if utils.DataIsNil(username) {
return
}
id := ctx.Params().Get("id")
status := getChessRoomStatus(id)
if utils.DataIsNil(status) {
ctx.StatusCode(iris.StatusBadRequest)
ctx.SetErr(errors.New("房间号不存在"))
return
}
if len(strings.Split(status.Players, ",")) >= 2 {
ctx.StatusCode(iris.StatusBadRequest)
ctx.SetErr(errors.New("房间人数已满"))
return
}
if utils.CheckListItem(strings.Split(status.Players, ","), username.Username) {
ctx.StatusCode(iris.StatusBadRequest)
ctx.SetErr(errors.New("不可重复加入房间"))
return
}
status.Players += fmt.Sprintf(",%s", username.Username)
db := database.GetInstance().GetMysqlDb()
if err := db.Save(&status).Error; err != nil {
logrus.Errorln("sql执行失败", err)
}
if err := db.Create(&model.ChessStatusLog{
RoomId: status.ID,
Players: status.Players,
Current: status.Current,
IsEnd: status.IsEnd,
Status: status.Status,
Winner: status.Winner,
ChessId: status.ChessId,
Time: status.CreatedAt,
}).Error; err != nil {
logrus.Errorln("sql执行失败", err)
}
err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", status))
if utils.ErrHandle(ctx, err) {
return
}
}
// 离开房间
func leaveChessRoom(ctx iris.Context) {
username := utils.GetLoginUser(ctx)
if utils.DataIsNil(username) {
return
}
id := ctx.Params().Get("id")
status := getChessRoomStatus(id)
if utils.DataIsNil(status) {
ctx.StatusCode(iris.StatusBadRequest)
ctx.SetErr(errors.New("房间号不存在"))
return
}
if !utils.CheckListItem(strings.Split(status.Players, ","), username.Username) {
ctx.StatusCode(iris.StatusBadRequest)
ctx.SetErr(errors.New("未加入该房间"))
return
}
for _, v := range strings.Split(status.Players, ",") {
if v != username.Username {
status.Players = v
}
}
db := database.GetInstance().GetMysqlDb()
if status.Players == username.Username || status.Players == "ai--"+id {
if err := db.Delete(&status).Error; err != nil {
logrus.Errorln("sql执行失败", err)
}
} else {
status.Current = status.Players
status.ChessId = initChessPawns.ChessId
status.IsEnd = initChessPawns.IsEnd
status.Winner = initChessPawns.Winner
status.Status = initChessPawns.Status
if err := db.Save(&status).Error; err != nil {
logrus.Errorln("sql执行失败", err)
}
}
err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", status))
if utils.ErrHandle(ctx, err) {
return
}
}
// 获取房间状态接口
func getChessRoom(ctx iris.Context) {
id := ctx.Params().Get("id")
status := getChessRoomStatus(id)
if utils.DataIsNil(status) {
ctx.StatusCode(iris.StatusBadRequest)
ctx.SetErr(errors.New("房间号不存在"))
return
}
err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", status))
if utils.ErrHandle(ctx, err) {
return
}
}
// 更新棋盘状态
func updateChessStatus(ctx iris.Context) {
username := utils.GetLoginUser(ctx)
if utils.DataIsNil(username) {
return
}
id := ctx.Params().Get("id")
pawn := ctx.URLParam("pawn")
des := ctx.URLParam("des")
status := getChessRoomStatus(id)
if utils.DataIsNil(status) {
ctx.StatusCode(iris.StatusBadRequest)
ctx.SetErr(errors.New("房间号不存在"))
return
}
if len(strings.Split(status.Players, ",")) < 2 {
ctx.StatusCode(iris.StatusBadRequest)
ctx.SetErr(errors.New("房间人数不足"))
return
}
pawnNum, _ := strconv.Atoi(pawn)
if (strings.Split(status.Players, ",")[0] == username.Username && pawnNum > 15) || (strings.Split(status.Players, ",")[1] == username.Username && pawnNum < 16) {
ctx.StatusCode(iris.StatusBadRequest)
ctx.SetErr(errors.New("只能移动自己的棋子"))
return
}
if status.Current != username.Username {
ctx.StatusCode(iris.StatusBadRequest)
ctx.SetErr(errors.New("当前不是你的回合"))
return
}
if checkChessStep(pawn, status.Status, des) {
tmpStatus := chessPawnStrToSlice(status.Status)
locTmp := getPawnLoc(status.Status, pawn)
toTmp0, _ := strconv.Atoi(strings.Split(des, ",")[0])
toTmp1, _ := strconv.Atoi(strings.Split(des, ",")[1])
tmpStatus[locTmp[0]][locTmp[1]] = "-1"
tmpStatus[toTmp0][toTmp1] = pawn
status.Status = chessPawnSliceToStr(tmpStatus)
status.ChessId = pawn
for _, v := range strings.Split(status.Players, ",") {
if v != username.Username {
status.Current = v
}
}
if checkWinner(status.Status) == 1 {
status.Winner = username.Username
status.IsEnd = true
status.Current = ""
}
} else {
ctx.StatusCode(iris.StatusBadRequest)
ctx.SetErr(errors.New("当前无法移到该位置"))
return
}
db := database.GetInstance().GetMysqlDb()
if checkWinner(status.Status) == 2 {
for _, v := range strings.Split(status.Players, ",") {
if v != username.Username {
status.Winner = v
status.IsEnd = true
}
}
}
if err := db.Save(&status).Error; err != nil {
logrus.Errorln("sql执行失败", err)
}
if err := db.Create(&model.ChessStatusLog{
RoomId: status.ID,
Current: status.Current,
Players: status.Players,
IsEnd: status.IsEnd,
Status: status.Status,
Winner: status.Winner,
ChessId: status.ChessId,
Time: status.CreatedAt,
}).Error; err != nil {
logrus.Errorln("sql执行失败", err)
}
err := ctx.JSON(utils.FormatRes(iris.StatusOK, "", status))
if utils.ErrHandle(ctx, err) {
return
}
}
// 获取房间状态方法
func getChessRoomStatus(id string) model.ChessStatus {
db := database.GetInstance().GetMysqlDb()
var status model.ChessStatus
if err := db.Where("id = ?", id).First(&status).Error; err != nil {
logrus.Errorln("sql执行失败", err)
}
return status
}
func checkWinner(status string) int {
if utils.DataIsNil(getPawnLoc(status, "4")) || utils.DataIsNil(getPawnLoc(status, "20")) {
return 1
}
if checkNullPath(chessPawnStrToSlice(status), getPawnLoc(status, "4"), getPawnLoc(status, "20")) {
return 2
}
return 0
}
// 获取棋子位置
func getPawnLoc(statusStr string, pawn string) [2]int {
var res [2]int
status := chessPawnStrToSlice(statusStr)
for row, i := range status {
for col, j := range i {
if j == pawn {
res[0] = row
res[1] = col
}
}
}
return res
}
// 判断棋子是否可移动
func checkChessStep(pawn string, statusStr string, to string) bool {
locList := getPawnLoc(statusStr, pawn)
status := chessPawnStrToSlice(statusStr)
locTmp0 := locList[0]
locTmp1 := locList[1]
toTmp := strings.Split(to, ",")
toTmp0, _ := strconv.Atoi(toTmp[0])
toTmp1, _ := strconv.Atoi(toTmp[1])
//目标位置超出棋盘
if toTmp0 < 0 || toTmp1 < 0 || toTmp0 > 10 || toTmp1 > 8 {
return false
}
pawnNum, _ := strconv.Atoi(pawn)
toPawnNum, _ := strconv.Atoi(status[toTmp0][toTmp1])
if (pawnNum < 16 && toPawnNum < 16 && toPawnNum >= 0) || (pawnNum > 15 && toPawnNum > 15) {
return false
}
switch pawn {
case "0", "8", "16", "24": //车:同行列,路径为空
if (toTmp0 == locTmp0 && toTmp1 != locTmp1) || (toTmp1 == locTmp1 && toTmp0 != locTmp0) && checkNullPath(status, [2]int{locTmp0, locTmp1}, [2]int{toTmp0, toTmp1}) {
return true
}
case "1", "7", "17", "23": //马:日字,前后左右无子
if (utils.IntAbs(toTmp0-locTmp0) == 1 && utils.IntAbs(toTmp1-locTmp1) == 2 && status[locTmp0][(locTmp1+toTmp1)/2] == "-1") || (utils.IntAbs(toTmp0-locTmp0) == 2 && utils.IntAbs(toTmp1-locTmp1) == 1 && status[(locTmp0+toTmp0)/2][locTmp1] == "-1") {
return true
}
case "2", "6", "18", "22": //象:田字,不过河,中间格子为空
if utils.IntAbs(toTmp0-locTmp0) == 2 && utils.IntAbs(toTmp1-locTmp1) == 2 && ((locTmp0 < 5 && toTmp0 < 5) || (locTmp0 > 4 && toTmp0 > 4)) && status[(locTmp0+toTmp0)/2][(locTmp1+toTmp1)/2] == "-1" {
return true
}
case "3", "5", "19", "21": //士:斜着走,不超出方格
if toTmp1 < 3 || toTmp1 > 5 || (locTmp0 < 5 && toTmp0 > 2) || (locTmp0 > 4 && toTmp0 < 7) {
return false
}
if utils.IntAbs(toTmp0-locTmp0) == 1 && utils.IntAbs(toTmp1-locTmp1) == 1 {
return true
}
case "4", "20": //将:走一步,不超出方格
if toTmp1 < 3 || toTmp1 > 5 || (locTmp0 < 5 && toTmp0 > 2) || (locTmp0 > 4 && toTmp0 < 7) {
return false
}
if utils.IntAbs(toTmp0-locTmp0)+utils.IntAbs(toTmp1-locTmp1) == 1 {
return true
}
case "9", "10", "25", "26": //炮:同行列,路径不为空,目标位置不为空
if ((toTmp0 == locTmp0 && toTmp1 != locTmp1) || (toTmp1 == locTmp1 && toTmp0 != locTmp0)) && ((!checkNullPath(status, [2]int{locTmp0, locTmp1}, [2]int{toTmp0, toTmp1}) && status[toTmp0][toTmp1] != "-1") || (checkNullPath(status, [2]int{locTmp0, locTmp1}, [2]int{toTmp0, toTmp1}) && status[toTmp0][toTmp1] == "-1")) {
return true
}
case "11", "12", "13", "14", "15": //兵:走一步,过河后可横移
if (toTmp0 < 5 && toTmp1 == locTmp1 && toTmp0-locTmp0 == 1) || (toTmp0 > 4 && utils.IntAbs(toTmp0-locTmp0+toTmp1-locTmp1) == 1) {
return true
}
case "27", "28", "29", "30", "31": //兵:走一步,过河后可横移
if (toTmp0 > 4 && toTmp1 == locTmp1 && toTmp0-locTmp0 == -1) || (toTmp0 < 5 && utils.IntAbs(toTmp0-locTmp0+toTmp1-locTmp1) == 1) {
return true
}
default:
return false
}
return false
}
// 棋盘状态字符串转数组
func chessPawnStrToSlice(status string) [][]string {
var res [][]string
for _, i := range strings.Split(status, ";") {
var colList []string
for _, j := range strings.Split(i, ",") {
colList = append(colList, j)
}
res = append(res, colList)
}
return res
}
// 棋盘状态数组转字符串
func chessPawnSliceToStr(status [][]string) string {
var resList []string
for _, i := range status {
resList = append(resList, strings.Join(i, ","))
}
res := strings.Join(resList, ";")
return res
}
func chessPawnStatusToFen(status [][]string) string {
var res []string
for _, i := range status {
resTmp := ""
count := 0
for col, j := range i {
if j == "-1" && col == len(i)-1 {
resTmp += strconv.Itoa(count + 1)
} else if j == "-1" {
count += 1
} else {
if count != 0 {
resTmp += strconv.Itoa(count)
count = 0
}
switch j {
case "0", "8":
resTmp += "R"
case "1", "7":
resTmp += "N"
case "2", "6":
resTmp += "B"
case "3", "5":
resTmp += "A"
case "4":
resTmp += "K"
case "9", "10":
resTmp += "C"
case "11", "12", "13", "14", "15":
resTmp += "P"
case "16", "24":
resTmp += "r"
case "17", "23":
resTmp += "n"
case "18", "22":
resTmp += "b"
case "19", "21":
resTmp += "a"
case "20":
resTmp += "k"
case "25", "26":
resTmp += "c"
case "27", "28", "29", "30", "31":
resTmp += "p"
}
}
}
res = append(res, resTmp)
}
return utils.Reverse(strings.Join(res, "/"))
}
// 判断路径是否为空
func checkNullPath(status [][]string, start [2]int, end [2]int) bool {
if start[0] == end[0] {
path := []int{start[1], end[1]}
sort.Ints(path)
for i := path[0] + 1; i < path[1]; i++ {
if status[start[0]][i] != "-1" {
return false
}
}
return true
}
if start[1] == end[1] {
path := []int{start[0], end[0]}
sort.Ints(path)
for i := path[0] + 1; i < path[1]; i++ {
if status[i][start[1]] != "-1" {
return false
}
}
return true
}
return false
}
func getAiChessStep(fenStatus string, t string, p string) string {
urlPath, err := url.Parse("http://www.chessdb.cn/chessdb.php")
params := url.Values{}
params.Set("action", t)
params.Set("board", fmt.Sprintf("%s %s", fenStatus, p))
params.Set("showall", "1")
urlPath.RawQuery = params.Encode()
res, err := http.Get(urlPath.String())
if err != nil {
return err.Error()
}
defer func(Body io.ReadCloser) {
err := Body.Close()
if err != nil {
}
}(res.Body)
body, _ := io.ReadAll(res.Body)
return string(body)
}