#include <stdio.h>
#include "genlib.h"
#include "random.h"

#define MAP_SIZE_X 5
#define MAP_SIZE_Y 5
#define MAX_OBJS 100
#define NO_OF_BARRELS 12

typedef enum{
  NORMAL, IN_RINGOS_HAND, ON_THE_GROUND
}State;

/*   Enumeration to keep track of the locations.   */
typedef enum {
   LOC_GRAVE,
   LOC_DAVY,
   LOC_SINKHOLE,
   LOC_MICKY,

   LOC_BAR,
   LOC_SHOP,
   LOC_BRINE,
   LOC_CRAB,
   LOC_SUB,

   LOC_CORAL,
   LOC_ROCKS,
   LOC_OCTOPUS,
   LOC_EEL,
   LOC_WATER,

   LOC_SHIP,
   LOC_PUPPETS,
   LOC_BASE,
   LOC_PILE,

   LOC_PARTY,
   LOC_HORSES,
   LOC_COWBOYS,
   LOC_MONKEYS,
   LOC_CENTER,


} Location;

/*   Enumeration for game objects.   */
typedef enum {
   OBJ_RINGO,
   OBJ_OCTOPUS,
   OBJ_EEL,
   OBJ_CRAB,
   OBJ_DRUM_START,
} GameObj;

typedef struct{
  int x;
  int y;
  State obj_state;
  GameObj obj_type;
}ObjectInfo;
   
void View(Location map[MAP_SIZE_Y][MAP_SIZE_X], string locnames[25])
{
  int x, y; 
   
  printf("\n");
   
  for(y = 0; y < MAP_SIZE_Y; y++)
    {
      for(x = 0; x < MAP_SIZE_X; x++)
        {
          printf("%-16s", locnames[map[y][x]]);
        }
      printf("\n");
    }
}

string LocationStr(Location loc)
{
   string locstr = "";

   /*   Find out the string corresponding to the location.   */
   switch(loc)
   {
   case LOC_GRAVE:    locstr = "watery grave";  break;
   case LOC_DAVY:     locstr = "Davy Jones locker";  break;
   case LOC_SINKHOLE: locstr = "spooky sinkhole";  break;
   case LOC_MICKY:    locstr = "Micky Dolens locker";  break;

   case LOC_BAR:      locstr = "sand bar & grill";  break;
   case LOC_SHOP:     locstr = "prawn shop";  break;
   case LOC_BRINE:    locstr = "brine & spirits";  break;
   case LOC_CRAB:     locstr = "crabs cottage";  break;
   case LOC_SUB:      locstr = "yellow submarine";  break;

   case LOC_CORAL:    locstr = "coral reef";  break;
   case LOC_ROCKS:    locstr = "pointy rocks";  break;
   case LOC_OCTOPUS:  locstr = "octopus garden";  break;
   case LOC_EEL:      locstr = "eels estates";  break;

   case LOC_WATER:    locstr = "empty ocean spot";  break;
   case LOC_SHIP:     locstr = "sunken pirate ship";  break;
   case LOC_PUPPETS:  locstr = "stolen puppets";  break;
   case LOC_BASE:     locstr = "marine refinery";  break;
   case LOC_PILE:     locstr = "barnacle pile";  break;

   case LOC_PARTY:    locstr = "manatee party";  break;
   case LOC_HORSES:   locstr = "sea horses";  break;
   case LOC_COWBOYS:  locstr = "sea cowboys";  break;
   case LOC_MONKEYS:  locstr = "sea monkeys";  break;
   case LOC_CENTER:   locstr = "visitors center";  break;

   default:
      printf("No location string for %d\n", loc);
   }
   return locstr;
}

string DirectionStr(char act)
{
   string dirstr = "";

   /*   Find out the string corresponding to the direction.   */
   switch(act)
   {
   case 'n': dirstr = "north"; break;
   case 's': dirstr = "south"; break;
   case 'e': dirstr = "east"; break;
   case 'w': dirstr = "west"; break;
   }
   return dirstr;
}

string ObjectStr(GameObj object){
  string objstr ="";

  /* Find out the string corresponding to the object */
  switch(object){
  case OBJ_RINGO:      objstr="Ringo"; break;
  case OBJ_CRAB:       objstr="a crab"; break;
  case OBJ_OCTOPUS:    objstr="an octopus"; break;
  case OBJ_EEL:        objstr="an eel"; break;
  default: objstr="a drum of liquid waste"; break;
  }
  return objstr;
}

