diff --git a/ChessStats/ChessStats/Helpers/StatsGraph.cs b/ChessStats/ChessStats/Helpers/StatsGraph.cs index 8398b07..694f51a 100644 --- a/ChessStats/ChessStats/Helpers/StatsGraph.cs +++ b/ChessStats/ChessStats/Helpers/StatsGraph.cs @@ -37,12 +37,15 @@ internal class StatsGraph public double GraphWidth { get; private set; } public float TextSize { get; private set; } public float TextSizeMsg { get; private set; } + public double GameTypeDivisor { get; private set; } - internal StatsGraph(double width = 3840, float textSize = 140, float textSizeMsg = 100) + internal StatsGraph(double width = 3840, float textSize = 140, float textSizeMsg = 100, int gameModesPlayed = 0) { - TextSize = textSize; - TextSizeMsg = textSizeMsg; + GameTypeDivisor = (4 - (gameModesPlayed == 0 ? 3 : gameModesPlayed)); + TextSize = textSize/(float)GameTypeDivisor; + TextSizeMsg = textSizeMsg / (float)GameTypeDivisor; GraphWidth = width; + font = new(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.TimesRoman), TextSize); fontMessage = new(FontFamily.ResolveFontFamily(FontFamily.StandardFontFamilies.TimesItalic), TextSizeMsg); @@ -52,7 +55,7 @@ private Document CreateDocument(double height) { Document doc = new(); doc.Pages.Add(new(GraphWidth, height)); - + LinearGradientBrush bkgBrush = new(new Point(0, 0), new Point(GraphWidth, height), new GradientStop(COL_BKG_GRAD_START, 0), @@ -105,6 +108,8 @@ internal async Task RenderCapsGraph(List capsScoresWhite, Li { return await Task.Run(() => { + height /= GameTypeDivisor; + //Make sure the data is sorted correctly capsScoresWhite = capsScoresWhite.OrderBy(item => item.GameDate).TakeLast((int)maxCapsGames).ToList(); capsScoresBlack = capsScoresBlack.OrderBy(item => item.GameDate).TakeLast((int)maxCapsGames).ToList(); @@ -162,12 +167,12 @@ internal async Task RenderCapsGraph(List capsScoresWhite, Li GraphicsPath gpWhiteSmooth = new(); _ = gpWhiteSmooth.AddSmoothSpline(gpWhitePoints.ToArray()); - gpr.StrokePath(IS_SMOOTH_CAPS ? gpWhiteSmooth : gpWhite, COL_CAPS_WHITE, lineWidth: GRAPH_LINE_WIDTH); + gpr.StrokePath(IS_SMOOTH_CAPS ? gpWhiteSmooth : gpWhite, COL_CAPS_WHITE, lineWidth: GRAPH_LINE_WIDTH/GameTypeDivisor); GraphicsPath gpBlackSmooth = new(); _ = gpBlackSmooth.AddSmoothSpline(gpBlackPoints.ToArray()); - gpr.StrokePath(IS_SMOOTH_CAPS ? gpBlackSmooth : gpBlack, COL_CAPS_BLACK, lineWidth: GRAPH_LINE_WIDTH); + gpr.StrokePath(IS_SMOOTH_CAPS ? gpBlackSmooth : gpBlack, COL_CAPS_BLACK, lineWidth: GRAPH_LINE_WIDTH/GameTypeDivisor); WriteMessage(gpr, height, $"* Based on the last {whiteMovingAv.Length}/{blackMovingAv.Length} games with available CAPs scores"); } @@ -181,6 +186,7 @@ internal async Task RenderAllCapsGraph(List capsScores, doub { return await Task.Run(() => { + height /= GameTypeDivisor; Document doc = CreateDocument(height); VectSharp.Graphics gpr = doc.Pages[0].Graphics; @@ -229,11 +235,12 @@ internal async Task RenderAllCapsGraph(List capsScores, doub }).ConfigureAwait(false); } - internal async Task RenderRatingGraph(List<(DateTime gameDate, int rating, - string gameType)> ratingsPostGame, double height = 1920) + internal async Task RenderRatingGraph(List<(DateTime gameDate, int rating, string gameType)> ratingsPostGame, + double height = 1920) { return await Task.Run(() => { + height /= GameTypeDivisor; Document doc = CreateDocument(height); VectSharp.Graphics gpr = doc.Pages[0].Graphics; @@ -283,8 +290,8 @@ internal async Task RenderRatingGraph(List<(DateTime gameDate, int ratin WriteRangeMessage(gpr, height, $"{graphMin}", $"{graphMax}"); - gpr.FillRectangle(0, ((graphMax - ratingsPostGameOrdered[^1].rating) * RatingStepY) - (CUR_RATING_BAR_HEIGHT / 2), - GraphWidth, CUR_RATING_BAR_HEIGHT, + gpr.FillRectangle(0, ((graphMax - ratingsPostGameOrdered[^1].rating) * RatingStepY) - ((CUR_RATING_BAR_HEIGHT / GameTypeDivisor) / 2), + GraphWidth, CUR_RATING_BAR_HEIGHT / GameTypeDivisor, COL_RATING); } @@ -293,11 +300,13 @@ internal async Task RenderRatingGraph(List<(DateTime gameDate, int ratin }).ConfigureAwait(false); } - internal async Task RenderAverageStatsGraph(List<(string TimeControl, int VsMin, int Worst, int LossAv, int DrawAv, - int WinAv, int Best, int VsMax)> graphData, double height = 1280) + internal async Task RenderAverageStatsGraph(List<(string TimeControl, int VsMin, int Worst, int LossAv, int DrawAv, int WinAv, int Best, int VsMax)> graphData, + double height = 1280) { return await Task.Run(() => { + height /= ((double)GameTypeDivisor); + int graphMinCalc = graphData.Where(x => x.WinAv != 0 && x.LossAv != 0).Select(x => x.WinAv).DefaultIfEmpty(0).Min(); int graphMaxCalc = graphData.Where(x => x.WinAv != 0 && x.LossAv != 0).Select(x => x.LossAv).DefaultIfEmpty(0).Max(); diff --git a/ChessStats/ChessStats/Helpers/StatsHtml.cs b/ChessStats/ChessStats/Helpers/StatsHtml.cs index 647cec6..3de5a19 100644 --- a/ChessStats/ChessStats/Helpers/StatsHtml.cs +++ b/ChessStats/ChessStats/Helpers/StatsHtml.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Text; namespace ChessStats.Helpers @@ -12,7 +13,7 @@ public static string GetHtmlTail(Uri chessdotcomUrl, string versionNumber, strin : $""; } - public static string GetHtmlTop(string pageTitle, string backgroundImage, string favIconImage, string font700Fragment, string font800Fragment) + public static string GetHtmlTop(string pageTitle, string backgroundImage, string favIconImage, string font700Fragment, string font800Fragment, int controlsPlayed=3) { StringBuilder htmlReport = new(); _ = htmlReport.AppendLine("") @@ -53,12 +54,15 @@ public static string GetHtmlTop(string pageTitle, string backgroundImage, string .AppendLine(" .headRow {display: grid; grid-template-columns: 200px auto; grid-gap: 0px; border:0px; height: auto; padding: 0px; }") .AppendLine(" .headRow > div {padding: 0px; }") .AppendLine(" .headBox img {vertical-align: middle}") - .AppendLine(" .ratingRow {display: grid;grid-template-columns: auto auto auto;grid-gap: 20px;padding: 10px;}") + .AppendLine($" .ratingRow {{display: grid;grid-template-columns: {string.Join(" ","auto auto auto".Split(" ").Take(controlsPlayed))} ;grid-gap: 20px;padding: 10px;}}") .AppendLine(" .ratingRow > div {font-family: Montserrat; font-weight: 700; text-align: center; padding: 0px; color: whitesmoke; font-size: 15px; font-weight: bold;}") .AppendLine(" .ratingBox {cursor: pointer;}") - .AppendLine(" .graphRow {display: grid;grid-template-columns: auto auto auto;grid-gap: 10px;padding: 5px;}") + .AppendLine($" .graphRow {{display: grid;grid-template-columns: {string.Join(" ", "auto auto auto".Split(" ").Take(controlsPlayed))} ;grid-gap: 10px;padding: 5px;}}") .AppendLine(" .graphRow > div {font-family: Montserrat; font-weight: 700; text-align: center; padding: 0px; color: whitesmoke; font-size: 15px; font-weight: bold;}") - .AppendLine(" .graphBox img { max-width:100%; height:auto; }") + .AppendLine(" .graphBox img { width:100%; height:auto; object-fit: cover; }") + .AppendLine(" .graphCapsRow {display: grid;grid-template-columns: 60% auto;grid-gap: 10px;padding: 5px;}") + .AppendLine(" .graphCapsRow>div {font-family: Montserrat;font-weight: 700;text-align: center;padding: 0px;color: whitesmoke;font-size: 15px;font-weight: bold;}") + .AppendLine(" .graphCapsBox img {max-width: 100%; width: auto;height: auto;}") .AppendLine(" .yearSplit {border-top: thin dotted; border-color: #1583b7;}") .AppendLine(" .higher {background-color: hsla(120, 100%, 50%, 0.25);}") .AppendLine(" .lower {background-color: hsla(0, 100%, 70%, 0.4);}") @@ -70,9 +74,6 @@ public static string GetHtmlTop(string pageTitle, string backgroundImage, string .AppendLine(" .capsRollingTable tbody td:nth-child(1) {font-size: 14px;font-weight: bold;}") .AppendLine(" .playingStatsTable tbody td:nth-child(1) {font-size: 14px;font-weight: bold;}") .AppendLine(" .playingStatsMonthTable tbody td:nth-child(1) {font-size: 14px;font-weight: bold;}") - .AppendLine(" .graphCapsRow {display: grid;grid-template-columns: 60% auto;grid-gap: 10px;padding: 5px;}") - .AppendLine(" .graphCapsRow>div {font-family: Montserrat;font-weight: 700;text-align: center;padding: 0px;color: whitesmoke;font-size: 15px;font-weight: bold;}") - .AppendLine(" .graphCapsBox img {max-width: 100%; width: auto;height: auto;}") .AppendLine(" .playingStatsTable tbody td:nth-child(5) {border-right: thin solid; border-color: #1583b7;}") .AppendLine(" .playingStatsTable tbody td:nth-child(8) {border-left: thin dotted; border-color: #1583b7;}") .AppendLine(" .playingStatsTable tbody td:nth-child(11) {border-left: thin dotted; border-color: #1583b7;}") diff --git a/ChessStats/ChessStats/Program.cs b/ChessStats/ChessStats/Program.cs index 722d4a8..869e9b5 100644 --- a/ChessStats/ChessStats/Program.cs +++ b/ChessStats/ChessStats/Program.cs @@ -16,8 +16,8 @@ namespace ChessStats { internal class Program { - private const string VERSION_NUMBER = "0.8.0"; - private const string RELEEASE_DATE = "05/2022"; + private const string VERSION_NUMBER = "0.8.1"; + private const string RELEEASE_DATE = "06/2022"; private const string RESULTS_DIR_NAME = "ChessStatsResults"; private const string CACHE_DIR_NAME = "ChessStatsCache"; private const string CACHE_VERSION_NUMBER = "2"; @@ -237,11 +237,11 @@ private static async Task Main(string[] args) // ************* Helpers.StatsConsole.StartTimedSection($">>Rendering Graphs"); - StatsGraph statsGraph = new(); + StatsGraph statsGraph = new(gameModesPlayed: ((userStats.ChessBullet is null ? 0 : 1) + (userStats.ChessBlitz is null ? 0 : 1) + (userStats.ChessRapid is null ? 0 : 1))); Task graphT1 = statsGraph.RenderRatingGraph(ratingsPostGame.Where(x => x.gameType == "Bullet").ToList()); - Task graphT2 = statsGraph.RenderRatingGraph(ratingsPostGame.Where(x => x.gameType == "Blitz").ToList()); - Task graphT3 = statsGraph.RenderRatingGraph(ratingsPostGame.Where(x => x.gameType == "Rapid").ToList()); + Task graphT2 = statsGraph.RenderRatingGraph(ratingsPostGame.Where(x => x.gameType == "Blitz").ToList() ); + Task graphT3 = statsGraph.RenderRatingGraph(ratingsPostGame.Where(x => x.gameType == "Rapid").ToList() ); Task graphT4 = statsGraph.RenderAverageStatsGraph(graphData.Where(x => x.TimeControl.Contains("Bullet", StringComparison.InvariantCultureIgnoreCase)).OrderBy(x => x.TimeControl).ToList()); Task graphT5 = statsGraph.RenderAverageStatsGraph(graphData.Where(x => x.TimeControl.Contains("Blitz", StringComparison.InvariantCultureIgnoreCase)).OrderBy(x => x.TimeControl).ToList()); @@ -249,8 +249,8 @@ private static async Task Main(string[] args) Task graphT7 = statsGraph.RenderAllCapsGraph(capsScores["All"].Where(x => x.TimeClass == "Bullet").ToList(), 576); - Task graphT8 = statsGraph.RenderAllCapsGraph(capsScores["All"].Where(x => x.TimeClass == "Blitz").ToList(), 576); - Task graphT9 = statsGraph.RenderAllCapsGraph(capsScores["All"].Where(x => x.TimeClass == "Rapid").ToList(), 576); + Task graphT8 = statsGraph.RenderAllCapsGraph(capsScores["All"].Where(x => x.TimeClass == "Blitz").ToList() , 576); + Task graphT9 = statsGraph.RenderAllCapsGraph(capsScores["All"].Where(x => x.TimeClass == "Rapid").ToList() , 576); Task graphT10 = statsGraph.RenderCapsGraph(capsScores["White"].Where(x => x.TimeClass == "Bullet").ToList(), @@ -467,7 +467,12 @@ private static async Task BuildHtmlReport(string VERSION_NUMBER, PlayerP { StringBuilder htmlOut = new(); - _ = htmlOut.Append(Helpers.StatsHtml.GetHtmlTop($"ChessStats for {chessdotcomUsername}", bkgImageBase64, favIconBase64, font700Fragment, font800Fragment)) + int timeControlsPlayed = (userStats.ChessBullet is null ? 0 : 1) + + (userStats.ChessBlitz is null ? 0 : 1) + + (userStats.ChessRapid is null ? 0 : 1); + + _ = htmlOut.Append(Helpers.StatsHtml.GetHtmlTop($"ChessStats for {chessdotcomUsername}", bkgImageBase64, favIconBase64, + font700Fragment, font800Fragment, (timeControlsPlayed==0?3:timeControlsPlayed))) .AppendLine($"
") .AppendLine($"
") .AppendLine($" logo") @@ -476,73 +481,116 @@ private static async Task BuildHtmlReport(string VERSION_NUMBER, PlayerP .AppendLine($"

Live Games
Summary
For {chessdotcomUsername}
On {DateTime.UtcNow.ToShortDateString()} ({DateTime.UtcNow.ToShortTimeString()} UTC)

") .AppendLine($"
") .AppendLine($"
") - .AppendLine($"
") - .AppendLine($"
") - .AppendLine($"
") - .AppendLine($" Bullet {Helpers.StatsConsole.ValueOrDash(userStats.ChessBullet?.Last.Rating)}
(Gliko RD {Helpers.StatsConsole.ValueOrDash(userStats.ChessBullet?.Last.GlickoRd)})
{((userStats.ChessBullet == null) ? "-" : userStats.ChessBullet?.Last.Date.ToShortDateString())}") - .AppendLine($"
") - .AppendLine($"
") - .AppendLine($"
") - .AppendLine($"
") - .AppendLine($" Blitz {Helpers.StatsConsole.ValueOrDash(userStats.ChessBlitz?.Last.Rating)}
(Gliko RD {Helpers.StatsConsole.ValueOrDash(userStats.ChessBlitz?.Last.GlickoRd)})
{((userStats.ChessBlitz == null) ? "-" : userStats.ChessBlitz?.Last.Date.ToShortDateString())}") - .AppendLine($"
") - .AppendLine($"
") - .AppendLine($"
") - .AppendLine($" Rapid {Helpers.StatsConsole.ValueOrDash(userStats.ChessRapid?.Last.Rating)}
(Gliko RD {Helpers.StatsConsole.ValueOrDash(userStats.ChessRapid?.Last.GlickoRd)})
{((userStats.ChessRapid == null) ? "-" : userStats.ChessRapid?.Last.Date.ToShortDateString())}") - .AppendLine($"
") - .AppendLine($"
") - .AppendLine($"
") - .AppendLine($"
") - .AppendLine($"
") - .AppendLine($"
") - .AppendLine($"

{pawnFragment}Ratings/CAPs/Win Loss Avg.

") - .AppendLine($"
") - .AppendLine($"
{bulletGraphHtmlFragment}
") - .AppendLine($"
{blitzGraphHtmlFragment}
") - .AppendLine($"
{rapidGraphHtmlFragment}
") - .AppendLine($"
") - .AppendLine($"
") - .AppendLine($"
{capsGraphBullet}
") - .AppendLine($"
{capsGraphBlitz}
") - .AppendLine($"
{capsGraphRapid}
") - .AppendLine($"
") - .AppendLine($"
") - .AppendLine($"
{bulletAvStatsGraphHtmlFragment}
") - .AppendLine($"
{blitzAvStatsGraphHtmlFragment}
") - .AppendLine($"
{rapidAvStatsGraphHtmlFragment}
") - .AppendLine($"
") - .AppendLine($"
") - .AppendLine($"
") - .AppendLine($"
") - .AppendLine($"

{pawnFragment}Last 40 Openings

") - .AppendLine($"{whiteOpeningsRecenthtmlOut}") - .AppendLine($"{blackOpeningsRecenthtmlOut}") - .AppendLine($"

{pawnFragment}All Openings (Max 15)

") - .AppendLine($"{whiteOpeningshtmlOut}") - .AppendLine($"{blackOpeningshtmlOut}") - .AppendLine($"

") - .AppendLine($"

{pawnFragment}CAPs Rolling 3 Game Avg.

") - .AppendLine($"
") - .AppendLine($"
{capsGraphRollingShortBullet}
") - .AppendLine($"
{capsGraphRollingShortBlitz}
") - .AppendLine($"
{capsGraphRollingShortRapid}
") - .AppendLine($"
") - .AppendLine($"
") - .AppendLine($"

") - .AppendLine($"

{pawnFragment}CAPs Rolling 10 Game Avg.

") - .AppendLine($"
") - .AppendLine($"
{capsGraphRollingLongBullet}
") - .AppendLine($"
{capsGraphRollingLongBlitz}
") - .AppendLine($"
{capsGraphRollingLongRapid}
") - .AppendLine($"
") - .AppendLine($"
") - .AppendLine($"

{pawnFragment}Stats by Time Control/Month

") - .AppendLine(playingStatshtmlOut) - .AppendLine($"

{pawnFragment}Time Played by Month

") - .AppendLine(timePlayedByMonthhtmlOut) - .AppendLine(Helpers.StatsHtml.GetHtmlTail(new Uri(CHESSCOM_URL), VERSION_NUMBER, PROJECT_LINK)) - .AppendLine(" ") - .AppendLine(""); + .AppendLine($"
"); + + + + if (userStats.ChessBullet != null) + { + _ = htmlOut.AppendLine($"
") + .AppendLine($"
") + .AppendLine($" Bullet {Helpers.StatsConsole.ValueOrDash(userStats.ChessBullet?.Last.Rating)}
(Gliko RD {Helpers.StatsConsole.ValueOrDash(userStats.ChessBullet?.Last.GlickoRd)})
{((userStats.ChessBullet == null) ? "-" : userStats.ChessBullet?.Last.Date.ToShortDateString())}") + .AppendLine($"
") + .AppendLine($"
"); + } + + if (userStats.ChessBlitz != null) + { + _ = htmlOut.AppendLine($"
") + .AppendLine($"
") + .AppendLine($" Blitz {Helpers.StatsConsole.ValueOrDash(userStats.ChessBlitz?.Last.Rating)}
(Gliko RD {Helpers.StatsConsole.ValueOrDash(userStats.ChessBlitz?.Last.GlickoRd)})
{((userStats.ChessBlitz == null) ? "-" : userStats.ChessBlitz?.Last.Date.ToShortDateString())}") + .AppendLine($"
"); + } + + if (userStats.ChessRapid != null) + { + _ = htmlOut.AppendLine($"
") + .AppendLine($"
") + .AppendLine($" Rapid {Helpers.StatsConsole.ValueOrDash(userStats.ChessRapid?.Last.Rating)}
(Gliko RD {Helpers.StatsConsole.ValueOrDash(userStats.ChessRapid?.Last.GlickoRd)})
{((userStats.ChessRapid == null) ? "-" : userStats.ChessRapid?.Last.Date.ToShortDateString())}") + .AppendLine($"
"); + } + + + + _ = htmlOut.AppendLine($"
") + .AppendLine($"
") + + + + + + + .AppendLine($"
") + .AppendLine($"
") + .AppendLine($"
") + .AppendLine($"

{pawnFragment}Ratings/CAPs/Win Loss Avg.

") + .AppendLine($"
") + + + + .AppendLine((userStats.ChessBullet == null ? " " : $"
{bulletGraphHtmlFragment}
")) + .AppendLine((userStats.ChessBlitz == null ? " " : $"
{blitzGraphHtmlFragment}
")) + .AppendLine((userStats.ChessRapid == null ? " " : $"
{rapidGraphHtmlFragment}
")) + + + + .AppendLine($"
") + .AppendLine($"
") + + + .AppendLine((userStats.ChessBullet == null ? " " : $"
{capsGraphBullet}
")) + .AppendLine((userStats.ChessBlitz == null ? " " : $"
{capsGraphBlitz}
")) + .AppendLine((userStats.ChessRapid == null ? " " : $"
{capsGraphRapid}
")) + + + + .AppendLine($"
") + .AppendLine($"
") + + + + .AppendLine((userStats.ChessBullet == null ? " " : $"
{bulletAvStatsGraphHtmlFragment}
")) + .AppendLine((userStats.ChessBlitz == null ? " " : $"
{blitzAvStatsGraphHtmlFragment}
")) + .AppendLine((userStats.ChessRapid == null ? " " : $"
{rapidAvStatsGraphHtmlFragment}
")) + + + + + + + .AppendLine($"
") + .AppendLine($"
") + .AppendLine($"
") + .AppendLine($"
") + .AppendLine($"

{pawnFragment}Last 40 Openings

") + .AppendLine($"{whiteOpeningsRecenthtmlOut}") + .AppendLine($"{blackOpeningsRecenthtmlOut}") + .AppendLine($"

{pawnFragment}All Openings (Max 15)

") + .AppendLine($"{whiteOpeningshtmlOut}") + .AppendLine($"{blackOpeningshtmlOut}") + .AppendLine($"

") + .AppendLine($"

{pawnFragment}CAPs Rolling 3 Game Avg.

") + .AppendLine($"
") + .AppendLine((userStats.ChessBullet == null ? " " : $"
{capsGraphRollingShortBullet}
")) + .AppendLine((userStats.ChessBlitz == null ? " " : $"
{capsGraphRollingShortBlitz}
")) + .AppendLine((userStats.ChessRapid == null ? " " : $"
{capsGraphRollingShortRapid}
")) + .AppendLine($"
") + .AppendLine($"
") + .AppendLine($"

") + .AppendLine($"

{pawnFragment}CAPs Rolling 10 Game Avg.

") + .AppendLine($"
") + .AppendLine((userStats.ChessBullet == null ? " " : $"
{capsGraphRollingLongBullet}
")) + .AppendLine((userStats.ChessBlitz == null ? " " : $"
{capsGraphRollingLongBlitz}
")) + .AppendLine((userStats.ChessRapid == null ? " " : $"
{capsGraphRollingLongRapid}
")) + .AppendLine($"
") + .AppendLine($"
") + .AppendLine($"

{pawnFragment}Stats by Time Control/Month

") + .AppendLine(playingStatshtmlOut) + .AppendLine($"

{pawnFragment}Time Played by Month

") + .AppendLine(timePlayedByMonthhtmlOut) + .AppendLine(Helpers.StatsHtml.GetHtmlTail(new Uri(CHESSCOM_URL), VERSION_NUMBER, PROJECT_LINK)) + .AppendLine(" ") + .AppendLine(""); return htmlOut.ToString(); }).ConfigureAwait(false);