AoC - Day 3

:date: 2023-12-03 12:00

Part One

WTF????!?!! Everything works 100% fine except line 117. No reason. You can delete that line and the new 117 fails. You can chop off the first half of the data sent and then it works - including the problematic line that was 117. This is bonkers!! WTF is going on? I solved this by just manually overriding the errors from 117 and all the rest were correct.

Here's a sample of the output showing the situation. Each number is isolated and its previous and next line's cut from the position are shown before and after. Only on line 117, the current line is repeated instead of using the next line cut.

OK-3   ....3./..
OK-376   ...*..376......
OK-191   *.....191......
115 | .........&....*..%........................................183......*......61................/........657..........163............255........
OK-183   ......183....*.
OK-61   ..#..61.....
---657   ......657......
OK-163   .*....163......
OK-255   ......255.....*
116 | ..674.......46..............392..251....507.................*.248...74..........+...775.97.....222.........%..........135...950.....*.......
OK-674   ......674....*.
OK-46   ...*.46.....
OK-392   ......392..*...
---251   ......251......
OK-507   ......507...*..
OK-248   ......248...#..
OK-74   *....74.....
---775   ......775......
---97   .....97.....
---222   ......222......
OK-135   ......135....*.
OK-950   ......950.&....
117 | ....*...........*967..533...*............*......949..245.276...#.........*.....728...................677..137.....*.....*..&........522.....
OK-967   .....*967.*967.
---533   ......533..533.
---949   ......949..949.
---245   ......245..245.
OK-276   ....*.276..276.
OK-728   ..+...728..728.
---677   ......677..677.
OK-137   ..%...137..137.
OK-522   .*....522..522.
118 | ...367.......196.......$..239..........510.748..*............/...481..722....................800........*........735..268...................
OK-367   ..*...367......
OK-196   ....*.196......
OK-239   ...*..239......
OK-510   ...*..510......
---748   ......748......
OK-481   ......481...*..
OK-722   ....*.722......
OK-800   ......800.%....
OK-735   ..*...735......
OK-268   ...*..268......
119 | .........442...................111...............752........567...*.........*..........696..%............790................................
---442   ......442......
---111   ......111......

#!/bin/bash
T=0
I=$1

LINES=$(wc -l $I)
for L in $(seq $(wc -l < $I)); do # Current line number.
    Z=$(sed -n ${L}p $I);  # Current line.
    echo "$L | $Z" # Print original current line.

    DOTSTATE=1
    POS=1
    S=$POS
    set -f  # Because some of the syms are stars which glob when alone in $($C)
    for C in $( fold -w1 <<<${Z} ); do
        if echo $C | grep '[^0-9]'  >/dev/null ; then # Character is not a digit.
            if test $DOTSTATE == "0"; then
                E=$POS
                N=$(cut -c ${S}-${E} <<<"$Z" )

        if test $L -eq 1; then
            ZP=$(sed -n ${L}p $I)
        else
            ZP=$(sed -n $((L-1))p $I)
        fi
        if test $L -eq $LINES; then
            ZN=$(sed -n ${L}p $I)
        else
            ZN=$(sed -n $((L+1))p $I)
        fi
        OKS="$(cut -c ${S}-${E} <<<$ZP )${N}$(cut -c ${S}-${E} <<<$ZN )"

        THENUMBER=$(tr -cd [0-9] <<<$N)
        if echo "$OKS" | grep -v '^[0-9.][0-9.]*$' >/dev/null; then
            echo "OK-$THENUMBER   $OKS"
            T=$(( $T + $THENUMBER ))
        else
            echo "---$THENUMBER   $OKS"
        fi

            fi
            DOTSTATE=1
        else
            if test $DOTSTATE == "1"; then
                if test "$POS" -gt "1" ; then 
                    S=$(( $POS - 1 ))
                else
                    S=$POS
                fi
            fi
            DOTSTATE=0
        fi
        POS=$(( $POS + 1 ))
    done

    #Add final if number is in final position
    if test $DOTSTATE == "0"; then
        E=$POS
        N=$(cut -c ${S}-${E} <<<"$Z" )
        #echo "Start: $S End: $E Cut: $N"
        echo "FINAL: $N"
        OKS="$(cut -c ${S}-${E} <<<$ZP )${N}$(cut -c ${S}-${E} <<<$ZN )"
        THENUMBER=$(tr -cd [0-9] <<<$N)
        if echo "$OKS" | grep -v '^[0-9.][0-9.]*$' >/dev/null; then
           #THENUMBER=$(tr -cd [0-9] <<<$N)
           echo "OK-$THENUMBER   $OKS"
           T=$(( $T + $THENUMBER ))
        else
           echo "---$THENUMBER   $OKS"
        fi

        if test $L -eq 1; then
            ZP=$(sed -n ${L}p $I)
        else
            ZP=$(sed -n $((L-1))p $I)
        fi
        if test $L -eq $LINES; then
            ZN=$(sed -n ${L}p $I)
        else
            ZN=$(sed -n $((L+1))p $I)
        fi
        #echo "$(cut -c ${S}-${E} <<<$ZP )${N}$(cut -c ${S}-${E} <<<$ZN )"
    fi
    
