Files

59 lines
2.4 KiB
C#
Raw Permalink Normal View History

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;
}
}