
#include "Sockets.h"
#include "List.h"
#include "FastAlloc.h"
#include "Timers.h"

#define MaxPackages (10000)

	typedef struct PlayerStruct *Player, PlayerS;

	struct PlayerStruct {
		int id;
		int x, y;		// Current location.
		List packages;	// Packages this player currently holds.
		int score;		// Estimate (unless it's us)
		int weight;		// How much carrying? (estimate unelss it's us)
		int lastAlive;	// Last turn alive.
		int dead;		// Player id dead.
	};

	typedef struct PackageStruct *Package, *PackageS;

	struct PackageStruct {
		int id;
		int x, y;	// Current location (only valid if on floor)
		int xd, yd;	// Destination
		//int dist;	// dist from x, y to xd, yd (only if on floor); -1 if N/A
		int weight;
		int state;	// 0 = on floor, 1 = carried, 2 = delivered (or deleted)
		Player carriedBy;	// Often NULL
	};

	typedef struct GradientStruct *Gradient, GradientS;

	struct GradientStruct {
		int x, y;			// Center of the gradient.
		int **dist;			// dist[y][x] = distance from center.
		int lastAccess;		// Turn last accessed.
		int maxDist;		// Max dist[][]  :)
		int hydrophobic;	// True if this gradient was biased against water.
	};


static pointer freePks = NULL;
#define PackageAllocate() FastAllocate(PackageStruct, freePks, 100)
#define PackageFree(pk) FastFree(pk, freePks)
static void PackageFreeF(pointer pkp) { PackageFree((Package)pkp); }

	static List players   = NULL;
	static List packages  = NULL;
	static List gradients = NULL;

	static Player me = NULL;	// Initialized fairly early.
	static File in, out;
	static int width, height;	// Board size.
	static cstring *board;		// The raw board, indexed [y][x].
	//static cstring tokens = ".@~#";	// Valid tokens in the board...
	static int debug;	// In debug mode?
	static int waitUser;
	static int myId, maxWeight, startingMoney;
	static int turn = 0;
	static int hydrophobicTime = 0;	// We become hydrophobic when encountering others;
									//  but with time it counts down and we forget..

	static int target[2] = { -1, -1 };	// Where we want to go right now, or -1 if N/A

	static cstring coms[] = { "N", "E", "S", "W", "P", "D", "X", "Y", NULL };
	static int deltas[4][2] = { {0,1}, {1,0}, {0,-1}, {-1,0} };


	// Stats
	static int gradientsComputed = 0;	// Just for efficiency tracking.
	static int packageCacheTries = 0;	// Likewise.
	static int packageCacheMisses= 0;	// Deeto.
	static int attacks           = 0;


#define offBoard(x, y) ((x) < 0 || (x) >= width || (y) < 0 || (y) >= height)


static
void showGradient(Gradient g)
{
	int x, y, yy;

	forn(yy, height) {
		y = height-1-yy;
		forn(x, width) {
			int d;
			d = g->dist[y][x];
			if (d < 0)
				putchar('.');
			else
				putchar('0' + d * 10 / g->maxDist);
		}
		putchar('\n');
	}
}

