using Chessistics.Engine.Events; using Chessistics.Engine.Model; namespace Chessistics.Engine.Rules; public static class TransferResolver { public static List ResolveTransfers(BoardState state) { var events = new List(); var participated = new HashSet(); // piece IDs that already gave or received var productionGave = new HashSet(); // productions that already gave // Phase A: Productions give to adjacent pieces ResolveProductionTransfers(state, events, participated, productionGave); // Phase B: Pieces give to demands or other pieces ResolvePieceTransfers(state, events, participated); return events; } private static void ResolveProductionTransfers( BoardState state, List events, HashSet participated, HashSet productionGave) { // Sort productions deterministically (by position) var productions = state.Productions.Values .Where(p => state.ProductionBuffers[p.Position] != null) .OrderBy(p => p.Position.Col).ThenBy(p => p.Position.Row) .ToList(); foreach (var prod in productions) { var cargoType = state.ProductionBuffers[prod.Position]!.Value; // Find adjacent pieces without cargo, sorted by receiver priority var receivers = GetAdjacentPiecesWithoutCargo(state, prod.Position, participated); if (receivers.Count == 0) continue; var receiver = receivers[0]; receiver.Cargo = cargoType; state.ProductionBuffers[prod.Position] = null; participated.Add(receiver.Id); productionGave.Add(prod.Position); events.Add(new CargoTransferredEvent( prod.Position, receiver.CurrentCell, cargoType, GivingPieceId: null, ReceivingPieceId: receiver.Id)); } } private static void ResolvePieceTransfers( BoardState state, List events, HashSet participated) { // Get all pieces with cargo that haven't participated, sorted by giver priority var givers = state.Pieces .Where(p => p.Cargo != null && !participated.Contains(p.Id)) .OrderByDescending(p => p.SocialStatus) .ThenBy(p => MinDistanceToProduction(p.CurrentCell, state)) .ThenBy(p => p.PlacementOrder) .ToList(); foreach (var giver in givers) { if (participated.Contains(giver.Id)) continue; var cargoType = giver.Cargo!.Value; // Priority 1: deliver to adjacent demand var adjacentDemand = GetAdjacentCompatibleDemand(state, giver.CurrentCell, cargoType); if (adjacentDemand != null) { giver.Cargo = null; adjacentDemand.ReceivedCount++; participated.Add(giver.Id); events.Add(new CargoTransferredEvent( giver.CurrentCell, adjacentDemand.Position, cargoType, GivingPieceId: giver.Id, ReceivingPieceId: null)); events.Add(new DemandProgressEvent( adjacentDemand.Position, adjacentDemand.Name, adjacentDemand.ReceivedCount, adjacentDemand.Required)); continue; } // Priority 2: transfer to adjacent piece without cargo var receivers = GetAdjacentPiecesWithoutCargo(state, giver.CurrentCell, participated); if (receivers.Count == 0) continue; var receiver = receivers[0]; receiver.Cargo = cargoType; giver.Cargo = null; participated.Add(giver.Id); participated.Add(receiver.Id); events.Add(new CargoTransferredEvent( giver.CurrentCell, receiver.CurrentCell, cargoType, GivingPieceId: giver.Id, ReceivingPieceId: receiver.Id)); } } private static List GetAdjacentPiecesWithoutCargo( BoardState state, Coords position, HashSet participated) { var adjacent = position.GetAdjacent4(state.Width, state.Height); return state.Pieces .Where(p => p.Cargo == null && !participated.Contains(p.Id) && adjacent.Contains(p.CurrentCell)) .OrderByDescending(p => p.SocialStatus) .ThenBy(p => MinDistanceToProduction(p.CurrentCell, state)) .ThenBy(p => p.PlacementOrder) .ToList(); } private static DemandState? GetAdjacentCompatibleDemand( BoardState state, Coords position, CargoType cargoType) { var adjacent = position.GetAdjacent4(state.Width, state.Height); return state.Demands.Values .Where(d => !d.IsSatisfied && d.Cargo == cargoType && adjacent.Contains(d.Position)) .FirstOrDefault(); } private static int MinDistanceToProduction(Coords cell, BoardState state) { if (state.Productions.Count == 0) return int.MaxValue; return state.Productions.Keys.Min(p => cell.ManhattanDistance(p)); } }