For those that have noticed - Yes I've been listening to the same thing for like 4 problems in a row now. It's such a long mix and I haven't even gone through it once, in part because I've only been listening while doing Advent of Code. It's good for focusing oddly enough.
Time for l̴͖͍̃̕ā̵̤͛͜ń̴̩͇t̶̺̑e̷̗̞̙͗r̵̯̟̩̂͐n̶̛͙̗͘f̵̨̺̟́i̸̳͐͜s̴͍̿h̶̜͝.
Well okay not really. Narratively it's lanternfish, but the problem itself is far different. It's more reminiscent of Sokoban, fun little box-pushing puzzles.
Anyway, I think a recusive solution might work for this? Writing a function for each direction that recursively tries to push boxes in each of the given directions:
- If the space is empty (
.
), it returns success to allow the previous move to work - If the space is a wall (
#
), it returns failure to disallow moving - If the space is anythign else, it recursively keeps going in that direction
foreach (var move in moves)
{
switch (move)
{
case '<': if (Move(warehouse, x, y, 0, -1)) y -= 1; break;
case '>': if (Move(warehouse, x, y, 0, 1)) y += 1; break;
case '^': if (Move(warehouse, x, y, -1, 0)) x -= 1; break;
case 'v': if (Move(warehouse, x, y, 1, 0)) x += 1; break;
}
}
private bool Move(List<List<char>> warehouse, int x, int y, int dx, int dy)
{
var nx = x + dx;
var ny = y + dy;
bool canMove = warehouse[nx][ny] switch
{
'#' => false,
'.' => true,
_ => Move(warehouse, nx, ny, dx, dy)
};
if (canMove)
{
warehouse[nx][ny] = warehouse[x][y];
warehouse[x][y] = '.';
}
return canMove;
}
Boxes are fun!
Mr. President, a second robot has hit the warehouse.
But no, really, this seems like an amusing second part. Everything is twice as wide - a completely different puzzle from the first puzzle. Aside from modifying the grid for the warehouse, there really only needs to be one modification:
- If moving a box (
[
or]
) either up or down (dx != 0
), then it's a recursive check for both sides of the box
I wasn't sure before if doing it recursively would have been the right move, but it turns out to have been perfect! The only real significant change I need to do is split the Move()
function to a CanMove()
and a DoMove()
, otherwise it can get really tricky.
private bool CanMove(List<List<char>> warehouse, int x, int y, int dx, int dy)
{
var nx = x + dx;
var ny = y + dy;
return warehouse[nx][ny] switch
{
'#' => false,
'.' => true,
'[' => dx == 0 ? CanMove(warehouse, nx, ny, dx, dy) :
CanMove(warehouse, nx, ny, dx, dy) && CanMove(warehouse, nx, ny + 1, dx, dy),
']' => dx == 0 ? CanMove(warehouse, nx, ny, dx, dy) :
CanMove(warehouse, nx, ny, dx, dy) && CanMove(warehouse, nx, ny - 1, dx, dy),
_ => CanMove(warehouse, nx, ny, dx, dy)
};
}
private void DoMove(List<List<char>> warehouse, int x, int y, int dx, int dy)
{
var nx = x + dx;
var ny = y + dy;
if (dx != 0 && warehouse[nx][ny] == '[') DoMove(warehouse, nx, ny + 1, dx, dy);
if (dx != 0 && warehouse[nx][ny] == ']') DoMove(warehouse, nx, ny - 1, dx, dy);
if (warehouse[nx][ny] != '.') DoMove(warehouse, nx, ny, dx, dy);
Utils.Swap2D(warehouse, x, y, nx, ny);
}
That worked easily, despite taking a bit of tweaking for the up/down movement.