Загрузка данных


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