// anttest5.c 
// S-bot test with chain forming arising from mechanism that makes ant in 
// chain resist changes in its genes" stronger than an ant that's not 
// part of a chain. 
// MR Jun 2001

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>
#include <limits.h>

#include "bool.h"
#include "byte.h"
#include "int2char.h"
#include "rand.h"

#include "phmap1.h"


//----------------------------------------------------------------------------
//  Data structures for administration of s-bot states and positions
//----------------------------------------------------------------------------

typedef struct
	{

	// Programming in brain of s-bot (table of "prio" values):
	// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	double l;


	// Other state variables of s-bot:
	// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	int x, y; //position on chessboard
	int dirn;  //direction, in [0,3)

	int isb_clamp;  //ix of s-bot that is clamping to me (-1 if none)
	int isb_target; //ix of s-bot I'm clamping to (-1 if none)

	int isb_chain;  //ix of s-bot at base of chain (ix of myself if 
	                // I'm the only one in the chain)

	}
	sb_t; //S-bot


typedef struct
	{
	int isb; //Index of s-bot in Gasb[] array, or -1 if empty.
	}
	sq_t; //Chessboard square



typedef struct
	{
	//S-bot collection:
	#define SBMAX 64
	sb_t asb[SBMAX];
	int nsb;

	//Chessboard:
	#define CBXMAX 30
	#define CBYMAX 20
	sq_t aasq[CBXMAX][CBYMAX];
	int nsqx;
	int nsqy;

	//Pheromone map (used for internal calculations)
	int phmap[ CBXMAX * CBYMAX ];
	}
	w_t; //World



//----------------------------------------------------------------------------
//  Direction data structures/funcs
//  Chessboard position/bounds checking funcs
//----------------------------------------------------------------------------

const char DIRN_CHAR[4] = { '>', 'V', '<', '^' };
const int DIRN_DX[4] =    { +1,   0,  -1,   0 };
const int DIRN_DY[4] =    {  0,  +1,   0,  -1 };

char getDirnChar( int dirn ) //For printing etc.
	{
	assert( 0 <= dirn && dirn < 4 );
	return DIRN_CHAR[ dirn ];
	}

void oneforward( 
	int * px, int * py, 
	int ix, int iy, int dirn )
	{
	assert( 0 <= dirn && dirn < 4 );
	*px = ix + DIRN_DX[dirn];
	*py = iy + DIRN_DY[dirn];
	}


_bool inboard( int x, int y, int nx, int ny )
	{
	return ( 0 <= x && x < nx &&
	         0 <= y && y < ny );
	}





//----------------------------------------------------------------------------
//  Low-level aux funcs to manipulate s-bots
//----------------------------------------------------------------------------


_bool w_sqisempty( const w_t * pw,
	int ix, int iy )
	{
	assert( inboard( ix, iy, pw->nsqx, pw->nsqy ) );
	return ( pw->aasq[ix][iy].isb == -1 );
	}

_bool w_botlookfwd( const w_t * pw,
	int isb,
	int * pfx, int * pfy, 
	int * pfisb )
	// Examine the square in front of s-bot 'isb'.
	// If that square is inside the chessboard, then return 1,
	//  if not, then return 0.
	// Set (*pfx,*pfy) to the coords of the square in front
	//  of s-bot 'isb' (which may be outside the chessboard).
	// If the square is inside the chessboard and occupied by
	//  another s-bot, then set *pfsib to ix of that s-bot; set
	//  it to -1 otherwise.
	{
	const sb_t * psb;
	assert( 0 <= isb && isb < pw->nsb );
	psb = pw->asb + isb;

	oneforward( pfx, pfy, 
	   psb->x, psb->y, psb->dirn );

	if ( ! inboard( *pfx, *pfy, pw->nsqx, pw->nsqy ) ) 
		{ *pfisb = -1; return 0; }

	*pfisb = pw->aasq[*pfx][*pfy].isb; // -1 if square is empty.
	return 1;
	}


void w_pickupsbot( w_t * pw,
	int isb )
	// Remove s-bot 'isb' from the chessboard 
	// (but keep it in the asb[] array).
	{
	int ix = pw->asb[isb].x;
	int iy = pw->asb[isb].y;
	pw->aasq[ix][iy].isb = -1;
	}
void w_putinsbot( w_t * pw,
	int isb,
	int ix, int iy )
	// Put s-bot 'isb' on empty square (ix,iy).
	{ 
	assert ( w_sqisempty( pw, ix, iy ) );
	pw->aasq[ix][iy].isb = isb;
	pw->asb[isb].x = ix;
	pw->asb[isb].y = iy;
	}


#if 0
void w_chainbots( w_t * pw, 
	int isb_base,
	int iChainVal )
	// Set the fields 'isb_chain' of all s-bots in the chain
	// based at s-bot 'isb_base' to value 'iChainVal'.
	{
	int ii;
	int irep = 0;
	for ( ii = isb_base; ii != -1; ii = pw->asb[ii].isb_target )
		{
		if ( irep > 0 && ii == isb_base ) 
			{ return; } //Circular chain !

		pw->asb[ii].isb_chain = iChainVal;

		irep++;

		//if ( ++irep > 100 ) { fatal( "irep>100" ); }
		}
	}
