feat(svc): ChooseClassAsync + ChooseCardAsync (draft state machine)
Implements the class-selection and card-pick turns for the Take Two arena draft: - ChooseClassAsync validates class is offered, locks ClassId, generates first pick set via pool - ChooseCardAsync appends the two picked cards, advances SelectTurn 1–15, completes draft at turn 15 - 6 new tests covering happy paths and all error codes (class_not_offered, invalid_state, invalid_selection) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -142,8 +142,82 @@ public class ArenaTwoPickService : IArenaTwoPickService
|
||||
.AsSplitQuery()
|
||||
.FirstAsync(v => v.Id == viewerId);
|
||||
}
|
||||
public Task<ClassChooseResponseDto> ChooseClassAsync(long viewerId, int classId) => throw new NotImplementedException();
|
||||
public Task<CardChooseResponseDto> ChooseCardAsync(long viewerId, long selectedId) => throw new NotImplementedException();
|
||||
public async Task<ClassChooseResponseDto> ChooseClassAsync(long viewerId, int classId)
|
||||
{
|
||||
var run = await _runs.GetByViewerIdAsync(viewerId)
|
||||
?? throw new ArenaTwoPickException("arena_two_pick_no_active_run");
|
||||
if (run.ClassId != 0)
|
||||
throw new ArenaTwoPickException("arena_two_pick_invalid_state");
|
||||
var candidates = JsonSerializer.Deserialize<List<int>>(run.CandidateClassIdsJson) ?? new();
|
||||
if (!candidates.Contains(classId))
|
||||
throw new ArenaTwoPickException("arena_two_pick_class_not_offered");
|
||||
|
||||
run.ClassId = classId;
|
||||
run.LeaderSkinId = ResolveClassDefaultLeaderSkin(classId);
|
||||
var pairs = _pool.GeneratePickSetsForTurn(classId, turn: 1, startingPairId: run.NextCandidateId, _rng);
|
||||
run.NextCandidateId += pairs.Count;
|
||||
run.SelectTurn = 1;
|
||||
run.PendingPickSetsJson = JsonSerializer.Serialize(pairs);
|
||||
await _runs.UpsertAsync(run);
|
||||
|
||||
return new ClassChooseResponseDto
|
||||
{
|
||||
ClassInfo = ProjectClassInfo(run),
|
||||
DeckInfo = ProjectDeckInfo(run),
|
||||
CandidateCardList = pairs.Select(p => new CandidatePairDto
|
||||
{
|
||||
Id = p.Id, Turn = p.Turn, SetNum = p.SetNum,
|
||||
CardId1 = p.CardId1, CardId2 = p.CardId2,
|
||||
IsSelected = p.IsSelected ? 1 : 0,
|
||||
}).ToList(),
|
||||
};
|
||||
}
|
||||
|
||||
// Placeholder: class default skin = class id. Matches the capture's "leader_skin_id":"1" when class_id=1.
|
||||
private static long ResolveClassDefaultLeaderSkin(int classId) => classId;
|
||||
|
||||
public async Task<CardChooseResponseDto> ChooseCardAsync(long viewerId, long selectedId)
|
||||
{
|
||||
var run = await _runs.GetByViewerIdAsync(viewerId)
|
||||
?? throw new ArenaTwoPickException("arena_two_pick_no_active_run");
|
||||
if (run.ClassId == 0 || run.IsSelectCompleted)
|
||||
throw new ArenaTwoPickException("arena_two_pick_invalid_state");
|
||||
|
||||
var pending = JsonSerializer.Deserialize<List<CandidatePair>>(run.PendingPickSetsJson) ?? new();
|
||||
var pick = pending.FirstOrDefault(p => p.Id == selectedId)
|
||||
?? throw new ArenaTwoPickException("arena_two_pick_invalid_selection");
|
||||
|
||||
var selectedCards = JsonSerializer.Deserialize<List<long>>(run.SelectedCardIdsJson) ?? new();
|
||||
selectedCards.Add(pick.CardId1);
|
||||
selectedCards.Add(pick.CardId2);
|
||||
run.SelectedCardIdsJson = JsonSerializer.Serialize(selectedCards);
|
||||
|
||||
List<CandidatePair>? nextPairs = null;
|
||||
if (run.SelectTurn < 15)
|
||||
{
|
||||
run.SelectTurn += 1;
|
||||
nextPairs = _pool.GeneratePickSetsForTurn(run.ClassId, run.SelectTurn, run.NextCandidateId, _rng);
|
||||
run.NextCandidateId += nextPairs.Count;
|
||||
run.PendingPickSetsJson = JsonSerializer.Serialize(nextPairs);
|
||||
}
|
||||
else
|
||||
{
|
||||
run.IsSelectCompleted = true;
|
||||
run.PendingPickSetsJson = "[]";
|
||||
}
|
||||
await _runs.UpsertAsync(run);
|
||||
|
||||
return new CardChooseResponseDto
|
||||
{
|
||||
DeckInfo = ProjectDeckInfo(run),
|
||||
CandidateCardList = nextPairs?.Select(p => new CandidatePairDto
|
||||
{
|
||||
Id = p.Id, Turn = p.Turn, SetNum = p.SetNum,
|
||||
CardId1 = p.CardId1, CardId2 = p.CardId2,
|
||||
IsSelected = p.IsSelected ? 1 : 0,
|
||||
}).ToList(),
|
||||
};
|
||||
}
|
||||
public Task<FinishResponseDto> RetireAsync(long viewerId) => throw new NotImplementedException();
|
||||
public Task<FinishResponseDto> FinishAsync(long viewerId) => throw new NotImplementedException();
|
||||
public Task<BattleFinishResultDto> RecordBattleResultAsync(long viewerId, bool isWin) => throw new NotImplementedException();
|
||||
|
||||
Reference in New Issue
Block a user