Esempio n. 1
0
 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);
 }
Esempio n. 2
0
 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);
     }
 }
Esempio n. 4
0
        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);
        }
Esempio n. 5
0
        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();
        }
Esempio n. 6
0
        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();
        }