#endif

void w_unclampbot( w_t * pw,
	int isb_actor,
	int isb_oldtarget )
	{
	pw->asb[isb_actor].isb_target = -1;
	pw->asb[isb_oldtarget].isb_clamp = -1;

	{
	int isb = isb_oldtarget;
	while ( isb != -1 && pw->asb[isb].isb_chain == isb_actor )
		{
		pw->asb[isb].isb_chain == isb_oldtarget;
		isb = pw->asb[isb].isb_target;
		}
	}
	//w_chainbots( pw, isb_oldtarget, isb_oldtarget );
	}
void w_clampbot( w_t * pw,
	int isb_actor,
	int isb_newtarget )
	{
	pw->asb[isb_actor].isb_target = isb_newtarget;
	pw->asb[isb_newtarget].isb_clamp = isb_actor;

	//w_chainbots( pw, isb_actor, pw->asb[isb_actor].isb_chain );
	}




int w_chainlen( w_t * pw, 
	int isb_base )
	// Return # of s-bots in front of s-bot 'isb-base'
	// in same chain.  (Return 0 if none.)
	{
	int irep = 0;
	int isb = isb_base;

	for(;;)
		{
		int isb_next = pw->asb[isb].isb_target;

		// End of chain
		if ( isb_next == -1 ) { return irep; }

		// Circular chain bites tail !
		if ( isb_next == isb_base ) { return irep; } 


		//Next s-bot in chain
		isb = isb_next;

		irep++;
		//if ( ++irep > 100 ) { fatal( "irep>100" ); }
		}
	}



//----------------------------------------------------------------------------
//  (De-)Initialization of w_t data structure
//----------------------------------------------------------------------------


const char * w_init( w_t * pw,
	int wx, int wy,  //chessboard size
	int nsb )        //# of s-bots	
	// Return err.msg. on failure, NULL if OK.
	{
	int isb;
	int ix, iy; 
	
	assert ( wx > 0 && wy > 0 && nsb > 0 );

	if ( wx > CBXMAX ) { return "wx > CBXMAX"; }
	if ( wy > CBYMAX ) { return "wy > CBYMAX"; }
	if ( nsb > SBMAX ) { return "nsb > SBMAX"; }


	// Clear chessboard
	for ( ix = 0; ix < CBXMAX; ix++ )
		{
		for ( iy = 0; iy < CBYMAX; iy++ )
			{
			pw->aasq[ix][iy].isb = -1;
			}
		}

	pw->nsqx = wx;
	pw->nsqy = wy;


	// Put in randomly initialized s-bots in random positions
	for ( isb = 0; isb < nsb; isb++ )
		{ 
		int itry = 0;

		//find empty but otherwise random square
		do
			{
			ix = n_rand( wx );
			iy = n_rand( wy );
			itry++;
			}
		while ( ! w_sqisempty( pw, ix, iy ) && itry < 1000 );

		if ( itry > 1000 )
			{ return "init trouble: too may s-bots on too "
			         "small chessboard"; }

		//init s-bot contents/values
		pw->asb[isb].dirn = n_rand( 4 );
		pw->asb[isb].isb_clamp = -1;
		pw->asb[isb].isb_target = -1;
		pw->asb[isb].isb_chain = isb;

		pw->asb[isb].l = f_rand( 1.0 ); 


		//put s-bot in empty square
		w_putinsbot( pw, isb, ix, iy );
		}

	pw->nsb = nsb;
	
	return NULL; //success
	}


//----------------------------------------------------------------------------
//  Stdio drawing funcs
//----------------------------------------------------------------------------


void w_fdraw( FILE * fp,
	const w_t * pw,
	int isb_hi ) // S-bot to highlight; -1 for none.
	{
	int ix, iy;
	char nextBr = 0;

	for ( iy = 0; iy < pw->nsqy; iy++ )
		{
		for ( ix = 0; ix < pw->nsqx; ix++ )
			{
			char c;
			_bool hi = 0;

			if ( w_sqisempty( pw, ix, iy ) )
				{ 
				c = '.'; 
				}
			else
				{
				int isb = pw->aasq[ix][iy].isb;
				int dirn = pw->asb[isb].dirn;
				c = getDirnChar( dirn );
				if ( isb == isb_hi ) 
					{ hi = 1; }
				}

			if ( hi ) 
				{ 
				fprintf( fp, "[%c", c ); 
				nextBr = 1;
				}
			else if ( nextBr )
				{ 
				fprintf( fp, "]%c", c ); 
				nextBr = 0;
				}
			else 
				{ 
				fprintf( fp, " %c", c ); 
				}
			}

		if ( nextBr ) 
			{ fprintf( fp, "]" ); nextBr = 0; }
		fprintf( fp, "\n" );
		}
	}



//----------------


