// algorithm more or less by krogothe // ported from Submarine's original C++ version public void SearchMetalSpots() { logfile.WriteLine("SearchMetalSpots() >>>"); isMetalMap = false; ArrayList metalspotsal = new ArrayList(); int mapheight = aicallback.GetMapHeight() / 2; //metal map has 1/2 resolution of normal map int mapwidth = aicallback.GetMapWidth() / 2; double mapmaxmetal = aicallback.GetMaxMetal(); int totalcells = mapheight * mapwidth; logfile.WriteLine("mapwidth: " + mapwidth + " mapheight " + mapheight + " maxmetal:" + mapmaxmetal); byte[] metalmap = aicallback.GetMetalMap(); // original metal map int[,] metalremaining = new int[mapwidth, mapheight]; // actual metal available at that point. we remove metal from this as we add spots to MetalSpots int[,] SpotAvailableMetal = new int [mapwidth, mapheight]; // amount of metal an extractor on this spot could make int[,] NormalizedSpotAvailableMetal = new int [mapwidth, mapheight]; // SpotAvailableMetal, normalized to 0-255 range int totalmetal = 0; ArrayIndexer arrayindexer = new ArrayIndexer(mapwidth, mapheight); //Load up the metal Values in each pixel logfile.WriteLine("width: " + mapwidth + " height: " + mapheight); for (int y = 0; y < mapheight; y++) { //string logline = ""; for (int x = 0; x < mapwidth; x++) { metalremaining[x, y] = (int)metalmap[arrayindexer.GetIndex(x, y)]; totalmetal += metalremaining[x, y]; // Count the total metal so you can work out an average of the whole map //logline += metalremaining[ x, y ].ToString() + " "; // logline += metalremaining[ x, y ] + " "; } // logfile.WriteLine( logline ); } logfile.WriteLine("*******************************************"); double averagemetal = ((double)totalmetal) / ((double)totalcells); //do the average // int maxmetal = 0; int ExtractorRadius = (int)(aicallback.GetExtractorRadius() / 16.0); int DoubleExtractorRadius = ExtractorRadius * 2; int SquareExtractorRadius = ExtractorRadius * ExtractorRadius; //used to speed up loops so no recalculation needed int FourSquareExtractorRadius = 4 * SquareExtractorRadius; // same as above double CellsInRadius = Math.PI * ExtractorRadius * ExtractorRadius; int maxmetalspotamount = 0; logfile.WriteLine("Calculating available metal for each spot..."); SpotAvailableMetal = CalculateAvailableMetalForEachSpot(metalremaining, ExtractorRadius, out maxmetalspotamount); logfile.WriteLine("Normalizing..."); // normalize the metal so any map will have values 0-255, no matter how much metal it has int[,] NormalizedMetalRemaining = new int[mapwidth, mapheight]; for (int y = 0; y < mapheight; y++) { for (int x = 0; x < mapwidth; x++) { NormalizedSpotAvailableMetal[x, y] = (SpotAvailableMetal[x, y] * 255) / maxmetalspotamount; } } logfile.WriteLine("maxmetalspotamount: " + maxmetalspotamount); bool Stopme = false; int SpotsFound = 0; //logfile.WriteLine( BuildTable.GetInstance().GetBiggestMexUnit().ToString() ); // IUnitDef biggestmex = BuildTable.GetInstance().GetBiggestMexUnit(); // logfile.WriteLine( "biggestmex is " + biggestmex.name + " " + biggestmex.humanName ); for (int spotindex = 0; spotindex < MaxSpots && !Stopme; spotindex++) { logfile.WriteLine("spotindex: " + spotindex); int bestspotx = 0, bestspoty = 0; int actualmetalatbestspot = 0; // use to try to put extractors over spot itself //finds the best spot on the map and gets its coords int BestNormalizedAvailableSpotAmount = 0; for (int y = 0; y < mapheight; y++) { for (int x = 0; x < mapwidth; x++) { if (NormalizedSpotAvailableMetal[x, y] > BestNormalizedAvailableSpotAmount || (NormalizedSpotAvailableMetal[x, y] == BestNormalizedAvailableSpotAmount && metalremaining[x, y] > actualmetalatbestspot)) { BestNormalizedAvailableSpotAmount = NormalizedSpotAvailableMetal[x, y]; bestspotx = x; bestspoty = y; actualmetalatbestspot = metalremaining[x, y]; } } } logfile.WriteLine("BestNormalizedAvailableSpotAmount: " + BestNormalizedAvailableSpotAmount); if (BestNormalizedAvailableSpotAmount < MinMetalForSpot) { Stopme = true; // if the spots get too crappy it will stop running the loops to speed it all up logfile.WriteLine("Remaining spots too small; stopping search"); } if (!Stopme) { Float3 pos = new Float3(); pos.x = bestspotx * 2 * MovementMaps.SQUARE_SIZE; pos.z = bestspoty * 2 * MovementMaps.SQUARE_SIZE; pos.y = aicallback.GetElevation(pos.x, pos.z); //pos = Map.PosToFinalBuildPos( pos, biggestmex ); logfile.WriteLine("Metal spot: " + pos + " " + BestNormalizedAvailableSpotAmount); MetalSpot thismetalspot = new MetalSpot((int)((BestNormalizedAvailableSpotAmount * mapmaxmetal * maxmetalspotamount) / 255), pos); // if (aicallback.CanBuildAt(biggestmex, pos) ) // { // pos = Map.PosToBuildMapPos( pos, biggestmex ); // logfile.WriteLine( "Metal spot: " + pos + " " + BestNormalizedAvailableSpotAmount ); // if(pos.z >= 2 && pos.x >= 2 && pos.x < mapwidth -2 && pos.z < mapheight -2) // { // if(CanBuildAt(pos.x, pos.z, biggestmex.xsize, biggestmex.ysize)) // { metalspotsal.Add(thismetalspot); SpotsFound++; //if(pos.y >= 0) //{ // SetBuildMap(pos.x-2, pos.z-2, biggestmex.xsize+4, biggestmex.ysize+4, 1); //} //else //{ //SetBuildMap(pos.x-2, pos.z-2, biggestmex.xsize+4, biggestmex.ysize+4, 5); //} // } // } // } for (int myx = bestspotx - (int)ExtractorRadius; myx < bestspotx + (int)ExtractorRadius; myx++) { if (myx >= 0 && myx < mapwidth) { for (int myy = bestspoty - (int)ExtractorRadius; myy < bestspoty + (int)ExtractorRadius; myy++) { if (myy >= 0 && myy < mapheight && ((bestspotx - myx) * (bestspotx - myx) + (bestspoty - myy) * (bestspoty - myy)) <= (int)SquareExtractorRadius) { metalremaining[myx, myy] = 0; //wipes the metal around the spot so its not counted twice NormalizedSpotAvailableMetal[myx, myy] = 0; } } } } // Redo the whole averaging process around the picked spot so other spots can be found around it for (int y = bestspoty - (int)DoubleExtractorRadius; y < bestspoty + (int)DoubleExtractorRadius; y++) { if (y >= 0 && y < mapheight) { for (int x = bestspotx - (int)DoubleExtractorRadius; x < bestspotx + (int)DoubleExtractorRadius; x++) { //funcion below is optimized so it will only update spots between r and 2r, greatly speeding it up if ((bestspotx - x) * (bestspotx - x) + (bestspoty - y) * (bestspoty - y) <= (int)FourSquareExtractorRadius && x >= 0 && x < mapwidth && NormalizedSpotAvailableMetal[x, y] > 0) { totalmetal = 0; for (int myx = x - (int)ExtractorRadius; myx < x + (int)ExtractorRadius; myx++) { if (myx >= 0 && myx < mapwidth) { for (int myy = y - (int)ExtractorRadius; myy < y + (int)ExtractorRadius; myy++) { if (myy >= 0 && myy < mapheight && ((x - myx) * (x - myx) + (y - myy) * (y - myy)) <= (int)SquareExtractorRadius) { totalmetal += metalremaining[myx, myy]; //recalculate nearby spots to account for deleted metal from chosen spot } } } } NormalizedSpotAvailableMetal[x, y] = totalmetal * 255 / maxmetalspotamount; //set that spots metal amount } } } } } } if (SpotsFound > 500) { isMetalMap = true; metalspotsal.Clear(); logfile.WriteLine("Map is considered to be a metal map"); } else { isMetalMap = false; // debug //for(list<AAIMetalSpot>::iterator spot = metal_spots.begin(); spot != metal_spots.end(); spot++) } MetalSpots = ( MetalSpot[] )metalspotsal.ToArray(typeof(MetalSpot)); SaveCache(); logfile.WriteLine("SearchMetalSpots() <<<"); }