Skip to content
This repository has been archived by the owner on Feb 21, 2022. It is now read-only.

Commit

Permalink
Подготовка к слиянию - испраление QBot
Browse files Browse the repository at this point in the history
  • Loading branch information
Chae4ek committed Sep 8, 2021
1 parent caf4f60 commit a2c8259
Show file tree
Hide file tree
Showing 20 changed files with 671 additions and 85 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@

1. В корне проекта выполнить команду: `mvn package`
- Можно добавить флаг `-DskipTests`, чтобы пропустить тесты
2. В папке *core/target/* выполнить: `java -jar QChess-core-0.0.1-jar-with-dependencies.jar`
2. В папке *core/target/* выполнить: `java -jar QChess-core-0.0.2-jar-with-dependencies.jar`
3. Ввести `s`, если вы хотите запустить сервер или `c`, чтобы запустить клиент.

Также в других папках лежат отдельные модули проекта:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,7 @@ private static io.deeplay.qchess.game.player.PlayerType getType(final PlayerType
case USER -> io.deeplay.qchess.game.player.PlayerType.REMOTE_PLAYER;
case EASYBOT -> io.deeplay.qchess.game.player.PlayerType.RANDOM_BOT;
case MEDIUMBOT -> io.deeplay.qchess.game.player.PlayerType.ATTACK_BOT;
// TODO: заменить на своего бота
case HARDBOT -> io.deeplay.qchess.game.player.PlayerType.ATTACK_BOT;
case HARDBOT -> io.deeplay.qchess.game.player.PlayerType.HARD_BOT;
};
}

Expand Down
4 changes: 2 additions & 2 deletions core/src/main/java/io/deeplay/qchess/core/Arena.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import io.deeplay.qchess.game.player.BotFactory.SpecificFactory;
import io.deeplay.qchess.game.player.RemotePlayer;
import io.deeplay.qchess.game.player.TimeWrapper;
import io.deeplay.qchess.qbot.QNegamaxTTBot;
import io.deeplay.qchess.qbot.QNegamaxBot;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
Expand All @@ -23,7 +23,7 @@ public class Arena {
private static final Map<String, Function<RemotePlayer, String>> optionalLogs =
Map.of(
"NegaMaxBot",
bot -> "Обращений к ТТ: " + ((QNegamaxTTBot) bot).getCountFindingTT());
bot -> "Обращений к ТТ: " + ((QNegamaxBot) bot).getCountFindingTT());
private static final RatingELO rating = new RatingELO();

static {
Expand Down
1 change: 0 additions & 1 deletion elo_rating.txt

This file was deleted.

10 changes: 7 additions & 3 deletions game/src/main/java/io/deeplay/qchess/game/model/Cell.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@
import static io.deeplay.qchess.game.model.Board.STD_BOARD_SIZE;

import com.google.gson.annotations.SerializedName;
import java.io.Serializable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Cell {
public class Cell implements Serializable {
public static final transient int[][] hashCodes = new int[STD_BOARD_SIZE][STD_BOARD_SIZE];

private static final transient Logger logger = LoggerFactory.getLogger(Cell.class);

static {
for (int i = 0; i < STD_BOARD_SIZE; ++i)
for (int j = 0; j < STD_BOARD_SIZE; ++j)
hashCodes[i][j] = (i * STD_BOARD_SIZE + j) * 10;
for (int j = 0; j < STD_BOARD_SIZE; ++j) hashCodes[i][j] = i * STD_BOARD_SIZE + j;
}

@SerializedName("column")
Expand Down Expand Up @@ -73,6 +73,10 @@ public int hashCode() {
return hashCodes[column][row];
}

public byte toByte() {
return (byte) hashCodes[column][row];
}

@Override
public boolean equals(final Object o) {
if (this == o) return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@ public abstract class Figure {
private static final transient Logger logger = LoggerFactory.getLogger(Figure.class);

static {
for (int i = 0; i < 8; ++i)
for (int j = 0; j < 8; ++j) {
hashCodes[0][0][i][j] = Cell.hashCodes[i][j];
hashCodes[0][1][i][j] = Cell.hashCodes[i][j] + 1;
hashCodes[1][0][i][j] = Cell.hashCodes[i][j] + 2;
hashCodes[1][1][i][j] = Cell.hashCodes[i][j] + 3;
for (int i = 0; i < Board.STD_BOARD_SIZE; ++i)
for (int j = 0; j < Board.STD_BOARD_SIZE; ++j) {
hashCodes[0][0][i][j] = Cell.hashCodes[i][j] * 10;
hashCodes[0][1][i][j] = Cell.hashCodes[i][j] * 10 + 1;
hashCodes[1][0][i][j] = Cell.hashCodes[i][j] * 10 + 2;
hashCodes[1][1][i][j] = Cell.hashCodes[i][j] * 10 + 3;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ public enum PlayerType {
LOCAL_PLAYER,
REMOTE_PLAYER,
RANDOM_BOT,
ATTACK_BOT
// TODO: добавить тип своего бота
ATTACK_BOT,
HARD_BOT
}
45 changes: 45 additions & 0 deletions qbot/src/main/java/io/deeplay/qchess/qbot/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package io.deeplay.qchess.qbot;

import static io.deeplay.qchess.qbot.profile.ParserKt.fill;
import static io.deeplay.qchess.qbot.profile.ParserKt.getProfilesMap;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import io.deeplay.qchess.qbot.profile.Profile;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.util.HashMap;

public class Main {

private static final Path toReplace = Paths.get("./data/profiles.gson");
private static final Path newContents = toReplace.resolveSibling("profiles_temp.json");

public static void main(final String[] args) throws IOException {
// Запуск парсинга профилей
fill();
try (final BufferedWriter writer =
Files.newBufferedWriter(
newContents,
StandardCharsets.UTF_8,
StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING)) {
new Gson()
.toJson(
getProfilesMap(),
new TypeToken<HashMap<String, Profile>>() {}.getType(),
writer);
}
Files.move(
newContents,
toReplace,
StandardCopyOption.REPLACE_EXISTING,
StandardCopyOption.ATOMIC_MOVE);
}
}
6 changes: 6 additions & 0 deletions qbot/src/main/java/io/deeplay/qchess/qbot/QBot.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import io.deeplay.qchess.game.exceptions.ChessError;
import io.deeplay.qchess.game.model.Color;
import io.deeplay.qchess.game.model.Move;
import io.deeplay.qchess.game.player.PlayerType;
import io.deeplay.qchess.game.player.RemotePlayer;
import io.deeplay.qchess.qbot.strategy.Strategy;
import java.util.List;
Expand Down Expand Up @@ -36,6 +37,11 @@ public Move getNextMove() throws ChessError {
return topMoves.get(new Random().nextInt(topMoves.size()));
}

@Override
public PlayerType getPlayerType() {
return PlayerType.HARD_BOT;
}

abstract List<Move> getTopMoves() throws ChessError;

abstract static class Builder {
Expand Down
122 changes: 122 additions & 0 deletions qbot/src/main/java/io/deeplay/qchess/qbot/QExpectimaxBot.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package io.deeplay.qchess.qbot;

import static io.deeplay.qchess.qbot.profile.ParserKt.fill;
import static io.deeplay.qchess.qbot.profile.ParserKt.getProfilesMap;

import io.deeplay.qchess.game.GameSettings;
import io.deeplay.qchess.game.exceptions.ChessError;
import io.deeplay.qchess.game.logics.EndGameDetector.EndGameType;
import io.deeplay.qchess.game.model.Color;
import io.deeplay.qchess.game.model.Move;
import io.deeplay.qchess.qbot.profile.Profile;
import io.deeplay.qchess.qbot.strategy.PestoStrategy;
import io.deeplay.qchess.qbot.strategy.Strategy;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

public class QExpectimaxBot extends QBot {
private final Profile enemyProfile;

protected QExpectimaxBot(
final GameSettings roomSettings,
final Color color,
final int searchDepth,
final Strategy strategy) {
this(roomSettings, color, searchDepth, strategy, null);
}

protected QExpectimaxBot(
final GameSettings roomSettings,
final Color color,
final int searchDepth,
final Strategy strategy,
final String enemyProfileStr) {
super(roomSettings, color, searchDepth, strategy, "ExpectiMaxBot");
if (enemyProfileStr != null) {
if (getProfilesMap().isEmpty()) fill();
enemyProfile = getProfilesMap().get(enemyProfileStr);
Objects.requireNonNull(enemyProfile, "Профиль " + enemyProfileStr + " не найден!");
} else enemyProfile = new Profile();
}

protected QExpectimaxBot(
final GameSettings roomSettings, final Color color, final int searchDepth) {
this(roomSettings, color, searchDepth, new PestoStrategy());
}

@Override
public List<Move> getTopMoves() throws ChessError {
final List<Move> topMoves = new ArrayList<>();
int maxGrade = color == Color.WHITE ? Integer.MIN_VALUE : Integer.MAX_VALUE;
final List<Move> allMoves = ms.getAllPreparedMoves(color);
for (final Move move : allMoves) {
final int curGrade = minimaxRootWithVirtualMove(move);
if (color == Color.WHITE) {
if (curGrade > maxGrade) {
maxGrade = curGrade;
topMoves.clear();
}
if (curGrade >= maxGrade) {
topMoves.add(move);
}
} else {
if (curGrade < maxGrade) {
maxGrade = curGrade;
topMoves.clear();
}
if (curGrade <= maxGrade) {
topMoves.add(move);
}
}
}
return topMoves;
}

/** Точка входа в минимакс после выполнения виртуального хода */
public int minimaxRootWithVirtualMove(final Move move) throws ChessError {
ms.move(move);
final int res = expectimax(depth, color.inverse());
ms.undoMove();
return res;
}

