Example #1
0
        // destination with hint in which direction to go
        public Tuple <Point, Direction> GetDestination(Man man)
        {
            var enemies = MenOfRace(Enemy(man.Race)).ToArray();

            {
                var enimiesDistance = enemies.ToDictionary(e => e, e => e.Position.Distance(man.Position)).ToArray();
                var minDistanceAsIs = enimiesDistance.Min(p => p.Value);
                Assert.True(minDistanceAsIs > 0, "never enemies on the same position");
                if (minDistanceAsIs == 1)
                {
                    // we are in position to attack an enemy, no need to move
                    return(null);
                }
            }

            // positions in range versus enemies
            var inRange = enemies
                          .SelectMany(e => _map.Directions(e.Position).Select(e.Position.Go))
                          .ToHashSet()
                          .ToDictionary(p => p, p => p.Distance(man.Position))
                          .ToArray()
            ;                     // different enemies can have same in range positions

            // it could happen that all enemies (ie the only one) are rounded from all sides
            // in this way no way to go
            if (!inRange.Any())
            {
                return(null);
            }

            // simplest strategy: select path with minimal distance
            var minDistance = inRange.Min(p => p.Value);

            if (minDistance == 0)
            {
                return(null);                              // no way to go
            }
            // more difficult task
            var optimizer = new Optimizer(_map, man.Position);

            var partialResult      = new Dictionary <Point, int>();
            int?curMinimalDistance = null;

            foreach (var inRangePoint in inRange.OrderBy(p => p.Value))
            {
                // we try from  by air shortest distance
                if (curMinimalDistance.HasValue)
                {
                    if (curMinimalDistance.Value < inRangePoint.Value)
                    {
                        // the distance already found is less then shortest candidate by air
                        // we may terminate
                        break;
                    }
                }

                var distance = optimizer.Distance(inRangePoint.Key);
                if (distance.HasValue)
                {
                    partialResult.Add(inRangePoint.Key, distance.Value);
                    curMinimalDistance = curMinimalDistance.HasValue
                                                ? Math.Min(curMinimalDistance.Value, distance.Value) : distance.Value;
                }
            }

            if (!curMinimalDistance.HasValue)
            {
                // no way to connect two points
                return(null);
            }

            var destinationList     = partialResult.Where(p => p.Value == curMinimalDistance.Value).Select(p => p.Key);
            var selectedDestination = destinationList.OrderBy(p => p.ReadingOrder).First();

            var optimizer1 = new Optimizer(_map, selectedDestination);
            var options    = _map.Directions(man.Position)
                             .ToDictionary(d => d, d => man.Position.Go(d))
                             .OrderBy(p => p.Value.ReadingOrder)
                             .ToArray();

            foreach (var o in options)
            {
                var distance = optimizer1.Distance(o.Value);                 // decreased distance due to the step ahead to the target
                if (distance.HasValue)
                {
                    if ((curMinimalDistance.Value - 1) == distance.Value)
                    {
                        // this is the correct direction and expected distance
                        return(Tuple.Create(selectedDestination, o.Key));
                    }
                }
            }
            throw new Exception("unexpected processing of options");
        }