Skip to content
Snippets Groups Projects
Commit 4e88c5cd authored by TL044CN's avatar TL044CN
Browse files

cleaned up the Code and commented more

parent 9b2ea44b
Branches master
No related tags found
No related merge requests found
......@@ -45,6 +45,7 @@ int main()
Input::update();
if(bRedraw){
//Draw the Main Menu text
Interface::setCursorPosition({8,2});
Interface::printUnicode(L"Chess++");
Interface::setCursorPosition({7,3});
......@@ -53,12 +54,12 @@ int main()
Interface::printUnicode(L"Would you like to");
Interface::setCursorPosition({7,8});
Interface::printUnicode(L"enable AI?");
Interface::setCursorPosition({1,10});
Interface::printUnicode(L"WARNING: AI may crash");
Interface::setCursorPosition({5,13});
Interface::setCursorPosition({5,11});
//save the current Text Attributes
unsigned short textAttribs = Interface::getTextAttribute();
//if the AI is enabled, show it in the Menu
if(AI){
Interface::setTextAttribute(BACKGROUND_GREEN);
}else{
......@@ -66,10 +67,12 @@ int main()
}
Interface::printUnicode(L"Enable");
//reset the Text Attributes to the previous Attribs, for the space inbetween the 2 Options
Interface::setTextAttribute(textAttribs);
Interface::printUnicode(L" ");
//if the AI is not enabled, show that in the Menu
if(!AI){
Interface::setTextAttribute(BACKGROUND_RED);
}else{
......@@ -77,37 +80,40 @@ int main()
}
Interface::printUnicode(L"Disable");
//reset the Text Attributes to the previous Attribs, and print the controls
Interface::setTextAttribute(textAttribs);
Interface::setCursorPosition({6,14});
Interface::setCursorPosition({6,12});
Interface::printUnicode(L"[←A] [D→]");
Interface::setCursorPosition({0,17});
Interface::printUnicode(L"[ESC] Quit\n[Enter] Confirm");
//dont redraw unless something happened
bRedraw = false;
}
//get Human Input
//if A or Left was pressed, enable the AI
if( Input::isPressed(Input::KeyCode::A) ||
Input::isPressed(VK_LEFT) ){
AI = true;
bRedraw = true;
}
//if D or Right was pressed, disable the AI
if( Input::isPressed(Input::KeyCode::D) ||
Input::isPressed(VK_RIGHT) ){
AI = false;
bRedraw = true;
}
//if Enter was pressed, confirm and start the game
if( Input::isPressed(VK_RETURN) ){
Interface::clearScreen();
/* if(AI){
aiRootNode = new Chess::AI::Move_Node(cBoard.getPieces());
}
*/
bRedraw = true;
break;
}
//Quit the game
//Quit the game if Esc was pressed or held or anything
if(Input::isPressed(VK_ESCAPE) || Input::isHeld(VK_ESCAPE)){
exit(0);
}
......@@ -163,6 +169,7 @@ int main()
//do best move
if(!cBoard.do_move(cBoard.get(aiMove->m_Position), aiMove->m_Target)){
//if the AI already stalled a few times, print out a Stalled message
if(aiStallCounter == 2){
Interface::setCursorPosition({0,0});
Interface::printUnicode(L"AI STALLED!!");
......@@ -173,6 +180,7 @@ int main()
aiRootNode = new Chess::AI::Move_Node(cBoard.getPieces());
}else{
//if the ai didn't stall, reset the stall counter and do the move
aiRootNode->do_move(aiMove);
aiStallCounter = 0;
}
......
......@@ -12,8 +12,10 @@ namespace AI{
//predeclare evaluateTree
void evaluateTree(Move_Node* root, bool first = true);
//New Move_Node from State and Parent node
Move_Node::Move_Node(const State& state, Move_Node* parent)
:m_Parent(parent), m_State(new State(state, state.m_Board.getActivePlayer(), &state.m_Move)){
//generates the next possible moves in a tree structure
generateMoves();
}
......@@ -23,28 +25,39 @@ namespace AI{
if(m_Children.size() > 0)
m_Children.clear();
//save the best possible state's value
double best_eval = 0;
//iterate over all the pieces on the current board/virtual board
for(int piece_index = 0; piece_index < 64; piece_index++){
//iterate over all possible places on the board to check if there is a valid move
for(int target_index = 0; target_index < 64; target_index++){
//create a unique pointer to automatically clear up memory when the current iteration is over
std::unique_ptr<Board> virtualBoard = make_unique<Board>(m_State->m_Board.getPieces(),m_State->m_Board.getActivePlayer());
//create Position objects for the current Piece's place and the Target Place
Position from = {(int8_t)(piece_index%8), (int8_t)(piece_index/8)};
Position target(target_index%8, target_index/8);
//get the current piece. can be nullptr when there is no piece
Piece* currentPiece = virtualBoard.get()->get(from);
//if there is no piece there will not be a possible move
if(!currentPiece)
continue;
//if the current piece does not belong to the currently active player, there wont be a move possible
if(currentPiece->getTeam() != m_State->m_Board.getActivePlayer())
continue;
//if there is a move possible from the current piece to the target, then do it on the virtual board
if(virtualBoard.get()->do_move(currentPiece, target)){
//create a Move instance to save the done move.
Move* step = new Move(currentPiece, from, target);
//generate a new State from the state of the virtual Board
// std::unique_ptr<State> newState = make_unique<State>(virtualBoard->getPieces(), m_State->m_Board.getActivePlayer());
State* newState = new State(virtualBoard->getPieces(), m_State->m_Board.getActivePlayer(), step);
//set initial evaluation
......@@ -63,7 +76,8 @@ namespace AI{
//if the Simulation is not at its deepest part yet, recurse
if(m_sSimulationDepth > 0){
//create a new Move_Node
//create a new Move_Node and push it onto the stack of moves that are possible after the current move.
//This also recursively calls this function, letting the new child generate its child nodes.
Move_Node* newNode = new Move_Node(*newState,this);
m_Children.push_back(newNode);
}
......@@ -89,20 +103,29 @@ namespace AI{
//if this was the first iteration, then evaluate EVERY child instead of just the best one.
//keep the current Evaluation and afterwards sort the Children again
if(first){
//save a copy of the root eval
double eval = root->m_State->m_Evaluation;
//for every child of the root, evaluate this function.
//this will lead to the child nodes of the root having the value of the best move
//calculated into their eval.
for(Move_Node* child : root->m_Children){
evaluateTree(child,false);
}
//let the root keep its original eval. only works for root. children dont do this
root->m_State->m_Evaluation = eval;
//sort the child nodes by their evaluated values.
std::sort(root->m_Children.begin(), root->m_Children.end(),[](Move_Node* a, Move_Node* b){
return a->m_State->m_Evaluation < b->m_State->m_Evaluation;
});
} else {
//if this is not the first element
//follow the best children in each branch
//then, from the bottom up, subtract the Evaluated Values from the parent
//because the parents will always be for the oposing player
if(root->m_Children.size()>0)
evaluateTree(root->m_Children.front(), false);
if(root->m_Parent)
......@@ -110,20 +133,31 @@ namespace AI{
}
}
//returns the best possible Move, according to the evaluation
const Move* Move_Node::getBestMove() {
//check if there is even children,
//if so then return the best childs move
if(m_Children.size()>0)
return &m_Children.front()->m_State->m_Move;
//else return an empty move
return new Move(nullptr, Position(), Position());
}
//do a move in the AI...
//tries to make the coding a little faster
bool Move_Node::do_move(const Move* move){
//check if the move is somewhere in the children.
//if it is, then set the root node to the child and regenerate it's children
for(auto child : m_Children)
if(child->m_State->m_Move == *move){
// child->m_State->setActivePlayer(m_State->m_Board.getActivePlayer()==Color::eWhite?Color::eBlack:Color::eWhite);
//regenerate the Moves for the Child
child->generateMoves();
//set the root node (this) to the child
*this = *child;
return true;
}
//if there was no such move, return false after regenerating all the moves
generateMoves();
return false;
}
......
......@@ -64,49 +64,45 @@ constexpr std::array<PieceValue,6> PiecePositionFactor = {{{
}}};
State::State(const Board_Data& data, Chess::Color player)
:m_Board(data), m_Move(nullptr, Position(), Position()){
//set the active Player of the Board
m_Board.m_ActivePlayer = player;
:m_Board(data, player), m_Move(nullptr, Position(), Position()){
//evaluate the State of the board
reevaluateState();
}
State::State(const State& previous, Color player)
:m_Board(previous.m_Board.m_Pieces), m_Move(nullptr, Position(), Position()){
m_Board.m_ActivePlayer = player;
reevaluateState();
}
State::State(const State& previous, Color player, const Move* move)
:m_Board(previous.m_Board.m_Pieces), m_Move(*move){
m_Board.m_ActivePlayer = player;
:m_Board(previous.m_Board.getPieces(),player), m_Move(*move){
//evaluate the State of the virtual board
reevaluateState();
}
State::State(const State& previous)
:m_Board(previous.m_Board.m_Pieces), m_Move(nullptr, Position(), Position()){
m_Board.m_ActivePlayer = m_Board.getActivePlayer()==Color::eBlack?Color::eWhite:Color::eBlack;
}
void State::reevaluateState(){
//reset evaluation
m_Evaluation = 0.0;
//iterate over every piece on the board
for(auto piece : m_Board.getPieces()){
//if there is no piece, continue untill there is a piece
if(!piece)
continue;
//get the Base Value of the Type of Piece
double dBaseValue = PieceTypeValue[piece->getType()];
//get the Position Factor for the Piece Type at the Position of the Piece
double dPositionFactor;
if(piece->getTeam() == Color::eWhite){
//if the Piece is White, dont mirror the Factors
dPositionFactor = PiecePositionFactor[piece->getType()][piece->indexFromPosition()];
} else {
//if the Piece is Black the factors have to be mirrored, since the black pieces are mirrored to the white ones
dPositionFactor = PiecePositionFactor[piece->getType()][piece->getPosition().x + (8 - piece->getPosition().y) * 8];
}
//if the Piece belongs to the currently active Player, add the Evaluation to the overall evaluation,
//else the Position is better for the other Party, so subtract it.
if(piece->getTeam() == m_Board.getActivePlayer())
m_Evaluation += dBaseValue + (dBaseValue * dPositionFactor);
else
......@@ -114,8 +110,4 @@ constexpr std::array<PieceValue,6> PiecePositionFactor = {{{
}
}
void State::setActivePlayer(Chess::Color player){
m_Board.m_ActivePlayer = player;
}
}}
......@@ -13,9 +13,7 @@ namespace AI{
struct State{
public:
State(const Board_Data& state, Color player = Color::eBlack);
State(const State& previous, Color player);
State(const State& previous, Color player, const Move* move);
State(const State& previous);
Board m_Board;
Move m_Move;
......@@ -24,7 +22,6 @@ namespace AI{
void reevaluateState();
void setActivePlayer(Color);
};
}}
......
......@@ -209,8 +209,12 @@ namespace Chess {
if(piece->m_Identifier == Identifier::ePawn)
if((target.y == 7 && piece->getTeam() == Color::eWhite) ||
(target.y == 0 && piece->getTeam() == Color::eBlack)){
//if it did, turn it into a queen at that position (by adding a new Queen).
add(new Queen(piece->getTeam(), target));
//and removing the old Pawn
set(piece->m_Position, nullptr);
//set the board so that its the enemys turn now and return true
if(m_ActivePlayer == Color::eBlack)
m_ActivePlayer = Color::eWhite;
else
......@@ -223,6 +227,7 @@ namespace Chess {
set(piece->m_Position, nullptr);
piece->m_Position = target;
//set the board so that its the enemys turn now and return true
if(m_ActivePlayer == Color::eBlack)
m_ActivePlayer = Color::eWhite;
else
......@@ -230,6 +235,8 @@ namespace Chess {
return true;
}
//the move was invalid. dont do anything and return false
return false;
}
......@@ -238,16 +245,20 @@ namespace Chess {
for(Piece* piece:m_Pieces){
//check if the piece exists
if(piece){
//set the enemy Color to be the current color temporarily
Color eTeam = piece->getTeam();
//flip it around
if(eTeam == eWhite)
eTeam = eBlack;
else
eTeam = eWhite;
//ckeck if a move on the enemy King is possible. if yes, its a check
if(piece->move(m_Kings[eTeam]->m_Position,m_Pieces))
return true;
}
}
//if no piece can strike the enemy king, its not a check
return false;
}
}
......@@ -11,14 +11,8 @@
namespace Chess{
namespace AI{
struct State;
}
//Board class tracks the entire game
class Board{
friend AI::State;
//every field on the board
Board_Data m_Pieces;
......@@ -57,7 +51,7 @@ namespace Chess{
Piece* get(Position pos) const;
//get all the pieces
inline Board_Data getPieces(){ return m_Pieces; }
inline Board_Data getPieces() const { return m_Pieces; }
//get the currently active player
inline Color getActivePlayer() const { return m_ActivePlayer; }
......
......@@ -6,12 +6,19 @@
namespace Chess {
#if _MSC_VER && !__INTEL_COMPILER
template<typename T, typename ... Arguments>
std::unique_ptr<T> make_unique(Arguments&& ... args){
return std::make_unique<T>(std::forward<Arguments>(args)...);
}
#elif defined(__MINGW32__)
//Apparently QT Creator does not suport std::make_unique...
//So here is an implementation of std::make_unique in the Chess namespace
template<typename T, typename ... Arguments>
std::unique_ptr<T> make_unique(Arguments&&... args){
return std::unique_ptr<T>(new T(std::forward<Arguments>(args)...));
}
#endif
//the Enum corresponding to the different Pieces
enum Identifier{
......
......@@ -30,6 +30,7 @@ namespace Chess {
if(strike && !enemy)
return false;
//if the movement is Diagonal, check if the Diagonal Movement is valid and return the answer
if(movement.x*movement.x == movement.y*movement.y){
return checkDiagonal(m_Position, pieces, to);
}
......
......@@ -30,8 +30,10 @@ namespace Chess {
if(strike && !enemy)
return false;
//check if the movement is 1 in one direction and 2 in the other, then return true
if((movement.x*movement.x == 1 || movement.x == 0) && (movement.y*movement.y == 1 || movement.y == 0))
return true;
return false;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment