中国象棋
本文最后更新于:2024年4月3日 晚上
中国象棋
花了三个晚上写的小游戏。勉强能玩。
我编写的程序里,AI 只能看自己的一步棋,所以很弱。但是看到他被我捉子会躲,被将军会垫子,还是产生了巨大的成就感。
这么着,就决定发到博客里了。
后来又花了三个晚上重写了部分代码,各方面都有进步。目前 AI 已经有了一些好的征兆,我想多少也有《FC 中国象棋》里简单难度的水平了吧……反正已经接近我这个臭棋篓子的水平了
一些说明
-
使用鼠标点击棋子进行操作
在任何情况下,点击我方棋子即选中该棋子。
被选中的棋子被品红色圆圈指出,其移动范围被白点标记,攻击范围被青色点标记。
选中棋子的情况下,点击要移动的格子即完成移动,按右键或点击空白处则取消选中。
-
键盘
E
键:红黑交换。同时也会让出行棋权键盘
L
键:投降键盘
O
键:直接获胜键盘
P
键:悔棋
Java 代码(version 0.5)
// 开始游戏的方法:执行 new ChessFrame();
class ChessFrame extends JFrame {
private int windowWidth = 900; //600;
private int windowHeight = 1020; //680;
private int cellEdge = 90; //60;
private GamePanel gamePanel = new GamePanel(this);
ChessFrame() {
setSize(windowWidth, windowHeight);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(gamePanel);
addKeyListener(gamePanel);
addMouseListener(gamePanel);
new Thread(gamePanel).start();
}
public int getCellEdge() {
return cellEdge;
}
public int getWindowWidth() {
return windowWidth;
}
public int getWindowHeight() {
return windowHeight;
}
}
class GamePanel extends JPanel implements Runnable, KeyListener, MouseListener {
public static final char NONE_FLAG = ' ';
public static final Color CHESS_FILL = new Color(240, 170, 70);
public static final String init = "cnxsksxnc r r p p p p p P P P P P R R CNXSKSXNC";
private static final String[] cnc = {"零", "一", "二", "三", "四", "五", "六", "七", "八", "九", "十"};
private int cellEdge;
private int ori;
private final ChessFrame root;
private char[] chessboard = new char[90];
public Player p1 = new Player(1, this); // 电脑
public Player p2 = new Player(0, this); // 玩家
private int selected = -1;
private ArrayList<Integer> selectedRange = null;
private boolean playerCheesing = true;
private int end = 0;
private int AI_level = 4; // 不能小于 1
private int lastMove = 0;
private LinkedList<String> chessManual = new LinkedList<>();
GamePanel(ChessFrame cf) {
this.root = cf;
cellEdge = cf.getCellEdge();
ori = cellEdge >> 1;
chessboard = init.toCharArray();
AI_level = Math.max(1, AI_level);
chessManual.add(new String(chessboard));
}
@Override
public void run() {
while (true) {
repaint();
if (end == 0) {
if (p1.lose) end |= 1;
if (p2.lose) end |= 2;
}
if (end == 0 && !playerCheesing) {
int analyse = AnalyseHelper.analyse(boardToString(), AI_level, lastMove);
movePiece(analyse & 0b1111_1111, analyse >> 8);
playerCheesing = true;
}
}
}
@Override
public void paint(Graphics g) {
drawBoard(g);
drawPieces(g);
drawSelectedPiece(g);
drawMovedBox(g);
if (!playerCheesing) drawAICheesing(g);
if (end > 0) drawEndGame(g, end == 1);
}
private void drawAICheesing(Graphics g) {
if (end > 0) return;
g.setColor(Color.LIGHT_GRAY);
g.fillRect(0, (root.getWindowHeight() >> 1) - ((cellEdge + 3 * ori) >> 1), root.getWindowWidth(), ori);
g.setFont(new Font("华文行楷", Font.BOLD, (3 * ori >> 1)));
String out = "对手正在思考……";
Color c = new Color(30, 30, 50);
g.setColor(c);
g.drawString(out, root.getWindowWidth() >> 2, (root.getWindowHeight() >> 1) - cellEdge);
}
private void drawEndGame(Graphics g, boolean win) {
g.setColor(win ? Color.WHITE : Color.LIGHT_GRAY);
g.fillRect(0, (root.getWindowHeight() >> 1) - ((cellEdge + 3 * ori) >> 1), root.getWindowWidth(), ori);
g.setFont(new Font("华文行楷", Font.BOLD, (3 * ori >> 1)));
String out = win ? "技高一筹" : "棋差一着";
Color c = win ? new Color(200, 50, 50) : new Color(30, 30, 50);
g.setColor(c);
g.drawString(out, root.getWindowWidth() >> 1, (root.getWindowHeight() >> 1) - cellEdge);
}
private void drawBoard(Graphics g) {
g.setColor(Color.ORANGE);
g.fillRect(0, 0, cellEdge * 9, cellEdge * 10);
g.setColor(Color.BLACK);
g.drawRect(ori, ori, cellEdge * 8, cellEdge * 9);
g.drawRect(ori - (ori >> 3), ori - (ori >> 3), cellEdge * 8 + (ori >> 3) * 2, cellEdge * 9 + (ori >> 3) * 2);
for (int i = 1; i <= 8; i++) {
g.drawLine(ori, ori + cellEdge * i, ori + 8 * cellEdge, ori + cellEdge * i);
}
for (int i = 1; i <= 7; i++) {
g.drawLine(ori + cellEdge * i, ori, ori + cellEdge * i, ori + cellEdge * 4);
g.drawLine(ori + cellEdge * i, ori + cellEdge * 5, ori + cellEdge * i, ori + cellEdge * 9);
}
g.drawLine(ori + 3 * cellEdge, ori, ori + 5 * cellEdge, ori + 2 * cellEdge);
g.drawLine(ori + 3 * cellEdge, ori + 2 * cellEdge, ori + 5 * cellEdge, ori);
g.drawLine(ori + 3 * cellEdge, ori + 7 * cellEdge, ori + 5 * cellEdge, ori + 9 * cellEdge);
g.drawLine(ori + 3 * cellEdge, ori + 9 * cellEdge, ori + 5 * cellEdge, ori + 7 * cellEdge);
for (int i = 0; i < 5; i++) {
int dir = (i == 0 ? 0 : 1) | (i == 4 ? 0 : 2);
drawBroadALine(i * 2, 3, dir, g);
drawBroadALine(i * 2, 6, dir, g);
}
drawBroadALine(1, 2, 3, g);
drawBroadALine(7, 2, 3, g);
drawBroadALine(1, 7, 3, g);
drawBroadALine(7, 7, 3, g);
}
private void drawBroadALine(int x, int y, int dir, Graphics g) {
int small = cellEdge >> 3;
int small2 = small * 2;
if ((dir & 1) > 0) {
g.drawLine(ori + x * cellEdge - small, ori + y * cellEdge - small2, ori + x * cellEdge - small, ori + y * cellEdge - small);
g.drawLine(ori + x * cellEdge - small2, ori + y * cellEdge - small, ori + x * cellEdge - small, ori + y * cellEdge - small);
g.drawLine(ori + x * cellEdge - small, ori + y * cellEdge + small2, ori + x * cellEdge - small, ori + y * cellEdge + small);
g.drawLine(ori + x * cellEdge - small2, ori + y * cellEdge + small, ori + x * cellEdge - small, ori + y * cellEdge + small);
}
if ((dir & 2) > 0) {
g.drawLine(ori + x * cellEdge + small, ori + y * cellEdge - small2, ori + x * cellEdge + small, ori + y * cellEdge - small);
g.drawLine(ori + x * cellEdge + small2, ori + y * cellEdge - small, ori + x * cellEdge + small, ori + y * cellEdge - small);
g.drawLine(ori + x * cellEdge + small, ori + y * cellEdge + small2, ori + x * cellEdge + small, ori + y * cellEdge + small);
g.drawLine(ori + x * cellEdge + small2, ori + y * cellEdge + small, ori + x * cellEdge + small, ori + y * cellEdge + small);
}
}
private void drawPieces(Graphics g) {
for (int t = 0; t < 90; t++) {
char piece = chessboard[t];
if (piece != NONE_FLAG)
drawSingleChess(g, t % 9, t / 9, piece, Character.isUpperCase(piece) ? p2.color : p1.color);
}
}
private void drawSingleChess(Graphics g, int x, int y, char species, Color cc) {
int chessLength = (cellEdge >> 2) + (cellEdge >> 3) + (cellEdge >> 4);
int outRing = (int) (chessLength * 0.83);
int half_fontsize = (int) (chessLength * 0.707 * 0.9);
g.setColor(CHESS_FILL);
g.fillOval(ori + x * cellEdge - chessLength, ori + y * cellEdge - chessLength, chessLength * 2, chessLength * 2);
g.setColor(Color.BLACK);
g.drawOval(ori + x * cellEdge - chessLength, ori + y * cellEdge - chessLength, chessLength * 2, chessLength * 2);
g.drawOval(ori + x * cellEdge - outRing, ori + y * cellEdge - outRing, outRing * 2, outRing * 2);
g.setColor(cc);
g.setFont(new Font("楷体", Font.BOLD, half_fontsize * 2));
g.drawString(Piece.chineseCharacter.getOrDefault(species, ' ') + "", ori + x * cellEdge - half_fontsize, ori + y * cellEdge + half_fontsize);
}
private void drawSelectedPiece(Graphics g) {
if (selectedRange == null || selected == -1) return;
int bullSize = cellEdge >> 3;
char selectedPiece = chessboard[selected];
if (selectedPiece == '\u0000') return;
for (int i : selectedRange) {
int x = Math.abs(i) % 9, y = Math.abs(i) / 9;
if (i < 0) g.setColor(Color.CYAN);
else g.setColor(Color.WHITE);
g.fillOval(ori + x * cellEdge - bullSize, ori + y * cellEdge - bullSize, bullSize * 2, bullSize * 2);
}
g.setColor(Color.MAGENTA);
g.fillOval(ori + (selected % 9) * cellEdge - (cellEdge >> 1), ori + (selected / 9) * cellEdge - (cellEdge >> 1), cellEdge, cellEdge);
drawSingleChess(g, selected % 9, selected / 9, selectedPiece, Character.isUpperCase(selectedPiece) ? p2.color : p1.color);
}
private void drawMovedBox(Graphics g) {
if (lastMove <= 0) return;
int old = lastMove & 0b1111_1111, move = lastMove >> 8;
int x = old % 9, y = old / 9, mx = move % 9, my = move / 9;
int chessLength = (cellEdge >> 2) + (cellEdge >> 3) + (cellEdge >> 4);
g.setColor(Color.BLUE);
g.drawRect(ori + x * cellEdge - chessLength, ori + y * cellEdge - chessLength, chessLength * 2, chessLength * 2);
g.drawRect(ori + mx * cellEdge - chessLength, ori + my * cellEdge - chessLength, chessLength * 2, chessLength * 2);
}
public char checkPos(int p) {
if (p < 0 || p > 89) return NONE_FLAG;
return chessboard[p];
}
public void movePiece(int ori, int np) {
if (chessboard[np] == 'k') p1.lose = true;
else if (chessboard[np] == 'K') p2.lose = true;
char p = chessboard[ori];
int ox = 9 - (ori % 9), oy = (ori / 9) + 1, nx = 9 - (np % 9), ny = (np / 9) + 1;
boolean rp = Character.isUpperCase(p);
if (!rp) {
ox = 10 - ox;
oy = 11 - oy;
nx = 10 - nx;
ny = 11 - ny;
}
System.out.print(Piece.chineseCharacter.get(p) + "" + (rp ? cnc[ox] : ox));
if (p == 'N' || p == 'n' || p == 'x' || p == 'X' || p == 's' || p == 'S')
System.out.println((ny > oy ? "退" : "进") + (rp ? cnc[nx] : nx));
else {
if (ny == oy) System.out.println("平" + (rp ? cnc[nx] : nx));
else if (ny > oy) System.out.println("退" + (rp ? cnc[ny - oy] : ny - oy));
else System.out.println("进" + (rp ? cnc[oy - ny] : oy - ny));
}
chessboard[np] = p;
chessboard[ori] = NONE_FLAG;
chessManual.add(new String(chessboard));
lastMove = ori | (np << 8);
}
private void repent() {
if (!playerCheesing || end > 0 || chessManual.size() < 3) return;
selected = -1;
selectedRange = null;
System.out.println("------> 悔棋");
chessManual.removeLast();
chessManual.removeLast();
chessboard = chessManual.getLast().toCharArray();
}
public String boardToString() {
return new String(chessboard);
}
private void setSelected(int p) {
selected = p;
if (selected < 0 || selected > 89 || chessboard[selected] == NONE_FLAG) {
selectedRange = null;
} else if (Character.isUpperCase(chessboard[selected])) {
selectedRange = Piece.getRange(boardToString(), p);
} else {
selectedRange = new ArrayList<>();
}
}
private void reverse() {
for (int t = 0; t < 45; t++) {
char c = chessboard[t], ct = chessboard[89 - t];
if (c == ' ') chessboard[89-t] = c;
else if (Character.isUpperCase(c)) chessboard[89-t] = Character.toLowerCase(c);
else chessboard[89-t] = Character.toUpperCase(c);
if (ct == ' ') chessboard[t] = ct;
else if (Character.isUpperCase(ct)) chessboard[t] = Character.toLowerCase(ct);
else chessboard[t] = Character.toUpperCase(ct);
}
playerCheesing = !playerCheesing;
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_W:
playerCheesing = false;
break;
case KeyEvent.VK_L:
p2.lose = true;
break;
case KeyEvent.VK_O:
p1.lose = true;
break;
case KeyEvent.VK_P:
repent();
break;
case KeyEvent.VK_R:
reverse();
break;
}
}
@Override
public void mouseClicked(MouseEvent e) {
if (!playerCheesing || end > 0 || e.getButton() != MouseEvent.BUTTON1) {
setSelected(-1);
return;
}
int y = (e.getY() - ori) / cellEdge, x = e.getX() / cellEdge;
int cp = y * 9 + x;
if (selected >= 0 && selectedRange != null) {
for (int p : selectedRange) {
if (Math.abs(p) == cp) {
movePiece(selected, cp);
setSelected(-1);
playerCheesing = false;
return;
}
}
}
if (y >= 0 && x >= 0 && y < 10 && x < 9) setSelected(y * 9 + x);
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
}
class Player {
public boolean lose = false;
private final GamePanel broad;
public final Color color;
public Player(int p, GamePanel broad) {
this.broad = broad;
if ((p & 1) == 0) {
color = new Color(200, 0, 0);
} else {
color = new Color(0, 100, 0);
}
}
}
class Piece {
// 以下各标记,大写为红方,小写为黑方
public static final char K = 'K'; // 帥将
public static final char C = 'C'; // 俥車
public static final char N = 'N'; // 傌馬
public static final char P = 'P'; // 兵卒
public static final char X = 'X'; // 相象
public static final char R = 'R'; // 炮砲
public static final char S = 'S'; // 仕士
public static final Map<Character, Character> chineseCharacter = new HashMap<>();
static {
chineseCharacter.put('K', '帥');
chineseCharacter.put('k', '将');
chineseCharacter.put('C', '俥');
chineseCharacter.put('c', '車');
chineseCharacter.put('N', '傌');
chineseCharacter.put('n', '馬');
chineseCharacter.put('P', '兵');
chineseCharacter.put('p', '卒');
chineseCharacter.put('X', '相');
chineseCharacter.put('x', '象');
chineseCharacter.put('R', '炮');
chineseCharacter.put('r', '砲');
chineseCharacter.put('S', '仕');
chineseCharacter.put('s', '士');
}
public static ArrayList<Integer> getRange(String state, int p) {
switch (state.charAt(p)) {
case 'K':
case 'k':
return getKRange(state, p);
case 'C':
case 'c':
return getCRange(state, p);
case 'N':
case 'n':
return getNRange(state, p);
case 'P':
case 'p':
return getPRange(state, p);
case 'X':
case 'x':
return getXRange(state, p);
case 'R':
case 'r':
return getRRange(state, p);
case 'S':
case 's':
return getSRange(state, p);
default:
return new ArrayList<>();
}
}
private static ArrayList<Integer> getKRange(String state, int p) {
int y = p / 9, x = p % 9;
ArrayList<Integer> res = new ArrayList<>();
if (x > 3) checkToAdd(state, p, x - 1, y, res);
if (x < 5) checkToAdd(state, p, x + 1, y, res);
if ((y > 0 && y < 3) || (y > 7)) checkToAdd(state, p, x, y - 1, res);
if ((y < 2) || (y > 6 && y < 9)) checkToAdd(state, p, x, y + 1, res);
return res;
}
private static ArrayList<Integer> getCRange(String state, int p) {
int y = p / 9, x = p % 9;
ArrayList<Integer> res = new ArrayList<>();
for (int i = 1; ; i++) if (checkToAdd(state, p, x + i, y, res)) break;
for (int i = 1; ; i++) if (checkToAdd(state, p, x - i, y, res)) break;
for (int i = 1; ; i++) if (checkToAdd(state, p, x, y + i, res)) break;
for (int i = 1; ; i++) if (checkToAdd(state, p, x, y - i, res)) break;
return res;
}
private static ArrayList<Integer> getNRange(String state, int p) {
int y = p / 9, x = p % 9;
ArrayList<Integer> res = new ArrayList<>();
if (checkPos(x - 1, y) && state.charAt(p - 1) == GamePanel.NONE_FLAG) {
checkToAdd(state, p, x - 2, y + 1, res);
checkToAdd(state, p, x - 2, y - 1, res);
}
if (checkPos(x + 1, y) && state.charAt(p + 1) == GamePanel.NONE_FLAG) {
checkToAdd(state, p, x + 2, y + 1, res);
checkToAdd(state, p, x + 2, y - 1, res);
}
if (checkPos(x, y - 1) && state.charAt(p - 9) == GamePanel.NONE_FLAG) {
checkToAdd(state, p, x + 1, y - 2, res);
checkToAdd(state, p, x - 1, y - 2, res);
}
if (checkPos(x, y + 1) && state.charAt(p + 9) == GamePanel.NONE_FLAG) {
checkToAdd(state, p, x - 1, y + 2, res);
checkToAdd(state, p, x + 1, y + 2, res);
}
return res;
}
private static ArrayList<Integer> getPRange(String state, int p) {
int y = p / 9, x = p % 9;
char c = state.charAt(p);
ArrayList<Integer> res = new ArrayList<>();
if (Character.isLowerCase(c)) {
if (y < 5) checkToAdd(state, p, x, y + 1, res);
else {
checkToAdd(state, p, x - 1, y, res);
checkToAdd(state, p, x + 1, y, res);
checkToAdd(state, p, x, y + 1, res);
}
} else {
if (y > 4) checkToAdd(state, p, x, y - 1, res);
else {
checkToAdd(state, p, x - 1, y, res);
checkToAdd(state, p, x + 1, y, res);
checkToAdd(state, p, x, y - 1, res);
}
}
return res;
}
private static ArrayList<Integer> getXRange(String state, int p) {
int y = p / 9, x = p % 9;
ArrayList<Integer> res = new ArrayList<>();
char c = state.charAt(p);
if (p < 45) {
if (checkPos(x - 2, y - 2) && state.charAt(p - 10) == GamePanel.NONE_FLAG) checkToAdd(state, p, x - 2, y - 2, res);
if (y < 3 && checkPos(x + 2, y + 2) && state.charAt(p + 10) == GamePanel.NONE_FLAG)
checkToAdd(state, p, x + 2, y + 2, res);
if (checkPos(x + 2, y - 2) && state.charAt(p - 8) == GamePanel.NONE_FLAG) checkToAdd(state, p, x + 2, y - 2, res);
if (y < 3 && checkPos(x - 2, y + 2) && state.charAt(p + 8) == GamePanel.NONE_FLAG) checkToAdd(state, p, x - 2, y + 2, res);
} else {
if (checkPos(x - 2, y + 2) && state.charAt(p + 8) == GamePanel.NONE_FLAG) checkToAdd(state, p, x - 2, y + 2, res);
if (y > 6 && checkPos(x + 2, y - 2) && state.charAt(p - 8) == GamePanel.NONE_FLAG) checkToAdd(state, p, x + 2, y - 2, res);
if (checkPos(x + 2, y + 2) && state.charAt(p + 10) == GamePanel.NONE_FLAG) checkToAdd(state, p, x + 2, y + 2, res);
if (y > 6 && checkPos(x - 2, y - 2) && state.charAt(p - 10) == GamePanel.NONE_FLAG)
checkToAdd(state, p, x - 2, y - 2, res);
}
return res;
}
private static ArrayList<Integer> getRRange(String state, int p) {
int y = p / 9, x = p % 9;
ArrayList<Integer> res = new ArrayList<>();
for (int i = 1, f = 0; x + i < 9; i++) {
char temp = state.charAt(p + i);
if (temp != GamePanel.NONE_FLAG) {
if (f == 1) {
checkToAdd(state, p, x + i, y, res);
break;
}
f = 1;
} else if (f == 0) {
checkToAdd(state, p, x + i, y, res);
}
}
for (int i = 1, f = 0; x - i >= 0; i++) {
char temp = state.charAt(p - i);
if (temp != GamePanel.NONE_FLAG) {
if (f == 1) {
checkToAdd(state, p, x - i, y, res);
break;
}
f = 1;
} else if (f == 0) {
checkToAdd(state, p, x - i, y, res);
}
}
for (int i = 1, f = 0; y + i < 10; i++) {
char temp = state.charAt(p + 9 * i);
if (temp != GamePanel.NONE_FLAG) {
if (f == 1) {
checkToAdd(state, p, x, y + i, res);
break;
}
f = 1;
} else if (f == 0) {
checkToAdd(state, p, x, y + i, res);
}
}
for (int i = 1, f = 0; y - i >= 0; i++) {
char temp = state.charAt(p - i * 9);
if (temp != GamePanel.NONE_FLAG) {
if (f == 1) {
checkToAdd(state, p, x, y - i, res);
break;
}
f = 1;
} else if (f == 0) {
checkToAdd(state, p, x, y - i, res);
}
}
return res;
}
private static ArrayList<Integer> getSRange(String state, int p) {
int y = p / 9, x = p % 9;
ArrayList<Integer> res = new ArrayList<>();
boolean up = Character.isUpperCase(state.charAt(p));
if (x == 4) {
checkToAdd(state, p, p + 10, res);
checkToAdd(state, p, p + 8, res);
checkToAdd(state, p, p - 8, res);
checkToAdd(state, p, p - 10, res);
} else checkToAdd(state, p, 4, up ? 8 : 1, res);
return res;
}
private static boolean checkToAdd(String state, int p, int np, ArrayList<Integer> res) {
char s = state.charAt(p);
if (!checkPos(np)) return true;
char tar = state.charAt(np);
if (tar == GamePanel.NONE_FLAG) res.add(np);
else {
if (Character.isUpperCase(tar) ^ Character.isUpperCase(s)) res.add(-np);
return true;
}
return false;
}
private static boolean checkToAdd(String state, int p, int x, int y, ArrayList<Integer> res) {
char s = state.charAt(p);
if (!checkPos(x, y)) return true;
char tar = state.charAt(y * 9 + x);
if (tar == GamePanel.NONE_FLAG) res.add(y * 9 + x);
else {
if (Character.isUpperCase(tar) ^ Character.isUpperCase(s)) res.add(-(y * 9 + x));
return true;
}
return false;
}
private static boolean checkPos(int x, int y) {
return x < 9 && x >= 0 && y < 10 && y >= 0;
}
private static boolean checkPos(int p) {
return p >= 0 && p < 90;
}
private Piece() {
}
}
class AnalyseHelper {
public static Map<Character, int[]> pos_value = new HashMap<>();
public static Map<Character, Integer> value = new HashMap<>();
private static AnalyseHelper root = null;
static {
int[] TEMP = new int[90];
pos_value.put('K', TEMP);
value.put('K', 100000);
pos_value.put('C', TEMP);
value.put('C', 100);
pos_value.put('N', TEMP);
value.put('N', 45);
pos_value.put('R', TEMP);
value.put('R', 45);
pos_value.put('X', TEMP);
value.put('X', 20);
pos_value.put('S', TEMP);
value.put('S', 20);
pos_value.put('P', TEMP);
value.put('P', 9);
value.put(' ', 0);
}
public static int analyse(String states, int level, int move) {
if (root == null || !root.next.containsKey(move) || !root.next.get(move).curr.equals(states)) {
root = new AnalyseHelper(states);
root.step = -move;
} else root = root.next.get(move);
root = root.analyse(level);
return Math.abs(root.step);
}
private AnalyseHelper() {
}
private AnalyseHelper(String lCurr, int step) {
next = new HashMap<>();
this.step = step;
char[] temp = lCurr.toCharArray();
int lp = Math.abs(step) & 0b1111_1111, np = Math.abs(step) >> 8;
temp[np] = temp[lp];
temp[lp] = ' ';
this.curr = new String(temp);
score = getScore();
}
private AnalyseHelper(String init) {
next = new HashMap<>();
step = 0;
curr = init;
score = getScore();
}
private Map<Integer, AnalyseHelper> next = null;
private String curr = null;
private int step = 0; // 大于 0 则当前为 AI 行棋,否则为 玩家 行棋
private int score = 0;
private AnalyseHelper analyse(int level) {
if (level <= 0) return this;
int resS = step > 0 ? 100000 : -100000;
AnalyseHelper res = null;
if (next != null && next.size() > 0) {
for (AnalyseHelper a : next.values()) {
a.analyse(level - 1);
if ((step > 0) ^ (a.score > resS)) {
resS = a.score;
res = a;
}
}
} else if (step <= 0) {
Set<Integer> move = new HashSet<>();
for (int p = 0; p < 90; p++) {
char c = curr.charAt(p);
if (c >= 'a' && c <= 'z') {
for (int np: Piece.getRange(curr, p)) move.add(p | (Math.abs(np) << 8));
}
}
for (int nm : move) {
AnalyseHelper na = new AnalyseHelper(curr, nm);
next.put(nm, na);
na.analyse(level - 1);
if (na.score > resS) {
resS = na.score;
res = na;
}
}
} else {
Set<Integer> move = new HashSet<>();
for (int p = 0; p < 90; p++) {
char c = curr.charAt(p);
if (c >= 'A' && c <= 'Z') {
for (int np: Piece.getRange(curr, p)) move.add(p | (Math.abs(np) << 8));
}
}
for (int nm : move) {
AnalyseHelper na = new AnalyseHelper(curr, -nm);
next.put(nm, na);
na.analyse(level - 1);
if (na.score < resS) {
resS = na.score;
res = na;
}
}
}
if (res == null) return fail();
this.score = resS;
return res;
}
private int getScore() {
int currentScore = 0;
int[] scores = new int[90];
for (int p = 0; p < 90; p++) {
char c = curr.charAt(p);
if (c >= 'a' && c <= 'z') scores[p] = value.get(Character.toUpperCase(c));
else scores[p] = -value.get(c);
}
for (int p = 0; p < 90; p++) {
char c = curr.charAt(p);
if (c == GamePanel.NONE_FLAG) continue;
for (Integer r: Piece.getRange(curr, p)) {
if (r < 0) scores[Math.abs(r)] >>= 1;
}
}
for (int n : scores) currentScore += n;
return currentScore;
}
private AnalyseHelper fail() {
if (step > 0) this.score = -99999;
else this.score = 99999;
return this;
}
}
中国象棋
https://i-melody.github.io/2023/05/25/杂项/中国象棋/