diff --git a/ConsoleApp/ConsoleApp.csproj b/ConsoleApp/ConsoleApp.csproj
index 4afbb5b..6ebd867 100644
--- a/ConsoleApp/ConsoleApp.csproj
+++ b/ConsoleApp/ConsoleApp.csproj
@@ -2,12 +2,13 @@
Exe
- net5.0
+ net7.0
9
+
diff --git a/ConsoleApp/Program.cs b/ConsoleApp/Program.cs
index cf39300..bf70ea8 100644
--- a/ConsoleApp/Program.cs
+++ b/ConsoleApp/Program.cs
@@ -1,6 +1,9 @@
using System;
+using System.Collections.Generic;
using System.Linq;
using Game.Domain;
+using JetBrains.Annotations;
+using Tests;
namespace ConsoleApp
{
@@ -8,12 +11,15 @@ class Program
{
private readonly IUserRepository userRepo;
private readonly IGameRepository gameRepo;
+ private readonly IGameTurnRepository gameTurnRepo;
private readonly Random random = new Random();
private Program(string[] args)
{
- userRepo = new InMemoryUserRepository();
- gameRepo = new InMemoryGameRepository();
+ var db = TestMongoDatabase.Create();
+ userRepo = new MongoUserRepository(db, false);
+ gameRepo = new MongoGameRepository(db, false);
+ gameTurnRepo = new MongoGameTurnRepository(db, false);
}
public static void Main(string[] args)
@@ -125,8 +131,8 @@ private bool HandleOneGameTurn(Guid humanUserId)
if (game.HaveDecisionOfEveryPlayer)
{
- // TODO: Сохранить информацию о прошедшем туре в IGameTurnRepository. Сформировать информацию о закончившемся туре внутри FinishTurn и вернуть её сюда.
- game.FinishTurn();
+ var gameTurn = game.FinishTurn();
+ gameTurnRepo.Insert(gameTurn);
}
ShowScore(game);
@@ -180,8 +186,23 @@ private void UpdatePlayersWhenGameFinished(GameEntity game)
private void ShowScore(GameEntity game)
{
var players = game.Players;
- // TODO: Показать информацию про 5 последних туров: кто как ходил и кто в итоге выиграл. Прочитать эту информацию из IGameTurnRepository
+ var lastTurns = gameTurnRepo.FindLastTurns(game.Id);
+ foreach (var lastTurn in lastTurns)
+ {
+ var winner = GetWinner(players, lastTurn.WinnerId);
+ Console.WriteLine($"Turn {lastTurn.TurnIndex}: {players[0].Name} {lastTurn.FirstPlayerDecision} : {players[1].Name} {lastTurn.SecondPlayerDecision}");
+ Console.WriteLine($"Turn result: {(winner is null ? "draw" : $"{winner.Name} win")}");
+ }
Console.WriteLine($"Score: {players[0].Name} {players[0].Score} : {players[1].Score} {players[1].Name}");
}
+
+ [CanBeNull]
+ private Player GetWinner(IReadOnlyList players, Guid winnerId)
+ {
+ if (winnerId == Guid.Empty)
+ return null;
+
+ return players[0].UserId == winnerId ? players[0] : players[1];
+ }
}
}
diff --git a/Game/Domain/GameEntity.cs b/Game/Domain/GameEntity.cs
index ec7b5ec..815ec64 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;
@@ -31,6 +34,7 @@ public Guid Id
public IReadOnlyList Players => players.AsReadOnly();
+ [BsonElement]
public int TurnsCount { get; }
public int CurrentTurnIndex { get; private set; }
@@ -88,9 +92,7 @@ public GameTurnEntity FinishTurn()
winnerId = player.UserId;
}
}
- //TODO Заполнить все внутри GameTurnEntity, в том числе winnerId
- var result = new GameTurnEntity();
- // Это должно быть после создания GameTurnEntity
+ var result = new GameTurnEntity(Id, winnerId, Players[0].Decision!.Value, Players[1].Decision!.Value, CurrentTurnIndex);
foreach (var player in Players)
player.Decision = null;
CurrentTurnIndex++;
diff --git a/Game/Domain/GameTurnEntity.cs b/Game/Domain/GameTurnEntity.cs
index 0c07495..38828d7 100644
--- a/Game/Domain/GameTurnEntity.cs
+++ b/Game/Domain/GameTurnEntity.cs
@@ -1,7 +1,35 @@
+using System;
+using MongoDB.Bson.Serialization.Attributes;
+
namespace Game.Domain
{
public class GameTurnEntity
{
- //TODO: Придумать какие свойства должны быть в этом классе, чтобы сохранять всю информацию о закончившемся туре.
+ public Guid Id { get; set; }
+
+ [BsonElement]
+ public Guid GameId { get; }
+
+ [BsonElement]
+ public Guid WinnerId { get; }
+
+ [BsonElement]
+ public PlayerDecision FirstPlayerDecision { get; }
+
+ [BsonElement]
+ public PlayerDecision SecondPlayerDecision { get; }
+
+ [BsonElement]
+ public int TurnIndex { get; }
+
+ [BsonConstructor]
+ public GameTurnEntity(Guid gameId, Guid winnerId, PlayerDecision firstPlayerDecision, PlayerDecision secondPlayerDecision, int turnIndex)
+ {
+ GameId = gameId;
+ WinnerId = winnerId;
+ FirstPlayerDecision = firstPlayerDecision;
+ SecondPlayerDecision = secondPlayerDecision;
+ TurnIndex = turnIndex;
+ }
}
}
\ No newline at end of file
diff --git a/Game/Domain/IGameTurnRepository.cs b/Game/Domain/IGameTurnRepository.cs
index f540e2c..f6490c0 100644
--- a/Game/Domain/IGameTurnRepository.cs
+++ b/Game/Domain/IGameTurnRepository.cs
@@ -1,7 +1,12 @@
+using System;
+using System.Collections.Generic;
+
namespace Game.Domain
{
public interface IGameTurnRepository
{
- // TODO: Спроектировать интерфейс исходя из потребностей ConsoleApp
+ GameTurnEntity Insert(GameTurnEntity turn);
+
+ IList FindLastTurns(Guid gameId, int turnsCount = 5);
}
}
\ No newline at end of file
diff --git a/Game/Domain/MongoGameRepository.cs b/Game/Domain/MongoGameRepository.cs
index 86873d4..6e9b3bc 100644
--- a/Game/Domain/MongoGameRepository.cs
+++ b/Game/Domain/MongoGameRepository.cs
@@ -7,39 +7,51 @@ 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 db, bool dropCollection = true)
{
+ if (dropCollection)
+ db.DropCollection(CollectionName);
+
+ gameCollection = db.GetCollection(CollectionName);
}
public GameEntity Insert(GameEntity game)
{
- throw new NotImplementedException();
+ gameCollection.InsertOne(game);
+ return game;
}
public GameEntity FindById(Guid gameId)
{
- throw new NotImplementedException();
+ var filter = Builders.Filter.Eq(g => g.Id, gameId);
+ return gameCollection.Find(filter).FirstOrDefault();
}
public void Update(GameEntity game)
{
- throw new NotImplementedException();
+ var filter = Builders.Filter.Eq(g => g.Id, game.Id);
+ gameCollection.ReplaceOne(filter, game);
}
// Возвращает не более чем limit игр со статусом GameStatus.WaitingToStart
public IList FindWaitingToStart(int limit)
{
- //TODO: Используй Find и Limit
- throw new NotImplementedException();
+ var filter = Builders.Filter.Eq(g => g.Status, GameStatus.WaitingToStart);
+ return gameCollection.Find(filter)
+ .Limit(limit)
+ .ToList();
}
// Обновляет игру, если она находится в статусе GameStatus.WaitingToStart
public bool TryUpdateWaitingToStart(GameEntity game)
{
- //TODO: Для проверки успешности используй IsAcknowledged и ModifiedCount из результата
- throw new NotImplementedException();
+ var filterBuilder = Builders.Filter;
+ var filter = filterBuilder.Eq(g => g.Status, GameStatus.WaitingToStart) & filterBuilder.Eq(g => g.Id, game.Id);
+ var result = gameCollection.ReplaceOne(filter, game);
+ return result.IsAcknowledged && result.ModifiedCount == 1;
}
}
}
\ No newline at end of file
diff --git a/Game/Domain/MongoGameTurnRepository.cs b/Game/Domain/MongoGameTurnRepository.cs
index 1fa10a5..4461e62 100644
--- a/Game/Domain/MongoGameTurnRepository.cs
+++ b/Game/Domain/MongoGameTurnRepository.cs
@@ -1,6 +1,43 @@
+using System;
+using System.Collections.Generic;
+using MongoDB.Driver;
+
namespace Game.Domain
{
public class MongoGameTurnRepository : IGameTurnRepository
{
+ private IMongoCollection turnsCollection;
+ public const string CollectionName = "turns";
+
+ public MongoGameTurnRepository(IMongoDatabase db, bool dropCollection = true)
+ {
+ if (dropCollection)
+ db.DropCollection(CollectionName);
+
+ turnsCollection = db.GetCollection(CollectionName);
+
+ var indexBuilder = Builders.IndexKeys.Ascending(entity => entity.GameId);
+ var createIndexModel = new CreateIndexModel(indexBuilder);
+ turnsCollection.Indexes.CreateOne(createIndexModel);
+ indexBuilder = Builders.IndexKeys.Descending(entity => entity.TurnIndex);
+ createIndexModel = new CreateIndexModel(indexBuilder);
+ turnsCollection.Indexes.CreateOne(createIndexModel);
+ }
+
+
+ public GameTurnEntity Insert(GameTurnEntity turn)
+ {
+ turnsCollection.InsertOne(turn);
+ return turn;
+ }
+
+ public IList FindLastTurns(Guid gameId, int turnsCount = 5)
+ {
+ var filterBuilder = Builders.Filter;
+ var filter = filterBuilder.Eq(entity => entity.GameId, gameId);
+ var lastTurns = turnsCollection.Find(filter).SortByDescending(t => t.TurnIndex).Limit(turnsCount).ToList();
+ lastTurns.Reverse();
+ return lastTurns;
+ }
}
}
\ No newline at end of file
diff --git a/Game/Domain/MongoUserRepositoty.cs b/Game/Domain/MongoUserRepositoty.cs
index 51aeba5..7ba6fd8 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,46 +9,72 @@ public class MongoUserRepository : IUserRepository
private readonly IMongoCollection userCollection;
public const string CollectionName = "users";
- public MongoUserRepository(IMongoDatabase database)
+ public MongoUserRepository(IMongoDatabase database, bool dropCollection = true)
{
+ if (dropCollection)
+ database.DropCollection(CollectionName);
+
userCollection = database.GetCollection(CollectionName);
+ userCollection.Indexes.CreateOne(Builders.IndexKeys.Ascending(e => e.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();
+ var filter = new BsonDocument("_id", id);
+ return userCollection.Find(filter).FirstOrDefault();
}
public UserEntity GetOrCreateByLogin(string login)
{
- //TODO: Это Find или Insert
- throw new NotImplementedException();
+ var filter = new BsonDocument("Login", login);
+ var user = userCollection.Find(filter).FirstOrDefault();
+ if (user is null)
+ {
+ user = new UserEntity { Login = login };
+ try
+ {
+ userCollection.InsertOne(user);
+ }
+ catch (MongoWriteException)
+ {
+ user = userCollection.Find(filter).FirstOrDefault();
+ }
+ }
+
+ return user;
}
public void Update(UserEntity user)
{
- //TODO: Ищи в документации ReplaceXXX
- throw new NotImplementedException();
+ var filter = new BsonDocument("_id", user.Id);
+ userCollection.ReplaceOne(filter, user);
}
public void Delete(Guid id)
{
- throw new NotImplementedException();
+ var filter = new BsonDocument("_id", id);
+ userCollection.DeleteOne(filter);
}
// Для вывода списка всех пользователей (упорядоченных по логину)
// страницы нумеруются с единицы
public PageList GetPage(int pageNumber, int pageSize)
{
- //TODO: Тебе понадобятся SortBy, Skip и Limit
- throw new NotImplementedException();
+ var findResult = userCollection.Find(FilterDefinition.Empty)
+ .SortBy(entity => entity.Login)
+ .Skip((pageNumber - 1) * pageSize)
+ .Limit(pageSize);
+ return new PageList(
+ findResult.ToList(),
+ userCollection.CountDocuments(FilterDefinition.Empty),
+ pageNumber,
+ pageSize);
}
// Не нужно реализовывать этот метод
diff --git a/Game/Domain/Player.cs b/Game/Domain/Player.cs
index 9c4f838..6b9e6d2 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,21 @@ 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/Game.csproj b/Game/Game.csproj
index 33c4f28..b0a85a0 100644
--- a/Game/Game.csproj
+++ b/Game/Game.csproj
@@ -1,7 +1,7 @@
- net5.0
+ net7.0
9
diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj
index 6071f4a..2a6c9f5 100644
--- a/Tests/Tests.csproj
+++ b/Tests/Tests.csproj
@@ -1,7 +1,7 @@
- net5.0
+ net7.0
9
diff --git a/global.json b/global.json
new file mode 100644
index 0000000..7cd6a1f
--- /dev/null
+++ b/global.json
@@ -0,0 +1,7 @@
+{
+ "sdk": {
+ "version": "7.0.0",
+ "rollForward": "latestMajor",
+ "allowPrerelease": true
+ }
+}
\ No newline at end of file