/* Robot API for the GNU Robots game */

/* Copyright (C) 1998 Jim Hall, jhall1@isd.net */

/*
  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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/


#include <stdio.h>
#include <stdlib.h>				/* for abs, free */

#include <gh.h>					/* GNU Guile high */

#include "api.h"				/* GNU Robots API */
#include "hooks.h"				/* GNU Robots UI hooks */
#include "main.h"				/* for exit_nicely */
#include "sign.h"				/* a hack for +/- sign */


/* Globals (from main.c) */

extern int **map;
extern int nrows;
extern int ncols;

extern int robot_x;
extern int robot_y;
extern int robot_dir;
extern int robot_score;
extern int robot_energy;
extern int robot_shields;


/* Globals.  (this file only) */

int cdirs[] = {'N', 'E', 'S', 'W'};

char *things[] = {"space", "food", "prize", "wall", "baddie", "robot"};
int  cthings[] = {SPACE  , FOOD  , PRIZE  , WALL  , BADDIE  , ROBOT};


/* Functions */

SCM
robot_turn (SCM s_n)
{
  int n;
  int i;
  int incr;

  n = gh_scm2int (s_n);

  /* turn left or right? */

  incr = sign (n);

  for (i = 0; i < abs (n); i++) {
    robot_dir += incr;

    if (robot_dir > 3) {
      robot_dir = 0;
    }

    else if (robot_dir < 0) {
      robot_dir = 3;
    }

    /* animate the robot */

    hook_add_robot (robot_x, robot_y, cdirs[robot_dir]);
    if (--robot_energy < 1) {
      exit_nicely ();
    }
  } /* for */

  return (SCM_BOOL_T);
}

SCM
robot_move (SCM s_n)
{
  int x_to, y_to;
  int dx, dy;
  int i;
  int n;

  n = gh_scm2int (s_n);

  /* determine changes to x,y */

  switch (robot_dir) {
  case 0: /* N */
    dx = 0;
    dy = -1 * sign (n);
    break;
  case 1: /* E */
    dx = sign (n);
    dy = 0;
    break;
  case 2: /* S */
    dx = 0;
    dy = sign (n);
    break;
  case 3: /* W */
    dx = -1 * sign (n);
    dy = 0;
    break;
  }

  /* Move the robot */

  for (i = 0; i < abs (n); i++) {
    /* check for a space */

    x_to = robot_x + dx;
    y_to = robot_y + dy;

    /* no matter what, this took energy */

    if (--robot_energy < 1) {
      exit_nicely ();
    }

    switch (map[y_to][x_to]) {
    case SPACE: /* space */
      /* move the robot there */

      map[robot_y][robot_x] = SPACE;
      map[y_to][x_to] = ROBOT;
      hook_add_thing (robot_x, robot_y, SPACE);
      hook_add_robot (x_to, y_to, cdirs[robot_dir]);
      robot_x = x_to;
      robot_y = y_to;
      break;

    case BADDIE: /* baddie */
      /* Damage */

      robot_shields -= 10;
      if (robot_shields < 1) {
	exit_nicely ();
      }

      return (SCM_BOOL_F);
      break;

    case WALL: /* wall */
      /* less damage */

      robot_shields -= 2;
      if (robot_shields < 1) {
	exit_nicely ();
      }

      return (SCM_BOOL_F);
      break;

    default:
      /* even less damage */

      if (--robot_shields < 1) {
	exit_nicely ();
      }

      return (SCM_BOOL_F);
      break;
    }
  } /* for */

  return (SCM_BOOL_T);
}

SCM
robot_smell (SCM s_th)
{
  int th;
  int i, j;
  char *str;

  str = gh_scm2newstr (s_th, NULL);
  th = what_thing (str);
  free (str);

  /* no matter what, this took energy */

  if (--robot_energy < 1) {
    exit_nicely ();
  }

  /* Smell for the thing */

  for (i = robot_x - 1; i <= robot_x +1; i++) {
    for (j = robot_y - 1; j <= robot_y + 1; j++) {
      if (map[j][i] == th) {
	/* Found it */

	return(SCM_BOOL_T);
      }
    } /* for */
  } /* for */

  /* Failed to find it */

  return (SCM_BOOL_F);
}

SCM
robot_feel (SCM s_th)
{
  int th;
  int x_to, y_to;
  int dx, dy;
  char *str;

  str = gh_scm2newstr (s_th, NULL);
  th = what_thing (str);
  free (str);
#ifndef NO_DEBUG_MSGS
  printf ("thing is %c\n", th);
  printf ("direction is %d\n", robot_dir);
#endif /* NO_DEBUG_MSGS */

  /* determine changes to x,y */

  switch (robot_dir) {
  case 0: /* N */
    dx = 0;
    dy = -1;
    break;
  case 1: /* E */
    dx = 1;
    dy = 0;
    break;
  case 2: /* S */
    dx = 0;
    dy = 1;
    break;
  case 3: /* W */
    dx = -1;
    dy = 0;
    break;
  }

  /* no matter what, this took energy */

  if (--robot_energy < 1) {
    exit_nicely ();
  }

  /* Feel for the thing */

  x_to = robot_x + dx;
  y_to = robot_y + dy;

  hook_robot_feel (robot_x, robot_y, cdirs[robot_dir], x_to, y_to);

  if (map[y_to][x_to] == BADDIE) {
    /* touching a baddie is hurtful */
    if (robot_shields < 1) {
      exit_nicely ();
    }
  }

#ifndef NO_DEBUG_MSGS
    printf ("map[%d][%d] is %c\n", y_to, x_to, map[y_to][x_to]);
#endif /* NO_DEBUG_MSGS */

  if (map[y_to][x_to] == th) {
#ifndef NO_DEBUG_MSGS
    printf ("found %c\n", th);
#endif /* NO_DEBUG_MSGS */

    return (SCM_BOOL_T);
  }

  /* Did not feel it */

#ifndef NO_DEBUG_MSGS
    printf ("not found %c\n", th);
#endif /* NO_DEBUG_MSGS */

  return (SCM_BOOL_F);
}

