中国象棋

本文最后更新于: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/杂项/中国象棋/
作者
Melody
发布于
2023年5月25日
许可协议