/* This program performs iterative-deepening A* on the sliding tile
puzzles, using the Manhattan distance evaluation function. It was
written by Richard E.  Korf, Computer Science Department, University
of California, Los Angeles, Ca.  90024.  

This program implements a slight modification over the original
version: time is accounted for every instance solved and various stats
about time are provided while the test set is being solved */

#include <stdio.h>                                   /* standard I/O library */ 
#include <string.h>                                                /* memcpy */ 
#include <sys/time.h>                                       /* time handling */ 
#include <unistd.h>                                         /* time handling */ 

#define NUMBER 100               /* number of problem instances to be solved */ 
#define X 4                                    /* squares in the x dimension */ 
#define SIZE 16                                   /* total number of squares */ 

int s[SIZE];                       /* state of puzzle: tile in each position */

struct operators                  
{int num;                                 /* number of applicable oprs: 2..4 */
 int pos[4];} oprs[SIZE];    /* position of adjacent tiles for each position */

int increment [SIZE] [SIZE] [SIZE];    /* incr eval func: tile, source, dest */

int thresh;                                       /* search cutoff threshold */
double generated;                /* number of states generated per iteration */
double total;                            /* total number of states generated */

/* INITOPS initializes the operator table. */

initops ()

{int blank;                                   /* possible positions of blank */

 for (blank = 0; blank < SIZE; blank++)  /* for each possible blank position */
   {oprs[blank].num = 0;                               /* no moves initially */
    if (blank > X - 1)                                       /* not top edge */
      oprs[blank].pos[oprs[blank].num++] = blank - X;       /* add a move up */
    if (blank % X > 0)                                      /* not left edge */
      oprs[blank].pos[oprs[blank].num++] = blank - 1;     /* add a move left */
    if (blank % X < X - 1)                                 /* not right edge */
      oprs[blank].pos[oprs[blank].num++] = blank + 1;    /* add a move right */
    if (blank < SIZE - X)                                 /* not bottom edge */
      oprs[blank].pos[oprs[blank].num++] = blank + X;}}   /* add a move down */

/* INIT pre-computes the incremental evaluation function table. For a
given tile moving from a given source position to a given destination
position, it returns the net change in the value of the Manhattan
distance, which is either +1 or -1.  */

init (increment)

int increment [SIZE] [SIZE] [SIZE];/* incr eval function: tile, source, dest */

{int tile;                                               /* tile to be moved */
 int source, dest;                       /* source and destination positions */
 int destindex;                       /* destination index in operator table */

 for (tile = 1; tile < SIZE; tile++)                   /* all physical tiles */
   for (source = 0; source < SIZE; source++)   /* all legal source positions */
     for (destindex = 0; destindex < oprs[source].num; destindex++) 
                                             /* legal destinations of source */
       {dest = oprs[source].pos[destindex];    /* dest is new blank position */
	increment[tile][source][dest]  = abs((tile % X) - (dest % X))   
                                       - abs((tile % X) - (source % X)) 
                                       + abs((tile / X) - (dest / X))
                                       - abs((tile / X) - (source / X));}}

/* INPUT accepts an initial state from the terminal, assuming it is
preceded by a problem number. It stores it in the state vector and
returns the position of the blank tile. */

input (s)

int s[SIZE];                                                 /* state vector */

{int index;                                       /* index to tile positions */
 int blank;                                        /* position of blank tile */

 scanf ("%*d");                                  /* skip over problem number */
 for (index = 0; index < SIZE; index++)                 /* for each position */
   {scanf ("%d", &s[index]);                  /* input tile in that position */
    if (s[index] == 0) blank = index;}     /* note blank position in passing */
 return (blank);}

/* MANHATTAN returns the sum of the Manhattan distances of each tile,
 except the blank, from its goal position. */

manhattan (s)

int s[SIZE];                                                        /* state */

{int value;                                                   /* accumulator */
 int pos;                                                   /* tile position */

  value = 0;
  for (pos = 0; pos < SIZE; pos++)
    if (s[pos] != 0)            /* blank isn't counted in Manhattan distance */
      value = value + abs((pos % X) - (s[pos] % X))            /* X distance */
                    + abs((pos / X) - (s[pos] / X));           /* Y distance */
  return(value);}

/* SEARCH performs one depth-first iteration of the search, cutting
 off when the depth plus the heuristic evaluation exceeds THRESH. If
 it succeeds, it returns 1 and records the sequence of tiles moved in
 the solution.  Otherwise, it returns 0 */

search (blank, oldblank, g, h)

int blank;			                /* current position of blank */
int oldblank;			               /* previous position of blank */
int g;                                            /* current depth of search */
int h;	                           /* value of heuristic evaluation function */

{ int index;                                    /* index into operator array */
  int newblank;                               /* blank position in new state */
  int tile;                                              /* tile being moved */
  int newh;                             /* heuristic evaluation of new state */
  
  for (index = 0; index < oprs[blank].num; index++)     /* for each appl opr */
    if ((newblank = oprs[blank].pos[index]) != oldblank) /*not inv last move */
      {tile = s[newblank];            /* tile moved is in new blank position */
      newh = h + increment[tile][newblank][blank];   /* new heuristic est */
       generated++;                                 /* count nodes generated */
       if (newh+g+1 <= thresh)                    /* less than search cutoff */
	 {s[blank] = tile;                               /* make actual move */
	  if ((newh == 0) ||                     /* goal state is reached or */
	      (search(newblank, blank, g+1, newh)))       /* search succeeds */
	    return (1);                                 /* exit with success */
	  s[newblank] = tile;}}       /* undo current move before doing next */
  return (0);}                                          /* exit with failure */

/* Main program does the initialization, inputs an initial state, solves it,
   and prints the solution. */

main ()

{
  int success;                         /* boolean flag for success of search */
  int blank;                                    /* initial position of blank */
  int initeval;                       /* manhattan distance of initial state */
  int problem;                                           /* problem instance */
  int index;                                      /* index to tile positions */
  long totalTime = 0;       /* time elapsed for solving the whole test suite */
  
  struct timeval stv,etv;
  struct timezone stz,etz;
  float thistime;
  float totaltime=0.0;
  
  initops ();                                   /* initialize operator table */
  init (increment);                        /* initialize evaluation function */
  
  for (problem = 1; problem <= NUMBER; problem++){ /* for each initial state */
    blank = input(s);                                 /* input initial state */
    gettimeofday (&stv,&stz);
    thresh = initeval = manhattan(s);      /* initial threshold is initial h */
    total = 0;                           /* initialize total nodes generated */
    
    do{                    /* depth-first iterations until solution is found */
      generated = 0;            /* initialize number generated per iteration */
      success = search (blank, -1, 0, initeval);           /* perform search */
      fflush(stdout);       /* flush output buffer to see progress of search */
      total = total + generated;    /* keep track of total nodes per problem */
      thresh += 2;}                     /* threshold always increases by two */
    while (!success);                             /* until solution is found */
    
    gettimeofday (&etv,&etz);
    if (etv.tv_usec>stv.tv_usec){
      thistime=(etv.tv_sec-stv.tv_sec)+(etv.tv_usec-stv.tv_usec)/1000000.0;}
    else{
      thistime=(etv.tv_sec-stv.tv_sec)+(1000000.0+etv.tv_usec-stv.tv_usec)/1000000.0;}
    
    totaltime+=thistime;
    
    printf ("%d %d %10.f %2.2f/%2.2f (%2.2f)\n", problem, thresh-2, total,
	    thistime,totaltime,totaltime/(problem*1.0));}}