// L-39 MCS 360 Wed 29 Nov 2017 : mazepath.cpp

/* A maze is represented by an integer matrix with entries of 3 types:
   1) 0 means the the tile in the grid is free;
   2) -1 means the tile in the grid is blocked;
   3) a positive value means the tile has been visited. */ 

#include <iostream>
#include <sstream>
#include <string>
#include <iomanip>
#include <cstdlib>
#include <vector>

using namespace std;

void allocate_integer_matrix
 ( vector< vector<int> >& mat, size_t rows, size_t cols );
/*
   Allocates an integer matrix with as many rows
   as rows and as many columns as cols. */

int loaded_coin ( double p );
/*
   A fair coin will turn out as many 0 as 1 if p = 0.5,
   as p is the probability of 0. */

void random_integer_matrix
 ( vector< vector<int> >& mat, double prb );
/*
   Generates at random -1 and 0 values,
   placed at random in the matrix mat,
   where prb is the probability for 0. */

void write_integer_matrix ( vector< vector<int> >& mat );
/*
   Writes the matrix, leaving two open spaces for 0,
   using xx for -1, and 2 decimals for positive values. */

bool search
 ( vector< vector<int> >& mat, size_t count,
   size_t row, size_t col );
/*
   Applies depth-first search to find a path
   from the first row to the last column in the grid.
   The parameter have the following meaning:

   mat   : a grid of tiles, free, block, or visited;
   count : counts the number of tiles visited;
   row   : row of the next tile;
   col   : column of the next tile.

   Visited tiles are marked in mat by the counter.
   Returns true if a path is found, false otherwise. */

int main ( void )
{
   // two specific setting of the seed:
   // size_t seed = 1511975066; // no path
   // rows = 15, columns = 15, probability = 0.7
   // size_t seed = 1511975636; // nice path
   // rows = 15, columns = 15, probability = 0.7
   size_t seed = time(NULL);
   cout << "The seed : " << seed << endl;
   srand(seed);

   cout << "Give the number of rows : ";
   int rows; cin >> rows;

   cout << "Give the number of columns : ";
   int cols; cin >> cols;

   cout << "Give the probability for zero : ";
   double prb; cin >> prb;

   vector< vector<int> > mat;
   allocate_integer_matrix(mat, rows, cols);
   random_integer_matrix(mat, prb);
   mat[0][0] = 0; mat[rows-1][cols-1] = 0;
   cout << "The maze before the search :" << endl;
   write_integer_matrix(mat);

   bool found = search(mat,0,0,0);
   cout << "The maze after the search :" << endl;
   write_integer_matrix(mat);
   if(found)
      cout << "Found a path." << endl;
   else
      cout << "No path found." << endl;

   return 0;
}

void allocate_integer_matrix
 ( vector< vector<int> >& mat, size_t rows, size_t cols )
{
   for(size_t i=0; i<rows; i++)
   {
      vector<int> row;

      for(size_t j=0; j<cols; j++) row.push_back(0);

      mat.push_back(row);
   }
}

int loaded_coin ( double p )
{
   double r = ((double) rand())/RAND_MAX;

   return (r <= p ? 0 : 1);
}

void random_integer_matrix
 ( vector< vector<int> >& mat, double prb )
{
   const int rows = mat.size();
   const int cols = mat[0].size();

   for(size_t i=0; i<rows; i++)
      for(size_t j=0; j<cols; j++)
         mat[i][j] = -(loaded_coin(prb));
}

void write_integer_matrix ( vector< vector<int> >& mat )
{
   const int rows = mat.size();
   const int cols = mat[0].size();

   for(size_t i=0; i<rows; i++)
   {
      for(size_t j=0; j<cols; j++)
         if(mat[i][j] == 0)
            cout << "   ";
         else if(mat[i][j] == -1)
            cout << "xxx";
         else
            cout << setw(3) << mat[i][j];
      cout << endl;
   }
}

bool search
 ( vector< vector<int> >& mat,
   size_t count, size_t row, size_t col )
{
   if(mat[row][col] != 0) // tile is not free
      return false;
   else
   {
      mat[row][col] = count+1; // visit

      const int rows = mat.size();
      const int cols = mat[0].size();

      if((row == rows-1) and (col == cols-1))
         return true; // reached destination
      else
      {
         bool found = false;
      
         if(col < cols-1)                 // go down 
            found = search(mat, count+1, row, col+1);
         if(not found and (row < rows-1)) // go right
            found = search(mat, count+1, row+1, col);
         if(not found and (col > 0))      // go left
            found = search(mat, count+1, row, col-1);
         if(not found and (row > 0))      // go up
            found = search(mat, count+1, row-1, col);

         return found;
      }
   }
}
