public Float3 GetNearestMetalSpot(Float3 mypos) { if (!isMetalMap) { double closestdistancesquared = 1000000000000; Float3 bestpos = null; MetalSpot bestspot = null; foreach (MetalSpot metalspot in MetalSpots) { if (!MetalSpotsUsed.Contains(metalspot)) { if (bestpos == null) { bestpos = metalspot.Pos; } double thisdistancesquared = Float3Helper.GetSquaredDistance(mypos, metalspot.Pos); //logfile.WriteLine( "thisdistancesquared = " + thisdistancesquared + " closestdistancesquared= " + closestdistancesquared ); if (thisdistancesquared < closestdistancesquared) { closestdistancesquared = thisdistancesquared; bestpos = metalspot.Pos; bestspot = metalspot; } } } return(bestspot.Pos); } else { return(mypos); // if metal map just return passed-in pos } }
void ReserveMetalExtractorSpaces() { foreach (object metalspotobject in MetalSpots) { MetalSpot metalspot = metalspotobject as MetalSpot; logfile.WriteLine("reserving space for " + metalspot.Pos.ToString()); BuildMap.GetInstance().ReserveSpace(this, (int)(metalspot.Pos.x / 8), (int)(metalspot.Pos.z / 8), 6, 6); } }
// loads cache file // returns true if cache loaded ok, otherwise false if not found, out-of-date, etc // we check the version and return false if out-of-date bool LoadCache() { string MapName = aicallback.GetMapName(); string cachefilepath = Path.Combine(csai.CacheDirectoryPath, MapName + "_metal.xml"); if (!File.Exists(cachefilepath)) { logfile.WriteLine("cache file doesnt exist -> building"); return(false); } XmlDocument cachedom = XmlHelper.OpenDom(cachefilepath); XmlElement metadata = cachedom.SelectSingleNode("/root/metadata") as XmlElement; string cachemetalclassversion = metadata.GetAttribute("version"); if (cachemetalclassversion != MetalClassVersion) { logfile.WriteLine("cache file out of date ( " + cachemetalclassversion + " vs " + MetalClassVersion + " ) -> rebuilding"); return(false); } logfile.WriteLine(cachedom.InnerXml); isMetalMap = Convert.ToBoolean(metadata.GetAttribute("ismetalmap")); if (isMetalMap) { logfile.WriteLine("metal map"); return(true); } XmlElement metalspots = cachedom.SelectSingleNode("/root/metalspots") as XmlElement; ArrayList metalspotsal = new ArrayList(); foreach (XmlElement metalspot in metalspots.SelectNodes("metalspot")) { int amount = Convert.ToInt32(metalspot.GetAttribute("amount")); Float3 pos = new Float3(); Float3Helper.WriteXmlElementToFloat3(metalspot, pos); //pos.LoadCsv( metalspot.GetAttribute("pos") ); MetalSpot newmetalspot = new MetalSpot(amount, pos); metalspotsal.Add(newmetalspot); // logfile.WriteLine( "metalspot xml: " + metalspot.InnerXml ); logfile.WriteLine("metalspot: " + newmetalspot.ToString()); } MetalSpots = (MetalSpot[])metalspotsal.ToArray(typeof(MetalSpot)); logfile.WriteLine("cache file loaded"); return(true); }
// for debugging / convincing oneself spots are in right place void DrawMetalSpots() { if (!isMetalMap) { foreach (object metalspotobject in MetalSpots) { MetalSpot metalspot = metalspotobject as MetalSpot; logfile.WriteLine("drawing spot at " + metalspot.Pos); aicallback.DrawUnit("ARMMEX", metalspot.Pos, 0.0f, 500, aicallback.GetMyAllyTeam(), true, true); } foreach (object metalspotobject in MetalSpotsUsed) { MetalSpot metalspot = metalspotobject as MetalSpot; logfile.WriteLine("drawing usedspot at " + metalspot.Pos); aicallback.DrawUnit("ARMFORT", metalspot.Pos, 0.0f, 500, aicallback.GetMyAllyTeam(), true, true); } } else { aicallback.SendTextMsg("Metal analyzer reports this is a metal map", 0); } }
// 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() <<<"); }
// loads cache file // returns true if cache loaded ok, otherwise false if not found, out-of-date, etc // we check the version and return false if out-of-date bool LoadCache() { string MapName = aicallback.GetMapName(); string cachefilepath = Path.Combine( csai.CacheDirectoryPath, MapName + "_metal.xml" ); if( !File.Exists( cachefilepath ) ) { logfile.WriteLine( "cache file doesnt exist -> building" ); return false; } XmlDocument cachedom = XmlHelper.OpenDom( cachefilepath ); XmlElement metadata = cachedom.SelectSingleNode( "/root/metadata" ) as XmlElement; string cachemetalclassversion = metadata.GetAttribute( "version" ); if( cachemetalclassversion != MetalClassVersion ) { logfile.WriteLine( "cache file out of date ( " + cachemetalclassversion + " vs " + MetalClassVersion + " ) -> rebuilding" ); return false; } logfile.WriteLine( cachedom.InnerXml ); isMetalMap = Convert.ToBoolean( metadata.GetAttribute( "ismetalmap" ) ); if( isMetalMap ) { logfile.WriteLine( "metal map" ); return true; } XmlElement metalspots = cachedom.SelectSingleNode( "/root/metalspots" ) as XmlElement; ArrayList metalspotsal = new ArrayList(); foreach( XmlElement metalspot in metalspots.SelectNodes( "metalspot" ) ) { int amount = Convert.ToInt32( metalspot.GetAttribute("amount") ); Float3 pos = new Float3(); Float3Helper.WriteXmlElementToFloat3( metalspot, pos ); //pos.LoadCsv( metalspot.GetAttribute("pos") ); MetalSpot newmetalspot = new MetalSpot( amount, pos ); metalspotsal.Add( newmetalspot ); // logfile.WriteLine( "metalspot xml: " + metalspot.InnerXml ); logfile.WriteLine( "metalspot: " + newmetalspot.ToString() ); } MetalSpots = (MetalSpot[])metalspotsal.ToArray( typeof( MetalSpot ) ); logfile.WriteLine( "cache file loaded" ); return true; }
// 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() <<<"); }