void stamp_clear( char aas[5][3] )
	{
	int i, j;
	for ( i = 0; i < 5; i++ )
		{ for ( j = 0; j < 3; j++ ) { aas[i][j] = ' '; } }
	}
void stamp_addpheromone( char aas[5][3],
	const w_t * pw,
	int ix, int iy )
	{
	aas[1][2] = 
	   int2char( phmap_getdist( pw->phmap, 
	      pw->nsqx, pw->nsqy,
	      ix, iy ) ); 
	}
void stamp_addbot( char aas[5][3],
	const w_t * pw,
	int isb,
	char mode,
	_bool highlight )
	{
	const sb_t * psb = &( pw->asb[isb] );

	aas[2][1] = int2char( isb );

	switch ( psb->dirn )
		{
	case 0: aas[4][1] = aas[3][1] = '-'; break;
	case 1: aas[2][2] = '|'; break;
	case 2: aas[0][1] = aas[1][1] = '-'; break;
	case 3: aas[2][0] = '|'; break;
		}

	if ( mode == 'c' || mode == '*' )
		{ aas[3][2] = int2char( psb->isb_clamp ); }
	if ( mode == 'C' || mode == '*' )
		{ aas[1][0] = int2char( psb->isb_chain ); }

	//if ( psb->siP == 1 ) { aas[3][0] = '\''; }

	if ( highlight ) 
		{ aas[0][0] = aas[4][0] = aas[4][2] = aas[0][2] = '*'; }
	}
	

static char GLOB_fdrawbigmode = 'C'; 

void w_fdrawBig( FILE * fp,
	const w_t * pw,
	int isb_hi ) // S-bot to highlight; -1 for none.
	{
	int ix, iy; int line;

	for ( ix = 0; ix < pw->nsqx; ix++ )
		{ fprintf( fp, "._____" ); }
	fprintf( fp, ".\n" );

	for ( iy = 0; iy < pw->nsqy; iy++ )
		{
		for ( line = 0; line < 3; line++ )
			{
			fputc( ( line == 2 ? '.' : ' ' ), fp ); 

			for ( ix = 0; ix < pw->nsqx; ix++ )
				{
				char aas[5][3];
				int icol;

				stamp_clear( aas );

				if ( GLOB_fdrawbigmode == 'p' ||
				     GLOB_fdrawbigmode == '*' )
					{
					stamp_addpheromone( aas, pw,
					   ix, iy );
					}

				if ( ! w_sqisempty( pw, ix, iy ) )
					{ 
					int isb = pw->aasq[ix][iy].isb;

					stamp_addbot( aas, pw, isb, 
					   GLOB_fdrawbigmode, 
					   ( isb == isb_hi ) );
					}

				for ( icol = 0; icol < 5; icol++ )
					{ fputc( aas[icol][line], fp ); }

				fputc( ( line == 2 ? '.' : ' ' ), fp ); 
				}

			fputc( '\n', fp );
			}
		}
	}




//----------------------------------------------------------------------------
//  Parameter values
//----------------------------------------------------------------------------

static double GLOB_f_mixin = 0.3;  //0.5;
static double GLOB_f_mixrev = 0.0; //0.3;

static double GLOB_fNoncDegr = 0.5;

static double GLOB_f_mutate = 0.0001;

static _byte GLOB_deltav = 0x20;

static _bool GLOB_chainsupph = 1; //If 1, bots don't smell bots in
                                  // same chain as themselves.




//----------------------------------------------------------------------------
//  General-purpose funcs
//----------------------------------------------------------------------------

void fatal( const char * pmsg )
	{	
	fprintf( stderr, "anttest5 fatal: %s\n", pmsg );
	exit( -1 );
	}



//----------------------------------------------------------------------------
//  Aux funcs to interpret input smell to s-bot
//----------------------------------------------------------------------------



//
// w_neighborGetSmell():
// 
// Look at the neighbour square of square of s-bot 'isb' in the direction 
// 'dirnRel' *RELATIVE* to where the s-bot is oriented: 
// dirnRel = 0  means forward,
// dirnRel = 1  means right
// dirnRel = 2  means backward
// dirnRel = 3  means left.
//
// The neighbour square is first examined as to whether it's a "valid" 
// square; return 1 if it's valid, 0 if not. The square is not valid if 
// it's outside the chessboard. The square is not valid is it's occupied 
// by a non-smell-emitting s-bot.  The square is valid if it's empty or 
// if it's occupied by a smell-emitting s-bot.  Squares containing a 
// smell-emitting s-bot are recognized from their having the value iPh = 
// 0 in the pheromone map.  
//    This definition of a "valid square" makes that s-bots that base their 
// action upon this function walk towards smell-emitting s-bots and avoid 
// squares containing non-smell-emitting s-bots. 
//  
// If the square is "valid", then set *piPh to the pheromone level in 
// that square (and to INT_MAX if there's no pherom.emitter on the 
// chessboard) and return 1. 
//    If the square is not "valid", then set *piPh to INT_MAX and 
// return 0.
//    The value written to *piPh is always >= 0.
//
_bool w_neighborGetSmell( 
	const w_t * pw, 
	int isb,
	int dirnRel,
	int * piPh )
	{
	const sb_t * psb;
	int ix, iy;
	int iPh;
	assert( 0 <= isb && isb < pw->nsb );
	
	// Determine coords (ix,iy) of the square to examine
	psb = pw->asb + isb;
	oneforward( &ix, &iy, psb->x, psb->y, 
	   ( psb->dirn + dirnRel ) % 4 );

	// Check whether square is in board
	if ( ! inboard( ix, iy, pw->nsqx, pw->nsqy ) )
		{ *piPh = INT_MAX; return 0; } 

	// Get pheromone level
	iPh = phmap_getdist( pw->phmap, pw->nsqx, pw->nsqy, 
	   ix, iy );
	if ( iPh == -1 ) { iPh = INT_MAX; }
	assert( iPh >= 0 );

	// Exclude square occupied by non-pheromone emitting s-bot
	if ( pw->aasq[ix][iy].isb != -1 && //occupied by s-bot
	     iPh != 0 )  //not a pheromone source
		{ *piPh = INT_MAX; return 0; } 

	*piPh = iPh;
	return 1;
	}


