]> Trent Huber's Code - adventOfGo2025.git/commitdiff
Initial commit
authorTrent Huber <trentmhuber@gmail.com>
Wed, 17 Dec 2025 22:51:25 +0000 (17:51 -0500)
committerTrent Huber <trentmhuber@gmail.com>
Wed, 17 Dec 2025 22:51:25 +0000 (17:51 -0500)
15 files changed:
01.go [new file with mode: 0644]
02.go [new file with mode: 0644]
03.go [new file with mode: 0644]
04.go [new file with mode: 0644]
05.go [new file with mode: 0644]
06.go [new file with mode: 0644]
07.go [new file with mode: 0644]
08.go [new file with mode: 0644]
09.go [new file with mode: 0644]
10.go [new file with mode: 0644]
11.go [new file with mode: 0644]
12.go [new file with mode: 0644]
LICENSE [new file with mode: 0644]
README.md [new file with mode: 0644]
template.go [new file with mode: 0644]

diff --git a/01.go b/01.go
new file mode 100644 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
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 (file)
index 0000000..ecec109
--- /dev/null
@@ -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))
+}