1
0
mirror of synced 2024-11-23 22:41:01 +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 IAiDatumService aiDatumService;
public BaidController(IUserDatumService userDatumService, ICardService cardService,
ISongBestDatumService songBestDatumService, IDanScoreDatumService danScoreDatumService)
ISongBestDatumService songBestDatumService, IDanScoreDatumService danScoreDatumService, IAiDatumService aiDatumService)
{
this.userDatumService = userDatumService;
this.cardService = cardService;
this.songBestDatumService = songBestDatumService;
this.danScoreDatumService = danScoreDatumService;
this.aiDatumService = aiDatumService;
}
@ -112,6 +115,9 @@ public class BaidController : BaseController<BaidController>
var genericInfoFlgLength = genericInfoFlg.Any()? genericInfoFlg.Max() + 1 : 0;
var genericInfoFlgArray = FlagCalculator.GetBitArrayFromIds(genericInfoFlg, (int)genericInfoFlgLength, Logger);
var aiScores = await aiDatumService.GetAllAiScoreById(baid);
var totalWin = aiScores.Count(datum => datum.IsWin);
response = new BAIDResponse
{
Result = 1,
@ -159,7 +165,7 @@ public class BaidController : BaseController<BaidController>
LastPlayMode = userData.LastPlayMode,
IsDispSouuchiOn = true,
AiRank = 0,
AiTotalWin = 0,
AiTotalWin = (uint)totalWin,
Accesstoken = "123456",
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")]
[ApiController]
public class GetAiDataController : BaseController<GetAiDataController>
{
private readonly IAiDatumService aiDatumService;
public GetAiDataController(IAiDatumService aiDatumService)
{
this.aiDatumService = aiDatumService;
}
[HttpPost]
[Produces("application/protobuf")]
public IActionResult GetAiData([FromBody] GetAiDataRequest request)
public async Task<IActionResult> GetAiData([FromBody] GetAiDataRequest request)
{
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
{
Result = 1,
TotalWinnings = 0
TotalWinnings = (uint)totalWin
};
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")]
[ApiController]
public class GetAiScoreController : BaseController<GetAiScoreController>
{
private readonly IAiDatumService aiDatumService;
public GetAiScoreController(IAiDatumService aiDatumService)
{
this.aiDatumService = aiDatumService;
}
[HttpPost]
[Produces("application/protobuf")]
public IActionResult GetAiScore([FromBody] GetAiScoreRequest request)
public async Task<IActionResult> GetAiScore([FromBody] GetAiScoreRequest request)
{
Logger.LogInformation("GetAiScore request : {Request}", request.Stringify());
@ -15,6 +25,30 @@ public class GetAiScoreController : BaseController<GetAiScoreController>
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
// SectionNo doesn't seem to actually affect which section is being assigned to, only the List order matters
/*response.AryBestSectionDatas.Add(new GetAiScoreResponse.AiBestSectionData()

View File

@ -20,13 +20,16 @@ public class PlayResultController : BaseController<PlayResultController>
private readonly IDanScoreDatumService danScoreDatumService;
private readonly IAiDatumService aiDatumService;
public PlayResultController(IUserDatumService userDatumService, ISongPlayDatumService songPlayDatumService,
ISongBestDatumService songBestDatumService, IDanScoreDatumService danScoreDatumService)
ISongBestDatumService songBestDatumService, IDanScoreDatumService danScoreDatumService, IAiDatumService aiDatumService)
{
this.userDatumService = userDatumService;
this.songPlayDatumService = songPlayDatumService;
this.songBestDatumService = songBestDatumService;
this.danScoreDatumService = danScoreDatumService;
this.aiDatumService = aiDatumService;
}
[HttpPost]
@ -77,9 +80,7 @@ public class PlayResultController : BaseController<PlayResultController>
if (playMode == PlayMode.AiBattle)
{
// 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 UpdateAiBattleData(request, stageData);
}
await UpdateBestData(request, stageData, bestData);
@ -163,7 +164,7 @@ public class PlayResultController : BaseController<PlayResultController>
ComboCount = stageData.ComboCnt,
HitCount = stageData.HitCnt,
DrumrollCount = stageData.PoundCnt,
Crown = PlayResultToCrown(stageData),
Crown = PlayResultToCrown(stageData.PlayResult, stageData.OkCnt),
Score = stageData.PlayScore,
ScoreRate = stageData.ScoreRate,
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
var crown = PlayResultToCrown(stageData);
var crown = PlayResultToCrown(stageData.PlayResult, stageData.OkCnt);
bestDatum.UpdateBestData(crown, stageData.ScoreRank, stageData.PlayScore, stageData.ScoreRate);
@ -302,23 +303,67 @@ public class PlayResultController : BaseController<PlayResultController>
}
// 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++)
{
// Only update crown if it's a higher crown than the previous best crown
var difficulty = (Difficulty)stageData.Level;
difficulty.Throw().IfOutOfRange();
var existing = await aiDatumService.GetSongAiScore(request.BaidConf,
stageData.SongNo, difficulty);
// Maybe have a "SectionNo" variable for which section number it is on the DB
// compare DB.SectionNo == i
// if any aspect of the section is higher than the previous best, update it
// Similar to Dan best play updates
if (existing is null)
{
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);
}
}*/
private static CrownType PlayResultToCrown(StageData stageData)
await aiDatumService.InsertSongAiScore(aiScoreDatum);
return;
}
for (var index = 0; index < stageData.ArySectionDatas.Count; index++)
{
var crown = (CrownType)stageData.PlayResult;
if (crown == CrownType.Gold && stageData.OkCnt == 0)
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 sectionData = stageData.ArySectionDatas[index];
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;
}

View File

@ -1,4 +1,6 @@
namespace TaikoLocalServer.Entities;
using SharedProject.Utils;
namespace TaikoLocalServer.Entities;
public class AiSectionScoreDatum
{
@ -25,4 +27,21 @@ public class AiSectionScoreDatum
public uint DrumrollCount { get; set; }
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);
if (existing is null)
{
await context.DanScoreData.AddAsync(datum);
context.DanScoreData.Add(datum);
await context.SaveChangesAsync();
return;
}

View File

@ -11,6 +11,7 @@ public static class ServiceExtensions
services.AddScoped<ISongPlayDatumService, SongPlayDatumService>();
services.AddScoped<ISongBestDatumService, SongBestDatumService>();
services.AddScoped<IDanScoreDatumService, DanScoreDatumService>();
services.AddScoped<IAiDatumService, AiDatumService>();
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;
}
await context.SongBestData.AddAsync(datum);
context.SongBestData.Add(datum);
await context.SaveChangesAsync();
}

View File

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