From fb822625cdfc9e0f929de477dad1f15cf5501326 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 22 Dec 2024 07:49:00 +0000 Subject: [PATCH] Update docs on Sun Dec 22 07:49:00 UTC 2024 --- 2024/21/index.html | 248 ++++++++++++--------------------------------- 1 file changed, 62 insertions(+), 186 deletions(-) diff --git a/2024/21/index.html b/2024/21/index.html index d3bff135..810ce795 100644 --- a/2024/21/index.html +++ b/2024/21/index.html @@ -288,215 +288,91 @@

Keypad Conundrum

using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; -using System.Numerics; -using AngleSharp.Common; -using Cache = System.Collections.Concurrent.ConcurrentDictionary<(char, System.Numerics.Complex, int, System.Numerics.Complex), (long, string, System.Numerics.Complex)>; +using Cache = System.Collections.Concurrent.ConcurrentDictionary<(char currentKey, char nextKey, int depth), long>; +using Keypad = System.Collections.Generic.Dictionary<Vec2, char>; +record struct Vec2(int x, int y); [ProblemName("Keypad Conundrum")] class Solution : Solver { -/* - _________ _______ - |\ /|\__ __/( ____ ) - | ) ( | ) ( | ( )| - | | _ | | | | | (____)| - | |( )| | | | | _____) - | || || | | | | ( - | () () |___) (___| ) - (_______)\_______/|/ - -*/ - - - public object PartOne(string input) { - return input.Split("\n").Sum(line => Solve2(line, 2).Item1); - } - public object PartTwo(string input) { - return input.Split("\n").Sum(line => Solve2(line, 25).Item1); - } - - static readonly Complex Left = -1; - static readonly Complex Right = 1; - static readonly Complex Up = Complex.ImaginaryOne; - static readonly Complex Down = -Complex.ImaginaryOne; - - (long, string) Solve2(string line, int depth) { + public object PartOne(string input) => Solve(input, 2); + public object PartTwo(string input) => Solve(input, 25); + long Solve(string input, int depth) { var keypad1 = ParseKeypad("789\n456\n123\n 0A"); var keypad2 = ParseKeypad(" ^A\n<v>"); + var keypads = Enumerable.Repeat(keypad2, depth).Prepend(keypad1).ToArray(); - Cache cache = new Cache(); - var res = long.MaxValue; - var st = ""; - foreach (var plan in Encode(line, keypad1, keypad1['A'])) { - var (length, stT, _) = EncodeString(plan, keypad2, depth, cache, keypad2['A']); - if (length < res) { - res = Math.Min(res, length); - st = stT; - } + var cache = new Cache(); + var res = 0L; + + foreach (var line in input.Split("\n")) { + var num = int.Parse(line[..^1]); + res += num * EncodeKeys(line, keypads, cache); } - return (res * int.Parse(line.Substring(0, line.Length - 1)), st); + return res; } - (long, string, Complex) EncodeString(string st, Dictionary<char, Complex> keypad2, int depth, Cache cache, Complex top) { - if (depth == 0) { - return (st.Length, st, top); + // Determines the length of the shortest sequence that is needed to enter the given + // keys. An empty keypad array means that the sequence is simply entered by a human + // and no further encoding is needed. Otherwise the sequence is entered by a robot + // which needs to be programmed. In practice this means that the keys are encoded + // using the robots keypad (the first keypad), generating an other sequence of keys. + // This other sequence is then recursively encoded using the rest of the keypads. + long EncodeKeys(string keys, Keypad[] keypads, Cache cache) { + if (keypads.Length == 0) { + return keys.Length; } else { + // invariant: the robot starts and finishes by pointing at the 'A' key + var currentKey = 'A'; var length = 0L; - var pos = keypad2['A']; ; //depth == 1 ? top : keypad2['A']; - var originalTop = top; - var resSt = ""; - foreach (var step in st) { - long cost; - string stT; - (cost, stT, top) = EncodeKey(step, pos, keypad2, depth, cache, top); - length += cost; - resSt += stT; - pos = keypad2[step]; - } - if (depth == 1) { - top = st.Length == 1 ? originalTop : keypad2[st[^1]]; - } - return (length, resSt, top); - } - } - (long, string, Complex) EncodeKey(char ch, Complex pos, Dictionary<char, Complex> keypad2, int depth, Cache cache, Complex top) { - var key = (ch, pos, depth, top); - if (cache.ContainsKey(key)) { - return cache[key]; - } - - if (depth == 0) { - throw new Exception(); - } - - var target = keypad2[ch]; - - var dy = (int)(target.Imaginary - pos.Imaginary); - var dx = (int)(target.Real - pos.Real); - - var resCost = long.MaxValue; - var resTop = Complex.Infinity; - var resSt = ""; - - string toEncode = ""; - - - toEncode = ""; - if (pos + dy * Up != keypad2[' ']) { - if (dy < 0) { - toEncode += new string('v', Math.Abs(dy)); - } else if (dy > 0) { - toEncode += new string('^', Math.Abs(dy)); + foreach (var nextKey in keys) { + length += EncodeKey(currentKey, nextKey, keypads, cache); + // while the sequence is entered the current key changes accordingly + currentKey = nextKey; } - if (dx < 0) { - toEncode += new string('<', Math.Abs(dx)); - } else if (dx > 0) { - toEncode += new string('>', Math.Abs(dx)); - } - toEncode += "A"; - var (cost, stT, topT) = EncodeString(toEncode, keypad2, depth - 1, cache, top); - if (cost < resCost) { - resCost = cost; - resTop = topT; - resSt = stT; - } + // at the end the current key should be reset to 'A' + Debug.Assert(currentKey == 'A', "The robot should point at the 'A' key"); + return length; } - - - - toEncode = ""; - if (pos + dx * Right != keypad2[' ']) { - if (dx < 0) { - toEncode += new string('<', Math.Abs(dx)); - } else if (dx > 0) { - toEncode += new string('>', Math.Abs(dx)); - } - - if (dy < 0) { - toEncode += new string('v', Math.Abs(dy)); - } else if (dy > 0) { - toEncode += new string('^', Math.Abs(dy)); - } - toEncode += "A"; - - var (cost, stT, topT) = EncodeString(toEncode, keypad2, depth - 1, cache, top); - - if (cost < resCost) { - resCost = cost; - resTop = topT; - resSt = stT; - } - } - resSt = ""; - - cache[key] = (resCost, resSt, resTop); - return cache[key]; - } - - - - IEnumerable<string> Encode(string st, Dictionary<char, Complex> keymap, Complex pos) { - if (st == "") { - yield return ""; - yield break; - } - - - var target = keymap[st[0]]; - - var dy = (int)(target.Imaginary - pos.Imaginary); - var dx = (int)(target.Real - pos.Real); - - if (pos + dy * Up != keymap[' ']) { - var res = ""; - if (dy < 0) { - res += new string('v', Math.Abs(dy)); - } else if (dy > 0) { - res += new string('^', Math.Abs(dy)); - } - if (dx < 0) { - res += new string('<', Math.Abs(dx)); - } else if (dx > 0) { - res += new string('>', Math.Abs(dx)); - } - res += "A"; - foreach (var resT in Encode(st[1..], keymap, target)) { - yield return res + resT; - } - } - - if (pos + dx * Right != keymap[' ']) { - var res = ""; - if (dx < 0) { - res += new string('<', Math.Abs(dx)); - } else if (dx > 0) { - res += new string('>', Math.Abs(dx)); - } - - if (dy < 0) { - res += new string('v', Math.Abs(dy)); - } else if (dy > 0) { - res += new string('^', Math.Abs(dy)); - } - - res += "A"; - foreach (var resT in Encode(st[1..], keymap, target)) { - yield return res + resT; - } - } - } - - Dictionary<char, Complex> ParseKeypad(string keypad) { + long EncodeKey(char currentKey, char nextKey, Keypad[] keypads, Cache cache) => + cache.GetOrAdd((currentKey, nextKey, keypads.Length), _ => { + var keypad = keypads[0]; + + var currentPos = keypad.Single(kvp => kvp.Value == currentKey).Key; + var nextPos = keypad.Single(kvp => kvp.Value == nextKey).Key; + + var dy = nextPos.y - currentPos.y; + var vert = new string(dy < 0 ? 'v' : '^', Math.Abs(dy)); + + var dx = nextPos.x - currentPos.x; + var horiz = new string(dx < 0 ? '<' : '>', Math.Abs(dx)); + + var cost = long.MaxValue; + // we can usually go vertical first then horizontal or vica versa, + // but we should check for the extra condition and don't position + // the robot over the ' ' key: + if (keypad[new Vec2(currentPos.x, nextPos.y)] != ' ') { + cost = Math.Min(cost, EncodeKeys($"{vert}{horiz}A", keypads[1..], cache)); + } + + if (keypad[new Vec2(nextPos.x, currentPos.y)] != ' ') { + cost = Math.Min(cost, EncodeKeys($"{horiz}{vert}A", keypads[1..], cache)); + } + return cost; + }); + + Keypad ParseKeypad(string keypad) { var lines = keypad.Split("\n"); return ( from y in Enumerable.Range(0, lines.Length) from x in Enumerable.Range(0, lines[0].Length) - select new KeyValuePair<char, Complex>(lines[y][x], x + y * Down) + select new KeyValuePair<Vec2, char>(new Vec2(x, -y), lines[y][x]) ).ToDictionary(); } }