#read -p "Press enter to continue..."
done # looping over every line
echo "TOTAL: $T"

That's so weird. But... moving on!

Part Two

This was nuts too. I'll just post the whole Python script to demonstrate how busy these solutions can be. I'm pretty sure I'm not overlooking tons of obvious consolidations into more elegant code. This problem just had tons of weird cases to worry about.

#!/usr/bin/python
# Correct: 78915902
R= list()
with open('input3','r') as f:
    for l in f:
        C= l.strip()
        CL= list(C)
        for i,c in enumerate(C):
            if  not ((ord(c) > 47) and (ord(c) < 58)):
                if c != '.' and c != '*':
                    CL[i]= '.'
        R.append(CL)

def find_top_row_pair(R,r,c):
    digits1,digits2= str(),str()
    d= c+1
    still_in_number= True
    while still_in_number:
        try:
            if R[r-1][d].isdigit():
                digits2 += R[r-1][d]
                #print(f"D2:{digits2}")
                d+=1
            else:
                still_in_number= False
        except IndexError:
            still_in_number= False
    d= c-1
    still_in_number= True
    while still_in_number:
        try:
            if R[r-1][d].isdigit():
                digits1 += R[r-1][d]
                d-=1
            else:
                still_in_number= False
        except IndexError:
            still_in_number= False
    digits1= digits1[::-1]
    print(f"{digits1}x{digits2}")
    return int(digits1) * int(digits2)


def find_bot_row_pair(R,r,c):
    digits1,digits2= str(),str()
    d= c+1
    still_in_number= True
    while still_in_number:
        try:
            if R[r+1][d].isdigit():
                digits2 += R[r+1][d]
                d+=1
            else:
                still_in_number= False
        except IndexError:
            still_in_number= False
    d= c-1
    still_in_number= True
    while still_in_number:
        try:
            if R[r+1][d].isdigit():
                digits1 += R[r+1][d]
                d-=1
            else:
                still_in_number= False
        except IndexError:
            still_in_number= False
    digits1= digits1[::-1]
    print(f"{digits1}x{digits2}")
    return int(digits1) * int(digits2)

def find_same_line_pair(R,r,c):
    d1= find_single_right(R,r,c)
    d2= find_single_left(R,r,c)
    print(f"{d1}x{d2}")
    return int(d1) * int(d2)

