private bool ProcessChickenUnitMoves(IList <ChickenUnit> aliveChickens) { //// TODO: [vmcl] Use bisection to get conflicting units closer to each other //// TODO: [vmcl] Optimize number of collision checks! //// TODO: [vmcl] Divide move: eg. unit couldn't move but could turn beak or vice versa _moveInfoStates.Clear(); for (var unitIndex = 0; unitIndex < aliveChickens.Count; unitIndex++) { if (IsStopping()) { return(false); } var unit = aliveChickens[unitIndex]; _moveInfoStates[unit] = MoveInfoStates.Handled; var moveInfo = _moveInfos.GetValueOrDefault(unit); if (moveInfo == null) { continue; } DebugHelper.WriteLine( "{0} is processing move {{{1}}} of chicken {{{2}}}.", GetType().Name, moveInfo, unit); var movementAndNewPosition = GameHelper.GetMovementAndNewPosition( unit.Position, unit.BeakAngle, moveInfo.MoveDirection, GameConstants.ChickenUnit.DefaultRectilinearSpeed); var beakMovementAndNewAngle = GameHelper.GetBeakMovementAndNewAngle(unit.BeakAngle, moveInfo.BeakTurn); var newPositionElement = new ChickenElement( movementAndNewPosition.Position, beakMovementAndNewAngle.Position); if (HasOutOfBoardCollision(newPositionElement)) { _moveInfoStates[unit] = MoveInfoStates.RejectedBoardCollision; DebugHelper.WriteLine( "Blocked collision of chicken {{{0}}} with game board border.", unit); continue; } ChickenUnit conflictingChicken = null; // ReSharper disable once LoopCanBeConvertedToQuery // ReSharper disable once ForCanBeConvertedToForeach for (var conflictingIndex = 0; conflictingIndex < aliveChickens.Count; conflictingIndex++) { var aliveChicken = aliveChickens[conflictingIndex]; if (aliveChicken == unit) { continue; } if (CollisionDetector.CheckCollision(newPositionElement, aliveChicken.GetElement())) { conflictingChicken = aliveChicken; break; } } if (conflictingChicken != null) { _moveInfoStates[unit] = MoveInfoStates.RejectedOtherUnitCollision; DebugHelper.WriteLine( "Blocked collision of chicken {{{0}}} with {{{1}}}.", unit, conflictingChicken); continue; } unit.SetMovement(movementAndNewPosition.Movement, beakMovementAndNewAngle.Movement); DebugHelper.WriteLine("Chicken {{{0}}} has moved.", unit); } return(true); }
private void ProcessEngineStep() { var aliveChickens = this.AliveChickens .Where(item => !item.IsDead && item.LogicExecutor.Error == null) .ToList(); _moveInfos.Clear(); _previousMoves.Clear(); for (var logicIndex = 0; logicIndex < _logicExecutors.Count; logicIndex++) { var logic = _logicExecutors[logicIndex]; lock (logic.UnitsMovesLock) { foreach (var pair in logic.UnitsMoves) { _moveInfos.Add(pair.Key, pair.Value); _previousMoves.Add(pair.Key, pair.Value); } } } if (!ProcessChickenUnitMoves(aliveChickens)) { return; } // Processing new shot units var shootingMoves = _moveInfos.Where(item => !item.Key.IsDead && item.Value.FireMode != FireMode.None); ProcessNewShots(shootingMoves); #region Processing Shot Collisions foreach (var shotUnit in _shotUnitsDirect) { var movement = GameHelper.GetMovement( shotUnit.Angle, MoveDirection.MoveForward, GameConstants.ShotUnit.DefaultRectilinearSpeed); shotUnit.SetMovement(movement); DebugHelper.WriteLine("Shot {{{0}}} has moved.", shotUnit); var hasOutOfBoardCollision = HasOutOfBoardCollision(shotUnit.GetElement()); if (hasOutOfBoardCollision) { shotUnit.Exploded = true; DebugHelper.WriteLine("Shot {{{0}}} has exploded outside of game board.", shotUnit); } } var injuredChickens = new List <ChickenUnit>(aliveChickens.Count); for (var index = 0; index < _shotUnitsDirect.Count; index++) { var shotUnit = _shotUnitsDirect[index]; for (var otherIndex = index + 1; otherIndex < _shotUnitsDirect.Count; otherIndex++) { var otherShotUnit = _shotUnitsDirect[otherIndex]; var isCollision = CollisionDetector.CheckCollision( shotUnit.GetElement(), otherShotUnit.GetElement()); if (!isCollision) { continue; } shotUnit.Exploded = true; otherShotUnit.Exploded = true; DebugHelper.WriteLine( "Mutual annihilation of shots {{{0}}} and {{{1}}}.", shotUnit, otherShotUnit); } var shotElement = shotUnit.GetElement(); injuredChickens.Clear(); // ReSharper disable once LoopCanBeConvertedToQuery // ReSharper disable once ForCanBeConvertedToForeach for (var chickenIndex = 0; chickenIndex < aliveChickens.Count; chickenIndex++) { var aliveChicken = aliveChickens[chickenIndex]; if (!aliveChicken.IsDead && CollisionDetector.CheckCollision(shotElement, aliveChicken.GetElement())) { injuredChickens.Add(aliveChicken); } } foreach (var injuredChicken in injuredChickens) { shotUnit.Exploded = true; //// TODO [vmcl] Move out of loop injuredChicken.IsDead = true; injuredChicken.KilledBy = shotUnit.Owner; var suicide = shotUnit.Owner == injuredChicken; if (!suicide) { shotUnit.Owner.KillCount++; } DebugHelper.WriteLine( "Shot {{{0}}} has exploded and killed {{{1}}}{2}.", shotUnit, injuredChicken, suicide ? " [suicide]" : string.Empty); } } #endregion UpdateLastGamePresentation(); _aliveChickensDirect.RemoveAll(item => item.IsDead); _aliveChickensDirect.DoForEach(item => item.ApplyMovement()); _shotUnitsDirect.RemoveAll(item => item.Exploded); _shotUnitsDirect.DoForEach(item => item.ApplyMovement()); var aliveTeams = _aliveChickensDirect.Select(item => item.Team).Distinct().ToList(); if (aliveTeams.Count > 1) { return; } _finalizingStage = true; if (_shotUnitsDirect.Any()) { return; } var winningTeam = aliveTeams.SingleOrDefault(); ChickenUnitLogic winningLogic; switch (winningTeam) { case GameTeam.Light: winningLogic = _lightTeamLogicExecutor.Logic; break; case GameTeam.Dark: winningLogic = _darkTeamLogicExecutor.Logic; break; default: winningLogic = null; break; } RaiseGameEnded(winningTeam, winningLogic); }