public void StateDeepCopyTest() { var game = new GameInstance(3); var ability = new AbilityInfo(3, 1, 1, 0); var abilityId = game.AddAbilityWithInfo(ability); var abilities1 = new List <int>(); var abilities2 = new List <int> { abilityId }; var info1 = new MobInfo(TeamColor.Red, 5, 1, 0, abilities1); var info2 = new MobInfo(TeamColor.Blue, 5, 1, 1, abilities2); var m1 = game.AddMobWithInfo(info1); var m2 = game.AddMobWithInfo(info2); game.PrepareEverything(); var copy = game.CopyStateOnly(); TestHelpers.GameInstancesEqual(game, copy); var copy2 = copy.CopyStateOnly(); ActionEvaluator.F(copy2, UctAction.AbilityUseAction(abilityId, m1, m2)); TestHelpers.GameInstancesEqual(game, copy); TestHelpers.MobManagersEqual(game.MobManager, copy2.MobManager); TestHelpers.MapsEqual(game.Map, copy2.Map); }
public void BasicIniciativeTest() { var game = new GameInstance(3); var m2 = game.AddMobWithInfo(new MobInfo(TeamColor.Blue, 10, 10, 2, new List <int>())); var m3 = game.AddMobWithInfo(new MobInfo(TeamColor.Blue, 10, 10, 3, new List <int>())); var m4 = game.AddMobWithInfo(new MobInfo(TeamColor.Red, 10, 10, 4, new List <int>())); var m1 = game.AddMobWithInfo(new MobInfo(TeamColor.Red, 10, 10, 1, new List <int>())); game.PrepareEverything(); Assert.AreEqual(game.CurrentMob, m1); ActionEvaluator.FNoCopy(game, UctAction.EndTurnAction()); Assert.AreEqual(game.CurrentMob, m2); ActionEvaluator.FNoCopy(game, UctAction.EndTurnAction()); Assert.AreEqual(game.CurrentMob, m3); ActionEvaluator.FNoCopy(game, UctAction.EndTurnAction()); Assert.AreEqual(game.CurrentMob, m4); // At this point a new turn should start ActionEvaluator.FNoCopy(game, UctAction.EndTurnAction()); Assert.AreEqual(game.CurrentMob, m1); ActionEvaluator.FNoCopy(game, UctAction.EndTurnAction()); Assert.AreEqual(game.CurrentMob, m2); ActionEvaluator.FNoCopy(game, UctAction.EndTurnAction()); Assert.AreEqual(game.CurrentMob, m3); ActionEvaluator.FNoCopy(game, UctAction.EndTurnAction()); Assert.AreEqual(game.CurrentMob, m4); }
public static void AssertValidAction(GameInstance game, UctAction action) { switch (action.Type) { case UctActionType.Null: break; case UctActionType.EndTurn: break; case UctActionType.Move: case UctActionType.DefensiveMove: AssertValidMoveAction(game, action); break; case UctActionType.AbilityUse: AssertValidAbilityUseAction(game, action); break; case UctActionType.AttackMove: AssertValidMoveAction(game, action); var afterMove = ActionEvaluator.F(game, action.ToPureMove()); AssertValidAbilityUseAction(afterMove, action.ToPureAbilityUse()); break; } }
public void NodeActionComputeTest() { var game = new GameInstance(3); var ability = new AbilityInfo(3, 1, 1, 0); var abilityId = game.AddAbilityWithInfo(ability); var abilities1 = new List <int>(); var abilities2 = new List <int> { abilityId }; var info1 = new MobInfo(TeamColor.Red, 5, 1, 0, abilities1); var info2 = new MobInfo(TeamColor.Blue, 5, 1, 1, abilities2); var m1 = game.AddMobWithInfo(info1); var m2 = game.AddMobWithInfo(info2); game.PlaceMob(m1, new AxialCoord(1, 1)); game.PlaceMob(m2, new AxialCoord(-1, -1)); game.PrepareEverything(); Assert.IsTrue(game.CurrentMob.HasValue); Assert.AreEqual(m1, game.CurrentMob.Value); var firstNode = new UctNode(0, 0, UctAction.NullAction(), game); firstNode.PrecomputePossibleActions(true, true); Assert.AreEqual(3, firstNode.PossibleActions.Count); }
public static void AssertAndRecord(GameInstance game, UctAction action, bool condition, string message) { if (!condition) { ReplayRecorder.Instance.SaveAndClear(game, 0); throw new InvariantViolationException($"Check failed for action {action}, reason: {message}"); } }
public HistoryLogEntry(int actionIndex, TeamColor currentTeam, UctAction action, CachedMob mob, CachedMob target, AbilityInfo abilityInfo, int?moveCost, AssetManager assetManager) { _actionIndex = actionIndex; _assetManager = assetManager; Renderer = this; CurrentTeam = currentTeam; Message = BuildMessage(action, mob, target, abilityInfo, moveCost); }
public static void AssertValidMoveAction(GameInstance game, UctAction action) { var atCoord = game.State.AtCoord(action.Coord, true); var mobInstance = game.State.MobInstances[action.MobId]; var distance = game.Pathfinder.Distance(mobInstance.Coord, action.Coord); AssertAndRecord(game, action, mobInstance.Ap >= distance, "mobInstance.Ap >= distance"); AssertAndRecord(game, action, game.Map[action.Coord] == HexType.Empty, "Trying to move into a wall"); AssertAndRecord(game, action, atCoord != action.MobId, "Trying to move into the coord you're already standing on."); AssertAndRecord(game, action, atCoord == null, "Trying to move into a mob."); }
public async Task SlowActionApplied(UctAction action) { if (action.Type == UctActionType.Move) { await SlowEventMobMoved(action.MobId, action.Coord); } else if (action.Type == UctActionType.AbilityUse) { await SlowEventAbilityUsed(action.MobId, action.TargetId, _game.MobManager.Abilities[action.AbilityId]); } else { throw new ArgumentException( $"{nameof(GameBoardController)} only supports {nameof(UctActionType.Move)} and {nameof(UctActionType.AbilityUse)} actions."); } }
public void IsFinishedFastAutoUpdateTest() { var game = new GameInstance(3); var a1 = game.AddAbilityWithInfo(new AbilityInfo(5, 1, 5, 0)); var m1 = game.AddMobWithInfo(new MobInfo(TeamColor.Red, 1, 10, 0, new[] { a1 })); var m2 = game.AddMobWithInfo(new MobInfo(TeamColor.Blue, 1, 10, 0, new[] { a1 })); game.PrepareEverything(); Assert.IsFalse(game.IsFinished); ActionEvaluator.FNoCopy(game, UctAction.AbilityUseAction(a1, m1, m2)); Assert.IsTrue(game.IsFinished); }
public static UctAction CanMoveTo(GameInstance game, CachedMob mob, AxialCoord coord) { bool isEmpty = game.Map[coord] == HexType.Empty && game.State.AtCoord(coord, true) == null; int distance = game.Pathfinder.Distance(mob.MobInstance.Coord, coord); int remainingAp = mob.MobInstance.Ap - distance; bool enoughAp = remainingAp >= 0; if (isEmpty && enoughAp) { return(UctAction.MoveAction(mob.MobId, coord)); } else { return(UctAction.NullAction()); } }
public static GameInstance FNoCopy(GameInstance state, UctAction action) { Actions++; ActionCounts[action.Type]++; Constants.WriteLogLine(action); if (Constants.RecordReplays) { ReplayRecorder.Instance.Actions.Add(action); } GameInvariants.AssertValidAction(state, action); switch (action.Type) { case UctActionType.Null: // do nothing break; case UctActionType.EndTurn: state.State.LastTeamColor = state.CurrentTeam; state.TurnManager.NextMobOrNewTurn(); break; case UctActionType.AbilityUse: FastUse(state, action.AbilityId, action.MobId, action.TargetId); break; case UctActionType.AttackMove: FastMove(state, action.MobId, action.Coord); FastUse(state, action.AbilityId, action.MobId, action.TargetId); break; case UctActionType.Move: case UctActionType.DefensiveMove: FastMove(state, action.MobId, action.Coord); break; default: throw new InvalidOperationException($"Invalid value of {action.Type}"); } return(state); }
private void AttackMob(int targetId) { Debug.Assert(SelectedAbilityIndex != null, "_gameInstance.TurnManager.SelectedAbilityIndex != null"); var abilityIndex = SelectedAbilityIndex.Value; var mobId = _game.CurrentMob; Debug.Assert(mobId != null); var abilityId = _game.MobManager.MobInfos[mobId.Value].Abilities[abilityIndex]; var visibilityPath = _game.Map.AxialLinedraw(_game.State.MobInstances[mobId.Value].Coord, _game.State.MobInstances[targetId].Coord); bool isVisible = true; foreach (var coord in visibilityPath) { if (_game.Map[coord] == HexType.Wall) { isVisible = false; } } if (isVisible) { bool withinRange = (visibilityPath.Count - 1) <= _game.MobManager.Abilities[abilityId].Range; if (withinRange) { InputManager.Instance.UserInputEnabled = false; _eventHub.SlowPlayAction(_game, UctAction.AbilityUseAction(abilityId, mobId.Value, targetId)) .ContinueWith(t => { InputManager.Instance.UserInputEnabled = true; }) .LogContinuation(); } else { ShowMessage("Target is outside the range of the currently selected ability."); } } else { ShowMessage("The target is not visible."); } }
public HistoryLog(SpriteFont font, int width, AssetManager assetManager) { _font = font; _width = width; _assetManager = assetManager; _childrenPlaceholder = AddChild(new VerticalLayout()); _childrenPlaceholder.Position = _textOffset; _childrenPlaceholder.Renderer = new SpriteRenderer(_assetManager[AssetManager.HistoryLogBg]); _childrenPlaceholder.Padding = new Vector4(35, 20, 20, 20); for (int i = 0; i < MaxHistorySize; i++) { var entry = new HistoryLogEntry(-1, TeamColor.Red, UctAction.NullAction(), null, null, null, null, assetManager); _log.Add(entry); _childrenPlaceholder.AddChild(entry); } }
public void DefaultPolicyTest() { var game = new GameInstance(3); var ability1 = new AbilityInfo(1, 1, 1, 0); var a1 = game.AddAbilityWithInfo(ability1); var ability2 = new AbilityInfo(3, 1, 1, 0); var a2 = game.AddAbilityWithInfo(ability2); var abilities1 = new List <int>(); var abilities2 = new List <int> { a1, a2 }; var info1 = new MobInfo(TeamColor.Red, 5, 1, 0, abilities1); var info2 = new MobInfo(TeamColor.Blue, 5, 1, 1, abilities2); game.AddMobWithInfo(info1); game.AddMobWithInfo(info2); game.PrepareEverything(); Assert.IsFalse(game.IsFinished); var uct = new UctAlgorithm(100); var result = UctAlgorithm.DefaultPolicy(game, TeamColor.Red); Assert.AreEqual(0, result); ActionEvaluator.FNoCopy(game, UctAction.EndTurnAction()); Assert.AreEqual(TeamColor.Blue, game.CurrentTeam); var bestAction = ActionGenerator.DefaultPolicyAction(game); Console.WriteLine($"Best: {bestAction}"); var node = uct.UctSearch(game); Console.WriteLine(node); }
public string BuildMessage(UctAction action, CachedMob mob, CachedMob target, AbilityInfo abilityInfo, int?moveCost) { if (action.Type == UctActionType.Null) { return(" "); } string str; switch (action.Type) { case UctActionType.AbilityUse: str = $"Did {abilityInfo.Dmg} damage for {abilityInfo.Cost} AP."; break; case UctActionType.AttackMove: str = $"Moved towards enemy for {moveCost} AP and did {abilityInfo.Dmg} damage for {abilityInfo.Cost} AP."; break; case UctActionType.Move: str = $"Moved from {mob.MobInstance.Coord} to {action.Coord} for {moveCost} AP."; break; case UctActionType.DefensiveMove: str = $"Is trying to hide at {action.Coord} for {moveCost} AP."; break; case UctActionType.EndTurn: throw new InvalidOperationException("End turn shouldn't be logged."); case UctActionType.Null: throw new InvalidOperationException("Null action should never be logged."); default: throw new ArgumentException($"Invalid action type ${action.Type}", nameof(action)); } return($"{string.Format("{0,3}", _actionIndex)}. {str}"); }
/// <summary> /// Asnychronously run a given action, notifying all of the subscribers. /// </summary> public async Task SlowPlayAction(GameInstance game, UctAction action) { Debug.Assert(game == _gameInstance, "instance == _gameInstance"); foreach (var subscriber in _subscribers) { subscriber.ActionApplied(action); } switch (action.Type) { case UctActionType.AbilityUse: await SlowBroadcastAction(action); break; case UctActionType.EndTurn: break; case UctActionType.AttackMove: GameInvariants.AssertValidMoveAction(_gameInstance, action); await SlowBroadcastAction(action.ToPureMove()); await SlowBroadcastAction(action.ToPureAbilityUse()); break; case UctActionType.DefensiveMove: case UctActionType.Move: GameInvariants.AssertValidMoveAction(_gameInstance, action); await SlowBroadcastAction(action.ToPureMove()); break; case UctActionType.Null: break; } }
public async Task <int> SlowMainLoop(Func <bool> turnEndFunc, Action gameFinishedFunc) { // Wait for GUI initialization await Task.Delay(TimeSpan.FromSeconds(1)); State = GameEventState.SettingUpTurn; var state = _gameInstance.State; _gameInstance.Reset(); int totalTurns = 0; state.SlowUpdateIsFinished(_gameInstance.MobManager); while (!_gameInstance.IsFinished) { while (IsPaused) { await Task.Delay(TimeSpan.FromMilliseconds(100)); } totalTurns++; await _gameInstance.CurrentController.SlowPlayTurn(this); ActionEvaluator.FNoCopy(_gameInstance, UctAction.EndTurnAction()); turnEndFunc(); await Task.Delay(200); } ReplayRecorder.Instance.SaveAndClear(_gameInstance); Console.WriteLine(Constants.GetLogBuffer()); gameFinishedFunc(); return(totalTurns); }
public static void AssertValidAbilityUseAction(GameInstance game, UctAction action) { var mobInstance = game.State.MobInstances[action.MobId]; var targetInstance = game.State.MobInstances[action.TargetId]; var abilityInfo = game.MobManager.Abilities[action.AbilityId]; AssertAndRecord(game, action, game.State.Cooldowns[action.AbilityId] == 0, "game.State.Cooldowns[action.AbilityId] == 0"); AssertAndRecord(game, action, mobInstance.Ap >= abilityInfo.Cost, "mobInstance.Ap >= abilityInfo.Cost"); AssertAndRecord(game, action, mobInstance.Hp > 0, $"Using an ability with {mobInstance.Hp}HP"); AssertAndRecord(game, action, targetInstance.Hp > 0, $"Using an ability on a target with {mobInstance.Hp}HP"); var isVisible = game.Map.IsVisible(mobInstance.Coord, targetInstance.Coord); AssertAndRecord(game, action, isVisible, "Target is not visible"); int distance = mobInstance.Coord.Distance(targetInstance.Coord); AssertAndRecord(game, action, abilityInfo.Range >= distance, "abilityInfo.Range >= mobInstance.Coord.Distance(targetInstance.Coord)"); }
public void Log(TeamColor currentTeam, UctAction action, CachedMob mob, CachedMob target, AbilityInfo abilityInfo, int?moveCost) { GameManager.CurrentSynchronizationContext.Post(_ => { var entry = new HistoryLogEntry(ActionCount++, currentTeam, action, mob, target, abilityInfo, moveCost, _assetManager); _log.Add(entry); _childrenPlaceholder.AddChild(entry); if (_log.Count > MaxHistorySize) { _log.RemoveAt(0); _childrenPlaceholder.Children.RemoveAt(0); } }, null); }
public void ActionApplied(UctAction action) { var mob = _game.CachedMob(action.MobId); var target = _game.CachedMob(action.TargetId); AbilityInfo abilityInfo = null; if (action.Type == UctActionType.AttackMove || action.Type == UctActionType.AbilityUse) { abilityInfo = _game.MobManager.Abilities[action.AbilityId]; } int?moveCost = null; if (action.Type == UctActionType.AttackMove || action.Type == UctActionType.DefensiveMove || action.Type == UctActionType.Move) { moveCost = _game.Pathfinder.Distance(mob.MobInstance.Coord, action.Coord); } Debug.Assert(_game.CurrentTeam != null, "_gameInstance.CurrentTeam != null"); HistoryLog.Instance.Log(_game.CurrentTeam.Value, action, mob, target, abilityInfo, moveCost); }
public static GameInstance F(GameInstance state, UctAction action) { return(FNoCopy(state.CopyStateOnly(), action)); }
public void FastPlayAction(UctAction action) { ActionEvaluator.FNoCopy(_gameInstance, action); }
private async Task SlowBroadcastAction(UctAction action) { await Task.WhenAll(_subscribers.Select(x => x.SlowActionApplied(action))); ActionEvaluator.FNoCopy(_gameInstance, action); }
private void HandleLeftClick() { var abilitySelected = SelectedAbilityIndex.HasValue; var mouseHex = Camera2D.Instance.MouseHex; var currentMob = _game.CurrentMob; if (_game.Pathfinder.IsValidCoord(mouseHex)) { var targetId = _game.State.AtCoord(mouseHex, true); if (targetId != null) { if (targetId == currentMob) { ShowMessage("You can't target yourself."); } else { var mobInfo = _game.MobManager.MobInfos[currentMob.Value]; var targetInstance = _game.State.MobInstances[targetId.Value]; var targetInfo = _game.MobManager.MobInfos[targetId.Value]; if (targetInstance.Hp == 0) { ShowMessage("This mob is already dead."); } else { if (mobInfo.Team == targetInfo.Team) { ShowMessage("You can't target your team."); } else if (SelectedAbilityIndex.HasValue) { if (abilitySelected) { AttackMob(targetId.Value); } else { ShowMessage("You can't move here."); } } } } } else { if (abilitySelected) { ShowMessage("You can't cast spells on the ground."); } else { var mobInstance = _game.State.MobInstances[currentMob.Value]; if (_game.Map[mouseHex] == HexType.Empty) { var distance = _game.Pathfinder.Distance(mobInstance.Coord, mouseHex); if (distance == int.MaxValue) { ShowMessage("Target is unreachable"); } else if (distance > mobInstance.Ap) { ShowMessage("You don't have enough AP."); } else { InputManager.Instance.UserInputEnabled = false; _eventHub.SlowPlayAction(_game, UctAction.MoveAction(currentMob.Value, mouseHex)) .ContinueWith(t => { InputManager.Instance.UserInputEnabled = true; }) .LogContinuation(); } } else { ShowMessage("You can't walk into a wall."); } } } } }