ObjectInfo Move (ObjectInfo object, char direction, Location map [5][5] )
{
   int x, y, vx, vy;
   ObjectInfo new_location;
   GameObj who = object.obj_type;

   x = object.x;
   y = object.y;

   /*   find the location next to it and return the value.   */
   switch(direction)
   {
   case 'e': vx = 1; vy = 0; break;
   case 'w': vx = -1; vy = 0; break;
   case 'n': vx = 0; vy = -1; break;
   case 's': vx = 0; vy = 1; break;
   }

   /* make sure Ringo does not leave map */
   if((x + vx < 0 || x + vx >= MAP_SIZE_X ||
       y + vy < 0 || y + vy >= MAP_SIZE_Y)&&(who == OBJ_RINGO))
   {
      printf("Nothing interesting in this direction.\n");
      printf("Let's stay here.\n");
      new_location.x = x;
      new_location.y = y;
      new_location.obj_state = NORMAL;
      new_location.obj_type = who;
      return(new_location);
   }

   /* make sure the crab does not leave map */
   if((x + vx < 0 || x + vx >= MAP_SIZE_X ||
       y + vy < 0 || y + vy >= MAP_SIZE_Y)&&(who == OBJ_CRAB))
     {
       printf("The crab tries to swim %s, out of %s, but the current draws it back.\n",
	      DirectionStr(direction), LocationStr(map[y][x]));
       new_location.x = x;
       new_location.y = y;
       new_location.obj_state = NORMAL;
       new_location.obj_type = who;
       return(new_location);
     }
   
 if (who==OBJ_RINGO){
   printf("You swim %s, out of the %s, into the %s.\n",
	  DirectionStr(direction), 
	  LocationStr(map[y][x]), 
	  LocationStr(map[y + vy][x + vx]));
 }

 if (who==OBJ_CRAB){
   printf("The crab swims %s, out of the %s, into the %s.\n",
          DirectionStr(direction),
          LocationStr(map[y][x]),
          LocationStr(map[y + vy][x + vx]));
 }

   new_location.x = x+vx;
   new_location.y = y+vy;
   new_location.obj_state = NORMAL;
   new_location.obj_type = who;
   return(new_location);
}


void Grab(ObjectInfo objects[MAX_OBJS])
{
   int i;

   /*   Check if ringo has anything.   */
   for(i = 0; i < MAX_OBJS; i++)
   {
      if(objects[i].obj_state == IN_RINGOS_HAND)
      {
         printf("You already have something in your hand.\n");
         return;
      }
   }

   /*  Find an object where ringo is.   */
   for(i = 0; i < MAX_OBJS; i++)
     {
       if(objects[i].x == objects[OBJ_RINGO].x &&
	  objects[i].y == objects[OBJ_RINGO].y && 
	  (i != OBJ_RINGO))
	 {
	   switch(i)
	     {
	     case OBJ_RINGO:
	       /*   just skip.   */
	       break;
	       
             case OBJ_OCTOPUS:
               printf("You grab the octopus with your mighty European ");
               printf("arms.\n");
               break;

	     default:
	       /*   anything but Ringo   */
	       printf("You grab %s.\n",ObjectStr(i));
	       break;
	     }
	   objects[i].obj_state = IN_RINGOS_HAND;
	   return;
	 }
     }
   
   printf("Nothing here.\n");
}


void Throw(ObjectInfo objects[MAX_OBJS])
{
   int i;
   bool found;

   /*   Find the one in ringo's hand.   */
   found = FALSE;
   for(i = 0; i < MAX_OBJS; i++)
   {
      if(objects[i].obj_state == IN_RINGOS_HAND)
	{
	  switch(i)
	    {
	    case OBJ_RINGO:
	      /*   shouldn't happen.   */
	      printf("Ringo holding Ringo???\n");
	      exit(1);
	      break;
	      
            case OBJ_OCTOPUS:
	      printf("You throw the octopus onto the ground.  It is ");
	      printf("momentarily dazed.\n");
              break;

	    default:
	      /*   anything but Ringo   */
	      printf("You throw %s onto the ground.\n",ObjectStr(i));
	      break;
	    }
	  objects[i].x = objects[OBJ_RINGO].x;
	  objects[i].y = objects[OBJ_RINGO].y;
          if (i == OBJ_OCTOPUS) {
	    objects[i].obj_state = ON_THE_GROUND;
          } else {
	    objects[i].obj_state = NORMAL;
          }
	  found = TRUE;
	  printf("There is %s here.\n",ObjectStr(i));
	  break;
	}
   }
   if(!found)
     {
       printf("You try to throw something, but you have nothing ");
       printf("in your hands.\n");
     }
}


