public async Task <EngineEvaluation> BuildEngineEvaluations(TreeMove treeMove, params string[] previousMoves) { var engineEvaluation = await chessMetersContext.EngineEvaluations.SingleOrDefaultAsync(x => x.TreeMoveId == treeMove.Id && x.EngineId == engine.EngineId); if (engineEvaluation?.Depth >= engineDepth) { return(engineEvaluation); } await engine.SetPosition(previousMoves.Any()?$"{string.Join(' ', previousMoves)} {treeMove.Move}" : treeMove.Move); var evaluationCentipawns = await engine.GetEvaluationCentipawns(treeMove.ColorId); if (engineEvaluation != null) { engineEvaluation.Depth = engineDepth; engineEvaluation.EvaluationCentipawns = evaluationCentipawns; chessMetersContext.Update(engineEvaluation); await chessMetersContext.SaveChangesAsync(); } else { await chessMetersContext.EngineEvaluations.AddAsync(new EngineEvaluation { TreeMoveId = treeMove.Id, EngineId = engine.EngineId, Depth = engineDepth, EvaluationCentipawns = evaluationCentipawns }); await chessMetersContext.SaveChangesAsync(); } return(engineEvaluation); }
public async Task <IEnumerable <TreeMove> > BuildTree(short analyzeDepth, Game game) { var treeMoves = new List <TreeMove>(); try { var moves = game.Moves.Split(' '); if (!moves.Any()) { return(treeMoves); } var fullPathIds = new List <long>(); TreeMove parentTreeMove = null; await engineAnalyzeEvaluator.StartNewGame(analyzeDepth); foreach (var move in moves) { var parentTreeMoveId = parentTreeMove?.Id; var treeMove = await chessMetersContext.TreeMoves.SingleOrDefaultAsync(x => x.Move == move && x.ParentTreeMoveId == parentTreeMoveId); if (treeMove == null) { treeMove = new TreeMove { Move = move, FullPath = fullPathIds.Any() ? string.Join(" ", fullPathIds) : null, ParentTreeMoveId = parentTreeMoveId, ParentTreeMove = parentTreeMove, ColorId = fullPathIds.Count % 2 == 0 ? ColorEnum.White : ColorEnum.Black }; await chessMetersContext.TreeMoves.AddAsync(treeMove); await chessMetersContext.SaveChangesAsync(); } await engineAnalyzeEvaluator.BuildEngineEvaluations(treeMove, treeMoves.Select(x => x.Move).ToArray()); treeMoves.Add(treeMove); fullPathIds.Add(treeMove.Id); parentTreeMove = treeMove; } game.LastTreeMoveId = fullPathIds.LastOrDefault(); chessMetersContext.Games.Update(game); await chessMetersContext.SaveChangesAsync(); } catch (Exception ex) { game.AnalyzeExceptionStackTrace = ex.ToString(); } return(treeMoves); }
public async Task <IActionResult> Generate([FromBody] GenerateReportViewModel generateReport) { if (!ModelState.IsValid) { return(BadRequest(ModelState)); } var games = await gameConverter.ConvertFromPGN(generateReport.PGN); for (var i = 0; i < games.Count(); i++) { games.ElementAt(i).UserColorId = generateReport.UserColors.ElementAt(i); } var report = new Report { Description = generateReport.Description, PGN = generateReport.PGN, UserId = User.FindFirst(ClaimTypes.NameIdentifier).Value, CreationDate = DateTime.Now, Games = games.ToList() }; await chessMetersContext.Reports.AddAsync(report); await chessMetersContext.SaveChangesAsync(); var data = new Dictionary <string, int> { { "id", report.Id } }; var jobData = new JobDataMap(data); var scheduler = await schedulerFactory.GetScheduler(); await scheduler.TriggerJob(new JobKey(typeof(ReportGeneratorJob).Name), jobData); return(Ok()); }
public async Task Schedule(Game game, short engineDepth) { try { var treeMoves = await treeMoveBuilder.BuildTree(engineDepth, game); game.LastTreeMoveId = treeMoves.LastOrDefault()?.Id; await flagBuilder.AnalizeGame(game); game.Analyzed = true; chessMetersContext.Games.Update(game); } catch (Exception ex) { game.AnalyzeExceptionStackTrace = ex.ToString(); } finally { await chessMetersContext.SaveChangesAsync(); } }
public async Task BuildTree_Should_SetupTreeAndSaveToDbIfMoveNotFound() { string pgn = "[Event \"Live Chess\"] [Site \"Chess.com\"] [Date \"2020.11.25\"] [Round \"?\"] [White \"claudiuoprea\"] [Black \"Cest_Sebastian\"] [Result \"0-1\"] [ECO \"C26\"] [WhiteElo \"1174\"] [BlackElo \"1212\"] [TimeControl \"600\"] [EndTime \"5:58:12 PST\"] [Termination \"Cest_Sebastian won by resignation\"] 1. e4 {[%timestamp 1]} 1... e5 {[%timestamp 1]} 2. Bc4 {[%timestamp 1]} 2... Nf6 {[%timestamp 1]} 3. Nc3 {[%timestamp 16]} 3... Nc6 {[%timestamp 253]} 4. d3 {[%timestamp 21]} 4... Bb4 {[%timestamp 93]} 5. f4 {[%timestamp 79]} 5... Bxc3+ {[%timestamp 70]} 6. bxc3 {[%timestamp 10]} 6... O-O {[%timestamp 80]} 7. fxe5 {[%timestamp 91]} 7... Nxe5 {[%timestamp 66]} 8. Bb3 {[%timestamp 33]} 8... d6 {[%timestamp 52]} 9. Nf3 {[%timestamp 24]} 9... Bg4 {[%timestamp 27]} 10. h3 {[%timestamp 114]} 10... Bxf3 {[%timestamp 14]} 11. gxf3 {[%timestamp 39]} 11... d5 {[%timestamp 33]} 12. Rg1 {[%timestamp 107]} 12... dxe4 {[%timestamp 58]} 13. Bh6 {[%timestamp 48]} 13... Re8 {[%timestamp 135]} 14. Bxg7 {[%timestamp 126]} 14... Ng6 {[%timestamp 1]} 15. Bh6 {[%timestamp 250]} 15... exf3+ {[%timestamp 59]} 16. Kd2 {[%timestamp 49]} 16... f2 {[%timestamp 174]} 17. Rg2 {[%timestamp 187]} 17... Ne4+ {[%timestamp 237]} 18. Kc1 {[%timestamp 74]} 18... Nxc3 {[%timestamp 142]} 19. Qd2 {[%timestamp 109]} 19... f1=Q+ {[%timestamp 58]} 20. Kb2 {[%timestamp 60]} 20... Na4+ {[%timestamp 431]} 21. Bxa4 {[%timestamp 175]} 21... Qd4+ {[%timestamp 76]} 22. Ka3 {[%timestamp 128]} 22... Qc5+ {[%timestamp 114]} 23. Kb3 {[%timestamp 112]} 23... Qd5+ {[%timestamp 60]} 24. Ka3 {[%timestamp 35]} 24... Qfxg2 {[%timestamp 40]} 25. Qc3 {[%timestamp 74]} 25... Qd6+ {[%timestamp 179]} 26. Kb3 {[%timestamp 149]} 26... Qgd5+ {[%timestamp 69]} 27. Kb2 {[%timestamp 29]} 27... Qb6+ {[%timestamp 96]} 28. Bb3 {[%timestamp 52]} 28... Qdd4 {[%timestamp 1]} 29. Qxd4 {[%timestamp 1]} 29... Qxd4+ {[%timestamp 1]} 30. Ka3 {[%timestamp 31]} 0-1"; using var context = new ChessMetersContext(options, new OperationalStoreOptionsMigrations()); var engineProcess = new EngineProcess(); var stockfishEngine = new StockfishEngine(engineProcess); var engineAnalyzeEvaluator = new EngineEvaluationBuilder(stockfishEngine, context); var gameAnalyzer = new TreeMovesBuilder(context, engineAnalyzeEvaluator); var user = await context.Users.FirstAsync(); var report = new Report { PGN = pgn, Description = $"Unit test ${DateTime.Now}", UserId = user.Id }; await context.Reports.AddAsync(report); await context.SaveChangesAsync(); var moves = await gameAnalyzer.BuildTree(EngineConsts.defaultAnalyzeDepth, new Game { ReportId = report.Id, Result = "0-1", Moves = "d2d4 d7d5 c2c4 c7c6 g1f3" }); Assert.Equal(5, moves.Count()); Assert.All(moves, x => Assert.NotEqual(0, x.Id)); Assert.Null(moves.First().ParentTreeMoveId); Assert.NotNull(moves.Last().ParentTreeMoveId); Assert.NotNull(moves.Last().ParentTreeMove); }
public async Task AnalizeGame(Game game) { var lastTreeMove = await chessMetersContext.TreeMoves.FindAsync(game.LastTreeMoveId); var pathIds = game.LastTreeMove.FullPath.Split(' '); var moveIds = pathIds.Select(x => long.Parse(x)).ToList(); moveIds.Add(game.LastTreeMoveId.Value); var moves = await chessMetersContext.TreeMoves.Include(x => x.EngineEvaluations).Include(x => x.TreeMoveFlags) .Where(x => moveIds.Contains(x.Id)).ToListAsync(); var treeMoves = new List <TreeMove>(); foreach (var moveId in moveIds) { treeMoves.Add(moves.Single(x => x.Id == moveId)); } boardState.Initialize(treeMoves, game.UserColorId); var rules = assemblyLoader.GetAllTypesOf <IRule>(); var gameFlags = new HashSet <FlagEnum>(); foreach (var treeMove in treeMoves) { var treeMoveFlags = new HashSet <FlagEnum>(); foreach (var rule in rules) { if (!rule.Evaluate(boardState)) { continue; } if (rule.IsGameRule) { gameFlags.Add(rule.Flag); } else { treeMoveFlags.Add(rule.Flag); } } chessMetersContext.TreeMoveFlags.RemoveRange(treeMove.TreeMoveFlags.Where(x => !treeMoveFlags.Contains(x.FlagId))); await chessMetersContext.TreeMoveFlags.AddRangeAsync(treeMoveFlags.Where(x => !treeMove.TreeMoveFlags.Any(tmf => tmf.FlagId == x)).Select(x => new TreeMoveFlag { TreeMoveId = treeMove.Id, FlagId = x })); boardState.SetNextTreeMove(); } chessMetersContext.GameFlags.RemoveRange(game.GameFlags); await chessMetersContext.GameFlags.AddRangeAsync(gameFlags.Select(x => new GameFlag { FlagId = x, GameId = game.Id })); await chessMetersContext.SaveChangesAsync(); }