├── 2021 ├── go │ ├── d1 │ │ └── main.go │ ├── d10 │ │ └── main.go │ ├── d11 │ │ └── main.go │ ├── d12 │ │ └── main.go │ ├── d13 │ │ └── main.go │ ├── d14 │ │ └── main.go │ ├── d15 │ │ └── main.go │ ├── d16 │ │ └── main.go │ ├── d17 │ │ └── main.go │ ├── d18 │ │ └── main.go │ ├── d19 │ │ └── main.go │ ├── d2 │ │ └── main.go │ ├── d20 │ │ └── main.go │ ├── d21 │ │ └── main.go │ ├── d22 │ │ └── main.go │ ├── d3 │ │ └── main.go │ ├── d4 │ │ └── main.go │ ├── d5 │ │ └── main.go │ ├── d6 │ │ └── main.go │ ├── d7 │ │ └── main.go │ ├── d8 │ │ └── main.go │ ├── d9 │ │ └── main.go │ ├── go.mod │ ├── go.sum │ └── util │ │ └── util.go ├── input │ ├── 10a.txt │ ├── 10a_test.txt │ ├── 11a.txt │ ├── 11a_test.txt │ ├── 12a.txt │ ├── 12a_test.txt │ ├── 12a_test2.txt │ ├── 12a_test3.txt │ ├── 13a.txt │ ├── 13a_test.txt │ ├── 14a.txt │ ├── 14a_test.txt │ ├── 15a.txt │ ├── 15a_test.txt │ ├── 16a.txt │ ├── 17a.txt │ ├── 17a_test.txt │ ├── 18a.txt │ ├── 18a_test.txt │ ├── 19a.txt │ ├── 19a_test.txt │ ├── 1a.txt │ ├── 1a_test.txt │ ├── 20a.txt │ ├── 20a_test.txt │ ├── 22a.txt │ ├── 22a_test.txt │ ├── 22a_test2.txt │ ├── 22a_test3.txt │ ├── 24a.txt │ ├── 24a_test.txt │ ├── 25a.txt │ ├── 25a_test.txt │ ├── 2a.txt │ ├── 2a_test.txt │ ├── 3a.txt │ ├── 3a_test.txt │ ├── 4a.txt │ ├── 4a_test.txt │ ├── 5a.txt │ ├── 5a_test.txt │ ├── 6a.txt │ ├── 6a_test.txt │ ├── 7a.txt │ ├── 7a_test.txt │ ├── 8a.txt │ ├── 8a_test.txt │ ├── 9a.txt │ └── 9a_test.txt └── java │ └── src │ └── main │ └── java │ └── aoc2021 │ ├── D1.java │ ├── D10.java │ ├── D11.java │ ├── D12.java │ ├── D13.java │ ├── D14.java │ ├── D15.java │ ├── D16.java │ ├── D17.java │ ├── D18.java │ ├── D19.java │ ├── D2.java │ ├── D20.java │ ├── D21.java │ ├── D22.java │ ├── D23.java │ ├── D24.java │ ├── D25.java │ ├── D3.java │ ├── D4.java │ ├── D5.java │ ├── D6.java │ ├── D7.java │ ├── D8.java │ └── D9.java ├── 2022 ├── go │ ├── day1 │ │ └── day1.go │ ├── day10 │ │ └── day10.go │ ├── day11 │ │ └── day11.go │ ├── day12 │ │ └── day12.go │ ├── day13 │ │ └── day13.go │ ├── day14 │ │ └── day14.go │ ├── day15 │ │ └── day15.go │ ├── day2 │ │ └── day2.go │ ├── day3 │ │ └── day3.go │ ├── day4 │ │ └── day4.go │ ├── day5 │ │ └── day5.go │ ├── day6 │ │ └── day6.go │ ├── day7 │ │ └── day7.go │ ├── day8 │ │ └── day8.go │ ├── day9 │ │ └── day9.go │ └── go.mod ├── input │ ├── day1.txt │ ├── day10.txt │ ├── day11.txt │ ├── day12.txt │ ├── day13.txt │ ├── day14.txt │ ├── day15.txt │ ├── day2.txt │ ├── day3.txt │ ├── day4.txt │ ├── day5.txt │ ├── day6.txt │ ├── day7.txt │ ├── day8.txt │ └── day9.txt └── java │ ├── Day1.java │ ├── Day10.java │ ├── Day11.java │ ├── Day12.java │ ├── Day13.java │ ├── Day14.java │ ├── Day15.java │ ├── Day2.java │ ├── Day3.java │ ├── Day4.java │ ├── Day5.java │ ├── Day6.java │ ├── Day7.java │ ├── Day8.java │ └── Day9.java ├── 2023 ├── input │ ├── day10.txt │ ├── day10_test.txt │ ├── day10_test2.txt │ ├── day10_test3.txt │ ├── day10_test4.txt │ ├── day11.txt │ ├── day11_test.txt │ ├── day12.txt │ ├── day12_test.txt │ ├── day13.txt │ ├── day13_test.txt │ ├── day14.txt │ ├── day14_test.txt │ ├── day15.txt │ ├── day15_test.txt │ ├── day16.txt │ ├── day16_test.txt │ ├── day17.txt │ ├── day17_test.txt │ ├── day17_test2.txt │ ├── day18.txt │ ├── day18_test.txt │ ├── day19.txt │ ├── day19_test.txt │ ├── day1_a.txt │ ├── day1_test.txt │ ├── day1_test2.txt │ ├── day20.txt │ ├── day20_test.txt │ ├── day2_a.txt │ ├── day2_test.txt │ ├── day3.txt │ ├── day3_test.txt │ ├── day4.txt │ ├── day4_test.txt │ ├── day5.txt │ ├── day5_test.txt │ ├── day6.txt │ ├── day6_test.txt │ ├── day7.txt │ ├── day7_test.txt │ ├── day8.txt │ ├── day8_test.txt │ ├── day8_test2.txt │ ├── day9.txt │ └── day9_test.txt └── js │ ├── day1.js │ ├── day10.js │ ├── day11.js │ ├── day12.js │ ├── day13.js │ ├── day14.js │ ├── day15.js │ ├── day16.js │ ├── day17.js │ ├── day18.js │ ├── day19.js │ ├── day2.js │ ├── day20.js │ ├── day3.js │ ├── day4.js │ ├── day5.js │ ├── day6.js │ ├── day7.js │ ├── day8.js │ ├── day9.js │ ├── package.json │ └── util.js ├── 2024 ├── input │ ├── 1.txt │ ├── 10.txt │ ├── 12.txt │ ├── 1_test.txt │ ├── 2.txt │ ├── 2_test.txt │ ├── 3.txt │ ├── 4.txt │ ├── 5.txt │ ├── 6.txt │ ├── 7.txt │ ├── 8.txt │ └── 9.txt └── js │ ├── day1.js │ ├── day10.js │ ├── day11.js │ ├── day12.js │ ├── day2.js │ ├── day3.js │ ├── day4.js │ ├── day5.js │ ├── day6.js │ ├── day7.js │ ├── day8.js │ ├── day9.js │ └── util.js ├── LICENSE └── README.md /2021/go/d1/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "aoc2021/util" 5 | "fmt" 6 | "math" 7 | "strconv" 8 | ) 9 | 10 | // Advent of Code (AOC) 2021 Day 1 11 | func main() { 12 | 13 | var prev1 int = math.MaxInt 14 | var count1 uint = 0 15 | 16 | util.ReadFile("../input/1a.txt", func(line string) { 17 | depth, _ := strconv.Atoi(line) 18 | if depth > prev1 && prev1 != math.MaxInt { 19 | count1++ 20 | } 21 | prev1 = depth 22 | }) 23 | 24 | fmt.Printf("part 1: %d\n", count1) 25 | 26 | var prev2 int = math.MaxInt 27 | var count2 uint = 0 28 | var slider []int 29 | 30 | util.ReadFile("../input/1a.txt", func(line string) { 31 | depth, _ := strconv.Atoi(line) 32 | slider = append(slider, depth) 33 | if len(slider) < 3 { 34 | return 35 | } else if len(slider) > 3 { 36 | slider = slider[1:] 37 | } 38 | 39 | sum := 0 40 | for _, i := range slider { 41 | sum += i 42 | } 43 | 44 | if sum > prev2 && prev2 != math.MaxInt { 45 | count2++ 46 | } 47 | prev2 = sum 48 | 49 | }) 50 | 51 | fmt.Printf("part 2: %d\n", count2) 52 | } 53 | -------------------------------------------------------------------------------- /2021/go/d10/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "aoc2021/util" 5 | "fmt" 6 | "sort" 7 | ) 8 | 9 | type NavLine struct { 10 | FirstIllegal rune 11 | Missing []rune 12 | } 13 | 14 | var braces map[rune]rune = map[rune]rune{ 15 | '(': ')', 16 | '{': '}', 17 | '[': ']', 18 | '<': '>', 19 | } 20 | 21 | var scorePart1 map[rune]int = map[rune]int{ 22 | ')': 3, 23 | ']': 57, 24 | '}': 1197, 25 | '>': 25137, 26 | } 27 | 28 | var scoresPart2 map[rune]uint64 = map[rune]uint64{ 29 | ')': 1, 30 | ']': 2, 31 | '}': 3, 32 | '>': 4, 33 | } 34 | 35 | // Advent of Code (AOC) 2021 Day 10 part 1 and part 2 36 | func main() { 37 | 38 | part1Score := 0 39 | part2Score := []uint64{} 40 | 41 | util.ReadFile("../input/10a.txt", func(line string) { 42 | nav := ParseLine(line) 43 | if nav.FirstIllegal > 0 { 44 | part1Score += scorePart1[nav.FirstIllegal] 45 | } else { 46 | part2Score = append(part2Score, scorePart2(nav.Missing)) 47 | } 48 | }) 49 | 50 | fmt.Printf("part 1: %v\n", part1Score) 51 | sort.Slice(part2Score, func(i, j int) bool { return part2Score[i] < part2Score[j] }) 52 | fmt.Printf("part 2: %v\n", part2Score[len(part2Score)/2]) 53 | } 54 | 55 | func ParseLine(line string) NavLine { 56 | stack := []rune{} 57 | 58 | for _, c := range line { 59 | if closing, ok := braces[c]; ok { 60 | stack = append([]rune{closing}, stack...) 61 | } else { 62 | expected := stack[0] 63 | stack = stack[1:] 64 | if expected != c { 65 | return NavLine{FirstIllegal: c} 66 | } 67 | } 68 | } 69 | return NavLine{Missing: stack} 70 | } 71 | 72 | func scorePart2(missing []rune) uint64 { 73 | var score uint64 74 | for _, c := range missing { 75 | score = score*5 + scoresPart2[c] 76 | } 77 | return score 78 | } 79 | -------------------------------------------------------------------------------- /2021/go/d11/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "aoc2021/util" 5 | "fmt" 6 | ) 7 | 8 | // Advent of Code (AOC) 2021 Day 11 part 1 and part 2 9 | func main() { 10 | 11 | var eMap [][]byte 12 | 13 | util.ReadFile("../input/11a.txt", func(line string) { 14 | row := []byte{} 15 | for _, c := range line { 16 | row = append(row, byte(c-'0')) 17 | } 18 | eMap = append(eMap, row) 19 | }) 20 | 21 | count := 0 22 | for x := 0; x < 100; x++ { 23 | count += step(eMap) 24 | } 25 | 26 | fmt.Printf("part 1: %v\n", count) 27 | 28 | steps := 100 29 | for !allFlashes(eMap) { 30 | step(eMap) 31 | steps++ 32 | } 33 | fmt.Printf("part 2: %v\n", steps) 34 | } 35 | 36 | func allFlashes(eMap [][]byte) bool { 37 | for y := 0; y < len(eMap); y++ { 38 | for x := 0; x < len(eMap[y]); x++ { 39 | if eMap[y][x] != 0 { 40 | return false 41 | } 42 | } 43 | } 44 | return true 45 | } 46 | 47 | func step(eMap [][]byte) int { 48 | // increase all by one 49 | for y := 0; y < len(eMap); y++ { 50 | for x := 0; x < len(eMap[y]); x++ { 51 | eMap[y][x]++ 52 | } 53 | } 54 | 55 | // count flashes 56 | count := 0 57 | for y := 0; y < len(eMap); y++ { 58 | for x := 0; x < len(eMap[y]); x++ { 59 | count += flash(eMap, x, y) 60 | } 61 | } 62 | return count 63 | } 64 | 65 | func flash(eMap [][]byte, x int, y int) int { 66 | if eMap[y][x] < 10 { // Flash 10 or above 67 | return 0 68 | } 69 | 70 | eMap[y][x] = 0 71 | count := 1 72 | for ny := y - 1; ny <= y+1; ny++ { 73 | for nx := x - 1; nx <= x+1; nx++ { 74 | if nx < 0 || ny < 0 || ny >= len(eMap) || nx >= len(eMap[ny]) || eMap[ny][nx] == 0 { 75 | continue // Skip if out of energy map or already flashed this step (==0) 76 | } 77 | eMap[ny][nx]++ 78 | count += flash(eMap, nx, ny) 79 | } 80 | } 81 | return count 82 | } 83 | -------------------------------------------------------------------------------- /2021/go/d12/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "aoc2021/util" 5 | "fmt" 6 | "strings" 7 | "unicode" 8 | ) 9 | 10 | type linkMap map[string][]string 11 | 12 | // Advent of Code (AOC) 2021 Day 12 13 | func main() { 14 | 15 | links := make(linkMap) 16 | 17 | util.ReadFile("../input/12a.txt", func(line string) { 18 | link := strings.Split(line, "-") 19 | addLink(links, link[0], link[1]) 20 | addLink(links, link[1], link[0]) 21 | }) 22 | 23 | paths := findPaths(links, []string{"start"}, make(map[string]byte), 1) 24 | 25 | fmt.Printf("part 1: %v\n", len(paths)) 26 | 27 | paths = findPaths(links, []string{"start"}, make(map[string]byte), 2) 28 | 29 | fmt.Printf("part 2: %v\n", len(paths)) 30 | } 31 | 32 | func findPaths(links linkMap, currentPath []string, visited map[string]byte, maxSingleSmallVisits byte) [][]string { 33 | currentCave := currentPath[len(currentPath)-1] 34 | 35 | if currentCave == "end" { 36 | return [][]string{currentPath} 37 | } 38 | 39 | visited[currentCave]++ 40 | 41 | paths := [][]string{} 42 | for _, nextCave := range links[currentCave] { 43 | if !alreadyMaxVisits(visited, nextCave, maxSingleSmallVisits) { 44 | nextPaths := findPaths(links, append(currentPath, nextCave), cloneMap(visited), maxSingleSmallVisits) 45 | paths = append(paths, nextPaths...) 46 | } 47 | } 48 | return paths 49 | } 50 | 51 | func isSmallCave(cave string) bool { 52 | return !unicode.IsUpper([]rune(cave)[0]) 53 | } 54 | 55 | func cloneMap(m map[string]byte) map[string]byte { 56 | r := make(map[string]byte) 57 | for k, v := range m { 58 | r[k] = v 59 | } 60 | return r 61 | } 62 | 63 | func alreadyMaxVisits(visited map[string]byte, cave string, maxSingleSmallVisits byte) bool { 64 | if !isSmallCave(cave) || visited[cave] == 0 { 65 | return false 66 | } else if cave == "start" || cave == "end" || maxSingleSmallVisits < 2 { 67 | return true 68 | } 69 | 70 | for k, v := range visited { 71 | if isSmallCave(k) && v >= maxSingleSmallVisits { 72 | return true 73 | } 74 | } 75 | 76 | return false 77 | } 78 | 79 | func addLink(links linkMap, a string, b string) { 80 | links[a] = append(links[a], b) 81 | } 82 | -------------------------------------------------------------------------------- /2021/go/d13/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "aoc2021/util" 5 | "fmt" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | type Dot struct { 11 | x, y int 12 | } 13 | 14 | type Fold struct { 15 | axle byte 16 | pos int 17 | } 18 | 19 | // Advent of Code (AOC) 2021 Day 13 20 | func main() { 21 | 22 | paper := []Dot{} 23 | folds := []Fold{} 24 | 25 | util.ReadFile("../input/13a.txt", func(line string) { 26 | if strings.HasPrefix(line, "fold along ") { 27 | folds = append(folds, newFold(line)) 28 | } else if line != "" { 29 | paper = append(paper, newDot(line)) 30 | } 31 | }) 32 | 33 | paper = folds[0].foldPaper(paper) 34 | fmt.Printf("part 1: %v\n", len(paper)) 35 | 36 | folds = folds[1:] 37 | for _, fold := range folds { 38 | paper = fold.foldPaper(paper) 39 | } 40 | fmt.Print("part 2:\n") 41 | print(paper) 42 | } 43 | 44 | func (fold *Fold) foldPaper(paper []Dot) []Dot { 45 | res := []Dot{} 46 | for _, d := range paper { 47 | folded := fold.foldDot(d) 48 | if !contains(res, folded) { 49 | res = append(res, folded) 50 | } 51 | } 52 | return res 53 | } 54 | 55 | func (fold *Fold) foldDot(dot Dot) Dot { 56 | if fold.axle == 'x' && dot.x > fold.pos { 57 | return Dot{x: 2*fold.pos - dot.x, y: dot.y} 58 | } else if fold.axle == 'y' && dot.y > fold.pos { 59 | return Dot{x: dot.x, y: 2*fold.pos - dot.y} 60 | } else { 61 | return dot 62 | } 63 | } 64 | 65 | func print(paper []Dot) { 66 | width, height := size(paper) 67 | for y := 0; y <= height; y++ { 68 | for x := 0; x <= width; x++ { 69 | fmt.Print(charAt(paper, x, y)) 70 | } 71 | fmt.Println() 72 | } 73 | } 74 | 75 | func charAt(paper []Dot, x int, y int) string { 76 | for _, d := range paper { 77 | if d.x == x && d.y == y { 78 | return "█" 79 | } 80 | } 81 | return " " 82 | } 83 | 84 | func size(paper []Dot) (int, int) { 85 | var width, height int 86 | for _, d := range paper { 87 | if d.x > width { 88 | width = d.x 89 | } 90 | if d.y > height { 91 | height = d.y 92 | } 93 | } 94 | return width, height 95 | } 96 | 97 | func contains(dots []Dot, dot Dot) bool { 98 | for _, d := range dots { 99 | if d == dot { 100 | return true 101 | } 102 | } 103 | return false 104 | } 105 | 106 | func newFold(line string) Fold { 107 | parts := strings.Split(strings.Replace(line, "fold along ", "", 1), "=") 108 | axle := parts[0][0] 109 | pos, _ := strconv.Atoi(parts[1]) 110 | return Fold{axle: axle, pos: pos} 111 | } 112 | 113 | func newDot(line string) Dot { 114 | parts := strings.Split(line, ",") 115 | x, _ := strconv.Atoi(parts[0]) 116 | y, _ := strconv.Atoi(parts[1]) 117 | return Dot{x: x, y: y} 118 | } 119 | -------------------------------------------------------------------------------- /2021/go/d14/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "aoc2021/util" 5 | "fmt" 6 | "math" 7 | "strings" 8 | ) 9 | 10 | // Advent of Code (AOC) 2021 Day 14 11 | func main() { 12 | 13 | var template string 14 | var pairToCharRules = make(map[string]string) 15 | 16 | util.ReadFile("../input/14a.txt", func(line string) { 17 | if strings.Contains(line, " -> ") { 18 | parts := strings.Split(line, " -> ") 19 | pairToCharRules[parts[0]] = parts[1] 20 | } else if line != "" { 21 | template = line 22 | } 23 | }) 24 | 25 | part1 := solve(template, 10, pairToCharRules) 26 | fmt.Printf("part 1: %d\n", part1) 27 | 28 | part2 := solve(template, 40, pairToCharRules) 29 | fmt.Printf("part 2: %d\n", part2) 30 | } 31 | 32 | func solve(template string, steps int, pairToCharRules map[string]string) uint64 { 33 | pairCounts := countPairs(stringAsPairs(template)) 34 | for i := 0; i < steps; i++ { 35 | pairCounts = doStep(pairCounts, pairToCharRules) 36 | } 37 | 38 | charCounts := countFirstChars(pairCounts) 39 | // since the above does not include last char of the template, add it 40 | charCounts[template[len(template)-1]]++ 41 | 42 | // Calculate and return the difference between max and min char counts 43 | var min uint64 = math.MaxUint64 44 | var max uint64 = 0 45 | for _, v := range charCounts { 46 | if v < min { 47 | min = v 48 | } 49 | if v > max { 50 | max = v 51 | } 52 | } 53 | return max - min 54 | } 55 | 56 | func doStep(pairs map[string]uint64, pairToCharRules map[string]string) map[string]uint64 { 57 | pairCounts := make(map[string]uint64) 58 | for pair, count := range pairs { 59 | // explode pair to two pairs and add those counts to result 60 | pairToChar := pairToCharRules[pair] 61 | pair1 := string(pair[0]) + pairToChar 62 | pair2 := pairToChar + string(pair[1]) 63 | pairCounts[pair1] += count 64 | pairCounts[pair2] += count 65 | } 66 | return pairCounts 67 | } 68 | 69 | func countFirstChars(pairs map[string]uint64) map[byte]uint64 { 70 | charCounts := make(map[byte]uint64) 71 | for pair, count := range pairs { 72 | charCounts[pair[0]] += count 73 | } 74 | return charCounts 75 | } 76 | 77 | func countPairs(pairs []string) map[string]uint64 { 78 | pairCount := make(map[string]uint64) 79 | for _, pair := range pairs { 80 | pairCount[pair]++ 81 | } 82 | return pairCount 83 | } 84 | 85 | func stringAsPairs(str string) []string { 86 | var prev rune 87 | var pairs []string 88 | for _, c := range str { 89 | if prev != 0 { 90 | pairs = append(pairs, string([]rune{prev, c})) 91 | } 92 | prev = c 93 | } 94 | return pairs 95 | } 96 | -------------------------------------------------------------------------------- /2021/go/d15/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "aoc2021/util" 5 | "fmt" 6 | ) 7 | 8 | type point struct { 9 | x, y int 10 | } 11 | 12 | // Advent of Code (AOC) 2021 Day 15 13 | func main() { 14 | 15 | riskMap := readMap("../input/15a.txt") 16 | 17 | part1 := shortestPath(point{x: 0, y: 0}, point{x: len(riskMap[0]), y: len(riskMap)}, riskMap) 18 | fmt.Printf("part 1: %v\n", part1) 19 | 20 | riskMap = createPart2Map(riskMap) 21 | 22 | part2 := shortestPath(point{x: 0, y: 0}, point{x: len(riskMap[0]), y: len(riskMap)}, riskMap) 23 | fmt.Printf("part 2: %v\n", part2) 24 | } 25 | 26 | func shortestPath(from point, to point, riskMap [][]int) int { 27 | existingRisk := makeTable(len(riskMap), len(riskMap[0])) 28 | visit := map[point]int{from: 0} // points to visit mapped to risk 29 | for len(visit) > 0 { // loop while we have points to visit 30 | p, risk := takeOne(visit) 31 | if existing := existingRisk[p.y][p.x]; existing == 0 || existing > risk { 32 | existingRisk[p.y][p.x] = risk // found new or better solution, update it 33 | for _, adjacent := range adjacentPoints(p, riskMap) { // loop adjacent points 34 | adjacentRisk := riskMap[adjacent.y][adjacent.x] + risk 35 | if r, ok := visit[adjacent]; !ok || adjacentRisk < r { 36 | // adjacent point not in visit list or lower risk than existing entry, update it 37 | visit[adjacent] = adjacentRisk 38 | } 39 | } 40 | } 41 | } 42 | return existingRisk[to.y-1][to.x-1] 43 | } 44 | 45 | func adjacentPoints(p point, riskMap [][]int) []point { 46 | points := []point{} 47 | for _, a := range []point{p.adj(-1, 0), p.adj(1, 0), p.adj(0, -1), p.adj(0, 1)} { 48 | if a.x >= 0 && a.y >= 0 && a.y < len(riskMap) && a.x < len(riskMap[a.y]) { 49 | points = append(points, a) 50 | } 51 | } 52 | return points 53 | } 54 | 55 | func createPart2Map(source [][]int) [][]int { 56 | target := makeTable(len(source)*5, len(source[0])*5) 57 | for y := 0; y < 5; y++ { 58 | for x := 0; x < 5; x++ { 59 | fillMap(target, x, y, source) 60 | } 61 | } 62 | return target 63 | } 64 | 65 | func fillMap(target [][]int, px int, py int, source [][]int) { 66 | height := len(source) 67 | width := len(source[0]) 68 | for y, row := range source { 69 | for x, risk := range row { 70 | target[py*height+y][px*width+x] = (risk+px+py-1)%9 + 1 // if over 9 rotate to 1 71 | } 72 | } 73 | } 74 | 75 | func (p *point) adj(h int, v int) point { 76 | return point{x: p.x + h, y: p.y + v} 77 | } 78 | 79 | func takeOne(riskMap map[point]int) (point, int) { 80 | for k, v := range riskMap { 81 | delete(riskMap, k) 82 | return k, v 83 | } 84 | return point{}, 0 85 | } 86 | 87 | func readMap(file string) [][]int { 88 | riskMap := [][]int{} 89 | util.ReadFile(file, func(line string) { 90 | row := []int{} 91 | for _, c := range line { 92 | row = append(row, int(c-'0')) 93 | } 94 | riskMap = append(riskMap, row) 95 | }) 96 | return riskMap 97 | } 98 | 99 | func makeTable(height int, width int) [][]int { 100 | table := make([][]int, height) 101 | for i := 0; i < height; i++ { 102 | table[i] = make([]int, width) 103 | } 104 | return table 105 | } 106 | -------------------------------------------------------------------------------- /2021/go/d17/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "aoc2021/util" 5 | "fmt" 6 | "math" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | type Area struct { 12 | x1, x2, y1, y2 float64 13 | } 14 | 15 | type Probe struct { 16 | x, y, xv, yv, maxHeight float64 17 | } 18 | 19 | // Advent of Code (AOC) 2021 Day 17 20 | func main() { 21 | 22 | var target Area 23 | util.ReadFile("../input/17a.txt", func(line string) { 24 | parts := strings.Split(line[len("target area:"):], ",") 25 | target.x1, target.x2 = parseLimit(parts[0]) 26 | target.y2, target.y1 = parseLimit(parts[1]) 27 | }) 28 | 29 | hits := 0. 30 | maxHeight := 0. 31 | 32 | for xv := 0.; xv <= target.x2; xv++ { // iterate x velocity from 0 to limit 33 | for yv := target.y2; yv < 1000; yv++ { // iterate y velocity from y limit 34 | probe := shoot(Probe{xv: xv, yv: yv}, target) 35 | if probe.hit(target) { // count only hits 36 | hits++ 37 | maxHeight = math.Max(maxHeight, probe.maxHeight) 38 | } 39 | } 40 | } 41 | 42 | fmt.Printf("part 1: %v\n", maxHeight) 43 | fmt.Printf("part 2: %v\n", hits) 44 | } 45 | 46 | func shoot(probe Probe, target Area) Probe { 47 | for !probe.miss(target) && !probe.hit(target) { 48 | probe = probe.move() // move until hit or miss 49 | } 50 | return probe 51 | } 52 | 53 | func (p Probe) move() Probe { 54 | return Probe{p.x + p.xv, p.y + p.yv, math.Max(0, p.xv-1), p.yv - 1, math.Max(p.maxHeight, p.y+p.yv)} 55 | } 56 | 57 | func (p Probe) hit(target Area) bool { 58 | return p.x >= target.x1 && p.x <= target.x2 && p.y <= target.y1 && p.y >= target.y2 59 | } 60 | 61 | func (p Probe) miss(target Area) bool { 62 | return p.y < target.y2 || p.x > target.x2 63 | } 64 | 65 | func parseLimit(part string) (float64, float64) { 66 | parts := strings.Split(part[3:], "..") 67 | start, _ := strconv.ParseFloat(parts[0], 32) 68 | end, _ := strconv.ParseFloat(parts[1], 32) 69 | return start, end 70 | } 71 | -------------------------------------------------------------------------------- /2021/go/d2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "aoc2021/util" 5 | "fmt" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | type command struct { 11 | dir string 12 | amount int 13 | } 14 | 15 | // Advent of Code (AOC) 2021 Day 2 16 | func main() { 17 | 18 | var data []command 19 | 20 | util.ReadFile("../input/2a.txt", func(line string) { 21 | fields := strings.Fields(line) 22 | amount, _ := strconv.Atoi(fields[1]) 23 | data = append(data, command{dir: fields[0], amount: amount}) 24 | }) 25 | 26 | var pos, depth int 27 | 28 | for _, cmd := range data { 29 | switch cmd.dir { 30 | case "forward": 31 | pos += cmd.amount 32 | case "down": 33 | depth += cmd.amount 34 | case "up": 35 | depth -= cmd.amount 36 | } 37 | } 38 | 39 | fmt.Printf("part 1: %d\n", pos*depth) 40 | 41 | pos, depth = 0, 0 42 | var aim int 43 | 44 | for _, cmd := range data { 45 | switch cmd.dir { 46 | case "forward": 47 | pos += cmd.amount 48 | depth += aim * cmd.amount 49 | case "down": 50 | aim += cmd.amount 51 | case "up": 52 | aim -= cmd.amount 53 | } 54 | } 55 | 56 | fmt.Printf("part 1: %d\n", pos*depth) 57 | } 58 | -------------------------------------------------------------------------------- /2021/go/d21/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Player struct { 8 | pos, score int 9 | } 10 | 11 | type State struct { 12 | totalRolls, prevRoll, prevRoll2 int 13 | players [2]Player 14 | } 15 | 16 | // Advent of Code (AOC) 2021 Day 21 Dira Dice 17 | func main() { 18 | state := State{players: [2]Player{{pos: 8}, {pos: 6}}} 19 | for !state.gameOver(1000) { 20 | state = state.rollPracticeDice() 21 | } 22 | 23 | losingScore := state.players[0].score 24 | if state.players[1].score < losingScore { 25 | losingScore = state.players[1].score 26 | } 27 | fmt.Printf("part 1: %v\n", losingScore*state.totalRolls) 28 | 29 | state = State{players: [2]Player{{pos: 8}, {pos: 6}}} 30 | cache := map[State][2]int64{} 31 | res := playRecursiveUniverses(state, cache) 32 | 33 | max := res[0] 34 | if res[1] > max { 35 | max = res[1] 36 | } 37 | 38 | fmt.Printf("part 2: %v\n", max) 39 | } 40 | 41 | // return number of wins as array player1, player2 42 | func playRecursiveUniverses(state State, cache map[State][2]int64) [2]int64 { 43 | res, ok := cache[state] 44 | if !ok { 45 | for i := 1; i <= 3; i++ { // play Dira Dice all faces 1,2,3 and count winners together 46 | lr := playRound(i, state, cache) 47 | res[0] += lr[0] 48 | res[1] += lr[1] 49 | } 50 | cache[state] = res 51 | } 52 | return res 53 | } 54 | 55 | // return number of wins as array player1, player2 56 | func playRound(currentRoll int, state State, cache map[State][2]int64) [2]int64 { 57 | state = state.roll(currentRoll) 58 | 59 | if state.gameOver(21) { // We have winner, return it as array 0,1 or 1,0 60 | currentPlayer := (state.totalRolls / 3) % 2 61 | if currentPlayer == 0 { 62 | return [2]int64{1, 0} 63 | } else { 64 | return [2]int64{0, 1} 65 | } 66 | } 67 | return playRecursiveUniverses(state, cache) 68 | } 69 | 70 | func (state State) gameOver(score int) bool { 71 | return state.players[0].score >= score || state.players[1].score >= score 72 | } 73 | 74 | func (state State) rollPracticeDice() State { 75 | currentRoll := (state.totalRolls)%100 + 1 76 | return state.roll(currentRoll) 77 | } 78 | 79 | func (state State) roll(currentRoll int) State { 80 | playerRollNumber := state.totalRolls % 3 // number of roll for current player 81 | currentPlayer := (state.totalRolls / 3) % 2 82 | state.totalRolls += 1 83 | 84 | if playerRollNumber == 2 { // third roll for the player, move it 85 | totalRollSum := currentRoll + state.prevRoll + state.prevRoll2 // rolls sum 86 | moved := state.players[currentPlayer].move(totalRollSum) 87 | state.players[currentPlayer] = moved 88 | } 89 | state.prevRoll, state.prevRoll2 = currentRoll, state.prevRoll 90 | return state 91 | } 92 | 93 | func (p Player) move(move int) Player { 94 | pos := (p.pos+move-1)%10 + 1 95 | score := pos + p.score 96 | return Player{pos, score} 97 | } 98 | -------------------------------------------------------------------------------- /2021/go/d3/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "aoc2021/util" 5 | "fmt" 6 | "strconv" 7 | ) 8 | 9 | // Advent of Code (AOC) 2021 Day 3 10 | func main() { 11 | 12 | var data []string 13 | 14 | util.ReadFile("../input/3a.txt", func(line string) { 15 | data = append(data, line) 16 | }) 17 | 18 | bitSize := len(data[0]) 19 | 20 | // calculate ones for each bit position 21 | oneCounts := make([]int, bitSize) 22 | for _, line := range data { 23 | for pos, char := range line { 24 | oneCounts[pos] += int(byte(char) - '0') 25 | } 26 | } 27 | 28 | var gamma uint 29 | var epsilon uint 30 | for pos, ones := range oneCounts { 31 | if ones*2 >= len(data) { // half or more of ones, set gamma bit 32 | gamma = gamma | (1 << (bitSize - pos - 1)) 33 | } else { // otherwise set epsilon bit 34 | epsilon = epsilon | (1 << (bitSize - pos - 1)) 35 | } 36 | } 37 | 38 | fmt.Printf("part 1: %d\n", gamma*epsilon) 39 | 40 | o2, _ := filterUntilOne(true, data) 41 | co2, _ := filterUntilOne(false, data) 42 | 43 | fmt.Printf("part 2: %d\n", o2*co2) 44 | } 45 | 46 | func filterUntilOne(acceptMostCommon bool, data []string) (int64, error) { 47 | pos := 0 48 | for len(data) > 1 { 49 | data = filterByBitPos(pos, data, acceptMostCommon) 50 | pos++ 51 | } 52 | return strconv.ParseInt(data[0], 2, 32) 53 | } 54 | 55 | func filterByBitPos(pos int, data []string, acceptMostCommon bool) []string { 56 | accept := mostCommonInPos(pos, data) 57 | if !acceptMostCommon { 58 | accept = !accept 59 | } 60 | result := []string{} 61 | for _, line := range data { 62 | if line[pos] == '1' == accept { 63 | result = append(result, line) 64 | } 65 | } 66 | return result 67 | } 68 | 69 | func mostCommonInPos(pos int, data []string) bool { 70 | ones := 0 71 | for _, line := range data { 72 | ones += int(line[pos] - '0') 73 | } 74 | return ones*2 >= len(data) 75 | } 76 | -------------------------------------------------------------------------------- /2021/go/d4/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "aoc2021/util" 5 | "fmt" 6 | "reflect" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | type board struct { 12 | numbers [][]int 13 | marks []int 14 | } 15 | 16 | // Advent of Code (AOC) 2021 Day 4 17 | func main() { 18 | var data []string 19 | 20 | util.ReadFile("../input/4a.txt", func(line string) { 21 | data = append(data, line) 22 | }) 23 | 24 | var numberData = strings.Split(data[0], ",") 25 | 26 | boards := parseBoards(data[1:]) 27 | winner1 := play(numberData, boards) 28 | 29 | fmt.Printf("part 1: %v\n", winner1.score()) 30 | 31 | boards = parseBoards(data[1:]) 32 | var winner2 board 33 | for len(boards) > 0 { 34 | res := []board{} 35 | winner2 = play(numberData, boards) 36 | for _, b := range boards { 37 | if !reflect.DeepEqual(b, winner2) { 38 | res = append(res, b) 39 | } 40 | } 41 | boards = res 42 | } 43 | 44 | fmt.Printf("part 2: %v\n", winner2.score()) 45 | } 46 | 47 | func (me *board) score() int { 48 | return me.sumUnmarked() * me.marks[len(me.marks)-1] 49 | } 50 | 51 | func (me *board) mark(n int) bool { 52 | for y, row := range me.numbers { 53 | for x, v := range row { 54 | if v == n { 55 | me.numbers[y][x] = -1 56 | me.marks = append(me.marks, n) 57 | return me.bingo() 58 | } 59 | } 60 | } 61 | return false 62 | } 63 | 64 | func (me *board) bingo() bool { 65 | for i := 0; i < 5; i++ { 66 | rcount := 0 67 | ccount := 0 68 | for j := 0; j < 5; j++ { 69 | if me.numbers[i][j] == -1 { 70 | rcount++ 71 | } 72 | if me.numbers[j][i] == -1 { 73 | ccount++ 74 | } 75 | if rcount > 4 || ccount > 4 { 76 | return true 77 | } 78 | } 79 | } 80 | return false 81 | } 82 | 83 | func (me *board) sumUnmarked() int { 84 | sum := 0 85 | for _, row := range me.numbers { 86 | for _, v := range row { 87 | if v != -1 { 88 | sum += v 89 | } 90 | } 91 | } 92 | return sum 93 | } 94 | 95 | func play(numbers []string, boards []board) board { 96 | for _, n := range numbers { 97 | for idx, b := range boards { 98 | ni, _ := strconv.Atoi(n) 99 | if b.mark(ni) { 100 | boards[idx] = b 101 | return b 102 | } 103 | } 104 | } 105 | return board{} 106 | } 107 | 108 | func parseBoards(data []string) []board { 109 | var boards []board 110 | 111 | var b *board 112 | for _, line := range data { 113 | if line == "" { 114 | boards = append(boards, board{}) 115 | b = &boards[len(boards)-1] 116 | } else { 117 | row := strings.Fields(line) 118 | var rowInts []int 119 | for _, n := range row { 120 | ni, _ := strconv.Atoi(n) 121 | rowInts = append(rowInts, ni) 122 | } 123 | b.numbers = append(b.numbers, rowInts) 124 | } 125 | } 126 | 127 | return boards 128 | } 129 | -------------------------------------------------------------------------------- /2021/go/d5/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "aoc2021/util" 5 | "fmt" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | type Point struct { 11 | X, Y int 12 | } 13 | 14 | type Line struct { 15 | P1, P2 Point 16 | } 17 | 18 | // Advent of Code (AOC) 2021 Day 5 19 | func main() { 20 | 21 | coord1 := make(map[int]map[int]int) 22 | coord2 := make(map[int]map[int]int) 23 | 24 | util.ReadFile("../input/5a.txt", func(line string) { 25 | l := NewLine(line) 26 | if l.P1.X == l.P2.X || l.P1.Y == l.P2.Y { 27 | Plot(coord1, l) 28 | } 29 | Plot(coord2, l) 30 | }) 31 | 32 | fmt.Printf("part 1: %v\n", Count(coord1)) 33 | fmt.Printf("part 2: %v\n", Count(coord2)) 34 | } 35 | 36 | func NewPoint(pointString string) Point { 37 | parts := strings.Split(pointString, ",") 38 | x, _ := strconv.Atoi(parts[0]) 39 | y, _ := strconv.Atoi(parts[1]) 40 | return Point{X: x, Y: y} 41 | } 42 | 43 | func NewLine(lineString string) Line { 44 | parts := strings.Split(lineString, " -> ") 45 | return Line{P1: NewPoint(parts[0]), P2: NewPoint(parts[1])} 46 | } 47 | 48 | func numbersBetween(a int, b int) []int { 49 | var result []int 50 | if a > b { 51 | for i := a; i >= b; i-- { 52 | result = append(result, i) 53 | } 54 | } else { 55 | for i := a; i <= b; i++ { 56 | result = append(result, i) 57 | } 58 | } 59 | return result 60 | } 61 | 62 | func PlotPoint(coord map[int]map[int]int, x int, y int) { 63 | row := coord[y] 64 | if row == nil { 65 | row = make(map[int]int) 66 | coord[y] = row 67 | } 68 | row[x]++ 69 | } 70 | 71 | func Plot(coord map[int]map[int]int, line Line) { 72 | xs := numbersBetween(line.P1.X, line.P2.X) 73 | ys := numbersBetween(line.P1.Y, line.P2.Y) 74 | 75 | xl := len(xs) 76 | yl := len(ys) 77 | 78 | len := xl 79 | if yl > xl { 80 | len = yl 81 | } 82 | 83 | for i := 0; i < len; i++ { 84 | var x, y int 85 | if i < xl { 86 | x = xs[i] 87 | } else { 88 | x = xs[xl-1] 89 | } 90 | if i < yl { 91 | y = ys[i] 92 | } else { 93 | y = ys[yl-1] 94 | } 95 | PlotPoint(coord, x, y) 96 | } 97 | } 98 | 99 | func Count(coord map[int]map[int]int) int { 100 | count := 0 101 | for _, row := range coord { 102 | for _, val := range row { 103 | if val > 1 { 104 | count++ 105 | } 106 | } 107 | } 108 | return count 109 | } 110 | -------------------------------------------------------------------------------- /2021/go/d6/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "aoc2021/util" 5 | "fmt" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | // Advent of Code (AOC) 2021 Day 6 11 | func main() { 12 | fish := make(map[int]int) 13 | util.ReadFile("../input/6a.txt", func(line string) { 14 | parts := strings.Split(line, ",") 15 | for _, str := range parts { 16 | i, _ := strconv.Atoi(str) 17 | fish[i]++ 18 | } 19 | }) 20 | 21 | for i := 0; i < 80; i++ { 22 | fish = spawn(fish) 23 | } 24 | 25 | fmt.Printf("part 1: %v\n", count(fish)) 26 | 27 | for i := 80; i < 256; i++ { 28 | fish = spawn(fish) 29 | } 30 | 31 | fmt.Printf("part 2: %v\n", count(fish)) 32 | } 33 | 34 | func count(fish map[int]int) int { 35 | var res int 36 | for _, count := range fish { 37 | res += count 38 | } 39 | return res 40 | } 41 | 42 | func spawn(fish map[int]int) map[int]int { 43 | res := make(map[int]int) 44 | for timer, count := range fish { 45 | if timer == 0 { 46 | res[8] += count 47 | res[6] += count 48 | } else { 49 | res[timer-1] += count 50 | } 51 | } 52 | return res 53 | } 54 | -------------------------------------------------------------------------------- /2021/go/d7/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "aoc2021/util" 5 | "fmt" 6 | "sort" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | // Advent of Code (AOC) 2021 Day 7 12 | func main() { 13 | 14 | var crab []int 15 | util.ReadFile("../input/7a.txt", func(line string) { 16 | parts := strings.Split(line, ",") 17 | for _, str := range parts { 18 | i, _ := strconv.Atoi(str) 19 | crab = append(crab, i) 20 | } 21 | }) 22 | 23 | pos1 := median(crab) 24 | fuel1 := calculateCost(pos1, crab, func(dist int) int { return dist }) 25 | 26 | fmt.Printf("part 1: %v\n", fuel1) 27 | 28 | pos2 := mean(crab) 29 | fuel2 := calculateCost(pos2, crab, cost) 30 | 31 | fmt.Printf("part 2: %v\n", fuel2) 32 | } 33 | 34 | func calculateCost(pos int, crab []int, costForDistance func(int) int) int { 35 | fuel := 0 36 | for _, c := range crab { 37 | dist := pos - c 38 | if dist < 0 { 39 | dist = -dist 40 | } 41 | fuel += costForDistance(dist) 42 | } 43 | return fuel 44 | } 45 | 46 | func cost(dist int) int { 47 | cost := 0 48 | for i := 1; i <= dist; i++ { 49 | cost += i 50 | } 51 | return cost 52 | } 53 | 54 | func mean(n []int) int { 55 | sum := 0 56 | for _, v := range n { 57 | sum += v 58 | } 59 | return sum / len(n) 60 | } 61 | 62 | func median(n []int) int { 63 | sort.Ints(n) 64 | return n[len(n)/2] 65 | } 66 | -------------------------------------------------------------------------------- /2021/go/d9/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "aoc2021/util" 5 | "fmt" 6 | "sort" 7 | ) 8 | 9 | type point struct { 10 | x, y int 11 | } 12 | 13 | type empty struct{} 14 | 15 | // Advent of Code (AOC) 2021 Day 9 16 | func main() { 17 | 18 | var heightMap []string 19 | 20 | util.ReadFile("../input/9a.txt", func(line string) { 21 | heightMap = append(heightMap, line) 22 | }) 23 | 24 | lowPoints := lowPoints(heightMap) 25 | 26 | sum := 0 27 | for _, v := range lowPoints { 28 | sum += int(heightMap[v.y][v.x]) - '0' + 1 29 | } 30 | 31 | fmt.Printf("part 1: %v\n", sum) 32 | 33 | found := basins(heightMap) 34 | sizes := []int{} 35 | for _, basin := range found { 36 | sizes = append(sizes, len(basin)) 37 | } 38 | sort.Ints(sizes) 39 | sizes = sizes[len(sizes)-3:] 40 | multiply := 1 41 | for _, size := range sizes { 42 | multiply *= size 43 | } 44 | 45 | fmt.Printf("part 2: %v\n", multiply) 46 | } 47 | 48 | func basins(hmap []string) []map[point]empty { 49 | lowPoints := lowPoints(hmap) 50 | 51 | var found []map[point]empty 52 | for _, p := range lowPoints { 53 | basin := basin(p, hmap, make(map[point]empty)) 54 | found = append(found, basin) 55 | } 56 | return found 57 | } 58 | 59 | func basin(p point, hmap []string, included map[point]empty) map[point]empty { 60 | height := height(p.x, p.y, hmap) 61 | _, visited := included[p] 62 | if height >= '9' || visited { 63 | return make(map[point]empty) 64 | } 65 | 66 | included[p] = empty{} 67 | 68 | basin(point{x: p.x + 1, y: p.y}, hmap, included) 69 | basin(point{x: p.x - 1, y: p.y}, hmap, included) 70 | basin(point{x: p.x, y: p.y + 1}, hmap, included) 71 | basin(point{x: p.x, y: p.y - 1}, hmap, included) 72 | 73 | return included 74 | } 75 | 76 | func lowPoints(hmap []string) []point { 77 | lowPoints := []point{} 78 | for y, row := range hmap { 79 | for x := range row { 80 | adjacentMin := min(height(x-1, y, hmap), height(x+1, y, hmap), height(x, y-1, hmap), height(x, y+1, hmap)) 81 | if row[x] < adjacentMin { 82 | lowPoints = append(lowPoints, point{x: x, y: y}) 83 | } 84 | } 85 | } 86 | return lowPoints 87 | } 88 | 89 | func min(h ...byte) byte { 90 | var m byte = 128 91 | for _, v := range h { 92 | if v < m { 93 | m = v 94 | } 95 | } 96 | return m 97 | } 98 | 99 | func height(x int, y int, hmap []string) byte { 100 | if x < 0 || y < 0 || y >= len(hmap) || x >= len(hmap[y]) { 101 | return '9' 102 | } 103 | return hmap[y][x] 104 | } 105 | -------------------------------------------------------------------------------- /2021/go/go.mod: -------------------------------------------------------------------------------- 1 | module aoc2021 2 | 3 | go 1.17 4 | 5 | require github.com/google/go-cmp v0.5.6 6 | -------------------------------------------------------------------------------- /2021/go/go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= 2 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 3 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 4 | -------------------------------------------------------------------------------- /2021/go/util/util.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "bufio" 5 | "log" 6 | "os" 7 | ) 8 | 9 | type LineHandler func(string) 10 | 11 | func ReadFile(name string, handler LineHandler) { 12 | file, err := os.Open(name) 13 | if err != nil { 14 | log.Fatal(err) 15 | } 16 | defer file.Close() 17 | 18 | scanner := bufio.NewScanner(file) 19 | for scanner.Scan() { 20 | handler(scanner.Text()) 21 | } 22 | 23 | if err := scanner.Err(); err != nil { 24 | log.Fatal(err) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /2021/input/10a_test.txt: -------------------------------------------------------------------------------- 1 | [({(<(())[]>[[{[]{<()<>> 2 | [(()[<>])]({[<{<<[]>>( 3 | {([(<{}[<>[]}>{[]{[(<()> 4 | (((({<>}<{<{<>}{[]{[]{} 5 | [[<[([]))<([[{}[[()]]] 6 | [{[{({}]{}}([{[{{{}}([] 7 | {<[[]]>}<{[{[{[]{()[[[] 8 | [<(<(<(<{}))><([]([]() 9 | <{([([[(<>()){}]>(<<{{ 10 | <{([{{}}[<[[[<>{}]]]>[]] -------------------------------------------------------------------------------- /2021/input/11a.txt: -------------------------------------------------------------------------------- 1 | 4112256372 2 | 3143253712 3 | 4516848631 4 | 3783477137 5 | 3746723582 6 | 5861358884 7 | 4843351774 8 | 2316447621 9 | 6643817745 10 | 6366815868 -------------------------------------------------------------------------------- /2021/input/11a_test.txt: -------------------------------------------------------------------------------- 1 | 5483143223 2 | 2745854711 3 | 5264556173 4 | 6141336146 5 | 6357385478 6 | 4167524645 7 | 2176841721 8 | 6882881134 9 | 4846848554 10 | 5283751526 -------------------------------------------------------------------------------- /2021/input/12a.txt: -------------------------------------------------------------------------------- 1 | GC-zi 2 | end-zv 3 | lk-ca 4 | lk-zi 5 | GC-ky 6 | zi-ca 7 | end-FU 8 | iv-FU 9 | lk-iv 10 | lk-FU 11 | GC-end 12 | ca-zv 13 | lk-GC 14 | GC-zv 15 | start-iv 16 | zv-QQ 17 | ca-GC 18 | ca-FU 19 | iv-ca 20 | start-lk 21 | zv-FU 22 | start-zi -------------------------------------------------------------------------------- /2021/input/12a_test.txt: -------------------------------------------------------------------------------- 1 | start-A 2 | start-b 3 | A-c 4 | A-b 5 | b-d 6 | A-end 7 | b-end -------------------------------------------------------------------------------- /2021/input/12a_test2.txt: -------------------------------------------------------------------------------- 1 | dc-end 2 | HN-start 3 | start-kj 4 | dc-start 5 | dc-HN 6 | LN-dc 7 | HN-end 8 | kj-sa 9 | kj-HN 10 | kj-dc -------------------------------------------------------------------------------- /2021/input/12a_test3.txt: -------------------------------------------------------------------------------- 1 | fs-end 2 | he-DX 3 | fs-he 4 | start-DX 5 | pj-DX 6 | end-zg 7 | zg-sl 8 | zg-pj 9 | pj-he 10 | RW-he 11 | fs-DX 12 | pj-RW 13 | zg-RW 14 | start-pj 15 | he-WI 16 | zg-he 17 | pj-fs 18 | start-RW -------------------------------------------------------------------------------- /2021/input/13a_test.txt: -------------------------------------------------------------------------------- 1 | 6,10 2 | 0,14 3 | 9,10 4 | 0,3 5 | 10,4 6 | 4,11 7 | 6,0 8 | 6,12 9 | 4,1 10 | 0,13 11 | 10,12 12 | 3,4 13 | 3,0 14 | 8,4 15 | 1,10 16 | 2,14 17 | 8,10 18 | 9,0 19 | 20 | fold along y=7 21 | fold along x=5 -------------------------------------------------------------------------------- /2021/input/14a.txt: -------------------------------------------------------------------------------- 1 | PBVHVOCOCFFNBCNCCBHK 2 | 3 | FV -> C 4 | SS -> B 5 | SC -> B 6 | BP -> K 7 | VP -> S 8 | HK -> K 9 | FS -> F 10 | CC -> V 11 | VB -> P 12 | OP -> B 13 | FO -> N 14 | FH -> O 15 | VK -> N 16 | PV -> S 17 | HV -> O 18 | PF -> F 19 | HH -> F 20 | NK -> S 21 | NC -> S 22 | FC -> H 23 | FK -> K 24 | OO -> N 25 | HP -> C 26 | NN -> H 27 | BB -> H 28 | CN -> P 29 | PS -> N 30 | VF -> S 31 | CB -> B 32 | OH -> S 33 | CF -> C 34 | OK -> P 35 | CV -> V 36 | CS -> H 37 | KN -> B 38 | OV -> S 39 | HB -> C 40 | OS -> V 41 | PC -> B 42 | CK -> S 43 | PP -> K 44 | SN -> O 45 | VV -> C 46 | NS -> F 47 | PN -> K 48 | HS -> P 49 | VO -> B 50 | VC -> B 51 | NV -> P 52 | VS -> N 53 | FP -> F 54 | HO -> S 55 | KS -> O 56 | BN -> F 57 | VN -> P 58 | OC -> K 59 | SF -> P 60 | PO -> P 61 | SB -> O 62 | FN -> F 63 | OF -> F 64 | CP -> C 65 | HC -> O 66 | PH -> O 67 | BC -> O 68 | NO -> C 69 | BH -> C 70 | VH -> S 71 | KK -> O 72 | SV -> K 73 | KB -> K 74 | BS -> S 75 | HF -> B 76 | NH -> S 77 | PB -> N 78 | HN -> K 79 | SK -> B 80 | FB -> F 81 | KV -> S 82 | BF -> S 83 | ON -> S 84 | BV -> P 85 | KC -> S 86 | NB -> S 87 | NP -> B 88 | BK -> K 89 | NF -> C 90 | BO -> K 91 | KF -> B 92 | KH -> N 93 | SP -> O 94 | CO -> S 95 | KO -> V 96 | SO -> B 97 | CH -> C 98 | KP -> C 99 | FF -> K 100 | PK -> F 101 | OB -> H 102 | SH -> C -------------------------------------------------------------------------------- /2021/input/14a_test.txt: -------------------------------------------------------------------------------- 1 | NNCB 2 | 3 | CH -> B 4 | HH -> N 5 | CB -> H 6 | NH -> C 7 | HB -> C 8 | HC -> B 9 | HN -> C 10 | NN -> C 11 | BH -> H 12 | NC -> B 13 | NB -> B 14 | BN -> B 15 | BB -> N 16 | BC -> B 17 | CC -> N 18 | CN -> C -------------------------------------------------------------------------------- /2021/input/15a_test.txt: -------------------------------------------------------------------------------- 1 | 1163751742 2 | 1381373672 3 | 2136511328 4 | 3694931569 5 | 7463417111 6 | 1319128137 7 | 1359912421 8 | 3125421639 9 | 1293138521 10 | 2311944581 -------------------------------------------------------------------------------- /2021/input/16a.txt: -------------------------------------------------------------------------------- 1 | 6051639005B56008C1D9BB3CC9DAD5BE97A4A9104700AE76E672DC95AAE91425EF6AD8BA5591C00F92073004AC0171007E0BC248BE0008645982B1CA680A7A0CC60096802723C94C265E5B9699E7E94D6070C016958F99AC015100760B45884600087C6E88B091C014959C83E740440209FC89C2896A50765A59CE299F3640D300827902547661964D2239180393AF92A8B28F4401BCC8ED52C01591D7E9D2591D7E9D273005A5D127C99802C095B044D5A19A73DC0E9C553004F000DE953588129E372008F2C0169FDB44FA6C9219803E00085C378891F00010E8FF1AE398803D1BE25C743005A6477801F59CC4FA1F3989F420C0149ED9CF006A000084C5386D1F4401F87310E313804D33B4095AFBED32ABF2CA28007DC9D3D713300524BCA940097CA8A4AF9F4C00F9B6D00088654867A7BC8BCA4829402F9D6895B2E4DF7E373189D9BE6BF86B200B7E3C68021331CD4AE6639A974232008E663C3FE00A4E0949124ED69087A848002749002151561F45B3007218C7A8FE600FC228D50B8C01097EEDD7001CF9DE5C0E62DEB089805330ED30CD3C0D3A3F367A40147E8023221F221531C9681100C717002100B36002A19809D15003900892601F950073630024805F400150D400A70028C00F5002C00252600698400A700326C0E44590039687B313BF669F35C9EF974396EF0A647533F2011B340151007637C46860200D43085712A7E4FE60086003E5234B5A56129C91FC93F1802F12EC01292BD754BCED27B92BD754BCED27B100264C4C40109D578CA600AC9AB5802B238E67495391D5CFC402E8B325C1E86F266F250B77ECC600BE006EE00085C7E8DF044001088E31420BCB08A003A72BF87D7A36C994CE76545030047801539F649BF4DEA52CBCA00B4EF3DE9B9CFEE379F14608 -------------------------------------------------------------------------------- /2021/input/17a.txt: -------------------------------------------------------------------------------- 1 | target area: x=169..206, y=-108..-68 -------------------------------------------------------------------------------- /2021/input/17a_test.txt: -------------------------------------------------------------------------------- 1 | target area: x=20..30, y=-10..-5 -------------------------------------------------------------------------------- /2021/input/18a_test.txt: -------------------------------------------------------------------------------- 1 | [[[0,[5,8]],[[1,7],[9,6]]],[[4,[1,2]],[[1,4],2]]] 2 | [[[5,[2,8]],4],[5,[[9,9],0]]] 3 | [6,[[[6,2],[5,6]],[[7,6],[4,7]]]] 4 | [[[6,[0,7]],[0,9]],[4,[9,[9,0]]]] 5 | [[[7,[6,4]],[3,[1,3]]],[[[5,5],1],9]] 6 | [[6,[[7,3],[3,2]]],[[[3,8],[5,7]],4]] 7 | [[[[5,4],[7,7]],8],[[8,3],8]] 8 | [[9,3],[[9,9],[6,[4,9]]]] 9 | [[2,[[7,7],7]],[[5,8],[[9,3],[0,2]]]] 10 | [[[[5,2],5],[8,[3,7]]],[[5,[7,5]],[4,4]]] -------------------------------------------------------------------------------- /2021/input/19a_test.txt: -------------------------------------------------------------------------------- 1 | --- scanner 0 --- 2 | 404,-588,-901 3 | 528,-643,409 4 | -838,591,734 5 | 390,-675,-793 6 | -537,-823,-458 7 | -485,-357,347 8 | -345,-311,381 9 | -661,-816,-575 10 | -876,649,763 11 | -618,-824,-621 12 | 553,345,-567 13 | 474,580,667 14 | -447,-329,318 15 | -584,868,-557 16 | 544,-627,-890 17 | 564,392,-477 18 | 455,729,728 19 | -892,524,684 20 | -689,845,-530 21 | 423,-701,434 22 | 7,-33,-71 23 | 630,319,-379 24 | 443,580,662 25 | -789,900,-551 26 | 459,-707,401 27 | 28 | --- scanner 1 --- 29 | 686,422,578 30 | 605,423,415 31 | 515,917,-361 32 | -336,658,858 33 | 95,138,22 34 | -476,619,847 35 | -340,-569,-846 36 | 567,-361,727 37 | -460,603,-452 38 | 669,-402,600 39 | 729,430,532 40 | -500,-761,534 41 | -322,571,750 42 | -466,-666,-811 43 | -429,-592,574 44 | -355,545,-477 45 | 703,-491,-529 46 | -328,-685,520 47 | 413,935,-424 48 | -391,539,-444 49 | 586,-435,557 50 | -364,-763,-893 51 | 807,-499,-711 52 | 755,-354,-619 53 | 553,889,-390 54 | 55 | --- scanner 2 --- 56 | 649,640,665 57 | 682,-795,504 58 | -784,533,-524 59 | -644,584,-595 60 | -588,-843,648 61 | -30,6,44 62 | -674,560,763 63 | 500,723,-460 64 | 609,671,-379 65 | -555,-800,653 66 | -675,-892,-343 67 | 697,-426,-610 68 | 578,704,681 69 | 493,664,-388 70 | -671,-858,530 71 | -667,343,800 72 | 571,-461,-707 73 | -138,-166,112 74 | -889,563,-600 75 | 646,-828,498 76 | 640,759,510 77 | -630,509,768 78 | -681,-892,-333 79 | 673,-379,-804 80 | -742,-814,-386 81 | 577,-820,562 82 | 83 | --- scanner 3 --- 84 | -589,542,597 85 | 605,-692,669 86 | -500,565,-823 87 | -660,373,557 88 | -458,-679,-417 89 | -488,449,543 90 | -626,468,-788 91 | 338,-750,-386 92 | 528,-832,-391 93 | 562,-778,733 94 | -938,-730,414 95 | 543,643,-506 96 | -524,371,-870 97 | 407,773,750 98 | -104,29,83 99 | 378,-903,-323 100 | -778,-728,485 101 | 426,699,580 102 | -438,-605,-362 103 | -469,-447,-387 104 | 509,732,623 105 | 647,635,-688 106 | -868,-804,481 107 | 614,-800,639 108 | 595,780,-596 109 | 110 | --- scanner 4 --- 111 | 727,592,562 112 | -293,-554,779 113 | 441,611,-461 114 | -714,465,-776 115 | -743,427,-804 116 | -660,-479,-426 117 | 832,-632,460 118 | 927,-485,-438 119 | 408,393,-506 120 | 466,436,-512 121 | 110,16,151 122 | -258,-428,682 123 | -393,719,612 124 | -211,-452,876 125 | 808,-476,-593 126 | -575,615,604 127 | -485,667,467 128 | -680,325,-822 129 | -627,-443,-432 130 | 872,-547,-609 131 | 833,512,582 132 | 807,604,487 133 | 839,-516,451 134 | 891,-625,532 135 | -652,-548,-490 136 | 30,-46,-14 -------------------------------------------------------------------------------- /2021/input/1a_test.txt: -------------------------------------------------------------------------------- 1 | 199 2 | 200 3 | 208 4 | 210 5 | 200 6 | 207 7 | 240 8 | 269 9 | 260 10 | 263 11 | -------------------------------------------------------------------------------- /2021/input/20a_test.txt: -------------------------------------------------------------------------------- 1 | ..#.#..#####.#.#.#.###.##.....###.##.#..###.####..#####..#....#..#..##..###..######.###...####..#..#####..##..#.#####...##.#.#..#.##..#.#......#.###.######.###.####...#.##.##..#..#..#####.....#.#....###..#.##......#.....#..#..#..##..#...##.######.####.####.#.#...#.......#..#.#.#...####.##.#......#..#...##.#.##..#...##.#.##..###.#......#.#.......#.#.#.####.###.##...#.....####.#..#..#.##.#....##..#.####....##...##..#...#......#.#.......#.......##..####..#...#.#.#...##..#.#..###..#####........#..####......#..# 2 | 3 | #..#. 4 | #.... 5 | ##..# 6 | ..#.. 7 | ..### -------------------------------------------------------------------------------- /2021/input/22a_test.txt: -------------------------------------------------------------------------------- 1 | on x=10..12,y=10..12,z=10..12 2 | on x=11..13,y=11..13,z=11..13 3 | off x=9..11,y=9..11,z=9..11 4 | on x=10..10,y=10..10,z=10..10 -------------------------------------------------------------------------------- /2021/input/22a_test2.txt: -------------------------------------------------------------------------------- 1 | on x=-20..26,y=-36..17,z=-47..7 2 | on x=-20..33,y=-21..23,z=-26..28 3 | on x=-22..28,y=-29..23,z=-38..16 4 | on x=-46..7,y=-6..46,z=-50..-1 5 | on x=-49..1,y=-3..46,z=-24..28 6 | on x=2..47,y=-22..22,z=-23..27 7 | on x=-27..23,y=-28..26,z=-21..29 8 | on x=-39..5,y=-6..47,z=-3..44 9 | on x=-30..21,y=-8..43,z=-13..34 10 | on x=-22..26,y=-27..20,z=-29..19 11 | off x=-48..-32,y=26..41,z=-47..-37 12 | on x=-12..35,y=6..50,z=-50..-2 13 | off x=-48..-32,y=-32..-16,z=-15..-5 14 | on x=-18..26,y=-33..15,z=-7..46 15 | off x=-40..-22,y=-38..-28,z=23..41 16 | on x=-16..35,y=-41..10,z=-47..6 17 | off x=-32..-23,y=11..30,z=-14..3 18 | on x=-49..-5,y=-3..45,z=-29..18 19 | off x=18..30,y=-20..-8,z=-3..13 20 | on x=-41..9,y=-7..43,z=-33..15 21 | on x=-54112..-39298,y=-85059..-49293,z=-27449..7877 22 | on x=967..23432,y=45373..81175,z=27513..53682 -------------------------------------------------------------------------------- /2021/input/22a_test3.txt: -------------------------------------------------------------------------------- 1 | on x=-5..47,y=-31..22,z=-19..33 2 | on x=-44..5,y=-27..21,z=-14..35 3 | on x=-49..-1,y=-11..42,z=-10..38 4 | on x=-20..34,y=-40..6,z=-44..1 5 | off x=26..39,y=40..50,z=-2..11 6 | on x=-41..5,y=-41..6,z=-36..8 7 | off x=-43..-33,y=-45..-28,z=7..25 8 | on x=-33..15,y=-32..19,z=-34..11 9 | off x=35..47,y=-46..-34,z=-11..5 10 | on x=-14..36,y=-6..44,z=-16..29 11 | on x=-57795..-6158,y=29564..72030,z=20435..90618 12 | on x=36731..105352,y=-21140..28532,z=16094..90401 13 | on x=30999..107136,y=-53464..15513,z=8553..71215 14 | on x=13528..83982,y=-99403..-27377,z=-24141..23996 15 | on x=-72682..-12347,y=18159..111354,z=7391..80950 16 | on x=-1060..80757,y=-65301..-20884,z=-103788..-16709 17 | on x=-83015..-9461,y=-72160..-8347,z=-81239..-26856 18 | on x=-52752..22273,y=-49450..9096,z=54442..119054 19 | on x=-29982..40483,y=-108474..-28371,z=-24328..38471 20 | on x=-4958..62750,y=40422..118853,z=-7672..65583 21 | on x=55694..108686,y=-43367..46958,z=-26781..48729 22 | on x=-98497..-18186,y=-63569..3412,z=1232..88485 23 | on x=-726..56291,y=-62629..13224,z=18033..85226 24 | on x=-110886..-34664,y=-81338..-8658,z=8914..63723 25 | on x=-55829..24974,y=-16897..54165,z=-121762..-28058 26 | on x=-65152..-11147,y=22489..91432,z=-58782..1780 27 | on x=-120100..-32970,y=-46592..27473,z=-11695..61039 28 | on x=-18631..37533,y=-124565..-50804,z=-35667..28308 29 | on x=-57817..18248,y=49321..117703,z=5745..55881 30 | on x=14781..98692,y=-1341..70827,z=15753..70151 31 | on x=-34419..55919,y=-19626..40991,z=39015..114138 32 | on x=-60785..11593,y=-56135..2999,z=-95368..-26915 33 | on x=-32178..58085,y=17647..101866,z=-91405..-8878 34 | on x=-53655..12091,y=50097..105568,z=-75335..-4862 35 | on x=-111166..-40997,y=-71714..2688,z=5609..50954 36 | on x=-16602..70118,y=-98693..-44401,z=5197..76897 37 | on x=16383..101554,y=4615..83635,z=-44907..18747 38 | off x=-95822..-15171,y=-19987..48940,z=10804..104439 39 | on x=-89813..-14614,y=16069..88491,z=-3297..45228 40 | on x=41075..99376,y=-20427..49978,z=-52012..13762 41 | on x=-21330..50085,y=-17944..62733,z=-112280..-30197 42 | on x=-16478..35915,y=36008..118594,z=-7885..47086 43 | off x=-98156..-27851,y=-49952..43171,z=-99005..-8456 44 | off x=2032..69770,y=-71013..4824,z=7471..94418 45 | on x=43670..120875,y=-42068..12382,z=-24787..38892 46 | off x=37514..111226,y=-45862..25743,z=-16714..54663 47 | off x=25699..97951,y=-30668..59918,z=-15349..69697 48 | off x=-44271..17935,y=-9516..60759,z=49131..112598 49 | on x=-61695..-5813,y=40978..94975,z=8655..80240 50 | off x=-101086..-9439,y=-7088..67543,z=33935..83858 51 | off x=18020..114017,y=-48931..32606,z=21474..89843 52 | off x=-77139..10506,y=-89994..-18797,z=-80..59318 53 | off x=8476..79288,y=-75520..11602,z=-96624..-24783 54 | on x=-47488..-1262,y=24338..100707,z=16292..72967 55 | off x=-84341..13987,y=2429..92914,z=-90671..-1318 56 | off x=-37810..49457,y=-71013..-7894,z=-105357..-13188 57 | off x=-27365..46395,y=31009..98017,z=15428..76570 58 | off x=-70369..-16548,y=22648..78696,z=-1892..86821 59 | on x=-53470..21291,y=-120233..-33476,z=-44150..38147 60 | off x=-93533..-4276,y=-16170..68771,z=-104985..-24507 -------------------------------------------------------------------------------- /2021/input/24a_test.txt: -------------------------------------------------------------------------------- 1 | inp w 2 | add w 10 3 | inp y 4 | add y 2 5 | add w y -------------------------------------------------------------------------------- /2021/input/25a_test.txt: -------------------------------------------------------------------------------- 1 | v...>>.vv> 2 | .vv>>.vv.. 3 | >>.>v>...v 4 | >>v>>.>.v. 5 | v>v.vv.v.. 6 | >.>>..v... 7 | .vv..>.>v. 8 | v.v..>>v.v 9 | ....v..v.> -------------------------------------------------------------------------------- /2021/input/2a_test.txt: -------------------------------------------------------------------------------- 1 | forward 5 2 | down 5 3 | forward 8 4 | up 3 5 | down 8 6 | forward 2 -------------------------------------------------------------------------------- /2021/input/3a_test.txt: -------------------------------------------------------------------------------- 1 | 00100 2 | 11110 3 | 10110 4 | 10111 5 | 10101 6 | 01111 7 | 00111 8 | 11100 9 | 10000 10 | 11001 11 | 00010 12 | 01010 -------------------------------------------------------------------------------- /2021/input/4a_test.txt: -------------------------------------------------------------------------------- 1 | 7,4,9,5,11,17,23,2,0,14,21,24,10,16,13,6,15,25,12,22,18,20,8,19,3,26,1 2 | 3 | 22 13 17 11 0 4 | 8 2 23 4 24 5 | 21 9 14 16 7 6 | 6 10 3 18 5 7 | 1 12 20 15 19 8 | 9 | 3 15 0 2 22 10 | 9 18 13 17 5 11 | 19 8 7 25 23 12 | 20 11 10 24 4 13 | 14 21 16 12 6 14 | 15 | 14 21 17 24 4 16 | 10 16 15 9 19 17 | 18 8 23 26 20 18 | 22 11 13 6 5 19 | 2 0 12 3 7 -------------------------------------------------------------------------------- /2021/input/5a_test.txt: -------------------------------------------------------------------------------- 1 | 0,9 -> 5,9 2 | 8,0 -> 0,8 3 | 9,4 -> 3,4 4 | 2,2 -> 2,1 5 | 7,0 -> 7,4 6 | 6,4 -> 2,0 7 | 0,9 -> 2,9 8 | 3,4 -> 1,4 9 | 0,0 -> 8,8 10 | 5,5 -> 8,2 -------------------------------------------------------------------------------- /2021/input/6a.txt: -------------------------------------------------------------------------------- 1 | 4,1,1,4,1,2,1,4,1,3,4,4,1,5,5,1,3,1,1,1,4,4,3,1,5,3,1,2,5,1,1,5,1,1,4,1,1,1,1,2,1,5,3,4,4,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,5,1,1,1,4,1,2,3,5,1,2,2,4,1,4,4,4,1,2,5,1,2,1,1,1,1,1,1,4,1,1,4,3,4,2,1,3,1,1,1,3,5,5,4,3,4,1,5,1,1,1,2,2,1,3,1,2,4,1,1,3,3,1,3,3,1,1,3,1,5,1,1,3,1,1,1,5,4,1,1,1,1,4,1,1,3,5,4,3,1,1,5,4,1,1,2,5,4,2,1,4,1,1,1,1,3,1,1,1,1,4,1,1,1,1,2,4,1,1,1,1,3,1,1,5,1,1,1,1,1,1,4,2,1,3,1,1,1,2,4,2,3,1,4,1,2,1,4,2,1,4,4,1,5,1,1,4,4,1,2,2,1,1,1,1,1,1,1,1,1,1,1,4,5,4,1,3,1,3,1,1,1,5,3,5,5,2,2,1,4,1,4,2,1,4,1,2,1,1,2,1,1,5,4,2,1,1,1,2,4,1,1,1,1,2,1,1,5,1,1,2,2,5,1,1,1,1,1,2,4,2,3,1,2,1,5,4,5,1,4 -------------------------------------------------------------------------------- /2021/input/6a_test.txt: -------------------------------------------------------------------------------- 1 | 3,4,3,1,2 -------------------------------------------------------------------------------- /2021/input/7a_test.txt: -------------------------------------------------------------------------------- 1 | 16,1,2,0,4,2,7,1,2,14 -------------------------------------------------------------------------------- /2021/input/8a_test.txt: -------------------------------------------------------------------------------- 1 | be cfbegad cbdgef fgaecd cgeb fdcge agebfd fecdb fabcd edb | fdgacbe cefdb cefbgd gcbe 2 | edbfga begcd cbg gc gcadebf fbgde acbgfd abcde gfcbed gfec | fcgedb cgb dgebacf gc 3 | fgaebd cg bdaec gdafb agbcfd gdcbef bgcad gfac gcb cdgabef | cg cg fdcagb cbg 4 | fbegcd cbd adcefb dageb afcb bc aefdc ecdab fgdeca fcdbega | efabcd cedba gadfec cb 5 | aecbfdg fbg gf bafeg dbefa fcge gcbea fcaegb dgceab fcbdga | gecf egdcabf bgf bfgea 6 | fgeab ca afcebg bdacfeg cfaedg gcfdb baec bfadeg bafgc acf | gebdcfa ecba ca fadegcb 7 | dbcfg fgd bdegcaf fgec aegbdf ecdfab fbedc dacgb gdcebf gf | cefg dcbef fcge gbcadfe 8 | bdfegc cbegaf gecbf dfcage bdacg ed bedf ced adcbefg gebcd | ed bcgafe cdgba cbgef 9 | egadfb cdbfeg cegd fecab cgb gbdefca cg fgcdab egfdb bfceg | gbdfcae bgc cg cgb 10 | gcafb gcf dcaebfg ecagb gf abcdeg gaef cafbge fdbac fegbdc | fgae cfgab fg bagce -------------------------------------------------------------------------------- /2021/input/9a_test.txt: -------------------------------------------------------------------------------- 1 | 2199943210 2 | 3987894921 3 | 9856789892 4 | 8767896789 5 | 9899965678 -------------------------------------------------------------------------------- /2021/java/src/main/java/aoc2021/D1.java: -------------------------------------------------------------------------------- 1 | package aoc2021; 2 | 3 | import java.nio.file.Files; 4 | import java.nio.file.Path; 5 | import java.util.LinkedList; 6 | 7 | /** 8 | * Advent of Code (AOC) 2021 Day 1 9 | */ 10 | public class D1 { 11 | 12 | public static void main(String ... args) throws Exception { 13 | 14 | Counter part1 = Files.lines(Path.of("../input/1a.txt")) 15 | .map(Integer::parseInt) 16 | .reduce(new Counter(), (c, d) -> c.apply(d) , (a,b) -> b); 17 | 18 | System.out.printf("part 1: %d\n", part1.count); 19 | 20 | CounterWithSlider part2 = Files.lines(Path.of("../input/1a.txt")) 21 | .map(Integer::parseInt) 22 | .reduce(new CounterWithSlider(), (c, d) -> c.accept(d), (a,b) -> b); 23 | 24 | System.out.printf("part 2: %d\n", part2.count); 25 | } 26 | 27 | record Counter(int prev, int count) { 28 | 29 | Counter() { 30 | this(Integer.MAX_VALUE, 0); 31 | } 32 | 33 | Counter apply(int depth) { 34 | return new Counter(depth, prev == Integer.MAX_VALUE || depth <= prev ? count : count+1); 35 | } 36 | } 37 | 38 | record CounterWithSlider(int prev, int count, LinkedList slider) { 39 | 40 | CounterWithSlider() { 41 | this(Integer.MAX_VALUE, 0, new LinkedList<>()); 42 | } 43 | 44 | CounterWithSlider accept(int depth) { 45 | slider.add(depth); 46 | if (slider.size() < 3) { 47 | return new CounterWithSlider(prev, count, slider); 48 | } else if (slider.size() > 3) { 49 | slider.removeFirst(); 50 | } 51 | int sum = slider.stream().mapToInt(Integer::intValue).sum(); 52 | return new CounterWithSlider(sum, prev != Integer.MAX_VALUE && sum > prev ? count +1 : count, slider); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /2021/java/src/main/java/aoc2021/D10.java: -------------------------------------------------------------------------------- 1 | package aoc2021; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Files; 5 | import java.nio.file.Path; 6 | import java.util.LinkedList; 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.Optional; 10 | 11 | /** 12 | * Advent of Code (AOC) 2021 Day 10 part 1 and 2 13 | */ 14 | public class D10 { 15 | 16 | record NavigationLine(Optional firstIllegal, List missing) {}; 17 | 18 | public static void main(String ... args) throws IOException { 19 | 20 | var navLines = Files.lines(Path.of("../input/10a.txt")) 21 | .map(row -> parseLine(row)) 22 | .toList(); 23 | 24 | long scorePart1 = navLines.stream() 25 | .flatMap(l -> l.firstIllegal.stream()) 26 | .mapToLong(c -> scoresPart1.get(c)) 27 | .sum(); 28 | 29 | System.out.printf("part 1: %d\n", scorePart1); 30 | 31 | var scorePart2 = navLines.stream() 32 | .map(l -> l.missing) 33 | .filter(m -> !m.isEmpty()) 34 | .map(m -> scorePart2(m)) 35 | .sorted() 36 | .toList(); 37 | 38 | System.out.printf("part 2: %d\n", scorePart2.get(scorePart2.size()/2)); 39 | } 40 | 41 | static Map braces = Map.of('(',')','{','}','[',']','<','>'); 42 | 43 | static NavigationLine parseLine(String line) { 44 | LinkedList stack = new LinkedList<>(); 45 | for(char c : line.toCharArray()) { 46 | if (braces.containsKey(c)) { 47 | stack.push(braces.get(c)); 48 | } else if (stack.pop() != (char)c) { 49 | return new NavigationLine(Optional.of(c), List.of()); 50 | } 51 | } 52 | return new NavigationLine(Optional.empty(), stack); 53 | } 54 | 55 | static Map scoresPart1 = Map.of(')', 3l, ']', 57l, '}', 1197l, '>', 25137l); 56 | static Map scoresPart2 = Map.of(')', 1l, ']', 2l, '}', 3l, '>', 4l); 57 | 58 | static long scorePart2(List row) { 59 | return row.stream() 60 | .map(c -> scoresPart2.get(c)) 61 | .reduce(0l, (a, b) -> a*5+b, (a,b) -> b); 62 | } 63 | } -------------------------------------------------------------------------------- /2021/java/src/main/java/aoc2021/D11.java: -------------------------------------------------------------------------------- 1 | package aoc2021; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Files; 5 | import java.nio.file.Path; 6 | import java.util.stream.IntStream; 7 | import java.util.stream.Stream; 8 | 9 | /** 10 | * Advent of Code (AOC) 2021 Day 11 part 1 and 2 11 | */ 12 | public class D11 { 13 | 14 | record Point(int x, int y) {}; 15 | 16 | public static void main(String ... args) throws IOException { 17 | 18 | var map = Files.lines(Path.of("../input/11a.txt")) 19 | .map(row -> row.chars().map(i -> i - '0').toArray()) 20 | .toArray(int[][]::new); 21 | 22 | var countFlashes = IntStream.range(0, 100) 23 | .mapToLong(i -> step(map)).sum(); 24 | 25 | System.out.printf("part 1: %d\n", countFlashes); 26 | 27 | int step = 100; 28 | while(!allFlashes(map)) { 29 | step(map); 30 | step++; 31 | } 32 | 33 | System.out.printf("part 2: %d\n", step); 34 | } 35 | 36 | /** Return true if all points in the map flashed */ 37 | static boolean allFlashes(int[][] map) { 38 | return !allPoints(map).anyMatch(p -> map[p.y][p.x] != 0); 39 | } 40 | 41 | /** Run one step and return flashes. */ 42 | static long step(int[][] map) { 43 | allPoints(map).forEach(p -> map[p.y][p.x]++); // increase energy by one 44 | return allPoints(map).mapToLong(p -> flash(map, p)).sum(); // flash 45 | } 46 | 47 | /** Flash given point and adjacent points if enough energy. 48 | * @return return count of flashes triggered 49 | */ 50 | static long flash(int[][] map, Point p) { 51 | if (map[p.y][p.x] < 10) { // only flash 10 or larger 52 | return 0; 53 | } 54 | map[p.y][p.x] = 0; // reset to 0 after flashing 55 | return 1 + adjacentPoints(p, map) // count this + adjacent flashesh 56 | .filter(a -> map[a.y][a.x] != 0) // can flash only once, filter out already flashed (energy 0) 57 | .mapToLong(a -> { map[a.y][a.x]++; return flash(map, a); }) // increase adjacent energy and flash if needed 58 | .sum(); 59 | } 60 | 61 | /** Return stream of all adjacent points for given point inside map */ 62 | static Stream adjacentPoints(Point origin, int[][] map) { 63 | return pointsByBox(new Point(origin.x-1, origin.y-1), new Point(origin.x+1, origin.y+1)) 64 | // filter out points outside map and origin point itself 65 | .filter(p -> p.x >= 0 && p.y >= 0 && p.y < map.length && p.x < map[p.y].length && !(p.x == origin.x && p.y == origin.y)); 66 | } 67 | 68 | /** Return stream of all points for map */ 69 | static Stream allPoints(int[][] map) { 70 | return pointsByBox(new Point(0, 0), new Point(map[0].length-1, map.length-1)); 71 | } 72 | 73 | /** Return stream of all points inside box defined by points p1 and p2 */ 74 | static Stream pointsByBox(Point p1, Point p2) { 75 | return IntStream.range(p1.y, p2.y+1).mapToObj(i -> i) 76 | .flatMap(y -> IntStream.range(p1.x, p2.x+1).mapToObj(x -> new Point(x, y))); 77 | } 78 | } -------------------------------------------------------------------------------- /2021/java/src/main/java/aoc2021/D12.java: -------------------------------------------------------------------------------- 1 | package aoc2021; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Files; 5 | import java.nio.file.Path; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.TreeMap; 9 | import java.util.stream.Stream; 10 | 11 | import static java.util.stream.Collectors.groupingBy; 12 | import static java.util.stream.Collectors.mapping; 13 | import static java.util.stream.Collectors.toList; 14 | 15 | /** 16 | * Advent of Code (AOC) 2021 Day 12 17 | */ 18 | public class D12 { 19 | 20 | public static void main(String ... args) throws IOException { 21 | 22 | Map> caveToCaves = Files.lines(Path.of("../input/12a.txt")) 23 | .map(line -> line.split("-")) // split to cave a and b 24 | .flatMap(l -> Stream.of(l, new String[] {l[1], l[0]})) // build both directions a -> b and b -> a 25 | .collect(groupingBy(l -> l[0], mapping(l -> l[1], toList()))); // to map from cave to caves 26 | 27 | var paths1 = findPaths(List.of("start"), Map.of(), 1, caveToCaves).toList(); 28 | 29 | System.out.printf("part 1: %s\n", paths1.size()); 30 | 31 | var paths2 = findPaths(List.of("start"), Map.of(), 2, caveToCaves).toList(); 32 | 33 | System.out.printf("part 2: %s\n", paths2.size()); 34 | } 35 | 36 | static Stream> findPaths(List currentPath, Map prevVisited, int maxSingleSmallVisits, Map> caveToCaves) { 37 | String currentCave = currentPath.get(currentPath.size()-1); 38 | 39 | if (currentCave.equals("end")) { 40 | return Stream.of(currentPath); 41 | } 42 | 43 | var visited = new TreeMap<>(prevVisited); 44 | visited.merge(currentCave, 1, (k,v) -> v+1); 45 | 46 | return caveToCaves.get(currentCave).stream() // next caves 47 | .filter(next -> !alreadyMaxVisits(next, visited, maxSingleSmallVisits)) // filter out ones cannot be visited anymore 48 | .map(next -> appendToList(currentPath, next)) // build next path = current path + next cave 49 | .flatMap(path -> findPaths(path, Map.copyOf(visited), maxSingleSmallVisits, caveToCaves)); // solve next paths forward 50 | } 51 | 52 | static List appendToList(List path, String cave) { 53 | return Stream.concat(path.stream(), Stream.of(cave)).toList(); 54 | } 55 | 56 | static boolean alreadyMaxVisits(String cave, Map visited, int maxSingleSmallVisits) { 57 | if (isBigCave(cave) || !visited.containsKey(cave)) { 58 | return false; // big cave or never visited 59 | } else if (cave.equals("start") || cave.equals("end") || maxSingleSmallVisits < 2) { 60 | return true; // start or end can be visisted only once 61 | } else { 62 | // do we have any small cave already visited max times 63 | return visited.entrySet().stream() 64 | .anyMatch(e -> !isBigCave(e.getKey()) && e.getValue() >= maxSingleSmallVisits); 65 | } 66 | } 67 | 68 | static boolean isBigCave(String cave) { 69 | return Character.isUpperCase(cave.charAt(0)); 70 | } 71 | } -------------------------------------------------------------------------------- /2021/java/src/main/java/aoc2021/D13.java: -------------------------------------------------------------------------------- 1 | package aoc2021; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Files; 5 | import java.nio.file.Path; 6 | import java.util.List; 7 | 8 | /** 9 | * Advent of Code (AOC) 2021 Day 13 10 | */ 11 | public class D13 { 12 | 13 | static final String FOLD_ALONG = "fold along "; 14 | 15 | public static void main(String ... args) throws IOException { 16 | List data = Files.lines(Path.of("../input/13a.txt")).filter(l -> !l.isBlank()).toList(); 17 | 18 | List paper = data.stream().filter(l -> !l.startsWith(FOLD_ALONG)).map(Dot::parse).toList(); 19 | List folds = data.stream().filter(l -> l.startsWith(FOLD_ALONG)).map(Fold::parse).toList(); 20 | 21 | paper = folds.get(0).apply(paper); 22 | System.out.printf("part 1: %s\n", paper.size()); 23 | 24 | paper = folds.stream().skip(1) 25 | .reduce(paper, (a,b) -> b.apply(a), (a,b) -> b); 26 | System.out.printf("part 2:\n"); 27 | print(paper); 28 | } 29 | 30 | record Dot(int x, int y) { 31 | static Dot parse(String dot) { 32 | String[] parts = dot.split(","); 33 | return new Dot(Integer.parseInt(parts[0]), Integer.parseInt(parts[1])); 34 | } 35 | } 36 | 37 | record Fold(char axle, int pos) { 38 | static Fold parse(String fold) { 39 | String parts[] = fold.replace(FOLD_ALONG, "").split("="); 40 | return new Fold(parts[0].charAt(0), Integer.parseInt(parts[1])); 41 | } 42 | 43 | List apply(List paper) { 44 | return paper.stream().map(p -> apply(p)).distinct().toList(); 45 | } 46 | 47 | Dot apply(Dot d) { 48 | if (axle == 'x' && d.x > pos) { 49 | return new Dot(2*pos-d.x, d.y); 50 | } else if (axle == 'y' && d.y > pos) { 51 | return new Dot(d.x, 2*pos-d.y); 52 | } 53 | return d; 54 | } 55 | } 56 | 57 | static void print(List paper) { 58 | int height = paper.stream().mapToInt(d -> d.y).max().getAsInt(); 59 | int width = paper.stream().mapToInt(d -> d.x).max().getAsInt(); 60 | for (int y = 0; y <= height; y++) { 61 | for(int x = 0; x <= width; x++) { 62 | System.out.print(charAt(x, y, paper)); 63 | } 64 | System.out.println(); 65 | } 66 | } 67 | 68 | static String charAt(int x, int y, List paper) { 69 | return paper.stream().anyMatch(d -> d.x == x && d.y == y) ? "█" : " "; 70 | } 71 | } -------------------------------------------------------------------------------- /2021/java/src/main/java/aoc2021/D17.java: -------------------------------------------------------------------------------- 1 | package aoc2021; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Files; 5 | import java.nio.file.Path; 6 | import java.util.regex.Pattern; 7 | import java.util.stream.IntStream; 8 | import java.util.stream.Stream; 9 | 10 | /** 11 | * Advent of Code (AOC) 2021 Day 17 12 | */ 13 | public class D17 { 14 | 15 | record Area(int x1, int x2, int y1, int y2) {} 16 | record Probe(int x, int y, int xv, int yv, int maxHeight) { 17 | Probe move() { 18 | return new Probe(x+xv, y+yv, Math.max(0, xv-1), yv - 1, Math.max(maxHeight, y+yv)); 19 | } 20 | boolean hit(Area target) { 21 | return x >= target.x1 && x <= target.x2 && y <= target.y1 && y >= target.y2; 22 | } 23 | boolean miss(Area target) { 24 | return x > target.x2 || y < target.y2; 25 | } 26 | } 27 | 28 | public static void main(String... args) throws IOException { 29 | int[] numbers = Pattern.compile("(-?\\d+)") 30 | .matcher(Files.readString(Path.of("../input/17a.txt"))).results() 31 | .mapToInt(mr -> Integer.parseInt(mr.group(1))).toArray(); 32 | Area target = new Area(numbers[0], numbers[1], numbers[3], numbers[2]); 33 | 34 | var hits = IntStream.rangeClosed(0, target.x2).boxed() // iterate x velocities 35 | .flatMap(xv -> IntStream.range(target.y2, 1000) // and y velocitios 36 | .mapToObj(yv -> shoot(xv, yv, target))) // shoot each x,y 37 | .filter(r -> r.hit(target)).toList(); // collect hits to list 38 | 39 | System.out.printf("part 1: %d\n", hits.stream().mapToInt(i -> i.maxHeight).max().getAsInt()); 40 | System.out.printf("part 2: %d\n", hits.size()); 41 | } 42 | 43 | static Probe shoot(int xv, int yv, Area target) { 44 | return Stream.iterate(new Probe(0, 0, xv, yv, 0), Probe::move) // iterate probe moves 45 | .dropWhile(p -> !p.hit(target) && !p.miss(target)) // until hit or miss 46 | .findFirst().get(); 47 | } 48 | } -------------------------------------------------------------------------------- /2021/java/src/main/java/aoc2021/D2.java: -------------------------------------------------------------------------------- 1 | package aoc2021; 2 | 3 | import java.nio.file.Files; 4 | import java.nio.file.Path; 5 | 6 | /** 7 | * Advent of Code (AOC) 2021 Day 2 8 | */ 9 | public class D2 { 10 | 11 | public static void main(String ... args) throws Exception { 12 | 13 | var pos = Files.lines(Path.of("../input/2a.txt")) 14 | .map(line -> Instruction.parse(line)) 15 | .reduce(new Position(0, 0), (p, i) -> switch(i.dir()) { 16 | case "forward" -> new Position(p.horizontal() + i.amount(), p.depth(), p.aim()); 17 | case "down" -> new Position(p.horizontal(), p.depth() + i.amount(), p.aim()); 18 | case "up" -> new Position(p.horizontal(), p.depth() - i.amount(), p.aim()); 19 | default -> throw new IllegalArgumentException("invalid instruction " + i.dir()); 20 | }, (a,b) -> b); 21 | 22 | System.out.printf("part 1: %d\n", pos.depth() * pos.horizontal()); 23 | 24 | pos = Files.lines(Path.of("../input/2a.txt")) 25 | .map(line -> Instruction.parse(line)) 26 | .reduce(new Position(0, 0, 0), (p, i) -> switch(i.dir()) { 27 | case "forward" -> new Position(p.horizontal() + i.amount(), p.depth() + i.amount() * p.aim(), p.aim()); 28 | case "down" -> new Position(p.horizontal(), p.depth(), p.aim() + i.amount()); 29 | case "up" -> new Position(p.horizontal(), p.depth(), p.aim() - i.amount()); 30 | default -> throw new IllegalArgumentException("invalid instruction " + i.dir()); 31 | }, (a,b) -> b); 32 | 33 | System.out.printf("part 2: %d\n", pos.depth() * pos.horizontal()); 34 | } 35 | 36 | record Instruction(String dir, int amount) { 37 | static Instruction parse(String line) { 38 | String parts[] = line.split(" "); 39 | return new Instruction(parts[0], Integer.parseInt(parts[1])); 40 | } 41 | } 42 | 43 | record Position(int horizontal, int depth, int aim) { 44 | Position(int horizontal, int depth) { 45 | this(0, 0, 0); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /2021/java/src/main/java/aoc2021/D25.java: -------------------------------------------------------------------------------- 1 | package aoc2021; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Files; 5 | import java.nio.file.Path; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | /** 11 | * Advent of Code (AOC) 2021 Day 25 Sea Cucumber 12 | */ 13 | public class D25 { 14 | 15 | record Location(int x, int y){} 16 | 17 | public static void main(String... args) throws IOException { 18 | 19 | List data = Files.readAllLines(Path.of("../input/25a.txt")); 20 | Map map = new HashMap<>(); 21 | for (int y = 0; y < data.size(); y++) { 22 | for (int x = 0; x < data.get(y).length(); x++) { 23 | char ch = data.get(y).charAt(x); 24 | if (data.get(y).charAt(x) != '.') { 25 | map.put(new Location(x,y), ch); 26 | } 27 | } 28 | } 29 | 30 | boolean moving = true; 31 | int steps = 0; 32 | while(moving) { 33 | var next = move('>', map, data.get(0).length(), data.size()); 34 | next = move('v', next, data.get(0).length(), data.size()); 35 | steps++; 36 | moving = !next.equals(map); 37 | map = next; 38 | } 39 | System.out.printf("part 1: %s\n", steps); 40 | } 41 | 42 | 43 | static Map move(char ch, Map map, int width, int height) { 44 | Map next = new HashMap<>(); 45 | for (var e : map.entrySet()) { 46 | if (e.getValue() == ch) { 47 | var moveTo = ch == '>' ? new Location((e.getKey().x+1) % width, e.getKey().y) : new Location(e.getKey().x, (e.getKey().y+1)%height); 48 | if (!map.containsKey(moveTo)) { 49 | next.put(moveTo, ch); 50 | } else { 51 | next.put(e.getKey(), ch); 52 | } 53 | } else { 54 | next.put(e.getKey(), e.getValue()); 55 | } 56 | } 57 | return next; 58 | } 59 | } -------------------------------------------------------------------------------- /2021/java/src/main/java/aoc2021/D3.java: -------------------------------------------------------------------------------- 1 | package aoc2021; 2 | 3 | import java.nio.file.Files; 4 | import java.nio.file.Path; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | import java.util.stream.Collectors; 8 | 9 | /** 10 | * Advent of Code (AOC) 2021 Day 3 11 | */ 12 | public class D3 { 13 | 14 | public static void main(String ... args) throws Exception { 15 | List data = Files.lines(Path.of("../input/3a.txt")) 16 | .map(line -> line.chars().map(c -> c - '0').toArray()) 17 | .toList(); 18 | 19 | int bitLen = data.get(0).length; 20 | int[] oneCounts = data.stream().reduce(new int[bitLen], (count, line) -> sum(count, line), (a,b) -> b); // count ones in int array 21 | int[] gammaBits = Arrays.stream(oneCounts).map(oneCount -> oneCount*2 >= data.size() ? 1 : 0).toArray(); // translate most common bits 22 | int[] epsilonBits = Arrays.stream(oneCounts).map(oneCount -> oneCount*2 < data.size() ? 1 : 0).toArray(); // translate least common bits 23 | 24 | System.out.printf("part 1: %d\n", bitsToNumber(gammaBits) * bitsToNumber(epsilonBits)); 25 | 26 | int[] o2Bits = filter(data, true); 27 | int[] co2Bits = filter(data, false); 28 | 29 | System.out.printf("part 2: %d\n", bitsToNumber(o2Bits) * bitsToNumber(co2Bits)); 30 | } 31 | 32 | static int bitsToNumber(int[] bits) { 33 | return Integer.parseInt(Arrays.stream(bits).mapToObj(i -> Integer.toString(i)).collect(Collectors.joining()), 2); 34 | } 35 | 36 | static int[] sum(int[] a, int[] b) { 37 | int[] r = new int[a.length]; 38 | for (int i = 0; i < a.length; i++) { 39 | r[i] = a[i] + b[i]; 40 | } 41 | return r; 42 | } 43 | 44 | static int mostCommonBitInPos(List data, int pos) { 45 | var ones = data.stream().map(l -> l[pos]).filter(c -> c == 1).count(); 46 | return ones*2 >= data.size() ? 1 : 0; 47 | } 48 | 49 | static int[] filter(List data, boolean selectMostCommon) { 50 | int pos = 0; 51 | while(data.size() > 1) { 52 | int p2 = pos; 53 | int mostCommonBit = mostCommonBitInPos(data, pos); 54 | int accept = selectMostCommon ? mostCommonBit : mostCommonBit == 1 ? 0 : 1; 55 | data = data.stream().filter(l -> l[p2] == accept).toList(); 56 | pos++; 57 | } 58 | return data.get(0); 59 | } 60 | } -------------------------------------------------------------------------------- /2021/java/src/main/java/aoc2021/D5.java: -------------------------------------------------------------------------------- 1 | package aoc2021; 2 | 3 | import java.nio.file.Files; 4 | import java.nio.file.Path; 5 | import java.util.Collections; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.TreeMap; 9 | import java.util.stream.Collectors; 10 | import java.util.stream.IntStream; 11 | 12 | /** 13 | * Advent of Code (AOC) 2021 Day 5 14 | */ 15 | public class D5 { 16 | 17 | record Point(int x, int y) { 18 | static Point parse(String str) { 19 | var parts = str.split(","); 20 | return new Point(Integer.parseInt(parts[0]), Integer.parseInt(parts[1])); 21 | } 22 | } 23 | 24 | record Line(Point p1, Point p2) { 25 | static Line parse(String str) { 26 | var parts = str.split(" -> "); 27 | return new Line(Point.parse(parts[0]), Point.parse(parts[1])); 28 | } 29 | } 30 | 31 | private static List range(int x1, int x2) { 32 | if (x1 < x2) { 33 | return IntStream.rangeClosed(x1, x2).boxed().toList(); 34 | } else { 35 | var l = IntStream.rangeClosed(x2, x1).boxed().collect(Collectors.toList()); 36 | Collections.reverse(l); 37 | return l; 38 | } 39 | } 40 | 41 | private static void plot(Map> coords, int x, int y) { 42 | var row = coords.computeIfAbsent(y, ys -> new TreeMap<>()); 43 | row.merge(x, 1, (k, v) -> v + 1); 44 | } 45 | 46 | static void plot(Map> coords, Line line) { 47 | var xs = range(line.p1.x, line.p2.x); 48 | var ys = range(line.p1.y, line.p2.y); 49 | 50 | int len = Math.max(xs.size(), ys.size()); 51 | 52 | for (int i = 0; i < len; i++) { 53 | int x = i < xs.size() ? xs.get(i) : xs.get(xs.size()-1); 54 | int y = i < ys.size() ? ys.get(i) : ys.get(ys.size()-1); 55 | plot(coords, x, y); 56 | } 57 | } 58 | 59 | static long sum(Map> coords) { 60 | return coords.values().stream().flatMap(r -> r.values().stream()) 61 | .filter(v -> v > 1) 62 | .count(); 63 | } 64 | 65 | public static void main(String ... args) throws Exception { 66 | 67 | var data = Files.lines(Path.of("../input/5a.txt")) 68 | .map(Line::parse) 69 | .toList(); 70 | 71 | Map> coords = new TreeMap<>(); 72 | data.stream() 73 | .filter(l -> l.p1.x == l.p2.x || l.p1.y == l.p2.y) 74 | .forEach(l -> plot(coords, l)); 75 | 76 | System.out.printf("part 1: %d\n", sum(coords)); 77 | 78 | Map> coords2 = new TreeMap<>(); 79 | data.forEach(l -> plot(coords2, l)); 80 | 81 | System.out.printf("part 2: %d\n", sum(coords2)); 82 | } 83 | } -------------------------------------------------------------------------------- /2021/java/src/main/java/aoc2021/D6.java: -------------------------------------------------------------------------------- 1 | package aoc2021; 2 | 3 | import java.nio.file.Files; 4 | import java.nio.file.Path; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.stream.Collectors; 9 | import java.util.stream.Stream; 10 | 11 | /** 12 | * Advent of Code (AOC) 2021 Day 6 13 | */ 14 | public class D6 { 15 | 16 | public static void main(String ... args) throws Exception { 17 | var fish = Files.lines(Path.of("../input/6a.txt")) 18 | .flatMap(l -> Arrays.asList(l.split(",")).stream()).map(Integer::parseInt).toList(); 19 | 20 | part1(fish); 21 | 22 | part2(fish); 23 | } 24 | 25 | static void part1(List fish) { 26 | for(int i = 0; i < 80; i++) { 27 | fish = spawn1(fish); 28 | } 29 | 30 | System.out.printf("part 1: %d\n", fish.size()); 31 | } 32 | 33 | static List spawn1(List fish) { 34 | return fish.stream() 35 | .flatMap(c -> c == 0 ? Stream.of(8, 6) : Stream.of(c -1)) 36 | .toList(); 37 | } 38 | 39 | record Fish(int timer, long count) {} 40 | 41 | static void part2(List data) { 42 | var fish = data.stream() 43 | .map(c -> new Fish(c, 1)) 44 | .collect(Collectors.groupingBy(i -> i.timer(), Collectors.summingLong(i -> i.count()))); 45 | 46 | for(int i = 0; i < 256; i++) { 47 | fish = spawn2(fish); 48 | } 49 | 50 | System.out.printf("part 2: %s\n", fish.values().stream().mapToLong(i -> i).sum()); 51 | } 52 | 53 | static Map spawn2(Map fish) { 54 | return fish.entrySet().stream().map(e -> new Fish(e.getKey(), e.getValue())) 55 | .flatMap(f -> f.timer == 0 ? 56 | Stream.of(new Fish(8, f.count), new Fish(6, f.count)) : 57 | Stream.of(new Fish(f.timer - 1, f.count))) 58 | .collect(Collectors.groupingBy(f -> f.timer, Collectors.summingLong(f -> f.count))); 59 | } 60 | } -------------------------------------------------------------------------------- /2021/java/src/main/java/aoc2021/D7.java: -------------------------------------------------------------------------------- 1 | package aoc2021; 2 | 3 | import java.nio.file.Files; 4 | import java.nio.file.Path; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | import java.util.function.Function; 8 | import java.util.stream.IntStream; 9 | 10 | /** 11 | * Advent of Code (AOC) 2021 Day 7 12 | */ 13 | public class D7 { 14 | 15 | public static void main(String ... args) throws Exception { 16 | 17 | var crab = Files.lines(Path.of("../input/7a.txt")) 18 | .flatMap(l -> Arrays.asList(l.split(",")).stream()) 19 | .map(Integer::parseInt) 20 | .sorted().toList(); 21 | 22 | int median = crab.get(crab.size()/2); 23 | var cost1 = calculateCost(median, crab, d -> d); 24 | System.out.printf("part 1: %d\n", cost1); 25 | 26 | int mean = (int) crab.stream().mapToInt(i -> i).average().getAsDouble(); 27 | var cost2 = calculateCost(mean, crab, d -> IntStream.rangeClosed(1, d).sum()); 28 | System.out.printf("part 2: %d\n", cost2); 29 | } 30 | 31 | static int calculateCost(int pos, List crab, Function costFn) { 32 | return crab.stream().mapToInt(i -> costFn.apply(Math.abs(pos-i))).sum(); 33 | } 34 | } -------------------------------------------------------------------------------- /2021/java/src/main/java/aoc2021/D9.java: -------------------------------------------------------------------------------- 1 | package aoc2021; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Files; 5 | import java.nio.file.Path; 6 | import java.util.ArrayList; 7 | import java.util.Collections; 8 | import java.util.List; 9 | import java.util.stream.IntStream; 10 | import java.util.stream.Stream; 11 | 12 | /** 13 | * Advent of Code (AOC) 2021 Day 9 14 | */ 15 | public class D9 { 16 | 17 | record Point(int x, int y) { 18 | Point adj(int nx, int ny) { 19 | return new Point(x + nx, y + ny); 20 | } 21 | } 22 | 23 | public static void main(String ... args) throws IOException { 24 | 25 | var map = readMap("../input/9a.txt"); 26 | 27 | var lowPoints = findLowPoints(map); 28 | 29 | var sumOfRiskLevels = lowPoints.stream() 30 | .mapToInt(p -> map[p.y][p.x] + 1) 31 | .sum(); 32 | 33 | System.out.printf("part 1: %d\n", sumOfRiskLevels); 34 | 35 | var biggestBasinsMultiplied = lowPoints.stream() 36 | .map(p -> findBasin(p, map, new ArrayList<>())) 37 | .map(b -> b.size()) 38 | .sorted((a,b) -> b-a) 39 | .limit(3) 40 | .reduce(1, (a,b) -> a * b, (a,b) -> b); 41 | 42 | System.out.printf("part 2: %s\n", biggestBasinsMultiplied); 43 | } 44 | 45 | static List findLowPoints(int[][] map) { 46 | return IntStream.range(0, map.length).boxed() 47 | .flatMap(y -> IntStream.range(0, map[y].length).mapToObj(x -> new Point(x, y))) 48 | .filter(p -> isLowPoint(p, map)) 49 | .toList(); 50 | } 51 | 52 | static boolean isLowPoint(Point p, int[][]map) { 53 | int h = height(p, map); 54 | int adjacentMin = Stream.of(p.adj(-1,0), p.adj(1,0), p.adj(0, -1), p.adj(0, 1)) // adjacent points 55 | .mapToInt(a -> height(a, map)) // get height 56 | .min().getAsInt(); // min height 57 | return h < adjacentMin; 58 | } 59 | 60 | static int height(Point p, int [][]map) { 61 | if (p.x < 0 || p.y < 0 || p.y >= map.length || p.x >= map[0].length) { 62 | return 9; 63 | } 64 | return map[p.y][p.x]; 65 | } 66 | 67 | static List findBasin(Point p, int[][]map, List basin) { 68 | int h = height(p, map); 69 | if (h >= 9 || basin.contains(p)) { 70 | return Collections.emptyList(); 71 | } 72 | basin.add(p); 73 | 74 | findBasin(new Point(p.x()+1, p.y()), map, basin); 75 | findBasin(new Point(p.x()-1, p.y()), map, basin); 76 | findBasin(new Point(p.x(), p.y()+1), map, basin); 77 | findBasin(new Point(p.x(), p.y()-1), map, basin); 78 | 79 | return basin; 80 | } 81 | 82 | static int[][] readMap(String file) throws IOException { 83 | return Files.lines(Path.of(file)) 84 | .map(row -> row.chars().map(i -> i - '0').toArray()) 85 | .toArray(int[][]::new); 86 | } 87 | } -------------------------------------------------------------------------------- /2022/go/day1/day1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "sort" 8 | "strconv" 9 | ) 10 | 11 | func main() { 12 | elfs := readElfs("../../input/day1.txt") 13 | 14 | sort.Sort(sort.Reverse(sort.IntSlice(elfs))) 15 | 16 | fmt.Printf("part 1: %v\n", elfs[0]) 17 | 18 | sum := 0 19 | for _, c := range elfs[0:3] { 20 | sum += c 21 | } 22 | 23 | fmt.Printf("part 2: %v\n", sum) 24 | } 25 | 26 | func readElfs(name string) []int { 27 | elfs := []int{0} 28 | currentElf := &elfs[0] 29 | 30 | fileByLine(name, func(line string) { 31 | if line == "" { 32 | elfs = append(elfs, 0) 33 | currentElf = &elfs[len(elfs)-1] 34 | } else { 35 | cals, _ := strconv.Atoi(line) 36 | *currentElf += cals 37 | } 38 | }) 39 | return elfs 40 | } 41 | 42 | func fileByLine(name string, handler func(line string)) { 43 | file, err := os.Open(name) 44 | if err != nil { 45 | panic(err) 46 | } 47 | defer file.Close() 48 | 49 | scanner := bufio.NewScanner(file) 50 | for scanner.Scan() { 51 | handler(scanner.Text()) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /2022/go/day10/day10.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | ) 8 | 9 | func main() { 10 | input, _ := os.ReadFile("../../input/day10.txt") 11 | program := parseProgram(string(input)) 12 | 13 | fmt.Printf("part1 : %d\n", part1(program)) 14 | fmt.Printf("part2 :\n") 15 | part2(program) 16 | } 17 | 18 | func part2(program []instruction) { 19 | for cycle := 1; cycle <= 6*40; cycle++ { 20 | sprite := getState(cycle, program).x 21 | pos := (cycle - 1) % 40 22 | if pos >= sprite-1 && pos <= sprite+1 { 23 | fmt.Print("#") 24 | } else { 25 | fmt.Print(" ") 26 | } 27 | if cycle%40 == 0 { 28 | fmt.Print("\n") 29 | } 30 | } 31 | } 32 | 33 | func part1(program []instruction) (sum int) { 34 | for _, cycle := range []int{20, 60, 100, 140, 180, 220} { 35 | sum += cycle * getState(cycle, program).x 36 | } 37 | return sum 38 | } 39 | 40 | func getState(atCycle int, program []instruction) state { 41 | state := state{x: 1} 42 | cycle := 1 43 | for _, inst := range program { 44 | cycles, new := run(inst, state) 45 | cycle = cycle + cycles 46 | if cycle > atCycle { 47 | break 48 | } 49 | state = new 50 | } 51 | return state 52 | } 53 | 54 | func run(i instruction, old state) (int, state) { 55 | switch i.op { 56 | case "noop": 57 | return 1, old 58 | case "addx": 59 | return 2, state{x: old.x + i.v} 60 | } 61 | panic("error") 62 | } 63 | 64 | type state struct { 65 | x int 66 | } 67 | 68 | type instruction struct { 69 | op string 70 | v int 71 | } 72 | 73 | func parseProgram(input string) (program []instruction) { 74 | lines := strings.Split(input, "\n") 75 | for _, line := range lines { 76 | var i instruction 77 | fmt.Sscanf(line, "%s %d", &i.op, &i.v) 78 | program = append(program, i) 79 | } 80 | return program 81 | } 82 | -------------------------------------------------------------------------------- /2022/go/day11/day11.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "sort" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | func main() { 12 | fmt.Printf("part1 : %d\n", monkeyBusiness(20)) 13 | fmt.Printf("part2 : %d\n", monkeyBusiness(10000)) 14 | } 15 | 16 | func monkeyBusiness(rounds int) int { 17 | input, _ := os.ReadFile("../../input/day11.txt") 18 | monkeys := parseMonkeys(string(input)) 19 | 20 | for i := 0; i < rounds; i++ { 21 | doRound(monkeys, rounds <= 20) 22 | } 23 | 24 | inspects := []int{} 25 | for _, m := range monkeys { 26 | inspects = append(inspects, m.inspectCount) 27 | } 28 | sort.Sort(sort.Reverse(sort.IntSlice(inspects))) 29 | return inspects[0] * inspects[1] 30 | } 31 | 32 | func doRound(monkeys []*monkey, manageWorry bool) { 33 | commonMultiplier := 1 34 | for _, m := range monkeys { 35 | commonMultiplier *= m.divisible 36 | } 37 | 38 | for _, m := range monkeys { 39 | for _, i := range m.items { 40 | m.inspectCount++ 41 | newLevel := op(i, m.oper) 42 | if manageWorry { 43 | newLevel = newLevel / 3 44 | } 45 | newLevel = newLevel % commonMultiplier 46 | 47 | if newLevel%m.divisible == 0 { 48 | monkeys[m.trueTo].items = append(monkeys[m.trueTo].items, newLevel) 49 | } else { 50 | monkeys[m.falseTo].items = append(monkeys[m.falseTo].items, newLevel) 51 | } 52 | } 53 | m.items = []int{} 54 | } 55 | } 56 | 57 | func op(val int, op operation) int { 58 | var v2 int 59 | if op.val == "old" { 60 | v2 = val 61 | } else { 62 | fmt.Sscanf(op.val, "%d", &v2) 63 | } 64 | 65 | switch op.op { 66 | case '*': 67 | return val * v2 68 | case '+': 69 | return val + v2 70 | } 71 | panic("error") 72 | } 73 | 74 | func parseMonkeys(input string) (monkeys []*monkey) { 75 | monkey := strings.Split(input, "\n\n") 76 | for _, m := range monkey { 77 | monkeys = append(monkeys, parseMonkey(m)) 78 | } 79 | return monkeys 80 | } 81 | 82 | func parseMonkey(input string) *monkey { 83 | var m monkey 84 | lines := strings.Split(input, "\n") 85 | m.items = parseItems(strings.Split(lines[1], ":")[1]) 86 | fmt.Sscanf(lines[2], " Operation: new = old %c %s", &m.oper.op, &m.oper.val) 87 | fmt.Sscanf(lines[3], " Test: divisible by %d", &m.divisible) 88 | fmt.Sscanf(lines[4], " If true: throw to monkey %d", &m.trueTo) 89 | fmt.Sscanf(lines[5], " If false: throw to monkey %d", &m.falseTo) 90 | return &m 91 | } 92 | 93 | func parseItems(input string) (items []int) { 94 | parts := strings.Split(input, ", ") 95 | for _, p := range parts { 96 | i, _ := strconv.Atoi(strings.TrimSpace(p)) 97 | items = append(items, i) 98 | } 99 | return items 100 | } 101 | 102 | type operation struct { 103 | op byte 104 | val string 105 | } 106 | 107 | type monkey struct { 108 | items []int 109 | oper operation 110 | divisible int 111 | trueTo, falseTo int 112 | inspectCount int 113 | } 114 | -------------------------------------------------------------------------------- /2022/go/day12/day12.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | ) 8 | 9 | func main() { 10 | input, _ := os.ReadFile("../../input/day12.txt") 11 | height, start, end := readMap(string(input)) 12 | 13 | part1 := shortestPath(height, map[xy]int{}, visit{start, 0}, 14 | func(dh int) bool { return dh <= 1 }, 15 | func(p xy) bool { return end == p }) 16 | 17 | part2 := shortestPath(height, map[xy]int{}, visit{end, 0}, 18 | func(dh int) bool { return dh >= -1 }, 19 | func(p xy) bool { return height[p] == 'a' }) 20 | 21 | fmt.Printf("part1: %v\npart2: %v\n", part1, part2) 22 | } 23 | 24 | func shortestPath(height map[xy]int, visits map[xy]int, loc visit, ok func(d int) bool, target func(xy) bool) int { 25 | min := 9999 26 | if target(loc.xy) { 27 | return loc.distance 28 | } else if old, visited := visits[loc.xy]; visited && old <= loc.distance { 29 | return min 30 | } 31 | visits[loc.xy] = loc.distance 32 | for _, m := range []xy{{-1, 0}, {1, 0}, {0, -1}, {0, 1}} { 33 | next := visit{xy{loc.x + m.x, loc.y + m.y}, loc.distance + 1} 34 | if h, inside := height[next.xy]; inside { 35 | deltaH := h - height[loc.xy] 36 | if !ok(deltaH) { 37 | continue 38 | } 39 | d := shortestPath(height, visits, next, ok, target) 40 | if d < min { 41 | min = d 42 | } 43 | } 44 | } 45 | return min 46 | } 47 | 48 | func readMap(input string) (height map[xy]int, start, end xy) { 49 | rows := strings.Split(input, "\n") 50 | height = map[xy]int{} 51 | for y, row := range rows { 52 | for x, c := range row { 53 | if c == 'S' { 54 | start = xy{x, y} 55 | c = 'a' 56 | } else if c == 'E' { 57 | end = xy{x, y} 58 | c = 'z' 59 | } 60 | height[xy{x, y}] = int(c) 61 | } 62 | } 63 | return height, start, end 64 | } 65 | 66 | type visit struct { 67 | xy 68 | distance int 69 | } 70 | 71 | type xy struct { 72 | x, y int 73 | } 74 | -------------------------------------------------------------------------------- /2022/go/day13/day13.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "os" 7 | "reflect" 8 | "sort" 9 | "strings" 10 | ) 11 | 12 | func main() { 13 | input, _ := os.ReadFile("../../input/day13.txt") 14 | pairs := readPairs(string(input)) 15 | 16 | packets := [][]any{} 17 | sum := 0 18 | for i, pair := range pairs { 19 | if less(pair[0], pair[1]) { 20 | sum += i + 1 21 | } 22 | packets = append(packets, pair[0], pair[1]) 23 | } 24 | fmt.Printf("part1: %v\n", sum) 25 | 26 | packets = append(packets, dividers...) 27 | sort.Sort(ByPacket(packets)) 28 | var key int 29 | for i, p := range packets { 30 | if reflect.DeepEqual(p, dividers[0]) { 31 | key = i + 1 32 | } else if reflect.DeepEqual(p, dividers[1]) { 33 | key = key * (i + 1) 34 | } 35 | } 36 | fmt.Printf("part2: %v\n", key) 37 | } 38 | 39 | func less(left []any, right []any) (less bool) { 40 | return compare(left, right, 0) <= 0 41 | } 42 | 43 | func compare(left []any, right []any, i int) (res int) { 44 | if i >= len(left) && i >= len(right) { 45 | return 0 // end of both lists 46 | } else if i >= len(left) { 47 | return -1 // left ended first 48 | } else if i >= len(right) { 49 | return 1 // right shorter 50 | } 51 | 52 | // number comparison 53 | li, ri := left[i], right[i] 54 | ln, isln := li.(float64) 55 | rn, isrn := ri.(float64) 56 | if isln && isrn && ln == rn { 57 | return compare(left, right, i+1) // same, continue to next item 58 | } else if isln && isrn { 59 | return int(ln - rn) // different numbers 60 | } 61 | 62 | // list comparison 63 | llist, isllist := li.([]any) 64 | rlist, isrlist := ri.([]any) 65 | if !isllist { 66 | llist = []any{ln} // mixed, convert to list 67 | } 68 | if !isrlist { 69 | rlist = []any{rn} // mixed, convert to list 70 | } 71 | res = compare(llist, rlist, 0) 72 | if res == 0 { // tie, continue to next item 73 | return compare(left, right, i+1) 74 | } 75 | return res 76 | } 77 | 78 | var dividers = [][]any{{[]any{float64(2)}}, {[]any{float64(6)}}} 79 | 80 | type ByPacket [][]any 81 | 82 | func (p ByPacket) Len() int { return len(p) } 83 | func (p ByPacket) Swap(i, j int) { p[i], p[j] = p[j], p[i] } 84 | func (p ByPacket) Less(i, j int) bool { return less(p[i], p[j]) } 85 | 86 | func readPairs(input string) (res [][2][]any) { 87 | pairs := strings.Split(input, "\n\n") 88 | for _, pair := range pairs { 89 | part := strings.Split(pair, "\n") 90 | var left, right []any 91 | json.Unmarshal([]byte(part[0]), &left) 92 | json.Unmarshal([]byte(part[1]), &right) 93 | pair := [2][]any{left, right} 94 | res = append(res, pair) 95 | } 96 | return res 97 | } 98 | -------------------------------------------------------------------------------- /2022/go/day14/day14.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | ) 8 | 9 | func main() { 10 | input, _ := os.ReadFile("../../input/day14.txt") 11 | 12 | fmt.Printf("part1: %v\n", pourSand(input, false)) 13 | fmt.Printf("part2: %v\n", pourSand(input, true)) 14 | } 15 | 16 | func pourSand(input []byte, useFoor bool) (count int) { 17 | cave := readRocks(string(input)) 18 | floor := 0 19 | if useFoor { 20 | floor = bottom(cave) 21 | } 22 | var start = xy{500, 0} 23 | count = -1 24 | for count != countSand(cave) { 25 | count = countSand(cave) 26 | dropSand(cave, floor, start) 27 | } 28 | return count 29 | } 30 | 31 | func countSand(cave map[xy]byte) (count int) { 32 | for _, i := range cave { 33 | if i == 'o' { 34 | count++ 35 | } 36 | } 37 | return count 38 | } 39 | 40 | func dropSand(cave map[xy]byte, floor int, sand xy) { 41 | if sand.y != floor-1 && sand.y < 1000 { // Try if sand can be moved down 42 | for _, v := range moves { 43 | try := xy{sand.x + v.x, sand.y + v.y} 44 | if _, found := cave[try]; !found { 45 | dropSand(cave, floor, try) // can be moved, try next 46 | return 47 | } 48 | } 49 | } 50 | 51 | if _, support := cave[xy{sand.x, sand.y + 1}]; support || sand.y+1 == floor { 52 | cave[sand] = 'o' // support below, place sand 53 | } 54 | } 55 | 56 | func bottom(cave map[xy]byte) (res int) { 57 | for p := range cave { 58 | if p.y > res { 59 | res = p.y 60 | } 61 | } 62 | return res + 2 63 | } 64 | 65 | var moves = []xy{{0, 1}, {-1, 1}, {1, 1}} 66 | 67 | func readRocks(input string) map[xy]byte { 68 | lines := strings.Split(input, "\n") 69 | rocks := map[xy]byte{} 70 | for _, line := range lines { 71 | points := strings.Split(line, " -> ") 72 | for i := 1; i < len(points); i++ { 73 | var p1, p2 xy 74 | fmt.Sscanf(points[i-1], "%d,%d", &p1.x, &p1.y) 75 | fmt.Sscanf(points[i], "%d,%d", &p2.x, &p2.y) 76 | draw(rocks, p1, p2) 77 | } 78 | } 79 | return rocks 80 | } 81 | 82 | func draw(rocks map[xy]byte, p1, p2 xy) { 83 | rocks[p1] = '#' 84 | if p1 == p2 { 85 | return 86 | } 87 | 88 | if p1.x == p2.x && p1.y < p2.y { 89 | draw(rocks, xy{p1.x, p1.y + 1}, p2) 90 | } else if p1.x == p2.x && p1.y > p2.y { 91 | draw(rocks, xy{p1.x, p1.y - 1}, p2) 92 | } else if p1.y == p2.y && p1.x < p2.x { 93 | draw(rocks, xy{p1.x + 1, p1.y}, p2) 94 | } else if p1.y == p2.y && p1.x > p2.x { 95 | draw(rocks, xy{p1.x - 1, p1.y}, p2) 96 | } 97 | } 98 | 99 | type xy struct{ x, y int } 100 | -------------------------------------------------------------------------------- /2022/go/day15/day15.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "sort" 7 | "strings" 8 | ) 9 | 10 | func main() { 11 | input, _ := os.ReadFile("../../input/day15.txt") 12 | sensors := readInput(string(input)) 13 | 14 | spans, count := spansForY(sensors, 2000000), 0 15 | for _, s := range spans { 16 | count += s.end - s.start 17 | } 18 | 19 | fmt.Printf("part1: %v\n", count) 20 | 21 | var x, y int 22 | for y = 0; y <= 4000000; y++ { 23 | spans := spansForY(sensors, y) 24 | if len(spans) > 1 { 25 | x = spans[0].end + 1 26 | break 27 | } 28 | } 29 | fmt.Printf("part2: %v\n", x*4000000+y) 30 | } 31 | 32 | func spansForY(sensors []sensor, y int) (spans []span) { 33 | for _, s := range sensors { 34 | sbDist, srDist := abs(s.loc.x-s.beacon.x)+abs(s.loc.y-s.beacon.y), abs(s.loc.y-y) 35 | dist := sbDist - srDist 36 | if dist > 0 { 37 | spans = append(spans, span{s.loc.x - dist, s.loc.x + dist}) 38 | } 39 | } 40 | return merge(spans) 41 | } 42 | 43 | func merge(spans []span) (res []span) { 44 | sort.Sort(byStart(spans)) 45 | res = append(res, spans[0]) 46 | for _, next := range spans[1:] { 47 | prev := res[len(res)-1] 48 | if prev.end >= next.start-1 { 49 | res[len(res)-1] = span{prev.start, max(prev.end, next.end)} 50 | } else { 51 | res = append(res, next) 52 | } 53 | } 54 | return res 55 | } 56 | 57 | func readInput(input string) (res []sensor) { 58 | lines := strings.Split(input, "\n") 59 | for _, l := range lines { 60 | var s sensor 61 | fmt.Sscanf(l, "Sensor at x=%d, y=%d: closest beacon is at x=%d, y=%d", 62 | &s.loc.x, &s.loc.y, &s.beacon.x, &s.beacon.y) 63 | res = append(res, s) 64 | } 65 | return res 66 | } 67 | 68 | type span struct{ start, end int } 69 | type sensor struct{ loc, beacon xy } 70 | type xy struct{ x, y int } 71 | type byStart []span 72 | 73 | func (a byStart) Len() int { return len(a) } 74 | func (a byStart) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 75 | func (a byStart) Less(i, j int) bool { return a[i].start < a[j].start } 76 | 77 | func max(a, b int) int { 78 | if a > b { 79 | return a 80 | } 81 | return b 82 | } 83 | 84 | func abs(v int) int { 85 | if v < 0 { 86 | return -v 87 | } 88 | return v 89 | } 90 | -------------------------------------------------------------------------------- /2022/go/day2/day2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | ) 8 | 9 | func main() { 10 | input, _ := os.ReadFile("../../input/day2.txt") 11 | lines := strings.Split(string(input), "\n") 12 | 13 | score1 := 0 14 | for _, line := range lines { 15 | score1 += game1(line) 16 | } 17 | fmt.Printf("part 1: %v\n", score1) 18 | 19 | score2 := 0 20 | for _, line := range lines { 21 | score2 += game2(line) 22 | } 23 | fmt.Printf("part 2: %v\n", score2) 24 | } 25 | 26 | var toWin = []byte{1, 2, 0} // to win 0 (Rock) you need 1 (Paper) etc 27 | var toLose = []byte{2, 0, 1} // to lose 0 (Rock) you need 2 (Scissors) etc 28 | 29 | func game1(line string) int { 30 | shape1 := line[0] - 'A' // 0=Rock, 1=Paper, 2=Scissors 31 | shape2 := line[2] - 'X' 32 | 33 | shapeScore := shape2 + 1 34 | return int(shapeScore) + gameScore(shape1, shape2) 35 | } 36 | 37 | func game2(line string) int { 38 | shape1 := line[0] - 'A' 39 | shape2 := shape1 40 | switch line[2] { 41 | case 'X': 42 | shape2 = toLose[shape1] 43 | case 'Z': 44 | shape2 = toWin[shape1] 45 | } 46 | shapeScore := shape2 + 1 47 | return int(shapeScore) + gameScore(shape1, shape2) 48 | } 49 | 50 | func gameScore(shape1 byte, shape2 byte) int { 51 | if toWin[shape1] == shape2 { 52 | return 6 53 | } else if toLose[shape1] == shape2 { 54 | return 0 55 | } else { 56 | return 3 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /2022/go/day3/day3.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | ) 8 | 9 | func main() { 10 | input, _ := os.ReadFile("../../input/day3.txt") 11 | lines := strings.Split(string(input), "\n") 12 | 13 | sum1 := 0 14 | for _, line := range lines { 15 | p1, p2 := []byte(line[:len(line)/2]), []byte(line[len(line)/2:]) 16 | sum1 += priority(commonItem(p1, p2)[0]) 17 | } 18 | fmt.Printf("part 1: %v\n", sum1) 19 | 20 | sum2 := 0 21 | var group [][]byte 22 | for _, line := range lines { 23 | group = append(group, []byte(line)) 24 | if len(group) == 3 { 25 | sum2 += priority(commonItem(group...)[0]) 26 | group = nil 27 | } 28 | } 29 | fmt.Printf("part 2: %v\n", sum2) 30 | } 31 | 32 | func commonItem(values ...[]byte) []byte { 33 | res := values[0] 34 | for _, next := range values[1:] { 35 | res = commonValues(res, next) 36 | } 37 | return res 38 | } 39 | 40 | func commonValues(a, b []byte) []byte { 41 | res := []byte{} 42 | for _, v := range a { 43 | if contains(b, v) && !contains(res, v) { 44 | res = append(res, v) 45 | } 46 | } 47 | return res 48 | } 49 | 50 | func contains(items []byte, v byte) bool { 51 | for _, v1 := range items { 52 | if v1 == v { 53 | return true 54 | } 55 | } 56 | return false 57 | } 58 | 59 | func priority(c byte) int { 60 | if c < 'a' { 61 | return int(c - 'A' + 27) 62 | } else { 63 | return int(c - 'a' + 1) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /2022/go/day4/day4.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | ) 8 | 9 | func main() { 10 | input, _ := os.ReadFile("../../input/day4.txt") 11 | lines := strings.Split(string(input), "\n") 12 | 13 | sum1 := 0 14 | sum2 := 0 15 | for _, line := range lines { 16 | sections1, sections2 := parsePair(line) 17 | if sections1.fullyContains(sections2) || sections2.fullyContains(sections1) { 18 | sum1 += 1 19 | } 20 | 21 | if sections1.overlap(sections2) || sections2.overlap(sections1) { 22 | sum2 += 1 23 | } 24 | } 25 | fmt.Printf("part 1: %v\n", sum1) 26 | fmt.Printf("part 2: %v\n", sum2) 27 | } 28 | 29 | type sections struct { 30 | start, end int 31 | } 32 | 33 | func (s1 sections) fullyContains(s2 sections) bool { 34 | return s1.start <= s2.start && s1.end >= s2.end 35 | } 36 | 37 | func (s1 sections) overlap(s2 sections) bool { 38 | return s1.start <= s2.start && s1.end >= s2.start 39 | } 40 | 41 | func parsePair(line string) (sections, sections) { 42 | var s1, s2 sections 43 | fmt.Sscanf(line, "%d-%d,%d-%d", &s1.start, &s1.end, &s2.start, &s2.end) 44 | return s1, s2 45 | } 46 | -------------------------------------------------------------------------------- /2022/go/day5/day5.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "regexp" 7 | "strings" 8 | ) 9 | 10 | type move struct { 11 | count, from, to int 12 | } 13 | 14 | type moves []move 15 | 16 | func main() { 17 | input, _ := os.ReadFile("../../input/day5.txt") 18 | parts := strings.Split(string(input), "\n\n") 19 | stacks := parseStacks(parts[0]) 20 | moves := parseMoves(parts[1]) 21 | 22 | result1 := moves.apply(stacks, crateMover9000) 23 | fmt.Printf("part1: %s\n", result(result1)) 24 | 25 | result2 := moves.apply(stacks, crateMover9001) 26 | fmt.Printf("part2: %s\n", result(result2)) 27 | 28 | } 29 | 30 | func parseStacks(input string) []string { 31 | levels := strings.Split(input, "\n") 32 | stackCount := (len(levels[0]) + 1) / 4 33 | stacks := make([]string, stackCount) 34 | for _, level := range levels[0 : len(levels)-1] { 35 | re := regexp.MustCompile(".(.)..?") 36 | crates := re.FindAllStringSubmatch(level, -1) 37 | for i, c := range crates { 38 | if c[1] != " " { 39 | stacks[i] = c[1] + stacks[i] 40 | } 41 | } 42 | } 43 | return stacks 44 | } 45 | 46 | func parseMoves(input string) moves { 47 | lines := strings.Split(string(input), "\n") 48 | m := make([]move, len(lines)) 49 | for i, line := range lines { 50 | fmt.Sscanf(line, "move %d from %d to %d", &m[i].count, &m[i].from, &m[i].to) 51 | } 52 | return m 53 | } 54 | 55 | func (ms moves) apply(stacks []string, crane func(move, []string)) []string { 56 | result := make([]string, len(stacks)) 57 | copy(result, stacks) 58 | for _, m := range ms { 59 | crane(m, result) 60 | } 61 | return result 62 | } 63 | 64 | func crateMover9000(m move, stacks []string) { 65 | for i := 0; i < m.count; i++ { 66 | moveCrates(1, m.from, m.to, stacks) 67 | } 68 | } 69 | 70 | func crateMover9001(m move, stacks []string) { 71 | moveCrates(m.count, m.from, m.to, stacks) 72 | } 73 | 74 | func moveCrates(count, from, to int, stacks []string) { 75 | from, to = from-1, to-1 76 | stacks[to] = stacks[to] + stacks[from][len(stacks[from])-count:] // append crates to new stack 77 | stacks[from] = stacks[from][:len(stacks[from])-count] // remove from old stack 78 | } 79 | 80 | func result(stacks []string) string { 81 | var res string 82 | for _, s := range stacks { 83 | res += s[len(s)-1:] 84 | } 85 | return res 86 | } 87 | -------------------------------------------------------------------------------- /2022/go/day6/day6.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | func main() { 9 | input, _ := os.ReadFile("../../input/day6.txt") 10 | fmt.Printf("part1: %d\n", findMarker(input, 4)) 11 | fmt.Printf("part2: %d\n", findMarker(input, 14)) 12 | } 13 | 14 | func findMarker(input []byte, len int) int { 15 | for i := range input { 16 | if allDifferent(input[i : i+len]) { 17 | return i + len 18 | } 19 | } 20 | return -1 21 | } 22 | 23 | func allDifferent(s []byte) bool { 24 | bytes := map[byte]bool{} 25 | for _, c1 := range s { 26 | bytes[c1] = true 27 | } 28 | return len(bytes) == len(s) 29 | } 30 | -------------------------------------------------------------------------------- /2022/go/day7/day7.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | ) 8 | 9 | func main() { 10 | input, _ := os.ReadFile("../../input/day7.txt") 11 | root := parseTerminal(string(input)) 12 | var part1 int 13 | root.forEach(func(n *node) { 14 | if n.fileSize == 0 && n.size() <= 100000 { 15 | part1 += n.size() 16 | } 17 | }) 18 | fmt.Printf("part1: %v\n", part1) 19 | 20 | needed := 30000000 - (70000000 - root.size()) 21 | 22 | smallest := 70000000 23 | root.forEach(func(n *node) { 24 | if n.size() >= needed && n.size() < smallest { 25 | smallest = n.size() 26 | } 27 | }) 28 | fmt.Printf("part2: %v\n", smallest) 29 | } 30 | 31 | type node struct { 32 | fileSize int 33 | parent *node 34 | nodes map[string]*node 35 | } 36 | 37 | func parseTerminal(input string) *node { 38 | root := newNode(0, nil) 39 | cwd := root 40 | lines := strings.Split(input, "\n") 41 | for _, line := range lines { 42 | cwd = cwd.apply(line) 43 | } 44 | return root 45 | } 46 | 47 | func (n *node) apply(line string) *node { 48 | name, size := "", 0 49 | if strings.HasPrefix(line, "$ cd ") { 50 | return n.cd(line[5:]) 51 | } else if _, err := fmt.Sscanf(line, "%d %s", &size, &name); err == nil { 52 | return n.add(name, size) 53 | } 54 | return n 55 | } 56 | 57 | func (n *node) cd(name string) *node { 58 | if name == "/" && n.parent == nil { 59 | return n 60 | } else if name == "/" { 61 | return n.cd("/") 62 | } else if name == ".." { 63 | return n.parent 64 | } 65 | if _, ok := n.nodes[name]; !ok { 66 | n.nodes[name] = newNode(0, n) 67 | } 68 | return n.nodes[name] 69 | } 70 | 71 | func (n *node) add(name string, size int) *node { 72 | n.nodes[name] = newNode(size, n) 73 | return n 74 | } 75 | 76 | func (n *node) size() int { 77 | if n.fileSize > 0 { 78 | return n.fileSize 79 | } 80 | var sum int 81 | for _, c := range n.nodes { 82 | sum += c.size() 83 | } 84 | return sum 85 | } 86 | 87 | func newNode(size int, parent *node) *node { 88 | return &node{fileSize: size, parent: parent, nodes: make(map[string]*node)} 89 | } 90 | 91 | func (n *node) forEach(f func(*node)) { 92 | for _, c := range n.nodes { 93 | f(c) 94 | c.forEach(f) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /2022/go/day8/day8.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | ) 8 | 9 | func main() { 10 | input, _ := os.ReadFile("../../input/day8.txt") 11 | heights := parseMap(string(input)) 12 | 13 | fmt.Printf("part1 : %d\n", countVisible(heights)) 14 | fmt.Printf("part2 : %d\n", highestScenicScore(heights)) 15 | } 16 | 17 | func highestScenicScore(heights [][]byte) (max int) { 18 | for y, row := range heights { 19 | for x := range row { 20 | score := scenicScore(heights, y, x) 21 | if score > max { 22 | max = score 23 | } 24 | } 25 | } 26 | return max 27 | } 28 | 29 | func scenicScore(heights [][]byte, y, x int) (score int) { 30 | up := visibleTrees(heights, y, x, func(x, y int) (int, int) { return x, y - 1 }) 31 | down := visibleTrees(heights, y, x, func(x, y int) (int, int) { return x, y + 1 }) 32 | left := visibleTrees(heights, y, x, func(x, y int) (int, int) { return x - 1, y }) 33 | right := visibleTrees(heights, y, x, func(x, y int) (int, int) { return x + 1, y }) 34 | return up * down * left * right 35 | } 36 | 37 | func visibleTrees(heights [][]byte, oy, ox int, move func(x, y int) (int, int)) (count int) { 38 | for x, y := move(ox, oy); inside(heights, y, x); x, y = move(x, y) { 39 | count++ 40 | if heights[y][x] >= heights[oy][ox] { 41 | return count 42 | } 43 | } 44 | return count 45 | } 46 | 47 | func inside(heights [][]byte, y, x int) bool { 48 | return y >= 0 && y < len(heights) && x >= 0 && x < len(heights[y]) 49 | } 50 | 51 | func countVisible(heights [][]byte) (sum int) { 52 | for y, row := range heights { 53 | for x := range row { 54 | if visible(heights, y, x) { 55 | sum++ 56 | } 57 | } 58 | } 59 | return sum 60 | } 61 | 62 | func visible(heights [][]byte, y, x int) bool { 63 | if y == 0 || y == len(heights)-1 || x == 0 || x == len(heights[y])-1 { 64 | return true // edge cases 65 | } 66 | height := heights[y][x] 67 | row := heights[y] 68 | 69 | if max(row[:x]) < height || max(row[x+1:]) < height { 70 | return true 71 | } 72 | col := column(heights, x) 73 | if max(col[:y]) < height || max(col[y+1:]) < height { 74 | return true 75 | } 76 | return false 77 | } 78 | 79 | func column(heights [][]byte, x int) (col []byte) { 80 | for y := 0; y < len(heights); y++ { 81 | col = append(col, heights[y][x]) 82 | } 83 | return col 84 | } 85 | 86 | func max(data []byte) (max byte) { 87 | for _, b := range data { 88 | if b > max { 89 | max = b 90 | } 91 | } 92 | return max 93 | } 94 | 95 | func parseMap(input string) (heights [][]byte) { 96 | lines := strings.Split(input, "\n") 97 | for _, line := range lines { 98 | var row []byte 99 | for _, c := range line { 100 | row = append(row, byte(c-'0')) 101 | } 102 | heights = append(heights, row) 103 | } 104 | return heights 105 | } 106 | -------------------------------------------------------------------------------- /2022/go/day9/day9.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "os" 7 | "strings" 8 | ) 9 | 10 | func main() { 11 | input, _ := os.ReadFile("../../input/day9.txt") 12 | moves := parseMoves(string(input)) 13 | head := tieKnots(10) 14 | head = runMoves(head, moves) 15 | fmt.Printf("part1 : %d\n", len(head.getNth(1).visited)) 16 | fmt.Printf("part2 : %d\n", len(head.getNth(9).visited)) 17 | } 18 | 19 | func tieKnots(count int) *knot { 20 | knot := newKnot() 21 | prev := knot 22 | for i := 1; i < count; i++ { 23 | prev.tail = newKnot() 24 | prev = prev.tail 25 | } 26 | return knot 27 | } 28 | 29 | func runMoves(knot *knot, moves []move) *knot { 30 | for _, m := range moves { 31 | for s := byte(0); s < m.steps; s++ { 32 | knot.move(dirs[m.dir]) 33 | } 34 | } 35 | return knot 36 | } 37 | 38 | type pos struct { 39 | x, y int 40 | } 41 | 42 | type knot struct { 43 | pos pos 44 | visited map[pos]bool 45 | tail *knot 46 | } 47 | 48 | type move struct { 49 | dir, steps byte 50 | } 51 | 52 | func (k *knot) getNth(count int) *knot { 53 | if count == 0 { 54 | return k 55 | } 56 | return k.tail.getNth(count - 1) 57 | } 58 | 59 | func (k *knot) move(v pos) { 60 | k.pos = k.pos.move(v) 61 | k.visited[k.pos] = true 62 | if k.tail != nil { 63 | k.tail.follow(k) 64 | } 65 | } 66 | 67 | func (p pos) move(v pos) pos { 68 | return pos{p.x + v.x, p.y + v.y} 69 | } 70 | 71 | func (tail *knot) follow(head *knot) { 72 | if tail.pos.touching(head.pos) { 73 | return 74 | } 75 | dx := (head.pos.x-tail.pos.x)/2 + (head.pos.x-tail.pos.x)%2 76 | dy := (head.pos.y-tail.pos.y)/2 + (head.pos.y-tail.pos.y)%2 77 | tail.move(pos{dx, dy}) 78 | } 79 | 80 | func (k1 pos) touching(k2 pos) bool { 81 | return math.Abs(float64(k1.x)-float64(k2.x)) < 1.5 && math.Abs(float64(k1.y)-float64(k2.y)) < 1.5 82 | } 83 | 84 | func parseMoves(input string) (moves []move) { 85 | lines := strings.Split(input, "\n") 86 | for _, line := range lines { 87 | var m move 88 | fmt.Sscanf(line, "%c %d", &m.dir, &m.steps) 89 | moves = append(moves, m) 90 | } 91 | return moves 92 | } 93 | 94 | func newKnot() *knot { 95 | k := &knot{visited: make(map[pos]bool)} 96 | k.move(pos{}) 97 | return k 98 | } 99 | 100 | var dirs = map[byte]pos{'U': {0, -1}, 'D': {0, 1}, 'L': {-1, 0}, 'R': {1, 0}} 101 | -------------------------------------------------------------------------------- /2022/go/go.mod: -------------------------------------------------------------------------------- 1 | module aoc2022 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /2022/input/day10.txt: -------------------------------------------------------------------------------- 1 | noop 2 | noop 3 | noop 4 | addx 6 5 | noop 6 | addx 30 7 | addx -26 8 | noop 9 | addx 5 10 | noop 11 | noop 12 | noop 13 | noop 14 | addx 5 15 | addx -5 16 | addx 6 17 | addx 5 18 | addx -1 19 | addx 5 20 | noop 21 | noop 22 | addx -14 23 | addx -18 24 | addx 39 25 | addx -39 26 | addx 25 27 | addx -22 28 | addx 2 29 | addx 5 30 | addx 2 31 | addx 3 32 | addx -2 33 | addx 2 34 | noop 35 | addx 3 36 | addx 2 37 | addx 2 38 | noop 39 | addx 3 40 | noop 41 | addx 3 42 | addx 2 43 | addx 5 44 | addx 4 45 | addx -18 46 | addx 17 47 | addx -38 48 | addx 5 49 | addx 2 50 | addx -5 51 | addx 27 52 | addx -19 53 | noop 54 | addx 3 55 | addx 4 56 | noop 57 | noop 58 | addx 5 59 | addx -1 60 | noop 61 | noop 62 | addx 4 63 | addx 5 64 | addx 2 65 | addx -4 66 | addx 5 67 | noop 68 | addx -11 69 | addx 16 70 | addx -36 71 | noop 72 | addx 5 73 | noop 74 | addx 28 75 | addx -23 76 | noop 77 | noop 78 | noop 79 | addx 21 80 | addx -18 81 | noop 82 | addx 3 83 | addx 2 84 | addx 2 85 | addx 5 86 | addx 1 87 | noop 88 | noop 89 | addx 4 90 | noop 91 | noop 92 | noop 93 | noop 94 | noop 95 | addx 8 96 | addx -40 97 | noop 98 | addx 7 99 | noop 100 | addx -2 101 | addx 5 102 | addx 2 103 | addx 25 104 | addx -31 105 | addx 9 106 | addx 5 107 | addx 2 108 | addx 2 109 | addx 3 110 | addx -2 111 | noop 112 | addx 3 113 | addx 2 114 | noop 115 | addx 7 116 | addx -2 117 | addx 5 118 | addx -40 119 | addx 20 120 | addx -12 121 | noop 122 | noop 123 | noop 124 | addx -5 125 | addx 7 126 | addx 7 127 | noop 128 | addx -1 129 | addx 1 130 | addx 5 131 | addx 3 132 | addx -2 133 | addx 2 134 | noop 135 | addx 3 136 | addx 2 137 | noop 138 | noop 139 | noop 140 | noop 141 | addx 7 142 | noop 143 | noop 144 | noop 145 | noop -------------------------------------------------------------------------------- /2022/input/day11.txt: -------------------------------------------------------------------------------- 1 | Monkey 0: 2 | Starting items: 84, 72, 58, 51 3 | Operation: new = old * 3 4 | Test: divisible by 13 5 | If true: throw to monkey 1 6 | If false: throw to monkey 7 7 | 8 | Monkey 1: 9 | Starting items: 88, 58, 58 10 | Operation: new = old + 8 11 | Test: divisible by 2 12 | If true: throw to monkey 7 13 | If false: throw to monkey 5 14 | 15 | Monkey 2: 16 | Starting items: 93, 82, 71, 77, 83, 53, 71, 89 17 | Operation: new = old * old 18 | Test: divisible by 7 19 | If true: throw to monkey 3 20 | If false: throw to monkey 4 21 | 22 | Monkey 3: 23 | Starting items: 81, 68, 65, 81, 73, 77, 96 24 | Operation: new = old + 2 25 | Test: divisible by 17 26 | If true: throw to monkey 4 27 | If false: throw to monkey 6 28 | 29 | Monkey 4: 30 | Starting items: 75, 80, 50, 73, 88 31 | Operation: new = old + 3 32 | Test: divisible by 5 33 | If true: throw to monkey 6 34 | If false: throw to monkey 0 35 | 36 | Monkey 5: 37 | Starting items: 59, 72, 99, 87, 91, 81 38 | Operation: new = old * 17 39 | Test: divisible by 11 40 | If true: throw to monkey 2 41 | If false: throw to monkey 3 42 | 43 | Monkey 6: 44 | Starting items: 86, 69 45 | Operation: new = old + 6 46 | Test: divisible by 3 47 | If true: throw to monkey 1 48 | If false: throw to monkey 0 49 | 50 | Monkey 7: 51 | Starting items: 91 52 | Operation: new = old + 1 53 | Test: divisible by 19 54 | If true: throw to monkey 2 55 | If false: throw to monkey 5 -------------------------------------------------------------------------------- /2022/input/day15.txt: -------------------------------------------------------------------------------- 1 | Sensor at x=1555825, y=18926: closest beacon is at x=1498426, y=-85030 2 | Sensor at x=697941, y=3552290: closest beacon is at x=595451, y=3788543 3 | Sensor at x=3997971, y=2461001: closest beacon is at x=3951198, y=2418718 4 | Sensor at x=3818312, y=282332: closest beacon is at x=4823751, y=1061753 5 | Sensor at x=2874142, y=3053631: closest beacon is at x=3074353, y=3516891 6 | Sensor at x=1704479, y=2132468: closest beacon is at x=1749091, y=2000000 7 | Sensor at x=3904910, y=2080560: closest beacon is at x=3951198, y=2418718 8 | Sensor at x=657061, y=3898803: closest beacon is at x=595451, y=3788543 9 | Sensor at x=3084398, y=3751092: closest beacon is at x=3074353, y=3516891 10 | Sensor at x=2582061, y=972407: closest beacon is at x=1749091, y=2000000 11 | Sensor at x=2886721, y=3971936: closest beacon is at x=3074353, y=3516891 12 | Sensor at x=303399, y=548513: closest beacon is at x=-1010425, y=986825 13 | Sensor at x=3426950, y=2290126: closest beacon is at x=3951198, y=2418718 14 | Sensor at x=3132858, y=3592272: closest beacon is at x=3074353, y=3516891 15 | Sensor at x=3773724, y=3751243: closest beacon is at x=3568452, y=3274758 16 | Sensor at x=3987212, y=2416515: closest beacon is at x=3951198, y=2418718 17 | Sensor at x=61559, y=3806326: closest beacon is at x=595451, y=3788543 18 | Sensor at x=2693503, y=2291389: closest beacon is at x=2269881, y=2661753 19 | Sensor at x=3953437, y=2669220: closest beacon is at x=3951198, y=2418718 20 | Sensor at x=763035, y=3997568: closest beacon is at x=595451, y=3788543 21 | Sensor at x=3999814, y=2370103: closest beacon is at x=3951198, y=2418718 22 | Sensor at x=1290383, y=1696257: closest beacon is at x=1749091, y=2000000 23 | Sensor at x=3505508, y=2805537: closest beacon is at x=3568452, y=3274758 24 | Sensor at x=3276207, y=3323122: closest beacon is at x=3568452, y=3274758 25 | Sensor at x=2244609, y=3517499: closest beacon is at x=3074353, y=3516891 26 | Sensor at x=1370860, y=3436382: closest beacon is at x=595451, y=3788543 27 | Sensor at x=3831063, y=3042662: closest beacon is at x=3568452, y=3274758 28 | Sensor at x=551202, y=3971545: closest beacon is at x=595451, y=3788543 29 | Sensor at x=336629, y=2519780: closest beacon is at x=595451, y=3788543 30 | Sensor at x=2033602, y=2882628: closest beacon is at x=2269881, y=2661753 31 | Sensor at x=3939808, y=2478271: closest beacon is at x=3951198, y=2418718 32 | Sensor at x=1958708, y=2370822: closest beacon is at x=1749091, y=2000000 33 | Sensor at x=3039958, y=3574483: closest beacon is at x=3074353, y=3516891 -------------------------------------------------------------------------------- /2022/java/Day1.java: -------------------------------------------------------------------------------- 1 | import java.nio.file.Files; 2 | import java.nio.file.Path; 3 | import java.util.Collections; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | 7 | class Day1 { 8 | public static void main(String ... args) throws Exception { 9 | var cals = new LinkedList<>(List.of(0)); 10 | Files.readAllLines(Path.of("../input/day1.txt")).forEach(line -> { 11 | if (line.isBlank()) { 12 | cals.add(0); 13 | } else { 14 | cals.add(cals.removeLast() + Integer.parseInt(line)); 15 | } 16 | }); 17 | 18 | Collections.sort(cals, (a,b) -> b-a); // reverse sort 19 | 20 | System.out.printf("part 1: %d\n", cals.getFirst()); 21 | System.out.printf("part 2: %d\n", cals.stream().limit(3).mapToInt(i -> i).sum()); 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /2022/java/Day10.java: -------------------------------------------------------------------------------- 1 | import java.nio.file.Files; 2 | import java.nio.file.Path; 3 | import java.util.List; 4 | import java.util.Set; 5 | import java.util.stream.Collectors; 6 | import java.util.stream.IntStream; 7 | 8 | class Day10 { 9 | 10 | public static void main(String ... args) throws Exception { 11 | var program = parseProgram(Files.readString(Path.of("../input/day10.txt"))); 12 | System.out.printf("part1: %d\n", part1(program)); 13 | System.out.printf("part2:\n%s", part2(program)); 14 | } 15 | 16 | static String part2(List program) { 17 | return IntStream.range(1, 6*40+1) 18 | .mapToObj(cycle -> pixelAtCycle(cycle, program) + (cycle%40 == 0 ? "\n" : "")) 19 | .collect(Collectors.joining()); 20 | } 21 | 22 | static String pixelAtCycle(int cycle, List program) { 23 | var middle = getState(cycle, program).x; 24 | var sprite = Set.of(middle, middle-1, middle+1); 25 | return sprite.contains((cycle-1) % 40) ? "#" : " "; 26 | } 27 | 28 | static int part1(List program) { 29 | return IntStream.of(20,60,100,140,180,220).map(c -> c * getState(c, program).x).sum(); 30 | } 31 | 32 | static State getState(int atCycle, List program) { 33 | var state = new State(1, 1); 34 | for (var i : program) { 35 | var newState = run(i, state); 36 | if (newState.cycle > atCycle) { 37 | break; 38 | } 39 | state = newState; 40 | } 41 | return state; 42 | } 43 | 44 | static State run(Instruction i, State state) { 45 | return switch (i.op) { 46 | case "noop" -> new State(state.cycle+1, state.x); 47 | case "addx" -> new State(state.cycle+2, state.x + i.value); 48 | default -> throw new IllegalArgumentException("invalid operation"); 49 | }; 50 | } 51 | 52 | static List parseProgram(String input) { 53 | return input.lines().map(l -> Instruction.parse(l.split(" "))).toList(); 54 | } 55 | 56 | record State(int cycle, int x) {} 57 | 58 | record Instruction(String op, int value) { 59 | static Instruction parse(String ... parts) { 60 | return new Instruction(parts[0], parts[0].equals("noop") ? 0 : Integer.parseInt(parts[1])); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /2022/java/Day11.java: -------------------------------------------------------------------------------- 1 | import java.nio.file.Files; 2 | import java.nio.file.Path; 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | import java.util.stream.Collectors; 7 | 8 | class Day11 { 9 | 10 | public static void main(String ... args) throws Exception { 11 | System.out.printf("part1: %d\n", monkeyBusiness(20, true)); 12 | System.out.printf("part2: %d\n", monkeyBusiness(10_000, false)); 13 | } 14 | 15 | static long monkeyBusiness(int rounds, boolean manageLevel) throws Exception { 16 | var monkeys = readMonkeys(Files.readString(Path.of("../input/day11.txt"))); 17 | var commonMultiplier = monkeys.stream().mapToInt(m -> m.divisible).reduce(1, (a,b) -> a*b); 18 | long[] inspects = new long[monkeys.size()]; 19 | 20 | for (int j = 0; j < rounds; j++) { 21 | for (int mi = 0; mi < monkeys.size(); mi++) { 22 | var m = monkeys.get(mi); 23 | for (var i : m.items) { 24 | inspects[mi]++; 25 | var level = m.worryLevel(i) / (manageLevel ? 3 : 1) % commonMultiplier; 26 | var target = level % m.divisible == 0 ? m.trueTo : m.falseTo; 27 | monkeys.get(target).items.add(level); 28 | } 29 | m.items.clear(); 30 | } 31 | } 32 | return Arrays.stream(inspects).mapToObj(i -> (Long)i).sorted((a,b) -> b.compareTo(a)) 33 | .limit(2).reduce(1l, (a,b) -> a*b); 34 | } 35 | 36 | static List readMonkeys(String input) { 37 | return Arrays.stream(input.split("\n\n")) 38 | .map(Monkey::parse).collect(Collectors.toCollection(() -> new ArrayList<>())); 39 | } 40 | 41 | record Monkey (List items, String[] oper, int divisible, int trueTo, int falseTo) { 42 | static Monkey parse(String input) { 43 | var lines = input.split("\n"); 44 | var items = Arrays.stream(lines[1].split(":")[1].split(",")) 45 | .map(i -> Long.parseLong(i.trim())).collect(Collectors.toCollection(ArrayList::new)); 46 | var operation = lines[2].split("= old ")[1].split(" "); 47 | var divisible = Integer.parseInt(lines[3].split("by ")[1]); 48 | var trueTo = Integer.parseInt(lines[4].split("monkey ")[1]); 49 | var falseTo = Integer.parseInt(lines[5].split("monkey ")[1]); 50 | return new Monkey(items, operation, divisible, trueTo, falseTo); 51 | } 52 | 53 | long worryLevel(long level) { 54 | var val = oper[1].equals("old") ? level : Integer.parseInt(oper[1]); 55 | return switch (oper[0]) { 56 | case "*" -> level * val; 57 | case "+" -> level + val; 58 | default -> throw new IllegalArgumentException(); 59 | }; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /2022/java/Day12.java: -------------------------------------------------------------------------------- 1 | import java.nio.file.Files; 2 | import java.nio.file.Path; 3 | import java.util.HashMap; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.function.Function; 8 | 9 | class Day12 { 10 | 11 | public static void main(String ... args) throws Exception { 12 | var map = readMap(Files.readString(Path.of("../input/day12.txt"))); 13 | XY start = map.keySet().stream().filter(k -> map.get(k) == 'S').findFirst().get(); 14 | XY end = map.keySet().stream().filter(k -> map.get(k) == 'E').findFirst().get(); 15 | map.put(start, (byte) 'a'); 16 | map.put(end, (byte) 'z'); 17 | 18 | System.out.printf("part1: %d\n", 19 | shortestPath(map, new HashMap<>(), start, dh -> dh <= 1, p -> p.equals(end))); 20 | System.out.printf("part2: %d\n", 21 | shortestPath(map, new HashMap<>(), end, dh -> dh >= -1, p -> map.get(p) == 'a')); 22 | } 23 | 24 | static int shortestPath(Map map, Map visits, XY start, 25 | Function ok, Function target) { 26 | var visited = new HashMap(); 27 | var toVisit = new LinkedList(); 28 | toVisit.add(new Visit(start, 0)); 29 | while(!toVisit.isEmpty()) { 30 | var c = toVisit.removeFirst(); 31 | if (!visited.containsKey(c.xy) || visited.get(c.xy) > c.distance) { // not visited or longer distance 32 | visited.put(c.xy, c.distance); // visit 33 | moves.stream().map(m -> c.move(m)) // next location 34 | .filter(n -> map.containsKey(n.xy)) // is inside map 35 | .filter(n -> ok.apply((byte) (map.get(n.xy) - map.get(c.xy)))) // ok to climb 36 | .forEach(n -> toVisit.add(n)); // add to visit list 37 | } 38 | } 39 | 40 | return visited.keySet().stream() 41 | .filter(p -> target.apply(p)) // match or target 42 | .map(p -> visited.get(p)) // map to distance 43 | .sorted().findFirst().get(); // get minimum 44 | } 45 | 46 | static Map readMap(String input) { 47 | Map map = new HashMap<>(); 48 | var lines = input.lines().toList(); 49 | for (int y = 0; y < lines.size(); y++) { 50 | for (int x = 0; x < lines.get(y).length(); x++) { 51 | map.put(new XY(x,y), (byte) lines.get(y).charAt(x)); 52 | } 53 | } 54 | return map; 55 | } 56 | 57 | record Visit(XY xy, int distance) { 58 | Visit move(XY v) { return new Visit(new XY(xy.x+v.x, xy.y+v.y), distance + 1); } 59 | } 60 | 61 | record XY(int x, int y) {} 62 | 63 | static List moves = List.of(new XY(1,0), new XY(-1,0), new XY(0,1), new XY(0,-1)); 64 | } -------------------------------------------------------------------------------- /2022/java/Day13.java: -------------------------------------------------------------------------------- 1 | import java.nio.file.Files; 2 | import java.nio.file.Path; 3 | import java.util.Arrays; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | import java.util.stream.IntStream; 7 | import java.util.stream.Stream; 8 | 9 | class Day13 { 10 | public static void main(String ... args) throws Exception { 11 | var pairs = readPairs(Files.readString(Path.of("../input/day13.txt"))); 12 | 13 | int sum = IntStream.range(0, pairs.size()) 14 | .filter(i -> compare(pairs.get(i).get(0), pairs.get(i).get(1), 0) <= 0) 15 | .map(i -> i + 1).sum(); 16 | System.out.printf("part1: %s\n", sum); 17 | 18 | var dividers = List.of(List.of(2), List.of(6)); 19 | var packets = pairs.stream().flatMap(p -> Stream.of(p.get(0), p.get(1))).toList(); 20 | var sorted = Stream.concat(packets.stream(), dividers.stream()) 21 | .sorted((a,b) -> compare(a, b, 0)).toList(); 22 | var key = IntStream.range(0, sorted.size()) 23 | .filter(p -> dividers.contains(sorted.get(p))) 24 | .map(i -> i + 1).reduce(1, (a,b) -> a * b); 25 | System.out.printf("part2: %s\n", key); 26 | } 27 | 28 | static int compare(List left, List right, int i) { 29 | if (i >= left.size() && i >= right.size()) { 30 | return 0; 31 | } else if (i >= left.size()) { 32 | return -1; 33 | } else if (i >= right.size()) { 34 | return 1; 35 | } 36 | 37 | var li = left.get(i); 38 | var ri = right.get(i); 39 | if (li instanceof Integer && ri instanceof Integer) { 40 | return li.equals(ri) ? compare(left, right, i+1) : (int)li -(int)ri; 41 | } 42 | 43 | List ll = li instanceof List ? (List)(li) : List.of(li); 44 | List rl = ri instanceof List ? (List)(ri) : List.of(ri); 45 | var res = compare(ll, rl, 0); 46 | return res == 0 ? compare(left, right, i+1) : res; 47 | } 48 | 49 | static List>> readPairs(String input) { 50 | return Arrays.stream(input.split("\n\n")) 51 | .map(pair -> pair.split("\n")) 52 | .map(pair -> List.of(parse(pair[0]), parse(pair[1]))).toList(); 53 | } 54 | 55 | static List parse(String input) { 56 | LinkedList> stack = new LinkedList<>(); 57 | List latest = new LinkedList<>(); 58 | StringBuilder buffer = new StringBuilder(); 59 | 60 | Runnable add = () -> { 61 | if (!buffer.isEmpty()) { 62 | stack.getFirst().add(Integer.parseInt(buffer.toString())); 63 | buffer.setLength(0); 64 | } 65 | }; 66 | 67 | for (int i = 0; i < input.length(); i++) { 68 | switch (input.charAt(i)) { 69 | case '[' -> stack.push(new LinkedList<>()); 70 | case ']' -> { 71 | add.run(); 72 | latest = stack.pop(); 73 | if (!stack.isEmpty()) stack.getFirst().add(latest); 74 | } 75 | case ',' -> add.run(); 76 | default -> buffer.append(input.charAt(i)); 77 | } 78 | } 79 | return latest; 80 | } 81 | } -------------------------------------------------------------------------------- /2022/java/Day14.java: -------------------------------------------------------------------------------- 1 | import java.nio.file.Files; 2 | import java.nio.file.Path; 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.stream.Collectors; 9 | import java.util.stream.Stream; 10 | 11 | class Day14 { 12 | 13 | public static void main(String ... args) throws Exception { 14 | System.out.printf("part1: %d\n", pourSand(false)); 15 | System.out.printf("part2: %d\n", pourSand(true)); 16 | } 17 | 18 | static long pourSand(boolean haveFloor) throws Exception { 19 | var cave = readRocks(Files.readString(Path.of("../input/day14.txt"))); 20 | int floor = haveFloor ? cave.keySet().stream().mapToInt(r -> r.y).max().getAsInt() + 2 : 0; 21 | long count = -1; 22 | while(count != countSand(cave)) { 23 | count = countSand(cave); 24 | dropSand(cave, floor); 25 | } 26 | return count; 27 | } 28 | 29 | static void dropSand(Map cave, int floor) { 30 | XY sand = new XY(500, 0), prev; 31 | do { 32 | prev = sand; 33 | XY old = sand; 34 | sand = moves.stream().map(m -> new XY(old.x + m.x, old.y + m.y)) 35 | .filter(n -> !cave.containsKey(n)).findFirst().orElse(sand); // find next place for sand 36 | } while (prev != sand && sand.y + 1 != floor && sand.y < 999); 37 | 38 | if (cave.containsKey(new XY(sand.x, sand.y+1)) || sand.y + 1 == floor) { 39 | cave.put(sand, Entry.SAND); // We have support below, place sand 40 | } 41 | } 42 | 43 | static long countSand(Map cave) { 44 | return cave.values().stream().filter(e -> e == Entry.SAND).count(); 45 | } 46 | 47 | static Map readRocks(String input) { 48 | return Arrays.stream(input.split("\n")).flatMap(l -> toRocks(l).stream()) 49 | .collect(Collectors.toMap(r -> r, r -> Entry.ROCK, (a,b) -> a, () -> new HashMap())); 50 | } 51 | 52 | static List toRocks(String line) { 53 | String[] parts = line.split(" -> "); 54 | var res = new ArrayList(); 55 | for (int i = 1; i < parts.length; i++) { 56 | res.addAll(draw(XY.parse(parts[i-1]), XY.parse(parts[i])).toList()); 57 | } 58 | return res; 59 | } 60 | 61 | static Stream draw(XY p1, XY p2) { 62 | if (p1.equals(p2)) { return Stream.of(p1); } 63 | return Stream.concat(Stream.of(p1), draw(p1.moveTowards(p2), p2)); 64 | } 65 | 66 | enum Entry{ROCK, SAND}; 67 | 68 | record XY(int x, int y) { 69 | static XY parse(String input) { 70 | var parts = input.split(","); 71 | return new XY(Integer.parseInt(parts[0]), Integer.parseInt(parts[1])); 72 | } 73 | XY moveTowards(XY o) { 74 | if (x == o.x && y < o.y) return new XY(x, y+1); 75 | if (x == o.x && y > o.y) return new XY(x, y-1); 76 | if (y == o.y && x < o.x) return new XY(x+1, y); 77 | if (y == o.y && x > o.x) return new XY(x-1, y); 78 | throw new IllegalStateException(); 79 | } 80 | } 81 | 82 | static List moves = List.of(new XY(0, 1), new XY(-1, 1), new XY(1, 1)); 83 | } -------------------------------------------------------------------------------- /2022/java/Day15.java: -------------------------------------------------------------------------------- 1 | import java.nio.file.*; 2 | import java.util.ArrayList; 3 | import java.util.Arrays; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | import java.util.regex.Pattern; 7 | import java.util.stream.IntStream; 8 | import java.util.stream.Stream; 9 | 10 | class Day15 { 11 | 12 | public static void main(String ... args) throws Exception { 13 | var lines = Files.readString(Path.of("../input/day15.txt")).split("\n"); 14 | var sensors = Arrays.stream(lines).map(Sensor::parse).toList(); 15 | 16 | var count = rangesForY(sensors, 2000000).stream().mapToLong(r -> r.end() - r.start()).sum(); 17 | System.out.printf("part1: %d\n", count); 18 | 19 | var freq = IntStream.range(0, 4000000) 20 | .mapToObj(y -> rangesForY(sensors, y)) 21 | .filter(rs -> rs.size() > 1) // find the one hole in the middle, two ranges 22 | .map(rs -> rs.get(0)) // get the first range, location is after it end 23 | .map(r -> (r.end + 1)*4000000+r.y) // calculate the frequency 24 | .findFirst().get(); 25 | 26 | System.out.printf("part2: %d\n", freq); 27 | } 28 | 29 | static List rangesForY(List sensors, int y) { 30 | var ranges = sensors.stream().flatMap(s -> { 31 | var beaconDist = Math.abs(s.loc.x - s.beacon.x) + Math.abs(s.loc.y - s.beacon.y); 32 | var dist = beaconDist - Math.abs(s.loc.y - y); // x distance for this sensor for this y 33 | return dist > 0 ? Stream.of(new Range(s.loc.x - dist, s.loc.x + dist, y)) : Stream.of(); 34 | }).sorted((a,b) -> (int) (a.start()-b.start())).toList(); // sorted ranges 35 | // merge adjoining or overlapping ranges 36 | LinkedList res = new LinkedList<>(List.of(ranges.get(0))); 37 | for (var next : ranges.subList(1, ranges.size())) { 38 | var prev = res.getLast(); 39 | if (prev.end() >= next.start() - 1) { 40 | res.removeLast(); 41 | res.add(new Range(prev.start(), Math.max(prev.end(), next.end()), y)); 42 | } else { 43 | res.add(next); 44 | } 45 | } 46 | return res; 47 | } 48 | 49 | record Range (long start, long end, long y) {} 50 | record XY(long x, long y) {} 51 | record Sensor(XY loc, XY beacon) { 52 | static Sensor parse(String line) { 53 | var m = Pattern.compile("=(-?\\d+)").matcher(line); 54 | var ns = m.results().mapToInt(r -> Integer.parseInt(r.group(1))).toArray(); 55 | return new Sensor(new XY(ns[0], ns[1]), new XY(ns[2], ns[3])); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /2022/java/Day2.java: -------------------------------------------------------------------------------- 1 | import java.nio.file.Files; 2 | import java.nio.file.Path; 3 | 4 | class Day2 { 5 | 6 | static int[] toWin = { 1, 2, 0 }; //for 0/Rock -> 1/Paper, 1/Paper -> 2/Scissors, 2/Scissors -> 0/Rock 7 | static int[] toLose = { 2, 0, 1 }; //for 0/Rock -> 2/Scissors, 1/Paper -> 0/Rock , 2/Scissors -> 1/Paper 8 | 9 | public static void main(String ... args) throws Exception { 10 | var input = Files.readString(Path.of("../input/day2.txt")); 11 | var score = input.lines() 12 | .map(line -> game1(line)) 13 | .mapToInt(game -> score(game)).sum(); 14 | System.out.printf("part 1: %d\n", score); 15 | 16 | var score2 = input.lines() 17 | .map(line -> game2(line)) 18 | .mapToInt(game -> score(game)).sum(); 19 | System.out.printf("part 2: %d\n", score2); 20 | } 21 | 22 | static int[] game1(String line) { 23 | return new int[] { line.charAt(0) - 'A', line.charAt(2)-'X' }; 24 | } 25 | 26 | static int[] game2(String line) { 27 | int shape1 = line.charAt(0)-'A'; 28 | int shape2 = switch (line.charAt(2)) { 29 | case 'X' -> toLose[shape1]; 30 | case 'Z' -> toWin[shape1]; 31 | default -> shape1; 32 | }; 33 | return new int[] { shape1, shape2 }; 34 | } 35 | 36 | static int score(int[] game) { 37 | var shapeScore = game[1] + 1; 38 | return shapeScore + gameScore(game[0], game[1]); 39 | } 40 | 41 | static int gameScore(int shape1, int shape2) { 42 | if (toWin[shape1] == shape2) return 6; 43 | if (toLose[shape1] == shape2) return 0; 44 | else return 3; 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /2022/java/Day3.java: -------------------------------------------------------------------------------- 1 | import java.nio.file.Files; 2 | import java.nio.file.Path; 3 | import java.util.List; 4 | import java.util.stream.Collectors; 5 | import java.util.stream.IntStream; 6 | 7 | class Day3 { 8 | public static void main(String ... args) throws Exception { 9 | var input = Files.readString(Path.of("../input/day3.txt")); 10 | var sum = input.lines().mapToInt(l -> { 11 | var p1 = l.substring(0, l.length()/2); 12 | var p2 = l.substring(l.length()/2, l.length()); 13 | return commonPriority(List.of(p1, p2)); 14 | }).sum(); 15 | System.out.printf("part 1: %d\n", sum); 16 | 17 | var counter = IntStream.range(0, input.length()).iterator(); 18 | var sum2 = input.lines() 19 | .collect(Collectors.groupingBy(c -> counter.nextInt()/3)).values().stream() 20 | .mapToInt(l -> commonPriority(l)).sum(); 21 | System.out.printf("part 2: %d\n", sum2); 22 | } 23 | 24 | static int commonPriority(List strings) { 25 | // common character in list of strings 26 | var c = strings.stream().reduce((s1, s2) -> commonChars(s1, s2)).get().charAt(0); 27 | return c < 'a' ? c - 'A' + 27 : c - 'a' + 1; // convert to priority 28 | } 29 | 30 | static String commonChars(String a, String b) { 31 | return a.chars().filter(i1 -> b.chars().anyMatch(i2 -> i1 == i2)).distinct() 32 | .mapToObj(c -> Character.toString(c)).collect(Collectors.joining()); 33 | } 34 | } -------------------------------------------------------------------------------- /2022/java/Day4.java: -------------------------------------------------------------------------------- 1 | import java.nio.file.Files; 2 | import java.nio.file.Path; 3 | import java.util.stream.Stream; 4 | 5 | class Day4 { 6 | public static void main(String ... args) throws Exception { 7 | var input = Files.readString(Path.of("../input/day4.txt")); 8 | 9 | var pairs = input.lines().map(l -> Range.parsePair(l)).toList(); 10 | 11 | var sum1 = pairs.stream() 12 | .filter(pair -> pair[0].fullyContains(pair[1]) || pair[1].fullyContains(pair[0])) 13 | .count(); 14 | System.out.printf("part 1: %d\n", sum1); 15 | 16 | var sum2 = pairs.stream() 17 | .filter(pair -> pair[0].overlap(pair[1]) || pair[1].overlap(pair[0])) 18 | .count(); 19 | System.out.printf("part 2: %d\n", sum2); 20 | } 21 | } 22 | 23 | record Range (int start, int end) { 24 | static Range[] parsePair(String line) { 25 | return Stream.of(line.split(",")).map(s -> parse(s)).toArray(Range[]::new); 26 | } 27 | 28 | static Range parse(String range) { 29 | var ends = Stream.of(range.split("-")).mapToInt(s -> Integer.parseInt(s)).toArray(); 30 | return new Range(ends[0], ends[1]); 31 | } 32 | 33 | boolean fullyContains(Range o) { 34 | return start <= o.start && end >= o.end; 35 | } 36 | 37 | boolean overlap(Range o) { 38 | return start <= o.start && end >= o.start; 39 | } 40 | } -------------------------------------------------------------------------------- /2022/java/Day6.java: -------------------------------------------------------------------------------- 1 | import java.nio.file.Files; 2 | import java.nio.file.Path; 3 | import java.util.stream.IntStream; 4 | 5 | class Day6 { 6 | public static void main(String ... args) throws Exception { 7 | var input = Files.readString(Path.of("../input/day6.txt")); 8 | System.out.printf("part 1: %d\n", findMarker(input, 4)); 9 | System.out.printf("part 2: %d\n", findMarker(input, 14)); 10 | } 11 | 12 | static int findMarker(String input, int len) { 13 | return IntStream.range(0, input.length()-len) 14 | .filter(i -> allDifferent(input.substring(i, i + len))).findFirst().getAsInt() + len; 15 | } 16 | 17 | static boolean allDifferent(String input) { 18 | return input.chars().distinct().count() == input.length(); 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /2022/java/Day7.java: -------------------------------------------------------------------------------- 1 | import java.nio.file.Files; 2 | import java.nio.file.Path; 3 | import java.util.Map; 4 | import java.util.TreeMap; 5 | import java.util.regex.Pattern; 6 | import java.util.stream.Stream; 7 | 8 | class Day7 { 9 | public static void main(String ... args) throws Exception { 10 | var root = parseTerminal(Files.readString(Path.of("../input/day7.txt"))); 11 | 12 | var part1 = root.dirs() 13 | .filter(n -> n.size() <= 100000) 14 | .mapToInt(n -> n.size()).sum(); 15 | System.out.printf("part 1: %s\n", part1); 16 | 17 | var needed = 30000000 - (70000000 - root.size()); 18 | var part2 = root.dirs() 19 | .filter(n -> n.size() >= needed) 20 | .mapToInt(n -> n.size()).sorted().findFirst().getAsInt(); 21 | System.out.printf("part 2: %s\n", part2); 22 | } 23 | 24 | static Node parseTerminal(String input) { 25 | Node root = new Node(0, null, new TreeMap<>()); 26 | input.lines().reduce(root, (n, l) -> n.apply(l), (a,b) -> {return null;}); 27 | return root; 28 | } 29 | } 30 | 31 | record Node(int size, Node parent, Map nodes) { 32 | static Pattern filePattern = Pattern.compile("(\\d+) ([\\w.]+)"); 33 | 34 | Node apply(String line) { 35 | if (line.startsWith("$ cd ")) { 36 | return cd(line.substring(5)); 37 | } 38 | var fileMatcher = filePattern.matcher(line); 39 | if (fileMatcher.matches()) { 40 | return add(fileMatcher.group(2), fileMatcher.group(1)); 41 | } 42 | return this; 43 | } 44 | 45 | Node cd(String name) { 46 | return switch (name) { 47 | case "/" -> parent == null ? this : parent.cd("/"); 48 | case ".." -> parent; 49 | default -> nodes.computeIfAbsent(name, k -> new Node(0, this, new TreeMap<>())); 50 | }; 51 | } 52 | 53 | Node add(String name, String size) { 54 | nodes.put(name, new Node(Integer.parseInt(size), this, new TreeMap<>())); 55 | return this; 56 | } 57 | 58 | Stream dirs() { 59 | return Stream.concat(Stream.of(this), nodes.values().stream().flatMap(n -> n.dirs()) 60 | .filter(n -> n.size == 0)); 61 | } 62 | 63 | public int size() { 64 | return size > 0 ? size : nodes.values().stream().mapToInt(n -> n.size()).sum(); 65 | } 66 | }; -------------------------------------------------------------------------------- /2022/java/Day8.java: -------------------------------------------------------------------------------- 1 | import java.nio.file.Files; 2 | import java.nio.file.Path; 3 | import java.util.function.Function; 4 | import java.util.stream.IntStream; 5 | import java.util.stream.Stream; 6 | 7 | class Day8 { 8 | 9 | public static void main(String ... args) throws Exception { 10 | var map = parseMap(Files.readString(Path.of("../input/day8.txt"))); 11 | 12 | System.out.printf("part 1: %s\n", countVisible(map)); 13 | System.out.printf("part 2: %s\n", highestScenicScore(map)); 14 | } 15 | 16 | static int highestScenicScore(int[][] map) { 17 | return IntStream.range(0, map.length) 18 | .flatMap(y -> IntStream.range(0, map[y].length) 19 | .map(x -> scenicScore(map, new Point(x, y)))).max().getAsInt(); 20 | } 21 | 22 | static int scenicScore(int[][] map, Point op) { 23 | var up = visibleTrees(map, op, p -> p.move(0, -1)); 24 | var down = visibleTrees(map, op, p -> p.move(0, 1)); 25 | var left = visibleTrees(map, op, p -> p.move(-1, 0)); 26 | var right = visibleTrees(map, op, p -> p.move(1, 0)); 27 | 28 | return up * down * left * right; 29 | } 30 | 31 | static int visibleTrees(int[][] map, Point op, Function move) { 32 | int count = 0; 33 | for (var p = move.apply(op); p.inside(map); p = move.apply(p)) { 34 | count++; 35 | if (map[p.y()][p.x()] >= map[op.y()][op.x()]) { 36 | return count; 37 | } 38 | } 39 | return count; 40 | } 41 | 42 | static long countVisible(int[][] map) { 43 | return IntStream.range(0, map.length) 44 | .flatMap(y -> IntStream.range(0, map[y].length).filter(x -> visible(map, y, x))).count(); 45 | } 46 | 47 | static boolean visible(int[][] map, int y, int x) { 48 | if (y == 0 || y == map.length-1 || x == 0 || x == map[y].length-1) { 49 | return true; // edge cases 50 | } 51 | var height = map[y][x]; 52 | return Stream.of( 53 | IntStream.of(map[y]).limit(x).max(), // left max height 54 | IntStream.of(map[y]).skip(x+1).max(), // right max 55 | IntStream.of(col(map,x)).limit(y).max(), // up 56 | IntStream.of(col(map,x)).skip(y+1).max()) // down 57 | .mapToInt(m -> m.getAsInt()).anyMatch(h -> h < height); // any shorter than given tree 58 | } 59 | 60 | static int[] col(int[][] map, int x) { 61 | return IntStream.range(0, map.length).map(y -> map[y][x]).toArray(); 62 | } 63 | 64 | static int[][] parseMap(String input) { 65 | return input.lines().map(l -> l.chars().map(c -> c - '0').toArray()).toArray(int[][]::new); 66 | } 67 | } 68 | 69 | record Point(int x, int y) { 70 | Point move(int mx, int my) { return new Point(x + mx, y + my);} 71 | boolean inside(int[][] map) { return y >= 0 && y < map.length && x >= 0 && x < map[y].length; } 72 | } -------------------------------------------------------------------------------- /2022/java/Day9.java: -------------------------------------------------------------------------------- 1 | import java.nio.file.Files; 2 | import java.nio.file.Path; 3 | import java.util.List; 4 | import java.util.Map; 5 | import java.util.Set; 6 | import java.util.stream.Collectors; 7 | import java.util.stream.IntStream; 8 | import java.util.stream.Stream; 9 | 10 | class Day9 { 11 | 12 | public static void main(String ... args) throws Exception { 13 | var moves = parseMoves(Files.readString(Path.of("../input/day9.txt"))); 14 | var head = tieKnots(10); 15 | head = runMoves(moves, head); 16 | System.out.printf("part 1: %s\n", nth(head, 1).history.stream().distinct().count()); 17 | System.out.printf("part 2: %s\n", nth(head, 9).history.stream().distinct().count()); 18 | } 19 | 20 | static Knot nth(Knot k, int count) { 21 | return count == 0 ? k : nth(k.tail, count-1); 22 | } 23 | 24 | static Knot runMoves(List moves, Knot head) { 25 | return moves.stream().reduce(head, (k, m) -> k.apply(m), (a,b) -> {return a;}); 26 | } 27 | 28 | static Knot tieKnots(int count) { 29 | return new Knot(new XY(0,0), Set.of(new XY(0,0)), count > 1 ? tieKnots(count-1): null); 30 | } 31 | 32 | static List parseMoves(String input) { 33 | return input.lines() 34 | .map(l -> new Move(vector.get(l.charAt(0)), Integer.parseInt(l.substring(2)))) 35 | .toList(); 36 | } 37 | 38 | record Knot(XY pos, Set history, Knot tail) { 39 | Knot apply(Move m) { 40 | return IntStream.range(0, m.steps).mapToObj(i -> m) 41 | .reduce(this, (k, move) -> k.move(move.vector), (a,b) -> {return a;}); 42 | } 43 | 44 | Knot move(XY vector) { 45 | var newPos = pos.move(vector); 46 | var newHistory = Stream.concat(history.stream(), Stream.of(newPos)).collect(Collectors.toSet()); 47 | return new Knot(newPos, newHistory, tail != null ? tail.follow(newPos) : null); 48 | } 49 | 50 | Knot follow(XY head) { 51 | if (head.touching(pos)) { 52 | return this; 53 | } 54 | var dx = (head.x-pos.x)/2 + (head.x-pos.x)%2; 55 | var dy = (head.y-pos.y)/2 + (head.y-pos.y)%2; 56 | return move(new XY(dx, dy)); 57 | } 58 | } 59 | 60 | record Move(XY vector, int steps) {} 61 | 62 | record XY(int x, int y) { 63 | XY move(XY vector) { 64 | return new XY(x + vector.x, y + vector.y); 65 | } 66 | boolean touching(XY o) { 67 | return Math.abs(x-o.x) <= 1 && Math.abs(y-o.y) <= 1; 68 | } 69 | } 70 | 71 | static Map vector = Map.of( 72 | 'U', new XY(0, -1), 'D', new XY(0, +1), 73 | 'L', new XY(-1, 0), 'R', new XY(+1, 0)); 74 | } 75 | -------------------------------------------------------------------------------- /2023/input/day10_test.txt: -------------------------------------------------------------------------------- 1 | 7-F7- 2 | .FJ|7 3 | SJLL7 4 | |F--J 5 | LJ.LJ -------------------------------------------------------------------------------- /2023/input/day10_test2.txt: -------------------------------------------------------------------------------- 1 | ........... 2 | .S-------7. 3 | .|F-----7|. 4 | .||.....||. 5 | .||.....||. 6 | .|L-7.F-J|. 7 | .|..|.|..|. 8 | .L--J.L--J. 9 | ........... -------------------------------------------------------------------------------- /2023/input/day10_test3.txt: -------------------------------------------------------------------------------- 1 | .......... 2 | .S------7. 3 | .|F----7|. 4 | .||OOOO||. 5 | .||OOOO||. 6 | .|L-7F-J|. 7 | .|II||II|. 8 | .L--JL--J. 9 | .......... -------------------------------------------------------------------------------- /2023/input/day10_test4.txt: -------------------------------------------------------------------------------- 1 | FF7FSF7F7F7F7F7F---7 2 | L|LJ||||||||||||F--J 3 | FL-7LJLJ||||||LJL-77 4 | F--JF--7||LJLJ7F7FJ- 5 | L---JF-JLJ.||-FJLJJ7 6 | |F|F-JF---7F7-L7L|7| 7 | |FFJF7L7F-JF7|JL---7 8 | 7-L-JL7||F7|L7F-7F7| 9 | L.L7LFJ|||||FJL7||LJ 10 | L7JLJL-JLJLJL--JLJ.L -------------------------------------------------------------------------------- /2023/input/day11_test.txt: -------------------------------------------------------------------------------- 1 | ...#...... 2 | .......#.. 3 | #......... 4 | .......... 5 | ......#... 6 | .#........ 7 | .........# 8 | .......... 9 | .......#.. 10 | #...#..... -------------------------------------------------------------------------------- /2023/input/day12_test.txt: -------------------------------------------------------------------------------- 1 | ???.### 1,1,3 2 | .??..??...?##. 1,1,3 3 | ?#?#?#?#?#?#?#? 1,3,1,6 4 | ????.#...#... 4,1,1 5 | ????.######..#####. 1,6,5 6 | ?###???????? 3,2,1 -------------------------------------------------------------------------------- /2023/input/day13_test.txt: -------------------------------------------------------------------------------- 1 | #.##..##. 2 | ..#.##.#. 3 | ##......# 4 | ##......# 5 | ..#.##.#. 6 | ..##..##. 7 | #.#.##.#. 8 | 9 | #...##..# 10 | #....#..# 11 | ..##..### 12 | #####.##. 13 | #####.##. 14 | ..##..### 15 | #....#..# -------------------------------------------------------------------------------- /2023/input/day14_test.txt: -------------------------------------------------------------------------------- 1 | O....#.... 2 | O.OO#....# 3 | .....##... 4 | OO.#O....O 5 | .O.....O#. 6 | O.#..O.#.# 7 | ..O..#O..O 8 | .......O.. 9 | #....###.. 10 | #OO..#.... -------------------------------------------------------------------------------- /2023/input/day15_test.txt: -------------------------------------------------------------------------------- 1 | rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7 -------------------------------------------------------------------------------- /2023/input/day16_test.txt: -------------------------------------------------------------------------------- 1 | .|...\.... 2 | |.-.\..... 3 | .....|-... 4 | ........|. 5 | .......... 6 | .........\ 7 | ..../.\\.. 8 | .-.-/..|.. 9 | .|....-|.\ 10 | ..//.|.... -------------------------------------------------------------------------------- /2023/input/day17_test.txt: -------------------------------------------------------------------------------- 1 | 2413432311323 2 | 3215453535623 3 | 3255245654254 4 | 3446585845452 5 | 4546657867536 6 | 1438598798454 7 | 4457876987766 8 | 3637877979653 9 | 4654967986887 10 | 4564679986453 11 | 1224686865563 12 | 2546548887735 13 | 4322674655533 -------------------------------------------------------------------------------- /2023/input/day17_test2.txt: -------------------------------------------------------------------------------- 1 | 111111111111 2 | 999999999991 3 | 999999999991 4 | 999999999991 5 | 999999999991 6 | -------------------------------------------------------------------------------- /2023/input/day18_test.txt: -------------------------------------------------------------------------------- 1 | R 6 (#70c710) 2 | D 5 (#0dc571) 3 | L 2 (#5713f0) 4 | D 2 (#d2c081) 5 | R 2 (#59c680) 6 | D 2 (#411b91) 7 | L 5 (#8ceee2) 8 | U 2 (#caa173) 9 | L 1 (#1b58a2) 10 | U 2 (#caa171) 11 | R 2 (#7807d2) 12 | U 3 (#a77fa3) 13 | L 2 (#015232) 14 | U 2 (#7a21e3) -------------------------------------------------------------------------------- /2023/input/day19_test.txt: -------------------------------------------------------------------------------- 1 | px{a<2006:qkq,m>2090:A,rfg} 2 | pv{a>1716:R,A} 3 | lnx{m>1548:A,A} 4 | rfg{s<537:gd,x>2440:R,A} 5 | qs{s>3448:A,lnx} 6 | qkq{x<1416:A,crn} 7 | crn{x>2662:A,R} 8 | in{s<1351:px,qqz} 9 | qqz{s>2770:qs,m<1801:hdj,R} 10 | gd{a>3333:R,R} 11 | hdj{m>838:A,pv} 12 | 13 | {x=787,m=2655,a=1222,s=2876} 14 | {x=1679,m=44,a=2067,s=496} 15 | {x=2036,m=264,a=79,s=2244} 16 | {x=2461,m=1339,a=466,s=291} 17 | {x=2127,m=1623,a=2188,s=1013} -------------------------------------------------------------------------------- /2023/input/day1_test.txt: -------------------------------------------------------------------------------- 1 | 1abc2 2 | pqr3stu8vwx 3 | a1b2c3d4e5f 4 | treb7uchet 5 | -------------------------------------------------------------------------------- /2023/input/day1_test2.txt: -------------------------------------------------------------------------------- 1 | two1nine 2 | eightwothree 3 | abcone2threexyz 4 | xtwone3four 5 | 4nineeightseven2 6 | zoneight234 7 | 7pqrstsixteen 8 | -------------------------------------------------------------------------------- /2023/input/day20.txt: -------------------------------------------------------------------------------- 1 | %nz -> jm, ms 2 | %xk -> cs, ks 3 | %rh -> ks 4 | %ml -> mf, ks 5 | %mf -> ks, km 6 | &ms -> lq, fm, sz 7 | %bk -> zz, ks 8 | %zf -> dn 9 | %qf -> kf 10 | %zv -> mz, ms 11 | &tc -> mn, xf, jl, xs, zk 12 | %hd -> dn, mm 13 | %nv -> pn, dn 14 | &gc -> zr 15 | &ks -> jn, cs, cm 16 | %vz -> pz, tc 17 | %jl -> ps 18 | %lq -> ms, fm 19 | %fl -> ms 20 | %zz -> ks, vd 21 | %td -> bj, tc 22 | broadcaster -> mn, jn, hd, lq 23 | &dn -> jk, qf, gc, hf, hd, nr, mm 24 | %vd -> ks, ml 25 | %cp -> fl, ms 26 | %jn -> ks, xk 27 | %xg -> tc 28 | %xs -> zk 29 | %kf -> dz, dn 30 | %qx -> ks, rh 31 | %kb -> ms, tl 32 | %mm -> nv 33 | %mn -> tc, xs 34 | %cs -> gb 35 | %jm -> ms, cp 36 | %bj -> tc, xx 37 | %pn -> dn, jk 38 | %fm -> zv 39 | %jk -> nr 40 | %pz -> td, tc 41 | %xx -> tc, xg 42 | %gb -> bk, ks 43 | %dz -> zb, dn 44 | %vl -> jl, tc 45 | &sz -> zr 46 | %gx -> ms, kb 47 | %zb -> dn, zf 48 | %tl -> pp, ms 49 | %pp -> nz, ms 50 | %km -> ks, qx 51 | %ps -> tc, vz 52 | %mz -> ms, gx 53 | &cm -> zr 54 | %hf -> qf 55 | &zr -> rx 56 | &xf -> zr 57 | %nr -> hf 58 | %zk -> vl -------------------------------------------------------------------------------- /2023/input/day20_test.txt: -------------------------------------------------------------------------------- 1 | broadcaster -> a 2 | %a -> inv, con 3 | &inv -> b 4 | %b -> con 5 | &con -> output -------------------------------------------------------------------------------- /2023/input/day2_test.txt: -------------------------------------------------------------------------------- 1 | Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green 2 | Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue 3 | Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red 4 | Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red 5 | Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green -------------------------------------------------------------------------------- /2023/input/day3_test.txt: -------------------------------------------------------------------------------- 1 | 467..114.. 2 | ...*...... 3 | ..35..633. 4 | ......#... 5 | 617*...... 6 | .....+.58. 7 | ..592..... 8 | ......755. 9 | ...$.*.... 10 | .664.598.. -------------------------------------------------------------------------------- /2023/input/day4_test.txt: -------------------------------------------------------------------------------- 1 | Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53 2 | Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19 3 | Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1 4 | Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83 5 | Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36 6 | Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11 -------------------------------------------------------------------------------- /2023/input/day5_test.txt: -------------------------------------------------------------------------------- 1 | seeds: 79 14 55 13 2 | 3 | seed-to-soil map: 4 | 50 98 2 5 | 52 50 48 6 | 7 | soil-to-fertilizer map: 8 | 0 15 37 9 | 37 52 2 10 | 39 0 15 11 | 12 | fertilizer-to-water map: 13 | 49 53 8 14 | 0 11 42 15 | 42 0 7 16 | 57 7 4 17 | 18 | water-to-light map: 19 | 88 18 7 20 | 18 25 70 21 | 22 | light-to-temperature map: 23 | 45 77 23 24 | 81 45 19 25 | 68 64 13 26 | 27 | temperature-to-humidity map: 28 | 0 69 1 29 | 1 0 69 30 | 31 | humidity-to-location map: 32 | 60 56 37 33 | 56 93 4 -------------------------------------------------------------------------------- /2023/input/day6.txt: -------------------------------------------------------------------------------- 1 | Time: 60 94 78 82 2 | Distance: 475 2138 1015 1650 -------------------------------------------------------------------------------- /2023/input/day6_test.txt: -------------------------------------------------------------------------------- 1 | Time: 7 15 30 2 | Distance: 9 40 200 3 | -------------------------------------------------------------------------------- /2023/input/day7_test.txt: -------------------------------------------------------------------------------- 1 | 32T3K 765 2 | T55J5 684 3 | KK677 28 4 | KTJJT 220 5 | QQQJA 483 -------------------------------------------------------------------------------- /2023/input/day8_test.txt: -------------------------------------------------------------------------------- 1 | LLR 2 | 3 | AAA = (BBB, BBB) 4 | BBB = (AAA, ZZZ) 5 | ZZZ = (ZZZ, ZZZ) -------------------------------------------------------------------------------- /2023/input/day8_test2.txt: -------------------------------------------------------------------------------- 1 | LR 2 | 3 | 11A = (11B, XXX) 4 | 11B = (XXX, 11Z) 5 | 11Z = (11B, XXX) 6 | 22A = (22B, XXX) 7 | 22B = (22C, 22C) 8 | 22C = (22Z, 22Z) 9 | 22Z = (22B, 22B) 10 | XXX = (XXX, XXX) -------------------------------------------------------------------------------- /2023/input/day9_test.txt: -------------------------------------------------------------------------------- 1 | 0 3 6 9 12 15 2 | 1 3 6 10 15 21 3 | 10 13 16 21 30 45 -------------------------------------------------------------------------------- /2023/js/day1.js: -------------------------------------------------------------------------------- 1 | 2 | import { asLines } from './util.js'; 3 | 4 | const lineToCalibrationValueA = (line) => { 5 | const first = line.match(/^[^\d]*(\d)/)[1]; 6 | const last = line.match(/(\d)[^\d]*$/)[1]; 7 | return parseInt(first + last); 8 | }; 9 | 10 | const day1a = asLines('../input/day1_a.txt') 11 | .map(lineToCalibrationValueA) 12 | .reduce((a,b) => a + b, 0); 13 | 14 | console.log(`day1 part a: ${day1a}`); 15 | 16 | const lineToCalibrationValueB = (line) => { 17 | return parseInt(findDigits(line).join('')); 18 | } 19 | 20 | const numbers = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine']; 21 | const number = (str) => { 22 | if (str.charAt(0) > '0' && str.charAt(0) <= '9') { 23 | return parseInt(str.charAt(0)); 24 | } 25 | const i = numbers.findIndex((n) => str.startsWith(n)); 26 | return i < 0 ? null : i + 1; 27 | }; 28 | 29 | const findDigits = (line) => { 30 | let first, last; 31 | for (let i = 0; i < line.length; i++) { 32 | const n = number(line.substring(i)); 33 | if (n) { 34 | first = first || n; 35 | last = n; 36 | } 37 | } 38 | return [first, last]; 39 | }; 40 | 41 | const day1b = asLines('../input/day1_a.txt') 42 | .map(lineToCalibrationValueB) 43 | .reduce((a,b) => a + b, 0); 44 | 45 | console.log(`day1 part b: ${day1b}`); 46 | -------------------------------------------------------------------------------- /2023/js/day11.js: -------------------------------------------------------------------------------- 1 | import { asLines } from './util.js'; 2 | 3 | const galaxy = asLines('../input/day11.txt').flatMap((row, y) => 4 | Array.from(row).map((char, x) => char === '#' ? [BigInt(x), BigInt(y)] : []).filter(x => x.length > 0) 5 | ).filter(x => x.length > 0); 6 | 7 | const width = galaxy.reduce((max, [x, y]) => max > x ? max : Number(x), 0) + 1; 8 | const height = galaxy.reduce((max, [x, y]) => max > y ? max : Number(y), 0) + 1; 9 | 10 | const emptyCols =[...Array(width).keys()].map(x => BigInt(x)).filter((col) => !galaxy.find(([x, y]) => x === col)); 11 | const emptyRows =[...Array(height).keys()].map(y => BigInt(y)).filter((row) => !galaxy.find(([x, y]) => y === row)); 12 | 13 | const expand = (x, y, emptyCols, emptyRows, factor) => { 14 | x += BigInt(emptyCols.filter((col) => col < x).length) * (factor - 1n); 15 | y += BigInt(emptyRows.filter((row) => row < y).length) * (factor - 1n); 16 | return [x, y]; 17 | }; 18 | 19 | const expanded = galaxy.map(([x, y]) => expand(x, y, emptyCols, emptyRows, 2n)); 20 | 21 | const genPairs = (arr) => { 22 | return arr.map((a, i) => arr.slice(i + 1).map(b => [a, b])).flat(); 23 | }; 24 | 25 | const pairs = genPairs(expanded) 26 | 27 | const abs = (x) => x < 0 ? -x : x; 28 | const shortestPath = (a, b) => abs(a[0] - b[0]) + abs(a[1] - b[1]); 29 | 30 | const sum1 = pairs.map(([a, b]) => shortestPath(a, b)).reduce((a, b) => a + b, 0n); 31 | 32 | console.log(`part1: ${sum1}`); 33 | 34 | const expanded2 = galaxy.map(([x, y]) => expand(x, y, emptyCols, emptyRows, 1000000n)); 35 | const pairs2 = genPairs(expanded2); 36 | const sum2 = pairs2.map(([a, b]) => shortestPath(a, b)).reduce((a, b) => a + b, 0n); 37 | 38 | console.log(`part2: ${sum2}`); 39 | -------------------------------------------------------------------------------- /2023/js/day12.js: -------------------------------------------------------------------------------- 1 | import { asLines } from './util.js'; 2 | 3 | const records = asLines('../input/day12_test.txt') 4 | 5 | const isValid = (line) => { 6 | const [springs, damagedGroups] = line.split(' '); 7 | const damaged = springs.split('.').filter(s => s.length > 0); 8 | const damagedLengths = damagedGroups.split(',').map(s => parseInt(s)); 9 | if (damaged.length !== damagedLengths.length) { 10 | return false; 11 | } 12 | const mismatch = damaged.filter(group => group.length !== damagedLengths.shift()); 13 | return mismatch.length === 0; 14 | } 15 | 16 | const replaceCharAt = (str, index, replacement) => { 17 | return str.substring(0, index) + replacement + str.substring(index + 1); 18 | } 19 | 20 | const solve = (line) => { 21 | const next = line.indexOf('?'); 22 | if (next === -1) { 23 | return isValid(line) ? 1 : 0; 24 | } 25 | 26 | const opt1 = replaceCharAt(line, next, '.'); 27 | const opt2 = replaceCharAt(line, next, '#'); 28 | 29 | return solve(opt1) + solve(opt2); 30 | } 31 | 32 | const sum = records.map(r => solve(r)).reduce((a, b) => a + b, 0); 33 | 34 | console.log(`part 1: ${sum}`); 35 | 36 | const unfold = (line, times) => { 37 | const [left, right] = line.split(' '); 38 | const left5 = Array(times).fill(left).join('?') 39 | const right5 = Array(times).fill(right).join(',') 40 | return `${left5} ${right5}`; 41 | } 42 | 43 | // This does not work, takes too long 44 | const unfolded = records.map(r => unfold(r, 5)); 45 | const sum2 = unfolded.map(r => solve(r)); 46 | -------------------------------------------------------------------------------- /2023/js/day13.js: -------------------------------------------------------------------------------- 1 | import { readFileSync } from 'fs'; 2 | 3 | const patterns = readFileSync('../input/day13.txt').toString().split('\n\n'); 4 | const pattern = patterns.map(pattern => pattern.split('\n').map(line => line.split(''))); 5 | 6 | const isMirror = (line, mirrorPosition) => { 7 | for (let distance = 0; ; distance++) { 8 | const left = line[mirrorPosition - distance - 1]; 9 | const right = line[mirrorPosition + distance]; 10 | if (left === undefined || right === undefined) { 11 | return true; 12 | } 13 | if (left !== right) { 14 | return false; 15 | } 16 | } 17 | } 18 | 19 | const findMirrorVertical = (pattern, ignore) => { 20 | let options = [...Array(pattern[0].length-1).keys()].map(i => i + 1); 21 | if (ignore) { 22 | options = options.filter(o => o !== ignore); 23 | } 24 | for (let i = 0; i < pattern.length; i++) { 25 | const toCheck = [...options]; 26 | options = options.filter(opt => isMirror(pattern[i], opt)); 27 | } 28 | if (options.length === 1) { 29 | return options[0]; 30 | } 31 | return undefined; 32 | } 33 | 34 | const transpose = (pattern) => { 35 | let res = Array(pattern[0].length).fill('b'); 36 | res = res.map(l => Array(pattern.length).fill('-')); 37 | for (let i = 0; i < pattern.length; i++) { 38 | for (let j = 0; j < pattern[i].length; j++) { 39 | res[j][i] = pattern[i][j]; 40 | } 41 | } 42 | return res; 43 | } 44 | 45 | const score = (pattern) => { 46 | const vert = findMirrorVertical(pattern); 47 | return vert ? vert : findMirrorVertical(transpose(pattern)) * 100; 48 | }; 49 | 50 | const sum1 = pattern.map(p => score(p)).reduce((a, b) => a + b, 0); 51 | 52 | console.log(`part1: ${sum1}`); 53 | 54 | const scoreFixed = (pattern) => { 55 | const origVertical = findMirrorVertical(pattern); 56 | const origHorizontal = findMirrorVertical(transpose(pattern)); 57 | for (let i = 0; i < pattern.length; i++) { 58 | for (let j = 0; j < pattern[i].length; j++) { 59 | const clone = JSON.parse(JSON.stringify(pattern)); 60 | clone[i][j] = clone[i][j] === '.' ? '#' : '.'; 61 | const m = findMirrorVertical(clone, origVertical); 62 | if (m) { 63 | return m; 64 | } 65 | const h = transpose(clone); 66 | const hm = findMirrorVertical(h, origHorizontal); 67 | if (hm) { 68 | return hm * 100; 69 | } 70 | } 71 | } 72 | return undefined; 73 | }; 74 | 75 | const sum2 = pattern.map(p => scoreFixed(p)).reduce((a, b) => a + b, 0); 76 | 77 | console.log(`part2: ${sum2}`); 78 | -------------------------------------------------------------------------------- /2023/js/day14.js: -------------------------------------------------------------------------------- 1 | import { asLines } from './util.js'; 2 | 3 | const platform = asLines('../input/day14.txt').map(line => line.split('')); 4 | 5 | const move = (platform, x, y, nx, ny) => { 6 | const c = platform[y][x]; 7 | const nc = platform[ny] ? platform[ny][nx] : undefined; 8 | if (c === 'O' && nc === '.') { 9 | platform[ny][nx] = c; 10 | platform[y][x] = '.'; 11 | return 1; 12 | } 13 | return 0; 14 | } 15 | 16 | const NORTH = { x: 0, y: -1, start: _ => 0, next: v => v + 1 }; 17 | const WEST = { x: -1, y: 0, start: _ => 0, next: v => v + 1 }; 18 | const SOUTH = { x: 0, y: 1, start: p => p.length - 1, next: v => v - 1 }; 19 | const EAST = { x: 1, y: 0, start: p => p.length - 1, next: v => v - 1 }; 20 | 21 | const tilt = (platform, dir) => { 22 | let moves = 1; 23 | while (moves) { 24 | moves = 0; 25 | for (let y = dir.start(platform); y >= 0 && y < platform.length; y = dir.next(y)) { 26 | for (let x = dir.start(platform); x >= 0 && x < platform[y].length; x = dir.next(x)) { 27 | moves += move(platform, x, y, x + dir.x, y + dir.y); 28 | } 29 | } 30 | }; 31 | }; 32 | 33 | tilt(platform, NORTH); 34 | 35 | const load = (platform) => 36 | platform.map((line, y) => line.map(c => c !== 'O' ? 0 : platform.length - y) // calculate distance from bottom 37 | .reduce((a, b) => a + b, 0) // sum all distances for line 38 | ).reduce((a, b) => a + b, 0); // sum all lines 39 | 40 | console.log(`part1: ${load(platform)}`); 41 | 42 | const platform2 = asLines('../input/day14.txt').map(line => line.split('')); 43 | 44 | const cycle = (platform) => { 45 | const maps = new Map(); // store maps and their index to detect cycles 46 | for(let i = 0; i < 1000_000_000; i++) { 47 | tilt(platform, NORTH); 48 | tilt(platform, WEST); 49 | tilt(platform, SOUTH); 50 | tilt(platform, EAST); 51 | const mapId = platform.map(line => line.join('')).join('\n'); 52 | if (maps.get(mapId)) { 53 | // found cycle, skip to end 54 | const cycleLength = i - maps.get(mapId); 55 | const remaining = 1000_000_000 - i; 56 | i += Math.floor(remaining / cycleLength) * cycleLength; 57 | maps.clear(); 58 | } 59 | maps.set(mapId, i); 60 | } 61 | } 62 | 63 | cycle(platform2); 64 | 65 | console.log(`part2: ${load(platform2)}`); 66 | -------------------------------------------------------------------------------- /2023/js/day15.js: -------------------------------------------------------------------------------- 1 | import { readFileSync } from 'fs'; 2 | 3 | const initializationSequence = readFileSync('../input/day15.txt').toString().split(','); 4 | 5 | const hash = (str) => str.split('').reduce((a, b) => (a + b.charCodeAt(0)) * 17 % 256, 0); 6 | 7 | const sum1 = initializationSequence.map(step => hash(step)).reduce((a, b) => a + b, 0); 8 | 9 | console.log(`part1: ${sum1}`); 10 | 11 | const boxes = Array(256).fill(0).map((a) => []); 12 | 13 | const minus = (box, label) => box = box.filter(l => l.label !== label); 14 | const equal = (box, label, focalLength) => { 15 | if (box.find(l => l.label === label)) { 16 | return box.map(l => l.label === label ? { label, focalLength } : l); 17 | } else { 18 | return [...box, { label, focalLength }]; 19 | } 20 | }; 21 | 22 | const apply = (step, boxes) => { 23 | const [_, label, instruction, focalLength] = step.match(/([a-z]+)([-=])([0-9]*)/); 24 | const box = boxes[hash(label)]; 25 | const newBox = instruction === '-' ? minus(box, label) : equal(box, label, focalLength); 26 | boxes[hash(label)] = newBox; 27 | }; 28 | 29 | initializationSequence.forEach((step) => apply(step, boxes)); 30 | 31 | const lensPower = (box, position, focalLength) => (box + 1) * (position + 1) * focalLength; 32 | const boxPower = (box, i) => box.map((l, j) => lensPower(i, j, l.focalLength)).reduce((a, b) => a + b, 0); 33 | 34 | const sum2 = boxes.map((box, i) => boxPower(box, i)).reduce((a, b) => a + b, 0); 35 | 36 | console.log(`part2: ${sum2}`); -------------------------------------------------------------------------------- /2023/js/day16.js: -------------------------------------------------------------------------------- 1 | import { asLines } from './util.js'; 2 | 3 | const layout = asLines('../input/day16.txt').map(line => line.split('')); 4 | 5 | const traceLight = (layout, light, visited) => { 6 | const {x, y, dx, dy} = light; 7 | if (x < 0 || y < 0 || x >= layout[0].length || y >= layout.length) { 8 | return []; // light has left the building 9 | } 10 | if (!visited.has(`${x},${y}`)) visited.set(`${x},${y}`, new Set()); 11 | if (visited.get(`${x},${y}`).has(`${dx},${dy}`)) return []; // light has looped 12 | visited.get(`${x},${y}`).add(`${dx},${dy}`); 13 | 14 | const current = layout[y][x]; 15 | let next = [{dx, dy}]; 16 | if (current === '/') next = [{dx: -dy, dy: -dx}]; 17 | if (current === '\\') next = [{dx: dy, dy: dx}]; 18 | if (current === '|' && dx !==0) next = [{dx: dy, dy: -1}, {dx: dy, dy: 1}]; 19 | if (current === '-' && dy !==0) next = [{dx: -1, dy: dx}, {dx: 1, dy: dx}]; 20 | return next.map(({dx, dy}) => ({x: x + dx, y: y + dy, dx, dy})); 21 | } 22 | 23 | const traceLights = (layout, lights) => { 24 | const visited = new Map(); 25 | while (lights.length > 0) { 26 | lights = lights.flatMap(light => traceLight(layout, light, visited)); 27 | } 28 | return visited.size; 29 | } 30 | 31 | const energized = traceLights(layout, [{x: 0, y: 0, dx: 1, dy: 0}]); 32 | console.log(`part1: ${energized}`); 33 | 34 | let energized2 = 0; 35 | for (let y = 0; y < layout.length; y++) { 36 | energized2 = Math.max(energized2, traceLights(layout, [{x: 0, y, dx: 1, dy: 0}])); 37 | energized2 = Math.max(energized2, traceLights(layout, [{x: layout[0].length - 1, y, dx: -1, dy: 0}])); 38 | } 39 | for (let x = 0; x < layout[0].length; x++) { 40 | energized2 = Math.max(energized2, traceLights(layout, [{x, y: 0, dx: 0, dy: 1}])); 41 | energized2 = Math.max(energized2, traceLights(layout, [{x, y: layout.length - 1, dx: 0, dy: -1}])); 42 | } 43 | console.log(`part2: ${energized2}`); 44 | -------------------------------------------------------------------------------- /2023/js/day17.js: -------------------------------------------------------------------------------- 1 | import { asLines } from './util.js'; 2 | 3 | const map = asLines('../input/day17.txt').map(line => line.split('').map(c => parseInt(c))); 4 | 5 | const next = { R: [1, 0], L: [-1, 0], U: [0, -1], D: [0, 1], '' : [0, 0] }; 6 | const nextXY = (x, y, dir) => [x + next[dir][0], y + next[dir][1]]; 7 | 8 | const opposite = { R: 'L', L: 'R', U: 'D', D: 'U' }; 9 | const getPossibleDirs = (dir) => ['R', 'D', 'U', 'L'].filter(d => d !== opposite[dir]); 10 | 11 | const shortestPath = (map, minSteps, maxSteps) => { 12 | const visited = new Map(); 13 | const queue = [{x: 0, y: 0, dir: '', dirSteps: minSteps, temp: -map[0][0]}]; 14 | let min = 999999; 15 | while (queue.length > 0) { 16 | const {x: ox, y: oy, dir, dirSteps, temp: prevTemp} = queue.shift(); 17 | const [x, y] = nextXY(ox, oy, dir); 18 | 19 | if (x < 0 || y < 0 || x >= map[0].length || y >= map.length) continue; // out of bounds 20 | if (dirSteps > maxSteps) continue; // too many steps in same direction 21 | const temp = prevTemp + map[y][x]; // add to temp 22 | if (temp >= min) continue; // already too big 23 | const prev = visited.get(`${x},${y},${dir},${dirSteps}`); 24 | if (prev && prev <= temp) continue; // already visited with same or better temp 25 | visited.set(`${x},${y},${dir},${dirSteps}`, temp); 26 | 27 | if (dirSteps >= minSteps && x === map[0].length - 1 && y === map.length - 1) { 28 | min = Math.min(min, temp); // reach goal, update min 29 | } else { 30 | const possibleDirs = dirSteps < minSteps ? [dir] : getPossibleDirs(dir); 31 | const nextSteps = possibleDirs.map(n => ({x, y, dir: n, dirSteps: dir === n ? dirSteps + 1 : 1, temp})); 32 | queue.push(...nextSteps); 33 | queue.sort((a, b) => a.temp - b.temp); 34 | } 35 | } 36 | return min; 37 | } 38 | 39 | const shortestPath1 = shortestPath(map, 0, 3); 40 | console.log(`part1: ${shortestPath1}`); 41 | 42 | const shortestPath2 = shortestPath(map, 4, 10); 43 | console.log(`part2: ${shortestPath2}`); 44 | -------------------------------------------------------------------------------- /2023/js/day18.js: -------------------------------------------------------------------------------- 1 | import { asLines } from './util.js'; 2 | 3 | const plan = asLines('../input/day18.txt').map(line => line.split(' ')); 4 | 5 | const dirs = { R: [1, 0], L: [-1, 0], U: [0, -1], D: [0, 1] }; 6 | 7 | const digCorners = (plan) => { 8 | let pos = [0, 0]; 9 | const holes = [ ]; 10 | plan.forEach(([dir, meters]) => { 11 | const [dx, dy] = dirs[dir]; 12 | pos = [pos[0] + dx * meters, pos[1] + dy * meters]; 13 | holes.push(pos); 14 | }); 15 | return holes; 16 | } 17 | 18 | const shoelaceFormula = (coords) => { 19 | let sum = 0; 20 | let trenchLen = 0; 21 | for (let i = 0; i < coords.length; i++) { 22 | const next = coords[i + 1] ? coords[i + 1] : coords[0]; 23 | const x = coords[i][0]; 24 | const ny = next[1]; 25 | const y = coords[i][1]; 26 | const nx = next[0]; 27 | sum += x * ny - y * nx; 28 | trenchLen += Math.abs(coords[i][0] - next[0]) + Math.abs(coords[i][1] - next[1]); 29 | } 30 | 31 | const shoelace = Math.abs(sum / 2); 32 | // fix error caused by square units 33 | return shoelace + trenchLen / 2 + 1; 34 | } 35 | 36 | const corners = digCorners(plan); 37 | const area1 = shoelaceFormula(corners); 38 | console.log(`part1: ${area1}`); 39 | 40 | const fixPlan = (plan) => plan.map(([,, color]) => { 41 | const dirMap = [ 'R', 'D', 'L', 'U' ]; 42 | const meters = parseInt(color.substring(2, 7), 16); 43 | const dir = dirMap[parseInt(color.substring(7, 8))]; 44 | return [dir, meters, color]; 45 | }); 46 | 47 | const corners2 = digCorners(fixPlan(plan)); 48 | const part2 = shoelaceFormula(corners2); 49 | console.log(`part2: ${part2}`); 50 | -------------------------------------------------------------------------------- /2023/js/day19.js: -------------------------------------------------------------------------------- 1 | import { readFileSync } from 'fs'; 2 | 3 | const [flowsData, partsData] = readFileSync('../input/day19.txt').toString().split('\n\n') 4 | .map(data => data.split('\n')); 5 | 6 | const parseFlow = (line) => { 7 | const [_, name, rulesData] = line.match(/^([a-z]+){(.*)}$/); 8 | const rules = rulesData.split(',').map(rule => rule.split(':')); 9 | return [name, rules]; 10 | } 11 | 12 | const parsePart = (line) => JSON.parse(line.replaceAll(/([a-z]+)=/g, '"$1":')); 13 | 14 | const flows = flowsData.map(parseFlow); 15 | const parts = partsData.map(parsePart); 16 | parts.forEach(part => part.state = 'in'); 17 | 18 | const matchRule = (cond, part) => { 19 | const [_, left, operator, right] = cond.match(/([a-z]+)([<>=]+)([0-9]+)/); 20 | return eval(`${part[left]} ${operator} ${right}`); 21 | } 22 | 23 | const runFlow = ([_, rules], part) => { 24 | return rules.flatMap(([cond, target]) => { 25 | if (target === undefined) return [cond]; 26 | if (matchRule(cond, part)) return [target]; 27 | return []; 28 | }); 29 | }; 30 | 31 | while (parts.find(part => (part.state !== 'A' && part.state !== 'R'))) { 32 | for (const part of parts) { 33 | if (part.state === 'A' || part.state === 'R') continue; 34 | const flow = flows.find(([name]) => name === part.state); 35 | const dest = runFlow(flow, part)[0]; 36 | part.state = dest; 37 | } 38 | } 39 | 40 | const sum1 = parts.filter(part => part.state === 'A') 41 | .map(part => Object.entries(part) 42 | .reduce((a, [rating, value]) => a + (rating != 'state' ? value : 0), 0)) // sum of all ratings 43 | .reduce((a, b) => a + b, 0); // sum of all parts 44 | 45 | console.log(`part1 ${sum1}`); 46 | -------------------------------------------------------------------------------- /2023/js/day2.js: -------------------------------------------------------------------------------- 1 | 2 | import { asLines } from './util.js'; 3 | 4 | const getGames = () => { 5 | return asLines('../input/day2_a.txt').map(line => toGame(line)); 6 | }; 7 | 8 | const toGame = (line) => { 9 | const [gameStr, setsStr] = line.split(':'); 10 | const id = parseInt(gameStr.split(' ')[1]); 11 | const sets = setsStr.split(';').map(setStr => { 12 | const set = {}; 13 | const colors = setStr.split(','); 14 | colors.forEach(color => { 15 | const [count, colorName] = color.trim().split(' '); 16 | set[colorName] = parseInt(count); 17 | }); 18 | return set; 19 | }); 20 | return { id, sets }; 21 | }; 22 | 23 | const isPossibleGame = (game, red, green, blue) => { 24 | return game.sets.filter(set => !isPossibleSet(set, red, green, blue)).length === 0; 25 | }; 26 | 27 | const isPossibleSet = (set, red, green, blue) => { 28 | return (set.red || 0) <= red && (set.green || 0) <= green && (set.blue || 0) <= blue; 29 | } 30 | 31 | const minimumSet = (sets) => { 32 | const red = sets.reduce((min, set) => Math.max(min, set.red || 0), 0); 33 | const green = sets.reduce((min, set) => Math.max(min, set.green || 0), 0); 34 | const blue = sets.reduce((min, set) => Math.max(min, set.blue || 0), 0); 35 | return { red, green, blue }; 36 | }; 37 | 38 | const sum1 = getGames() 39 | .filter(game => isPossibleGame(game, 12, 13, 14)) 40 | .map(game => game.id) 41 | .reduce((a,b) => a + b, 0); 42 | 43 | console.log(`part 1: ${sum1}`); 44 | 45 | const sum2 = getGames() 46 | .map(game => minimumSet(game.sets)) 47 | .map(minSet => minSet.red * minSet.green * minSet.blue) 48 | .reduce((a,b) => a + b, 0); 49 | 50 | console.log(`part 2: ${sum2}`); 51 | -------------------------------------------------------------------------------- /2023/js/day20.js: -------------------------------------------------------------------------------- 1 | import { asLines } from './util.js'; 2 | 3 | const parseModule = (line) => { 4 | const [[module], outputs] = line.split(' -> ').map(str => str.split(', ')); 5 | const [_, type, name] = module.match(/([\%\&]?)([a-z]+)/); 6 | return { type, name, outputs }; 7 | }; 8 | 9 | const modules = asLines('../input/day20.txt').map(parseModule) 10 | .reduce((map, mod) => map.set(mod.name, mod), new Map()); 11 | 12 | const ON = 1, OFF = 0, LOW = 0, HIGH = 1; 13 | 14 | [...modules.values()].filter(mod => mod.type === '&').forEach(mod => { 15 | const inputs = [...modules.values()].filter(modi => modi.outputs.includes(mod.name)).map(modi => modi.name); 16 | mod.inputs = inputs.reduce((map, input) => map.set(input, LOW), new Map()); 17 | }); 18 | 19 | const send = (mod, signal, counter, queue) => { 20 | queue.push(...mod.outputs.map(output => [output, signal, mod.name])); 21 | counter[signal] += mod.outputs.length; 22 | }; 23 | 24 | let count = 0; 25 | let firstHigh = {}; 26 | let secondHigh = {}; 27 | 28 | const ping = (counter) => { 29 | const queue = [ ['broadcaster', LOW, 'button'] ]; 30 | counter[0]++; 31 | while (queue.length > 0) { 32 | const [dest, signal, source] = queue.shift(); 33 | 34 | // part2, identify cycles for zr inputs, that control rx 35 | const zrInputs = modules.get('zr').inputs; // zr controls rx 36 | if ([...zrInputs.entries()].find(([key, value]) => dest === key && value === HIGH)) { 37 | if (!firstHigh[source]) { 38 | firstHigh[source] = count; 39 | } else if (count > firstHigh[source] && !secondHigh[source]) { 40 | secondHigh[source] = count; 41 | } 42 | } 43 | 44 | const mod = modules.get(dest); 45 | if (mod === undefined) { 46 | continue; 47 | } 48 | if (mod.name === 'broadcaster') { 49 | send(mod, signal, counter, queue); 50 | } else if (mod.type === '%' && signal === LOW) { 51 | mod.state = mod.state ? OFF : ON; 52 | send(mod, mod.state, counter, queue); 53 | } else if (mod.type === '&') { 54 | mod.inputs.set(source, signal); 55 | const allHigh = [...mod.inputs.values()].find(input => input === LOW) === undefined; 56 | const pulse = allHigh ? LOW : HIGH; 57 | send(mod, pulse, counter, queue); 58 | } 59 | } 60 | }; 61 | 62 | let counter = [0, 0]; 63 | for (let i = 0; i < 1000; i++) { 64 | ping(counter); 65 | } 66 | 67 | console.log(`part1: ${counter[0]*counter[1]}`); 68 | 69 | while(true) { 70 | count++; 71 | ping(counter); 72 | if (Object.keys(secondHigh).length === 4) { 73 | break; // we have found cycles for all zr inputs that control rx 74 | } 75 | } 76 | 77 | // calculate the cycle lengths for the zr inputs 78 | const cycles = Object.entries(secondHigh).map(([key, value]) => value - firstHigh[key]); 79 | 80 | // calculate the count when cycles match and will trigger rx 81 | const greatestCommonDivisor = (a, b) => b ? greatestCommonDivisor(b, a % b) : a; 82 | const leastCommonMultiple = (a, b) => a * b / greatestCommonDivisor(a, b); 83 | const lcm = cycles.reduce((a, b) => leastCommonMultiple(a, b)); 84 | 85 | console.log(`part2: ${lcm}`); -------------------------------------------------------------------------------- /2023/js/day3.js: -------------------------------------------------------------------------------- 1 | 2 | import { asLines } from './util.js'; 3 | 4 | const schematic = asLines('../input/day3.txt'); 5 | 6 | const sum1 = () => schematic.flatMap((row, y) => { 7 | const foundNumbers = Array.from(row.matchAll(/[\d]+/g)); // find numbers on this row 8 | return foundNumbers.flatMap((match) => { 9 | const { 0: number, index: x } = match; // number and x position of the number 10 | return adjacentToSymbol(schematic, x, x + number.length - 1, y) 11 | ? [parseInt(number)] // if adjacent to symbol, return the number 12 | : []; // else return empty and flatMap will remove it 13 | }); 14 | }).reduce((a,b) => a + b, 0); 15 | 16 | // check if there is a symbol adjacent to then given x1-x2 range on given row 17 | const adjacentToSymbol = (schematic, x1, x2, row) => { 18 | for (let y = row - 1; y <= row + 1; y++) { 19 | for (let x = x1 - 1; x <= x2 + 1; x++) { 20 | if (!schematic[y] || !schematic[y][x]) continue; 21 | const c = schematic[y][x]; 22 | if (c != '.' && !(c >= '0' && c <= '9')) return true; 23 | } 24 | } 25 | return false; 26 | } 27 | 28 | console.log(`part 1: ${sum1()}`); 29 | 30 | const sum2 = () => schematic.flatMap((row, y) => { 31 | return row.split('').flatMap((c, x) => c === '*' ? [x] : []) // find all start x positions 32 | .map((x) => findAdjacentNumbers(schematic, x, y)) // find all adjacent numbers for stars (x,y) 33 | .filter(n => n.length === 2) // filter stars with 2 adjacent numbers 34 | .map(n => n.reduce((a,b) => a*b, 1)) // multiply with each other 35 | }).reduce((a,b) => a + b, 0); // sum all 36 | 37 | const findAdjacentNumbers = (schematic, x, y) => { 38 | const numbers = []; 39 | for (let dy = y-1; dy <= y+1; dy++) { 40 | const row = schematic[dy]; 41 | const foundNumbers = Array.from(row.matchAll(/[\d]+/g)); 42 | const adjacent = foundNumbers.flatMap((match) => { 43 | const { 0: number, index: x1 } = match; // number and x position of the number 44 | const x2 = x1 + number.length - 1; // end position of the number 45 | return x2 >= x - 1 && x1 <= x + 1 ? [parseInt(number)] : []; 46 | }); 47 | numbers.push(...adjacent); 48 | } 49 | return numbers; 50 | } 51 | 52 | console.log(`part 2: ${sum2()}`); 53 | -------------------------------------------------------------------------------- /2023/js/day4.js: -------------------------------------------------------------------------------- 1 | 2 | import { asLines } from './util.js'; 3 | 4 | const input = asLines('../input/day4.txt'); 5 | 6 | const parseCard = (line) => { 7 | const [_, cardContent] = line.split(': '); 8 | const [winStr, haveStr] = cardContent.split(' | '); 9 | const [win, have] = [winStr.split(/[ ]+/), haveStr.split(/[ ]+/)]; 10 | return {win, have}; 11 | }; 12 | 13 | const sum1 = () => input.map((line) => { 14 | const {win, have} = parseCard(line); 15 | const points = have.filter((n) => win.includes(n)) // filter numbers that are in both 16 | .reduce((a,b) => a * 2, 0.5); // multiply with 2 each number 17 | return points >= 1 ? points : 0; 18 | }).reduce((a,b) => a + b, 0); 19 | 20 | console.log(`part 1: ${sum1()}`); 21 | 22 | const sum2 = () => { 23 | const counts = Array(input.length).fill(1); // initially have 1 of each card 24 | input.forEach((line, index) => { 25 | const {win, have} = parseCard(line); 26 | const matches = have.filter((n) => win.includes(n)).length; 27 | for (let i = 1; i <= matches; i++) { // for each match, add the count to the next i cards 28 | counts[index+i] += counts[index]; // add times we already have this card 29 | } 30 | }); 31 | return counts.reduce((a,b) => a + b, 0); // sum all counts 32 | } 33 | 34 | console.log(`part 2: ${sum2()}`); 35 | -------------------------------------------------------------------------------- /2023/js/day5.js: -------------------------------------------------------------------------------- 1 | 2 | import { readFileSync } from 'fs'; 3 | 4 | const input = readFileSync('../input/day5.txt').toString().split('\n\n'); 5 | const seeds = input.shift().split(':')[1].trim().split(' ').map((n) => parseInt(n)); 6 | 7 | // format [ { from: 'seed', to: 'location', ranges: [ { rstart: 0, rend: 0, diff: 0 } ] } ] 8 | const buildMap = (input) => { 9 | return input.map((map) => { 10 | const [mapping, rangesStr] = map.split(' map:\n'); 11 | const [from, to] = mapping.split('-to-'); 12 | const ranges = rangesStr.split('\n').map((range) => { 13 | const [to, from, len] = range.trim().split(' ').map((n) => parseInt(n)); 14 | return { rstart: from, rend: from + len - 1, diff: to - from }; 15 | }).sort((a, b) => a.rstart - b.rstart); 16 | return { from, to, ranges }; 17 | }); 18 | }; 19 | 20 | const maps = buildMap(input); 21 | 22 | const mapRangeToLocation = (from, [start, end]) => { 23 | if (from === 'location') { 24 | return [start, end]; 25 | } 26 | const targetMap = maps.filter((m) => m.from === from)[0]; 27 | const targetRanges = targetMap.ranges.filter((r) => r.rstart <= end && r.rend >= start); 28 | const nextRanges = []; 29 | for (let i = start; i <= end;) { // loop through the range 30 | if (targetRanges.length === 0) { 31 | nextRanges.push([i, end]); // no more target ranges, push the rest of the range 32 | break; 33 | } 34 | const match = targetRanges.find((r) => r.rstart <= i && r.rend >= i); 35 | if (match) { // there is a match in target ranges 36 | const { rend, diff } = targetRanges.shift(); 37 | const matchEnd = Math.min(end, rend); 38 | const nextRange = [i + diff, matchEnd + diff]; 39 | nextRanges.push(nextRange); 40 | i = matchEnd + 1; 41 | } else { 42 | const { rstart } = targetRanges[0]; 43 | const matchEnd = Math.min(end, rstart); 44 | const nextRange = [i, matchEnd]; 45 | nextRanges.push(nextRange); 46 | i = matchEnd + 1; 47 | } 48 | } 49 | return nextRanges.flatMap(([start, end]) => { 50 | return mapRangeToLocation(targetMap.to, [start, end]); 51 | }); 52 | } 53 | 54 | const lowestLocation = () => { 55 | return seeds.map((seed) => { 56 | const locationsForSeed = mapRangeToLocation('seed', [seed, seed]); 57 | return locationsForSeed.reduce((a, b) => Math.min(a, b), Number.MAX_SAFE_INTEGER); 58 | }).reduce((a, b) => Math.min(a, b), Number.MAX_SAFE_INTEGER); 59 | } 60 | 61 | console.log(`part 1: ${lowestLocation()}`); 62 | 63 | const lowestLocation2 = () => { 64 | const res = []; 65 | for (let i = 0; i < seeds.length; i += 2) { 66 | const seedRange = [seeds[i], seeds[i] + seeds[i+1] - 1]; 67 | const locations = mapRangeToLocation('seed', seedRange); 68 | const minLocation = locations.reduce((a, b) => Math.min(a, b), Number.MAX_SAFE_INTEGER); 69 | res.push(minLocation); 70 | } 71 | return res.reduce((a, b) => Math.min(a, b), Number.MAX_SAFE_INTEGER); 72 | } 73 | 74 | console.log(`part 2: ${lowestLocation2()}`); 75 | -------------------------------------------------------------------------------- /2023/js/day6.js: -------------------------------------------------------------------------------- 1 | import { asLines } from './util.js'; 2 | 3 | const input = asLines('../input/day6.txt'); 4 | 5 | const toDocument = (input) => input.map((line) => { 6 | const [name, numbersStr] = line.split(/:[ ]*/); 7 | const numbers = numbersStr.split(/[ ]+/).map((n) => parseInt(n)); 8 | return {name, numbers}; 9 | }).reduce((a, b) => { 10 | a[b.name] = b.numbers; 11 | return a; 12 | }, {}); 13 | 14 | const waysToWin = (input) => { 15 | const doc = toDocument(input); 16 | const races = doc.Time.map((time, index) => [time, doc.Distance[index]]); 17 | const ways = races.map(([recordTime, recordDistance]) => { 18 | // speed * (recordTime - holdTime) = distance, speed = holdTime 19 | // holdTime * (recordTime - holdTime) = distance 20 | // holdTime * (recordTime - holdTime) > recordDistance 21 | // holdTime * recordTime - holdTime^2 > recordDistance 22 | // holdTime^2 - recordTime * holdTime + recordDistance < 0 23 | // solve quadratic equation with quadratic formula 24 | const p1 = Math.ceil((recordTime + Math.sqrt(recordTime**2-4*recordDistance))/2)-1; 25 | const p2 = Math.floor((recordTime - Math.sqrt(recordTime**2-4*recordDistance))/2)+1; 26 | return p1 - p2 + 1; 27 | }); 28 | return ways.reduce((a, b) => a * b, 1); 29 | } 30 | 31 | console.log(`part 1: ${waysToWin(input)}`); 32 | 33 | const input2 = input.map(line => line.replaceAll(' ', '')); 34 | 35 | console.log(`part 2: ${waysToWin(input2)}`); 36 | -------------------------------------------------------------------------------- /2023/js/day7.js: -------------------------------------------------------------------------------- 1 | import { asLines } from './util.js'; 2 | 3 | const input = asLines('../input/day7.txt'); 4 | const hands = input.map(line => line.split(' ')) 5 | 6 | const countCards = (hand) => { // build up a map of card kinds to counts 7 | return Array.from(hand).reduce((acc, cur) => { 8 | acc[cur] ? acc[cur]++ : acc[cur] = 1; 9 | return acc; 10 | }, {}); 11 | } 12 | 13 | const cardScore = 'AKQJT98765432'.split(''); 14 | const jokerScore = 'AKQT98765432J'.split(''); 15 | const cardScorer = (cardIndex) => (card) => cardIndex.length - cardIndex.indexOf(card); 16 | 17 | const handScorer = (useJokers) => (hand) => { 18 | const cards = countCards(hand); 19 | let jokers = 0; 20 | if (useJokers) { 21 | jokers = cards['J'] ?? 0; 22 | delete cards['J']; 23 | } 24 | const kindCounts = Object.keys(cards).length; 25 | const maxKind = Math.max(...Object.values(cards)) + jokers; 26 | if (kindCounts <= 1) { 27 | return 6; // five of a kind 28 | } else if (kindCounts === 2 && maxKind === 4) { 29 | return 5; // four of a kind 30 | } else if (kindCounts === 2) { 31 | return 4; // full house 32 | } else if (maxKind === 3) { 33 | return 3; // three of a kind 34 | } else if (kindCounts === 3 && maxKind === 2) { 35 | return 2; // two pair 36 | } else if (maxKind === 2) { 37 | return 1; // one pair 38 | } 39 | return 0; // high card 40 | } 41 | 42 | const handSorter = (handScorer, cardScorer) => (a, b) => { 43 | if (handScorer(a) !== handScorer(b)) { 44 | return handScorer(a) - handScorer(b); 45 | } 46 | // same hand, sort by card 47 | for (let i = 0; i < a.length; i++) { 48 | if (cardScorer(a[i]) !== cardScorer(b[i])) { 49 | return cardScorer(a[i]) - cardScorer(b[i]); 50 | } 51 | } 52 | return 0; 53 | } 54 | 55 | const winnings = (handSorter) => { 56 | const sorted = hands.sort((a, b) => handSorter(a[0], b[0])); 57 | return sorted.map((hand, index) => hand[1] * (index+1)) 58 | .reduce((a, b) => a + b, 0); 59 | } 60 | 61 | const part1Sorter = handSorter(handScorer(false), cardScorer(cardScore)); 62 | const winnings1 = winnings(part1Sorter); 63 | 64 | console.log(`part 1: ${winnings1}`); 65 | 66 | const part2Sorter = handSorter(handScorer(true), cardScorer(jokerScore)); 67 | const winnings2 = winnings(part2Sorter); 68 | 69 | console.log(`part 2: ${winnings2}`); 70 | -------------------------------------------------------------------------------- /2023/js/day8.js: -------------------------------------------------------------------------------- 1 | import { readFileSync } from 'fs'; 2 | 3 | const [instructions, network] = readFileSync('../input/day8.txt').toString().split('\n\n'); 4 | const nodes = network.split('\n').map(line => line.replaceAll(/[\(\)]/g,'').split(' = ')); 5 | const nodeMap = nodes.reduce((acc, cur) => { 6 | acc[cur[0]] = cur[1].split(', '); 7 | return acc; 8 | }, {}); 9 | 10 | 11 | const solveSteps = (endCondition) => (position) => { 12 | let step; 13 | for (step = 0; !endCondition(position); step++) { 14 | const instruction = instructions.charAt(step % instructions.length); 15 | const options = nodeMap[position]; 16 | position = options[instruction === 'R' ? 1 : 0]; 17 | } 18 | return step; 19 | } 20 | 21 | const part1Solver = solveSteps(position => position === 'ZZZ'); 22 | const part1 = part1Solver('AAA'); 23 | 24 | console.log(`part1: ${part1}`); 25 | 26 | const greatestCommonDivisor = (a, b) => { 27 | return b ? greatestCommonDivisor(b, a % b) : a; 28 | } 29 | const leastCommonMultiple = (a, b) => { 30 | return a * b / greatestCommonDivisor(a, b); 31 | } 32 | 33 | let startPositions = Object.keys(nodeMap).filter(key => key.endsWith('A')); 34 | const part2Solver = solveSteps(position => position.endsWith('Z')); 35 | const stepsForEacStart = startPositions.map(position => part2Solver(position)); 36 | const lcm = stepsForEacStart.reduce((a, b) => leastCommonMultiple(a, b)); 37 | 38 | console.log(`part2: ${lcm}`); 39 | -------------------------------------------------------------------------------- /2023/js/day9.js: -------------------------------------------------------------------------------- 1 | import { asLines } from './util.js'; 2 | 3 | const history = asLines('../input/day9.txt') 4 | .map(line => line.split(' ') 5 | .map(n => parseInt(n))); 6 | 7 | const difference = (history) => { 8 | const res = []; 9 | for (let i = 0; i < history.length - 1; i++) { 10 | const d = history[i+1] - history[i]; 11 | res.push(d); 12 | } 13 | return res; 14 | }; 15 | 16 | const differences = (history) => { 17 | const res = [ history ]; 18 | while(history.find(n => n != 0)) { 19 | const d = difference(history); 20 | res.push(difference(history)); 21 | history = d; 22 | } 23 | return res; 24 | }; 25 | 26 | const part1Extrapolator = (prevValue, prevLine) => prevValue + prevLine[prevLine.length - 1]; 27 | const part2Extrapolator = (prevValue, prevLine) => prevLine[0] - prevValue; 28 | 29 | const extrapolate = (extrapolator) => (history) => { 30 | const d = differences(history); 31 | // from last differences to first, extrapolate backwards 32 | return d.toReversed().slice(1).reduce((acc, cur) => extrapolator(acc, cur), 0); 33 | }; 34 | 35 | const sum = history.map(h => extrapolate(part1Extrapolator)(h)) 36 | .reduce((a, b) => a + b, 0); 37 | 38 | console.log(`part1: ${sum}`); 39 | 40 | const sum2 = history.map(h => extrapolate(part2Extrapolator)(h)) 41 | .reduce((a, b) => a + b, 0); 42 | 43 | console.log(`part2: ${sum2}`); 44 | -------------------------------------------------------------------------------- /2023/js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "name": "aoc2023", 4 | "version": "1.0.0", 5 | "scripts": { 6 | "day1": "node day1.js", 7 | "day2": "node day2.js", 8 | "day3": "node day3.js", 9 | "day4": "node day4.js", 10 | "day5": "node day5.js", 11 | "day6": "node day6.js", 12 | "day7": "node day7.js", 13 | "day8": "node day8.js", 14 | "day9": "node day9.js", 15 | "day10": "node day10.js", 16 | "day11": "node day11.js", 17 | "day12": "node day12.js", 18 | "day13": "node day13.js", 19 | "day14": "node day14.js", 20 | "day15": "node day15.js", 21 | "day16": "node day16.js", 22 | "day17": "node day17.js", 23 | "day18": "node day18.js", 24 | "day19": "node day19.js", 25 | "day20": "node day20.js" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /2023/js/util.js: -------------------------------------------------------------------------------- 1 | import { readFileSync } from 'fs'; 2 | 3 | export const asLines = (fname) => { 4 | return readFileSync(fname).toString() 5 | .split('\n') 6 | .filter((l) => l.length > 0); 7 | } 8 | -------------------------------------------------------------------------------- /2024/input/10.txt: -------------------------------------------------------------------------------- 1 | 981050112210121034565432321032103321234567567 2 | 872341201014434921878891434549012310432198678 3 | 765434318123567890989760023678543986501021549 4 | 545985329854356792105654116707621677893430932 5 | 456576416763447883498743209811030543082567801 6 | 327676509823430976541050124322345892121078998 7 | 218489678016521050132011267401276734289010867 8 | 109345908987210763249121278569889825678123459 9 | 543237817876345854358230989078778910107645678 10 | 654106726541096981267345678123498723216530501 11 | 761235430432387870301076521014567234345321432 12 | 890144321001456765432189410019010121237876501 13 | 781055697878921212345674321928723210345989432 14 | 218966788965430101254789345839654234326710101 15 | 109875692100123270163231236743010165019823321 16 | 018754103018786789872100109852123278956754430 17 | 325673214109695698987212121961094109849869543 18 | 234981005234534321210103030878785603732778672 19 | 189012346943413210321234549879676712011001981 20 | 076321057892100110450321676566543893425652760 21 | 325401069787012026565410789457012798534743854 22 | 410512178976543237656998898308932687649887923 23 | 567893467689854148997867891219801456308996310 24 | 234554992501763056788950750304762343210105409 25 | 147667881432612234563441065403451012308712358 26 | 018589670109803189612532674312332305419654347 27 | 189478521210789078703675689100123498569323256 28 | 789767432345650876589986789210096567878012100 29 | 679854343217821987676521654323487458901223321 30 | 565943034506934910505430598714510329874341234 31 | 450012129645445823412014567609621210965210585 32 | 321101518789336798703123455418760145670195696 33 | 234965400689221209654328954303210234987784787 34 | 105874321678100118765017763214301001056653610 35 | 876701234549010329870126894505212892347542123 36 | 965891098238321234561235214676703083498230036 37 | 874782347107630145490344305987894176580121545 38 | 923690456016549854987432234589765237891234694 39 | 012541219897896768876501105676894396790346787 40 | 001432108766541089004321212988787781687656698 41 | 123498789655432376113410145679695670521044567 42 | 876567610141045645223567034034544321430123698 43 | 965458901232038764344038123129632487654310432 44 | 012307676540129854965129876038701898943216581 45 | 103212789432100123876434565345612781012107890 46 | -------------------------------------------------------------------------------- /2024/input/1_test.txt: -------------------------------------------------------------------------------- 1 | 3 4 2 | 4 3 3 | 2 5 4 | 1 3 5 | 3 9 6 | 3 3 7 | -------------------------------------------------------------------------------- /2024/input/2_test.txt: -------------------------------------------------------------------------------- 1 | 7 6 4 2 1 2 | 1 2 7 8 9 3 | 9 7 6 2 1 4 | 1 3 2 4 5 5 | 8 6 4 4 1 6 | 1 3 6 7 9 7 | -------------------------------------------------------------------------------- /2024/input/8.txt: -------------------------------------------------------------------------------- 1 | .....y..........................p................r 2 | ........I......................................... 3 | ......................4.s......................... 4 | ..........4....................................... 5 | ....y............................................. 6 | ......................................p.........r. 7 | ..........0..s......N..................1.....p.... 8 | ..y........4.......................p.............. 9 | ...............0.................................. 10 | ..............0....t....N....h.................... 11 | .............N.................................... 12 | ......j...................s............H...l..O... 13 | ..........q.................H................O.... 14 | ..f...e.qj.....y...0.............................. 15 | ...........t..........................k..Q..r..... 16 | .........6................Q..s...x......W......... 17 | ....2..b...e....t..4.........c.....xW.j........... 18 | ...e....................w................1.....O.. 19 | ..e..j..5...........................c............. 20 | .........B..2...............MK................H... 21 | ...2......b...g..X...q..........h...............O. 22 | ...q...2..........m....k...i...............QV.x... 23 | ...................i.........W.k.............HQ... 24 | ........b...X...............D..........c...N...... 25 | ................................l..........h.....I 26 | .m...........g......l.......c.............3......V 27 | ....X.......m........g...V.K...7......F.d......... 28 | .........b.X...U..........................C....... 29 | .....................l..............o.1....C...... 30 | ............u.............K..............3...d.... 31 | ......................i.T....f................V... 32 | ..............................1.k................. 33 | .B.....E......9..m....K..5.M...................... 34 | ...P...............M...95....o..i........I........ 35 | ...............................S......3......wI... 36 | .....EP...........9........5..T.R................. 37 | .P..........v..9......f.............R.Co..w3...... 38 | ..........h...SG..v.E...7..f.T.................... 39 | ..........6..........L.................Y.......d.. 40 | ..........B...............U........D.............. 41 | ....B................U.....8..M....n...J.......... 42 | .........................L................Fw...... 43 | ....L6E.P.................7.UG....J.....Y.D....... 44 | ........t........v...SJ........n..d............... 45 | ......................8v.....uG................... 46 | ..................L.....n......................... 47 | ...............T..............n......D............ 48 | ..............o.........8................J.Y.R.... 49 | ..................S...............u....F.......R.. 50 | ........6..............u.....7.8..........Y..F.... 51 | -------------------------------------------------------------------------------- /2024/js/day1.js: -------------------------------------------------------------------------------- 1 | 2 | import { asLines } from './util.js'; 3 | 4 | const day1a = asLines('../input/1.txt').map((l) => l.split(/\s+/)); 5 | const leftList = day1a.map((l) => Number(l[0])).sort(); 6 | const rightList = day1a.map((l) => Number(l[1])).sort(); 7 | 8 | const diffs = leftList.map((v, i) => Math.abs(v-rightList[i])); 9 | const sum = diffs.reduce((a,b) => a+b); 10 | 11 | console.log(`day1 part a: ${sum}`); 12 | 13 | const similarityScore = leftList.map((v) => rightList.filter((v2) => v2 == v).length * v); 14 | const sum2 = similarityScore.reduce((a,b) => a+b); 15 | 16 | console.log(`day1 part a: ${sum2}`); 17 | -------------------------------------------------------------------------------- /2024/js/day10.js: -------------------------------------------------------------------------------- 1 | import { asLines } from './util.js'; 2 | 3 | const data = asLines('../input/10.txt').map(l => l.split("").map(h => Number(h))); 4 | 5 | const trailHeads = data.flatMap((r, y) => r.map((h, x) => [h, x, y]).filter(([h]) => h == 0)); 6 | 7 | const moves = [[0, 1], [0, -1], [1, 0], [-1, 0]]; 8 | 9 | const score = ([_, x, y], type = 1) => { 10 | const visited = new Set(); 11 | let score = 0; 12 | const queue = [[x, y]]; 13 | while(queue.length > 0) { 14 | const [x, y] = queue.shift(); 15 | if (visited.has(`${x}.${y}`) && type == 1) continue; 16 | visited.add(`${x}.${y}`); 17 | const height = data[y][x]; 18 | if (height >= 9) { 19 | score++; 20 | } else { 21 | const nextPositions = moves.map(([dx, dy]) => [x + dx, y + dy]) 22 | .filter(([x, y]) => x >= 0 && y >= 0 && x < data[0].length && y < data.length) // inside map 23 | .filter(([x, y]) => data[y][x] == height + 1) // one higher 24 | queue.push(...nextPositions); 25 | } 26 | } 27 | return score; 28 | }; 29 | 30 | const scores = trailHeads.map(tr => score(tr)); 31 | const sum = scores.reduce((a,b) => a + b); 32 | 33 | console.log(`part1: ${sum}`); 34 | 35 | const scores2 = trailHeads.map(tr => score(tr, 2)); 36 | const sum2 = scores2.reduce((a,b) => a + b); 37 | 38 | console.log(`part2: ${sum2}`); 39 | -------------------------------------------------------------------------------- /2024/js/day11.js: -------------------------------------------------------------------------------- 1 | const stones = '572556 22 0 528 4679021 1 10725 2790'.split(' ').map(s => Number(s)); 2 | 3 | const blinkStone = (stone) => { 4 | if (stone == 0) return [1]; 5 | const str = `${stone}`; 6 | if (str.length % 2 == 0) { 7 | const middle = str.length / 2; 8 | const [left, right] = [str.substring(0, middle), str.substring(middle)]; 9 | return [Number(left), Number(right)]; 10 | } 11 | return [2024 * stone]; 12 | }; 13 | 14 | const cache = {}; 15 | const numberAfterBlinks = (stone, blinks) => { 16 | const key = `${stone}.${blinks}`; 17 | if (cache[key]) return cache[key]; 18 | if (blinks == 0) return 1; 19 | const stones = blinkStone(stone); 20 | const ret = stones.map(s => numberAfterBlinks(s, blinks-1)).reduce((a, b) => a + b); 21 | cache[key] = ret; 22 | return ret; 23 | } 24 | 25 | const stonesAfterBlinks = (stones, blinks) => { 26 | return stones.map(s => numberAfterBlinks(s, blinks)).reduce((a, b) => a + b); 27 | } 28 | 29 | console.log(`part1: ${stonesAfterBlinks(stones, 25)}`); 30 | console.log(`part2: ${stonesAfterBlinks(stones, 75)}`); 31 | -------------------------------------------------------------------------------- /2024/js/day12.js: -------------------------------------------------------------------------------- 1 | import { asLines } from './util.js'; 2 | const garden = asLines('../input/12.txt').map(l => l.split("")); 3 | 4 | const inSameRegion = ([x,y], region) => region[`${x}.${y}`] != undefined; 5 | const inAnyRegion = ([x, y], regions) => regions.some(r => inSameRegion([x, y], r)); 6 | 7 | const buildRegion = ([x, y], plant, region = {}) => { 8 | region[`${x}.${y}`] = 4; // initialize perimeter to 4 9 | const moves = [[0, 1], [0, -1], [1, 0], [-1, 0]]; 10 | for (const [dx, dy] of moves) { 11 | const [nx, ny] = [x + dx, y + dy]; 12 | if (nx < 0 || nx >= garden[0].length || ny < 0 || ny >= garden.length) continue; // outside 13 | if (garden[ny][nx] != plant) continue; // different plant 14 | region[`${x}.${y}`]--; // same plant, no perimeter needed 15 | if (inSameRegion([nx, ny], region)) continue; // already calculated 16 | buildRegion([nx, ny], plant, region); 17 | } 18 | return region; 19 | }; 20 | 21 | const compute = (garden) => { 22 | const regions = []; 23 | garden.forEach((_, y) => garden[y].forEach((_, x) => { 24 | if (inAnyRegion([x,y], regions)) return; // alredy in region 25 | const plant = garden[y][x]; 26 | const region = buildRegion([x, y], plant); 27 | regions.push(region); 28 | })); 29 | return regions; 30 | }; 31 | 32 | const regions = compute(garden); 33 | const area = (region) => Object.entries(region).length; 34 | const perimeter = (region) => Object.values(region).reduce((a, b) => a + b); 35 | const price = regions.map(r => area(r) * perimeter(r)).reduce((a, b) => a + b); 36 | console.log(price); 37 | -------------------------------------------------------------------------------- /2024/js/day2.js: -------------------------------------------------------------------------------- 1 | 2 | import { asLines } from './util.js'; 3 | 4 | const day2a = asLines('../input/2.txt').map((l) => l.split(/\s+/).map((v) => Number(v))); 5 | 6 | const badCount = (rest, expectedSign) => { 7 | if (rest.length < 2) return 0; 8 | const diff = rest[0] - rest[1]; 9 | const bad = diff > 3 || diff < -3 || diff === 0 || Math.sign(diff) !== expectedSign ? 1 : 0; 10 | return bad + badCount(rest.slice(1), expectedSign); 11 | } 12 | const safe = (line) => badCount(line, Math.sign(line[0] - line[1])) === 0; 13 | const s = day2a.filter((l) => safe(l)); 14 | console.log(`day1 part a: ${s.length}`); 15 | 16 | const safe2 = (line) => { 17 | for (let i = 0; i < line.length; i++) { 18 | const oneRemoved = line.toSpliced(i, 1); 19 | if (safe(oneRemoved)) return true; 20 | } 21 | return false; 22 | }; 23 | const s2 = day2a.filter((l) => safe2(l)); 24 | console.log(`day2 part a: ${s2.length}`); 25 | -------------------------------------------------------------------------------- /2024/js/day3.js: -------------------------------------------------------------------------------- 1 | 2 | import { asLines } from './util.js'; 3 | 4 | const day3 = asLines('../input/3.txt').join(); 5 | 6 | const matches = day3.matchAll(/mul\((\d{1,3})\,(\d{1,3})\)/g); 7 | const muls = Array.from(matches, (m) => m[1] * m[2]); 8 | const res = muls.reduce((a, b) => a + b) 9 | console.log(`day3a ${res}`); 10 | 11 | const matches2 = day3.matchAll(/(?:mul\((\d{1,3})\,(\d{1,3})\))|(?:do\(\))|(?:don\'t\(\))/g); 12 | let act = true; 13 | let sum = 0; 14 | for (const match of matches2) { 15 | if (match[0] == 'do()') act = true; 16 | else if (match[0] == 'don\'t()') act = false; 17 | else if (act) sum += match[1] * match[2]; 18 | } 19 | console.log(`day3b: ${sum}`); 20 | -------------------------------------------------------------------------------- /2024/js/day4.js: -------------------------------------------------------------------------------- 1 | 2 | import { asLines } from './util.js'; 3 | 4 | const day4 = asLines('../input/4.txt').map((l) => l.split("")); 5 | 6 | const moves = [[-1, 0], [1, 0], [0, -1], [0, 1], [-1, -1], [-1, 1], [1, -1], [1, 1]]; 7 | 8 | const count = (word, row, col, data, dir) => { 9 | if (row < 0 || row >= data.length || col < 0 || col >= data[row].length) return 0; // outside 10 | if (data[row][col] !== word.at(0)) return 0; // no mach 11 | if (word.length === 1) return 1; // final matching character 12 | const dirs = dir ? [dir] : moves; // continue with direction or test all directtions 13 | return dirs.map(([x, y]) => count(word.slice(1), row + x, col + y, data, [x, y])).reduce((a, b) => a + b); 14 | } 15 | 16 | const countHits = (fn) => { 17 | return day4.map((_, row) => day4[row].map((_, col) => fn(row, col)) // for each row and col find hits 18 | .reduce( (a, b) => a + b)).reduce((a, b) => a + b); // sum all of them 19 | } 20 | 21 | const sum = countHits((row, col) => count('XMAS', row, col, day4)); 22 | console.log(`day4a: ${sum}`); 23 | 24 | const moves2 = [[-1, -1], [-1, 1], [1, -1], [1, 1]]; 25 | 26 | const startCount = (row, col, data) => { 27 | if (row < 0 || row >= data.length || col < 0 || col >= data[row].length) return 0; // outside 28 | if (data[row][col] !== 'A') return 0; // not a starting point 29 | // count MAS diagonally from starting point 'A' 30 | const found = moves2.map((m) => count("MAS", row + m[0], col + m[1], data, [-m[0], -m[1]])) 31 | .reduce((a, b) => a + b); 32 | return found > 1 ? 1 : 0; // convert 2 "MAX" found as 1 hit 33 | } 34 | 35 | const sum2 = countHits((row, col) => startCount(row, col, day4)); 36 | console.log(`day4b: ${sum2}`); 37 | -------------------------------------------------------------------------------- /2024/js/day5.js: -------------------------------------------------------------------------------- 1 | 2 | import { readFileSync } from 'fs'; 3 | 4 | const day4 = readFileSync('../input/5.txt').toString(); 5 | const rules = day4.split('\n\n')[0].split('\n').map(l => l.split('|').map(s => Number(s))); 6 | const updates = day4.split('\n\n')[1].split('\n').map(l => l.split(',').map(s => Number(s))); 7 | 8 | const validPage = (page, to, pages) => { 9 | const test = pages.slice(0, to); // test pages before position 10 | return !test.some(testPage => rules.some(r => r[0] == page && r[1] == testPage)); 11 | }; 12 | 13 | const valid = (pages) => pages.some((p, i) => !validPage(p, i, pages)) == false; 14 | const validUpdates = updates.filter(valid); 15 | const middles = validUpdates.map(u => u[Math.floor(u.length/2)]); 16 | const sum = middles.reduce((a, b) => a + b); 17 | 18 | console.log(`sum of middle pages: ${sum}`); 19 | 20 | const reorder = (pages) => { 21 | for (let pos = 0; pos < pages.length; pos++) { 22 | const test = pages.slice(0, pos); 23 | for (let testPos = 0; testPos < test.length; testPos++) { 24 | const failed = rules.some(r => r[0] == pages[pos] && r[1] == pages[testPos]); 25 | if (failed) { 26 | // testPage was before page but rule says otherwise, move page before testPage 27 | pages = pages.toSpliced(pos, 1).toSpliced(testPos, 0, pages[pos]) 28 | return reorder(pages); 29 | } 30 | } 31 | } 32 | return pages; 33 | } 34 | 35 | const invalid = updates.filter(u => !valid(u)); 36 | const ordered = invalid.map(u => reorder(u)); 37 | const middles2 = ordered.map(u => u[Math.floor(u.length/2)]); 38 | const sum2 = middles2.reduce((a, b) => a + b); 39 | console.log(`sum of middle pages: ${sum2}`); -------------------------------------------------------------------------------- /2024/js/day6.js: -------------------------------------------------------------------------------- 1 | import { asLines } from './util.js'; 2 | 3 | const map = asLines('../input/6.txt').map(l => l.split("")); 4 | 5 | const dirs = { '^': [0, -1, '>'], 'v': [0, 1, '<'], '<': [-1, 0, '^'], '>': [1, 0, 'v']}; 6 | const findPosAndDir = (map) => map.flatMap((r, y) => r.flatMap((c, x) => dirs[c] ? [x, y, c] : null).filter(p => p !== null)); 7 | const outOfBounds = (map, x, y) => x < 0 || x > map[0].length - 1 || y < 0 || y > map.length - 1; 8 | 9 | const patrol = (map, obs) => { 10 | let [x, y, dir] = findPosAndDir(map); 11 | const route = []; 12 | while (true) { 13 | const posAndDir = `${x},${y} ${dir}`; 14 | if (route.includes(posAndDir)) return null; 15 | route.push(posAndDir); 16 | const [dx, dy, rotate] = dirs[dir]; 17 | const [nx, ny] = [x + dx, y + dy]; 18 | if (outOfBounds(map, nx, ny)) return route; 19 | if (map[ny][nx] == '#' || (obs?.x == nx && obs?.y == ny)) dir = rotate; 20 | else [x, y] = [nx, ny]; 21 | } 22 | } 23 | 24 | const route = patrol(map); 25 | const positions = route.map(posAndDir => posAndDir.split(' ')[0]); 26 | const uniqPositions = [...new Set(positions)]; 27 | console.log(`day6a: ${uniqPositions.length}`); 28 | 29 | const firstPosition = positions[0]; 30 | // try adding obstacle to visited position on route and check for loop 31 | const loop = uniqPositions.filter(pos => { 32 | if (pos == firstPosition) return false; // not allowed to add obstacle to start 33 | const [x, y] = pos.split(',').map(Number); 34 | return patrol(map, {x, y}) == null; // patrol returns null in case of loop 35 | }); 36 | const uniqLoop = new Set(loop); 37 | console.log(`day6b: ${uniqLoop.size}`); -------------------------------------------------------------------------------- /2024/js/day7.js: -------------------------------------------------------------------------------- 1 | import { asLines } from './util.js'; 2 | 3 | const equations = asLines('../input/7.txt').map(l => l.split(":").map(s => s.trim().split(" ").map(Number))); 4 | 5 | const solve = (eq, operators) => { 6 | if (eq.length == 1) return eq; 7 | const a = eq[0]; // operand a for the next operation 8 | const b = eq[1]; // operand b for the next operation 9 | const res = operators.map(op => op(a, b)); // results of different ops 10 | // continue calculating equation forfard and build list of possible results 11 | return res.flatMap(r => solve([r, ...eq.slice(2)], operators)) 12 | } 13 | 14 | const solvable = (eq, operators) => { 15 | const expected = eq[0][0]; 16 | return solve(eq[1], operators).includes(expected); 17 | } 18 | 19 | const operators = [ (a, b) => a + b, (a, b) => a * b]; 20 | const sumOfSolvableEqs = (eqs, ops) => eqs.filter(e => solvable(e, ops)) 21 | .map(e => e[0][0]) 22 | .reduce((a, b) => a + b); 23 | 24 | console.log(`a: ${sumOfSolvableEqs(equations, operators)}`); 25 | 26 | const operators2 = [ (a, b) => a + b, (a, b) => a * b, (a, b) => Number('' + a + b)]; 27 | console.log(`b: ${sumOfSolvableEqs(equations, operators2)}`); 28 | -------------------------------------------------------------------------------- /2024/js/day8.js: -------------------------------------------------------------------------------- 1 | import { asLines } from './util.js'; 2 | 3 | const map = asLines('../input/8.txt').map(l => l.split("")); 4 | 5 | const antennaByFreq = {}; 6 | map.forEach((r, y) => r.forEach((freq, x) => { 7 | if (freq == '.') return; 8 | if (!antennaByFreq[freq]) antennaByFreq[freq] = []; 9 | antennaByFreq[freq].push([x, y]); 10 | })); 11 | 12 | const valid = ([x, y]) => x >= 0 && x < map[0].length && y >= 0 && y < map.length; 13 | 14 | const anti = ([x, y], [dx, dy], distanceLimit) => { 15 | const anti = new Set(); 16 | for (const an = [x, y]; valid(an); an[0] += dx, an[1] += dy) { 17 | if (distanceLimit && an[0] - dx != x && an[1] - dy != y) continue; 18 | anti.add(JSON.stringify(an)); 19 | } 20 | return anti; 21 | }; 22 | 23 | const antinodes = (distanceLimit) => { 24 | const antinodes = new Set(); 25 | Object.entries(antennaByFreq).forEach(([_, coords]) => { 26 | for (let i = 0; i < coords.length; i++) { 27 | for (let j = 0; j < coords.length; j++) { 28 | if (i == j) continue; 29 | const [x1, y1] = coords[i]; 30 | const [x2, y2] = coords[j]; 31 | const dx = x2 - x1; 32 | const dy = y2 - y1; 33 | anti([x1, y1], [-dx, -dy], distanceLimit).forEach(a => antinodes.add(a)); 34 | anti([x2, y2], [dx, dy], distanceLimit).forEach(a => antinodes.add(a)); 35 | } 36 | } 37 | }); 38 | return antinodes; 39 | } 40 | 41 | console.log(`day8a ${antinodes(true).size}`); 42 | console.log(`day8b ${antinodes(false).size}`); 43 | -------------------------------------------------------------------------------- /2024/js/day9.js: -------------------------------------------------------------------------------- 1 | import { asLines } from './util.js'; 2 | 3 | const data = asLines('../input/9.txt')[0].split('').map(c => Number(c)); 4 | 5 | const buildBlocks = (data) => { 6 | let idCounter = 0; 7 | return data.flatMap((_, i) => new Array(data[i]).fill(i % 2 ? '.' : idCounter++)); 8 | } 9 | 10 | const compact = (blocks) => { 11 | let p1 = 0; 12 | let p2 = blocks.length - 1; 13 | 14 | while(p1 < p2) { 15 | while (blocks[p1] != '.' && p1 < p2 && p1 < blocks.length - 1) p1++; 16 | while (blocks[p2] == '.' && p1 < p2 && p2 > 0) p2--; 17 | if (p1 >= p2 || blocks[p1] != '.' || blocks[p2] == '.') break; 18 | blocks[p1] = blocks[p2]; 19 | blocks[p2] = '.'; 20 | } 21 | return blocks; 22 | }; 23 | 24 | const compacted = compact(buildBlocks(data)); 25 | 26 | const checksum = (blocks) => blocks.reduce((a, b, i) => a + (b == '.' ? 0 : (i * b))); 27 | 28 | console.log(`day9a: ${checksum(compacted)}`); 29 | 30 | const findFile = (blocks, p) => { 31 | if (p < 0) return [-1, -1, -1]; 32 | while(p > 0 && blocks[p] == '.') p--; 33 | const end = p; 34 | const id = blocks[end]; 35 | while(p > 0 && blocks[p-1] == id) p--; 36 | const len = end - p + 1; 37 | return [id, p, len]; 38 | } 39 | 40 | const findFirstFit = (blocks, len, max) => { 41 | let p = 0; 42 | let found = 0; 43 | while (p < max) { 44 | if (blocks[p] == '.') found++; 45 | else found = 0; 46 | if (found == len) return p - found + 1; 47 | p++; 48 | } 49 | return -1; 50 | } 51 | 52 | const compact2 = (blocks) => { 53 | let p = blocks.length; 54 | while(true) { 55 | const [id, fileStart, len] = findFile(blocks, p - 1); 56 | if (id == -1) break; 57 | const fit = findFirstFit(blocks, len, fileStart); 58 | if (fit != -1) { 59 | for (let i = 0; i < len; i++) { 60 | blocks[fit + i] = blocks[fileStart + i]; 61 | blocks[fileStart + i] = '.'; 62 | } 63 | } 64 | p = fileStart; 65 | } 66 | return blocks; 67 | }; 68 | 69 | const compacted2 = compact2(buildBlocks(data)); 70 | console.log(`day9b: ${checksum(compacted2)}`); 71 | -------------------------------------------------------------------------------- /2024/js/util.js: -------------------------------------------------------------------------------- 1 | import { readFileSync } from 'fs'; 2 | 3 | export const asLines = (fname) => { 4 | return readFileSync(fname).toString() 5 | .split('\n') 6 | .filter((l) => l.length > 0); 7 | } 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Pekka Vainio 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # adventofcode AOC 2 | 3 | [Advent of Code (AOC)](https://adventofcode.com/) 4 | 5 | 2024: 6 | - [JavaScript](2024/js) 7 | 8 | 2023: 9 | - [JavaScript](2023/js) 10 | 11 | 2022: 12 | - [Go](2022/go) 13 | - [Java](2022/java) 14 | 15 | 2021: 16 | - [Go](2021/go) 17 | - [Java](2021/java/src/main/java/aoc2021) 18 | --------------------------------------------------------------------------------