2022-spring-final-ntran27-ykanash1-kelsman1 / Game.cpp
Game.cpp
Raw
#include <cassert>
#include "Exceptions.h"
#include "Game.h"
#include <stdlib.h>
#include <vector>

namespace Chess
{
	/////////////////////////////////////
	// DO NOT MODIFY THIS FUNCTION!!!! //
	/////////////////////////////////////
	Game::Game() : is_white_turn(true)
	{
		// Add the pawns
		for (int i = 0; i < 8; i++)
		{
			board.add_piece(Position('A' + i, '1' + 1), 'P');
			board.add_piece(Position('A' + i, '1' + 6), 'p');
		}

		// Add the rooks
		board.add_piece(Position('A' + 0, '1' + 0), 'R');
		board.add_piece(Position('A' + 7, '1' + 0), 'R');
		board.add_piece(Position('A' + 0, '1' + 7), 'r');
		board.add_piece(Position('A' + 7, '1' + 7), 'r');

		// Add the knights
		board.add_piece(Position('A' + 1, '1' + 0), 'N');
		board.add_piece(Position('A' + 6, '1' + 0), 'N');
		board.add_piece(Position('A' + 1, '1' + 7), 'n');
		board.add_piece(Position('A' + 6, '1' + 7), 'n');

		// Add the bishops
		board.add_piece(Position('A' + 2, '1' + 0), 'B');
		board.add_piece(Position('A' + 5, '1' + 0), 'B');
		board.add_piece(Position('A' + 2, '1' + 7), 'b');
		board.add_piece(Position('A' + 5, '1' + 7), 'b');

		// Add the kings and queens
		board.add_piece(Position('A' + 3, '1' + 0), 'Q');
		board.add_piece(Position('A' + 4, '1' + 0), 'K');
		board.add_piece(Position('A' + 3, '1' + 7), 'q');
		board.add_piece(Position('A' + 4, '1' + 7), 'k');
	}

	Game::~Game() {}

	void Game::make_move(const Position &start, const Position &end)
	{
		// EXCEPTION: start position on board?
		if (!position_on_board(start))
		{
			throw Exception(" start position is not on board");
		}
		// EXCEPTION: end position on board?
		if (!position_on_board(end))
		{
			throw Exception(" end position is not on board");
		}
		// EXCEPTION: no piece at start position?
		if (board(start) == nullptr)
		{
			throw Exception(" no piece at start position");
		}
		// EXCEPTION: piece color and turn match?
		if ((board(start))->is_white() != is_white_turn)
		{
			throw Exception(" piece color and turn do not match");
		}

		// EXCEPTION: illegal move shape?
		if (board(end) == nullptr && (board(start)->to_ascii() == 'p' || board(start)->to_ascii() == 'P'))
		{
			if (!(board(start))->legal_move_shape(start, end))
			{
				throw Exception(" illegal move shape");
			}
		}
		if (board(start)->to_ascii() != 'p' && board(start)->to_ascii() != 'P')
		{
			if (!(board(start))->legal_move_shape(start, end))
			{
				throw Exception(" illegal move shape");
			}
		}

		// EXCEPTION: cannot capture own piece?
		if (board(end) != nullptr)
		{
			if (board(start)->is_white() == board(end)->is_white())
			{
				throw Exception(" cannot capture own piece");
			}
		}
		// EXCEPTION: illegal capture shape?
		if (board(end) != nullptr)
		{
			if (board(start)->is_white() != board(end)->is_white())
			{
				if (!(board(start)->legal_capture_shape(start, end)))
				{
					throw Exception(" illegal capture shape");
				}
			}
		}
		// EXCEPTION: path is not clear?
		if (!path_clear_check(start, end))
		{
			throw Exception(" path is not clear");
		}

		// Make copy of the board
		Game fakegame = *this;

		// Move in fake board
		if (fakegame.board(end) != nullptr)
		{
			if (fakegame.board(start)->is_white() != fakegame.board(end)->is_white())
			{
				fakegame.board.erase_piece(end);
			}
		}
		fakegame.board.move_piece(start, end);

		// EXCEPTION: move exposes check?
		if (fakegame.in_check(is_white_turn))
		{
			throw Exception(" move exposes check");
		}

		// Normal Move and capturing
		if (board(end) != nullptr)
		{
			if (board(start)->is_white() != board(end)->is_white())
			{
				board.erase_piece(end);
			}
		}
		board.move_piece(start, end);

		// Change turn
		if (is_white_turn)
			is_white_turn = false;
		else
			is_white_turn = true;
	}

