// Mole.cpp

//

// IEEE Challenge 13 - The Hard Life Of A Mole

// http://www.ieee.org/portal/cms_docs_iportals/iportals/membership/

//       /students/scholarshipsawardscontests/

//       /IEEEXtreme2008_Competitition_book_2.pdf

//

// by Adrian Dale 15/12/2008 / 26/07/2009

 

#include <iostream>

#include "Mole.h"

 

int main( int argc, char **argv)

{

      Underground theUnderground;

     

      cout << theUnderground.Solve() << endl;

 

      return 0;

}

 

// Does what it says on the tin!

int Underground::Solve()

{

      ReadInput();

 

      Solve_Init();

           

      // Clear out mWorkMap's visited record

      // We no longer need to do this. Code is a left-over from when I

      // was using the mWorkMap to do pre-checks on the validity of the map.

      // I've left it in (but commented out) as a record of how to

      // do this should it be needed in future

      //for_each( mWorkMap.data(),

      //          mWorkMap.data() + mWorkMap.num_elements(),

      //          bind2nd(mem_fun_ref(&WorkingMapCell::SetVisited), false) );

 

      Solve_FirstPass();

 

      // Map is only solveable if the first pass visited our end point,

      // having started from the start point.

      if ( !mWorkMap[mEndPos.x][mEndPos.y].mVisited )

      {

            cout << "ERROR: Map has no solution" << endl;

            return -1;

      }

           

      // Repeat scan until done

      while ( RescanCheapest() != 0 )

            ;

 

      return SolutionCost();

}

 

// Set up the map ready to be solved

void Underground::Solve_Init()

{

      // Set up our working map

      mWorkMap.resize(boost::extents[mCols][mRows]);

      // Initialise it from the map data we read in.

      // We'll keep a pristine copy of that, rather than work on it, so

      // we can use it to refresh the display or work map, if necessary.

      for( int y=0; y<mRows; ++y )

            for( int x=0; x<mCols; ++x )

                  mWorkMap[x][y].SetCell( mMapData[x][y] );

 

      // Find our starting and ending points - they are member variables

      // to save us having to repeat this calculation

      mStartPos = FindSquare('A');

      mEndPos = FindSquare('B');

     

      // Our first move will never be 0,0 as there will always

      // a wall around the map. This checks that we did find a first move.

      // We're using assertions to assert that a valid map was passed in.

      // For a proper app, we'd have separate code to check maps.

      // eg for valid start and end positions

      assert(mStartPos.x != 0 && mStartPos.y != 0);

      assert(mEndPos.x != 0 && mEndPos.y != 0);

}

 

// Routine to locate either our start ('A') or our end ('B') position

// Assumes map data is valid

Move Underground::FindSquare( MapDataCellType squType ) const

{

      assert(squType == 'A' || squType == 'B');

 

      for( int y=0; y<mRows; ++y )

            for( int x=0; x<mCols; ++x )

                  if ( mWorkMap[x][y].mCell == squType )

                        return Move(x,y);

      assert(false); // Map doesn't contain start or end - ERROR!

      return Move();

}

 

// Solve_FirstPass starts from the start square and recursively

// (simulated using a stack) visits each neighbouring square and

// adds up the cost to travel back to the start square.

// This only gives an approximation to the final answer, because

// sometimes the cheapest route is one that hasn't yet been found

// by this pass of the solver.

// Once we have a first pass, which has visited all visitable

// squares, we will iteratively refine the solution using RescanCheapest

void Underground::Solve_FirstPass()