static
void computeGradient(Gradient g, int hydrophobic)
{
	// Theoretical max might be only 4000, but no time to figure it out.
	static short leadingEdge[10000][2][2];	// [item][alternate][x,y]
	int num, dist, x, y, toggle;

	forn(y, height)
		forn(x, width)
			g->dist[y][x] = -1;

	num    = 1;
	toggle = 0;
	dist   = 0;
	leadingEdge[0][toggle][0] = g->x;
	leadingEdge[0][toggle][1] = g->y;
	g->dist[g->y][g->x] = dist;

	while (num>0) {
		int oldNum, oldToggle;
		int old;

		oldNum    = num;
		oldToggle = toggle;

		num     = 0;
		toggle ^= 1;
		dist++;

		forn(old, oldNum) {// foreach old leading element
			int d, c;
			int xo, yo;
			xo = leadingEdge[old][oldToggle][0];
			yo = leadingEdge[old][oldToggle][1];

			if (hydrophobic)
				dist = g->dist[yo][xo] + 1;

			for4(d) {	// For each direction it might go..
				x = xo + deltas[d][0];
				y = yo + deltas[d][1];
				if (offBoard(x, y))
					continue;

				c = board[y][x];
				if (c == '~' || c == '#')
					continue;

				if (hydrophobic) {
					int hdist;

					if (g->dist[y][x] >= 0 && g->dist[y][x] <= dist)
						continue;

					hdist = dist;

					if (x > 0 && x < width-1 && y > 0 && y < height-1) {
						int cn, cs, ce, cw;
						cn = board[y+1][x];
						cs = board[y-1][x];
						ce = board[y][x+1];
						cw = board[y][x-1];
						if ((cn == '~' && (cs == '.' || cs == '@')) ||
							(cs == '~' && (cn == '.' || cn == '@')))
							hdist+=2;
						if ((ce == '~' && (cw == '.' || cw == '@')) ||
							(cw == '~' && (ce == '.' || ce == '@')))
							hdist+=2;
					}
					g->dist[y][x] = hdist;
				} else {
					if (g->dist[y][x] >= 0)
						continue;
					g->dist[y][x] = dist;
				}

				leadingEdge[num][toggle][0] = x;
				leadingEdge[num][toggle][1] = y;
				num++;
			}
		}
	}

	// Hmm... maxDist may be off a little in the hydrophobic case...  No matter, just for debugging...
	g->maxDist     = dist;	// Should be dist-1, but maxDist=0 is a hazard rather not have to worry about.

	g->hydrophobic = hydrophobic;

	gradientsComputed++;
}

static
Gradient findGradient(int x, int y, int hydrophobic)
{
	ListIndex li;
	Gradient g;

	if (hydrophobic)
		hydrophobic = 1;

	if (!gradients)
		gradients = ListCreate();

	ListFor(gradients, li, g, Gradient)
		if (g->x == x && g->y == y && g->hydrophobic == hydrophobic) {
			g->lastAccess = turn;
			if (debug > 2)
				printf("Returning cached gradient for (%d,%d;%c)\n", x, y, hydrophobic?'H':'-');
			return g;
		}

	if (ListSize(gradients) > 9) {
		Gradient oldest;
		oldest = NULL;
		ListFor(gradients, li, g, Gradient)
			if (!oldest || g->lastAccess < oldest->lastAccess)
				oldest = g;
		if (oldest->lastAccess < turn) { // Don't throw away anything we might still be using..
			//ListRemoveEntryByValue(gradients, oldest);
			g = oldest;	// Recycle!
		}
	}
	if (!g) {	// Only allocate new if not recycling
		int y;
		if (debug)
			printf("Allocating NEW Gradient object.\n");
		g = stalloc(GradientStruct);
		g->dist = zalloc(int *, height);
		forn(y, height)
			g->dist[y] = zalloc(int, width);
		ListAppendEntry(gradients, g);
	}
	g->lastAccess = turn;
	g->x = x;
	g->y = y;

	computeGradient(g, hydrophobic);

	if (debug>4) {
		printf("Gradient to %d,%d:\n", x, y);
		showGradient(g);
	}

	return g;
}


// Creates if not found.
static
Player findPlayer(int id)
{
	ListIndex li;
	Player p;

	if (!players)
		players = ListCreate();

	ListFor(players, li, p, Player)
		if (!p->dead && p->id == id)
			return p;

	p = stalloc(PlayerStruct);

	p->id = id;
	p->x  = 0;
	p->y  = 0;
	p->packages  = NULL;
	p->lastAlive = turn;
	p->dead = 0;

	ListAppendEntry(players, p);

	return p;
}

