AdventOfCode/AoC_2025/Day10.cs

132 lines
4.7 KiB
C#

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.IO;
using System.Linq;
using AdvenOfCode.Contracts;
using Microsoft.Z3;
namespace AoC_2025;
public class Day10 : IPuzzleSolver<long>
{
private (uint lamps, (int[] buttons, uint buttonsMap)[] buttons, int[] joltages)[] ParsePuzzleInput(string path)
{
var puzzleInput = File.ReadAllLines(path)
.Where(line => !string.IsNullOrWhiteSpace(line))
.Select(line => line.Split(' ', StringSplitOptions.RemoveEmptyEntries))
.Select(instruction => instruction switch
{
[var lamps, ..var buttons, var joltages] => (lamps: lamps[1..^1], buttons, joltages: joltages[1..^1]),
_ => throw new DataException("Misaligned data")
})
.OrderBy(inst => inst.lamps.Length)
.ToArray();
var lamps = puzzleInput
.Select(input => input.lamps)
.Select(lamps => lamps.Index())
.Select(ixlamps =>
ixlamps.Aggregate((uint)0L, (acc, ixLamp) => acc | ((uint)(ixLamp.Item == '#' ? 1L : 0L) << ixLamp.Index)))
.ToArray();
var buttons = puzzleInput
.Select(input => input.buttons)
.Select(buttons => buttons
.Select(button => button[1..^1]
.Split(',', StringSplitOptions.RemoveEmptyEntries)
.Select(lampIndex => int.Parse(lampIndex))
.ToArray())
.Select(lampIndexes => (lampIndexes, lampIndexes.Aggregate((uint)0L, (acc, lampIndex) => acc | ((uint)1 << lampIndex))))
.ToArray())
.ToArray();
var joltages = puzzleInput
.Select(input => input.joltages)
.Select(joltage => joltage
.Split(',', StringSplitOptions.RemoveEmptyEntries)
.Select(jolt => int.Parse(jolt))
.ToArray())
.ToArray();
var res = lamps
.Zip(buttons, (lamp, button) => (lamp, button))
.Zip(joltages, (bl, joltage) => (bl.lamp, bl.button, joltage))
.ToArray();
return res;
}
public long SolvePart1(string pathToPuzzleInput)
{
var instructions = ParsePuzzleInput(pathToPuzzleInput);
var minCounts = instructions
.Select(ins => GetMinCountButtonPressesForLamps(ins.lamps, [..ins.buttons.Select(b => b.buttonsMap)]))
.ToArray();
return minCounts.Sum();
}
public long SolvePart2(string pathToPuzzleInput)
{
var instructions = ParsePuzzleInput(pathToPuzzleInput);
var minCounts = instructions
.Select(ins => GetMinCountButtonPressesForJoltages(ins.joltages, [..ins.buttons.Select(b => b.buttons)]))
.ToArray();
var sum = minCounts.Sum();
return sum;
}
private long GetMinCountButtonPressesForLamps(uint lamps, uint[] buttons)
{
var presses = 0L;
HashSet<uint> lampStates = [lamps];
while (lampStates.All(state => state > 0) && presses < 100)
{
lampStates = lampStates
.Select(lampState => GetLampStates(lampState, buttons))
.Aggregate((current, states) => [..current,..states])
.ToHashSet();
presses++;
}
return presses;
}
private uint[] GetLampStates(uint lampState, uint[] buttons)
{
var states = buttons
.Where(button => (lampState & button) > 0)
.Select(button => lampState ^ button);
return [..states];
}
private long GetMinCountButtonPressesForJoltages(int[] joltages, int[][] buttons)
{
using var context = new Context();
var optimize = context.MkOptimize();
var variables = Enumerable.Range(0, buttons.Length)
.Select(ix => context.MkIntConst($"n{ix}"))
.ToArray();
foreach (var variable in variables)
{
optimize.Add(variable >= 0);
}
var zero = context.MkInt(0);
foreach (var (i, joltage) in joltages.Index())
{
var equation = zero + zero;
foreach (var (b, button) in buttons.Index())
{
if(button.Contains(i))
equation += variables[b];
}
optimize.Add(context.MkEq(equation, context.MkInt(joltage)));
}
var sumVariables = variables.Aggregate(zero + zero, (current, variable) => current + variable);
optimize.MkMinimize(sumVariables);
optimize.Check();
var answerExpression = optimize.Model.Eval(sumVariables);
var answer = answerExpression is IntNum number ? number.Int64 : 0;
return answer;
}
}