//
// w_botTrackSmell():
//
// Look at the neighbour squares of s-bot 'isb' that are "valid", with 
// "valid" in the sense/definition of the function 'w_neighborGetSmell()'. 
//    Return 'f' if the square forward is valid and if its pherom.concentr. 
// value is the highest of all the valid neighb. squares.  I.e. fwd and 
// left valid and equal max. ph.conc. in both returns 'f'. 
// ( Note: "highest ph.conc." means that the distance to nearest 
// ph-emitter is smallest. ) 
//    Return 'L' or 'R' when the neighbour square to the left or right, 
// respectively, of the s-bot is valid and contains the max. pherom.conc. 
// value of all the valid neighbour squares.  I.e. return 'R' if only 
// right and back are valid and both have equal ph.conc. 
//    Return '?' if both the left and right square are valid and have the 
// max. and equal ph.conc. 
//    Return 'b' if the square behind is valid and has a ph.conc. greater 
// than any other valid neighbour square. 
//    Return 'n' if there's no valid neighbour square.
//
char w_botTrackSmell( 
	const w_t * pw, 
	int isb )
	{
	#define FWD 0
	#define RIGHT 1
	#define BACK 2
	#define LEFT 3
	_bool ok[4];
	int iph[4];
	int i;
	_bool oneOK = 0;
	int iph_best = INT_MAX;

	// Get input smell values
	for ( i = 0; i < 4; i++ )
		{
		ok[i] = w_neighborGetSmell( pw, isb, 
		   i, //dirnRel
		   iph + i );

		if ( ok[i] ) { oneOK = 1; }
		if ( iph[i] < iph_best ) { iph_best = iph[i]; }
		}
	
	#if 0 //DEBUG
	printf( "FRBL=%d%d%d%d/%c%c%c%c\n",
	  ok[0], ok[1], ok[2], ok[3], 
	  int2char(iph[0]), int2char(iph[1]),
	  int2char(iph[2]), int2char(iph[3]) );
	#endif

	// No input
	if ( ! oneOK ) { return 'n'; }

	// Check 'f'
	if ( ok[FWD] && iph_best == iph[FWD] ) { return 'f'; }

	// Decide L-R
	if ( ok[LEFT] && ok[RIGHT] && 
	   iph_best == iph[LEFT] && iph_best == iph[RIGHT] ) { return '?'; }
	if ( ok[LEFT] && iph_best == iph[LEFT] ) { return 'L'; }
	if ( ok[RIGHT] && iph_best == iph[RIGHT] ) { return 'R'; }

	// The one remaining possibility is 'b'
	if ( ok[BACK] && iph_best == iph[BACK] ) { return 'b'; }

	assert( 0 ); //shouldn't be possible to reach here
	}


char w_botMaxPhAction( const w_t * pw,
	int isb )
	// Get input smell, and translate to a valid motor
	// command:
	// Return the motor action command character (f,L,R) that
	// moves the s-bot nearer to the nearest ph-emitter.
	// Return 'n' if no valid neighbour square.
	{
	char c = w_botTrackSmell( pw, isb );
	if ( c == '?' || c == 'b' )
		{
		return ( n_rand(2) == 0 ? 'L' : 'R' );
		}
	else return c;
	}




//----------------------------------------------------------------------------
//  Function w_movebot()
//----------------------------------------------------------------------------