SCM
robot_look (SCM s_th)
{
  int th;
  int x_to, y_to;
  int dx, dy;
  char *str;

  str = gh_scm2newstr (s_th, NULL);
  th = what_thing (str);
  free (str);

  /* determine changes to x,y */

  switch (robot_dir) {
  case 0: /* N */
    dx = 0;
    dy = -1;
    break;
  case 1: /* E */
    dx = 1;
    dy = 0;
    break;
  case 2: /* S */
    dx = 0;
    dy = 1;
    break;
  case 3: /* W */
    dx = -1;
    dy = 0;
    break;
  }

  /* no matter what, this took energy */

  if (--robot_energy < 1) {
    exit_nicely ();
  }

  /* Look for the thing */

  x_to = robot_x + dx;
  y_to = robot_y + dy;

  while (map[y_to][x_to] == SPACE) {
    /* move the focus */

    x_to += dx;
    y_to += dy;
  } 

  /* Outside the loop, we have found something */

  if (map[y_to][x_to] == th) {
    return (SCM_BOOL_T);
  }

  /* else, we did not find it */

  return (SCM_BOOL_F);
}

SCM
robot_grab (void)
{
  int x_to, y_to;
  int dx, dy;

  /* determine changes to x,y */

  switch (robot_dir) {
  case 0: /* N */
    dx = 0;
    dy = -1;
    break;
  case 1: /* E */
    dx = 1;
    dy = 0;
    break;
  case 2: /* S */
    dx = 0;
    dy = 1;
    break;
  case 3: /* W */
    dx = -1;
    dy = 0;
    break;
  }

  /* Try to grab the thing */

  x_to = robot_x + dx;
  y_to = robot_y + dy;

  if (--robot_energy < 1) {
    exit_nicely ();
  }
  hook_robot_grab (robot_x, robot_y, cdirs[robot_dir], x_to, y_to);

  /* Did we grab it? */

  switch (map[y_to][x_to]) {
  case SPACE:
  case WALL:
  case ROBOT:
    return (SCM_BOOL_F);
    break;

  case BADDIE:
    robot_shields -= 10;
    if (robot_shields < 1) {
      exit_nicely ();
    }
    return (SCM_BOOL_F);

  case FOOD:
    robot_energy += 10;
    break;

  case PRIZE:
    robot_score++;
    break;
  }

  /* only successful grabs get here */

  map[y_to][x_to] = SPACE;
  hook_add_thing (x_to, y_to, SPACE);
  return (SCM_BOOL_T);
}

SCM
robot_zap (void)
{
  int x_to, y_to;
  int dx, dy;

  /* determine changes to x,y */

  switch (robot_dir) {
  case 0: /* N */
    dx = 0;
    dy = -1;
    break;
  case 1: /* E */
    dx = 1;
    dy = 0;
    break;
  case 2: /* S */
    dx = 0;
    dy = 1;
    break;
  case 3: /* W */
    dx = -1;
    dy = 0;
    break;
  }

  /* Try to zap the thing */

  x_to = robot_x + dx;
  y_to = robot_y + dy;

  robot_energy -= 5;
  if (robot_energy < 1) {
    exit_nicely ();
  }
  hook_robot_zap (robot_x, robot_y, cdirs[robot_dir], x_to, y_to);

  /* Did we destroy it? */

  switch (map[y_to][x_to]) {
  case SPACE:
  case WALL:
  case ROBOT:					/* what to w/ robots? */
    return (SCM_BOOL_F);
    break;

  case BADDIE:
  case FOOD:
  case PRIZE:
    hook_add_thing (x_to, y_to, SPACE);
    break;
  }

  /* only success gets here */

  map[y_to][x_to] = SPACE;
  hook_add_thing (x_to, y_to, SPACE);
  return (SCM_BOOL_T);
}

SCM
robot_stop (void)
{
  /* Must be a SCM function, even though it returns no value */
  /* Stop the robot immediately */

  exit_nicely ();
  return (SCM_BOOL_T);			/* never gets here */
}

/* what_thing - this function scans the list of possible things
   (strings) and returns a cthing.  Returns -1 if not found in the
   list. */

/* My idea here is that by return -1 on error, this won't match
   anything int he list of cthings.  That way, the function that uses
   what_thing to determine the cthing doesn't have to care if the call
   failed or not.  This helps me keep the code simple, since now I
   don't have to add a branch for failure, but which also decrements
   energy. */

int
what_thing (const char *th)
{
  int i;

  for (i = 0; i < 6; i++) {
    if (strcmp (th, things[i]) == 0) {
      return (cthings[i]);
    }
  } /* for */

  /* not found */

  return (-1);
}
