From 29d529da0e538974c049b103289f17328d09f3ce Mon Sep 17 00:00:00 2001 From: huesosRedkostniy Date: Tue, 15 Oct 2024 17:35:18 +0500 Subject: [PATCH 1/2] commit --- ConsoleApp/Program.cs | 10 ++++--- Game/Domain/GameEntity.cs | 10 ++++--- Game/Domain/MongoGameRepository.cs | 20 ++++++++------ Game/Domain/MongoUserRepositoty.cs | 43 ++++++++++++++++++++++-------- Game/Domain/Player.cs | 6 ++++- Game/Domain/UserEntity.cs | 6 +++++ Tests/MongoUserRepositoryShould.cs | 2 -- db.sln.DotSettings | 5 ++++ 8 files changed, 73 insertions(+), 29 deletions(-) diff --git a/ConsoleApp/Program.cs b/ConsoleApp/Program.cs index cf39300..a8fc761 100644 --- a/ConsoleApp/Program.cs +++ b/ConsoleApp/Program.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using Game.Domain; +using MongoDB.Driver; namespace ConsoleApp { @@ -8,12 +9,13 @@ class Program { private readonly IUserRepository userRepo; private readonly IGameRepository gameRepo; - private readonly Random random = new Random(); + private readonly Random random = new(); private Program(string[] args) { - userRepo = new InMemoryUserRepository(); - gameRepo = new InMemoryGameRepository(); + var db = new MongoClient("mongodb://localhost:27017").GetDatabase("game"); + userRepo = new MongoUserRepository(db); + gameRepo = new MongoGameRepository(db); } public static void Main(string[] args) @@ -184,4 +186,4 @@ private void ShowScore(GameEntity game) Console.WriteLine($"Score: {players[0].Name} {players[0].Score} : {players[1].Score} {players[1].Name}"); } } -} +} \ No newline at end of file diff --git a/Game/Domain/GameEntity.cs b/Game/Domain/GameEntity.cs index ec7b5ec..494bd8d 100644 --- a/Game/Domain/GameEntity.cs +++ b/Game/Domain/GameEntity.cs @@ -1,18 +1,21 @@ using System; using System.Collections.Generic; using System.Linq; +using MongoDB.Bson.Serialization.Attributes; namespace Game.Domain { public class GameEntity { + [BsonElement] private readonly List players; - + public GameEntity(int turnsCount) : this(Guid.Empty, GameStatus.WaitingToStart, turnsCount, 0, new List()) { } - + + [BsonConstructor] public GameEntity(Guid id, GameStatus status, int turnsCount, int currentTurnIndex, List players) { Id = id; @@ -30,7 +33,8 @@ public Guid Id } public IReadOnlyList Players => players.AsReadOnly(); - + + [BsonElement] public int TurnsCount { get; } public int CurrentTurnIndex { get; private set; } diff --git a/Game/Domain/MongoGameRepository.cs b/Game/Domain/MongoGameRepository.cs index 86873d4..e744dcd 100644 --- a/Game/Domain/MongoGameRepository.cs +++ b/Game/Domain/MongoGameRepository.cs @@ -7,39 +7,43 @@ namespace Game.Domain // TODO Сделать по аналогии с MongoUserRepository public class MongoGameRepository : IGameRepository { + private readonly IMongoCollection gameCollection; public const string CollectionName = "games"; - public MongoGameRepository(IMongoDatabase db) + public MongoGameRepository(IMongoDatabase database) { + gameCollection = database.GetCollection(CollectionName); } public GameEntity Insert(GameEntity game) { - throw new NotImplementedException(); + gameCollection.InsertOne(game); + return game; } public GameEntity FindById(Guid gameId) { - throw new NotImplementedException(); + return gameCollection.Find(x => x.Id == gameId).SingleOrDefault(); } public void Update(GameEntity game) { - throw new NotImplementedException(); + gameCollection.ReplaceOne(x => x.Id == game.Id, game); } // Возвращает не более чем limit игр со статусом GameStatus.WaitingToStart public IList FindWaitingToStart(int limit) { - //TODO: Используй Find и Limit - throw new NotImplementedException(); + return gameCollection.Find(x => x.Status == GameStatus.WaitingToStart) + .Limit(limit) + .ToList(); } // Обновляет игру, если она находится в статусе GameStatus.WaitingToStart public bool TryUpdateWaitingToStart(GameEntity game) { - //TODO: Для проверки успешности используй IsAcknowledged и ModifiedCount из результата - throw new NotImplementedException(); + var replaceResult = gameCollection.ReplaceOne(x => x.Id == game.Id && x.Status == GameStatus.WaitingToStart, game); + return replaceResult.IsAcknowledged && replaceResult.ModifiedCount > 0; } } } \ No newline at end of file diff --git a/Game/Domain/MongoUserRepositoty.cs b/Game/Domain/MongoUserRepositoty.cs index 51aeba5..49b639f 100644 --- a/Game/Domain/MongoUserRepositoty.cs +++ b/Game/Domain/MongoUserRepositoty.cs @@ -7,47 +7,68 @@ public class MongoUserRepository : IUserRepository { private readonly IMongoCollection userCollection; public const string CollectionName = "users"; + private static readonly object lockObject = new(); + public MongoUserRepository(IMongoDatabase database) { userCollection = database.GetCollection(CollectionName); + userCollection.Indexes.CreateOne(new CreateIndexModel( + Builders.IndexKeys.Ascending(x => x.Login), + new CreateIndexOptions { Unique = true })); } public UserEntity Insert(UserEntity user) { - //TODO: Ищи в документации InsertXXX. - throw new NotImplementedException(); + userCollection.InsertOne(user); + return user; } public UserEntity FindById(Guid id) { - //TODO: Ищи в документации FindXXX - throw new NotImplementedException(); + return userCollection.Find(x => x.Id == id).FirstOrDefault(); } public UserEntity GetOrCreateByLogin(string login) { - //TODO: Это Find или Insert - throw new NotImplementedException(); + lock(lockObject) + { + var user = userCollection.Find(x => x.Login == login).FirstOrDefault(); + if (user is not null) + { + return user; + } + + user = new UserEntity(login); + Insert(user); + + return user; + } } public void Update(UserEntity user) { - //TODO: Ищи в документации ReplaceXXX - throw new NotImplementedException(); + userCollection.ReplaceOne(x => x.Id == user.Id, user); } public void Delete(Guid id) { - throw new NotImplementedException(); + userCollection.DeleteOne(x => x.Id == id); } // Для вывода списка всех пользователей (упорядоченных по логину) // страницы нумеруются с единицы public PageList GetPage(int pageNumber, int pageSize) { - //TODO: Тебе понадобятся SortBy, Skip и Limit - throw new NotImplementedException(); + var totalCount = userCollection.CountDocuments(x => true); + + var users = userCollection.Find(x => true) + .SortBy(x => x.Login) + .Skip((pageNumber - 1) * pageSize) + .Limit(pageSize) + .ToList(); + + return new PageList(users, totalCount, pageNumber, pageSize); } // Не нужно реализовывать этот метод diff --git a/Game/Domain/Player.cs b/Game/Domain/Player.cs index 9c4f838..59aadf1 100644 --- a/Game/Domain/Player.cs +++ b/Game/Domain/Player.cs @@ -1,4 +1,5 @@ using System; +using MongoDB.Bson.Serialization.Attributes; namespace Game.Domain { @@ -7,17 +8,20 @@ namespace Game.Domain /// public class Player { + [BsonConstructor] public Player(Guid userId, string name) { UserId = userId; Name = name; } - + + [BsonElement] public Guid UserId { get; } /// /// Снэпшот имени игрока на момент старта игры. Считайте, что это такое требование к игре. /// + [BsonElement] public string Name { get; } /// diff --git a/Game/Domain/UserEntity.cs b/Game/Domain/UserEntity.cs index 671510c..806ace1 100644 --- a/Game/Domain/UserEntity.cs +++ b/Game/Domain/UserEntity.cs @@ -24,6 +24,12 @@ public UserEntity(Guid id, string login, string lastName, string firstName, int CurrentGameId = currentGameId; } + public UserEntity(string login) + { + Id = new Guid(); + Login = login; + } + public Guid Id { get; diff --git a/Tests/MongoUserRepositoryShould.cs b/Tests/MongoUserRepositoryShould.cs index 6037761..a33cba1 100644 --- a/Tests/MongoUserRepositoryShould.cs +++ b/Tests/MongoUserRepositoryShould.cs @@ -114,7 +114,6 @@ public void Delete() [Test(Description = "Тест на наличие индекса по логину")] - [Explicit("Это дополнительная задача Индекс")] [MaxTime(15000)] public void SearchByLoginFast() { @@ -124,7 +123,6 @@ public void SearchByLoginFast() [Test(Description = "Тест на уникальный индекс по логину")] - [Explicit("Это дополнительная задача Индекс")] public void LoginDuplicateNotAllowed() { Action action = () => diff --git a/db.sln.DotSettings b/db.sln.DotSettings index b5ef7ae..3744498 100644 --- a/db.sln.DotSettings +++ b/db.sln.DotSettings @@ -19,6 +19,10 @@ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="" Suffix="" Style="aaBb" /></Policy> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /> + <Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static readonly fields (private)"><ElementKinds><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="" Suffix="" Style="aaBb" /></Policy></Policy> + <Policy><Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy> + <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Types and namespaces"><ElementKinds><Kind Name="NAMESPACE" /><Kind Name="CLASS" /><Kind Name="STRUCT" /><Kind Name="ENUM" /><Kind Name="DELEGATE" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /></Policy> + <Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static fields (private)"><ElementKinds><Kind Name="FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> @@ -69,4 +73,5 @@ True True True + True True \ No newline at end of file From 5a0a6ce7f6719eca5b4dce8f4899908707396594 Mon Sep 17 00:00:00 2001 From: huesosRedkostniy Date: Tue, 22 Oct 2024 17:29:38 +0500 Subject: [PATCH 2/2] poiasjmdop-qwdxcasd --- ConsoleApp/Program.cs | 25 +++++++++++++++++--- Game/Domain/GameEntity.cs | 4 ++-- Game/Domain/GameTurnEntity.cs | 29 ++++++++++++++++++++++- Game/Domain/IGameTurnRepository.cs | 6 ++++- Game/Domain/MongoGameRepository.cs | 1 - Game/Domain/MongoGameTurnRepository.cs | 32 ++++++++++++++++++++++++++ Game/Domain/MongoUserRepositoty.cs | 4 ++-- 7 files changed, 91 insertions(+), 10 deletions(-) diff --git a/ConsoleApp/Program.cs b/ConsoleApp/Program.cs index a8fc761..872c98b 100644 --- a/ConsoleApp/Program.cs +++ b/ConsoleApp/Program.cs @@ -9,6 +9,7 @@ class Program { private readonly IUserRepository userRepo; private readonly IGameRepository gameRepo; + private readonly IGameTurnRepository gameTurnRepository; private readonly Random random = new(); private Program(string[] args) @@ -16,6 +17,7 @@ private Program(string[] args) var db = new MongoClient("mongodb://localhost:27017").GetDatabase("game"); userRepo = new MongoUserRepository(db); gameRepo = new MongoGameRepository(db); + gameTurnRepository = new MongoGameTurnRepository(db); } public static void Main(string[] args) @@ -127,8 +129,8 @@ private bool HandleOneGameTurn(Guid humanUserId) if (game.HaveDecisionOfEveryPlayer) { - // TODO: Сохранить информацию о прошедшем туре в IGameTurnRepository. Сформировать информацию о закончившемся туре внутри FinishTurn и вернуть её сюда. - game.FinishTurn(); + var gameTurnEntity = game.FinishTurn(); + gameTurnRepository.Insert(gameTurnEntity); } ShowScore(game); @@ -182,7 +184,24 @@ private void UpdatePlayersWhenGameFinished(GameEntity game) private void ShowScore(GameEntity game) { var players = game.Players; - // TODO: Показать информацию про 5 последних туров: кто как ходил и кто в итоге выиграл. Прочитать эту информацию из IGameTurnRepository + var playersDictionary = players.ToDictionary(p => p.UserId, p => p.Name); + var lastFiveTurns = gameTurnRepository.GetLastTurns(game.Id, 5); + foreach (var turn in lastFiveTurns) + { + var winnerName = Guid.Empty != turn.WinnerId && playersDictionary.ContainsKey(turn.WinnerId) + ? playersDictionary[turn.WinnerId] + : null; + + var decisionsDescription = string.Join(" | ", turn.Decisions + .Select(x => $"Player {playersDictionary[Guid.Parse(x.Key)]} shows {x.Value}")); + + var resultDescription = string.IsNullOrEmpty(winnerName) + ? "Result: draw" + : $"Result: {winnerName} wins"; + + Console.WriteLine($"Turn {turn.TurnIndex}: {decisionsDescription}\n{resultDescription}"); + } + Console.WriteLine($"Score: {players[0].Name} {players[0].Score} : {players[1].Score} {players[1].Name}"); } } diff --git a/Game/Domain/GameEntity.cs b/Game/Domain/GameEntity.cs index 494bd8d..a4a7f7b 100644 --- a/Game/Domain/GameEntity.cs +++ b/Game/Domain/GameEntity.cs @@ -92,8 +92,8 @@ public GameTurnEntity FinishTurn() winnerId = player.UserId; } } - //TODO Заполнить все внутри GameTurnEntity, в том числе winnerId - var result = new GameTurnEntity(); + var result = new GameTurnEntity(Guid.NewGuid(), Id, CurrentTurnIndex, winnerId, + Players.ToDictionary(p => p.UserId.ToString(), p=> p.Decision ?? throw new InvalidOperationException())); // Это должно быть после создания GameTurnEntity foreach (var player in Players) player.Decision = null; diff --git a/Game/Domain/GameTurnEntity.cs b/Game/Domain/GameTurnEntity.cs index 0c07495..3b0727c 100644 --- a/Game/Domain/GameTurnEntity.cs +++ b/Game/Domain/GameTurnEntity.cs @@ -1,7 +1,34 @@ +using System; +using System.Collections.Generic; +using MongoDB.Bson.Serialization.Attributes; + namespace Game.Domain { public class GameTurnEntity { - //TODO: Придумать какие свойства должны быть в этом классе, чтобы сохранять всю информацию о закончившемся туре. + [BsonConstructor] + public GameTurnEntity(Guid id, Guid gameId, int turnIndex, Guid winnerId, Dictionary decisions) + { + Id = id; + GameId = gameId; + TurnIndex = turnIndex; + WinnerId = winnerId; + Decisions = decisions; + } + + [BsonElement] + public Guid Id { get; set; } + + [BsonElement] + public Guid GameId { get; set; } + + [BsonElement] + public int TurnIndex { get; set; } + + [BsonElement] + public Guid WinnerId { get; set; } + + [BsonElement] + public Dictionary Decisions { get; set; } } } \ No newline at end of file diff --git a/Game/Domain/IGameTurnRepository.cs b/Game/Domain/IGameTurnRepository.cs index f540e2c..a1ec63c 100644 --- a/Game/Domain/IGameTurnRepository.cs +++ b/Game/Domain/IGameTurnRepository.cs @@ -1,7 +1,11 @@ +using System; +using System.Collections.Generic; + namespace Game.Domain { public interface IGameTurnRepository { - // TODO: Спроектировать интерфейс исходя из потребностей ConsoleApp + GameTurnEntity Insert(GameTurnEntity gameTurn); + List GetLastTurns(Guid gameId, int maxCountTurns); } } \ No newline at end of file diff --git a/Game/Domain/MongoGameRepository.cs b/Game/Domain/MongoGameRepository.cs index e744dcd..4dcb481 100644 --- a/Game/Domain/MongoGameRepository.cs +++ b/Game/Domain/MongoGameRepository.cs @@ -4,7 +4,6 @@ namespace Game.Domain { - // TODO Сделать по аналогии с MongoUserRepository public class MongoGameRepository : IGameRepository { private readonly IMongoCollection gameCollection; diff --git a/Game/Domain/MongoGameTurnRepository.cs b/Game/Domain/MongoGameTurnRepository.cs index 1fa10a5..cf27384 100644 --- a/Game/Domain/MongoGameTurnRepository.cs +++ b/Game/Domain/MongoGameTurnRepository.cs @@ -1,6 +1,38 @@ +using System; +using System.Collections.Generic; +using MongoDB.Driver; + namespace Game.Domain { public class MongoGameTurnRepository : IGameTurnRepository { + private readonly IMongoCollection gameTurnCollection; + public const string CollectionName = "gameTurns"; + + public MongoGameTurnRepository(IMongoDatabase database) + { + gameTurnCollection = database.GetCollection(CollectionName); + gameTurnCollection.Indexes.CreateOne(new CreateIndexModel( + Builders.IndexKeys.Ascending(x => x.GameId) + .Ascending(x => x.TurnIndex))); + } + + public GameTurnEntity Insert(GameTurnEntity gameTurn) + { + gameTurnCollection.InsertOne(gameTurn); + return gameTurn; + } + + public List GetLastTurns(Guid gameId, int maxCountTurns) + { + var lastTurns = gameTurnCollection + .Find(x => x.GameId == gameId) + .SortByDescending(x => x.TurnIndex) + .Limit(maxCountTurns) + .ToList(); + + lastTurns.Reverse(); + return lastTurns; + } } } \ No newline at end of file diff --git a/Game/Domain/MongoUserRepositoty.cs b/Game/Domain/MongoUserRepositoty.cs index 49b639f..55cdf64 100644 --- a/Game/Domain/MongoUserRepositoty.cs +++ b/Game/Domain/MongoUserRepositoty.cs @@ -1,4 +1,5 @@ using System; +using MongoDB.Bson; using MongoDB.Driver; namespace Game.Domain @@ -8,8 +9,7 @@ public class MongoUserRepository : IUserRepository private readonly IMongoCollection userCollection; public const string CollectionName = "users"; private static readonly object lockObject = new(); - - + public MongoUserRepository(IMongoDatabase database) { userCollection = database.GetCollection(CollectionName);