예제 #1
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() <<<");
        }