utility.cpp 5.75 KB
#include "utility.h"

// stl
#include "numeric"
#include "iostream"

namespace othello { namespace utility
{
  ////////////////////
  //
  //
  // Interface Utility
  // Functions
  //
  //
  ////////////////////

  BitPieces occupiedPositions(const BitBoard& board)
  {
    return othello::BitPieces(board[0] | board[1]);
  }

  bool occupied(const BitPieces& pieces, const BitPos& board_pos)
  {
    return pieces.test(board_pos.value()); //.test checks without giving it access to modify
  }

  bool occupied(const BitBoard& board, const BitPos& board_pos)
  {
    //return occupiedPositions(board).test(board_pos.value());

    const auto isOccupied = occupiedPositions(board);
    return occupied(isOccupied, board_pos);
  }

  BitPos nextPosition(const BitPos& board_pos, const MoveDirection& dir)
  {
      switch(dir) {
      case othello::MoveDirection::N:
          if(board_pos.value() < 56)
              return othello::BitPos(board_pos.value() + 8);
          break;
      case othello::MoveDirection::NE:
          if((board_pos.value() < 56) and (board_pos.value() % 8 != 0))
              return othello::BitPos(board_pos.value() + 7);
          break;
      case othello::MoveDirection::E:
          if(board_pos.value() % 8 != 0)
              return othello::BitPos(board_pos.value() - 1);
          break;
      case othello::MoveDirection::SE:
          if((board_pos.value() > 7) and (board_pos.value() % 8 != 0))
              return othello::BitPos(board_pos.value() - 9);
          break;
      case othello::MoveDirection::S:
          if(board_pos.value() > 7)
              return othello::BitPos(board_pos.value() - 8);
          break;
      case othello::MoveDirection::SW:
          if((board_pos.value() > 7) and (board_pos.value() % 8 != 7))
              return othello::BitPos(board_pos.value() - 7);
          break;
      case othello::MoveDirection::W:
          if(board_pos.value() % 8 != 7)
              return othello::BitPos(board_pos.value() + 1);
          break;
      case othello::MoveDirection::NW:
          if((board_pos.value() < 56) and (board_pos.value() % 8 != 7))
              return othello::BitPos(board_pos.value() + 9);
          break;
      }
    return BitPos::invalid();
  }

  BitPosSet findBorder(const BitBoard &board)
  {
      BitPieces occupied_positions = occupiedPositions(board);
      BitPosSet border_pieces;
        for (size_t i = 0; i < detail::computeBoardSize(); ++i){
            if (occupied_positions.test(i)){
                for (int j=0; j < 8; ++j){
                    BitPos neighbor = nextPosition(BitPos {i}, static_cast <MoveDirection>(j));
                    if (neighbor.isValid() && !occupied(board, neighbor)){
                        border_pieces.insert(neighbor);
                    }
                }
            }
        }
      return border_pieces; //not as efficient as it goes through the whole board for every move instead of simply updating
  }



  BitPos findBracketingPiece(const BitBoard& board,
                             const BitPos& board_pos,
                             const PlayerId& player_id,
                             const MoveDirection& dir)
  {
        BitPos next_position = nextPosition(board_pos, dir);

        const BitPieces& current_player_pieces = board.at(size_t(player_id));
        const BitPieces& opponent_player_pieces = board.at(size_t(opponent(player_id)));

        if (not next_position.isValid() or not opponent_player_pieces.test(next_position.value())){
            return BitPos::invalid();
        }
        next_position = nextPosition(next_position, dir);

        while (next_position.isValid()){
            if (current_player_pieces.test(next_position.value()))
                return next_position;
            if (not opponent_player_pieces.test(next_position.value()))
                return BitPos::invalid(); // Must be an empty position

            next_position = nextPosition(next_position, dir);

        }

        return BitPos::invalid();
  }



  BitPosSet legalMoves(const BitBoard& board, const PlayerId& player_id)
  {
    BitPosSet legal_moves;
        for (size_t i = 0; i < detail::computeBoardSize(); ++i){
            if(isLegalMove(board, player_id, BitPos(i))){
                legal_moves.insert(BitPos(i));
                    }
                }
     return legal_moves;
  }

  bool isLegalMove(const BitBoard& board, const PlayerId& player_id,
                   const BitPos& board_pos)
  {
    for (size_t i = 0; i < 8; ++i){
        auto dir = static_cast<MoveDirection>(i);
        BitPos bracketing_pieces = findBracketingPiece(board, board_pos, player_id, dir);
        if (bracketing_pieces.isValid()and !occupiedPositions(board).test(board_pos.value()))//added and !occupiedPositions
          return true;
    }
    return false;
  }


  void placeAndFlip(BitBoard& board, const PlayerId& player_id,
                    const BitPos& board_pos)
  {
    if(isLegalMove(board, player_id, board_pos)){
      board.at(size_t(player_id)).set(board_pos.value());
      for (size_t i = 0; i < 8; ++i){
        auto dir = static_cast<MoveDirection>(i);
        BitPos bracketing_piece = findBracketingPiece(board, board_pos, player_id, dir);
        if (bracketing_piece.isValid()){
            BitPos captured_piece = nextPosition(board_pos, dir);
            while (bracketing_piece != captured_piece){
                std::for_each(std::begin(board),std::end(board),[&captured_piece](auto& piece_set){
                    piece_set.flip(captured_piece.value());
                });
                captured_piece = nextPosition(captured_piece, dir);
            }
        }

      }
    }
  }

  PlayerId opponent(const PlayerId& player_id){
      if (player_id == PlayerId::One){
         return PlayerId::Two;
      }
      return PlayerId::One;
  }
}
}   // namespace othello::utility