The first map puzzle of the year. Part 1 was a simple count; just loop through each position and see how many neighboring rolls it has, if it’s less than 4, increment a counter. Return the counter when done. I added a new function to the ADVENT package, MAPCHAR. All it does is return the element from the collection at the corresponding indexes. So map(m,x,y) is mostly equivalent to m(x)(y). The difference being the function captures no_data_found exceptions, returning NULL when you try to retrieve a value from an index that isn’t in the collection. This makes certain types of checks (as in the code below) simpler without needing to do explicit bounds checking.
DECLARE
v_map advent.t_map := advent.data2map('advent2025-4sample');
v_neighbor INTEGER;
v_prevcnt integer := null;
v_cnt INTEGER := 0;
BEGIN
FOR y IN 1 .. v_map(1).COUNT
LOOP
FOR x IN 1 .. v_map.COUNT
LOOP
IF advent.mapchar(v_map,x,y) = '@'
THEN
v_neighbor := 0;
IF advent.mapchar(v_map,x - 1,y - 1) = '@'
THEN
v_neighbor := v_neighbor + 1;
END IF;
IF advent.mapchar(v_map,x - 1,y) = '@'
THEN
v_neighbor := v_neighbor + 1;
END IF;
IF advent.mapchar(v_map,x - 1,y + 1) = '@'
THEN
v_neighbor := v_neighbor + 1;
END IF;
IF advent.mapchar(v_map,x,y - 1) = '@'
THEN
v_neighbor := v_neighbor + 1;
END IF;
IF advent.mapchar(v_map,x,y + 1) = '@'
THEN
v_neighbor := v_neighbor + 1;
END IF;
IF advent.mapchar(v_map,x + 1,y - 1) = '@'
THEN
v_neighbor := v_neighbor + 1;
END IF;
IF advent.mapchar(v_map,x + 1,y) = '@'
THEN
v_neighbor := v_neighbor + 1;
END IF;
IF advent.mapchar(v_map,x + 1,y + 1) = '@'
THEN
v_neighbor := v_neighbor + 1;
END IF;
IF v_neighbor < 4
THEN
v_cnt := v_cnt + 1;
END IF;
END IF;
END LOOP;
END LOOP;
DBMS_OUTPUT.put_line(v_cnt);
END;
Part 2 was more of the same, except after counting up all the rolls that could be moved, you then have to remove them (but don't remove them as you go or it can mess up the counts. After you remove those, repeat the search again, keep going until the total count of removed rolls doesn't change.
DECLARE
v_map advent.t_map := advent.data2map('advent2025-4');
v_remove advent.charmap_tab;
v_neighbor INTEGER;
v_prevcnt INTEGER := NULL;
v_cnt INTEGER := 0;
v_start TIMESTAMP WITH TIME ZONE := SYSTIMESTAMP;
BEGIN
LOOP
v_prevcnt := v_cnt;
v_remove := advent.charmap_tab();
FOR y IN 1 .. v_map(1).COUNT
LOOP
FOR x IN 1 .. v_map.COUNT
LOOP
IF advent.mapchar(v_map, x, y) = '@'
THEN
v_neighbor := 0;
IF advent.mapchar(v_map, x - 1, y - 1) = '@'
THEN
v_neighbor := v_neighbor + 1;
END IF;
IF advent.mapchar(v_map, x - 1, y) = '@'
THEN
v_neighbor := v_neighbor + 1;
END IF;
IF advent.mapchar(v_map, x - 1, y + 1) = '@'
THEN
v_neighbor := v_neighbor + 1;
END IF;
IF advent.mapchar(v_map, x, y - 1) = '@'
THEN
v_neighbor := v_neighbor + 1;
END IF;
IF advent.mapchar(v_map, x, y + 1) = '@'
THEN
v_neighbor := v_neighbor + 1;
END IF;
IF advent.mapchar(v_map, x + 1, y - 1) = '@'
THEN
v_neighbor := v_neighbor + 1;
END IF;
IF advent.mapchar(v_map, x + 1, y) = '@'
THEN
v_neighbor := v_neighbor + 1;
END IF;
IF advent.mapchar(v_map, x + 1, y + 1) = '@'
THEN
v_neighbor := v_neighbor + 1;
END IF;
IF v_neighbor < 4
THEN
v_remove.EXTEND;
v_remove(v_remove.COUNT) := advent.charmap_rec('@', x, y);
v_cnt := v_cnt + 1;
END IF;
END IF;
END LOOP;
END LOOP;
FOR i IN 1 .. v_remove.COUNT
LOOP
v_map(v_remove(i).x)(v_remove(i).y) := 'x';
END LOOP;
IF v_prevcnt = v_cnt
THEN
EXIT;
END IF;
END LOOP;
DBMS_OUTPUT.put_line(v_cnt);
END;
I've still got more catching up to do, but I'm moving forward.