private int expectimax(final int curDepth, final Color curColor) throws ChessError {
if (curDepth == 0) return strategy.evaluateBoard(board);

final List<Move> moves = ms.getAllPreparedMoves(curColor);
final EndGameType endGameType = egd.updateEndGameStatus(moves, curColor);
egd.revertEndGameStatus();
if (endGameType != EndGameType.NOTHING)
return strategy.gradeIfTerminalNode(endGameType, curDepth);

if (color == curColor) {
int bestMoveValue = Integer.MIN_VALUE;
for (final Move move : moves) {
ms.move(move);
final int currentValue = expectimax(curDepth - 1, curColor.inverse());
ms.undoMove();
bestMoveValue = Math.max(bestMoveValue, currentValue);
}
return bestMoveValue;
} else {
double sum = 0;
for (final Move move : moves) {
ms.move(move);
final int currentValue = expectimax(curDepth - 1, curColor.inverse());
ms.undoMove();
Double coef;
final String fen = history.getBoardToStringForsythEdwards();
final Map<Move, Double> probs = enemyProfile.movesWithProb(fen);
if (probs.isEmpty()) coef = 1. / moves.size();
else {
coef = probs.get(move);
if (coef == null) coef = 0.0;
}
sum += currentValue * coef;
}
return (int) Math.round(sum);
}
}
}
2 changes: 1 addition & 1 deletion qbot/src/main/java/io/deeplay/qchess/qbot/QMinimaxBot.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public QMinimaxBot(final GameSettings roomSettings, final Color color, final int
}

public QMinimaxBot(final GameSettings roomSettings, final Color color) {
this(roomSettings, color, 3);
this(roomSettings, color, 6);
}

/**
Expand Down
Loading

0 comments on commit a2c8259

Please sign in to comment.