Files
2026-08-14 13:17:03 +08:00

153 lines
3.4 KiB
Go
Raw Permalink 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 xiangqi 实现中国象棋的完整规则:走子合法性、将军/将死判定与极大极小搜索 AI
package xiangqi
// 棋盘约定10 行 x 9 列row 0 为黑方底线上方row 9 为红方底线(下方)
// 棋子编码 int8正数=红方,负数=黑方,绝对值代表兵种:
// 1=帅/将 2=仕/士 3=相/象 4=马 5=车 6=炮 7=兵/卒
const (
King = 1 // 帅/将
Advisor = 2 // 仕/士
Bishop = 3 // 相/象
Knight = 4 // 马
Rook = 5 // 车
Cannon = 6 // 炮
Pawn = 7 // 兵/卒
)
// 红黑双方标识
const (
Red = 1 // 红方(下方,先手)
Black = -1 // 黑方(上方)
)
// Board 棋盘:[行][列]0 表示空位
type Board [10][9]int8
// pieceNames 棋子中文名:[兵种][0]=红名 [1]=黑名
var pieceNames = map[int][2]string{
King: {"帅", "将"}, Advisor: {"仕", "士"}, Bishop: {"相", "象"},
Knight: {"马", "马"}, Rook: {"车", "车"}, Cannon: {"炮", "炮"}, Pawn: {"兵", "卒"},
}
// PieceName 棋子中文名(按红黑区分)
func PieceName(p int8) string {
if p == 0 {
return ""
}
kind := int(p)
idx := 0
if p < 0 {
kind = -kind
idx = 1
}
return pieceNames[kind][idx]
}
// Initial 初始棋盘布局(红下黑上)
func Initial() Board {
var b Board
// 黑方(上方,负数)
back := []int8{Rook, Knight, Bishop, Advisor, King, Advisor, Bishop, Knight, Rook}
for c, p := range back {
b[0][c] = -p
}
b[2][1], b[2][7] = -Cannon, -Cannon
for c := 0; c < 9; c += 2 {
b[3][c] = -Pawn
}
// 红方(下方,正数)
for c, p := range back {
b[9][c] = p
}
b[7][1], b[7][7] = Cannon, Cannon
for c := 0; c < 9; c += 2 {
b[6][c] = Pawn
}
return b
}
// Move 一步着法:从 (FromR,FromC) 到 (ToR,ToC)
type Move struct {
FromR int `json:"from_r"` // 起点行
FromC int `json:"from_c"` // 起点列
ToR int `json:"to_r"` // 终点行
ToC int `json:"to_c"` // 终点列
}
// side 返回棋子所属方1红 -1黑 0空
func side(p int8) int {
if p > 0 {
return Red
}
if p < 0 {
return Black
}
return 0
}
// Apply 执行着法并返回被吃的棋子(调用方需自行保证合法性)
func (b *Board) Apply(m Move) int8 {
captured := b[m.ToR][m.ToC]
b[m.ToR][m.ToC] = b[m.FromR][m.FromC]
b[m.FromR][m.FromC] = 0
return captured
}
// Undo 撤销着法(配合 Apply 的返回值恢复现场,搜索用)
func (b *Board) Undo(m Move, captured int8) {
b[m.FromR][m.FromC] = b[m.ToR][m.ToC]
b[m.ToR][m.ToC] = captured
}
// findKing 找到指定方的帅/将位置
func (b *Board) findKing(s int) (int, int) {
for r := 0; r < 10; r++ {
for c := 3; c <= 5; c++ {
if int(b[r][c])*s > 0 && abs8(b[r][c]) == King {
return r, c
}
}
}
return -1, -1
}
// abs8 int8 绝对值
func abs8(v int8) int8 {
if v < 0 {
return -v
}
return v
}
// inBoard 坐标是否在棋盘内
func inBoard(r, c int) bool { return r >= 0 && r < 10 && c >= 0 && c < 9 }
// inPalace 坐标是否在指定方的九宫内
func inPalace(r, c, s int) bool {
if c < 3 || c > 5 {
return false
}
if s == Red {
return r >= 7 && r <= 9
}
return r >= 0 && r <= 2
}
// crossedRiver 兵卒是否已过河
func crossedRiver(r, s int) bool {
if s == Red {
return r <= 4
}
return r >= 5
}
// MoveDesc 着法的中文描述(如 "红车 (9,0)→(5,0)",用于聊天与记录展示)
func (b *Board) MoveDesc(m Move) string {
p := b[m.FromR][m.FromC]
color := "红"
if p < 0 {
color = "黑"
}
return color + PieceName(p)
}