Skip to content

Commit

Permalink
Merge branch 'chess'
Browse files Browse the repository at this point in the history
  • Loading branch information
tolstenko committed Mar 20, 2023
2 parents fb42040 + 9573935 commit 409e61a
Show file tree
Hide file tree
Showing 20 changed files with 403 additions and 250 deletions.
71 changes: 35 additions & 36 deletions examples/chess/Heuristics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,69 +2,68 @@
#include "WorldState.h"
#include "pieces/Pieces.h"

/*
Material. The easiest one to evaluate: just count up the value of your pieces vs. the value of your opponent's pieces. Pawn = 1, bishop/knight = 3,
rook = 5, and queen = 9 is generally a good rule.
Pawn structure. Isolated, passed, chained, backward, doubled, tripled, quadrupled (I have seen this happen): identify them in both your position and in your opponent's. Check also for any open files or half-open files either of you may have as a result of the pawn structure. You can also determine, using the pawn structure, the nature of the position: is it a closed position (the center is blocked by pawns) or an open position (the center is relatively free of pawns, with open files and diagonals)?
Space. Technically this would be done by counting up the squares controlled by each side, but it's pretty easy to gauge a spatial advantage by examining the pawns, which tend to mark out space both by forming a frontier between the two sides and by denying squares to the opponent. Weak squares / strong squares. Are there any squares that can't be protected by pawns? Also note that your weak squares are only really weak if they can be turned into strong squares for the opponent.
Minor piece imbalance. Easy to take stock of; difficult to evaluate. Do you have the same minor pieces (bishops and knights) as your opponent, or is it a bishop vs. knight situation? If it's bishop vs. bishop, are they on squares of the same color? Two bishops vs. knight and bishop? You get the idea.
Development. Who has more pieces out?
King safety. Are there any structural weaknesses in either king's position? Does one player have a bunch of attacking pieces near the opponent's king?
Initiative. This is probably the most difficult of all of them to figure out: the initiative is the ability to dictate the tempo of the game. Generally speaking, the initiative belongs to the player who is able to make threats, positional or tactical. The player with the initiative makes the opponent's plan subservient to his own--you will carry out your plan only if I want you to, and only on my terms.
*/
// Material. The easiest one to evaluate: just count up the value of your pieces vs. the value of your opponent's pieces. Pawn = 1, bishop/knight = 3,
//rook = 5, and queen = 9 is generally a good rule.
// Pawn structure. Isolated, passed, chained, backward, doubled, tripled, quadrupled (I have seen this happen): identify them in both your position and in your opponent's. Check also for any open files or half-open files either of you may have as a result of the pawn structure. You can also determine, using the pawn structure, the nature of the position: is it a closed position (the center is blocked by pawns) or an open position (the center is relatively free of pawns, with open files and diagonals)?
// Space. Technically this would be done by counting up the squares controlled by each side, but it's pretty easy to gauge a spatial advantage by examining the pawns, which tend to mark out space both by forming a frontier between the two sides and by denying squares to the opponent. Weak squares / strong squares. Are there any squares that can't be protected by pawns? Also note that your weak squares are only really weak if they can be turned into strong squares for the opponent.
// Minor piece imbalance. Easy to take stock of; difficult to evaluate. Do you have the same minor pieces (bishops and knights) as your opponent, or is it a bishop vs. knight situation? If it's bishop vs. bishop, are they on squares of the same color? Two bishops vs. knight and bishop? You get the idea.
// Development. Who has more pieces out?
// King safety. Are there any structural weaknesses in either king's position? Does one player have a bunch of attacking pieces near the opponent's king?
// Initiative. This is probably the most difficult of all of them to figure out: the initiative is the ability to dictate the tempo of the game. Generally speaking, the initiative belongs to the player who is able to make threats, positional or tactical. The player with the initiative makes the opponent's plan subservient to his own--you will carry out your plan only if I want you to, and only on my terms.


