1
0
mirror of synced 2024-11-27 16:10:53 +01:00

Add initial ai battle saving

This commit is contained in:
asesidaa 2022-09-18 22:35:51 +08:00
parent 1d89ce2009
commit 2de8d8ab86
12 changed files with 220 additions and 28 deletions

View File

@ -0,0 +1,14 @@
namespace SharedProject.Utils;
public static class ValueHelpers
{
public static T Min<T>(T a, T b) where T : IComparable
{
return Comparer<T>.Default.Compare(a, b) <= 0 ? a : b;
}
public static T Max<T>(T a, T b) where T : IComparable
{
return Comparer<T>.Default.Compare(a, b) >= 0 ? a : b;
}
}

View File

@ -16,13 +16,16 @@ public class BaidController : BaseController<BaidController>
private readonly IDanScoreDatumService danScoreDatumService; private readonly IDanScoreDatumService danScoreDatumService;
private readonly IAiDatumService aiDatumService;
public BaidController(IUserDatumService userDatumService, ICardService cardService, public BaidController(IUserDatumService userDatumService, ICardService cardService,
ISongBestDatumService songBestDatumService, IDanScoreDatumService danScoreDatumService) ISongBestDatumService songBestDatumService, IDanScoreDatumService danScoreDatumService, IAiDatumService aiDatumService)
{ {
this.userDatumService = userDatumService; this.userDatumService = userDatumService;
this.cardService = cardService; this.cardService = cardService;
this.songBestDatumService = songBestDatumService; this.songBestDatumService = songBestDatumService;
this.danScoreDatumService = danScoreDatumService; this.danScoreDatumService = danScoreDatumService;
this.aiDatumService = aiDatumService;
} }
@ -112,6 +115,9 @@ public class BaidController : BaseController<BaidController>
var genericInfoFlgLength = genericInfoFlg.Any()? genericInfoFlg.Max() + 1 : 0; var genericInfoFlgLength = genericInfoFlg.Any()? genericInfoFlg.Max() + 1 : 0;
var genericInfoFlgArray = FlagCalculator.GetBitArrayFromIds(genericInfoFlg, (int)genericInfoFlgLength, Logger); var genericInfoFlgArray = FlagCalculator.GetBitArrayFromIds(genericInfoFlg, (int)genericInfoFlgLength, Logger);
var aiScores = await aiDatumService.GetAllAiScoreById(baid);
var totalWin = aiScores.Count(datum => datum.IsWin);
response = new BAIDResponse response = new BAIDResponse
{ {
Result = 1, Result = 1,
@ -159,7 +165,7 @@ public class BaidController : BaseController<BaidController>
LastPlayMode = userData.LastPlayMode, LastPlayMode = userData.LastPlayMode,
IsDispSouuchiOn = true, IsDispSouuchiOn = true,
AiRank = 0, AiRank = 0,
AiTotalWin = 0, AiTotalWin = (uint)totalWin,
Accesstoken = "123456", Accesstoken = "123456",
ContentInfo = GZipBytesUtil.GetGZipBytes(new byte[10]) ContentInfo = GZipBytesUtil.GetGZipBytes(new byte[10])
}; };

View File

