// THIS FILE HAS TO BE DISTRIBUTED WITH mravenec.txt
// IF YOU DIDN'T GET IT, CONTANT WHOM GIVE THIS PROGRAM TO YOU
// OR CHECK THE http://tangens.sinus.cz/~pasky/mravenec

// NOTE THAT THIS IS THE ANT-HIVE SIMULATOR.
// PLATFORM: DOS, LINUX/UNIX

// ON DOS CHECKED THAT IT COULD BE COMPILED UNDER DJGPP, DUNNO ABOUT OTHERS

// (c) Petr Baudis <pasky@pruvodce.cz> 2000
// MRAVENEC v1.1

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<time.h>


// Defines - you can change here the desk size, number of robots,
//	     mess quantity, refresh rate of animation and if it will be
//	     compiled for dos or no.

//#define DOS // comment this if you want to compile this under Linux.

#ifdef DOS
#  include<conio.h> // DOS's console
#else
#  include<curses.h> // Linux's console
#endif

//#define REBELS	// turn of to ultimate AI - the ants will be rebelic
			// ones and will get the mess from the 'ant-hive' also.
#define YMAX		25
#define XMAX    	80
#define ROBOTS		10
#define	ITEMS		100
#define REFRESH_RATE	50000
		      //(in microseconds - one milisecond=1000 microseconds,
		      //one second is 1000 miliseconds - default is 50000(50ms))

// Global variables - desk array, besk for DOS animation, structure of
//		      one robot's record and the robots list.

char desk[YMAX][XMAX];

#ifdef DOS
char besk[YMAX][XMAX]; // backup desk, because dos has very slow updates
#endif

typedef struct {
  signed char dirx, diry;
  unsigned char carry;
  /*unsigned*/ char posx, posy;
} t_robot;

t_robot *robots[ROBOTS];



// Functions - all the neccessary things are done here :)


//   Display one frame of the ant-hive animation.
//
// Dunno what it should get.
// Dunno what it should return.
// Uses desk and besk if neccessary.
// Doesn't call any local function.

void
paint_desk()
{
  int x, y; char disp[5]=" =@#";
  for(y=0; y<YMAX; y++) for (x=0; x<XMAX; x++) {

#ifdef DOS
    if (besk[y][x]!=desk[y][x]) { gotoxy(x+1, y+1); putch(disp[desk[y][x]]); }
    besk[y][x]=desk[y][x];

#else
    mvaddch(y, x, disp[desk[y][x]]);

#endif
  }
}


// The most neccessary, AI kernel of the program. Moves the robot.
//
// Gets record of moving robot.
// Dunno what it should return.
// Uses desk array.
// Doesn't call any local function.

void
move_robot(t_robot *robo)
{
  int x,y,s=0;
  desk[robo->posy][robo->posx]=0;
 
  do {
    if (rand()%15==8 && robo->dirx<1) robo->dirx++; else
    if (rand()%15==4 && robo->diry<1) robo->diry++; else
    if (rand()%15==6 && robo->dirx>-1) robo->dirx--; else
    if (rand()%15==9 && robo->diry>-1) robo->diry--;
  } while (desk[robo->posy+robo->diry][robo->posx+robo->dirx]>1);
  
  if (robo->posx+robo->dirx>0 && robo->posx+robo->dirx<XMAX &&
      robo->posy+robo->diry>0 && robo->posy+robo->diry<YMAX &&
      desk[robo->posy+robo->diry][robo->posx+robo->dirx]==1 && robo->carry)
  { robo->dirx=0-robo->dirx; robo->diry=0-robo->diry;
    desk[robo->posy][robo->posx]=1; robo->carry=0; }
  if (desk[robo->posy+robo->diry][robo->posx+robo->dirx]==1 && robo->carry==0)
  { desk[robo->posy+robo->diry][robo->posx+robo->dirx]=0; robo->carry++;
#ifdef REBELS // I'm not sure that everything work fine
    x=0;
    do { x++; robo->dirx*=rand()%2; robo->diry*=rand()%2; } while (desk[robo->posy+robo->diry][robo->posx+robo->dirx]>0 && x<3);
    if (x==3) {
      for ((x=-1), y=-1; y>1; (x++), x>1?(x=-1), y++:(x=x)) if (!desk[robo->posy+y][robo->posx+x])
      { robo->dirx=x; robo->diry=y; goto win; } else if (desk[robo->posy+y][robo->posx+x]==1)
      { robo->dirx=x; robo->diry=y; s=1; } else if (desk[robo->posy+y][robo->posx+x]==2 && !s)
      { robo->dirx=x; robo->diry=y; } // at least without a mess
      // at least a mess around - so drop a mess and get it there!
      if (s) { desk[robo->posy][robo->posx]=1; desk[robo->posy+robo->diry][robo->posx+robo->dirx]=0; } else
      { desk[robo->posy][robo->posx]=1; robo->carry--; }
    }
win:
#endif
  }

  robo->posx+=robo->dirx; robo->posy+=robo->diry;
  // this's an 'extreme' handling... so there's still a possibility of mess lost
  if (robo->posx<0) { robo->posx=0; robo->dirx+=rand()%2+1; } else
    if (robo->posx>XMAX-1) { robo->posx=XMAX-1; robo->dirx-=rand()%2+1; }
  if (robo->posy<0) { robo->posy=0; robo->diry+=rand()%2+1; } else
    if (robo->posy>YMAX-1) { robo->posy=YMAX-1; robo->diry-=rand()%2+1; }
  
  desk[robo->posy][robo->posx]=2+robo->carry;
}


// Pseudo functions for DOS ncurses emulation ;)

#ifdef DOS
#define initscr() clrscr();
#define refresh() ;
#define endwin() ;
#endif


// Also neccessary function, main animation and robot's moving loop.
// Use it, should be handy.
//
// Dunno what it should get.
// Returns -1 that some mysterious thing happened.
// Uses desk and robots.
// Calls paint_desk() and move_robot().

int
main()
{
  int key, watch=1, x, y; char mezi[256];
  initscr();
  refresh();
  srand(time(NULL));
 
  for (key=0; key<ITEMS; key++) { do { x=rand()%XMAX; y=rand()%YMAX; } while (desk[y][x]); desk[y][x]=1; }
   for (key=0; key<ROBOTS; key++) { do { x=rand()%XMAX; y=rand()%YMAX; } while (desk[y][x]); desk[y][x]=2;
  				   robots[key]=malloc(sizeof(t_robot)); robots[key]->dirx=rand()%3-1; robots[key]->diry=rand()%3-1;
  				   robots[key]->carry=0; robots[key]->posx=x; robots[key]->posy=y; }

  paint_desk();
  key=getch();
  while (1) {
    for (key=0; key<ROBOTS; key++) move_robot(robots[key]);
    if ((x=rand()%XMAX, y=rand()%YMAX, desk[y][x]) || rand()%150==22) { desk[y][x]=1; } // random mess placing
    paint_desk();
    refresh();
    usleep(REFRESH_RATE);
  }
  
  endwin();
  return -1;
}
