public bool Push(ObservedTurnInfo turnInfo) { if (!IsLive) { return(false); } if (_queue.Count >= Settings.MaxObserverQueue) { IsLive = false; _queue = null; return(false); } _queue.Enqueue(turnInfo); return(true); }
public ObservedGameInfo ObserveNextTurn(Observer observer) { lock (_liveLock) { checkRunState(); ObserverQueue q = findObserver(observer.ObserverId); ObservedTurnInfo ot = q.Pop(); if (ot == null && q.IsLive) { Monitor.Wait(_liveLock, Settings.ObserverPollTimeoutMillis); ot = q.Pop(); } ObservedGameInfo gi = new ObservedGameInfo { GameId = GameId, GameState = State, QueuedTurns = (q.IsLive) ? q.Count : -1, TurnInfo = ot }; return(gi); } }
public bool Push(ObservedTurnInfo turnInfo) { lock (_lock) { if (!IsLive) { return(false); } if (_queue.Count >= Settings.MaxObserverQueue) { IsLive = false; _queue = null; return(false); } _queue.Enqueue(turnInfo); if (_queue.Count == 1) { Monitor.PulseAll(_lock); } return(true); } }
public ObservedGameInfo ObserveNextTurn(Observer observer) { ObserverQueue queue; List <int> lags = new List <int>(); DateTime now = DateTime.Now; lock (_liveLock) { CheckRunState(); queue = FindObserver(observer.ObserverId); for (int i = 0; i < _playerStates.Length; i++) { if (_playerStates[i].TurnCompleted >= _gameTurnStarted) { continue; } double delayMillis = (now - _turnStart).TotalSeconds; if (delayMillis > Settings.SlowTurnIntervalSeconds) { lags.Add(i); } } } int queueLength; ObservedTurnInfo ot = queue.Pop(out queueLength); // This has its own blocking ObservedGameInfo gi = new ObservedGameInfo { GameId = GameId, GameState = State, QueuedTurns = queueLength, TurnInfo = ot, SlowPlayers = lags }; return(gi); }
private void CompleteTurn() { if (StartNextTurnMaybe() == false) { return; } if (_playerStates.Any(t => t.IsActive && t.TurnCompleted < _gameTurnStarted)) { return; } var mapChanges = new List <MapChange>(); Point oldTecmanPos = _map.TecmanPosition; Point[] oldGhostPos = (Point[])_map.GhostPosition.Clone(); // Check individual actor moves for (int p = 0; p < _playerStates.Length; p++) { if (_playerStates[p].IsActive == false) { continue; } try { if (p == 0) { // --- Tecman checks // Position reporting if (_playerStates[p].TurnPositions == null || _playerStates[p].TurnPositions.Length != 1) { throw new RulesException("Tecman position specified incorrectly"); } Point position = _playerStates[p].TurnPositions[0]; // Movement distance and target MovementDirection direction = GetMovementDirection(oldTecmanPos, position); if (direction == MovementDirection.Invalid) { throw new RulesException("Invalid movement distance or direction"); } if (!_map.InBounds(position)) { throw new RulesException("Move outside of map"); } if (_map[position] == TileType.Wall) { throw new RulesException("Move into an obstacle"); } // Move is possible so we move _lastTecmanPosition = _map.TecmanPosition; _map.TecmanPosition = position; // Eat cookie if (_map[position] == TileType.Cookie) { _map[position, mapChanges] = TileType.Empty; _playerStates[p].Score++; } } else { // --- Ghost checks // Position reporting if (_playerStates[p].TurnPositions == null || _playerStates[p].TurnPositions.Length != oldGhostPos.Length) { throw new RulesException("Ghosts positions specified incorrectly"); } Point[] position = _playerStates[p].TurnPositions; // Check each ghost individually for (int ghost = 0; ghost < oldGhostPos.Length; ghost++) { // Movement distance and target MovementDirection direction = GetMovementDirection(oldGhostPos[ghost], position[ghost]); if (direction == MovementDirection.Invalid) { throw new RulesException($"Invalid movement distance or direction for ghost {ghost}"); } if (direction == MovementDirection.Stay) { throw new RulesException($"Ghosts must move but ghost {ghost} stayed"); } if (!_map.InBounds(position[ghost])) { throw new RulesException($"Move outside of map for ghost {ghost}"); } if (_map[position[ghost]] == TileType.Wall) { throw new RulesException($"Move into an obstacle for ghost {ghost}"); } // Movement direction if (position[ghost].Equals(_lastGhostPosition[ghost])) { // Going back is only possible in a dead-end int exits = 0; for (int dr = -1; dr <= 1; dr++) { for (int dc = -1; dc <= 1; dc++) { if (dr * dc != 0 || dr + dc == 0) { continue; } Point around = new Point((_map.GhostPosition[ghost].Row + dr + _map.Height) % _map.Height, (_map.GhostPosition[ghost].Col + dc + _map.Width) % _map.Width); if (_map[around] == TileType.Empty) { exits++; } } } if (exits > 1) { throw new RulesException($"Illegal direction reverse for ghost {ghost}"); } } _lastGhostPosition[ghost] = _map.GhostPosition[ghost]; // Move is possible so we move _map.GhostPosition[ghost] = position[ghost]; } // If ghosts survived, award them points _playerStates[p].Score = Settings.GameTurnLimit - _gameTurnStarted; } } catch (RulesException re) { _playerStates[p].Condition = PlayerCondition.Draw; _playerStates[p].Comment = re.Message; _proto.LogPlayerCondition(_playerStates[p]); continue; } } // Check fair game finish conditions if (_playerStates.Where(p => p.IsActive).Count() > 1) { // Tecman losing conditions if (_playerStates[0].IsActive) { // Direct capture of Tecman by sharing final position with a ghost if (_map.GhostPosition.Any(p => p.Equals(_map.TecmanPosition))) { _playerStates[0].Condition = PlayerCondition.Draw; _playerStates[0].Comment = "Tecman captured by ghosts"; _proto.LogPlayerCondition(_playerStates[0]); } else { // "In flight" capture when passing through each other for (int ghost = 0; ghost < oldGhostPos.Length; ghost++) { if (oldGhostPos[ghost].Equals(_map.TecmanPosition) && oldTecmanPos.Equals(_map.GhostPosition[ghost])) { _playerStates[0].Condition = PlayerCondition.Draw; _playerStates[0].Comment = "Tecman captured by ghosts when passing"; _proto.LogPlayerCondition(_playerStates[0]); break; } } } } // Ghost losing conditions if (_playerStates[1].IsActive) { // All cookies have been eaten bool haveCookies = false; foreach (TileType t in _map.Tiles) { if (t == TileType.Cookie) { haveCookies = true; break; } } if (!haveCookies) { _playerStates[1].Condition = PlayerCondition.Draw; _playerStates[1].Comment = "All cookies have been eaten"; _proto.LogPlayerCondition(_playerStates[1]); } } } // Handle game finish condition var activePlayers = _playerStates.Where(p => p.IsActive); if (activePlayers.Count() <= 1) { // Game finishes State = GameState.Finish; if (activePlayers.Count() == 1) { // We have a winner PlayerState winner = activePlayers.Single(); winner.Condition = PlayerCondition.Won; winner.Comment = "Congratulations !"; _proto.LogPlayerCondition(winner); foreach (PlayerState p in _playerStates) { if (p.Condition == PlayerCondition.Draw) { p.Condition = PlayerCondition.Lost; _proto.LogPlayerCondition(p); } } } // else we are in a draw } else if (_gameTurnStarted >= Settings.GameTurnLimit) { // Game finishes in a draw because of turn limit State = GameState.Finish; foreach (PlayerState p in _playerStates) { if (!p.IsActive) { continue; } p.Condition = PlayerCondition.Draw; p.Comment = "Game turn limit reached"; _proto.LogPlayerCondition(p); } } // Complete the turn _gameTurnEnded = _gameTurnStarted; _proto.LogGameTurnEnd(_gameTurnEnded); // Notify observers ObservedTurnInfo ot = new ObservedTurnInfo { Turn = _gameTurnEnded, GameState = State, PlayerStates = _playerStates.Select(p => new PlayerStateInfo(p)).ToArray(), MapChanges = mapChanges.ToArray(), TecmanPosition = _map.TecmanPosition, GhostPositions = (Point[])_map.GhostPosition.Clone() }; foreach (ObserverQueue queue in _observers) { queue.Push(ot); } // Continue StartNextTurnMaybe(); }
private void completeTurn() { if (!startNextTurnMaybe()) { return; } if (_pstates.Any(t => t.IsActive && t.TurnCompleted < _gameTurnStarted)) { return; } // Move all heads to new positions Point[] necks = _pstates.Select(p => p.Head).ToArray(); for (int p = 0; p < _pstates.Length; p++) { if (!_pstates[p].IsActive) { continue; } Point oldHead = _pstates[p].Head; Point newHead = _pstates[p].TurnPosition; bool good = true; string reason = "OK"; do { if (newHead.Equals(oldHead)) { good = false; reason = "Did not move"; break; } if (newHead.Row != oldHead.Row && newHead.Row != (oldHead.Row + 1) % _map.Height && newHead.Row != (oldHead.Row - 1 + _map.Height) % _map.Height) { good = false; reason = "Invalid row change"; break; } if (newHead.Col != oldHead.Col && newHead.Col != (oldHead.Col + 1) % _map.Width && newHead.Col != (oldHead.Col - 1 + _map.Width) % _map.Width) { good = false; reason = "Invalid col change"; break; } if (newHead.Row != oldHead.Row && newHead.Col != oldHead.Col) { good = false; reason = "Invalid move"; break; } if (_map.Tiles[newHead.Row, newHead.Col] != TileType.EMPTY) { good = false; reason = "Move to non-empty space"; break; } } while (false); if (!good) { _pstates[p].Condition = PlayerCondition.DRAW; _pstates[p].Comment = reason; _proto.LogPlayerCondition(_pstates[p]); continue; } _pstates[p].Head = newHead; } // Check for head-to-head collisions for (int i = 0; i < _pstates.Length - 1; i++) { if (!_pstates[i].IsActive) { continue; } for (int j = i + 1; j < _pstates.Length; j++) { if (!_pstates[j].IsActive) { continue; } if (!_pstates[i].Head.Equals(_pstates[j].Head)) { continue; } _pstates[i].Condition = PlayerCondition.DRAW; _pstates[i].Comment = "Head-to-head collision"; _proto.LogPlayerCondition(_pstates[i]); _pstates[j].Condition = PlayerCondition.DRAW; _pstates[j].Comment = "Head-to-head collision"; _proto.LogPlayerCondition(_pstates[j]); } } // Update map List <MapChange> mapChanges = new List <MapChange>(); for (int p = 0; p < _pstates.Length; p++) { Point head = _pstates[p].Head; Point neck = necks[p]; if (head.Equals(neck)) { continue; } changeTile(head.Row, head.Col, TileType.HEAD0 + (byte)p, mapChanges); changeTile(neck.Row, neck.Col, TileType.BODY0 + (byte)p, mapChanges); } // Check game finish condition int activeCount = _pstates.Count(p => p.IsActive); if (activeCount > 1) { // Game continues foreach (PlayerState p in _pstates) { if (p.Condition == PlayerCondition.DRAW) { p.Condition = PlayerCondition.LOST; _proto.LogPlayerCondition(p); } } } else { // Game finishes State = GameState.FINISH; if (activeCount == 1) { // We have a winner PlayerState winner = _pstates.First(p => p.IsActive); winner.Condition = PlayerCondition.WON; _proto.LogPlayerCondition(winner); foreach (PlayerState p in _pstates) { if (p.Condition == PlayerCondition.DRAW) { p.Condition = PlayerCondition.LOST; _proto.LogPlayerCondition(p); } } } } // Complete the turn _gameTurnEnded = _gameTurnStarted; _proto.LogGameTurnEnd(_gameTurnEnded); // Notify observers ObservedTurnInfo ot = new ObservedTurnInfo { Turn = _gameTurnEnded, GameState = State, PlayerStates = _pstates.Select(p => new PlayerStateInfo(p)).ToArray(), MapChanges = mapChanges.ToArray() }; bool haveObservers = false; foreach (ObserverQueue queue in _observers) { if (queue.Push(ot)) { haveObservers = true; } } if (haveObservers) { Monitor.PulseAll(_liveLock); } // Continue startNextTurnMaybe(); }