Skip to content

Commit

Permalink
README and temp Move Prober in settings pannel.
Browse files Browse the repository at this point in the history
  • Loading branch information
Spebby committed Nov 12, 2024
1 parent bfae829 commit f4615c1
Show file tree
Hide file tree
Showing 12 changed files with 123 additions and 49 deletions.
37 changes: 37 additions & 0 deletions Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,41 @@ namespace ClassGame {
game->setUpBoard();
}

void drawMoveProber() {
const ImGuiTableFlags flags = ImGuiTableFlags_Borders |
ImGuiTableFlags_RowBg;

ImGui::BeginChild("Moves", ImVec2(0, 0), true);
if (ImGui::BeginTable("Move Prober", 2, flags)) {
ImGui::TableSetupColumn("I", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_DefaultSort, 12.0f);
ImGui::TableSetupColumn("Moves", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_DefaultSort);
ImGui::TableHeadersRow();

std::unordered_map<int, std::vector<int>> moves = game->getMoves();
std::vector<std::pair<int, std::vector<int>>> moveList;
moveList.reserve(64);
for (int i = 0; i < 64; i++) {
moveList.emplace_back(i, moves[i]);
}

for(const std::pair<int, std::vector<int>>& data : moveList) {
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::Text("%s", std::to_string(data.first).c_str());

ImGui::TableSetColumnIndex(1);
std::string moves;
for (int i : data.second) {
moves += std::to_string(i) + " ";
}
ImGui::Text("%s", moves.c_str());
}

ImGui::EndTable();
}
ImGui::EndChild();
}

