/*
  Copyright (C) 2005 Dan Alcantara (dfalcantara@ucdavis.edu, http://wwwcsif.cs.ucdavis.edu/~alcantar)
  Based on the board game "Blokus" by Educational Insights, Inc.
  
  This program is free software; you can redistribute 
  it and/or modify it under the terms of the GNU General 
  Public License as published by the Free Software Foundation; 
  either version 2 of the License, or (at your option) any later version.
  
  This program is distributed in the hope that it will be useful, but 
  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 
  more details.
  
  You should have received a copy of the GNU General Public License along 
  with this program; if not, write to the Free Software Foundation, Inc., 
  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  
  Full license visible at http://www.opensource.org/licenses/gpl-license.php
*/

/*	This file is contains the AI logic.
 */

#include "ai.h"

AI::AI(BlokishBoard &board)
{
  boardPtr = &board;
}

int AI::GetNumDone()
{
  return nRow * boardPtr->GetHeight() + nCol;
}

// Resets the AI.  Must be called before Step is run.
void AI::Reset(AIOptions opts, BlokishID playerID, std::vector<PieceVector*> &allPlayerPieces)
{
  bRestartedAlready = false;
  mOptions = opts;
  srand(time(NULL));

  nRow = 0;
  nCol = 0;
  nPiece = 0;

  allPieces = allPlayerPieces;
  player = playerID;
  maxRating = INT_MIN;
  numWithMaxRating = 0;
  possible_moves.clear();
}

// Runs the AI logic.  If the threads are enabled, it runs everything in one sweep.
// Otherwise, it evaluates one row, then returns.
AI::StepOutcome AI::Step(BlokishBoard::Move &move)
{
  // Analyze one row then return to update the progress bar.
  
  if(nRow >= boardPtr->GetHeight())
  { // This shouldn't happen.  If it does, start from the beginning.
    std::cerr << __FILE__ << ", " << __LINE__ << ": Restarted.\n";
    return MustReset;
  }

  PieceVector &pieces = *(allPieces[player]);

  for(nRow = 0; nRow < int(boardPtr->GetHeight()); nRow++)
  for(nCol = 0; nCol < int(boardPtr->GetWidth()); nCol++)
  {
    for(nPiece = 0; nPiece < int(pieces.size()); nPiece++)
    {
      BlokishPiece &piece = *(pieces[nPiece]);

      for(int nFlip = 0; nFlip < piece.mnSides; nFlip++)
      {
        for(int nRotation = 0; nRotation < piece.mnUniqueRotations; nRotation++)
        {
          Try();
          piece.RotateClockwise();
        }

        piece.ReflectHorizontal();
      }
    }
  }

	// After all the possible moves are calculated, it picks a high-scoring one.
  if(possible_moves.size() == 0)
  { // No moves were found.  Something went terribly wrong.
    // Shouldn't need this logic anymore.  Try to phase it out.
    std::cerr << __FILE__ << ", " << __LINE__ << ": " << "possible_moves.size() = " << possible_moves.size() << "\n";
    std::cerr << "Restarting...\n";
    return MustReset;
  }
  
  // Pick one high scoring move.
  std::sort(possible_moves.begin(), possible_moves.end());
  int maxSelection = std::min(int(numWithMaxRating * 1.5), int(possible_moves.size()));
  int selection = possible_moves.size() - 1 - (rand() % maxSelection);
  move = possible_moves[selection];
  return Complete;
}

// Try calculates all the moves possible at one particular square.
void AI::Try()
{
  PieceVector &pieces = (*(allPieces[player]));
  BlokishPiece *piece = pieces[nPiece];
  
  if(boardPtr->PlacePiece(piece, nRow, nCol))
  {
    // Calculate the value of this move
    int score =   mOptions.weightSize * piece->GetSquares().size() 
                + mOptions.weightFriendlySpaces * boardPtr->GetNumCorners(player, pieces, mOptions.numFriendlyPiecesToCheck)
                - mOptions.weightEnemySpaces * boardPtr->GetNumEnemyCorners(player, allPieces, mOptions.numEnemyPiecesToCheck); 

    // Encourage moving to the center at the beginning
    if(pieces.size() > 17 && boardPtr->GetSpaceID(boardPtr->GetHeight() / 2, boardPtr->GetWidth() / 2) == none)
    {
      int maxCenterScore = INT_MAX;

      // Figure out how close the piece really gets
      const SquareVector& squares = pieces[nPiece]->GetSquares();
      
      for(SquareVector::const_iterator itr = squares.begin(); itr != squares.end(); itr++)
      {
        int aRow = nRow + itr->mY;
        int aCol = nCol + itr->mX;
        int centerScore = abs(aCol - boardPtr->GetWidth() / 2) + abs(aRow - boardPtr->GetHeight() / 2);
        centerScore *= centerScore;

        if(centerScore < maxCenterScore)
          maxCenterScore = centerScore;
      }

      score -= maxCenterScore;
    }
                
    boardPtr->UndoPlacePiece();

    BlokishBoard::Move possible(*pieces[nPiece], nRow, nCol, score);

    if(maxRating < score)
    {
      maxRating = score;
      numWithMaxRating = 1;
    }
    else if(maxRating == score)
    {
      numWithMaxRating++;
    }

    possible_moves.push_back(possible);
  }
}
