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) }