void main()
{
   char act;
   int i,d;
   ObjectInfo lastOctopusLoc;
   string locNames[25];

   /*   These keep track of locations of oil waste drums.   */
   ObjectInfo objs[MAX_OBJS];

   /*   Initialize the map.  LOC_WATER appears twice */
   Location map [MAP_SIZE_Y] [MAP_SIZE_X] =
   {
     { LOC_GRAVE,  LOC_WATER,   LOC_DAVY,    LOC_SINKHOLE, LOC_MICKY },
     { LOC_BAR,    LOC_SHOP,   LOC_BRINE,   LOC_CRAB,     LOC_SUB },
     { LOC_CORAL,  LOC_ROCKS,  LOC_OCTOPUS, LOC_EEL,      LOC_WATER },
     { LOC_WATER,  LOC_SHIP,   LOC_PUPPETS, LOC_BASE,     LOC_PILE },
     { LOC_PARTY,  LOC_HORSES, LOC_COWBOYS, LOC_MONKEYS,  LOC_CENTER }
   };

   locNames[(int)LOC_GRAVE]    = "watery grave";	
   locNames[(int)LOC_DAVY]     = "DJ's locker";
   locNames[(int)LOC_SINKHOLE] = "sinkhole";
   locNames[(int)LOC_MICKY]    = "MD's locker";
   
   locNames[(int)LOC_BAR]      = "sand bar/grill";
   locNames[(int)LOC_SHOP]     = "prawn shop";
   locNames[(int)LOC_BRINE]    = "brine/spirits";
   locNames[(int)LOC_CRAB]     = "crab";
   locNames[(int)LOC_SUB]      = "yellow sub";
   
   locNames[(int)LOC_CORAL]    = "coral reef";
   locNames[(int)LOC_ROCKS]    = "pointy rocks";
   locNames[(int)LOC_OCTOPUS]  = "octopus";
   locNames[(int)LOC_EEL]      = "eels";
   
   locNames[(int)LOC_WATER]    = "water";
   locNames[(int)LOC_SHIP]     = "pirate ship";
   locNames[(int)LOC_PUPPETS]  = "puppets";
   locNames[(int)LOC_BASE]     = "refinery";
   locNames[(int)LOC_PILE]     = "barnacles";
     
   locNames[(int)LOC_PARTY]    = "manatee party";
   locNames[(int)LOC_HORSES]   = "sea horses";
   locNames[(int)LOC_COWBOYS]  = "sea cowboys";
   locNames[(int)LOC_MONKEYS]  = "sea monkeys";
   locNames[(int)LOC_CENTER]   = "visitor ctr"; 

   /*   Initialize the location of game objects.   */
   objs[OBJ_RINGO].x = 2;
   objs[OBJ_RINGO].y = 2;
   objs[OBJ_RINGO].obj_type = OBJ_RINGO;
   objs[OBJ_RINGO].obj_state = NORMAL;
   objs[OBJ_OCTOPUS].x = 2;
   objs[OBJ_OCTOPUS].y = 2;
   objs[OBJ_OCTOPUS].obj_state = NORMAL;
   objs[OBJ_OCTOPUS].obj_type = OBJ_OCTOPUS;
   objs[OBJ_EEL].x = 3;
   objs[OBJ_EEL].y = 2;
   objs[OBJ_EEL].obj_type = OBJ_EEL;
   objs[OBJ_EEL].obj_state = NORMAL;
   objs[OBJ_CRAB].x = 3;
   objs[OBJ_CRAB].y = 1;
   objs[OBJ_CRAB].obj_state = NORMAL;
   objs[OBJ_CRAB].obj_type = OBJ_CRAB;
   for(i = OBJ_DRUM_START; i <= NO_OF_BARRELS+OBJ_DRUM_START; i++)
   {
      objs[i].x = 3;
      objs[i].y = 3;
      objs[i].obj_state = NORMAL;
      objs[i].obj_type = i;
   }

   /*Initialize all other non-existing objects to be outside the map*/
   for(i = OBJ_DRUM_START+NO_OF_BARRELS+1; i < MAX_OBJS; i++)
     {
       objs[i].x = -1;
       objs[i].y = -1;
       objs[i].obj_state = NORMAL;
     }

   Randomize();

   /*   Introductory text.   */
   printf("You're in an octopus' garden, just like in your ");
   printf("song, except the octopus is\nthrowing things at ");
   printf("you and frightening you.\n");

   while(1)
   {
      /*   Receive input from the user.   */
      printf("Your action: ");
      act = getchar();
      getchar();

      /*   Check for the quit command as a special case.   */
      if(act == 'q')
      {
         printf("You quit, bored of undersea life already.\n");
         break;
      }

      /*   Branch out depending on the user's input.   */
      lastOctopusLoc = objs[OBJ_OCTOPUS];
      switch(act)
      {
      case 'n':
      case 's':
      case 'e':
      case 'w':
         /*   Let's take care of all the cases for swiming in one shot.   */
         if(objs[OBJ_RINGO].x == objs[OBJ_OCTOPUS].x &&
	    objs[OBJ_RINGO].y == objs[OBJ_OCTOPUS].y && 
            objs[OBJ_OCTOPUS].obj_state != ON_THE_GROUND)
         {
           printf("You try to swim %s, ", DirectionStr(act));
           printf("but the octopus gets in your way.\n");
         }
         else
         {
	   objs[OBJ_RINGO] = Move(objs[OBJ_RINGO], act, map);           
	 }
         break;
	 
      case 't':
	/*   Throw.   */
	Throw(objs);
	break;
	
      case 'g':
         /*   Grab.   */
         Grab(objs);
         break;

      case 'y':
         /*   Yell.   */
         if(objs[OBJ_RINGO].x == objs[OBJ_OCTOPUS].x &&
	    objs[OBJ_RINGO].y == objs[OBJ_OCTOPUS].y
	    )
         {
             printf("You yell at the octopus, \"go watch TV!\"\n");
         }
         else
         {
             printf("You yell, \"I hate the ocean!\"\n");
             printf("Nothing happens.\n");
         }
         break;

      case 'h':
         /*   Hide.   */
         printf("You try to hide, but there is nothing to hide ");
         printf("behind.\n");
         break;

      case 'v':
         /*   View map.   */
         View(map,locNames);
         break; 

      default:
         /*   Error case.  Just let the user know.   */
         printf("Unknown action, type n, s, e, w, y, h, g, t, v, or q.\n");
         break;
         }

      d=RandomInteger(1,4);
      if (objs[OBJ_CRAB].obj_state != IN_RINGOS_HAND){
	switch (d){
	case 1: 
	  objs[OBJ_CRAB]= Move(objs[OBJ_CRAB], 'n', map);
	  break;
	case 2:
	  objs[OBJ_CRAB]= Move(objs[OBJ_CRAB], 'w', map);
          break; 
	case 3:
	  objs[OBJ_CRAB]= Move(objs[OBJ_CRAB], 's', map);
          break;
	default:
	  objs[OBJ_CRAB]= Move(objs[OBJ_CRAB], 'e', map);
          break;
	}
      }

      /*   Set the situation variables to new values.   */
      if(lastOctopusLoc.obj_state == IN_RINGOS_HAND &&
         objs[OBJ_OCTOPUS].obj_state != ON_THE_GROUND)
      {
         printf("The octopus wriggles free of your grasp.\n");
         objs[OBJ_OCTOPUS].x = objs[OBJ_RINGO].x;
	 objs[OBJ_OCTOPUS].y = objs[OBJ_RINGO].y;
	 objs[OBJ_OCTOPUS].obj_state = NORMAL;
      }
      if(lastOctopusLoc.obj_state == ON_THE_GROUND &&
         objs[OBJ_RINGO].x == objs[OBJ_OCTOPUS].x &&
	 objs[OBJ_RINGO].y == objs[OBJ_OCTOPUS].y &&
	 objs[OBJ_OCTOPUS].obj_state != IN_RINGOS_HAND
	)
	{
	  printf("The octopus recovers from its fall.\n");
	  objs[OBJ_OCTOPUS].x = objs[OBJ_RINGO].x;
	  objs[OBJ_OCTOPUS].y = objs[OBJ_RINGO].y;
	  objs[OBJ_OCTOPUS].obj_state = NORMAL;
	}
   
   /*   Check if Ringo is still being harrased.   */
   if(objs[OBJ_RINGO].x == objs[OBJ_OCTOPUS].x &&
      objs[OBJ_RINGO].y == objs[OBJ_OCTOPUS].y &&
      objs[OBJ_OCTOPUS].obj_state == NORMAL)
     {
       printf("The octopus keeps harassing you.\n");
     }
   }
}



















































