#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?"":""); } } 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[]\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