public Guid CreateGame(Guid player, Guid room) { using (var context = new MilleBornesEntities()) { var playerEntity = context.User .Where(us => us.LoggedInUser != null) .SingleOrDefault(us => us.LoggedInUser.Token == player); if (playerEntity == null) { throw new FaultException("Le joueur n'est pas valide ou est hors-ligne."); } var roomEntity = context.Room .SingleOrDefault(rm => rm.Token == room); if (roomEntity == null) { throw new FaultException("La salle n'est pas trouvée."); } if (roomEntity.MasterUserId != playerEntity.UserId) { throw new FaultException("Seul le maître de la salle peut démarrer la partie."); } var newGame = new Game() { Token = Guid.NewGuid(), StartDate = DateTime.UtcNow, IsTakingDisconnectDecision = false }; FillPlayerConfig(context, roomEntity, newGame); roomEntity.Game = newGame; roomEntity.Started = true; context.Game.Add(newGame); try { context.SaveChanges(); } catch (System.Data.Entity.Infrastructure.DbUpdateException dbue) { throw; } InitializeGame(newGame); try { context.SaveChanges(); } catch (System.Data.Entity.Infrastructure.DbUpdateException dbue) { throw; } return newGame.Token; } }
private static void FillPlayerConfig(MilleBornesEntities context, Room roomEntity, Game newGame) { // Essayer en premier lieu avec le service, sinon // regarder directement dans la base de données. List<PlayerGame> playerGameSource = new List<PlayerGame>(); #if false try { using (var client = new LobbyGameProxy.LobbyServiceClient()) { var configEntries = client.GetPlayerConfig(roomEntity.Token); foreach (var entry in configEntries) { var user = context.User .Where(us => us.LoggedInUser != null) .SingleOrDefault(us => us.LoggedInUser.Token == entry.UserToken); if (user == null) { continue; } var playerGame = new PlayerGame() { Game = newGame, UserId = user.UserId, User = user, LastHeartbeat = DateTime.UtcNow, Order = entry.Order, Team = entry.Team + 1, HasJoined = user.UserId == roomEntity.MasterUserId }; } } } #endif //catch (Exception ex) //{ playerGameSource = roomEntity.PlayerRoomState.Select((prs, inx) => new PlayerGame() { Game = newGame, UserId = prs.UserId, User = prs.User, LastHeartbeat = DateTime.UtcNow, Order = inx, Team = prs.Team + 1, HasJoined = prs.UserId == roomEntity.MasterUserId }) .ToList(); //} newGame.PlayerGame = playerGameSource; }
private void EndGame(Game game, GameEndReason reason) { game.EndDate = DateTime.UtcNow; game.EndToken = Guid.NewGuid(); game.EndReason = reason; }
private Tuple<int, List<DrawCardEvent>>[] GetAllPlayersHands(Game game) { var playerHands = game.GameEvent .Where(ge => ge.Type == GameEventType.DRAW_CARD) .Cast<DrawCardEvent>() .Where(dc => dc.PlayCardEvent.Count == 0) .GroupBy(dc => dc.PlayerId) .Select(g => Tuple.Create(g.Key, g.ToList())) .ToArray(); return playerHands; }
private bool CheckGameEnd(Game game) { // Finir la partie en gagnant. var teamStates = GetTeamsState(game); foreach (var teamState in teamStates) { if (teamState.DistanceTraveled == 1000) { EndGame(game, GameEndReason.WON_THOUSAND_MILES); return true; } } // Finir la partie en épuisant toutes les cartes. // (Dans la pile et celle dans les mains des joueurs) var playerHands = GetAllPlayersHands(game); if (playerHands.All(x => x.Item2.Count == 0)) { EndGame(game, GameEndReason.EXHAUSTED_DECK); return true; } return false; }
private void ChangePlayer(Game game, int newPlayerId) { game.GameEvent.Add(new PlayerChangeEvent() { ServerEvent = true, Date = DateTime.UtcNow, Type = GameEventType.PLAYER_CHANGE, NewPlayerId = newPlayerId }); }
private bool CanPlayCard(Game game, DrawCardEvent drawEvent, int playerTeamIndex, int targetTeamIndex) { if (targetTeamIndex == 0) { return true; } var targetTeamState = GetTeamState(game, targetTeamIndex); var card = CardDefinitions.Cards .Single(cd => cd.CardId == drawEvent.CardIndex); switch (card.CardType) { case CardType.VALUE: { if (playerTeamIndex != targetTeamIndex) { return false; } if (targetTeamState.CanGo && !targetTeamState.CurrentlyBrokenDown) { bool canPlayCard = true; if (targetTeamState.IsUnderSpeedLimit) { canPlayCard = card.Value <= 50; } if (!canPlayCard) { return false; } return (targetTeamState.DistanceTraveled + card.Value) <= 1000; } else { return false; } } case CardType.EFFECT_POSITIVE: { if (playerTeamIndex != targetTeamIndex) { return false; } switch (card.EffectType) { case EffectCardType.ACCIDENT: { return targetTeamState.HasAccident; } case EffectCardType.FUEL: { return targetTeamState.IsOutOfFuel; } case EffectCardType.TIRE: { return targetTeamState.HasFlatTire; } case EffectCardType.SPEED_LIMIT: { return targetTeamState.IsUnderSpeedLimit; } case EffectCardType.TRAFFIC_LIGHT: { return !targetTeamState.CanGo && !targetTeamState.CurrentlyBrokenDown; } case EffectCardType.NONE: default: { return false; } } } case CardType.EFFECT_NEGATIVE: { if (playerTeamIndex == targetTeamIndex) { return false; } switch (card.EffectType) { case EffectCardType.ACCIDENT: { return !targetTeamState.CurrentlyBrokenDown && !targetTeamState.InvincibleToAccidents; } case EffectCardType.FUEL: { return !targetTeamState.CurrentlyBrokenDown && !targetTeamState.InvincibleToFuel; } case EffectCardType.TIRE: { return !targetTeamState.CurrentlyBrokenDown && !targetTeamState.InvincibleToTire; } case EffectCardType.SPEED_LIMIT: { return !targetTeamState.IsUnderSpeedLimit && !targetTeamState.InvinciblePriority; } case EffectCardType.TRAFFIC_LIGHT: { return targetTeamState.CanGo && !targetTeamState.InvinciblePriority; } case EffectCardType.NONE: default: { return false; } } } case CardType.EFFECT_INVINCIBLE: { switch (card.EffectType) { case EffectCardType.ACCIDENT: { return !targetTeamState.InvincibleToAccidents; } case EffectCardType.FUEL: { return !targetTeamState.InvincibleToFuel; } case EffectCardType.TIRE: { return !targetTeamState.InvincibleToTire; } case EffectCardType.SPEED_LIMIT | EffectCardType.TRAFFIC_LIGHT: { return !targetTeamState.InvinciblePriority; } case EffectCardType.NONE: default: { return false; } } } default: { return false; } } }
private void DrawInitialHands(Game game) { Random random = new Random(); var remainingCards = CardDefinitions.Deck.ToList(); foreach (var player in GetPlayersInGame(game)) { for (int i = 0; i < 5; i++) { int listIndex = random.Next(remainingCards.Count); DrawCard(game, player.User, remainingCards[listIndex]); remainingCards.RemoveAt(listIndex); } } }
private TeamState GetTeamState(Game game, int teamIndex) { if (teamIndex == 0) { return null; } var teams = game.PlayerGame .Select(pg => pg.Team) .Distinct() .ToArray(); if (!teams.Contains(teamIndex)) { return null; } var cardsPlayedOnTeam = game.GameEvent .Where(ge => ge.Type == GameEventType.PLAY_CARD) .Cast<PlayCardEvent>() .Where(pc => pc.TargetTeamIndex == teamIndex) .ToList() .Join( CardDefinitions.Cards, pc => pc.DrawCardEvent.CardIndex, cd => cd.CardId, (pc, cd) => Tuple.Create(pc, cd) ) .OrderByDescending(x => x.Item1.GameEventId); var teamState = new TeamState() { TeamIndex = teamIndex }; teamState.HasAccident = IsTeamUnderEffect( cardsPlayedOnTeam, EffectCardType.ACCIDENT ); teamState.IsOutOfFuel = IsTeamUnderEffect( cardsPlayedOnTeam, EffectCardType.FUEL ); teamState.HasFlatTire = IsTeamUnderEffect( cardsPlayedOnTeam, EffectCardType.TIRE ); teamState.IsUnderSpeedLimit = IsTeamUnderEffect( cardsPlayedOnTeam, EffectCardType.SPEED_LIMIT ); ComputePlayedCardEffects(cardsPlayedOnTeam, teamState); bool accidentPlayed = teamState.PlayedCardEffects.HasFlag(EffectCardType.ACCIDENT); bool outOfFuelPlayed = teamState.PlayedCardEffects.HasFlag(EffectCardType.FUEL); bool flatTirePlayed = teamState.PlayedCardEffects.HasFlag(EffectCardType.TIRE); // Une équipe peut partir si elle a joué un feu vert. // Elle doit aussi, pour chaque effet dans [accident, essence, pneus], // avoir joué la carte qui la contre si une carte d'effet négatif // a été jouée contre l'équipe (les trois ifs). // // Le booléen détermine aussi si une carte feu vert peut être jouée, // les ifs évitent qu'un feu vert peut être joué lorsqu'il y a bris // non-réglé. // teamState.CanGo = IsTeamUnderEffect( cardsPlayedOnTeam, EffectCardType.TRAFFIC_LIGHT ); if (accidentPlayed) { if (teamState.HasAccident) { teamState.CanGo = false; } } if (outOfFuelPlayed) { if (teamState.IsOutOfFuel) { teamState.CanGo = false; } } if (flatTirePlayed) { if (teamState.HasFlatTire) { teamState.CanGo = false; } } // Une équipe ne peut partir immédiatement après un bris. if (teamState.CanGo) { var cardsPlayedOnTeamWithOrder = cardsPlayedOnTeam .OrderBy(tu => tu.Item1.GameEventId) .Select((tu, inx) => Tuple.Create(tu.Item1, tu.Item2, inx)); var lastAccident = GetLastCardEffectPlayedForTeam( cardsPlayedOnTeamWithOrder, EffectCardType.ACCIDENT ); var lastFuel = GetLastCardEffectPlayedForTeam( cardsPlayedOnTeamWithOrder, EffectCardType.FUEL ); var lastTire = GetLastCardEffectPlayedForTeam( cardsPlayedOnTeamWithOrder, EffectCardType.TIRE ); var lastLight = GetLastCardEffectPlayedForTeam( cardsPlayedOnTeamWithOrder, EffectCardType.TRAFFIC_LIGHT ); if (lastAccident != null && lastAccident.Item3 > lastLight.Item3) { teamState.CanGo = false; } if (lastFuel != null && lastFuel.Item3 > lastLight.Item3) { teamState.CanGo = false; } if (lastTire != null && lastTire.Item3 > lastLight.Item3) { teamState.CanGo = false; } } teamState.IsBrokenDown = teamState.CurrentlyBrokenDown; teamState.DistanceTraveled = cardsPlayedOnTeam .Where(x => x.Item2.CardType == CardType.VALUE) .Sum(x => x.Item2.Value); CalculateInvincibility(cardsPlayedOnTeam, teamState); return teamState; }
private TeamState[] GetTeamsState(Game game) { var teams = game.PlayerGame .Select(pg => pg.Team) .Distinct() .ToArray(); TeamState[] teamStates = new TeamState[teams.Length]; int index = 0; foreach (var team in teams) { teamStates[index] = GetTeamState(game, team); index++; } return teamStates; }
private User GoToNextPlayer(Game game) { var playerHands = GetAllPlayersHands(game); if (playerHands.All(x => x.Item2.Count == 0)) { throw new Exception("Impossible de passer au prochain joueur comme toutes les cartes ont été jouées."); } var players = GetPlayersInGame(game) .OrderBy(pg => pg.Order) .ToArray(); var playersWithHands = players .Join(playerHands, pg => pg.UserId, x => x.Item1, (pg, x) => Tuple.Create(pg, x.Item2) ) .ToArray(); var currentPlayerId = GetCurrentPlayerUserId(game); var newPlayerIndex = players .Select((pg, inx) => new { pg, inx }) .Single(x => x.pg.UserId == currentPlayerId) .inx; newPlayerIndex = (newPlayerIndex + 1) % playersWithHands.Length; var newPlayer = playersWithHands[newPlayerIndex]; // Trouver un joueur qui a des cartes à jouer, sinon le jeu va // attendre qu'il joue une carte dans une main vide. while (newPlayer.Item2.Count == 0) { newPlayerIndex = (newPlayerIndex + 1) % playersWithHands.Length; newPlayer = playersWithHands[newPlayerIndex]; } ChangePlayer(game, newPlayer.Item1.UserId); return newPlayer.Item1.User; }
private User GetCurrentPlayer(Game game) { var lastPlayerChangeEvent = GetLastPlayerChangeEvent(game); return lastPlayerChangeEvent.User; }
private int GetCurrentPlayerUserId(Game game) { var lastPlayerChangeEvent = GetLastPlayerChangeEvent(game); return lastPlayerChangeEvent.NewPlayerId; }
private PlayerChangeEvent GetLastPlayerChangeEvent(Game game) { return game.GameEvent .Where(ge => ge.Type == GameEventType.PLAYER_CHANGE) .Cast<PlayerChangeEvent>() .OrderByDescending(ge => ge.GameEventId) .FirstOrDefault(); }
/// <summary> /// Obtient les joueurs qui sont connectés, soit ceux que leur ordre est positif. /// /// Les joueurs déconnectés dans une partie qui continue ont comme ordre /// la valeur négative de celle-ci. /// </summary> /// <param name="game"></param> /// <returns></returns> private IEnumerable<PlayerGame> GetPlayersInGame(Game game) { return game.PlayerGame .Where(pg => pg.Order >= 0); }
private void DrawCard(Game game, User user, int cardId) { game.GameEvent.Add(new DrawCardEvent() { ServerEvent = true, Date = DateTime.UtcNow, Type = GameEventType.DRAW_CARD, CardIndex = cardId, Token = Guid.NewGuid(), User = user }); }
private void InitializeGame(Game game) { DrawInitialHands(game); var firstPlayer = GetPlayersInGame(game) .OrderBy(pg => pg.Order) .FirstOrDefault(); ChangePlayer(game, firstPlayer.UserId); }
private void PlayCardInGame(Game game, DrawCardEvent drawEvent, int targetTeamIndex) { game.GameEvent.Add(new PlayCardEvent() { ServerEvent = false, Date = DateTime.UtcNow, Type = GameEventType.PLAY_CARD, TargetTeamIndex = targetTeamIndex, DrawCardEventId = drawEvent.GameEventId }); }
private bool CheckAllPlayersHeartbeat(Game game) { if (GetPlayersInGame(game) .Where(pg => pg.HasJoined) .Any(pg => !ValidatePlayerHeartbeat(pg))) { // S'assurer qu'il reste plus d'un joueur, sinon terminer la partie. if (GetPlayersInGame(game) .Where(pg => pg.HasJoined) .Where(pg => ValidatePlayerHeartbeat(pg)) .Count() <= 1) { EndGame(game, GameEndReason.PLAYER_DISCONNECTION); return false; } var masterPlayerId = game.Room.First().MasterUserId; // S'assurer que le maître de la salle est présent pour prendre // la décision, sionon terminer la partie aussi. if (GetPlayersInGame(game) .Where(pg => pg.HasJoined) .Where(pg => ValidatePlayerHeartbeat(pg)) .Where(pg => pg.UserId == masterPlayerId) .Count() != 1) { EndGame(game, GameEndReason.PLAYER_DISCONNECTION); return false; } game.IsTakingDisconnectDecision = true; return false; } return true; }
private int[] GetRemainingCards(Game game) { var deck = CardDefinitions.Deck.ToList(); var playedCards = game.GameEvent .Where(ge => ge.Type == GameEventType.DRAW_CARD) .Cast<DrawCardEvent>(); foreach (var card in playedCards) { deck.Remove(card.CardIndex); } return deck.ToArray(); }
private void DrawNewCardToPlayer(Game game, User user) { var remainingDeck = GetRemainingCards(game); if (remainingDeck.Length <= 0) { return; } Random random = new Random(); int listIndex = random.Next(remainingDeck.Length); DrawCard(game, user, remainingDeck[listIndex]); }