59 lines
2.4 KiB
C#
59 lines
2.4 KiB
C#
using System;
|
|||
|
|
using System.Collections.Generic;
|
||
|
|
|
||
|
|
// МОДУЛЬ АРМИИ: войска и их распределение. Документ: Docs/Modules/Army.md
|
||
|
|
// Без Unity: правила деления войск одинаковы на клиенте и на сервере.
|
||
|
|
|
||
|
|
public static class ArmyMath
|
||
|
|
{
|
||
|
|
// Сколько воинов каждого типа взять, чтобы набрать count, пропорционально составу армии.
|
||
|
|
// Остаток округления раздаётся типам с наибольшей дробной частью. null -- столько воинов нет.
|
||
|
|
public static int[] TakeProportional(int[] troops, int count)
|
||
|
|
{
|
||
|
|
int total = 0;
|
||
|
|
foreach (var t in troops) total += t;
|
||
|
|
if (count <= 0 || count > total) return null;
|
||
|
|
|
||
|
|
var taken = new int[troops.Length];
|
||
|
|
int given = 0;
|
||
|
|
var remainders = new List<KeyValuePair<int, double>>();
|
||
|
|
for (int i = 0; i < troops.Length; i++)
|
||
|
|
{
|
||
|
|
double exact = (double)count * troops[i] / total;
|
||
|
|
taken[i] = Math.Min(troops[i], (int)Math.Floor(exact));
|
||
|
|
given += taken[i];
|
||
|
|
remainders.Add(new KeyValuePair<int, double>(i, exact - taken[i]));
|
||
|
|
}
|
||
|
|
remainders.Sort((a, b) => b.Value.CompareTo(a.Value));
|
||
|
|
for (int k = 0; given < count && k < remainders.Count * 2; k++)
|
||
|
|
{
|
||
|
|
int i = remainders[k % remainders.Count].Key;
|
||
|
|
if (taken[i] < troops[i]) { taken[i]++; given++; }
|
||
|
|
}
|
||
|
|
return taken;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Как разложить survivors выживших по типам, если в поход ушли sentUnits.
|
||
|
|
// Остаток округления -- в самый многочисленный тип похода.
|
||
|
|
public static int[] SplitSurvivors(int[] sentUnits, int survivors)
|
||
|
|
{
|
||
|
|
var back = new int[sentUnits.Length];
|
||
|
|
survivors = Math.Max(0, survivors);
|
||
|
|
|
||
|
|
int sent = 0;
|
||
|
|
foreach (var u in sentUnits) sent += u;
|
||
|
|
|
||
|
|
int given = 0;
|
||
|
|
for (int i = 0; i < sentUnits.Length; i++)
|
||
|
|
{
|
||
|
|
back[i] = sent > 0 ? (int)Math.Floor((double)survivors * sentUnits[i] / sent) : 0;
|
||
|
|
given += back[i];
|
||
|
|
}
|
||
|
|
|
||
|
|
int best = 0;
|
||
|
|
for (int i = 1; i < sentUnits.Length; i++) if (sentUnits[i] > sentUnits[best]) best = i;
|
||
|
|
if (back.Length > 0) back[best] += survivors - given;
|
||
|
|
return back;
|
||
|
|
}
|
||
|
|
}
|