This is the first day's problem I'm doing "Late". I've been busy for the past few days with some friends visiting, making merry, sharing in homemade eggnog, and right now for me it's about 2 hours before Day 22 is released. I'm hoping to get this done before then so I can start that one on-time, but as we're in the last week of problems, I'm also expecting it to be somewhat complex, so let's see how this goes!
I'm speechless. This is the weirdest, wildest, most indirect sort of puzzle I've seen yet for this. Using a keypad to control a robot to press a keypad to control a robot to press a keypad. Wild.
I suppose I could try and work backwards with this - First try and solve the shortest sequence for the base keypad, focusing on longer sections of repeated presses by keeping movements going as long as possible. Then for the directional keypad, keep a dictionary of different (from, to)
values, so I wouldn't have to constantly compute them. Or maybe even hard-code the values?
private string GetNumericPattern(string keycode)
{
var finalPattern = new StringBuilder();
int x = 3, y = 2;
foreach (var c in keycode)
{
int dx = c switch
{
'7' or '8' or '9' => 0,
'4' or '5' or '6' => 1,
'1' or '2' or '3' => 2,
'0' or 'A' => 3,
_ => -1
};
int dy = c switch
{
'7' or '4' or '1' => 0,
'8' or '5' or '2' or '0' => 1,
'9' or '6' or '3' or 'A' => 2,
_ => -1
};
while (x > dx) { finalPattern.Append('^'); x--; }
while (x < dx) { finalPattern.Append('v'); x++; }
while (y > dy) { finalPattern.Append('<'); y--; }
while (y < dy) { finalPattern.Append('>'); y++; }
finalPattern.Append('A');
}
return finalPattern.ToString();
}
While that gave a 'valid' response, it ended up being a bit too simplistic. After a LOT of tweaking, I realized that it was generating invalid patterns, but also non-optimal patterns. I assumed just taking the first (valid) pattern with minimum turns would be best, but it's not entirely the case. Trying something a bit more thorough...
private int GetNumericLength(char start, char end)
{
var x = start switch { '7' or '8' or '9' => 0, '4' or '5' or '6' => 1, '1' or '2' or '3' => 2, '0' or 'A' => 3, _ => -1 };
var y = start switch { '7' or '4' or '1' => 0, '8' or '5' or '2' or '0' => 1, '9' or '6' or '3' or 'A' => 2, _ => -1 };
var dx = end switch { '7' or '8' or '9' => 0, '4' or '5' or '6' => 1, '1' or '2' or '3' => 2, '0' or 'A' => 3, _ => -1 };
var dy = end switch { '7' or '4' or '1' => 0, '8' or '5' or '2' or '0' => 1, '9' or '6' or '3' or 'A' => 2, _ => -1 };
var nextPattern = new StringBuilder();
while (y > dy) { nextPattern.Append('<'); y--; }
while (y < dy) { nextPattern.Append('>'); y++; }
while (x > dx) { nextPattern.Append('^'); x--; }
while (x < dx) { nextPattern.Append('v'); x++; }
return nextPattern.ToString()
.ToCharArray()
.Permute()
.Select(c => string.Join("", c) + 'A')
.Distinct()
.Where(p => IsPatternValid(p, start))
.Select(p => GetDirectionalPattern(GetDirectionalPattern(p)))
.Min(p => p.Length);
}
The GetDirectionalPattern()
call is just a hardcoded optimal pattern for the directional pad I figured out by hand.
Anyway, I finished this at like 9:40, well past my deadline, and this is only part 1... I have a feeling I won't do Day 21 until tomorrow sometime...
Of course it's just a "Part 1 but more" problem.
I basically had to explode part of the solution out to allow for memoization, but once I did that it ran basically the same code to get the right answer
private long GetPatternDirectionalLength(string pattern, int iterations)
{
long sum = 0;
char curChar = 'A';
foreach (char c in pattern)
{
sum += GetDirectionalLength(curChar, c, iterations);
curChar = c;
}
return sum;
}
private long GetDirectionalLength(char start, char end, int iterations)
{
var nextPattern = directionalPatterns[(start, end)];
if (iterations == 1) return nextPattern.Length + 1;
if (patternDepthLength.TryGetValue((start, end, iterations), out var cached)) return cached;
var length = nextPattern.Permute()
.Select(c => string.Join("", c) + 'A')
.Distinct()
.Where(p => IsDirectionalValid(p, start))
.Select(p => GetPatternDirectionalLength(p, iterations - 1))
.Min();
patternDepthLength[(start, end, iterations)] = length;
return length;
}
Whew.