Skip to content

Commit

Permalink
[PDX-401] Handle collection (#21)
Browse files Browse the repository at this point in the history
* Handle account address

* Bump lib9c

* Remove 0c

* Handle collection data
  • Loading branch information
Atralupus authored Mar 15, 2024
1 parent bd34fa0 commit a0ce995
Show file tree
Hide file tree
Showing 10 changed files with 267 additions and 200 deletions.
40 changes: 36 additions & 4 deletions NineChroniclesUtilBackend/Arena/ArenaBulkSimulator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
using Nekoyume.TableData;
using Nekoyume.Model.BattleStatus.Arena;
using Nekoyume.Arena;
using Libplanet.Crypto;
using Nekoyume.Model.Stat;
using Nekoyume.Model.State;
using SimulatorRandom = NineChroniclesUtilBackend.Random;
using SystemRandom = System.Random;

Expand All @@ -10,20 +13,25 @@ namespace NineChroniclesUtilBackend.Arena;
public class ArenaBulkSimulator
{
private readonly int _rounds;
private const int TotalSimulations = 700;
private const int TotalSimulations = 1000;

public ArenaBulkSimulator(int rounds = 5)
{
_rounds = rounds;
}

public async Task<double> BulkSimulate(AvatarStatesForArena myAvatar, AvatarStatesForArena enemyAvatar, ArenaSimulatorSheets simulatorSheets)
public async Task<double> BulkSimulate(
AvatarStatesForArena myAvatar,
AvatarStatesForArena enemyAvatar,
ArenaSimulatorSheets simulatorSheets,
Dictionary<Address, CollectionState> collectionStates,
CollectionSheet collectionSheets)
{
int winCount = 0;

var tasks = Enumerable.Range(0, TotalSimulations).Select(async _ =>
{
var arenaLog = SimulateBattle(myAvatar, enemyAvatar, simulatorSheets);
var arenaLog = SimulateBattle(myAvatar, enemyAvatar, simulatorSheets, collectionStates, collectionSheets);
if (arenaLog.Result == ArenaLog.ArenaResult.Win)
{
Interlocked.Increment(ref winCount);
Expand All @@ -35,16 +43,40 @@ public async Task<double> BulkSimulate(AvatarStatesForArena myAvatar, AvatarStat
return (double)winCount / TotalSimulations;
}

public ArenaLog SimulateBattle(AvatarStatesForArena myAvatar, AvatarStatesForArena enemyAvatar, ArenaSimulatorSheets simulatorSheets)
public ArenaLog SimulateBattle(
AvatarStatesForArena myAvatar,
AvatarStatesForArena enemyAvatar,
ArenaSimulatorSheets simulatorSheets,
Dictionary<Address, CollectionState> collectionStates,
CollectionSheet collectionSheets)
{
var seed = new SystemRandom().Next();
var random = new SimulatorRandom(seed);
var simulator = new ArenaSimulator(random, _rounds);

var modifiers = new Dictionary<Address, List<StatModifier>>
{
[myAvatar.AvatarState.address] = new(),
[enemyAvatar.AvatarState.address] = new(),
};
if (collectionStates.Count > 0)
{
foreach (var (address, state) in collectionStates)
{
var modifier = modifiers[address];
foreach (var collectionId in state.Ids)
{
modifier.AddRange(collectionSheets[collectionId].StatModifiers);
}
}
}

var arenaLog = simulator.Simulate(
new ArenaPlayerDigest(myAvatar.AvatarState, myAvatar.ItemSlotState.Equipments, myAvatar.ItemSlotState.Costumes, myAvatar.RuneStates),
new ArenaPlayerDigest(enemyAvatar.AvatarState, enemyAvatar.ItemSlotState.Equipments, enemyAvatar.ItemSlotState.Costumes, enemyAvatar.RuneStates),
simulatorSheets,
modifiers[myAvatar.AvatarState.address],
modifiers[enemyAvatar.AvatarState.address],
true);

return arenaLog;
Expand Down
126 changes: 23 additions & 103 deletions NineChroniclesUtilBackend/Controllers/ArenaController.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
using Bencodex.Types;
using Libplanet.Crypto;
using Microsoft.AspNetCore.Mvc;
using Nekoyume;
using Nekoyume.Action;
using Nekoyume.Model.EnumType;
using Nekoyume.Model.Item;
using Nekoyume.Model.State;
using Nekoyume.TableData;
using NineChroniclesUtilBackend.Arena;
using NineChroniclesUtilBackend.Models.Arena;
using NineChroniclesUtilBackend.Repositories;
using NineChroniclesUtilBackend.Services;
using NineChroniclesUtilBackend.Util;

namespace NineChroniclesUtilBackend.Controllers;

Expand All @@ -36,93 +31,16 @@ public async Task<ArenaSimulateResponse> Simulate(
IStateService stateService
)
{
async Task<T> GetSheet<T>()
where T : ISheet, new()
{
var sheetState = await stateService.GetState(
Addresses.TableSheet.Derive(typeof(T).Name)
);
if (sheetState is not Text sheetValue)
{
throw new ArgumentException(nameof(T));
}

var sheet = new T();
sheet.Set(sheetValue.Value);
return sheet;
}

async Task<AvatarState> GetAvatarState(Address avatarAddress)
{
var state = await stateService.GetState(avatarAddress);
if (state is not Dictionary dictionary)
{
throw new ArgumentException(nameof(avatarAddress));
}

var inventoryAddress = avatarAddress.Derive("inventory");
var inventoryState = await stateService.GetState(inventoryAddress);
if (inventoryState is not List list)
{
throw new ArgumentException(nameof(avatarAddress));
}

var inventory = new Inventory(list);

var avatarState = new AvatarState(dictionary) { inventory = inventory };

return avatarState;
}

async Task<ItemSlotState> GetItemSlotState(Address avatarAddress)
{
var state = await stateService.GetState(
ItemSlotState.DeriveAddress(avatarAddress, BattleType.Arena)
);
return state switch
{
List list => new ItemSlotState(list),
null => new ItemSlotState(BattleType.Arena),
_ => throw new ArgumentException(nameof(avatarAddress))
};
}

async Task<List<RuneState>> GetRuneStates(Address avatarAddress)
{
var state = await stateService.GetState(
RuneSlotState.DeriveAddress(avatarAddress, BattleType.Arena)
);
var runeSlotState = state switch
{
List list => new RuneSlotState(list),
null => new RuneSlotState(BattleType.Arena),
_ => throw new ArgumentException(nameof(avatarAddress))
};

var runes = new List<RuneState>();
foreach (
var runeStateAddress in runeSlotState
.GetEquippedRuneSlotInfos()
.Select(info => RuneState.DeriveAddress(avatarAddress, info.RuneId))
)
{
if (await stateService.GetState(runeStateAddress) is List list)
{
runes.Add(new RuneState(list));
}
}

return runes;
}
var stateGetter = new StateGetter(stateService);

var myAvatarAddress = new Address(arenaSimulateRequest.MyAvatarAddress);
var enemyAvatarAddress = new Address(arenaSimulateRequest.EnemyAvatarAddress);
var myAvatarState = await GetAvatarState(myAvatarAddress);
var myAvatarItemSlotState = await GetItemSlotState(myAvatarAddress);
var myAvatarRuneStates = await GetRuneStates(myAvatarAddress);
var enemyAvatarState = await GetAvatarState(enemyAvatarAddress);
var enemyAvatarItemSlotState = await GetItemSlotState(enemyAvatarAddress);
var enemyAvatarRuneStates = await GetRuneStates(enemyAvatarAddress);
var myAvatarState = await stateGetter.GetAvatarState(myAvatarAddress);
var myAvatarItemSlotState = await stateGetter.GetItemSlotState(myAvatarAddress);
var myAvatarRuneStates = await stateGetter.GetRuneStates(myAvatarAddress);
var enemyAvatarState = await stateGetter.GetAvatarState(enemyAvatarAddress);
var enemyAvatarItemSlotState = await stateGetter.GetItemSlotState(enemyAvatarAddress);
var enemyAvatarRuneStates = await stateGetter.GetRuneStates(enemyAvatarAddress);

var bulkSimulator = new ArenaBulkSimulator();
var result = await bulkSimulator.BulkSimulate(
Expand All @@ -133,19 +51,21 @@ var runeStateAddress in runeSlotState
enemyAvatarRuneStates
),
new ArenaSimulatorSheets(
await GetSheet<MaterialItemSheet>(),
await GetSheet<SkillSheet>(),
await GetSheet<SkillBuffSheet>(),
await GetSheet<StatBuffSheet>(),
await GetSheet<SkillActionBuffSheet>(),
await GetSheet<ActionBuffSheet>(),
await GetSheet<CharacterSheet>(),
await GetSheet<CharacterLevelSheet>(),
await GetSheet<EquipmentItemSetEffectSheet>(),
await GetSheet<CostumeStatSheet>(),
await GetSheet<WeeklyArenaRewardSheet>(),
await GetSheet<RuneOptionSheet>()
)
await stateGetter.GetSheet<MaterialItemSheet>(),
await stateGetter.GetSheet<SkillSheet>(),
await stateGetter.GetSheet<SkillBuffSheet>(),
await stateGetter.GetSheet<StatBuffSheet>(),
await stateGetter.GetSheet<SkillActionBuffSheet>(),
await stateGetter.GetSheet<ActionBuffSheet>(),
await stateGetter.GetSheet<CharacterSheet>(),
await stateGetter.GetSheet<CharacterLevelSheet>(),
await stateGetter.GetSheet<EquipmentItemSetEffectSheet>(),
await stateGetter.GetSheet<CostumeStatSheet>(),
await stateGetter.GetSheet<WeeklyArenaRewardSheet>(),
await stateGetter.GetSheet<RuneOptionSheet>()
),
await stateGetter.GetCollectionStates([myAvatarAddress, enemyAvatarAddress]),
await stateGetter.GetSheet<CollectionSheet>()
);

return new ArenaSimulateResponse(result);
Expand Down
2 changes: 1 addition & 1 deletion NineChroniclesUtilBackend/NineChroniclesUtilBackend.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="7.3.1" />
<PackageReference Include="MongoDB.Driver" Version="2.24.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
<PackageReference Include="Lib9c" Version="1.8.0-dev.4e2f5d9bfed5366a94e67b88c4b0e7171c0cd0d6" />
<PackageReference Include="Lib9c" Version="1.8.0-dev.04d6656b74af6476768a0f1f9980340ecccf12ee" />
<PackageReference Include="Libplanet" Version="4.1.0-dev.20242745721" />
<PackageReference Include="StrawberryShake" Version="13.8.1" />
<PackageReference Include="StrawberryShake.Transport.Http" Version="13.8.1" />
Expand Down
6 changes: 0 additions & 6 deletions NineChroniclesUtilBackend/Options/EmptyChronicleOption.cs

This file was deleted.

1 change: 0 additions & 1 deletion NineChroniclesUtilBackend/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

builder.Services.Configure<HeadlessStateServiceOption>(builder.Configuration.GetRequiredSection("StateService"));
builder.Services.Configure<DatabaseOption>(builder.Configuration.GetRequiredSection("Database"));
builder.Services.Configure<EmptyChronicleOption>(builder.Configuration.GetRequiredSection("EmptyChronicle"));
builder.Services.ConfigureHttpJsonOptions(options =>
options.SerializerOptions.Converters.Add(new JsonStringEnumConverter()));

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
using Microsoft.Extensions.Options;
using MongoDB.Bson;
using MongoDB.Driver;
using Nekoyume.Model.State;
using NineChroniclesUtilBackend.Models.Agent;
using NineChroniclesUtilBackend.Models.Arena;
using NineChroniclesUtilBackend.Options;
using NineChroniclesUtilBackend.Services;

namespace NineChroniclesUtilBackend.Repositories;

public class ArenaRankingRepository(MongoDBCollectionService mongoDBCollectionService, IOptions<EmptyChronicleOption> options)
public class ArenaRankingRepository(MongoDBCollectionService mongoDBCollectionService, IStateService stateService)
{
private CpRepository cpRepository = new CpRepository(options.Value.Endpoint);
private CpRepository cpRepository = new CpRepository(stateService);

private readonly IMongoCollection<dynamic> ArenaCollection =
mongoDBCollectionService.GetCollection<dynamic>("arena");
Expand Down
Loading

0 comments on commit a0ce995

Please sign in to comment.