Skip to content

Commit 6533a9f

Browse files
committed
15/22/23: Pure bash. Minor speedup in slowest days
14: Use printf -v instead of eval 15B: Use function name reference instead of eval 22B: Remove subshells for score Simplify hash and recursion check. Script return code is not depending on the winner of part 2. Speeds up by 15% for some reason. 23B: Add optional pure bash version instead of awk only. Runs in about 20 minutes. Had a 15 minute version but I didn't commit it. Cleanup: Use globs in game of life.
1 parent 7d0c52c commit 6533a9f

File tree

9 files changed

+85
-53
lines changed

9 files changed

+85
-53
lines changed

11.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ while [ ${#I} != 0 ]; do
77
for i in $I; do
88
for j in $J; do
99
x=${B[i]:j:1}
10-
if [[ "$x" == "L" || "$x" == "X" ]]; then
10+
if [[ "$x" == [LX] ]]; then
1111
s="${B[i-1]:j-1:3}${B[i]:j-1:3}${B[i+1]:j-1:3}"
1212
s=${s//[L.]}; s=${s/$x}
13-
if [[ ${#s} == 0 ]]; then x=X; elif [[ ${#s} -ge 4 ]]; then x=L; fi
13+
if [[ -z $s ]]; then x=X; elif (( ${#s} >= 4 )); then x=L; fi
1414
fi
1515
l+=$x
1616
done
@@ -33,7 +33,7 @@ while [ ${#change} != 0 ]; do
3333
for i in $I; do
3434
for j in $J; do
3535
x=${B[i]:$j:1}
36-
[[ "$x" != "L" && "$x" != "X" ]] && l+=$x && continue
36+
[[ "$x" != [LX] ]] && l+=$x && continue
3737
r=${B[i]:j+1}; r=${r//.}; R=${B[i]:0:j}; R=${R//.}
3838
s=${R: -1}${r:0:1}
3939
k=1; d=${B[i-k]:j-k:1}; while [ "$d" = '.' ]; do ((++k)); d=${B[i-k]:j-k:1}; done; s+=$d
@@ -44,7 +44,7 @@ while [ ${#change} != 0 ]; do
4444
k=1; d=${B[i+k]:j+k:1}; while [ "$d" = '.' ]; do ((++k)); d=${B[i+k]:j+k:1}; done; s+=$d
4545
#[[ ${#s} == 8 ]] || echo "ERROR: ($s) $k = $i, $j"
4646
s=${s//L}
47-
if [[ ${#s} == 0 ]]; then l+=X; elif [[ ${#s} -ge 5 ]]; then l+=L; else l+=$x; fi
47+
if [[ -z $s ]]; then l+=X; elif (( ${#s} >= 5 )); then l+=L; else l+=$x; fi
4848
done
4949
[ "${B[i]}" != "L${l}L" ] && change+=1
5050
C+=(L${l}L); l=

14.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
#! /usr/bin/env bash
22
IFS=$'\n'
33
A=($(< "${1:-14.txt}"))
4-
declare -a mem
4+
declare -a mem mem2
55
for i in "${A[@]}"; do
66
case $i in
7-
mask*) y=${i//*= }; o=$((2#${y//X/0})); z=$((2#${y//X/1}));;
8-
mem*) y=${i//*= }; eval "${i// =*}=$(((y&z)|o))";;
7+
mask*) y=${i/*= }; o=$((2#${y//X/0})); z=$((2#${y//X/1}));;
8+
mem*) y=${i/*= }; printf -v "${i/ =*}" "%s" $(((y&z)|o));;
99
esac
1010
done
1111
printf -v sum "+%s" "${mem[@]}"

15.sh

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#! /usr/bin/env bash
22
A=($(<"${1:-15.txt}"))
3-
PUREBASH=${2:-$PUREBASH}
43
declare -a B=()
54
i=0; l=""
65
for a in "${A[@]}"; do B[a]=$((++i)); done
@@ -9,28 +8,27 @@ while [ $i -lt 2020 ]; do
98
done
109
echo "15A: $n"
1110

12-
# eval is slow, but large arrays are slower
1311
# Split the higher numbers into smaller arrays
12+
# large arrays are incredibly slow
1413
sharded_swap() { # syntax: l=B[$1]; B[$1]=$2
15-
local x="B$(($1>>8))[$(($1&255))]"
16-
#local x="B$(($1>>6))[$1&63]"
17-
eval "l=\${$x}; $x=$2"
14+
local -n x=$1
15+
l=$x; x=$2
1816
}
1917

20-
if [ -n "$PUREBASH" ]; then
18+
if [[ -n ${2:-$PUREBASH} ]]; then
2119
trap 'echo "Giving up (i=$i, $SECONDS seconds)"; exit 1' TERM INT
22-
echo "This could take 15-20 minutes. Ctrl-C or 'kill $$' to stop."
20+
echo "Part 2 could take 15-20 minutes. Ctrl-C or 'kill $$' to stop."
2321
declare -a B{1..1000}
24-
for N in $(seq 1000000 1000000 30000000 ); do
22+
for N in {1000000..30000000..1000000}; do
2523
while ((i < N)); do
2624
n=$((i-${l:-$i}));
2725
if (( n < 2048 )); then
2826
l=${B[n]}; B[n]=$((++i))
2927
else
30-
sharded_swap $n $((++i))
28+
sharded_swap "B$((n>>8))[$((n&255))]" $((++i))
3129
fi
3230
done
33-
echo "$i took $SECONDS seconds" ;
31+
echo "$i took $SECONDS seconds"
3432
done
3533
echo "15B: $n"
3634
else

17.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ solve17() {
3030
done
3131
for i in "${!C[@]}"; do
3232
if [[ -n "${B[$i]}" ]]; then
33-
[[ ${#C[$i]} == 2 || ${#C[$i]} == 3 ]] || unset "B[$i]"
33+
[[ ${#C[$i]} == [23] ]] || unset "B[$i]"
3434
else
3535
[[ ${#C[$i]} == 3 ]] && B[$i]=1
3636
fi

22.sh

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,27 @@ while [[ -n "$A" && -n "$B" ]]; do
1717
B=(${B[@]:1})
1818
#((++round%500==0)) && echo "$round: ${#A[@]}/${#B[@]}"
1919
done
20-
echo "22A: $(score "${A[@]}" "${B[@]}")"
20+
printf "22A: "; score "${A[@]}" "${B[@]}"
2121

2222
r() {
2323
declare -A H
24-
local a=($1) b=($2) hash
24+
local a=($1) b=($2) depth=$3
2525
# shellcheck disable=SC2128,SC2181
2626
while [[ -n "$a" && -n "$b" ]]; do
27-
hash=${a[*]}X${b[*]}
28-
if (( H[${hash// /_}]++ > 0)); then
27+
if (( ++H[${a[*]}X${b[*]}] > 1)); then
2928
return 0
3029
elif [[ $a -lt ${#a[@]} && $b -lt ${#b[@]} ]]; then
31-
r "${a[*]:1:a}" "${b[*]:1:b}"
30+
r "${a[*]:1:a}" "${b[*]:1:b}" $((depth+1))
3231
else
3332
[[ $a -gt $b ]]
3433
fi
3534
if [[ $? == 0 ]]; then a+=($a $b); else b+=($b $a); fi
3635
a=(${a[@]:1})
3736
b=(${b[@]:1})
38-
#((${#H[@]}%1000==0)) && echo "$round:${#H[@]}: ${#a[@]}/${#b[@]}"
37+
#((${#H[@]}%1000==0)) && echo "$depth:${#H[@]}: ${#a[@]}/${#b[@]}"
3938
done
40-
((${#a[*]}+${#b[*]} == N)) && echo "22B: $(score "${a[@]}" "${b[@]}")"
41-
return ${#b[@]}
39+
[[ $depth != 0 ]] && return ${#b[@]}
40+
printf "22B: "; score "${a[@]}" "${b[@]}"
4241
}
4342

44-
r "${C[*]:0:N/2}" "${C[*]:N/2}"
43+
r "${C[*]:0:N/2}" "${C[*]:N/2}" 0

23.sh

Lines changed: 55 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,60 @@ while [ "${A:i:1}" != "$c" ]; do ((++i==9)) && i=0; done
1313
((++i==9)) && i=0
1414
done
1515
echo "23A: ${A/*1}${A/1*}"
16-
A=$(<"${1:-23.txt}")
16+
A=$(<"${1:-23.txt}") a=0 b=0 c=0
1717
#A=389125467
18-
grep -o "[1-9]" <<< "$A" | awk '
19-
{ if(last){ C[last]=$0 }else{ cup=$0 }; last=$0; ++i; }
20-
END{
21-
N=10000000;
22-
max=1000000;
23-
C[last]=++i;
24-
while (i<max) {C[i-1]=++i}
25-
C[max]=cup;
26-
for (i=0; i<N; i++) {
27-
a=C[cup]; b=C[a]; c=C[b];
28-
d=cup; if (--d<=0) { d=max }
29-
while (d == a || d == b || d == c) {
30-
if (--d<=0) { d=max }
31-
}
32-
C[cup]=C[c];
33-
tmp=C[d]; C[d]=a; C[c]=tmp; cup=C[cup];
18+
if [[ -n ${2:-$PUREBASH} ]]; then
19+
getC() {
20+
local -n x_=$1 Cy=$2
21+
# shellcheck disable=SC2034
22+
x_=$Cy
23+
}
24+
swap() {
25+
declare -n Cc=$1 Cd=$2 Ccup=$3
26+
Ccup=$Cc; tmp=$Cd; Cd=$a; Cc=$tmp; cup=$Ccup
3427
}
35-
printf "23B: %d*%d = %d\n", C[1] , C[C[1]], C[1]*C[C[1]]
36-
}'
28+
trap 'echo "Giving up (i=$i, $SECONDS seconds)"; exit 1' TERM INT
29+
echo "Part 2 could take 15-20 minutes. Ctrl-C or 'kill $$' to stop."
30+
N=10000000 max=1000000
31+
B=($(grep -o "[1-9]" <<< "$A"))
32+
cup=${B[0]}
33+
j=0; for i in {1..1000000}; do printf -v "C$((j>>8))[j&255]" "%s" "$i"; j=$i; done
34+
last=$cup; for i in "${B[@]:1}"; do C0[last]=$i; last=$i; done
35+
printf -v "C$((max>>8))[max&255]" "%s" "$cup"
36+
echo "Ready $SECONDS second $cup"
37+
i=0
38+
for N in {500000..10000000..500000}; do
39+
for (( ; i < N; ++i)); do
40+
getC a "C$((cup>>8))[cup&255]"; getC b "C$((a>>8))[a&255]"; getC c "C$((b>>8))[b&255]"
41+
((d=cup-1)) || d=$max
42+
while ((d == a || d == b || d == c)); do
43+
((--d)) || d=$max
44+
done
45+
swap "C$((c>>8))[c&255]" "C$((d>>8))[d&255]" "C$((cup>>8))[cup&255]"
46+
done
47+
echo "$i took $SECONDS seconds: cup=$cup"
48+
done
49+
a=${C0[1]}; getC b "C$((a>>8))[a&255]"
50+
echo "23B: $a * $b = $((a*b))"
51+
else
52+
grep -o "[1-9]" <<< "$A" | awk '
53+
{ if(last){ C[last]=$0 }else{ cup=$0 }; last=$0; ++i; }
54+
END {
55+
N=10000000;
56+
max=1000000;
57+
C[last]=++i;
58+
while (i<max) {C[i-1]=++i}
59+
C[max]=cup;
60+
for (i=0; i<N; i++) {
61+
#if (i%500000 ==0) {print i, "cup=", cup }
62+
a=C[cup]; b=C[a]; c=C[b];
63+
d=cup; if (--d<=0) { d=max }
64+
while (d == a || d == b || d == c) {
65+
if (--d<=0) { d=max }
66+
}
67+
C[cup]=C[c]; tmp=C[d]; C[d]=a; C[c]=tmp;
68+
cup=C[cup];
69+
}
70+
printf "23B(awk): %d*%d = %d\n", C[1] , C[C[1]], C[1]*C[C[1]]
71+
}'
72+
fi

24.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ solve24() {
3030
done
3131
for i in "${!C[@]}"; do
3232
if [[ -n "${B[$i]}" ]]; then
33-
[[ ${#C[$i]} == 1 || ${#C[$i]} == 2 ]] || unset "B[$i]"
33+
[[ ${#C[$i]} == [12] ]] || unset "B[$i]"
3434
else
3535
[[ ${#C[$i]} == 2 ]] && B[$i]=1
3636
fi

25.sh

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
#! /usr/bin/env bash
22
A=($(< "${1:-25.txt}"))
3-
PUREBASH=${2:-$PUREBASH}
4-
if [ -n "$PUREBASH" ]; then
3+
if [[ -n ${2:-$PUREBASH} ]]; then
54
trap 'echo "Giving up after $SECONDS seconds"; exit 1' INT TERM
65
echo "This could take a minute. Ctrl-C or 'kill $$' to exit"
76
declare -i k=1 e=1 key1=${A[0]} key2=${A[1]}

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ https://adventofcode.com/2020/
44

55
Input not included, but can be given on the command line.
66
Defaults to *number*.txt in the same folder (no leading 0).
7-
Setting env variable PUREBASH to any value (or giving a second argument) will use slow bash instead of using faster awk in days 15 and 25.
7+
Setting env variable PUREBASH to any value (or giving a second argument) will use slow bash instead of using faster awk in days 15, 23 and 25.
88

99
Description of what I'm doing. Contains spoilers....
1010

@@ -66,7 +66,7 @@ Description of what I'm doing. Contains spoilers....
6666
2. Simple (possibly not correct) implementation of Chinese Remainder Theorem.
6767

6868
### 14.sh
69-
1. Switch case similar to day 4 and 8. Use eval to avoid parsing the number in mem\[x\].
69+
1. Switch case similar to day 4 and 8. Use printf -v to avoid parsing the number in mem\[x\].
7070
2. Use IFS and read to simplify handling.
7171
Recursive function that converts the first X to 1 and 0, and calls itself twice until no X remains. Then do the thing.
7272

@@ -113,13 +113,13 @@ Description of what I'm doing. Contains spoilers....
113113

114114
### 23.sh
115115
1. String manipulation. Lots of trial and error to get it right.
116-
2. Not suitable for bash. Done in awk, but still takes about half a minute
116+
2. Just over 20 minutes in bash. Awk is more than 60 times faster.
117117

118118
### 24.sh
119119
1. Use an X-Y grid with only diagonals. One step can be (±2,0) or (±1,±1). Modify input so that standalone e/w are doubled, and count the total movement.
120120
2. Hex game of life. Use the same index trick as day 17. Access the grid manually, since a loop would be a PITA.
121121

122122
### 25.sh
123-
1. A couple of while loops. Yay.
123+
1. While loop for 15M rounds. Yay. Awk finishes in seconds.
124124
2. I need to complete 20-2 to get this one. Nope.
125125

0 commit comments

Comments
 (0)