Close

Advent of Code 2025 – Day 4

Printing Department

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.

Leave a Reply