// This really needs a hash table for efficiency.
// Update: Ok, let's add one!
static
Package findPackage(int id)
{
	// A hashed cache; misses fall through to the old mechanism!
	static Package hashCache[MaxPackages];
	static int initted = 0;

	Package pk;
	ListIndex li;

	// Hash cache begin
	if (!initted) {
		int i;
		forn(i, MaxPackages)
			hashCache[i] = NULL;
		initted = 1;
	}
	packageCacheTries++;
	if ((pk = hashCache[id%MaxPackages]) && pk->id == id)
		return pk;
	packageCacheMisses++;
	// Hash cache end

	if (!packages)
		packages = ListCreate();

	ListFor(packages, li, pk, Package)
		if (pk->id == id)
			return pk;

	pk = PackageAllocate();

	pk->id     = id;
	pk->x      = -1;
	pk->y      = -1;
	pk->xd     = -1;
	pk->yd     = -1;
	pk->state  = -1;
	pk->weight =  1;	// Until known.
	//pk->dist   = -1;

	pk->carriedBy = NULL;

	ListAppendEntry(packages, pk);

	// Hash cache begin
	hashCache[id%MaxPackages] = pk;
	// Hash cache end

	return pk;
}

static
void showBoard()
{
	int yy, y;

	forn(yy, height) {
	//	printf("%s\n", board[height-1-y]);
		int x;

		y = height-1-yy;
		forn(x, width) {
			ListIndex li;
			Player p;
//			putchar(board[y][x]);
			if (players) {
				ListFor(players, li, p, Player) {
					if (!p->dead && p->x == x && p->y == y) {

						if (p == me) {
							if (hydrophobicTime)
								printf("H");
							else
								printf("M");
						} else
							printf("%d", p->id%10);

						goto continue2;	// Can't show multiple players on one spot.
					}
				}
			}
//			putchar(' ');
			putchar(board[y][x]);
continue2:;
		}
		putchar('\n');
	}
	putchar('\n');
}

static
void showPlayer(Player p)
{
	printf("[%2d] At:%2d,%2d #Pks:%d/%d Weight:~%d/%d Score:~%d %s\n",
			p->id,
			p->x, p->y,
			p->packages?ListSize(p->packages):0, packages?ListSize(packages):0,
			p->weight, maxWeight,
			p->score,
			p->dead?"DEAD!":(p==me?"ME":""));
}

static
void showPlayers()
{
	ListIndex li;
	Player p;

	ListFor(players, li, p, Player)
		showPlayer(p);
}


static
void putStringVa(int cr, cstring fmt, va_list arg)
{
	vfprintf(out, fmt, arg);
	if (cr)
		fputc('\n', out);
	fflush(out);
	if (debug > 3) {
		printf("SEND[");
		vfprintf(stdout, fmt, arg);
		printf("%s]\n", cr?"<cr>":"");
	}

}

void putLine(cstring fmt, ...)
{
	va_list arg;

	va_start(arg, fmt);
	putStringVa(1, fmt, arg);
	va_end(arg);
}

void putString(cstring fmt, ...)
{
	va_list arg;

	va_start(arg, fmt);
	putStringVa(0, fmt, arg);
	va_end(arg);
}


// Return value only valid until next call.
// Returns one NULL at end of line!
static int     ungotten = 0;	// we're on the clock.
static cstring lastgot  = NULL;	// Yeah, that's the excuse.
static
cstring getWord()
{
	static char inbuf[64];
	int i, c;

	if (ungotten) {
		ungotten = 0;
		return lastgot;
	}

	i = 0;

	while ((c = getc(in)) != EOF && c != '\n' && isspace(c))
		;

	if (c == EOF) {
		printf("Server closed our connection!\n");
		exit(0);
	}

	if (c == '\n') {
		if (debug>3)
			printf("READ[<cr>]\n");
		return NULL;
	}

	inbuf[i++] = c;

	while ((c = getc(in)) != EOF && c != '\n' && !isspace(c))
		inbuf[i++] = c;

	if (c == EOF) {
		printf("Server closed our connection!\n");
		exit(0);
	}

	if (c == '\n')	// Need this later, but don't need whitespace.
		ungetc(c, in);

	inbuf[i] = '\0';

	if (debug>3)
		printf("READ[%s]\n", inbuf);

	return inbuf;
}