void w_movebot( w_t * pw,
	int isb,
	char action )
	// Execute the specified motor action in s-bot 'isb'.
	// If the motor action says so, execute gene swapping.
	// Don't do any mutation.
	{
	sb_t * psb;
	int fx, fy; // Coords of square in front of me.
	int fInBoard; // 1 if square in front of me is inside chessboard.
	int fisb;     // Ix of s-bot in front of me, -1 if none.
	int cisb;     // Ix of s-bot thatI'm clamping.
	// ( Terminology note: "Me" = s-bot 'isb'. )

	assert( 0 <= isb && isb < pw->nsb );
	psb = pw->asb + isb;


	//
	// If I'm not clamped by anyone, then 
	// reset my isb_chain to my own isb.
	//
	if ( psb->isb_clamp == -1 )
		{
		psb->isb_chain = isb;
		}

	//
	// If I'm clamping someone, and if the cmd is 'f','q', or 'Q',
	// then swap my "program" into him.
	//
	cisb = psb->isb_target;
	if ( ( cisb != -1 ) //I'm clamping someone
	     &&
	     ( action == 'f' || action == 'q' || action == 'Q' ) )
		{
		// 
		if ( action == 'f' || action == 'Q' )
			{
			//Overwrite target s-bot's isb_chain and "l"
			// with my own.
			pw->asb[cisb].isb_chain = pw->asb[isb].isb_chain;
			pw->asb[cisb].l = pw->asb[isb].l;
			}

		return;
		}


	//
	// Otherwise :
	//
       
	// (1) Un-clamp the s-bot I'm clamping at this moment
        cisb = psb->isb_target;
        if ( cisb != -1 )
                {
                w_unclampbot( pw, isb, cisb );

		//This resets his 'isb_chain'
		//pw->asb[cisb].isb_chain = cisb;
                }

	// (2) Examine what's in front of me.
	fInBoard = w_botlookfwd( pw, isb, &fx, &fy, &fisb );

	// (3) Translate action 'q'/'Q' to n/f/L/R
	if ( action == 'q' || action == 'Q' )
		{
		action = w_botMaxPhAction( pw, isb );
		//printf( "q/Q --> %c\n", action );
		}

	// (4) Execute movement n/f/L/R
	switch ( action )
		{
	case 'L': //Turn left
		psb->dirn = ( psb->dirn + 3 ) % 4;
	break;
	case 'R': //Turn right
		psb->dirn = ( psb->dirn + 1 ) % 4;
	break;
	
	case 'f': //Go one step forward if possible
		if ( ! fInBoard ) 
			{ break; } //dest. square not in board
		if ( fisb != -1 )
			{ break; } //dest. square not empty 
			
		//if someone is clamping me, then my movement
		// un-clamps him from me
		if ( psb->isb_clamp != -1 )
			{ 
			w_unclampbot( pw, psb->isb_clamp, isb );

			// This also makes my 'isb_chain'
			// revert to my own 'isb'.
			//psb->isb_chain = isb;
			}

		//move s-bot
		w_pickupsbot( pw, isb );
		w_putinsbot( pw, isb, fx, fy );
	break;
		}//switch

	// (5) Again examine what's in front of me (my movement may have
	//     changed this)
	fInBoard = w_botlookfwd( pw, isb, &fx, &fy, &fisb );

	// (6) Automatically (re-)clamp the s-bot in front of me, if
	//     it's not already clamped by someone else.
		#if 0
	if ( fisb != -1 && //s-bot in front of me
	     pw->asb[fisb].isb_clamp == -1 ) //he's not clamped
		// and if he is not already clamping me,
		// and if he's not in the same chain as me.
		     pw->asb[isb].isb_clamp != fisb && //I'm not clamped by him
		     pw->asb[isb].isb_chain != 
		      pw->asb[fisb].isb_chain ) //Not same chain
		#endif
	if ( fisb != -1 && //s-bot in front of me
	     pw->asb[fisb].isb_clamp == -1 && //he's not clamped
	     pw->asb[isb].isb_clamp != fisb && //I'm not clamped by him
	     pw->asb[isb].isb_chain != 
	      pw->asb[fisb].isb_chain ) //Not same chain

	  //   pw->asb[fisb].isb_chain == psb->isb_chain )
		{
		w_clampbot( pw, isb, fisb );
		}

	}




//----------------------------------------------------------------------------
//  High-level s-bot funcs
//----------------------------------------------------------------------------


// Functions iterating over all s-bot ``brains'' (= l-values)

_bool w_allBrainsSame( const w_t * pw )
	// ---> delta( l-values ) smaller than given arg "d"
	{
	int isb;
	double hjhja;
	
	for ( isb = 1; isb < pw->nsb; isb++ )
		{
		}
	return 1;
	}

void w_mutatebot( w_t * pw,
	int isb ) // Mutate the s-bot's ``brain(s)''
	{
	// l +-  f_rand( f_mutrate );
	}
void w_mutateallbots( w_t * pw )
	{
	int isb;
	for ( isb = 0; isb < pw->nsb; isb++ )
		{
		w_mutatebot( pw, isb );
		}
	}

void w_randReinitAllBots( w_t * pw )
	{
	int isb;
	for ( isb = 0; isb < pw->nsb; isb++ )
		{
		pw->asb[isb].l = f_rand( 1.0 );
		}
	}





