├── 2015 ├── day08 │ ├── day08b │ ├── day08a │ └── day08a.pl ├── day05 │ ├── day05b │ └── day05a ├── day10 │ ├── day10b │ └── day10a ├── day12 │ ├── day12a │ └── day12b ├── day01 │ ├── day01a │ └── day01b ├── day04 │ ├── day04b │ ├── day04a │ ├── day04b_par │ └── day04b.pl ├── day11 │ ├── day11b.pl │ └── day11b ├── day03 │ ├── day03a │ └── day03b ├── day20 │ └── day20b.pl ├── day17 │ ├── day17a │ ├── day17a.pl │ └── day17b.pl ├── day25 │ └── day25a ├── day16 │ ├── day16a │ └── day16b ├── day02 │ ├── day02a │ └── day02b ├── day19 │ ├── day19a │ └── day19b ├── day06 │ ├── day06b.pl │ └── day06b └── day14 │ └── day14a ├── 2016 ├── day21 │ └── day21b ├── day20 │ ├── day20a │ └── day20a.awk ├── bonus │ └── bonus ├── day07 │ ├── day07b_bash │ ├── day07a_bash │ ├── day07a │ └── day07b ├── day03 │ ├── day03a │ └── day03b ├── day05 │ ├── day05a │ ├── day05a.pl │ ├── day05b │ └── day05b.pl ├── day06 │ ├── day06a │ └── day06b ├── day09 │ ├── day09b │ └── day09a ├── day22 │ └── day22a ├── day16 │ ├── day16b.pl │ ├── day16a │ └── day16b ├── day04 │ ├── day04a_bash │ └── day04b_bash ├── day02 │ ├── day02a │ └── day02b ├── day15 │ ├── day15a │ └── day15b ├── day19 │ ├── day19a │ └── day19b ├── day01 │ ├── day01a │ └── day01b ├── day18 │ ├── day18b │ └── day18a ├── day12 │ ├── day12a │ └── day12b ├── day13 │ ├── day13a │ └── day13b └── day08 │ └── day08a ├── 2017 ├── day04 │ ├── day04a │ ├── day04a.pl │ ├── day04b.pl │ ├── day04a.go │ └── day04b.go ├── day07 │ ├── day07a │ └── day07a.go ├── day02 │ ├── day02a.pl │ ├── day02b.pl │ ├── day02a.go │ └── day02b.go ├── day09 │ ├── day09b.pl │ ├── day09a.pl │ ├── day09a.go │ └── day09b.go ├── day17 │ ├── day17a.pl │ └── day17b.pl ├── day05 │ ├── day05a.pl │ ├── day05b.pl │ ├── day05a.go │ └── day05b.go ├── day01 │ ├── day01a.pl │ ├── day01b.pl │ ├── day01a.go │ └── day01b.go ├── day08 │ ├── day08a.pl │ └── day08b.pl ├── day15 │ ├── day15a.pl │ └── day15b.pl ├── day23 │ └── day23b.pl ├── day12 │ ├── day12a.pl │ └── day12b.pl ├── day03 │ └── day03a.pl ├── day11 │ ├── day11a.pl │ └── day11b.pl ├── day06 │ ├── day06a.pl │ └── day06b.pl ├── day13 │ ├── day13a.pl │ ├── day13a.go │ ├── day13b.go │ └── day13b.pl ├── day16 │ └── day16a.pl ├── day22 │ └── day22a.pl ├── day24 │ └── day24a.pl ├── day20 │ └── day20a.pl └── day10 │ ├── day10a.pl │ └── day10a.go ├── 2018 ├── day01 │ ├── day01a │ ├── day01a.pl │ └── day01b.pl ├── day03 │ ├── day03a.pl │ └── day03b.pl ├── day14 │ ├── day14a.pl │ └── day14b.pl ├── day02 │ ├── day02a.pl │ └── day02b.pl ├── day23 │ └── day23a.pl ├── day07 │ └── day07a.pl ├── day08 │ ├── day08a.pl │ └── day08b.pl ├── day05 │ ├── day05a.pl │ └── day05b.pl ├── day11 │ └── day11a.pl ├── day10 │ └── day10b.pl ├── day09 │ ├── day09a.pl │ └── day09b.pl ├── day06 │ └── day06b.pl └── day04 │ └── day04b.pl ├── 2019 ├── runner.bats ├── day01 │ ├── day01a.pl │ ├── day01b.pl │ ├── first │ │ └── day01a.go │ └── second │ │ └── day01b.go ├── day04 │ ├── day04a.pl │ └── day04b.pl ├── day02 │ ├── day02a.pl │ ├── first │ │ └── day02a.go │ └── day02b.pl ├── day06 │ ├── first │ │ └── day06a.go │ ├── day06a.pl │ └── day06b.pl ├── day08 │ ├── day08b.pl │ └── day08a.pl ├── day05 │ ├── first │ │ └── day05a.go │ └── second │ │ └── day05b.go ├── day09 │ ├── first │ │ └── day09a.go │ └── second │ │ └── day09b.go └── day13 │ └── first │ └── day13a.go ├── 2020 ├── day18 │ ├── day18b │ └── day18a ├── day24 │ ├── day24a │ ├── day24b │ └── day24a.awk ├── day02 │ ├── day02a │ └── day02b ├── day10 │ ├── day10a │ └── day10b ├── day03 │ ├── day03a │ └── day03b ├── day06 │ ├── day06a │ └── day06b ├── day01 │ ├── day01a │ └── day01b ├── day04 │ └── day04a ├── day05 │ ├── day05a │ └── day05b ├── day07 │ ├── day07b │ └── day07a ├── day08 │ ├── day08a │ └── day08b ├── day15 │ ├── day15a │ └── day15b ├── day25 │ └── day25a ├── day13 │ ├── day13a │ └── day13b ├── day16 │ └── day16a ├── day09 │ ├── day09a │ └── day09b ├── day12 │ ├── day12a │ └── day12b └── day19 │ └── day19a ├── 2021 ├── day08 │ ├── day08a │ └── day08a.js ├── day02 │ ├── awk │ │ ├── day02a │ │ └── day02b │ ├── day02a.js │ └── day02b.js ├── day01 │ ├── awk │ │ ├── day01a │ │ └── day01b │ ├── day01a.js │ └── day01b.js ├── .eslintrc.json ├── day17 │ ├── day17a.js │ └── day17b.js ├── day03 │ ├── awk │ │ ├── day03a │ │ └── day03b │ ├── day03a.js │ └── day03b.js ├── day06 │ ├── day06a.js │ └── day06b.js ├── day07 │ ├── day07a.js │ └── day07b.js ├── package.json ├── day10 │ ├── day10a.js │ └── day10b.js ├── day09 │ └── day09a.js ├── day13 │ └── day13a.js ├── day12 │ └── day12a.js ├── day05 │ ├── day05b.js │ └── day05a.js └── day14 │ └── day14a.js ├── 2022 ├── day01 │ ├── day01a │ └── day01b ├── day02 │ ├── day02a │ └── day02b ├── day03 │ ├── day03a │ └── day03b ├── day04 │ ├── day04a.pl │ └── day04b.pl ├── day06 │ ├── day06a.pl │ └── day06b.pl ├── day10 │ ├── day10a.pl │ └── day10b.pl └── day05 │ ├── day05b.pl │ └── day05a.pl ├── 2023 ├── day01 │ ├── day01a │ └── day01b ├── day15 │ ├── day15a │ └── day15b ├── day06 │ ├── day06b │ └── day06a ├── day04 │ ├── day04a │ └── day04b ├── day08 │ ├── day08a │ └── day08b ├── day02 │ ├── day02b │ └── day02a ├── day09 │ ├── day09a │ └── day09b ├── day14 │ └── day14a ├── day11 │ ├── day11a │ └── day11b ├── day12 │ └── day12a ├── day05 │ └── day05a └── day13 │ └── day13a ├── 2024 ├── day01 │ ├── day01b │ └── day01a ├── day03 │ ├── day03a │ └── day03b ├── day11 │ ├── day11a │ └── day11b ├── day05 │ ├── day05a │ └── day05b ├── day02 │ ├── day02a │ └── day02b ├── day06 │ └── day06a ├── day04 │ ├── day04b │ └── day04a ├── day07 │ ├── day07a │ └── day07b ├── map.rb ├── day14 │ ├── day14b │ └── day14a ├── day08 │ ├── day08a │ └── day08b ├── day13 │ ├── day13b │ └── day13a ├── day09 │ ├── day09a │ └── day09b └── day10 │ └── day10b ├── .gitignore ├── go.mod ├── .mailmap ├── go ├── log │ └── log.go ├── grid │ ├── vec2_test.go │ └── vec3.go ├── ioutil │ └── ioutil.go └── convert │ └── convert.go └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | input 3 | output 4 | node_modules 5 | testinput* 6 | -------------------------------------------------------------------------------- /2016/day21/day21b: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | awk -f day21b.awk <(tac input) 4 | -------------------------------------------------------------------------------- /2017/day04/day04a: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | grep -Pvc '(\b\w+\b).*\1' "$1" 4 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bewuethr/advent-of-code 2 | 3 | go 1.15 4 | -------------------------------------------------------------------------------- /2017/day07/day07a: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | grep -oE '[a-z]+' "$1" | sort | uniq -u 4 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | <8521043+bewuethr@users.noreply.github.com> 2 | -------------------------------------------------------------------------------- /2018/day01/day01a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | { printf 0; paste -s "$1"; } | bc 4 | -------------------------------------------------------------------------------- /2016/day20/day20a: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | awk -f day20a.awk <(sort -t '-' -k 1,1 -n "$1") 4 | -------------------------------------------------------------------------------- /2020/day18/day18b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | sed 's/[()]/&&/g;s/^/(/;s/$/)/;s/\*/)*(/g' "$1" | paste -sd+ | bc 4 | -------------------------------------------------------------------------------- /2020/day24/day24a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | sed -E 's/e|se|sw|w|nw|ne/ &/g' "$1" \ 4 | | awk -f day24a.awk 5 | -------------------------------------------------------------------------------- /2020/day24/day24b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | sed -E 's/e|se|sw|w|nw|ne/ &/g' "$1" \ 4 | | awk -f day24b.awk 5 | -------------------------------------------------------------------------------- /2021/day08/day08a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | awk -F'|' '{print $2}' "$1" \ 4 | | grep -Eow '\w{2}|\w{3}|\w{4}|\w{7}' \ 5 | | wc -l 6 | -------------------------------------------------------------------------------- /2016/bonus/bonus: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./assembunny < input | while read n; do 4 | printf "\x$(printf '%x' "$n")"; 5 | done | ./display 6 | -------------------------------------------------------------------------------- /2016/day20/day20a.awk: -------------------------------------------------------------------------------- 1 | BEGIN { 2 | FS = "-" 3 | min = 0 4 | } 5 | 6 | $1 <= min && $2 >= min { min = $2 + 1 } 7 | 8 | END { print min } 9 | -------------------------------------------------------------------------------- /2021/day02/awk/day02a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | /forward/ { x += $2 } 4 | /down/ { d += $2 } 5 | /up/ { d -= $2 } 6 | 7 | END { print x * d } 8 | -------------------------------------------------------------------------------- /2016/day07/day07b_bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | grep -cP '(?:^|\])[^[]*(.)(?!\1)(.)\1[^[]*(?=\[).*\[[^]]*\2\1\2[^]]*\]|\[[^]]*(.)(?!\3)(.)\3[^]]*\].*(?<=\])[^[]*\4\3\4[^[]*(?:\[|$)' "$1" 4 | -------------------------------------------------------------------------------- /2021/day01/awk/day01a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | NR == 1 { depth = $1 } 4 | 5 | NR > 1 { 6 | if ($1 > depth) ++count 7 | depth = $1 8 | } 9 | 10 | END { print count } 11 | -------------------------------------------------------------------------------- /2015/day08/day08b: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | before=$(cat input | wc -c) 4 | after=$(sed -e 's/\(["\]\)/\\\1/g' -e 's/\(.*\)/"\1"/' input | wc -c) 5 | echo "$after - $before = $(( after - before ))" 6 | -------------------------------------------------------------------------------- /2020/day02/day02a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | BEGIN { FS = "[ :-]+" } 4 | 5 | { 6 | n = gsub($3, $3, $4) 7 | if (n >= $1 && n <= $2) 8 | ++count 9 | } 10 | 11 | END { print count } 12 | -------------------------------------------------------------------------------- /2020/day10/day10a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | sort -n "$1" \ 4 | | awk ' 5 | { 6 | ++jolts[$1-prev] 7 | prev = $1 8 | } 9 | 10 | END { print jolts[1] * (jolts[3]+1) } 11 | ' 12 | -------------------------------------------------------------------------------- /2023/day01/day01a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | file = File.open(ARGV[0]) 4 | 5 | s = file.readlines.sum do |line| 6 | line.scan(/\d/).values_at(0, -1).join.to_i 7 | end 8 | 9 | puts s 10 | -------------------------------------------------------------------------------- /2020/day02/day02b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | BEGIN { FS = "[ :-]+" } 4 | 5 | { 6 | if ((substr($4, $1, 1) == $3) != (substr($4, $2, 1) == $3)) 7 | ++count 8 | } 9 | 10 | END { print count } 11 | -------------------------------------------------------------------------------- /2021/day02/awk/day02b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | /forward/ { 4 | pos += $2 5 | d += aim * $2 6 | } 7 | 8 | /down/ { aim += $2 } 9 | 10 | /up/ { aim -= $2 } 11 | 12 | END { print pos * d } 13 | -------------------------------------------------------------------------------- /2021/day01/awk/day01b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | { depth[NR] = $1 } 4 | 5 | END { 6 | for (i = 1; i <= NR-3; ++i) { 7 | if (depth[i] < depth[i+3]) ++count 8 | } 9 | 10 | print count 11 | } 12 | -------------------------------------------------------------------------------- /2015/day05/day05b: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Bash regex in its conditions don't support backreferences, so I cheated 4 | 5 | n_strings=$(grep -E "(..).*\1" input | grep -cE "(.).\1") 6 | echo "Number of good strings: $n_strings" 7 | -------------------------------------------------------------------------------- /2022/day01/day01a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | BEGIN { RS = "" } 4 | 5 | { 6 | sum = 0 7 | for (i = 1; i <= NF; ++i) { 8 | sum += $i 9 | } 10 | 11 | max = sum > max ? sum : max 12 | } 13 | 14 | END { print max } 15 | -------------------------------------------------------------------------------- /2024/day01/day01b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | file = File.open(ARGV[0]) 4 | 5 | l1, l2 = file.readlines(chomp: true) 6 | .map { _1.split.map(&:to_i) } 7 | .transpose 8 | 9 | puts l1.map { _1 * l2.count(_1) }.sum 10 | -------------------------------------------------------------------------------- /2024/day03/day03a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | memory = File.read(ARGV[0]).strip 4 | 5 | res = memory.scan(/mul\(\d{1,3},\d{1,3}\)/).sum do |expr| 6 | expr.scan(/\d+/).map(&:to_i).reduce(:*) 7 | end 8 | 9 | puts res 10 | -------------------------------------------------------------------------------- /2015/day10/day10b: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | num=$(cat input) 4 | 5 | for i in {1..50}; do 6 | echo "Iteration $i" 7 | num=$(fold -1 <<< $num | uniq -c | tr -d $'\n ') 8 | done 9 | 10 | echo "Length of final: ${#num}" 11 | -------------------------------------------------------------------------------- /2019/runner.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | @test "Run all days" { 4 | for d in day??/{first,second}; do 5 | cd "$d" 6 | run go run day???.go 7 | echo "$d" 8 | cmp <(echo "$output") output 9 | cd "$OLDPWD" 10 | done 11 | } 12 | -------------------------------------------------------------------------------- /2024/day01/day01a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | file = File.open(ARGV[0]) 4 | 5 | puts file.readlines(chomp: true) 6 | .map { _1.split.map(&:to_i) } 7 | .transpose 8 | .map(&:sort) 9 | .transpose 10 | .map { (_1[0] - _1[1]).abs } 11 | .sum 12 | -------------------------------------------------------------------------------- /2016/day07/day07a_bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | grep -vP '\[[^]]*(.)(?!\1)(.)\2\1[^]]*\]' "$1" | sed 's/\[[^]]*\]/ /g' | grep -P '(.)(?!\1)(.)\2\1' | wc -l 4 | grep -vP '\[[^]]*(.)(?!\1)(.)\2\1[^]]*\]' "$1" | grep -P '(?:^|\])[^[]*(.)(?!\1)(.)\2\1[^[]*(?:\[|$)' | wc -l 5 | -------------------------------------------------------------------------------- /2020/day03/day03a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | BEGIN { 4 | x = 0 5 | } 6 | 7 | NR == 1 { xmax = length() } 8 | 9 | { 10 | xpos = x % xmax + 1 11 | if (substr($0, xpos, 1) == "#") { 12 | ++trees 13 | } 14 | x += 3 15 | } 16 | 17 | END { print trees } 18 | -------------------------------------------------------------------------------- /2015/day12/day12a: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sum=0 4 | re='(-?[[:digit:]]+)' 5 | 6 | while read -r line; do 7 | if [[ "$line" =~ $re ]]; then 8 | (( sum += ${BASH_REMATCH[1]} )) 9 | fi 10 | done < <(json_pp < input) 11 | 12 | echo "Sum of all numbers: $sum" 13 | -------------------------------------------------------------------------------- /2020/day06/day06a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | BEGIN { RS = "" } 4 | 5 | { 6 | delete counts 7 | gsub(/[^[:lower:]]/, "") 8 | split($0, arr, "") 9 | for (i in arr) { 10 | ++counts[arr[i]] 11 | } 12 | 13 | total += length(counts) 14 | } 15 | 16 | END { print total } 17 | -------------------------------------------------------------------------------- /2022/day01/day01b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | BEGIN { RS = "" } 4 | 5 | { 6 | sum = 0 7 | for (i = 1; i <= NF; ++i) { 8 | sum += $i 9 | } 10 | 11 | sums[NR] = sum 12 | } 13 | 14 | END { 15 | asort(sums, sums, "@val_num_desc") 16 | print sums[1] + sums[2] + sums[3] 17 | } 18 | -------------------------------------------------------------------------------- /go/log/log.go: -------------------------------------------------------------------------------- 1 | // Package log provides logging functionality. 2 | package log 3 | 4 | import ( 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | // Die prints msg and err, then exits. 10 | func Die(msg string, err error) { 11 | fmt.Fprintf(os.Stderr, "%s: %v\n", msg, err) 12 | os.Exit(1) 13 | } 14 | -------------------------------------------------------------------------------- /2016/day03/day03a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | { 4 | sum = $1 5 | max = $1 6 | for (i = 2; i <= NF; ++i) { 7 | if ($i > max) 8 | max = $i 9 | sum += $i 10 | } 11 | if (max < sum/2) 12 | ++valid 13 | } 14 | 15 | END { print valid } 16 | -------------------------------------------------------------------------------- /2021/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true 5 | }, 6 | "extends": "eslint:recommended", 7 | "parserOptions": { 8 | "ecmaVersion": 13, 9 | "sourceType": "module" 10 | }, 11 | "rules": { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /2023/day15/day15a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | def hash(str) 4 | str.chars.reduce(0) do |total, c| 5 | total += c.ord 6 | total *= 17 7 | total % 256 8 | end 9 | end 10 | 11 | file = File.open(ARGV[0]) 12 | 13 | puts file.gets.chomp.split(",").sum { |step| hash(step) } 14 | -------------------------------------------------------------------------------- /2020/day01/day01a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | { 4 | entries[NR] = $1 5 | } 6 | 7 | END { 8 | for (i = 1; i <= NR-1; ++i) { 9 | for (j = i+1; j <= NR; ++j) { 10 | if (entries[i] + entries[j] == 2020) { 11 | print entries[i] * entries[j] 12 | exit 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /2018/day01/day01a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | my $sum; 14 | while (my $line = <$fh>) { 15 | $sum += $line; 16 | } 17 | say $sum; 18 | -------------------------------------------------------------------------------- /2023/day06/day06b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | file = File.open(ARGV[0]) 4 | 5 | lines = file.readlines 6 | 7 | time = lines[0].scan(/\d+/).join.to_i 8 | distance = lines[1].scan(/\d+/).join.to_i 9 | 10 | puts (1...time).map { |t_charge| (time - t_charge) * t_charge } 11 | .count { |d| d > distance } 12 | -------------------------------------------------------------------------------- /2015/day01/day01a: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | input=$(< "$1") 4 | floor=0 5 | 6 | for (( i = 0; i < ${#input}; ++i )); do 7 | char=${input:i:1} 8 | if [[ $char == '(' ]]; then 9 | (( ++floor )) 10 | else 11 | (( --floor )) 12 | fi 13 | done 14 | 15 | echo "Santa should go to floor $floor." 16 | -------------------------------------------------------------------------------- /2015/day04/day04b: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | input=$(< "$1") 4 | i=282749 5 | 6 | while true; do 7 | md5=$(printf '%s' "$input$i" | md5sum) 8 | if [[ $md5 == 000000* ]]; then 9 | echo "$input with an attached $i has a hash starting with 000000" 10 | break 11 | fi 12 | (( ++i )) 13 | done 14 | -------------------------------------------------------------------------------- /2021/day17/day17a.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import process from "process"; 4 | import * as fs from "fs"; 5 | 6 | let yMin = Number(fs.readFileSync(process.argv[2], "utf8") 7 | .trim() 8 | .match(/-?\d+/g)[2]); 9 | 10 | let dyInit = Math.abs(yMin) - 1; 11 | 12 | console.log((dyInit * (dyInit + 1)) / 2); 13 | -------------------------------------------------------------------------------- /2015/day08/day08a: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | before=$(cat input | wc -c) 4 | 5 | after=$(sed -e 's/\\\\/S/g' -e 's/\\"/Q/g' -e 's/\\x[[:xdigit:]]\{2\}/X/g' input | wc -c) 6 | 7 | echo "Chars before: $before" 8 | echo "Chars after: $after" 9 | echo "Difference: $(( before - after + 2 * $(cat day08_input | wc -l) ))" 10 | 11 | 12 | -------------------------------------------------------------------------------- /2015/day04/day04a: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | input=$(< "$1") 4 | i=1 5 | 6 | while true; do 7 | md5=$(printf '%s' "$input$i" | md5sum) 8 | if [[ $md5 == 00000* ]]; then 9 | echo "$input with an attached $i has a hash starting with 00000" 10 | break # Happens at 282749 11 | fi 12 | (( ++i )) 13 | done 14 | -------------------------------------------------------------------------------- /2021/day01/day01a.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import process from "process"; 4 | import * as fs from "fs"; 5 | 6 | console.log(fs.readFileSync(process.argv[2], "utf8") 7 | .trim() 8 | .split("\n") 9 | .map(Number) 10 | .map((el, idx, arr) => arr[idx-1] && el > arr[idx-1] ? 1 : 0) 11 | .reduce((prev, curr) => prev + curr)); 12 | -------------------------------------------------------------------------------- /2021/day01/day01b.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import process from "process"; 4 | import * as fs from "fs"; 5 | 6 | console.log(fs.readFileSync(process.argv[2], "utf8") 7 | .trim() 8 | .split("\n") 9 | .map(Number) 10 | .map((el, idx, arr) => arr[idx-3] && el > arr[idx-3] ? 1 : 0) 11 | .reduce((prev, curr) => prev + curr)); 12 | -------------------------------------------------------------------------------- /2023/day04/day04a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | file = File.open(ARGV[0]) 4 | 5 | s = file.readlines.sum do |line| 6 | winning, have = line.split(/[:|]/)[1..].map { |list| list.scan(/\d+/).map(&:to_i) } 7 | matches = winning.count { |n| have.include?(n) } 8 | matches.positive? ? 2**(matches - 1) : 0 9 | end 10 | 11 | puts s 12 | -------------------------------------------------------------------------------- /2017/day02/day02a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | use List::Util qw(max min sum); 9 | 10 | my $fname = shift; 11 | 12 | open my $fh, "<", $fname 13 | or die "Can't open $fname: $!"; 14 | 15 | say sum map { chomp; my @arr = split "\t"; (max @arr) - (min @arr) } <$fh>; 16 | -------------------------------------------------------------------------------- /2019/day01/day01a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | my $sum; 14 | while (my $num = <$fh>) { 15 | chomp $num; 16 | $sum += int($num / 3) - 2; 17 | } 18 | 19 | say $sum; 20 | -------------------------------------------------------------------------------- /2020/day06/day06b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | BEGIN { RS = "" } 4 | 5 | { 6 | delete counts 7 | for (i = 1; i <= NF; ++i) { 8 | split($i, arr, "") 9 | for (j in arr) { 10 | ++counts[arr[j]] 11 | } 12 | } 13 | 14 | for (i in counts) { 15 | if (counts[i] == NF) { 16 | ++total 17 | } 18 | } 19 | } 20 | 21 | END { print total } 22 | -------------------------------------------------------------------------------- /2017/day04/day04a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | my $ctr = 0; 14 | 15 | while (my $line = <$fh>) { 16 | chomp $line; 17 | $ctr++ if not $line =~ /(\b\w+\b).*\1/; 18 | } 19 | 20 | say $ctr; 21 | -------------------------------------------------------------------------------- /2020/day01/day01b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | { 4 | entries[NR] = $1 5 | } 6 | 7 | END { 8 | for (i = 1; i <= NR-2; ++i) { 9 | for (j = i+1; j <= NR-1; ++j) { 10 | for (k = j+1; k <= NR; ++k) { 11 | if (entries[i] + entries[j] + entries[k] == 2020) { 12 | print entries[i] * entries[j] * entries[k] 13 | exit 14 | } 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /2015/day11/day11b.pl: -------------------------------------------------------------------------------- 1 | #!/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use 5.022; 6 | 7 | my $passwd = 'vzbxxyzz'; 8 | 9 | while (1) { 10 | ++$passwd; 11 | next if $passwd =~ m/i|o|l/; 12 | last if $passwd =~ m/abc|bcd|cde|def|efg|fgh|ghi|hij|ijk|jkl|klm|lmn|mno|nop|opq|pqr|qrs|rst|stu|tuv|uvw|vwx|wxy|xyz/ and $passwd =~ m/(.)\1.*(?!\1)(.)\2/; 13 | } 14 | say "$passwd"; 15 | -------------------------------------------------------------------------------- /2020/day04/day04a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | BEGIN { RS = "" } 4 | 5 | # Passports with 8 fields are always valid 6 | NF == 8 { ++valid } 7 | 8 | # If the cid field is present here, a mandatory field must be missing 9 | NF == 7 { 10 | for (i = 1; i <= NF; ++i) { 11 | if (substr($i, 1, 3) == "cid") { 12 | next 13 | } 14 | } 15 | 16 | ++valid 17 | } 18 | 19 | END { print valid } 20 | -------------------------------------------------------------------------------- /2023/day06/day06a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | file = File.open(ARGV[0]) 4 | 5 | lines = file.readlines 6 | 7 | times = lines[0].scan(/\d+/).map(&:to_i) 8 | distances = lines[1].scan(/\d+/).map(&:to_i) 9 | 10 | n_ways = times.map.with_index do |t, idx| 11 | (1...t).map { |t_charge| (t - t_charge) * t_charge } 12 | .count { |d| d > distances[idx] } 13 | end 14 | 15 | puts n_ways.reduce(:*) 16 | -------------------------------------------------------------------------------- /2022/day02/day02a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | $2 == "X" { 4 | score += 1 5 | if ($1 == "A") score += 3 6 | if ($1 == "C") score += 6 7 | } 8 | 9 | $2 == "Y" { 10 | score += 2 11 | if ($1 == "B") score += 3 12 | if ($1 == "A") score += 6 13 | } 14 | 15 | $2 == "Z" { 16 | score += 3 17 | if ($1 == "C") score += 3 18 | if ($1 == "B") score += 6 19 | } 20 | 21 | END { print score } 22 | 23 | -------------------------------------------------------------------------------- /2024/day11/day11a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | stones = File.read(ARGV[0]).split 4 | 5 | 25.times do 6 | stones_new = stones.flat_map do |n| 7 | if n == "0" 8 | "1" 9 | elsif n.length.even? 10 | [n[...n.length / 2], n[n.length / 2..].to_i.to_s] 11 | else 12 | (n.to_i * 2024).to_s 13 | end 14 | end 15 | 16 | stones = stones_new 17 | end 18 | 19 | puts stones.length 20 | -------------------------------------------------------------------------------- /2015/day11/day11b: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | passwd=vzbxxyzz # From first part 4 | 5 | while true; do 6 | passwd=$(perl -e '$w = shift; print substr ++$w, -8' $passwd) 7 | grep -Ev 'i|o|l' <<< "$passwd" | 8 | grep -E 'abc|bcd|cde|def|efg|fgh|ghi|hij|ijk|jkl|klm|lmn|mno|nop|opq|pqr|qrs|rst|stu|tuv|uvw|vwx|wxy|xyz' | 9 | grep -P '(.)\1.*(?!\1)(.)\2' && { echo "Next password: $passwd"; exit; } 10 | done 11 | -------------------------------------------------------------------------------- /2019/day01/day01b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | my $sum; 14 | while (my $num = <$fh>) { 15 | chomp $num; 16 | while (1) { 17 | $num = int($num / 3) - 2; 18 | last if ($num <= 0); 19 | $sum += $num; 20 | } 21 | } 22 | 23 | say $sum; 24 | -------------------------------------------------------------------------------- /2021/day08/day08a.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import process from "process"; 4 | import * as fs from "fs"; 5 | 6 | let input = fs.readFileSync(process.argv[2], "utf8") 7 | .trim() 8 | .split("\n") 9 | .map(e => e.split(" | ")[1].split(" ")) 10 | .flat() 11 | .filter(e => 12 | e.length == 2 || 13 | e.length == 3 || 14 | e.length == 4 || 15 | e.length == 7); 16 | 17 | console.log(input.length); 18 | -------------------------------------------------------------------------------- /2015/day03/day03a: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | input=$(< "$1") 4 | 5 | x=0 6 | y=0 7 | declare -A visited=([$x/$y]=1) 8 | 9 | for (( i = 0; i < ${#input}; ++i )); do 10 | case ${input:$i:1} in 11 | '>') (( ++x )) ;; 12 | '^') (( ++y )) ;; 13 | '<') (( --x )) ;; 14 | 'v') (( --y )) ;; 15 | esac 16 | visited[$x/$y]=1 17 | done 18 | 19 | echo "Santa has visited ${#visited[@]} houses." 20 | -------------------------------------------------------------------------------- /2020/day05/day05a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | function bin2dec(n, l, i, d, fac) { 4 | l = length(n) 5 | fac = 1 6 | for (i = l; i >= 1; --i) { 7 | d += substr(n, i, 1) * fac 8 | fac *= 2 9 | } 10 | 11 | return d 12 | } 13 | 14 | { 15 | gsub(/F/, 0) 16 | gsub(/B/, 1) 17 | gsub(/L/, 0) 18 | gsub(/R/, 1) 19 | 20 | id = bin2dec($0) 21 | maxid = id > maxid ? id : maxid 22 | } 23 | 24 | END { print maxid } 25 | -------------------------------------------------------------------------------- /2021/day03/awk/day03a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | { 4 | for (i = 1; i <= length($1); ++i) { 5 | counts[i] += substr($1, i, 1) == 1 6 | } 7 | } 8 | 9 | END { 10 | for (i = 1; i <= length($1); ++i) { 11 | gamchar = counts[i] > NR / 2 ? 1 : 0 12 | epschar = gamchar == 1 ? 0 : 1 13 | gamma = gamma gamchar 14 | epsilon = epsilon epschar 15 | } 16 | 17 | printf "ibase = 2; %s * %s\n", gamma, epsilon | "bc" 18 | } 19 | -------------------------------------------------------------------------------- /2018/day01/day01b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | 9 | my $fname = shift; 10 | 11 | my $sum; 12 | my %seen; 13 | while (1) { 14 | open my $fh, "<", $fname 15 | or die "Can't open $fname: $!"; 16 | while (my $line = <$fh>) { 17 | $sum += $line; 18 | $seen{$sum}++; 19 | if ($seen{$sum} == 2) { 20 | say $sum; 21 | exit; 22 | } 23 | } 24 | close $fh; 25 | } 26 | -------------------------------------------------------------------------------- /2015/day04/day04b_par: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | input=$(< "$1") 4 | i=282749 5 | 6 | for subshell in {0..3}; do ( 7 | (( i += subshell )) 8 | while true; do 9 | md5=$(printf '%s' "$input$i" | md5sum) 10 | if [[ $md5 == 000000* ]]; then 11 | echo "$input with an attached $i has a hash starting with 000000" 12 | break 13 | fi 14 | (( i += 4 )) 15 | done ) & 16 | done 17 | wait 18 | -------------------------------------------------------------------------------- /2016/day05/day05a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | @load "general" 4 | 5 | BEGIN { passwd_len = 8 } 6 | 7 | { 8 | id = $0 9 | idx = 0 10 | 11 | while (1) { 12 | hash = md5(id idx) 13 | 14 | if (hash ~ /^0{5}/) { 15 | passwd = passwd substr(hash, 6, 1) 16 | print passwd 17 | if (length(passwd) == passwd_len) 18 | break 19 | } 20 | ++idx 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /2017/day09/day09b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | my $res = 0; 14 | 15 | my $line = <$fh>; 16 | chomp $line; 17 | 18 | # Clean up escapes 19 | $line =~ s/!.//g; 20 | 21 | # Count garbage 22 | while ($line =~ s/<(.*?)>//) { 23 | $res += length $1; 24 | } 25 | 26 | say $res; 27 | -------------------------------------------------------------------------------- /2016/day06/day06a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | { 4 | n = split($0, arr, "") 5 | for (i = 1; i <= n; ++i) 6 | ++freq[i][arr[i]] 7 | } 8 | 9 | END { 10 | for (i = 1; i <= n; ++i) { 11 | max = 0 12 | for (j in freq[i]) { 13 | if (freq[i][j] > max) { 14 | max = freq[i][j] 15 | letter = j 16 | } 17 | } 18 | msg = msg letter 19 | } 20 | print msg 21 | } 22 | -------------------------------------------------------------------------------- /2016/day06/day06b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | { 4 | n = split($0, arr, "") 5 | for (i = 1; i <= n; ++i) 6 | ++freq[i][arr[i]] 7 | } 8 | 9 | END { 10 | for (i = 1; i <= n; ++i) { 11 | min = 573 12 | for (j in freq[i]) { 13 | if (freq[i][j] < min) { 14 | min = freq[i][j] 15 | letter = j 16 | } 17 | } 18 | msg = msg letter 19 | } 20 | print msg 21 | } 22 | -------------------------------------------------------------------------------- /2017/day17/day17a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | chomp(my $steps = <$fh>); 14 | 15 | my @buffer = (0); 16 | my $pos = 0; 17 | 18 | for my $num (1..2017) { 19 | $pos = ($pos + $steps) % @buffer; 20 | splice @buffer, $pos+1, 0, $num; 21 | $pos++; 22 | } 23 | 24 | say $buffer[$pos+1]; 25 | -------------------------------------------------------------------------------- /2023/day04/day04b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | copies = Hash.new(0) 4 | 5 | file = File.open(ARGV[0]) 6 | 7 | count = 0 8 | 9 | file.readlines.each_with_index do |line, idx| 10 | winning, have = line.split(/[:|]/)[1..].map { |list| list.scan(/\d+/).map(&:to_i) } 11 | matches = winning.count { |n| have.include?(n) } 12 | (idx + 1..idx + matches).each { |n| copies[n] += 1 + copies[idx] } 13 | count += 1 14 | end 15 | 16 | puts copies.values.sum + count 17 | -------------------------------------------------------------------------------- /2015/day05/day05a: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | re1='[aeiou].*[aeiou].*[aeiou]' 4 | re2=$(for i in {a..z}; do printf "$i$i|"; done) 5 | re2="${re2:0:-1}" # Remove last pipe 6 | re3='ab|cd|pq|xy' 7 | 8 | nice_strings=0 9 | 10 | while read line; do 11 | if [[ "$line" =~ $re1 && "$line" =~ $re2 && ! "$line" =~ $re3 ]]; then 12 | echo "Match: $line" 13 | (( ++nice_strings )) 14 | fi 15 | done < input 16 | 17 | echo "Number of nice strings: $nice_strings." 18 | -------------------------------------------------------------------------------- /2015/day08/day08a.pl: -------------------------------------------------------------------------------- 1 | #!/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use 5.022; 6 | 7 | my $ifname = 'input'; 8 | open my $fh, '<', $ifname or die "Can't open $ifname: $!"; 9 | 10 | my $total; 11 | 12 | while (<$fh>) { 13 | chomp; 14 | 15 | my $len = length $_; 16 | 17 | s/^"(.*)"$/$1/; 18 | s/\\\\/S/g; 19 | s/\\"/Q/g; 20 | s/\\x[[:xdigit:]]{2}/X/g; 21 | 22 | $total += $len - length $_; 23 | } 24 | 25 | say "Total diff: $total"; 26 | -------------------------------------------------------------------------------- /2017/day02/day02b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | use Algorithm::Combinatorics qw(variations); 9 | use List::Util qw(sum); 10 | 11 | my $fname = shift; 12 | 13 | open my $fh, "<", $fname 14 | or die "Can't open $fname: $!"; 15 | 16 | say sum map { 17 | chomp; 18 | my @arr = split "\t"; 19 | sum map { $_->[0] / $_->[1] } grep { $_->[0] % $_->[1] == 0 } variations(\@arr, 2); 20 | } <$fh>; 21 | -------------------------------------------------------------------------------- /2017/day05/day05a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | chomp(my @arr = <$fh>); 14 | my $pos = 0; 15 | my $res = 0; 16 | 17 | while (1) { 18 | my $jump = $arr[$pos]; 19 | $arr[$pos]++; 20 | $pos += $jump; 21 | $res++; 22 | last if ($pos > $#arr or $pos < 0); 23 | } 24 | 25 | say $res; 26 | -------------------------------------------------------------------------------- /2017/day04/day04b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | my $ctr = 0; 14 | 15 | while (my $line = <$fh>) { 16 | chomp $line; 17 | my @arr = map { my @chars = split //, $_; join '', sort @chars } split / /, $line; 18 | $ctr++ if not ( (join ' ', @arr) =~ /(\b\w+\b).*\1/ ); 19 | } 20 | 21 | say $ctr; 22 | -------------------------------------------------------------------------------- /2017/day17/day17b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | chomp(my $steps = <$fh>); 14 | 15 | my $len = 1; 16 | my $pos = 0; 17 | my $res; 18 | 19 | for my $num (1..50_000_000) { 20 | $pos = ($pos + $steps) % $len; 21 | $res = $num if $pos == 0; 22 | $len++; 23 | $pos++; 24 | } 25 | 26 | say $res; 27 | -------------------------------------------------------------------------------- /2022/day03/day03a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | while IFS= read -r line; do 4 | len=${#line} 5 | comp1=${line:0:len/2} 6 | comp2=${line:len/2} 7 | comm -12 <(fold -w1 <<< "$comp1" | sort) <(fold -w1 <<< "$comp2" | sort) | uniq 8 | done < "$1" \ 9 | | while IFS= read -r char; do 10 | if [[ $char == [[:lower:]] ]]; then 11 | echo $(($(printf '%d' "'$char") - 96)) 12 | else 13 | echo $(($(printf '%d' "'$char") - 38)) 14 | fi 15 | done \ 16 | | paste -sd+ | bc 17 | -------------------------------------------------------------------------------- /2015/day20/day20b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use 5.022; 6 | use Math::Prime::Util qw(fordivisors); 7 | 8 | open my $fh, '<', 'input'; 9 | chomp(my $input = <$fh>); 10 | 11 | my @houses; 12 | 13 | foreach my $i (1 .. $input/11) { 14 | my $sum = 0; 15 | fordivisors { $sum += 11 * $_ unless $i/50 > $_ } $i; 16 | if ($sum > $input) { 17 | say "House $i gets more than $input presents!"; 18 | last; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /2017/day01/day01a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | my $line = <$fh>; 14 | chomp $line; 15 | my @nums = split //, $line; 16 | 17 | my $sum = 0; 18 | foreach my $idx (0..$#nums-1) { 19 | $sum += $nums[$idx] == $nums[$idx+1] ? $nums[$idx] : 0; 20 | } 21 | $sum += $nums[-1] == $nums[0] ? $nums[-1] : 0; 22 | 23 | say $sum; 24 | -------------------------------------------------------------------------------- /2023/day08/day08a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | file = File.open(ARGV[0]) 4 | 5 | lr, _, *instr = file.readlines 6 | 7 | nodes = {} 8 | 9 | instr.map(&:chomp).each do |line| 10 | from, left, right = line.scan(/[A-Z]+/) 11 | nodes[from] = { 12 | "L" => left, 13 | "R" => right 14 | } 15 | end 16 | 17 | loc = "AAA" 18 | count = 0 19 | 20 | lr.chomp.chars.cycle do |c| 21 | loc = nodes[loc][c] 22 | count += 1 23 | break if loc.eql?("ZZZ") 24 | end 25 | 26 | puts count 27 | -------------------------------------------------------------------------------- /2020/day07/day07b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | contents() { 4 | grep "^$1" "$infile" \ 5 | | grep -Po '\d \w+ \w+' \ 6 | | awk -v mult="$2" '{$1 *= mult}1' 7 | } 8 | 9 | infile=$1 10 | 11 | readarray -t arr < <(contents 'shiny gold' 1) 12 | 13 | i=0 14 | while true; do 15 | read -r n color <<< "${arr[i]}" 16 | ((total += n)) 17 | readarray -t arr2 < <(contents "$color" "$n") 18 | arr+=("${arr2[@]}") 19 | ((++i)) 20 | ((i == ${#arr[@]})) && break 21 | done 22 | 23 | echo "$total" 24 | -------------------------------------------------------------------------------- /2020/day07/day07a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | iscontained() { 4 | grep -P "\d $1" "$infile" \ 5 | | cut -d ' ' -f -2 6 | } 7 | 8 | infile=$1 9 | 10 | readarray -t arr < <(iscontained 'shiny gold') 11 | 12 | count=${#arr[@]} 13 | 14 | while ((${#arr[@]} > 0)); do 15 | for i in "${!arr[@]}"; do 16 | echo "${arr[i]}" 17 | readarray -t arr2 < <(iscontained "${arr[i]}") 18 | ((count += ${#arr2[@]})) 19 | arr+=("${arr2[@]}") 20 | unset "arr[i]" 21 | done 22 | done | sort -u | wc -l 23 | -------------------------------------------------------------------------------- /2022/day04/day04a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | my $count = 0; 14 | 15 | while ( my $line = <$fh> ) { 16 | chomp $line; 17 | my ( $min1, $max1, $min2, $max2 ) = $line =~ /\d+/g; 18 | ++$count 19 | if $min1 <= $min2 and $max1 >= $max2 20 | or $min1 >= $min2 and $max1 <= $max2; 21 | } 22 | 23 | say $count; 24 | -------------------------------------------------------------------------------- /2023/day02/day02b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | file = File.open(ARGV[0]) 4 | 5 | prod_sum = file.readlines.map do |line| 6 | games = line.match(/Game \d+: (.*)/)[1] 7 | g = games.split("; ").map do |game| 8 | game.split(", ").reduce({}) do |total, val| 9 | n, col = val.split(" ") 10 | {col => n.to_i}.merge(total) 11 | end 12 | end 13 | 14 | %w[red green blue].map do |col| 15 | g.map { _1[col] }.compact.max 16 | end.reduce(:*) 17 | end.sum 18 | 19 | puts prod_sum 20 | -------------------------------------------------------------------------------- /2023/day01/day01b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | DIGITS = { 4 | "one" => 1, 5 | "two" => 2, 6 | "three" => 3, 7 | "four" => 4, 8 | "five" => 5, 9 | "six" => 6, 10 | "seven" => 7, 11 | "eight" => 8, 12 | "nine" => 9 13 | } 14 | 15 | RE = /(?=(#{DIGITS.keys.join("|")}|\d))/ 16 | 17 | file = File.open(ARGV[0]) 18 | 19 | s = file.readlines.sum do |line| 20 | line.scan(RE).flatten.values_at(0, -1).map do |val| 21 | DIGITS[val] || val 22 | end.join.to_i 23 | end 24 | 25 | puts s 26 | -------------------------------------------------------------------------------- /2016/day09/day09b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | function get_deco_len(str, arr, rs, rl, len) { 4 | if (match(str, /\(([0-9]+)x([0-9]+)\)/, arr)) { 5 | rs = RSTART 6 | rl = RLENGTH 7 | len = rs - 1 \ 8 | + arr[2] * get_deco_len(substr(str, rs + rl, arr[1])) \ 9 | + get_deco_len(substr(str, rs + rl + arr[1])) 10 | return len 11 | } 12 | else { 13 | return length(str) 14 | } 15 | } 16 | 17 | { 18 | print get_deco_len($0) 19 | } 20 | -------------------------------------------------------------------------------- /2016/day22/day22a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | BEGIN { FS = "[ T]*" } 4 | 5 | NR > 2 { 6 | node[NR]["size"] = $2 + 0 7 | node[NR]["used"] = $3 + 0 8 | node[NR]["avail"] = $4 + 0 9 | } 10 | 11 | END { 12 | for (i = 1; i <= NR; ++i) { 13 | for (j = 1; j <= NR; ++j) { 14 | if (i == j) 15 | continue 16 | if (node[i]["used"] > 0 && node[i]["used"] <= node[j]["avail"]) 17 | ++pairs 18 | } 19 | } 20 | print pairs 21 | } 22 | -------------------------------------------------------------------------------- /2020/day08/day08a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | { instr[NR] = $0 } 4 | 5 | END { 6 | idx = 1 7 | 8 | while (1) { 9 | if (seen[idx]++) { 10 | print accumulator 11 | exit 12 | } 13 | 14 | split(instr[idx], a) 15 | if (a[1] == "acc") { 16 | accumulator += a[2] 17 | ++idx 18 | } 19 | else if (a[1] == "jmp") { 20 | idx += a[2] 21 | } 22 | else if (a[1] == "nop") { 23 | ++idx 24 | } 25 | else { 26 | print "unknown instruction", a[1] 27 | exit 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /2020/day15/day15a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | BEGIN { FS = "," } 4 | 5 | { 6 | for (i = 1; i <= NF; ++i) { 7 | last_seen[$i] = i 8 | } 9 | 10 | n = $i 11 | } 12 | 13 | END { 14 | while (i <= 2020) { 15 | if (!(n in before_seen)) { 16 | nxt = 0 17 | } 18 | else { 19 | nxt = last_seen[n] - before_seen[n] 20 | } 21 | 22 | if (nxt in last_seen) { 23 | before_seen[nxt] = last_seen[nxt] 24 | } 25 | last_seen[nxt] = i 26 | 27 | n = nxt 28 | ++i 29 | } 30 | 31 | print n 32 | } 33 | -------------------------------------------------------------------------------- /2022/day03/day03b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | while IFS= read -r ruck1; do 4 | IFS= read -r ruck2 5 | IFS= read -r ruck3 6 | comm -12 <(fold -w1 <<< "$ruck1" | sort) <(fold -w1 <<< "$ruck2" | sort) \ 7 | | comm -12 <(fold -w1 <<< "$ruck3" | sort) - | uniq 8 | done < "$1" \ 9 | | while IFS= read -r char; do 10 | if [[ $char == [[:lower:]] ]]; then 11 | echo $(($(printf '%d' "'$char") - 96)) 12 | else 13 | echo $(($(printf '%d' "'$char") - 38)) 14 | fi 15 | done \ 16 | | paste -sd+ | bc 17 | -------------------------------------------------------------------------------- /2017/day01/day01b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | my $line = <$fh>; 14 | chomp $line; 15 | 16 | my @nums = split //, $line; 17 | my $len = scalar @nums; 18 | my $half = $len / 2; 19 | 20 | my $sum = 0; 21 | foreach my $idx (0..$#nums) { 22 | $sum += $nums[$idx] == $nums[ ($idx+$half) % $len ] ? $nums[$idx] : 0; 23 | } 24 | 25 | say $sum; 26 | -------------------------------------------------------------------------------- /2020/day15/day15b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | BEGIN { FS = "," } 4 | 5 | { 6 | for (i = 1; i <= NF; ++i) { 7 | last_seen[$i] = i 8 | } 9 | 10 | n = $i 11 | } 12 | 13 | END { 14 | while (i <= 30000000) { 15 | if (!(n in before_seen)) { 16 | nxt = 0 17 | } 18 | else { 19 | nxt = last_seen[n] - before_seen[n] 20 | } 21 | 22 | if (nxt in last_seen) { 23 | before_seen[nxt] = last_seen[nxt] 24 | } 25 | last_seen[nxt] = i 26 | 27 | n = nxt 28 | ++i 29 | } 30 | 31 | print n 32 | } 33 | -------------------------------------------------------------------------------- /2015/day01/day01b: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | input=$(< "$1") 4 | floor=0 5 | basement=0 6 | 7 | for (( i = 0; i < ${#input}; ++i )); do 8 | char=${input:i:1} 9 | if [[ $char == '(' ]]; then 10 | (( ++floor )) 11 | else 12 | (( --floor )) 13 | fi 14 | if (( basement == 0 && floor < 0)); then 15 | basement=$(( i + 1 )) 16 | fi 17 | 18 | done 19 | 20 | echo "Santa should go to floor $floor." 21 | echo "He first entered the basement after instruction number $basement." 22 | -------------------------------------------------------------------------------- /2020/day25/day25a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | function transform(val, subj) { 4 | return val * subj % 20201227 5 | } 6 | 7 | NR == 1 { key1 = $1 } 8 | NR == 2 { key2 = $1 } 9 | 10 | END { 11 | subj = 7 12 | loop_size = 0 13 | val = 1 14 | 15 | while (val != key1 && val != key2) { 16 | val = transform(val, subj) 17 | ++loop_size 18 | } 19 | 20 | subj = (val == key1) ? key2 : key1 21 | val = 1 22 | for (i = 1; i <= loop_size; ++i) { 23 | val = transform(val, subj) 24 | } 25 | 26 | print val 27 | } 28 | -------------------------------------------------------------------------------- /2024/day03/day03b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | def evaluate(expr) = expr.scan(/\d+/).map(&:to_i).reduce(:*) 4 | 5 | ENABLE = "do()" 6 | DISABLE = "don't()" 7 | 8 | memory = File.read(ARGV[0]).strip 9 | 10 | total = 0 11 | enabled = true 12 | 13 | memory.scan(/mul\(\d{1,3},\d{1,3}\)|do\(\)|don't\(\)/).each do |expr| 14 | case expr 15 | when ENABLE 16 | enabled = true 17 | when DISABLE 18 | enabled = false 19 | else 20 | total += evaluate(expr) if enabled 21 | end 22 | end 23 | 24 | puts total 25 | -------------------------------------------------------------------------------- /2024/day05/day05a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | lines = File.readlines(ARGV[0]).map(&:chomp) 4 | blank_idx = lines.index("") 5 | 6 | rules = lines[...blank_idx].map { _1.split("|").map(&:to_i) }.to_set 7 | pages = lines[blank_idx + 1..].map { _1.split(",").map(&:to_i) } 8 | 9 | total = pages.select do |list| 10 | list.each_with_index.all? do |page, idx| 11 | list[idx + 1..].all? { rules.include?([page, _1]) || !rules.include?([_1, page]) } 12 | end 13 | end.map { _1[_1.length / 2] }.sum 14 | 15 | puts total 16 | -------------------------------------------------------------------------------- /2020/day13/day13a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | BEGIN { FS = "," } 4 | 5 | NR == 1 { earliest = $1 } 6 | 7 | NR == 2 { 8 | for (i = 1; i <= NF; ++i) { 9 | if ($i == "x") { 10 | continue 11 | } 12 | 13 | t_wait = $i - (earliest % $i) 14 | wait[$i] = t_wait 15 | max = t_wait > max ? t_wait : max 16 | } 17 | 18 | min = max 19 | for (i in wait) { 20 | if (wait[i] < min) { 21 | min = wait[i] 22 | id = i 23 | } 24 | } 25 | 26 | printf "min: %d, ID: %d\n", min, id 27 | print min * id 28 | } 29 | 30 | -------------------------------------------------------------------------------- /2018/day03/day03a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | my %squares; 14 | 15 | while (my $line = <$fh>) { 16 | chomp $line; 17 | my (undef, $id, $dx, $dy, $w, $h) = split qr{[# @,:x]+}, $line; 18 | foreach my $y ($dy .. $dy+$h-1) { 19 | foreach my $x ($dx .. $dx+$w-1) { 20 | $squares{$x, $y}++; 21 | } 22 | } 23 | } 24 | 25 | say scalar grep { $_ > 1 } values %squares; 26 | -------------------------------------------------------------------------------- /2020/day05/day05b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | function bin2dec(n, l, i, d, fac) { 4 | l = length(n) 5 | fac = 1 6 | for (i = l; i >= 1; --i) { 7 | d += substr(n, i, 1) * fac 8 | fac *= 2 9 | } 10 | 11 | return d 12 | } 13 | 14 | { 15 | gsub(/F/, 0) 16 | gsub(/B/, 1) 17 | gsub(/L/, 0) 18 | gsub(/R/, 1) 19 | 20 | seats[NR] = bin2dec($0) 21 | } 22 | 23 | END { 24 | asort(seats) 25 | for (i = 1; i < length(seats); ++i) { 26 | if (seats[i+1] - seats[i] != 1) { 27 | print seats[i] + 1 28 | exit 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /2023/day09/day09a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | def predict(seq) 4 | seqs = [seq] 5 | until seqs.last.all?(0) 6 | next_seq = [] 7 | seqs.last.each_cons(2) { |el| next_seq << (el[1] - el[0]) } 8 | seqs << next_seq 9 | end 10 | 11 | seqs.reverse! 12 | 13 | seqs[1..].each_with_index do |s, idx| 14 | s << s.last + seqs[idx].last 15 | end 16 | 17 | seqs.last.last 18 | end 19 | 20 | file = File.open(ARGV[0]) 21 | 22 | s = file.readlines.sum do |line| 23 | predict(line.split.map(&:to_i)) 24 | end 25 | 26 | puts s 27 | -------------------------------------------------------------------------------- /2017/day01/day01a.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "os" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | func main() { 12 | input, err := ioutil.ReadFile(os.Args[1]) 13 | if err != nil { 14 | panic(err) 15 | } 16 | 17 | s := strings.TrimRight(string(input), "\n") 18 | 19 | var sum int 20 | 21 | for i, r := range s { 22 | if string(r) == string(s[(i+1)%len(s)]) { 23 | if num, err := strconv.Atoi(string(r)); err == nil { 24 | sum += num 25 | } 26 | } 27 | } 28 | 29 | fmt.Println(sum) 30 | } 31 | -------------------------------------------------------------------------------- /2022/day06/day06a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | use List::Util 'uniq'; 9 | 10 | sub unique { 11 | return (scalar uniq @_) == @_; 12 | } 13 | 14 | my $fname = shift; 15 | 16 | open my $fh, "<", $fname 17 | or die "Can't open $fname: $!"; 18 | 19 | my @signal = split //, <$fh>; 20 | my $count = 4; 21 | my @window = @signal[0..$count-1]; 22 | 23 | while (not unique(@window)) { 24 | shift @window; 25 | push @window, $signal[$count++]; 26 | } 27 | 28 | say "$count"; 29 | -------------------------------------------------------------------------------- /2022/day06/day06b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | use List::Util 'uniq'; 9 | 10 | sub unique { 11 | return (scalar uniq @_) == @_; 12 | } 13 | 14 | my $fname = shift; 15 | 16 | open my $fh, "<", $fname 17 | or die "Can't open $fname: $!"; 18 | 19 | my @signal = split //, <$fh>; 20 | my $count = 14; 21 | my @window = @signal[0..$count-1]; 22 | 23 | while (not unique(@window)) { 24 | shift @window; 25 | push @window, $signal[$count++]; 26 | } 27 | 28 | say "$count"; 29 | -------------------------------------------------------------------------------- /2016/day09/day09a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | { 4 | compr = $0 5 | gsub(/[[:blank:]]*/, "", compr) 6 | 7 | while (match(compr, /\(([0-9]+)x([0-9]+)\)/, arr)) { 8 | 9 | decompr = decompr substr(compr, 1, RSTART - 1) 10 | 11 | appstring = substr(compr, RSTART + RLENGTH, arr[1]) 12 | 13 | for (i = 1; i <= arr[2]; ++i) 14 | decompr = decompr appstring 15 | 16 | compr = substr(compr, RSTART + RLENGTH + arr[1]) 17 | } 18 | 19 | decompr = decompr compr 20 | print length(decompr) 21 | } 22 | -------------------------------------------------------------------------------- /2021/day06/day06a.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import process from "process"; 4 | import * as fs from "fs"; 5 | 6 | function update(state) { 7 | let zeroes = state.filter(e => e == 0).length; 8 | let newState = state.map(e => e == 0 ? 6 : e -1); 9 | 10 | return newState.concat(Array(zeroes).fill(8)); 11 | } 12 | 13 | let state = fs.readFileSync(process.argv[2], "utf8") 14 | .trim() 15 | .split(","); 16 | 17 | const days = 80; 18 | 19 | for (let i = 0; i < days; ++i) { 20 | state = update(state); 21 | } 22 | 23 | console.log(state.length); 24 | -------------------------------------------------------------------------------- /2024/day02/day02a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | def increasing(levels) = levels.eql?(levels.sort) 4 | 5 | def decreasing(levels) = levels.eql?(levels.sort.reverse) 6 | 7 | def max_diff(levels) 8 | levels.each_cons(2).to_a.all? do |a, b| 9 | diff = (a - b).abs 10 | (1..3).cover?(diff) 11 | end 12 | end 13 | 14 | file = File.open(ARGV[0]) 15 | 16 | safe = file.readlines(chomp: true).count do |report| 17 | levels = report.split.map(&:to_i) 18 | (increasing(levels) || decreasing(levels)) && max_diff(levels) 19 | end 20 | 21 | puts safe 22 | -------------------------------------------------------------------------------- /go/grid/vec2_test.go: -------------------------------------------------------------------------------- 1 | package grid 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestAzimuth(t *testing.T) { 8 | var vecs = []Vec2{ 9 | NewVec2(1, 0), 10 | NewVec2(0, 1), 11 | NewVec2(-3, 0), 12 | NewVec2(0, -2), 13 | } 14 | 15 | var angles []float64 16 | for _, v := range vecs { 17 | angles = append(angles, v.Azimuth()) 18 | } 19 | 20 | for i := 0; i < len(angles)-1; i++ { 21 | if angles[i] > angles[i+1] { 22 | t.Errorf("angle for %+v is larger than for %+v, expected smaller", vecs[i], vecs[i+1]) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /2018/day14/day14a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | use List::Util qw(sum); 9 | 10 | my $fname = shift; 11 | 12 | open my $fh, "<", $fname 13 | or die "Can't open $fname: $!"; 14 | 15 | chomp(my $num = <$fh> + 0); 16 | 17 | my @r = (3, 7); 18 | 19 | my ($i, $j) = (0, 1); 20 | 21 | while (@r < $num + 10) { 22 | my $sum = sum @r[$i, $j]; 23 | push @r, split( //, $sum); 24 | $i = ($i + $r[$i] + 1) % @r; 25 | $j = ($j + $r[$j] + 1) % @r; 26 | } 27 | 28 | say join "", @r[$num .. $num+9]; 29 | -------------------------------------------------------------------------------- /2020/day03/day03b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | { map[NR] = $0 } 4 | 5 | END { 6 | dx[1] = 1; dy[1] = 1 7 | dx[2] = 3; dy[2] = 1 8 | dx[3] = 5; dy[3] = 1 9 | dx[4] = 7; dy[4] = 1 10 | dx[5] = 1; dy[5] = 2 11 | 12 | xmax = length(map[1]) 13 | 14 | total = 1 15 | 16 | for (i = 1; i <= 5; ++i) { 17 | x = 0 18 | trees = 0 19 | for (y = 1; y <= NR; y += dy[i]) { 20 | xpos = x % xmax + 1 21 | if (substr(map[y], xpos, 1) == "#") { 22 | ++trees 23 | } 24 | x += dx[i] 25 | } 26 | total *= trees 27 | } 28 | 29 | print total 30 | } 31 | -------------------------------------------------------------------------------- /2015/day17/day17a: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | declare -r total=150 4 | 5 | mapfile -t containers < <(sort -nr input) 6 | 7 | combo=0 8 | solutions=0 9 | 10 | for combo in $(seq 510 917504); do 11 | idx=$(bc <<< "obase=2; $combo") 12 | sum=0 13 | for i in $(seq 1 ${#idx}); do 14 | (( sum += (${idx: -i:1} * ${containers[@]: -i:1}) )) 15 | done 16 | if (( sum == total )); then 17 | (( ++solutions )) 18 | echo -e "\n$solutions\n${containers[@]}\n$idx ($combo)\n$sum" 19 | fi 20 | done 21 | echo "Total solutions: $solutions" 22 | -------------------------------------------------------------------------------- /2017/day05/day05b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | chomp(my @arr = <$fh>); 14 | my $pos = 0; 15 | my $res = 0; 16 | 17 | while (1) { 18 | my $jump = $arr[$pos]; 19 | if ($jump >= 3) { 20 | $arr[$pos]-- 21 | } 22 | else { 23 | $arr[$pos]++; 24 | }; 25 | $pos += $jump; 26 | $res++; 27 | last if ($pos > $#arr or $pos < 0); 28 | } 29 | 30 | say $res; 31 | -------------------------------------------------------------------------------- /2023/day09/day09b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | def predict(seq) 4 | seqs = [seq] 5 | until seqs.last.all?(0) 6 | next_seq = [] 7 | seqs.last.each_cons(2) { |el| next_seq << (el[1] - el[0]) } 8 | seqs << next_seq 9 | end 10 | 11 | seqs.reverse! 12 | 13 | seqs[1..].each_with_index do |s, idx| 14 | s.reverse! 15 | s << s.last - seqs[idx].last 16 | end 17 | 18 | seqs.last.last 19 | end 20 | 21 | file = File.open(ARGV[0]) 22 | 23 | s = file.readlines.sum do |line| 24 | predict(line.split.map(&:to_i)) 25 | end 26 | 27 | puts s 28 | -------------------------------------------------------------------------------- /2015/day17/day17a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use 5.022; 6 | use File::Slurp; 7 | use List::Util qw(sum); 8 | 9 | use constant TOTAL => 150; 10 | 11 | my @buckets = read_file('input'); 12 | chomp @buckets; 13 | @buckets = sort { $b <=> $a } @buckets; 14 | 15 | my $solutions = 0; 16 | 17 | foreach my $combo (1..2**20) { 18 | my @idx = split //, sprintf "%020b", $combo; 19 | if ((sum @buckets[grep { $idx[$_] } (0..$#idx)]) == TOTAL) { 20 | ++$solutions; 21 | } 22 | } 23 | 24 | say "Total solutions: $solutions"; 25 | -------------------------------------------------------------------------------- /2024/day06/day06a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | grid = File.readlines(ARGV[0]).map(&:chomp).map(&:chars) 4 | 5 | y_range = (0...grid.length) 6 | x_range = (0...grid.first.length) 7 | 8 | y = grid.index { _1.include?("^") } 9 | x = grid[y].index("^") 10 | 11 | dy = -1 12 | dx = 0 13 | 14 | visited = Set.new 15 | 16 | while y_range.cover?(y) && x_range.cover?(x) 17 | visited.add([y, x]) 18 | dy, dx = dx, -dy while y_range.cover?(y + dy) && x_range.cover?(x + dx) && grid[y + dy][x + dx] == "#" 19 | y += dy 20 | x += dx 21 | end 22 | 23 | puts visited.size 24 | -------------------------------------------------------------------------------- /2024/day11/day11b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | def blink(n) 4 | if n == "0" 5 | ["1"] 6 | elsif n.length.even? 7 | [n[...n.length / 2], n[n.length / 2..].to_i.to_s] 8 | else 9 | [(n.to_i * 2024).to_s] 10 | end 11 | end 12 | 13 | stones = File.read(ARGV[0]).split 14 | 15 | stone_count = stones.tally 16 | 17 | 75.times do |i| 18 | new_count = Hash.new(0) 19 | stone_count.keys.each do |key| 20 | blink(key).each { new_count[_1] += stone_count[key] } 21 | end 22 | 23 | stone_count = new_count 24 | end 25 | 26 | puts stone_count.values.sum 27 | -------------------------------------------------------------------------------- /2015/day04/day04b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | use Digest::MD5 qw(md5_hex); 9 | 10 | my $fname = shift; 11 | 12 | open my $fh, "<", $fname 13 | or die "Can't open $fname: $!"; 14 | 15 | my $secret = <$fh>; 16 | chomp $secret; 17 | my $try = 282749; # Answer from part 1 18 | 19 | while (1) { 20 | my $md5 = md5_hex("$secret$try"); 21 | if ( $md5 =~ /^0{6}/ ) { 22 | say "$secret with an attached $try has a hash starting with 000000."; 23 | last; 24 | } 25 | $try++; 26 | } 27 | -------------------------------------------------------------------------------- /2016/day16/day16b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use feature 'say'; 6 | 7 | my $fname = shift; 8 | 9 | open my $fh, "<", $fname 10 | or die "Can't open $fname: $!"; 11 | my $str = <$fh>; 12 | chomp $str; 13 | 14 | my $len = 35651584; 15 | 16 | $str .= 0 . reverse ($str =~ tr/01/10/r) while length($str) < $len; 17 | 18 | my $chksum = substr $str, 0, $len; 19 | 20 | $chksum 21 | = join "", 22 | map { (substr $_, 0, 1) == (substr $_, 1) ? 1 : 0 } ($chksum =~ m/../g) 23 | until length($chksum) % 2; 24 | 25 | say $chksum; 26 | -------------------------------------------------------------------------------- /2017/day01/day01b.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "os" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | func main() { 12 | input, err := ioutil.ReadFile(os.Args[1]) 13 | if err != nil { 14 | panic(err) 15 | } 16 | 17 | s := strings.TrimRight(string(input), "\n") 18 | 19 | var sum int 20 | 21 | len := len(s) 22 | 23 | for i, r := range s { 24 | if string(r) == string(s[(i+len/2)%len]) { 25 | if num, err := strconv.Atoi(string(r)); err == nil { 26 | sum += num 27 | } 28 | } 29 | } 30 | 31 | fmt.Println(sum) 32 | } 33 | -------------------------------------------------------------------------------- /2016/day04/day04a_bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | re='([[:digit:]]+)\[(.*)\]' 4 | 5 | while read -r line; do 6 | [[ $line =~ $re ]] 7 | id=${BASH_REMATCH[1]} 8 | given_chksum=${BASH_REMATCH[2]} 9 | 10 | actual_chksum=$( 11 | sed 's/-[^-]*$//' <<< "$line" | tr -d '-' | fold -w 1 | 12 | sort | uniq -c | sed 's/^ *//' | sort -t ' ' -k 1,1nr -k 2,2 | 13 | head -n 5 | tr -dc 'a-z' | paste -s -d '' 14 | ) 15 | 16 | if [[ $given_chksum == $actual_chksum ]]; then 17 | (( id_sum += id )) 18 | fi 19 | done < "$1" 20 | 21 | echo "$id_sum" 22 | -------------------------------------------------------------------------------- /2016/day05/day05a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use Digest::MD5 qw(md5_hex); 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | my $id = <$fh>; 13 | chomp $id; 14 | 15 | my $idx = 0; 16 | my $passwd; 17 | my $passwd_len = 8; 18 | 19 | while (1) { 20 | my $hash = md5_hex("$id$idx"); 21 | if ( $hash =~ /^0{5}/ ) { 22 | $passwd .= substr $hash, 5, 1; 23 | last if length $passwd == $passwd_len; 24 | } 25 | $idx++; 26 | } 27 | 28 | say $passwd; 29 | -------------------------------------------------------------------------------- /2022/day04/day04b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | my $count = 0; 14 | 15 | while ( my $line = <$fh> ) { 16 | chomp $line; 17 | my ( $min1, $max1, $min2, $max2 ) = $line =~ /\d+/g; 18 | ++$count 19 | if $min2 >= $min1 and $min2 <= $max1 20 | or $max2 >= $min1 and $max2 <= $max1 21 | or $min1 >= $min2 and $min1 <= $max2 22 | or $max1 >= $min2 and $max1 <= $max2; 23 | } 24 | 25 | say "$count"; 26 | -------------------------------------------------------------------------------- /go/ioutil/ioutil.go: -------------------------------------------------------------------------------- 1 | // Package ioutil provides functionality to read input files. 2 | package ioutil 3 | 4 | import ( 5 | "bufio" 6 | "os" 7 | ) 8 | 9 | // GetInputScanner returns a scanner for newline separated input in a file 10 | // specified as a command line argument, or "../input" if none is specified. 11 | func GetInputScanner() (*bufio.Scanner, error) { 12 | if len(os.Args) == 1 { 13 | os.Args = append(os.Args, "../input") 14 | } 15 | input, err := os.Open(os.Args[1]) 16 | if err != nil { 17 | return nil, err 18 | } 19 | 20 | return bufio.NewScanner(input), nil 21 | } 22 | -------------------------------------------------------------------------------- /2016/day03/day03b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | { 4 | # Get next two lines and append to current line 5 | for (i = 1; i <= 2; ++i) { 6 | getline nextrow 7 | $0 = $0 nextrow 8 | } 9 | 10 | # Triangles are now in fields 1/4/7, 2/5/8 and 3/6/9 11 | for (i = 1; i <= 3; ++i) { 12 | sum = $i 13 | max = $i 14 | for (j = i+3; j <= i+6; j+=3) { 15 | if ($j > max) 16 | max = $j 17 | sum += $j 18 | } 19 | if (max < sum/2) 20 | ++valid 21 | } 22 | } 23 | 24 | END { print valid } 25 | -------------------------------------------------------------------------------- /2024/day04/day04b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | search = File.open(ARGV[0]).readlines(chomp: true).map(&:chars) 4 | range_y = (1...search.size - 1) 5 | range_x = (1...search.first.size - 1) 6 | 7 | count = 0 8 | 9 | search.each_with_index do |row, y| 10 | row.each_with_index do |char, x| 11 | next unless char == "A" && range_y.cover?(y) && range_x.cover?(x) 12 | 13 | if [search[y - 1][x - 1], search[y + 1][x + 1]].sort.join == "MS" && 14 | [search[y + 1][x - 1], search[y - 1][x + 1]].sort.join == "MS" 15 | count += 1 16 | end 17 | end 18 | end 19 | 20 | puts count 21 | -------------------------------------------------------------------------------- /2015/day25/day25a: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | read line < input 4 | re='row ([[:digit:]]+), column ([[:digit:]]+)' 5 | [[ $line =~ $re ]] 6 | goal_row=${BASH_REMATCH[1]} 7 | goal_column=${BASH_REMATCH[2]} 8 | 9 | row=1 10 | column=1 11 | val=20151125 12 | 13 | while true; do 14 | (( val = val * 252533 % 33554393 )) 15 | if (( row == 1 )); then 16 | (( row = column + 1 )) 17 | column=1 18 | else 19 | (( --row )) 20 | (( ++column )) 21 | fi 22 | if (( row == goal_row && column == goal_column )); then break; fi 23 | done 24 | 25 | echo "$row/$column: $val" 26 | -------------------------------------------------------------------------------- /2017/day08/day08a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | use List::Util qw(max); 9 | 10 | my $fname = shift; 11 | 12 | open my $fh, "<", $fname 13 | or die "Can't open $fname: $!"; 14 | 15 | my %vars; 16 | 17 | while (my $line = <$fh>) { 18 | chomp $line; 19 | my @arr = split / /, $line; 20 | foreach my $idx (0, 4) { 21 | $vars{$arr[$idx]} //= 0; 22 | $arr[$idx] =~ s/.*/\$vars{$&}/; 23 | } 24 | $arr[1] =~ s/inc/+=/; 25 | $arr[1] =~ s/dec/-=/; 26 | eval "@arr"; 27 | } 28 | 29 | say max values %vars; 30 | -------------------------------------------------------------------------------- /2024/day07/day07a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | lines = File.readlines(ARGV[0]).map(&:chomp) 4 | 5 | equations = lines.map do |line| 6 | test_val = line.split(":").first.to_i 7 | numbers = line.split(":").last.strip.split.map(&:to_i) 8 | [test_val, numbers] 9 | end 10 | 11 | total = equations.select do |test_val, numbers| 12 | ops = [:+, :*].repeated_permutation(numbers.length - 1).to_a 13 | ops.any? do |perm| 14 | numbers.each_with_index.reduce do |(acc, _), (n, idx)| 15 | acc.send(perm[idx - 1], n) 16 | end.eql?(test_val) 17 | end 18 | end.sum { _1.first } 19 | 20 | puts total 21 | -------------------------------------------------------------------------------- /2020/day16/day16a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | BEGIN { part = 1 } 4 | 5 | /^$/ { 6 | ++part 7 | if (part == 2) { 8 | FS = "," 9 | } 10 | } 11 | 12 | part == 1 { 13 | ranges[++i][1] = "" 14 | split($(NF-2), ranges[i], /-/) 15 | 16 | ranges[++i][1] = "" 17 | split($NF, ranges[i], /-/) 18 | } 19 | 20 | part == 3 && /,/ { 21 | for (i = 1; i <= NF; ++i) { 22 | valid = 0 23 | for (j in ranges) { 24 | if ($i >= ranges[j][1] && $i <= ranges[j][2]) { 25 | valid = 1 26 | break 27 | } 28 | } 29 | 30 | if (!valid) { 31 | rate += $i 32 | } 33 | } 34 | } 35 | 36 | END { print rate } 37 | -------------------------------------------------------------------------------- /2015/day16/day16a: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | declare -A sue 4 | 5 | sue=([children]=3 [cats]=7 [samoyeds]=2 [pomeranians]=3 [akitas]=0 6 | [vizslas]=0 [goldfish]=5 [trees]=3 [cars]=2 [perfumes]=1) 7 | 8 | while read _ num key0 val0 key1 val1 key2 val2; do 9 | num=${num%:} 10 | key0=${key0%:} 11 | val0=${val0%,} 12 | key1=${key1%:} 13 | val1=${val1%,} 14 | key2=${key2%:} 15 | 16 | if (( sue[$key0] == val0 && 17 | sue[$key1] == val1 && 18 | sue[$key2] == val2 )); then 19 | echo "The correct Sue is number $num." 20 | break 21 | fi 22 | done < input 23 | -------------------------------------------------------------------------------- /2023/day14/day14a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | def tilt_up(rock_map) 4 | rock_map.transpose.map do |line_arr| 5 | line = line_arr.join 6 | while line.include?(".O") 7 | line.gsub!(".O", "O.") 8 | end 9 | 10 | line.split("") 11 | end.transpose 12 | end 13 | 14 | def calculate_load(rock_map) 15 | rock_map.reverse_each.with_index.sum do |line, idx| 16 | line.count { |el| el == "O" } * (idx + 1) 17 | end 18 | end 19 | 20 | file = File.open(ARGV[0]) 21 | 22 | rock_map = file.readlines.map(&:chomp).map(&:chars) 23 | tilted_map = tilt_up(rock_map) 24 | 25 | puts calculate_load(tilted_map) 26 | -------------------------------------------------------------------------------- /2017/day15/day15a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | chomp(my @arr = <$fh>); 14 | 15 | "@arr" =~ /(\d+).*?(\d+)/; 16 | my ($valA, $valB) = ($1, $2); 17 | 18 | my ($facA, $facB) = (16807, 48271); 19 | my $divi = 2147483647; 20 | 21 | my $count = 0; 22 | 23 | foreach my $i ( 1 .. 40_000_000 ) { 24 | $valA = $valA * $facA % $divi; 25 | $valB = $valB * $facB % $divi; 26 | $count++ if ($valA & 0xffff) == ($valB & 0xffff); 27 | } 28 | 29 | say $count; 30 | -------------------------------------------------------------------------------- /2020/day09/day09a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | BEGIN { window = 25 } 4 | 5 | function valid(n, i, j) { 6 | for (i in prev25) { 7 | for (j in prev25) { 8 | if (i == j) { 9 | continue 10 | } 11 | 12 | if (prev25[i] == prev25[j]) { 13 | continue 14 | } 15 | 16 | if (prev25[i] + prev25[j] == n) { 17 | return 1 18 | } 19 | } 20 | } 21 | 22 | return 0 23 | } 24 | 25 | NR <= window { prev25[NR] = $1 } 26 | 27 | NR > window && valid($1) { 28 | # shuffle previous 25 29 | delete prev25[NR-window] 30 | prev25[NR] = $1 31 | next 32 | } 33 | 34 | NR > window { 35 | print 36 | exit 37 | } 38 | -------------------------------------------------------------------------------- /2018/day02/day02a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | my ($twos, $threes); 14 | 15 | chomp(my @lines = <$fh>); 16 | 17 | foreach my $line (@lines) { 18 | ++$twos if (hasrepeated($line, 2)); 19 | ++$threes if (hasrepeated($line, 3)); 20 | } 21 | 22 | say $twos * $threes; 23 | 24 | sub hasrepeated { 25 | my ($str, $n) = @_; 26 | foreach my $l (split //, $str) { 27 | if ((() = $str =~ /$l/g) == $n) { 28 | return 1; 29 | } 30 | } 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /2021/day07/day07a.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import process from "process"; 4 | import * as fs from "fs"; 5 | 6 | let input = fs.readFileSync(process.argv[2], "utf8") 7 | .trim() 8 | .split(",") 9 | .map(Number); 10 | 11 | let range = input .reduce((prev, curr) => ({ 12 | min: prev.min ? Math.min(prev.min, curr) : curr, 13 | max: prev.max ? Math.max(prev.max, curr) : curr 14 | })); 15 | 16 | let costs = []; 17 | for (let pos = range.min; pos <= range.max; ++pos) { 18 | costs.push(input.reduce((prev, curr) => prev + Math.abs(pos - curr), 0)); 19 | } 20 | 21 | console.log(costs.reduce((prev, curr) => Math.min(prev, curr))); 22 | -------------------------------------------------------------------------------- /2017/day23/day23b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | use Math::Prime::Util qw(is_prime); 9 | 10 | my $fname = shift; 11 | my $debug = shift; 12 | 13 | open my $fh, "<", $fname 14 | or die "Can't open $fname: $!"; 15 | 16 | chomp(my @arr = <$fh>); 17 | 18 | @arr = map { [ split / /, $_ ] } @arr; 19 | 20 | my $b = $arr[0]->[2] * $arr[4]->[2] - $arr[5]->[2]; 21 | my $c = $b - $arr[7]->[2]; 22 | my $step = -$arr[30]->[2]; 23 | 24 | my $ctr = 0; 25 | 26 | for (my $num = $b; $num <= $c; $num += $step) { 27 | $ctr++ unless is_prime($num); 28 | } 29 | 30 | say $ctr; 31 | -------------------------------------------------------------------------------- /2019/day04/day04a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | my $input = <$fh>; 14 | chomp $input; 15 | my ( $from, $to ) = split /-/, $input; 16 | 17 | my $count = 0; 18 | 19 | OUTER: 20 | foreach my $n ( $from .. $to ) { 21 | next unless $n =~ /(.)\g1/; 22 | my $prev = substr( $n, 0, 1 ); 23 | foreach my $digit ( split //, substr( $n, 1 ) ) { 24 | next OUTER if $digit < $prev; 25 | $prev = $digit; 26 | } 27 | ++$count; 28 | } 29 | 30 | say $count; 31 | -------------------------------------------------------------------------------- /2023/day08/day08b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | file = File.open(ARGV[0]) 4 | 5 | lr, _, *instr = file.readlines 6 | 7 | nodes = {} 8 | 9 | instr.map(&:chomp).each do |line| 10 | from, left, right = line.scan(/[[:alnum:]]+/) 11 | nodes[from] = { 12 | "L" => left, 13 | "R" => right 14 | } 15 | end 16 | 17 | locs = nodes.keys.select { |n| n.end_with?("A") } 18 | 19 | steps = [] 20 | 21 | locs.each do |loc| 22 | count = 0 23 | lr.chomp.chars.cycle do |c| 24 | loc = nodes[loc][c] 25 | count += 1 26 | break if loc.end_with?("Z") 27 | end 28 | 29 | steps << count 30 | end 31 | 32 | puts steps.reduce(&:lcm) 33 | -------------------------------------------------------------------------------- /2015/day10/day10a: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | lookandsay () { 4 | local num="$1" 5 | local i 6 | local digit=${num:0:1} 7 | local ctr=0 8 | local res 9 | for (( i = 0; i < ${#num}; ++i )); do 10 | if [[ ${num:$i:1} == $digit ]]; then 11 | (( ++ctr )) 12 | else 13 | res+=$ctr$digit 14 | ctr=1 15 | digit=${num:$i:1} 16 | fi 17 | done 18 | res+=$ctr$digit 19 | echo "$res" 20 | } 21 | 22 | num=$(cat input) 23 | 24 | for i in {1..40}; do 25 | echo "Iteration $i" 26 | num=$(lookandsay $num) 27 | done 28 | 29 | echo "Length of final: ${#num}" 30 | -------------------------------------------------------------------------------- /2017/day08/day08b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | use List::Util qw(max); 9 | 10 | my $fname = shift; 11 | 12 | open my $fh, "<", $fname 13 | or die "Can't open $fname: $!"; 14 | 15 | my %vars; 16 | my @maxs; 17 | 18 | while (my $line = <$fh>) { 19 | chomp $line; 20 | my @arr = split / /, $line; 21 | foreach my $idx (0, 4) { 22 | $vars{$arr[$idx]} //= 0; 23 | $arr[$idx] =~ s/.*/\$vars{$&}/; 24 | } 25 | $arr[1] =~ s/inc/+=/; 26 | $arr[1] =~ s/dec/-=/; 27 | eval "@arr"; 28 | push @maxs, max values %vars; 29 | } 30 | 31 | say max @maxs; 32 | -------------------------------------------------------------------------------- /2020/day10/day10b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # - sort 4 | # - add the socket and the device joltage 5 | # - print a blank line for every time the difference is 3 jolts 6 | # - for each group of consecutive jolts, multiply the number of options by the 7 | # number of options for that group 8 | 9 | sort -n "$1" \ 10 | | awk 'BEGIN { print 0 }; 1; END { print $1 + 3 }' \ 11 | | awk '{ if ($1 - prev == 3) { print "" }; prev = $1; print }' \ 12 | | awk ' 13 | BEGIN { 14 | RS = "" 15 | options = 1 16 | } 17 | 18 | NF == 3 { options *= 2 } 19 | NF == 4 { options *= 4 } 20 | NF == 5 { options *= 7 } 21 | 22 | END { print options } 23 | ' 24 | -------------------------------------------------------------------------------- /2017/day15/day15b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | chomp(my @arr = <$fh>); 14 | 15 | "@arr" =~ /(\d+).*?(\d+)/; 16 | my ($valA, $valB) = ($1, $2); 17 | 18 | my ($facA, $facB) = (16807, 48271); 19 | my $divi = 2147483647; 20 | 21 | my $count = 0; 22 | 23 | foreach my $i ( 1 .. 5_000_000 ) { 24 | do { $valA = $valA * $facA % $divi } while $valA % 4; 25 | do { $valB = $valB * $facB % $divi } while $valB % 8; 26 | $count++ if ($valA & 0xffff) == ($valB & 0xffff); 27 | } 28 | 29 | say $count; 30 | -------------------------------------------------------------------------------- /2018/day14/day14b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | chomp(my $num = <$fh>); 14 | 15 | my $r = '37'; 16 | my ($i, $j) = (0, 1); 17 | my $idx; 18 | 19 | while (1) { 20 | my $sum = substr($r, $i, 1) + substr($r, $j, 1); 21 | $r .= "$sum"; 22 | $i = ($i + substr($r, $i, 1) + 1) % length($r); 23 | $j = ($j + substr($r, $j, 1) + 1) % length($r); 24 | next if length($r) < length($num) + 2; 25 | $idx = index( $r, $num, length($r) - length($num) - 2 ); 26 | last if $idx != -1; 27 | } 28 | 29 | say $idx; 30 | -------------------------------------------------------------------------------- /2019/day01/first/day01a.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | 7 | "github.com/bewuethr/advent-of-code/go/ioutil" 8 | "github.com/bewuethr/advent-of-code/go/log" 9 | ) 10 | 11 | func main() { 12 | scanner, err := ioutil.GetInputScanner() 13 | if err != nil { 14 | log.Die("getting scanner", err) 15 | } 16 | 17 | fuel := 0 18 | for scanner.Scan() { 19 | module, err := strconv.Atoi(scanner.Text()) 20 | if err != nil { 21 | log.Die("converting to int", err) 22 | } 23 | 24 | fuel += module/3 - 2 25 | } 26 | if err := scanner.Err(); err != nil { 27 | log.Die("reading input", err) 28 | } 29 | 30 | fmt.Println(fuel) 31 | } 32 | -------------------------------------------------------------------------------- /2017/day09/day09a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | my $res = 0; 14 | 15 | my $line = <$fh>; 16 | chomp $line; 17 | 18 | # Clean up escapes 19 | $line =~ s/!.//g; 20 | 21 | # Clean up garbage 22 | $line =~ s/<.*?>//g; 23 | 24 | # Where we go, we need no commas 25 | $line =~ s/,//g; 26 | 27 | my $indent = 1; 28 | 29 | foreach my $char (split //, $line) { 30 | if ($char eq '{') { 31 | $res += $indent; 32 | $indent++; 33 | } 34 | $indent-- if $char eq '}'; 35 | } 36 | 37 | say $res; 38 | -------------------------------------------------------------------------------- /2021/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "advent-of-code", 3 | "version": "0.1.0", 4 | "description": "Advent of Code", 5 | "type": "module", 6 | "main": "index.js", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/bewuethr/advent-of-code.git" 13 | }, 14 | "author": "Benjamin Wuethrich", 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/bewuethr/advent-of-code/issues" 18 | }, 19 | "homepage": "https://github.com/bewuethr/advent-of-code#readme", 20 | "devDependencies": { 21 | "eslint": "^8.6.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /2024/day04/day04a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | search = File.open(ARGV[0]).readlines(chomp: true).map(&:chars) 4 | range_y = (0...search.size) 5 | range_x = (0...search.first.size) 6 | 7 | count = 0 8 | 9 | directions = (-1..1).to_a.product((-1..1).to_a).reject { _1 == [0, 0] } 10 | 11 | search.each_with_index do |row, y| 12 | row.each_with_index do |char, x| 13 | next unless char == "X" 14 | 15 | directions.each do |dx, dy| 16 | next unless range_y.cover?(y + 3 * dy) && range_x.cover?(x + 3 * dx) 17 | suffix = (1..3).map { search[y + _1 * dy][x + _1 * dx] }.join 18 | count += 1 if suffix == "MAS" 19 | end 20 | end 21 | end 22 | 23 | puts count 24 | -------------------------------------------------------------------------------- /2019/day04/day04b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | my $input = <$fh>; 14 | chomp $input; 15 | my ( $from, $to ) = split /-/, $input; 16 | 17 | my $count = 0; 18 | 19 | OUTER: 20 | foreach my $n ( $from .. $to ) { 21 | next unless $n =~ /(.)\g1/; 22 | my $prev = substr( $n, 0, 1 ); 23 | foreach my $digit ( split //, substr( $n, 1 ) ) { 24 | next OUTER if $digit < $prev; 25 | $prev = $digit; 26 | } 27 | ++$count if grep { length $_ == 2 } ( $n =~ /((.)\g2+)/g ); 28 | } 29 | 30 | say $count; 31 | -------------------------------------------------------------------------------- /2024/day07/day07b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | class Integer 4 | def concat(n) = [self, n].join.to_i 5 | end 6 | 7 | lines = File.readlines(ARGV[0]).map(&:chomp) 8 | 9 | equations = lines.map do |line| 10 | test_val = line.split(":").first.to_i 11 | numbers = line.split(":").last.strip.split.map(&:to_i) 12 | [test_val, numbers] 13 | end 14 | 15 | total = equations.select do |test_val, numbers| 16 | ops = [:+, :*, :concat].repeated_permutation(numbers.length - 1).to_a 17 | ops.any? do |perm| 18 | numbers.each_with_index.reduce do |(acc, _), (n, idx)| 19 | acc.send(perm[idx - 1], n) 20 | end.eql?(test_val) 21 | end 22 | end.sum { _1.first } 23 | 24 | puts total 25 | -------------------------------------------------------------------------------- /2016/day05/day05b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | @load "general" 4 | 5 | BEGIN { passwd_len = 8 } 6 | 7 | { 8 | id = $0 9 | idx = -1 10 | 11 | while (1) { 12 | ++idx 13 | hash = md5(id idx) 14 | 15 | if (match(hash, /^0{5}([0-7])(.)/, arr)) { 16 | print "Match: " hash 17 | if (passwd[arr[1]]) 18 | continue 19 | passwd[arr[1]] = arr[2] 20 | print "Found letter at position " arr[1] 21 | if (length(passwd) == passwd_len) 22 | break 23 | } 24 | } 25 | 26 | for (i = 0; i < length(passwd); ++i) 27 | pass = pass passwd[i] 28 | print pass 29 | } 30 | 31 | -------------------------------------------------------------------------------- /2021/day10/day10a.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import process from "process"; 4 | import * as fs from "fs"; 5 | 6 | let input = fs.readFileSync(process.argv[2], "utf8") 7 | .trim() 8 | .split("\n"); 9 | 10 | let score = 0; 11 | let pairs = ["()", "[]", "{}", "<>"]; 12 | let scores = new Map(); 13 | scores.set(")", 3); 14 | scores.set("]", 57); 15 | scores.set("}", 1197); 16 | scores.set(">", 25137); 17 | 18 | for (let line of input) { 19 | let stack = []; 20 | for (let e of line.split("")) { 21 | if (e.match(/[[({<]/)) stack.push(e); 22 | else if (!pairs.includes(stack.pop() + e)) { 23 | score += scores.get(e); 24 | break; 25 | } 26 | } 27 | } 28 | 29 | console.log(score); 30 | -------------------------------------------------------------------------------- /2022/day02/day02b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | BEGIN { 4 | map["X"]["A"] = "Z" 5 | map["X"]["B"] = "X" 6 | map["X"]["C"] = "Y" 7 | 8 | map["Y"]["A"] = "X" 9 | map["Y"]["B"] = "Y" 10 | map["Y"]["C"] = "Z" 11 | 12 | map["Z"]["A"] = "Y" 13 | map["Z"]["B"] = "Z" 14 | map["Z"]["C"] = "X" 15 | } 16 | 17 | { mine = map[$2][$1] } 18 | 19 | mine == "X" { 20 | score += 1 21 | if ($1 == "A") score += 3 22 | if ($1 == "C") score += 6 23 | } 24 | 25 | mine == "Y" { 26 | score += 2 27 | if ($1 == "B") score += 3 28 | if ($1 == "A") score += 6 29 | } 30 | 31 | mine == "Z" { 32 | score += 3 33 | if ($1 == "C") score += 3 34 | if ($1 == "B") score += 6 35 | } 36 | 37 | END { print score } 38 | 39 | -------------------------------------------------------------------------------- /2016/day02/day02a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | BEGIN { 4 | x = 2 5 | y = 2 6 | } 7 | 8 | { 9 | split($0, dirs, "") 10 | for (i = 1; i <= length(dirs); ++i) { 11 | switch (dirs[i]) { 12 | case "U": 13 | y = y > 1 ? y - 1 : y 14 | break 15 | case "D": 16 | y = y < 3 ? y + 1 : y 17 | break 18 | case "L": 19 | x = x > 1 ? x - 1 : x 20 | break 21 | case "R": 22 | x = x < 3 ? x + 1 : x 23 | break 24 | default: 25 | print "Illegal direction" 26 | exit 27 | } 28 | } 29 | code = code (y-1) * 3 + x 30 | } 31 | 32 | END { print code } 33 | -------------------------------------------------------------------------------- /2016/day05/day05b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use Digest::MD5 qw(md5_hex); 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | my $id = <$fh>; 13 | chomp $id; 14 | 15 | my $idx = -1; 16 | my @passwd; 17 | my $passwd_len = 8; 18 | my $found = 0; 19 | 20 | while (1) { 21 | ++$idx; 22 | my $hash = md5_hex("$id$idx"); 23 | if ( $hash =~ /^0{5}[0-7]/ ) { 24 | my $pos = substr $hash, 5, 1; 25 | next if defined $passwd[$pos]; 26 | 27 | $passwd[$pos] = substr $hash, 6, 1; 28 | $found++; 29 | last if $found == $passwd_len; 30 | } 31 | } 32 | 33 | say @passwd; 34 | -------------------------------------------------------------------------------- /2016/day15/day15a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | function advance(pos, size, i) { 4 | for (i in pos) { 5 | ++pos[i] 6 | if (pos[i] == size[i]) 7 | pos[i] = 0 8 | } 9 | } 10 | 11 | function aligned(pos, size, i) { 12 | for (i in pos) { 13 | if ((pos[i] + i) % size[i] != 0) 14 | return 0 15 | } 16 | return 1 17 | } 18 | 19 | { 20 | size[NR] = $4 21 | sub(/\./, "", $NF) 22 | pos[NR] = $NF 23 | } 24 | 25 | END { 26 | time = 0 27 | while (1) { 28 | if (aligned(pos, size)) { 29 | print "Push button at time " time 30 | exit 31 | } 32 | advance(pos, size) 33 | ++time 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /2021/day02/day02a.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import process from "process"; 4 | import * as fs from "fs"; 5 | 6 | let pos = 0; 7 | let depth = 0; 8 | 9 | fs.readFileSync(process.argv[2], "utf8") 10 | .trim() 11 | .split("\n") 12 | .map(line => { 13 | let [instr, amount] = line.split(" "); 14 | return {instr, amount: Number(amount)} 15 | }) 16 | .forEach(({instr, amount}) => { 17 | switch(instr) { 18 | case "forward": 19 | pos += amount; 20 | break; 21 | case "down": 22 | depth += amount; 23 | break; 24 | case "up": 25 | depth -= amount; 26 | break; 27 | default: 28 | console.error(`invalid instruction: ${instr}`); 29 | } 30 | }); 31 | 32 | console.log(pos * depth); 33 | -------------------------------------------------------------------------------- /2020/day09/day09b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | function extremes(idxmin, idxmax, i, min, max) { 4 | min = nums[idxmin] 5 | max = nums[idxmin] 6 | for (i = idxmin+1; i <= idxmax; ++i) { 7 | min = nums[i] < min ? nums[i] : min 8 | max = nums[i] > max ? nums[i] : max 9 | } 10 | 11 | return min + max 12 | } 13 | 14 | BEGIN { val = 22477624 } 15 | # BEGIN { val = 127 } 16 | 17 | { nums[NR] = $1 } 18 | 19 | END { 20 | for (i = 1; i <= NR - 1; ++i ) { 21 | sum = nums[i] 22 | offset = 1 23 | while (1) { 24 | sum += nums[i+offset] 25 | if (sum == val) { 26 | print extremes(i, i+offset) 27 | exit 28 | } 29 | 30 | if (sum > val) { 31 | break 32 | } 33 | 34 | ++offset 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /2021/day03/day03a.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import process from "process"; 4 | import * as fs from "fs"; 5 | 6 | let report = fs.readFileSync(process.argv[2], "utf8") 7 | .trim() 8 | .split("\n") 9 | .map(el => el.split("").map(Number)); 10 | 11 | let len = report[0].length; 12 | 13 | let gamma = report.reduce((prev, curr) => { 14 | curr.forEach((el, idx) => { 15 | prev[idx][el]++; 16 | }); 17 | return prev; 18 | }, Array.from(Array(len), () => [0, 0])) 19 | .map(([zero, one]) => zero > one ? 0 : 1) 20 | .join(""); 21 | 22 | let epsilon = gamma.split("") 23 | .map(Number) 24 | .map((digit, idx) => gamma[idx] == 0 ? 1 : 0) 25 | .join(""); 26 | 27 | console.log(parseInt(gamma, 2) * parseInt(epsilon, 2)); 28 | -------------------------------------------------------------------------------- /2021/day09/day09a.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import process from "process"; 4 | import * as fs from "fs"; 5 | 6 | let input = fs.readFileSync(process.argv[2], "utf8") 7 | .trim() 8 | .split("\n") 9 | .map(e => e.split("").map(Number)); 10 | 11 | let lows = []; 12 | 13 | for (let y = 0; y < input.length; ++y) { 14 | for (let x = 0; x < input[y].length; ++x) { 15 | let val = input[y][x]; 16 | if ((y == 0 || input[y-1][x] > val) 17 | && (x == 0 || input[y][x-1] > val) 18 | && (x == input[y].length - 1 || input[y][x+1] > val) 19 | && (y == input.length - 1 || input[y+1][x] > val)) 20 | { 21 | lows.push(val); 22 | } 23 | } 24 | } 25 | 26 | console.log(lows.map(e => e + 1).reduce((a, b) => a + b)); 27 | 28 | -------------------------------------------------------------------------------- /2019/day02/day02a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | my $line = <$fh>; 14 | chomp $line; 15 | my @p = split /,/, $line; 16 | @p[ 1, 2 ] = ( 12, 2 ); 17 | 18 | my $i = 0; 19 | while (1) { 20 | last if $p[$i] == 99; 21 | if ( $p[$i] == 1 ) { 22 | $p[ $p[ $i + 3 ] ] = $p[ $p[ $i + 1 ] ] + $p[ $p[ $i + 2 ] ]; 23 | } 24 | elsif ( $p[$i] == 2 ) { 25 | $p[ $p[ $i + 3 ] ] = $p[ $p[ $i + 1 ] ] * $p[ $p[ $i + 2 ] ]; 26 | } 27 | else { 28 | die "illegal opcode at index $i: $p[$i]"; 29 | } 30 | $i += 4; 31 | } 32 | 33 | say $p[0]; 34 | -------------------------------------------------------------------------------- /2016/day07/day07a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | function has_abba(str, i) { 4 | for (i = 1; i <= length(str) - 3; ++i) { 5 | if ( \ 6 | substr(str, i+1, 1) == substr(str, i+2, 1) && \ 7 | substr(str, i, 1) == substr(str, i+3, 1) && \ 8 | substr(str, i, 1) != substr(str, i+1, 1) \ 9 | ) { 10 | return 1 11 | } 12 | } 13 | return 0 14 | } 15 | 16 | BEGIN { FS = "[][]" } 17 | 18 | { 19 | for (i = 2; i <= NF - 1; i += 2) { 20 | if (has_abba($i)) 21 | next 22 | } 23 | for (i = 1; i <= NF; ++i) { 24 | if (has_abba($i)) { 25 | ++tls 26 | next 27 | } 28 | } 29 | } 30 | 31 | END { print tls } 32 | -------------------------------------------------------------------------------- /2020/day13/day13b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -Mf 2 | 3 | function extended_euclid(a, b, b0, t, q, x0, x1) { 4 | b0 = b 5 | x0 = 0 6 | x1 = 1 7 | 8 | if (b == 1) { 9 | return 1 10 | } 11 | 12 | while (a > 1) { 13 | q = int(a / b) 14 | t = b 15 | b = a % b 16 | a = t 17 | t = x0 18 | x0 = x1 - q * x0 19 | x1 = t 20 | } 21 | 22 | return x1 < 0 ? x1 + b0 : x1 23 | } 24 | 25 | BEGIN { FS = "," } 26 | 27 | NR == 2 { 28 | N = 1 29 | for (i = 1; i <= NF; ++i) { 30 | if ($i != "x") { 31 | n[i] = $i 32 | a[i] = ($i - i + 1) % $i 33 | N *= $i 34 | } 35 | } 36 | } 37 | 38 | END { 39 | for (i in n) { 40 | p = int(N / n[i]) 41 | result += a[i] * extended_euclid(p, n[i]) * p 42 | } 43 | 44 | print result % N 45 | } 46 | -------------------------------------------------------------------------------- /2017/day04/day04a.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "log" 7 | "os" 8 | "strings" 9 | ) 10 | 11 | func main() { 12 | f, err := os.Open(os.Args[1]) 13 | if err != nil { 14 | log.Fatal(err) 15 | } 16 | defer f.Close() 17 | 18 | scanner := bufio.NewScanner(f) 19 | 20 | valid := 0 21 | 22 | Loop: 23 | for scanner.Scan() { 24 | words := strings.Split(scanner.Text(), " ") 25 | counts := make(map[string]int) 26 | for _, word := range words { 27 | counts[word]++ 28 | if counts[word] > 1 { 29 | continue Loop 30 | } 31 | } 32 | valid++ 33 | } 34 | 35 | if err := scanner.Err(); err != nil { 36 | fmt.Fprintln(os.Stderr, "Reading from file:", err) 37 | } 38 | 39 | fmt.Println(valid) 40 | } 41 | -------------------------------------------------------------------------------- /2017/day12/day12a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | sub visit { 9 | my ($con, $seen, $el) = @_; 10 | 11 | foreach my $val ( @{ $con->{$el} } ) { 12 | if (not $seen->{$val}++) { 13 | visit($con, $seen, $val); 14 | } 15 | } 16 | return; 17 | } 18 | 19 | my $fname = shift; 20 | 21 | open my $fh, "<", $fname 22 | or die "Can't open $fname: $!"; 23 | 24 | my %con; 25 | 26 | while (my $line = <$fh>) { 27 | chomp $line; 28 | my @arr = split /[ <>,-]+/, $line; 29 | $con{$arr[0]} = [ @arr[1 .. $#arr] ]; 30 | } 31 | 32 | my $el = 0; 33 | my %seen = ( $el => 1 ); 34 | 35 | visit(\%con, \%seen, $el); 36 | 37 | say scalar keys %seen; 38 | -------------------------------------------------------------------------------- /2021/day07/day07b.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import process from "process"; 4 | import * as fs from "fs"; 5 | 6 | function cost(from, to) { 7 | let diff = Math.abs(from - to); 8 | return diff * (diff + 1) / 2; 9 | } 10 | 11 | let input = fs.readFileSync(process.argv[2], "utf8") 12 | .trim() 13 | .split(",") 14 | .map(Number); 15 | 16 | let range = input .reduce((prev, curr) => ({ 17 | min: prev.min ? Math.min(prev.min, curr) : curr, 18 | max: prev.max ? Math.max(prev.max, curr) : curr 19 | })); 20 | 21 | let costs = []; 22 | for (let pos = range.min; pos <= range.max; ++pos) { 23 | costs.push(input.reduce((prev, curr) => prev + cost(pos, curr), 0)); 24 | } 25 | 26 | console.log(costs.reduce((prev, curr) => Math.min(prev, curr))); 27 | -------------------------------------------------------------------------------- /2023/day02/day02a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | RED_CUBES = 12 4 | GREEN_CUBES = 13 5 | BLUES_CUBES = 14 6 | 7 | file = File.open(ARGV[0]) 8 | 9 | id_sum = file.readlines.map do |line| 10 | id, games = line.match(/Game (\d+): (.*)/)[1, 2] 11 | g = games.split("; ").map do |game| 12 | game.split(", ").reduce({}) do |total, val| 13 | n, col = val.split(" ") 14 | {col => n.to_i}.merge(total) 15 | end 16 | end 17 | 18 | max_vals = {} 19 | %w[red green blue].each do |col| 20 | max_vals[col] = g.map { _1[col] }.compact.max 21 | end 22 | 23 | id.to_i if max_vals["red"] <= RED_CUBES && 24 | max_vals["green"] <= GREEN_CUBES && 25 | max_vals["blue"] <= BLUES_CUBES 26 | end.compact.sum 27 | 28 | puts id_sum 29 | -------------------------------------------------------------------------------- /2016/day19/day19a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | BEGIN { PROCINFO["sorted_in"] = "@ind_num_asc" } 4 | 5 | { elfcount = $0 } 6 | 7 | END { 8 | for (i = 1; i <= elfcount; ++i) 9 | gifts[i] = 1 10 | 11 | while (1) { 12 | print "Round " ++round 13 | for (i in gifts) { 14 | if (! gifts[i]) 15 | continue 16 | j = (i == elfcount) ? 1 : i + 1 17 | while (! gifts[j]) 18 | j = (j == elfcount) ? 1 : j + 1 19 | 20 | gifts[i] += gifts[j] 21 | delete gifts[j] 22 | 23 | if (gifts[i] == elfcount) { 24 | print "Elf number " i " gets it all!" 25 | exit 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /2018/day23/day23a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | use List::Util qw(first max); 9 | 10 | sub dist { 11 | my ($from, $to) = @_; 12 | return abs($from->{x} - $to->{x}) 13 | + abs($from->{y} - $to->{y}) 14 | + abs($from->{z} - $to->{z}); 15 | } 16 | 17 | my $fname = shift; 18 | 19 | open my $fh, "<", $fname 20 | or die "Can't open $fname: $!"; 21 | 22 | my @bots; 23 | 24 | while (my $line = <$fh>) { 25 | chomp $line; 26 | my ($x, $y, $z, $r) = ( $line =~ /(-?\d+)/g ); 27 | push @bots, { x => $x, y => $y, z => $z, r => $r }; 28 | } 29 | 30 | my $strongest = first { $_->{r} == max map { $_->{r} } @bots } @bots; 31 | 32 | say scalar grep { dist($strongest, $_) <= $strongest->{r} } @bots; 33 | -------------------------------------------------------------------------------- /2018/day02/day02b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | chomp(my @lines = <$fh>); 14 | 15 | foreach my $idx1 (0 .. $#lines-1) { 16 | foreach my $idx2 ($idx1+1 .. $#lines) { 17 | compare($lines[$idx1], $lines[$idx2]); 18 | } 19 | } 20 | 21 | sub compare { 22 | my ($l1, $l2) = @_; 23 | my $count = 0; 24 | my $common; 25 | foreach my $idx (0 .. length($l1)-1) { 26 | if (substr($l1, $idx, 1) ne substr($l2, $idx, 1)) { 27 | ++$count; 28 | } 29 | else { 30 | $common .= substr($l1, $idx, 1); 31 | } 32 | } 33 | if ($count == 1) { 34 | say $common; 35 | exit; 36 | } 37 | return; 38 | } 39 | -------------------------------------------------------------------------------- /2018/day07/day07a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | my (%steps, %degrees); 14 | 15 | while (my $line = <$fh>) { 16 | chomp $line; 17 | $line =~ /Step (.).*step (.)/; 18 | my ($from, $to) = ($1, $2); 19 | push @{$steps{$from}}, $to; 20 | $degrees{$from} //= 0; 21 | $degrees{$to}++; 22 | } 23 | 24 | my $result; 25 | 26 | while (1) { 27 | my @roots = sort grep { $degrees{$_} == 0 } keys %degrees; 28 | last if @roots == 0; 29 | $result .= $roots[0]; 30 | foreach my $node (@{ $steps{$roots[0]} }) { 31 | $degrees{$node}--; 32 | } 33 | delete $degrees{$roots[0]}; 34 | } 35 | 36 | say $result; 37 | -------------------------------------------------------------------------------- /2021/day13/day13a.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import process from "process"; 4 | import * as fs from "fs"; 5 | 6 | let [points, folds] = fs.readFileSync(process.argv[2], "utf8") 7 | .trim() 8 | .split("\n\n"); 9 | 10 | points = points 11 | .split("\n") 12 | .map(p => p.split(",").map(Number)); 13 | 14 | folds = folds 15 | .split("\n") 16 | .map(f => f.substring(11).split("=")) 17 | .map(([axis, val]) => [axis, Number(val)]); 18 | 19 | let folded = new Set(); 20 | let idx = folds[0][0] == "x" ? 0 : 1; 21 | let fold = folds[0][1]; 22 | 23 | points.forEach(p => { 24 | let pNew = [...p]; 25 | if (pNew[idx] > fold) { 26 | pNew[idx] -= 2 * (pNew[idx] - fold); 27 | } 28 | folded.add(JSON.stringify(pNew)); 29 | }); 30 | 31 | console.log(folded.size); 32 | -------------------------------------------------------------------------------- /2016/day15/day15b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | function advance(pos, size, i) { 4 | for (i in pos) { 5 | ++pos[i] 6 | if (pos[i] == size[i]) 7 | pos[i] = 0 8 | } 9 | } 10 | 11 | function aligned(pos, size, i) { 12 | for (i in pos) { 13 | if ((pos[i] + i) % size[i] != 0) 14 | return 0 15 | } 16 | return 1 17 | } 18 | 19 | { 20 | size[NR] = $4 21 | sub(/\./, "", $NF) 22 | pos[NR] = $NF 23 | } 24 | 25 | END { 26 | size[NR+1] = 11 27 | pos[NR+1] = 0 28 | time = 0 29 | while (1) { 30 | if (aligned(pos, size)) { 31 | print "Push button at time " time 32 | exit 33 | } 34 | advance(pos, size) 35 | ++time 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /2021/day02/day02b.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import process from "process"; 4 | import * as fs from "fs"; 5 | 6 | let pos = 0; 7 | let depth = 0; 8 | let aim = 0; 9 | 10 | fs.readFileSync(process.argv[2], "utf8") 11 | .trim() 12 | .split("\n") 13 | .map(line => { 14 | let [instr, amount] = line.split(" "); 15 | return {instr, amount: Number(amount)} 16 | }) 17 | .forEach(({instr, amount}) => { 18 | switch(instr) { 19 | case "forward": 20 | pos += amount; 21 | depth += aim * amount; 22 | break; 23 | case "down": 24 | aim += amount; 25 | break; 26 | case "up": 27 | aim -= amount; 28 | break; 29 | default: 30 | console.error(`invalid instruction: ${instr}`); 31 | } 32 | }); 33 | 34 | console.log(pos * depth); 35 | -------------------------------------------------------------------------------- /2024/day02/day02b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | def increasing(levels) = levels.eql?(levels.sort) 4 | 5 | def decreasing(levels) = levels.eql?(levels.sort.reverse) 6 | 7 | def max_diff(levels) 8 | levels.each_cons(2).to_a.all? do |a, b| 9 | diff = (a - b).abs 10 | (1..3).cover?(diff) 11 | end 12 | end 13 | 14 | def are_safe(levels) = (increasing(levels) || decreasing(levels)) && max_diff(levels) 15 | 16 | def is_safe(report) 17 | levels = report.split.map(&:to_i) 18 | return true if are_safe(levels) 19 | 20 | levels.each_with_index.any? do |level, idx| 21 | copy = levels.dup 22 | copy.delete_at(idx) 23 | are_safe(copy) 24 | end 25 | end 26 | 27 | file = File.open(ARGV[0]) 28 | 29 | puts file.readlines(chomp: true).count { is_safe(_1) } 30 | -------------------------------------------------------------------------------- /2016/day16/day16a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | { str = $0 } 4 | 5 | END { 6 | len = 272 7 | while (length(str) < len) { 8 | cur_len = length(str) 9 | str = str 0 10 | for (i = cur_len; i >= 1; --i) { 11 | next_digit = substr(str, i, 1) == 1 ? 0 : 1 12 | str = str next_digit 13 | } 14 | } 15 | 16 | chksum = substr(str, 1, len) 17 | while (length(chksum) % 2 == 0) { 18 | cur_len = length(chksum) 19 | for (i = 1; i <= cur_len-1; i += 2) { 20 | next_digit = substr(chksum, i, 1) == substr(chksum, i+1, 1) ? 1 : 0; 21 | next_chksum = next_chksum next_digit 22 | } 23 | chksum = next_chksum 24 | next_chksum = "" 25 | } 26 | print chksum 27 | } 28 | -------------------------------------------------------------------------------- /2022/day10/day10a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | my $x = 1; 9 | my $cycle = 0; 10 | my $sum = 0; 11 | 12 | sub tick { 13 | ++$cycle; 14 | if ( ( $cycle - 20 ) % 40 == 0 ) { 15 | $sum += $cycle * $x; 16 | } 17 | } 18 | 19 | my $fname = shift; 20 | 21 | open my $fh, "<", $fname 22 | or die "Can't open $fname: $!"; 23 | 24 | while ( my $line = <$fh> ) { 25 | chomp $line; 26 | my ( $op, $n ) = split / /, $line; 27 | 28 | if ( $op eq "noop" ) { 29 | tick(); 30 | } 31 | elsif ( $op eq "addx" ) { 32 | tick(); 33 | tick(); 34 | $x += $n; 35 | } 36 | else { 37 | die "invalid operation $op"; 38 | } 39 | } 40 | 41 | say $sum; 42 | -------------------------------------------------------------------------------- /2016/day16/day16b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | { str = $0 } 4 | 5 | END { 6 | len = 35651584 7 | while (length(str) < len) { 8 | cur_len = length(str) 9 | str = str 0 10 | for (i = cur_len; i >= 1; --i) { 11 | next_digit = substr(str, i, 1) == 1 ? 0 : 1 12 | str = str next_digit 13 | } 14 | } 15 | 16 | chksum = substr(str, 1, len) 17 | while (length(chksum) % 2 == 0) { 18 | cur_len = length(chksum) 19 | for (i = 1; i <= cur_len-1; i += 2) { 20 | next_digit = substr(chksum, i, 1) == substr(chksum, i+1, 1) ? 1 : 0; 21 | next_chksum = next_chksum next_digit 22 | } 23 | chksum = next_chksum 24 | next_chksum = "" 25 | } 26 | print chksum 27 | } 28 | -------------------------------------------------------------------------------- /2020/day24/day24a.awk: -------------------------------------------------------------------------------- 1 | { 2 | x = 0 3 | y = 0 4 | for (i = 1; i <= NF; ++i) { 5 | switch ($i) { 6 | case "e": 7 | ++x 8 | break 9 | case "se": 10 | if (y % 2) { 11 | ++x 12 | } 13 | ++y 14 | break 15 | case "sw": 16 | if (! (y % 2)) { 17 | --x 18 | } 19 | ++y 20 | break 21 | case "w": 22 | --x 23 | break 24 | case "nw": 25 | if (! (y % 2)) { 26 | --x 27 | } 28 | --y 29 | break 30 | case "ne": 31 | if (y % 2) { 32 | ++x 33 | } 34 | --y 35 | break 36 | default: 37 | print "unknown direction", $i 38 | exit 1 39 | } 40 | } 41 | 42 | ++tiles[x, y] 43 | } 44 | 45 | END { 46 | for (coord in tiles) { 47 | if (tiles[coord] % 2) { 48 | ++count 49 | } 50 | } 51 | 52 | print count 53 | } 54 | -------------------------------------------------------------------------------- /2015/day03/day03b: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | input=$(< "$1") 4 | 5 | # Santa's coordinates 6 | xs=0 7 | ys=0 8 | 9 | # Robo-Santa's coordinates 10 | xr=0 11 | yr=0 12 | 13 | declare -A visited=([$xs/$ys]=1) 14 | 15 | flag=0 # 0: Santa, 1: Robo-Santa 16 | 17 | for (( i = 0; i < ${#input}; ++i )); do 18 | case ${input:$i:1} in 19 | '>') if (( flag )); then (( ++xr )); else (( ++xs )); fi ;; 20 | '^') if (( flag )); then (( ++yr )); else (( ++ys )); fi ;; 21 | '<') if (( flag )); then (( --xr )); else (( --xs )); fi ;; 22 | 'v') if (( flag )); then (( --yr )); else (( --ys )); fi ;; 23 | esac 24 | visited[$xs/$ys]=1 25 | visited[$xr/$yr]=1 26 | (( flag = flag == 0 ? 1 : 0 )) 27 | done 28 | 29 | echo "The Santas have visited ${#visited[@]} houses." 30 | -------------------------------------------------------------------------------- /2016/day01/day01a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | BEGIN { 4 | FS = ", " 5 | dx[1] = 0; dx[2] = 1; dx[3] = 0; dx[4] = -1 6 | dy[1] = 1; dy[2] = 0; dy[3] = -1; dy[4] = 0 7 | dir_index = 1 8 | x = 0 9 | y = 0 10 | } 11 | 12 | { 13 | for (i = 1; i <= NF; ++i) { 14 | dir = substr($i, 1, 1) 15 | dist = substr($i, 2) 16 | 17 | if (dir == "R") 18 | ++dir_index 19 | else 20 | --dir_index 21 | 22 | if (dir_index == 0) 23 | dir_index = 4 24 | else if (dir_index == 5) 25 | dir_index = 1 26 | 27 | x += dx[dir_index] * dist 28 | y += dy[dir_index] * dist 29 | } 30 | } 31 | 32 | END { 33 | x = x < 0 ? -x : x 34 | y = y < 0 ? -y : y 35 | print x + y 36 | } 37 | -------------------------------------------------------------------------------- /2015/day02/day02a: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | min () { 4 | min=$1 5 | shift 6 | while (( $# )); do 7 | min=$(( $1 < min ? $1 : min )) 8 | shift 9 | done 10 | echo "$min" 11 | } 12 | 13 | regex='([[:digit:]]+)x([[:digit:]]+)x([[:digit:]]+)' 14 | total=0 15 | debug=0 16 | 17 | while read -r line; do 18 | (( debug )) && echo "Line: $line" >&2 19 | [[ "$line" =~ $regex ]] 20 | dim1=${BASH_REMATCH[1]} 21 | dim2=${BASH_REMATCH[2]} 22 | dim3=${BASH_REMATCH[3]} 23 | s1=$(( dim1 * dim2 )) 24 | s2=$(( dim1 * dim3 )) 25 | s3=$(( dim2 * dim3 )) 26 | smallest=$(min $s1 $s2 $s3) 27 | (( total += 2 * (s1 + s2 + s3) + smallest )) 28 | (( debug )) && echo "Sum: $total" >&2 29 | done < "$1" 30 | 31 | echo "Total square footage needed: $total" 32 | -------------------------------------------------------------------------------- /2015/day19/day19a: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | while IFS= read -r line; do 4 | if [[ -n $line ]]; then 5 | if [[ $line == *=\>* ]]; then 6 | replacements+=("$line") 7 | else 8 | start_mol="$line" 9 | fi 10 | fi 11 | done < "$1" 12 | 13 | declare -A new_mols 14 | 15 | re='([[:alnum:]]+) => ([[:alnum:]]+)' 16 | for replacement in "${replacements[@]}"; do 17 | [[ $replacement =~ $re ]] 18 | from="${BASH_REMATCH[1]}" 19 | to="${BASH_REMATCH[2]}" 20 | num_reps=$(grep -o "$from" <<< "$start_mol" | wc -l) 21 | 22 | for n in $(seq 1 $num_reps); do 23 | new_mol=$(sed s/"$from"/"$to"/$n <<< "$start_mol") 24 | new_mols["$new_mol"]=1 25 | done 26 | done 27 | 28 | echo "There are ${#new_mols[@]} unique molecules reachable." 29 | -------------------------------------------------------------------------------- /2016/day19/day19b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | function incr(i) { return (i == elfcount) ? 1 : i+1 } 4 | 5 | { elfcount = $0 } 6 | 7 | END { 8 | # First victim 9 | victim = int(elfcount/2) + 1 10 | elim[victim] = 1 11 | eliminated = 1 12 | 13 | # First step size depends on number of elves 14 | step = (elfcount % 2) ? 2 : 1 15 | 16 | while (1) { 17 | for (i = 1; i <= step; ++i) { 18 | victim = incr(victim) 19 | while (elim[victim]) 20 | victim = incr(victim) 21 | } 22 | 23 | elim[victim] = 1 24 | 25 | step = (step == 1) ? 2 : 1 26 | ++eliminated 27 | if (eliminated == elfcount) 28 | break 29 | } 30 | 31 | print "Sole survivor: elf number " victim 32 | } 33 | -------------------------------------------------------------------------------- /2017/day03/day03a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | sub rot_left { 9 | my $dir = shift; 10 | @$dir = (-$dir->[1], $dir->[0]); 11 | } 12 | 13 | my $fname = shift; 14 | 15 | open my $fh, "<", $fname 16 | or die "Can't open $fname: $!"; 17 | 18 | my $target = <$fh>; 19 | chomp $target; 20 | 21 | my ($x, $y) = (0, 0); 22 | my $val = 1; 23 | 24 | my $dir = [1, 0]; 25 | my $steps = 1; 26 | 27 | OUTER: while (1) { 28 | foreach my $side (1..2) { 29 | foreach my $step (1..$steps) { 30 | ($x, $y) = ($x + $dir->[0], $y + $dir->[1]); 31 | $val++; 32 | last OUTER if $val == $target; 33 | } 34 | rot_left($dir); 35 | } 36 | $steps++; 37 | } 38 | 39 | say abs $x + abs $y; 40 | -------------------------------------------------------------------------------- /2015/day17/day17b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use 5.022; 6 | use File::Slurp; 7 | use List::Util qw(sum); 8 | 9 | use constant TOTAL => 150; 10 | 11 | my @buckets = read_file('input'); 12 | chomp @buckets; 13 | @buckets = sort { $b <=> $a } @buckets; 14 | 15 | my $min_conts = 20; 16 | my @solutions; 17 | 18 | foreach my $combo (1..2**20) { 19 | my @idx = split //, sprintf "%020b", $combo; 20 | my $n_conts = sum @idx; 21 | if ((sum @buckets[grep { $idx[$_] } (0..$#idx)]) == TOTAL) { 22 | push @solutions, \@idx; 23 | $min_conts = $n_conts if $n_conts < $min_conts; 24 | } 25 | } 26 | 27 | my $min_solutions = scalar grep { sum(@{$_}) == $min_conts } @solutions; 28 | 29 | say "Number of solutions with minimum amount of containers: $min_solutions"; 30 | -------------------------------------------------------------------------------- /2016/day18/day18b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | BEGIN { PROCINFO["sorted_in"] = "@ind_num_asc" } 4 | 5 | { rows[1] = $0 } 6 | 7 | END { 8 | for (i = 2; i <= 400000; ++i) { 9 | split("." rows[i-1] ".", prev, "") 10 | for (j = 2; j <= length(prev) - 1; ++j) { 11 | if (prev[j-1] == "." && prev[j+1] == "^" || \ 12 | prev[j+1] == "." && prev[j-1] == "^") 13 | { 14 | rows[i] = rows[i] "^" 15 | } 16 | else { 17 | rows[i] = rows[i] "." 18 | } 19 | } 20 | } 21 | 22 | for (i in rows) { 23 | split(rows[i], row, "") 24 | for (j in row) { 25 | if (row[j] == ".") 26 | ++safe 27 | } 28 | } 29 | print "Safe: " safe 30 | } 31 | -------------------------------------------------------------------------------- /2017/day05/day05a.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "log" 7 | "os" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | func main() { 13 | input, err := ioutil.ReadFile(os.Args[1]) 14 | if err != nil { 15 | log.Fatal(err) 16 | } 17 | 18 | lines := strings.Split(string(input), "\n") 19 | // Drop last element (is empty) 20 | lines = lines[:len(lines)-1] 21 | 22 | // Convert to integer slice 23 | offsets := make([]int, len(lines)) 24 | for i, v := range lines { 25 | offsets[i], err = strconv.Atoi(v) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | } 30 | 31 | ctr := 0 32 | 33 | for idx := 0; idx >= 0 && idx < len(offsets); { 34 | next := idx + offsets[idx] 35 | offsets[idx]++ 36 | ctr++ 37 | idx = next 38 | } 39 | 40 | fmt.Println(ctr) 41 | } 42 | -------------------------------------------------------------------------------- /2023/day11/day11a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | def dist(a, b) 4 | (a.first - b.first).abs + (a.last - b.last).abs 5 | end 6 | 7 | file = File.open(ARGV[0]) 8 | 9 | image = [] 10 | galaxy_offsets = Set.new 11 | 12 | file.readlines.map(&:chomp).each do |line| 13 | image << line 14 | image << line unless line.match?(/#/) 15 | 16 | line.chars.each_with_index { |c, idx| galaxy_offsets.add(idx) if c == "#" } 17 | end 18 | 19 | image.map! do |line| 20 | line.chars.map!.with_index { |c, idx| galaxy_offsets.include?(idx) ? c : ".." }.join 21 | end 22 | 23 | coords = image.each_with_object([]).with_index do |(line, locs), y| 24 | line.chars.each_with_index do |c, x| 25 | locs << [x, y] if c == "#" 26 | end 27 | 28 | locs 29 | end 30 | 31 | puts coords.combination(2).sum { |a, b| dist(a, b) } 32 | -------------------------------------------------------------------------------- /2021/day03/day03b.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import process from "process"; 4 | import * as fs from "fs"; 5 | 6 | function getRating(report, selector) { 7 | for (let idx = 0; report.length > 1; ++idx) { 8 | let count = report.reduce((prev, curr) => { 9 | ++prev[curr[idx]]; 10 | return prev; 11 | }, [0, 0]); 12 | 13 | report = report.filter(el => el[idx] == selector(count)); 14 | } 15 | 16 | return report[0].join(""); 17 | } 18 | 19 | let report = fs.readFileSync(process.argv[2], "utf8") 20 | .trim() 21 | .split("\n") 22 | .map(el => el.split("").map(Number)); 23 | 24 | let oxygen = getRating(Array.from(report), ([zero, one]) => zero <= one ? 1 : 0); 25 | let co2 = getRating(Array.from(report), ([zero, one]) => zero <= one ? 0 : 1); 26 | 27 | console.log(parseInt(oxygen, 2) * parseInt(co2, 2)); 28 | -------------------------------------------------------------------------------- /2024/map.rb: -------------------------------------------------------------------------------- 1 | Point = Struct.new(:x, :y) do 2 | def to_s = "#{x}/#{y}" 3 | 4 | def +(other) = Point.new(x + other.x, y + other.y) 5 | end 6 | 7 | class Map 8 | attr_reader :rows 9 | 10 | def initialize(rows) 11 | @rows = rows 12 | @y_range = (0...@rows.length) 13 | @x_range = (0...@rows[0].length) 14 | end 15 | 16 | def include?(p) = @y_range.cover?(p.y) && @x_range.cover?(p.x) 17 | 18 | def at(p) 19 | return nil unless include?(p) 20 | @rows.dig(p.y, p.x) 21 | end 22 | 23 | def [](p) = at(p) 24 | 25 | def to_s = @rows.map(&:join).join("\n") 26 | 27 | def find_point(val) 28 | y = @rows.find_index { _1.include?(val) } 29 | x = @rows[y].find_index(val) 30 | Point.new(x, y) 31 | end 32 | 33 | def []=(p, val) 34 | @rows[p.y][p.x] = val 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /2020/day12/day12a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | function abs(n) { 4 | return n > 0 ? n : -n 5 | } 6 | 7 | BEGIN { 8 | dx = 1 9 | dy = 0 10 | } 11 | 12 | /^N/ { y += substr($1, 2) } 13 | /^S/ { y -= substr($1, 2) } 14 | /^E/ { x += substr($1, 2) } 15 | /^W/ { x -= substr($1, 2) } 16 | 17 | /^F/ { 18 | x += dx * substr($1, 2) 19 | y += dy * substr($1, 2) 20 | } 21 | 22 | /^[LR]/ { 23 | dir = substr($1, 1, 1) 24 | deg = substr($1, 2) 25 | 26 | if (deg == 180) { 27 | dx *= -1 28 | dy *= -1 29 | next 30 | } 31 | 32 | if (deg == 90 && dir == "R" || deg == 270 && dir == "L") { 33 | dx_new = dy 34 | dy = -dx 35 | dx = dx_new 36 | } 37 | else if (deg == 270 && dir == "R" || deg == 90 && dir == "L") { 38 | dx_new = -dy 39 | dy = dx 40 | dx = dx_new 41 | } 42 | } 43 | 44 | END { print abs(x) + abs(y) } 45 | -------------------------------------------------------------------------------- /2016/day18/day18a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | BEGIN { PROCINFO["sorted_in"] = "@ind_num_asc" } 4 | 5 | { rows[1] = $0 } 6 | 7 | END { 8 | for (i = 2; i <= 40; ++i) { 9 | split("." rows[i-1] ".", prev, "") 10 | for (j = 2; j <= length(prev) - 1; ++j) { 11 | if (prev[j-1] == "." && prev[j+1] == "^" || \ 12 | prev[j+1] == "." && prev[j-1] == "^") 13 | { 14 | rows[i] = rows[i] "^" 15 | } 16 | else { 17 | rows[i] = rows[i] "." 18 | } 19 | } 20 | } 21 | 22 | for (i in rows) { 23 | print rows[i] 24 | split(rows[i], row, "") 25 | for (j in row) { 26 | if (row[j] == ".") 27 | ++safe 28 | } 29 | } 30 | print "Safe: " safe 31 | } 32 | -------------------------------------------------------------------------------- /2017/day09/day09a.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "log" 7 | "os" 8 | "regexp" 9 | "strings" 10 | ) 11 | 12 | func main() { 13 | if len(os.Args) == 1 { 14 | log.Fatal("Filename not specified") 15 | } 16 | input, err := ioutil.ReadFile(os.Args[1]) 17 | if err != nil { 18 | log.Fatal(err) 19 | } 20 | 21 | stream := strings.TrimRight(string(input), "\n") 22 | 23 | regexps := []*regexp.Regexp{ 24 | regexp.MustCompile("!."), 25 | regexp.MustCompile("<.*?>"), 26 | } 27 | for _, re := range regexps { 28 | stream = re.ReplaceAllLiteralString(stream, "") 29 | } 30 | 31 | total, score := 0, 1 32 | for _, ch := range stream { 33 | switch ch { 34 | case '{': 35 | total += score 36 | score++ 37 | case '}': 38 | score-- 39 | } 40 | } 41 | fmt.Println(total) 42 | } 43 | -------------------------------------------------------------------------------- /2017/day11/day11a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | use List::MoreUtils qw(pairwise); 9 | use List::Util qw(sum); 10 | 11 | my $fname = shift; 12 | 13 | open my $fh, "<", $fname 14 | or die "Can't open $fname: $!"; 15 | 16 | my $line = <$fh>; 17 | chomp $line; 18 | my @dirs = split /,/, $line; 19 | 20 | my @coords = (0, 0, 0); 21 | my $dist = 0; 22 | 23 | foreach my $dir (@dirs) { 24 | my @vec = (0, 0, 0); 25 | $vec[0] = 1 if $dir =~ /e/; 26 | $vec[0] = -1 if $dir =~ /w/; 27 | $vec[1] = 1 if $dir =~ /\Anw?\z/; 28 | $vec[1] = -1 if $dir =~ /\Ase?\z/; 29 | $vec[2] = 1 if $dir =~ /\Asw?\z/; 30 | $vec[2] = -1 if $dir =~ /\Ane?\z/; 31 | @coords = pairwise { $a + $b } @coords, @vec; 32 | } 33 | 34 | say 0.5 * sum map { abs } @coords; 35 | -------------------------------------------------------------------------------- /2019/day01/second/day01b.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | 7 | "github.com/bewuethr/advent-of-code/go/ioutil" 8 | "github.com/bewuethr/advent-of-code/go/log" 9 | ) 10 | 11 | func main() { 12 | scanner, err := ioutil.GetInputScanner() 13 | if err != nil { 14 | log.Die("getting scanner", err) 15 | } 16 | 17 | fuel := 0 18 | for scanner.Scan() { 19 | module, err := strconv.Atoi(scanner.Text()) 20 | if err != nil { 21 | log.Die("converting to int", err) 22 | } 23 | 24 | fuel += getFuel(module) 25 | } 26 | if err := scanner.Err(); err != nil { 27 | log.Die("reading input", err) 28 | } 29 | 30 | fmt.Println(fuel) 31 | } 32 | 33 | func getFuel(module int) int { 34 | fuel := module/3 - 2 35 | if fuel <= 0 { 36 | return 0 37 | } 38 | 39 | return fuel + getFuel(fuel) 40 | } 41 | -------------------------------------------------------------------------------- /2020/day12/day12b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | function abs(n) { 4 | return n > 0 ? n : -n 5 | } 6 | 7 | BEGIN { 8 | dx = 10 9 | dy = 1 10 | } 11 | 12 | /^N/ { dy += substr($1, 2) } 13 | /^S/ { dy -= substr($1, 2) } 14 | /^E/ { dx += substr($1, 2) } 15 | /^W/ { dx -= substr($1, 2) } 16 | 17 | /^F/ { 18 | x += dx * substr($1, 2) 19 | y += dy * substr($1, 2) 20 | } 21 | 22 | /^[LR]/ { 23 | dir = substr($1, 1, 1) 24 | deg = substr($1, 2) 25 | 26 | if (deg == 180) { 27 | dx *= -1 28 | dy *= -1 29 | next 30 | } 31 | 32 | if (deg == 90 && dir == "R" || deg == 270 && dir == "L") { 33 | dx_new = dy 34 | dy = -dx 35 | dx = dx_new 36 | } 37 | else if (deg == 270 && dir == "R" || deg == 90 && dir == "L") { 38 | dx_new = -dy 39 | dy = dx 40 | dx = dx_new 41 | } 42 | } 43 | 44 | END { print abs(x) + abs(y) } 45 | -------------------------------------------------------------------------------- /go/convert/convert.go: -------------------------------------------------------------------------------- 1 | // Package convert provides conversion helpers. 2 | package convert 3 | 4 | import "strconv" 5 | 6 | // StrSliceToInt converts a slice of strings to ints; if any of the strings 7 | // cannot be converted to an int, it returns an error. 8 | func StrSliceToInt(strSlice []string) ([]int, error) { 9 | intSlice := make([]int, len(strSlice)) 10 | for i, s := range strSlice { 11 | val, err := strconv.Atoi(s) 12 | if err != nil { 13 | return nil, err 14 | } 15 | intSlice[i] = val 16 | } 17 | 18 | return intSlice, nil 19 | } 20 | 21 | // IntSliceToStr converts a slice of ints to strings. 22 | func IntSliceToStr(intSlice []int) []string { 23 | strSlice := make([]string, len(intSlice)) 24 | for i, n := range intSlice { 25 | strSlice[i] = strconv.Itoa(n) 26 | } 27 | 28 | return strSlice 29 | } 30 | -------------------------------------------------------------------------------- /2017/day06/day06a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | use List::MoreUtils qw(firstidx); 9 | use List::Util qw(max); 10 | 11 | my $fname = shift; 12 | 13 | open my $fh, "<", $fname 14 | or die "Can't open $fname: $!"; 15 | 16 | my $line = <$fh>; 17 | chomp $line; 18 | my @arr = split "\t", $line; 19 | 20 | my %seen; 21 | my $ctr = 0; 22 | 23 | $seen{"@arr"} = 1; 24 | 25 | while (1) { 26 | my $idx_max = firstidx { $_ == max @arr } @arr; 27 | my $tomove = $arr[$idx_max]; 28 | $arr[$idx_max] = 0; 29 | my $cur_idx = ($idx_max + 1) % @arr; 30 | while ($tomove--) { 31 | $arr[$cur_idx]++; 32 | $cur_idx = ($cur_idx + 1) % @arr; 33 | } 34 | $ctr++; 35 | last if $seen{"@arr"}; 36 | $seen{"@arr"} = 1; 37 | } 38 | 39 | say $ctr; 40 | -------------------------------------------------------------------------------- /2018/day08/day08a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | use List::Util qw(sum); 9 | 10 | sub makenode { 11 | my ($arr, $idx, $metasum) = @_; 12 | my ($childCount, $metaCount) = ($arr->[$$idx], $arr->[$$idx+1]); 13 | $$idx += 2; 14 | my $node = {}; 15 | push @{ $node->{children} }, makenode($arr, $idx, $metasum) while $childCount--; 16 | push @{ $node->{meta} }, $arr->[$$idx++] while $metaCount--; 17 | $$metasum += sum @{ $node->{meta} }; 18 | return $node; 19 | } 20 | 21 | my $fname = shift; 22 | 23 | open my $fh, "<", $fname 24 | or die "Can't open $fname: $!"; 25 | 26 | my $line = <$fh>; 27 | chomp $line; 28 | my @arr = split / /, $line; 29 | 30 | my $idx = 0; 31 | my $metasum = 0; 32 | 33 | my $tree = makenode(\@arr, \$idx, \$metasum); 34 | 35 | say $metasum; 36 | -------------------------------------------------------------------------------- /2015/day02/day02b: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | debug=0 4 | 5 | min_circumf () { 6 | local a=$1 7 | local b=$2 8 | local c=$3 9 | local min=$(( 2*a + 2*b )) 10 | min=$(( 2*a + 2*c < min ? 2*a + 2*c : min )) 11 | min=$(( 2*b + 2*c < min ? 2*b + 2*c : min )) 12 | (( debug )) && echo "Minimum: $min" >&2 13 | echo "$min" 14 | } 15 | 16 | regex='([[:digit:]]+)x([[:digit:]]+)x([[:digit:]]+)' 17 | total=0 18 | 19 | while read -r line; do 20 | (( debug )) && echo "Line: $line" >&2 21 | [[ "$line" =~ $regex ]] 22 | dim1=${BASH_REMATCH[1]} 23 | dim2=${BASH_REMATCH[2]} 24 | dim3=${BASH_REMATCH[3]} 25 | smallest=$(min_circumf $dim1 $dim2 $dim3) 26 | (( total += smallest + dim1 * dim2 * dim3 )) 27 | (( debug )) && echo "Sum: $total" >&2 28 | done < "$1" 29 | 30 | echo "Total ribbon needed: $total" 31 | -------------------------------------------------------------------------------- /2017/day09/day09b.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "log" 7 | "os" 8 | "regexp" 9 | "strings" 10 | ) 11 | 12 | func main() { 13 | if len(os.Args) == 1 { 14 | log.Fatal("Filename not specified") 15 | } 16 | input, err := ioutil.ReadFile(os.Args[1]) 17 | if err != nil { 18 | log.Fatal(err) 19 | } 20 | 21 | stream := strings.TrimRight(string(input), "\n") 22 | 23 | re1 := regexp.MustCompile("!.") 24 | re2 := regexp.MustCompile("<.*?>") 25 | 26 | // Remove escapes 27 | stream = re1.ReplaceAllLiteralString(stream, "") 28 | 29 | // Get all garbage sequences 30 | garbage := re2.FindAllString(stream, -1) 31 | 32 | // Length includes two angle brackets each 33 | totalLen := 0 34 | for _, seq := range garbage { 35 | totalLen += len(seq) - 2 36 | } 37 | 38 | fmt.Println(totalLen) 39 | } 40 | -------------------------------------------------------------------------------- /2017/day07/day07a.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "log" 7 | "os" 8 | "regexp" 9 | ) 10 | 11 | func main() { 12 | f, err := os.Open(os.Args[1]) 13 | if err != nil { 14 | log.Fatal(err) 15 | } 16 | defer f.Close() 17 | 18 | scanner := bufio.NewScanner(f) 19 | 20 | re := regexp.MustCompile("[[:alpha:]]+") 21 | progCounter := make(map[string]int) 22 | 23 | for scanner.Scan() { 24 | programs := re.FindAllString(scanner.Text(), -1) 25 | for _, prog := range programs { 26 | progCounter[prog]++ 27 | } 28 | } 29 | if err := scanner.Err(); err != nil { 30 | fmt.Fprintln(os.Stderr, "Reading from file:", err) 31 | } 32 | 33 | var base string 34 | for prog, count := range progCounter { 35 | if count == 1 { 36 | base = prog 37 | break 38 | } 39 | } 40 | 41 | fmt.Println(base) 42 | } 43 | -------------------------------------------------------------------------------- /2015/day19/day19b: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | while IFS= read -r line; do 4 | if [[ -n $line ]]; then 5 | if [[ $line == *=\>* ]]; then 6 | replacements+=("$line") 7 | else 8 | mol="$line" 9 | fi 10 | fi 11 | done < "$1" 12 | 13 | steps=0 14 | 15 | re='([[:alnum:]]+) => ([[:alnum:]]+)' 16 | 17 | while [[ $mol != e ]]; do 18 | for replacement in "${replacements[@]}"; do 19 | [[ $replacement =~ $re ]] 20 | from="${BASH_REMATCH[2]}" 21 | to="${BASH_REMATCH[1]}" 22 | 23 | num_reps=$(grep -o "$from" <<< "$mol" | wc -l) 24 | if (( num_reps > 0 )); then 25 | mol=${mol//"$from"/$to} 26 | (( steps += num_reps )) 27 | grep "$to" <<< $mol 28 | fi 29 | done 30 | done 31 | 32 | echo "We reached 'e' after $steps steps." 33 | -------------------------------------------------------------------------------- /2016/day04/day04b_bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | re='([[:digit:]]+)\[(.*)\]' 4 | 5 | while read -r line; do 6 | [[ $line =~ $re ]] 7 | id=${BASH_REMATCH[1]} 8 | given_chksum=${BASH_REMATCH[2]} 9 | room_name=${line%-*} 10 | 11 | actual_chksum=$( 12 | tr -d '-' <<< "$room_name" | fold -w 1 | 13 | sort | uniq -c | sed 's/^ *//' | sort -t ' ' -k 1,1nr -k 2,2 | 14 | head -n 5 | tr -dc 'a-z' | paste -s -d '' 15 | ) 16 | 17 | if [[ $given_chksum == $actual_chksum ]]; then 18 | rot=$(( id % 26 )) 19 | for (( i = 0; i < rot; ++i )); do 20 | room_name=$(tr a-z b-za <<< "$room_name") 21 | done 22 | if [[ $room_name == *pole* ]]; then 23 | printf '%s has ID %d\n' "${room_name//-/ }" "$id" 24 | break 25 | fi 26 | fi 27 | done < "$1" 28 | -------------------------------------------------------------------------------- /2019/day06/first/day06a.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/bewuethr/advent-of-code/go/ioutil" 8 | "github.com/bewuethr/advent-of-code/go/log" 9 | ) 10 | 11 | var orbits = make(map[string][]string) 12 | 13 | func main() { 14 | scanner, err := ioutil.GetInputScanner() 15 | if err != nil { 16 | log.Die("getting scanner", err) 17 | } 18 | 19 | for scanner.Scan() { 20 | orbit := strings.Split(scanner.Text(), ")") 21 | orbits[orbit[0]] = append(orbits[orbit[0]], orbit[1]) 22 | } 23 | 24 | if err := scanner.Err(); err != nil { 25 | log.Die("reading input", err) 26 | } 27 | 28 | fmt.Println(traverse("COM", 0)) 29 | } 30 | 31 | func traverse(obj string, depth int) int { 32 | sum := depth 33 | for _, o := range orbits[obj] { 34 | sum += traverse(o, depth+1) 35 | } 36 | return sum 37 | } 38 | -------------------------------------------------------------------------------- /2015/day12/day12b: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Get parsed input, leaves only (requires JSON.sh) 4 | parsed=$(json_parse -l < input) 5 | 6 | # Get leafs with value red that are in an object, return the part before the 7 | # name of the object 8 | remove=$(grep -Po '(.*)(?="."][[:space:]]+"red")' <<< "$parsed") 9 | 10 | # Remove all the lines that are in the objects to be removed; use sed to escape 11 | # the unmatched square brackets 12 | filtered=$(grep -vf <(echo "$remove" | sed 's/\[/\\[/') <<< "$parsed") 13 | 14 | # Count sum of numbers after removing part in brackets 15 | sum=0 16 | re='(-?[[:digit:]]+)' 17 | while read -r line; do 18 | if [[ "$line" =~ $re ]]; then 19 | (( sum += ${BASH_REMATCH[1]} )) 20 | fi 21 | done < <(echo "$filtered" | sed 's/\[.*\][[:space:]]\+//') 22 | 23 | echo "Sum without objects that have a 'red' value: $sum" 24 | -------------------------------------------------------------------------------- /2017/day06/day06b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | use List::MoreUtils qw(firstidx); 9 | use List::Util qw(max); 10 | 11 | my $fname = shift; 12 | 13 | open my $fh, "<", $fname 14 | or die "Can't open $fname: $!"; 15 | 16 | my $line = <$fh>; 17 | chomp $line; 18 | my @arr = split "\t", $line; 19 | 20 | my %seen; 21 | my $ctr = 0; 22 | 23 | $seen{"@arr"} = $ctr; 24 | 25 | while (1) { 26 | my $idx_max = firstidx { $_ == max @arr } @arr; 27 | my $tomove = $arr[$idx_max]; 28 | $arr[$idx_max] = 0; 29 | my $cur_idx = ($idx_max + 1) % @arr; 30 | while ($tomove--) { 31 | $arr[$cur_idx]++; 32 | $cur_idx = ($cur_idx + 1) % @arr; 33 | } 34 | $ctr++; 35 | last if $seen{"@arr"}; 36 | $seen{"@arr"} = $ctr; 37 | } 38 | 39 | say $ctr - $seen{"@arr"}; 40 | -------------------------------------------------------------------------------- /2018/day03/day03b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | my %squares; 14 | 15 | while (my $line = <$fh>) { 16 | chomp $line; 17 | my (undef, $id, $dx, $dy, $w, $h) = split qr{[# @,:x]+}, $line; 18 | foreach my $y ($dy .. $dy+$h-1) { 19 | foreach my $x ($dx .. $dx+$w-1) { 20 | $squares{$x, $y}[0]++; 21 | push @{$squares{$x, $y}[1]}, $id; 22 | } 23 | } 24 | } 25 | 26 | my %singles; 27 | my %multiples; 28 | foreach my $idlist (values %squares) { 29 | if (@{$idlist->[1]} == 1) { 30 | $singles{$idlist->[1][0]} = 1; 31 | } 32 | else { 33 | foreach my $id (@{$idlist->[1]}) { 34 | $multiples{$id} = 1; 35 | } 36 | } 37 | } 38 | 39 | say grep { not exists $multiples{$_} } keys %singles; 40 | -------------------------------------------------------------------------------- /2020/day08/day08b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | function swap(op) { 4 | return op == "nop" ? "jmp" : "nop" 5 | } 6 | 7 | $1 == "nop" || $1 == "jmp" { nopsjmps[++njidx] = NR } 8 | 9 | { instr[NR] = $0 } 10 | 11 | END { 12 | for (chgidx in nopsjmps) { 13 | idx = 1 14 | accumulator = 0 15 | delete seen 16 | while (1) { 17 | if (seen[idx]++) { 18 | break 19 | } 20 | if (idx == NR + 1) { 21 | print accumulator 22 | exit 23 | } 24 | 25 | split(instr[idx], a) 26 | if (idx == nopsjmps[chgidx]) { 27 | a[1] = swap(a[1]) 28 | } 29 | 30 | if (a[1] == "acc") { 31 | accumulator += a[2] 32 | ++idx 33 | } 34 | else if (a[1] == "jmp") { 35 | idx += a[2] 36 | } 37 | else if (a[1] == "nop") { 38 | ++idx 39 | } 40 | else { 41 | print "unknown instruction", a[1] 42 | exit 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /2022/day10/day10b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | my $x = 1; 7 | my $cycle = 0; 8 | my $sum = 0; 9 | my $crtPos = 0; 10 | 11 | sub tick { 12 | ++$cycle; 13 | if ( abs( $x - $crtPos ) <= 1 ) { 14 | print "#"; 15 | } 16 | else { 17 | print "."; 18 | } 19 | 20 | $crtPos = ( $crtPos + 1 ) % 40; 21 | print "\n" if $crtPos == 0; 22 | } 23 | 24 | my $fname = shift; 25 | 26 | open my $fh, "<", $fname 27 | or die "Can't open $fname: $!"; 28 | 29 | while ( my $line = <$fh> ) { 30 | chomp $line; 31 | my ( $op, $n ) = split / /, $line; 32 | 33 | if ( $op eq "noop" ) { 34 | tick(); 35 | } 36 | elsif ( $op eq "addx" ) { 37 | tick(); 38 | tick(); 39 | $x += $n; 40 | } 41 | else { 42 | die "invalid operation $op"; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /2017/day05/day05b.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "log" 7 | "os" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | func main() { 13 | input, err := ioutil.ReadFile(os.Args[1]) 14 | if err != nil { 15 | log.Fatal(err) 16 | } 17 | 18 | lines := strings.Split(string(input), "\n") 19 | // Drop last element (is empty) 20 | lines = lines[:len(lines)-1] 21 | 22 | // Convert to integer slice 23 | offsets := make([]int, len(lines)) 24 | for i, v := range lines { 25 | offsets[i], err = strconv.Atoi(v) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | } 30 | 31 | ctr := 0 32 | 33 | for idx := 0; idx >= 0 && idx < len(offsets); { 34 | next := idx + offsets[idx] 35 | if offsets[idx] >= 3 { 36 | offsets[idx]-- 37 | } else { 38 | offsets[idx]++ 39 | } 40 | ctr++ 41 | idx = next 42 | } 43 | 44 | fmt.Println(ctr) 45 | } 46 | -------------------------------------------------------------------------------- /2017/day11/day11b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | use List::MoreUtils qw(pairwise); 9 | use List::Util qw(max sum); 10 | 11 | my $fname = shift; 12 | 13 | open my $fh, "<", $fname 14 | or die "Can't open $fname: $!"; 15 | 16 | my $line = <$fh>; 17 | chomp $line; 18 | my @dirs = split /,/, $line; 19 | 20 | my @coords = (0, 0, 0); 21 | my $maxdist = 0; 22 | 23 | foreach my $dir (@dirs) { 24 | my @vec = (0, 0, 0); 25 | $vec[0] = 1 if $dir =~ /e/; 26 | $vec[0] = -1 if $dir =~ /w/; 27 | $vec[1] = 1 if $dir =~ /\Anw?\z/; 28 | $vec[1] = -1 if $dir =~ /\Ase?\z/; 29 | $vec[2] = 1 if $dir =~ /\Asw?\z/; 30 | $vec[2] = -1 if $dir =~ /\Ane?\z/; 31 | @coords = pairwise { $a + $b } @coords, @vec; 32 | $maxdist = max ((0.5 * sum map { abs } @coords), $maxdist); 33 | } 34 | 35 | say $maxdist; 36 | -------------------------------------------------------------------------------- /2021/day03/awk/day03b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | chomp( my @nums = <$fh> ); 14 | 15 | my @oxy = grep { 1 } @nums; 16 | my $idx = 0; 17 | while ( @oxy > 1 ) { 18 | my $ones = grep { substr( $_, $idx, 1 ) == 1 } @oxy; 19 | my $mostCommon = $ones >= @oxy / 2 ? 1 : 0; 20 | @oxy = grep { substr( $_, $idx, 1 ) == $mostCommon } @oxy; 21 | ++$idx; 22 | } 23 | 24 | my @co2 = grep { 1 } @nums; 25 | $idx = 0; 26 | while ( @co2 > 1 ) { 27 | my $zeroes = grep { substr( $_, $idx, 1 ) == 0 } @co2; 28 | my $leastCommon = $zeroes <= @co2 / 2 ? 0 : 1; 29 | @co2 = grep { substr( $_, $idx, 1 ) == $leastCommon } @co2; 30 | ++$idx; 31 | } 32 | 33 | exec "echo 'ibase = 2; $oxy[0] * $co2[0]' | bc"; 34 | -------------------------------------------------------------------------------- /2021/day06/day06b.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import process from "process"; 4 | import * as fs from "fs"; 5 | 6 | function update(state) { 7 | let newState = Array(state.length).fill(0); 8 | 9 | let zeroes = state[0]; 10 | for (let i = 0; i < 8; ++i) { 11 | newState[i] = state[i + 1]; 12 | } 13 | 14 | newState[6] += zeroes; 15 | newState[8] += zeroes; 16 | 17 | return newState; 18 | } 19 | 20 | let state = fs.readFileSync(process.argv[2], "utf8") 21 | .trim() 22 | .split(",") 23 | .reduce((prev, curr) => { 24 | prev[curr] = prev[curr] ? prev[curr] + 1 : 1; 25 | return prev; 26 | }, []); 27 | 28 | for (let i = 0; i <= 8; ++i) { 29 | state[i] = state[i] ? state[i] : 0; 30 | } 31 | 32 | const days = 256; 33 | 34 | for (let i = 0; i < days; ++i) { 35 | state = update(state); 36 | } 37 | 38 | console.log(Object.values(state).reduce((prev, curr) => prev + curr)); 39 | -------------------------------------------------------------------------------- /2024/day14/day14b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | WIDTH = 101 4 | HEIGHT = 103 5 | 6 | Point = Struct.new(:x, :y) do 7 | def to_s = "#{x}/#{y}" 8 | 9 | def +(other) = Point.new((x + other.x) % WIDTH, (y + other.y) % HEIGHT) 10 | end 11 | 12 | def check(robots) 13 | robo_map = (0...HEIGHT).map { "." * WIDTH } 14 | 15 | robots.each do |robot| 16 | p = robot[:p] 17 | robo_map[p.y][p.x] = "X" 18 | end 19 | 20 | robo_map.any? { _1.include?("X" * (WIDTH / 10)) } 21 | end 22 | 23 | rows = File.readlines(ARGV[0]).map(&:chomp) 24 | 25 | robots = rows.map do |row| 26 | x, y, dx, dy = row.scan(/-?\d+/).map(&:to_i) 27 | { 28 | p: Point.new(x, y), 29 | v: Point.new(dx, dy) 30 | } 31 | end 32 | 33 | t = 0 34 | 35 | until check(robots) 36 | robots.map! do |robot| 37 | robot[:p] += robot[:v] 38 | robot 39 | end 40 | 41 | t += 1 42 | end 43 | 44 | puts t 45 | -------------------------------------------------------------------------------- /2023/day12/day12a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | def to_list(record) 4 | record.scan(/#+/).map(&:length).join(",") 5 | end 6 | 7 | def generate(record) 8 | locs = record.chars.each_with_object([]).with_index do |(c, locs), idx| 9 | locs << idx if c == "?" 10 | end 11 | 12 | len = locs.length 13 | (0...2**len).to_a.map do |n| 14 | arrangement = n.to_s(2).rjust(len, "0").tr("01", ".#") 15 | new_record = record.clone 16 | arrangement.chars.zip(locs).each { |spring, idx| new_record[idx] = spring } 17 | 18 | new_record 19 | end 20 | end 21 | 22 | file = File.open(ARGV[0]) 23 | 24 | entries = file.readlines.map do |line| 25 | (record, list) = line.split 26 | {record:, list:} 27 | end 28 | 29 | sum_counts = entries.sum do |entry| 30 | generate(entry[:record]).count do |record| 31 | to_list(record) == entry[:list] 32 | end 33 | end 34 | 35 | puts sum_counts 36 | -------------------------------------------------------------------------------- /2024/day08/day08a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | Vec2 = Struct.new(:x, :y) do 4 | def +(other) = Vec2.new(x + other.x, y + other.y) 5 | 6 | def -(other) = Vec2.new(x - other.x, y - other.y) 7 | end 8 | 9 | lines = File.readlines(ARGV[0]).map(&:chomp) 10 | 11 | y_range = (0...lines.length) 12 | x_range = (0...lines[0].length) 13 | 14 | antennas = Hash.new { |h, k| h[k] = [] } 15 | 16 | lines.each_with_index do |row, y| 17 | row.chars.each_with_index do |loc, x| 18 | next if loc == "." 19 | antennas[loc] << Vec2.new(x, y) 20 | end 21 | end 22 | 23 | antinodes = Set.new 24 | 25 | antennas.each_pair do |freq, coords| 26 | coords.combination(2) do |v1, v2| 27 | candidates = [v1 - (v2 - v1), v2 - (v1 - v2)] 28 | candidates.each do |v| 29 | antinodes.add(v) if y_range.include?(v.y) && x_range.include?(v.x) 30 | end 31 | end 32 | end 33 | 34 | puts antinodes.size 35 | -------------------------------------------------------------------------------- /2017/day04/day04b.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "log" 7 | "os" 8 | "sort" 9 | "strings" 10 | ) 11 | 12 | func main() { 13 | f, err := os.Open(os.Args[1]) 14 | if err != nil { 15 | log.Fatal(err) 16 | } 17 | defer f.Close() 18 | 19 | scanner := bufio.NewScanner(f) 20 | 21 | valid := 0 22 | 23 | Loop: 24 | for scanner.Scan() { 25 | words := strings.Split(scanner.Text(), " ") 26 | 27 | counts := make(map[string]int) 28 | for _, word := range words { 29 | runes := []rune(word) 30 | sort.Slice(runes, func(i, j int) bool { return runes[i] < runes[j] }) 31 | word = string(runes) 32 | counts[word]++ 33 | if counts[word] > 1 { 34 | continue Loop 35 | } 36 | } 37 | valid++ 38 | } 39 | 40 | if err := scanner.Err(); err != nil { 41 | fmt.Fprintln(os.Stderr, "Reading from file:", err) 42 | } 43 | 44 | fmt.Println(valid) 45 | } 46 | -------------------------------------------------------------------------------- /2022/day05/day05b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | my %stacks = ( 11 | 1 => [qw/B W N/], 12 | 2 => [qw/L Z S P T D M B/], 13 | 3 => [qw/Q H Z W R/], 14 | 4 => [qw/W D V J Z R/], 15 | 5 => [qw/S H M B/], 16 | 6 => [qw/L G N J H V P B/], 17 | 7 => [qw/J Q Z F H D L S/], 18 | 8 => [qw/W S F J G Q B/], 19 | 9 => [qw/Z W M S C D J/] 20 | ); 21 | 22 | open my $fh, "<", $fname 23 | or die "Can't open $fname: $!"; 24 | 25 | while ( my $line = <$fh> ) { 26 | chomp $line; 27 | next if $line !~ /^move/; 28 | my ( $n, $from, $to ) = $line =~ /\d+/g; 29 | 30 | push @{ $stacks{$to} }, splice( @{ $stacks{$from} }, -$n ); 31 | } 32 | 33 | my $crates; 34 | foreach my $key ( sort { $a <=> $b } keys %stacks ) { 35 | $crates .= pop @{ $stacks{$key} }; 36 | } 37 | 38 | say "$crates"; 39 | -------------------------------------------------------------------------------- /2023/day15/day15b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | def hash_func(str) 4 | str.chars.reduce(0) do |total, c| 5 | total += c.ord 6 | total *= 17 7 | total % 256 8 | end 9 | end 10 | 11 | file = File.open(ARGV[0]) 12 | 13 | boxes = Array.new(256) { [] } 14 | 15 | file.gets.chomp.split(",").each do |step| 16 | label, op, foc_len = step.split(/([=-])/) 17 | foc_len = foc_len.to_i 18 | box_num = hash_func(label) 19 | idx = boxes[box_num].find_index { |l| l[0] == label } 20 | 21 | if op == "-" 22 | next unless idx 23 | boxes[box_num].delete_at(idx) 24 | elsif idx 25 | boxes[box_num][idx] = [label, foc_len] 26 | else 27 | boxes[box_num].push([label, foc_len]) 28 | end 29 | end 30 | 31 | s = boxes.each_with_index.sum do |lenses, box_idx| 32 | lenses.each_with_index.sum do |lens, lens_idx| 33 | (1 + box_idx) * (lens_idx + 1) * lens[1] 34 | end 35 | end 36 | 37 | puts s 38 | -------------------------------------------------------------------------------- /2016/day12/day12a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | { instr[NR] = $0 } 4 | 5 | END { 6 | i = 1 7 | while (1) { 8 | if (!(i in instr)) 9 | break 10 | split(instr[i], arr) 11 | 12 | if (instr[i] ~ /cpy/) { 13 | regs[arr[3]] = arr[2] ~ /[abcd]/ ? regs[arr[2]] : arr[2] 14 | ++i 15 | } 16 | 17 | else if (instr[i] ~ /inc/) { 18 | ++regs[arr[2]] 19 | ++i 20 | } 21 | 22 | else if (instr[i] ~ /dec/) { 23 | --regs[arr[2]] 24 | ++i 25 | } 26 | 27 | else if (instr[i] ~ /jnz/) { 28 | if ((arr[2] ~ /[abcd]/ && regs[arr[2]] != 0) || \ 29 | (arr[2] ~ /[0-9]/ && arr[2] != 0)) 30 | { 31 | i += arr[3] 32 | } 33 | else 34 | ++i 35 | } 36 | } 37 | print regs["a"] 38 | } 39 | -------------------------------------------------------------------------------- /2024/day13/day13b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | COST_A = 3 4 | COST_B = 1 5 | 6 | Point = Struct.new(:x, :y) 7 | 8 | def cost(n_a, n_b) = COST_A * n_a + COST_B * n_b 9 | 10 | def solve(machine) 11 | a = machine[:a].x 12 | b = machine[:b].x 13 | t_x = machine[:target].x 14 | c = machine[:a].y 15 | d = machine[:b].y 16 | t_y = machine[:target].y 17 | 18 | n_b = (a * t_y - c * t_x).div(a * d - b * c) 19 | n_a = (t_x - b * n_b).div(a) 20 | 21 | return 0 unless n_a * a + n_b * b == t_x && n_a * c + n_b * d == t_y 22 | cost(n_a, n_b) 23 | end 24 | 25 | input = File.readlines(ARGV[0]).map(&:chomp) 26 | 27 | machines = input.each_slice(4).map do |a, b, prize, _| 28 | { 29 | a: Point.new(*a.scan(/\d+/).map(&:to_i)), 30 | b: Point.new(*b.scan(/\d+/).map(&:to_i)), 31 | target: Point.new(*prize.scan(/\d+/).map { _1.to_i + 1_000_0000_000_000 }) 32 | } 33 | end 34 | 35 | puts machines.sum { solve(_1) } 36 | -------------------------------------------------------------------------------- /2022/day05/day05a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | my $fname = shift; 8 | 9 | my %stacks = ( 10 | 1 => [qw/B W N/], 11 | 2 => [qw/L Z S P T D M B/], 12 | 3 => [qw/Q H Z W R/], 13 | 4 => [qw/W D V J Z R/], 14 | 5 => [qw/S H M B/], 15 | 6 => [qw/L G N J H V P B/], 16 | 7 => [qw/J Q Z F H D L S/], 17 | 8 => [qw/W S F J G Q B/], 18 | 9 => [qw/Z W M S C D J/] 19 | ); 20 | open my $fh, "<", $fname 21 | or die "Can't open $fname: $!"; 22 | 23 | while ( my $line = <$fh> ) { 24 | chomp $line; 25 | next if $line !~ /^move/; 26 | my ( $n, $from, $to ) = $line =~ /\d+/g; 27 | 28 | while ( $n-- ) { 29 | push @{ $stacks{$to} }, pop @{ $stacks{$from} }; 30 | } 31 | } 32 | 33 | my $crates = ""; 34 | foreach my $key ( sort { $a <=> $b } keys %stacks ) { 35 | $crates .= pop @{ $stacks{$key} }; 36 | } 37 | 38 | say "$crates"; 39 | -------------------------------------------------------------------------------- /2024/day05/day05b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | def is_correct?(list, rules) 4 | list.each_with_index.all? do |page, idx| 5 | list[idx + 1..].all? { rules.include?([page, _1]) || !rules.include?([_1, page]) } 6 | end 7 | end 8 | 9 | def swap(list, idx_a, idx_b) 10 | list[idx_a], list[idx_b] = list[idx_b], list[idx_a] 11 | end 12 | 13 | lines = File.readlines(ARGV[0]).map(&:chomp) 14 | blank_idx = lines.index("") 15 | 16 | rules = lines[...blank_idx].map { _1.split("|").map(&:to_i) }.to_set 17 | pages = lines[blank_idx + 1..].map { _1.split(",").map(&:to_i) } 18 | 19 | incorrect_pages = pages.reject { is_correct?(_1, rules) } 20 | 21 | total = incorrect_pages.sum do |list| 22 | until is_correct?(list, rules) 23 | (0...list.length).to_a.combination(2).each do |a, b| 24 | swap(list, a, b) if rules.include?([list[b], list[a]]) 25 | end 26 | end 27 | 28 | list[list.length / 2] 29 | end 30 | 31 | puts total 32 | -------------------------------------------------------------------------------- /2024/day09/day09a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | disk_map = File.read(ARGV[0]).strip 4 | 5 | expanded = [] 6 | id = 0 7 | 8 | disk_map.chars.map(&:to_i).each_with_index do |n, idx| 9 | if idx.even? 10 | expanded.concat([id] * n) 11 | id += 1 12 | else 13 | expanded.concat(["."] * n) 14 | end 15 | end 16 | 17 | empty_slots = expanded.each_with_index 18 | .select { |block, _| block == "." } 19 | .map { |_, idx| idx } 20 | 21 | block_slots = expanded.each_with_index 22 | .reject { |block, _| block == "." } 23 | .map { |_, idx| idx } 24 | .reverse 25 | 26 | empty_slots.each_with_index do |expanded_idx, idx| 27 | break if expanded_idx > block_slots[idx] 28 | expanded[expanded_idx], expanded[block_slots[idx]] = expanded[block_slots[idx]], expanded[expanded_idx] 29 | end 30 | 31 | checksum = 0 32 | 33 | expanded.each_with_index do |n, idx| 34 | checksum += n * idx unless n == "." 35 | end 36 | 37 | puts checksum 38 | -------------------------------------------------------------------------------- /2016/day01/day01b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | BEGIN { 4 | FS = ", " 5 | dx[1] = 0; dx[2] = 1; dx[3] = 0; dx[4] = -1 6 | dy[1] = 1; dy[2] = 0; dy[3] = -1; dy[4] = 0 7 | dir_index = 1 8 | x = 0 9 | y = 0 10 | } 11 | 12 | { 13 | for (i = 1; i <= NF; ++i) { 14 | dir = substr($i, 1, 1) 15 | dist = substr($i, 2) + 0 16 | 17 | if (dir == "R") 18 | ++dir_index 19 | else 20 | --dir_index 21 | 22 | if (dir_index == 0) 23 | dir_index = 4 24 | else if (dir_index == 5) 25 | dir_index = 1 26 | 27 | 28 | for (j = 1; j <= dist; ++j) { 29 | x += dx[dir_index] 30 | y += dy[dir_index] 31 | if (visited[x, y]) 32 | next 33 | ++visited[x, y] 34 | } 35 | } 36 | } 37 | 38 | END { 39 | x = x < 0 ? -x : x 40 | y = y < 0 ? -y : y 41 | print x + y 42 | } 43 | -------------------------------------------------------------------------------- /2016/day12/day12b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | { 4 | instr[NR] = $0 5 | regs["c"] = 1 6 | } 7 | 8 | END { 9 | i = 1 10 | while (1) { 11 | if (!(i in instr)) 12 | break 13 | split(instr[i], arr) 14 | 15 | if (instr[i] ~ /cpy/) { 16 | regs[arr[3]] = arr[2] ~ /[abcd]/ ? regs[arr[2]] : arr[2] 17 | ++i 18 | } 19 | 20 | else if (instr[i] ~ /inc/) { 21 | ++regs[arr[2]] 22 | ++i 23 | } 24 | 25 | else if (instr[i] ~ /dec/) { 26 | --regs[arr[2]] 27 | ++i 28 | } 29 | 30 | else if (instr[i] ~ /jnz/) { 31 | if ((arr[2] ~ /[abcd]/ && regs[arr[2]] != 0) || \ 32 | (arr[2] ~ /[0-9]/ && arr[2] != 0)) 33 | { 34 | i += arr[3] 35 | } 36 | else 37 | ++i 38 | } 39 | } 40 | print regs["a"] 41 | } 42 | -------------------------------------------------------------------------------- /2017/day02/day02a.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | func main() { 12 | f, err := os.Open(os.Args[1]) 13 | if err != nil { 14 | panic(err) 15 | } 16 | defer f.Close() 17 | 18 | scanner := bufio.NewScanner(f) 19 | 20 | checksum := 0 21 | 22 | for scanner.Scan() { 23 | numsAsStrings := strings.Split(scanner.Text(), "\t") 24 | nums := make([]int, len(numsAsStrings)) 25 | for i, v := range numsAsStrings { 26 | nums[i], err = strconv.Atoi(v) 27 | if err != nil { 28 | panic(err) 29 | } 30 | } 31 | 32 | min, max := nums[0], nums[0] 33 | for _, v := range nums { 34 | if v < min { 35 | min = v 36 | } 37 | if v > max { 38 | max = v 39 | } 40 | } 41 | checksum += max - min 42 | } 43 | if err := scanner.Err(); err != nil { 44 | fmt.Fprintln(os.Stderr, "Reading from file:", err) 45 | } 46 | 47 | fmt.Println(checksum) 48 | } 49 | -------------------------------------------------------------------------------- /2024/day14/day14a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | WIDTH = 101 4 | HEIGHT = 103 5 | 6 | Point = Struct.new(:x, :y) do 7 | def to_s = "#{x}/#{y}" 8 | 9 | def +(other) = Point.new((x + other.x) % WIDTH, (y + other.y) % HEIGHT) 10 | end 11 | 12 | rows = File.readlines(ARGV[0]).map(&:chomp) 13 | 14 | robots = rows.map do |row| 15 | x, y, dx, dy = row.scan(/-?\d+/).map(&:to_i) 16 | { 17 | p: Point.new(x, y), 18 | v: Point.new(dx, dy) 19 | } 20 | end 21 | 22 | 100.times do 23 | robots.map! do |robot| 24 | robot[:p] += robot[:v] 25 | robot 26 | end 27 | end 28 | 29 | quadrants = robots.map do |robot| 30 | p = robot[:p] 31 | next if p.x == WIDTH / 2 || p.y == HEIGHT / 2 32 | 33 | if p.y < HEIGHT / 2 34 | if p.x < WIDTH / 2 35 | "tl" 36 | else 37 | "tr" 38 | end 39 | elsif p.x < WIDTH / 2 40 | "bl" 41 | else 42 | "br" 43 | end 44 | end.compact 45 | 46 | puts quadrants.tally.values.reduce(&:*) 47 | -------------------------------------------------------------------------------- /2021/day17/day17b.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import process from "process"; 4 | import * as fs from "fs"; 5 | 6 | function update({x, y, dx, dy}) { 7 | return { 8 | x: x + dx, 9 | y: y + dy, 10 | dx: dx > 0 ? dx - 1 : 11 | dx < 0 ? dx = 1 : 0, 12 | dy: dy -1 13 | }; 14 | } 15 | 16 | function inTarget({x, y}) { 17 | return x >= xMin && x <= xMax && y >= yMin && y <= yMax; 18 | } 19 | 20 | let [xMin, xMax, yMin, yMax] = [ 21 | ...fs.readFileSync(process.argv[2], "utf8") 22 | .trim() 23 | .matchAll(/-?\d+/g) 24 | ].map(e => Number(e[0])); 25 | 26 | let count = 0; 27 | 28 | for (let dy = yMin; dy <= Math.abs(yMin - 1); ++dy) { 29 | for (let dx = 0; dx <= xMax; ++dx) { 30 | let proj = {x: 0, y: 0, dx, dy}; 31 | for (;;) { 32 | proj = update(proj); 33 | if (proj.x > xMax || proj.y < yMin) break; 34 | 35 | if (inTarget(proj)) { 36 | ++count; 37 | break; 38 | } 39 | } 40 | } 41 | } 42 | 43 | console.log(count); 44 | -------------------------------------------------------------------------------- /2017/day12/day12b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | use List::MoreUtils qw(firstval); 9 | 10 | sub visit { 11 | my ($con, $seen, $el) = @_; 12 | 13 | foreach my $val ( @{ $con->{$el} } ) { 14 | if (not $seen->{$val}++) { 15 | visit($con, $seen, $val); 16 | } 17 | } 18 | return; 19 | } 20 | 21 | my $fname = shift; 22 | 23 | open my $fh, "<", $fname 24 | or die "Can't open $fname: $!"; 25 | 26 | my %con; 27 | my $progs = 0; 28 | 29 | while (my $line = <$fh>) { 30 | chomp $line; 31 | my @arr = split /[ <>,-]+/, $line; 32 | $con{$arr[0]} = [ @arr[1 .. $#arr] ]; 33 | $progs++; 34 | } 35 | 36 | my %seen; 37 | my $groups = 0; 38 | 39 | while (scalar keys %seen < $progs) { 40 | my $el = firstval { not $seen{$_} } ( 0 .. $progs-1 ); 41 | $seen{$el}++; 42 | visit(\%con, \%seen, $el); 43 | $groups++; 44 | } 45 | 46 | say $groups; 47 | -------------------------------------------------------------------------------- /2018/day05/day05a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | my $line = <$fh>; 14 | chomp $line; 15 | 16 | my @arr = split //, $line; 17 | my @new; 18 | 19 | my $before = @arr; 20 | 21 | while (1) { 22 | my ($cur, $next) = (0, 1); 23 | while (1) { 24 | if ($next > $#arr) { 25 | push @new, $arr[$cur]; 26 | last; 27 | } 28 | if ($arr[$cur] =~ /[[:lower:]]/ and $arr[$next] =~ /[[:upper:]]/ or 29 | $arr[$cur] =~ /[[:upper:]]/ and $arr[$next] =~ /[[:lower:]]/) { 30 | if (lc $arr[$cur] eq lc $arr[$next]) { 31 | $cur = $next + 1; 32 | $next = $cur + 1; 33 | next; 34 | } 35 | } 36 | push @new, $arr[$cur]; 37 | $cur++; 38 | $next++; 39 | } 40 | last if @new == $before; 41 | @arr = (); 42 | @arr = @new; 43 | @new = (); 44 | $before = @arr; 45 | } 46 | 47 | say scalar @new; 48 | -------------------------------------------------------------------------------- /2017/day13/day13a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | use List::Util qw(max); 9 | 10 | my $fname = shift; 11 | 12 | open my $fh, "<", $fname 13 | or die "Can't open $fname: $!"; 14 | 15 | my %fwall; 16 | 17 | while (my $line = <$fh>) { 18 | chomp $line; 19 | my ($depth, $range) = split /: /, $line; 20 | @{$fwall{$depth}}{ qw(range pos dir) } = ($range, 1, 1); 21 | } 22 | 23 | my $severity = 0; 24 | 25 | foreach my $t ( 0 .. max keys %fwall ) { 26 | if ($fwall{$t} and $fwall{$t}{pos} == 1) { 27 | $severity += $t * $fwall{$t}{range}; 28 | say "Busted at t = $t, total now $severity"; 29 | } 30 | foreach my $key (keys %fwall) { 31 | $fwall{$key}{pos} += $fwall{$key}{dir}; 32 | $fwall{$key}{dir} *= -1 if ( $fwall{$key}{pos} == $fwall{$key}{range} 33 | or $fwall{$key}{pos} == 1); 34 | } 35 | } 36 | 37 | say $severity; 38 | -------------------------------------------------------------------------------- /2021/day12/day12a.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import process from "process"; 4 | import * as fs from "fs"; 5 | 6 | function dfs(graph, seen, path, node) { 7 | if (node == "end") { 8 | ++count; 9 | return; 10 | } 11 | 12 | let newSeen = [...seen]; 13 | if (/^[a-z]+$/.test(node) && !newSeen.includes(node)) 14 | newSeen.push(node); 15 | for (let next of graph[node]) { 16 | if (!newSeen.includes(next)) dfs(graph, newSeen, [...path, node], next); 17 | } 18 | } 19 | 20 | let graph = Object.create(null); 21 | 22 | fs.readFileSync(process.argv[2], "utf8") 23 | .trim() 24 | .split("\n") 25 | .forEach(line => { 26 | function addEdge(from, to) { 27 | if (graph[from] == null) graph[from] = [to]; 28 | else graph[from].push(to); 29 | } 30 | 31 | let [from, to] = line.split("-"); 32 | addEdge(from, to); 33 | addEdge(to, from); 34 | }); 35 | 36 | let seen = [], path = [], count = 0; 37 | 38 | dfs(graph, seen, path, "start"); 39 | console.log(count); 40 | -------------------------------------------------------------------------------- /2016/day07/day07b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | function find_abas(abas, str, i) { 4 | for (i = 1; i <= length(str) - 2; ++i) { 5 | if ( \ 6 | substr(str, i, 1) == substr(str, i+2, 1) && \ 7 | substr(str, i, 1) != substr(str, i+1, 1) \ 8 | ) { 9 | ++abas[substr(str, i, 3)] 10 | } 11 | } 12 | return 0 13 | } 14 | 15 | BEGIN { FS = "[][]" } 16 | 17 | { 18 | delete abas 19 | delete babs 20 | 21 | for (i = 1; i <= NF; i += 2) 22 | find_abas(abas, $i) 23 | 24 | for (i = 2; i <= NF - 1; i += 2) 25 | find_abas(babs, $i) 26 | 27 | for (i in abas) { 28 | for (j in babs) { 29 | if ( \ 30 | substr(i, 1, 1) == substr(j, 2, 1) && \ 31 | substr(i, 2, 1) == substr(j, 1, 1) \ 32 | ) { 33 | ++ssl 34 | next 35 | } 36 | } 37 | } 38 | } 39 | 40 | END { print ssl } 41 | -------------------------------------------------------------------------------- /2023/day05/day05a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | class Mapping 4 | def initialize(map_str) 5 | header, *raw_ranges = map_str.split("\n") 6 | @src, @dst = header.split[0].split("-").values_at(0, -1) 7 | 8 | @ranges = {} 9 | 10 | raw_ranges.each do |line| 11 | dst_start, src_start, len = line.split.map(&:to_i) 12 | @ranges[(src_start...src_start + len)] = (dst_start...dst_start + len) 13 | end 14 | end 15 | 16 | def convert(src) 17 | key = @ranges.keys.find { |key| key.include?(src) } 18 | return src unless key 19 | 20 | @ranges[key].first + src - key.first 21 | end 22 | end 23 | 24 | file = File.open(ARGV[0]) 25 | 26 | seed_str, *mappings_str = file.readlines.join.split("\n\n") 27 | 28 | seeds = seed_str.scan(/\d+/).map(&:to_i) 29 | mappings = mappings_str.map { |str| Mapping.new(str) } 30 | 31 | min_loc = seeds.map do |seed| 32 | mappings.reduce(seed) { |src, mapping| mapping.convert(src) } 33 | end.min 34 | 35 | puts min_loc 36 | -------------------------------------------------------------------------------- /2019/day06/day06a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | no warnings 'recursion'; 8 | 9 | my %orbitHash; 10 | my $checksum; 11 | 12 | sub buildTree { 13 | my ( $key, $depth ) = @_; 14 | return 1 if ( not defined $orbitHash{$key} ); 15 | ++$depth; 16 | my $subtree; 17 | foreach my $val ( @{ $orbitHash{$key} } ) { 18 | $checksum += $depth; 19 | $subtree->{$val} = buildTree( $val, $depth ); 20 | } 21 | return $subtree; 22 | } 23 | 24 | my $fname = shift; 25 | 26 | open my $fh, "<", $fname 27 | or die "Can't open $fname: $!"; 28 | 29 | chomp( my @lines = <$fh> ); 30 | 31 | my @orbits = 32 | map { my ( $k, $v ) = split /\)/, $_; my $s = { $k => $v }; $s } @lines; 33 | 34 | foreach my $orbit (@orbits) { 35 | my ($k) = keys %$orbit; 36 | my ($v) = values %$orbit; 37 | push @{ $orbitHash{$k} }, $v; 38 | } 39 | 40 | my $tree = { "COM" => buildTree( "COM", 0 ) }; 41 | 42 | say $checksum; 43 | -------------------------------------------------------------------------------- /2015/day06/day06b.pl: -------------------------------------------------------------------------------- 1 | #!/bin/perl 2 | 3 | use 5.022; 4 | use warnings; 5 | no warnings 'experimental'; 6 | use List::Util qw(sum); 7 | use Data::Dumper; 8 | 9 | my @grid; 10 | my $ifname = "input"; 11 | 12 | open my $fh, '<', $ifname; 13 | 14 | while (my $line = <$fh>) { 15 | chomp $line; 16 | my ($action, $x1, $y1, $x2, $y2) = $line =~ /(.*) (\d+),(\d+) through (\d+),(\d+)/; 17 | foreach my $y ($y1..$y2) { 18 | foreach my $x ($x1..$x2) { 19 | for ($action) { 20 | when (/turn on/) { $grid[$y][$x]++ } 21 | when (/turn off/) { $grid[$y][$x]-- if defined $grid[$y][$x] and $grid[$y][$x] > 0 } 22 | when (/toggle/) { $grid[$y][$x] += 2 } 23 | default { die "Illegal action: $action" } 24 | } 25 | } 26 | } 27 | } 28 | 29 | close $fh; 30 | 31 | my $brightness = sum map { sum grep { defined $_ } @$_ } grep { defined $_ } @grid; 32 | 33 | say "Total brightness is $brightness."; 34 | -------------------------------------------------------------------------------- /2017/day02/day02b.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | func main() { 12 | f, err := os.Open(os.Args[1]) 13 | if err != nil { 14 | panic(err) 15 | } 16 | defer f.Close() 17 | 18 | scanner := bufio.NewScanner(f) 19 | 20 | checksum := 0 21 | 22 | for scanner.Scan() { 23 | numsAsStrings := strings.Split(scanner.Text(), "\t") 24 | nums := make([]int, len(numsAsStrings)) 25 | for i, v := range numsAsStrings { 26 | nums[i], err = strconv.Atoi(v) 27 | if err != nil { 28 | panic(err) 29 | } 30 | } 31 | 32 | OuterLoop: 33 | for i, v1 := range nums { 34 | for j, v2 := range nums { 35 | if i == j { 36 | continue 37 | } 38 | if v1%v2 == 0 { 39 | checksum += v1 / v2 40 | break OuterLoop 41 | } 42 | } 43 | } 44 | } 45 | if err := scanner.Err(); err != nil { 46 | fmt.Fprintln(os.Stderr, "Reading from file:", err) 47 | } 48 | 49 | fmt.Println(checksum) 50 | } 51 | -------------------------------------------------------------------------------- /2017/day16/day16a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | no warnings 'experimental'; 6 | 7 | use v5.10.1; 8 | 9 | use List::MoreUtils qw(firstidx); 10 | 11 | my $fname = shift; 12 | 13 | open my $fh, "<", $fname 14 | or die "Can't open $fname: $!"; 15 | 16 | my @progs = map { chr( ord('a') + $_ ) } (0..15); 17 | 18 | my $line = <$fh>; 19 | chomp $line; 20 | my @instrs = split /,/, $line; 21 | 22 | foreach my $instr (@instrs) { 23 | given ($instr) { 24 | when (/^s(\d+)$/) { 25 | my $els = $1; 26 | unshift @progs, pop @progs while $els--; 27 | } 28 | when (/^x(\d+)\/(\d+)$/) { 29 | @progs[$1, $2] = @progs[$2, $1]; 30 | } 31 | when (/^p(\w)\/(\w)$/) { 32 | my $idx1 = firstidx { $_ eq $1 } @progs; 33 | my $idx2 = firstidx { $_ eq $2 } @progs; 34 | @progs[ $idx1, $idx2 ] = @progs[ $idx2, $idx1 ]; 35 | } 36 | } 37 | } 38 | 39 | say join '', @progs; 40 | -------------------------------------------------------------------------------- /2023/day13/day13a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | def is_symmetry_line?(lines, (a, b)) 4 | while a > 0 && b < lines.length - 1 5 | a -= 1 6 | b += 1 7 | return false if lines[a] != lines[b] 8 | end 9 | 10 | true 11 | end 12 | 13 | def find_symmetry(lines) 14 | idx_pairs = [] 15 | lines.each_with_index.each_cons(2) do |((line_a, idx_a), (line_b, idx_b))| 16 | idx_pairs << [idx_a, idx_b] if line_a == line_b 17 | end 18 | 19 | return nil if idx_pairs.empty? 20 | 21 | sym_line = idx_pairs.select { |pair| is_symmetry_line?(lines, pair) } 22 | return nil if sym_line.empty? 23 | 24 | sym_line.first.first + 1 25 | end 26 | 27 | def summarize(pattern) 28 | lines = pattern.split("\n") 29 | count = find_symmetry(lines) 30 | return 100 * count if count 31 | 32 | find_symmetry(lines.map(&:chars).transpose.map(&:join)) 33 | end 34 | 35 | file = File.open(ARGV[0]) 36 | 37 | patterns = file.readlines.join.split("\n\n") 38 | 39 | puts patterns.sum { |p| summarize(p) } 40 | -------------------------------------------------------------------------------- /2018/day11/day11a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | sub getPower { 9 | my ($x, $y, $serNum) = @_; 10 | my $power = (($x + 10) * $y + $serNum) * ($x + 10); 11 | $power =~ /(.)..$/; 12 | return ($1 // 0) - 5; 13 | } 14 | 15 | sub getPowerSquare { 16 | my ($x, $y, $serNum) = @_; 17 | my $power = 0; 18 | for my $xx ($x..$x+2) { 19 | for my $yy ($y..$y+2) { 20 | $power += getPower($xx, $yy, $serNum); 21 | } 22 | } 23 | return $power; 24 | } 25 | 26 | my $fname = shift; 27 | 28 | open my $fh, "<", $fname 29 | or die "Can't open $fname: $!"; 30 | 31 | my $serNum = <$fh>; 32 | chomp $serNum; 33 | 34 | my $maxPower = getPowerSquare(1, 1, $serNum); 35 | my @maxCoords = (1, 1); 36 | 37 | for my $x (1..298) { 38 | for my $y (1..298) { 39 | my $power = getPowerSquare($x, $y, $serNum); 40 | if ($power > $maxPower) { 41 | $maxPower = $power; 42 | @maxCoords = ($x, $y); 43 | } 44 | } 45 | } 46 | 47 | say join ",", @maxCoords; 48 | -------------------------------------------------------------------------------- /2021/day05/day05b.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import process from "process"; 4 | import * as fs from "fs"; 5 | 6 | let input = fs.readFileSync(process.argv[2], "utf8") 7 | .trim() 8 | .split("\n") 9 | .map(line => { 10 | let [x1, y1, x2, y2] = line.split(/[, >-]+/).map(Number); 11 | return {x1, y1, x2, y2}; 12 | }); 13 | 14 | let points = input.map(({x1, y1, x2, y2}) => { 15 | let dx = x1 < x2 ? 1 16 | : x1 > x2 ? -1 17 | : 0; 18 | let dy = y1 < y2 ? 1 19 | : y1 > y2 ? -1 20 | : 0; 21 | 22 | let p = []; 23 | let x = x1, y = y1; 24 | for (; x != x2 || y != y2; x += dx, y += dy) { 25 | p.push({x, y}); 26 | } 27 | p.push({x, y}); // add last point 28 | return p; 29 | }).flat(); 30 | 31 | let counts = Object.create(null); 32 | for (let p of points) { 33 | let pCount = counts[JSON.stringify(p)]; 34 | counts[JSON.stringify(p)] = pCount ? pCount + 1 : 1; 35 | } 36 | 37 | let overlaps = Object.values(counts) 38 | .filter(v => v > 1) 39 | .length; 40 | 41 | console.log(overlaps); 42 | -------------------------------------------------------------------------------- /2017/day13/day13a.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "log" 7 | "os" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | func main() { 13 | if len(os.Args) < 2 { 14 | log.Fatal("No input file specified") 15 | } 16 | 17 | file, err := os.Open(os.Args[1]) 18 | if err != nil { 19 | log.Fatal(err) 20 | } 21 | defer file.Close() 22 | 23 | scanner := bufio.NewScanner(file) 24 | var secScanners = make(map[int]int) 25 | 26 | for scanner.Scan() { 27 | line := strings.Split(strings.TrimRight(scanner.Text(), "\n"), ": ") 28 | d, err := strconv.Atoi(line[0]) 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | r, err := strconv.Atoi(line[1]) 33 | if err != nil { 34 | log.Fatal(err) 35 | } 36 | secScanners[d] = r 37 | } 38 | 39 | if err := scanner.Err(); err != nil { 40 | log.Fatal(err) 41 | } 42 | 43 | var severity int 44 | for d, r := range secScanners { 45 | if d%((r-1)*2) == 0 { 46 | severity += d * r 47 | } 48 | } 49 | fmt.Println(severity) 50 | } 51 | -------------------------------------------------------------------------------- /2018/day10/day10b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | use List::Util qw(max min); 9 | 10 | my $fname = shift; 11 | 12 | open my $fh, "<", $fname 13 | or die "Can't open $fname: $!"; 14 | 15 | my @lights; 16 | 17 | while (my $line = <$fh>) { 18 | chomp $line; 19 | $line =~ /(-?\d+),.*?(-?\d+).*?(-?\d), *?(-?\d)/; 20 | push @lights, { x => $1, y => $2, vx => $3, vy => $4 }; 21 | } 22 | 23 | my $t = 0; 24 | my ($prevBBSize, $bbSize); 25 | 26 | while (1) { 27 | my $xmin = min map { $_->{x} } @lights; 28 | my $xmax = max map { $_->{x} } @lights; 29 | my $ymin = min map { $_->{y} } @lights; 30 | my $ymax = max map { $_->{y} } @lights; 31 | $bbSize = ($xmax - $xmin + 1) * ($ymax - $ymin + 1); 32 | if ($t > 0 and $bbSize > $prevBBSize) { 33 | say $t - 1; 34 | exit; 35 | } 36 | 37 | foreach my $light (@lights) { 38 | $light->{x} += $light->{vx}; 39 | $light->{y} += $light->{vy}; 40 | } 41 | $prevBBSize = $bbSize; 42 | $t++; 43 | } 44 | 45 | -------------------------------------------------------------------------------- /2019/day02/first/day02a.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/bewuethr/advent-of-code/go/convert" 8 | "github.com/bewuethr/advent-of-code/go/intcode" 9 | "github.com/bewuethr/advent-of-code/go/ioutil" 10 | "github.com/bewuethr/advent-of-code/go/log" 11 | ) 12 | 13 | func main() { 14 | scanner, err := ioutil.GetInputScanner() 15 | if err != nil { 16 | log.Die("getting scanner", err) 17 | } 18 | 19 | scanner.Scan() 20 | opCodesStr := strings.Split(scanner.Text(), ",") 21 | if err := scanner.Err(); err != nil { 22 | log.Die("reading input", err) 23 | } 24 | 25 | opCodes, err := convert.StrSliceToInt(opCodesStr) 26 | if err != nil { 27 | log.Die("converting string slice to int", err) 28 | } 29 | 30 | opCodes[1], opCodes[2] = 12, 2 31 | comp := intcode.NewComputer(opCodes) 32 | comp.StartProgram() 33 | select { 34 | case err := <-comp.Err: 35 | log.Die("running op codes", err) 36 | case <-comp.Done: 37 | fmt.Println(comp.Value(0)) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /2019/day06/day06b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | no warnings 'recursion'; 8 | 9 | my %astroMap; 10 | my %distance; 11 | my ( $start, $target ); 12 | 13 | sub visit { 14 | my $cur = shift; 15 | foreach my $next ( @{ $astroMap{$cur} } ) { 16 | next if defined $distance{$next}; 17 | if ( $next eq $target ) { 18 | say $distance{$cur} + 1; 19 | exit; 20 | } 21 | $distance{$next} = $distance{$cur} + 1; 22 | visit($next); 23 | } 24 | } 25 | 26 | my $fname = shift; 27 | 28 | open my $fh, "<", $fname 29 | or die "Can't open $fname: $!"; 30 | 31 | chomp( my @lines = <$fh> ); 32 | 33 | foreach my $edge ( map { [ split /\)/, $_ ] } @lines ) { 34 | push @{ $astroMap{ $edge->[0] } }, $edge->[1]; 35 | push @{ $astroMap{ $edge->[1] } }, $edge->[0]; 36 | } 37 | 38 | ( $start, $target ) = ( $astroMap{"YOU"}[0], $astroMap{"SAN"}[0] ); 39 | $distance{$start} = 0; 40 | 41 | visit($start); 42 | -------------------------------------------------------------------------------- /2019/day08/day08b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | my $input = <$fh>; 14 | chomp $input; 15 | my @digits = split //, $input; 16 | 17 | my ( $w, $h ) = ( 25, 6 ); 18 | 19 | my @layers; 20 | 21 | while (@digits) { 22 | my @layer; 23 | foreach my $y ( 0 .. $h - 1 ) { 24 | foreach my $x ( 0 .. $w - 1 ) { 25 | my $digit = shift @digits; 26 | $layer[$y][$x] = $digit; 27 | } 28 | } 29 | push @layers, \@layer; 30 | } 31 | 32 | my @image; 33 | 34 | foreach my $y ( 0 .. $h - 1 ) { 35 | foreach my $x ( 0 .. $w - 1 ) { 36 | foreach my $layer (@layers) { 37 | next if $layer->[$y][$x] == 2; 38 | $image[$y][$x] = $layer->[$y][$x]; 39 | last; 40 | } 41 | } 42 | } 43 | 44 | foreach my $row (@image) { 45 | say map { $_ == 0 ? '.' : '#' } @$row; 46 | } 47 | -------------------------------------------------------------------------------- /2017/day22/day22a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | my %grid; 14 | my ($x, $y) = (1, 1); 15 | 16 | while (my $line = <$fh>) { 17 | chomp $line; 18 | my @arr = split //, $line; 19 | foreach my $el (@arr) { 20 | $grid{$x++ . "/$y"} = $el; 21 | } 22 | $x = 1; 23 | $y++; 24 | } 25 | 26 | my $infect = 0; 27 | my ($xpos, $ypos) = (13, 13); 28 | my ($dx, $dy) = (0, -1); 29 | 30 | foreach my $i (1..10_000) { 31 | $grid{"$xpos/$ypos"} //= '.'; 32 | if ($grid{"$xpos/$ypos"} eq '#') { 33 | # rotate right 34 | ($dx, $dy) = (-$dy, $dx); 35 | $grid{"$xpos/$ypos"} = '.'; 36 | } 37 | else { 38 | # rotate left 39 | ($dx, $dy) = ($dy, -$dx); 40 | $grid{"$xpos/$ypos"} = '#'; 41 | $infect++; 42 | } 43 | $xpos += $dx; 44 | $ypos += $dy; 45 | } 46 | 47 | say $infect; 48 | -------------------------------------------------------------------------------- /2024/day09/day09b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | BlockFile = Struct.new(:idx, :length) 4 | 5 | disk_map = File.read(ARGV[0]).strip 6 | 7 | expanded = [] 8 | id = 0 9 | files = {} 10 | 11 | disk_map.chars.map(&:to_i).each_with_index do |n, idx| 12 | if idx.even? 13 | l = expanded.length 14 | expanded.concat([id] * n) 15 | files[id] = BlockFile.new(l, n) 16 | id += 1 17 | else 18 | expanded.concat(["."] * n) 19 | end 20 | end 21 | 22 | files.to_a.reverse_each do |id, file| 23 | free_idx = expanded.each_cons(file.length).each_with_index.find do |slice, idx| 24 | slice.all?(".") 25 | end&.last 26 | next unless free_idx 27 | next if free_idx > file.idx 28 | 29 | expanded[free_idx...free_idx + file.length], expanded[file.idx...file.idx + file.length] = 30 | expanded[file.idx...file.idx + file.length], expanded[free_idx...free_idx + file.length] 31 | end 32 | 33 | checksum = 0 34 | 35 | expanded.each_with_index do |n, idx| 36 | checksum += n * idx unless n == "." 37 | end 38 | 39 | puts checksum 40 | -------------------------------------------------------------------------------- /2024/day13/day13a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | COST_A = 3 4 | COST_B = 1 5 | 6 | Point = Struct.new(:x, :y) do 7 | def to_s = "#{x}/#{y}" 8 | 9 | def +(other) = Point.new(x + other.x, y + other.y) 10 | 11 | def *(n) = Point.new(x * n, y * n) 12 | end 13 | 14 | def hits_target?(machine, n_a, n_b) 15 | (machine[:a] * n_a) + (machine[:b] * n_b) == machine[:target] 16 | end 17 | 18 | def cost(n_a, n_b) = COST_A * n_a + COST_B * n_b 19 | 20 | input = File.readlines(ARGV[0]).map(&:chomp) 21 | 22 | machines = input.each_slice(4).map do |a, b, prize, _| 23 | { 24 | a: Point.new(*a.scan(/\d+/).map(&:to_i)), 25 | b: Point.new(*b.scan(/\d+/).map(&:to_i)), 26 | target: Point.new(*prize.scan(/\d+/).map(&:to_i)) 27 | } 28 | end 29 | 30 | cost = machines.map do |machine| 31 | valid_combos = (0..100).to_a.repeated_permutation(2).select do |n_a, n_b| 32 | hits_target?(machine, n_a, n_b) 33 | end 34 | 35 | costs = valid_combos.map { |n_a, n_b| cost(n_a, n_b) } 36 | costs.min 37 | end.compact.sum 38 | 39 | puts cost 40 | -------------------------------------------------------------------------------- /2019/day08/day08a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | my $input = <$fh>; 14 | chomp $input; 15 | my @digits = split //, $input; 16 | 17 | my ( $w, $h ) = ( 25, 6 ); 18 | 19 | my @layers; 20 | my @metadata; 21 | 22 | while (@digits) { 23 | my @layer; 24 | my %lData = ( layer => scalar @layers ); 25 | foreach my $y ( 0 .. $h - 1 ) { 26 | foreach my $x ( 0 .. $w - 1 ) { 27 | my $digit = shift @digits; 28 | $layer[$y][$x] = $digit; 29 | $lData{zeroes}++ if $digit == 0; 30 | $lData{ones}++ if $digit == 1; 31 | $lData{twos}++ if $digit == 2; 32 | } 33 | } 34 | push @layers, \@layer; 35 | push @metadata, \%lData; 36 | } 37 | my $fewestZeroes = 38 | ( sort { ( $a->{zeroes} // 0 ) <=> ( $b->{zeroes} // 0 ) } @metadata )[0]; 39 | 40 | say $fewestZeroes->{ones} * $fewestZeroes->{twos}; 41 | -------------------------------------------------------------------------------- /2024/day08/day08b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | Vec2 = Struct.new(:x, :y) do 4 | def +(other) = Vec2.new(x + other.x, y + other.y) 5 | 6 | def -(other) = Vec2.new(x - other.x, y - other.y) 7 | 8 | def *(k) = Vec2.new(x * k, y * k) 9 | end 10 | 11 | lines = File.readlines(ARGV[0]).map(&:chomp) 12 | 13 | y_range = (0...lines.length) 14 | x_range = (0...lines[0].length) 15 | 16 | antennas = Hash.new { |h, k| h[k] = [] } 17 | 18 | lines.each_with_index do |row, y| 19 | row.chars.each_with_index do |loc, x| 20 | next if loc == "." 21 | antennas[loc] << Vec2.new(x, y) 22 | end 23 | end 24 | 25 | antinodes = Set.new 26 | 27 | antennas.each_pair do |freq, coords| 28 | coords.combination(2) do |v1, v2| 29 | diffs = [v2 - v1, v1 - v2] 30 | diffs.each do |v| 31 | k = 0 32 | candidate = v1 + v * k 33 | while y_range.cover?(candidate.y) && x_range.cover?(candidate.x) 34 | antinodes.add(candidate) 35 | candidate += v 36 | end 37 | end 38 | end 39 | end 40 | 41 | puts antinodes.size 42 | -------------------------------------------------------------------------------- /2018/day08/day08b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | use List::Util qw(sum); 9 | 10 | sub makenode { 11 | my ($arr, $idx) = @_; 12 | my ($childCount, $metaCount) = ($arr->[$$idx], $arr->[$$idx+1]); 13 | $$idx += 2; 14 | my $node = {}; 15 | push @{ $node->{children} }, makenode($arr, $idx) while $childCount--; 16 | push @{ $node->{meta} }, $arr->[$$idx++] while $metaCount--; 17 | 18 | if (not exists $node->{children}) { 19 | $node->{value} = sum @{ $node->{meta} }; 20 | } 21 | else { 22 | foreach my $childIdx (@{ $node->{meta} }) { 23 | next if $childIdx > @{ $node->{children} }; 24 | $node->{value} += $node->{children}[$childIdx-1]{value}; 25 | } 26 | } 27 | 28 | return $node; 29 | } 30 | 31 | my $fname = shift; 32 | 33 | open my $fh, "<", $fname 34 | or die "Can't open $fname: $!"; 35 | 36 | my $line = <$fh>; 37 | chomp $line; 38 | my @arr = split / /, $line; 39 | 40 | my $idx = 0; 41 | 42 | my $tree = makenode(\@arr, \$idx); 43 | 44 | say $tree->{value}; 45 | -------------------------------------------------------------------------------- /2020/day19/day19a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | function join(arr, i, res) { 4 | for (i in arr) { 5 | res = res arr[i] 6 | } 7 | 8 | return res 9 | } 10 | 11 | BEGIN { 12 | FS = "[ \":]+" 13 | PROCINFO["sorted_in"] = "@ind_num_asc" 14 | } 15 | 16 | ! msg { 17 | if ($0 ~ /"/) { 18 | known[$1] = $2 19 | } 20 | else { 21 | for (i = 2; i <= NF; ++i) { 22 | rules[$1][i-1] = $i 23 | } 24 | } 25 | } 26 | 27 | msg && $0 ~ "^" known[0] "$" { ++count } 28 | 29 | /^$/ { 30 | msg = 1 31 | 32 | while (length(rules)) { 33 | for (i in rules) { 34 | for (j in rules[i]) { 35 | # Substitute known rules 36 | if (rules[i][j] in known) { 37 | rules[i][j] = known[rules[i][j]] 38 | } 39 | } 40 | 41 | # Check if this one is now known 42 | newrule = join(rules[i]) 43 | if (newrule !~ /[[:digit:]]/) { 44 | if (newrule ~ /\|/) { 45 | known[i] = "(" newrule ")" 46 | } 47 | else { 48 | known[i] = newrule 49 | } 50 | delete rules[i] 51 | } 52 | } 53 | } 54 | } 55 | 56 | END { print count } 57 | -------------------------------------------------------------------------------- /2021/day05/day05a.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import process from "process"; 4 | import * as fs from "fs"; 5 | 6 | let input = fs.readFileSync(process.argv[2], "utf8") 7 | .trim() 8 | .split("\n") 9 | .map(line => { 10 | let [x1, y1, x2, y2] = line.split(/[, >-]+/).map(Number); 11 | return {x1, y1, x2, y2}; 12 | }) 13 | .filter(e => e.x1 == e.x2 || e.y1 == e.y2); 14 | 15 | let points = input.map(({x1, y1, x2, y2}) => { 16 | let dx = x1 < x2 ? 1 17 | : x1 > x2 ? -1 18 | : 0; 19 | let dy = y1 < y2 ? 1 20 | : y1 > y2 ? -1 21 | : 0; 22 | 23 | let p = []; 24 | let x = x1, y = y1; 25 | for (; x != x2 || y != y2; x += dx, y += dy) { 26 | p.push({x, y}); 27 | } 28 | p.push({x, y}); // add last point 29 | return p; 30 | }).flat(); 31 | 32 | let counts = Object.create(null); 33 | for (let p of points) { 34 | let pCount = counts[JSON.stringify(p)]; 35 | counts[JSON.stringify(p)] = pCount ? pCount + 1 : 1; 36 | } 37 | 38 | let overlaps = Object.values(counts) 39 | .filter(v => v > 1) 40 | .length; 41 | 42 | console.log(overlaps); 43 | -------------------------------------------------------------------------------- /2017/day24/day24a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | use List::Util qw(sum max); 9 | 10 | sub findstrongest { 11 | my ($first, $comps) = @_; 12 | my $strongest = 0; 13 | foreach my $idx (0 .. $#$comps) { 14 | if ( grep { $_ == $first } @{ $comps->[$idx] } ) { 15 | my $newfirst = $comps->[$idx][0] == $first ? $comps->[$idx][1] : $comps->[$idx][0]; 16 | my @newcomps = @$comps; 17 | splice @newcomps, $idx, 1; 18 | my $strength = ( sum @{ $comps->[$idx] } ) + findstrongest($newfirst, \@newcomps); 19 | $strongest = max ($strongest, $strength); 20 | } 21 | } 22 | return $strongest; 23 | } 24 | 25 | my $fname = shift; 26 | 27 | open my $fh, "<", $fname 28 | or die "Can't open $fname: $!"; 29 | 30 | chomp(my @comps = <$fh>); 31 | 32 | foreach my $idx (0 .. $#comps) { 33 | $comps[$idx] = [ split /\//, $comps[$idx] ]; 34 | } 35 | 36 | my $strongest = findstrongest(0, \@comps); 37 | 38 | say $strongest; 39 | -------------------------------------------------------------------------------- /2021/day14/day14a.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import process from "process"; 4 | import * as fs from "fs"; 5 | 6 | function step(polymer) { 7 | let newPol = []; 8 | newPol.push(polymer.shift()); 9 | 10 | let prev = newPol[0]; 11 | 12 | for (let el of polymer) { 13 | newPol.push(rules[prev + el], el); 14 | prev = el; 15 | } 16 | 17 | return newPol; 18 | } 19 | 20 | let [polymer, ruleInput] = fs.readFileSync(process.argv[2], "utf8") 21 | .trim() 22 | .split("\n\n"); 23 | 24 | polymer = polymer.split(""); 25 | 26 | let rules = Object.create(null); 27 | 28 | ruleInput.split("\n") 29 | .forEach(line => { 30 | let [from, to] = line.split(" -> "); 31 | rules[from] = to; 32 | }); 33 | 34 | for (let i = 0; i < 10; ++i) { 35 | polymer = step(polymer); 36 | } 37 | 38 | let counts = Object.values(polymer 39 | .reduce((counts, curr) => { 40 | counts[curr] = counts[curr] ? counts[curr] + 1 : 1; 41 | return counts; 42 | }, Object.create(null))) 43 | .sort((a, b) => b - a); 44 | 45 | console.log(counts[0] - counts[counts.length - 1]); 46 | -------------------------------------------------------------------------------- /2016/day13/day13a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | function is_wall(x, y, num, count) { 4 | num = x^2 + 3*x + 2*x*y + y + y^2 + favnum 5 | count = 0 6 | for (; num != 0; num = rshift(num, 1)) 7 | count += and(num, 1) 8 | return count % 2 9 | } 10 | 11 | function flood(x, y, len) { 12 | if (x < 0 || y < 0 || is_wall(x, y) || dist[x, y]) 13 | return 14 | if (x == 31 && y == 39) { 15 | printf "Distance to %d/%d: %d\n", x, y, len 16 | exit 17 | } 18 | dist[x, y] = len 19 | } 20 | 21 | { favnum = $0 } 22 | 23 | END { 24 | len = 0 25 | dist[1, 1] = len 26 | while (1) { 27 | for (coords in dist) { 28 | if (dist[coords] == len) { 29 | split(coords, coord, SUBSEP) 30 | x = coord[1] 31 | y = coord[2] 32 | flood(x+1, y, len+1) 33 | flood(x-1, y, len+1) 34 | flood(x, y+1, len+1) 35 | flood(x, y-1, len+1) 36 | } 37 | } 38 | ++len 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /2016/day13/day13b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | function is_wall(x, y, num, count) { 4 | num = x^2 + 3*x + 2*x*y + y + y^2 + favnum 5 | count = 0 6 | for (; num != 0; num = rshift(num, 1)) 7 | count += and(num, 1) 8 | return count % 2 9 | } 10 | 11 | function flood(x, y, len) { 12 | if (x < 0 || y < 0 || is_wall(x, y) || dist[x, y]) 13 | return 14 | dist[x, y] = len 15 | } 16 | 17 | { favnum = $0 } 18 | 19 | END { 20 | len = 0 21 | dist[1, 1] = len 22 | while (1) { 23 | for (coords in dist) { 24 | if (dist[coords] == len) { 25 | split(coords, coord, SUBSEP) 26 | x = coord[1] 27 | y = coord[2] 28 | flood(x+1, y, len+1) 29 | flood(x-1, y, len+1) 30 | flood(x, y+1, len+1) 31 | flood(x, y-1, len+1) 32 | } 33 | } 34 | ++len 35 | if (len == 50) 36 | break 37 | } 38 | for (coords in dist) 39 | ++count 40 | print count 41 | } 42 | -------------------------------------------------------------------------------- /2018/day09/day09a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | use List::Util qw(max); 9 | 10 | my $fname = shift; 11 | 12 | open my $fh, "<", $fname 13 | or die "Can't open $fname: $!"; 14 | 15 | my $line = <$fh>; 16 | chomp $line; 17 | $line =~ /(\d+).* (\d+)/; 18 | my ($players, $last) = ($1, $2); 19 | 20 | my @circle = ( 0 ); 21 | my @scores; 22 | my $current = 0; 23 | my $player = 0; 24 | 25 | foreach my $val (1 .. $last) { 26 | if ($val % 23 == 0) { 27 | my $removeIdx = ($current + @circle - 7) % @circle; 28 | $scores[$player] += $val + $circle[$removeIdx]; 29 | splice @circle, $removeIdx, 1; 30 | $current = $removeIdx > $#circle ? 0 : $removeIdx; 31 | } 32 | else { 33 | my $insertIdx = ($current + 2) % @circle; 34 | if ($insertIdx == 0) { 35 | push @circle, $val; 36 | $insertIdx = $#circle; 37 | } 38 | else { 39 | splice @circle, $insertIdx, 0, $val; 40 | } 41 | $current = $insertIdx; 42 | } 43 | $player = ($player + 1) % $players; 44 | } 45 | 46 | say max @scores; 47 | -------------------------------------------------------------------------------- /2020/day18/day18a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | function peek(stack) { 4 | return stack[length(stack)] 5 | } 6 | 7 | function pop(stack, val) { 8 | val = peek(stack) 9 | delete stack[length(stack)] 10 | return val 11 | } 12 | 13 | function eval_term( nxt, ret) { 14 | nxt = pop(tokens) 15 | if (nxt ~ /[[:digit:]]/) { 16 | ret = nxt 17 | } 18 | else if (nxt == ")") { 19 | ret = eval_expression() 20 | nxt = pop(tokens) 21 | if (nxt != "(") { 22 | print "syntax error" 23 | exit 1 24 | } 25 | } 26 | 27 | return ret 28 | } 29 | 30 | function eval_expression( ret, pk) { 31 | if (! length(tokens)) { 32 | return 0 33 | } 34 | 35 | ret = eval_term() 36 | 37 | if (length(tokens)) { 38 | pk = peek(tokens) 39 | if (pk == "+") { 40 | pop(tokens) 41 | ret += eval_expression() 42 | } 43 | else if (pk == "*") { 44 | pop(tokens) 45 | ret *= eval_expression() 46 | } 47 | } 48 | 49 | return ret 50 | } 51 | 52 | { 53 | gsub(/ /, "") 54 | split($0, tokens, //) 55 | sum += eval_expression() 56 | } 57 | 58 | END { print sum } 59 | -------------------------------------------------------------------------------- /2017/day20/day20a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | use List::Util qw(sum); 9 | 10 | my $fname = shift; 11 | 12 | open my $fh, "<", $fname 13 | or die "Can't open $fname: $!"; 14 | 15 | my @particles; 16 | my %minacc; 17 | 18 | while (my $line = <$fh>) { 19 | chomp $line; 20 | my @arr = split / /, $line; 21 | $arr[0] =~ /(-?\d+),(-?\d+),(-?\d+)/g; 22 | push @particles, { p => { x => $1, y => $2, z => $3 } }; 23 | $arr[1] =~ /(-?\d+),(-?\d+),(-?\d+)/g; 24 | $particles[-1]->{v} = { x => $1, y => $2, z => $3 }; 25 | $arr[2] =~ /(-?\d+),(-?\d+),(-?\d+)/g; 26 | $particles[-1]->{a} = { x => $1, y => $2, z => $3 }; 27 | $particles[-1]->{a_abs} = sqrt(sum map { $_ ** 2 } ($1, $2, $3)); 28 | $particles[-1]->{dist} = sum map { abs $_ } values %{ $particles[-1]->{p} }; 29 | if (not %minacc or $particles[-1]->{a_abs} < $minacc{a_abs}) { 30 | $minacc{a_abs} = $particles[-1]->{a_abs}; 31 | $minacc{idx} = $#particles; 32 | } 33 | } 34 | 35 | say $minacc{idx}; 36 | -------------------------------------------------------------------------------- /2019/day05/first/day05a.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/bewuethr/advent-of-code/go/convert" 8 | "github.com/bewuethr/advent-of-code/go/intcode" 9 | "github.com/bewuethr/advent-of-code/go/ioutil" 10 | "github.com/bewuethr/advent-of-code/go/log" 11 | ) 12 | 13 | func main() { 14 | scanner, err := ioutil.GetInputScanner() 15 | if err != nil { 16 | log.Die("getting scanner", err) 17 | } 18 | 19 | scanner.Scan() 20 | opCodesStr := strings.Split(scanner.Text(), ",") 21 | if err := scanner.Err(); err != nil { 22 | log.Die("reading input", err) 23 | } 24 | 25 | opCodes, err := convert.StrSliceToInt(opCodesStr) 26 | if err != nil { 27 | log.Die("converting string slice to int", err) 28 | } 29 | 30 | comp := intcode.NewComputer(opCodes) 31 | comp.StartProgram() 32 | comp.Input <- 1 33 | Loop: 34 | for { 35 | select { 36 | case err := <-comp.Err: 37 | log.Die("running op codes", err) 38 | case <-comp.Done: 39 | break Loop 40 | case output := <-comp.Output: 41 | fmt.Println(output) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /2019/day05/second/day05b.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/bewuethr/advent-of-code/go/convert" 8 | "github.com/bewuethr/advent-of-code/go/intcode" 9 | "github.com/bewuethr/advent-of-code/go/ioutil" 10 | "github.com/bewuethr/advent-of-code/go/log" 11 | ) 12 | 13 | func main() { 14 | scanner, err := ioutil.GetInputScanner() 15 | if err != nil { 16 | log.Die("getting scanner", err) 17 | } 18 | 19 | scanner.Scan() 20 | opCodesStr := strings.Split(scanner.Text(), ",") 21 | if err := scanner.Err(); err != nil { 22 | log.Die("reading input", err) 23 | } 24 | 25 | opCodes, err := convert.StrSliceToInt(opCodesStr) 26 | if err != nil { 27 | log.Die("converting string slice to int", err) 28 | } 29 | 30 | comp := intcode.NewComputer(opCodes) 31 | comp.StartProgram() 32 | comp.Input <- 5 33 | Loop: 34 | for { 35 | select { 36 | case err := <-comp.Err: 37 | log.Die("running op codes", err) 38 | case <-comp.Done: 39 | break Loop 40 | case output := <-comp.Output: 41 | fmt.Println(output) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /2019/day09/first/day09a.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/bewuethr/advent-of-code/go/convert" 8 | "github.com/bewuethr/advent-of-code/go/intcode" 9 | "github.com/bewuethr/advent-of-code/go/ioutil" 10 | "github.com/bewuethr/advent-of-code/go/log" 11 | ) 12 | 13 | func main() { 14 | scanner, err := ioutil.GetInputScanner() 15 | if err != nil { 16 | log.Die("getting scanner", err) 17 | } 18 | 19 | scanner.Scan() 20 | opCodesStr := strings.Split(scanner.Text(), ",") 21 | if err := scanner.Err(); err != nil { 22 | log.Die("reading input", err) 23 | } 24 | 25 | opCodes, err := convert.StrSliceToInt(opCodesStr) 26 | if err != nil { 27 | log.Die("converting string slice to int", err) 28 | } 29 | 30 | comp := intcode.NewComputer(opCodes) 31 | comp.StartProgram() 32 | comp.Input <- 1 33 | Loop: 34 | for { 35 | select { 36 | case err := <-comp.Err: 37 | log.Die("running op codes", err) 38 | case <-comp.Done: 39 | break Loop 40 | case output := <-comp.Output: 41 | fmt.Println(output) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /2019/day09/second/day09b.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/bewuethr/advent-of-code/go/convert" 8 | "github.com/bewuethr/advent-of-code/go/intcode" 9 | "github.com/bewuethr/advent-of-code/go/ioutil" 10 | "github.com/bewuethr/advent-of-code/go/log" 11 | ) 12 | 13 | func main() { 14 | scanner, err := ioutil.GetInputScanner() 15 | if err != nil { 16 | log.Die("getting scanner", err) 17 | } 18 | 19 | scanner.Scan() 20 | opCodesStr := strings.Split(scanner.Text(), ",") 21 | if err := scanner.Err(); err != nil { 22 | log.Die("reading input", err) 23 | } 24 | 25 | opCodes, err := convert.StrSliceToInt(opCodesStr) 26 | if err != nil { 27 | log.Die("converting string slice to int", err) 28 | } 29 | 30 | comp := intcode.NewComputer(opCodes) 31 | comp.StartProgram() 32 | comp.Input <- 2 33 | Loop: 34 | for { 35 | select { 36 | case err := <-comp.Err: 37 | log.Die("running op codes", err) 38 | case <-comp.Done: 39 | break Loop 40 | case output := <-comp.Output: 41 | fmt.Println(output) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /2023/day11/day11b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | EXPANSION = 1_000_000 4 | 5 | def dist(a, b, non_empty_rows, non_empty_cols) 6 | x_range = ([a.first, b.first].min...[a.first, b.first].max) 7 | x_dist = x_range.sum { |x| non_empty_cols.include?(x) ? 1 : EXPANSION } 8 | 9 | y_range = ([a.last, b.last].min...[a.last, b.last].max) 10 | y_dist = y_range.sum { |y| non_empty_rows.include?(y) ? 1 : EXPANSION } 11 | 12 | x_dist + y_dist 13 | end 14 | 15 | file = File.open(ARGV[0]) 16 | 17 | image = [] 18 | non_empty_rows = Set.new 19 | non_empty_cols = Set.new 20 | 21 | file.readlines.map(&:chomp).each_with_index do |line, y| 22 | image << line.chars 23 | 24 | line.chars.each_with_index { |c, idx| non_empty_cols.add(idx) if c == "#" } 25 | non_empty_rows.add(y) if line.match?("#") 26 | end 27 | 28 | coords = image.each_with_object([]).with_index do |(line, locs), y| 29 | line.each_with_index do |c, x| 30 | locs << [x, y] if c == "#" 31 | end 32 | 33 | locs 34 | end 35 | 36 | puts coords.combination(2).sum { |a, b| dist(a, b, non_empty_rows, non_empty_cols) } 37 | -------------------------------------------------------------------------------- /2017/day13/day13b.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "log" 7 | "os" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | func main() { 13 | if len(os.Args) < 2 { 14 | log.Fatal("No input file specified") 15 | } 16 | 17 | file, err := os.Open(os.Args[1]) 18 | if err != nil { 19 | log.Fatal(err) 20 | } 21 | defer file.Close() 22 | 23 | scanner := bufio.NewScanner(file) 24 | var secScanners = make(map[int]int) 25 | 26 | for scanner.Scan() { 27 | line := strings.Split(strings.TrimRight(scanner.Text(), "\n"), ": ") 28 | d, err := strconv.Atoi(line[0]) 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | r, err := strconv.Atoi(line[1]) 33 | if err != nil { 34 | log.Fatal(err) 35 | } 36 | secScanners[d] = r 37 | } 38 | 39 | if err := scanner.Err(); err != nil { 40 | log.Fatal(err) 41 | } 42 | 43 | var delay int 44 | OuterLoop: 45 | for { 46 | for d, r := range secScanners { 47 | if (delay+d)%((r-1)*2) == 0 { 48 | delay++ 49 | continue OuterLoop 50 | } 51 | } 52 | break 53 | } 54 | fmt.Println(delay) 55 | } 56 | -------------------------------------------------------------------------------- /2021/day10/day10b.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import process from "process"; 4 | import * as fs from "fs"; 5 | 6 | function isValid(line) { 7 | let stack = []; 8 | for (let e of line.split("")) { 9 | if (e.match(/[[({<]/)) stack.push(e); 10 | else if (!pairs.includes(stack.pop() + e)) return false; 11 | } 12 | 13 | return true; 14 | } 15 | 16 | function getScore(line) { 17 | let scores = new Map(); 18 | scores.set("(", 1); 19 | scores.set("[", 2); 20 | scores.set("{", 3); 21 | scores.set("<", 4); 22 | 23 | let stack = []; 24 | for (let e of line.split("")) { 25 | if (e.match(/[[({<]/)) stack.push(e); 26 | else stack.pop(); 27 | } 28 | 29 | let score = 0; 30 | for (let e of stack.reverse()) { 31 | score *= 5; 32 | score += scores.get(e); 33 | } 34 | 35 | return score 36 | } 37 | 38 | let input = fs.readFileSync(process.argv[2], "utf8") 39 | .trim() 40 | .split("\n"); 41 | 42 | let pairs = ["()", "[]", "{}", "<>"]; 43 | 44 | let scores = input 45 | .filter(isValid) 46 | .map(getScore); 47 | 48 | console.log(scores.sort((a, b) => a - b)[Math.floor(scores.length / 2)]); 49 | -------------------------------------------------------------------------------- /2019/day02/day02b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | my $line = <$fh>; 14 | chomp $line; 15 | my @prog = split /,/, $line; 16 | 17 | my $want = 19690720; 18 | 19 | foreach my $noun ( 0 .. 99 ) { 20 | foreach my $verb ( 0 .. 99 ) { 21 | my @p = @prog; 22 | @p[ 1, 2 ] = ( $noun, $verb ); 23 | 24 | my $i = 0; 25 | while (1) { 26 | last if $p[$i] == 99; 27 | if ( $p[$i] == 1 ) { 28 | $p[ $p[ $i + 3 ] ] = $p[ $p[ $i + 1 ] ] + $p[ $p[ $i + 2 ] ]; 29 | } 30 | elsif ( $p[$i] == 2 ) { 31 | $p[ $p[ $i + 3 ] ] = $p[ $p[ $i + 1 ] ] * $p[ $p[ $i + 2 ] ]; 32 | } 33 | else { 34 | die "illegal opcode at index $i: $p[$i]"; 35 | } 36 | $i += 4; 37 | } 38 | 39 | if ( $p[0] == $want ) { 40 | say 100 * $noun + $verb; 41 | last; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /2015/day14/day14a: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | while read v t_fly t_rest; do 4 | speed+=($v) 5 | flytime+=($t_fly) 6 | resttime+=($t_rest) 7 | done < <(cut --delimiter ' ' --fields=4,7,14 input) 8 | 9 | t=0 10 | t_max=2503 11 | n=${#speed[@]} 12 | 13 | for (( i = 0; i < n; ++i )); do 14 | status[$i]='F' 15 | timer[$i]=0 16 | dist[$i]=0 17 | done 18 | 19 | while (( t < t_max )); do 20 | for (( i = 0; i < n; ++i )); do 21 | (( ++timer[$i] )) 22 | if [[ ${status[$i]} == F ]]; then 23 | (( dist[$i] += speed[$i] )) 24 | if (( timer[$i] == flytime[$i] )); then 25 | status[$i]='R' 26 | timer[$i]=0 27 | fi 28 | else 29 | if (( timer[$i] == resttime[$i] )); then 30 | status[$i]='F' 31 | timer[$i]=0 32 | fi 33 | fi 34 | done 35 | (( ++t )) 36 | done 37 | 38 | for (( i = 0; i < n; ++i )); do 39 | if (( dist[$i] > max_dist )); then 40 | max_dist=${dist[$i]} 41 | fi 42 | done 43 | 44 | echo "Max distance traveled after $t_max s: $max_dist km" 45 | -------------------------------------------------------------------------------- /2018/day05/day05b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | my $line = <$fh>; 14 | chomp $line; 15 | my $shortest = length $line; 16 | 17 | foreach my $remove ('a' .. 'z') { 18 | my $input = $line =~ s/$remove//gir; 19 | 20 | my @arr = split //, $input; 21 | my @new; 22 | 23 | my $before = @arr; 24 | 25 | while (1) { 26 | my $cur = 0; 27 | while (1) { 28 | if ($cur >= $#arr) { 29 | push @new, $arr[$cur] if defined $arr[$cur]; 30 | last; 31 | } 32 | if ($arr[$cur] =~ /[[:lower:]]/ and $arr[$cur+1] =~ /[[:upper:]]/ or 33 | $arr[$cur] =~ /[[:upper:]]/ and $arr[$cur+1] =~ /[[:lower:]]/) { 34 | if (lc $arr[$cur] eq lc $arr[$cur+1]) { 35 | $cur += 2; 36 | next; 37 | } 38 | } 39 | push @new, $arr[$cur]; 40 | $cur++; 41 | } 42 | last if @new == $before; 43 | @arr = (); 44 | @arr = @new; 45 | @new = (); 46 | $before = @arr; 47 | } 48 | 49 | $shortest = @new if @new < $shortest; 50 | } 51 | 52 | say $shortest; 53 | -------------------------------------------------------------------------------- /go/grid/vec3.go: -------------------------------------------------------------------------------- 1 | package grid 2 | 3 | // Vec3 represents a 3d vector with integer components. 4 | type Vec3 struct { 5 | x, y, z int 6 | } 7 | 8 | // Special 3d vectors 9 | var ( 10 | Origin3 = Vec3{0, 0, 0} // Origin of the grid 11 | Ux3 = Vec3{1, 0, 0} // x unit vector 12 | Uy3 = Vec3{0, 1, 0} // y unit vector 13 | Uz3 = Vec3{0, 0, 1} // z unit vector 14 | ) 15 | 16 | // NewVec3 creates a new vector with components x, y, and z. 17 | func NewVec3(x, y, z int) Vec3 { 18 | return Vec3{ 19 | x: x, 20 | y: y, 21 | z: z, 22 | } 23 | } 24 | 25 | // X returns the x component of v. 26 | func (v Vec3) X() int { 27 | return v.x 28 | } 29 | 30 | // Y returns the y component of v. 31 | func (v Vec3) Y() int { 32 | return v.y 33 | } 34 | 35 | // Z returns the z component of v. 36 | func (v Vec3) Z() int { 37 | return v.z 38 | } 39 | 40 | // Add returns the sum of vectors v and w. 41 | func (v Vec3) Add(w Vec3) Vec3 { 42 | return NewVec3(v.x+w.x, v.y+w.y, v.z+w.z) 43 | } 44 | 45 | // Subtract returns the difference of vectors v and w. 46 | func (v Vec3) Subtract(w Vec3) Vec3 { 47 | return NewVec3(v.x-w.x, v.y-w.y, v.z-w.z) 48 | } 49 | -------------------------------------------------------------------------------- /2018/day06/day06b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | use List::Util qw(max min sum); 9 | use List::MoreUtils qw(firstidx); 10 | 11 | sub getdist { 12 | my ($x, $y, $p) = @_; 13 | return abs($p->[0] - $x) + abs($p->[1] - $y); 14 | } 15 | 16 | sub isinregion { 17 | my ($x, $y, $map) = @_; 18 | my $totaldist = sum map { getdist($x, $y, $_) } @$map; 19 | return $totaldist < 10000; 20 | } 21 | 22 | my $fname = shift; 23 | 24 | open my $fh, "<", $fname 25 | or die "Can't open $fname: $!"; 26 | 27 | my @map; 28 | 29 | while (my $line = <$fh>) { 30 | chomp $line; 31 | $line =~ m/(\d+), (\d+)/; 32 | push @map, [$1, $2]; 33 | } 34 | 35 | # Bounding box 36 | my ($xmin, $xmax, $ymin, $ymax) = ( 37 | (min map { $_->[0] } @map), 38 | (max map { $_->[0] } @map), 39 | (min map { $_->[1] } @map), 40 | (max map { $_->[1] } @map), 41 | ); 42 | 43 | my %area; 44 | my $count; 45 | foreach my $y ($ymin .. $ymax) { 46 | foreach my $x ($xmin .. $xmax) { 47 | my $val = isinregion($x, $y, \@map); 48 | $count += $val; 49 | print $val ? "X" : "."; 50 | } 51 | say ""; 52 | } 53 | 54 | say $count; 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Benjamin Wuethrich 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /2015/day06/day06b: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | re='(.*) ([[:digit:]]+),([[:digit:]]+) through ([[:digit:]]+),([[:digit:]]+)' 4 | 5 | declare -A grid 6 | 7 | idx=0 8 | while read line; do 9 | [[ "$line" =~ $re ]] 10 | action="${BASH_REMATCH[1]}" 11 | x1="${BASH_REMATCH[2]}" 12 | y1="${BASH_REMATCH[3]}" 13 | x2="${BASH_REMATCH[4]}" 14 | y2="${BASH_REMATCH[5]}" 15 | for (( y = y1; y <= y2; ++y )); do 16 | for (( x = x1; x <= x2; ++x )); do 17 | case "$action" in 18 | 'turn on') (( ++grid[$x,$y] )) ;; 19 | 'turn off') 20 | val="${grid[$x,$y]}" 21 | if (( val > 0 )); then 22 | (( --grid[$x,$y] )) 23 | fi 24 | ;; 25 | 'toggle') (( grid[$x,$y] += 2 )) ;; 26 | esac 27 | done 28 | done 29 | (( ++idx )) 30 | (( idx % 50 == 0 && idx > 0 )) && echo "Processed $idx instructions..." 31 | done < input 32 | 33 | brightness=0 34 | for light in "${grid[@]}"; do 35 | (( brightness += $light )) 36 | done 37 | 38 | echo "The total brightness is $brightness." 39 | -------------------------------------------------------------------------------- /2017/day10/day10a.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | my $line = <$fh>; 14 | chomp $line; 15 | 16 | my @lengths = split /,/, $line; 17 | my $skip = 0; 18 | my $cur = 0; 19 | 20 | my @list = (0..255); 21 | 22 | foreach my $len (@lengths) { 23 | my @longlist = (@list, @list); 24 | 25 | my @sublist; 26 | if ($len == 0) { 27 | @sublist = (); 28 | } 29 | else { 30 | @sublist = @longlist[ $cur .. $cur+$len-1 ]; 31 | } 32 | 33 | my @head; 34 | if ($cur == 0) { 35 | @head = (); 36 | } 37 | else { 38 | @head = @longlist[ 0 .. $cur-1 ]; 39 | } 40 | 41 | my @mod_longlist = (@head, (reverse @sublist), @longlist[ $cur+$len .. $#longlist ]); 42 | 43 | @list = @mod_longlist[0..$#list]; 44 | if ($cur+$len-1 > $#list) { 45 | @list[ 0 .. ($cur+$len-1) % @list ] = @mod_longlist[ @list .. $cur+$len-1 ]; 46 | } 47 | $cur = ($cur + $len + $skip) % @list; 48 | $skip++; 49 | } 50 | 51 | say $list[0] * $list[1]; 52 | -------------------------------------------------------------------------------- /2017/day10/day10a.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "log" 7 | "os" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | func main() { 13 | if len(os.Args) == 1 { 14 | log.Fatal("Filename not specified") 15 | } 16 | input, err := ioutil.ReadFile(os.Args[1]) 17 | if err != nil { 18 | log.Fatal(err) 19 | } 20 | 21 | splits := strings.Split(strings.TrimRight(string(input), "\n"), ",") 22 | lengths := make([]int, len(splits)) 23 | for i, v := range splits { 24 | lengths[i], _ = strconv.Atoi(v) 25 | } 26 | 27 | list := make([]int, 256) 28 | for i := range list { 29 | list[i] = i 30 | } 31 | 32 | curIdx, skip := 0, 0 33 | for _, l := range lengths { 34 | // Create reverse list 35 | revList := make([]int, l) 36 | for i := 0; i < l; i++ { 37 | tarIdx := l - i - 1 38 | srcIdx := (curIdx + i) % len(list) 39 | revList[tarIdx] = list[srcIdx] 40 | } 41 | 42 | // Write reverse list back to original list 43 | for i, v := range revList { 44 | list[(curIdx+i)%len(list)] = v 45 | } 46 | 47 | curIdx += l + skip 48 | curIdx %= len(list) 49 | skip++ 50 | } 51 | 52 | fmt.Println(list[0] * list[1]) 53 | } 54 | -------------------------------------------------------------------------------- /2024/day10/day10b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | Point = Struct.new(:x, :y) 4 | 5 | class Map 6 | attr_reader :rows 7 | 8 | def initialize(rows) 9 | @rows = rows 10 | @y_range = (0...@rows.length) 11 | @x_range = (0...@rows[0].length) 12 | end 13 | 14 | def include?(p) = @y_range.cover?(p.y) && @x_range.cover?(p.x) 15 | 16 | def at(p) = @rows[p.y][p.x] 17 | end 18 | 19 | def bfs(topo_map, root) 20 | q = [] 21 | rating = 0 22 | q << root 23 | 24 | until q.empty? 25 | v = q.shift 26 | rating += 1 if topo_map.at(v) == 9 27 | 28 | [[0, 1], [0, -1], [1, 0], [-1, 0]].each do |dx, dy| 29 | w = Point.new(v.x + dx, v.y + dy) 30 | q << w if topo_map.include?(w) && topo_map.at(w) == topo_map.at(v) + 1 31 | end 32 | end 33 | 34 | rating 35 | end 36 | 37 | rows = File.readlines(ARGV[0]).map { _1.chomp.chars.map(&:to_i) } 38 | topo_map = Map.new(rows) 39 | 40 | zeroes = topo_map.rows.each_with_index.select do |row, y| 41 | row.include?(0) 42 | end.flat_map do |row, y| 43 | row.each_with_index.select { |pos, x| pos == 0 } 44 | .map { |_, x| Point.new(x, y) } 45 | end 46 | 47 | puts zeroes.sum { bfs(topo_map, _1) } 48 | -------------------------------------------------------------------------------- /2017/day13/day13b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use feature 'say'; 7 | 8 | use List::Util qw(max sum); 9 | 10 | my $fname = shift; 11 | 12 | open my $fh, "<", $fname 13 | or die "Can't open $fname: $!"; 14 | 15 | my %fwall; 16 | 17 | while (my $line = <$fh>) { 18 | chomp $line; 19 | my ($depth, $range) = split /: /, $line; 20 | @{$fwall{$depth}}{ qw(range pos dir) } = ($range, 1, 1); 21 | } 22 | 23 | my @topmatrix; 24 | 25 | my $t = 0; 26 | my $maxkey = max keys %fwall; 27 | 28 | while (1) { 29 | push @topmatrix, 30 | [ 31 | map { 32 | (defined $fwall{$_} and $fwall{$_}{pos} == 1) ? 1 : 0 33 | } ( 0 .. $maxkey ) 34 | ]; 35 | foreach my $key (keys %fwall) { 36 | $fwall{$key}{pos} += $fwall{$key}{dir}; 37 | $fwall{$key}{dir} *= -1 if ( $fwall{$key}{pos} == $fwall{$key}{range} 38 | or $fwall{$key}{pos} == 1); 39 | } 40 | if ($t >= $maxkey) { 41 | last unless grep { $topmatrix[$_]->[$_] } ( 0 .. $maxkey ); 42 | shift @topmatrix; 43 | } 44 | $t++; 45 | } 46 | 47 | say $t - $maxkey; 48 | -------------------------------------------------------------------------------- /2018/day09/day09b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | use List::Util qw(max); 9 | 10 | my $fname = shift; 11 | 12 | open my $fh, "<", $fname 13 | or die "Can't open $fname: $!"; 14 | 15 | my $line = <$fh>; 16 | chomp $line; 17 | $line =~ /(\d+).* (\d+)/; 18 | my ($players, $last) = ($1, 100 * $2); 19 | 20 | my $node = { val => 0 }; 21 | $node->{next} = $node; 22 | $node->{prev} = $node; 23 | 24 | my @scores; 25 | my $current = $node; 26 | 27 | my $player = 0; 28 | 29 | foreach my $val (1 .. $last) { 30 | if ($val % 23 == 0) { 31 | $current = $current->{prev} for (1..7); 32 | $scores[$player] += $val + $current->{val}; 33 | $current->{prev}{next} = $current->{next}; 34 | $current->{next}{prev} = $current->{prev}; 35 | $current = $current->{next}; 36 | } 37 | else { 38 | $current = $current->{next}; 39 | my $newNode = { 40 | val => $val, 41 | prev => $current, 42 | next => $current->{next}, 43 | }; 44 | 45 | $current->{next}{prev} = $newNode; 46 | $current->{next} = $newNode; 47 | $current = $newNode; 48 | } 49 | $player = ($player + 1) % $players; 50 | } 51 | 52 | say max @scores; 53 | -------------------------------------------------------------------------------- /2019/day13/first/day13a.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/bewuethr/advent-of-code/go/convert" 8 | "github.com/bewuethr/advent-of-code/go/intcode" 9 | "github.com/bewuethr/advent-of-code/go/ioutil" 10 | "github.com/bewuethr/advent-of-code/go/log" 11 | ) 12 | 13 | const block = 2 14 | 15 | func main() { 16 | scanner, err := ioutil.GetInputScanner() 17 | if err != nil { 18 | log.Die("getting scanner", err) 19 | } 20 | 21 | scanner.Scan() 22 | opCodesStr := strings.Split(scanner.Text(), ",") 23 | if err := scanner.Err(); err != nil { 24 | log.Die("reading input", err) 25 | } 26 | 27 | opCodes, err := convert.StrSliceToInt(opCodesStr) 28 | if err != nil { 29 | log.Die("converting string slice to int", err) 30 | } 31 | 32 | comp := intcode.NewComputer(opCodes) 33 | comp.StartProgram() 34 | blocks := 0 35 | Loop: 36 | for { 37 | select { 38 | case err := <-comp.Err: 39 | log.Die("running op codes", err) 40 | case <-comp.Done: 41 | break Loop 42 | case <-comp.Output: 43 | <-comp.Output 44 | if <-comp.Output == block { 45 | blocks++ 46 | } 47 | } 48 | } 49 | 50 | fmt.Println(blocks) 51 | } 52 | -------------------------------------------------------------------------------- /2018/day04/day04b.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use feature 'say'; 7 | 8 | my $fname = shift; 9 | 10 | open my $fh, "<", $fname 11 | or die "Can't open $fname: $!"; 12 | 13 | chomp (my @lines = sort <$fh>); 14 | 15 | my $times; 16 | 17 | my $re1 = qr/:..\] Guard #(\w+) begins/; 18 | my $re2 = qr/(..)\] falls/; 19 | my $re3 = qr/(..)\] wakes/; 20 | 21 | my $currentGuard; 22 | my ($from, $to); 23 | 24 | foreach my $line (@lines) { 25 | chomp $line; 26 | if ($line =~ $re1) { 27 | $currentGuard = $1; 28 | } 29 | if ($line =~ $re2) { 30 | $from = $1; 31 | } 32 | if ($line =~ $re3) { 33 | $to = $1; 34 | $times->{$currentGuard}{total} += $to - $from; 35 | foreach my $minute ($from .. $to-1) { 36 | $times->{$currentGuard}{minutes}{$minute}++; 37 | } 38 | } 39 | } 40 | 41 | my $max = 0; 42 | my $bestMinute; 43 | my $sid; 44 | foreach my $guard (keys %$times) { 45 | foreach my $minute (keys %{$times->{$guard}{minutes}}) { 46 | if ($times->{$guard}{minutes}{$minute} > $max) { 47 | $max = $times->{$guard}{minutes}{$minute}; 48 | $bestMinute = $minute; 49 | $sid = $guard; 50 | } 51 | } 52 | } 53 | 54 | say $sid * $bestMinute; 55 | -------------------------------------------------------------------------------- /2016/day02/day02b: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | function abs(x) { return x > 0 ? x : -x } 4 | 5 | function dist(x, y) { return abs(x) + abs(y) } 6 | 7 | BEGIN { 8 | x = -2 9 | y = 0 10 | 11 | # Mapping from coordinates to key labels 12 | label = 1 13 | for (yy = 2; yy >= -2; --yy) { 14 | for (xx = -2; xx <= 2; ++xx) { 15 | if (dist(xx, yy) < 3) 16 | labels[xx, yy] = sprintf("%X", label++) 17 | } 18 | } 19 | } 20 | 21 | { 22 | split($0, dirs, "") 23 | for (i = 1; i <= length(dirs); ++i) { 24 | switch (dirs[i]) { 25 | case "U": 26 | if (dist(x, y+1) < 3) 27 | ++y 28 | break 29 | case "D": 30 | if (dist(x, y-1) < 3) 31 | --y 32 | break 33 | case "L": 34 | if (dist(x-1, y) < 3) 35 | --x 36 | break 37 | case "R": 38 | if (dist(x+1, y) < 3) 39 | ++x 40 | break 41 | default: 42 | print "Illegal direction" 43 | exit 44 | } 45 | } 46 | code = code labels[x, y] 47 | } 48 | 49 | END { print code } 50 | -------------------------------------------------------------------------------- /2016/day08/day08a: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | BEGIN { 4 | xmax = 50 5 | ymax = 6 6 | for (x = 0; x < xmax; ++x) 7 | for (y = 0; y < ymax; ++y) 8 | pxmatr[x][y] = "." 9 | } 10 | 11 | /rect/ { 12 | split($2, arr, "x") 13 | for (x = 0; x < arr[1]; ++x) 14 | for (y = 0; y < arr[2]; ++y) 15 | pxmatr[x][y] = "#" 16 | } 17 | 18 | /rotate row/ { 19 | split($3, arr, "=") 20 | row = arr[2] 21 | dist = $NF 22 | for (i = 0; i < dist; ++i) { 23 | tmp = pxmatr[xmax-1][row] 24 | for (x = xmax-1; x > 0; --x) { 25 | pxmatr[x][row] = pxmatr[x-1][row] 26 | } 27 | pxmatr[0][row] = tmp 28 | } 29 | } 30 | 31 | /rotate column/ { 32 | split($3, arr, "=") 33 | col = arr[2] 34 | dist = $NF 35 | for (i = 0; i < dist; ++i) { 36 | tmp = pxmatr[col][ymax-1] 37 | for (y = ymax-1; y > 0; --y) { 38 | pxmatr[col][y] = pxmatr[col][y-1] 39 | } 40 | pxmatr[col][0] = tmp 41 | } 42 | } 43 | 44 | END { 45 | for (x = 0; x < xmax; ++x) 46 | for (y = 0; y < ymax; ++y) 47 | if (pxmatr[x][y] == "#") 48 | ++lit 49 | print lit 50 | } 51 | -------------------------------------------------------------------------------- /2015/day16/day16b: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # cats and trees: Sue has more than measured 4 | # pomeranians and golfish: Sue has fewer than measured 5 | 6 | declare -A sue 7 | 8 | sue=([children]=3 [cats]=7 [samoyeds]=2 [pomeranians]=3 [akitas]=0 9 | [vizslas]=0 [goldfish]=5 [trees]=3 [cars]=2 [perfumes]=1) 10 | 11 | while read _ num key0 val0 key1 val1 key2 val2; do 12 | num=${num%:} 13 | keys=(${key0%:} ${key1%:} ${key2%:}) 14 | vals=(${val0%,} ${val1%,} $val2) 15 | 16 | matches=0 17 | for i in {0..2}; do 18 | case ${keys[$i]} in 19 | cats | trees ) 20 | if (( sue[${keys[$i]}] < vals[$i] )); then (( ++matches )); fi 21 | ;; 22 | pomeranians | goldfish ) 23 | if (( sue[${keys[$i]}] > vals[$i] )); then (( ++matches )); fi 24 | ;; 25 | *) 26 | if (( sue[${keys[$i]}] == vals[$i] )); then (( ++matches )); fi 27 | ;; 28 | esac 29 | 30 | if (( matches == 3 )); then 31 | echo "$num: ${keys[@]} => ${vals[@]}" 32 | echo "The correct Sue is number $num." 33 | break 34 | fi 35 | done 36 | done < input 37 | --------------------------------------------------------------------------------