// Blame it on speed hacking.
ungetWord(cstring w)
{
	ungotten = 1;
	lastgot  = w;
}


// Return value only valid until next call.
static
cstring getLine()
{
	static char inbuf[2048];
	int len;

	if (!fgets(inbuf, 2048, in)) {
		printf("Server closed our connection!\n");
		exit(0);
	}

	len = strlen(inbuf);

	if (len > 0 && inbuf[len-1] == '\n')
		inbuf[len-1] = '\0';

	if (debug > 3)
		printf("READLINE[%s]\n", inbuf);

	return inbuf;
}

// This bails on error, but dumps what the Server has to say...
static
void giveUp(void)
{
	int i;
	cstring s;

	printf("\nUpon giving up, server says:\n");
	forn(i, 200)	// Show at most 200 more syms.
		if ((s=getWord()))
			printf(" %s", s);
		else
			printf("\n");

	exit(0);
}

static
void getBoard(void)
{
	cstring s;
	int y;

	s = getLine();

	if (sscanf(s, "%d %d", &width, &height) != 2) {
		printf("Couldn't parse board size from '%s'. Giving up.\n", s);
		giveUp();
	}

	board = zalloc(cstring, height);
	forn(y, height) {
		s = getLine();
		if (strlen(s) < width) {
			printf("Row %d of board is short. Giving up.\n", y+1);
			giveUp();
		}
		board[y] = crbncpy(s);
	}

	if (debug) {
		printf("Board is %dx%d:\n", width, height);
		showBoard();
		printf("\n");
	}
}

static
void getConfig(void)
{
	cstring s;

	s = getLine();

	if (sscanf(s, "%d %d %d", &myId, &maxWeight, &startingMoney) != 3) {
		printf("Couldn't parse config from '%s'. Giving up.\n", s);
		giveUp();
	}

	if (debug) {
		printf("     My id: %d\n", myId);
		printf("Max Weight: %d\n", maxWeight);
		printf("Starting $: %d\n", startingMoney);
		printf("\n");
	}

	me = findPlayer(myId);
}


static
int getOneUpdate(Player p)
{
	cstring s;
	int what;

	if (!(s = getWord()) || s[0] == '#') {
		ungetWord(s);
		return 0;
	}

	what = lookup(s, coms);

	if (what < 0 || what > 7) {
		printf("Server claims player %d did '%s' ???.\n", p->id);
		return 1;	// Probably should give up, but can't hurt to keep trying.
	}

	if (what < 4) {// move
		p->x += deltas[what][0];
		p->y += deltas[what][1];
		return 1;
	}

	if (what == 6 || what == 7) {// X or Y
		if (!(s = getWord())) {
			printf("Server didn't send coordinate after X or Y\n");
			return 1;	// Probably should give up...
		}
		if (what == 6)
			p->x = atoi(s) - 1;
		else
			p->y = atoi(s) - 1;

		return 1;
	}

	// Pickup or drop (what == 4 or 5)

	if (!(s = getWord())) {
		printf("Server didn't send package id after P or D\n");
		return 1;	// Probably should give up...
	}

	// If we picked up or dropped anything, forget our
	//  current target location since we should recompute
	//  it now.
	if (p == me)
		target[0] = -1;

	if (what == 4) {// Pickup

		Package pk;

		if (!p->packages)
			p->packages = ListCreate();

		pk = findPackage(atoi(s));

		// If someone's already carrying it, they must have dropped it...
		if (pk->carriedBy) {
			// Presumably we'll get a Drop for this in a moment, but
			//  that's too confusing so we'll just manage it here..
			pk->carriedBy->weight -= pk->weight;
			ListRemoveEntryByValue(pk->carriedBy->packages, pk);
		}

		pk->carriedBy = p;
		pk->state     = 1;	// Carry (yeah, redundant).

		ListAppendEntry(p->packages, pk);
		p->weight += pk->weight;

		if (debug > 2 && p == me)
			printf("Server says I picked up package %d.\n", pk->id);

	} else {	// Drop

		Package pk;

		pk = findPackage(atoi(s));

		if (debug > 2 && p == me)
			printf("Server says I dropped package %d.\n", pk->id);

		if (pk->carriedBy && pk->carriedBy != p) {
			/*
			 * This *should* only happen if someone picked up a
			 *   package that they just stole from someone else
			 *   in the same turn (and we got notified of the Pick
			 *   before getting this Drop).
			 *
			 * In that case, the "Drop" has already been handled.
			 *  (cross fingers...)
			 *
			 * *** NO, wait -- that can't happen -- how can a player
			 *     Pick and Move in one turn?  Maybe if they're
			 *     bumped by someone else?  Does the server handle
			 *     that?  And what are the odds someone's bot is
			 *     going to predict that?
			 */
			printf("WARNING: player (%d) dropped package, but not who we expected (%d)!\n",
					p->id,
					pk->carriedBy?pk->carriedBy->id:-1);
			return 1;
		}

		if (pk->carriedBy) {
			pk->carriedBy->weight -= pk->weight;
			ListRemoveEntryByValue(pk->carriedBy->packages, pk);
			pk->carriedBy = NULL;
		}

		if (pk->x != p->x || pk->y != p->y) {
			pk->x = p->x;
			pk->y = p->y;
			//pk->dist = -1;	// Need to recompute.
		}

		if (pk->x == pk->xd && pk->y == pk->yd) {

			pk->state = 2;	// Delivered!

			p->score += pk->weight;

		} else if (pk->xd < 0) {// Don't know it's destination...

			pk->state = -1;// So we don't know whether delivered or fumbled.

		} else {
			pk->state = 0;	// Dropped on the floor.

			// Eh, what the hell -- call it a "home base"
			board[pk->y][pk->x] = '@';
		}
	}

	return 1;
}

