diff --git a/.gitignore b/.gitignore index 2dd43d8..54a115b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ ./*.txt* 201[6-9]/ -202[1-9]/ +202[2-9]/ diff --git a/2021/01.sh b/2021/01.sh new file mode 100755 index 0000000..42251a4 --- /dev/null +++ b/2021/01.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +A=($(< "${1:-1.txt}")); a=${A[0]}; ANS=0 +for b in "${A[@]:1}"; do ((b>a && ++ANS)); a=$b; done +echo "1A: ${ANS}" +a=$((A[0]+A[1]+A[2])); b=$a; idx=(${!A[@]}); ANS2=0 +for i in "${idx[@]:3}"; do ((b+=A[i]-A[i-3], b>a && ++ANS2)); a=$b; done +echo "1B: ${ANS2}" diff --git a/2021/02.sh b/2021/02.sh new file mode 100755 index 0000000..534158c --- /dev/null +++ b/2021/02.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +input=${1:-2.txt} +declare -i X=0 Y=0 y=0 +while read -r dir n; do + case $dir in + up) Y+=-$n;; + down) Y+=$n;; + forward) X+=$n; y+=$((Y*n));; + esac +done < "${input}" +echo "12A: $X*$Y = $((X*Y))" +echo "12B: $X*$y = $((X*y))" +# golfed with awk +#awk 'n=$2 /u/{Y-=n}/n/{Y+=n}/f/{X+=n;y+=n*Y}END{print X*Y,X*y}' ${1:-2.txt} +#paste -d"*" <(paste -sd+ <(rg ^f "${input}" | rev | cut -c 1) | bc) <(paste -sd+ <(rg '^[du]' "${input}" | sed 's/down/1\*/;s/up/-1\*/g' | bc) | bc) | bc + +#paste -d"*" <(paste -sd+ <(paste -d* <(cut -c 1 "${input}") <(rev "${input}" | cut -c -1) <(sed 's/down/1\*/;s/up/-1\*/;s/forward/0\*/g' "${input}" | bc | awk '{total += $0; $0 = total}1') | rg f | tr f 1 | bc ) | bc) <(paste -sd+ <(rg ^f "${input}" | rev | cut -c 1) | bc) | bc +# /u/obluff +paste -d"*" <(paste -sd+ <(sed -n 's/f.* //p' "${input}" )|bc) <(paste -sd+ <(sed 's/down //;s/up /-/g;/f/d' "${input}")|bc) | bc +paste -d"*" <(paste -sd+ <(paste -d* <(cut -c1 "${input}") <(tr -dc '0-9\n' < "${input}") <(x=0 && sed 's/down //;s/up/-/;s/f.*/0/' "${input}" | while read -r a;do echo $((x+=a));done) | sed -n s/f.//p | bc) | bc) <(paste -sd+ <(sed -n 's/f.* //p' "${input}") | bc) | bc diff --git a/2021/03.sh b/2021/03.sh new file mode 100755 index 0000000..3937153 --- /dev/null +++ b/2021/03.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +nextchar() { + local -n most=$1 least=$2 in=$3 + local all; all=$(printf "%s\n" "${in[@]}" | cut -c"$4" | tr -d '\n' ) + local ones=${all//0} zeros=${all//1} + if (( ${#ones} >= ${#zeros} )); then + most+=1; least+=0 + else most+=0; least+=1; fi +} +A=($(< "${1:-3.txt}")) +idx=(${!A[@]}) len=${#A[0]} g="" e="" +for i in "${idx[@]:1:$len}"; do + nextchar g e A "$i" +done +echo "3A: $g*$e = $(($((2#$g))*$((2#$e))))" +O=(${A[@]}) C=(${A[@]}) c=${e:0:1} o=${g:0:1} +for i in "${idx[@]:2:len}"; do + O=($(printf "%s\n" "${O[@]}" | grep "^$o")) + (( ${#O[@]} <= 1 )) && break + nextchar o _ O "$i" +done +for i in "${idx[@]:2:len}"; do + C=($(printf "%s\n" "${C[@]}" | grep "^$c")) + (( ${#C[@]} <= 1 )) && break + nextchar _ c C "$i" +done +# shellcheck disable=SC2128 +echo "3B: $O*$C = $(($((2#$O))*$((2#$C))))" diff --git a/2021/04.sh b/2021/04.sh new file mode 100755 index 0000000..0044255 --- /dev/null +++ b/2021/04.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +declare -i i j=0 +while read -r a b c d e; do + if (( ${#NUMBERS[@]} == 0 )); then NUMBERS=(${a//,/ }); + elif [[ -n $a ]]; then + C[i++]="-$a--$b--$c--$d--$e-" # rows + C[j+5]+="-$a-";C[j+6]+="-$b-";C[j+7]+="-$c-";C[j+8]+="-$d-";C[j+9]+="-$e-"; + ((i%5==0)) && i+=5 j+=10 + fi +done < "${1:-4.txt}" +idx=(${!C[@]}) +for k in "${!NUMBERS[@]}"; do + n=${NUMBERS[k]} + C=("${C[@]//-$n-}") + B=(${C[@]}) # lazy "find empty" + (( ${#B[@]} != ${#C[@]} )) && break +done +for i in "${idx[@]}"; do [[ -z ${C[i]} ]] && break; done +j=$((10*(i/10))) # card number +printf -v sum "%s" "${C[@]:j:5}" +sum=${sum//--/+}; sum=${sum//-} +echo "12A: $((n*sum))" + +printf -v WON "=%d=" {0..990..10} +WON=${WON/=$j=} # only do each card once +for i in "${idx[@]:j:10}"; do C[i]=DONE; done +for n in "${NUMBERS[@]:k+1}"; do + C=("${C[@]//-$n-}") + B=(${C[@]}) + if (( ${#B[@]} != ${#C[@]} )); then + for i in "${idx[@]}"; do + if [[ -z ${C[i]} ]]; then + j=$((10*(i/10))) + WON=${WON/=$j=}; [[ -z $WON ]] && break 2 + for l in "${idx[@]:j:10}"; do C[l]=DONE; done + fi + done + fi +done +printf -v sum "%s" "${C[@]:j:5}" +sum=${sum//--/+}; sum=${sum//-} +echo "12B: $((n*sum))" diff --git a/2021/05.sh b/2021/05.sh new file mode 100755 index 0000000..edf493d --- /dev/null +++ b/2021/05.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +input=${1:-5.txt} +swap(){ + local -n _x=$1 _y=$2 + local tmp=$_x; _x=$_y; _y=$tmp +} +IFS=$' ,\n'; ANS=0; ANS2=0; declare -Ai C=() D=() +while read -r x y _ X Y; do + if (( x == X )); then + (( y > Y )) && swap y Y + while ((y<=Y)); do C[$x.$y]+=1; ((y++));done + elif (( y == Y )); then + (( x > X )) && swap x X + while ((x<=X)); do C[$x.$y]+=1; ((x++));done + elif (( (X-x) == (Y-y) || (X-x) == -(Y-y) )); then + (( x > X )) && swap x X && swap y Y + if (( y > Y )); then i=-1; else i=1; fi + while ((x<=X)); do D[$x.$y]+=1; ((x++,y+=i));done + fi +done < "$input" +ANS=(${C[@]//1}) # Lazy count. Fails if there are 11 crossings +echo "5A: ${#ANS[@]}" +for i in "${!C[@]}"; do D[$i]+=${C[$i]}; done +ANS2=(${D[@]//1}) +echo "5B: ${#ANS2[@]}" diff --git a/2021/06.sh b/2021/06.sh new file mode 100755 index 0000000..afbc848 --- /dev/null +++ b/2021/06.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +declare -i A=(0 0 0 0 0 0 0 0 0) n=0 +while read -r freq age; do A[age]=$freq; done < <(grep -o "[0-9]" "${1:-6.txt}" | sort | uniq -c) +solve() { + while ((n < $1)); do + i=$((n++%9)) + A[i-2]+=${A[i]} + done + sum=0; for i in "${!A[@]}"; do ((sum+=A[i])); done +} +solve 80 +echo "6A: $sum" +solve 256 +echo "6B: $sum" diff --git a/2021/07.sh b/2021/07.sh new file mode 100755 index 0000000..9efd043 --- /dev/null +++ b/2021/07.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +input=$(< "${1:-7.txt}") +A=($(echo -e "${input//,/\\n}" | sort -n)) +N=${#A[@]} n="" +declare -i avg=$(((${input//,/+})/N)) sum2=0 +for k in "${A[@]}"; do + n+=+$((k-A[N/2])); + dist=$((k-avg)); dist=${dist/-} + sum2+=$((dist*(dist+1)/2)) +done +sum=${n//-} # lazy abs() +echo "7A: $((sum))" +echo "7B: $sum2" diff --git a/2021/08.sh b/2021/08.sh new file mode 100755 index 0000000..fd71b08 --- /dev/null +++ b/2021/08.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +input=${1:-8.txt} +printf "8A: " +cut -d\| -f 2 "$input" | grep -Eo "[a-g]+" | grep -cE "^([a-g]{2,4}|[a-g]{7})$" +sum="" +while read -r -a A; do + T4="" T7="" n="" + for i in "${A[@]:0:10}"; do + case ${#i} in # Just find 4 and 7, ignore the rest + 3) T7=$i; [[ -n $T4 ]] && break;; + 4) T4=$i; [[ -n $T7 ]] && break;; + esac + done + for i in "${A[@]:11}"; do + case ${#i},${i//[$T4]},${i//[$T7]} in + 2,*) n+=1;; + 3,*) n+=7;; + 4,*) n+=4;; + 5,???,*) n+=2;; + 5,*,??) n+=3;; + 5,*) n+=5;; + 6,*,????) n+=6;; + 6,??,*) n+=9;; + 6,*) n+=0;; + 7,*) n+=8;; + *) echo "ERROR: ${#i},${i//[$T4]},${i//[$T7]}"; break 2;; + esac + done + sum+=+$((10#$n)) +done < "${input}" +echo "8B: $((sum))" diff --git a/2021/09.sh b/2021/09.sh new file mode 100755 index 0000000..4b4a9a9 --- /dev/null +++ b/2021/09.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash +A=($(< "${1:-9.txt}")) +B=(9${A//?/9}9); for i in "${A[@]}"; do B+=(9${i}9); done; B+=(${B[0]}); C=("${B[@]}"); +n=${#A} N=${#A[@]} +declare -A LOWS +for ((y=1; y<=N; ++y)); do + for ((x=1; x<=n; ++x)); do + while (( ${B[y]:x:1} > ${B[y]:x+1:1} )); do ((++x)); done + low=${B[y]:x:1} + if (( low < ${B[y]:x-1:1} && low < ${B[y]:x+1:1} + && low < ${B[y-1]:x:1} && low < ${B[y+1]:x:1})); then + LOWS[$y,$x]=$low + fi + done +done +printf -v sum "+%s" "${LOWS[@]}" +echo "9A: $((sum + ${#LOWS[@]}))" + +C=(${C[@]//[0-8]/-}) c=0 +F=({a..z} {A..Z} {0..8} + _ / "=") +idx=($(printf "%s\n" "${!LOWS[@]}" | sort -n)) +for i in "${idx[@]}"; do + LOWS[$i]=${F[c]} + x=${i/*,} y=${i/,*} + C[y]=${C[y]:0:x}${F[c]}${C[y]:x+1} + for k in 1 -1; do + j=$k + while [[ ${C[y+j]:x:1} != 9 ]]; do + C[y+j]=${C[y+j]:0:x}${F[c]}${C[y+j]:x+1} + ((j+=k)); + done + done + (( ++c >= ${#F[@]} && ++d)) && c=0 +done +# Terrible "grow" +for i in {1..6}; do + C=($(printf "%s\n" "${C[@]}" | sed -e "s/-\([^9-]\)/\1\1/g;s/\([^9-]\)-/\1\1/g")) +done +while [[ "${C[*]}" == *-* ]]; do + #echo "round $((++round))" + for y in "${!C[@]}"; do + [[ ${C[y]} != *-* ]] && continue + minus=($(sed "s/./&\n/g" <<< "${C[y]:1}" | grep -n '-')) + for x in "${minus[@]//:*}"; do + for j in 1 -1; do + k=$j + while [[ ${C[y+k]:x:1} == - ]]; do ((k+=j)); done + p=${C[y+k]:x:1} + if [[ $p != 9 ]]; then + while ((k+=-j)); do C[y+k]=${C[y+k]:0:x}$p${C[y+k]:x+1}; done + C[y]=${C[y]:0:x}$p${C[y]:x+1} + break + fi + done + done + done + for i in {1..2}; do + C=($(printf "%s\n" "${C[@]}" | sed -e "s/-\([^9-]\)/\1\1/g;s/\([^9-]\)-/\1\1/g")) + done +done + +AREA=() +for i in "${F[@]}"; do + while read -r A;do + AREA+=(${#A}:$A) + done < <(printf "%s\n" "${C[@]}" | grep -a1 "$i" | tr -cd "$i-" | tr -s '-' '\n') +done +BIG=($(printf "%s\n" "${AREA[@]//:*}" | sort -nr)) +echo "9B: $((BIG[0]*BIG[1]*BIG[2]))" diff --git a/2021/10.sh b/2021/10.sh new file mode 100755 index 0000000..f7613c5 --- /dev/null +++ b/2021/10.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +A=($(tr '()[]{}<>' '1a2b3c4d' < "${1:-10.txt}")) +while [[ ${A[*]} =~ (1a|2b|3c|4d) ]]; do + A=(${A[@]//1a}); A=(${A[@]//2b}); A=(${A[@]//3c}); A=(${A[@]//4d}) + echo $((++i)) +done +# P1 only contains closing brackets. P2 only contains lines with no closing brackets +# shellcheck disable=SC2034 +a=3 b=57 c=1197 d=25137 P1=(${A[@]//[1234]}) P2=(${A[@]/*[^1234]*}) C=() +printf -v sum "+%.1s" "${P1[@]}" # Only print first char +echo "10A: $((sum))" +while read -r -a B; do + sum=0 + for i in "${B[@]}"; do sum=$((sum*5+i)); done + C+=($sum) +done < <(printf "%s\n" "${P2[@]}" | rev | sed 's/./ &/g') +C=($(printf "%s\n" "${C[@]}" | sort -n )) +N=${#C[@]} +echo "10B: ${C[N/2]}" diff --git a/2021/11.sh b/2021/11.sh new file mode 100755 index 0000000..34865de --- /dev/null +++ b/2021/11.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +A=($(sed -e 's/^/X/;s/./& /g' "${1:-11.txt}")) +flashes=0 last=0 idx=(${!A[@]}) +A+=(X X X X X X X X X X X X) +A=(${A[@]//X/-999999999}) +flash(){ + local n=$1 j cur + ((++flashes)) + # hardcoded neighbours. There's an X on one side, so Y-offset is cols+1 + for j in -12 -11 -10 -1 1 10 11 12; do + cur=$((n+j)) + ((++A[cur] >= 10 && FLASHED[cur]++ == 0 )) && flash $cur + done +} +round(){ + FLASHED=() + for i in "${idx[@]}"; do ((++A[i] >= 10 && FLASHED[i]++ == 0)) && flash "$i"; done + #A=(${A[@]//-99??/-9999}) + A=(${A[@]//1?/0}) +} +for rounds in {1..100}; do + round +done +echo "11A: $flashes" +while ((flashes-last != 100)); do + ((last=flashes,rounds++)) + round +done +echo "11B: $rounds" diff --git a/2021/12.sh b/2021/12.sh new file mode 100755 index 0000000..1a60946 --- /dev/null +++ b/2021/12.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +declare -A Next=() BIG=() +IFS=$' -\n' +while read -r a b; do + [[ $a != start ]] && Next[$b]+="$a " + [[ $b != start ]] && Next[$a]+="$b " +done < "${1:-12.txt}" +for i in "${!Next[@]}"; do [[ -z ${i//[A-Z]} ]] && BIG[$i]=1; done +#for i in "${!Next[@]}"; do echo "$i: '${Next[$i]}'"; done + +r() { + local route=$1 cur=$2 visited=$3 k + if [[ $cur == end ]]; then + PATHS+=1 + elif [[ -n ${BIG[$cur]} || $route != *:${cur}:* ]] \ + || ((visited++ == 0)); then + for k in ${Next[$cur]}; do + r "$route:$cur" "$k" $visited + done + fi +} + +declare -i PATHS=0 +r "" start 1 +echo "12A: ${PATHS}" +PATHS=0 +r "" start 0 +echo "12B: ${PATHS}" diff --git a/2021/13.sh b/2021/13.sh new file mode 100755 index 0000000..c1c29a3 --- /dev/null +++ b/2021/13.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +# Oneliner for part 1. First fold hardcoded. +#echo "13A: $(while read x y; do ((x >655)) && x=$((2*655-x)); echo $x,$y; done < <(grep , 13.txt| tr , ' ') | sort -u | wc -l)" + +fold_x() { + local n=$1 C=(${DOTS[@]}) i + for i in "${!C[@]}"; do + x=${C[i]//,*} + ((x>n)) && C[i]=$(((2*n-x))),${C[i]/*,} + done + DOTS=($(printf "%s\n" "${C[@]}"| sort -n | uniq)) +} +fold_y() { + local n=$1 C=(${DOTS[@]}) i + for i in "${!C[@]}"; do + y=${C[i]//*,} + ((y>n)) && C[i]=${C[i]/,*},$(((2*n-y))) + done + DOTS=($(printf "%s\n" "${C[@]}"| sort -n | uniq)) +} + +DOTS=($(grep , "${1:-13.txt}")) +FOLDS=($(grep -o '.=.*' "${1:-13.txt}")) +fold_"${FOLDS[0]/=*}" "${FOLDS[0]:2}" +echo "13A: ${#DOTS[@]}" +for line in "${FOLDS[@]:1}"; do + xy=${line/=*} + "fold_$xy" "${line:2}" +done +TEXT=() spaces=" " +for i in "${!DOTS[@]}"; do + x=${DOTS[i]//,*} y=${DOTS[i]//*,} len=${#TEXT[y]} + TEXT[y]+="${spaces:0:x-len}#" +done +echo "13B:" +printf "%s\n" "${TEXT[@]}" diff --git a/2021/14.sh b/2021/14.sh new file mode 100755 index 0000000..f07d8e2 --- /dev/null +++ b/2021/14.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +input=${1:-14.txt} +declare -A SET=() +declare -iA COUNT=() +POL=$(head -1 "${input}") +while read -r a _ b;do + SET[$a]="${a:0:1}$b $b${a:1}"; +done < <( grep ' -> ' "${input}") +for ((i=0; i<${#POL}-1;i++)); do + j=${POL:i:2} + COUNT[$j]+=1; +done + +solve() { + for ((; round < $1; round++)); do + local -iA NEW_COUNT=() + for j in "${!COUNT[@]}"; do + for k in ${SET[$j]}; do + NEW_COUNT[$k]+=${COUNT[$j]} + done + done + COUNT=() + for j in "${!NEW_COUNT[@]}"; do COUNT[$j]=${NEW_COUNT[$j]}; done + done +} +diff() { + local -n ans=$1 + local -Ai ALPHA=([${POL:0:1}]=1 [${POL: -1}]=1) # ends are only counted once + for k in "${!COUNT[@]}"; do + ALPHA[${k:0:1}]+=$((COUNT["$k"])) + ALPHA[${k:1}]+=$((COUNT["$k"])) + done + FREQ=($(printf "%s\n" "${ALPHA[@]}" | sort -n)) + #shellcheck disable=SC2034 + ans=$((FREQ[-1]/2-FREQ[0]/2)) +} + +round=0 +for i in 10 40;do + solve $i + diff "ANS[n++]" +done +echo "14A: ${ANS[0]}" +echo "14B: ${ANS[1]}" diff --git a/2021/README.md b/2021/README.md new file mode 100644 index 0000000..1bc1077 --- /dev/null +++ b/2021/README.md @@ -0,0 +1,67 @@ +# adventofcode.sh +Advent of Code 2021, done in bash. Because I can't stop +https://adventofcode.com/2021/ + +Input can be given on the command line. +Defaults to *number*.txt in the same folder (no leading 0). +Added my input files in input/ folder. +Setting env variable PUREBASH to any value (or giving a second argument) will use slow bash instead of using faster awk when needed. + +Description of what I'm doing. Contains spoilers.... + +### 01.sh + 1. Dumb looping through all variables. + 2. Use a sliding window. 2/3 of the values in the windows are the same, but this is fine. + +### 02.sh + Simple switch to handle all cases. Both parts handled at once. + +### 03.sh + 1. Slightly tricky. Use a function to find the most/least common number in each position. + 2. Same function, but change the input for every char. + +### 04.sh + 1. Use an array to store all lines/rows of a bingo card. Delete numbers from all lines until a line is empty. + 2. Remove a card when a line is empty. Keep going until all cards are gone. + +### 05.sh + 1. Mark all points on the grid in a hash table. Return a count of points that are != 1. + 2. Mark diagonals on a second hash table. Add the first hash table to it and return a count of points that are != 1. + +### 06.sh + 80 rounds can be done in half a second with string shenaningans, but 256 days would take terabytes of RAM, and lots of time. + Instead store a count of the fishes each day in a circular buffer, with the current 0-day at n%9. Bash arrays are circular by default. + +### 07.sh + 1. Use the median. + 2. Use the mean. + +### 08.sh + 1. Simple grep to fish out the output digits and isolate the ones with 2-4 or 7 digits. + 2. A damn bother, since the letters were scrambled. Assigned the numbers based on length. The 5-6 length ones got special handling, by checking the length after removing the letters that make up 4 and 7. That was enough to identify every one. + +### 09.sh + 1. For each line, go forward in the string while the next char is lower. Once you reach that, add a low point if all 4 neighbours are higher. + 2. Clean out the map so that all numbers except edges and 9s are empty. Add each low point from part 1 with a different symbol. + Use terrible bash to grow the symbols until the whole map is filled. Count the size of each symbol block. + +### 10.sh + Renamed the brackets for easier grok-ing, and so that I could assign values to each one. + 1. Keep removing "innermost" bracket pairs until none remains, then remove opening brackets and sum up the values in the first column, if any. + 2. Remove any string containing a closing bracket, reverse the rest. Instead of adding a closing bracket, add the value for each opening bracket at a time. + +### 11.sh + 1. Convert the map to a 1D array, and add -9999999 to the sides so I don\'t need to think about edges. Recursive function to flash the octopi. + 2. Run until all fields flash at once. + +### 12.sh + 1. Brute force. Simplified version of the recursive functions of 2015 (9 and 13). Collect a hashmap with the destinations for each cave. Remove "start" from the destinations to simplify coding. + 2. Add a dumb check to allow the first lowercase cave twice. Takes way too long to run. + +### 13.sh + 1. Functions fold_x/fold_y for easier handling. Fold and remove duplicates for first line. + 2. Repeat for rest of the lines. Collect space-delimited dots in an array of strings. Print the array. + +### 14.sh + Collect the conversions in an array. Count the number of pairs in the initial polymer. For each polymer, convert the input to 2 outputs. + For the final count, every char is part of 2 pairs except the ends, so add 1 to the ends and then divide by 2 for the final number.