@ -1,19 +1,30 @@
namespace TaikoLocalServer.Controllers.Game; using TaikoLocalServer.Services.Interfaces;
namespace TaikoLocalServer.Controllers.Game;
[Route("/v12r03/chassis/getaidata.php")] [Route("/v12r03/chassis/getaidata.php")]
[ApiController] [ApiController]
public class GetAiDataController : BaseController<GetAiDataController> public class GetAiDataController : BaseController<GetAiDataController>
{ {
private readonly IAiDatumService aiDatumService;
public GetAiDataController(IAiDatumService aiDatumService)
{
this.aiDatumService = aiDatumService;
}
[HttpPost] [HttpPost]
[Produces("application/protobuf")] [Produces("application/protobuf")]
public IActionResult GetAiData([FromBody] GetAiDataRequest request) public async Task<IActionResult> GetAiData([FromBody] GetAiDataRequest request)
{ {
Logger.LogInformation("GetAiData request : {Request}", request.Stringify()); Logger.LogInformation("GetAiData request : {Request}", request.Stringify());
var aiScoreData = await aiDatumService.GetAllAiScoreById(request.Baid);
var totalWin = aiScoreData.Count(datum => datum.IsWin);
var response = new GetAiDataResponse var response = new GetAiDataResponse
{ {
Result = 1, Result = 1,
TotalWinnings = 0 TotalWinnings = (uint)totalWin
}; };
return Ok(response); return Ok(response);

View File

@ -1,12 +1,22 @@
namespace TaikoLocalServer.Controllers.Game; using TaikoLocalServer.Services.Interfaces;
using Throw;
namespace TaikoLocalServer.Controllers.Game;
[Route("/v12r03/chassis/getaiscore.php")] [Route("/v12r03/chassis/getaiscore.php")]
[ApiController] [ApiController]
public class GetAiScoreController : BaseController<GetAiScoreController> public class GetAiScoreController : BaseController<GetAiScoreController>
{ {
private readonly IAiDatumService aiDatumService;
public GetAiScoreController(IAiDatumService aiDatumService)
{
this.aiDatumService = aiDatumService;
}
[HttpPost] [HttpPost]
[Produces("application/protobuf")] [Produces("application/protobuf")]
public IActionResult GetAiScore([FromBody] GetAiScoreRequest request) public async Task<IActionResult> GetAiScore([FromBody] GetAiScoreRequest request)
{ {
Logger.LogInformation("GetAiScore request : {Request}", request.Stringify()); Logger.LogInformation("GetAiScore request : {Request}", request.Stringify());
@ -15,6 +25,30 @@ public class GetAiScoreController : BaseController<GetAiScoreController>
Result = 1 Result = 1
}; };
var difficulty = (Difficulty)request.Level;
difficulty.Throw().IfOutOfRange();
var aiData = await aiDatumService.GetSongAiScore(request.Baid, request.SongNo, difficulty);
if (aiData is null)
{
return Ok(response);
}
for (var index = 0; index < aiData.AiSectionScoreData.Count; index++)
{
var sectionScoreDatum = aiData.AiSectionScoreData[index];
response.AryBestSectionDatas.Add(new GetAiScoreResponse.AiBestSectionData
{
Crown = (uint)sectionScoreDatum.Crown,
GoodCnt = sectionScoreDatum.GoodCount,
OkCnt = sectionScoreDatum.OkCount,
NgCnt = sectionScoreDatum.MissCount,
PoundCnt = sectionScoreDatum.DrumrollCount,
Score = sectionScoreDatum.Score,
SectionNo = (uint)index
});
}
// There's either 3 or 5 total sections // There's either 3 or 5 total sections
// SectionNo doesn't seem to actually affect which section is being assigned to, only the List order matters // SectionNo doesn't seem to actually affect which section is being assigned to, only the List order matters
/*response.AryBestSectionDatas.Add(new GetAiScoreResponse.AiBestSectionData() /*response.AryBestSectionDatas.Add(new GetAiScoreResponse.AiBestSectionData()

View File

@ -20,13 +20,16 @@ public class PlayResultController : BaseController<PlayResultController>
private readonly IDanScoreDatumService danScoreDatumService; private readonly IDanScoreDatumService danScoreDatumService;
private readonly IAiDatumService aiDatumService;
public PlayResultController(IUserDatumService userDatumService, ISongPlayDatumService songPlayDatumService, public PlayResultController(IUserDatumService userDatumService, ISongPlayDatumService songPlayDatumService,
ISongBestDatumService songBestDatumService, IDanScoreDatumService danScoreDatumService) ISongBestDatumService songBestDatumService, IDanScoreDatumService danScoreDatumService, IAiDatumService aiDatumService)
{ {
this.userDatumService = userDatumService; this.userDatumService = userDatumService;
this.songPlayDatumService = songPlayDatumService; this.songPlayDatumService = songPlayDatumService;
this.songBestDatumService = songBestDatumService; this.songBestDatumService = songBestDatumService;
this.danScoreDatumService = danScoreDatumService; this.danScoreDatumService = danScoreDatumService;
this.aiDatumService = aiDatumService;
} }
[HttpPost] [HttpPost]
@ -77,9 +80,7 @@ public class PlayResultController : BaseController<PlayResultController>
if (playMode == PlayMode.AiBattle) if (playMode == PlayMode.AiBattle)
{ {
// await UpdateAiBattleData(request, stageData); await UpdateAiBattleData(request, stageData);
// Update AI win count here somewhere, or in UpdatePlayData?
// I have no clue how to update input median or variance
} }
await UpdateBestData(request, stageData, bestData); await UpdateBestData(request, stageData, bestData);
@ -163,7 +164,7 @@ public class PlayResultController : BaseController<PlayResultController>
ComboCount = stageData.ComboCnt, ComboCount = stageData.ComboCnt,
HitCount = stageData.HitCnt, HitCount = stageData.HitCnt,
DrumrollCount = stageData.PoundCnt, DrumrollCount = stageData.PoundCnt,
Crown = PlayResultToCrown(stageData), Crown = PlayResultToCrown(stageData.PlayResult, stageData.OkCnt),
Score = stageData.PlayScore, Score = stageData.PlayScore,
ScoreRate = stageData.ScoreRate, ScoreRate = stageData.ScoreRate,
ScoreRank = (ScoreRank)stageData.ScoreRank, ScoreRank = (ScoreRank)stageData.ScoreRank,
@ -294,7 +295,7 @@ public class PlayResultController : BaseController<PlayResultController>
}); });
// Determine whether it is dondaful crown as this is not reflected by play result // Determine whether it is dondaful crown as this is not reflected by play result
var crown = PlayResultToCrown(stageData); var crown = PlayResultToCrown(stageData.PlayResult, stageData.OkCnt);
bestDatum.UpdateBestData(crown, stageData.ScoreRank, stageData.PlayScore, stageData.ScoreRate); bestDatum.UpdateBestData(crown, stageData.ScoreRank, stageData.PlayScore, stageData.ScoreRate);
@ -302,23 +303,67 @@ public class PlayResultController : BaseController<PlayResultController>
} }
// TODO: AI battle // TODO: AI battle
/*private async Task UpdateAiBattleData(PlayResultRequest request, StageData stageData) private async Task UpdateAiBattleData(PlayResultRequest request, StageData stageData)
{ {
for (int i = 0; i < stageData.ArySectionDatas.Count; i++) var difficulty = (Difficulty)stageData.Level;
difficulty.Throw().IfOutOfRange();
var existing = await aiDatumService.GetSongAiScore(request.BaidConf,
stageData.SongNo, difficulty);
if (existing is null)
{ {
// Only update crown if it's a higher crown than the previous best crown var aiScoreDatum = new AiScoreDatum
{
Baid = request.BaidConf,
SongId = stageData.SongNo,
Difficulty = difficulty,
IsWin = stageData.IsWin
};
for (var index = 0; index < stageData.ArySectionDatas.Count; index++)
{
AddNewAiSectionScore(request, stageData, index, difficulty, aiScoreDatum);
}
// Maybe have a "SectionNo" variable for which section number it is on the DB await aiDatumService.InsertSongAiScore(aiScoreDatum);
// compare DB.SectionNo == i return;
// if any aspect of the section is higher than the previous best, update it
// Similar to Dan best play updates
} }
}*/
private static CrownType PlayResultToCrown(StageData stageData) for (var index = 0; index < stageData.ArySectionDatas.Count; index++)
{
var sectionData = stageData.ArySectionDatas[index];
if (index < existing.AiSectionScoreData.Count)
{
existing.AiSectionScoreData[index].UpdateBest(sectionData);
}
else
{
AddNewAiSectionScore(request,stageData,index,difficulty,existing);
}
}
await aiDatumService.UpdateSongAiScore(existing);
}
private static void AddNewAiSectionScore(PlayResultRequest request, StageData stageData, int index, Difficulty difficulty,
AiScoreDatum aiScoreDatum)
{ {
var crown = (CrownType)stageData.PlayResult; var sectionData = stageData.ArySectionDatas[index];
if (crown == CrownType.Gold && stageData.OkCnt == 0) var aiSectionScoreDatum = new AiSectionScoreDatum
{
Baid = request.BaidConf,
SongId = stageData.SongNo,
Difficulty = difficulty,
SectionIndex = index
};
aiSectionScoreDatum.UpdateBest(sectionData);
aiScoreDatum.AiSectionScoreData.Add(aiSectionScoreDatum);
}
private static CrownType PlayResultToCrown(uint playResult, uint okCount)
{
var crown = (CrownType)playResult;
if (crown == CrownType.Gold && okCount == 0)
{ {
crown = CrownType.Dondaful; crown = CrownType.Dondaful;
} }

View File

@ -1,4 +1,6 @@
namespace TaikoLocalServer.Entities; using SharedProject.Utils;
namespace TaikoLocalServer.Entities;
public class AiSectionScoreDatum public class AiSectionScoreDatum
{ {
@ -25,4 +27,21 @@ public class AiSectionScoreDatum
public uint DrumrollCount { get; set; } public uint DrumrollCount { get; set; }
public AiScoreDatum Parent { get; set; } = null!; public AiScoreDatum Parent { get; set; } = null!;
public void UpdateBest(PlayResultDataRequest.StageData.AiStageSectionData sectionData)
{
var crown = (CrownType)sectionData.Crown;
if (crown == CrownType.Gold && sectionData.OkCnt == 0)
{
crown = CrownType.Dondaful;
}
IsWin = sectionData.IsWin ? sectionData.IsWin : IsWin;
Crown = ValueHelpers.Max(crown, Crown);
Score = ValueHelpers.Max(sectionData.Score, Score);
GoodCount = ValueHelpers.Max(sectionData.GoodCnt, GoodCount);
OkCount = ValueHelpers.Min(sectionData.OkCnt, OkCount);
MissCount = ValueHelpers.Min(sectionData.NgCnt, MissCount);
DrumrollCount = ValueHelpers.Max(sectionData.PoundCnt, DrumrollCount);
}
} }

View File

@ -0,0 +1,50 @@
using TaikoLocalServer.Services.Interfaces;
using Throw;
namespace TaikoLocalServer.Services;
public class AiDatumService : IAiDatumService
{
private readonly TaikoDbContext context;
public AiDatumService(TaikoDbContext context)
{
this.context = context;
}
public async Task<List<AiScoreDatum>> GetAllAiScoreById(uint baid)
{
return await context.AiScoreData.Where(datum => datum.Baid == baid)
.Include(datum => datum.AiSectionScoreData)
.ToListAsync();
}
public async Task<AiScoreDatum?> GetSongAiScore(uint baid, uint songId, Difficulty difficulty)
{
return await context.AiScoreData.Where(datum => datum.Baid == baid &&
datum.SongId == songId &&
datum.Difficulty == difficulty)
.Include(datum => datum.AiSectionScoreData)
.FirstOrDefaultAsync();
}
public async Task UpdateSongAiScore(AiScoreDatum datum)
{
var existing = await context.AiScoreData.FindAsync(datum.Baid, datum.SongId, datum.Difficulty);
existing.ThrowIfNull("Cannot update a non-existing ai score!");
context.AiScoreData.Update(datum);
await context.SaveChangesAsync();
}
public async Task InsertSongAiScore(AiScoreDatum datum)
{
var existing = await context.AiScoreData.FindAsync(datum.Baid, datum.SongId, datum.Difficulty);
if (existing is not null)
{
throw new ArgumentException("Ai score already exists!", nameof(datum));
}
context.AiScoreData.Add(datum);
await context.SaveChangesAsync();
}
}

View File

@ -30,7 +30,7 @@ public class DanScoreDatumService : IDanScoreDatumService
var existing = await context.DanScoreData.FindAsync(datum.Baid, datum.DanId); var existing = await context.DanScoreData.FindAsync(datum.Baid, datum.DanId);
if (existing is null) if (existing is null)
{ {
await context.DanScoreData.AddAsync(datum); context.DanScoreData.Add(datum);
await context.SaveChangesAsync(); await context.SaveChangesAsync();
return; return;
} }

View File

@ -11,6 +11,7 @@ public static class ServiceExtensions
services.AddScoped<ISongPlayDatumService, SongPlayDatumService>(); services.AddScoped<ISongPlayDatumService, SongPlayDatumService>();
services.AddScoped<ISongBestDatumService, SongBestDatumService>(); services.AddScoped<ISongBestDatumService, SongBestDatumService>();
services.AddScoped<IDanScoreDatumService, DanScoreDatumService>(); services.AddScoped<IDanScoreDatumService, DanScoreDatumService>();
services.AddScoped<IAiDatumService, AiDatumService>();
return services; return services;
} }

View File

@ -0,0 +1,12 @@
namespace TaikoLocalServer.Services.Interfaces;
public interface IAiDatumService
{
public Task<List<AiScoreDatum>> GetAllAiScoreById(uint baid);
public Task<AiScoreDatum?> GetSongAiScore(uint baid, uint songId, Difficulty difficulty);
public Task UpdateSongAiScore(AiScoreDatum datum);
public Task InsertSongAiScore(AiScoreDatum datum);
}

View File

@ -32,7 +32,7 @@ public class SongBestDatumService : ISongBestDatumService
return; return;
} }
await context.SongBestData.AddAsync(datum); context.SongBestData.Add(datum);
await context.SaveChangesAsync(); await context.SaveChangesAsync();
} }

View File

@ -18,7 +18,7 @@ class SongPlayDatumService : ISongPlayDatumService
public async Task AddSongPlayDatum(SongPlayDatum datum) public async Task AddSongPlayDatum(SongPlayDatum datum)
{ {
await context.SongPlayData.AddAsync(datum); context.SongPlayData.Add(datum);
await context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
} }