	bool Game::position_on_board(const Position &x)
	{
		int flag = 0;
		for (char r = '8'; r >= '1'; r--)
		{
			if (x.second == r)
			{
				flag++;
			}
		}
		for (char c = 'A'; c <= 'H'; c++)
		{
			if (x.first == c)
			{
				flag++;
			}
		}
		if (flag == 2)
		{
			return true;
		}
		else
			return false;
	}

	bool Game::path_clear_check(const Position &start, const Position &end) const
	{

		int dx = end.first - start.first;
		int dy = end.second - start.second;

		// vertical movement
		if (dx == 0 && dy != 0)
		{
			if (dy > 0)
			{
				for (char row = start.second + 1; row < end.second; row++)
				{
					Position tmp = Position(start.first, row);
					if (board(tmp) != nullptr)
					{
						return false;
					}
				}
			}
			else
			{
				for (char row = end.second + 1; row < start.second; row++)
				{
					Position tmp = Position(start.first, row);
					if (board(tmp) != nullptr)
					{
						return false;
					}
				}
			}
		}

		// horizontal movement
		if (dx != 0 && dy == 0)
		{
			if (dx > 0)
			{
				for (char col = start.first + 1; col < end.first; col++)
				{
					Position tmp = Position(col, start.second);
					if (board(tmp) != nullptr)
					{
						return false;
					}
				}
			}
			else if (dx < 0)
			{

				for (char col = end.first + 1; col < start.first; col++)
				{
					Position tmp = Position(col, start.second);
					if (board(tmp) != nullptr)
					{
						return false;
					}
				}
			}
		}

		// diagonal movement
		if (abs(dx) == abs(dy))
		{
			if (dx > 0 && dy > 0)
			{
				for (char i = 1, j = 1; i < abs(dx); i++, j++)
				{
					Position tmp = Position(start.first + j, start.second + i);
					if (board(tmp) != nullptr)
					{
						return false;
					}
				}
			}
			if (dx > 0 && dy < 0)
			{
				for (char i = 1, j = 1; i < abs(dx); i++, j++)
				{
					Position tmp = Position(start.first + j, start.second - i);
					if (board(tmp) != nullptr)
					{
						return false;
					}
				}
			}
			if (dx < 0 && dy > 0)
			{
				for (char i = 1, j = 1; i < abs(dx); i++, j++)
				{
					Position tmp = Position(start.first - j, start.second + i);
					if (board(tmp) != nullptr)
					{
						return false;
					}
				}
			}
			if (dx < 0 && dy < 0)
			{
				for (char i = 1, j = 1; i < abs(dx); i++, j++)
				{
					Position tmp = Position(start.first - j, start.second - i);
					if (board(tmp) != nullptr)
					{
						return false;
					}
				}
			}
		}
		return true;
	}

	bool Game::in_check(const bool &white) const
	{

		Position king_pos = board.get_king(white);
		for (char r = '8'; r >= '1'; r--)
		{
			for (char c = 'A'; c <= 'H'; c++)
			{
				Position tempo(c, r);
				// if there is a piece at tempo
				if (board(tempo) != nullptr)
				{
					// if that piece belong to the other team (different color)
					if (board(tempo)->is_white() != white)
					{
						// if that piece has a legal move to capture king
						switch (board(tempo)->to_ascii())
						{
						case 'Q':
						case 'q':
						case 'R':
						case 'r':
						case 'B':
						case 'b':
						case 'N':
						case 'n':
						case 'K':
						case 'k':
						case 'M':
						case 'm':
							if (path_clear_check(tempo, king_pos) &&
								board(tempo)->legal_move_shape(tempo, king_pos))
							{
								return true;
							}
							break;
						case 'P':
						case 'p':
							if (board(tempo)->legal_capture_shape(tempo, king_pos))
							{
								return true;
							}
							break;
						default:
							break;
						}
					}
				}
			}
		}
		return false;
	}

