#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <math.h>

#ifdef USE_X11
# include <unistd.h>
# include <sys/types.h>
# include <netinet/in.h>
#else
# ifdef MS_WIN
#  include <winsock.h>
# endif
#endif

#include "global.h"

#include "world.h"
#include "rivers.h"
#include "MsgQ.h"
#include "misc.h"
#include "trans.h"
#include "graph.h"
#include "display.h"
#include "units.h"
#include "city.h"
#include "player.h"
#include "savefile.h"
#include "mapdisplay.h"

struct Terrains {
  TerrainType type;
  int goodiefood;
  int food;
  int irrigfood;
  int goodieprod;
  int prod;
  int miningprod;
  int goodietrade;
  int trade;
  int roadtrade;

  int irrigTime;
  int roadTime;
  int railTime;
  int mineTime;
  int unpolluteTime;

  double terrainDefense;

  CPixmap *pic;
  CPixmap *goodiePic;
};

Terrains TerrainData[LAST_TERRAIN_TYPE];

Terrains terrain_list[LAST_TERRAIN_TYPE] = {
  {WATER    ,3,1,0,0,0,0,1,2,0, -1,-1,-1,-1,-1,  1,   &ocean_pic, &fish_pic},
  {LAND     ,0,0,0,0,0,0,0,0,0, -1,-1,-1,-1,-1,  1,   NULL,NULL},
  {MOUNTAINS,0,0,0,1,1,1,6,0,0, -1, 8 ,8 ,8,  1,  3,  &mountains_pic, &gold_pic},
  {HILLS    ,1,1,1,2,0,3,0,0,0,  4, 6, 6, 6, 4,  2,   &hills_pic, &coal_pic},
  {DESERT   ,3,0,1,1,1,1,1,0,1, -1, 2, 2, 4, 2,  1,   &desert_pic, &oasis_pic},
  {SWAMP    ,1,1,0,4,0,0,0,0,0,  7, 7, 7,-1, 7,  1.5, &swamp_pic, &oil_pic},
  {PLAINS   ,1,1,1,3,1,0,0,0,1,  3, 1, 1,-1, 5,  1,   &plains_pic,&horses_pic},
  {GRASS    ,2,2,1,1,0,0,0,0,1,  3, 1, 1,-1, 5,  1,   &grass_pic, &basket_pic},
  {FOREST   ,3,1,0,2,2,0,0,0,0,  7, 6, 6,-1, 1,  1.5, &forest_pic,&horses_pic},
  {JUNGLE   ,1,1,0,0,0,0,4,0,0,  7, 6, 6,-1, 1,  1.5, &jungle_pic, &gems_pic},
  {RIVER    ,2,2,1,1,0,0,1,1,0,  2, 2, 2,-1, 6,  0.8, NULL, &rivgood_pic}
};

static unsigned char chunktypes[(LAST_TERRAIN_TYPE/8)+1];
#define DOESTYPEEXIST(t) ((chunktypes[(t) / 8] & \
                              (1 << ((t) % 8))) != 0)
#define TYPEEXISTS(t)     chunktypes[(t) / 8] |= \
                              (1 << ((t) % 8))

inline int rnd(int max) { return random() % max; }

// local alt variation
const int LocalVar = 15;

// percent of land which is a certain type
// anything remaining is automatically plains
const int Mountains = 6;
const int Hills = 8;
const int Desert = 3;
const int Swamp = 3;
const int GrassPer = 30;
const int Jungle = 5;
const int Forest = 6;

const int GoodiePer = 8; // percent of world which has goodies

int World::blackColor;
int World::waterColor;
int World::landColor;

World::World(int MaxX, int MaxY, int ContDist, int ChunkSize, int NumChunks,
	     int LandPercent, int NumRivers, int RiverLength)