Heuristics Heuristics::BoardAnalysis(WorldState* state) {
// todo: write your own heuristic here
return {};
}
int Heuristics::materialScore(WorldState* state) {
int score=0;
for(auto line = 0; line < 8; line++){
for(auto column = 0; column < 8; column++){
auto location = Point2D(column,line);
int Heuristics::MaterialScore(WorldState* state) {
int score = 0;
for (auto line = 0; line < 8; line++) {
for (auto column = 0; column < 8; column++) {
auto location = Point2D(column, line);
auto piece = state->PieceAtPosition(location);
auto pieceScore=0;
auto moves =0;
switch (piece.piece) {
auto pieceScore = 0;
int moves;
switch (piece.Piece()) {
case PieceType::King:
pieceScore += 1000; // piece value
pieceScore += King::PossibleMoves(*state,location).size(); // mobility
pieceScore += 1000; // piece value
pieceScore += King::AttackMoves(*state, location).size(); // mobility
pieceScore += distanceToCenter(location);
// todo: king safety, check, draw and mate
break;
case PieceType::Queen:
pieceScore += 90; // piece value
pieceScore += Queen::PossibleMoves(*state,location).size(); // mobility
pieceScore += 90; // piece value
pieceScore += Queen::AttackMoves(*state, location).size(); // mobility
pieceScore += distanceToCenter(location);
break;
case PieceType::Rook:
pieceScore += 50; // piece value
pieceScore += Rook::PossibleMoves(*state,location).size(); // mobility
pieceScore += 50; // piece value
pieceScore += Rook::AttackMoves(*state, location).size(); // mobility
pieceScore += distanceToCenter(location);
break;
case PieceType::Knight:
pieceScore += 35; // piece value
pieceScore += Knight::PossibleMoves(*state,location).size(); // mobility
pieceScore += 35; // piece value
pieceScore += Knight::AttackMoves(*state, location).size(); // mobility
pieceScore += distanceToCenter(location);
break;
case PieceType::Bishop:
pieceScore += 30; // piece value
pieceScore += Bishop::PossibleMoves(*state,location).size(); // mobility
pieceScore += 30; // piece value
pieceScore += Bishop::AttackMoves(*state, location).size(); // mobility
pieceScore += distanceToCenter(location);
break;
case PieceType::Pawn:
pieceScore += 10; // piece value
moves = Pawn::PossibleMoves(*state,location).size(); // mobility
pieceScore += 10; // piece value
moves = Pawn::PossibleMoves(*state, location).size(); // mobility
pieceScore += distanceToCenter(location);
pieceScore += moves;
if(moves==0) pieceScore -= 5; // blocked
pieceScore -= 2*Pawn::CountDoubles(*state,location); // doubled
if(Pawn::IsIsolated(*state,location)) pieceScore -=5; // isolation
if (moves == 0) pieceScore -= 5; // blocked
pieceScore -= 2 * Pawn::CountDoubles(*state, location); // doubled
if (Pawn::IsIsolated(*state, location)) pieceScore -= 5; // isolation
break;
default:
continue;
}
if (piece.color==PieceColor::Black)
if (piece.Color() == PieceColor::Black)
score -= pieceScore;
else
score += pieceScore;
Expand All @@ -74,7 +73,7 @@ int Heuristics::materialScore(WorldState* state) {
}
int Heuristics::distanceToCenter(Point2D location) {
// todo: improve. I am unsure if this is the best way. I am doubling because I am targeting the center.
auto doubled = Point2D(location.x*2-7,location.y*2-7);
auto maxed = 3-(std::min(std::abs(doubled.x), std::abs(doubled.y))-1)/2;
auto doubled = Point2D(location.x * 2 - 7, location.y * 2 - 7);
auto maxed = 3 - (std::min(std::abs(doubled.x), std::abs(doubled.y)) - 1) / 2;
return maxed;
}
2 changes: 1 addition & 1 deletion examples/chess/Heuristics.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
struct Heuristics {
public:
inline float Score() const { return score; };
static int MaterialScore(WorldState* state);

private:
// positive score means white is winning
float score;

static int materialScore(WorldState* state);
static int distanceToCenter(Point2D location);

public:
Expand Down
56 changes: 32 additions & 24 deletions examples/chess/Manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "pieces/Rook.h"
#include <unordered_map>
#include "Search.h"
#include "Heuristics.h"

void Manager::OnGui(ImGuiContext* context) {
ImGui::SetCurrentContext(context);
Expand All @@ -18,21 +19,24 @@ void Manager::OnGui(ImGuiContext* context) {
ImGui::Text("%.1fms %.0fFPS | AVG: %.2fms %.1fFPS", ImGui::GetIO().DeltaTime * 1000, 1.0f / ImGui::GetIO().DeltaTime,
1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
ImGui::Separator();
if (ImGui::Button("Reset")) this->state.Reset();
if (ImGui::Button("Reset")) {
this->state.Reset();
score = Heuristics::MaterialScore(&state);
};
ImGui::SameLine();
if (ImGui::Button("Undo") && !previousStates.empty()) {
validMoves = {};
selected = {INT32_MIN, INT32_MIN};
state = previousStates.top();
previousStates.pop();
}
ImGui::Separator();

ImGui::LabelText("Score", "%.1f", score);
ImGui::Separator();

if (ImGui::Checkbox("AI Enabled", &aiEnabled))
if (aiEnabled == true)
aiColor = PieceColor::Black;
else
aiColor = PieceColor::NONE;
if (aiEnabled == true) aiColor = PieceColor::Black;

static bool aiIsBlackStatic = true;
if (aiEnabled) {
Expand Down Expand Up @@ -62,9 +66,9 @@ void Manager::OnGui(ImGuiContext* context) {

if (selected.x == INT32_MIN || !validMoves.contains(index)) { // if not selected
selected = index;
if (piece.piece != PieceType::NONE && piece.color == state.GetTurn()) {
if (piece.Piece() != PieceType::NONE && piece.Color() == state.GetTurn()) {
// todo: create isincheck behavior and reject valid moves that maintain king in check
validMoves = getMoves(piece.piece, index);
validMoves = getMoves(piece.Piece(), index);
if (validMoves.empty()) {
validMoves = {};
selected = {INT32_MIN, INT32_MIN};
Expand All @@ -77,6 +81,7 @@ void Manager::OnGui(ImGuiContext* context) {
previousStates.push(state);

state.Move(selected, index);
score = Heuristics::MaterialScore(&state);

cout << state.toString() << endl;
validMoves = {};
Expand Down Expand Up @@ -146,15 +151,15 @@ unordered_set<Point2D> Manager::getMoves(PieceType t, Point2D point) {
case PieceType::Pawn:
return Pawn::PossibleMoves(state, point);
case PieceType::Rook:
return Rook::PossibleMoves(state, point);
return Rook::AttackMoves(state, point);
case PieceType::Knight:
return Knight::PossibleMoves(state, point);
return Knight::AttackMoves(state, point);
case PieceType::Bishop:
return Bishop::PossibleMoves(state, point);
return Bishop::AttackMoves(state, point);
case PieceType::Queen:
return Queen::PossibleMoves(state, point);
return Queen::AttackMoves(state, point);
case PieceType::King:
return King::PossibleMoves(state, point);
return King::AttackMoves(state, point);
default:
return {};
}
Expand All @@ -174,29 +179,31 @@ Manager::Manager(Engine* pEngine) : GameObject(pEngine) {
state.Reset();
cout << state.toString() << endl;
// todo: use asset subsystem loading!
piecePackedToTexture[(uint8_t)PieceType::Pawn | (uint8_t)PieceColor::White] = Texture::LoadSVGFromString(engine->window->sdlRenderer, PawnSvgWhite);
piecePackedToTexture[(uint8_t)PieceType::Pawn | (uint8_t)PieceColor::Black] = Texture::LoadSVGFromString(engine->window->sdlRenderer, PawnSvgBlack);
piecePackedToTexture[PieceData(PieceColor::White, PieceType::Pawn).Pack()] = Texture::LoadSVGFromString(engine->window->sdlRenderer, PawnSvgWhite);
piecePackedToTexture[PieceData(PieceColor::Black, PieceType::Pawn).Pack()] = Texture::LoadSVGFromString(engine->window->sdlRenderer, PawnSvgBlack);

piecePackedToTexture[(uint8_t)PieceType::Knight | (uint8_t)PieceColor::White]
piecePackedToTexture[PieceData(PieceColor::White, PieceType::Knight).Pack()]
= Texture::LoadSVGFromString(engine->window->sdlRenderer, KnightSvgWhite);
piecePackedToTexture[(uint8_t)PieceType::Knight | (uint8_t)PieceColor::Black]
piecePackedToTexture[PieceData(PieceColor::Black, PieceType::Knight).Pack()]
= Texture::LoadSVGFromString(engine->window->sdlRenderer, KnightSvgBlack);

piecePackedToTexture[(uint8_t)PieceType::Bishop | (uint8_t)PieceColor::White]
piecePackedToTexture[PieceData(PieceColor::White, PieceType::Bishop).Pack()]
= Texture::LoadSVGFromString(engine->window->sdlRenderer, BishopSvgWhite);
piecePackedToTexture[(uint8_t)PieceType::Bishop | (uint8_t)PieceColor::Black]
piecePackedToTexture[PieceData(PieceColor::Black, PieceType::Bishop).Pack()]
= Texture::LoadSVGFromString(engine->window->sdlRenderer, BishopSvgBlack);

piecePackedToTexture[(uint8_t)PieceType::Rook | (uint8_t)PieceColor::White] = Texture::LoadSVGFromString(engine->window->sdlRenderer, RookSvgWhite);
piecePackedToTexture[(uint8_t)PieceType::Rook | (uint8_t)PieceColor::Black] = Texture::LoadSVGFromString(engine->window->sdlRenderer, RookSvgBlack);
piecePackedToTexture[PieceData(PieceColor::White, PieceType::Rook).Pack()] = Texture::LoadSVGFromString(engine->window->sdlRenderer, RookSvgWhite);
piecePackedToTexture[PieceData(PieceColor::Black, PieceType::Rook).Pack()] = Texture::LoadSVGFromString(engine->window->sdlRenderer, RookSvgBlack);

piecePackedToTexture[(uint8_t)PieceType::Queen | (uint8_t)PieceColor::White]
piecePackedToTexture[PieceData(PieceColor::White, PieceType::Queen).Pack()]
= Texture::LoadSVGFromString(engine->window->sdlRenderer, QueenSvgWhite);
piecePackedToTexture[(uint8_t)PieceType::Queen | (uint8_t)PieceColor::Black]
piecePackedToTexture[PieceData(PieceColor::Black, PieceType::Queen).Pack()]
= Texture::LoadSVGFromString(engine->window->sdlRenderer, QueenSvgBlack);

piecePackedToTexture[(uint8_t)PieceType::King | (uint8_t)PieceColor::White] = Texture::LoadSVGFromString(engine->window->sdlRenderer, KingSvgWhite);
piecePackedToTexture[(uint8_t)PieceType::King | (uint8_t)PieceColor::Black] = Texture::LoadSVGFromString(engine->window->sdlRenderer, KingSvgBlack);
piecePackedToTexture[PieceData(PieceColor::White, PieceType::King).Pack()] = Texture::LoadSVGFromString(engine->window->sdlRenderer, KingSvgWhite);
piecePackedToTexture[PieceData(PieceColor::Black, PieceType::King).Pack()] = Texture::LoadSVGFromString(engine->window->sdlRenderer, KingSvgBlack);

score = Heuristics::MaterialScore(&state);
}

Manager::~Manager() {
Expand All @@ -209,5 +216,6 @@ void Manager::Update(float deltaTime) {
if (aiEnabled && aiColor == state.GetTurn()) {
auto move = Search::NextMove(state);
state.Move(move.From(), move.To());
score = Heuristics::MaterialScore(&state);
}
}
1 change: 1 addition & 0 deletions examples/chess/Manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class Manager : GameObject {
bool aiEnabled = false;

public:
double score;
explicit Manager(Engine* pEngine);
void Start() override;
~Manager();
Expand Down
Loading

0 comments on commit 409e61a

Please sign in to comment.