_bool w_botEmitsSmell( const w_t * pw,
	int isb,
	int isb_center )
	// Return 1 if s-bot 'isb' emits pheromone that is
	// observable by s-bot 'isb_center'.
	{
	int isb_chainbase;
	assert( isb_center != -1 );

	isb_chainbase = pw->asb[isb_center].isb_chain;


	// Myself I don't emit smell
	if ( isb == isb_center ) { return 0; }

	// Others in same chain don't emit smell
	if ( pw->asb[isb].isb_chain == isb_chainbase ) { return 0; }

	// S-bots that are being clamped don't emit smell
	if ( pw->asb[isb].isb_clamp != -1 ) { return 0; }


	return 1;
	}


void w_updatephmap( w_t * pw,
	int isb_center )
	// Update pheromone map, exclude s-bot 'isb_center' from it.
	// (I.e. calculate ph.map as seen from s-bot 'isb_center'.
	{
	int isb;

	// Init
	phmap_init( pw->phmap, pw->nsqx, pw->nsqy );

	// Enter pheromone-emitting entities
	for ( isb = 0; isb < pw->nsb; isb++ )
		{
		if ( ! w_botEmitsSmell( pw, isb, isb_center ) )
			{ continue; }

		phmap_enteremitter( pw->phmap, 
		   pw->nsqx, pw->nsqy,
		   pw->asb[isb].x,
		   pw->asb[isb].y );
		}

	// Calculate pheromone distribution map
	phmap_calc( pw->phmap, pw->nsqx, pw->nsqy );
	}



void w_execbot( 
	w_t * pw,
	int isb,
	char cmotor )
	// Execute movement action of s-bot 'isb'.
	// If cmotor == 'A', then 
	// let s-bot 'isb' determine its own action; otherwise
	// let it execute action "cmotor". 
	// Don't do mutation.
	{
	// Get pheromone map as seen by me
	w_updatephmap( pw, isb );

	if ( cmotor == 'A' )
		{
		// Choose motor action according to value of "l"
		cmotor = 'q';
		if ( f_rand( 1.0 ) < pw->asb[isb].l ) 
			{ cmotor = 'Q'; }
		}

	// Move the bot
	w_movebot( pw, isb, cmotor );
	}


void w_execrandombot( w_t * pw,
	char cmotor,
	_bool bmut )
	//Choose a random s-bot, and execute it.
	//if bmut == 1, then mutate the selected bot after the move.
	{
	int isb;
	assert( pw->nsb != 0 );
	isb = n_rand( pw->nsb );
	w_execbot( pw, isb, cmotor );
	if ( bmut ) { w_mutatebot( pw, isb ); }
	}




//----------------------------------------------------------------------------
//  Stdio output
//----------------------------------------------------------------------------



void w_fprintsbot( 
	FILE * fp,
	const w_t * pw,
	int isb )
	{
	const sb_t * psb = &( pw->asb[isb] );

	fprintf( fp, "%02d: x=%02d y=%02d dirn='%c' "
	   "l=%4.2f "
	   "TS=%c "
	   "cl=%02d ta=%02d Ch=%02d\n",

	   isb,
	   psb->x,
	   psb->y,
	   getDirnChar( psb->dirn ),

	   psb->l,

	   w_botTrackSmell( pw, isb ),

	   psb->isb_clamp,
	   psb->isb_target,
	   psb->isb_chain
	);
	}
void w_fprint( 
	FILE * fp,
	const w_t * pw )
	{
	int isb;
	fprintf( fp, "nsb = %d\n", pw->nsb );
	for ( isb = 0; isb < pw->nsb; isb++ )
		{
		w_fprintsbot( fp, pw, isb );
		}
	}







//----------------------------------------------------------------------------
//  Main
//----------------------------------------------------------------------------


_bool options( int argc, char ** argv,
	int * pwx,
	int * pwy,
	int * pnsb,
	unsigned int * pseed,
	_bool * pbatchmode )
	{
	int i;
	for ( i = 1; i < argc; i++ )
		{
		switch ( argv[i][0] )
			{
		//Chessboard size
		case 'x':
			if ( sscanf( argv[i] + 1, "%d", pwx ) != 1 ||
			   ! ( 1 <= *pwx <= CBXMAX ) ) { return 0; }
		break;
		case 'y': 
			if ( sscanf( argv[i] + 1, "%d", pwy ) != 1 ||
			   ! ( 1 <= *pwy <= CBYMAX ) ) { return 0; }
		break;

		case 'b': //# of s-bots
			if ( sscanf( argv[i] + 1, "%d", pnsb ) != 1 ||
			   ! ( 1 <= *pnsb <= SBMAX ) ) { return 0; }
		break;
		case 'R': //Random seed
			if ( sscanf( argv[i] + 1, "%ud", pseed ) != 1 )
				{ return 0; }
		break;

		case 'n': //Silent (batch) mode
			*pbatchmode = 1;
		break;

		case 'F': //Gene mix-in factor
			if ( sscanf( argv[i] + 1, "%le", 
			   &GLOB_f_mixin ) != 1 ||
			   ! ( 0.0 <= GLOB_f_mixin && GLOB_f_mixin <= 1.0 ) )
				{ return 0; }
		break;
		case 'f': //Reverse gene mix-in factor
			if ( sscanf( argv[i] + 1, "%le", 
			   &GLOB_f_mixrev ) != 1 ||
			   ! ( 0.0 <= GLOB_f_mixrev && GLOB_f_mixrev <= 1.0 ) )
				{ return 0; }
		break;
		case 'M': //Mutation rate
			if ( sscanf( argv[i] + 1, "%le", 
			   &GLOB_f_mutate ) != 1 ||
			   ! ( 0.0 <= GLOB_f_mutate && GLOB_f_mutate <= 1.0 ) )
				{ return 0; }
		break;
		case 'V': //Mutation byte change size (deltaV)
			{
			unsigned int v;
			if ( sscanf( argv[i] + 1, "%x", &v ) != 1 ||
			   ! ( v <= 0xff ) )
				{ return 0; }
			GLOB_deltav = v;
			}
		break;
		case 'H': //Ph.emission by co-chain ants ON/OFF
		case 'h':
			GLOB_chainsupph = ( argv[i][0] == 'H' );
		break;

		default: 
			return 0;
		break;
			}
		}
	return 1;
	}

