From f9ba7707dc6e53053a70121bded0933ddb28c53e Mon Sep 17 00:00:00 2001 From: Trent Huber Date: Wed, 17 Dec 2025 17:51:25 -0500 Subject: [PATCH] Initial commit --- 01.go | 93 ++++++++++++++++++++++++ 02.go | 95 ++++++++++++++++++++++++ 03.go | 117 ++++++++++++++++++++++++++++++ 04.go | 106 +++++++++++++++++++++++++++ 05.go | 107 +++++++++++++++++++++++++++ 06.go | 126 ++++++++++++++++++++++++++++++++ 07.go | 101 ++++++++++++++++++++++++++ 08.go | 137 +++++++++++++++++++++++++++++++++++ 09.go | 114 +++++++++++++++++++++++++++++ 10.go | 168 ++++++++++++++++++++++++++++++++++++++++++ 11.go | 93 ++++++++++++++++++++++++ 12.go | 205 ++++++++++++++++++++++++++++++++++++++++++++++++++++ LICENSE | 21 ++++++ README.md | 21 ++++++ template.go | 55 ++++++++++++++ 15 files changed, 1559 insertions(+) create mode 100644 01.go create mode 100644 02.go create mode 100644 03.go create mode 100644 04.go create mode 100644 05.go create mode 100644 06.go create mode 100644 07.go create mode 100644 08.go create mode 100644 09.go create mode 100644 10.go create mode 100644 11.go create mode 100644 12.go create mode 100644 LICENSE create mode 100644 README.md create mode 100644 template.go diff --git a/01.go b/01.go new file mode 100644 index 0000000..6a896e4 --- /dev/null +++ b/01.go @@ -0,0 +1,93 @@ +// https://adventofcode.com/2025/day/1 + +package main + +import ( + "bufio" + "fmt" + "os" + "strconv" +) + +func part1(scanner *bufio.Scanner) { + var pos, zeros int64 = 50, 0 + for scanner.Scan() { + rot := scanner.Bytes() + dir := rot[0] + num, _ := strconv.ParseInt(string(rot[1:]), 0, 0) + if dir == 'L' { + num = 100 - num + } + if pos = (pos + num) % 100; pos == 0 { + zeros++ + } + } + println(zeros) +} + +func part2(scanner *bufio.Scanner) { + var pos, zeros int64 = 50, 0 + for scanner.Scan() { + rot := scanner.Bytes() + dir := rot[0] + num, _ := strconv.ParseInt(string(rot[1:]), 0, 0) + zeros += num / 100 + num %= 100 + if dir == 'L' { + num *= -1 + } + if pos == 0 { + pos = (num + 100) % 100 + } else { + pos += num + if dir == 'L' { + if pos < 1 { + pos = (pos + 100) % 100 + zeros++ + } + } else { + if pos > 99 { + pos %= 100 + zeros++ + } + } + } + } + println(zeros) +} + +func main() { + var part func(*bufio.Scanner) + filename := "input.txt" + usage := func() { + fmt.Printf("usage: %v {1|2} [test]\n", os.Args[0]) + os.Exit(1) + } + switch len(os.Args) { + case 3: + if os.Args[2] == "test" { + filename = "test.txt" + } else { + usage() + } + fallthrough + case 2: + switch os.Args[1] { + case "1": + part = part1 + case "2": + part = part2 + default: + usage() + } + case 1: + usage() + } + file, err := os.Open(filename) + if err != nil { + fmt.Printf("Unable to open %v\n", filename) + os.Exit(1) + } + defer file.Close() + part(bufio.NewScanner(file)) +} diff --git a/02.go b/02.go new file mode 100644 index 0000000..ecf1cfd --- /dev/null +++ b/02.go @@ -0,0 +1,95 @@ +// https://adventofcode.com/2025/day/2 + +package main + +import ( + "bufio" + "fmt" + "os" + "strconv" + "strings" +) + +func part1(scanner *bufio.Scanner) { + scanner.Scan() + idRanges := strings.Split(scanner.Text(), ",") + total := 0 + for _, idRange := range idRanges { + ids := strings.Split(idRange, "-") + start, _ := strconv.Atoi(ids[0]) + end, _ := strconv.Atoi(ids[1]) + for i := start; i <= end; i++ { + idString := strconv.Itoa(i) + l := len(idString) / 2 + if idString[:l] == idString[l:] { + total += i + } + } + } + println(total) +} + +func part2(scanner *bufio.Scanner) { + scanner.Scan() + idRanges := strings.Split(scanner.Text(), ",") + total := 0 + for _, idRange := range idRanges { + ids := strings.Split(idRange, "-") + start, _ := strconv.Atoi(ids[0]) + end, _ := strconv.Atoi(ids[1]) + for i := start; i <= end; i++ { + idString := strconv.Itoa(i) + l := len(idString) + nextDivision: + for numDiv := 2; numDiv <= l; numDiv++ { + if l%numDiv == 0 { + subLen := l / numDiv + for j := 0; j < numDiv; j++ { + if idString[:subLen] != idString[j*subLen:(j+1)*subLen] { + continue nextDivision + } + } + total += i + break + } + } + } + } + println(total) +} + +func main() { + var part func(*bufio.Scanner) + filename := "input.txt" + usage := func() { + fmt.Printf("usage: %v {1|2} [test]\n", os.Args[0]) + os.Exit(1) + } + switch len(os.Args) { + case 3: + if os.Args[2] == "test" { + filename = "test.txt" + } else { + usage() + } + fallthrough + case 2: + switch os.Args[1] { + case "1": + part = part1 + case "2": + part = part2 + default: + usage() + } + case 1: + usage() + } + file, err := os.Open(filename) + if err != nil { + fmt.Printf("Unable to open %v\n", filename) + os.Exit(1) + } + defer file.Close() + part(bufio.NewScanner(file)) +} diff --git a/03.go b/03.go new file mode 100644 index 0000000..d8b6d9e --- /dev/null +++ b/03.go @@ -0,0 +1,117 @@ +// https://adventofcode.com/2025/day/3 + +package main + +import ( + "bufio" + "fmt" + "os" + "slices" +) + +func part1(scanner *bufio.Scanner) { + total := 0 + for scanner.Scan() { + digits := scanner.Bytes() + left, right := getMaxIndex(digits), 0 + if left == len(digits)-1 { + right = left + left = getMaxIndex(digits[:right]) + } else { + right = getMaxIndex(digits[left+1:]) + left + 1 + } + total += 10*int(digits[left]-byte('0')) + int(digits[right]-'0') + } + println(total) +} + +func getMaxIndex(digits []byte) int { + if len(digits) == 0 { + return -1 + } + var maxDigit byte = '0' + maxIndex := 0 + for i := 0; i < len(digits); i++ { + if digits[i] > maxDigit { + maxDigit = digits[i] + maxIndex = i + } + } + return maxIndex +} + +func part2(scanner *bufio.Scanner) { + total := 0 + for scanner.Scan() { + numLeft = 12 + digits := scanner.Bytes() + indices := getMaxIndices(digits) + slices.Sort(indices) + subtotal := 0 + for _, i := range indices { + subtotal *= 10 + subtotal += int(digits[i] - '0') + } + total += subtotal + } + println(total) +} + +var numLeft int + +func getMaxIndices(digits []byte) []int { + if numLeft == 0 { + return nil + } + i := getMaxIndex(digits) + if i == -1 { + return nil + } + numLeft-- + right := getMaxIndices(digits[i+1:]) + for j := range right { + right[j] += i + 1 + } + var left []int + if numLeft != 0 { + left = getMaxIndices(digits[:i]) + } + left = append(left, i) + return append(left, right...) +} + +func main() { + var part func(*bufio.Scanner) + filename := "input.txt" + usage := func() { + fmt.Printf("usage: %v {1|2} [test]\n", os.Args[0]) + os.Exit(1) + } + switch len(os.Args) { + case 3: + if os.Args[2] == "test" { + filename = "test.txt" + } else { + usage() + } + fallthrough + case 2: + switch os.Args[1] { + case "1": + part = part1 + case "2": + part = part2 + default: + usage() + } + case 1: + usage() + } + file, err := os.Open(filename) + if err != nil { + fmt.Printf("Unable to open %v\n", filename) + os.Exit(1) + } + defer file.Close() + part(bufio.NewScanner(file)) +} diff --git a/04.go b/04.go new file mode 100644 index 0000000..03b1dae --- /dev/null +++ b/04.go @@ -0,0 +1,106 @@ +// https://adventofcode.com/2025/day/4 + +package main + +import ( + "bufio" + "fmt" + "os" +) + +func part1(scanner *bufio.Scanner) { + var grid [][]byte + for scanner.Scan() { + line := scanner.Bytes() + row := make([]byte, len(line)) + copy(row, line) + grid = append(grid, row) + } + + println(len(getAccessableRolls(grid))) +} + +func part2(scanner *bufio.Scanner) { + var grid [][]byte + for scanner.Scan() { + line := scanner.Bytes() + row := make([]byte, len(line)) + copy(row, line) + grid = append(grid, row) + } + + total := 0 + for rolls := getAccessableRolls(grid); len(rolls) != 0; rolls = getAccessableRolls(grid) { + total += len(rolls) + for _, roll := range rolls { + grid[roll[0]][roll[1]] = byte('.') + } + } + println(total) +} + +func getAccessableRolls(grid [][]byte) (result [][]int) { + numRows, numCols, roll := len(grid), len(grid[0]), byte('@') + for row, gridRow := range grid { + for col, spot := range gridRow { + if spot != roll { + continue + } + numNeighbors := 0 + for i := -1; i <= 1; i++ { + if row+i == -1 || row+i == numRows { + continue + } + for j := -1; j <= 1; j++ { + if col+j == -1 || col+j == numCols { + continue + } + if grid[row+i][col+j] == roll { + numNeighbors++ + } + } + } + numNeighbors-- + if numNeighbors < 4 { + result = append(result, []int{row, col}) + } + } + } + return result +} + +func main() { + var part func(*bufio.Scanner) + filename := "input.txt" + usage := func() { + fmt.Printf("usage: %v {1|2} [test]\n", os.Args[0]) + os.Exit(1) + } + switch len(os.Args) { + case 3: + if os.Args[2] == "test" { + filename = "test.txt" + } else { + usage() + } + fallthrough + case 2: + switch os.Args[1] { + case "1": + part = part1 + case "2": + part = part2 + default: + usage() + } + case 1: + usage() + } + file, err := os.Open(filename) + if err != nil { + fmt.Printf("Unable to open %v\n", filename) + os.Exit(1) + } + defer file.Close() + part(bufio.NewScanner(file)) +} diff --git a/05.go b/05.go new file mode 100644 index 0000000..88d6ca8 --- /dev/null +++ b/05.go @@ -0,0 +1,107 @@ +// https://adventofcode.com/2025/day/5 + +package main + +import ( + "bufio" + "cmp" + "fmt" + "os" + "slices" + "strconv" + "strings" +) + +func part1(scanner *bufio.Scanner) { + var fresh [][2]int + + scanner.Scan() + for line := scanner.Text(); line != ""; line = scanner.Text() { + idRange := strings.Split(line, "-") + start, _ := strconv.Atoi(idRange[0]) + end, _ := strconv.Atoi(idRange[1]) + fresh = append(fresh, [2]int{start, end}) + scanner.Scan() + } + + total := 0 + for scanner.Scan() { + id, _ := strconv.Atoi(scanner.Text()) + for _, idRange := range fresh { + if id >= idRange[0] && id <= idRange[1] { + total++ + break + } + } + } + println(total) +} + +func part2(scanner *bufio.Scanner) { + var fresh [][2]int + + scanner.Scan() + for line := scanner.Text(); line != ""; line = scanner.Text() { + idRange := strings.Split(line, "-") + start, _ := strconv.Atoi(idRange[0]) + end, _ := strconv.Atoi(idRange[1]) + fresh = append(fresh, [2]int{start, end}) + scanner.Scan() + } + + slices.SortFunc(fresh, func(a, b [2]int) int { + return cmp.Compare(a[0], b[0]) + }) + + for i := 0; i < len(fresh)-1; i++ { + if fresh[i+1][0] <= fresh[i][1] { + fresh[i+1][0] = fresh[i][0] + if fresh[i+1][1] < fresh[i][1] { + fresh[i+1][1] = fresh[i][1] + } + fresh[i] = [2]int{1, 0} + } + } + + total := 0 + for _, idRange := range fresh { + total += idRange[1] - idRange[0] + 1 + } + println(total) +} + +func main() { + var part func(*bufio.Scanner) + filename := "input.txt" + usage := func() { + fmt.Printf("usage: %v {1|2} [test]\n", os.Args[0]) + os.Exit(1) + } + switch len(os.Args) { + case 3: + if os.Args[2] == "test" { + filename = "test.txt" + } else { + usage() + } + fallthrough + case 2: + switch os.Args[1] { + case "1": + part = part1 + case "2": + part = part2 + default: + usage() + } + case 1: + usage() + } + file, err := os.Open(filename) + if err != nil { + fmt.Printf("Unable to open %v\n", filename) + os.Exit(1) + } + defer file.Close() + part(bufio.NewScanner(file)) +} diff --git a/06.go b/06.go new file mode 100644 index 0000000..85efffd --- /dev/null +++ b/06.go @@ -0,0 +1,126 @@ +// https://adventofcode.com/2025/day/6 + +package main + +import ( + "bufio" + "fmt" + "os" + "strconv" + "strings" +) + +func part1(scanner *bufio.Scanner) { + var lines [][]string + for scanner.Scan() { + lines = append(lines, strings.Fields(scanner.Text())) + } + + ops, total := lines[len(lines)-1], 0 + for j, op := range ops { + var ( + opFunc func(int, int) int + subtotal int + ) + switch op { + case "+": + opFunc = func(a, b int) int { return a + b } + subtotal = 0 + case "*": + opFunc = func(a, b int) int { return a * b } + subtotal = 1 + } + + for i := 0; i < len(lines)-1; i++ { + num, _ := strconv.Atoi(lines[i][j]) + subtotal = opFunc(subtotal, num) + } + total += subtotal + } + println(total) +} + +func part2(scanner *bufio.Scanner) { + var lines [][]byte + for scanner.Scan() { + line := scanner.Bytes() + row := make([]byte, len(line)) + copy(row, line) + lines = append(lines, row) + } + + nums, index := [][]int{[]int{}}, 0 + for j := 0; j < len(lines[0]); j++ { + num := 0 + for i := 0; i < len(lines)-1; i++ { + if lines[i][j] != byte(' ') { + num *= 10 + num += int(lines[i][j] - byte('0')) + } + } + if num == 0 { + nums = append(nums, []int{}) + index++ + continue + } + nums[index] = append(nums[index], num) + } + + ops, total := strings.Fields(string(lines[len(lines)-1])), 0 + for j, op := range ops { + var ( + opFunc func(int, int) int + subtotal int + ) + switch op { + case "+": + opFunc = func(a, b int) int { return a + b } + subtotal = 0 + case "*": + opFunc = func(a, b int) int { return a * b } + subtotal = 1 + } + + for _, num := range nums[j] { + subtotal = opFunc(subtotal, num) + } + total += subtotal + } + println(total) +} + +func main() { + var part func(*bufio.Scanner) + filename := "input.txt" + usage := func() { + fmt.Printf("usage: %v {1|2} [test]\n", os.Args[0]) + os.Exit(1) + } + switch len(os.Args) { + case 3: + if os.Args[2] == "test" { + filename = "test.txt" + } else { + usage() + } + fallthrough + case 2: + switch os.Args[1] { + case "1": + part = part1 + case "2": + part = part2 + default: + usage() + } + case 1: + usage() + } + file, err := os.Open(filename) + if err != nil { + fmt.Printf("Unable to open %v\n", filename) + os.Exit(1) + } + defer file.Close() + part(bufio.NewScanner(file)) +} diff --git a/07.go b/07.go new file mode 100644 index 0000000..ae93b79 --- /dev/null +++ b/07.go @@ -0,0 +1,101 @@ +// https://adventofcode.com/2025/day/7 + +package main + +import ( + "bufio" + "bytes" + "fmt" + "os" +) + +func part1(scanner *bufio.Scanner) { + for scanner.Scan() { + line := scanner.Bytes() + row := make([]byte, len(line)) + copy(row, line) + grid = append(grid, row) + } + + println(countSpliters(1, bytes.Index(grid[0], []byte{byte('S')}))) +} + +var grid [][]byte + +func countSpliters(i, j int) int { + for ; i < len(grid); i++ { + if grid[i][j] == byte('|') { + return 0 + } + if grid[i][j] == byte('^') { + return countSpliters(i, j-1) + countSpliters(i, j+1) + 1 + } + grid[i][j] = byte('|') + } + return 0 +} + +func part2(scanner *bufio.Scanner) { + for scanner.Scan() { + line := scanner.Bytes() + row := make([]byte, len(line)) + copy(row, line) + grid = append(grid, row) + } + cache = make([][]int, len(grid)) + for i := range cache { + cache[i] = make([]int, len(grid[0])) + } + + println(countTimelines(1, bytes.Index(grid[0], []byte{byte('S')}))) +} + +var cache [][]int + +func countTimelines(i, j int) int { + for ; i < len(grid); i++ { + if grid[i][j] == byte('^') { + if cache[i][j] == 0 { + cache[i][j] = countTimelines(i, j-1) + countTimelines(i, j+1) + } + return cache[i][j] + } + } + return 1 +} + +func main() { + var part func(*bufio.Scanner) + filename := "input.txt" + usage := func() { + fmt.Printf("usage: %v {1|2} [test]\n", os.Args[0]) + os.Exit(1) + } + switch len(os.Args) { + case 3: + if os.Args[2] == "test" { + filename = "test.txt" + } else { + usage() + } + fallthrough + case 2: + switch os.Args[1] { + case "1": + part = part1 + case "2": + part = part2 + default: + usage() + } + case 1: + usage() + } + file, err := os.Open(filename) + if err != nil { + fmt.Printf("Unable to open %v\n", filename) + os.Exit(1) + } + defer file.Close() + part(bufio.NewScanner(file)) +} diff --git a/08.go b/08.go new file mode 100644 index 0000000..53c1eca --- /dev/null +++ b/08.go @@ -0,0 +1,137 @@ +// https://adventofcode.com/2025/day/8 + +package main + +import ( + "bufio" + "cmp" + "fmt" + "os" + "slices" + "strconv" + "strings" +) + +func part1(scanner *bufio.Scanner) { + _, cables, adj := getData(scanner) + + for _, c := range cables[:numConnections] { + adj[c.i][c.j], adj[c.j][c.i] = 1, 1 + } + + total := 1 + for i, sizes := 0, getCompSizes(adj); i < 3; i++ { + total *= sizes[i] + } + println(total) +} + +func part2(scanner *bufio.Scanner) { + vecs, cables, adj := getData(scanner) + + c := 0 + for numComps := len(vecs); c < len(cables); { + for n := 0; n < numComps-1; n, c = n+1, c+1 { + adj[cables[c].i][cables[c].j], adj[cables[c].j][cables[c].i] = 1, 1 + } + if numComps = len(getCompSizes(adj)); numComps == 1 { + break + } + } + println(vecs[cables[c-1].i][0] * vecs[cables[c-1].j][0]) +} + +type cable struct { + i, j, d int +} + +func getData(scanner *bufio.Scanner) (vecs [][3]int, cables []cable, adj [][]int) { + for scanner.Scan() { + var vec [3]int + for i, num := range strings.Split(scanner.Text(), ",") { + vec[i], _ = strconv.Atoi(num) + } + vecs = append(vecs, vec) + } + + for i := 0; i < len(vecs); i++ { + for j := i + 1; j < len(vecs); j++ { + dx, dy, dz := vecs[j][0]-vecs[i][0], vecs[j][1]-vecs[i][1], vecs[j][2]-vecs[i][2] + cables = append(cables, cable{i, j, dx*dx + dy*dy + dz*dz}) + } + } + slices.SortFunc(cables, func(a, b cable) int { + return cmp.Compare(a.d, b.d) + }) + + adj = make([][]int, len(vecs)) + for i := range adj { + adj[i] = make([]int, len(vecs)) + } + + return +} + +func getCompSizes(adj [][]int) (sizes []int) { + for i, checklist, queue := 0, make([]int, len(adj)), []int{}; i < len(adj); i++ { + if checklist[i] == 1 { + continue + } + checklist[i] = 1 + queue = append(queue, i) + for n := 0; n < len(queue); n++ { + for j := 0; j < len(adj); j++ { + if adj[queue[n]][j] == 1 && checklist[j] == 0 { + checklist[j] = 1 + queue = append(queue, j) + } + } + } + sizes = append(sizes, len(queue)) + queue = nil + } + slices.SortFunc(sizes, func(a, b int) int { + return cmp.Compare(b, a) + }) + return +} + +var numConnections int + +func main() { + var part func(*bufio.Scanner) + filename := "input.txt" + usage := func() { + fmt.Printf("usage: %v {1|2} [test]\n", os.Args[0]) + os.Exit(1) + } + numConnections = 1000 + switch len(os.Args) { + case 3: + if os.Args[2] == "test" { + filename = "test.txt" + numConnections = 10 + } else { + usage() + } + fallthrough + case 2: + switch os.Args[1] { + case "1": + part = part1 + case "2": + part = part2 + default: + usage() + } + case 1: + usage() + } + file, err := os.Open(filename) + if err != nil { + fmt.Printf("Unable to open %v\n", filename) + os.Exit(1) + } + defer file.Close() + part(bufio.NewScanner(file)) +} diff --git a/09.go b/09.go new file mode 100644 index 0000000..3e8c020 --- /dev/null +++ b/09.go @@ -0,0 +1,114 @@ +// https://adventofcode.com/2025/day/9 + +package main + +import ( + "bufio" + "fmt" + "os" + "strconv" + "strings" +) + +func part1(scanner *bufio.Scanner) { + getMax(scanner, func(r rect, tiles []tile) bool { + return true + }) +} + +func part2(scanner *bufio.Scanner) { + getMax(scanner, func(r rect, tiles []tile) bool { + for i := range tiles { + bitcode := func(r rect, p tile) (b byte) { + if p.x <= r.min.x { + b |= 0b1000 + } else if p.x >= r.max.x { + b |= 0b0100 + } + if p.y <= r.min.y { + b |= 0b0010 + } else if p.y >= r.max.y { + b |= 0b0001 + } + return + } + if bitcode(r, tiles[i])&bitcode(r, tiles[(i+1)%len(tiles)]) == 0 { + return false + } + } + return true + }) +} + +type tile struct { + x, y int +} + +type rect struct { + min, max tile +} + +func getMax(scanner *bufio.Scanner, valid func(r rect, tiles []tile) bool) { + var tiles []tile + for scanner.Scan() { + coords := strings.Split(scanner.Text(), ",") + var t tile + t.x, _ = strconv.Atoi(coords[0]) + t.y, _ = strconv.Atoi(coords[1]) + tiles = append(tiles, t) + } + + max := 0 + for i := 0; i < len(tiles); i++ { + for j := i + 1; j < len(tiles); j++ { + r := rect{tiles[i], tiles[j]} + if r.min.x > r.max.x { + r.min.x, r.max.x = r.max.x, r.min.x + } + if r.min.y > r.max.y { + r.min.y, r.max.y = r.max.y, r.min.y + } + area := (r.max.x - r.min.x + 1) * (r.max.y - r.min.y + 1) + if area > max && valid(r, tiles) { + max = area + } + } + } + println(max) +} + +func main() { + var part func(*bufio.Scanner) + filename := "input.txt" + usage := func() { + fmt.Printf("usage: %v {1|2} [test]\n", os.Args[0]) + os.Exit(1) + } + switch len(os.Args) { + case 3: + if os.Args[2] == "test" { + filename = "test.txt" + } else { + usage() + } + fallthrough + case 2: + switch os.Args[1] { + case "1": + part = part1 + case "2": + part = part2 + default: + usage() + } + case 1: + usage() + } + file, err := os.Open(filename) + if err != nil { + fmt.Printf("Unable to open %v\n", filename) + os.Exit(1) + } + defer file.Close() + part(bufio.NewScanner(file)) +} diff --git a/10.go b/10.go new file mode 100644 index 0000000..50d1971 --- /dev/null +++ b/10.go @@ -0,0 +1,168 @@ +// https://adventofcode.com/2025/day/10 + +// NOTE: Part 2 runs too slow to check for correctness + +package main + +import ( + "bufio" + "fmt" + "math" + "os" + "slices" + "strconv" + "strings" +) + +func part1(scanner *bufio.Scanner) { + total := 0 + for scanner.Scan() { + tokens := strings.Split(scanner.Text(), " ") + + lights := tokens[0][1 : len(tokens[0])-1] + var goal uint16 + for i := range lights { + goal <<= 1 + if lights[len(lights)-i-1] == '#' { + goal |= 1 + } + } + + schemes := tokens[1 : len(tokens)-1] + buttons := make([]uint16, len(schemes)) + for i, scheme := range schemes { + digits := strings.Split(scheme[1:len(scheme)-1], ",") + for _, digit := range digits { + n, _ := strconv.Atoi(digit) + buttons[i] |= 1 << n + } + } + + for i := 1; i <= len(buttons); i++ { + if configureLights(goal, buttons, i) { + total += i + break + } + } + } + println(total) +} + +func configureLights(goal uint16, buttons []uint16, depth int) bool { + if goal == 0 { + return true + } + if depth == 0 { + return false + } + for i := range buttons { + temp := make([]uint16, len(buttons)) + copy(temp, buttons) + slices.Delete(temp, i, i+1) + temp = temp[:len(temp)-1] + if configureLights(goal^buttons[i], temp, depth-1) { + return true + } + } + return false +} + +func part2(scanner *bufio.Scanner) { + total := 0 + for scanner.Scan() { + tokens := strings.Split(scanner.Text(), " ") + + goalToken := tokens[len(tokens)-1] + var goal []int + for _, digit := range strings.Split(goalToken[1:len(goalToken)-1], ",") { + n, _ := strconv.Atoi(digit) + goal = append(goal, n) + } + + buttonTokens := tokens[1 : len(tokens)-1] + buttons := make([][]int, len(buttonTokens)) + for i, buttonToken := range buttonTokens { + buttons[i] = make([]int, len(goal)) + for _, digit := range strings.Split(buttonToken[1:len(buttonToken)-1], ",") { + n, _ := strconv.Atoi(digit) + buttons[i][n] = 1 + } + } + + total += distanceFromOrigin(goal, buttons) + } + println(total) +} + +func distanceFromOrigin(position []int, buttons [][]int) (distance int) { + if len(buttons) == 0 { + return -1 + } + + m := math.MaxInt + for i, n := range buttons[0] { + if n == 1 && position[i] < m { + m = position[i] + } + } + for i := range buttons[0] { + position[i] -= buttons[0][i] * m + } + if slices.ContainsFunc(position, func(a int) bool { return a != 0 }) { + distance = -1 + for i := m; i >= 0; i-- { + d := distanceFromOrigin(position, buttons[1:]) + if d != -1 && (distance == -1 || i+d < distance) { + distance = i + d + } + for j := range buttons[0] { + position[j] += buttons[0][j] + } + } + for i := range buttons[0] { + position[i] -= buttons[0][i] + } + } else { + distance = m + for i := range buttons[0] { + position[i] += buttons[0][i] * m + } + } + return +} + +func main() { + var part func(*bufio.Scanner) + filename := "input.txt" + usage := func() { + fmt.Printf("usage: %v {1|2} [test]\n", os.Args[0]) + os.Exit(1) + } + switch len(os.Args) { + case 3: + if os.Args[2] == "test" { + filename = "test.txt" + } else { + usage() + } + fallthrough + case 2: + switch os.Args[1] { + case "1": + part = part1 + case "2": + part = part2 + default: + usage() + } + case 1: + usage() + } + file, err := os.Open(filename) + if err != nil { + fmt.Printf("Unable to open %v\n", filename) + os.Exit(1) + } + defer file.Close() + part(bufio.NewScanner(file)) +} diff --git a/11.go b/11.go new file mode 100644 index 0000000..ba18ac2 --- /dev/null +++ b/11.go @@ -0,0 +1,93 @@ +// https://adventofcode.com/2025/day/11 + +package main + +import ( + "bufio" + "fmt" + "os" + "strings" +) + +func part1(scanner *bufio.Scanner) { + initDevices(scanner) + + println(numPaths("you", "out", make(map[string]int))) +} + +func part2(scanner *bufio.Scanner) { + initDevices(scanner) + + a, b := "fft", "dac" + middle := numPaths(a, b, make(map[string]int)) + if middle == 0 { + a, b = b, a + middle = numPaths(a, b, make(map[string]int)) + } + println(numPaths("svr", a, make(map[string]int)) * + middle * numPaths(b, "out", make(map[string]int))) +} + +var devices map[string][]string + +func initDevices(scanner *bufio.Scanner) { + devices = make(map[string][]string) + for scanner.Scan() { + names := strings.Split(scanner.Text(), ":") + devices[names[0]] = strings.Split(names[1], " ")[1:] + } +} + +func numPaths(start, end string, cache map[string]int) int { + if start == end { + return 1 + } + if _, exists := cache[start]; !exists { + for _, output := range devices[start] { + cache[start] += numPaths(output, end, cache) + } + } + return cache[start] +} + +func main() { + var part func(*bufio.Scanner) + filename := "input.txt" + usage := func() { + fmt.Printf("usage: %v {1|2} [test]\n", os.Args[0]) + os.Exit(1) + } + switch len(os.Args) { + case 3: + if os.Args[2] != "test" { + usage() + } + switch os.Args[1] { + case "1": + filename = "test1.txt" + case "2": + filename = "test2.txt" + default: + usage() + } + fallthrough + case 2: + switch os.Args[1] { + case "1": + part = part1 + case "2": + part = part2 + default: + usage() + } + case 1: + usage() + } + file, err := os.Open(filename) + if err != nil { + fmt.Printf("Unable to open %v\n", filename) + os.Exit(1) + } + defer file.Close() + part(bufio.NewScanner(file)) +} diff --git a/12.go b/12.go new file mode 100644 index 0000000..15326d1 --- /dev/null +++ b/12.go @@ -0,0 +1,205 @@ +// https://adventofcode.com/2025/day/12 + +// NOTE: Part 1 runs too slow to check for correctness, and so Part 2 remains inaccessable to me. + +package main + +import ( + "bufio" + "fmt" + "os" + "strconv" + "strings" +) + +func part1(scanner *bufio.Scanner) { + total := 0 + for scanner.Scan() { + line := scanner.Text() + if line[len(line)-1] == ':' { + var s [3][3]bool + for i := 0; ; i++ { + scanner.Scan() + line = scanner.Text() + if len(line) == 0 { + break + } + + for j, c := range line { + if c == '#' { + s[i][j] = true + } + } + } + + var syms [8][3][3]bool + for rot := 0; rot < 4; rot++ { + for refl := 0; refl < 2; refl++ { + syms[2*rot+refl] = s + for i := range s { + s[i][0], s[i][2] = s[i][2], s[i][0] + } + } + temp := s + for i := range s { + for j := range s[0] { + s[i][j] = temp[2-j][i] + } + } + } + + var shape [][3]uint + nextShape: + for i := 0; i < 8; i++ { + nextPair: + for j := i + 1; j < 8; j++ { + for r := range syms[i] { + for c := range syms[i][r] { + if syms[i][r][c] != syms[j][r][c] { + continue nextPair + } + } + } + continue nextShape + } + + var data [3]uint + for r, row := range syms[i] { + for _, item := range row { + data[r] <<= 1 + if item { + data[r]++ + } + } + temp := data[r] + for i := 0; i < 16; i++ { + data[r] <<= 3 + data[r] |= temp + } + } + shape = append(shape, data) + } + shapes = append(shapes, shape) + } else { + parts := strings.Split(line, ":") + var dimensions, indices []uint8 + for _, value := range strings.Split(parts[0], "x") { + n, _ := strconv.Atoi(value) + dimensions = append(dimensions, uint8(n)) + } + data := make([]uint, dimensions[1]) + for _, value := range strings.Split(parts[1], " ")[1:] { + n, _ := strconv.Atoi(value) + indices = append(indices, uint8(n)) + } + region := region{dimensions[0], data, indices} + + if canFillRegion(region) { + total++ + } + } + } + println(total) +} + +var shapes [][][3]uint + +type region struct { + width uint8 + data []uint + indices []uint8 +} + +func canFillRegion(r region) bool { + var n int + for n = 0; n < len(r.indices); n++ { + if r.indices[n] != 0 { + break + } + } + if n == len(r.indices) { + return true + } + + r.indices[n]-- + for h := 0; h <= len(r.data)-3; h++ { + for _, shape := range shapes[n] { + var invalids [3]uint + for s := 0; s < 3; s++ { + invalids[s] = shape[0]&r.data[h] | shape[1]&r.data[h+1] | shape[2]&r.data[h+2] + shape[0] <<= 1 + shape[1] <<= 1 + shape[2] <<= 1 + } + shape[0] >>= 3 + shape[1] >>= 3 + shape[2] >>= 3 + + mask := uint(7) + for w, n := 0, 0; w <= int(r.width)-3; w, n = w+1, n+1 { + if mask&invalids[n] == 0 { + r.data[h] ^= shape[0] << w & mask + r.data[h+1] ^= shape[1] << w & mask + r.data[h+2] ^= shape[2] << w & mask + + if canFillRegion(r) { + return true + } + + r.data[h] ^= shape[0] << w & mask + r.data[h+1] ^= shape[1] << w & mask + r.data[h+2] ^= shape[2] << w & mask + } + mask <<= 1 + if n == 2 { + n = -1 + } + } + } + } + r.indices[n]++ + + return false +} + +func part2(scanner *bufio.Scanner) { + for scanner.Scan() { + println(scanner.Text()) + } +} + +func main() { + var part func(*bufio.Scanner) + filename := "input.txt" + usage := func() { + fmt.Printf("usage: %v {1|2} [test]\n", os.Args[0]) + os.Exit(1) + } + switch len(os.Args) { + case 3: + if os.Args[2] == "test" { + filename = "test.txt" + } else { + usage() + } + fallthrough + case 2: + switch os.Args[1] { + case "1": + part = part1 + case "2": + part = part2 + default: + usage() + } + case 1: + usage() + } + file, err := os.Open(filename) + if err != nil { + fmt.Printf("Unable to open %v\n", filename) + os.Exit(1) + } + defer file.Close() + part(bufio.NewScanner(file)) +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4236baa --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Trent Huber + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..45bf2be --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# Advent of Go 2025 + +Completing [this year's Advent of Code](https://adventofcode.com/2025/) to the +best of my ability entirely in [Go](https://go.dev/doc/). Day 10, Part 2 and Day +12, Part 1 remain unable to be checked for correctness since my code runs too +slow. Day 12, Part 2 remains unimplemented since I couldn't complete Part 1 for +that day. + +## Building + +To build a specific file, just `go build` it. The resulting executable expects +to take input from a file named `input.txt` located in the same directory as +you're running the executable in. To specify whether you'd like to process that +input according to the first or second part of that day, you have to pass either +a `1` or a `2` on the command line. Additionally, since each day comes with an +example input used for testing, you can optionally pass `test` at the end of the +command line to take input from `test.txt` instead, which is where I would paste +the test input copied directly from the webpage. + +If you find this code at all useful, I've included a template, `template.go`, +that can be used for any other Advent of Code challenges. diff --git a/template.go b/template.go new file mode 100644 index 0000000..ecec109 --- /dev/null +++ b/template.go @@ -0,0 +1,55 @@ +package main + +import ( + "bufio" + "fmt" + "os" +) + +func part1(scanner *bufio.Scanner) { + for scanner.Scan() { + println(scanner.Text()) + } +} + +func part2(scanner *bufio.Scanner) { + for scanner.Scan() { + println(scanner.Text()) + } +} + +func main() { + var part func(*bufio.Scanner) + filename := "input.txt" + usage := func() { + fmt.Printf("usage: %v {1|2} [test]\n", os.Args[0]) + os.Exit(1) + } + switch len(os.Args) { + case 3: + if os.Args[2] == "test" { + filename = "test.txt" + } else { + usage() + } + fallthrough + case 2: + switch os.Args[1] { + case "1": + part = part1 + case "2": + part = part2 + default: + usage() + } + case 1: + usage() + } + file, err := os.Open(filename) + if err != nil { + fmt.Printf("Unable to open %v\n", filename) + os.Exit(1) + } + defer file.Close() + part(bufio.NewScanner(file)) +} -- 2.51.0