#pragma warning disable
class Program
{
static string[] input;
static int P1Start;
static int P2Start;
public static void Main()
{
input = File.ReadAllLines(@"../../../../Input.in");
P1Start = (int)Char.GetNumericValue(input[0][^1]);
P2Start = (int)Char.GetNumericValue(input[1][^1]);
var die = new DeterministicDie();
var players = new List<Player>
{
new Player(P1Start),
new Player(P2Start),
};
var CurrentPlayer = 0;
while (players.All(p => p.CurScore < 1000))
{
players[CurrentPlayer].Move(die.Roll() + die.Roll() + die.Roll());
CurrentPlayer = (CurrentPlayer == 0) ? 1 : 0;
}
var losingPlayer = players.OrderBy(p => p.CurScore).First();
Console.WriteLine("The answer to part 1 is: " + (losingPlayer.CurScore * die.TimesRolled).ToString());
Dictionary<int, int> diracRolls = DiracRoll();
var startUniverse = (new Player(P1Start), new Player(P2Start), 0);
var universes = new Dictionary<(Player P1, Player P2, int CurPlayer), long>()
{
[startUniverse] = 1
};
long P1Wins = 0;
long P2Wins = 0;
var winningScore = 21;
while (universes.Count > 0)
{
var nextUniverses = new Dictionary<(Player, Player, int), long>();
foreach (var (universe, universeCount) in universes)
{
foreach (var (roll, timesRolled) in diracRolls)
{
Player newP1 = universe.P1.Copy();
Player newP2 = universe.P2.Copy();
if (universe.CurPlayer == 0)
{
newP1.Move(roll);
if (newP1.CurScore >= winningScore)
{
P1Wins += universeCount * timesRolled;
continue;
}
}
else
{
newP2.Move(roll);
if (newP2.CurScore >= winningScore)
{
P2Wins += universeCount * timesRolled;
continue;
}
}
var CurPlayer = (universe.CurPlayer + 1) % 2;
var newUniverse = (newP1, newP2, CurPlayer);
if (!nextUniverses.ContainsKey(newUniverse)) nextUniverses[newUniverse] = 0;
nextUniverses[newUniverse] += universeCount * timesRolled;
}
}
universes = nextUniverses;
}
Console.WriteLine("The winning player wins in " + Math.Max(P1Wins, P2Wins).ToString() + " universes");
}
class DeterministicDie
{
public int TimesRolled = 0;
public int LastRoll = 100;
public int Roll()
{
TimesRolled++;
LastRoll++;
if (LastRoll > 100) LastRoll %= 100;
return LastRoll;
}
}
class Player
{
public int CurSpace;
public int CurScore;
public static bool operator ==(Player p1, Player p2)
{
return (p1.CurSpace == p2.CurSpace) && (p1.CurScore == p2.CurScore);
}
public static bool operator !=(Player p1, Player p2)
{
return !(p1 == p2);
}
public override int GetHashCode()
{
return (CurSpace, CurScore).GetHashCode();
}
public override bool Equals(object obj)
{
return this == (Player)obj;
}
public override string ToString() => $"Space: {CurSpace}, Score: {CurScore}";
public void Move(int spaces)
{
CurSpace += spaces;
if (CurSpace > 10) CurSpace %= 10;
if (CurSpace == 0) CurSpace = 10;
CurScore += CurSpace;
}
public Player Copy()
{
return new Player(CurSpace, CurScore);
}
public Player(int startSpace, int startScore = 0)
{
CurSpace = startSpace;
CurScore = startScore;
}
}
public static Dictionary<int, int> DiracRoll()
{
var ans = new Dictionary<int, int>();
foreach (var d1 in Enumerable.Range(1, 3))
{
foreach (var d2 in Enumerable.Range(1, 3))
{
foreach (var d3 in Enumerable.Range(1, 3))
{
var CurRoll = d1 + d2 + d3;
if (!ans.ContainsKey(CurRoll)) ans[CurRoll] = 0;
ans[CurRoll] += 1;
}
}
}
return ans;
}
}