void usage( void )
	{
	fprintf( stderr, "Usage: anttest5 [options]\n" );
	fprintf( stderr, "Format of all options: letterArgument (no - in "
	                 "front and no space in between)\n" ); 
	fprintf( stderr, "Options (NN = dec.int, XX = hex int, "
		         "ZZ = float in [0,1]):\n"
		"  n     Silent (batch) mode on\n" 
		"  RNN   Random seed := NN\n"
		"  xNN   Chessboard width := NN\n"
		"  yNN   Chessboard height := NN\n"
		"  bNN   # of ants := NN\n"
		"  FZZ   f_mixin := ZZ\n"
		"  fZZ   f_mixrev := ZZ\n"
		"  MZZ   f_mutate := ZZ\n"
		"  VXX   deltaV := XX\n"
		"  H     set chainsupph ON\n" 
		"  h     set chainsupph OFF\n" 
	);
	}




void showparams( void )
	{
	printf( "f_mixin       =  %g\n", GLOB_f_mixin );
	printf( "f_mixrev      =  %g\n", GLOB_f_mixrev );
	printf( "fNoncDegr     =  %g\n", GLOB_fNoncDegr );
	printf( "f_mutate      =  %g\n", GLOB_f_mutate );
	printf( "deltaV        =  %02x (hex)\n", GLOB_deltav );
	printf( "chainsupph    =  %d\n", GLOB_chainsupph );
	}