: world(MaxX, MaxY),lastSeen(MaxX,MaxY), units(MaxX, MaxY), cities(MaxX, MaxY),
  working(MaxX, MaxY), visible(MaxX, MaxY)
{
  maxX = MaxX;
  maxY = MaxY;

  MapCoord::SetWorldSize(maxX, maxY);
  int per;
  for (MapCoord c(0,0); c.y < maxY; ++c) {
    Set(c, WATER);
    WhichCity(c) = 0;
    Working(c) = 0;
    visible(c.x,c.y) = WorldCreate & 0x02;
  }
  TYPEEXISTS(WATER);

  long now;
  time(&now);
  srandom((unsigned int)now);

  Debug('w', "cleared world, making terrains\n");
  int land = MakeTerrain(MaxX*MaxY, LandPercent, NumChunks, ChunkSize,
			 ContDist, PLAINS, WATER);
  MakeTerrain(land, Mountains, 5, 1, ContDist/2, MOUNTAINS, PLAINS);
  MakeTerrain(land, Hills, 7, 1, ContDist/2, HILLS, PLAINS);
  MakeTerrain(land, Desert, 7, 2, ContDist/2, DESERT, PLAINS);
  MakeTerrain(land, Swamp, 4, 2, ContDist/2, SWAMP, PLAINS);
  MakeTerrain(land, Forest, 7, 2, ContDist/2, FOREST, PLAINS);
  MakeTerrain(land, Jungle, 7, 2, ContDist/2, JUNGLE, PLAINS);
  MakeTerrain(land, GrassPer, 7, 2, ContDist/2, GRASS, PLAINS);
  Debug('w', "Making rivers\n");
  rivers = new Rivers(NumRivers, RiverLength, this);
  // add goodies
  Debug('w', "Placing goodies\n");
  for (MapCoord c(0,0); c.y < maxY; ++c) {
    switch(Terrain(c)) {
    case RIVER:
      per = 33;
      break;
    case MOUNTAINS:
    case SWAMP:
    case JUNGLE:
    case HILLS:
      per = (int) GoodiePer/2;
      break;
    case GRASS:
      per = GoodiePer * 2;
    default:
      per = GoodiePer;
    }
    if (rnd(100)<per) {
      what(c) |= GOODIE;
      //if (WorldCreate & 0x02)
      lastSeen(c.x, c.y) |= GOODIE;
    }
  }
  Debug('w', "Done placing goodies\n");
}

// read from save file
World::World()
{
  maxX = ReadUShort();
  maxY = ReadUShort();
  Debug('s', "world %d %d\n", maxX, maxY);

  MapCoord::SetWorldSize(maxX, maxY);
  world.Resize(maxX, maxY);
  lastSeen.Resize(maxX, maxY);
  cities.Resize(maxX, maxY);
  units.Resize(maxX, maxY);
  visible.Resize(maxX, maxY);
  working.Resize(maxX, maxY);

  for(MapCoord c(0,0); c.y < maxY; ++c)
    WhichCity(c) = 0;

  ReadBytes((char *)world.GetFirst(), world.Size());
  ReadBytes((char *)lastSeen.GetFirst(), lastSeen.Size());
  ReadBytes((char *)working.GetFirst(), working.Size());
  ReadBytes((char *)visible.GetFirst(), visible.Size());
  Debug('s', "Restored most of world\n");

  rivers = new Rivers(this);
}

World::World(MsgQ *q)
{
  char *buf;

  *q >> maxX;
  *q >> maxY;

  MapCoord::SetWorldSize(maxX, maxY);
  world.Resize(maxX, maxY);
  lastSeen.Resize(maxX, maxY);
  cities.Resize(maxX, maxY);
  units.Resize(maxX, maxY);
  visible.Resize(maxX, maxY);
  working.Resize(maxX, maxY);

  *q >> buf;
  world.CopyFrom((ushort *)buf);
  *q >> buf;
  lastSeen.CopyFrom((ushort*) buf);
  delete buf;

  for(MapCoord c(0,0); c.y < maxY; ++c) {
    WhichCity(c) = 0;
    visible(c.x, c.y) = WorldCreate & 0x02; // all black
    Working(c) = 0;
    what(c) = ntohs(what(c));
    lastSeen(c.x, c.y) = ntohs(lastSeen(c.x, c.y));
  }

  rivers = new Rivers(q, this);
}

World::~World()
{
  delete rivers;
}

void World::AllocColors()
{
  int i;

  blackColor = screen->AllocColor("black");
  landColor = screen->AllocColor("ForestGreen"); //#22008b002200");
  waterColor = screen->AllocColor("#00007000ff00");

  Terrains *t;
  for (i = 0; i < LAST_TERRAIN_TYPE; i++) {
    t = &(TerrainData[terrain_list[i].type]);
    *t = terrain_list[i];
  }
}

