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:
2021-12-21 22:51:21 -06:00
parent 60d61584c8
commit 8a6bf8604a
4 changed files with 160 additions and 27 deletions

View File

@ -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
View File

@ -0,0 +1,2 @@
Player 1 starting position: 4
Player 2 starting position: 8

134
src/21.cs Normal file
View 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;
}
}

View File

@ -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();
}