static
int getOneStatus()
{
	cstring s;
	int id;
	Player p;

	if (!(s = getWord()))
		return 0;	// End of line.

	if (s[0] != '#') {
		printf("Server sent '%s' when we were expecting robot #.  Giving up.\n", s);
		ungetWord(s);
		giveUp();
	}

	id = atoi(s+1);

	p = findPlayer(id);

	p->lastAlive = turn;

	while (getOneUpdate(p))
		;

	return 1;
}

static
void killPlayer(Player p)
{
	ListIndex li;
	Package pk;

	if (debug > 2)
		printf("Player %d DIED!\n", p->id);

	if (p->packages) {
		ListFor(p->packages, li, pk, Package)
			pk->state = 2;	// "delivered" (to hell, I guess)
		ListDestroy(p->packages, NULL);
	}
	p->packages = NULL;
	p->dead = 1;
}

static
void getStatus()
{
	ListIndex li;
	Player p;

	if (debug>1)
		printf("\nGetting status...\n");

	while (getOneStatus())
		;

	ListFor(players, li, p, Player)
		if (!p->dead && p->lastAlive != turn)
			killPlayer(p);

	if (debug>1) {
		showBoard();
		showPlayers();
	}
}


// Read package info from server -- pertains to packages under our feet.
static
Package getPackage()
{
	cstring s;
	Package pk;

	if (!(s = getWord()))
		return NULL;

	pk = findPackage(atoi(s));

	// We'll just assume the server is error free here...
	pk->xd     = atoi(getWord()) - 1;
	pk->yd     = atoi(getWord()) - 1;
	pk->weight = atoi(getWord());

	pk->x     = me->x;
	pk->y     = me->y;
	pk->state = 0;

	return pk;
}

// Returns list of packages under our feet.
static
List getPackages()
{
	List pks;
	Package pk;

	if (debug>1)
		printf("Getting packages...\n");

	pks = ListCreate();

	while (pk = getPackage())
		ListAppendEntry(pks, pk);

	return pks;
}


