Commit 90ded261 authored by 刘家荣's avatar 刘家荣 💬
Browse files

feat(AI) 没测试过

parent 09be9f36
Loading
Loading
Loading
Loading
+131 −12
Original line number Diff line number Diff line
package model;

import model.dataType.FlipStep;
import model.dataType.Step;
import model.game.Chess;
import model.game.Chessboard;
import model.game.Game;
import model.game.Square;
import model.dataType.*;
import model.game.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

//TODO: 悔棋时要由按钮来停止它?
public class AI extends Thread {
    
    public static final int DEPTH = 10;
    
    public static final double Q = 0.8;
    public static final double ALPHA = 2;
    
    public static final AI instance = new AI();
    
    @Override
    public void run() {
        try {
            Thread.sleep(2000);
            Game.instance.resolveStep(eval());
            Game.instance.resolveStep(eval0());
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
    
    public static Step eval(){
        for (Square square : Chessboard.instance.squareList) {
            if(square instanceof Chess chess && !chess.isReversal())
    public static Step eval0(){
        for (Chess chess : Game.instance.chessboard.chessList) {
            if(!chess.isReversal())
                return new FlipStep(chess.id);
        }
        return new FlipStep(0);
    }
    
    public Step eval() {
        Game game;
        double mark = -10000;
        Step bestStep = null;
        for (Step step: movableSteps(Game.instance.chessboard, Game.instance.hostColor.opponent())){
            double newMark = mark;
            game = Game.instance.clone();
            if(step instanceof FlipStep flipStep){
                newMark = directChangeEval(Game.instance, flipStep, game.hostColor.opponent(), true) + scoreboardMark(game);
            } else if(game.resolveStep(step) != null) {
                newMark = (1 - Q) * (directChangeEval(Game.instance, step, game.hostColor.opponent(), true) + scoreboardMark(game))
                    + Q * (DEPTH <= 0 ? 0 : ab_MinMaxEval(game, DEPTH, mark, game.hostColor, false));
            }
            if(newMark > mark) {
                bestStep = step;
                mark = newMark;
            }
        }
        return bestStep;
    }
    
    private double ab_MinMaxEval(Game game, int depth, double parent_ab, ChessColor player, boolean forMax) {
        
        double this_ab = forMax ? -10000 : 10000;
        //Step bestStep = null;
        for (Step step: movableSteps(game.chessboard, player)){
            double newMark = this_ab;
            Game nextState = game.clone();
            if(step instanceof FlipStep flipStep){
                newMark = directChangeEval(game, flipStep, player, forMax) + scoreboardMark(game);
            } else if(nextState.resolveStep(step) != null) {
                newMark = (1 - Q) * (directChangeEval(game, step, player, forMax) + scoreboardMark(game))
                    + Q * (depth <= 1 ? 0 : ab_MinMaxEval(nextState, depth - 1, this_ab, player.opponent(), !forMax));
            }
            if(forMax && newMark > this_ab) {
                this_ab = newMark;
                if (this_ab > parent_ab)
                    return parent_ab;
            }
            if(!forMax && newMark < this_ab) {
                this_ab = newMark;
                if (this_ab < parent_ab)
                    return parent_ab;
            }
        }
        return this_ab;
    }
    
    /**
     * 翻棋:
     *      自身价值期望
     *      宏观分布
     *      微观近邻
     *      是否联通
     * 走棋:
     * 吃棋:
     * (全部划掉)写不动太复杂的了
     */
    private double directChangeEval(Game game, Step step, ChessColor player, boolean forMax) {
        if(step instanceof FlipStep flipStep){
            return 0.1 * Arrays.stream(ChessType.values()).map(
                type -> type.points * (
                    allActiveChessOfColorAndType(game.chessboard, player.opponent(), type).size()
                    - allActiveChessOfColorAndType(game.chessboard, player, type).size()
                )).reduce(0, Integer::sum) / allHiddenChess(game.chessboard).size();
        } else {
            if(step instanceof EatStep eatStep){
                Chess chess = game.chessboard.getChessById(eatStep.dstChessId);
                if(!chess.isReversal()){
                    return -directChangeEval(game, new FlipStep(chess.id), player, forMax);
                }else return chess.chessType.points * (chess.chessColor == game.hostColor ? 1 : -1);
            } else return 0;
        }
    }
    
    private double scoreboardMark(Game game){
        double diff = game.getScoreboard()[0] - game.getScoreboard()[1];
        return Math.pow(Math.abs(diff), ALPHA) * Math.signum(diff) * (game.hostColor.opponent() == ChessColor.RED ? 1 : -1);
    }
    
    private List<Chess> allHiddenChess(Chessboard chessboard){
        return chessboard.chessList.stream().filter(chess -> !chess.isReversal()).toList();
    }
    
    private List<Chess> allActiveChessOfColor(Chessboard chessboard, ChessColor chessColor){
        return chessboard.chessList.stream().filter(chess -> chess.isReversal() && chess.chessColor == chessColor).toList();
    }
    
    private List<Chess> allActiveChessOfColorAndType(Chessboard chessboard, ChessColor chessColor, ChessType chessType){
        return chessboard.chessList.stream().filter(chess -> chess.isReversal() && chess.chessColor == chessColor && chess.chessType == chessType).toList();
    }
    
    private static List<Step> movableSteps(Chessboard chessboard, ChessColor player) {
        List<Step> steps = new ArrayList<>();
        for (Chess chess : chessboard.chessList) {
            if (chess.chessColor == player && chess.isReversal()) {
                steps = Stream.concat(CheckRule.searchDstList(chessboard, chess).stream().map(gridPoint -> {
                    Square dstSquare = chessboard.getSquareAtPoint(new Site(ChessColor.NONE, gridPoint));
                    return dstSquare instanceof Chess ?
                        new EatStep(chess.id, dstSquare.id, -1) : new MoveStep(chess.id, dstSquare.id);
                }), steps.stream()).collect(Collectors.toList());
            }else if (!chess.isReversal()){
                steps.add(new FlipStep(chess.id));
            }
        }
        return steps;
    }
}
+54 −38
Original line number Diff line number Diff line
package model.game;

import model.dataType.EatStep;
import model.dataType.MoveStep;
import model.dataType.Step;
import model.dataType.ChessColor;
import model.dataType.ChessType;
import model.dataType.Site;
import model.dataType.*;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.security.InvalidParameterException;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.NoSuchElementException;

/**
 * 这个类表示棋盘组建,其包含:
@@ -79,6 +75,7 @@ public class Chessboard implements Serializable, Cloneable {
    
    /**
     * 获取某一id对应的方格对象
     *
     * @param id 方格id
     */
    public Square getSquareById(int id) {
@@ -86,12 +83,25 @@ public class Chessboard implements Serializable, Cloneable {
            return id < 0 ? null :
                id < ID_DIVISION ? chessList.get(id) : squareList.get(id - ID_DIVISION);
        } catch (IndexOutOfBoundsException e) {
            throw new InvalidParameterException(String.valueOf(id));
            return null;
        }
    }
    
    /**
     * 获取某一id对应的棋子
     */
    public Chess getChessById(int id) {
        try {
            return id < 0 ? null :
                id < ID_DIVISION ? chessList.get(id) : null;
        } catch (IndexOutOfBoundsException e) {
            return null;
        }
    }
    
    /**
     * 获取棋盘中给定坐标的id
     *
     * @param site 棋盘坐标
     */
    public int getIdAtPoint(Site site) {
@@ -102,6 +112,7 @@ public class Chessboard implements Serializable, Cloneable {
    
    /**
     * 获取棋盘中给定坐标的id,但是会从表面坐标映射回真实坐标
     *
     * @param site 棋盘坐标
     */
    public int getIdAtVisualPoint(Site site) {
@@ -112,6 +123,7 @@ public class Chessboard implements Serializable, Cloneable {
    
    /**
     * 获取棋盘中给定坐标的方格子
     *
     * @param site 棋盘坐标
     */
    public Square getSquareAtPoint(Site site) {
@@ -120,6 +132,7 @@ public class Chessboard implements Serializable, Cloneable {
    
    /**
     * 获取棋盘中给定坐标的方格子,但是会从表面坐标映射回真实坐标
     *
     * @param site 棋盘坐标
     */
    public Square getSquareAtVisualPoint(Site site) {
@@ -135,6 +148,7 @@ public class Chessboard implements Serializable, Cloneable {
    
    /**
     * 获取某颜色的亡子堆
     *
     * @param chessColor 某颜色
     */
    public SideStack getSideStack(ChessColor chessColor) {
@@ -147,6 +161,7 @@ public class Chessboard implements Serializable, Cloneable {
    
    /**
     * 交换高亮的棋子和目的地方格,使棋子移过去,同时吃掉目的地的棋子
     *
     * @param square1 移动的棋子
     * @param square2 目的地方格
     * @return 如果成功,返回Step对象,失败返回null
@@ -270,4 +285,5 @@ public class Chessboard implements Serializable, Cloneable {
            throw new RuntimeException(e);
        }
    }
    
}