Загрузка данных
using System;
using System.Collections.Generic;
using System.Numerics;
class Program
{
static void Main()
{
int players = 6;
int targetWins = 4;
// Текущее состояние: (0,1,0,3,2,2)
int[] start = { 0, 1, 0, 3, 2, 2 };
// Считаем количество продолжений, заканчивающихся ровно за k ходов
// и общее количество продолжений до конца игры.
var (byK, total, maxK) = CountFinishSequences(start, targetWins);
Console.WriteLine("Start state: (" + string.Join(",", start) + ")");
Console.WriteLine($"Target = {targetWins}, players = {players}");
Console.WriteLine($"Max additional moves = {maxK}");
Console.WriteLine();
Console.WriteLine("Nk = number of continuations that finish exactly in k moves:");
for (int k = 1; k <= maxK; k++)
{
Console.WriteLine($"k={k,2}: {byK.GetValueOrDefault(k)}");
}
Console.WriteLine();
Console.WriteLine($"Total N = sum_k Nk = {total}");
// Дополнительно: вероятность выигрыша каждого игрока,
// если на каждом ходе все 6 равновероятны (1/6).
var winProbs = WinProbabilities(start, targetWins);
Console.WriteLine();
Console.WriteLine("Win probabilities (each move picks a random player 1/6):");
for (int i = 0; i < players; i++)
Console.WriteLine($"Player {i + 1}: {winProbs[i]:P6}");
}
// Сериализация состояния в ключ словаря
static ulong PackState(int[] a)
{
// хватает 3 бит на число 0..7, у нас 0..4
// 6 игроков -> 18 бит
ulong key = 0;
for (int i = 0; i < a.Length; i++)
key |= ((ulong)a[i] & 7UL) << (i * 3);
return key;
}
static int[] UnpackState(ulong key, int players)
{
var a = new int[players];
for (int i = 0; i < players; i++)
a[i] = (int)((key >> (i * 3)) & 7UL);
return a;
}
static bool IsTerminal(int[] a, int targetWins, out int winnerIndex)
{
winnerIndex = -1;
for (int i = 0; i < a.Length; i++)
{
if (a[i] >= targetWins)
{
winnerIndex = i;
return true;
}
}
return false;
}
static (Dictionary<int, BigInteger> byK, BigInteger total, int maxK)
CountFinishSequences(int[] start, int targetWins)
{
int players = start.Length;
// maxK можно вычислить как:
// если r_i = target - a_i, то maxK = 1 + sum_j (r_j - 1)
int maxK = 1;
for (int i = 0; i < players; i++)
maxK += (targetWins - start[i] - 1);
var byK = new Dictionary<int, BigInteger>();
BigInteger total = BigInteger.Zero;
// dp: количество способов попасть в состояние после t ходов (не терминальное!)
var dp = new Dictionary<ulong, BigInteger>();
dp[PackState(start)] = BigInteger.One;
for (int t = 0; t < maxK; t++)
{
var next = new Dictionary<ulong, BigInteger>();
foreach (var kv in dp)
{
var state = UnpackState(kv.Key, players);
BigInteger ways = kv.Value;
// Из нетерминального состояния делаем 6 переходов (кто выиграл ход)
for (int p = 0; p < players; p++)
{
var s2 = (int[])state.Clone();
s2[p]++;
if (IsTerminal(s2, targetWins, out _))
{
int k = t + 1; // игра закончилась за k ходов
if (!byK.ContainsKey(k)) byK[k] = BigInteger.Zero;
byK[k] += ways;
total += ways;
}
else
{
ulong key2 = PackState(s2);
if (!next.ContainsKey(key2)) next[key2] = BigInteger.Zero;
next[key2] += ways;
}
}
}
dp = next;
if (dp.Count == 0) break; // все ветки уже завершились
}
return (byK, total, maxK);
}
// Вероятности выигрыша при равновероятном выборе игрока на каждом ходе (1/6).
// Решаем систему уравнений динамикой: P(state) = avg P(nextState)
// Для терминального состояния вероятность 1 у победителя, 0 у остальных.
static double[] WinProbabilities(int[] start, int targetWins)
{
int players = start.Length;
// Соберём все достижимые нетерминальные состояния из start (BFS),
// чтобы потом итерационно решить уравнения.
var states = new List<ulong>();
var index = new Dictionary<ulong, int>();
var q = new Queue<ulong>();
ulong startKey = PackState(start);
q.Enqueue(startKey);
index[startKey] = 0;
states.Add(startKey);
while (q.Count > 0)
{
ulong key = q.Dequeue();
var s = UnpackState(key, players);
// если терминальное — не расширяем
if (IsTerminal(s, targetWins, out _)) continue;
for (int p = 0; p < players; p++)
{
var s2 = (int[])s.Clone();
s2[p]++;
ulong k2 = PackState(s2);
if (!index.ContainsKey(k2))
{
index[k2] = states.Count;
states.Add(k2);
q.Enqueue(k2);
}
}
}
int n = states.Count;
// probs[stateIndex][player]
var probs = new double[n, players];
// Инициализация: для терминальных состояний фиксируем 1 у победителя.
for (int si = 0; si < n; si++)
{
var s = UnpackState(states[si], players);
if (IsTerminal(s, targetWins, out int w))
probs[si, w] = 1.0;
}
// Итерации значения (value iteration). Состояний немного, сходится быстро.
for (int iter = 0; iter < 20000; iter++)
{
double maxDiff = 0.0;
for (int si = 0; si < n; si++)
{
var s = UnpackState(states[si], players);
if (IsTerminal(s, targetWins, out _)) continue;
// newP = average over 6 next states
var newP = new double[players];
for (int p = 0; p < players; p++)
{
var s2 = (int[])s.Clone();
s2[p]++;
int sj = index[PackState(s2)];
for (int pl = 0; pl < players; pl++)
newP[pl] += probs[sj, pl] / players;
}
for (int pl = 0; pl < players; pl++)
{
double diff = Math.Abs(newP[pl] - probs[si, pl]);
if (diff > maxDiff) maxDiff = diff;
probs[si, pl] = newP[pl];
}
}
if (maxDiff < 1e-12) break;
}
int startIdx = index[startKey];
var result = new double[players];
for (int pl = 0; pl < players; pl++)
result[pl] = probs[startIdx, pl];
return result;
}
}