// game render loop
// this is called by the main render loop in main.cpp
void RenderGame() {
Expand All @@ -26,6 +61,8 @@ namespace ClassGame {
ImGui::Text("Current Player Number: %d", game->getCurrentPlayer()->playerNumber());
ImGui::Text("Current Board State: %s", game->stateString().c_str());

drawMoveProber();

if (gameOver) {
ImGui::Text("Game Over!");
ImGui::Text("Winner: %d", gameWinner);
Expand Down
Binary file added BoardScreenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
78 changes: 51 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Chess AI Implementation Project
# Spebby's Chess AI

![Chess Board](https://raw.githubusercontent.com/zaphodgjd/class-chess-123/main/chess/w_king.png)
![Chess Board](./BoardScreenshot.png)

## 🎯 Project Overview
This repository contains a skeleton implementation of a Chess AI engine written in C++. The project is designed to teach fundamental concepts of game AI, including board representation, move generation, and basic game tree search algorithms.
A very rudamentry Chess program (and eventually, AI) based on Graeme Devine's [skeleton code](https://github.com/Spebby/CMPM123-Chess/commits/95c448471543cbf7a933316e770efa8766cd0943/), given a graphical interface with [Dear ImGui](https://github.com/ocornut/imgui/tree/docking). This is the final project for the CMPM-123's Fall '24 Quarter at the University of California, Santa Cruz.

### 🎓 Educational Purpose
This project serves as a teaching tool for computer science students to understand:
Expand Down Expand Up @@ -38,7 +38,7 @@ This project serves as a teaching tool for computer science students to understa
### Prerequisites
- C++ compiler with C++11 support or higher
- Image loading library for piece sprites
- CMake 3.10 or higher
- CMake 3.2 or higher

### Building the Project
```bash
Expand All @@ -58,34 +58,71 @@ make
### Current Features
- Basic board setup and initialization
- Piece movement validation framework
- No current support for En Passant and Castling.
- FEN notation parsing and generation
- Sprite loading for chess pieces
- Player turn management

### Planned Features
- [ ] Support for En Passant and Castling
- [ ] AI move generation
- [ ] Position evaluation
- [ ] Opening book integration
- [ ] Advanced search algorithms
- [ ] Game state persistence
- [ ] Rudimentary Bitboards

## 🔍 Code Examples

### Piece Movement Validation
```cpp
bool Chess::canBitMoveFromTo(Bit& bit, BitHolder& src, BitHolder& dst) {
// TODO: Implement piece-specific movement rules
return false;
bool Chess::canBitMoveFrom(Bit& bit, ChessSquare& src) {
if (_moves.count(src.getIndex())) {
return true;
}
return false;
}

bool Chess::canBitMoveFromTo(Bit& bit, ChessSquare& src, ChessSquare& dst) {
const int i = srcSquare.getIndex();
const int j = dstSquare.getIndex();
for (int pos : _moves[i]) {
if (pos == j) {
return true;
}
}

return false;
}
```
Validating that a player's attempted move is simple enough, but building a list of legal moves is harder. For convenience, generated moves for a given position are stored in a hashtable for convenience and quick lookup. While the move generator does not currently check for check (and thus, does not limit "psuedo-illegal" moves), it does generate all moves fairly efficiently for a non-bitboard approach, nix-En Passant and Castling.
### FEN Notation Generation
FEN notation for an individual position is computed and cached when a piece moves to a new holder.
```cpp
const char Chess::bitToPieceNotation(int row, int column) const {
if (row < 0 || row >= 8 || column < 0 || column >= 8) {
return '0';
}
// Implementation details for FEN notation
inline char generateNotation(ChessBit* abit) {
if (abit) {
const char* w = { "PNBRQK" };
const char* b = { "pnbrqk" };
// get the non-coloured piece
int piece = abit->gameTag() & 7;
return 8 & abit->gameTag() ? b[piece - 1] : w[piece - 1];
}
return '0';
}
```

```cpp
std::string Chess::stateString() {
std::string s;
for (int i = 0; i < _gameOps.size; i++) {
s += _grid[i].getNotation();
}

return s;
}
```

Expand All @@ -107,25 +144,12 @@ const char Chess::bitToPieceNotation(int row, int column) const {
- Add alpha-beta pruning
- Basic opening book

## 🤝 Contributing
Students are encouraged to:
1. Fork the repository
2. Create a feature branch
3. Implement assigned components
4. Submit their fork for review

## 🔒 Code Style and Standards
- Use consistent indentation (4 spaces)
- Follow C++ naming conventions
- Document all public methods
- Include unit tests for new features

## 📄 License
This project is licensed under the MIT License.

## 👥 Contributors
- [Your Name] - Initial work
- [Student Names] - Implementation and testing
- Graeme Devine - Initial Framework
- Thom Mott - Implementation and testing

## 🙏 Acknowledgments
- Chess piece sprites from [Wikipedia](https://en.wikipedia.org/wiki/Chess_piece)
Expand Down
File renamed without changes
17 changes: 9 additions & 8 deletions classes/Chess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ void Chess::setUpBoard() {
for (int rank = 0; rank < _gameOps.X; rank++) {
// Unfortunately the _gameOps.Y - y part is neccesary to get this to display properly.
_grid[file * 8 + rank].initHolder((ImVec2(rank * 64 + 50, (_gameOps.Y - file) * 64 + 50)),
"square.png", rank, file);
"chess/square.png", rank, file);
// game tag init to 0
// notation is set later.
}
Expand Down Expand Up @@ -206,16 +206,13 @@ bool Chess::actionForEmptyHolder(BitHolder &holder) {
}

bool Chess::canBitMoveFrom(Bit& bit, BitHolder& src) {
// un-lit the squares when clicking on a new square.
clearPositionHighlights();

ChessSquare& srcSquare = static_cast<ChessSquare&>(src);
bool canMove = false;
const int i = srcSquare.getIndex();

// un-lit the squares when clicking on a new square.
while (!_litSquare.empty()) {
_litSquare.top()->setMoveHighlighted(false);
_litSquare.pop();
}

if (_moves.count(i)) {
canMove = true;
for (int attacking : _moves[i]) {
Expand Down Expand Up @@ -246,11 +243,15 @@ bool Chess::canBitMoveFromTo(Bit& bit, BitHolder& src, BitHolder& dst) {
void Chess::bitMovedFromTo(Bit &bit, BitHolder &src, BitHolder &dst) {
// call base.
Game::bitMovedFromTo(bit, src, dst);
clearPositionHighlights();
moveGenerator();
}

inline void Chess::clearPositionHighlights() {
while (!_litSquare.empty()) {
_litSquare.top()->setMoveHighlighted(false);
_litSquare.pop();
}
moveGenerator();
}

// free all the memory used by the game on the heap
Expand Down
6 changes: 6 additions & 0 deletions classes/Chess.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,18 @@ class Chess : public Game {
void updateAI() override;
bool gameHasAI() override { return false; }

std::unordered_map<int, std::vector<int>> getMoves() const {
return _moves;
}

private:
ChessBit* PieceForPlayer(const int playerNumber, ChessPiece piece);
ChessBit* PieceForPlayer(const char piece);
const char bitToPieceNotation(int rank, int file) const;
const char bitToPieceNotation(int i) const;

inline void clearPositionHighlights();

// distances at a given position to the board's boundries. North, East, South, West, NE, SE, SW, NW
int _dist[64][8];
ChessSquare _grid[64];
Expand Down
3 changes: 3 additions & 0 deletions classes/ChessBit.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

class ChessBit : public Bit {
public:
/* I'm not including this in Bit.h b/c in some contexts an ally may not neccesairly have the same
owner, and it would be up to bit's children to implement it case by case. Some games may not
even have the concept of an ally, so it seems best to keep it in chess land */
bool isAlly(ChessBit* other) const { return other->_owner == this->_owner; }

bool friendly() const override { return Bit::friendly(); }
Expand Down
24 changes: 13 additions & 11 deletions classes/ChessSquare.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ std::string formatCords(const int a, const int b) {
return "(" + std::to_string(a) + ", " + std::to_string(b) + ")";
}

inline char generateNotation(ChessBit* abit) {
if (abit) {
const char* w = { "PNBRQK" };
const char* b = { "pnbrqk" };
// get the non-coloured piece
int piece = abit->gameTag() & 7;
return 8 & abit->gameTag() ? b[piece - 1] : w[piece - 1];
}
return '0';
}


void ChessSquare::setBit(Bit* abit) {
// consider a static_cast IF safe
ChessBit* bit = dynamic_cast<ChessBit*>(abit);
Expand All @@ -24,17 +36,7 @@ void ChessSquare::setBit(Bit* abit) {
}

BitHolder::setBit(bit);

unsigned char notation = '0';
if (_bit) {
const char* w = { "PNBRQK" };
const char* b = { "pnbrqk" };
// get the actual piece
int piece = _bit->gameTag() & 7;
notation = 8 & _bit->gameTag() ? b[piece - 1] : w[piece - 1];
}

_notation = notation;
_notation = generateNotation(bit);
}

void ChessSquare::initHolder(const ImVec2 &position, const char *spriteName, const int column, const int row) {
Expand Down
2 changes: 1 addition & 1 deletion main_macos.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ int main(int, char**)
#endif

// Create window with graphics context
GLFWwindow* window = glfwCreateWindow(1280, 720, "CMPM 123 Class Project", nullptr, nullptr);
GLFWwindow* window = glfwCreateWindow(1280, 720, "Chess", nullptr, nullptr);
if (window == nullptr)
return 1;
glfwMakeContextCurrent(window);
Expand Down
4 changes: 2 additions & 2 deletions main_win32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ int main(int, char**)
{
// Create application window
//ImGui_ImplWin32_EnableDpiAwareness();
WNDCLASSEXW wc = { sizeof(wc), CS_OWNDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, L"ImGui Example", NULL };
WNDCLASSEXW wc = { sizeof(wc), CS_OWNDC, WndProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, L"Chess", NULL };
::RegisterClassExW(&wc);
HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Dear ImGui Win32+OpenGL3 Example", WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, NULL, NULL, wc.hInstance, NULL);
HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Chess", WS_OVERLAPPEDWINDOW, 100, 100, 1280, 800, NULL, NULL, wc.hInstance, NULL);

// Initialize OpenGL
if (!CreateDeviceWGL(hwnd, &g_MainWindow))
Expand Down
Binary file added pawn.ico
Binary file not shown.
1 change: 1 addition & 0 deletions pawn.rc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
IDI_ICON1 ICON DISCARDABLE "pawn.ico"

0 comments on commit f4615c1

Please sign in to comment.