Exemplo n.º 1
0
    public override Dictionary <TileAction, double> ScoreGrid(GameObject[,] grid)
    {
        tiles = GridUtils.FlattenGridTiles(grid);

        var edges = GridUtils.GetEdgesOfEnabledGrid(grid);

        // find all valid targets that could do damage to this entity
        var targets = BehaviorUtils.GetAllEntitiesInTileList(tiles, BehaviorUtils.Hostility.FRIENDLY).Where(target => target.currentHP > 0);

        // get all the tiles that the targets can move to attack the aiEntity
        var targetRanges = targets.SelectMany(target => GridUtils.GenerateTileRing(grid, target.range + target.maxMoves, target.tile)).ToList();

        // get tiles that are valid to move to in this turn
        var nextTurnRange = GridUtils.GenerateTileCircle(grid, entity.maxMoves, entity.tile);

        nextTurnRange.Add(entity.tile);

        // score each tile in valid move range with distance to be in range of target
        var nextMoveMap = new Dictionary <TileAction, double>();

        nextTurnRange.ForEach(tile => {
            var score = targetRanges.Select(attackTile =>
                                            (Mathf.Abs(tile.x - attackTile.x) + Mathf.Abs(tile.y - attackTile.y)) * -1
                                            ).Sum();

            if (edges.Contains(tile))
            {
                score *= 2;
            }
            nextMoveMap[new TileAction(tile, entity)] = score;
        });
        return(nextMoveMap);
    }
Exemplo n.º 2
0
    public override Dictionary <TileAction, double> ScoreGrid(GameObject[,] grid)
    {
        tiles = GridUtils.FlattenGridTiles(grid);
        // find all valid targets for an attack
        var targets = BehaviorUtils.GetAllEntitiesInTileList(tiles, BehaviorUtils.Hostility.FRIENDLY).Where(target => target.currentHP > 0);

        // get all the tiles that the aiEntity can use to attack each target
        // var targetRanges = targets.SelectMany(target => GridUtils.GenerateTileRing(grid, entity.range, target.tile, movement: true)).ToList();
        var targetRanges = targets.ToDictionary(target => target, target => GridUtils.GenerateTileCircle(grid, entity.range, target.tile));

        // get tiles that are valid to move to in this turn
        var nextTurnRange = GridUtils.GenerateTileCircle(grid, entity.maxMoves, entity.tile).Where(tile => tile.occupier == null).ToList();

        nextTurnRange.Add(entity.tile);

        // score each tile in valid move range with distance to be in range of target
        var totalScoreMap = new Dictionary <TileAction, double>();

        var positionScoreMap = new Dictionary <TileAction, double>();
        var damageScoreMap   = new Dictionary <TileAction, double>();
        var skirtScoreMap    = new Dictionary <TileAction, double>();

        nextTurnRange.ForEach(tile => {
            targetRanges.ToList().ForEach(x => {
                var target      = x.Key;
                var targetRange = x.Value;

                var tileAction = new TileAction(tile, target);

                // boost score by closest tile to range in which AI can attack target
                positionScoreMap[tileAction] = targetRange.Select(attackTile => Mathf.Abs(tile.x - attackTile.x) + Mathf.Abs(tile.y - attackTile.y)).Min();

                // boost score against "weak" targets
                damageScoreMap[tileAction] = (double)1 / ((double)entity.damage / (double)target.currentHP);

                // boost score that maximizes distance between AI and target
                var distance = GridUtils.GetDistanceBetweenTiles(tile, target.tile);
                if (distance <= entity.range)
                {
                    skirtScoreMap[tileAction] = distance * -1;
                }
                else
                {
                    skirtScoreMap[tileAction] = int.MaxValue;
                }
            });
        });

        positionScoreMap = Utils.NormalizeDict(positionScoreMap);
        damageScoreMap   = Utils.NormalizeDict(damageScoreMap);
        skirtScoreMap    = Utils.NormalizeDict(skirtScoreMap);

        positionScoreMap.Keys.ToList().ForEach(tileAction => {
            // TODO implement factors that can be tuned for these scores
            totalScoreMap[tileAction] = positionScoreMap[tileAction] + damageScoreMap[tileAction] + skirtScoreMap[tileAction];
        });

        return(totalScoreMap);
    }
