/*	Chaotical Ant - Walk						*/
/*	Text / C version 0.4						*/
/*	(C) Eero Tamminen 1994						*/
/*									*/
/* Compile with: gcc -O -o ant anti.c -ltermcap				*/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <osbind.h>
#include <memory.h>
#include <macros.h>
#include <termcap.h>

#ifndef	TRUE
#define	FALSE	0
#define	TRUE	!FALSE
#endif

#define MAXLEN	sizeof(int) * 8		/* longer int, more memory ;-)	*/

/* -------------------------------------------------------------------- */
/*			globals and defaults				*/

/* termcap variables 							*/
static char *_tcpent, *_tcpbuf, *CL, *CM;
static int ROWS, COLUMNS;

/* Ant variables							*/
char raster[MAXLEN + 1] = ".#X*%+-:";	/* move 'color'			*/
long seed = 0x2;			/* move sequence bin. string	*/
int lenght = 2;				/* lenght of seq. (2-MAXLEN)	*/

/* -------------------------------------------------------------------- */

void GetEnv ();					/* get terminal stats	*/
int Outc ();
void ClrScrn ();
void GotoXY ();
void End ();
int Parse ();					/* parser command line	*/
void Help ();
void Walk ();					/* Do 'The Ant Walk'	*/

/* -------------------------------------------------------------------- */
/*				main					*/

int
main (argc, argv)
     int argc;
     char *argv[];
{
  GetEnv ();				/* get terminal stats		*/
  if (Parse (argc, argv))		/* get command line args	*/
    Walk ();				/* walk Ant			*/
  End ();
  return (0);
}

void
GetEnv ()
{
  char *tmp;

  if ((tmp = getenv ("TERM")) != NULL)
    {						/* use termcap		*/
      _tcpent = (char *) malloc (2048);		/* must be enough !	*/
      tgetent (_tcpent, tmp);
      _tcpbuf = _tcpent + 1024;

      CL = tgetstr ("cl", &_tcpbuf);		/* what the heck?	*/
      CM = tgetstr ("cm", &_tcpbuf);

      if ((tmp = getenv ("ROWS")) == NULL)
	ROWS = tgetnum ("li");
      else
	ROWS = atoi (tmp);

      if ((tmp = getenv ("COLUMNS")) == NULL)
	COLUMNS = tgetnum ("co");
      else
	COLUMNS = atoi (tmp);
    }
  else
    /* set acceptable defaults : VT52 code */
    {
      CL = "\33H\33J";				/* cls			*/
      CM = "\33Y%+ %+ ";			/* cursor movement	*/
      COLUMNS = 80;				/* screen size		*/
      ROWS = 25;
    }
}

int
Outc (c)
     int c;
{
  return putchar (c);
}

void
ClrScrn ()
{
  tputs (CL, 1, Outc);
}

void
GotoXY (x, y)
     int x, y;
{
  tputs (tgoto (CM, x, y), 1, Outc);
}

void
End ()
{
  free (_tcpent);
}

int
Parse (argc, argv)
     int argc;			/* number of args in command-line	*/
     char *argv[];		/* the arguments themselves		*/
{
  /* change global variables seed, lenght				*/
  int i = 1, idx, a;
  char mark;

  while (argc > i)
    {						/* check all arguments	*/
      mark = toupper (argv[i][0]);		/* first character...	*/
      switch (mark)
	{
	case '\"':				/* get 'color' string	*/
	  a = 0;
	  while (argv[i][0] >= ' ' && ++a < MAXLEN)
	      	raster[a - 1] = argv[i][a];
	  break;
	case 'R':				/* new seed		*/
	case 'L':
	  seed = idx = 0;
	  do
	    {
	      seed = (seed << 1);
	      if (mark == 'R')
		seed |= 1;
	      idx++;
	      mark = toupper (argv[i][idx]);
	    }
	  while (idx < MAXLEN && (mark == 'R' || mark == 'L'));
	  lenght = idx;
	  break;
	case 'X':				/* screen width		*/
	  COLUMNS = max (8, atoi (&argv[i][1]) & 0xFF);
	  break;
	case 'Y':				/* screen height	*/
	  ROWS = max (8, atoi (&argv[i][1]) & 0xFF);
	  break;
	default:				/* unrecogniced option	*/
	  Help ();
	  return (FALSE);
	}
      i++;
    }
  return (TRUE);
}

void
Help ()
{
  puts ("Chaotical Ant - Walk v 0.4 (C) 1994 Eero Tamminen.");
  puts ("");
  puts ("An example : anti RLLLLR x40 y20 \"Trails\"");
  puts ("  RLLLL    : Ant's movement sequence (R - right, L - left).");
  puts ("  x40      : Set screen width to 40 characters.");
  puts ("  y20      : Set screen height to 20 characters.");
  puts ("  \"Trails\" : characters used to mark the Ant's trail.");
  puts ("             (remember to quote needed chars if run from a shell)");
  puts ("If there's no termcap, VT-52 and screen size 80x25 are used.");
}

/* -------------------------------------------------------------------- */
/*			'Do the Ant Walk'				*/

void
Walk ()
{
  int **grid;				       /* 2-dim. array		*/
  int antx, anty, prev, value, heading = 0;
  long move = 0;			       /* movement counter	*/

  ClrScrn ();				       /* clear screen		*/

  /* create and initialize the grid array				*/
  grid = (int **) malloc (sizeof (int *) * (COLUMNS - 1));
  for (antx = 0; antx < COLUMNS; antx++)
    {
      grid[antx] = (int *) malloc (sizeof (int) * (ROWS - 1));
      for (anty = 0; anty < ROWS; anty++)
	grid[antx][anty] = 0;
    }

  antx = COLUMNS >> 1;				/* initial position	*/
  anty = ROWS >> 1;				/* at center		*/

  while (Cconis ())				/* empty key buffer	*/
	Cnecin ();

  do
    {
      prev = grid[antx][anty];			/* previous grid value	*/
      value = (prev + 1) % lenght;		/* new grid value	*/
      grid[antx][anty] = value;			/* store		*/

      /* display at [antx, anty] (but prevent scrolling)		*/
      if (!(antx + 1 == COLUMNS && anty + 1 == ROWS))
	{
	  GotoXY (antx, anty);
	  Outc (raster[value]);
	}
      if (seed & (1 << prev))
	heading++;
      else					/* which turning ?	*/
	heading--;

      if (heading < 0)				/* new heading		*/
	heading = 3;
      if (heading > 3)
	heading = 0;

      switch (heading)
	{			/* new place	*/
	case 0:
	  anty++;		/* down		*/
	  break;
	case 1:
	  antx--;		/* left		*/
	  break;
	case 2:
	  anty--;		/* up		*/
	  break;
	case 3:
	  antx++;		/* right	*/
	  break;
	}

      if (antx >= COLUMNS)	/* check boundarys	*/
	antx = 0;
      if (anty >= ROWS)
	anty = 0;
      if (antx < 0)
	antx = COLUMNS - 1;
      if (anty < 0)
	anty = ROWS - 1;

      move++;

    }
  while (!Cconis ());				/* until a key is hit	*/

  for (antx = 0; antx < COLUMNS; antx++)	/* free array alloc.	*/
    free (grid[antx]);
  free (grid);

  GotoXY (0, ROWS);				/* to the last line	*/
}