// Herein lies most of the "brains".
static
void doMove(List underfoot)	// packages under our feet
{
	Gradient g;
	ListIndex li;
	Package pk;

	if (debug>1)
		printf("Contemplating move (%d packages here)...\n",
				underfoot?ListSize(underfoot):0);

	/*
	 * If we've reached our target, stop seeking it!
	 */
	if (target[0] == me->x && target[1] == me->y)
		target[0] = -1;

	/*
	 * Always check to see if we're on a drop-off spot:
	 * (I just moved this above pickup since obviously
	 *  if we can pick up and drop off stuff here, we
	 *  want to drop stuff first so we have free hands!)
	 */
	if (me->packages) {
		List toDrop;
		toDrop = NULL;
		ListFor(me->packages, li, pk, Package) {
			if (pk->xd == me->x && pk->yd == me->y) {
				if (!toDrop)
					toDrop = ListCreate();
				ListAppendEntry(toDrop, pk);
			}
		}
		if (toDrop) {
			if (debug > 1)
				printf("Dropping %d packages.\n", ListSize(toDrop));
			putString("1 Drop");
			ListFor(toDrop, li, pk, Package)
				putString(" %d", pk->id);
			putLine("");
			ListDestroy(toDrop, NULL);
			return;
		}
	}

	/*
	 * If there're any packages here...
	 */
	if (underfoot && ListSize(underfoot) > 0) {

		if (me->weight < maxWeight) {
			List toGet;
			int pivot[2];
			int roomLeft;

			/*
			 * If we're already carrying, then we should probably
			 *  prefer something near our current target rather
			 *  than near here (since the latter may be in the
			 *  wrong direction):
			 */
			if (me->packages && ListSize(me->packages) > 0 && target[0] >= 0) {
				pivot[0] = target[0];
				pivot[1] = target[1];
			} else {
				// By default, start from here..
				pivot[0] = me->x;
				pivot[1] = me->y;
			}

			roomLeft = maxWeight - me->weight;

			/*
			 * What should we pick up?
			 *
			 * This could get a bit slow, but we won't be doing it
			 *  often, so... hopefully we'll average out ok?
			 *
			 * And no, I'm not even going to try to solve the
			 *  traveling salesman problem before bedtime (which
			 *  is when I'm turning this thing in!).
			 *
			 * [Ok, ok, so they've decided at 3:30pm on Saturday
			 *  to allow separate submissions to the lightning
			 *  and regular categories...  I guess I'll fix those
			 *  things I thought of in the shower right after
			 *  I turned the first version in...]
			 */
			toGet = NULL;
			while (1) {

				Package best;
				real    bestValue;

				best = NULL;

				g = findGradient(pivot[0], pivot[1], 0);

				ListFor(underfoot, li, pk, Package) {

					real value;

					if (pk->weight > roomLeft)
						continue;

					if (toGet && ListFind(toGet, NULL, pk))
						continue;

					// value = -g->dist[pk->yd][pk->xd] + pk->weight*(1./roomLeft);
					// Doh!  I had this right before and then reverted it
					//   to the above in a brain fart!  The real "value" of a
					//   package to us is in its points-per-dollar, or weight
					//   over the cost to deliver.  Cost to deliver is at least
					//   the distance to the destination from our pivot (the
					//   pivot is where we expect to be when we start thinking
					//   about delivering this package), plus one or two dollars
					//   to pick it up and drop it, but that gets tricky since
					//   often we're picking up and dropping more than one at
					//   a time...  We'll add .5 with a hand-wave.
					value = pk->weight / (g->dist[pk->yd][pk->xd] + 0.5);
					// Note if g->dist == -1, the package is unreachable, and
					//  the value goes negative..

					if (!best || value > bestValue) {
						best      = pk;
						bestValue = value;
					}
				}

				if (!best)
					break;

				if (!toGet)
					toGet = ListCreate();
				ListAppendEntry(toGet, best);
				roomLeft -= best->weight;
				pivot[0] = best->xd;
				pivot[1] = best->yd;
			}
			if (toGet) {
				if (debug > 1)
					printf("Picking up %d packages weighing %d units:",
							ListSize(toGet), maxWeight - me->weight - roomLeft);
				putString("1 Pick");
				ListFor(toGet, li, pk, Package) {
					putString(" %d", pk->id);
					if (debug > 1)
						printf(" %d", pk->id);
				}
				putLine("");
				if (debug > 1)
					printf("\n");
				ListDestroy(toGet, NULL);
				return;
			}
		}

	} else {
		/*
		 * If there are no packages here, forget it
		 * was ever a home base if it is one now:
		 */
		if (board[me->y][me->x] == '@')	// Could probably safely skip this
			board[me->y][me->x] = '.';	// And just always do this.  But, whatever.
	}

	/*
	 * If we know where we want to go, just head there:
	 */
retarget:	// I feel so dirty
	if (target[0] >= 0) {

		int  d;
		int  bestDir, bestBid;
		real bestDist;

		g = findGradient(target[0], target[1], hydrophobicTime);	// Almost always cached.

		if (hydrophobicTime)
			hydrophobicTime--;

		bestBid  =  1;	// Default bid
		bestDir  = -1;
		bestDist = -1.;

		for4(d) {	// Each direction we might go
			int x, y;
			real dist;

			x = me->x + deltas[d][0];
			y = me->y + deltas[d][1];

			if (offBoard(x, y))
				continue;

			dist = g->dist[y][x];

			if (dist < 0.)
				continue;

			// Note we could add more cost factors to dist here...
			{// like...
				int dd, xx, yy;
				ListIndex lj;
				Player pp;

				/*
				 * See if there's someone in the target space right now:
				 */
				ListFor(players, lj, pp, Player) {

					if (pp == me || pp->dead)
						continue;

					if (pp->x == x && pp->y == y) {	// Yep!

						/*
						 * Ok, just to be a pooper, let's see if they happen
						 *  to be waiting to get pushed into the pool.  If
						 *  so, give'em a shove:
						 */
						xx = x + deltas[d][0];	// Look further in the direction of motion..
						yy = y + deltas[d][1];

						if (!offBoard(xx, yy) && board[yy][xx] == '~') {
							bestDir = d;
							bestBid = 3;	// Pay a small margin, for those running on the cheap.
							attacks++;		// Just keeping stats
							goto break2;	// C really needs labeled loops, doesn't it?
						} else {
							// Randomly side-step (or not) when we're head-to-head,
							//  to break sphexish behavior.
							dist+=ranval(0., 3.);
							hydrophobicTime = 10;	// Arbitrary..
						}
					}
				}

				// Check the spots adjacent to this target:
				for4(dd) {
					xx = x + deltas[dd][0];
					yy = y + deltas[dd][1];

					if (offBoard(xx, yy))
						continue;

					/*
					 * Penalize spots that neighbor other players
					 *   (since they may jump us this or next turn...)
					 */
					ListFor(players, lj, pp, Player) {
						if (pp == me || pp->dead)	// Oops!  Forgot to check for dead in the lightning submission.
							continue;
						if (pp->x == xx && pp->y == yy) {
							dist+=ranval(0., 5.);
							hydrophobicTime = 10;	// Arbitrary..
						}
					}

					/*
					 * Penalize spots that border water.  But not
					 *  so much as to overcome the gradient, or
					 *  a spot bordered on both sides by water may
					 *  appear impassable.  This will just be a
					 *  tie-breaker.
					 */
					if (board[yy][xx] == '~')
						dist += .3;
				}
			}


			if (bestDist < 0. || dist < bestDist) {
				bestDist = dist;
				bestDir  = d;
			}
		}
break2:
		if (bestDir >= 0) {	// Otherwise, we're stuck!!?
			if (debug > 1)
				printf("Moving %s (bid=%d).\n", coms[bestDir], bestBid);
			putLine("%d Move %s", bestBid, coms[bestDir]);
			return;
		}
	}

	/*
	 * If our hands are empty, we need to get where we can pick up a package:
	 */
	if (!me->packages || ListSize(me->packages) < 1) {

		int x, y, nearest;

		/*
		 * So, how far are we from the various potential pickup spots?
		 * (Or, how far is everything from us?)
		 */
		g = findGradient(me->x, me->y, 0);

		/*
		 * Since for all we know there will be a board with all '@'s,
		 *  I'd rather not pre-make a list of '@'s, so I'll just scan
		 *  the board here.  Hopefully it's not toooo slow:
		 */
		nearest = -1;
		forn(y, height) {
			forn(x, width) {
				if (board[y][x] == '@') {
					if (nearest < 0 || g->dist[y][x] < nearest) {
						nearest = g->dist[y][x];
						target[0] = x;
						target[1] = y;
					}
				}
			}
		}

		if (target[0]>=0 && (target[0]!=me->x || target[1]!=me->y))
			goto retarget;	// wash me!  wash me!
	}

	/*
	 * If we have packages to deliver, pick the nearest:
	 */
	if (me->packages && ListSize(me->packages)) {

		Package best;
		real    bestValue;

		g = findGradient(me->x, me->y, 0);

		best = NULL;

		ListFor(me->packages, li, pk, Package) {

			real value;

			value = -g->dist[pk->yd][pk->xd] + pk->weight*(1./maxWeight);

			if (!best || value > bestValue) {
				bestValue = value;
				best      = pk;
			}
		}

		if (best) {
			target[0] = best->xd;
			target[1] = best->yd;
			goto retarget;	// Hey, it's getting late.
		}
	}

	// Our brain has rotted if we get here.
	while (1) {
		int c;
		target[0] = ranval2i(0, width);
		target[1] = ranval2i(0, height);
		c = board[target[1]][target[0]];
		if (c == '.' || c == '@')
			break;
	}

	putLine("1 Pick");	// don't want to retarget, since might make infinite loop
}


