initial commit

main
magical 2024-12-02 01:22:03 +00:00
commit 02ba7c97f9
5 changed files with 198 additions and 0 deletions

1
.gitignore vendored 100644
View File

@ -0,0 +1 @@
/cookie

79
cron.sh 100755
View File

@ -0,0 +1,79 @@
#!/bin/bash
set -euo pipefail
# install me in your crontab!
# 0 5 1-25 12 * $HOME/adventofcode2023/cron.sh auto
year=2023
root=~/adventofcode${year}
getinput() {
curl -fsS -b "$root/cookie" -O "https://adventofcode.com/$year/day/$1/input"
}
getsamples() {
curl -fsS "https://adventofcode.com/$year/day/$1" |
awk '/<pre><code>/ {sub("<pre><code>", ""); CODE=1; IDX+=1} /<\/code>/ {CODE=0} CODE {print > sprintf("sample%d.in", IDX)}'
}
if [[ "$*" = "auto" ]]; then
# get the current day in UTC with and without zero padding.
# UTC is a few hours ahead of US Eastern time, so
# it should match the day of the current puzzle even
# if the clocks on tilde.town and AoC aren't synced exactly
day_unpadded=$(TZ=UTC date '+%-d')
day_padded=$(TZ=UTC date '+%0d')
dir=$root/day$day_padded
elif (( $# == 1 || $# == 2 )); then
day_unpadded=$(printf "%d" "$1")
day_padded=$(printf "%02d" "$1")
dir=${2:-}
else
echo >&2 "usage: $0 day [out_dir]"
echo >&2 " $0 auto"
exit 0
fi
if [[ -z "$dir" ]]; then
dir=$root/day$day_padded
fi
if test -e "$dir"; then
echo >&2 "'$dir' already exists! refusing to continue"
exit 1
fi
if ! test -e "$root/cookie"; then
echo >&2 "warning: cookiefile '$root/cookie' not found. consider creating it"
fi
cwd=${PWD:-$(pwd)}
mkdir "$dir"
cd "$dir" || exit 1
error=0
if ! getinput "$day_unpadded"; then
echo >&2 "error: failed to get input for day $day_unpadded"
error=1
fi
if ! getsamples "$day_unpadded"; then
echo >&2 "error: failed to get sample inputs for day $day_unpadded"
error=1
fi
if [[ $error -ne 0 ]]; then
# we might not have managed to grab any files
# at all. try to clean up after ourselves by removing
# the directory (if it's empty)
if cd "$cwd" && rmdir --ignore-fail-on-non-empty "$dir" >&2; then
if ! test -d "$dir"; then
echo "cleaning up '$dir'"
else
echo "not cleaning up '$dir'"
fi
fi
fi
exit $error

3
getinput.sh 100755
View File

@ -0,0 +1,3 @@
#!/bin/bash
set -eu
curl -b ../cookie -O "https://adventofcode.com/2024/day/$1/input"

3
getsample.sh 100755
View File

@ -0,0 +1,3 @@
#!/bin/bash
set -eu -o pipefail
curl -fsS https://adventofcode.com/2024/day/"$1" | awk '/<pre><code>/ {sub("<pre><code>", ""); CODE=1; IDX+=1} /<\/code>/ {CODE=0} CODE {print > sprintf("sample%d.in", IDX)}'

112
prelude.tcl 100644
View File

@ -0,0 +1,112 @@
package require Tcl 8.6
#package require textutil ;# from tcllib
namespace import tcl::mathfunc::min
namespace import tcl::mathfunc::max
#puts "prelude [info script]"
proc {#} args {}
# short aliases for common things
proc llen {lst} { return [llength $lst] }
proc slen {str} { return [string length $str] }
proc trim args { return [uplevel [concat string trim $args]] }
proc replace {str args} { return [string map $args $str] }
# sum and product
proc ladd {list} {
set t 0
foreach x $list { incr t $x }
return $t
}
proc lmul {list} {
set p 1
foreach x $list { set p [expr {$p * $x}] }
return $p
}
# split s on a substring
proc splitstr {s sep} {
# replace $sep with ascii char 30, aka "record separator"
return [split [replace $s $sep "\x1E"] "\x1E"]
}
proc must_regexp args {
if {! [uplevel [concat regexp $args]]} {
error "regexp failed"
}
}
proc regsplit {re str {varname {}}} {
set result [textutil::split::splitx $str $re]
if {$varname ne ""} {
upvar $varname dest
set dest $result
}
return $result
}
# transpose a list of strings
#
# % transpose {abc 123}
# a1 b2 c3
# % transpose a1 b2 c3
# abc 123
#
proc transpose strings {
set out {}
set C [slen [lindex $strings 0]]
for {set i 0} {$i < $C} {incr i} {
set column {}
foreach row $strings {
lappend column [string index $row $i]
}
lappend out [join $column ""]
}
return $out
}
# transpose a list of lists
#
# % ltranspose {{a b c} {1 2 3}}
# {a 1} {b 2} {c 3}
# % ltranspose {{a 1} {b 2} {c 3}}
# {a b c} {1 2 3}
#
proc ltranspose lists {
set out {}
set C [llen [lindex $lists 0]]
for {set i 0} {$i < $C} {incr i} {
set column {}
foreach row $lists {
lappend column [lindex $row $i]
}
lappend out $column
}
return $out
}
# extracts one or more indexes from a strided list
# e.g.
# % set l {0 1 2 a b c x y z}
# % lextract $l 3 0
# 0 a x
# % lextract $l 3 {1 2}
# 1 2 b c y z
#
# equivalent to [lmap {a b c} $lst {list $b $c}] except that
# you don't have to name the list elements
proc lextract {lst stride index} {
set i 0
set out {}
if {$stride <= 0} { error "stride must be positive: $stride" }
for {set i 0} {$i < [llength $lst]} {incr i $stride} {
foreach j $index {
lappend out [lindex $lst $i+$j]
}
}
return $out
}