Skip to content

Commit

Permalink
2024/15
Browse files Browse the repository at this point in the history
  • Loading branch information
encse committed Dec 15, 2024
1 parent e18f390 commit d358221
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 7 deletions.
11 changes: 11 additions & 0 deletions 2024/Day15/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,14 @@ You appear back inside your own mini submarine! Each Historian drives their mini
You look up to see a vast school of [lanternfish](/2021/day/6) swimming past you. On closer inspection, they seem quite anxious, so you drive your mini submarine over to see if you can help.

_Visit the website for the full story and [full puzzle](https://adventofcode.com/2024/day/15) description._

Here’s a revised version with improved grammar, clarity, and flow:


A nice Sokoban-style puzzle for the weekend! The main difference is that in the original Sokoban, the robot could push only a single box, not multiple boxes. This adds complexity to both parts of the puzzle. However, it’s not that difficult to handle... I moved the hard parts into the `TryToStep` function that takes the map, a position, and a direction, then attempts to make a move in that direction.

If the position corresponds to the robot or a box, the function checks whether the neighboring cell is free or can be made free by pushing boxes in the given direction. The .NET API sometimes uses the `TryToDoX` pattern, where a function returns a boolean result and provides an `out` parameter. I reused this pattern here. On success, the updated map is returned via the `ref` parameter. If the move fails, the map remains unchanged.

The real challenge lies in `part 2`, where recursion needs to branch whenever the robot pushes more than one box at a time. In such cases, we must invoke `TryToStep` for each box. Due to the recursive nature of the algorithm, it’s possible for one branch to succeed while the other fails. When this happens, the entire map must be reset to its original state before we pop from the recursion. This could be quite tricky to manage unless you make copies of the dictionary that holds the state — or, as I did, use an immutable dictionary instead.

I’m not entirely satisfied with the "if-ladder" structure of the `TryToStep` function, but for now, I don’t have a better idea to handle all the cases in a more readable way.
14 changes: 7 additions & 7 deletions 2024/Day15/Solution.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public double Solve(string input) {

var robot = map.Keys.Single(k => map[k] == '@');
foreach (var dir in steps) {
if (Step(ref map, robot, dir)) {
if (TryToStep(ref map, robot, dir)) {
robot += dir;
}
}
Expand All @@ -37,39 +37,39 @@ public double Solve(string input) {
// Attempts to move the robot in the given direction on the map, pushing boxes as necessary.
// If the move is successful, the map is updated to reflect the new positions and the function returns true.
// Otherwise, the map remains unchanged and the function returns false.
bool Step(ref Map map, Complex pos, Complex dir) {
bool TryToStep(ref Map map, Complex pos, Complex dir) {
var mapOrig = map;

if (map[pos] == '.') {
return true;
} else if (map[pos] == 'O' || map[pos] == '@') {
if (Step(ref map, pos + dir, dir)) {
if (TryToStep(ref map, pos + dir, dir)) {
map = map
.SetItem(pos + dir, map[pos])
.SetItem(pos, '.');
return true;
}
} else if (map[pos] == ']') {
return Step(ref map, pos + Left, dir);
return TryToStep(ref map, pos + Left, dir);
} else if (map[pos] == '[') {
if (dir == Left) {
if (Step(ref map, pos + Left, Left)) {
if (TryToStep(ref map, pos + Left, dir)) {
map = map
.SetItem(pos + Left, '[')
.SetItem(pos, ']')
.SetItem(pos + Right, '.');
return true;
}
} else if (dir == Right) {
if (Step(ref map, pos + 2 * Right, dir)) {
if (TryToStep(ref map, pos + 2 * Right, dir)) {
map = map
.SetItem(pos, '.')
.SetItem(pos + Right, '[')
.SetItem(pos + 2 * Right, ']');
return true;
}
} else {
if (Step(ref map, pos + dir, dir) && Step(ref map, pos + Right + dir, dir)) {
if (TryToStep(ref map, pos + dir, dir) && TryToStep(ref map, pos + Right + dir, dir)) {
map = map
.SetItem(pos, '.')
.SetItem(pos + Right, '.')
Expand Down

0 comments on commit d358221

Please sign in to comment.