Exemplo n.º 3
0
    public override Dictionary <TileAction, double> ScoreGrid(GameObject[,] grid)
    {
        tiles = GridUtils.FlattenGridTiles(grid);

        // find all valid targets for a melee attack
        var targets = BehaviorUtils.GetAllEntitiesInTileList(tiles, BehaviorUtils.Hostility.FRIENDLY).Where(target => target.currentHP > 0);

        // get all the tiles that the aiEntity can use to attack each target
        var targetRanges = targets.ToDictionary(target => target, target => {
            return(GridUtils.GenerateTileCircle(grid, 1, target.tile, movement: true)
                   // this filters out any tiles that are occupied by anything other than the entity calculating this move
                   .Where(tile => {
                if (tile.occupier != null)
                {
                    return tile.occupier == entity;
                }
                else
                {
                    return true;
                }
            }));
        });

        // get tiles that are valid to move to in this turn
        var nextTurnRange = GridUtils.GenerateTileCircle(grid, entity.maxMoves, entity.tile, movement: true).Where(tile => tile.occupier == null).ToList();

        nextTurnRange.Add(entity.tile);

        // score each tile in valid move range with distance to be in range of target
        var nextMoveMap = new Dictionary <TileAction, double>();

        nextTurnRange.ForEach(tile => {
            targetRanges.ToList().ForEach(x => {
                var target      = x.Key;
                var targetRange = x.Value;

                if (targetRange.Count() > 0)
                {
                    // using Min() means that the positionScore will focus on getting circles as close to overlapping, but not score higher than that
                    // using Sum() means that the positionScore will focus on getting circle centers to overlap, as the dead center of the circle has
                    //    the shortest distance to all tiles in the circle.
                    var positioningScore = targetRange.Select(attackTile => Mathf.Abs(tile.x - attackTile.x) + Mathf.Abs(tile.y - attackTile.y)).Min();
                    var damageScore      = 1 / ((float)entity.damage / (float)target.currentHP);

                    nextMoveMap[new TileAction(tile, target)] = positioningScore + damageScore;
                }
            });
        });
        return(nextMoveMap);
    }
Exemplo n.º 4
0
    // when this behavior is used, enemies will queue a teleport on Turn 1, then teleport to a tile as far away from allies as possible.
    // this will not trigger in fear, and thus the edges of the grid will not enable the enemy to escape

    public override Dictionary <TileAction, double> ScoreGrid(GameObject[,] grid)
    {
        tiles = GridUtils.FlattenGridTiles(grid, true).Where(tile => tile.gameObject.activeInHierarchy).ToList();

        // find all valid targets that could do damage to this entity
        var targets = BehaviorUtils.GetAllEntitiesInTileList(tiles, BehaviorUtils.Hostility.FRIENDLY).Where(target => target.currentHP > 0);

        // get all the tiles that the targets can move to attack the aiEntity
        var targetRanges = targets.SelectMany(target => GridUtils.GenerateTileRing(grid, target.range + target.maxMoves, target.tile)).ToList();

        // get tiles that are valid to move to in this turn (all tiles that are not currently occupied)
        var nextTurnRange = tiles.Where(tile => tile.occupier == null).ToList();

        var edgeTiles = GridUtils.GetEdgesOfEnabledGrid(grid);

        // score each tile in valid teleport range to avoid range of targets
        var nextMoveMap = new Dictionary <TileAction, double>();

        nextTurnRange.ForEach(tile => {
            double score = targetRanges.Select(attackTile => {
                // score is first determined as inversely related to the distance to the attack range of allies
                double dist = Mathf.Abs(tile.x - attackTile.x) + Mathf.Abs(tile.y - attackTile.y) + 1;
                return(1 / dist);
            }).Sum();

            if (entity.currentHP == entity.maxHP)
            {
                // clamp to avoid being picked at high health
                score = double.MaxValue;
            }
            else
            {
                // boost when health is lower
                // this value will always be between 0 and 1, and will "boost" properly
                score *= (double)entity.currentHP / (double)entity.maxHP;
            }


            if (entity.lastSelectedBehavior is EvasiveTeleport)
            {
                // improve chance this behavior is picked. uses - instead of * in order to keep order of scoring per tile
                score -= 1;
            }
            nextMoveMap[new TileAction(tile, entity)] = score;
        });
        return(nextMoveMap);
    }