2022-spring-final-ntran27-ykanash1-kelsman1 / Board.cpp
Board.cpp
Raw
#include <iostream>
#include <utility>
#include <map>
#ifndef _WIN32
#include "Terminal.h"
#endif // !_WIN32
#include "Board.h"
#include "CreatePiece.h"
#include "Exceptions.h"

namespace Chess
{
  /////////////////////////////////////
  // DO NOT MODIFY THIS FUNCTION!!!! //
  /////////////////////////////////////
  Board::Board() {}

  Board::Board(const Board &rhs)
  {
    *this = rhs;
  }

  Board &Board::operator=(const Board &rhs)
  {
    // deallocate memory before copy
    for (char r = '8'; r >= '1'; r--)
    {
      for (char c = 'A'; c <= 'H'; c++)
      {
        if (occ.find(Position(c, r)) != occ.end())
        {
          erase_piece(Position(c, r));
        }
      }
    }
    // copy here
    for (std::map<Position, Piece *>::const_iterator it = rhs.occ.begin();
         it != rhs.occ.end();
         it++)
    {
      this->add_piece(it->first, it->second->to_ascii());
    }
    return *this;
  }

  Board ::~Board()
  {
    for (char r = '8'; r >= '1'; r--)
    {
      for (char c = 'A'; c <= 'H'; c++)
      {
        if (occ.find(Position(c, r)) != occ.end())
        {
          erase_piece(Position(c, r));
        }
      }
    }
  }

  Position Board::get_king(const bool white) const
  {
    Position x;
    if (white)
    {
      for (std::map<Position, Piece *>::const_iterator it = occ.begin();
           it != occ.end();
           it++)
      {
        if ((*it->second).to_ascii() == 'K')
        {
          x = it->first;
        }
      }
    }
    else
    {
      for (std::map<Position, Piece *>::const_iterator it = occ.begin();
           it != occ.end();
           it++)
      {
        if ((*it->second).to_ascii() == 'k')
        {
          x = it->first;
        }
      }
    }
    return x;
  }

  const Piece *Board::operator()(const Position &position) const
  {
    if (occ.find(position) != occ.end())
      return occ.at(position);
    else
      return nullptr;
  }

  void Board::promote(const Position &position, const bool white)
  {

    // white piece promotion
    if (white)
    {
      erase_piece(position);
      add_piece(position, 'Q');
    }

    // black piece promotion
    else
    {
      erase_piece(position);
      add_piece(position, 'q');
    }
  }

  void Board::add_piece(const Position &position, const char &piece_designator)
  {

    // invalid designator
    Piece *new_piece = create_piece(piece_designator);
    if (new_piece == nullptr)
    {
      throw Exception("invalid designator\n");
    }
    // invalid position
    int flag = 0;
    for (char r = '8'; r >= '1'; r--)
    {
      if (position.second == r)
      {
        flag++;
      }
    }
    for (char c = 'A'; c <= 'H'; c++)
    {
      if (position.first == c)
      {
        flag++;
      }
    }
    if (flag != 2)
    {
      throw Exception("invalid position\n");
    }

    // position is occupied
    if (occ.find(position) != occ.end())
    {
      throw Exception("position is occupied");
    }

    // create new piece at given position
    occ[position] = new_piece;
  }