	bool Game::in_mate(const bool &white) const
	{

		if (this->in_check(white))
		{
			// create fake board
			Game fakegame;
			for (char r = '8'; r >= '1'; r--)
			{
				for (char c = 'A'; c <= 'H'; c++)
				{
					Position tempo(c, r);
					// if there is a piece at tempo
					if (board(tempo) != nullptr)
					{
						// if that piece belong to the other team (same color)
						if (board(tempo)->is_white() == white)
						{
							// if that piece has any legal move to protect king
							for (char r2 = '8'; r2 >= '1'; r2--)
							{
								for (char c2 = 'A'; c2 <= 'H'; c2++)
								{
									fakegame = *this;
									Position pos(c2, r2);
									try
									{
										fakegame.make_move(tempo, pos);
									}
									catch (...)
									{
										continue;
									}
									if (!fakegame.in_check(white))
									{
										return false;
									}
								}
							}
						}
					}
				}
			}
			return true;
		}
		return false;
	}

	bool Game::in_stalemate(const bool &white) const
	{
		if (!this->in_check(white))
		{
			// create fake board
			Game fakegame;
			for (char r = '8'; r >= '1'; r--)
			{
				for (char c = 'A'; c <= 'H'; c++)
				{
					Position tempo(c, r);
					// if there is a piece at tempo
					if (board(tempo) != nullptr)
					{
						// if that piece belong to the other team (same color)
						if (board(tempo)->is_white() == white)
						{
							// if that piece has any legal move to protect king
							for (char r2 = '8'; r2 >= '1'; r2--)
							{
								for (char c2 = 'A'; c2 <= 'H'; c2++)
								{
									fakegame = *this;
									Position pos(c2, r2);
									try
									{
										fakegame.make_move(tempo, pos);
									}
									catch (...)
									{
										continue;
									}
									if (!fakegame.in_check(white))
									{
										return false;
									}
								}
							}
						}
					}
				}
			}
			return true;
		}
		return false;
	}

	int Game::point_value(const bool &white) const
	{
		int sum = 0;

		for (char r = '8'; r >= '1'; r--)
		{
			for (char c = 'A'; c <= 'H'; c++)
			{
				Position tempo(c, r);

				// if there is a piece at tempo
				if (board(tempo) != nullptr)
				{

					// if that piece belongs to the player’s team
					if (board(tempo)->is_white() == white)
					{

						sum += board(tempo)->point_value();
					}
				}
			}
		}

		return sum;
	}

	std::istream &operator>>(std::istream &is, Game &game)
	{
		// Deallocate current game
		game.~Game();
		// loop through input file, and added a piece
		char token;
		std::vector<char> list;
		while (is >> token)
		{
			list.push_back(token);
		}
		if (list.size() != 65)
		{
			throw Chess::Exception("Cannot load the game!\n");
		}
		// loop through the board and add piece
		std::vector<char>::iterator it = list.begin();
		for (char r = '8'; r >= '1'; r--)
		{
			for (char c = 'A'; c <= 'H'; c++)
			{
				Position tempo(c, r);
				if (*it != '-')
				{
					game.board.add_piece(tempo, *it);
					it++;
				}
				else
					it++;
			}
		}
		if (list[list.size() - 1] == 'w')
		{
			game.is_white_turn = true;
		}
		if (list[list.size() - 1] == 'b')
		{
			game.is_white_turn = false;
		}
		return is;
	}

	/////////////////////////////////////
	// DO NOT MODIFY THIS FUNCTION!!!! //
	/////////////////////////////////////
	std::ostream &operator<<(std::ostream &os, const Game &game)
	{
		// Write the board out and then either the character 'w' or the character 'b',
		// depending on whose turn it is
		return os << game.board << (game.turn_white() ? 'w' : 'b');
	}
}