1025 lines
22 KiB
Go
1025 lines
22 KiB
Go
package chess
|
||
|
||
import (
|
||
"fmt"
|
||
"strconv"
|
||
"strings"
|
||
)
|
||
|
||
var pieceJPZ = []Piece{
|
||
BR, BN, BS, BM, BK, BS, BN, BR,
|
||
"", "", "", "", "", "", "", "",
|
||
BP, BP, BP, BP, BP, BP, BP, BP,
|
||
"", "", "", "", "", "", "", "",
|
||
"", "", "", "", "", "", "", "",
|
||
WP, WP, WP, WP, WP, WP, WP, WP,
|
||
"", "", "", "", "", "", "", "",
|
||
WR, WN, WS, WK, WM, WS, WN, WR,
|
||
}
|
||
|
||
const (
|
||
MoveWK = 0
|
||
MoveWM = 1
|
||
MoveBM = 2
|
||
MoveBK = 3
|
||
)
|
||
|
||
// RecordJPZ 走棋记录
|
||
type RecordJPZ struct {
|
||
FromTo [2]*PositionPiece
|
||
CambodianMove [4]bool
|
||
}
|
||
|
||
// ChessJPZ 棋盘
|
||
type ChessJPZ struct {
|
||
Variant int
|
||
Width int // 宽度
|
||
Height int // 高度
|
||
Count int // 格子数量
|
||
Act bool // 是否该白字走,false白棋
|
||
Round int // 回合数
|
||
Buf []Piece // 棋子
|
||
|
||
Castling [4]bool // 王车易位可行性 [KQkq]
|
||
EnPassant int // 过路兵位置
|
||
CambodianMove [4]bool // 柬埔寨移动可行性(DEde,D白王,E白仕,d黑仕,e黑王)
|
||
|
||
Records []*RecordJPZ // 走棋记录
|
||
Check [2]bool // 将军
|
||
Checkmate [2]bool // 将死
|
||
}
|
||
|
||
func (c *ChessJPZ) GetChess() []string {
|
||
ret := make([]string, len(c.Buf))
|
||
for k, v := range c.Buf {
|
||
ret[k] = string(v)
|
||
}
|
||
return ret
|
||
}
|
||
|
||
func (c *ChessJPZ) GetAct() string {
|
||
if c.Act {
|
||
return "b"
|
||
} else {
|
||
return "w"
|
||
}
|
||
}
|
||
|
||
func (c *ChessJPZ) GetCastling() []bool {
|
||
ret := make([]bool, len(c.Castling))
|
||
for k, v := range c.Castling {
|
||
ret[k] = v
|
||
}
|
||
return ret
|
||
}
|
||
|
||
func (c *ChessJPZ) GetEnPassant() int {
|
||
return c.EnPassant
|
||
}
|
||
|
||
func (c *ChessJPZ) GetCambodianMove() []bool {
|
||
ret := make([]bool, len(c.CambodianMove))
|
||
for k, v := range c.CambodianMove {
|
||
ret[k] = v
|
||
}
|
||
return ret
|
||
}
|
||
|
||
func (c *ChessJPZ) GetRound() int {
|
||
return c.Round
|
||
}
|
||
|
||
func (c *ChessJPZ) GetPiece(index int) Piece {
|
||
return c.Buf[index]
|
||
}
|
||
|
||
func (c *ChessJPZ) GetCheck() []bool {
|
||
ret := make([]bool, len(c.Check))
|
||
for k, v := range c.Check {
|
||
ret[k] = v
|
||
}
|
||
return ret
|
||
}
|
||
|
||
func (c *ChessJPZ) GetCheckmate() []bool {
|
||
ret := make([]bool, len(c.Checkmate))
|
||
for k, v := range c.Checkmate {
|
||
ret[k] = v
|
||
}
|
||
return ret
|
||
}
|
||
|
||
func (c *ChessJPZ) NextAct() {
|
||
// 判断将军
|
||
c.Check[0] = c.IsCheck(true)
|
||
c.Check[1] = c.IsCheck(false)
|
||
// 判断将死
|
||
c.Checkmate[0] = false
|
||
c.Checkmate[1] = false
|
||
if !c.Act && c.IsCheckmate(true) {
|
||
c.Checkmate[0] = true
|
||
}
|
||
if c.Act && c.IsCheckmate(false) {
|
||
c.Checkmate[1] = true
|
||
}
|
||
|
||
// 下一个玩家操作,记录回合数
|
||
if !c.Act {
|
||
c.Round++
|
||
}
|
||
c.Act = !c.Act
|
||
}
|
||
|
||
func (c *ChessJPZ) PrintChess() {
|
||
f := func(n int) string {
|
||
str := fmt.Sprintf(" %d", n+1)
|
||
if c.IsWhiteMove() && n == 0 || c.IsBlackMove() && n == -1 {
|
||
str = strings.Replace(str, " ", "*", 1)
|
||
}
|
||
return str
|
||
}
|
||
|
||
splitStr := " "
|
||
for y := c.Height - 1; y >= 0; y-- {
|
||
fmt.Print(f(y))
|
||
for x := 0; x < c.Width; x++ {
|
||
fmt.Print(splitStr)
|
||
idx, _ := XYToIndex(c.Width, c.Height, x, y)
|
||
value := c.Buf[idx]
|
||
if value == "" {
|
||
fmt.Print("--")
|
||
} else {
|
||
fmt.Print(value)
|
||
}
|
||
splitStr = " "
|
||
}
|
||
fmt.Println()
|
||
splitStr = " "
|
||
}
|
||
fmt.Print(" ")
|
||
for x := 0; x < c.Width; x++ {
|
||
fmt.Print(splitStr)
|
||
fmt.Print(fmt.Sprintf(" %d", x+1))
|
||
splitStr = " "
|
||
}
|
||
fmt.Println()
|
||
}
|
||
|
||
func (c *ChessJPZ) GenFenChessBoard() string {
|
||
var build strings.Builder
|
||
splitStr := ""
|
||
emptyCnt := 0
|
||
|
||
writeEmptyCount := func() {
|
||
if emptyCnt > 0 {
|
||
countStr := fmt.Sprintf("%d", emptyCnt)
|
||
build.WriteString(countStr)
|
||
emptyCnt = 0
|
||
}
|
||
}
|
||
|
||
for y := c.Height - 1; y >= 0; y-- {
|
||
build.WriteString(splitStr)
|
||
for x := 0; x < c.Width; x++ {
|
||
idx, _ := XYToIndex(c.Width, c.Height, x, y)
|
||
value := c.Buf[idx]
|
||
if value == "" {
|
||
//build.WriteString("")
|
||
emptyCnt++
|
||
} else {
|
||
writeEmptyCount()
|
||
t := value[1:]
|
||
c := value[:1]
|
||
if c == "W" {
|
||
build.WriteString(string(t))
|
||
} else if c == "B" {
|
||
build.WriteString(strings.ToLower(string(t)))
|
||
}
|
||
}
|
||
}
|
||
writeEmptyCount()
|
||
splitStr = "/"
|
||
}
|
||
return build.String()
|
||
}
|
||
|
||
func (c *ChessJPZ) GenFenCastling() string {
|
||
return "-" // 客户端需要支持王车易位走位后才可使用
|
||
//var build strings.Builder
|
||
//if this.castling[0] {
|
||
// build.WriteString("K")
|
||
//}
|
||
//if this.castling[1] {
|
||
// build.WriteString("Q")
|
||
//}
|
||
//if this.castling[2] {
|
||
// build.WriteString("k")
|
||
//}
|
||
//if this.castling[3] {
|
||
// build.WriteString("q")
|
||
//}
|
||
//str := build.String()
|
||
//if len(str) == 0 {
|
||
// return "-"
|
||
//}
|
||
//return build.String()
|
||
}
|
||
|
||
func (c *ChessJPZ) GenFenCambodianMove() string {
|
||
var build strings.Builder
|
||
if c.CambodianMove[MoveWK] {
|
||
build.WriteString("D")
|
||
}
|
||
if c.CambodianMove[MoveWM] {
|
||
build.WriteString("E")
|
||
}
|
||
if c.CambodianMove[MoveBM] {
|
||
build.WriteString("d")
|
||
}
|
||
if c.CambodianMove[MoveBK] {
|
||
build.WriteString("e")
|
||
}
|
||
str := build.String()
|
||
if len(str) == 0 {
|
||
return "-"
|
||
}
|
||
return build.String()
|
||
}
|
||
|
||
func (c *ChessJPZ) GenFenEnPassant() string {
|
||
return "-"
|
||
}
|
||
|
||
func (c *ChessJPZ) GenHalfRoundCount() string {
|
||
return "0"
|
||
}
|
||
|
||
func (c *ChessJPZ) GenRoundCount() string {
|
||
return fmt.Sprintf("%d", c.Round)
|
||
}
|
||
|
||
func (c *ChessJPZ) GenFen() string {
|
||
var build strings.Builder
|
||
build.WriteString("fen ")
|
||
build.WriteString(c.GenFenChessBoard())
|
||
build.WriteString(" ")
|
||
build.WriteString(c.GetAct())
|
||
build.WriteString(" ")
|
||
build.WriteString(c.GenFenCambodianMove())
|
||
build.WriteString(" ")
|
||
build.WriteString(c.GenFenEnPassant())
|
||
build.WriteString(" ")
|
||
build.WriteString(c.GenHalfRoundCount())
|
||
build.WriteString(" ")
|
||
build.WriteString(c.GenRoundCount())
|
||
return build.String()
|
||
}
|
||
|
||
func (c *ChessJPZ) UciTwoPosToIdxList(str string) []int64 {
|
||
result := []int64{-1, -1}
|
||
result[0] = c.UciOnePosToIdx(str[0:2])
|
||
result[1] = c.UciOnePosToIdx(str[2:4]) // 这里出现过g5f6m这样的返回,暂时不清楚这个m表示的意义,是兵进行吃子造成的
|
||
return result
|
||
}
|
||
|
||
func (c *ChessJPZ) UciOnePosToIdx(str string) int64 {
|
||
x := int(strings.ToLower(str[:1])[0] - 'a')
|
||
y, _ := strconv.Atoi(str[1:])
|
||
y--
|
||
idx := y*c.Width + x
|
||
//fmt.Println("UciOnePosToIdx", str, x, y, idx)
|
||
return int64(idx)
|
||
}
|
||
|
||
func (c *ChessJPZ) GetChessNum(piece Piece) int {
|
||
n := 0
|
||
for _, v := range c.Buf {
|
||
if piece == v {
|
||
n++
|
||
}
|
||
}
|
||
return n
|
||
}
|
||
|
||
func (c *ChessJPZ) GetWhiteNum() int {
|
||
n := 0
|
||
for _, v := range c.Buf {
|
||
if IsWhite(v) {
|
||
n++
|
||
}
|
||
}
|
||
return n
|
||
}
|
||
|
||
func (c *ChessJPZ) GetBlackNum() int {
|
||
n := 0
|
||
for _, v := range c.Buf {
|
||
if IsBlack(v) {
|
||
n++
|
||
}
|
||
}
|
||
return n
|
||
}
|
||
|
||
func (c *ChessJPZ) Has(piece Piece) bool {
|
||
for _, v := range c.Buf {
|
||
if v == piece {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
func (c *ChessJPZ) GetVariant() int {
|
||
return c.Variant
|
||
}
|
||
|
||
func (c *ChessJPZ) SetChess(strings []string) {
|
||
for k, v := range strings {
|
||
c.Buf[k] = Piece(v)
|
||
}
|
||
}
|
||
|
||
func (c *ChessJPZ) Set(bufStr []string, bufPiece []Piece, act string, castling []bool, cambodianMove []bool, enPassant int, round int) {
|
||
//var buf2 []Piece
|
||
//if len(bufStr) > 0 {
|
||
// buf2 = make([]Piece, len(bufStr))
|
||
// for x := 0; x < c.Width; x++ {
|
||
// for y := 0; y < c.Height; y++ {
|
||
// buf2[(c.Height-1-y)*c.Width+x] = Piece(bufStr[y*c.Width+x])
|
||
// }
|
||
// }
|
||
//} else {
|
||
// buf2 = make([]Piece, len(bufPiece))
|
||
// for x := 0; x < c.Width; x++ {
|
||
// for y := 0; y < c.Height; y++ {
|
||
// buf2[(c.Height-1-y)*c.Width+x] = bufPiece[y*c.Width+x]
|
||
// }
|
||
// }
|
||
//}
|
||
|
||
if len(bufStr) > 0 {
|
||
for k, v := range bufStr {
|
||
c.Buf[k] = Piece(v)
|
||
}
|
||
}
|
||
if len(bufPiece) > 0 {
|
||
for k, v := range bufPiece {
|
||
c.Buf[k] = v
|
||
}
|
||
}
|
||
|
||
if act == "w" {
|
||
c.Act = false
|
||
} else if act == "b" {
|
||
c.Act = true
|
||
}
|
||
|
||
if len(castling) == 4 {
|
||
for k, v := range castling {
|
||
c.Castling[k] = v
|
||
}
|
||
}
|
||
|
||
if len(cambodianMove) == 4 {
|
||
for k, v := range cambodianMove {
|
||
c.CambodianMove[k] = v
|
||
}
|
||
}
|
||
|
||
c.EnPassant = enPassant
|
||
c.Round = round
|
||
}
|
||
|
||
func (c *ChessJPZ) Init() {
|
||
c.Variant = ChessVarCambodian
|
||
c.Width = 8
|
||
c.Height = 8
|
||
c.Count = c.Width * c.Height
|
||
c.Act = false
|
||
c.Round = 1
|
||
|
||
if c.Buf == nil || len(c.Buf) != c.Count {
|
||
c.Buf = make([]Piece, c.Count)
|
||
}
|
||
for x := 0; x < c.Width; x++ {
|
||
for y := 0; y < c.Height; y++ {
|
||
c.Buf[(c.Height-1-y)*c.Width+x] = pieceJPZ[y*c.Width+x]
|
||
}
|
||
}
|
||
|
||
c.Castling = [4]bool{true, true, true, true}
|
||
c.EnPassant = -1
|
||
c.CambodianMove = [4]bool{true, true, true, true}
|
||
c.Records = c.Records[:0]
|
||
c.Check = [2]bool{false, false}
|
||
c.Checkmate = [2]bool{false, false}
|
||
}
|
||
|
||
// GetWidth 获取棋盘的宽度
|
||
func (c *ChessJPZ) GetWidth() int {
|
||
return c.Width
|
||
}
|
||
|
||
// GetHeight 获取棋盘的高度
|
||
func (c *ChessJPZ) GetHeight() int {
|
||
return c.Height
|
||
}
|
||
|
||
// MoveR 获取某个位置的车能走的下一步位置
|
||
func (c *ChessJPZ) MoveR(x, y int) []*PositionPiece {
|
||
// 坐标转换成索引
|
||
index, err := XYToIndex(c.Width, c.Height, x, y)
|
||
if err != nil {
|
||
return nil
|
||
}
|
||
|
||
// 判断这个位置是不是车
|
||
o := c.Buf[index]
|
||
if !IsR(o) {
|
||
return nil
|
||
}
|
||
|
||
var validMoves []*PositionPiece
|
||
|
||
// 遍历四个方向
|
||
for _, dir := range StraightDirections {
|
||
dx, dy := dir.X, dir.Y
|
||
newX, newY := x+dx, y+dy
|
||
|
||
// 检查新位置是否在棋盘范围内
|
||
for newX >= 0 && newX < c.Width && newY >= 0 && newY < c.Height {
|
||
// 获取新位置的索引
|
||
index, err := XYToIndex(c.Width, c.Height, newX, newY)
|
||
if err != nil {
|
||
break
|
||
}
|
||
|
||
// 获取新位置的棋子
|
||
piece := c.Buf[index]
|
||
|
||
if IsEmpty(piece) {
|
||
// 记录位置
|
||
validMoves = append(validMoves, &PositionPiece{Index: index})
|
||
} else {
|
||
if IsWhite(piece) != IsWhite(o) {
|
||
validMoves = append(validMoves, &PositionPiece{Index: index, P: piece})
|
||
}
|
||
break
|
||
}
|
||
|
||
// 新位置是空的,继续向前走
|
||
newX += dx
|
||
newY += dy
|
||
}
|
||
}
|
||
|
||
return validMoves
|
||
}
|
||
|
||
// MoveN 获取某个位置的马能走的下一步位置
|
||
func (c *ChessJPZ) MoveN(x, y int) []*PositionPiece {
|
||
// 坐标转换成索引
|
||
index, err := XYToIndex(c.Width, c.Height, x, y)
|
||
if err != nil {
|
||
return nil
|
||
}
|
||
|
||
// 判断这个位置是不是马
|
||
o := c.Buf[index]
|
||
if !IsN(o) {
|
||
return nil
|
||
}
|
||
|
||
var validMoves []*PositionPiece
|
||
|
||
for _, dir := range KnightDirections {
|
||
dx, dy := dir.X, dir.Y
|
||
newX, newY := x+dx, y+dy
|
||
|
||
// 检查新位置是否在棋盘范围内
|
||
if newX >= 0 && newX < c.Width && newY >= 0 && newY < c.Height {
|
||
// 获取新位置的索引
|
||
index, err := XYToIndex(c.Width, c.Height, newX, newY)
|
||
if err == nil {
|
||
// 获取新位置的棋子
|
||
piece := c.Buf[index]
|
||
|
||
if IsEmpty(piece) || (IsWhite(piece) != IsWhite(o)) {
|
||
validMoves = append(validMoves, &PositionPiece{Index: index, P: piece})
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return validMoves
|
||
}
|
||
|
||
// MoveS 获取某个位置的象能走的下一步位置
|
||
func (c *ChessJPZ) MoveS(x, y int) []*PositionPiece {
|
||
// 坐标转换成索引
|
||
index, err := XYToIndex(c.Width, c.Height, x, y)
|
||
if err != nil {
|
||
return nil
|
||
}
|
||
|
||
// 判断这个位置是不是象
|
||
o := c.Buf[index]
|
||
if !IsS(o) {
|
||
return nil
|
||
}
|
||
|
||
var validMoves []*PositionPiece
|
||
|
||
directions := QueenDirections
|
||
if IsWhite(o) {
|
||
directions = append(directions, Position{Y: 1})
|
||
} else {
|
||
directions = append(directions, Position{Y: -1})
|
||
}
|
||
|
||
// 遍历象的移动方向
|
||
for _, dir := range directions {
|
||
dx, dy := dir.X, dir.Y
|
||
newX, newY := x+dx, y+dy
|
||
|
||
// 检查新位置是否在棋盘范围内
|
||
if newX >= 0 && newX < c.Width && newY >= 0 && newY < c.Height {
|
||
// 获取新位置的索引
|
||
index, err := XYToIndex(c.Width, c.Height, newX, newY)
|
||
if err == nil {
|
||
// 获取新位置的棋子
|
||
piece := c.Buf[index]
|
||
|
||
if IsEmpty(piece) || (IsWhite(piece) != IsWhite(o)) {
|
||
validMoves = append(validMoves, &PositionPiece{Index: index, P: piece})
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return validMoves
|
||
}
|
||
|
||
// MoveM 获取某个位置的士能走的下一步位置
|
||
func (c *ChessJPZ) MoveM(x, y int) []*PositionPiece {
|
||
// 坐标转换成索引
|
||
index, err := XYToIndex(c.Width, c.Height, x, y)
|
||
if err != nil {
|
||
return nil
|
||
}
|
||
|
||
// 判断这个位置是不是士
|
||
o := c.Buf[index]
|
||
if !IsM(o) {
|
||
return nil
|
||
}
|
||
|
||
var validMoves []*PositionPiece
|
||
|
||
for _, dir := range QueenDirections {
|
||
dx, dy := dir.X, dir.Y
|
||
newX, newY := x+dx, y+dy
|
||
|
||
// 检查新位置是否在棋盘范围内
|
||
if newX >= 0 && newX < c.Width && newY >= 0 && newY < c.Height {
|
||
// 获取新位置的索引
|
||
index, err := XYToIndex(c.Width, c.Height, newX, newY)
|
||
if err == nil {
|
||
// 获取新位置的棋子
|
||
piece := c.Buf[index]
|
||
|
||
if IsEmpty(piece) || (IsWhite(piece) != IsWhite(o)) {
|
||
validMoves = append(validMoves, &PositionPiece{Index: index, P: piece})
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if IsWhite(o) && c.CambodianMove[MoveWM] && index == 4 {
|
||
index, err = XYToIndex(c.Width, c.Height, x, y+2)
|
||
if err == nil && IsEmpty(c.Buf[index]) {
|
||
validMoves = append(validMoves, &PositionPiece{Index: index})
|
||
}
|
||
}
|
||
if IsBlack(o) && c.CambodianMove[MoveBM] && index == 59 {
|
||
index, err = XYToIndex(c.Width, c.Height, x, y-2)
|
||
if err == nil && IsEmpty(c.Buf[index]) {
|
||
validMoves = append(validMoves, &PositionPiece{Index: index})
|
||
}
|
||
}
|
||
|
||
return validMoves
|
||
}
|
||
|
||
// MoveK 获取某个位置的王能走的下一步位置
|
||
func (c *ChessJPZ) MoveK(x, y int) []*PositionPiece {
|
||
// 坐标转换成索引
|
||
i, err := XYToIndex(c.Width, c.Height, x, y)
|
||
if err != nil {
|
||
return nil
|
||
}
|
||
|
||
// 判断这个位置是不是王
|
||
o := c.Buf[i]
|
||
if !IsK(o) {
|
||
return nil
|
||
}
|
||
|
||
var validMoves []*PositionPiece
|
||
|
||
for _, dir := range KingDirections {
|
||
dx, dy := dir.X, dir.Y
|
||
newX, newY := x+dx, y+dy
|
||
|
||
// 检查新位置是否在棋盘范围内
|
||
if newX >= 0 && newX < c.Width && newY >= 0 && newY < c.Height {
|
||
// 获取新位置的索引
|
||
index, err := XYToIndex(c.Width, c.Height, newX, newY)
|
||
if err == nil {
|
||
// 获取新位置的棋子
|
||
piece := c.Buf[index]
|
||
|
||
if IsEmpty(piece) || (IsWhite(piece) != IsWhite(o)) {
|
||
validMoves = append(validMoves, &PositionPiece{Index: index, P: piece})
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 没有移动过,不能被将军,不能有棋子
|
||
if IsWhite(o) && c.CambodianMove[MoveWK] {
|
||
if !c.IsCheck(false) {
|
||
//index, err := XYToIndex(c.Width, c.Height, x-1, y+2)
|
||
//if err == nil && IsEmpty(c.Buf[index]) {
|
||
// validMoves = append(validMoves, &PositionPiece{Index: index})
|
||
//}
|
||
//index, err = XYToIndex(c.Width, c.Height, x+1, y+2)
|
||
//if err == nil && IsEmpty(c.Buf[index]) {
|
||
// validMoves = append(validMoves, &PositionPiece{Index: index})
|
||
//}
|
||
index, err := XYToIndex(c.Width, c.Height, x-2, y+1)
|
||
if err == nil && IsEmpty(c.Buf[index]) {
|
||
validMoves = append(validMoves, &PositionPiece{Index: index})
|
||
}
|
||
index, err = XYToIndex(c.Width, c.Height, x+2, y+1)
|
||
if err == nil && IsEmpty(c.Buf[index]) {
|
||
validMoves = append(validMoves, &PositionPiece{Index: index})
|
||
}
|
||
}
|
||
}
|
||
// 没有移动过,不能被将军,不能有棋子
|
||
if IsBlack(o) && c.CambodianMove[MoveBK] {
|
||
// 不能被将军,不能有棋子
|
||
if !c.IsCheck(true) {
|
||
//index, err := XYToIndex(c.Width, c.Height, x-1, y-2)
|
||
//if err == nil && IsEmpty(c.Buf[index]) {
|
||
// validMoves = append(validMoves, &PositionPiece{Index: index})
|
||
//}
|
||
//index, err = XYToIndex(c.Width, c.Height, x+1, y-2)
|
||
//if err == nil && IsEmpty(c.Buf[index]) {
|
||
// validMoves = append(validMoves, &PositionPiece{Index: index})
|
||
//}
|
||
index, err := XYToIndex(c.Width, c.Height, x-2, y-1)
|
||
if err == nil && IsEmpty(c.Buf[index]) {
|
||
validMoves = append(validMoves, &PositionPiece{Index: index})
|
||
}
|
||
index, err = XYToIndex(c.Width, c.Height, x+2, y-1)
|
||
if err == nil && IsEmpty(c.Buf[index]) {
|
||
validMoves = append(validMoves, &PositionPiece{Index: index})
|
||
}
|
||
}
|
||
}
|
||
|
||
// 移动之后不能被将军
|
||
var moves []*PositionPiece
|
||
for _, v := range validMoves {
|
||
c.Move(i, v.Index)
|
||
if IsWhite(o) {
|
||
if !c.IsCheck(false) {
|
||
moves = append(moves, v)
|
||
}
|
||
} else {
|
||
if !c.IsCheck(true) {
|
||
moves = append(moves, v)
|
||
}
|
||
}
|
||
c.MoveBack()
|
||
}
|
||
|
||
return moves
|
||
}
|
||
|
||
// MoveP 获取某个位置的兵能走的下一步位置
|
||
func (c *ChessJPZ) MoveP(x, y int) []*PositionPiece {
|
||
// 坐标转换成索引
|
||
index, err := XYToIndex(c.Width, c.Height, x, y)
|
||
if err != nil {
|
||
return nil
|
||
}
|
||
|
||
// 判断这个位置是不是兵
|
||
o := c.Buf[index]
|
||
if !IsP(o) {
|
||
return nil
|
||
}
|
||
|
||
var validMoves []*PositionPiece
|
||
|
||
newY := y
|
||
isWhite := IsWhite(o)
|
||
if isWhite {
|
||
newY++
|
||
} else {
|
||
newY--
|
||
}
|
||
|
||
// 前进一格
|
||
index, err = XYToIndex(c.Width, c.Height, x, newY)
|
||
if err == nil && IsEmpty(c.Buf[index]) {
|
||
validMoves = append(validMoves, &PositionPiece{Index: index})
|
||
}
|
||
|
||
// 斜吃
|
||
index, err = XYToIndex(c.Width, c.Height, x-1, newY)
|
||
if err == nil && !IsEmpty(c.Buf[index]) && IsWhite(c.Buf[index]) != isWhite {
|
||
validMoves = append(validMoves, &PositionPiece{Index: index, P: c.Buf[index]})
|
||
}
|
||
index, err = XYToIndex(c.Width, c.Height, x+1, newY)
|
||
if err == nil && !IsEmpty(c.Buf[index]) && IsWhite(c.Buf[index]) != isWhite {
|
||
validMoves = append(validMoves, &PositionPiece{Index: index, P: c.Buf[index]})
|
||
}
|
||
|
||
return validMoves
|
||
}
|
||
|
||
func (c *ChessJPZ) movePositions(index int, flag bool) []*PositionPiece {
|
||
x, y, err := IndexToXY(c.Width, c.Height, index)
|
||
if err != nil {
|
||
return nil
|
||
}
|
||
|
||
o := c.Buf[index]
|
||
if IsEmpty(o) {
|
||
return nil
|
||
}
|
||
|
||
var moves []*PositionPiece
|
||
switch o {
|
||
case WR, BR:
|
||
moves = c.MoveR(x, y)
|
||
case WN, BN:
|
||
moves = c.MoveN(x, y)
|
||
case WS, BS:
|
||
moves = c.MoveS(x, y)
|
||
case WM, BM:
|
||
moves = c.MoveM(x, y)
|
||
case WK, BK:
|
||
moves = c.MoveK(x, y)
|
||
case WP, BP:
|
||
moves = c.MoveP(x, y)
|
||
}
|
||
|
||
// 被将军必须应将,走子后不能被将军
|
||
if !flag {
|
||
return moves
|
||
}
|
||
var moves2 []*PositionPiece
|
||
for _, v := range moves {
|
||
c.Move(index, v.Index)
|
||
if IsWhite(o) && c.IsCheck(false) || IsBlack(o) && c.IsCheck(true) {
|
||
c.MoveBack()
|
||
continue
|
||
}
|
||
c.MoveBack()
|
||
moves2 = append(moves2, v)
|
||
}
|
||
|
||
return moves2
|
||
}
|
||
|
||
// GetMovePositions 获取指定索引位置的棋子能走的位置
|
||
func (c *ChessJPZ) GetMovePositions(index int) []*PositionPiece {
|
||
return c.movePositions(index, true)
|
||
}
|
||
|
||
func (c *ChessJPZ) IsWhiteMove() bool {
|
||
return !c.Act
|
||
}
|
||
|
||
func (c *ChessJPZ) IsBlackMove() bool {
|
||
return c.Act
|
||
}
|
||
|
||
// GetIndexes 获取指定棋子的位置
|
||
func (c *ChessJPZ) GetIndexes(p Piece) []int {
|
||
var positions []int
|
||
for k, v := range c.Buf {
|
||
if v == p {
|
||
positions = append(positions, k)
|
||
}
|
||
}
|
||
return positions
|
||
}
|
||
|
||
// IsCheck 判断将军
|
||
func (c *ChessJPZ) IsCheck(isWhite bool) bool {
|
||
for k, v := range c.Buf {
|
||
if IsEmpty(v) || IsWhite(v) != isWhite {
|
||
continue
|
||
}
|
||
if !IsK(v) {
|
||
for _, vv := range c.movePositions(k, false) {
|
||
if IsK(vv.P) {
|
||
return true
|
||
}
|
||
}
|
||
} else {
|
||
// 白王是否吃黑王
|
||
x, y, _ := IndexToXY(c.Width, c.Height, k)
|
||
for _, dir := range KingDirections {
|
||
dx, dy := dir.X, dir.Y
|
||
newX, newY := x+dx, y+dy
|
||
|
||
// 检查新位置是否在棋盘范围内
|
||
if newX >= 0 && newX < c.Width && newY >= 0 && newY < c.Height {
|
||
// 获取新位置的索引
|
||
index, err := XYToIndex(c.Width, c.Height, newX, newY)
|
||
if err == nil {
|
||
if IsK(c.Buf[index]) {
|
||
return true
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
// IsCheckmate 判断将死
|
||
func (c *ChessJPZ) IsCheckmate(isWhite bool) bool {
|
||
if !c.IsCheck(isWhite) {
|
||
return false
|
||
}
|
||
// 走任何一步,都将军
|
||
for k, v := range c.Buf {
|
||
if !IsEmpty(v) && IsWhite(v) != isWhite {
|
||
for _, vv := range c.movePositions(k, false) {
|
||
c.Move(k, vv.Index)
|
||
if !c.IsCheck(isWhite) {
|
||
c.MoveBack()
|
||
return false
|
||
}
|
||
c.MoveBack()
|
||
}
|
||
}
|
||
}
|
||
|
||
return true
|
||
}
|
||
|
||
// CanMove 判断是否可以移动
|
||
func (c *ChessJPZ) CanMove(fromIndex, toIndex int) bool {
|
||
if fromIndex < 0 || fromIndex >= len(c.Buf) || toIndex < 0 || toIndex >= len(c.Buf) {
|
||
return false
|
||
}
|
||
|
||
if fromIndex == toIndex {
|
||
return false
|
||
}
|
||
|
||
from := c.Buf[fromIndex]
|
||
if IsEmpty(from) {
|
||
return false
|
||
}
|
||
|
||
// 检查操作权
|
||
if c.Act == IsWhite(from) {
|
||
return false
|
||
}
|
||
|
||
list := c.GetMovePositions(fromIndex)
|
||
for _, v := range list {
|
||
if v.Index == toIndex {
|
||
return true
|
||
}
|
||
}
|
||
|
||
return false
|
||
}
|
||
|
||
// Move 移动棋子
|
||
func (c *ChessJPZ) Move(fromIndex, toIndex int) *PositionPiece {
|
||
if fromIndex < 0 || fromIndex >= len(c.Buf) || toIndex < 0 || toIndex >= len(c.Buf) {
|
||
return nil
|
||
}
|
||
|
||
if fromIndex == toIndex {
|
||
return nil
|
||
}
|
||
|
||
from := c.Buf[fromIndex]
|
||
if IsEmpty(from) {
|
||
return nil
|
||
}
|
||
|
||
fromPiece := &PositionPiece{
|
||
Index: fromIndex,
|
||
P: c.Buf[fromIndex],
|
||
}
|
||
to := &PositionPiece{
|
||
Index: toIndex,
|
||
P: c.Buf[toIndex],
|
||
}
|
||
|
||
c.Records = append(c.Records, &RecordJPZ{
|
||
FromTo: [2]*PositionPiece{fromPiece, to},
|
||
CambodianMove: c.CambodianMove,
|
||
})
|
||
|
||
// 王,士移动
|
||
switch fromPiece.P {
|
||
case WK:
|
||
if c.CambodianMove[MoveWK] {
|
||
c.CambodianMove[MoveWK] = false
|
||
}
|
||
case BK:
|
||
if c.CambodianMove[MoveBK] {
|
||
c.CambodianMove[MoveBK] = false
|
||
}
|
||
case WM:
|
||
if c.CambodianMove[MoveWM] && fromIndex == 4 {
|
||
c.CambodianMove[MoveWM] = false
|
||
}
|
||
case BM:
|
||
if c.CambodianMove[MoveBM] && fromIndex == 59 {
|
||
c.CambodianMove[MoveBM] = false
|
||
}
|
||
}
|
||
|
||
// 兵升士
|
||
if IsP(fromPiece.P) {
|
||
_, y, err := IndexToXY(c.Width, c.Height, toIndex)
|
||
if err == nil {
|
||
if IsBlack(fromPiece.P) {
|
||
if y == 2 {
|
||
c.Buf[fromIndex] = BM
|
||
}
|
||
} else {
|
||
if y == 5 {
|
||
c.Buf[fromIndex] = WM
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
c.Buf[toIndex] = c.Buf[fromIndex]
|
||
c.Buf[fromIndex] = ""
|
||
return to
|
||
}
|
||
|
||
// MoveBack 悔棋一步
|
||
func (c *ChessJPZ) MoveBack() bool {
|
||
if len(c.Records) == 0 {
|
||
return false
|
||
}
|
||
|
||
record := c.Records[len(c.Records)-1]
|
||
c.Records = c.Records[:len(c.Records)-1]
|
||
|
||
c.CambodianMove = record.CambodianMove
|
||
c.Buf[record.FromTo[0].Index] = record.FromTo[0].P
|
||
c.Buf[record.FromTo[1].Index] = record.FromTo[1].P
|
||
return true
|
||
}
|
||
|
||
// IsStop 是否无子可走
|
||
func (c *ChessJPZ) IsStop(isWhite bool) bool {
|
||
for k, v := range c.Buf {
|
||
if !IsEmpty(v) && IsWhite(v) == isWhite {
|
||
if len(c.GetMovePositions(k)) > 0 {
|
||
return false
|
||
}
|
||
}
|
||
}
|
||
|
||
return true
|
||
}
|
||
|
||
// GetMoveAllPositions 所有可移动棋子及位置
|
||
func (c *ChessJPZ) GetMoveAllPositions(isWhite bool) [][2]*PositionPiece {
|
||
var positions [][2]*PositionPiece
|
||
for k, v := range c.Buf {
|
||
if !IsEmpty(v) && IsWhite(v) == isWhite {
|
||
for _, p := range c.GetMovePositions(k) {
|
||
positions = append(positions, [2]*PositionPiece{{
|
||
Index: k,
|
||
P: v,
|
||
}, p})
|
||
}
|
||
}
|
||
}
|
||
return positions
|
||
}
|