  void Board::display() const
  {
    // make output pretty
    Terminal ::color_fg(true, Terminal::RED);
    std::cout << "   A  B  C  D  E  F  G  H"
              << "\n";
    int i = 8;
    Terminal ::color_fg(true, Terminal::RED);
    for (char r = '8'; r >= '1'; r--)
    {
      std::cout << i << " ";
      for (char c = 'A'; c <= 'H'; c++)
      {
        if (occ.find(Position(c, r)) != occ.end())
        {
          Terminal ::color_fg(true, Terminal::RED);

          if (c % 2 != 0 && r % 2 != 0)
          {
            Terminal ::color_bg(Terminal::BLACK);
            switch (occ.at(Position(c, r))->to_ascii())
            {
            case 'R':
              std::cout << "";
              break;
            case 'K':
              std::cout << "";
              break;
            case 'Q':
              std::cout << "";
              break;
            case 'N':
              std::cout << "";
              break;
            case 'P':
              std::cout << "";
              break;
            case 'B':
              std::cout << "";
              break;
            case 'r':
              std::cout << "";
              break;
            case 'k':
              std::cout << "";
              break;
            case 'q':
              std::cout << "";
              break;
            case 'n':
              std::cout << "";
              break;
            case 'p':
              std::cout << "";
              break;
            case 'b':
              std::cout << "";
              break;
            case 'm':
              std::cout << " ? ";
              break;
            case 'M':
              std::cout << "";
              break;
            default:
              break;
            }
          }
          if (c % 2 == 0 && r % 2 != 0)
          {
            Terminal ::color_bg(Terminal::WHITE);
            switch (occ.at(Position(c, r))->to_ascii())
            {
            case 'R':
              std::cout << "";
              break;
            case 'K':
              std::cout << "";
              break;
            case 'Q':
              std::cout << "";
              break;
            case 'N':
              std::cout << "";
              break;
            case 'P':
              std::cout << "";
              break;
            case 'B':
              std::cout << "";
              break;
            case 'r':
              std::cout << "";
              break;
            case 'k':
              std::cout << "";
              break;
            case 'q':
              std::cout << "";
              break;
            case 'n':
              std::cout << "";
              break;
            case 'p':
              std::cout << "";
              break;
            case 'b':
              std::cout << "";
              break;
            case 'm':
              std::cout << " ? ";
              break;
            case 'M':
              std::cout << "";
              break;
            default:
              break;
            }
          }
          if (c % 2 == 0 && r % 2 == 0)
          {
            Terminal ::color_bg(Terminal::BLACK);
            switch (occ.at(Position(c, r))->to_ascii())
            {
            case 'R':
              std::cout << "";
              break;
            case 'K':
              std::cout << "";
              break;
            case 'Q':
              std::cout << "";
              break;
            case 'N':
              std::cout << "";
              break;
            case 'P':
              std::cout << "";
              break;
            case 'B':
              std::cout << "";
              break;
            case 'r':
              std::cout << "";
              break;
            case 'k':
              std::cout << "";
              break;
            case 'q':
              std::cout << "";
              break;
            case 'n':
              std::cout << "";
              break;
            case 'p':
              std::cout << "";
              break;
            case 'b':
              std::cout << "";
              break;
            case 'm':
              std::cout << " ? ";
              break;
            case 'M':
              std::cout << "";
              break;
            default:
              break;
            }
          }
          if (c % 2 != 0 && r % 2 == 0)
          {
            Terminal ::color_bg(Terminal::WHITE);
            switch (occ.at(Position(c, r))->to_ascii())
            {
            case 'R':
              std::cout << "";
              break;
            case 'K':
              std::cout << "";
              break;
            case 'Q':
              std::cout << "";
              break;
            case 'N':
              std::cout << "";
              break;
            case 'P':
              std::cout << "";
              break;
            case 'B':
              std::cout << "";
              break;
            case 'r':
              std::cout << "";
              break;
            case 'k':
              std::cout << "";
              break;
            case 'q':
              std::cout << "";
              break;
            case 'n':
              std::cout << "";
              break;
            case 'p':
              std::cout << "";
              break;
            case 'b':
              std::cout << "";
              break;
            case 'm':
              std::cout << " ? ";
              break;
            case 'M':
              std::cout << "";
              break;
            default:
              break;
            }
          }
          Terminal ::set_default();
        }
        else
        {
          if (c % 2 != 0 && r % 2 == 0)
          {
            Terminal ::color_bg(Terminal::WHITE);
            std::cout << "   ";
            Terminal ::set_default();
          }
          if (c % 2 == 0 && r % 2 == 0)
          {
            Terminal ::color_bg(Terminal::BLACK);
            std::cout << "   ";
            Terminal ::set_default();
          }
          if (c % 2 == 0 && r % 2 != 0)
          {
            Terminal ::color_bg(Terminal::WHITE);
            std::cout << "   ";
            Terminal ::set_default();
          }
          if (c % 2 != 0 && r % 2 != 0)
          {
            Terminal ::color_bg(Terminal::BLACK);
            std::cout << "   ";
            Terminal ::set_default();
          }
        }
      }
      Terminal ::color_fg(true, Terminal::RED);
      std::cout << " " << i;
      i--;
      std::cout << std::endl;
    }
    Terminal ::color_fg(true, Terminal::RED);
    std::cout << "   A  B  C  D  E  F  G  H"
              << "\n";
    Terminal ::set_default();
  }

  void Board::erase_piece(const Position &position)
  {
    delete occ.at(position);
    occ.erase(position);
  }

  void Board::move_piece(const Position &start, const Position &end)
  {
    add_piece(end, occ.at(start)->to_ascii());
    erase_piece(start);
    // promote piece if needed
    if (occ.at(end)->to_ascii() == 'P' && end.second == '8')
    { // white pawn
      bool white = true;
      promote(end, white);
    }
    if (occ.at(end)->to_ascii() == 'p' && end.second == '1')
    { // black pawn
      bool white = false;
      promote(end, white);
    }
  }

  bool Board::has_valid_kings() const
  {
    int white_king_count = 0;
    int black_king_count = 0;
    for (std::map<std::pair<char, char>, Piece *>::const_iterator it = occ.begin();
         it != occ.end();
         it++)
    {
      if (it->second)
      {
        switch (it->second->to_ascii())
        {
        case 'K':
          white_king_count++;
          break;
        case 'k':
          black_king_count++;
          break;
        }
      }
    }
    return (white_king_count == 1) && (black_king_count == 1);
  }
  /////////////////////////////////////
  // DO NOT MODIFY THIS FUNCTION!!!! //
  /////////////////////////////////////
  std::ostream &operator<<(std::ostream &os, const Board &board)
  {
    for (char r = '8'; r >= '1'; r--)
    {
      for (char c = 'A'; c <= 'H'; c++)
      {
        const Piece *piece = board(Position(c, r));
        if (piece)
        {
          os << piece->to_ascii();
        }
        else
        {
          os << '-';
        }
      }
      os << std::endl;
    }
    return os;
  }
}