def find_single_on_top(R,r,c):
    digits= str()
    d= c-1
    still_in_number= True
    while still_in_number:
        try:
            if R[r-1][d].isdigit():
                digits += R[r-1][d]
                d-=1
            else:
                still_in_number= False
        except IndexError:
            still_in_number= False
    digits= digits[::-1] # These were searched from center to left. Reverse now.
    if R[r-1][c].isdigit():
        d= c # Start looking again directly above.
    else:
        d= c+1 # Start looking upper right.
    still_in_number= True
    while still_in_number:
        try:
            if R[r-1][d].isdigit():
                digits += R[r-1][d]
                d+=1
            else:
                still_in_number= False
        except IndexError:
            still_in_number= False
    return int(digits)

def find_single_on_bot(R,r,c):
    digits= str()
    d= c-1
    still_in_number= True
    while still_in_number:
        try:
            if R[r+1][d].isdigit():
                digits += R[r+1][d]
                d-=1
            else:
                still_in_number= False
        except IndexError:
            still_in_number= False
    digits= digits[::-1] # These were searched from center to left. Reverse now.
    if R[r+1][c].isdigit():
        d= c # Start looking again directly below.
    else:
        d= c+1 # Start looking lower right.
    still_in_number= True
    while still_in_number:
        try:
            if R[r+1][d].isdigit():
                digits += R[r+1][d]
                d+=1
            else:
                still_in_number= False
        except IndexError:
            still_in_number= False
    return int(digits)

def find_single_left(R,r,c):
    digits= str()
    d= c-1
    still_in_number= True
    while still_in_number:
        try:
            if R[r][d].isdigit():
                digits += R[r][d]
                d-=1
            else:
                still_in_number= False
        except IndexError:
            still_in_number= False
    digits= digits[::-1] # These were searched from center to left. Reverse now.
    return int(digits)

def find_single_right(R,r,c):
    digits= str()
    d= c+1
    still_in_number= True
    while still_in_number:
        try:
            if R[r][d].isdigit():
                digits += R[r][d]
                d+=1
            else:
                still_in_number= False
        except IndexError:
            still_in_number= False
    return int(digits)

T= 0
for ir,r in enumerate(R):
    for ic,c in enumerate(r):
        if R[ir][ic] == '*':
            top_row_pair= R[ir-1][ic-1].isdigit() and not R[ir-1][ic].isdigit() and R[ir-1][ic+1].isdigit()
            bot_row_pair= R[ir+1][ic-1].isdigit() and not R[ir+1][ic].isdigit() and R[ir+1][ic+1].isdigit()
            one_on_top= R[ir-1][ic-1].isdigit() or R[ir-1][ic].isdigit() or R[ir-1][ic+1].isdigit()
            one_on_bot= R[ir+1][ic-1].isdigit() or R[ir+1][ic].isdigit() or R[ir+1][ic+1].isdigit()
            both_same_line= R[ir][ic-1].isdigit() and R[ir][ic+1].isdigit()
            if top_row_pair:
                T += find_top_row_pair(R,ir,ic)
            elif bot_row_pair:
                T += find_bot_row_pair(R,ir,ic)
            elif one_on_top and one_on_bot:
                A= find_single_on_top(R,ir,ic)
                B= find_single_on_bot(R,ir,ic)
                T+= (A*B)
            elif both_same_line:
                T += find_same_line_pair(R,ir,ic)
            elif one_on_top and R[ir][ic-1].isdigit():
                A= find_single_on_top(R,ir,ic)
                B= find_single_left(R,ir,ic)
                T+= (A*B)
            elif one_on_bot and R[ir][ic-1].isdigit():
                A= find_single_on_bot(R,ir,ic)
                B= find_single_left(R,ir,ic)
                T+= (A*B)
            elif one_on_top and R[ir][ic+1].isdigit():
                A= find_single_on_top(R,ir,ic)
                B= find_single_right(R,ir,ic)
                T+= (A*B)
            elif one_on_bot and R[ir][ic+1].isdigit():
                A= find_single_on_bot(R,ir,ic)
                B= find_single_right(R,ir,ic)
                T+= (A*B)
            else: # It's a star but only one number near.
                pass
print(f"TOTAL: {T}")

That's so baroque it's astonishing that it works. But I'm pretty sure most of that is necessary.