int main( int argc, char ** argv )
	{
	w_t w;
	int wx = 7; //20;
	int wy = 7; //15;
	int nsb = 5;
	unsigned int seed = time( NULL );
	_bool batchmode = 0;
	_bool _v = 1;
	const char * perm;
	
	char cmd[80];
	char cmd_bck[80] = ""; //Previous command

	int isb_hi = -1;

	void (* drawfunc)( FILE *, const w_t *, int ) = w_fdrawBig;
	



	// Cmd line options
	if ( ! options( argc, argv, 
	   &wx, &wy, &nsb, &seed, &batchmode ) )
		{
		usage(); return -1; 
		}

	_v = ! batchmode;


	//
	// Initialize
	//

	srand( seed );

	perm = w_init( &w, wx, wy, nsb );
	if ( perm != NULL )
		{
		fprintf( stderr, "anttest5: w_init() failed: %s\n" );
		return -1;
		}

	GLOB_fdrawbigmode = 'C';



	//
	// Main loop
	//
	
	if ( ! batchmode )
		{ drawfunc( stdout, &w, isb_hi ); }


	while ( fgets( cmd, sizeof(cmd), stdin ) && cmd[0] != 'q' )
		{
		_bool draw = ! batchmode;

	L_repeat:
		switch ( cmd[0] )
			{
		case 'F': //Set f_mixin
		case 'f': //Set f_mixrev
		case 'N': //Set fNoncDegr
		case 'M': //Set mutation rate
			{
			double f;
			const char * pnm;
			if ( sscanf( cmd+1, "%le", &f ) != 1 ||
			   ! ( 0.0 <= f && f <= 1.0 ) )
				{ printf( "%c?\n", cmd[0] ); break; }

			switch ( cmd[0] ) 
				{
			case 'F': GLOB_f_mixin = f; break;
			case 'f': GLOB_f_mixrev = f; break;
			case 'N': GLOB_fNoncDegr = f; break;
			case 'M': GLOB_f_mutate = f; break;
				}
			showparams();
			draw = 0;
			}
		break;
		case 'V': //Set mutation change size (deltaV)
			{
			unsigned int v;
			if ( sscanf( cmd+1, "%x", &v ) != 1 ||
			   ! ( v <= 0xff ) )
				{ printf( "%c?\n", cmd[0] ); break; }

			GLOB_deltav = v;
			showparams();
			draw = 0;
			}
		break;
		case 'H': //Set GLOB_chainsupph
		case 'h': 
			GLOB_chainsupph = ( cmd[0] == 'H' );
			showparams();
			draw = 0;
		break;
		case '=': //Show values of all parameters
			showparams();
			draw =0;
		break;

		case 'p': //Print state of all s-bots
			w_fprint( stdout, &w );
			draw = 0;
		break;
		case 'P': //Print to file
			{
			FILE * poutfile = fopen( "out", "w" );
			if ( poutfile == NULL ) 
				{ fatal( "Failed to open 'out' file" ); }

			w_fprint( poutfile, &w );

			fclose( poutfile );
			draw = 0;
			}
		break;

		case 'd': //Draw map with positions of all bots
			drawfunc( stdout, &w, isb_hi );
			draw = 0;
		break;
		//Change drawfunc
		case 'B': 
			drawfunc = w_fdrawBig;
			GLOB_fdrawbigmode = cmd[1];
		break;
		case 'b': 
			drawfunc = w_fdraw;
		break;


		// Change highlighted (selected) s-bot
		case '>': 
			isb_hi = ( isb_hi + 1 ) % w.nsb;
		break;
		case '<':
			isb_hi = ( isb_hi + w.nsb - 1 ) % w.nsb;
		break;
		case '0':
			isb_hi = -1;
		break;


		// Command 'e', 'm', 'r', 'R', 'k':
		//Execute the specified motor action.
		//Action 'A' = let s-bot determine own action.

		case 'e': //Move the highlighted bot.
			{
			char caction;
			if ( isb_hi == -1 ) 
				{ printf( "No bot selected\n" ); continue; }

			if ( sscanf( cmd+1, "%c", &caction ) != 1 )
				{ printf( "%c?\n", cmd[0] ); break; }

			w_updatephmap( &w, isb_hi );
			w_execbot( &w, isb_hi, caction );
			}
		break;

		case 'r': 
		case 'R': //Move one random bot [and repeat n times]
			  //Cmd format: "RaNNN" / "raNNN" / "kaNNN" / ...
			  //  a = motor action letter (movement)
			  //  NNN = optional # of repeats (default 1).
			  //r: without mutation
			  //R: with mutation after the move.
		case 'k': //Keep repeating command 'r' (for max. the
		          //given # of times) until all brain contents
		          // are the same.
		case 'K': //Keep repeating command 'R' (for max. the
		          //given # of times) until all brain contents
		          // are the same.
			{
			char caction;
			int i;
			int nrep;
			_bool bmut = ( cmd[0] == 'R' || cmd[0] == 'K' );
			_bool bcheck = ( cmd[0] == 'k' || cmd[0] == 'K' );

			if ( sscanf( cmd+1, "%c%d", 
			       &caction, &nrep ) != 2 ||
			   ! ( nrep >= 1 ) )
				{ printf( "%c?\n", cmd[0] ); break; }

			for ( i = 0; i < nrep; i++ )
				{ 
				if ( bcheck && w_allBrainsSame( &w ) )
					{ break; }

				w_execrandombot( &w, caction, bmut );
				}

			if ( ! batchmode ) 
				{ printf( "%d steps executed\n", i ); }
			}
		break;


		case 'U': // mUtate all bots
			w_mutateallbots( &w );
			if ( ! batchmode ) { printf( "mUtation done\n" ); }
			draw = 0;
		break;


		case 'D': //Draw pheromone map seen from highlighted bot
			if ( isb_hi == -1 ) 
				{ printf( "No bot selected\n" ); continue; }

			w_updatephmap( &w, isb_hi );
			phmap_fprint( stdout, w.phmap, w.nsqx, w.nsqy );
			draw = 0;
		break;


		case 'I': //Init brain of each s-bot with the
		          // same constant pre-defined contents
			{
			int isb;
			for ( isb = 0; isb < w.nsb; isb++ )
				{
				// ... set "l"
				}
			}
			if ( ! batchmode ) { printf( "const Init done\n" ); }
			draw = 0;
		break;
		case 'i': //Re-init all brains with completely random
		          // contents
			w_randReinitAllBots( &w );
			if ( ! batchmode ) { printf( "rand init done\n" ); }
			draw = 0;
		break;


		case 'S': //Test whether all brains have same contents
			if ( w_allBrainsSame( &w ) )
				{ printf( "All brains same\n" ); }
			draw = 0;
		break;
		
	
		case '!': //Repeat previous command
		case '\n':
			if ( batchmode ) 
				{ 
				if ( cmd[0] == 'r' ) { printf( "rXX\n" ); }
				break; 
				}

			strcpy( cmd, cmd_bck );
			goto L_repeat;
		break;

		default:
			printf( "?\n" );
			draw = 0;
		break;
			}//switch


		if ( draw )
			{
			if ( isb_hi != -1 )
				{ w_updatephmap( &w, isb_hi ); }

			drawfunc( stdout, &w, isb_hi ); 

			if ( isb_hi != -1 )
				{ 
				w_fprintsbot( stdout, &w, isb_hi );
				}

			if ( w_allBrainsSame( &w ) )
				{ printf( "All brains same\n" ); }
			}

		
		// Save executed command
		strcpy( cmd_bck, cmd );


		}//while




	return 0;
	}