void World::Send()
{
  MsgQ *q = new MsgQ;
  *q << "map_start";
  *q << maxX;
  *q << maxY;

  Array<ushort> nWorld(maxX, maxY);
  Array<ushort> nLastSeen(maxX, maxY);
  for(MapCoord c(0,0); c.y < maxY; ++c) {
    nWorld(c.x, c.y) = htons(what(c));
    nLastSeen(c.x, c.y) = htons(lastSeen(c.x, c.y));
  }

  char *buf = (char *)nWorld.GetArray();
  *q << Msg(buf, (int)world.Size());
  buf = (char *)nLastSeen.GetArray();
  *q << Msg(buf, (int)world.Size());

  rivers->Send(q);
  SendQ(q);
}

void World::Set(MapCoord c, int what)
{
  world(c.x,c.y) = what;
  // if (Visible(x,y))
  //  lastSeen(x,y) = what;
}

int World::Distance(MapCoord c1, MapCoord c2)
{
  int dx, dy;
  dx = abs(c1.x-c2.x);
  dy = abs(c1.y-c2.y);
  if (dx > maxX/2)
    dx -= maxX/2;
  if (dy > maxY/2)
    dy -= maxY/2;
  return int(sqrt(dx*dx+dy*dy));
}

int World::MakeTerrain(int max, int percent, int NumChunks, int ChunkSize,
		       int ContDist, TerrainType type, int where)
{
  int x, y;
  long left = (long(max)*percent)/100, n;
  while (left > 0) {
    long numcont = left / (NumChunks * 3L * ChunkSize*ChunkSize) + 1;
    Debug('w', "new continents of %d\n", type);
    for (n = 0; n < numcont; ++n) {
      NewChunk(x, y, ContDist + rnd(ContDist), type, where);
      left -= MakeChunk(x, y, ChunkSize, type);
    }
    Debug('w', "growing\n");
    for (n = 0; n < numcont * NumChunks; ++n) {
      GrowChunk(x, y, type);
      left -= MakeChunk(x, y, ChunkSize, type);
    }
  }
  left = 0;
  for (MapCoord c(0,0); c.y < maxY; ++c)
    if (Terrain(c) == type) ++left;
  return left;
}

// find a chunk of the same type to merge with
void World::GrowChunk(int &xp, int &yp, TerrainType type)
{
  int x, y;
  while (Terrain(MapCoord(x = rnd(maxX), y = rnd(maxY))) != type);
  xp = x;
  yp = y;
}

// check if there is enough open space to make a new chunk of type
int World::Verify(int x, int y, int dist, TerrainType type, int where)
{
  int xbase, ybase, x1, y1, n = 0, side = 2*dist+1;
  for (ybase = y - dist; ybase <= y + dist; ++ybase) {
    y1 = ybase < 0 ? ybase+maxY : ybase % maxY;
#ifdef NEWWORLD
    if (ybase == y1)
#endif
    for (xbase = x - dist; xbase <= x + dist; ++xbase) {
      x1 = xbase < 0 ? xbase+maxX : xbase % maxX;
      if (Terrain(MapCoord(x1, y1)) == type)
	return 0;
      if (Terrain(MapCoord(x1, y1)) == where) ++n;
    }
  }
  double frac = double(n)/side/side;
  return frac > 0.5;
}

// find a spot with enough open space amongst where to make a new chunk of type
void World::NewChunk(int &xp, int &yp, int dist, TerrainType type, int where)
{
  int x = 0, y = 0, i = 0;
  uchar existtype=DOESTYPEEXIST(type);

  do {
   for (i = 0; i < 30; ++i) {
     x = rnd(maxX);
#ifdef NEWWORLD
   y = rnd(maxY-(maxY/3))+maxY/6;
#else
   y = rnd(maxY);
#endif
     if (Terrain(MapCoord(x, y)) == where && Verify(x, y, dist, type, where)) break;
   }
  } while ((!existtype) && (i==30));
  if (i == 30) // didn't find open space, just merge with another
    GrowChunk(xp, yp, type);
  else { // found open space
    xp = x;
    yp = y;
  }
}

