Files
advent-of-code/2024/day-15/solution-2.sh

251 lines
6.0 KiB
Bash
Raw Normal View History

2024-12-15 20:47:09 +08:00
#!/usr/bin/env bash
FUNCNEST=99999
MAP_FILE=input-map
DIRECTIONS_FILE=input-movements
MAP_WIDTH=$(( ( $( head -1 "$MAP_FILE" | wc -c ) -1 ) * 2 ))
# Move the robot
move_robot() {
MAP_ARRAY[ROBOT_POSITION]=.
ROBOT_POSITION=$NEXT_POSITION
MAP_ARRAY[ROBOT_POSITION]=@
}
# Function to move box in n direction
vertical_move_box () {
local BOX_COORDINATE=$1
local BOX_MOVE_DIRECTION=$2
# Normalise coordinates
if [[ ${MAP_ARRAY[$BOX_COORDINATE]} == "[" ]]
then
local L_SIDE=$BOX_COORDINATE
local R_SIDE=$(( BOX_COORDINATE +1 ))
elif [[ ${MAP_ARRAY[$BOX_COORDINATE]} == "]" ]]
then
local L_SIDE=$(( BOX_COORDINATE -1 ))
local R_SIDE=$BOX_COORDINATE
fi
local L_SIDE_MOVE_COORDINATE=$(( L_SIDE + BOX_MOVE_DIRECTION ))
local R_SIDE_MOVE_COORDINATE=$(( R_SIDE + BOX_MOVE_DIRECTION ))
# Recurse if there are more boxes
if [[ ${MAP_ARRAY[L_SIDE_MOVE_COORDINATE]} == "[" ]] || \
[[ ${MAP_ARRAY[L_SIDE_MOVE_COORDINATE]} == "]" ]]
then
vertical_move_box "$L_SIDE_MOVE_COORDINATE" "$BOX_MOVE_DIRECTION"
fi
if [[ ${MAP_ARRAY[R_SIDE_MOVE_COORDINATE]} == "[" ]] || \
[[ ${MAP_ARRAY[R_SIDE_MOVE_COORDINATE]} == "]" ]]
then
vertical_move_box "$R_SIDE_MOVE_COORDINATE" "$BOX_MOVE_DIRECTION"
fi
MAP_ARRAY[L_SIDE_MOVE_COORDINATE]="["
MAP_ARRAY[R_SIDE_MOVE_COORDINATE]="]"
MAP_ARRAY[L_SIDE]="."
MAP_ARRAY[R_SIDE]="."
}
# Function to check if box can be moved in n direction
vertical_check_move_box () {
local BOX_COORDINATE=$1
local BOX_MOVE_DIRECTION=$2
# Normalise coordinates
if [[ ${MAP_ARRAY[$BOX_COORDINATE]} == "[" ]]
then
local L_SIDE=$BOX_COORDINATE
local R_SIDE=$(( BOX_COORDINATE +1 ))
elif [[ ${MAP_ARRAY[$BOX_COORDINATE]} == "]" ]]
then
L_SIDE=$(( BOX_COORDINATE -1 ))
R_SIDE=$BOX_COORDINATE
fi
# Check if boxes are movable
local L_SIDE_MOVE_COORDINATE=$(( L_SIDE + BOX_MOVE_DIRECTION ))
local R_SIDE_MOVE_COORDINATE=$(( R_SIDE + BOX_MOVE_DIRECTION ))
if [[ ${MAP_ARRAY[$L_SIDE_MOVE_COORDINATE]} == '.' ]] && \
[[ ${MAP_ARRAY[$R_SIDE_MOVE_COORDINATE]} == '.' ]] # Can move
then
return 0
elif [[ ${MAP_ARRAY[$L_SIDE_MOVE_COORDINATE]} == '#' ]] || \
[[ ${MAP_ARRAY[$R_SIDE_MOVE_COORDINATE]} == '#' ]] # Blocked by wall
then
return 1
fi
# Recurse if there are more boxes
if [[ ${MAP_ARRAY[L_SIDE_MOVE_COORDINATE]} == "[" ]] || \
[[ ${MAP_ARRAY[L_SIDE_MOVE_COORDINATE]} == "]" ]]
then
vertical_check_move_box "$L_SIDE_MOVE_COORDINATE" "$BOX_MOVE_DIRECTION"
if [[ $? -eq 1 ]]
then
return 1
fi
fi
if [[ ${MAP_ARRAY[R_SIDE_MOVE_COORDINATE]} == "[" ]] || \
[[ ${MAP_ARRAY[R_SIDE_MOVE_COORDINATE]} == "]" ]]
then
vertical_check_move_box "$R_SIDE_MOVE_COORDINATE" "$BOX_MOVE_DIRECTION"
if [[ $? -eq 1 ]]
then
return 1
fi
fi
}
horizontal_move_box () {
local BOX_COOR=$1
local BOX_MOVE_DIRECTION=$2
local NEXT_SPOT=$(( BOX_COOR + BOX_MOVE_DIRECTION * 2 ))
if [[ ${MAP_ARRAY[$NEXT_SPOT]} == "#" ]]
then
return 1 # Failed to move box
elif [[ ${MAP_ARRAY[$NEXT_SPOT]} == "[" ]] || \
[[ ${MAP_ARRAY[$NEXT_SPOT]} == "]" ]]
then
horizontal_move_box "$NEXT_SPOT" "$BOX_MOVE_DIRECTION"
if [[ $? -eq 1 ]] ; then return 1 ; fi
elif [[ ${MAP_ARRAY[$NEXT_SPOT]} != "." ]]
then
return 1
fi
# Move the box
if [[ $NEXT_SPOT -lt $(( BOX_COOR + BOX_MOVE_DIRECTION )) ]]
then
MAP_ARRAY[NEXT_SPOT]="["
MAP_ARRAY[BOX_COOR + BOX_MOVE_DIRECTION]="]"
else
MAP_ARRAY[BOX_COOR + BOX_MOVE_DIRECTION]="["
MAP_ARRAY[NEXT_SPOT]="]"
fi
MAP_ARRAY[BOX_COOR]="."
}
# Load map
read -r -a MAP_ARRAY <<< "$(
< "$MAP_FILE" paste -s -d "" |
sed -E '
s/#/##/g;
s/\./../g;
s/@/@./g;
s/O/[]/g;
s/(.)(.)/\1 \2 /g'
)"
MAP_LEN=${#MAP_ARRAY[@]}
print_map () {
printf "%s " "${MAP_ARRAY[@]}" |
fold -w $(( MAP_WIDTH * 2 ))
printf "\n"
}
# Get robot position
for (( i=0; i<MAP_LEN; i++ ))
do
if [[ ${MAP_ARRAY[$i]} == "@" ]]
then
ROBOT_POSITION=$i
break
fi
done
printf "Robot position : %s\n" "$ROBOT_POSITION"
# Hardcode direction values
UP=$(( - MAP_WIDTH ))
DOWN=$MAP_WIDTH
LEFT=-1
RIGHT=1
# Iterate through directions
MOVES=0
print_map
while read -r DIRECTION
do
# Interpret directions
if [[ $DIRECTION == "^" ]]
then
DIRECTION_VALUE=$UP
elif [[ $DIRECTION == "v" ]]
then
DIRECTION_VALUE=$DOWN
elif [[ $DIRECTION == "<" ]]
then
DIRECTION_VALUE=$LEFT
elif [[ $DIRECTION == ">" ]]
then
DIRECTION_VALUE=$RIGHT
fi
NEXT_POSITION=$(( ROBOT_POSITION + DIRECTION_VALUE ))
# Box in front
if [[ ${MAP_ARRAY[$NEXT_POSITION]} == "[" ]] || \
[[ ${MAP_ARRAY[$NEXT_POSITION]} == "]" ]]
then
#printf "Hit box.\n"
if [[ $DIRECTION_VALUE -eq $LEFT ]] || \
[[ $DIRECTION_VALUE -eq $RIGHT ]]
then
#printf "Left right box movement.\n"
if horizontal_move_box "$NEXT_POSITION" "$DIRECTION_VALUE"
then
move_robot
fi
elif [[ $DIRECTION_VALUE -eq $UP ]] || \
[[ $DIRECTION_VALUE -eq $DOWN ]]
then
#printf "Up down box movement.\n"
if vertical_check_move_box "$NEXT_POSITION" "$DIRECTION_VALUE"
then
vertical_move_box "$NEXT_POSITION" "$DIRECTION_VALUE"
move_robot
fi
fi
# Wall in front, do nothing
elif [[ ${MAP_ARRAY[$NEXT_POSITION]} == "#" ]]
then
#printf "Next: %s Skipping\n" "${MAP_ARRAY[$NEXT_POSITION]}"
:
# Empty space, move forward
elif [[ ${MAP_ARRAY[$NEXT_POSITION]} == "." ]]
then
#printf "Next: %s Move robot forward\n" "${MAP_ARRAY[$NEXT_POSITION]}"
move_robot
fi
(( MOVES++ ))
#print_map
done <<< "$( < "$DIRECTIONS_FILE" paste -s -d "" | sed -E 's/(.)(.)/\1 \2 /g' | tr ' ' '\n' )"
printf "Moves: %s\n" "$MOVES"
print_map
# Calculate GPS values for boxes
SUM=0
BOX_COUNT=0
for (( i=0; i<MAP_LEN; i++ ))
do
if [[ ${MAP_ARRAY[i]} == "[" ]]
then
(( BOX_COUNT++ ))
X=$(( i % MAP_WIDTH ))
Y=$(( i / MAP_WIDTH * 100 ))
GPS=$(( X + Y ))
#printf "Box %s X: %s Y: %s GPS: %s\n" "$i" "$X" "$Y" "$GPS"
(( SUM+=GPS ))
fi
done
printf "Sum: %s\n" "$SUM"
printf "Box count: %s\n" "$BOX_COUNT"