From bb2fe2b1ab835e1be5e6e4c421356521f983173d Mon Sep 17 00:00:00 2001
From: Joris <joris.dauphin@gmail.com>
Date: Wed, 13 Jun 2012 18:23:30 +0200
Subject: [PATCH] Rewrite AiFindLumberMillPlace and AiFindHallPlace to use the
 new TerrainTraversal code.

---
 src/ai/ai_building.cpp | 377 +++++++++++++++++------------------------
 1 file changed, 155 insertions(+), 222 deletions(-)

diff --git a/src/ai/ai_building.cpp b/src/ai/ai_building.cpp
index db23f8598..a8d9d17a1 100644
--- a/src/ai/ai_building.cpp
+++ b/src/ai/ai_building.cpp
@@ -140,14 +140,14 @@ static int AiCheckSurrounding(const CUnit &worker,
 class BuildingPlaceFinder
 {
 public:
-	BuildingPlaceFinder(const CUnit &worker, const CUnitType &type, bool checkSurround, Vec2i* resPos) :
+	BuildingPlaceFinder(const CUnit &worker, const CUnitType &type, bool checkSurround, Vec2i* resultPos) :
 		worker(worker), type(type),
 		movemask(worker.Type->MovementMask & ~(MapFieldLandUnit | MapFieldAirUnit | MapFieldSeaUnit)),
 		checkSurround(checkSurround),
-		resPos(resPos)
+		resultPos(resultPos)
 	{
-		resPos->x = -1;
-		resPos->y = -1;
+		resultPos->x = -1;
+		resultPos->y = -1;
 	}
 	VisitResult Visit(TerrainTraversal &terrainTraversal, const Vec2i &pos, const Vec2i &from);
 private:
@@ -155,7 +155,7 @@ private:
 	const CUnitType &type;
 	unsigned int movemask;
 	bool checkSurround;
-	Vec2i* resPos;
+	Vec2i* resultPos;
 };
 
 VisitResult BuildingPlaceFinder::Visit(TerrainTraversal &terrainTraversal, const Vec2i &pos, const Vec2i &from)
@@ -170,10 +170,10 @@ VisitResult BuildingPlaceFinder::Visit(TerrainTraversal &terrainTraversal, const
 		&& !AiEnemyUnitsInDistance(*worker.Player, NULL, pos, 8)) {
 		bool backupok;
 		if (AiCheckSurrounding(worker, type, pos.x, pos.y, backupok) && checkSurround) {
-			*resPos = pos;
+			*resultPos = pos;
 			return VisitResult_Finished;
-		} else if (backupok && resPos->x == -1) {
-			*resPos = pos;
+		} else if (backupok && resultPos->x == -1) {
+			*resultPos = pos;
 		}
 	}
 	if (CanMoveToMask(pos, movemask)) { // reachable
@@ -218,13 +218,91 @@ static int AiFindBuildingPlace2(const CUnit &worker, const CUnitType &type, cons
 		terrainTraversal.PushPos(startPos);
 	}
 
-
 	BuildingPlaceFinder buildingPlaceFinder(worker, type, checkSurround, dpos);
 
 	terrainTraversal.Run(buildingPlaceFinder);
 	return Map.Info.IsPointOnMap(*dpos);
 }
 
+class HallPlaceFinder
+{
+public:
+	HallPlaceFinder(const CUnit &worker, const CUnitType &type, int resource, Vec2i* resultPos) :
+		worker(worker), type(type),
+		movemask(worker.Type->MovementMask & ~(MapFieldLandUnit | MapFieldAirUnit | MapFieldSeaUnit)),
+		resource(resource),
+		resultPos(resultPos)
+	{}
+	VisitResult Visit(TerrainTraversal &terrainTraversal, const Vec2i &pos, const Vec2i &from);
+private:
+	bool IsAUsableMine(const CUnit &mine) const;
+private:
+	const CUnit &worker;
+	const CUnitType &type;
+	const unsigned int movemask;
+	const int resource;
+	Vec2i* resultPos;
+};
+
+bool HallPlaceFinder::IsAUsableMine(const CUnit &mine) const
+{
+	// Check units around mine
+	const Vec2i offset = {5, 5};
+	const Vec2i minpos = mine.tilePos - offset;
+	const Vec2i typeSize = {mine.Type->TileWidth - 1, mine.Type->TileHeight - 1};
+	const Vec2i maxpos = mine.tilePos + typeSize + offset;
+	std::vector<CUnit *> units;
+
+	Map.Select(minpos, maxpos, units);
+
+	const size_t nunits = units.size();
+	int buildings = 0;
+
+	for (size_t j = 0; j < nunits; ++j) {
+		const CUnit &unit = *units[j];
+		// Enemy near mine
+		if (AiPlayer->Player->IsEnemy(*unit.Player)) {
+			return false;
+		}
+		// Town hall near mine
+		if (unit.Type->CanStore[resource]) {
+			return false;
+		}
+		// Town hall may not be near but we may be using it, check
+		// for 2 buildings near it and assume it's been used
+		if (unit.Type->Building && !unit.Type->GivesResource == resource) {
+			++buildings;
+			if (buildings == 2) {
+				return false;
+			}
+		}
+	}
+	return true;
+}
+
+VisitResult HallPlaceFinder::Visit(TerrainTraversal &terrainTraversal, const Vec2i &pos, const Vec2i &from)
+{
+#if 0
+	if (!player.AiEnabled && !Map.IsFieldExplored(player, pos)) {
+		terrainTraversal.Get(pos) = -1;
+		return VisitResult_DeadEnd;
+	}
+#endif
+	CUnit *mine = ResourceOnMap(pos, resource);
+	if (mine && IsAUsableMine(*mine)) {
+		if (AiFindBuildingPlace2(worker, type, pos, mine, resultPos, true)) {
+			return VisitResult_Finished;
+		}
+	}
+	if (CanMoveToMask(pos, movemask)) { // reachable
+		terrainTraversal.Get(pos) = 1;
+		return VisitResult_Ok;
+	} else { // unreachable
+		terrainTraversal.Get(pos) = -1;
+		return VisitResult_DeadEnd;
+	}
+}
+
 /**
 **  Find building place for hall. (flood fill version)
 **
@@ -236,11 +314,10 @@ static int AiFindBuildingPlace2(const CUnit &worker, const CUnitType &type, cons
 **  5) no hall already near
 **  !6) enough gold in mine
 **
-**  @param worker  Worker to build building.
-**  @param type    Type of building.
-**  @param nx      Start search X position (if == -1 then unit X pos used).
-**  @param ny      Start search Y position (if == -1 then unit Y pos used).
-**  @param dpos    Pointer for position returned.
+**  @param worker    Worker to build building.
+**  @param type      Type of building.
+**  @param startPos  Start search position (if == -1 then unit X pos used).
+**  @param resultPos OUT: Pointer for position returned.
 **
 **  @return        True if place found, false if not found.
 **
@@ -249,232 +326,88 @@ static int AiFindBuildingPlace2(const CUnit &worker, const CUnitType &type, cons
 */
 static int AiFindHallPlace(const CUnit &worker,
 						   const CUnitType &type,
-						   int nx, int ny,
-						   Vec2i *dpos,
+						   const Vec2i &startPos,
+						   Vec2i *resultPos,
 						   int resource = GoldCost)
 {
-	const Vec2i offset[] = {{0, -1}, { -1, 0}, {1, 0}, {0, 1}, { -1, -1}, {1, -1}, { -1, 1}, {1, 1}};
-	Vec2i *points;
-	int size;
-	Vec2i pos;
-	Vec2i rpos;
-	int mask;
-	int wp;
-	int rp;
-	int ep;
-	int w;
-	unsigned char *m;
-	unsigned char *morg;
-	unsigned char *matrix;
-	CUnit *mine;
+	TerrainTraversal terrainTraversal;
 
-	pos.x = (nx != -1 ? nx : worker.tilePos.x);
-	pos.y = (ny != -1 ? ny : worker.tilePos.y);
-	size = Map.Info.MapWidth * Map.Info.MapHeight / 4;
-	points = new Vec2i[size];
+	terrainTraversal.SetSize(Map.Info.MapWidth, Map.Info.MapHeight);
+	terrainTraversal.Init(-1);
 
-	//
-	// Make movement matrix. FIXME: can create smaller matrix.
-	//
-	morg = MakeMatrix();
-	w = Map.Info.MapWidth + 2;
-	matrix = morg + w + w + 1;
-
-	points[0] = pos;
-	rp = 0;
-	matrix[pos.x + pos.y * w] = 1; // mark start point
-	ep = wp = 1; // start with one point
-
-	mask = worker.Type->MovementMask;
-
-	//
-	// Pop a point from stack, push all neighbors which could be entered.
-	//
-	for (;;) {
-		while (rp != ep) {
-			rpos = points[rp];
-			for (int i = 0; i < 8; ++i) { // mark all neighbors
-				pos = rpos + offset[i];
-				m = matrix + pos.x + pos.y * w;
-				if (*m) { // already checked
-					continue;
-				}
-				//
-				// Look if there is a mine
-				//
-				if ((mine = ResourceOnMap(pos, resource))) {
-					// Check units around mine
-					const Vec2i offset = {5, 5};
-					const Vec2i minpos = mine->tilePos - offset;
-					const Vec2i typeSize = {mine->Type->TileWidth - 1, mine->Type->TileHeight - 1};
-					const Vec2i maxpos = mine->tilePos + typeSize + offset;
-					std::vector<CUnit *> units;
-
-					Map.Select(minpos, maxpos, units);
-
-					const size_t nunits = units.size();
-					int buildings = 0;
-					size_t j;
-
-					for (j = 0; j < nunits; ++j) {
-						// Enemy near mine
-						if (AiPlayer->Player->IsEnemy(*units[j]->Player)) {
-							break;
-						}
-						// Town hall near mine
-						if (units[j]->Type->CanStore[resource]) {
-							break;
-						}
-						// Town hall may not be near but we may be using it, check
-						// for 2 buildings near it and assume it's been used
-						if (units[j]->Type->Building
-							&& !units[j]->Type->GivesResource == resource) {
-							++buildings;
-							if (buildings == 2) {
-								break;
-							}
-						}
-					}
-					if (j == nunits) {
-						if (AiFindBuildingPlace2(worker, type, pos, mine, dpos, true)) {
-							delete[] morg;
-							delete[] points;
-							return 1;
-						}
-					}
-				}
-
-				if (CanMoveToMask(pos, mask)) { // reachable
-					*m = 1;
-					points[wp] = pos; // push the point
-					if (++wp >= size) { // round about
-						wp = 0;
-					}
-				} else { // unreachable
-					*m = 99;
-				}
-			}
-			if (++rp >= size) { // round about
-				rp = 0;
-			}
-		}
-
-		//
-		// Continue with next frame.
-		//
-		if (rp == wp) { // unreachable, no more points available
-			break;
-		}
-		ep = wp;
+	if (Map.Info.IsPointOnMap(startPos)) {
+		terrainTraversal.PushPos(startPos);
+	} else {
+		terrainTraversal.PushPos(worker.tilePos);
 	}
 
-	delete[] morg;
-	delete[] points;
-	return 0;
+	HallPlaceFinder hallPlaceFinder(worker, type, resource, resultPos);
+
+	return terrainTraversal.Run(hallPlaceFinder);
+}
+
+class LumberMillPlaceFinder
+{
+public:
+	LumberMillPlaceFinder(const CUnit &worker, const CUnitType &type, Vec2i* resultPos) :
+		worker(worker), type(type),
+		movemask(worker.Type->MovementMask & ~(MapFieldLandUnit | MapFieldAirUnit | MapFieldSeaUnit)),
+		resultPos(resultPos)
+	{}
+	VisitResult Visit(TerrainTraversal &terrainTraversal, const Vec2i &pos, const Vec2i &from);
+private:
+	const CUnit &worker;
+	const CUnitType &type;
+	unsigned int movemask;
+	Vec2i* resultPos;
+};
+
+VisitResult LumberMillPlaceFinder::Visit(TerrainTraversal &terrainTraversal, const Vec2i &pos, const Vec2i &from)
+{
+#if 0
+	if (!player.AiEnabled && !Map.IsFieldExplored(player, pos)) {
+		terrainTraversal.Get(pos) = -1;
+		return VisitResult_DeadEnd;
+	}
+#endif
+	if (Map.ForestOnMap(pos)) {
+		if (AiFindBuildingPlace2(worker, type, pos, NULL, resultPos, true)) {
+			return VisitResult_Finished;
+		}
+	}
+	if (CanMoveToMask(pos, movemask)) { // reachable
+		terrainTraversal.Get(pos) = 1;
+		return VisitResult_Ok;
+	} else { // unreachable
+		terrainTraversal.Get(pos) = -1;
+		return VisitResult_DeadEnd;
+	}
 }
 
 /**
 **  Find free building place for lumber mill. (flood fill version)
 **
-**  @param worker  Worker to build building.
-**  @param type    Type of building.
-**  @param nx      Start search X position (if == -1 then unit X pos used).
-**  @param ny      Start search X position (if == -1 then unit X pos used).
-**  @param dpos    Pointer for position returned.
+**  @param worker   Worker to build building.
+**  @param type     Type of building.
+**  @param startPos Start search X position (if == -1 then unit X pos used).
+**  @param dpos     OUT: Pointer for position returned.
 **
 **  @return        True if place found, false if not found.
 **
-**  @todo          FIXME: This is slow really slow, using two flood
-**                 fills, is not a perfect solution.
+**  @todo          FIXME: This is slow really slow, using two flood fills, is not a perfect solution.
 */
-static int AiFindLumberMillPlace(const CUnit &worker, const CUnitType &type,
-								 int nx, int ny, Vec2i *dpos)
+static bool AiFindLumberMillPlace(const CUnit &worker, const CUnitType &type, const Vec2i &startPos, Vec2i *dpos)
 {
-	const Vec2i offset[] = {{0, -1}, { -1, 0}, {1, 0}, {0, 1}, { -1, -1}, {1, -1}, { -1, 1}, {1, 1}};
-	Vec2i *points;
-	int size;
-	Vec2i rpos;
-	int mask;
-	int wp;
-	int rp;
-	int ep;
-	int i;
-	int w;
-	unsigned char *m;
-	unsigned char *morg;
-	unsigned char *matrix;
-	Vec2i pos;
+	TerrainTraversal terrainTraversal;
 
-	pos.x = nx != -1 ? nx : worker.tilePos.x;
-	pos.y = ny != -1 ? ny : worker.tilePos.y;
-	size = Map.Info.MapWidth * Map.Info.MapHeight / 4;
-	points = new Vec2i[size];
+	terrainTraversal.SetSize(Map.Info.MapWidth, Map.Info.MapHeight);
+	terrainTraversal.Init(-1);
 
-	//
-	// Make movement matrix.
-	//
-	morg = MakeMatrix();
-	w = Map.Info.MapWidth + 2;
-	matrix = morg + w + w + 1;
+	terrainTraversal.PushPos(startPos);
 
-	points[0] = pos;
-	rp = 0;
-	matrix[pos.x + pos.y * w] = 1; // mark start point
-	ep = wp = 1; // start with one point
+	LumberMillPlaceFinder lumberMillPlaceFinder(worker, type, dpos);
 
-	mask = worker.Type->MovementMask;
-
-	//
-	// Pop a point from stack, push all neightbors which could be entered.
-	//
-	for (;;) {
-		while (rp != ep) {
-			rpos = points[rp];
-			for (i = 0; i < 8; ++i) { // mark all neighbors
-				pos = rpos + offset[i];
-				m = matrix + pos.x + pos.y * w;
-				if (*m) { // already checked
-					continue;
-				}
-				//
-				// Look if there is wood
-				//
-				if (Map.ForestOnMap(pos)) {
-					if (AiFindBuildingPlace2(worker, type, pos, NULL, dpos, true)) {
-						delete[] morg;
-						delete[] points;
-						return 1;
-					}
-				}
-
-				if (CanMoveToMask(pos, mask)) { // reachable
-					*m = 1;
-					points[wp] = pos; // push the point
-					if (++wp >= size) { // round about
-						wp = 0;
-					}
-				} else { // unreachable
-					*m = 99;
-				}
-			}
-
-			if (++rp >= size) { // round about
-				rp = 0;
-			}
-		}
-
-		//
-		// Continue with next frame.
-		//
-		if (rp == wp) { // unreachable, no more points available
-			break;
-		}
-		ep = wp;
-	}
-
-	delete[] morg;
-	delete[] points;
-	return 0;
+	return terrainTraversal.Run(lumberMillPlaceFinder);
 }
 
 static int AiFindMiningPlace(const CUnit &worker,
@@ -589,9 +522,9 @@ int AiFindBuildingPlace(const CUnit &worker, const CUnitType &type, const Vec2i
 		//Depots
 		if (type.CanStore[i]) {
 			if (resinfo && resinfo->TerrainHarvester) {
-				return AiFindLumberMillPlace(worker, type, nearPos.x, nearPos.y, dpos);
+				return AiFindLumberMillPlace(worker, type, nearPos, dpos);
 			} else {
-				return AiFindHallPlace(worker, type, nearPos.x, nearPos.y, dpos, i);
+				return AiFindHallPlace(worker, type, nearPos, dpos, i);
 			}
 		} else
 			//mines