{

      // The stack will hold each square that remains to be checked.

      // This simulates recursion but without the function call

      // overhead.

      MoveStackType moveStack;

     

      // Kick the algorithm off by marking our start position as

      // visited and pushing it onto the stack

      mWorkMap[mStartPos.x][mStartPos.y].mVisited = true;

      mWorkMap[mStartPos.x][mStartPos.y].mCostToStart = 0;

      mWorkMap[mStartPos.x][mStartPos.y].mCheapestMove = mStartPos;

      moveStack.push(mStartPos);

 

      // We keep popping moves off the stack and processing them

      // until there are no more moves left on the stack.

      // Processing means checking each neighbour to see if we've

      // visited it before, and if not, if it is visitable.

      // If yes, then push it onto the stack, so its neighbours can

      // be checked in turn.

      // Eventually (when there are no moves left on the stack) we'll

      // have visited all of the visitable squares.

      while( !moveStack.empty() )

      {

            Move &currentMove = moveStack.top();

            moveStack.pop();

 

            const int x = currentMove.x;    // A bit of renaming so that our macro

            const int y = currentMove.y;    // works. Messy, but compiler should

                                                            // optimise it away

#define DIRECTION(xx,yy) ProcessDirection(moveStack, xx,yy);

            DIRECTION(x-1,y-1);

            DIRECTION(x,y-1);

            DIRECTION(x+1,y-1);

            DIRECTION(x+1,y);

            DIRECTION(x+1,y+1);

            DIRECTION(x,y+1);

            DIRECTION(x-1,y+1);

            DIRECTION(x-1,y);

#undef DIRECTION

      }

}

 

// Not sure what this could be, but didn't want to use -1, as we're

// always looking for a number less than this to start the algorithm

#define MAX_COST 1000000

 

// ProcessDirection is called from Solve_FirstPass and is used to

// work out what to do with a square joining onto the one we are

// currently investigating.

// We need to mark it as visited, then find the cheapest cost back to

// the start. If the cheapest cost back to the start is lower than an

// existing route to the start then we need to recurse back through the

// map updating the cheapest route

void Underground::ProcessDirection(MoveStackType &moveStack, int x, int y)

{

      // If we've already visited this square, then no need to process it again.

      if ( mWorkMap[x][y].mVisited == true || mWorkMap[x][y].mCell == '@')

            return;

 

      mWorkMap[x][y].mVisited = true;

 

      // Store move so that all possible moves from this direction will

      // get processed once this move is popped from the stack

      moveStack.push(Move(x,y));

 

      // We need to see which (known) direction has the cheapest cost of a move back

      // to the start.

      int cheapestToStart = MAX_COST;

      Move cheapestMove;

      WorkingMapCell *workCell;

 

#define CHECK_COST_TO_START(xx,yy)                                  \

      if (workCell->mVisited == true)                               \

      {                                                                             \

            if ( workCell->mCostToStart < cheapestToStart ) \

            {                                                                       \

                  cheapestToStart = workCell->mCostToStart;    \

                  cheapestMove = Move(xx,yy);                              \

            }                                                                       \

      }

 

      // This code calls CHECK_COST_TO_START for each direction.

#define DIRECTION(xx,yy)                    \

            workCell = &mWorkMap[xx][yy];      \

            CHECK_COST_TO_START(xx,yy);

 

            DIRECTION(x-1,y-1);

            DIRECTION(x,y-1);

            DIRECTION(x+1,y-1);

            DIRECTION(x+1,y);

            DIRECTION(x+1,y+1);

            DIRECTION(x,y+1);

            DIRECTION(x-1,y+1);

            DIRECTION(x-1,y);

#undef DIRECTION

#undef CHECK_COST_TO_START

 

      // The end result should be that cheapestToStart shows the cost

      // of the cheapest move back to the start and cheapestMove shows

      // which square to move to to get that.

 

      // We need to store these values for the current square and also add in

      // the cost of moving to the current square

      mWorkMap[x][y].mCostToStart = cheapestToStart + MoveCost(x,y);

      mWorkMap[x][y].mCheapestMove = cheapestMove;

}

 

// RescanCheapest scans through the map and adjusts the cheapest costs

// of squares where there is a cheaper route via a neighbouring square.

// It returns a count of the number of adjustments made.

// If this is zero the map is fully optimised. This function should be

// called repeatedly until this is the case.

int Underground::RescanCheapest()

{

      int cellsAltered = 0;

 

      // Note the 1 and -1 so we don't try to scan the walls

      for( int y=1; y<mRows-1; ++y )

      {

            for( int x=1; x<mCols-1; ++x )

            {

                  WorkingMapCell *workCell = &mWorkMap[x][y];

 

                  // See how the cost from our cell compares to the

                  // costs from neighbouring cells

                  WorkingMapCell *neighbourCell;

#define DIRECTION(xx,yy)                                                        \

                  neighbourCell = &mWorkMap[xx][yy];                              \

                  if ( RescanCheapestCell(workCell, neighbourCell,    \

                                                                        x, y, xx, yy) )      \

                        ++cellsAltered;

            DIRECTION(x-1,y-1);

            DIRECTION(x,y-1);

            DIRECTION(x+1,y-1);

            DIRECTION(x+1,y);

            DIRECTION(x+1,y+1);

            DIRECTION(x,y+1);

            DIRECTION(x-1,y+1);

            DIRECTION(x-1,y);

#undef DIRECTION                    

            }

      }

 

      return cellsAltered;

}

 

