Compare commits

..

24 Commits

Author SHA1 Message Date
6ffa3b6dd4 remove println 2021-12-20 18:43:14 -05:00
562fc231a4 remove unnecessary slice boxing 2021-12-20 18:42:36 -05:00
44c8a7659d day 16 part one finally 2021-12-20 17:52:18 -05:00
0f07ffcc00 day 15 part 1 works. part 2 works on the sample, not the real input. 2021-12-15 22:11:02 -05:00
e165726833 remove extra function 2021-12-14 10:12:10 -05:00
3f7f533592 add missing comment 2021-12-14 10:11:06 -05:00
0ce04d208b day 14 2021-12-14 10:09:05 -05:00
daf897cea2 document interables 2021-12-13 20:12:13 -05:00
6b1552d65e don't actually need to do the maxx/maxy check 2021-12-13 19:10:53 -05:00
6b2abb3ebc day 13 2021-12-13 19:02:42 -05:00
c09e706d42 empty struct instead of bool for better memory usage 2021-12-12 13:29:41 -05:00
137cfd379b don't need the top-level waitgroup 2021-12-12 13:07:36 -05:00
c2a087abf7 memorize the double-room check on day two 2021-12-12 13:05:45 -05:00
6fe9d251bd day 12 2021-12-12 13:03:33 -05:00
349e2a8f19 day 11 2021-12-11 13:25:27 -05:00
0a19c487cc day 10 2021-12-10 14:53:17 -05:00
5f9c19bf47 day 9 finally done, ugly but functional 2021-12-09 21:13:24 -05:00
214c505d8c day 8 2021-12-08 10:13:49 -05:00
8e68234caa day 7 in the books 2021-12-07 09:19:13 -05:00
55383aeedf day six is done 2021-12-06 09:45:29 -05:00
fb73d56bd5 fix logic bug in new input curl 2021-12-06 09:22:08 -05:00
056ac16cf1 inline grid field to type alias 2021-12-05 14:46:48 -05:00
d2699dfd24 use a single slice instead of two 2021-12-05 14:45:08 -05:00
019b87c43c speed up day 5 with preallocated slices, not a map 2021-12-05 13:24:59 -05:00
17 changed files with 1637 additions and 16 deletions

View File

