package xiangqi // pseudoMoves 生成指定方的伪合法着法(不考虑送将与对脸,由 LegalMoves 过滤) func (b *Board) pseudoMoves(s int) []Move { moves := make([]Move, 0, 48) // tryAdd 目标格为空或敌子时加入候选 tryAdd := func(fr, fc, tr, tc int) { if !inBoard(tr, tc) { return } if side(b[tr][tc]) == s { return } moves = append(moves, Move{fr, fc, tr, tc}) } for r := 0; r < 10; r++ { for c := 0; c < 9; c++ { p := b[r][c] if side(p) != s { continue } switch abs8(p) { case King: // 帅/将:九宫内横竖走一步 for _, d := range [][2]int{{1, 0}, {-1, 0}, {0, 1}, {0, -1}} { tr, tc := r+d[0], c+d[1] if inPalace(tr, tc, s) { tryAdd(r, c, tr, tc) } } case Advisor: // 仕/士:九宫内斜走一步 for _, d := range [][2]int{{1, 1}, {1, -1}, {-1, 1}, {-1, -1}} { tr, tc := r+d[0], c+d[1] if inPalace(tr, tc, s) { tryAdd(r, c, tr, tc) } } case Bishop: // 相/象:田字走,塞象眼判定,不能过河 for _, d := range [][2]int{{2, 2}, {2, -2}, {-2, 2}, {-2, -2}} { tr, tc := r+d[0], c+d[1] er, ec := r+d[0]/2, c+d[1]/2 if !inBoard(tr, tc) || b[er][ec] != 0 { continue } // 不能过河:红象只能在 5-9 行,黑象只能在 0-4 行 if (s == Red && tr < 5) || (s == Black && tr > 4) { continue } tryAdd(r, c, tr, tc) } case Knight: // 马:日字走,蹩马腿判定(马腿在直线相邻位置) knightSteps := [][4]int{ {-2, -1, -1, 0}, {-2, 1, -1, 0}, {2, -1, 1, 0}, {2, 1, 1, 0}, {-1, -2, 0, -1}, {1, -2, 0, -1}, {-1, 2, 0, 1}, {1, 2, 0, 1}, } for _, st := range knightSteps { tr, tc := r+st[0], c+st[1] lr, lc := r+st[2], c+st[3] if inBoard(tr, tc) && inBoard(lr, lc) && b[lr][lc] == 0 { tryAdd(r, c, tr, tc) } } case Rook: // 车:直线滑行,遇子停止(敌子可吃) for _, d := range [][2]int{{1, 0}, {-1, 0}, {0, 1}, {0, -1}} { tr, tc := r+d[0], c+d[1] for inBoard(tr, tc) { if b[tr][tc] == 0 { tryAdd(r, c, tr, tc) } else { tryAdd(r, c, tr, tc) break } tr += d[0] tc += d[1] } } case Cannon: // 炮:平移同车(不吃子),吃子需隔一个炮架 for _, d := range [][2]int{{1, 0}, {-1, 0}, {0, 1}, {0, -1}} { tr, tc := r+d[0], c+d[1] jumped := false for inBoard(tr, tc) { if !jumped { if b[tr][tc] == 0 { tryAdd(r, c, tr, tc) } else { jumped = true // 遇到炮架,之后找吃子目标 } } else { if b[tr][tc] != 0 { if side(b[tr][tc]) != s { tryAdd(r, c, tr, tc) } break } } tr += d[0] tc += d[1] } } case Pawn: // 兵/卒:向前一步,过河后可左右 forward := -1 if s == Black { forward = 1 } tryAdd(r, c, r+forward, c) if crossedRiver(r, s) { tryAdd(r, c, r, c-1) tryAdd(r, c, r, c+1) } } } } return moves } // kingsFacing 判断双方帅将是否对脸(同列且中间无子,规则禁止) func (b *Board) kingsFacing() bool { rr, rc := b.findKing(Red) br, bc := b.findKing(Black) if rr < 0 || br < 0 || rc != bc { return false } for r := br + 1; r < rr; r++ { if b[r][rc] != 0 { return false } } return true } // InCheck 判断指定方是否被将军(敌方任一伪着法可吃到帅/将) func (b *Board) InCheck(s int) bool { kr, kc := b.findKing(s) if kr < 0 { return true // 帅已不在(理论不会发生)视为被将 } for _, m := range b.pseudoMoves(-s) { if m.ToR == kr && m.ToC == kc { return true } } return false } // LegalMoves 生成指定方的所有合法着法(过滤走后被将军与帅将对脸) func (b *Board) LegalMoves(s int) []Move { pseudo := b.pseudoMoves(s) legal := make([]Move, 0, len(pseudo)) for _, m := range pseudo { captured := b.Apply(m) ok := !b.InCheck(s) && !b.kingsFacing() b.Undo(m, captured) if ok { legal = append(legal, m) } } return legal } // IsLegal 校验一步着法是否合法(联机走子校验入口) func (b *Board) IsLegal(m Move, s int) bool { if !inBoard(m.FromR, m.FromC) || !inBoard(m.ToR, m.ToC) { return false } if side(b[m.FromR][m.FromC]) != s { return false } for _, lm := range b.LegalMoves(s) { if lm == m { return true } } return false } // GameOver 判断走子方是否已无合法着法(被将死或困毙,即负) func (b *Board) GameOver(sideToMove int) bool { return len(b.LegalMoves(sideToMove)) == 0 }