// workCell is the cell we're considering.

// neighbourCell is the one we're checking. This will be called for each

// neighbour of workCell

// x,y is the coords of workCell

// xx,yy is the coord of neighbourCell

bool Underground::RescanCheapestCell( WorkingMapCell * workCell,

                                                       WorkingMapCell * neighbourCell,

                                                       int x, int y, int xx, int yy )

{

      // Don't need to worry about unreachable cells

      if ( neighbourCell->mVisited == false )

            return false;

 

      // If it costs less from our cell to the start than it would cost

      // to get to our cell from the neighbour, then onto the start, then

      // our cell is cheaper than going that way, so continue.

      if ( neighbourCell->mCostToStart + MoveCost(x,y) >= workCell->mCostToStart )

            return false;

 

      // Otherwise we need to alter our cell to go via our neighbour

      workCell->mCostToStart = MoveCost(x,y) + neighbourCell->mCostToStart;

      workCell->mCheapestMove = Move(xx,yy);

      return true;

}

 

// Calculates the cost of the route from B to A.

// This only should be called after RescanCheapest has been called

// enough times to find the solution.

int Underground::SolutionCost()

{

#ifdef _DEBUG

      int safetyCount = 0;

#endif

      Move currentMove = mEndPos;

      int squaresDug = 0;

      while(true)

      {

            // This loop traverses from currentMove to the preceding

            // closest to the start move, until the start is reached.

            // Note that we count moves rather than move costs.

            squaresDug += MoveCost(currentMove.x, currentMove.y) ? 1 : 0;

            currentMove = mWorkMap[currentMove.x][currentMove.y].mCheapestMove;

            if ( currentMove.x == mStartPos.x && currentMove.y == mStartPos.y )

                  break;

            assert( ++safetyCount < 10000 ); // In case of infinite loop

      }

      return squaresDug;

}

 

// Read the input map from stdin

void Underground::ReadInput()

{

      cin >> mRows;

      cin >> mCols;

 

      // We're going to add a fake stone border to the map

      // to save us having to worry about making paths that

      // go off the edge of the map, so increase the map size

      // to allow for it.

      mRows += 2;

      mCols += 2;

 

      // Set the size of our map data multi_array.

      mMapData.resize(boost::extents[mCols][mRows]);

 

      for( int y=0; y < mRows; ++y )

      {

            if ( y == 0 || y == mRows-1 )

            {

                  // Add our fake row of stones

                  for( int x=0; x < mCols; ++x )

                        mMapData[x][y] = '@';

            }

            else

            {

                  // All other rows get read from the input

                  for( int x=0; x < mCols; ++x )

                  {

                        if ( x == 0 || x == mCols-1 )

                        {

                              // Add stones at start and end of row

                              mMapData[x][y] = '@';

                        }

                        else

                        {

                              char mapCell;

                              cin >> mapCell;

                              mMapData[x][y] = mapCell;

                        }

                  }

            }

      }

}

 

#ifdef _DEBUG

// Dump the map onto the console

// For debug purposes

void Underground::DumpMap() const

{

      for( int y=0; y<mRows; ++y )

      {

            for( int x=0; x<mCols; ++x )

            {

                  cout << mMapData[x][y];

            }

            cout << endl;

      }

      // Blank line to separate repeated map dumps

      cout << endl;

}

#endif

 

// Assign costs to moving to the different types of square.

// This allows us to define the cheapest route.

int Underground::MoveCost(int x, int y) const

{

      switch ( mMapData[x][y] )

      {

      case 'X':

            return -1;

      case 'A':

            return 0;

      case 'B':

            return 1;

      case '.':

            return 0;

      case '@':

            return -1;

      case '#':

            return 1;

      case '~':

            // This is the count of all the #'s in puzzle to show that

            // the mole would rather dig all of the earth than a single unit of castor oil.

            return mRows * mCols;

      };

 

      return 0;

}