Exemplo n.º 1
0
        public void SelectAbility(int index)
        {
            var currentMob = _game.CurrentMob;

            if (currentMob == null)
            {
                return;
            }

            var mobInfo = _game.MobManager.MobInfos[currentMob.Value];

            if (index >= mobInfo.Abilities.Count)
            {
                Utils.Log(LogSeverity.Info, nameof(GameBoardController),
                          "Trying to select an ability index higher than the number of abilities.");
                return;
            }
            var ability = mobInfo.Abilities[index];

            if (SelectedAbilityIndex.HasValue)
            {
                if (SelectedAbilityIndex.Value == index)
                {
                    SelectedAbilityIndex = null;
                }
                else if (GameInvariants.IsAbilityUsableNoTarget(_game, currentMob.Value, ability))
                {
                    SelectedAbilityIndex = index;
                }
            }
            else if (GameInvariants.IsAbilityUsableNoTarget(_game, currentMob.Value, ability))
            {
                SelectedAbilityIndex = index;
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Generates possible direct ability use actions.
        /// </summary>
        public static bool GenerateDirectAbilityUse(GameInstance state,
                                                    CachedMob mob,
                                                    List <UctAction> result)
        {
            bool foundAbilityUse = false;
            var  mobInfo         = mob.MobInfo;
            var  mobId           = mob.MobId;

            foreach (var abilityId in mobInfo.Abilities)
            {
                if (!GameInvariants.IsAbilityUsableNoTarget(state, mobId, abilityId))
                {
                    continue;
                }

                foreach (var targetId in state.MobManager.Mobs)
                {
                    if (GameInvariants.IsAbilityUsable(state, mob, state.CachedMob(targetId), abilityId))
                    {
                        foundAbilityUse = true;

                        var action = UctAction.AbilityUseAction(abilityId, mobId, targetId);
                        GameInvariants.AssertValidAction(state, action);

                        result.Add(action);
                    }
                }
            }

            return(foundAbilityUse);
        }
Exemplo n.º 3
0
        public void Render(Entity entity, SpriteBatch batch, AssetManager assetManager)
        {
            var effect = assetManager.LoadEffect(AssetManager.ShaderAbility);

            batch.Begin(effect: effect, samplerState: Camera2D.SamplerState);

            var mob = _mobFunc();

            if (mob != null)
            {
                var abilityId = mob.MobInfo.Abilities[_abilityIndex];

                var isActive = _gameBoardController.SelectedAbilityIndex == _abilityIndex;

                if (GameInvariants.IsAbilityUsableNoTarget(_game, mob.MobId, abilityId))
                {
                    isActive = true;
                }

                var ability = _game.MobManager.Abilities[abilityId];
                batch.Draw(assetManager[AssetManager.SpellBg], entity.RenderPosition);

                if (entity.AABB.Contains(InputManager.Instance.MousePosition))
                {
                    batch.Draw(assetManager[AssetManager.SpellHighlight], entity.RenderPosition);
                }
            }
            else
            {
                Debug.WriteLine("ERROR - Rendering abilities, but no mob is currently active.");
                batch.Draw(assetManager[AssetManager.NoTexture], entity.RenderPosition);
            }

            batch.End();
        }
Exemplo n.º 4
0
        /// <summary>
        /// Generates a list of possible actions, truncated for the purposes of MCTS.
        /// </summary>
        public static List <UctAction> PossibleActions(GameInstance game, UctNode parent, bool allowMove,
                                                       bool allowEndTurn)
        {
            var result = new List <UctAction>(10);

            var currentMob = game.CurrentMob;

            if (currentMob.HasValue)
            {
                var mob = game.CachedMob(currentMob.Value);

                GameInvariants.AssertMobPlayable(game, mob);

                bool foundAbilityUse = GenerateDirectAbilityUse(game, mob,
                                                                result);

                // We disable movement if there is a possibility to cast abilities.
                if (allowMove && (Constants.AlwaysAttackMove || !foundAbilityUse))
                {
                    GenerateAttackMoveActions(game, game.CachedMob(mob.MobId), result);
                }

                if (allowMove)
                {
                    if (parent == null || parent.Action.Type != UctActionType.DefensiveMove)
                    {
                        GenerateDefensiveMoveActions(game, mob, result);
                    }
                }
            }
            else
            {
                Utils.Log(LogSeverity.Warning, nameof(UctNode),
                          "Final state reached while trying to compute possible actions.");
                throw new InvalidOperationException();
            }

            if (allowEndTurn)
            {
                // We would skip end turn if there are not enough actions.
                if (!Constants.EndTurnAsLastResort || result.Count <= 1)
                {
                    result.Add(UctAction.EndTurnAction());
                }
            }

            GameInvariants.AssertValidActions(game, result);

            return(result);
        }
Exemplo n.º 5
0
        private void UnselectAbilityIfNeeded()
        {
            var mobId = _game.CurrentMob;

            if (mobId == null || !SelectedAbilityIndex.HasValue)
            {
                return;
            }

            var mobInfo         = _game.MobManager.MobInfos[mobId.Value];
            var selectedAbility = mobInfo.Abilities[SelectedAbilityIndex.Value];

            if (!GameInvariants.IsAbilityUsableNoTarget(_game, mobId.Value, selectedAbility))
            {
                SelectedAbilityIndex = null;
            }
        }
Exemplo n.º 6
0
        /// <summary>
        /// Generates a number of defensive move actions based on a heatmap.
        /// </summary>
        public static void GenerateDefensiveMoveActions(GameInstance state, CachedMob mob, List <UctAction> result)
        {
            var heatmap = Heatmap.BuildHeatmap(state, null, false);
            var coords  = new List <AxialCoord>();

            var mobInstance = mob.MobInstance;
            var mobId       = mob.MobId;

            foreach (var coord in heatmap.Map.AllCoords)
            {
                if (heatmap.Map[coord] != heatmap.MinValue)
                {
                    continue;
                }
                if (state.Map[coord] == HexType.Wall)
                {
                    continue;
                }
                if (state.State.AtCoord(coord, true).HasValue)
                {
                    continue;
                }

                bool canMoveTo = state.Pathfinder.Distance(mobInstance.Coord, coord) <= mobInstance.Ap;

                if (!canMoveTo)
                {
                    continue;
                }

                coords.Add(coord);
            }

            coords.Shuffle();

            int maximumMoveActions = Math.Max(0, 3 - result.Count);

            for (int i = 0; i < Math.Min(coords.Count, maximumMoveActions); i++)
            {
                var action = UctAction.DefensiveMoveAction(mobId, coords[i]);
                GameInvariants.AssertValidAction(state, action);

                result.Add(action);
            }
        }
Exemplo n.º 7
0
        /// <summary>
        /// Calculates a move towards an enemy, or ends a turn if no such move is possible.
        /// </summary>
        private static UctAction PickMoveTowardsEnemyAction(GameInstance game, CachedMob mob,
                                                            CachedMob possibleTarget)
        {
            foreach (var targetId in game.MobManager.Mobs)
            {
                var target = game.CachedMob(targetId);

                if (!GameInvariants.IsTargetableNoSource(game, mob, target))
                {
                    continue;
                }

                var action = FastMoveTowardsEnemy(game, mob, target);

                if (action.Type == UctActionType.Move)
                {
                    return(action);
                }
            }

            return(UctAction.EndTurnAction());
        }
Exemplo n.º 8
0
        /// <summary>
        /// Runs a benchmark comparing two different AIs against each other on a fixed map.
        /// </summary>
        public static void CompareAi()
        {
            var dna = new DNA(2, 2);

            dna.Randomize();

            var map = Map.Load(@"data/map.json");

            var game = GameSetup.GenerateFromDna(dna, dna, map);

            game.PrepareEverything();

            GameInvariants.AssertMobsNotStandingOnEachother(game);

            IMobController c1, c2;

            switch (Constants.MctsBenchType)
            {
            case 0:
                c1 = new MctsController(game, Constants.MctsBenchIterations);
                c2 = new AiRandomController(game);
                break;

            case 1:
                c1 = new MctsController(game, Constants.MctsBenchIterations);
                c2 = new AiRuleBasedController(game);
                break;

            case 2:
                c1 = new AiRuleBasedController(game);
                c2 = new AiRandomController(game);
                break;

            default:
                throw new ArgumentException($"Invalid value of {Constants.MctsBenchType} for --MctsBenchType");
            }

            var iterationStopwatch = new Stopwatch();

            int c1Wins = 0;
            int c2Wins = 0;

            for (int i = 0; i < 1000; i++)
            {
                dna.Randomize();
                iterationStopwatch.Restart();

                GameSetup.OverrideGameDna(game, dna, dna);
                var r1 = GameEvaluator.Playout(game, c1, c2);

                GameSetup.OverrideGameDna(game, dna, dna);
                var r2 = GameEvaluator.Playout(game, c2, c1);

                iterationStopwatch.Stop();

                Console.WriteLine(Accounting.GetStats());

                c1Wins += r1.RedWins + r2.BlueWins;
                c2Wins += r1.BlueWins + r2.RedWins;

                Console.WriteLine($"{i.ToString("0000")} STATS: M2: {c1Wins}, M5: {c2Wins}, winrate: {((double)c1Wins/(c1Wins+c2Wins)).ToString("0.000")}");
            }
        }
Exemplo n.º 9
0
        /// <summary>
        /// Generates possible attack move actions.
        /// </summary>
        public static void GenerateAttackMoveActions(GameInstance state, CachedMob mob, List <UctAction> result)
        {
            var mobInfo     = mob.MobInfo;
            var mobInstance = mob.MobInstance;

            foreach (var enemyId in state.MobManager.Mobs)
            {
                var target = state.CachedMob(enemyId);

                if (!GameInvariants.IsTargetableNoSource(state, mob, target))
                {
                    continue;
                }

                AxialCoord myCoord         = mobInstance.Coord;
                AxialCoord?closestCoord    = null;
                int?       distance        = null;
                int?       chosenAbilityId = null;

                foreach (var coord in state.Map.EmptyCoords)
                {
                    if (!state.Map.IsVisible(coord, target.MobInstance.Coord))
                    {
                        continue;
                    }

                    var possibleMoveAction = GameInvariants.CanMoveTo(state, mob, coord);

                    if (possibleMoveAction.Type == UctActionType.Null)
                    {
                        continue;
                    }
                    Debug.Assert(possibleMoveAction.Type == UctActionType.Move);

                    foreach (var abilityId in mobInfo.Abilities)
                    {
                        if (!GameInvariants.IsAbilityUsableFrom(state, mob, coord, target, abilityId))
                        {
                            continue;
                        }

                        int myDistance = state.Pathfinder.Distance(myCoord, coord);

                        if (!closestCoord.HasValue)
                        {
                            chosenAbilityId = abilityId;
                            closestCoord    = coord;
                            distance        = myDistance;
                        }
                        else if (distance.Value > myDistance)
                        {
                            chosenAbilityId = abilityId;
                            closestCoord    = coord;
                            distance        = myDistance;
                        }
                    }
                }

                if (closestCoord.HasValue)
                {
                    if (Constants.AttackMoveEnabled)
                    {
                        var action = UctAction.AttackMoveAction(mob.MobId,
                                                                closestCoord.Value,
                                                                chosenAbilityId.Value,
                                                                target.MobId);

                        var after = ActionEvaluator.F(state, action.ToPureMove());
                        GameInvariants.AssertValidAbilityUseAction(after, action.ToPureAbilityUse());

                        GameInvariants.AssertValidAction(state, action);

                        result.Add(action);
                    }
                    else
                    {
                        var action = UctAction.MoveAction(mob.MobId, closestCoord.Value);
                        GameInvariants.AssertValidAction(state, action);

                        result.Add(action);
                    }
                }
            }
        }
Exemplo n.º 10
0
        /// <summary>
        /// Calculates an action according to a simple default policy. Used mainly
        /// in MCTS playouts.
        /// </summary>
        public static UctAction DefaultPolicyAction(GameInstance state)
        {
            var mobId = state.CurrentMob;

            if (mobId == null)
            {
                throw new InvalidOperationException("Requesting mob action when there is no current mob.");
            }

            Debug.Assert(state.State.MobInstances[mobId.Value].Hp > 0, "Current mob is dead");

            var mob = state.CachedMob(mobId.Value);

            if (mob.MobInstance.Ap == 0)
            {
                return(UctAction.EndTurnAction());
            }

            var abilityIds = new List <int>();

            foreach (var possibleAbilityId in mob.MobInfo.Abilities)
            {
                if (GameInvariants.IsAbilityUsableNoTarget(state, mobId.Value, possibleAbilityId))
                {
                    abilityIds.Add(possibleAbilityId);
                }
            }

            int moveTargetId = MobInstance.InvalidId;

            var actions = new List <UctAction>();

            foreach (var possibleTargetId in state.MobManager.Mobs)
            {
                var possibleTarget = state.CachedMob(possibleTargetId);

                moveTargetId = possibleTargetId;

                if (!GameInvariants.IsTargetable(state, mob, possibleTarget))
                {
                    continue;
                }

                if (abilityIds.Count == 0)
                {
                    continue;
                }

                foreach (var abilityId in abilityIds)
                {
                    if (GameInvariants.IsAbilityUsableApRangeCheck(state, mob, possibleTarget, abilityId))
                    {
                        actions.Add(UctAction.AbilityUseAction(abilityId, mob.MobId, possibleTargetId));
                    }
                }
            }

            if (actions.Count > 0)
            {
                return(MaxAbilityRatio(state, actions));
            }

            if (moveTargetId != MobInstance.InvalidId)
            {
                return(PickMoveTowardsEnemyAction(state, state.CachedMob(mobId.Value),
                                                  state.CachedMob(moveTargetId)));
            }
            else
            {
                Utils.Log(LogSeverity.Error, nameof(ActionGenerator), "No targets, game should be over");

                throw new InvalidOperationException("No targets, game should be over.");
            }
        }