static
void doTurn()
{
	List underfoot;	// Packages

	++turn;

	if (debug>1) {
		//printf("\033[H");
		printf("Turn:%d/%d Hydrophobic:%d  Gradients:%d/%d  PackageCache:%d/%d=%d%% Attacks:%d\n",
				turn,
				startingMoney,
				hydrophobicTime,
				gradients?ListSize(gradients):0, gradientsComputed,
				packageCacheTries-packageCacheMisses,
				packageCacheTries,
				packageCacheTries?((packageCacheTries-packageCacheMisses) * 100 / packageCacheTries):0,
				attacks);
	}

	getStatus();

	underfoot = getPackages();

	doMove(underfoot);

	//ListDestroy(underfoot, PackageFreeF);
	if (underfoot)
		ListDestroy(underfoot, NULL);	// Now remembering packages forever

	if (waitUser)
		getchar();	// Slow things down to human speed
}

Main()
{
	cstring hostname;
	int port;

	Aparse(argv, NULL,
			"debug=%i(DebugLevel,0)", &debug,
			"-w%i<1,0,pause for enter-key between frames>", &waitUser,
			"%s(HostName,localhost)", &hostname,
			"%i(Port,20005)", &port,
		NULL);

	if (ClientSocketFopen(hostname, port, &in, &out))
		ErCreateFatal(666, "Couldn't connect to server.");

	putLine("Player");

	getBoard();
	getConfig();

	while (1)
		doTurn();
}


// Timing loop to see how fast we can compute gradients.
// Answer: We should be able to manage at least 4 per second
//  on the target machine for the worst-case 1000x1000 board.
#if 0
{
	int reps;
	TimeVal t;
	t = TimeTicks();
	forn(reps, 10000) {
		int x,y;
		while (1) {
			x = ranval2i(0, width);
			y = ranval2i(0, height);
			if (board[y][x] == '.' || board[y][x] == '@')
				break;
		}
		g = findGradient(x, y, 0);
		turn++;	// So we don't keep allocating new gradients.
		if (TimeTicks() - t > TimeTicksPerSecond * 10.)
			break;
	}
	t = TimeTicks() - t;
	printf("Time to do %d gradients: %g seconds = %g/second\n", reps,
			t*1./TimeTicksPerSecond,
			reps*1.*TimeTicksPerSecond/t);
	exit(0);
}
#endif

