Day 21 part 1 and WIP for part 2
I also improved the main startup code to require a little less maintenance.
This commit is contained in:
@ -78,6 +78,9 @@
|
||||
<None Update="inputs\20.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="inputs\21.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
2
inputs/21.txt
Normal file
2
inputs/21.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Player 1 starting position: 4
|
||||
Player 2 starting position: 8
|
134
src/21.cs
Normal file
134
src/21.cs
Normal file
@ -0,0 +1,134 @@
|
||||
namespace aoc2021;
|
||||
|
||||
internal class Day21 : Day
|
||||
{
|
||||
private record struct GameState((int p1, int p2) Positions, int Turn, (int p1, int p2) Score, (long p1, long p2) Wins, int RollVal, int TotalRolls, int TotalRounds)
|
||||
{
|
||||
public override int GetHashCode() => HashCode.Combine(Positions.p1, Positions.p2, Turn, RollVal, TotalRounds);
|
||||
}
|
||||
|
||||
internal override void Go()
|
||||
{
|
||||
var lines = Util.ReadAllLines("inputs/21.txt");
|
||||
var player1Pos = int.Parse(lines.ElementAt(0).Split(": ")[1]);
|
||||
var player2Pos = int.Parse(lines.ElementAt(1).Split(": ")[1]);
|
||||
Part1(player1Pos, player2Pos);
|
||||
Part2(player1Pos, player2Pos);
|
||||
}
|
||||
|
||||
private static void Part1(int player1Pos, int player2Pos)
|
||||
{
|
||||
using var t = new Timer();
|
||||
|
||||
var playerPos = new int[2]
|
||||
{
|
||||
player1Pos,
|
||||
player2Pos,
|
||||
};
|
||||
var (playerScore, numRolls) = PlayGame(playerPos, 1000, 10);
|
||||
|
||||
t.Stop();
|
||||
Logger.Log($"<+black>> part1: <+white>{numRolls * playerScore.Min()}<r>");
|
||||
}
|
||||
|
||||
private static void Part2(int player1Pos, int player2Pos)
|
||||
{
|
||||
using var t = new Timer();
|
||||
|
||||
var playerPos = new int[2]
|
||||
{
|
||||
player1Pos,
|
||||
player2Pos,
|
||||
};
|
||||
var (p1wins, p2wins) = PlayQuantumGame(new List<int>(playerPos), 21);
|
||||
|
||||
t.Stop();
|
||||
Logger.Log($"<+black>> part2: p1: {p1wins:N0}, p2: {p2wins:N0} -> <+white>{Math.Max(p1wins, p2wins)}<r>");
|
||||
}
|
||||
|
||||
private static (long[] scores, long numRolls) PlayGame(int[] playerPos, int maxScore, int dieSides)
|
||||
{
|
||||
var playerScore = new long[2]
|
||||
{
|
||||
0,
|
||||
0,
|
||||
};
|
||||
|
||||
int dieVal = 1;
|
||||
int turn = 0;
|
||||
long numRolls = 0;
|
||||
while (!playerScore.Any(x => x >= maxScore))
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
playerPos[turn] = PlayOneRoll(playerPos[turn], dieVal);
|
||||
dieVal = (dieVal + 1) % dieSides;
|
||||
numRolls++;
|
||||
}
|
||||
|
||||
playerScore[turn] += playerPos[turn];
|
||||
turn = 1 - turn;
|
||||
}
|
||||
|
||||
return (playerScore, numRolls);
|
||||
}
|
||||
|
||||
private static int PlayOneRoll(int playerPos, int dieVal)
|
||||
{
|
||||
return ((playerPos + dieVal - 1) % 10) + 1;
|
||||
}
|
||||
|
||||
private static readonly Dictionary<int, GameState> cachedWinCases = new();
|
||||
|
||||
private static (long, long) PlayQuantumGame(List<int> playerPos, int maxScore, List<int>? playerScores = null, int turn = 0, int rollNum = 0, int rollVal = 0, int totalRounds = 0, int totalRolls = 0)
|
||||
{
|
||||
playerScores ??= new List<int> { 0, 0 };
|
||||
if (cachedWinCases.TryGetValue(HashCode.Combine(playerPos[0], playerPos[1], turn, rollVal, totalRounds), out GameState winState))
|
||||
{
|
||||
return winState.Wins;
|
||||
}
|
||||
|
||||
var wins = (0L, 0L);
|
||||
while (true)
|
||||
{
|
||||
totalRounds++;
|
||||
for (int i = rollNum; i < 3; i++)
|
||||
{
|
||||
totalRolls++;
|
||||
var twoWins = PlayQuantumGame(new List<int>(playerPos), maxScore, new List<int>(playerScores), turn, i + 1, 2, totalRounds, totalRolls);
|
||||
var threeWins = PlayQuantumGame(new List<int>(playerPos), maxScore, new List<int>(playerScores), turn, i + 1, 3, totalRounds, totalRolls);
|
||||
wins = (wins.Item1 + twoWins.Item1 + threeWins.Item1, wins.Item2 + twoWins.Item2 + threeWins.Item2);
|
||||
playerPos[turn] = PlayOneRoll(playerPos[turn], 1);
|
||||
rollVal = 1;
|
||||
}
|
||||
if (rollNum == 3)
|
||||
{
|
||||
playerPos[turn] = PlayOneRoll(playerPos[turn], rollVal);
|
||||
}
|
||||
|
||||
playerScores[turn] += playerPos[turn];
|
||||
if (playerScores[turn] >= maxScore)
|
||||
{
|
||||
if (turn == 0)
|
||||
{
|
||||
wins.Item1++;
|
||||
}
|
||||
else
|
||||
{
|
||||
wins.Item2++;
|
||||
}
|
||||
|
||||
GameState state = new((playerPos[0], playerPos[1]), turn, (playerScores[0], playerScores[1]), wins, rollVal, totalRolls, totalRounds);
|
||||
cachedWinCases[state.GetHashCode()] = state;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
turn = 1 - turn;
|
||||
rollNum = 0;
|
||||
rollVal = 0;
|
||||
}
|
||||
|
||||
return wins;
|
||||
}
|
||||
}
|
48
src/main.cs
48
src/main.cs
@ -1,14 +1,14 @@
|
||||
using aoc2021;
|
||||
|
||||
var arg = args.FirstOrDefault();
|
||||
if (arg == "all")
|
||||
{
|
||||
var types = System.Reflection.Assembly
|
||||
var types = System.Reflection.Assembly
|
||||
.GetExecutingAssembly()
|
||||
.GetTypes()
|
||||
.Where(t => t.IsSubclassOf(typeof(Day)) && !t.IsAbstract && t.Name != "DayTemplate")
|
||||
.OrderBy(t => t.Name);
|
||||
|
||||
var arg = args.FirstOrDefault();
|
||||
if (arg == "all")
|
||||
{
|
||||
foreach (var type in types)
|
||||
{
|
||||
using var day = (Day)Activator.CreateInstance(type)!;
|
||||
@ -17,28 +17,22 @@ if (arg == "all")
|
||||
}
|
||||
else
|
||||
{
|
||||
using Day day = arg switch
|
||||
Day? day = null;
|
||||
if (string.IsNullOrEmpty(arg))
|
||||
{
|
||||
"1" => new Day01(),
|
||||
"2" => new Day02(),
|
||||
"3" => new Day03(),
|
||||
//"4" => new Day04(),
|
||||
"5" => new Day05(),
|
||||
"6" => new Day06(),
|
||||
"7" => new Day07(),
|
||||
"8" => new Day08(),
|
||||
"9" => new Day09(),
|
||||
"10" => new Day10(),
|
||||
"11" => new Day11(),
|
||||
"12" => new Day12(),
|
||||
"13" => new Day13(),
|
||||
"14" => new Day14(),
|
||||
"15" => new Day15(),
|
||||
"16" => new Day16(),
|
||||
"17"=> new Day17(),
|
||||
"18" => new Day18(),
|
||||
"19" => new Day19(),
|
||||
_ => new Day20(),
|
||||
};
|
||||
day.Go();
|
||||
day = new Day21();
|
||||
}
|
||||
else
|
||||
{
|
||||
var type = types.FirstOrDefault(x => x.Name == $"Day{arg?.PadLeft(2, '0')}");
|
||||
if (type == null)
|
||||
{
|
||||
Logger.Log($"Unknown day <cyan>{arg}<r>");
|
||||
}
|
||||
else
|
||||
{
|
||||
day = (Day?)Activator.CreateInstance(type);
|
||||
}
|
||||
}
|
||||
day?.Go();
|
||||
}
|
||||
|
Reference in New Issue
Block a user