// make a block of given terrain type possibly leaving out spots on the edge
int World::MakeChunk(int x, int y, int dist, TerrainType type)
{
#ifdef NEWWORLD
  int edge = 0;
#endif
  int xbase, ybase;
  int xfail, yfail;
  int x1, y1;
  int created = 0;

  for (ybase = y - dist; ybase <= y + dist; ybase++) {
    yfail = y - ybase;
    if (yfail < 0)
      yfail = -yfail;
    y1 = ybase < 0 ? ybase+maxY : ybase % maxY;
#ifdef NEWWORLD
    if (y1 == ybase)
#endif
     for (xbase = x - dist; xbase <= x + dist; xbase++) {
       xfail = x - xbase;
       if (xfail < 0)
        xfail = -xfail;
       if (xfail < yfail)
        xfail = yfail;
       x1 = xbase < 0 ? xbase+maxX : xbase % maxX;
       if (Terrain(MapCoord(x1, y1)) == type)
        continue;
#ifdef NEWWORLD
       if (y1 < maxY/6) edge = (y1 * 100) / (maxY / 6 + 1);
       if (y1 > maxY-maxY/6) edge = (maxY - y1) * 100 / (maxY / 6 + 1);
       if ((xfail == 0 || !rnd(xfail + 1) || !rnd(xfail)) &&
           ((edge == 0) || (rnd(100)<edge))) {
#else
       if (xfail == 0 || !rnd(xfail + 1) || !rnd(xfail)) {
#endif
         Set(MapCoord(x1, y1), type);
         TYPEEXISTS(type);
         created++;
       }
     }
  }
  return created;
}

// draw top left corner of map
void World::Draw(WDisplay *w, MapCoord topleft)
{
  Draw(w, topleft, SquaresWide, SquaresHigh);
}

void World::Draw(WDisplay *w, MapCoord topleft, int cols, int rows)
{
  MapCoord c = topleft;
  for (int y1 = 0; y1 < rows; ++y1, c = topleft.down(y1)) {
    if (c.valid()) {
      for (int x1 = 0; x1 < cols; ++x1, c = c.right()) {
	if (RealVisible(c) > 1 && WhichCity(c) != 0)
	  trans->TransCity(WhichCity(c))->Draw(w);
	else {
	  DrawSquare(w, c);
	  if (RealVisible(c) > 1 && Units(c))
	    trans->TransUnit(Units(c).First())->Draw(w);
	}
      }
    }
    else
      for (int x1 = 0; x1 < cols; ++x1)
	; //w->DrawBlankSquare((realx + x1)%maxX, realy/* (?) */); // FIXME
  }
}

void World::DrawSquare(WDisplay *w, MapCoord coord)
{
  if (RealVisible(coord) == 0) {
    w->DrawBlankSquare(coord);
    return;
  }
  long what = lastSeen(coord.x, coord.y);
  long terrain = what & TERRAIN;
  if (terrain == RIVER)
    rivers->DrawRiver(w, coord);
  else
    w->DrawPixmap(coord, TerrainData[terrain].pic);
  if (what & GOODIE)
    w->DrawPixmap(coord, TerrainData[terrain].goodiePic);

  if (what & MINED)
    w->DrawPixmap(coord, &mining_pic);
  if (what & IRRIG)
    w->DrawPixmap(coord, &irrig_pic);
  if (what & RAILROADED) {
    int i,j;
    for (i = -1;i < 2; i++) for (j = -1;j < 2; j++)
      if (IsRailRoad(coord.down(i).right(j)))
	w->DrawPixmap(coord, rails_pic[i+1][j+1]);
      else if (IsRoad(coord.down(i).right(j)))
	w->DrawPixmap(coord, roads_pic[i+1][j+1]);
  }
  else if (what & ROAD) {
    int i,j;
    
    for (i = -1;i < 2; i++) for (j = -1;j < 2; j++)
      if ((IsRoad(coord.down(i).right(j)) || IsRailRoad(coord.down(i).right(j))))
	w->DrawPixmap(coord, roads_pic[i+1][j+1]);
  }
  if (what & POLLUTED)
    w->DrawPixmap(coord, &pollution_pic);
}

void World::DrawBase(WDisplay *w, MapCoord coord)
{
  if (RealVisible(coord) > 1 && WhichCity(coord) != 0)
    trans->TransCity(WhichCity(coord))->Draw(w);
  else
    DrawSquare(w, coord);
}

int World::OpponentPresent(int player, MapCoord c)
{
  for (Lister<ulong> unitl = Units(c); unitl; unitl.Next()) {
    Unit *unit = trans->TransUnit(unitl.Elem());
    if (unit->ownerId != player) {
      return 1;
    }
  }
  return 0;
}

int World::NextToOpponent(int player, MapCoord coord)
{
  for (int i = -1;i < 2; i++) for (int j = -1;j < 2; j++)
    if (OpponentPresent(player,coord.up(i).right(j)))
      return 1;
  return 0;
}

void World::SeeSquare(MapDisplay *window, MapCoord coord, int side)
{
  MapCoord c;
  for (int i = -side; i <= side; i++)
    for (int j = -side; j <= side; j++)
      {
	c = coord.up(i).left(j);
	int stat = RealVisible(c);
	if (stat == 0) // discovered new square, mark it on the map
	  MarkSquare(window, c, -1);
	MakeVisible(c);
	if (lastSeen(c.x,c.y) != What(c)) {
	  lastSeen(c.x,c.y) = What(c);
	  stat = 1;
	}
	if (stat < 2 && window->Visible(c)) // has become visible
	  Draw(window, c, 1, 1);
      }
}

void World::HideSquare(MapDisplay *window, MapCoord coord, int side)
{
  MapCoord c;
  for (int i = -side; i <= side; i++)
    for (int j = -side; j <= side; j++)
      {
	c = coord.left(i).up(j);
	LoseVisible(c);
	if (RealVisible(c) == 1 && window->Visible(c))
	  Draw(window, c, 1, 1);
      }
}

void World::MarkSquare(MapDisplay *window, MapCoord coord, int c)
{
  // shows up on main map
  if (c == -1)
    c = Terrain(coord) == WATER ? waterColor : landColor;
  window->ColorMainMap(coord,c);
}

double World::SquareDefense(MapCoord c, int attacker)
{
  double mod = 1;
  City *city;
  if ((city = trans->TransCity(WhichCity(c))) != NULL &&
      attacker != BOMBER && attacker != ARTILLERY &&
      city->HasBuilding(CITYWALLS))
    mod *= 3;
  mod *= TerrainData[Terrain(c)].terrainDefense;
  return mod;
}

void World::DrawMainMap(MapDisplay *window)
{
  for(MapCoord c(0,0); c.y < maxY; ++c)
    if (RealVisible(c) > 0)
      MarkSquare(window, c, -1);
}

int World::Food(MapCoord c, int player, int LoveTheRuler)
{
  int food = IsGoodie(c) ? TerrainData[Terrain(c)].goodiefood : TerrainData[Terrain(c)].food;
  if (Irrigated(c)) {
    food += TerrainData[Terrain(c)].irrigfood;
  }
  if (Polluted(c)) {
    food = int(food/2.0+0.5);
  } 
  if (IsRailRoad(c)) food = int(food*1.5);
  if (food >= 3 && players[player]->govt < MONARCH && !LoveTheRuler) {
    --food;
  }
  return food;
}

int World::Prod(MapCoord c, int player, int LoveTheRuler)
{
  int p = IsGoodie(c) ? TerrainData[Terrain(c)].goodieprod : TerrainData[Terrain(c)].prod;
  if (Mined(c)) p += TerrainData[Terrain(c)].miningprod;
  if (IsRailRoad(c)) p = int(p*1.5);
  if (Polluted(c)) p = int(p/2.0+0.5);
  if (p >= 3 && players[player]->govt < MONARCH && !LoveTheRuler)
    --p;
  return p;
}

int World::Trade(MapCoord c, int player, int LoveTheRuler)
{
  int trade = IsGoodie(c) ? TerrainData[Terrain(c)].goodietrade : TerrainData[Terrain(c)].trade;
  if (IsRoad(c) || IsRailRoad(c)) trade += TerrainData[Terrain(c)].roadtrade;
  if (IsRailRoad(c)) trade = int(trade*1.5);
  if (trade > 0 && ((players[player]->govt >= REPUBLIC) || (players[player]->govt >= MONARCH && LoveTheRuler)))
    ++trade;
  if (Polluted(c)) trade = int(trade/2.0+0.5);
  if (trade >= 3 && players[player]->govt < MONARCH && !LoveTheRuler)
    --trade;
  return trade;
}

int World::IrrigTime(MapCoord c)
{
  int ter = Terrain(c);
  if (WhichCity(c) != 0 || Irrigated(c)) return -1;
  if (TerrainData[ter].irrigTime == -1 && !(ter == DESERT || IsGoodie(c)))
    return -1;
  // try and find water or irrig in four directions
  if (GivesWater(c.up()) || GivesWater(c.down()) || GivesWater(c.left())
      || GivesWater(c.right())) {
    if (ter == DESERT)
      return 3;
    else
      return TerrainData[ter].irrigTime;
  }
  return -1;
}

int World::RoadTime(MapCoord c)
{
  return IsRoad(c) || IsRailRoad(c) ? -1 : TerrainData[Terrain(c)].roadTime;
}

int World::RailRoadTime(MapCoord c)
{
  if (IsRailRoad(c))
    return -1;
  return IsRoad(c) ? TerrainData[Terrain(c)].railTime : -1;
}

int World::MineTime(MapCoord c)
{
  return Mined(c) ? -1 : TerrainData[Terrain(c)].mineTime;
}

int World::UnPolluteTime(MapCoord c)
{
  return Polluted(c) ? TerrainData[Terrain(c)].unpolluteTime : -1;
}

void World::Save()
{
  WriteUShort(maxX);
  WriteUShort(maxY);
  Debug('s', "world %d %d\n", maxX, maxY);

  WriteBytes((char *)world.GetFirst(), world.Size());
  WriteBytes((char *)lastSeen.GetFirst(), lastSeen.Size());
  WriteBytes((char *)working.GetFirst(), working.Size());
  WriteBytes((char *)visible.GetFirst(), visible.Size());

  rivers->Save();
}

void World::MakeRoad(MapCoord c)
{
  if (currTurn != playerId)
    Debug('F', "Can't make road when not in move turn\n");
  what(c) |= ROAD;
  *moveQ << PieceMove(0, TERRAIN_CHANGE, c, what(c));
}

void World::MakeRailRoad(MapCoord c)
{
  if (currTurn != playerId)
    Debug('F', "Can't make railroad when not in move turn\n");
  what(c) &= ~ROAD;
  what(c) |= RAILROADED;
  *moveQ << PieceMove(0, TERRAIN_CHANGE, c, what(c));
}

void World::Irrigate(MapCoord c)
{
  if (currTurn != playerId)
    Debug('F', "Can't irrigate when not in move turn\n");
  int ter = Terrain(c);
  switch (ter) {
  case SWAMP:
  case JUNGLE:
  case FOREST:
    what(c) = what(c) & ~TERRAIN | GRASS;
    break;
  default:
    what(c) |= IRRIG;
    // irrigation floods out mines
    what(c) &= ~MINED;
  }
  *moveQ << PieceMove(0, TERRAIN_CHANGE, c, what(c));
}

void World::Mine(MapCoord c)
{
  if (currTurn != playerId)
    Debug('F', "Can't mine when not in move turn\n");
  what(c) |= MINED;
  Debug('F', "Mine created at %d %d\n", c);
  *moveQ << PieceMove(0, TERRAIN_CHANGE, c, what(c));
}

void World::UnPollute(MapCoord c)
{
  if (currTurn != playerId)
    Debug('F', "Can't unpollute when not in move turn\n");
  what(c) &= ~POLLUTED;
  *moveQ << PieceMove(0, TERRAIN_CHANGE, c, what(c));
}

void World::Pollute(MapCoord c)
{
  if (currTurn != playerId)
    Debug('F', "Can't pollute when not in move turn\n");
  what(c) |= POLLUTED;
  lastSeen(c.x,c.y) |= POLLUTED;
  *moveQ << PieceMove(0, TERRAIN_CHANGE, c, what(c));
}

void World::PolluteNear(MapCoord coord)
{
  int dx,dy;
  int f = 0;
  // first check that there is a spot not already polluted
  for (dx = 0; dx < 5; ++dx) for (dy = 0; dy < 5; ++dy) {
    if ((dx % 4) == 0 && (dy % 4) == 0) continue;
    MapCoord c = coord.down(dx-2).right(dy-2);
    if (!WhichCity(c) && !Polluted(c) && (Terrain(c) != WATER))
      f = 1;
  }
  // if there are any spots unpolluted, roll randomly until we pick one
  while (f) {
    dx = random() % 5;
    dy = random() % 5;
    if ((dx % 4) == 0 && (dx % 4) == 0) continue;
    MapCoord c = coord.down(dx-2).right(dy-2);
    if (!WhichCity(c) && !Polluted(c) && (Terrain(c) != WATER)) {
      Pollute(c);
      f = 0;
    }
  }
}