@@ -41,13 +41,11 @@ type point struct {
y int
}
type grid struct {
m map[int]int
}
type grid []int
func (g *grid) AddLine(start, end point, includeHorizontal bool) {
for _, p := range LineFromPoints(start, end, includeHorizontal) {
g.m[p.x+p.y*1000]++
(*g)[p.x+p.y*1000]++
}
}
@@ -108,17 +106,15 @@ func parsePoints(line string) (point, point) {
func partOne() {
scanner := makeScanner(false)
grid := &grid{
m: make(map[int]int, 1000),
}
grid := make(grid, 1_000_000)
for scanner.Scan() {
line := scanner.Text()
start, end := parsePoints(line)
grid.AddLine(start, end, false)
}
var danger int
for i := range grid.m {
if grid.m[i] > 1 {
for i := range grid {
if grid[i] > 1 {
danger++
}
}
@@ -128,17 +124,15 @@ func partOne() {
func partTwo() {
scanner := makeScanner(false)
grid := &grid{
m: make(map[int]int, 1000),
}
grid := make(grid, 1_000_000)
for scanner.Scan() {
line := scanner.Text()
start, end := parsePoints(line)
grid.AddLine(start, end, true)
}
var danger int
for i := range grid.m {
if grid.m[i] > 1 {
for i := range grid {
if grid[i] > 1 {
danger++
}
}

117
06/main.go Normal file
View File

@@ -0,0 +1,117 @@
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
"time"
)
func main() {
start := time.Now()
partOne()
duration := time.Since(start)
partTwo()
duration2 := time.Since(start)
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
}
func makeScanner(test bool) *bufio.Scanner {
var f *os.File
if test {
f, _ = os.Open("inputs/testinput")
} else {
f, _ = os.Open("inputs/input")
}
reader := bufio.NewReader(f)
return bufio.NewScanner(reader)
}
func mustAtoi(line string) int {
i, _ := strconv.Atoi(line)
return i
}
type school struct {
zero int
one int
two int
three int
four int
five int
six int
seven int
eight int
}
func (s *school) AddOneDay() {
s.zero, s.one, s.two, s.three, s.four, s.five, s.six, s.seven, s.eight = s.one, s.two, s.three, s.four, s.five, s.six, s.seven+s.zero, s.eight, s.zero
}
func (s *school) Sum() int {
return s.zero + s.one + s.two + s.three + s.four + s.five + s.six + s.seven + s.eight
}
func partOne() {
scanner := makeScanner(false)
// just a single line today
scanner.Scan()
input := strings.Split(scanner.Text(), ",")
school := school{}
for _, i := range input {
switch i {
case "0":
school.zero++
case "1":
school.one++
case "2":
school.two++
case "3":
school.three++
case "4":
school.four++
case "5":
school.five++
case "6":
school.six++
}
}
for i := 0; i < 80; i++ {
school.AddOneDay()
}
fmt.Println(school.Sum())
}
func partTwo() {
scanner := makeScanner(false)
// just a single line today
scanner.Scan()
input := strings.Split(scanner.Text(), ",")
school := school{}
for _, i := range input {
switch i {
case "0":
school.zero++
case "1":
school.one++
case "2":
school.two++
case "3":
school.three++
case "4":
school.four++
case "5":
school.five++
case "6":
school.six++
}
}
for i := 0; i < 256; i++ {
school.AddOneDay()
}
fmt.Println(school.Sum())
}

111
07/main.go Normal file
View File

@@ -0,0 +1,111 @@
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
"time"
)
func main() {
start := time.Now()
partOne()
duration := time.Since(start)
partTwo()
duration2 := time.Since(start)
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
}
func makeScanner(test bool) *bufio.Scanner {
var f *os.File
if test {
f, _ = os.Open("inputs/testinput")
} else {
f, _ = os.Open("inputs/input")
}
reader := bufio.NewReader(f)
return bufio.NewScanner(reader)
}
func mustAtoi(line string) int {
i, _ := strconv.Atoi(line)
return i
}
func fuelCostPartOne(crabs []int, column int) int {
var totalFuel int
for _, v := range crabs {
if column < v {
totalFuel += v - column
}
if column > v {
totalFuel += column - v
}
}
return totalFuel
}
func fuelCostPartTwo(crabs []int, column int) int {
var totalFuel int
for _, v := range crabs {
if column < v {
totalFuel += ((v - column) * ((v - column) + 1)) / 2
}
if column > v {
totalFuel += ((column - v) * ((column - v) + 1)) / 2
}
}
return totalFuel
}
func partOne() {
scanner := makeScanner(false)
// just a single line today
scanner.Scan()
input := strings.Split(scanner.Text(), ",")
crabs := []int{}
var max int
for _, v := range input {
crab := mustAtoi(v)
crabs = append(crabs, crab)
if max < crab {
max = crab
}
}
lowestFuel := 1000000000 // some initial awful max fuel cost
for i := 0; i <= max; i++ {
cost := fuelCostPartOne(crabs, i)
if cost < lowestFuel {
lowestFuel = cost
}
}
fmt.Println(lowestFuel)
}
func partTwo() {
scanner := makeScanner(false)
// just a single line today
scanner.Scan()
input := strings.Split(scanner.Text(), ",")
crabs := []int{}
var max int
for _, v := range input {
crab := mustAtoi(v)
crabs = append(crabs, crab)
if max < crab {
max = crab
}
}
lowestFuel := 1000000000 // some initial awful max fuel cost
for i := 0; i <= max; i++ {
cost := fuelCostPartTwo(crabs, i)
if cost < lowestFuel {
lowestFuel = cost
}
}
fmt.Println(lowestFuel)
}

207
08/main.go Normal file
View File

@@ -0,0 +1,207 @@
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
"time"
)
func main() {
start := time.Now()
partOne()
duration := time.Since(start)
partTwo()
duration2 := time.Since(start)
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
}
func makeScanner(test bool) *bufio.Scanner {
var f *os.File
if test {
f, _ = os.Open("inputs/testinput")
} else {
f, _ = os.Open("inputs/input")
}
reader := bufio.NewReader(f)
return bufio.NewScanner(reader)
}
type display struct {
inputs []string
outputs []string
// mappings from input wires to display pieces
top string
topleft string
topright string
center string
bottomleft string
bottomright string
bottom string
}
func parseLine(line string) display {
display := display{
inputs: make([]string, 0),
outputs: make([]string, 0),
}
acc := []byte{}
outputs := false
for i := 0; i < len(line); i++ {
switch line[i] {
case 'a', 'b', 'c', 'd', 'e', 'f', 'g':
acc = append(acc, line[i])
case ' ':
if !outputs {
display.inputs = append(display.inputs, string(acc))
acc = []byte{}
} else {
display.outputs = append(display.outputs, string(acc))
acc = []byte{}
}
case '|':
i++
acc = []byte{}
outputs = true
}
}
// and catch the last one
display.outputs = append(display.outputs, string(acc))
return display
}
func removeAll(s, remove string) string {
for _, v := range remove {
s = strings.ReplaceAll(s, string(v), "")
}
return s
}
func timesAppears(s string, list []string) (r int) {
for _, v := range list {
if strings.Contains(v, s) {
r++
}
}
return
}
func partOne() {
scanner := makeScanner(false)
displays := []display{}
for scanner.Scan() {
displays = append(displays, parseLine(scanner.Text()))
}
hit := 0
for _, display := range displays {
for _, v := range display.outputs {
if len(v) == 7 || len(v) == 4 || len(v) == 3 || len(v) == 2 { // 8, 4, 7, 1
hit++
}
}
}
fmt.Println(hit)
}
func solve(display *display) {
mapping := map[int]string{}
for _, v := range display.inputs {
if len(v) == 2 {
mapping[1] = v
}
if len(v) == 3 {
mapping[7] = v
}
if len(v) == 4 {
mapping[4] = v
}
if len(v) == 7 {
mapping[8] = v
}
}
for _, v := range []string{"a", "b", "c", "d", "e", "f", "g"} {
if timesAppears(v, display.inputs) == 9 {
display.bottomright = v
}
if timesAppears(v, display.inputs) == 4 {
display.bottomleft = v
}
}
display.top = removeAll(mapping[7], mapping[1])
display.bottom = removeAll(mapping[8], mapping[4]+mapping[7]+display.bottomleft)
// find 0
for _, v := range display.inputs {
if len(v) == 6 {
remainder := removeAll(v, display.top+display.bottomleft+display.bottom+mapping[1])
if len(remainder) == 1 { // this is "0", so we found top-left and center
display.topleft = remainder
display.center = removeAll(mapping[8], v)
}
}
}
display.topright = removeAll(mapping[8], display.top+display.topleft+display.center+display.bottomleft+display.bottomright+display.bottom)
}
func valueFromSolved(d display, unknown string) string {
if len(unknown) == 2 {
return "1"
}
if len(unknown) == 3 {
return "7"
}
if len(unknown) == 4 {
return "4"
}
if len(unknown) == 5 {
if strings.Contains(unknown, d.topright) && strings.Contains(unknown, d.bottomright) {
return "3"
}
if strings.Contains(unknown, d.topright) && strings.Contains(unknown, d.bottomleft) {
return "2"
}
if strings.Contains(unknown, d.topleft) && strings.Contains(unknown, d.bottomright) {
return "5"
}
}
if len(unknown) == 6 {
if !strings.Contains(unknown, d.center) {
return "0"
}
if !strings.Contains(unknown, d.bottomleft) {
return "9"
}
if !strings.Contains(unknown, d.topright) {
return "6"
}
}
if len(unknown) == 7 {
return "8"
}
panic("uhoh")
}
func partTwo() {
scanner := makeScanner(false)
displays := []display{}
for scanner.Scan() {
displays = append(displays, parseLine(scanner.Text()))
}
total := 0
for _, v := range displays {
solve(&v)
sub := ""
for _, integer := range v.outputs {
sub += valueFromSolved(v, integer)
}
i, _ := strconv.Atoi(sub)
total += i
}
fmt.Println(total)
}

140
09/main.go Normal file
View File

@@ -0,0 +1,140 @@
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
"time"
)
func mustAtoi(line string) int {
i, _ := strconv.Atoi(line)
return i
}
func main() {
start := time.Now()
partOne()
duration := time.Since(start)
partTwo()
duration2 := time.Since(start)
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
}
func makeScanner(test bool) *bufio.Scanner {
var f *os.File
if test {
f, _ = os.Open("inputs/testinput")
} else {
f, _ = os.Open("inputs/input")
}
reader := bufio.NewReader(f)
return bufio.NewScanner(reader)
}
type floor []int
func partOne() {
scanner := makeScanner(false)
width := 100
height := 100
caveFloor := make([]int, width*height)
i := 0
for scanner.Scan() {
row := strings.Split(scanner.Text(), "")
for j, v := range row {
caveFloor[j+(width*i)] = mustAtoi(v)
}
i++
}
// find low points
points := 0
for row := 0; row < i; row++ {
for col := 0; col < width; col++ {
islow := 0
curr := caveFloor[col+(width*row)]
if col == 0 || curr < caveFloor[(col-1)+(width*row)] { // check left edge
islow++
}
if col+1 == width || curr < caveFloor[(col+1)+(width*row)] { // check right edge
islow++
}
if row == 0 || curr < caveFloor[col+(width*(row-1))] { // top
islow++
}
if row+1 == i || curr < caveFloor[col+(width*(row+1))] { // bottom
islow++
}
if islow == 4 {
points += curr + 1
}
}
}
fmt.Println(points)
}
func downhill(caveFloor []int, width int, col, row int) (int, int) {
curr := caveFloor[col+(width*row)]
nextcol := col
nextrow := row
if col > 0 && curr > caveFloor[(col-1)+(width*row)] { // check left edge
nextcol = (col - 1)
nextrow = row
}
if col < width-1 && curr > caveFloor[(col+1)+(width*row)] { // check right edge
nextcol = (col + 1)
nextrow = row
}
if row > 0 && curr > caveFloor[col+(width*(row-1))] { // top
nextcol = col
nextrow = row - 1
}
if col+(width*(row+1)) < len(caveFloor) && curr > caveFloor[col+(width*(row+1))] { // bottom
nextcol = col
nextrow = row + 1
}
if nextcol == col && nextrow == row { // found our low point
return col, row
}
return downhill(caveFloor, width, nextcol, nextrow)
}
func partTwo() {
scanner := makeScanner(false)
width := 100
height := 100
caveFloor := make([]int, width*height)
i := 0
for scanner.Scan() {
row := strings.Split(scanner.Text(), "")
for j, v := range row {
caveFloor[j+(width*i)] = mustAtoi(v)
}
i++
}
basins := map[int]int{} // map from low-point location to size of basin
for row := 0; row < height; row++ {
for col := 0; col < width; col++ {
if caveFloor[col+row*width] < 9 {
lowcol, lowrow := downhill(caveFloor, width, col, row)
basins[lowcol+lowrow*width]++
}
}
}
one, two, three := 0, 0, 0
for _, v := range basins {
if v >= one {
one, two, three = v, one, two
} else if v >= two {
two, three = v, two
} else if v >= three {
three = v
}
}
fmt.Println(one * two * three)
}

128
10/main.go Normal file
View File

@@ -0,0 +1,128 @@
package main
import (
"bufio"
"fmt"
"os"
"sort"
"strconv"
"time"
)
func mustAtoi(line string) int {
i, _ := strconv.Atoi(line)
return i
}
func main() {
start := time.Now()
partOne()
duration := time.Since(start)
partTwo()
duration2 := time.Since(start)
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
}
func makeScanner(test bool) *bufio.Scanner {
var f *os.File
if test {
f, _ = os.Open("inputs/testinput")
} else {
f, _ = os.Open("inputs/input")
}
reader := bufio.NewReader(f)
return bufio.NewScanner(reader)
}
type parsed struct {
errorChar rune
missingkeys []rune
autocompleteTotal int
}
func pop(stack []rune) (rune, []rune) {
pop := stack[len(stack)-1]
return pop, stack[:len(stack)-1]
}
func parseLine(line string) parsed {
stack := []rune{}
for _, v := range line {
var p rune
switch v {
case '[', '{', '<', '(':
stack = append(stack, v)
case '>':
p, stack = pop(stack)
if p != '<' {
return parsed{errorChar: v}
}
case ')':
p, stack = pop(stack)
if p != '(' {
return parsed{errorChar: v}
}
case ']':
p, stack = pop(stack)
if p != '[' {
return parsed{errorChar: v}
}
case '}':
p, stack = pop(stack)
if p != '{' {
return parsed{errorChar: v}
}
}
}
if len(stack) != 0 {
return parsed{missingkeys: stack}
}
return parsed{}
}
func partOne() {
scanner := makeScanner(false)
sum := 0
for scanner.Scan() {
result := parseLine(scanner.Text())
switch result.errorChar {
case ')':
sum += 3
case ']':
sum += 57
case '}':
sum += 1197
case '>':
sum += 25137
}
}
fmt.Println(sum)
}
func partTwo() {
scanner := makeScanner(false)
scores := []int{}
for scanner.Scan() {
result := parseLine(scanner.Text())
if result.errorChar == 0 {
for i := len(result.missingkeys) - 1; i >= 0; i-- {
result.autocompleteTotal *= 5
switch result.missingkeys[i] {
case '(':
result.autocompleteTotal += 1
case '[':
result.autocompleteTotal += 2
case '{':
result.autocompleteTotal += 3
case '<':
result.autocompleteTotal += 4
}
}
scores = append(scores, result.autocompleteTotal)
}
}
sort.Ints(scores)
fmt.Println(scores[(len(scores) / 2)])
}

147
11/main.go Normal file
View File

@@ -0,0 +1,147 @@
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"time"
)
func mustAtoi(line byte) int {
i, _ := strconv.Atoi(string(line))
return i
}
func main() {
start := time.Now()
partOne()
duration := time.Since(start)
partTwo()
duration2 := time.Since(start)
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
}
func makeScanner(test bool) *bufio.Scanner {
var f *os.File
if test {
f, _ = os.Open("inputs/testinput")
} else {
f, _ = os.Open("inputs/input")
}
reader := bufio.NewReader(f)
return bufio.NewScanner(reader)
}
type octopus struct {
energy int
flashed bool
}
type octopi []octopus // index is col + (row*10)
func (o *octopi) incrementEnergyLevels() {
for i := range *o {
(*o)[i].energy++
}
}
func (o *octopi) flash() bool {
flash := false
toIncrement := make([]int, 100)
for i := range *o {
if (*o)[i].energy > 9 && !(*o)[i].flashed {
(*o)[i].flashed = true
flash = true
if i > 9 {
toIncrement[i-10]++ // above
}
if i < 90 {
toIncrement[i+10]++ // below
}
if i%10 != 9 {
toIncrement[i+1]++ // right
}
if i%10 != 0 {
toIncrement[i-1]++ // left
}
if i%10 != 0 && i > 9 {
toIncrement[i-11]++ // above-left
}
if i%10 != 9 && i > 9 {
toIncrement[i-9]++ // above-right
}
if i%10 != 0 && i < 90 {
toIncrement[i+9]++ // below-left
}
if i%10 != 9 && i < 90 {
toIncrement[i+11]++ // below-right
}
}
}
for i := range toIncrement {
(*o)[i].energy += toIncrement[i]
}
return flash
}
func (o *octopi) reset() int {
count := 0
for i := range *o {
if (*o)[i].energy > 9 {
count++
(*o)[i].energy = 0
(*o)[i].flashed = false
}
}
return count
}
func partOne() {
scanner := makeScanner(false)
cave := octopi{}
for scanner.Scan() {
line := scanner.Text()
for i := range line {
cave = append(cave, octopus{energy: mustAtoi(line[i])})
}
}
total := 0
for step := 0; step < 100; step++ {
cave.incrementEnergyLevels()
for {
if !cave.flash() {
break
}
}
total += cave.reset()
}
fmt.Println(total)
}
func partTwo() {
scanner := makeScanner(false)
cave := octopi{}
for scanner.Scan() {
line := scanner.Text()
for i := range line {
cave = append(cave, octopus{energy: mustAtoi(line[i])})
}
}
step := 0
for {
step++
cave.incrementEnergyLevels()
for {
if !cave.flash() {
break
}
}
if cave.reset() == 100 {
break
}
}
fmt.Println(step)
}

140
12/main.go Normal file
View File

@@ -0,0 +1,140 @@
package main
import (
"bufio"
"fmt"
"os"
"strings"
"sync"
"time"
)
func main() {
start := time.Now()
partOne()
duration := time.Since(start)
partTwo()
duration2 := time.Since(start)
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
}
func makeScanner(test bool) *bufio.Scanner {
var f *os.File
if test {
f, _ = os.Open("inputs/testinput")
} else {
f, _ = os.Open("inputs/input")
}
reader := bufio.NewReader(f)
return bufio.NewScanner(reader)
}
type room struct {
name string
links []string
}
func isBigRoom(name string) bool {
return strings.ToUpper(name) == name
}
func canVisit(targetRoom room, visitedRooms []string) bool {
if isBigRoom(targetRoom.name) {
return true
}
for _, v := range visitedRooms {
if targetRoom.name == v {
return false
}
}
return true
}
func doubleSmallAlready(visitedRooms []string) bool {
counts := map[string]int{}
for _, v := range visitedRooms {
counts[v]++
}
for room, v := range counts {
if v == 2 && !isBigRoom(room) {
return true
}
}
return false
}
func descend(rooms map[string]room, currentRoom string, path []string, success chan struct{}, canVisitSmallRoomTwice bool) {
var wg sync.WaitGroup
if currentRoom == "end" {
success <- struct{}{}
return
}
for _, link := range rooms[currentRoom].links {
newPath := []string{currentRoom}
newPath = append(newPath, path...)
if canVisit(rooms[link], path) || // part one
(canVisitSmallRoomTwice && !doubleSmallAlready(newPath) && link != "start") { // part two
wg.Add(1)
go func(link string, path []string) {
defer wg.Done()
// in day 2, skip checks once we know we've visited a small room twice
descend(rooms, link, path, success, canVisitSmallRoomTwice && !doubleSmallAlready(newPath))
}(link, newPath)
}
}
wg.Wait()
}
func partOne() {
scanner := makeScanner(false)
rooms := map[string]room{}
for scanner.Scan() {
line := scanner.Text()
p := strings.Split(line, "-")
// create rooms if we haven't seen them before
if _, ok := rooms[p[0]]; !ok {
rooms[p[0]] = room{name: p[0]}
}
if _, ok := rooms[p[1]]; !ok {
rooms[p[1]] = room{name: p[1]}
}
// set up links
a := rooms[p[0]]
b := rooms[p[1]]
a.links = append(a.links, rooms[p[1]].name)
b.links = append(b.links, rooms[p[0]].name)
rooms[p[0]] = a
rooms[p[1]] = b
}
successes := make(chan struct{}, 10000)
descend(rooms, "start", []string{}, successes, false)
fmt.Printf("%+v\n", len(successes))
}
func partTwo() {
scanner := makeScanner(false)
rooms := map[string]room{}
for scanner.Scan() {
line := scanner.Text()
p := strings.Split(line, "-")
// create rooms if we haven't seen them before
if _, ok := rooms[p[0]]; !ok {
rooms[p[0]] = room{name: p[0]}
}
if _, ok := rooms[p[1]]; !ok {
rooms[p[1]] = room{name: p[1]}
}
// set up links
a := rooms[p[0]]
b := rooms[p[1]]
a.links = append(a.links, rooms[p[1]].name)
b.links = append(b.links, rooms[p[0]].name)
rooms[p[0]] = a
rooms[p[1]] = b
}
successes := make(chan struct{}, 200000)
descend(rooms, "start", []string{}, successes, true)
fmt.Printf("%+v\n", len(successes))
}

133
13/main.go Normal file
View File

@@ -0,0 +1,133 @@
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
"time"
)
func mustAtoi(line string) int {
i, _ := strconv.Atoi(line)
return i
}
func main() {
start := time.Now()
partOne()
duration := time.Since(start)
partTwo()
duration2 := time.Since(start)
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
}
func makeScanner(test bool) *bufio.Scanner {
var f *os.File
if test {
f, _ = os.Open("inputs/testinput")
} else {
f, _ = os.Open("inputs/input")
}
reader := bufio.NewReader(f)
return bufio.NewScanner(reader)
}
type point struct {
x int
y int
}
func partOne() {
scanner := makeScanner(false)
// parse points
points := make(map[point]struct{}, 0)
for scanner.Scan() {
line := scanner.Text()
if line == "" {
break
}
p := strings.Split(line, ",")
points[point{
x: mustAtoi(p[0]),
y: mustAtoi(p[1]),
}] = struct{}{}
}
// make first fold
scanner.Scan()
f := strings.Split(scanner.Text(), "=")
orientation := strings.Split(f[0], " ")[2]
crease := mustAtoi(f[1])
if orientation == "y" {
for p := range points {
if p.y > crease {
delete(points, p)
points[point{x: p.x, y: p.y - ((p.y - crease) * 2)}] = struct{}{}
}
}
}
if orientation == "x" {
for p := range points {
if p.x > crease {
delete(points, p)
points[point{x: p.x - ((p.x - crease) * 2), y: p.y}] = struct{}{}
}
}
}
fmt.Println(len(points))
}
func partTwo() {
scanner := makeScanner(false)
// parse points
points := make(map[point]struct{}, 0)
for scanner.Scan() {
line := scanner.Text()
if line == "" {
break
}
p := strings.Split(line, ",")
points[point{
x: mustAtoi(p[0]),
y: mustAtoi(p[1]),
}] = struct{}{}
}
// do folds
for scanner.Scan() {
line := scanner.Text()
f := strings.Split(line, "=")
orientation := strings.Split(f[0], " ")[2]
crease := mustAtoi(f[1])
if orientation == "y" {
for p := range points {
if p.y > crease {
delete(points, p)
points[point{x: p.x, y: p.y - ((p.y - crease) * 2)}] = struct{}{}
}
}
}
if orientation == "x" {
for p := range points {
if p.x > crease {
delete(points, p)
points[point{x: p.x - ((p.x - crease) * 2), y: p.y}] = struct{}{}
}
}
}
}
// print letters
for y := 0; y < 6; y++ { // six units tall
for x := 0; x < 5*8; x++ { // 5 units wide, 8 letters
if _, ok := points[point{x: x, y: y}]; ok {
fmt.Printf("#")
} else {
fmt.Printf(" ")
}
}
fmt.Printf("\n")
}
}

153
14/main.go Normal file
View File

@@ -0,0 +1,153 @@
package main
import (
"bufio"
"fmt"
"os"
"strings"
"time"
)
func main() {
start := time.Now()
partOne()
duration := time.Since(start)
partTwo()
duration2 := time.Since(start)
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
}
func makeScanner(test bool) *bufio.Scanner {
var f *os.File
if test {
f, _ = os.Open("inputs/testinput")
} else {
f, _ = os.Open("inputs/input")
}
reader := bufio.NewReader(f)
return bufio.NewScanner(reader)
}
func partOne() {
scanner := makeScanner(false)
polymer := map[string]int{}
// initial state
scanner.Scan()
initial := scanner.Text()
for i := 0; i < len(initial)-1; i++ {
pair := string(initial[i]) + string(initial[i+1])
polymer[pair]++
}
// eat empty line
scanner.Scan()
// parse rules
rulemap := map[string][]string{}
for scanner.Scan() {
rule := strings.Split(scanner.Text(), " -> ")
rulemap[rule[0]] = []string{
string(rule[0][0]) + rule[1],
rule[1] + string(rule[0][1]),
}
}
// apply rules
for i := 0; i < 10; i++ {
newPolymer := map[string]int{}
for p, c := range polymer {
newPolymer[rulemap[p][0]] += c
newPolymer[rulemap[p][1]] += c
}
polymer = newPolymer
}
// count elements
count := map[string]int{}
for p, c := range polymer {
count[string(p[0])] += c
count[string(p[1])] += c
}
// find max and min
max, min := 0, 0
for _, c := range count {
if c > max {
max = c
}
if c < min || min == 0 {
min = c
}
}
// handle rounding
if (max-min)%2 == 1 {
fmt.Println((max-min)/2 + 1)
} else {
fmt.Println((max - min) / 2)
}
}
func partTwo() {
scanner := makeScanner(false)
polymer := map[string]int{}
// initial state
scanner.Scan()
initial := scanner.Text()
for i := 0; i < len(initial)-1; i++ {
pair := string(initial[i]) + string(initial[i+1])
polymer[pair]++
}
// eat empty line
scanner.Scan()
// parse rules
rulemap := map[string][]string{}
for scanner.Scan() {
rule := strings.Split(scanner.Text(), " -> ")
rulemap[rule[0]] = []string{
string(rule[0][0]) + rule[1],
rule[1] + string(rule[0][1]),
}
}
// apply rules
for i := 0; i < 40; i++ {
newPolymer := map[string]int{}
for p, c := range polymer {
newPolymer[rulemap[p][0]] += c
newPolymer[rulemap[p][1]] += c
}
polymer = newPolymer
}
// count elements
count := map[string]int{}
for p, c := range polymer {
count[string(p[0])] += c
count[string(p[1])] += c
}
// find max and min
max, min := 0, 0
for _, c := range count {
if c > max {
max = c
}
if c < min || min == 0 {
min = c
}
}
// handle rounding
if (max-min)%2 == 1 {
fmt.Println((max-min)/2 + 1)
} else {
fmt.Println((max - min) / 2)
}
}

128
15/main.go Normal file
View File

@@ -0,0 +1,128 @@
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"time"
)
func mustAtoi(line string) int {
i, _ := strconv.Atoi(line)
return i
}
func main() {
start := time.Now()
partOne()
duration := time.Since(start)
partTwo()
duration2 := time.Since(start)
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
}
func makeScanner(test bool) *bufio.Scanner {
var f *os.File
if test {
f, _ = os.Open("inputs/testinput")
} else {
f, _ = os.Open("inputs/input")
}
reader := bufio.NewReader(f)
return bufio.NewScanner(reader)
}
type room struct {
row int
col int
distance int
cost int
}
func partOne() {
scanner := makeScanner(false)
grid := [100][100]room{}
col := 0
for scanner.Scan() {
line := scanner.Text()
for row, v := range line {
r := room{
distance: 1000,
cost: mustAtoi(string(v)),
}
grid[row][col] = r
}
col++
}
grid[0][0].distance = 0
for row := 0; row < 100; row++ {
for col := 0; col < 100; col++ {
if col+1 < 100 {
d := grid[row][col+1].distance
// if the cost of the space is less than the current known distance, this is the best route there
if d > grid[row][col+1].cost+grid[row][col].distance {
grid[row][col+1].distance = grid[row][col].distance + grid[row][col+1].cost
}
}
if row+1 < 100 {
d := grid[row+1][col].distance
// if the cost of the space is less than the current known distance, this is the best route there
if d > grid[row+1][col].cost+grid[row][col].distance {
grid[row+1][col].distance = grid[row][col].distance + grid[row+1][col].cost
}
}
}
}
fmt.Println(grid[99][99].distance)
}
func partTwo() {
scanner := makeScanner(false)
width := 100
scale := 5
len := width * scale
grid := [500][500]room{}
col := 0
for scanner.Scan() {
line := scanner.Text()
for row, v := range line {
for i := 0; i < scale; i++ {
for j := 0; j < scale; j++ {
cost := mustAtoi(string(v)) + i + j
if cost > 9 {
cost = ((mustAtoi(string(v)) + i + j) % 10) + 1
}
grid[row+i*width][col+j*width] = room{
row: row + i*width,
col: col + j*width,
distance: 100000,
cost: cost,
}
}
}
}
col++
}
grid[0][0].distance = 0
for row := 0; row < len; row++ {
for col := 0; col < len; col++ {
// if the cost of the space is less than the current known distance, this is the best route there
if col+1 < len {
if grid[row][col+1].distance > grid[row][col+1].cost+grid[row][col].distance {
grid[row][col+1].distance = grid[row][col].distance + grid[row][col+1].cost
}
}
if row+1 < len {
if grid[row+1][col].distance > grid[row+1][col].cost+grid[row][col].distance {
grid[row+1][col].distance = grid[row][col].distance + grid[row+1][col].cost
}
}
}
}
fmt.Println(grid[width*scale-1][width*scale-1])
}

153
16/main.go Normal file
View File

@@ -0,0 +1,153 @@
package main
import (
"bufio"
"fmt"
"os"
"time"
)
func main() {
start := time.Now()
partOne()
duration := time.Since(start)
partTwo()
duration2 := time.Since(start)
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
}
var hexMap = map[rune][]uint8{
'0': {0, 0, 0, 0},
'1': {0, 0, 0, 1},
'2': {0, 0, 1, 0},
'3': {0, 0, 1, 1},
'4': {0, 1, 0, 0},
'5': {0, 1, 0, 1},
'6': {0, 1, 1, 0},
'7': {0, 1, 1, 1},
'8': {1, 0, 0, 0},
'9': {1, 0, 0, 1},
'A': {1, 0, 1, 0},
'B': {1, 0, 1, 1},
'C': {1, 1, 0, 0},
'D': {1, 1, 0, 1},
'E': {1, 1, 1, 0},
'F': {1, 1, 1, 1},
}
func makeScanner(test bool) *bufio.Scanner {
var f *os.File
if test {
f, _ = os.Open("inputs/testinput")
} else {
f, _ = os.Open("inputs/input")
}
reader := bufio.NewReader(f)
return bufio.NewScanner(reader)
}
func parseNumber(stream []uint8, length int) int64 {
result := int64(0)
for i := 0; i < length; i++ {
result += int64(stream[i]) << (length - 1 - i)
}
return result
}
type packet struct {
version int64
typeID int64
subPackets []packet
encodedNumber int64
}
func getPacketVersion(stream []uint8) int64 {
return parseNumber(stream, 3)
}
func getPacketType(stream []uint8) int64 {
return parseNumber(stream, 3)
}
func getSubpacketLength(t uint8, stream []uint8) (int, int64) {
if t == 0 {
return 15, parseNumber(stream, 15)
}
if t == 1 {
return 11, parseNumber(stream, 11)
}
return 0, 0
}
func parsePayload(stream []uint8) (int64, int) {
num := []uint8{}
pos := 0
chars := 0
for {
currentsegment := stream[pos : pos+5]
chars += 4
num = append(num, currentsegment[1:5]...)
pos += 5
if currentsegment[0] == 0 {
break
}
}
return parseNumber(num, chars), pos
}
func parsePackets(stream []uint8) (int, packet) {
if len(stream) <= 7 {
return 0, packet{}
}
parsedPacket := packet{}
parsedPacket.version = getPacketVersion(stream[0:3])
parsedPacket.typeID = getPacketType(stream[3:6])
if parsedPacket.typeID == 4 {
encoded, consumed := parsePayload(stream[6:])
parsedPacket.encodedNumber = encoded
return consumed + 6, parsedPacket // 6 is packet header length
} else {
parsed := 0
size, subpacketlength := getSubpacketLength(stream[6], stream[7:])
for {
consumed, subs := parsePackets(stream[7+size+parsed:])
parsedPacket.subPackets = append(parsedPacket.subPackets, subs)
parsed += consumed
if size == 11 && int64(len(parsedPacket.subPackets)) == subpacketlength {
return parsed + 7 + size, parsedPacket
} else if size == 15 && int64(parsed) > subpacketlength || consumed == 0 {
return parsed + 7 + size, parsedPacket
}
}
}
}
func countVersions(pac packet) int {
i := int(pac.version)
for _, p := range pac.subPackets {
i += countVersions(p)
}
return i
}
func partOne() {
scanner := makeScanner(false)
scanner.Scan()
line := scanner.Text()
stream := []uint8{}
for _, hex := range line {
stream = append(stream, hexMap[hex]...)
}
_, parsedPacket := parsePackets(stream)
fmt.Println(countVersions(parsedPacket))
}
func partTwo() {
scanner := makeScanner(false)
for scanner.Scan() {
// line := scanner.Text()
}
}

61
16/main_test.go Normal file
View File

@@ -0,0 +1,61 @@
package main
import (
"testing"
)
func TestVersionParse(t *testing.T) {
tests := map[int64][]uint8{
0: {0, 0, 0},
1: {0, 0, 1},
2: {0, 1, 0},
3: {0, 1, 1},
4: {1, 0, 0},
5: {1, 0, 1},
6: {1, 1, 0},
7: {1, 1, 1},
}
for result, input := range tests {
if result != getPacketVersion(input) {
t.Fail()
}
if result != getPacketType(input) {
t.Fail()
}
}
}
func TestParsePayload(t *testing.T) {
if n, q := parsePayload([]uint8{0, 1, 0, 1, 0}); n != 10 || q != 5 {
t.Log(n, q)
t.Fail()
}
if n, q := parsePayload([]uint8{1, 0, 0, 0, 1, 0, 0, 1, 0, 0}); n != 20 || q != 10 {
t.Log(n, q)
t.Fail()
}
}
func TestCases(t *testing.T) {
inputs := map[string]int{
"D2FE28": 6,
"38006F45291200": 9,
"EE00D40C823060": 14,
"8A004A801A8002F478": 16,
"620080001611562C8802118E34": 12,
"C0015000016115A2E0802F182340": 23,
"A0016C880162017C3686B18A3D4780": 31,
}
for c, r := range inputs {
stream := []uint8{}
for _, hex := range c {
stream = append(stream, hexMap[hex]...)
}
_, parsedPacket := parsePackets(stream)
if r != countVersions(parsedPacket) {
t.Log(c, countVersions(parsedPacket))
t.Fail()
}
}
}

3
go.mod Normal file
View File

@@ -0,0 +1,3 @@
module adventofcode2021
go 1.17

View File

@@ -4,9 +4,15 @@ import (
"bufio"
"fmt"
"os"
"strconv"
"time"
)
func mustAtoi(line string) int {
i, _ := strconv.Atoi(line)
return i
}
func main() {
start := time.Now()
partOne()

2
new.sh
View File

@@ -5,7 +5,7 @@ __year=2021
if [ "${1}" != "" ]; then
padded=$(printf "%02g" ${1})
mkdir -p ${padded}/inputs
touch ${padded}/inputs/{input,testinput}
touch ${padded}/inputs/testinput
if [ ! -f "${padded}/inputs/input" ]; then
curl -s -H "Cookie: session=`cat .cookie`" https://adventofcode.com/${__year}/day/${1##0}/input > "${padded}/inputs/input"
fi

2
run.sh
View File

@@ -3,6 +3,6 @@
if [ "${1}" != "" ]; then
padded=$(printf "%02g" ${1})
cd ${padded}
go run *.go
go run main.go
cd - > /dev/null
fi