├── 2015 ├── day1.go ├── day10.go ├── day11.go ├── day12.go ├── day12b.go ├── day13.go ├── day14.go ├── day14b.go ├── day15.go ├── day16.go ├── day17.go ├── day18.go ├── day19.go ├── day19simple.go ├── day2.go ├── day20.go ├── day21.go ├── day22.go ├── day23.go ├── day24.go ├── day24.txt ├── day25.go ├── day3.go ├── day4.go ├── day5.go ├── day5b.go ├── day6.go ├── day6b.go ├── day7.go ├── day8.go ├── day8b.go ├── day9.go └── day9b.go ├── 2016 ├── .gitignore ├── README.md ├── day1.go ├── day10.go ├── day11.go ├── day12.go ├── day13.go ├── day14.go ├── day15.go ├── day16.go ├── day17a.go ├── day17b.go ├── day18.go ├── day19.go ├── day2.go ├── day20.go ├── day21.go ├── day22.go ├── day23.go ├── day24.go ├── day25.go ├── day3.go ├── day4.go ├── day5.go ├── day6.go ├── day7.go ├── day8.go ├── day9a.go └── day9b.go ├── 2017 ├── .gitignore ├── day01.go ├── day02.go ├── day03.go ├── day04.go ├── day05.go ├── day06.go ├── day07.go ├── day08.go ├── day09.go ├── day10.go ├── day11.go ├── day12.go ├── day13.go ├── day14.go ├── day15.go ├── day16.go ├── day17.go ├── day18.go ├── day18b.go ├── day19.go ├── day20.go ├── day21.go ├── day22.go ├── day23.go ├── day23b.go ├── day23b_naive.go ├── day24.go ├── day25.go └── src │ └── knot │ └── hash.go ├── 2018 ├── .gitignore ├── day01.go ├── day02.go ├── day03.go ├── day03_simple.go ├── day04.go ├── day05.go ├── day06.go ├── day07.go ├── day08.go ├── day09.go ├── day10.go ├── day11.go ├── day12.go ├── day13.go ├── day14.go ├── day15.go ├── day16.go ├── day17.go ├── day18.go ├── day19.go ├── day19_disassembly.go ├── day20.go ├── day21.go ├── day21_disassembly.go ├── day22.go ├── day23.go ├── day24.go ├── day25.go ├── src │ └── asm │ │ └── ops.go └── template.go ├── 2019 ├── .gitignore ├── day01.go ├── day02.go ├── day03.go ├── day04.go ├── day05.go ├── day06.go ├── day07.go ├── day08.go ├── day09.go ├── day10.go ├── day11.go ├── day12.go ├── day13.go ├── day14.go ├── day15.go ├── day16.go ├── day17.go ├── day18.go ├── day19.go ├── day20.go ├── day21.go ├── day22.go ├── day22.nb ├── day22.png ├── day23.go ├── day24.go ├── day25.go ├── go.mod └── intcode │ └── vm.go ├── 2020 ├── .gitignore ├── day01.go ├── day02.go ├── day03.go ├── day04.go ├── day05.go ├── day06.go ├── day07.go ├── day08.go ├── day09.go ├── day10.go ├── day11.go ├── day12.go ├── day13.go ├── day14.go ├── day15.go ├── day16.go ├── day17.go ├── day18.go ├── day19.go ├── day20.go ├── day21.go ├── day22.go ├── day23.go ├── day24.go └── day25.go ├── 2021 ├── .gitignore ├── day01.go ├── day02.go ├── day03.go ├── day04.go ├── day05.go ├── day06.go ├── day07.go ├── day08.go ├── day09.go ├── day10.go ├── day11.go ├── day12.go ├── day13.go ├── day14.go ├── day15.go ├── day16.go ├── day17.go ├── day18.go ├── day19.go ├── day20.go ├── day21.go ├── day22.go ├── day23.go ├── day24.go ├── day25.go ├── go.mod ├── go.sum └── trace │ └── trace.go ├── 2022 ├── .gitignore ├── day01.go ├── day02.go ├── day03.go ├── day04.go ├── day05.go ├── day06.go ├── day07.go ├── day08.go ├── day09.go ├── day10.go ├── day11.go ├── day12.go ├── day13.go ├── day14.go ├── day15.go ├── day16.go ├── day17.go ├── day18.go ├── day19.go ├── day20.go ├── day21.go ├── day22.go ├── day23.go ├── day24.go ├── day25.go ├── go.mod └── heapq │ └── heapqueue.go ├── 2023 ├── .gitignore ├── day01.go ├── day02.go ├── day03.go ├── day04.go ├── day05.go ├── day06.go ├── day07.go ├── day08.go ├── day09.go ├── day10.go ├── day11.go ├── day12.go ├── day13.go ├── day14.go ├── day15.go ├── day16.go ├── day17.go ├── day18.go ├── day19.go ├── day20.go ├── day21.go ├── day22.go ├── day23.go ├── day24.go ├── day25.go ├── go.mod └── go.sum ├── 2024 ├── .gitignore ├── day01.go ├── day02.go ├── day03.go ├── day04.go ├── day05.go ├── day06.go ├── day07.go ├── day08.go ├── day09.go ├── day10.go ├── day11.go ├── day12.go ├── day13.go ├── day14.go ├── day15.go ├── day16.go ├── day17.go ├── day18.go ├── day19.go ├── day20.go ├── day21.go ├── day22.go ├── day23.go ├── day24.go ├── day25.go ├── go.mod └── go.sum └── LICENSE /2015/day1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | func main() { 10 | reader := bufio.NewReader(os.Stdin) 11 | line, _ := reader.ReadString('\n') 12 | floor := 0 13 | hitNegative := false 14 | for i := range line[:len(line)-1] { 15 | switch line[i] { 16 | case '(': 17 | floor++ 18 | case ')': 19 | floor-- 20 | } 21 | if floor < 0 && !hitNegative { 22 | hitNegative = true 23 | fmt.Println(i + 1) 24 | } 25 | } 26 | fmt.Println(floor) 27 | } 28 | -------------------------------------------------------------------------------- /2015/day10.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | ) 7 | 8 | func main() { 9 | in := make(chan byte) 10 | out := make(chan byte) 11 | 12 | input := out 13 | 14 | for i := 0; i < 50; i++ { 15 | in = out 16 | out = make(chan byte) 17 | go machine(in, out) 18 | } 19 | 20 | output := out 21 | 22 | str := "3113322113" 23 | go func() { 24 | for c := range str { 25 | input <- str[c] 26 | } 27 | close(input) 28 | }() 29 | count := 0 30 | for _ = range output { 31 | count++ 32 | } 33 | fmt.Println(count) 34 | } 35 | 36 | func machine(in, out chan byte) { 37 | lastDigit := byte('x') 38 | count := 0 39 | for curDigit := range in { 40 | if lastDigit != curDigit && count != 0 { 41 | countStr := strconv.Itoa(count) 42 | for i := range countStr { 43 | out <- countStr[i] 44 | } 45 | out <- lastDigit 46 | count = 0 47 | } 48 | count++ 49 | lastDigit = curDigit 50 | } 51 | countStr := strconv.Itoa(count) 52 | for i := range countStr { 53 | out <- countStr[i] 54 | } 55 | out <- lastDigit 56 | close(out) 57 | } 58 | -------------------------------------------------------------------------------- /2015/day11.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | input := []byte("hxbxwxba") 9 | for ; !valid(input); inc(input) { 10 | } 11 | fmt.Println(string(input)) 12 | for inc(input); !valid(input); inc(input) { 13 | } 14 | fmt.Println(string(input)) 15 | } 16 | 17 | func valid(input []byte) bool { 18 | straight := false 19 | for i := 0; i < len(input)-2; i++ { 20 | if input[i]+1 == input[i+1] && input[i+1]+1 == input[i+2] { 21 | straight = true 22 | } 23 | } 24 | if !straight { 25 | return false 26 | } 27 | for i := range input { 28 | if input[i] == 'i' || input[i] == 'o' || input[i] == 'l' { 29 | return false 30 | } 31 | } 32 | 33 | pairs := make(map[byte]bool) 34 | for i := 0; i < len(input)-1; i++ { 35 | if input[i] == input[i+1] { 36 | pairs[input[i]] = true 37 | i++ 38 | } 39 | } 40 | return len(pairs) >= 2 41 | } 42 | 43 | func inc(input []byte) { 44 | carry := true 45 | for i := len(input) - 1; i >= 0 && carry; i-- { 46 | if input[i] == 'z' { 47 | input[i] = 'a' 48 | } else { 49 | input[i]++ 50 | carry = false 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /2015/day12.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "regexp" 8 | "strconv" 9 | ) 10 | 11 | func main() { 12 | reader := bufio.NewReader(os.Stdin) 13 | r := regexp.MustCompile("(-?[0-9]+)") 14 | for { 15 | line, err := reader.ReadString('\n') 16 | if err != nil { 17 | break 18 | } 19 | sum := 0 20 | matches := r.FindAllString(line, -1) 21 | for i := range matches { 22 | val, _ := strconv.Atoi(matches[i]) 23 | sum += val 24 | } 25 | fmt.Println(sum) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /2015/day12b.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "encoding/json" 6 | "fmt" 7 | "os" 8 | ) 9 | 10 | func main() { 11 | reader := bufio.NewReader(os.Stdin) 12 | for { 13 | line, err := reader.ReadString('\n') 14 | if err != nil { 15 | break 16 | } 17 | var j interface{} 18 | json.Unmarshal([]byte(line[:len(line)-1]), &j) 19 | m := j.(map[string]interface{}) 20 | fmt.Println(sum(m)) 21 | } 22 | } 23 | 24 | func sumList(l []interface{}) int { 25 | total := 0 26 | for i := range l { 27 | switch v := l[i].(type) { 28 | case int: 29 | total += v 30 | case int64: 31 | total += int(v) 32 | case float64: 33 | total += int(v) 34 | case map[string]interface{}: 35 | total += sum(v) 36 | case []interface{}: 37 | total += sumList(v) 38 | case string: 39 | default: 40 | fmt.Println(i, v, "is of a type I don't know how to handle") 41 | } 42 | } 43 | return total 44 | } 45 | 46 | func sum(m map[string]interface{}) int { 47 | total := 0 48 | for k, v := range m { 49 | switch vv := v.(type) { 50 | case string: 51 | if vv == "red" { 52 | return 0 53 | } 54 | case int: 55 | total += vv 56 | case int64: 57 | total += int(vv) 58 | case float64: 59 | total += int(vv) 60 | case []interface{}: 61 | total += sumList(vv) 62 | case map[string]interface{}: 63 | total += sum(vv) 64 | default: 65 | fmt.Println(k, vv, "is of a type I don't know how to handle") 66 | } 67 | } 68 | return total 69 | } 70 | -------------------------------------------------------------------------------- /2015/day13.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "regexp" 8 | "strconv" 9 | ) 10 | 11 | type Pair struct { 12 | x, y string 13 | } 14 | 15 | func main() { 16 | reader := bufio.NewReader(os.Stdin) 17 | r := regexp.MustCompile("([a-zA-Z]+) would (gain|lose) ([0-9]+) happiness units by sitting next to ([a-zA-Z]+).") 18 | happiness := make(map[Pair]int) 19 | people := make(map[string]bool) 20 | for { 21 | line, err := reader.ReadString('\n') 22 | if err != nil { 23 | break 24 | } 25 | parsed := r.FindStringSubmatch(line) 26 | x := parsed[1] 27 | y := parsed[4] 28 | happy, _ := strconv.Atoi(parsed[3]) 29 | if parsed[2] == "lose" { 30 | happy = -happy 31 | } 32 | happiness[Pair{x, y}] = happy 33 | people[x] = true 34 | people[y] = true 35 | } 36 | people["me"] = true 37 | 38 | toVisit := make([]string, len(people)) 39 | i := 0 40 | for k := range people { 41 | toVisit[i] = k 42 | i++ 43 | } 44 | 45 | fmt.Println(visitAll(nil, nil, toVisit, happiness)) 46 | } 47 | 48 | func visitAll(cur, last *string, toVisit []string, distances map[Pair]int) int { 49 | longest := 0 50 | for i := range toVisit { 51 | destination := toVisit[i] 52 | myLast := last 53 | if myLast == nil { 54 | myLast = &destination 55 | } 56 | remainingPlaces := make([]string, len(toVisit)-1) 57 | copy(remainingPlaces[:i], toVisit[:i]) 58 | copy(remainingPlaces[i:], toVisit[i+1:]) 59 | 60 | total := 0 61 | if len(remainingPlaces) > 0 { 62 | total += visitAll(&destination, myLast, remainingPlaces, distances) 63 | } else { 64 | total += distances[Pair{destination, *myLast}] 65 | total += distances[Pair{*myLast, destination}] 66 | } 67 | if cur != nil { 68 | total += distances[Pair{*cur, destination}] 69 | total += distances[Pair{destination, *cur}] 70 | } 71 | if total > longest { 72 | longest = total 73 | // fmt.Println(destination, remainingPlaces, total) 74 | } 75 | } 76 | return longest 77 | } 78 | -------------------------------------------------------------------------------- /2015/day14.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "regexp" 8 | "strconv" 9 | ) 10 | 11 | func main() { 12 | reader := bufio.NewReader(os.Stdin) 13 | r := regexp.MustCompile("([a-zA-Z]+) can fly ([0-9]+) km/s for ([0-9]+) seconds, but then must rest for ([0-9]+) seconds.") 14 | time := 2503 15 | 16 | furthest := 0 17 | bestName := "" 18 | for { 19 | line, err := reader.ReadString('\n') 20 | if err != nil { 21 | break 22 | } 23 | parsed := r.FindStringSubmatch(line) 24 | name := parsed[1] 25 | speed, _ := strconv.Atoi(parsed[2]) 26 | endurance, _ := strconv.Atoi(parsed[3]) 27 | rest, _ := strconv.Atoi(parsed[4]) 28 | 29 | cycle := endurance + rest 30 | distance := time / cycle * (speed * endurance) 31 | leftover := time % cycle 32 | if leftover >= endurance { 33 | distance += endurance * speed 34 | } else { 35 | distance += leftover * speed 36 | } 37 | 38 | if furthest < distance { 39 | furthest = distance 40 | bestName = name 41 | } 42 | } 43 | 44 | fmt.Println(furthest, bestName) 45 | } 46 | -------------------------------------------------------------------------------- /2015/day14b.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "regexp" 8 | "strconv" 9 | ) 10 | 11 | type Reindeer struct { 12 | Speed int 13 | Endurance int 14 | Rest int 15 | Distance int 16 | Points int 17 | } 18 | 19 | func main() { 20 | reader := bufio.NewReader(os.Stdin) 21 | r := regexp.MustCompile("([a-zA-Z]+) can fly ([0-9]+) km/s for ([0-9]+) seconds, but then must rest for ([0-9]+) seconds.") 22 | time := 2503 23 | 24 | reindeer := make(map[string]*Reindeer) 25 | for { 26 | line, err := reader.ReadString('\n') 27 | if err != nil { 28 | break 29 | } 30 | parsed := r.FindStringSubmatch(line) 31 | name := parsed[1] 32 | speed, _ := strconv.Atoi(parsed[2]) 33 | endurance, _ := strconv.Atoi(parsed[3]) 34 | rest, _ := strconv.Atoi(parsed[4]) 35 | 36 | reindeer[name] = &Reindeer{speed, endurance, rest, 0, 0} 37 | } 38 | 39 | for t := 1; t <= time; t++ { 40 | furthest := 0 41 | 42 | for i := range reindeer { 43 | r := reindeer[i] 44 | if (t-1)%(r.Endurance+r.Rest) < r.Endurance { 45 | r.Distance += r.Speed 46 | } 47 | if furthest < r.Distance { 48 | furthest = r.Distance 49 | } 50 | } 51 | for i := range reindeer { 52 | r := reindeer[i] 53 | if r.Distance == furthest { 54 | r.Points++ 55 | } 56 | } 57 | } 58 | 59 | mostPoints := 0 60 | for i := range reindeer { 61 | r := reindeer[i] 62 | if r.Points > mostPoints { 63 | mostPoints = r.Points 64 | } 65 | } 66 | fmt.Println(mostPoints) 67 | } 68 | -------------------------------------------------------------------------------- /2015/day15.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Ingredient struct { 8 | Capacity, Durability, Flavor, Texture, Calories int 9 | } 10 | type Recipe map[string]int 11 | 12 | func main() { 13 | ingredients := make(map[string]Ingredient) 14 | ingredients["Sugar"] = Ingredient{3, 0, 0, -3, 2} 15 | ingredients["Sprinkles"] = Ingredient{-3, 3, 0, 0, 9} 16 | ingredients["Candy"] = Ingredient{-1, 0, 4, 0, 1} 17 | ingredients["Chocolate"] = Ingredient{0, 0, -2, 2, 8} 18 | 19 | mostPoints := 0 20 | maxVolume := 100 21 | 22 | count := 1 23 | for i := 0; i < len(ingredients)-1; i++ { 24 | count *= (maxVolume + 1) 25 | } 26 | 27 | for i := 0; i < count; i++ { 28 | encoded := i 29 | r := make(Recipe) 30 | total := 0 31 | for name := range ingredients { 32 | if len(r) == len(ingredients)-1 { 33 | r[name] = maxVolume - total 34 | } else { 35 | quantity := encoded % (maxVolume + 1) 36 | encoded /= (maxVolume + 1) 37 | r[name] = quantity 38 | total += quantity 39 | } 40 | } 41 | points := evaluateRecipe(r, ingredients) 42 | if points > mostPoints { 43 | mostPoints = points 44 | } 45 | } 46 | 47 | fmt.Println(mostPoints) 48 | } 49 | 50 | func evaluateRecipe(r Recipe, ingredients map[string]Ingredient) int { 51 | capacity := 0 52 | durability := 0 53 | flavor := 0 54 | texture := 0 55 | calories := 0 56 | for n := range r { 57 | i := ingredients[n] 58 | quantity := r[n] 59 | if quantity < 0 { 60 | return -1 61 | } 62 | capacity += i.Capacity * quantity 63 | durability += i.Durability * quantity 64 | flavor += i.Flavor * quantity 65 | texture += i.Texture * quantity 66 | calories += i.Calories * quantity 67 | } 68 | if capacity <= 0 || durability <= 0 || flavor <= 0 || texture <= 0 || calories != 500 { 69 | return 0 70 | } 71 | return capacity * durability * flavor * texture 72 | } 73 | -------------------------------------------------------------------------------- /2015/day16.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "regexp" 8 | "strconv" 9 | ) 10 | 11 | func main() { 12 | toMatch := map[string]int{ 13 | "children": 3, 14 | "cats": 7, 15 | "samoyeds": 2, 16 | "pomeranians": 3, 17 | "akitas": 0, 18 | "vizslas": 0, 19 | "goldfish": 5, 20 | "trees": 3, 21 | "cars": 2, 22 | "perfumes": 1, 23 | } 24 | 25 | reader := bufio.NewReader(os.Stdin) 26 | r := regexp.MustCompile("Sue ([0-9]+): ((?:[a-z]+: [0-9]+(?:, )?)+)") 27 | r2 := regexp.MustCompile("([a-z]+): ([0-9]+)(?:, )?") 28 | 29 | readLoop: 30 | for { 31 | line, err := reader.ReadString('\n') 32 | if err != nil { 33 | break 34 | } 35 | parsed := r.FindStringSubmatch(line) 36 | sueNum, _ := strconv.Atoi(parsed[1]) 37 | 38 | properties := make(map[string]int) 39 | props := r2.FindAllStringSubmatch(parsed[2], -1) 40 | for i := range props { 41 | item := props[i][1] 42 | value, _ := strconv.Atoi(props[i][2]) 43 | properties[item] = value 44 | } 45 | 46 | for k := range toMatch { 47 | v, ok := properties[k] 48 | 49 | if k == "cats" || k == "trees" { 50 | if ok && v <= toMatch[k] { 51 | continue readLoop 52 | } 53 | } else if k == "pomeranians" || k == "goldfish" { 54 | if ok && v >= toMatch[k] { 55 | continue readLoop 56 | } 57 | } else { 58 | if ok && v != toMatch[k] { 59 | continue readLoop 60 | } 61 | } 62 | } 63 | 64 | fmt.Println(sueNum) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /2015/day17.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | volumes := []int{7, 10, 11, 18, 18, 21, 22, 24, 26, 32, 36, 40, 40, 42, 43, 44, 46, 47, 49, 50} 9 | target := 150 10 | combinations := 1 11 | for _ = range volumes { 12 | combinations *= 2 13 | } 14 | 15 | contCounts := make(map[int]int) 16 | 17 | valid := 0 18 | comb: 19 | for i := 0; i < combinations; i++ { 20 | mask := i 21 | total := 0 22 | containers := 0 23 | for v := range volumes { 24 | if mask%2 == 1 { 25 | total += volumes[v] 26 | containers++ 27 | } 28 | mask >>= 1 29 | if total > target { 30 | continue comb 31 | } 32 | } 33 | if total == target { 34 | valid++ 35 | contCounts[containers]++ 36 | } 37 | } 38 | fmt.Println(contCounts) 39 | } 40 | -------------------------------------------------------------------------------- /2015/day18.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | type Coord struct { 10 | x, y int 11 | } 12 | 13 | type Grid map[Coord]int 14 | 15 | func main() { 16 | reader := bufio.NewReader(os.Stdin) 17 | on := make(Grid) 18 | y := 0 19 | for { 20 | line, err := reader.ReadString('\n') 21 | if err != nil { 22 | break 23 | } 24 | for x := range line[:len(line)-1] { 25 | if line[x] == '#' { 26 | on[Coord{x, y}] = 1 27 | } 28 | } 29 | y++ 30 | } 31 | 32 | on[Coord{0, 0}] = 1 33 | on[Coord{0, 99}] = 1 34 | on[Coord{99, 0}] = 1 35 | on[Coord{99, 99}] = 1 36 | 37 | for i := 0; i < 100; i++ { 38 | fmt.Println(len(on)) 39 | on = step(on) 40 | } 41 | fmt.Println(len(on)) 42 | } 43 | 44 | func step(on Grid) Grid { 45 | next := make(Grid) 46 | 47 | for x := 0; x < 100; x++ { 48 | for y := 0; y < 100; y++ { 49 | neighborsOn := on[Coord{x - 1, y - 1}] + on[Coord{x - 1, y}] + on[Coord{x - 1, y + 1}] + on[Coord{x, y - 1}] + on[Coord{x, y + 1}] + on[Coord{x + 1, y - 1}] + on[Coord{x + 1, y}] + on[Coord{x + 1, y + 1}] 50 | 51 | if on[Coord{x, y}] == 1 && (neighborsOn == 2 || neighborsOn == 3) { 52 | next[Coord{x, y}] = 1 53 | } else if on[Coord{x, y}] == 0 && neighborsOn == 3 { 54 | next[Coord{x, y}] = 1 55 | } 56 | } 57 | } 58 | 59 | next[Coord{0, 0}] = 1 60 | next[Coord{0, 99}] = 1 61 | next[Coord{99, 0}] = 1 62 | next[Coord{99, 99}] = 1 63 | 64 | return next 65 | } 66 | -------------------------------------------------------------------------------- /2015/day2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | func main() { 12 | reader := bufio.NewReader(os.Stdin) 13 | paper := 0 14 | ribbon := 0 15 | for { 16 | line, err := reader.ReadString('\n') 17 | if err != nil { 18 | break 19 | } 20 | dim := strings.Split(line[:len(line)-1], "x") 21 | w, _ := strconv.Atoi(dim[0]) 22 | l, _ := strconv.Atoi(dim[1]) 23 | h, _ := strconv.Atoi(dim[2]) 24 | paper += 2*w*l + 2*w*h + 2*l*h 25 | if w*l < w*h && w*l < l*h { 26 | paper += w * l 27 | } else if w*h < l*h { 28 | paper += w * h 29 | } else { 30 | paper += l * h 31 | } 32 | ribbon += w * l * h 33 | if w+l < w+h && w+l < l+h { 34 | ribbon += 2 * (w + l) 35 | } else if w+h < l+h { 36 | ribbon += 2 * (w + h) 37 | } else { 38 | ribbon += 2 * (l + h) 39 | } 40 | } 41 | fmt.Println(paper) 42 | fmt.Println(ribbon) 43 | } 44 | -------------------------------------------------------------------------------- /2015/day20.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | i := 1 9 | for divisorSum(i) < 36000000 { 10 | i++ 11 | } 12 | fmt.Println(i) 13 | } 14 | 15 | func divisorSum(k int) int { 16 | sum := 0 17 | for i := 1; i*i < k; i++ { 18 | if k%i == 0 { 19 | if i <= 50 { 20 | sum += k / i 21 | } 22 | if k/i <= 50 { 23 | sum += i 24 | } 25 | } 26 | } 27 | return sum * 11 28 | } 29 | -------------------------------------------------------------------------------- /2015/day24.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "sort" 7 | ) 8 | 9 | func main() { 10 | packages := []int{1, 2, 3, 5, 7, 13, 17, 19, 23, 29, 31, 37, 41, 43, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113} 11 | numBuckets := 4 12 | sort.Sort(sort.Reverse(sort.IntSlice(packages))) 13 | 14 | sum := 0 15 | for i := range packages { 16 | sum += packages[i] 17 | } 18 | max := sum / numBuckets 19 | 20 | for n := 1; ; n++ { 21 | if found, product := recursiveTest(packages, max, n); found { 22 | fmt.Println(product) 23 | return 24 | } 25 | } 26 | } 27 | 28 | func recursiveTest(packages []int, maxSum, numPackages int) (bool, uint64) { 29 | found := false 30 | bestProduct := uint64(math.MaxUint64) 31 | for i := range packages { 32 | max := maxSum - packages[i] 33 | if max < 0 { 34 | continue 35 | } else if max == 0 { 36 | return true, uint64(packages[i]) 37 | } else if numPackages > 1 && i+i != len(packages) { 38 | if subFound, subProduct := recursiveTest(packages[i+1:], max, numPackages-1); subFound { 39 | found = true 40 | product := subProduct * uint64(packages[i]) 41 | if product < bestProduct { 42 | bestProduct = product 43 | } 44 | } 45 | } 46 | } 47 | return found, bestProduct 48 | } 49 | -------------------------------------------------------------------------------- /2015/day24.txt: -------------------------------------------------------------------------------- 1 | Hand solutions were: 2 | 3 | Part (a) 4 | 1*79*103*107*109*113 = 10723906903 5 | 1+79+103+107+109+113 = 512 6 | 7 | Part (b) 8 | 59*103*109*113 = 74850409 9 | 59+103+109+113 = 384 10 | -------------------------------------------------------------------------------- /2015/day25.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | n := getItem(3010, 3019) 9 | fmt.Println(n) 10 | v := 20151125 11 | for i := 0; i < n; i++ { 12 | v *= 252533 13 | v %= 33554393 14 | } 15 | fmt.Println(v) 16 | } 17 | 18 | func getItem(row, col int) int { 19 | result := 0 20 | for { 21 | if row == 1 && col == 1 { 22 | return result 23 | } 24 | result++ 25 | if col > 1 { 26 | col-- 27 | row++ 28 | } else { 29 | col = row - 1 30 | row = 1 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /2015/day3.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | type Coord struct { 10 | x, y int64 11 | } 12 | 13 | func main() { 14 | reader := bufio.NewReader(os.Stdin) 15 | line, _ := reader.ReadString('\n') 16 | var x1, y1, x2, y2 int64 17 | var visited map[Coord]int = make(map[Coord]int) 18 | visited[Coord{0, 0}] = 1 19 | isRobot := false 20 | for i := range line { 21 | if i == len(line)-1 { 22 | break 23 | } 24 | var x, y int64 25 | if isRobot { 26 | x = x2 27 | y = y2 28 | } else { 29 | x = x1 30 | y = y1 31 | } 32 | if line[i] == '^' { 33 | y += 1 34 | } else if line[i] == '>' { 35 | x += 1 36 | } else if line[i] == 'v' { 37 | y -= 1 38 | } else if line[i] == '<' { 39 | x -= 1 40 | } else { 41 | fmt.Println("Parse error") 42 | break 43 | } 44 | visited[Coord{x, y}] += 1 45 | if isRobot { 46 | x2 = x 47 | y2 = y 48 | } else { 49 | x1 = x 50 | y1 = y 51 | } 52 | isRobot = !isRobot 53 | } 54 | fmt.Println(len(visited)) 55 | } 56 | -------------------------------------------------------------------------------- /2015/day4.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/md5" 5 | "fmt" 6 | "io" 7 | "strconv" 8 | ) 9 | 10 | func main() { 11 | for i := 0; ; i++ { 12 | h := md5.New() 13 | io.WriteString(h, "yzbqklnj") 14 | io.WriteString(h, strconv.Itoa(i)) 15 | if fmt.Sprintf("%x", h.Sum(nil))[0:6] == "000000" { 16 | fmt.Println(i) 17 | break 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /2015/day5.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | func main() { 10 | reader := bufio.NewReader(os.Stdin) 11 | nice := 0 12 | vowels := map[rune]bool{ 13 | 'a': true, 14 | 'e': true, 15 | 'i': true, 16 | 'o': true, 17 | 'u': true, 18 | } 19 | blacklist := map[rune]rune{ 20 | 'a': 'b', 21 | 'c': 'd', 22 | 'p': 'q', 23 | 'x': 'y', 24 | } 25 | for { 26 | line, err := reader.ReadString('\n') 27 | if err != nil { 28 | break 29 | } 30 | last := rune('-') 31 | vowelCount := 0 32 | double := false 33 | blacklisted := false 34 | for i := 0; i < len(line)-1; i++ { 35 | cur := rune(line[i]) 36 | if last == cur { 37 | double = true 38 | } 39 | 40 | if vowels[cur] { 41 | vowelCount += 1 42 | } 43 | if blacklist[last] == cur { 44 | blacklisted = true 45 | } 46 | last = cur 47 | } 48 | fmt.Printf("%s: %d, %t, %t\n", line[:len(line)-1], vowelCount, double, blacklisted) 49 | if vowelCount >= 3 && double && !blacklisted { 50 | nice += 1 51 | } 52 | } 53 | fmt.Println(nice) 54 | } 55 | -------------------------------------------------------------------------------- /2015/day5b.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | type Pair struct { 10 | first, second rune 11 | } 12 | 13 | func main() { 14 | reader := bufio.NewReader(os.Stdin) 15 | nice := 0 16 | for { 17 | line, err := reader.ReadString('\n') 18 | if err != nil { 19 | break 20 | } 21 | penult := rune('+') 22 | last := rune('-') 23 | repeat := make(map[Pair]int) 24 | doubledouble := false 25 | alternate := false 26 | for i := 0; i < len(line)-1; i++ { 27 | cur := rune(line[i]) 28 | pair := Pair{last, cur} 29 | if repeat[pair] != i-1 && repeat[pair] != 0 { 30 | doubledouble = true 31 | } 32 | if repeat[pair] == 0 { 33 | repeat[pair] = i 34 | } 35 | if cur == penult && last != cur { 36 | alternate = true 37 | } 38 | penult = last 39 | last = cur 40 | } 41 | fmt.Printf("%s: %t, %t\n", line[:len(line)-1], doubledouble, alternate) 42 | if doubledouble && alternate { 43 | nice += 1 44 | } 45 | } 46 | fmt.Println(nice) 47 | } 48 | -------------------------------------------------------------------------------- /2015/day6.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "regexp" 8 | "strconv" 9 | ) 10 | 11 | type Coord struct { 12 | x, y int 13 | } 14 | 15 | func main() { 16 | reader := bufio.NewReader(os.Stdin) 17 | r := regexp.MustCompile("(turn off|toggle|turn on) ([0-9]+),([0-9]+) through ([0-9]+),([0-9]+)") 18 | var on map[Coord]bool = make(map[Coord]bool) 19 | for { 20 | line, err := reader.ReadString('\n') 21 | if err != nil { 22 | break 23 | } 24 | parsed := r.FindStringSubmatch(line) 25 | command := parsed[1] 26 | x1, _ := strconv.Atoi(parsed[2]) 27 | y1, _ := strconv.Atoi(parsed[3]) 28 | x2, _ := strconv.Atoi(parsed[4]) 29 | y2, _ := strconv.Atoi(parsed[5]) 30 | if command == "turn off" { 31 | for x := x1; x <= x2; x++ { 32 | for y := y1; y <= y2; y++ { 33 | delete(on, Coord{x, y}) 34 | } 35 | } 36 | } else if command == "turn on" { 37 | for x := x1; x <= x2; x++ { 38 | for y := y1; y <= y2; y++ { 39 | on[Coord{x, y}] = true 40 | } 41 | } 42 | } else { 43 | for x := x1; x <= x2; x++ { 44 | for y := y1; y <= y2; y++ { 45 | if on[Coord{x, y}] { 46 | delete(on, Coord{x, y}) 47 | } else { 48 | on[Coord{x, y}] = true 49 | } 50 | } 51 | } 52 | } 53 | } 54 | fmt.Println(len(on)) 55 | } 56 | -------------------------------------------------------------------------------- /2015/day6b.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "regexp" 8 | "strconv" 9 | ) 10 | 11 | type Coord struct { 12 | x, y int 13 | } 14 | 15 | func main() { 16 | reader := bufio.NewReader(os.Stdin) 17 | r := regexp.MustCompile("(turn off|toggle|turn on) ([0-9]+),([0-9]+) through ([0-9]+),([0-9]+)") 18 | var on map[Coord]int = make(map[Coord]int) 19 | for { 20 | line, err := reader.ReadString('\n') 21 | if err != nil { 22 | break 23 | } 24 | parsed := r.FindStringSubmatch(line) 25 | command := parsed[1] 26 | x1, _ := strconv.Atoi(parsed[2]) 27 | y1, _ := strconv.Atoi(parsed[3]) 28 | x2, _ := strconv.Atoi(parsed[4]) 29 | y2, _ := strconv.Atoi(parsed[5]) 30 | if command == "turn off" { 31 | for x := x1; x <= x2; x++ { 32 | for y := y1; y <= y2; y++ { 33 | if on[Coord{x, y}] >= 1 { 34 | on[Coord{x, y}]-- 35 | } 36 | } 37 | } 38 | } else if command == "turn on" { 39 | for x := x1; x <= x2; x++ { 40 | for y := y1; y <= y2; y++ { 41 | on[Coord{x, y}]++ 42 | } 43 | } 44 | } else { 45 | for x := x1; x <= x2; x++ { 46 | for y := y1; y <= y2; y++ { 47 | on[Coord{x, y}] += 2 48 | } 49 | } 50 | } 51 | } 52 | sum := 0 53 | for key := range on { 54 | sum += on[key] 55 | } 56 | fmt.Println(sum) 57 | } 58 | -------------------------------------------------------------------------------- /2015/day8.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | func main() { 10 | input := 0 11 | memory := 0 12 | 13 | reader := bufio.NewReader(os.Stdin) 14 | for { 15 | line, err := reader.ReadString('\n') 16 | if err != nil { 17 | break 18 | } 19 | for i := 0; i < len(line)-1; i++ { 20 | input++ 21 | if i == 0 || i == len(line)-2 { 22 | if line[i] != '"' { 23 | fmt.Println("Malformed input line") 24 | return 25 | } 26 | continue 27 | } 28 | memory++ 29 | if line[i] == '\\' { 30 | if line[i+1] == 'x' { 31 | input += 3 32 | i += 3 33 | } else if line[i+1] == '\\' { 34 | input += 1 35 | i += 1 36 | } else if line[i+1] == '"' { 37 | input += 1 38 | i += 1 39 | } 40 | } 41 | } 42 | } 43 | 44 | fmt.Println(input) 45 | fmt.Println(memory) 46 | fmt.Println(input - memory) 47 | } 48 | -------------------------------------------------------------------------------- /2015/day8b.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | func main() { 10 | input := 0 11 | output := 0 12 | 13 | reader := bufio.NewReader(os.Stdin) 14 | for { 15 | line, err := reader.ReadString('\n') 16 | if err != nil { 17 | break 18 | } 19 | input += len(line) - 1 20 | output += len(line) - 1 + 2 21 | for i := 0; i < len(line)-1; i++ { 22 | if line[i] == '\\' { 23 | output++ 24 | } else if line[i] == '"' { 25 | output++ 26 | } 27 | } 28 | } 29 | 30 | fmt.Println(input) 31 | fmt.Println(output) 32 | fmt.Println(output - input) 33 | } 34 | -------------------------------------------------------------------------------- /2015/day9.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "log" 7 | "math" 8 | "os" 9 | "regexp" 10 | "strconv" 11 | ) 12 | 13 | type Pair struct { 14 | x, y string 15 | } 16 | 17 | func main() { 18 | reader := bufio.NewReader(os.Stdin) 19 | r := regexp.MustCompile("([a-zA-Z]+) to ([a-zA-Z]+) = ([0-9]+)") 20 | distances := make(map[Pair]uint32) 21 | places := make(map[string]bool) 22 | for { 23 | line, err := reader.ReadString('\n') 24 | if err != nil { 25 | break 26 | } 27 | parsed := r.FindStringSubmatch(line) 28 | src := parsed[1] 29 | dst := parsed[2] 30 | distance, _ := strconv.Atoi(parsed[3]) 31 | distances[Pair{src, dst}] = uint32(distance) 32 | distances[Pair{dst, src}] = uint32(distance) 33 | places[src] = true 34 | places[dst] = true 35 | } 36 | 37 | toVisit := make([]string, len(places)) 38 | i := 0 39 | for k := range places { 40 | toVisit[i] = k 41 | i++ 42 | } 43 | 44 | fmt.Println(visitAll(nil, toVisit, distances)) 45 | } 46 | 47 | func visitAll(start *string, toVisit []string, distances map[Pair]uint32) uint32 { 48 | shortest := uint32(math.MaxUint32) 49 | for i := range toVisit { 50 | destination := toVisit[i] 51 | remainingPlaces := make([]string, len(toVisit)-1) 52 | copy(remainingPlaces[:i], toVisit[:i]) 53 | copy(remainingPlaces[i:], toVisit[i+1:]) 54 | 55 | total := uint32(0) 56 | if len(remainingPlaces) > 0 { 57 | total += visitAll(&destination, remainingPlaces, distances) 58 | } 59 | if start != nil { 60 | if distance, ok := distances[Pair{*start, destination}]; ok { 61 | total += distance 62 | } else { 63 | log.Fatalf("Failed to find city pair: %s, %s\n", *start, destination) 64 | } 65 | } 66 | if total < shortest { 67 | shortest = total 68 | } 69 | } 70 | return shortest 71 | } 72 | -------------------------------------------------------------------------------- /2015/day9b.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "log" 7 | "os" 8 | "regexp" 9 | "strconv" 10 | ) 11 | 12 | type Pair struct { 13 | x, y string 14 | } 15 | 16 | func main() { 17 | reader := bufio.NewReader(os.Stdin) 18 | r := regexp.MustCompile("([a-zA-Z]+) to ([a-zA-Z]+) = ([0-9]+)") 19 | distances := make(map[Pair]uint32) 20 | places := make(map[string]bool) 21 | for { 22 | line, err := reader.ReadString('\n') 23 | if err != nil { 24 | break 25 | } 26 | parsed := r.FindStringSubmatch(line) 27 | src := parsed[1] 28 | dst := parsed[2] 29 | distance, _ := strconv.Atoi(parsed[3]) 30 | distances[Pair{src, dst}] = uint32(distance) 31 | distances[Pair{dst, src}] = uint32(distance) 32 | places[src] = true 33 | places[dst] = true 34 | } 35 | 36 | toVisit := make([]string, len(places)) 37 | i := 0 38 | for k := range places { 39 | toVisit[i] = k 40 | i++ 41 | } 42 | 43 | fmt.Println(visitAll(nil, toVisit, distances)) 44 | } 45 | 46 | func visitAll(start *string, toVisit []string, distances map[Pair]uint32) uint32 { 47 | longest := uint32(0) 48 | for i := range toVisit { 49 | destination := toVisit[i] 50 | remainingPlaces := make([]string, len(toVisit)-1) 51 | copy(remainingPlaces[:i], toVisit[:i]) 52 | copy(remainingPlaces[i:], toVisit[i+1:]) 53 | 54 | total := uint32(0) 55 | if len(remainingPlaces) > 0 { 56 | total += visitAll(&destination, remainingPlaces, distances) 57 | } 58 | if start != nil { 59 | if distance, ok := distances[Pair{*start, destination}]; ok { 60 | total += distance 61 | } else { 62 | log.Fatalf("Failed to find city pair: %s, %s\n", *start, destination) 63 | } 64 | } 65 | if total > longest { 66 | longest = total 67 | } 68 | } 69 | return longest 70 | } 71 | -------------------------------------------------------------------------------- /2016/.gitignore: -------------------------------------------------------------------------------- 1 | *.input 2 | -------------------------------------------------------------------------------- /2016/README.md: -------------------------------------------------------------------------------- 1 | ## 2016 AoC solutions 2 | 3 | In Golang as usual! 4 | 5 | --lizthegrey 6 | -------------------------------------------------------------------------------- /2016/day1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | type coord struct { 11 | X, Y int 12 | } 13 | 14 | func main() { 15 | input := strings.Split("R5, R4, R2, L3, R1, R1, L4, L5, R3, L1, L1, R4, L2, R1, R4, R4, L2, L2, R4, L4, R1, R3, L3, L1, L2, R1, R5, L5, L1, L1, R3, R5, L1, R4, L5, R5, R1, L185, R4, L1, R51, R3, L2, R78, R1, L4, R188, R1, L5, R5, R2, R3, L5, R3, R4, L1, R2, R2, L4, L4, L5, R5, R4, L4, R2, L5, R2, L1, L4, R4, L4, R2, L3, L4, R2, L3, R3, R2, L2, L3, R4, R3, R1, L4, L2, L5, R4, R4, L1, R1, L5, L1, R3, R1, L2, R1, R1, R3, L4, L1, L3, R2, R4, R2, L2, R1, L5, R3, L3, R3, L1, R4, L3, L3, R4, L2, L1, L3, R2, R3, L2, L1, R4, L3, L5, L2, L4, R1, L4, L4, R3, R5, L4, L1, L1, R4, L2, R5, R1, R1, R2, R1, R5, L1, L3, L5, R2", ", ") 16 | dir := 0 17 | loc := coord{0, 0} 18 | var repeated *coord 19 | visited := make(map[coord]bool) 20 | visited[loc] = true 21 | for _, inst := range input { 22 | turn := string(inst[0]) 23 | distance, err := strconv.Atoi(inst[1:]) 24 | if err != nil { 25 | fmt.Printf("Invalid distance: %d\n", inst[1:]) 26 | return 27 | } 28 | if turn == "L" { 29 | dir = (dir - 1) % 4 30 | } else if turn == "R" { 31 | dir = (dir + 1) % 4 32 | } else { 33 | fmt.Printf("Invalid turn: %s\n", turn) 34 | return 35 | } 36 | if dir < 0 { 37 | dir = dir + 4 38 | } 39 | for i := 0; i < distance; i++ { 40 | switch dir { 41 | case 0: 42 | loc.Y += 1 43 | case 1: 44 | loc.X += 1 45 | case 2: 46 | loc.Y -= 1 47 | case 3: 48 | loc.X -= 1 49 | default: 50 | fmt.Printf("Invalid direction: %d", dir) 51 | return 52 | } 53 | if visited[loc] && repeated == nil { 54 | saved := loc 55 | repeated = &saved 56 | } 57 | visited[loc] = true 58 | } 59 | } 60 | fmt.Println(math.Abs(float64(loc.X)) + math.Abs(float64(loc.Y))) 61 | fmt.Println(math.Abs(float64(repeated.X)) + math.Abs(float64(repeated.Y))) 62 | } 63 | -------------------------------------------------------------------------------- /2016/day14.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | "fmt" 7 | ) 8 | 9 | type Repeateds struct { 10 | FirstTrip, Quints uint16 11 | } 12 | 13 | type Cache map[int]Repeateds 14 | 15 | var Salt string = "qzyelonm" 16 | var StretchFactor int = 2016 17 | 18 | func (cache Cache) GetRepeateds(i int) Repeateds { 19 | if r, ok := cache[i]; ok { 20 | return r 21 | } 22 | data := []byte(fmt.Sprintf("%s%d", Salt, i)) 23 | h := md5.Sum(data) 24 | 25 | for i := 0; i < StretchFactor; i++ { 26 | h = md5.Sum([]byte(hex.EncodeToString(h[:]))) 27 | } 28 | 29 | var r Repeateds 30 | 31 | var consecChar byte 32 | consecRepeats := 0 33 | for _, b := range h { 34 | eval := func(c byte) { 35 | if consecChar != c { 36 | consecRepeats = 1 37 | consecChar = c 38 | } else { 39 | consecRepeats++ 40 | if consecRepeats == 3 && r.FirstTrip == 0 { 41 | r.FirstTrip = 1 << consecChar 42 | } else if consecRepeats == 5 { 43 | r.Quints |= 1 << consecChar 44 | } 45 | } 46 | } 47 | 48 | lowHalf := b & 0xf 49 | highHalf := b >> 4 50 | 51 | eval(highHalf) 52 | eval(lowHalf) 53 | } 54 | cache[i] = r 55 | return r 56 | } 57 | 58 | func main() { 59 | cache := make(Cache) 60 | searchPos := 0 61 | for found := 0; found < 64; searchPos++ { 62 | r := cache.GetRepeateds(searchPos) 63 | if r.FirstTrip == 0 { 64 | continue 65 | } 66 | for i := searchPos + 1; i < searchPos+1001; i++ { 67 | if cache.GetRepeateds(i).Quints&r.FirstTrip != 0 { 68 | fmt.Println(searchPos) 69 | found++ 70 | break 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /2016/day15.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Disc struct { 8 | Position, Positions uint 9 | } 10 | 11 | func (d Disc) Tick(delta uint) uint { 12 | return (d.Position + delta) % d.Positions 13 | } 14 | 15 | type DiscStack []Disc 16 | 17 | func (ds DiscStack) Check(start uint) int { 18 | for i, d := range ds { 19 | if d.Tick(uint(i+1)+start) != 0 { 20 | return i 21 | } 22 | } 23 | return -1 24 | } 25 | 26 | func main() { 27 | discs := DiscStack{{0, 7}, {0, 13}, {2, 3}, {2, 5}, {0, 17}, {7, 19}, {0, 11}} 28 | inc := uint(1) 29 | current := 0 30 | for i := uint(0); ; i += inc { 31 | if fail := discs.Check(uint(i)); fail == -1 { 32 | fmt.Printf("Found match: %d\n", i) 33 | break 34 | } else if fail > current { 35 | for p := current; p < fail; p++ { 36 | inc *= discs[p].Positions 37 | } 38 | current = fail 39 | } 40 | fmt.Println(i) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /2016/day16.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type BitString []bool 8 | 9 | func (a BitString) Generate() BitString { 10 | ret := make(BitString, len(a)*2+1) 11 | copy(ret, a) 12 | ret[len(a)] = false 13 | for i := 1; i <= len(a); i++ { 14 | ret[len(a)+i] = !a[len(a)-i] 15 | } 16 | return ret 17 | } 18 | 19 | func (a BitString) Check() BitString { 20 | if len(a)%2 == 1 { 21 | return a 22 | } 23 | out := make([]bool, len(a)/2) 24 | for i := 0; i < len(a); i += 2 { 25 | out[i/2] = a[i] == a[i+1] 26 | } 27 | return out 28 | } 29 | 30 | func (a BitString) Print() { 31 | for _, b := range a { 32 | if b { 33 | fmt.Printf("1") 34 | } else { 35 | fmt.Printf("0") 36 | } 37 | } 38 | fmt.Println() 39 | } 40 | 41 | func main() { 42 | length := 35651584 43 | working := BitString{true, false, false, false, true, true, true, false, false, true, true, true, true, false, false, false, false} 44 | for ; len(working) < length; working = working.Generate() { 45 | //working.Print() 46 | } 47 | working = working[0:length] 48 | for c := working; ; c = c.Check() { 49 | //c.Print() 50 | if len(c)%2 == 1 { 51 | c.Print() 52 | return 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /2016/day18.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Row []bool 8 | 9 | func (r Row) Next() Row { 10 | ret := make(Row, len(r)) 11 | for i := range r { 12 | left := !(i == 0 || !r[i-1]) 13 | center := r[i] 14 | right := !(i == len(r)-1 || !r[i+1]) 15 | ret[i] = (left && center && !right) || (!left && center && right) || (left && !center && !right) || (!left && !center && right) 16 | } 17 | return ret 18 | } 19 | 20 | func (r Row) CountSafe() int { 21 | ret := 0 22 | for _, v := range r { 23 | if !v { 24 | ret++ 25 | } 26 | } 27 | return ret 28 | } 29 | 30 | func main() { 31 | input := ".^^..^...^..^^.^^^.^^^.^^^^^^.^.^^^^.^^.^^^^^^.^...^......^...^^^..^^^.....^^^^^^^^^....^^...^^^^..^" 32 | r := make(Row, len(input)) 33 | for i, c := range input { 34 | r[i] = c == '^' 35 | } 36 | sum := 0 37 | for i := 0; i < 400000; i++ { 38 | sum += r.CountSafe() 39 | r = r.Next() 40 | } 41 | fmt.Println(sum) 42 | } 43 | -------------------------------------------------------------------------------- /2016/day19.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "container/ring" 5 | "fmt" 6 | ) 7 | 8 | func regular(n int) int { 9 | active := ring.New(n) 10 | for i := 0; i < n; i++ { 11 | active.Value = i + 1 12 | active = active.Next() 13 | } 14 | for ; active.Next() != active; active = active.Next() { 15 | active.Unlink(1) 16 | } 17 | return active.Value.(int) 18 | } 19 | 20 | func revised(n int) int { 21 | active := ring.New(n) 22 | var opposite *ring.Ring 23 | for i := 0; i < n; i++ { 24 | active.Value = i + 1 25 | if i == (n-1)/2 { 26 | opposite = active 27 | } 28 | 29 | active = active.Next() 30 | } 31 | 32 | rCount := 0 33 | for ; rCount != n-1; active = active.Next() { 34 | opposite = opposite.Prev() 35 | opposite.Unlink(1) 36 | if rCount%2 == 0 { 37 | opposite = opposite.Next().Next() 38 | } else { 39 | opposite = opposite.Next() 40 | } 41 | rCount++ 42 | } 43 | return active.Value.(int) 44 | } 45 | 46 | func main() { 47 | fmt.Println(regular(5)) 48 | fmt.Println(regular(3014603)) 49 | fmt.Println(revised(5)) 50 | fmt.Println(revised(3014603)) 51 | } 52 | -------------------------------------------------------------------------------- /2016/day25.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | test: 9 | for i := 0; ; i++ { 10 | successes := 0 11 | last := 1 12 | 13 | d := i + 365*7 14 | b := d 15 | 16 | for { 17 | c := b % 2 18 | b = b >> 1 19 | 20 | if !emit(c, last, &successes) { 21 | continue test 22 | } 23 | if successes == 100 { 24 | fmt.Println(i) 25 | return 26 | } 27 | last = c 28 | if b != 0 { 29 | continue 30 | } else { 31 | b = d 32 | } 33 | } 34 | } 35 | } 36 | 37 | func emit(b int, last int, successes *int) bool { 38 | if b == last { 39 | return false 40 | } 41 | *successes++ 42 | return true 43 | } 44 | -------------------------------------------------------------------------------- /2016/day5.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | "fmt" 7 | ) 8 | 9 | func main() { 10 | found := 0 11 | result := []byte("________") 12 | scrambled := true 13 | for j := 0; found != 8; j++ { 14 | data := []byte(fmt.Sprintf("%s%d", "cxdnnyjw", j)) 15 | h := md5.Sum(data) 16 | hash := hex.EncodeToString(h[:]) 17 | if hash[0] == '0' && hash[1] == '0' && hash[2] == '0' && hash[3] == '0' && hash[4] == '0' { 18 | if !scrambled { 19 | result[found] = hash[5] 20 | } else if pos := hash[5] - '0'; pos >= 0 && pos < 8 && result[pos] == '_' { 21 | result[pos] = hash[6] 22 | } else { 23 | continue 24 | } 25 | fmt.Println(string(result)) 26 | found++ 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /2017/.gitignore: -------------------------------------------------------------------------------- 1 | *.input 2 | -------------------------------------------------------------------------------- /2017/day01.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "strconv" 7 | ) 8 | 9 | var input = flag.String("input", "1122", "The input to the problem.") 10 | var partB = flag.Bool("partB", true, "Whether to use the Part B logic.") 11 | 12 | func main() { 13 | flag.Parse() 14 | 15 | runningSum := 0 16 | digits := make([]int, len(*input)) 17 | 18 | for i, c := range *input { 19 | curVal, err := strconv.Atoi(string(c)) 20 | if err != nil { 21 | fmt.Printf("Couldn't parse: %v\n", err) 22 | return 23 | } 24 | digits[i] = curVal 25 | } 26 | 27 | for i, curVal := range digits { 28 | if *partB { 29 | if digits[(len(digits)/2+i)%len(digits)] == curVal { 30 | runningSum += curVal 31 | } 32 | } else { 33 | if i == 0 { 34 | if digits[len(digits)-1] == curVal { 35 | runningSum += curVal 36 | } 37 | } else if curVal == digits[i-1] { 38 | runningSum += curVal 39 | } 40 | } 41 | } 42 | 43 | fmt.Printf("%d\n", runningSum) 44 | } 45 | -------------------------------------------------------------------------------- /2017/day02.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "math" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | var inputFile = flag.String("inputFile", "inputs/day02.input", "Relative file path to use as input.") 13 | var partB = flag.Bool("partB", true, "Whether to use part B logic.") 14 | 15 | func main() { 16 | flag.Parse() 17 | 18 | bytes, err := ioutil.ReadFile(*inputFile) 19 | if err != nil { 20 | fmt.Printf("Could not open file %s because %v.\n", *inputFile, err) 21 | } 22 | contents := string(bytes) 23 | result := 0 24 | for _, line := range strings.Split(contents, "\n") { 25 | if len(line) == 0 { 26 | break 27 | } 28 | largest := -1 29 | smallest := math.MaxInt16 30 | fields := strings.Fields(line) 31 | seen := make([]int, len(fields)) 32 | for i, num := range fields { 33 | n, err := strconv.Atoi(num) 34 | seen[i] = n 35 | if err != nil { 36 | fmt.Printf("Could not parse %s because %v.\n", num, err) 37 | } 38 | if n < smallest { 39 | smallest = n 40 | } 41 | if n > largest { 42 | largest = n 43 | } 44 | if *partB { 45 | for j := 0; j < i; j++ { 46 | dividend, divisor := -1, -1 47 | if seen[j] < n { 48 | dividend = n 49 | divisor = seen[j] 50 | } else { 51 | dividend = seen[j] 52 | divisor = n 53 | } 54 | if dividend%divisor == 0 { 55 | result += dividend / divisor 56 | } 57 | } 58 | } 59 | } 60 | if !*partB { 61 | result += largest - smallest 62 | } 63 | } 64 | fmt.Printf("Result is %d\n", result) 65 | } 66 | -------------------------------------------------------------------------------- /2017/day03.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "math" 7 | ) 8 | 9 | var input = flag.Int("input", 277678, "The puzzle input value.") 10 | var partB = flag.Bool("partB", true, "Whether to use part B logic.") 11 | 12 | type Coord struct { 13 | X, Y int // Cartesian coordinates 14 | } 15 | 16 | type Heading int8 17 | 18 | type Status struct { 19 | Loc Coord 20 | Dir Heading 21 | } 22 | 23 | type Board map[Coord]uint64 24 | 25 | func main() { 26 | flag.Parse() 27 | 28 | s := Status{Coord{}, 2} 29 | seen := make(Board) 30 | for i := 1; i < *input; i++ { 31 | if i == 1 { 32 | seen[s.Loc] = 1 33 | } else { 34 | seen[s.Loc] = seen.SumNeighbors(s.Loc) 35 | if *partB && seen[s.Loc] > uint64(*input) { 36 | break 37 | } 38 | } 39 | 40 | if r := s.RotateCCW().Travel(); seen[r.Loc] == 0 { 41 | s = r 42 | } else { 43 | s = s.Travel() 44 | } 45 | } 46 | seen[s.Loc] = seen.SumNeighbors(s.Loc) 47 | fmt.Printf("%d steps (%d stored at (%d,%d))\n", int64(math.Abs(float64(s.Loc.X))+math.Abs(float64(s.Loc.Y))), seen[s.Loc], s.Loc.X, s.Loc.Y) 48 | } 49 | 50 | func (b Board) SumNeighbors(c Coord) uint64 { 51 | r := b[Coord{c.X + 1, c.Y + 0}] 52 | r += b[Coord{c.X + 1, c.Y + 1}] 53 | r += b[Coord{c.X + 0, c.Y + 1}] 54 | r += b[Coord{c.X - 1, c.Y + 1}] 55 | r += b[Coord{c.X - 1, c.Y + 0}] 56 | r += b[Coord{c.X - 1, c.Y - 1}] 57 | r += b[Coord{c.X + 0, c.Y - 1}] 58 | r += b[Coord{c.X + 1, c.Y - 1}] 59 | 60 | return r 61 | } 62 | 63 | func (s Status) Travel() Status { 64 | var c Coord 65 | switch s.Dir { 66 | case 0: // North 67 | c = Coord{s.Loc.X, s.Loc.Y + 1} 68 | case 1: // East 69 | c = Coord{s.Loc.X + 1, s.Loc.Y} 70 | case 2: // South 71 | c = Coord{s.Loc.X, s.Loc.Y - 1} 72 | case 3: // West 73 | c = Coord{s.Loc.X - 1, s.Loc.Y} 74 | } 75 | return Status{c, s.Dir} 76 | } 77 | 78 | func (s Status) RotateCCW() Status { 79 | return Status{s.Loc, (s.Dir + 3) % 4} 80 | } 81 | -------------------------------------------------------------------------------- /2017/day04.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "sort" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day04.input", "Relative file path to use for input.") 12 | var partB = flag.Bool("partB", true, "Whether to use part B logic.") 13 | 14 | func main() { 15 | flag.Parse() 16 | 17 | bytes, err := ioutil.ReadFile(*inputFile) 18 | if err != nil { 19 | fmt.Printf("Could not open file %s because %v.\n", *inputFile, err) 20 | } 21 | valid := 0 22 | outer: 23 | for _, line := range strings.Split(string(bytes), "\n") { 24 | if len(line) == 0 { 25 | break 26 | } 27 | fields := strings.Fields(line) 28 | seen := make(map[string]bool) 29 | for _, word := range fields { 30 | if *partB { 31 | word = normalize(word) 32 | } 33 | if seen[word] { 34 | continue outer 35 | } 36 | seen[word] = true 37 | } 38 | valid++ 39 | } 40 | fmt.Printf("Found %d valid passphrases.\n", valid) 41 | } 42 | 43 | type ByRune []rune 44 | 45 | func (a ByRune) Len() int { return len(a) } 46 | func (a ByRune) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 47 | func (a ByRune) Less(i, j int) bool { return a[i] < a[j] } 48 | 49 | func normalize(s string) string { 50 | var rs []rune = []rune(s) 51 | sort.Sort(ByRune(rs)) 52 | return string(rs) 53 | } 54 | -------------------------------------------------------------------------------- /2017/day05.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day05.input", "Relative file path to use as input.") 12 | var partB = flag.Bool("partB", true, "Whether to use part B logic.") 13 | 14 | type Inst int 15 | 16 | func main() { 17 | flag.Parse() 18 | 19 | bytes, err := ioutil.ReadFile(*inputFile) 20 | if err != nil { 21 | fmt.Printf("Could not open file %s because %v.\n", *inputFile, err) 22 | } 23 | contents := string(bytes) 24 | lines := strings.Split(contents, "\n") 25 | l := make([]Inst, len(lines)-1) 26 | 27 | for i, line := range lines { 28 | if len(line) == 0 { 29 | break 30 | } 31 | n, err := strconv.Atoi(line) 32 | if err != nil { 33 | fmt.Printf("Could not parse %s because %v.\n", line, err) 34 | return 35 | } 36 | l[i] = Inst(n) 37 | } 38 | 39 | steps := 0 40 | for pos := 0; pos >= 0 && pos < len(l); steps++ { 41 | offset := l[pos] 42 | if *partB && offset >= 3 { 43 | l[pos] += Inst(-1) 44 | } else { 45 | l[pos] += Inst(1) 46 | } 47 | pos += int(offset) 48 | } 49 | 50 | fmt.Printf("Result is %d\n", steps) 51 | } 52 | -------------------------------------------------------------------------------- /2017/day06.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | var input = flag.String("input", "14 0 15 12 11 11 3 5 1 6 8 4 9 1 8 4", "The input to use.") 11 | 12 | const NumBanks int = 16 13 | 14 | type State [NumBanks]int 15 | 16 | func main() { 17 | flag.Parse() 18 | 19 | fields := strings.Fields(*input) 20 | var allocator State 21 | seen := make(map[State]int) 22 | 23 | for i, num := range fields { 24 | n, err := strconv.Atoi(num) 25 | if err != nil { 26 | fmt.Printf("Could not parse %s because %v.\n", num, err) 27 | return 28 | } 29 | allocator[i] = n 30 | } 31 | 32 | i := 1 33 | for ; seen[allocator] == 0; i++ { 34 | seen[allocator] = i 35 | allocator.Reallocate() 36 | } 37 | fmt.Printf("%d steps to reach first repeat and %d steps in loop.\n", i-1, i-seen[allocator]) 38 | } 39 | 40 | func (s *State) Reallocate() { 41 | highIdx := 0 42 | highVal := 0 43 | for i, v := range *s { 44 | if highVal < v { 45 | highVal = v 46 | highIdx = i 47 | } 48 | } 49 | s[highIdx] = 0 50 | 51 | for i := (highIdx + 1) % NumBanks; highVal > 0; i = (i + 1) % NumBanks { 52 | s[i]++ 53 | highVal-- 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /2017/day08.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "math" 8 | "regexp" 9 | "strconv" 10 | "strings" 11 | ) 12 | 13 | var inputFile = flag.String("inputFile", "inputs/day08.input", "Relative file path to use as input.") 14 | 15 | func main() { 16 | flag.Parse() 17 | 18 | bytes, err := ioutil.ReadFile(*inputFile) 19 | if err != nil { 20 | fmt.Printf("Could not open file %s because %v.\n", *inputFile, err) 21 | } 22 | contents := string(bytes) 23 | lines := strings.Split(contents, "\n") 24 | 25 | re := regexp.MustCompile("([a-z]+) (inc|dec) ([0-9-]+) if ([a-z]+) (==|<|<=|>|>=|\\!=) ([0-9-]+)") 26 | 27 | regs := make(map[string]int) 28 | highestCurrentValue := 0 29 | for _, l := range lines { 30 | if len(l) == 0 { 31 | break 32 | } 33 | m := re.FindStringSubmatch(l) 34 | if m == nil { 35 | fmt.Printf("Failed to parse '%s'\n", l) 36 | return 37 | } 38 | r := m[1] 39 | delta := 1 40 | if m[2] == "dec" { 41 | delta = -1 42 | } 43 | amount, err := strconv.Atoi(m[3]) 44 | if err != nil { 45 | fmt.Printf("Failed to parse '%s'\n", l) 46 | return 47 | } 48 | cmp := regs[m[4]] 49 | operand := m[5] 50 | value, err := strconv.Atoi(m[6]) 51 | if err != nil { 52 | fmt.Printf("Failed to parse '%s'\n", l) 53 | return 54 | } 55 | operate := false 56 | switch operand { 57 | case "==": 58 | operate = cmp == value 59 | case "<": 60 | operate = cmp < value 61 | case "<=": 62 | operate = cmp <= value 63 | case ">": 64 | operate = cmp > value 65 | case ">=": 66 | operate = cmp >= value 67 | case "!=": 68 | operate = cmp != value 69 | default: 70 | fmt.Printf("Failed to parse '%s'\n", l) 71 | return 72 | } 73 | if operate { 74 | regs[r] += delta * amount 75 | if regs[r] > highestCurrentValue { 76 | highestCurrentValue = regs[r] 77 | } 78 | } 79 | } 80 | highest := math.MinInt32 81 | for _, v := range regs { 82 | if v > highest { 83 | highest = v 84 | } 85 | } 86 | fmt.Printf("Highest value at end: %d; highest value during execution: %d\n", highest, highestCurrentValue) 87 | } 88 | -------------------------------------------------------------------------------- /2017/day09.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strings" 8 | ) 9 | 10 | var inputFile = flag.String("inputFile", "inputs/day09.input", "Relative file path to use as input.") 11 | 12 | func main() { 13 | flag.Parse() 14 | 15 | bytes, err := ioutil.ReadFile(*inputFile) 16 | if err != nil { 17 | fmt.Printf("Could not open file %s because %v.\n", *inputFile, err) 18 | } 19 | contents := string(bytes[:len(bytes)-1]) 20 | for _, l := range strings.Split(contents, "\n") { 21 | score, garbage := process(l) 22 | fmt.Printf("Score for input with length %d: %d with %d removed garbage\n", len(l), score, garbage) 23 | } 24 | } 25 | 26 | func process(s string) (int, int) { 27 | totalScore := 0 28 | groupScore := 0 29 | removedGarbage := 0 30 | var inGarbage, escaped bool 31 | for _, c := range s { 32 | if escaped { 33 | escaped = false 34 | continue 35 | } 36 | if inGarbage { 37 | switch c { 38 | case '>': 39 | inGarbage = false 40 | case '!': 41 | escaped = true 42 | default: 43 | removedGarbage++ 44 | } 45 | continue 46 | } 47 | // We are in a non-garbage group. 48 | switch c { 49 | case '<': 50 | inGarbage = true 51 | case '{': 52 | groupScore++ 53 | case '}': 54 | totalScore += groupScore 55 | groupScore-- 56 | case ',': 57 | // Nothing needs to happen here. 58 | case '!': 59 | escaped = true 60 | default: 61 | // Nothing needs to happen here. 62 | } 63 | } 64 | return totalScore, removedGarbage 65 | } 66 | -------------------------------------------------------------------------------- /2017/day10.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "knot" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var input = flag.String("input", "3,4,1,5", "The input to the problem.") 12 | var length = flag.Int("length", 5, "The number of elements in the circular list.") 13 | var rounds = flag.Int("rounds", 1, "The number of rounds to perform.") 14 | var partB = flag.Bool("partB", true, "Whether to perform the ASCII conversion of part B.") 15 | 16 | func main() { 17 | flag.Parse() 18 | 19 | var ls []int 20 | if !*partB { 21 | lengths := strings.Split(*input, ",") 22 | ls = make([]int, len(lengths)) 23 | for i, l := range lengths { 24 | length, err := strconv.Atoi(l) 25 | if err != nil { 26 | fmt.Printf("Failed to parse input.\n") 27 | return 28 | } 29 | ls[i] = length 30 | } 31 | } else { 32 | ls = knot.Key(*input) 33 | } 34 | 35 | final := knot.Hash(*length, *rounds, ls) 36 | 37 | if !*partB { 38 | knot.Debug(final) 39 | a, b := knot.Get(final), knot.Get(final.Next()) 40 | fmt.Printf("Answer: %d*%d = %d\n", a, b, a*b) 41 | return 42 | } 43 | 44 | d := knot.Densify(final) 45 | for _, v := range d { 46 | fmt.Printf("%02x", v) 47 | } 48 | fmt.Println() 49 | } 50 | -------------------------------------------------------------------------------- /2017/day12.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day12.input", "Relative file path to use as input.") 12 | 13 | type Program struct { 14 | Neighbors []int 15 | } 16 | type Progs map[int]Program 17 | 18 | func main() { 19 | flag.Parse() 20 | 21 | bytes, err := ioutil.ReadFile(*inputFile) 22 | if err != nil { 23 | fmt.Printf("Could not open file %s because %v.\n", *inputFile, err) 24 | } 25 | contents := string(bytes) 26 | lines := strings.Split(contents, "\n") 27 | programs := make(Progs) 28 | 29 | for _, l := range lines { 30 | if len(l) == 0 { 31 | break 32 | } 33 | parts := strings.Split(l, " <-> ") 34 | 35 | n, err := strconv.Atoi(parts[0]) 36 | if err != nil { 37 | fmt.Printf("Failed to parse program number in '%s'\n", l) 38 | return 39 | } 40 | 41 | neighbors := strings.Split(parts[1], ", ") 42 | neighs := make([]int, len(neighbors)) 43 | for i, v := range neighbors { 44 | neigh, err := strconv.Atoi(v) 45 | if err != nil { 46 | fmt.Printf("Failed to parse neighbor number in '%s'\n", l) 47 | return 48 | } 49 | neighs[i] = neigh 50 | } 51 | programs[n] = Program{neighs} 52 | } 53 | visited := make(map[int]bool) 54 | programs.DeepTraverse(0, visited) 55 | fmt.Printf("Group with 0 contains %d nodes.\n", len(visited)) 56 | 57 | groups := 1 58 | for k := range programs { 59 | if visited[k] { 60 | continue 61 | } 62 | programs.DeepTraverse(k, visited) 63 | groups++ 64 | } 65 | fmt.Printf("Total groups: %d\n", groups) 66 | } 67 | 68 | func (p Progs) DeepTraverse(i int, visited map[int]bool) { 69 | prog := p[i] 70 | if visited[i] { 71 | return 72 | } 73 | visited[i] = true 74 | for _, n := range prog.Neighbors { 75 | p.DeepTraverse(n, visited) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /2017/day14.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "knot" 7 | ) 8 | 9 | var input = flag.String("input", "flqrgnkx", "The input to use.") 10 | 11 | func main() { 12 | flag.Parse() 13 | 14 | set := 0 15 | var bitField [128][128]bool 16 | for i := 0; i < 128; i++ { 17 | key := fmt.Sprintf("%s-%d", *input, i) 18 | r := knot.Densify(knot.Hash(256, 64, knot.Key(key))) 19 | bitField[i] = toBits(r) 20 | set += countBits(bitField[i]) 21 | } 22 | 23 | fmt.Printf("Found %d set bits.\n", set) 24 | 25 | var addedToRegions [128][128]bool 26 | 27 | regions := 0 28 | for i := 0; i < 128; i++ { 29 | for j := 0; j < 128; j++ { 30 | if addedToRegions[i][j] || !bitField[i][j] { 31 | // Only consider as seeds set tiles that are not part of regions. 32 | continue 33 | } 34 | regions++ 35 | expandRegion(i, j, &bitField, &addedToRegions) 36 | } 37 | } 38 | fmt.Printf("Found %d non-overlapping regions.\n", regions) 39 | } 40 | 41 | func expandRegion(x, y int, bits, used *[128][128]bool) { 42 | used[x][y] = true 43 | // Try to expand up, down, left, and right. 44 | if x > 0 && bits[x-1][y] && !used[x-1][y] { 45 | expandRegion(x-1, y, bits, used) 46 | } 47 | if x < 127 && bits[x+1][y] && !used[x+1][y] { 48 | expandRegion(x+1, y, bits, used) 49 | } 50 | if y > 0 && bits[x][y-1] && !used[x][y-1] { 51 | expandRegion(x, y-1, bits, used) 52 | } 53 | if y < 127 && bits[x][y+1] && !used[x][y+1] { 54 | expandRegion(x, y+1, bits, used) 55 | } 56 | } 57 | 58 | func toBits(d []int) [128]bool { 59 | var ret [128]bool 60 | for j, v := range d { 61 | lower := v & 15 62 | upper := v >> 4 63 | 64 | ret[8*j+0] = upper&8 > 0 65 | ret[8*j+1] = upper&4 > 0 66 | ret[8*j+2] = upper&2 > 0 67 | ret[8*j+3] = upper&1 > 0 68 | 69 | ret[8*j+4] = lower&8 > 0 70 | ret[8*j+5] = lower&4 > 0 71 | ret[8*j+6] = lower&2 > 0 72 | ret[8*j+7] = lower&1 > 0 73 | } 74 | return ret 75 | } 76 | 77 | func display(array [128]bool) string { 78 | ret := "" 79 | for _, v := range array { 80 | if v { 81 | ret += "#" 82 | } else { 83 | ret += "." 84 | } 85 | } 86 | return ret 87 | } 88 | 89 | func countBits(array [128]bool) int { 90 | ret := 0 91 | for _, v := range array { 92 | if v { 93 | ret++ 94 | } 95 | } 96 | return ret 97 | } 98 | -------------------------------------------------------------------------------- /2017/day15.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | ) 7 | 8 | var seedA = flag.Int("seedA", 65, "The first seed to use.") 9 | var seedB = flag.Int("seedB", 8921, "The second seed to use.") 10 | 11 | var factorA = flag.Int("factorA", 16807, "The first multiplier to use.") 12 | var factorB = flag.Int("factorB", 48271, "The second multiplier to use.") 13 | 14 | var modulus = flag.Int("modulus", 2147483647, "The modulus to use.") 15 | 16 | var cycles = flag.Int("cycles", 40000000, "The number of cycles to check for matches in.") 17 | 18 | var picky = flag.Bool("picky", true, "Whether to only emit values with certain divisibility.") 19 | 20 | type State struct { 21 | A, B uint64 22 | } 23 | 24 | func main() { 25 | flag.Parse() 26 | 27 | initial := State{uint64(*seedA), uint64(*seedB)} 28 | found := 0 29 | s := initial 30 | for i := 0; i < *cycles; i++ { 31 | s = s.cycle() 32 | if s.check() { 33 | found++ 34 | } 35 | } 36 | fmt.Printf("Found %d matches.\n", found) 37 | } 38 | 39 | func (s State) cycle() State { 40 | if !*picky { 41 | return State{ 42 | A: (s.A * uint64(*factorA)) % uint64(*modulus), 43 | B: (s.B * uint64(*factorB)) % uint64(*modulus), 44 | } 45 | } else { 46 | a := s.A 47 | b := s.B 48 | for { 49 | a = (a * uint64(*factorA)) % uint64(*modulus) 50 | if a%4 == 0 { 51 | break 52 | } 53 | } 54 | for { 55 | b = (b * uint64(*factorB)) % uint64(*modulus) 56 | if b%8 == 0 { 57 | break 58 | } 59 | } 60 | return State{a, b} 61 | } 62 | } 63 | 64 | func (s State) check() bool { 65 | return ((s.A^s.B)&((1<<16)-1) == 0) 66 | } 67 | -------------------------------------------------------------------------------- /2017/day17.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "container/ring" 5 | "flag" 6 | "fmt" 7 | ) 8 | 9 | var steps = flag.Int("steps", 3, "The number of steps to take between inserts.") 10 | var iterations = flag.Int("iterations", 2017, "The number of insertions to perform.") 11 | 12 | func main() { 13 | flag.Parse() 14 | 15 | start := ring.New(1) 16 | Write(start, 0) 17 | 18 | r := start 19 | for i := 1; i < *iterations+1; i++ { 20 | for s := 0; s < *steps; s++ { 21 | r = r.Next() 22 | } 23 | insert := ring.New(1) 24 | Write(insert, i) 25 | r = r.Link(insert).Prev() 26 | } 27 | fmt.Printf("Value after 2017 is %d.\n", Read(r.Next())) 28 | 29 | fmt.Printf("Value after 0 is %d.\n", Read(start.Next())) 30 | } 31 | 32 | func Read(r *ring.Ring) int { 33 | return r.Value.(int) 34 | } 35 | func Write(r *ring.Ring, i int) { 36 | r.Value = i 37 | } 38 | -------------------------------------------------------------------------------- /2017/day19.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strings" 8 | ) 9 | 10 | var inputFile = flag.String("inputFile", "inputs/day19.input", "Relative file path to use as input.") 11 | 12 | type Loc struct { 13 | Passable, ChangeDir bool 14 | Letter rune 15 | } 16 | 17 | func main() { 18 | flag.Parse() 19 | 20 | bytes, err := ioutil.ReadFile(*inputFile) 21 | if err != nil { 22 | fmt.Printf("Could not open file %s because %v.\n", *inputFile, err) 23 | } 24 | contents := string(bytes) 25 | lines := strings.Split(contents, "\n") 26 | 27 | height := len(lines) 28 | width := len(lines[0]) 29 | 30 | board := make([][]Loc, height) 31 | for y := 0; y < height; y++ { 32 | board[y] = make([]Loc, width) 33 | } 34 | 35 | for y, l := range lines { 36 | if len(l) == 0 { 37 | break 38 | } 39 | for x, c := range l { 40 | switch c { 41 | case ' ': 42 | board[y][x] = Loc{false, false, ' '} 43 | case '+': 44 | board[y][x] = Loc{true, true, ' '} 45 | case '|': 46 | fallthrough 47 | case '-': 48 | board[y][x] = Loc{true, false, ' '} 49 | default: 50 | board[y][x] = Loc{true, false, c} 51 | } 52 | } 53 | } 54 | 55 | var packetX, packetY int 56 | // 0 = N, 1 = E, 2 = S, 3 = W 57 | packetDir := 2 58 | for x, c := range board[0] { 59 | if c.Passable { 60 | packetX = x 61 | break 62 | } 63 | } 64 | 65 | for steps := 1; ; steps++ { 66 | switch packetDir { 67 | case 0: 68 | packetY-- 69 | case 1: 70 | packetX++ 71 | case 2: 72 | packetY++ 73 | case 3: 74 | packetX-- 75 | } 76 | if board[packetY][packetX].ChangeDir { 77 | switch packetDir { 78 | case 0: 79 | fallthrough 80 | case 2: 81 | if board[packetY][packetX+1].Passable { 82 | packetDir = 1 83 | } else { 84 | packetDir = 3 85 | } 86 | case 1: 87 | fallthrough 88 | case 3: 89 | if board[packetY+1][packetX].Passable { 90 | packetDir = 2 91 | } else { 92 | packetDir = 0 93 | } 94 | } 95 | } 96 | 97 | if board[packetY][packetX].Letter != ' ' { 98 | fmt.Printf("%c", board[packetY][packetX].Letter) 99 | } 100 | if !board[packetY][packetX].Passable { 101 | fmt.Println() 102 | fmt.Printf("Steps traversed: %d\n", steps) 103 | return 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /2017/day22.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strings" 8 | ) 9 | 10 | var inputFile = flag.String("inputFile", "inputs/day22.input", "Relative file path to use as input.") 11 | var cycles = flag.Int("cycles", 5, "The number of cycles to simulate.") 12 | var partB = flag.Bool("partB", true, "Use part B logic with 4 states.") 13 | 14 | type Coord struct { 15 | X, Y int 16 | } 17 | type Board map[Coord]byte 18 | type WorkerState struct { 19 | Environment Board 20 | Loc Coord 21 | Dir int 22 | } 23 | 24 | func main() { 25 | flag.Parse() 26 | 27 | bytes, err := ioutil.ReadFile(*inputFile) 28 | if err != nil { 29 | fmt.Printf("Could not open file %s because %v.\n", *inputFile, err) 30 | } 31 | contents := string(bytes[:len(bytes)-1]) 32 | lines := strings.Split(contents, "\n") 33 | 34 | b := make(Board) 35 | 36 | // always an odd number. coordinates will range from [-height/2..height/2] 37 | height := len(lines) 38 | width := len(lines[0]) 39 | 40 | // Convert input file into pattern of initial infection. 41 | for r, l := range lines { 42 | for c, v := range l { 43 | if v == '#' { 44 | loc := Coord{ 45 | X: c - (width / 2), 46 | Y: (height / 2) - r, 47 | } 48 | b[loc] = 2 49 | } 50 | } 51 | } 52 | 53 | carrier := WorkerState{b, Coord{0, 0}, 0} 54 | 55 | spread := 0 56 | for i := 0; i < *cycles; i++ { 57 | if carrier.Tick() { 58 | spread++ 59 | } 60 | } 61 | fmt.Println(spread) 62 | } 63 | 64 | func (ws *WorkerState) Tick() bool { 65 | infected := false 66 | 67 | // Turn 68 | switch ws.Environment[ws.Loc] { 69 | case 0: 70 | ws.Dir = (ws.Dir + 3) % 4 71 | case 1: 72 | // Nothing happens 73 | case 2: 74 | ws.Dir = (ws.Dir + 1) % 4 75 | case 3: 76 | ws.Dir = (ws.Dir + 2) % 4 77 | } 78 | 79 | // Toggle 80 | i := 2 81 | if *partB { 82 | i = 1 83 | } 84 | ws.Environment[ws.Loc] = (ws.Environment[ws.Loc] + byte(i)) % 4 85 | 86 | if ws.Environment[ws.Loc] == 2 { 87 | infected = true 88 | } 89 | 90 | switch ws.Dir { 91 | case 0: 92 | ws.Loc = Coord{ws.Loc.X, ws.Loc.Y + 1} 93 | case 1: 94 | ws.Loc = Coord{ws.Loc.X + 1, ws.Loc.Y} 95 | case 2: 96 | ws.Loc = Coord{ws.Loc.X, ws.Loc.Y - 1} 97 | case 3: 98 | ws.Loc = Coord{ws.Loc.X - 1, ws.Loc.Y} 99 | } 100 | 101 | return infected 102 | } 103 | -------------------------------------------------------------------------------- /2017/day23b.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | composites := 0 9 | 10 | for b := 106700; b <= 123700; b += 17 { 11 | for d := 2; d*d < b; d++ { 12 | if b%d == 0 { 13 | composites++ 14 | break 15 | } 16 | } 17 | } 18 | 19 | fmt.Println(composites) 20 | } 21 | -------------------------------------------------------------------------------- /2017/day23b_naive.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | composites := 0 9 | 10 | for b := 106700; b != 123700; b += 17 { 11 | composite := false 12 | for d := 2; d < b; d++ { 13 | for e := 2; e < b; e++ { 14 | if d*e == b { 15 | composite = true 16 | } 17 | } 18 | } 19 | if composite { 20 | composites++ 21 | } 22 | } 23 | 24 | fmt.Println(composites) 25 | } 26 | -------------------------------------------------------------------------------- /2017/day25.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Tape map[int]bool 8 | type Machine struct { 9 | S byte 10 | T Tape 11 | P int 12 | } 13 | 14 | func main() { 15 | m := Machine{'A', make(Tape), 0} 16 | for i := 0; i < 12523873; i++ { 17 | m.Iterate() 18 | } 19 | fmt.Println(len(m.T)) 20 | } 21 | 22 | func (m *Machine) Iterate() { 23 | switch m.S { 24 | case 'A': 25 | if !m.T[m.P] { 26 | m.T[m.P] = true 27 | m.P++ 28 | m.S = 'B' 29 | } else { 30 | m.T[m.P] = true 31 | m.P-- 32 | m.S = 'E' 33 | } 34 | case 'B': 35 | if !m.T[m.P] { 36 | m.T[m.P] = true 37 | m.P++ 38 | m.S = 'C' 39 | } else { 40 | m.T[m.P] = true 41 | m.P++ 42 | m.S = 'F' 43 | } 44 | case 'C': 45 | if !m.T[m.P] { 46 | m.T[m.P] = true 47 | m.P-- 48 | m.S = 'D' 49 | } else { 50 | delete(m.T, m.P) 51 | m.P++ 52 | m.S = 'B' 53 | } 54 | case 'D': 55 | if !m.T[m.P] { 56 | m.T[m.P] = true 57 | m.P++ 58 | m.S = 'E' 59 | } else { 60 | delete(m.T, m.P) 61 | m.P-- 62 | m.S = 'C' 63 | } 64 | case 'E': 65 | if !m.T[m.P] { 66 | m.T[m.P] = true 67 | m.P-- 68 | m.S = 'A' 69 | } else { 70 | delete(m.T, m.P) 71 | m.P++ 72 | m.S = 'D' 73 | } 74 | case 'F': 75 | if !m.T[m.P] { 76 | m.T[m.P] = true 77 | m.P++ 78 | m.S = 'A' 79 | } else { 80 | m.T[m.P] = true 81 | m.P++ 82 | m.S = 'C' 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /2017/src/knot/hash.go: -------------------------------------------------------------------------------- 1 | package knot 2 | 3 | import ( 4 | "container/ring" 5 | "fmt" 6 | ) 7 | 8 | func Key(input string) []int { 9 | ls := make([]int, len(input)+5) 10 | for i, l := range input { 11 | ls[i] = int(l) 12 | } 13 | addedLengths := [5]int{17, 31, 73, 47, 23} 14 | for i := 0; i < len(addedLengths); i++ { 15 | ls[i+len(input)] = addedLengths[i] 16 | } 17 | return ls 18 | } 19 | 20 | func Hash(length, rounds int, key []int) *ring.Ring { 21 | loop := ring.New(length) 22 | for i := 0; i < length; i++ { 23 | set(loop, i) 24 | loop = loop.Next() 25 | } 26 | 27 | pos := loop 28 | skip := 0 29 | offset := 0 30 | 31 | for i := 0; i < rounds; i++ { 32 | pos = round(pos, length, key, &skip, &offset) 33 | } 34 | 35 | final := pos.Move(-offset) 36 | return final 37 | } 38 | 39 | func Densify(final *ring.Ring) []int { 40 | length := final.Len() / 16 41 | ret := make([]int, length) 42 | for i := 0; i < length; i++ { 43 | intermediate := 0 44 | for j := 0; j < 16; j++ { 45 | digit := Get(final) 46 | final = final.Next() 47 | intermediate ^= digit 48 | } 49 | ret[i] = intermediate 50 | } 51 | return ret 52 | } 53 | 54 | // round performs a round of the "hashing" given the specified constraints. 55 | // Note that this modifies the input ring (danger will robinson) 56 | func round(r *ring.Ring, length int, ls []int, skip, offset *int) *ring.Ring { 57 | pos := r 58 | for _, n := range ls { 59 | if n != 0 { 60 | leftover := pos.Move(n) 61 | working := pos.Prev().Unlink(n) 62 | reversed := reverse(working) 63 | 64 | if n == length { 65 | pos = reversed 66 | } else { 67 | pos = leftover.Prev().Link(reversed) 68 | } 69 | } 70 | pos = pos.Move(*skip) 71 | *offset = (*offset + *skip + n) % length 72 | *skip++ 73 | } 74 | return pos 75 | } 76 | 77 | func set(r *ring.Ring, v int) { 78 | r.Value = v 79 | } 80 | 81 | func Get(r *ring.Ring) int { 82 | return r.Value.(int) 83 | } 84 | 85 | func Debug(r *ring.Ring) { 86 | pos := r 87 | for i := 0; i < r.Len(); i++ { 88 | fmt.Printf("%d,", Get(pos)) 89 | pos = pos.Next() 90 | } 91 | fmt.Println() 92 | } 93 | 94 | func reverse(r *ring.Ring) *ring.Ring { 95 | // [A], B, C, D -> [D], C, B, A 96 | pos := r.Prev() 97 | ret := ring.New(r.Len()) 98 | for i := 0; i < r.Len(); i++ { 99 | set(ret, Get(pos)) 100 | ret = ret.Next() 101 | pos = pos.Prev() 102 | } 103 | return ret 104 | } 105 | -------------------------------------------------------------------------------- /2018/.gitignore: -------------------------------------------------------------------------------- 1 | *.input 2 | *.swp 3 | -------------------------------------------------------------------------------- /2018/day01.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day01.input", "Relative file path to use as input.") 12 | var partB = flag.Bool("partB", false, "Whether to use the Part B logic.") 13 | 14 | func main() { 15 | flag.Parse() 16 | bytes, err := ioutil.ReadFile(*inputFile) 17 | if err != nil { 18 | return 19 | } 20 | contents := string(bytes) 21 | result := 0 22 | seen := make(map[int]bool) 23 | outer: 24 | for { 25 | for _, line := range strings.Split(contents, "\n") { 26 | if len(line) == 0 { 27 | break 28 | } 29 | offset, _ := strconv.Atoi(line) 30 | result += offset 31 | if seen[result] && *partB { 32 | fmt.Printf("Saw %d twice.\n", result) 33 | break outer 34 | } 35 | seen[result] = true 36 | } 37 | if !*partB { 38 | fmt.Printf("%d\n", result) 39 | break 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /2018/day02.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "os" 8 | ) 9 | 10 | var inputFile = flag.String("inputFile", "inputs/day02.input", "Relative file path to use as input.") 11 | 12 | func main() { 13 | flag.Parse() 14 | f, err := os.Open(*inputFile) 15 | if err != nil { 16 | return 17 | } 18 | defer f.Close() 19 | 20 | r := bufio.NewReader(f) 21 | triples := 0 22 | doubles := 0 23 | memo := make(map[int]map[string]bool) 24 | for { 25 | l, err := r.ReadString('\n') 26 | if err != nil || len(l) == 0 { 27 | break 28 | } 29 | l = l[:len(l)-1] 30 | 31 | for pos := range l { 32 | if memo[pos] == nil { 33 | memo[pos] = make(map[string]bool) 34 | } 35 | trunc := l[0:pos] + l[pos+1:] 36 | if memo[pos][trunc] { 37 | fmt.Printf("Shared characters: %s\n", trunc) 38 | break 39 | } 40 | memo[pos][trunc] = true 41 | } 42 | 43 | seen := make(map[rune]int) 44 | for _, c := range l { 45 | seen[c] += 1 46 | } 47 | seen3 := false 48 | seen2 := false 49 | for _, v := range seen { 50 | if v == 3 { 51 | seen3 = true 52 | } else if v == 2 { 53 | seen2 = true 54 | } 55 | } 56 | if seen3 { 57 | triples += 1 58 | } 59 | if seen2 { 60 | doubles += 1 61 | } 62 | } 63 | 64 | result := triples * doubles 65 | fmt.Printf("Result is %d\n", result) 66 | } 67 | -------------------------------------------------------------------------------- /2018/day03_simple.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "os" 8 | "regexp" 9 | "strconv" 10 | ) 11 | 12 | var inputFile = flag.String("inputFile", "inputs/day03.input", "Relative file path to use as input.") 13 | 14 | var reg = regexp.MustCompile("#([0-9]+) @ ([0-9]+),([0-9]+): ([0-9]+)x([0-9]+)") 15 | 16 | type Claim struct { 17 | top, bottom, left, right int // inclusive 18 | } 19 | 20 | type Coord struct { 21 | X, Y int 22 | } 23 | 24 | func main() { 25 | flag.Parse() 26 | f, err := os.Open(*inputFile) 27 | if err != nil { 28 | return 29 | } 30 | defer f.Close() 31 | 32 | claims := make(map[int]Claim) 33 | 34 | r := bufio.NewReader(f) 35 | for { 36 | l, err := r.ReadString('\n') 37 | if err != nil || len(l) == 0 { 38 | break 39 | } 40 | l = l[:len(l)-1] 41 | parsed := reg.FindStringSubmatch(l) 42 | claim, err := strconv.Atoi(parsed[1]) 43 | offsetX, err := strconv.Atoi(parsed[2]) 44 | offsetY, err := strconv.Atoi(parsed[3]) 45 | sizeX, err := strconv.Atoi(parsed[4]) 46 | sizeY, err := strconv.Atoi(parsed[5]) 47 | claims[claim] = Claim{offsetY, offsetY + sizeY - 1, offsetX, offsetX + sizeX - 1} 48 | } 49 | 50 | occupied := make(map[Coord][]int) 51 | overlaps := make(map[Coord]bool) 52 | conflicted := make(map[int]bool) 53 | for k, v := range claims { 54 | for x := v.left; x <= v.right; x++ { 55 | for y := v.top; y <= v.bottom; y++ { 56 | if occupied[Coord{x, y}] != nil { 57 | overlaps[Coord{x, y}] = true 58 | occupied[Coord{x, y}] = append(occupied[Coord{x, y}], k) 59 | for _, id := range occupied[Coord{x, y}] { 60 | conflicted[id] = true 61 | } 62 | } else { 63 | occupied[Coord{x, y}] = []int{k} 64 | } 65 | } 66 | } 67 | } 68 | 69 | fmt.Printf("Result is %d\n", len(overlaps)) 70 | for k := range claims { 71 | if !conflicted[k] { 72 | fmt.Printf("Not conflicted: %d\n", k) 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /2018/day05.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "os" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day05.input", "Relative file path to use as input.") 12 | var partB = flag.Bool("partB", false, "Whether to use the Part B logic.") 13 | 14 | func main() { 15 | flag.Parse() 16 | f, err := os.Open(*inputFile) 17 | if err != nil { 18 | return 19 | } 20 | defer f.Close() 21 | 22 | r := bufio.NewReader(f) 23 | l, err := r.ReadString('\n') 24 | l = l[:len(l)-1] 25 | 26 | if !*partB { 27 | unmodified := react(l) 28 | result := len(unmodified) 29 | fmt.Printf("Result is %d\n", result) 30 | } else { 31 | shortest := len(l) 32 | for c := 'A'; c <= 'Z'; c++ { 33 | second := c + 32 34 | modified := strings.Replace(l, string([]rune{second}), "", -1) 35 | modified = strings.Replace(modified, string([]rune{c}), "", -1) 36 | candidate := len(react(modified)) 37 | if candidate < shortest { 38 | shortest = candidate 39 | } 40 | } 41 | fmt.Printf("Result is %d\n", shortest) 42 | } 43 | } 44 | 45 | func react(l string) string { 46 | current := l 47 | for { 48 | transformed := false 49 | prev := ' ' 50 | for i, c := range current { 51 | if c-prev == 32 || prev-c == 32 { 52 | transformed = true 53 | current = current[0:i-1] + current[i+1:] 54 | break 55 | } 56 | prev = c 57 | } 58 | if !transformed { 59 | break 60 | } 61 | } 62 | return current 63 | } 64 | -------------------------------------------------------------------------------- /2018/day09.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "container/ring" 5 | "flag" 6 | "fmt" 7 | ) 8 | 9 | var numPlayers = flag.Int("numPlayers", 9, "The number of players.") 10 | var maxMarble = flag.Int("maxMarble", 25, "The maximum marble score.") 11 | 12 | func main() { 13 | flag.Parse() 14 | 15 | scores := make([]int, *numPlayers) 16 | pidx := -1 17 | current := ring.New(1) 18 | current.Value = 0 19 | 20 | for i := 1; i <= *maxMarble; i++ { 21 | pidx = (pidx + 1) % *numPlayers 22 | if i%23 != 0 { 23 | current = current.Next() 24 | added := ring.New(1) 25 | added.Value = i 26 | current.Link(added) 27 | current = current.Next() 28 | } else { 29 | scores[pidx] += i 30 | for v := 0; v < 7; v++ { 31 | current = current.Prev() 32 | } 33 | current = current.Prev() 34 | removed := current.Unlink(1) 35 | current = current.Next() 36 | scores[pidx] += removed.Value.(int) 37 | } 38 | } 39 | 40 | result := 0 41 | for _, v := range scores { 42 | if v > result { 43 | result = v 44 | } 45 | } 46 | fmt.Printf("Result is %d\n", result) 47 | } 48 | -------------------------------------------------------------------------------- /2018/day10.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "os" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | var inputFile = flag.String("inputFile", "inputs/day10.input", "Relative file path to use as input.") 13 | 14 | type Particle struct { 15 | PosX, PosY int 16 | VelX, VelY int 17 | } 18 | 19 | type Coord struct { 20 | X, Y int 21 | } 22 | 23 | func (p *Particle) Tick() Coord { 24 | p.PosX += p.VelX 25 | p.PosY += p.VelY 26 | return Coord{p.PosX, p.PosY} 27 | } 28 | 29 | func main() { 30 | flag.Parse() 31 | f, err := os.Open(*inputFile) 32 | if err != nil { 33 | return 34 | } 35 | defer f.Close() 36 | 37 | particles := make(map[*Particle]bool) 38 | r := bufio.NewReader(f) 39 | for { 40 | l, err := r.ReadString('\n') 41 | if err != nil || len(l) == 0 { 42 | break 43 | } 44 | l = l[:len(l)-1] 45 | posX, _ := strconv.Atoi(strings.TrimLeft(l[10:16], " ")) 46 | posY, _ := strconv.Atoi(strings.TrimLeft(l[18:24], " ")) 47 | velX, _ := strconv.Atoi(strings.TrimLeft(l[36:38], " ")) 48 | velY, _ := strconv.Atoi(strings.TrimLeft(l[40:42], " ")) 49 | p := Particle{posX, posY, velX, velY} 50 | particles[&p] = true 51 | } 52 | 53 | for secs := 1; ; secs++ { 54 | seenX := make(map[int]int) 55 | seenY := make(map[int]int) 56 | picture := make(map[Coord]bool) 57 | minX := 100000 58 | maxX := -100000 59 | minY := 100000 60 | maxY := -100000 61 | for k, _ := range particles { 62 | pos := k.Tick() 63 | seenX[pos.X] += 1 64 | seenY[pos.Y] += 1 65 | if minX > pos.X { 66 | minX = pos.X 67 | } 68 | if maxX < pos.X { 69 | maxX = pos.X 70 | } 71 | if minY > pos.Y { 72 | minY = pos.Y 73 | } 74 | if maxY < pos.Y { 75 | maxY = pos.Y 76 | } 77 | picture[pos] = true 78 | } 79 | candidateX := false 80 | candidateY := false 81 | for _, v := range seenX { 82 | if v > 20 { 83 | candidateX = true 84 | } 85 | } 86 | for _, v := range seenY { 87 | if v > 20 { 88 | candidateY = true 89 | } 90 | } 91 | if candidateX && candidateY { 92 | for y := minY; y <= maxY; y++ { 93 | for x := minX; x <= maxX; x++ { 94 | if picture[Coord{x, y}] { 95 | fmt.Printf("#") 96 | } else { 97 | fmt.Printf(".") 98 | } 99 | } 100 | fmt.Println() 101 | } 102 | fmt.Printf("Seen in %d secs\n", secs) 103 | break 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /2018/day11.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "sync" 7 | ) 8 | 9 | var serial = flag.Int("serial", 42, "The serial number of the power grid.") 10 | var partB = flag.Bool("partB", false, "Whether to use the Part B logic.") 11 | 12 | type Coord struct { 13 | X, Y int 14 | } 15 | type Square struct { 16 | X, Y, L int 17 | } 18 | 19 | func main() { 20 | flag.Parse() 21 | 22 | cells := make(map[Coord]int) 23 | for x := 1; x <= 300; x++ { 24 | for y := 1; y <= 300; y++ { 25 | rackID := x + 10 26 | level := (rackID * y) + *serial 27 | level *= rackID 28 | level %= 1000 29 | level /= 100 30 | level -= 5 31 | cells[Coord{x, y}] = level 32 | } 33 | } 34 | 35 | max := -1000 36 | var highest *Square 37 | var memo sync.Map 38 | if *partB { 39 | for squareSize := 1; squareSize <= 300; squareSize++ { 40 | score := evalSquare(cells, squareSize, &memo, max, &highest) 41 | if score > max { 42 | max = score 43 | } 44 | } 45 | } else { 46 | evalSquare(cells, 1, &memo, -1000, &highest) 47 | evalSquare(cells, 2, &memo, -1000, &highest) 48 | evalSquare(cells, 3, &memo, -1000, &highest) 49 | } 50 | 51 | fmt.Printf("Result is %d,%d,%d\n", highest.X, highest.Y, highest.L) 52 | } 53 | 54 | func evalSquare(cells map[Coord]int, squareSize int, memo *sync.Map, max int, highest **Square) int { 55 | ret := max 56 | var mtx sync.Mutex 57 | var wg sync.WaitGroup 58 | for x := 1; x <= 301-squareSize; x++ { 59 | wg.Add(1) 60 | go evalSquareInner(&mtx, x, &ret, cells, squareSize, memo, max, highest, &wg) 61 | } 62 | wg.Wait() 63 | return ret 64 | } 65 | 66 | func evalSquareInner(mtx *sync.Mutex, x int, ret *int, cells map[Coord]int, squareSize int, memo *sync.Map, max int, highest **Square, wg *sync.WaitGroup) { 67 | for y := 1; y <= 301-squareSize; y++ { 68 | cached, ok := memo.Load(Square{x, y, squareSize - 1}) 69 | total := 0 70 | if ok { 71 | total = cached.(int) 72 | } 73 | 74 | for offsetX := 0; offsetX < squareSize; offsetX++ { 75 | total += cells[Coord{x + offsetX, y + squareSize - 1}] 76 | } 77 | for offsetY := 0; offsetY < squareSize; offsetY++ { 78 | total += cells[Coord{x + squareSize - 1, y + offsetY}] 79 | } 80 | total -= cells[Coord{x + squareSize - 1, y + squareSize - 1}] 81 | 82 | memo.Store(Square{x, y, squareSize}, total) 83 | 84 | mtx.Lock() 85 | if total > *ret { 86 | *ret = total 87 | *highest = &Square{x, y, squareSize} 88 | } 89 | mtx.Unlock() 90 | } 91 | wg.Done() 92 | } 93 | -------------------------------------------------------------------------------- /2018/day19.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "asm" 5 | "bufio" 6 | "flag" 7 | "fmt" 8 | "os" 9 | "strconv" 10 | "strings" 11 | ) 12 | 13 | var inputFile = flag.String("inputFile", "inputs/day19.input", "Relative file path to use as input.") 14 | var partB = flag.Bool("partB", false, "Whether to use the Part B logic.") 15 | 16 | func main() { 17 | flag.Parse() 18 | f, err := os.Open(*inputFile) 19 | if err != nil { 20 | return 21 | } 22 | defer f.Close() 23 | 24 | reader := bufio.NewReader(f) 25 | 26 | instructions := make([]asm.Instruction, 0) 27 | ipreg := -1 28 | 29 | for idx := -1; ; idx++ { 30 | l, err := reader.ReadString('\n') 31 | if err != nil || len(l) == 0 { 32 | break 33 | } 34 | l = l[:len(l)-1] 35 | 36 | if idx == -1 { 37 | // Read the instruction pointer register number. 38 | ipreg, _ = strconv.Atoi(strings.Split(l, " ")[1]) 39 | continue 40 | } 41 | 42 | rawInstr := strings.Split(l, " ") 43 | var instr asm.Instruction 44 | instr.F = asm.AllOps[rawInstr[0]] 45 | for i := 1; i < 4; i++ { 46 | instr.Operands[i-1], _ = strconv.Atoi(rawInstr[i]) 47 | } 48 | instructions = append(instructions, instr) 49 | } 50 | 51 | var r asm.Registers 52 | if *partB { 53 | r[0] = 1 54 | } 55 | for r[ipreg] >= 0 && r[ipreg] < len(instructions) { 56 | instructions[r[ipreg]].Run(&r) 57 | r[ipreg]++ 58 | } 59 | 60 | fmt.Printf("Final value of register 0: %d\n", r[0]) 61 | } 62 | -------------------------------------------------------------------------------- /2018/day21.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "asm" 5 | "bufio" 6 | "flag" 7 | "fmt" 8 | "os" 9 | "strconv" 10 | "strings" 11 | ) 12 | 13 | var inputFile = flag.String("inputFile", "inputs/day21.input", "Relative file path to use as input.") 14 | 15 | func main() { 16 | flag.Parse() 17 | f, err := os.Open(*inputFile) 18 | if err != nil { 19 | return 20 | } 21 | defer f.Close() 22 | 23 | reader := bufio.NewReader(f) 24 | 25 | instructions := make([]asm.Instruction, 0) 26 | ipreg := -1 27 | 28 | for idx := -1; ; idx++ { 29 | l, err := reader.ReadString('\n') 30 | if err != nil || len(l) == 0 { 31 | break 32 | } 33 | l = l[:len(l)-1] 34 | 35 | if idx == -1 { 36 | // Read the instruction pointer register number. 37 | ipreg, _ = strconv.Atoi(strings.Split(l, " ")[1]) 38 | continue 39 | } 40 | 41 | rawInstr := strings.Split(l, " ") 42 | var instr asm.Instruction 43 | instr.F = asm.AllOps[rawInstr[0]] 44 | for i := 1; i < 4; i++ { 45 | instr.Operands[i-1], _ = strconv.Atoi(rawInstr[i]) 46 | } 47 | instructions = append(instructions, instr) 48 | } 49 | 50 | var r asm.Registers 51 | cycles := 0 52 | winners := make(map[int]int) 53 | foundFirstWinner := false 54 | 55 | for r[ipreg] >= 0 && r[ipreg] < len(instructions) { 56 | cycles++ 57 | if r[ipreg] == 28 { 58 | // This line tests for equality against our input. 59 | // Find out what value would have matched, then pretend we didn't match. 60 | if !foundFirstWinner { 61 | foundFirstWinner = true 62 | fmt.Printf("Smallest matching input is %d.\n", r[2]) 63 | } 64 | if winners[r[2]] == 0 { 65 | winners[r[2]] = cycles 66 | } else { 67 | // We've repeated and can stop. 68 | break 69 | } 70 | r[ipreg] += 2 71 | } 72 | if r[ipreg] == 17 { 73 | // Simulate doing the expensive divide operation to run faster. 74 | r[5] /= 256 75 | cycles += 9 + 7*r[5] 76 | r[ipreg] = 8 77 | } 78 | 79 | instructions[r[ipreg]].Run(&r) 80 | r[ipreg]++ 81 | } 82 | 83 | fmt.Printf("Found a loop after %d cycles; stopping.\n", cycles) 84 | 85 | winningInput := -1 86 | highest := 0 87 | for k, v := range winners { 88 | if v > highest { 89 | winningInput = k 90 | highest = v 91 | } 92 | } 93 | fmt.Printf("Largest matching input is %d.\n", winningInput) 94 | } 95 | -------------------------------------------------------------------------------- /2018/template.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "os" 8 | // "strconv" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/dayXX.input", "Relative file path to use as input.") 12 | var partB = flag.Bool("partB", false, "Whether to use the Part B logic.") 13 | 14 | func main() { 15 | flag.Parse() 16 | f, err := os.Open(*inputFile) 17 | if err != nil { 18 | return 19 | } 20 | defer f.Close() 21 | 22 | r := bufio.NewReader(f) 23 | for { 24 | l, err := r.ReadString('\n') 25 | if err != nil || len(l) == 0 { 26 | break 27 | } 28 | l = l[:len(l)-1] 29 | fmt.Println(l) 30 | } 31 | 32 | result := "" 33 | fmt.Printf("Result is %s\n", result) 34 | } 35 | -------------------------------------------------------------------------------- /2019/.gitignore: -------------------------------------------------------------------------------- 1 | *.input 2 | pkg 3 | -------------------------------------------------------------------------------- /2019/day01.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day01.input", "Relative file path to use as input.") 12 | var partB = flag.Bool("partB", false, "Whether to use the Part B logic.") 13 | 14 | func main() { 15 | flag.Parse() 16 | bytes, err := ioutil.ReadFile(*inputFile) 17 | if err != nil { 18 | return 19 | } 20 | contents := string(bytes) 21 | sum := 0 22 | split := strings.Split(contents, "\n") 23 | for _, s := range split { 24 | if s == "" { 25 | continue 26 | } 27 | n, err := strconv.Atoi(s) 28 | if err != nil { 29 | fmt.Printf("Failed to parse %s\n", s) 30 | break 31 | } 32 | fuel := (n / 3) - 2 33 | if fuel > 0 { 34 | sum += fuel 35 | } 36 | if *partB { 37 | for fuel >= 1 { 38 | fuel = (fuel / 3) - 2 39 | if fuel > 0 { 40 | sum += fuel 41 | } 42 | } 43 | } 44 | } 45 | fmt.Println(sum) 46 | } 47 | -------------------------------------------------------------------------------- /2019/day02.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "github.com/lizthegrey/adventofcode/2019/intcode" 7 | ) 8 | 9 | var inputFile = flag.String("inputFile", "inputs/day02.input", "Relative file path to use as input.") 10 | var partB = flag.Bool("partB", false, "Whether to use the Part B logic.") 11 | 12 | const expected = 19690720 13 | 14 | func main() { 15 | flag.Parse() 16 | tape := intcode.ReadInput(*inputFile) 17 | if tape == nil { 18 | fmt.Println("Failed to parse input.") 19 | return 20 | } 21 | 22 | if !*partB { 23 | workingTape := tape.Copy() 24 | workingTape[1] = 12 25 | workingTape[2] = 2 26 | _, done := workingTape.Process(nil) 27 | <-done 28 | fmt.Println(workingTape[0]) 29 | } else { 30 | for noun := 0; noun < 100; noun++ { 31 | for verb := 0; verb < 100; verb++ { 32 | workingTape := tape.Copy() 33 | workingTape[1] = noun 34 | workingTape[2] = verb 35 | _, done := workingTape.Process(nil) 36 | <-done 37 | if workingTape[0] == expected { 38 | fmt.Printf("Noun = %d, Verb = %d, Result = %d\n", noun, verb, 100*noun+verb) 39 | return 40 | } 41 | } 42 | } 43 | fmt.Println("Failed to find solution.") 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /2019/day03.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "math" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | var inputFile = flag.String("inputFile", "inputs/day03.input", "Relative file path to use as input.") 13 | 14 | type Coord struct { 15 | X, Y int 16 | } 17 | 18 | type Tracker [2]float64 19 | 20 | func main() { 21 | flag.Parse() 22 | bytes, err := ioutil.ReadFile(*inputFile) 23 | if err != nil { 24 | return 25 | } 26 | contents := string(bytes) 27 | split := strings.Split(contents, "\n") 28 | 29 | hit := make(map[Coord]Tracker) 30 | for i, s := range split { 31 | if s == "" { 32 | continue 33 | } 34 | loc := Coord{0, 0} 35 | traversed := float64(0) 36 | for _, step := range strings.Split(s, ",") { 37 | distance, err := strconv.Atoi(step[1:]) 38 | if err != nil { 39 | fmt.Printf("Failed to parse step %s\n", step) 40 | return 41 | } 42 | var f func(*Coord) 43 | switch step[0] { 44 | case 'R': 45 | f = func(c *Coord) { 46 | c.X++ 47 | } 48 | case 'L': 49 | f = func(c *Coord) { 50 | c.X-- 51 | } 52 | case 'U': 53 | f = func(c *Coord) { 54 | c.Y++ 55 | } 56 | case 'D': 57 | f = func(c *Coord) { 58 | c.Y-- 59 | } 60 | default: 61 | fmt.Printf("Failed to parse step %s\n", step) 62 | } 63 | for n := 1; n <= distance; n++ { 64 | f(&loc) 65 | hits := hit[loc] 66 | if hits[i] != float64(0) { 67 | hits[i] = math.Min(hits[i], float64(n)+traversed) 68 | } else { 69 | hits[i] = float64(n) + traversed 70 | } 71 | hit[loc] = hits 72 | } 73 | traversed += float64(distance) 74 | } 75 | } 76 | closest := math.MaxFloat64 77 | least := math.MaxFloat64 78 | for loc, hits := range hit { 79 | if hits[0] != float64(0) && hits[1] != float64(0) { 80 | distance := math.Abs(float64(loc.X)) + math.Abs(float64(loc.Y)) 81 | if distance < closest { 82 | closest = distance 83 | } 84 | sum := hits[0] + hits[1] 85 | if sum < least { 86 | least = sum 87 | } 88 | } 89 | } 90 | fmt.Printf("Part A: %d, Part B: %d\n", int(closest), int(least)) 91 | } 92 | -------------------------------------------------------------------------------- /2019/day04.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "strconv" 7 | ) 8 | 9 | var lower = flag.Int("lower", 123257, "Lower bound.") 10 | var upper = flag.Int("upper", 647015, "Upper bound.") 11 | var partB = flag.Bool("partB", false, "Whether to use the Part B logic.") 12 | 13 | func main() { 14 | flag.Parse() 15 | count := 0 16 | for i := *lower; i <= *upper; i++ { 17 | if eligible(i) { 18 | count++ 19 | } 20 | } 21 | fmt.Println(count) 22 | } 23 | 24 | func eligible(i int) bool { 25 | digits := strconv.Itoa(i) 26 | var prevDigit rune 27 | var double bool 28 | var exactDouble bool 29 | run := 1 30 | 31 | for _, d := range digits { 32 | if prevDigit == d { 33 | double = true 34 | run++ 35 | } else { 36 | if run == 2 { 37 | exactDouble = true 38 | } 39 | run = 1 40 | } 41 | if prevDigit > d { 42 | return false 43 | } 44 | prevDigit = d 45 | } 46 | if run == 2 { 47 | exactDouble = true 48 | } 49 | 50 | if *partB { 51 | return exactDouble 52 | } else { 53 | return double 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /2019/day05.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "github.com/lizthegrey/adventofcode/2019/intcode" 7 | ) 8 | 9 | var inputFile = flag.String("inputFile", "inputs/day05.input", "Relative file path to use as input.") 10 | var inputValue = flag.Int("inputValue", 0, "The input to the input instruction.") 11 | 12 | func main() { 13 | flag.Parse() 14 | tape := intcode.ReadInput(*inputFile) 15 | if tape == nil { 16 | fmt.Println("Failed to parse input.") 17 | return 18 | } 19 | 20 | input := make(chan int) 21 | result, _ := tape.Process(input) 22 | input <- *inputValue 23 | fmt.Printf("Result: %d\n", <-result) 24 | } 25 | -------------------------------------------------------------------------------- /2019/day06.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strings" 8 | ) 9 | 10 | var inputFile = flag.String("inputFile", "inputs/day06.input", "Relative file path to use as input.") 11 | var debug = flag.Bool("debug", false, "Whether to print debug output.") 12 | 13 | var com string = "COM" 14 | var me string = "YOU" 15 | var santa string = "SAN" 16 | 17 | func main() { 18 | flag.Parse() 19 | bytes, err := ioutil.ReadFile(*inputFile) 20 | if err != nil { 21 | return 22 | } 23 | contents := string(bytes) 24 | split := strings.Split(contents, "\n") 25 | 26 | // Inwards: outer -> center 27 | parents := make(map[string]string) 28 | 29 | for _, s := range split { 30 | if s == "" { 31 | continue 32 | } 33 | line := strings.Split(s, ")") 34 | if len(line) != 2 { 35 | fmt.Printf("Failed to parse line %s\n", s) 36 | return 37 | } 38 | center := line[0] 39 | outer := line[1] 40 | parents[outer] = center 41 | } 42 | 43 | distances := make(map[string]int) 44 | distances[com] = 0 45 | sum := 0 46 | for k := range parents { 47 | sum += computeDistance(k, distances, parents) 48 | } 49 | fmt.Printf("Part A: %d\n", sum) 50 | 51 | if parents[me] == "" || parents[santa] == "" { 52 | fmt.Println("Couldn't find YOU or SAN.") 53 | return 54 | } 55 | 56 | myParents := make([]string, 0) 57 | santaParents := make([]string, 0) 58 | for loc := me; loc != com; loc = parents[loc] { 59 | myParents = append(myParents, loc) 60 | } 61 | for loc := santa; loc != com; loc = parents[loc] { 62 | santaParents = append(santaParents, loc) 63 | } 64 | 65 | // Step backwards through the lists. 66 | for i := 1; ; i++ { 67 | s := santaParents[len(santaParents)-i] 68 | m := myParents[len(myParents)-i] 69 | if *debug { 70 | fmt.Printf("Step %d: %s, %s\n", i, s, m) 71 | } 72 | if s != m { 73 | fmt.Printf("Part B: %d\n", len(myParents)+len(santaParents)-(2*i)) 74 | break 75 | } 76 | } 77 | } 78 | 79 | func computeDistance(object string, distances map[string]int, parents map[string]string) int { 80 | if _, ok := distances[object]; !ok { 81 | distances[object] = computeDistance(parents[object], distances, parents) + 1 82 | } 83 | return distances[object] 84 | } 85 | -------------------------------------------------------------------------------- /2019/day08.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strconv" 8 | ) 9 | 10 | var inputFile = flag.String("inputFile", "inputs/day08.input", "Relative file path to use as input.") 11 | 12 | const cols int = 25 13 | const rows int = 6 14 | 15 | type Sheet [rows][cols]int 16 | 17 | func main() { 18 | flag.Parse() 19 | bytes, err := ioutil.ReadFile(*inputFile) 20 | if err != nil { 21 | return 22 | } 23 | contents := string(bytes[:len(bytes)-1]) 24 | layers := make([]Sheet, 0) 25 | layer := -1 26 | for i, s := range contents { 27 | n, err := strconv.Atoi(string(s)) 28 | if err != nil { 29 | fmt.Printf("Failed to parse %s\n", s) 30 | break 31 | } 32 | if i%(cols*rows) == 0 { 33 | layer++ 34 | layers = append(layers, Sheet{}) 35 | } 36 | idx := i % (cols * rows) 37 | layers[layer][idx/cols][idx%cols] = n 38 | } 39 | fewestZeroes := cols * rows 40 | ret := -1 41 | for _, layer := range layers { 42 | seen := make(map[int]int) 43 | for r := range layer { 44 | for c := range layer[r] { 45 | seen[layer[r][c]]++ 46 | } 47 | } 48 | if fewestZeroes > seen[0] { 49 | fewestZeroes = seen[0] 50 | ret = seen[1] * seen[2] 51 | } 52 | } 53 | fmt.Printf("Part A: %d\n", ret) 54 | 55 | for r := 0; r < rows; r++ { 56 | for c := 0; c < cols; c++ { 57 | outer: 58 | for _, sheet := range layers { 59 | switch sheet[r][c] { 60 | case 0: 61 | fmt.Printf(" ") 62 | break outer 63 | case 1: 64 | fmt.Printf("x") 65 | break outer 66 | case 2: 67 | continue outer 68 | } 69 | } 70 | } 71 | fmt.Println() 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /2019/day09.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "github.com/lizthegrey/adventofcode/2019/intcode" 7 | ) 8 | 9 | var inputFile = flag.String("inputFile", "inputs/day09.input", "Relative file path to use as input.") 10 | 11 | func main() { 12 | flag.Parse() 13 | tape := intcode.ReadInput(*inputFile) 14 | if tape == nil { 15 | fmt.Println("Failed to parse input.") 16 | return 17 | } 18 | 19 | for i := 1; i <= 2; i++ { 20 | workingTape := tape.Copy() 21 | input := make(chan int, 1) 22 | input <- i 23 | output, _ := workingTape.Process(input) 24 | for out := range output { 25 | fmt.Printf("%d,", out) 26 | } 27 | fmt.Println() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /2019/day11.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "github.com/lizthegrey/adventofcode/2019/intcode" 7 | "math" 8 | ) 9 | 10 | var inputFile = flag.String("inputFile", "inputs/day11.input", "Relative file path to use as input.") 11 | var partB = flag.Bool("partB", false, "Whether to use Part B logic.") 12 | 13 | type Coord struct { 14 | X, Y int 15 | } 16 | 17 | func main() { 18 | flag.Parse() 19 | tape := intcode.ReadInput(*inputFile) 20 | if tape == nil { 21 | fmt.Println("Failed to parse input.") 22 | return 23 | } 24 | 25 | workingTape := tape.Copy() 26 | input := make(chan int, 1) 27 | output, done := workingTape.Process(input) 28 | 29 | panels := make(map[Coord]bool) 30 | loc := Coord{0, 0} 31 | // Up 32 | dir := 0 33 | 34 | if *partB { 35 | panels[loc] = true 36 | } 37 | 38 | outer: 39 | for { 40 | if panels[loc] { 41 | input <- 1 42 | } else { 43 | input <- 0 44 | } 45 | select { 46 | case <-done: 47 | break outer 48 | case color := <-output: 49 | if color == 0 { 50 | panels[loc] = false 51 | } else { 52 | panels[loc] = true 53 | } 54 | turn := <-output 55 | if turn == 0 { 56 | dir-- 57 | } else { 58 | dir++ 59 | } 60 | if dir >= 4 { 61 | dir -= 4 62 | } else if dir < 0 { 63 | dir += 4 64 | } 65 | switch dir { 66 | // UP 67 | case 0: 68 | loc.Y++ 69 | // RIGHT 70 | case 1: 71 | loc.X++ 72 | // DOWN 73 | case 2: 74 | loc.Y-- 75 | // LEFT 76 | case 3: 77 | loc.X-- 78 | } 79 | } 80 | } 81 | fmt.Println(len(panels)) 82 | 83 | minX := math.MaxInt32 84 | maxX := math.MinInt32 85 | minY := math.MaxInt32 86 | maxY := math.MinInt32 87 | for coord := range panels { 88 | if !panels[coord] { 89 | continue 90 | } 91 | if coord.X > maxX { 92 | maxX = coord.X 93 | } 94 | if coord.X < minX { 95 | minX = coord.X 96 | } 97 | if coord.Y > maxY { 98 | maxY = coord.Y 99 | } 100 | if coord.Y < minY { 101 | minY = coord.Y 102 | } 103 | } 104 | for y := maxY; y >= minY; y-- { 105 | for x := minX; x <= maxX; x++ { 106 | if panels[Coord{x, y}] { 107 | fmt.Printf("x") 108 | } else { 109 | fmt.Printf(" ") 110 | } 111 | } 112 | fmt.Println() 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /2019/day21.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "github.com/lizthegrey/adventofcode/2019/intcode" 7 | "time" 8 | ) 9 | 10 | var inputFile = flag.String("inputFile", "inputs/day21.input", "Relative file path to use as input.") 11 | 12 | func main() { 13 | flag.Parse() 14 | tape := intcode.ReadInput(*inputFile) 15 | if tape == nil { 16 | fmt.Println("Failed to parse input.") 17 | return 18 | } 19 | 20 | // Part A 21 | workingTape := tape.Copy() 22 | input := make(chan int, 1) 23 | output, done := workingTape.Process(input) 24 | 25 | go func() { 26 | for l := range output { 27 | if l > 255 { 28 | fmt.Println(l) 29 | return 30 | } 31 | fmt.Printf("%c", l) 32 | } 33 | fmt.Println() 34 | }() 35 | 36 | inputString := []string{ 37 | // J = AND(OR(C, B, A), D) 38 | "NOT C J", 39 | "NOT B T", 40 | "OR T J", 41 | "NOT A T", 42 | "OR T J", 43 | "AND D J", 44 | "WALK", 45 | } 46 | for _, l := range inputString { 47 | for _, r := range l { 48 | input <- int(r) 49 | } 50 | input <- int('\n') 51 | } 52 | <-done 53 | time.Sleep(100 * time.Millisecond) 54 | 55 | // Part B 56 | input = make(chan int, 1) 57 | output, done = tape.Process(input) 58 | 59 | go func() { 60 | for l := range output { 61 | if l > 255 { 62 | fmt.Println(l) 63 | return 64 | } 65 | fmt.Printf("%c", l) 66 | } 67 | fmt.Println() 68 | }() 69 | 70 | inputString = []string{ 71 | // J = AND(OR(C, B, A), D) 72 | // except we also need to make sure E or H are clear before jumping. 73 | // J = AND(J, OR(E,H)) 74 | "NOT C J", 75 | "NOT B T", 76 | "OR T J", 77 | "NOT A T", 78 | "OR T J", 79 | "AND D J", 80 | "OR E T", 81 | "OR H T", 82 | "AND T J", 83 | "RUN", 84 | } 85 | for _, l := range inputString { 86 | for _, r := range l { 87 | input <- int(r) 88 | } 89 | input <- int('\n') 90 | } 91 | <-done 92 | time.Sleep(100 * time.Millisecond) 93 | } 94 | -------------------------------------------------------------------------------- /2019/day22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lizthegrey/adventofcode/fcc9bb5a81b5cd7a9f67db0666c3e854f276a3b7/2019/day22.png -------------------------------------------------------------------------------- /2019/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/lizthegrey/adventofcode/2019 2 | 3 | go 1.13 4 | -------------------------------------------------------------------------------- /2020/.gitignore: -------------------------------------------------------------------------------- 1 | *.input 2 | pkg 3 | -------------------------------------------------------------------------------- /2020/day01.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day01.input", "Relative file path to use as input.") 12 | 13 | func main() { 14 | flag.Parse() 15 | bytes, err := ioutil.ReadFile(*inputFile) 16 | if err != nil { 17 | return 18 | } 19 | contents := string(bytes) 20 | split := strings.Split(contents, "\n") 21 | split = split[:len(split)-1] 22 | seen := make([]int, len(split)) 23 | contains := make(map[int]int) 24 | for i, s := range split { 25 | n, err := strconv.Atoi(s) 26 | if err != nil { 27 | fmt.Printf("Failed to parse %s\n", s) 28 | break 29 | } 30 | if n <= 0 { 31 | fmt.Printf("Optimization invariant broken: %d <= 0 \n", n) 32 | break 33 | } 34 | seen[i] = n 35 | contains[n] = i 36 | } 37 | partA: 38 | for i, n := range seen { 39 | if pos, ok := contains[2020-n]; ok && pos != i { 40 | fmt.Println(n * (2020 - n)) 41 | break partA 42 | } 43 | } 44 | partB: 45 | for i, m := range seen { 46 | for j, n := range seen { 47 | if i >= j { 48 | continue 49 | } 50 | sumMN := m + n 51 | if sumMN >= 2020 { 52 | continue 53 | } 54 | if pos, ok := contains[2020-sumMN]; ok && pos != i && pos != j { 55 | fmt.Println(m * n * (2020 - sumMN)) 56 | break partB 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /2020/day02.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day02.input", "Relative file path to use as input.") 12 | 13 | func main() { 14 | flag.Parse() 15 | bytes, err := ioutil.ReadFile(*inputFile) 16 | if err != nil { 17 | return 18 | } 19 | contents := string(bytes) 20 | split := strings.Split(contents, "\n") 21 | split = split[:len(split)-1] 22 | var matchesA, matchesB int 23 | for _, s := range split { 24 | parsed := strings.Split(s, " ") 25 | if len(parsed) != 3 { 26 | fmt.Printf("Encountered bad line: %s\n", split) 27 | } 28 | 29 | lenSpec := strings.Split(parsed[0], "-") // "1-3" 30 | if len(lenSpec) != 2 { 31 | fmt.Printf("Encountered bad lenSpec: %s\n", parsed[0]) 32 | } 33 | char := rune(parsed[1][0]) // "n:" 34 | password := parsed[2] // "xxxxxxx" 35 | 36 | min, err := strconv.Atoi(lenSpec[0]) 37 | max, err := strconv.Atoi(lenSpec[1]) 38 | if err != nil { 39 | fmt.Printf("Failed to parse %s\n", parsed[0]) 40 | break 41 | } 42 | chars := make(map[rune]int) 43 | for _, c := range password { 44 | chars[c]++ 45 | } 46 | if chars[char] >= min && chars[char] <= max { 47 | matchesA++ 48 | } 49 | if (rune(password[min-1]) == char) != (rune(password[max-1]) == char) { 50 | matchesB++ 51 | } 52 | } 53 | fmt.Println(matchesA) 54 | fmt.Println(matchesB) 55 | } 56 | -------------------------------------------------------------------------------- /2020/day03.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strings" 8 | ) 9 | 10 | var inputFile = flag.String("inputFile", "inputs/day03.input", "Relative file path to use as input.") 11 | 12 | func main() { 13 | flag.Parse() 14 | bytes, err := ioutil.ReadFile(*inputFile) 15 | if err != nil { 16 | return 17 | } 18 | contents := string(bytes) 19 | split := strings.Split(contents, "\n") 20 | split = split[:len(split)-1] 21 | trees := make([][]bool, len(split)) 22 | for i, s := range split { 23 | trees[i] = make([]bool, len(s)) 24 | for j, c := range s { 25 | trees[i][j] = (c == '#') 26 | } 27 | if err != nil { 28 | fmt.Printf("Failed to parse %s\n", s) 29 | break 30 | } 31 | } 32 | 33 | fmt.Println(checkSlope(1, 3, trees)) 34 | a := checkSlope(1, 3, trees) 35 | b := checkSlope(1, 1, trees) 36 | c := checkSlope(1, 5, trees) 37 | d := checkSlope(1, 7, trees) 38 | e := checkSlope(2, 1, trees) 39 | fmt.Printf("%d*%d*%d*%d*%d = %d\n", a, b, c, d, e, a*b*c*d*e) 40 | } 41 | 42 | func checkSlope(down, right int, trees [][]bool) int { 43 | hit := 0 44 | for time := 0; time*down < len(trees); time++ { 45 | traversed := time * down 46 | column := (time * right) % len(trees[traversed]) 47 | if trees[traversed][column] { 48 | hit++ 49 | } 50 | } 51 | return hit 52 | } 53 | -------------------------------------------------------------------------------- /2020/day05.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day05.input", "Relative file path to use as input.") 12 | 13 | func main() { 14 | flag.Parse() 15 | bytes, err := ioutil.ReadFile(*inputFile) 16 | if err != nil { 17 | return 18 | } 19 | contents := string(bytes) 20 | split := strings.Split(contents, "\n") 21 | split = split[:len(split)-1] 22 | highestSeat := 0 23 | var seen [1024]bool 24 | for _, s := range split { 25 | // Read bits from MSB to LSB 26 | if len(s) != (7 + 3) { 27 | fmt.Printf("Invalid boarding pass length: %s\n", s) 28 | } 29 | bits := strings.ReplaceAll(s, "B", "1") 30 | bits = strings.ReplaceAll(bits, "R", "1") 31 | bits = strings.ReplaceAll(bits, "F", "0") 32 | bits = strings.ReplaceAll(bits, "L", "0") 33 | val, err := strconv.ParseInt(bits, 2, 0) 34 | if err != nil { 35 | fmt.Printf("Failed to parse %s\n", bits) 36 | } 37 | value := int(val) 38 | seen[value] = true 39 | if value > highestSeat { 40 | highestSeat = value 41 | } 42 | } 43 | fmt.Println(highestSeat) 44 | init := true 45 | for i, found := range seen { 46 | if init && found { 47 | init = false 48 | continue 49 | } 50 | if !init && !found { 51 | fmt.Println(i) 52 | break 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /2020/day06.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strings" 8 | ) 9 | 10 | var inputFile = flag.String("inputFile", "inputs/day06.input", "Relative file path to use as input.") 11 | 12 | func main() { 13 | flag.Parse() 14 | bytes, err := ioutil.ReadFile(*inputFile) 15 | if err != nil { 16 | return 17 | } 18 | contents := string(bytes) 19 | split := strings.Split(contents, "\n") 20 | split = split[:len(split)-1] 21 | groupNum := 0 22 | groups := []map[rune]int{ 23 | make(map[rune]int), 24 | } 25 | groupSize := []int{0} 26 | for _, s := range split { 27 | if s == "" { 28 | groups = append(groups, make(map[rune]int)) 29 | groupSize = append(groupSize, 0) 30 | groupNum++ 31 | continue 32 | } 33 | for _, r := range s { 34 | groups[groupNum][r]++ 35 | } 36 | groupSize[groupNum]++ 37 | } 38 | sum := 0 39 | all := 0 40 | for n, g := range groups { 41 | sum += len(g) 42 | for _, count := range g { 43 | if count == groupSize[n] { 44 | all++ 45 | } 46 | } 47 | } 48 | fmt.Println(sum) 49 | fmt.Println(all) 50 | } 51 | -------------------------------------------------------------------------------- /2020/day09.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day09.input", "Relative file path to use as input.") 12 | var window = flag.Int("window", 25, "The number of entries in the rolling window.") 13 | 14 | func main() { 15 | flag.Parse() 16 | bytes, err := ioutil.ReadFile(*inputFile) 17 | if err != nil { 18 | return 19 | } 20 | contents := string(bytes) 21 | split := strings.Split(contents, "\n") 22 | split = split[:len(split)-1] 23 | numbers := make([]int, len(split)) 24 | for i, s := range split { 25 | n, err := strconv.Atoi(s) 26 | if err != nil { 27 | fmt.Printf("Failed to parse %s\n", s) 28 | break 29 | } 30 | numbers[i] = n 31 | } 32 | 33 | var weakness int 34 | for i, v := range numbers { 35 | if i < *window { 36 | // preamble. 37 | continue 38 | } 39 | 40 | found := false 41 | middle: 42 | for n := i - *window; n < i; n++ { 43 | first := numbers[n] 44 | if first >= v { 45 | continue 46 | } 47 | for m := n + 1; m < i; m++ { 48 | second := numbers[m] 49 | if first+second == v { 50 | found = true 51 | break middle 52 | } 53 | } 54 | } 55 | if !found { 56 | // This doesn't have something that adds up. 57 | weakness = v 58 | break 59 | } 60 | } 61 | fmt.Println(weakness) 62 | 63 | for i, v := range numbers { 64 | rollingSum := v 65 | high := v 66 | low := v 67 | for n := 1; true; n++ { 68 | current := numbers[i+n] 69 | rollingSum += current 70 | if high < current { 71 | high = current 72 | } 73 | if low > current { 74 | low = current 75 | } 76 | if rollingSum == weakness { 77 | fmt.Println(high + low) 78 | return 79 | } 80 | if rollingSum > weakness { 81 | // We busted by going too high; start from the next i. 82 | break 83 | } 84 | // Otherwise keep searching. 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /2020/day10.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "sort" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | var inputFile = flag.String("inputFile", "inputs/day10.input", "Relative file path to use as input.") 13 | 14 | func main() { 15 | flag.Parse() 16 | bytes, err := ioutil.ReadFile(*inputFile) 17 | if err != nil { 18 | return 19 | } 20 | contents := string(bytes) 21 | split := strings.Split(contents, "\n") 22 | split = split[:len(split)-1] 23 | voltages := make([]int, len(split)) 24 | for i, s := range split { 25 | n, err := strconv.Atoi(s) 26 | if err != nil { 27 | fmt.Printf("Failed to parse %s\n", s) 28 | break 29 | } 30 | voltages[i] = n 31 | } 32 | sort.Ints(voltages) 33 | var prev, ones, threes int 34 | 35 | exists := make(map[int]int) 36 | for i, v := range voltages { 37 | exists[v] = i 38 | diff := v - prev 39 | switch diff { 40 | case 1: 41 | ones++ 42 | case 3: 43 | threes++ 44 | } 45 | prev = v 46 | } 47 | threes++ 48 | fmt.Println(ones * threes) 49 | 50 | // (1) (1) (1) ways(N-1) + ways(N) w(N-2)+w(N-1)+w(N) etc 51 | // N+3 -> N -> N-1 -> N-2 -> N-3 52 | // ............111 53 | // ........... 321 54 | // ...... ....6631 55 | // 0.............. 56 | // (except in 3d, not in 2d) 57 | ways := make([]int, len(voltages)) 58 | ways[len(voltages)-1] = 1 59 | for i := len(voltages) - 2; i >= 0; i-- { 60 | sum := 0 61 | for diff := 1; diff <= 3; diff++ { 62 | if pos, ok := exists[voltages[i]+diff]; ok { 63 | sum += ways[pos] 64 | } 65 | } 66 | ways[i] = sum 67 | } 68 | ret := 0 69 | for v := 1; v <= 3; v++ { 70 | if pos, ok := exists[v]; ok { 71 | ret += ways[pos] 72 | } 73 | } 74 | fmt.Println(ret) 75 | } 76 | -------------------------------------------------------------------------------- /2020/day13.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day13.input", "Relative file path to use as input.") 12 | var debug = flag.Bool("debug", false, "Whether to print debug output along the way.") 13 | 14 | func main() { 15 | flag.Parse() 16 | bytes, err := ioutil.ReadFile(*inputFile) 17 | if err != nil { 18 | return 19 | } 20 | contents := string(bytes) 21 | split := strings.Split(contents, "\n") 22 | split = split[:len(split)-1] 23 | startTime, err := strconv.Atoi(split[0]) 24 | if err != nil { 25 | fmt.Printf("Failed to parse %s\n", split[0]) 26 | return 27 | } 28 | lines := strings.Split(split[1], ",") 29 | soonest := startTime 30 | soonestNo := -1 31 | ids := make(map[int]int) 32 | for i, l := range lines { 33 | if l == "x" { 34 | continue 35 | } 36 | lineNo, err := strconv.Atoi(l) 37 | if err != nil { 38 | fmt.Printf("Failed to parse %s\n", l) 39 | return 40 | } 41 | ids[lineNo] = i 42 | minToWait := lineNo - (startTime % lineNo) 43 | if minToWait < soonest { 44 | soonest = minToWait 45 | soonestNo = lineNo 46 | } 47 | } 48 | fmt.Println(soonest * soonestNo) 49 | 50 | // Part B. 51 | // We are looking for the following relation to be satisfied: 52 | // ex t st t + v % ids[k] === 0 for each k,v 53 | minValue := 0 54 | runningProduct := 1 55 | for k, v := range ids { 56 | for (minValue+v)%k != 0 { 57 | minValue += runningProduct 58 | } 59 | runningProduct *= k 60 | if *debug { 61 | fmt.Printf("t + %d === 0 mod %d\n", v, k) 62 | fmt.Printf("Sum so far: %d, product so far: %d\n", minValue, runningProduct) 63 | } 64 | } 65 | fmt.Println(minValue) 66 | } 67 | -------------------------------------------------------------------------------- /2020/day15.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day15.input", "Relative file path to use as input.") 12 | var debug = flag.Bool("debug", false, "Whether to print debug output along the way.") 13 | var partB = flag.Bool("partB", false, "Whether to use part B logic.") 14 | 15 | func main() { 16 | flag.Parse() 17 | bytes, err := ioutil.ReadFile(*inputFile) 18 | if err != nil { 19 | return 20 | } 21 | contents := string(bytes) 22 | split := strings.Split(contents, "\n") 23 | split = split[:len(split)-1] 24 | split = strings.Split(split[0], ",") 25 | seen := make(map[int]int) 26 | prev := -1 27 | for i, s := range split { 28 | n, err := strconv.Atoi(s) 29 | if err != nil { 30 | fmt.Printf("Failed to parse %s\n", s) 31 | break 32 | } 33 | seen[n] = i 34 | prev = n 35 | } 36 | max := 2020 37 | if *partB { 38 | max = 30000000 39 | } 40 | for i := len(split); i < max; i++ { 41 | var val int 42 | if prevPos, seen := seen[prev]; seen { 43 | val = (i - 1) - prevPos 44 | } else { 45 | val = 0 46 | } 47 | if *debug { 48 | fmt.Println(val) 49 | } 50 | if prev >= 0 { 51 | seen[prev] = i - 1 52 | } 53 | prev = val 54 | } 55 | fmt.Println(prev) 56 | } 57 | -------------------------------------------------------------------------------- /2020/day17.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strings" 8 | ) 9 | 10 | var inputFile = flag.String("inputFile", "inputs/day17.input", "Relative file path to use as input.") 11 | var partB = flag.Bool("partB", false, "Whether to use part B logic.") 12 | 13 | type Coord4 struct { 14 | W, X, Y, Z int 15 | } 16 | 17 | func (c Coord4) Adjacent() []Coord4 { 18 | var ret []Coord4 19 | for xOffset := -1; xOffset <= 1; xOffset++ { 20 | for yOffset := -1; yOffset <= 1; yOffset++ { 21 | for zOffset := -1; zOffset <= 1; zOffset++ { 22 | for wOffset := -1; wOffset <= 1; wOffset++ { 23 | if wOffset != 0 && !*partB { 24 | continue 25 | } 26 | if xOffset == 0 && yOffset == 0 && zOffset == 0 && wOffset == 0 { 27 | continue 28 | } 29 | ret = append(ret, Coord4{c.W + wOffset, c.X + xOffset, c.Y + yOffset, c.Z + zOffset}) 30 | } 31 | } 32 | } 33 | } 34 | return ret 35 | } 36 | 37 | func main() { 38 | flag.Parse() 39 | bytes, err := ioutil.ReadFile(*inputFile) 40 | if err != nil { 41 | return 42 | } 43 | contents := string(bytes) 44 | split := strings.Split(contents, "\n") 45 | split = split[:len(split)-1] 46 | active := make(map[Coord4]bool) 47 | for y, s := range split { 48 | for x, v := range s { 49 | switch v { 50 | case '#': 51 | active[Coord4{0, x, y, 0}] = true 52 | } 53 | } 54 | } 55 | 56 | for i := 0; i < 6; i++ { 57 | prev := make(map[Coord4]bool) 58 | neighCount := make(map[Coord4]int) 59 | for k := range active { 60 | if _, exists := neighCount[k]; !exists { 61 | neighCount[k] = 0 62 | } 63 | prev[k] = true 64 | for _, n := range k.Adjacent() { 65 | neighCount[n]++ 66 | } 67 | } 68 | 69 | for k, activeNeighbors := range neighCount { 70 | if prev[k] { 71 | if activeNeighbors != 2 && activeNeighbors != 3 { 72 | delete(active, k) 73 | } 74 | } else { 75 | if activeNeighbors == 3 { 76 | active[k] = true 77 | } 78 | } 79 | } 80 | } 81 | 82 | result := 0 83 | for _, v := range active { 84 | if v { 85 | result++ 86 | } 87 | } 88 | fmt.Println(result) 89 | } 90 | -------------------------------------------------------------------------------- /2020/day23.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "container/ring" 5 | "flag" 6 | "fmt" 7 | "io/ioutil" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | var inputFile = flag.String("inputFile", "inputs/day23.input", "Relative file path to use as input.") 13 | var partB = flag.Bool("partB", false, "Whether to use part B logic.") 14 | 15 | func main() { 16 | flag.Parse() 17 | bytes, err := ioutil.ReadFile(*inputFile) 18 | if err != nil { 19 | return 20 | } 21 | contents := string(bytes) 22 | input := strings.Split(contents, "\n")[0] 23 | 24 | cache := make(map[int]*ring.Ring) 25 | 26 | cups := ring.New(len(input)) 27 | for _, c := range input { 28 | n, err := strconv.Atoi(string(c)) 29 | if err != nil { 30 | fmt.Printf("Failed to parse %s\n", input) 31 | break 32 | } 33 | cups.Value = n 34 | cache[n] = cups 35 | cups = cups.Next() 36 | } 37 | if *partB { 38 | last := cups.Prev() 39 | added := ring.New(1000000 - len(input)) 40 | for i := len(input) + 1; i <= 1000000; i++ { 41 | added.Value = i 42 | cache[i] = added 43 | added = added.Next() 44 | } 45 | last.Link(added) 46 | } 47 | 48 | ringSize := cups.Len() 49 | 50 | iters := 100 51 | if *partB { 52 | iters = 10000000 53 | } 54 | 55 | current := cups 56 | for i := 0; i < iters; i++ { 57 | removed := current.Unlink(3) 58 | dst := 1 + ((ringSize + current.Value.(int) - 2) % ringSize) 59 | inRemoved := make(map[int]bool) 60 | for n := 0; n < 3; n++ { 61 | inRemoved[removed.Move(n).Value.(int)] = true 62 | } 63 | for inRemoved[dst] { 64 | dst = 1 + ((ringSize + dst - 2) % ringSize) 65 | } 66 | cache[dst].Link(removed) 67 | current = current.Next() 68 | } 69 | 70 | first := cache[1] 71 | if *partB { 72 | a := first.Move(1).Value.(int) 73 | b := first.Move(2).Value.(int) 74 | fmt.Println(a * b) 75 | } else { 76 | for i := 1; i < len(input); i++ { 77 | first = first.Next() 78 | fmt.Printf("%d", first.Value.(int)) 79 | } 80 | fmt.Println() 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /2020/day25.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day25.input", "Relative file path to use as input.") 12 | 13 | func main() { 14 | flag.Parse() 15 | bytes, err := ioutil.ReadFile(*inputFile) 16 | if err != nil { 17 | return 18 | } 19 | contents := string(bytes) 20 | split := strings.Split(contents, "\n") 21 | split = split[:len(split)-1] 22 | card, err := strconv.Atoi(split[0]) 23 | if err != nil { 24 | fmt.Printf("Failed to parse %s\n", split[0]) 25 | return 26 | } 27 | door, err := strconv.Atoi(split[1]) 28 | if err != nil { 29 | fmt.Printf("Failed to parse %s\n", split[1]) 30 | return 31 | } 32 | 33 | dLoop := Loop(7, door) 34 | cValue := 1 35 | for i := 1; i <= dLoop; i++ { 36 | cValue *= card 37 | cValue %= 20201227 38 | } 39 | cLoop := Loop(7, card) 40 | dValue := 1 41 | for i := 1; i <= cLoop; i++ { 42 | dValue *= door 43 | dValue %= 20201227 44 | } 45 | if dValue != cValue { 46 | fmt.Printf("%d != %d", dValue, cValue) 47 | } 48 | fmt.Println(dValue) 49 | } 50 | 51 | func Loop(subject, target int) int { 52 | value := 1 53 | for i := 1; ; i++ { 54 | value *= subject 55 | value %= 20201227 56 | if value == target { 57 | return i 58 | } 59 | } 60 | return -1 61 | } 62 | -------------------------------------------------------------------------------- /2021/.gitignore: -------------------------------------------------------------------------------- 1 | *.input 2 | pkg 3 | -------------------------------------------------------------------------------- /2021/day01.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day01.input", "Relative file path to use as input.") 12 | 13 | func main() { 14 | flag.Parse() 15 | bytes, err := ioutil.ReadFile(*inputFile) 16 | if err != nil { 17 | return 18 | } 19 | contents := string(bytes) 20 | split := strings.Split(contents, "\n") 21 | 22 | // part A 23 | last := -1 24 | increments := 0 25 | for _, s := range split { 26 | i, _ := strconv.Atoi(s) 27 | if last != -1 && i > last { 28 | increments++ 29 | } 30 | last = i 31 | } 32 | fmt.Println(increments) 33 | 34 | // part B 35 | increments = 0 36 | // Keep a sliding window of the last 3 numbers seen. 37 | lastNums := []int{0, 0, 0} 38 | lastSum := 0 39 | for idx, s := range split { 40 | i, _ := strconv.Atoi(s) 41 | // Remove the oldest number and add ourselves. 42 | sum := lastSum + i - lastNums[0] 43 | // Roll the oldest number off. 44 | lastNums = append(lastNums[1:], i) 45 | 46 | if idx < 3 { 47 | lastSum = sum 48 | continue 49 | } 50 | if lastSum < sum { 51 | increments++ 52 | } 53 | lastSum = sum 54 | } 55 | fmt.Println(increments) 56 | } 57 | -------------------------------------------------------------------------------- /2021/day02.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day02.input", "Relative file path to use as input.") 12 | 13 | func main() { 14 | flag.Parse() 15 | bytes, err := ioutil.ReadFile(*inputFile) 16 | if err != nil { 17 | return 18 | } 19 | contents := string(bytes) 20 | split := strings.Split(contents, "\n") 21 | split = split[:len(split)-1] 22 | 23 | posX := 0 24 | posY := 0 25 | for _, s := range split { 26 | parts := strings.Split(s, " ") 27 | command := parts[0] 28 | num, _ := strconv.Atoi(parts[1]) 29 | switch command { 30 | case "up": 31 | posY -= num 32 | case "down": 33 | posY += num 34 | case "forward": 35 | posX += num 36 | } 37 | } 38 | fmt.Println(posX * posY) 39 | 40 | posX = 0 41 | posY = 0 42 | aim := 0 43 | for _, s := range split { 44 | parts := strings.Split(s, " ") 45 | command := parts[0] 46 | num, _ := strconv.Atoi(parts[1]) 47 | switch command { 48 | case "up": 49 | aim -= num 50 | case "down": 51 | aim += num 52 | case "forward": 53 | posX += num 54 | posY += num * aim 55 | } 56 | } 57 | fmt.Println(posX * posY) 58 | } 59 | -------------------------------------------------------------------------------- /2021/day03.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strings" 8 | ) 9 | 10 | var inputFile = flag.String("inputFile", "inputs/day03.input", "Relative file path to use as input.") 11 | 12 | func main() { 13 | flag.Parse() 14 | bytes, err := ioutil.ReadFile(*inputFile) 15 | if err != nil { 16 | return 17 | } 18 | contents := string(bytes) 19 | split := strings.Split(contents, "\n") 20 | split = split[:len(split)-1] 21 | 22 | var ones [12]int 23 | lines := 0 24 | data := make([][12]bool, 0) 25 | for _, s := range split { 26 | var line [12]bool 27 | lines++ 28 | for pos, c := range s { 29 | if c == '1' { 30 | line[pos] = true 31 | ones[pos]++ 32 | } 33 | } 34 | data = append(data, line) 35 | } 36 | var epsilon, gamma int 37 | for pos, count := range ones { 38 | if count*2 > lines { 39 | epsilon += 1 << (11 - pos) 40 | } else { 41 | gamma += 1 << (11 - pos) 42 | } 43 | } 44 | fmt.Println(epsilon * gamma) 45 | 46 | oxygen := iterate(data, func(ones, candidates int) bool { 47 | return ones*2 >= candidates 48 | }) 49 | scrubber := iterate(data, func(ones, candidates int) bool { 50 | return ones*2 < candidates 51 | }) 52 | fmt.Println(oxygen * scrubber) 53 | } 54 | 55 | func iterate(data [][12]bool, match func(int, int) bool) int { 56 | potential := make(map[int]bool) 57 | for i := range data { 58 | potential[i] = true 59 | } 60 | for pos := 0; ; pos++ { 61 | candidates := len(potential) 62 | if candidates == 1 { 63 | var ret int 64 | for idx := range potential { 65 | for pos, set := range data[idx] { 66 | if set { 67 | ret += 1 << (11 - pos) 68 | } 69 | } 70 | } 71 | return ret 72 | } 73 | oneCount := 0 74 | for idx := range potential { 75 | if data[idx][pos] { 76 | oneCount++ 77 | } 78 | } 79 | matched := match(oneCount, candidates) 80 | for idx := range potential { 81 | if data[idx][pos] != matched { 82 | delete(potential, idx) 83 | } 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /2021/day05.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day05.input", "Relative file path to use as input.") 12 | 13 | type Coord struct { 14 | X, Y int 15 | } 16 | 17 | func main() { 18 | flag.Parse() 19 | bytes, err := ioutil.ReadFile(*inputFile) 20 | if err != nil { 21 | return 22 | } 23 | contents := string(bytes) 24 | split := strings.Split(contents, "\n") 25 | split = split[:len(split)-1] 26 | 27 | cloudsA := make(map[Coord]int) 28 | cloudsB := make(map[Coord]int) 29 | for _, s := range split { 30 | parts := strings.Split(s, " -> ") 31 | startRaw := strings.Split(parts[0], ",") 32 | endRaw := strings.Split(parts[1], ",") 33 | startX, _ := strconv.Atoi(startRaw[0]) 34 | startY, _ := strconv.Atoi(startRaw[1]) 35 | endX, _ := strconv.Atoi(endRaw[0]) 36 | endY, _ := strconv.Atoi(endRaw[1]) 37 | 38 | if startX == endX { 39 | if startY < endY { 40 | for y := startY; y <= endY; y++ { 41 | cloudsA[Coord{startX, y}]++ 42 | cloudsB[Coord{startX, y}]++ 43 | } 44 | } else { 45 | for y := startY; y >= endY; y-- { 46 | cloudsA[Coord{startX, y}]++ 47 | cloudsB[Coord{startX, y}]++ 48 | } 49 | } 50 | } else if startY == endY { 51 | if startX < endX { 52 | for x := startX; x <= endX; x++ { 53 | cloudsA[Coord{x, startY}]++ 54 | cloudsB[Coord{x, startY}]++ 55 | } 56 | } else { 57 | for x := startX; x >= endX; x-- { 58 | cloudsA[Coord{x, startY}]++ 59 | cloudsB[Coord{x, startY}]++ 60 | } 61 | } 62 | } else { 63 | if startX < endX { 64 | yIncr := 1 65 | if startY > endY { 66 | yIncr = -1 67 | } 68 | y := startY 69 | for x := startX; x <= endX; x++ { 70 | cloudsB[Coord{x, y}]++ 71 | y += yIncr 72 | } 73 | } else { 74 | yIncr := 1 75 | if startY > endY { 76 | yIncr = -1 77 | } 78 | y := startY 79 | for x := startX; x >= endX; x-- { 80 | cloudsB[Coord{x, y}]++ 81 | y += yIncr 82 | } 83 | } 84 | } 85 | } 86 | fmt.Println(countMultiples(cloudsA)) 87 | fmt.Println(countMultiples(cloudsB)) 88 | } 89 | 90 | func countMultiples(clouds map[Coord]int) int { 91 | var count int 92 | for _, v := range clouds { 93 | if v >= 2 { 94 | count++ 95 | } 96 | } 97 | return count 98 | } 99 | -------------------------------------------------------------------------------- /2021/day06.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day06.input", "Relative file path to use as input.") 12 | 13 | type FishClock map[int]int 14 | 15 | func main() { 16 | flag.Parse() 17 | bytes, err := ioutil.ReadFile(*inputFile) 18 | if err != nil { 19 | return 20 | } 21 | contents := string(bytes) 22 | split := strings.Split(strings.Split(contents, "\n")[0], ",") 23 | 24 | fish := make(FishClock) 25 | for _, s := range split { 26 | n, _ := strconv.Atoi(s) 27 | fish[n]++ 28 | } 29 | fmt.Println(fish.iter(80)) 30 | fmt.Println(fish.iter(256)) 31 | } 32 | 33 | func (fish FishClock) iter(maxDays int) int { 34 | for day := 1; day <= maxDays; day++ { 35 | oldFish := fish 36 | fish = make(FishClock) 37 | for k, v := range oldFish { 38 | if k == 0 { 39 | fish[6] += v 40 | fish[8] += v 41 | continue 42 | } 43 | fish[k-1] += v 44 | } 45 | } 46 | 47 | count := 0 48 | for _, v := range fish { 49 | count += v 50 | } 51 | return count 52 | } 53 | -------------------------------------------------------------------------------- /2021/day07.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day07.input", "Relative file path to use as input.") 12 | 13 | type CrabRave map[int]int 14 | 15 | func main() { 16 | flag.Parse() 17 | bytes, err := ioutil.ReadFile(*inputFile) 18 | if err != nil { 19 | return 20 | } 21 | contents := string(bytes) 22 | split := strings.Split(strings.Split(contents, "\n")[0], ",") 23 | 24 | lowest := 10000000000 25 | highest := -1 26 | crabs := make(CrabRave) 27 | for _, s := range split { 28 | n, _ := strconv.Atoi(s) 29 | crabs[n]++ 30 | if n > highest { 31 | highest = n 32 | } 33 | if n < lowest { 34 | lowest = n 35 | } 36 | } 37 | 38 | lowestFuel := len(split) * 1000000 39 | lowestFuelB := len(split) * 1000000 40 | for target := lowest; target <= highest; target++ { 41 | fuel := crabs.equalize(target) 42 | fuelB := crabs.equalizeB(target) 43 | if fuel < lowestFuel { 44 | lowestFuel = fuel 45 | } 46 | if fuelB < lowestFuelB { 47 | lowestFuelB = fuelB 48 | } 49 | } 50 | 51 | fmt.Println(lowestFuel) 52 | fmt.Println(lowestFuelB) 53 | } 54 | 55 | func (c CrabRave) equalize(pos int) int { 56 | fuel := 0 57 | for k, v := range c { 58 | if k > pos { 59 | fuel += v * (k - pos) 60 | } else { 61 | fuel += v * (pos - k) 62 | } 63 | } 64 | return fuel 65 | } 66 | 67 | func (c CrabRave) equalizeB(pos int) int { 68 | fuel := 0 69 | for k, v := range c { 70 | if k > pos { 71 | fuel += v * (k - pos) * (1 + k - pos) / 2 72 | } else { 73 | fuel += v * (pos - k) * (1 + pos - k) / 2 74 | } 75 | } 76 | return fuel 77 | } 78 | -------------------------------------------------------------------------------- /2021/day10.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "sort" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day10.input", "Relative file path to use as input.") 12 | 13 | func main() { 14 | flag.Parse() 15 | bytes, err := ioutil.ReadFile(*inputFile) 16 | if err != nil { 17 | return 18 | } 19 | contents := string(bytes) 20 | split := strings.Split(contents, "\n") 21 | split = split[:len(split)-1] 22 | 23 | scoreA := 0 24 | scoreB := make([]int, 0) 25 | outer: 26 | for _, line := range split { 27 | stack := make([]rune, 0) 28 | for _, c := range line { 29 | switch c { 30 | case '{': 31 | stack = append(stack, '}') 32 | case '(': 33 | stack = append(stack, ')') 34 | case '[': 35 | stack = append(stack, ']') 36 | case '<': 37 | stack = append(stack, '>') 38 | case '>': 39 | if len(stack) == 0 || stack[len(stack)-1] != c { 40 | scoreA += 25137 41 | continue outer 42 | } 43 | stack = stack[:len(stack)-1] 44 | case ']': 45 | if len(stack) == 0 || stack[len(stack)-1] != c { 46 | scoreA += 57 47 | continue outer 48 | } 49 | stack = stack[:len(stack)-1] 50 | case ')': 51 | if len(stack) == 0 || stack[len(stack)-1] != c { 52 | scoreA += 3 53 | continue outer 54 | } 55 | stack = stack[:len(stack)-1] 56 | case '}': 57 | if len(stack) == 0 || stack[len(stack)-1] != c { 58 | scoreA += 1197 59 | continue outer 60 | } 61 | stack = stack[:len(stack)-1] 62 | } 63 | } 64 | // Part B needs to be scored. 65 | intermediate := 0 66 | for i := len(stack) - 1; i >= 0; i-- { 67 | intermediate *= 5 68 | switch stack[i] { 69 | case ')': 70 | intermediate += 1 71 | case ']': 72 | intermediate += 2 73 | case '}': 74 | intermediate += 3 75 | case '>': 76 | intermediate += 4 77 | } 78 | } 79 | scoreB = append(scoreB, intermediate) 80 | } 81 | fmt.Println(scoreA) 82 | sort.Ints(scoreB) 83 | fmt.Println(scoreB[len(scoreB)/2]) 84 | } 85 | -------------------------------------------------------------------------------- /2021/day11.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strings" 8 | ) 9 | 10 | var inputFile = flag.String("inputFile", "inputs/day11.input", "Relative file path to use as input.") 11 | 12 | type OctoArray [10][10]int 13 | type FlashArray [10][10]bool 14 | 15 | func (o *OctoArray) increment(r, c int, flashed FlashArray) { 16 | if r < 0 || c < 0 || r >= 10 || c >= 10 || flashed[r][c] { 17 | return 18 | } 19 | o[r][c]++ 20 | } 21 | 22 | func (o *OctoArray) iterate() int { 23 | for r, row := range o { 24 | for c, _ := range row { 25 | o[r][c]++ 26 | } 27 | } 28 | flashes := 0 29 | var flashed FlashArray 30 | for { 31 | done := true 32 | for r, row := range o { 33 | for c, v := range row { 34 | if !flashed[r][c] && v > 9 { 35 | done = false 36 | o[r][c] = 0 37 | flashed[r][c] = true 38 | flashes++ 39 | 40 | for neighborR := r - 1; neighborR <= r+1; neighborR++ { 41 | for neighborC := c - 1; neighborC <= c+1; neighborC++ { 42 | if neighborR == r && neighborC == c { 43 | continue 44 | } 45 | o.increment(neighborR, neighborC, flashed) 46 | } 47 | } 48 | } 49 | } 50 | } 51 | if done { 52 | break 53 | } 54 | } 55 | return flashes 56 | } 57 | 58 | func main() { 59 | flag.Parse() 60 | bytes, err := ioutil.ReadFile(*inputFile) 61 | if err != nil { 62 | return 63 | } 64 | contents := string(bytes) 65 | split := strings.Split(contents, "\n") 66 | split = split[:len(split)-1] 67 | 68 | var octopodes OctoArray 69 | for r, l := range split { 70 | for c, v := range l { 71 | octopodes[r][c] = int(v - '0') 72 | } 73 | } 74 | 75 | flashes := 0 76 | for i := 1; true; i++ { 77 | current := octopodes.iterate() 78 | flashes += current 79 | if i == 100 { 80 | fmt.Println(flashes) 81 | } 82 | if current == 100 { 83 | fmt.Println(i) 84 | break 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /2021/day17.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day17.input", "Relative file path to use as input.") 12 | 13 | func main() { 14 | flag.Parse() 15 | 16 | bytes, err := ioutil.ReadFile(*inputFile) 17 | if err != nil { 18 | return 19 | } 20 | contents := string(bytes) 21 | split := strings.Split(contents, "\n") 22 | split = split[:len(split)-1] 23 | 24 | parts := strings.Split(split[0], " ") 25 | partsX := strings.Split(parts[2], "..") 26 | partsY := strings.Split(parts[3], "..") 27 | targetXMin, _ := strconv.Atoi(partsX[0][2:]) 28 | targetXMax, _ := strconv.Atoi(partsX[1][:len(partsX[1])-1]) 29 | targetYMin, _ := strconv.Atoi(partsY[0][2:]) 30 | targetYMax, _ := strconv.Atoi(partsY[1]) 31 | 32 | possibleXVelMin := 0 33 | for x := 0; x < targetXMin; x += possibleXVelMin { 34 | possibleXVelMin++ 35 | } 36 | 37 | highestY := 0 38 | hits := 0 39 | for yVel := targetYMin; yVel <= 0-targetYMin; yVel++ { 40 | for xVel := possibleXVelMin; xVel <= targetXMax; xVel++ { 41 | p := Probe{0, 0, xVel, yVel} 42 | highest := 0 43 | for p.CanStillHitTarget(targetXMin, targetXMax, targetYMin, targetYMax) { 44 | p.Tick() 45 | if p.VelY == 0 { 46 | highest = p.CoordY 47 | } 48 | if p.HitTarget(targetXMin, targetXMax, targetYMin, targetYMax) { 49 | hits++ 50 | if highest > highestY { 51 | highestY = highest 52 | } 53 | break 54 | } 55 | } 56 | } 57 | } 58 | fmt.Println(highestY) 59 | fmt.Println(hits) 60 | } 61 | 62 | type Probe struct { 63 | CoordX, CoordY int 64 | VelX, VelY int 65 | } 66 | 67 | func (p *Probe) Tick() { 68 | p.CoordX += p.VelX 69 | p.CoordY += p.VelY 70 | if p.VelX > 0 { 71 | p.VelX-- 72 | } 73 | p.VelY-- 74 | } 75 | 76 | func (p *Probe) CanStillHitTarget(xMin, xMax, yMin, yMax int) bool { 77 | return p.CoordX <= xMax && p.CoordY >= yMin 78 | } 79 | 80 | func (p *Probe) HitTarget(xMin, xMax, yMin, yMax int) bool { 81 | return p.CoordX <= xMax && p.CoordX >= xMin && p.CoordY <= yMax && p.CoordY >= yMin 82 | } 83 | -------------------------------------------------------------------------------- /2021/day20.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strings" 8 | ) 9 | 10 | var inputFile = flag.String("inputFile", "inputs/day20.input", "Relative file path to use as input.") 11 | 12 | type Coord struct { 13 | R, C int 14 | } 15 | 16 | type Grid map[Coord]bool 17 | 18 | func (g Grid) Count() int { 19 | var set int 20 | for _, v := range g { 21 | if v { 22 | set++ 23 | } 24 | } 25 | return set 26 | } 27 | 28 | func (g Grid) Tick(key [512]bool, i int) Grid { 29 | ret := make(Grid) 30 | var defaultFill bool 31 | 32 | if key[0] && !key[511] { 33 | defaultFill = (i%2 == 0) 34 | } else { 35 | defaultFill = false 36 | } 37 | 38 | min := -2 * i 39 | max := 100 + 2*i 40 | for r := min; r < max; r++ { 41 | for c := min; c < max; c++ { 42 | var lookup int 43 | for rOffset := -1; rOffset <= 1; rOffset++ { 44 | for cOffset := -1; cOffset <= 1; cOffset++ { 45 | lookup = lookup << 1 46 | if val, ok := g[Coord{r + rOffset, c + cOffset}]; ok && val { 47 | lookup++ 48 | } else if !ok && defaultFill { 49 | lookup++ 50 | } 51 | } 52 | } 53 | ret[Coord{r, c}] = key[lookup] 54 | } 55 | } 56 | return ret 57 | } 58 | 59 | func main() { 60 | flag.Parse() 61 | 62 | bytes, err := ioutil.ReadFile(*inputFile) 63 | if err != nil { 64 | return 65 | } 66 | contents := string(bytes) 67 | split := strings.Split(contents, "\n") 68 | split = split[:len(split)-1] 69 | 70 | var key [512]bool 71 | for i, c := range split[0] { 72 | switch c { 73 | case '.': 74 | // Pass 75 | case '#': 76 | key[i] = true 77 | default: 78 | fmt.Println("invalid character.") 79 | return 80 | } 81 | } 82 | 83 | values := make(Grid) 84 | for r, l := range split[2:] { 85 | for c, v := range l { 86 | switch v { 87 | case '.': 88 | values[Coord{r, c}] = false 89 | case '#': 90 | values[Coord{r, c}] = true 91 | default: 92 | fmt.Println("invalid character.") 93 | return 94 | } 95 | } 96 | } 97 | for i := 1; i <= 50; i++ { 98 | values = values.Tick(key, i) 99 | if i == 2 { 100 | fmt.Println(values.Count()) 101 | } 102 | } 103 | fmt.Println(values.Count()) 104 | } 105 | -------------------------------------------------------------------------------- /2021/day25.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strings" 8 | ) 9 | 10 | var inputFile = flag.String("inputFile", "inputs/day25.input", "Relative file path to use as input.") 11 | 12 | type Coord struct { 13 | Row, Col int 14 | } 15 | 16 | func (pos *Coord) Wrap(max Coord) { 17 | if pos.Row >= max.Row { 18 | pos.Row = 0 19 | } 20 | if pos.Col >= max.Col { 21 | pos.Col = 0 22 | } 23 | } 24 | 25 | // Be careful: empty = no cucumber, true = downward cucumber, false = rightward 26 | type Board map[Coord]bool 27 | 28 | func (b Board) Print(max Coord) { 29 | for r := 0; r < max.Row; r++ { 30 | for c := 0; c < max.Col; c++ { 31 | if south, occupied := b[Coord{r, c}]; !occupied { 32 | fmt.Printf(".") 33 | } else if south { 34 | fmt.Printf("v") 35 | } else { 36 | fmt.Printf(">") 37 | } 38 | } 39 | fmt.Println() 40 | } 41 | } 42 | 43 | func main() { 44 | flag.Parse() 45 | 46 | bytes, err := ioutil.ReadFile(*inputFile) 47 | if err != nil { 48 | return 49 | } 50 | contents := string(bytes) 51 | split := strings.Split(contents, "\n") 52 | split = split[:len(split)-1] 53 | 54 | board := make(Board) 55 | max := Coord{len(split), len(split[0])} 56 | for r, line := range split { 57 | for c, elem := range line { 58 | switch elem { 59 | case '.': 60 | // Do nothing. 61 | case '>': 62 | board[Coord{r, c}] = false 63 | case 'v': 64 | board[Coord{r, c}] = true 65 | } 66 | } 67 | } 68 | var toggle bool 69 | var prevMoved bool 70 | for steps := 1; ; steps++ { 71 | var anyMoved bool 72 | next := make(Board) 73 | for src, south := range board { 74 | // During a single step, the east-facing herd moves first, 75 | if south == toggle { 76 | dst := src 77 | if toggle { 78 | dst.Row++ 79 | } else { 80 | dst.Col++ 81 | } 82 | dst.Wrap(max) 83 | if _, occupied := board[dst]; !occupied { 84 | next[dst] = south 85 | anyMoved = true 86 | } else { 87 | next[src] = south 88 | } 89 | } else { 90 | next[src] = south 91 | } 92 | } 93 | board = next 94 | toggle = !toggle 95 | if !anyMoved && !prevMoved { 96 | fmt.Println((steps + 1) / 2) 97 | break 98 | } 99 | prevMoved = anyMoved 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /2021/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/lizthegrey/adventofcode/2021 2 | 3 | go 1.18 4 | 5 | require ( 6 | go.opentelemetry.io/otel v1.21.0 7 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 8 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 9 | go.opentelemetry.io/otel/sdk v1.21.0 10 | google.golang.org/grpc v1.59.0 11 | ) 12 | 13 | require ( 14 | github.com/cenkalti/backoff/v4 v4.2.1 // indirect 15 | github.com/go-logr/logr v1.3.0 // indirect 16 | github.com/go-logr/stdr v1.2.2 // indirect 17 | github.com/golang/protobuf v1.5.3 // indirect 18 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1 // indirect 19 | go.opentelemetry.io/otel/metric v1.21.0 // indirect 20 | go.opentelemetry.io/otel/trace v1.21.0 // indirect 21 | go.opentelemetry.io/proto/otlp v1.0.0 // indirect 22 | golang.org/x/net v0.19.0 // indirect 23 | golang.org/x/sys v0.15.0 // indirect 24 | golang.org/x/text v0.14.0 // indirect 25 | google.golang.org/genproto v0.0.0-20231127180814-3a041ad873d4 // indirect 26 | google.golang.org/genproto/googleapis/api v0.0.0-20231127180814-3a041ad873d4 // indirect 27 | google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4 // indirect 28 | google.golang.org/protobuf v1.31.0 // indirect 29 | ) 30 | -------------------------------------------------------------------------------- /2021/trace/trace.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "os" 7 | 8 | "google.golang.org/grpc/credentials" 9 | 10 | "go.opentelemetry.io/otel" 11 | otlp "go.opentelemetry.io/otel/exporters/otlp/otlptrace" 12 | otlpgrpc "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" 13 | "go.opentelemetry.io/otel/propagation" 14 | "go.opentelemetry.io/otel/sdk/resource" 15 | sdktrace "go.opentelemetry.io/otel/sdk/trace" 16 | semconv "go.opentelemetry.io/otel/semconv/v1.4.0" 17 | ) 18 | 19 | func InitializeTracing(ctx context.Context) (*otlp.Exporter, *sdktrace.TracerProvider) { 20 | serviceName := "lizthegrey-adventofcode" 21 | 22 | // honeycomb OTLP gRPC exporter 23 | apikey, _ := os.LookupEnv("HONEYCOMB_API_KEY") 24 | dataset, _ := os.LookupEnv("HONEYCOMB_DATASET") 25 | driver := otlpgrpc.NewClient( 26 | otlpgrpc.WithTLSCredentials(credentials.NewClientTLSFromCert(nil, "")), 27 | otlpgrpc.WithEndpoint("api.honeycomb.io:443"), 28 | otlpgrpc.WithHeaders(map[string]string{ 29 | "x-honeycomb-team": apikey, 30 | "x-honeycomb-dataset": dataset, 31 | }), 32 | ) 33 | hny, err := otlp.New(ctx, driver) 34 | if err != nil { 35 | log.Fatal(err) 36 | } 37 | 38 | tp := sdktrace.NewTracerProvider( 39 | sdktrace.WithSampler(sdktrace.AlwaysSample()), 40 | sdktrace.WithResource(resource.NewWithAttributes(semconv.SchemaURL, semconv.ServiceNameKey.String(serviceName))), 41 | sdktrace.WithBatcher(hny)) 42 | if err != nil { 43 | log.Fatal(err) 44 | } 45 | otel.SetTracerProvider(tp) 46 | otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{})) 47 | 48 | return hny, tp 49 | } 50 | -------------------------------------------------------------------------------- /2022/.gitignore: -------------------------------------------------------------------------------- 1 | *.input 2 | -------------------------------------------------------------------------------- /2022/day01.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "sort" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | var inputFile = flag.String("inputFile", "inputs/day01.input", "Relative file path to use as input.") 13 | 14 | func main() { 15 | flag.Parse() 16 | bytes, err := ioutil.ReadFile(*inputFile) 17 | if err != nil { 18 | return 19 | } 20 | contents := string(bytes) 21 | split := strings.Split(contents, "\n") 22 | 23 | // part A 24 | var highest int 25 | var current int 26 | for _, s := range split { 27 | if s == "" { 28 | if highest < current { 29 | highest = current 30 | } 31 | current = 0 32 | } 33 | i, _ := strconv.Atoi(s) 34 | current += i 35 | } 36 | if highest < current { 37 | highest = current 38 | } 39 | fmt.Println(highest) 40 | 41 | // part B 42 | var found []int 43 | current = 0 44 | for _, s := range split { 45 | if s == "" { 46 | found = append(found, current) 47 | current = 0 48 | } 49 | i, _ := strconv.Atoi(s) 50 | current += i 51 | } 52 | found = append(found, current) 53 | sort.Ints(found) 54 | var sum int 55 | for i := 1; i <= 3; i++ { 56 | sum += found[len(found)-i] 57 | } 58 | fmt.Println(sum) 59 | } 60 | -------------------------------------------------------------------------------- /2022/day02.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strings" 8 | ) 9 | 10 | var inputFile = flag.String("inputFile", "inputs/day02.input", "Relative file path to use as input.") 11 | 12 | func main() { 13 | flag.Parse() 14 | bytes, err := ioutil.ReadFile(*inputFile) 15 | if err != nil { 16 | return 17 | } 18 | contents := string(bytes) 19 | split := strings.Split(contents, "\n") 20 | 21 | // part A 22 | var score int 23 | for _, s := range split { 24 | if s == "" { 25 | break 26 | } 27 | vals := strings.Split(s, " ") 28 | opp := vals[0] 29 | mine := vals[1] 30 | switch mine { 31 | case "X": 32 | score += 1 33 | case "Y": 34 | score += 2 35 | case "Z": 36 | score += 3 37 | } 38 | switch opp { 39 | case "A": 40 | switch mine { 41 | case "X": 42 | score += 3 43 | case "Y": 44 | score += 6 45 | case "Z": 46 | score += 0 47 | } 48 | case "B": 49 | switch mine { 50 | case "X": 51 | score += 0 52 | case "Y": 53 | score += 3 54 | case "Z": 55 | score += 6 56 | } 57 | case "C": 58 | switch mine { 59 | case "X": 60 | score += 6 61 | case "Y": 62 | score += 0 63 | case "Z": 64 | score += 3 65 | } 66 | } 67 | } 68 | fmt.Println(score) 69 | 70 | // part B 71 | score = 0 72 | for _, s := range split { 73 | if s == "" { 74 | break 75 | } 76 | vals := strings.Split(s, " ") 77 | opp := vals[0] 78 | outcome := vals[1] 79 | switch outcome { 80 | case "X": 81 | score += 0 82 | case "Y": 83 | score += 3 84 | case "Z": 85 | score += 6 86 | } 87 | switch opp { 88 | case "A": 89 | switch outcome { 90 | case "X": 91 | score += 3 92 | case "Y": 93 | score += 1 94 | case "Z": 95 | score += 2 96 | } 97 | case "B": 98 | switch outcome { 99 | case "X": 100 | score += 1 101 | case "Y": 102 | score += 2 103 | case "Z": 104 | score += 3 105 | } 106 | case "C": 107 | switch outcome { 108 | case "X": 109 | score += 2 110 | case "Y": 111 | score += 3 112 | case "Z": 113 | score += 1 114 | } 115 | } 116 | } 117 | fmt.Println(score) 118 | } 119 | -------------------------------------------------------------------------------- /2022/day03.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strings" 8 | ) 9 | 10 | var inputFile = flag.String("inputFile", "inputs/day03.input", "Relative file path to use as input.") 11 | 12 | func main() { 13 | flag.Parse() 14 | bytes, err := ioutil.ReadFile(*inputFile) 15 | if err != nil { 16 | return 17 | } 18 | contents := string(bytes) 19 | split := strings.Split(contents, "\n") 20 | 21 | // part A 22 | var priorities int 23 | for _, s := range split { 24 | if s == "" { 25 | break 26 | } 27 | inFirst := make(map[rune]bool) 28 | first := s[0 : len(s)/2] 29 | second := s[len(s)/2 : len(s)] 30 | for _, k := range first { 31 | inFirst[k] = true 32 | } 33 | for _, k := range second { 34 | if inFirst[k] { 35 | if k >= 'a' && k <= 'z' { 36 | priorities += int(k-'a') + 1 37 | } else { 38 | priorities += int(k-'A') + 27 39 | } 40 | break 41 | } 42 | } 43 | } 44 | fmt.Println(priorities) 45 | 46 | // part B 47 | priorities = 0 48 | for i := 0; i < len(split)-1; i += 3 { 49 | found := make(map[rune]int) 50 | for j := 0; j < 3; j++ { 51 | local := make(map[rune]bool) 52 | for _, k := range split[i+j] { 53 | if local[k] { 54 | continue 55 | } 56 | local[k] = true 57 | found[k]++ 58 | } 59 | } 60 | for k, v := range found { 61 | if v == 3 { 62 | if k >= 'a' && k <= 'z' { 63 | priorities += int(k-'a') + 1 64 | } else { 65 | priorities += int(k-'A') + 27 66 | } 67 | break 68 | } 69 | } 70 | } 71 | fmt.Println(priorities) 72 | } 73 | -------------------------------------------------------------------------------- /2022/day04.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day04.input", "Relative file path to use as input.") 12 | 13 | func main() { 14 | flag.Parse() 15 | bytes, err := ioutil.ReadFile(*inputFile) 16 | if err != nil { 17 | return 18 | } 19 | contents := string(bytes) 20 | split := strings.Split(contents, "\n") 21 | 22 | var countA, countB int 23 | for _, s := range split[:len(split)-1] { 24 | pairs := strings.Split(s, ",") 25 | first := strings.Split(pairs[0], "-") 26 | second := strings.Split(pairs[1], "-") 27 | firstStart, _ := strconv.Atoi(first[0]) 28 | firstEnd, _ := strconv.Atoi(first[1]) 29 | secondStart, _ := strconv.Atoi(second[0]) 30 | secondEnd, _ := strconv.Atoi(second[1]) 31 | if firstStart >= secondStart && firstEnd <= secondEnd { 32 | countA++ 33 | countB++ 34 | continue 35 | } 36 | if firstStart <= secondStart && firstEnd >= secondEnd { 37 | countA++ 38 | countB++ 39 | continue 40 | } 41 | // Not a complete overlap, but check for partial overlap 42 | if firstStart >= secondStart && firstStart <= secondEnd { 43 | countB++ 44 | continue 45 | } 46 | if secondStart >= firstStart && secondStart <= firstEnd { 47 | countB++ 48 | continue 49 | } 50 | if firstEnd >= secondStart && firstEnd <= secondEnd { 51 | countB++ 52 | continue 53 | } 54 | if secondEnd >= firstStart && secondEnd <= firstEnd { 55 | countB++ 56 | continue 57 | } 58 | } 59 | fmt.Println(countA) 60 | fmt.Println(countB) 61 | } 62 | -------------------------------------------------------------------------------- /2022/day05.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day05.input", "Relative file path to use as input.") 12 | 13 | type stacks [][]byte 14 | 15 | func (s stacks) insertBottom(col int, val byte) stacks { 16 | if len(s) <= col { 17 | backing := make(stacks, col+1) 18 | copy(backing, s) 19 | s = backing 20 | } 21 | if s[col] == nil { 22 | s[col] = make([]byte, 0) 23 | } 24 | s[col] = append([]byte{val}, s[col]...) 25 | return s 26 | } 27 | 28 | func (s stacks) move(from, to, count int) { 29 | vals := s[from][len(s[from])-count : len(s[from])] 30 | s[from] = s[from][:len(s[from])-count] 31 | s[to] = append(s[to], vals...) 32 | } 33 | 34 | func (s stacks) printTop() { 35 | for i := 0; i < len(s); i++ { 36 | if len(s[i]) == 0 { 37 | fmt.Printf(" ") 38 | continue 39 | } 40 | fmt.Printf("%c", s[i][len(s[i])-1]) 41 | } 42 | fmt.Println() 43 | } 44 | 45 | func main() { 46 | flag.Parse() 47 | bytes, err := ioutil.ReadFile(*inputFile) 48 | if err != nil { 49 | return 50 | } 51 | contents := string(bytes) 52 | split := strings.Split(contents, "\n") 53 | 54 | var crates, cratesB stacks 55 | for _, s := range split[:len(split)-1] { 56 | if strings.Contains(s, "[") { 57 | for i := 1; i < len(s); i += 4 { 58 | if s[i] != ' ' { 59 | crates = crates.insertBottom(i/4, s[i]) 60 | cratesB = cratesB.insertBottom(i/4, s[i]) 61 | } 62 | } 63 | } else if len(s) > 5 && s[0:4] == "move" { 64 | parts := strings.Split(s, " ") 65 | count, _ := strconv.Atoi(parts[1]) 66 | from, _ := strconv.Atoi(parts[3]) 67 | to, _ := strconv.Atoi(parts[5]) 68 | for i := 0; i < count; i++ { 69 | crates.move(from-1, to-1, 1) 70 | } 71 | cratesB.move(from-1, to-1, count) 72 | } 73 | } 74 | crates.printTop() 75 | cratesB.printTop() 76 | } 77 | -------------------------------------------------------------------------------- /2022/day06.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | ) 8 | 9 | var inputFile = flag.String("inputFile", "inputs/day06.input", "Relative file path to use as input.") 10 | 11 | func main() { 12 | flag.Parse() 13 | bytes, err := ioutil.ReadFile(*inputFile) 14 | if err != nil { 15 | return 16 | } 17 | contents := bytes[:len(bytes)-1] 18 | // part A 19 | fmt.Println(check(contents, 4)) 20 | // part B 21 | fmt.Println(check(contents, 14)) 22 | } 23 | 24 | func check(contents []byte, num int) int { 25 | for i := 0; i < len(contents)-num; i++ { 26 | seen := make(map[byte]bool) 27 | for j := 0; j < num; j++ { 28 | seen[contents[i+j]] = true 29 | } 30 | if len(seen) == num { 31 | return i + num 32 | } 33 | } 34 | return -1 35 | } 36 | -------------------------------------------------------------------------------- /2022/day07.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day07.input", "Relative file path to use as input.") 12 | 13 | type dir struct { 14 | parent *dir 15 | children []*dir 16 | size int 17 | } 18 | 19 | func (d dir) walk(cb func(dir)) { 20 | cb(d) 21 | for _, v := range d.children { 22 | v.walk(cb) 23 | } 24 | } 25 | 26 | func main() { 27 | flag.Parse() 28 | bytes, err := ioutil.ReadFile(*inputFile) 29 | if err != nil { 30 | return 31 | } 32 | contents := string(bytes) 33 | split := strings.Split(contents, "\n") 34 | 35 | var root dir 36 | cur := &root 37 | for _, s := range split[1 : len(split)-1] { 38 | tokens := strings.Split(s, " ") 39 | if tokens[0] == "$" { 40 | switch tokens[1] { 41 | case "cd": 42 | target := tokens[2] 43 | if target == ".." { 44 | cur = cur.parent 45 | } else { 46 | // Lazy-create the directory once we recurse into it. 47 | var child dir 48 | child.parent = cur 49 | cur.children = append(cur.children, &child) 50 | cur = &child 51 | } 52 | case "ls": 53 | // This will be handled by the else case. 54 | continue 55 | default: 56 | fmt.Printf("Bad command: %s\n", tokens[1]) 57 | return 58 | } 59 | } else { 60 | // This is the result of listing a directory. 61 | if tokens[0] == "dir" { 62 | // Created when we recurse into it. Do nothing. 63 | } else { 64 | size, err := strconv.Atoi(tokens[0]) 65 | if err != nil { 66 | fmt.Printf("Failed to parse size: %v\n", err) 67 | return 68 | } 69 | update := cur 70 | for update != nil { 71 | update.size += size 72 | update = update.parent 73 | } 74 | } 75 | } 76 | } 77 | 78 | // part A 79 | var total int 80 | root.walk(func(d dir) { 81 | if d.size <= 100000 { 82 | total += d.size 83 | } 84 | }) 85 | fmt.Println(total) 86 | 87 | // part B 88 | var smallest int 89 | root.walk(func(d dir) { 90 | if root.size-d.size < 70000000-30000000 { 91 | if smallest == 0 || d.size < smallest { 92 | smallest = d.size 93 | } 94 | } 95 | }) 96 | fmt.Println(smallest) 97 | } 98 | -------------------------------------------------------------------------------- /2022/day09.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day09.input", "Relative file path to use as input.") 12 | 13 | type coord struct { 14 | x, y int 15 | } 16 | 17 | func (c coord) dragged(o coord) coord { 18 | diffX := o.x - c.x 19 | diffY := o.y - c.y 20 | if diffX < 2 && diffX > -2 && diffY < 2 && diffY > -2 { 21 | // Within +/- 1 in each dimension, stay in place. 22 | return c 23 | } 24 | // We will always move 1 in the direction that we were off by 2. 25 | // and we will also move 1 in the other direction if there is a diff. 26 | if diffX > 0 { 27 | c.x += (diffX + 1) / 2 28 | } else { 29 | c.x += (diffX - 1) / 2 30 | } 31 | if diffY > 0 { 32 | c.y += (diffY + 1) / 2 33 | } else { 34 | c.y += (diffY - 1) / 2 35 | } 36 | return c 37 | } 38 | 39 | func main() { 40 | flag.Parse() 41 | bytes, err := ioutil.ReadFile(*inputFile) 42 | if err != nil { 43 | return 44 | } 45 | contents := string(bytes) 46 | split := strings.Split(contents, "\n") 47 | 48 | // part A 49 | visited := make(map[coord]bool) 50 | var head, tail coord 51 | visited[tail] = true 52 | for _, s := range split[:len(split)-1] { 53 | c, _ := strconv.Atoi(s[2:]) 54 | for i := 0; i < c; i++ { 55 | switch s[0] { 56 | case 'U': 57 | head.y++ 58 | case 'D': 59 | head.y-- 60 | case 'L': 61 | head.x-- 62 | case 'R': 63 | head.x++ 64 | } 65 | tail = tail.dragged(head) 66 | visited[tail] = true 67 | } 68 | } 69 | fmt.Println(len(visited)) 70 | 71 | // part B 72 | visited = make(map[coord]bool) 73 | var positions [10]coord 74 | visited[positions[len(positions)-1]] = true 75 | for _, s := range split[:len(split)-1] { 76 | c, _ := strconv.Atoi(s[2:]) 77 | for i := 0; i < c; i++ { 78 | switch s[0] { 79 | case 'U': 80 | positions[0].y++ 81 | case 'D': 82 | positions[0].y-- 83 | case 'L': 84 | positions[0].x-- 85 | case 'R': 86 | positions[0].x++ 87 | } 88 | for j := 1; j < len(positions); j++ { 89 | positions[j] = positions[j].dragged(positions[j-1]) 90 | } 91 | visited[positions[len(positions)-1]] = true 92 | } 93 | } 94 | fmt.Println(len(visited)) 95 | } 96 | -------------------------------------------------------------------------------- /2022/day10.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day10.input", "Relative file path to use as input.") 12 | 13 | func main() { 14 | flag.Parse() 15 | bytes, err := ioutil.ReadFile(*inputFile) 16 | if err != nil { 17 | return 18 | } 19 | contents := string(bytes) 20 | split := strings.Split(contents, "\n") 21 | 22 | var strength int 23 | var pixels [240]bool 24 | x := 1 25 | pc := 1 26 | for _, s := range split[:len(split)-1] { 27 | if pc%40 == 20 { 28 | strength += x * pc 29 | } 30 | 31 | if x >= ((pc-1)%40)-1 && x <= ((pc-1)%40)+1 { 32 | pixels[pc-1] = true 33 | } 34 | 35 | instr := strings.Split(s, " ") 36 | switch instr[0] { 37 | case "addx": 38 | val, _ := strconv.Atoi(instr[1]) 39 | pc++ 40 | if pc%40 == 20 { 41 | strength += x * pc 42 | } 43 | if x >= ((pc-1)%40)-1 && x <= ((pc-1)%40)+1 { 44 | pixels[pc-1] = true 45 | } 46 | pc++ 47 | x += val 48 | case "noop": 49 | pc++ 50 | } 51 | } 52 | 53 | // part A 54 | fmt.Println(strength) 55 | 56 | // part B 57 | for i, v := range pixels { 58 | if v { 59 | fmt.Printf("#") 60 | } else { 61 | fmt.Printf(".") 62 | } 63 | if i%40 == 39 { 64 | fmt.Println() 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /2022/day18.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day18.input", "Relative file path to use as input.") 12 | 13 | type coord struct { 14 | x, y, z int 15 | } 16 | 17 | func (c coord) neighbours() []coord { 18 | return []coord{ 19 | {c.x + 1, c.y + 0, c.z + 0}, 20 | {c.x - 1, c.y + 0, c.z + 0}, 21 | {c.x + 0, c.y + 1, c.z + 0}, 22 | {c.x + 0, c.y - 1, c.z + 0}, 23 | {c.x + 0, c.y + 0, c.z + 1}, 24 | {c.x + 0, c.y + 0, c.z - 1}, 25 | } 26 | } 27 | 28 | func (c coord) inBounds(min, max int) bool { 29 | return c.x >= min && c.x <= max && c.y >= min && c.y <= max && c.z >= min && c.z <= max 30 | } 31 | 32 | type model map[coord]bool 33 | 34 | func main() { 35 | flag.Parse() 36 | bytes, err := ioutil.ReadFile(*inputFile) 37 | if err != nil { 38 | return 39 | } 40 | contents := string(bytes) 41 | split := strings.Split(contents, "\n") 42 | 43 | droplet := make(model) 44 | for _, s := range split[:len(split)-1] { 45 | parts := strings.Split(s, ",") 46 | x, _ := strconv.Atoi(parts[0]) 47 | y, _ := strconv.Atoi(parts[1]) 48 | z, _ := strconv.Atoi(parts[2]) 49 | droplet[coord{x, y, z}] = true 50 | } 51 | 52 | // part A 53 | var totalArea int 54 | for k := range droplet { 55 | for _, n := range k.neighbours() { 56 | if !droplet[n] { 57 | totalArea++ 58 | } 59 | } 60 | } 61 | fmt.Println(totalArea) 62 | 63 | // part B 64 | // Grow a void starting from the walls inward. 65 | // Looking at the input, it ranges from [0,19] but not negative numbers or numbers above 20. 66 | // We can start from any points outside the sculpture and grow from there. 67 | seed := coord{-1, -1, -1} 68 | void := make(model) 69 | q := []coord{seed} 70 | for len(q) != 0 { 71 | head := q[0] 72 | q = q[1:] 73 | if void[head] { 74 | continue 75 | } 76 | void[head] = true 77 | for _, n := range head.neighbours() { 78 | if !void[n] && !droplet[n] && n.inBounds(-1, 20) { 79 | q = append(q, n) 80 | } 81 | } 82 | } 83 | var exteriorArea int 84 | for k := range void { 85 | for _, n := range k.neighbours() { 86 | if droplet[n] { 87 | exteriorArea++ 88 | } 89 | } 90 | } 91 | fmt.Println(exteriorArea) 92 | } 93 | -------------------------------------------------------------------------------- /2022/day20.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "container/ring" 5 | "flag" 6 | "fmt" 7 | "io/ioutil" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | var inputFile = flag.String("inputFile", "inputs/day20.input", "Relative file path to use as input.") 13 | 14 | const key = 811589153 15 | 16 | func main() { 17 | flag.Parse() 18 | bytes, err := ioutil.ReadFile(*inputFile) 19 | if err != nil { 20 | return 21 | } 22 | contents := string(bytes) 23 | input := strings.Split(contents, "\n") 24 | input = input[:len(input)-1] 25 | 26 | // Part A 27 | fmt.Println(run(input, 1, 1)) 28 | // Part B 29 | fmt.Println(run(input, key, 10)) 30 | } 31 | 32 | func run(input []string, k int, rounds int) int { 33 | ringSize := len(input) 34 | valIndex := make(map[int]*ring.Ring) 35 | seqIndex := make([]*ring.Ring, 0, ringSize) 36 | 37 | message := ring.New(ringSize) 38 | for _, c := range input { 39 | n, err := strconv.Atoi(string(c)) 40 | n *= k 41 | if err != nil { 42 | fmt.Printf("Failed to parse %s\n", input) 43 | break 44 | } 45 | message.Value = n 46 | valIndex[n] = message 47 | seqIndex = append(seqIndex, message) 48 | message = message.Next() 49 | } 50 | 51 | for i := 0; i < rounds; i++ { 52 | for _, cur := range seqIndex { 53 | val := cur.Value.(int) 54 | if val == 0 { 55 | continue 56 | } 57 | prev := cur.Move(-1) 58 | prev.Unlink(1) 59 | prev.Move(val % (len(seqIndex) - 1)).Link(cur) 60 | } 61 | } 62 | 63 | var total int 64 | cur := valIndex[0] 65 | for i := 1; i <= 3*1000; i++ { 66 | cur = cur.Next() 67 | if i%1000 == 0 { 68 | total += cur.Value.(int) 69 | } 70 | } 71 | return total 72 | } 73 | -------------------------------------------------------------------------------- /2022/day25.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strings" 8 | ) 9 | 10 | var inputFile = flag.String("inputFile", "inputs/day25.input", "Relative file path to use as input.") 11 | 12 | func main() { 13 | flag.Parse() 14 | bytes, err := ioutil.ReadFile(*inputFile) 15 | if err != nil { 16 | return 17 | } 18 | contents := string(bytes) 19 | split := strings.Split(contents, "\n") 20 | 21 | // there's only a part A. part B is completing all the other puzzles. 22 | var sum int 23 | for _, s := range split[:len(split)-1] { 24 | var num int 25 | place := 1 26 | for i := len(s) - 1; i >= 0; i -= 1 { 27 | switch s[i] { 28 | case '=': 29 | num -= 2 * place 30 | case '-': 31 | num -= place 32 | case '0': 33 | // Nothing to add or remove. 34 | case '1': 35 | num += place 36 | case '2': 37 | num += 2 * place 38 | } 39 | place *= 5 40 | } 41 | sum += num 42 | } 43 | fmt.Println(sum) 44 | } 45 | -------------------------------------------------------------------------------- /2022/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/lizthegrey/adventofcode/2022 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /2022/heapq/heapqueue.go: -------------------------------------------------------------------------------- 1 | package heapq 2 | 3 | import ( 4 | "container/heap" 5 | ) 6 | 7 | // Like the heap construct, but with generic types and an upsert operation. 8 | type HeapQueue[T comparable] struct { 9 | elems *[]T 10 | score map[T]int 11 | positions map[T]int 12 | } 13 | 14 | func New[T comparable]() *HeapQueue[T] { 15 | return &HeapQueue[T]{ 16 | elems: &[]T{}, 17 | score: make(map[T]int), 18 | positions: make(map[T]int), 19 | } 20 | } 21 | 22 | func (h HeapQueue[T]) Len() int { return len(*h.elems) } 23 | func (h HeapQueue[T]) Less(i, j int) bool { return h.score[(*h.elems)[i]] < h.score[(*h.elems)[j]] } 24 | func (h *HeapQueue[T]) Swap(i, j int) { 25 | h.positions[(*h.elems)[i]], h.positions[(*h.elems)[j]] = h.positions[(*h.elems)[j]], h.positions[(*h.elems)[i]] 26 | (*h.elems)[i], (*h.elems)[j] = (*h.elems)[j], (*h.elems)[i] 27 | } 28 | 29 | func (h *HeapQueue[T]) Push(x interface{}) { 30 | h.positions[x.(T)] = len(*h.elems) 31 | *h.elems = append(*h.elems, x.(T)) 32 | } 33 | 34 | func (h *HeapQueue[T]) PopSafe() T { 35 | return heap.Pop(h).(T) 36 | } 37 | 38 | func (h *HeapQueue[T]) Pop() interface{} { 39 | old := *h.elems 40 | n := len(old) 41 | x := old[n-1] 42 | *h.elems = old[0 : n-1] 43 | delete(h.positions, x) 44 | return x 45 | } 46 | 47 | func (h *HeapQueue[T]) Upsert(n T, score int) { 48 | h.score[n] = score 49 | if pos, ok := h.positions[n]; !ok { 50 | heap.Push(h, n) 51 | } else { 52 | heap.Fix(h, pos) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /2023/.gitignore: -------------------------------------------------------------------------------- 1 | *.input 2 | -------------------------------------------------------------------------------- /2023/day01.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strings" 8 | ) 9 | 10 | var inputFile = flag.String("inputFile", "inputs/day01.input", "Relative file path to use as input.") 11 | 12 | var digits = map[string]int{ 13 | "zero": 0, 14 | "one": 1, 15 | "two": 2, 16 | "three": 3, 17 | "four": 4, 18 | "five": 5, 19 | "six": 6, 20 | "seven": 7, 21 | "eight": 8, 22 | "nine": 9, 23 | } 24 | 25 | func main() { 26 | flag.Parse() 27 | bytes, err := ioutil.ReadFile(*inputFile) 28 | if err != nil { 29 | return 30 | } 31 | contents := string(bytes) 32 | split := strings.Split(contents, "\n") 33 | 34 | fmt.Println(process(split[:len(split)-1], false)) 35 | fmt.Println(process(split[:len(split)-1], true)) 36 | } 37 | 38 | func process(lines []string, partB bool) int { 39 | sum := 0 40 | for _, s := range lines { 41 | first := -1 42 | last := -1 43 | for i := 0; i < len(s); i++ { 44 | if c := s[i]; c >= '0' && c <= '9' { 45 | digit := int(c - '0') 46 | if first == -1 { 47 | first = digit 48 | } 49 | last = digit 50 | } else if partB { 51 | for str, digit := range digits { 52 | if i+len(str) <= len(s) && s[i:i+len(str)] == str { 53 | if first == -1 { 54 | first = digit 55 | } 56 | last = digit 57 | break 58 | } 59 | } 60 | } 61 | } 62 | sum += first*10 + last 63 | } 64 | return sum 65 | } 66 | -------------------------------------------------------------------------------- /2023/day02.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | var inputFile = flag.String("inputFile", "inputs/day02.input", "Relative file path to use as input.") 13 | 14 | var max = map[string]int{ 15 | "red": 12, 16 | "green": 13, 17 | "blue": 14, 18 | } 19 | 20 | func main() { 21 | flag.Parse() 22 | bytes, err := ioutil.ReadFile(*inputFile) 23 | if err != nil { 24 | return 25 | } 26 | contents := string(bytes) 27 | split := strings.Split(contents, "\n") 28 | 29 | sum := 0 30 | powers := 0 31 | for i, s := range split[:len(split)-1] { 32 | results := strings.Split(strings.Split(s, ": ")[1], "; ") 33 | possible := true 34 | seen := make(map[string]int) 35 | for _, r := range results { 36 | for _, v := range strings.Split(r, ", ") { 37 | parts := strings.Split(v, " ") 38 | count, err := strconv.Atoi(parts[0]) 39 | if err != nil { 40 | log.Fatalf("%s failed: %v", parts[0], err) 41 | } 42 | colour := parts[1] 43 | if max[colour] < count { 44 | possible = false 45 | } 46 | if seen[colour] < count { 47 | seen[colour] = count 48 | } 49 | } 50 | } 51 | if possible { 52 | sum += i + 1 53 | } 54 | power := 1 55 | for _, v := range seen { 56 | power *= v 57 | } 58 | powers += power 59 | } 60 | fmt.Println(sum) 61 | fmt.Println(powers) 62 | } 63 | -------------------------------------------------------------------------------- /2023/day03.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strings" 8 | ) 9 | 10 | var inputFile = flag.String("inputFile", "inputs/day03.input", "Relative file path to use as input.") 11 | 12 | type Coord struct { 13 | X, Y int 14 | } 15 | 16 | func main() { 17 | flag.Parse() 18 | bytes, err := ioutil.ReadFile(*inputFile) 19 | if err != nil { 20 | return 21 | } 22 | contents := string(bytes) 23 | split := strings.Split(contents, "\n") 24 | 25 | sum := 0 26 | grid := make(map[Coord]rune) 27 | gears := make(map[Coord][]int) 28 | var maxX, maxY int 29 | for y, row := range split[:len(split)-1] { 30 | for x, c := range row { 31 | grid[Coord{x, y}] = c 32 | if x > maxX { 33 | maxX = x 34 | } 35 | } 36 | if y > maxY { 37 | maxY = y 38 | } 39 | } 40 | for y := 0; y <= maxY; y++ { 41 | for x := 0; x <= maxX; { 42 | var length, num int 43 | for { 44 | c := grid[Coord{x + length, y}] 45 | if c < '0' || c > '9' { 46 | break 47 | } 48 | num = 10*num + int(c-'0') 49 | length++ 50 | } 51 | if length == 0 { 52 | x++ 53 | continue 54 | } 55 | var added bool 56 | for j := y - 1; j <= y+1; j++ { 57 | for i := x - 1; i <= x+length; i++ { 58 | nCoord := Coord{i, j} 59 | neigh := grid[nCoord] 60 | if neigh == 0 || neigh == '.' { 61 | continue 62 | } 63 | if neigh >= '0' && neigh <= '9' { 64 | continue 65 | } 66 | if !added { 67 | sum += num 68 | added = true 69 | } 70 | if neigh == '*' { 71 | gears[nCoord] = append(gears[nCoord], num) 72 | } 73 | } 74 | } 75 | x += length 76 | } 77 | } 78 | fmt.Println(sum) 79 | 80 | ratio := 0 81 | for _, v := range gears { 82 | if len(v) == 2 { 83 | ratio += v[0] * v[1] 84 | } 85 | } 86 | fmt.Println(ratio) 87 | } 88 | -------------------------------------------------------------------------------- /2023/day04.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | var inputFile = flag.String("inputFile", "inputs/day04.input", "Relative file path to use as input.") 13 | 14 | func main() { 15 | flag.Parse() 16 | bytes, err := ioutil.ReadFile(*inputFile) 17 | if err != nil { 18 | return 19 | } 20 | contents := string(bytes) 21 | split := strings.Split(contents, "\n") 22 | 23 | memo := make(map[int][]int) 24 | sum := 0 25 | pending := make(map[int]int) 26 | for i, s := range split[:len(split)-1] { 27 | pending[i] = 1 28 | parts := strings.Split(s[10:], " | ") 29 | winners := make(map[int]bool) 30 | for _, v := range strings.Split(parts[0], " ") { 31 | if v == "" { 32 | continue 33 | } 34 | w, err := strconv.Atoi(v) 35 | if err != nil { 36 | log.Fatalf("Failed parsing %s: %v", v, err) 37 | } 38 | winners[w] = true 39 | } 40 | var won int 41 | for _, v := range strings.Split(parts[1], " ") { 42 | if v == "" { 43 | continue 44 | } 45 | w, err := strconv.Atoi(v) 46 | if err != nil { 47 | log.Fatalf("Failed parsing %s: %v", v, err) 48 | } 49 | if winners[w] { 50 | won++ 51 | memo[i] = append(memo[i], i+won) 52 | } 53 | } 54 | var value int 55 | for j := 0; j < len(memo[i]); j++ { 56 | if value == 0 { 57 | value = 1 58 | } else { 59 | value *= 2 60 | } 61 | } 62 | sum += value 63 | } 64 | fmt.Println(sum) 65 | 66 | inventory := make(map[int]int) 67 | for len(pending) != 0 { 68 | next := make(map[int]int) 69 | for k, v := range pending { 70 | inventory[k] += v 71 | for _, a := range memo[k] { 72 | next[a] += v 73 | } 74 | } 75 | pending = next 76 | } 77 | var cards int 78 | for _, v := range inventory { 79 | cards += v 80 | } 81 | fmt.Println(cards) 82 | } 83 | -------------------------------------------------------------------------------- /2023/day06.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day06.input", "Relative file path to use as input.") 12 | 13 | func main() { 14 | flag.Parse() 15 | bytes, err := ioutil.ReadFile(*inputFile) 16 | if err != nil { 17 | return 18 | } 19 | contents := string(bytes) 20 | split := strings.Split(contents, "\n") 21 | 22 | var times, distances []int 23 | for _, v := range strings.Split(split[0], " ") { 24 | if i, err := strconv.Atoi(v); err == nil { 25 | times = append(times, i) 26 | } 27 | } 28 | for _, v := range strings.Split(split[1], " ") { 29 | if i, err := strconv.Atoi(v); err == nil { 30 | distances = append(distances, i) 31 | } 32 | } 33 | 34 | product := 1 35 | for i := 0; i < len(times); i++ { 36 | var ways int 37 | time := times[i] 38 | distance := distances[i] 39 | for t := 0; t <= time; t++ { 40 | if (time-t)*t > distance { 41 | ways++ 42 | } 43 | } 44 | product *= ways 45 | } 46 | 47 | fmt.Println(product) 48 | 49 | bigTime, _ := strconv.Atoi(strings.ReplaceAll(strings.Split(split[0], ": ")[1], " ", "")) 50 | bigDistance, _ := strconv.Atoi(strings.ReplaceAll(strings.Split(split[1], ": ")[1], " ", "")) 51 | var ways int 52 | for t := 0; t <= bigTime; t++ { 53 | if (bigTime-t)*t > bigDistance { 54 | ways++ 55 | } 56 | } 57 | fmt.Println(ways) 58 | } 59 | -------------------------------------------------------------------------------- /2023/day08.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strings" 8 | ) 9 | 10 | var inputFile = flag.String("inputFile", "inputs/day08.input", "Relative file path to use as input.") 11 | 12 | func main() { 13 | flag.Parse() 14 | bytes, err := ioutil.ReadFile(*inputFile) 15 | if err != nil { 16 | return 17 | } 18 | contents := string(bytes) 19 | split := strings.Split(contents, "\n") 20 | 21 | instrs := split[0] 22 | paths := make(map[string][2]string) 23 | for _, s := range split[2 : len(split)-1] { 24 | cur := s[0:3] 25 | left := s[7:10] 26 | right := s[12:15] 27 | paths[cur] = [2]string{left, right} 28 | } 29 | var steps int 30 | for loc := "AAA"; loc != "ZZZ"; steps++ { 31 | dir := instrs[steps%len(instrs)] 32 | switch dir { 33 | case 'L': 34 | loc = paths[loc][0] 35 | case 'R': 36 | loc = paths[loc][1] 37 | } 38 | } 39 | fmt.Println(steps) 40 | 41 | var locs []string 42 | for k := range paths { 43 | if k[2] == 'A' { 44 | locs = append(locs, k) 45 | } 46 | } 47 | var loop []int 48 | for _, loc := range locs { 49 | var steps int 50 | for loc[2] != 'Z' { 51 | dir := instrs[steps%len(instrs)] 52 | switch dir { 53 | case 'L': 54 | loc = paths[loc][0] 55 | case 'R': 56 | loc = paths[loc][1] 57 | } 58 | steps++ 59 | } 60 | loop = append(loop, steps) 61 | } 62 | result := uint64(1) 63 | for _, v := range loop { 64 | result = lcm(result, uint64(v)) 65 | } 66 | fmt.Println(result) 67 | } 68 | 69 | func lcm(a, b uint64) uint64 { 70 | return a * b / gcd(a, b) 71 | } 72 | 73 | func gcd(a, b uint64) uint64 { 74 | for a != b { 75 | if a > b { 76 | a -= b 77 | } else { 78 | b -= a 79 | } 80 | } 81 | return a 82 | } 83 | -------------------------------------------------------------------------------- /2023/day09.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | var inputFile = flag.String("inputFile", "inputs/day09.input", "Relative file path to use as input.") 13 | 14 | func main() { 15 | flag.Parse() 16 | bytes, err := ioutil.ReadFile(*inputFile) 17 | if err != nil { 18 | return 19 | } 20 | contents := string(bytes) 21 | split := strings.Split(contents, "\n") 22 | 23 | var values [][]int 24 | for _, s := range split[:len(split)-1] { 25 | var series []int 26 | elems := strings.Split(s, " ") 27 | for _, str := range elems { 28 | v, err := strconv.Atoi(str) 29 | if err != nil { 30 | log.Fatalf("Failed parsing %s: %v", str, err) 31 | } 32 | series = append(series, v) 33 | } 34 | values = append(values, series) 35 | } 36 | 37 | var sumA, sumB int 38 | for _, series := range values { 39 | var first, last []int 40 | for { 41 | allZeroes := true 42 | var next []int 43 | first = append(first, series[0]) 44 | last = append(last, series[len(series)-1]) 45 | 46 | for i := 1; i < len(series); i++ { 47 | delta := series[i] - series[i-1] 48 | next = append(next, delta) 49 | if delta != 0 { 50 | allZeroes = false 51 | } 52 | } 53 | if allZeroes { 54 | break 55 | } 56 | series = next 57 | } 58 | for i := len(last) - 2; i >= 0; i-- { 59 | first[i] -= first[i+1] 60 | last[i] += last[i+1] 61 | } 62 | sumA += last[0] 63 | sumB += first[0] 64 | } 65 | fmt.Println(sumA) 66 | fmt.Println(sumB) 67 | } 68 | -------------------------------------------------------------------------------- /2023/day11.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strings" 8 | ) 9 | 10 | var inputFile = flag.String("inputFile", "inputs/day11.input", "Relative file path to use as input.") 11 | 12 | type Coord struct { 13 | R, C int 14 | } 15 | 16 | func main() { 17 | flag.Parse() 18 | bytes, err := ioutil.ReadFile(*inputFile) 19 | if err != nil { 20 | return 21 | } 22 | contents := string(bytes) 23 | split := strings.Split(contents, "\n") 24 | 25 | stars := make(map[Coord]bool) 26 | bigRows := make(map[int]bool) 27 | bigCols := make(map[int]bool) 28 | 29 | var maxR, maxC int 30 | for r, line := range split[:len(split)-1] { 31 | maxR = r 32 | for c, v := range line { 33 | if c > maxC { 34 | maxC = c 35 | } 36 | if v == '#' { 37 | stars[Coord{r, c}] = true 38 | } 39 | } 40 | } 41 | 42 | outerR: 43 | for r := 0; r <= maxR; r++ { 44 | for c := 0; c <= maxC; c++ { 45 | if stars[Coord{r, c}] { 46 | continue outerR 47 | } 48 | } 49 | bigRows[r] = true 50 | } 51 | outerC: 52 | for c := 0; c <= maxC; c++ { 53 | for r := 0; r <= maxR; r++ { 54 | if stars[Coord{r, c}] { 55 | continue outerC 56 | } 57 | } 58 | bigCols[c] = true 59 | } 60 | 61 | var sumA, sumB int 62 | for x := range stars { 63 | for y := range stars { 64 | if x.R > y.R || x.R == y.R && x.C >= y.C { 65 | continue 66 | } 67 | for r := x.R; r != y.R; r++ { 68 | sumA++ 69 | sumB++ 70 | if bigRows[r] { 71 | sumA++ 72 | sumB += 999999 73 | } 74 | } 75 | incr := 1 76 | if x.C > y.C { 77 | incr = -1 78 | } 79 | for c := x.C; c != y.C; c += incr { 80 | sumA++ 81 | sumB++ 82 | if bigCols[c] { 83 | sumA++ 84 | sumB += 999999 85 | } 86 | } 87 | } 88 | } 89 | fmt.Println(sumA) 90 | fmt.Println(sumB) 91 | } 92 | -------------------------------------------------------------------------------- /2023/day13.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "math/bits" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day13.input", "Relative file path to use as input.") 12 | 13 | func main() { 14 | flag.Parse() 15 | bytes, err := ioutil.ReadFile(*inputFile) 16 | if err != nil { 17 | return 18 | } 19 | contents := string(bytes) 20 | split := strings.Split(contents, "\n") 21 | 22 | var verticalSum, horizontalSum int 23 | var verticalNear, horizontalNear int 24 | 25 | var r int 26 | var rChecksums, cChecksums []uint64 27 | 28 | for _, s := range split { 29 | if s == "" { 30 | mirrorRow, nearRow := process(rChecksums) 31 | // 1-indexed values. 32 | verticalSum += mirrorRow + 1 33 | verticalNear += nearRow + 1 34 | 35 | mirrorCol, nearCol := process(cChecksums) 36 | // 1-indexed values. 37 | horizontalSum += mirrorCol + 1 38 | horizontalNear += nearCol + 1 39 | 40 | r = 0 41 | rChecksums = nil 42 | cChecksums = nil 43 | continue 44 | } 45 | if r == 0 { 46 | cChecksums = make([]uint64, len(s)) 47 | } 48 | rChecksums = append(rChecksums, 0) 49 | for c, v := range s { 50 | if v == '#' { 51 | // Increment the horizontal and vertical checksums 52 | // for the row/column that we are in. 53 | rChecksums[r] += 1 << c 54 | cChecksums[c] += 1 << r 55 | } 56 | } 57 | r++ 58 | } 59 | fmt.Println(100*verticalSum + horizontalSum) 60 | fmt.Println(100*verticalNear + horizontalNear) 61 | } 62 | 63 | func process(checksums []uint64) (int, int) { 64 | m := -1 65 | v := -1 66 | for mirror := 0; mirror < len(checksums); mirror++ { 67 | valid := true 68 | var nearMiss bool 69 | var offset int 70 | for ; mirror-offset >= 0 && mirror+offset+1 < len(checksums); offset++ { 71 | left := checksums[mirror-offset] 72 | right := checksums[mirror+offset+1] 73 | if checksums[mirror-offset] != checksums[mirror+offset+1] { 74 | valid = false 75 | if bits.OnesCount64(left^right) == 1 && !nearMiss { 76 | nearMiss = true 77 | } else { 78 | // Too many failures, bail. 79 | nearMiss = false 80 | break 81 | } 82 | } 83 | } 84 | if nearMiss { 85 | m = mirror 86 | } 87 | if valid && offset > 0 { 88 | v = mirror 89 | } 90 | } 91 | return v, m 92 | } 93 | -------------------------------------------------------------------------------- /2023/day15.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | var inputFile = flag.String("inputFile", "inputs/day15.input", "Relative file path to use as input.") 13 | 14 | type Item struct { 15 | Label string 16 | Num int 17 | } 18 | 19 | func main() { 20 | flag.Parse() 21 | bytes, err := ioutil.ReadFile(*inputFile) 22 | if err != nil { 23 | return 24 | } 25 | contents := string(bytes[:len(bytes)-1]) 26 | steps := strings.Split(contents, ",") 27 | 28 | var boxes [256][]Item 29 | 30 | var sum int 31 | outer: 32 | for _, step := range steps { 33 | sum += hash(step) 34 | 35 | if step[len(step)-1] == '-' { 36 | // Remove from box. 37 | label := step[:len(step)-1] 38 | h := hash(label) 39 | for i, v := range boxes[h] { 40 | if v.Label == label { 41 | copy(boxes[h][i:], boxes[h][i+1:]) 42 | boxes[h] = boxes[h][:len(boxes[h])-1] 43 | break 44 | } 45 | } 46 | continue outer 47 | } 48 | parts := strings.Split(step, "=") 49 | if len(parts) != 2 { 50 | log.Fatalf("Found invalid step %s", step) 51 | } 52 | label := parts[0] 53 | h := hash(label) 54 | num, err := strconv.Atoi(parts[1]) 55 | if err != nil { 56 | log.Fatalf("Failed to parse value of %s: %v", step, err) 57 | } 58 | this := Item{label, num} 59 | for i, v := range boxes[h] { 60 | if v.Label == label { 61 | boxes[h][i] = this 62 | continue outer 63 | } 64 | } 65 | boxes[h] = append(boxes[h], this) 66 | } 67 | fmt.Println(sum) 68 | 69 | var power int 70 | for idx, arr := range boxes { 71 | for slot, v := range arr { 72 | power += (1 + idx) * (1 + slot) * v.Num 73 | } 74 | } 75 | fmt.Println(power) 76 | } 77 | 78 | func hash(step string) int { 79 | var value int 80 | for _, c := range step { 81 | value += int(c) 82 | value *= 17 83 | value %= 256 84 | } 85 | return value 86 | } 87 | -------------------------------------------------------------------------------- /2023/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/lizthegrey/adventofcode/2023 2 | 3 | go 1.21 4 | 5 | require github.com/lizthegrey/adventofcode/2022 v0.0.0-20231216053144-4c6c3eeed0a7 6 | -------------------------------------------------------------------------------- /2023/go.sum: -------------------------------------------------------------------------------- 1 | github.com/lizthegrey/adventofcode/2022 v0.0.0-20231216053144-4c6c3eeed0a7 h1:5FgwOAr5zi+ojyRSqwk99DRkNX6HQUzXpXH2eWUImIo= 2 | github.com/lizthegrey/adventofcode/2022 v0.0.0-20231216053144-4c6c3eeed0a7/go.mod h1:Q4sLWj8X5kQjol0CQb/NLq0sGK0veCWf8cUGK5Hm4H4= 3 | -------------------------------------------------------------------------------- /2024/.gitignore: -------------------------------------------------------------------------------- 1 | *.input 2 | -------------------------------------------------------------------------------- /2024/day01.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "sort" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | var inputFile = flag.String("inputFile", "inputs/day01.input", "Relative file path to use as input.") 13 | 14 | func main() { 15 | flag.Parse() 16 | bytes, err := ioutil.ReadFile(*inputFile) 17 | if err != nil { 18 | return 19 | } 20 | contents := string(bytes) 21 | split := strings.Split(contents, "\n") 22 | 23 | var listA, listB []int 24 | for _, s := range split[:len(split)-1] { 25 | parts := strings.Split(s, " ") 26 | first := true 27 | for _, p := range parts { 28 | if p == "" { 29 | // hacky but this works instead of writing a regex matcher. 30 | continue 31 | } 32 | n, _ := strconv.Atoi(p) 33 | if !first { 34 | listB = append(listB, n) 35 | } else { 36 | listA = append(listA, n) 37 | } 38 | first = false 39 | } 40 | } 41 | 42 | // part A 43 | sort.Ints(listA) 44 | sort.Ints(listB) 45 | var distance int 46 | for i := range listA { 47 | diff := listB[i] - listA[i] 48 | if diff < 0 { 49 | diff *= -1 50 | } 51 | distance += diff 52 | } 53 | fmt.Println(distance) 54 | 55 | // part B 56 | frequencies := make(map[int]int) 57 | for _, b := range listB { 58 | frequencies[b]++ 59 | } 60 | var similarity int 61 | for _, a := range listA { 62 | similarity += a * frequencies[a] 63 | } 64 | fmt.Println(similarity) 65 | } 66 | -------------------------------------------------------------------------------- /2024/day02.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day02.input", "Relative file path to use as input.") 12 | 13 | type row []int 14 | 15 | func main() { 16 | flag.Parse() 17 | bytes, err := ioutil.ReadFile(*inputFile) 18 | if err != nil { 19 | return 20 | } 21 | contents := string(bytes) 22 | split := strings.Split(contents, "\n") 23 | 24 | var rows []row 25 | for _, s := range split[:len(split)-1] { 26 | var r row 27 | for _, num := range strings.Split(s, " ") { 28 | n, _ := strconv.Atoi(num) 29 | r = append(r, n) 30 | } 31 | rows = append(rows, r) 32 | } 33 | var safe, recovered int 34 | outer: 35 | for _, r := range rows { 36 | if check(r) { 37 | safe++ 38 | recovered++ 39 | continue 40 | } 41 | for skip := range r { 42 | var copied row 43 | for i, v := range r { 44 | if i == skip { 45 | continue 46 | } 47 | copied = append(copied, v) 48 | } 49 | if check(copied) { 50 | recovered++ 51 | continue outer 52 | } 53 | } 54 | } 55 | fmt.Println(safe) 56 | fmt.Println(recovered) 57 | } 58 | 59 | func check(r row) bool { 60 | var prev int 61 | var increasing bool 62 | for i, v := range r { 63 | if i == 0 { 64 | prev = v 65 | continue 66 | } 67 | delta := v - prev 68 | if i == 1 { 69 | if v < prev { 70 | increasing = false 71 | } else if v > prev { 72 | increasing = true 73 | } else { 74 | return false 75 | } 76 | } else { 77 | if increasing && delta < 0 || !increasing && delta > 0 { 78 | return false 79 | } 80 | } 81 | if delta == 0 { 82 | return false 83 | } 84 | if delta < 0 { 85 | delta *= -1 86 | } 87 | if delta > 3 { 88 | return false 89 | } 90 | prev = v 91 | } 92 | return true 93 | } 94 | -------------------------------------------------------------------------------- /2024/day03.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "regexp" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | var inputFile = flag.String("inputFile", "inputs/day03.input", "Relative file path to use as input.") 13 | 14 | type row []int 15 | 16 | var pattern = regexp.MustCompile(`mul\(([0-9]+),([0-9]+)\)`) 17 | var enable = regexp.MustCompile(`do\(\)`) 18 | var disable = regexp.MustCompile(`don't\(\)`) 19 | 20 | type valid struct { 21 | pos, val int 22 | } 23 | 24 | func main() { 25 | flag.Parse() 26 | bytes, err := ioutil.ReadFile(*inputFile) 27 | if err != nil { 28 | return 29 | } 30 | contents := string(bytes) 31 | split := strings.Split(contents, "\n") 32 | 33 | fmt.Println(process(split[:len(split)-1], false)) 34 | fmt.Println(process(split[:len(split)-1], true)) 35 | } 36 | 37 | func process(lines []string, obeyDont bool) int { 38 | sum := 0 39 | enabled := true 40 | for _, s := range lines { 41 | enables := enable.FindAllStringIndex(s, -1) 42 | disables := disable.FindAllStringIndex(s, -1) 43 | results := pattern.FindAllStringSubmatchIndex(s, -1) 44 | var ops []valid 45 | for _, r := range results { 46 | a, _ := strconv.Atoi(s[r[2]:r[3]]) 47 | b, _ := strconv.Atoi(s[r[4]:r[5]]) 48 | ops = append(ops, valid{r[0], a * b}) 49 | } 50 | var eIdx, dIdx, oIdx int 51 | for i := range len(s) { 52 | if eIdx < len(enables) && enables[eIdx][0] == i { 53 | enabled = true 54 | eIdx++ 55 | } 56 | if obeyDont && dIdx < len(disables) && disables[dIdx][0] == i { 57 | enabled = false 58 | dIdx++ 59 | } 60 | if oIdx < len(ops) && ops[oIdx].pos == i { 61 | if enabled { 62 | sum += ops[oIdx].val 63 | } 64 | oIdx++ 65 | } 66 | } 67 | } 68 | return sum 69 | } 70 | -------------------------------------------------------------------------------- /2024/day04.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strings" 8 | ) 9 | 10 | var inputFile = flag.String("inputFile", "inputs/day04.input", "Relative file path to use as input.") 11 | 12 | const match = "XMAS" 13 | 14 | type coord struct { 15 | r, c int 16 | } 17 | 18 | func main() { 19 | flag.Parse() 20 | bytes, err := ioutil.ReadFile(*inputFile) 21 | if err != nil { 22 | return 23 | } 24 | contents := string(bytes) 25 | split := strings.Split(contents, "\n") 26 | grid := make(map[coord]rune) 27 | var maxR, maxC int 28 | for r, row := range split[:len(split)-1] { 29 | if r > maxR { 30 | maxR = r 31 | } 32 | for c, v := range row { 33 | if c > maxC { 34 | maxC = c 35 | } 36 | grid[coord{r, c}] = v 37 | } 38 | } 39 | 40 | var sum int 41 | sum += checkAll(grid, maxR, maxC, coord{0, 1}) 42 | sum += checkAll(grid, maxR, maxC, coord{0, -1}) 43 | sum += checkAll(grid, maxR, maxC, coord{1, 0}) 44 | sum += checkAll(grid, maxR, maxC, coord{-1, 0}) 45 | sum += checkAll(grid, maxR, maxC, coord{1, 1}) 46 | sum += checkAll(grid, maxR, maxC, coord{-1, -1}) 47 | sum += checkAll(grid, maxR, maxC, coord{1, -1}) 48 | sum += checkAll(grid, maxR, maxC, coord{-1, 1}) 49 | fmt.Println(sum) 50 | 51 | var count int 52 | for r := range maxR + 1 { 53 | for c := range maxC + 1 { 54 | if grid[coord{r, c}] != 'A' { 55 | continue 56 | } 57 | // Check the diagonals 58 | if !(grid[coord{r + 1, c - 1}] == 'M' && grid[coord{r - 1, c + 1}] == 'S' || grid[coord{r + 1, c - 1}] == 'S' && grid[coord{r - 1, c + 1}] == 'M') { 59 | continue 60 | } 61 | if !(grid[coord{r + 1, c + 1}] == 'M' && grid[coord{r - 1, c - 1}] == 'S' || grid[coord{r + 1, c + 1}] == 'S' && grid[coord{r - 1, c - 1}] == 'M') { 62 | continue 63 | } 64 | count++ 65 | } 66 | } 67 | fmt.Println(count) 68 | } 69 | 70 | func checkAll(grid map[coord]rune, maxR, maxC int, step coord) int { 71 | var count int 72 | for r := range maxR + 1 { 73 | for c := range maxC + 1 { 74 | if check(grid, coord{r, c}, step) { 75 | count++ 76 | } 77 | } 78 | } 79 | return count 80 | } 81 | 82 | func check(grid map[coord]rune, start coord, step coord) bool { 83 | loc := start 84 | for _, v := range match { 85 | if v != grid[loc] { 86 | return false 87 | } 88 | loc.r += step.r 89 | loc.c += step.c 90 | } 91 | return true 92 | } 93 | -------------------------------------------------------------------------------- /2024/day05.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "slices" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | var inputFile = flag.String("inputFile", "inputs/day05.input", "Relative file path to use as input.") 13 | 14 | func main() { 15 | flag.Parse() 16 | bytes, err := ioutil.ReadFile(*inputFile) 17 | if err != nil { 18 | return 19 | } 20 | contents := string(bytes) 21 | split := strings.Split(contents, "\n") 22 | 23 | rules := make(map[int][]int) 24 | var clean int 25 | var fixed int 26 | parsingRules := true 27 | for _, s := range split[:len(split)-1] { 28 | if len(s) == 0 { 29 | parsingRules = false 30 | continue 31 | } 32 | if parsingRules { 33 | parts := strings.Split(s, "|") 34 | before, _ := strconv.Atoi(parts[0]) 35 | after, _ := strconv.Atoi(parts[1]) 36 | rules[before] = append(rules[before], after) 37 | } else { 38 | parts := strings.Split(s, ",") 39 | var list []int 40 | for _, v := range parts { 41 | n, _ := strconv.Atoi(v) 42 | list = append(list, n) 43 | } 44 | 45 | cmp := func(a, b int) int { 46 | for _, v := range rules[b] { 47 | if v == a { 48 | return 1 49 | } 50 | } 51 | return -1 52 | } 53 | 54 | if slices.IsSortedFunc(list, cmp) { 55 | clean += list[len(list)/2] 56 | } else { 57 | slices.SortFunc(list, cmp) 58 | fixed += list[len(list)/2] 59 | } 60 | } 61 | } 62 | fmt.Println(clean) 63 | fmt.Println(fixed) 64 | } 65 | -------------------------------------------------------------------------------- /2024/day06.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strings" 8 | ) 9 | 10 | var inputFile = flag.String("inputFile", "inputs/day06.input", "Relative file path to use as input.") 11 | 12 | type coord struct { 13 | r, c int 14 | } 15 | 16 | type state struct { 17 | coord 18 | facing int 19 | } 20 | 21 | const ( 22 | Up int = iota 23 | Right 24 | Down 25 | Left 26 | ) 27 | 28 | func main() { 29 | flag.Parse() 30 | bytes, err := ioutil.ReadFile(*inputFile) 31 | if err != nil { 32 | return 33 | } 34 | contents := string(bytes) 35 | split := strings.Split(contents, "\n") 36 | 37 | var start coord 38 | passable := make(map[coord]bool) 39 | for r, s := range split[:len(split)-1] { 40 | for c, v := range s { 41 | loc := coord{r, c} 42 | passable[loc] = v != '#' 43 | if v == '^' { 44 | start = loc 45 | } 46 | } 47 | } 48 | seen := check(passable, start, nil) 49 | fmt.Println(len(seen)) 50 | 51 | var valid int 52 | for obs := range seen { 53 | if obs == start { 54 | continue 55 | } 56 | if check(passable, start, &obs) == nil { 57 | valid++ 58 | } 59 | } 60 | fmt.Println(valid) 61 | } 62 | 63 | func check(passable map[coord]bool, start coord, obs *coord) map[coord]bool { 64 | cur := start 65 | facing := Up 66 | seen := make(map[coord]bool) 67 | states := make(map[state]bool) 68 | for { 69 | seen[cur] = true 70 | key := state{cur, facing} 71 | if states[key] { 72 | return nil 73 | } 74 | states[key] = true 75 | next := cur 76 | switch facing { 77 | case Up: 78 | next.r-- 79 | case Right: 80 | next.c++ 81 | case Down: 82 | next.r++ 83 | case Left: 84 | next.c-- 85 | } 86 | if p, ok := passable[next]; !ok { 87 | // Off board. 88 | break 89 | } else if !p || (obs != nil && *obs == next) { 90 | facing++ 91 | facing %= 4 92 | } else { 93 | cur = next 94 | } 95 | } 96 | return seen 97 | } 98 | -------------------------------------------------------------------------------- /2024/day07.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day07.input", "Relative file path to use as input.") 12 | 13 | func main() { 14 | flag.Parse() 15 | bytes, err := ioutil.ReadFile(*inputFile) 16 | if err != nil { 17 | return 18 | } 19 | contents := string(bytes) 20 | split := strings.Split(contents, "\n") 21 | 22 | var validA, validB int64 23 | for _, s := range split[:len(split)-1] { 24 | parts := strings.Split(s, ": ") 25 | total, _ := strconv.Atoi(parts[0]) 26 | var nums []int64 27 | for _, part := range strings.Split(parts[1], " ") { 28 | n, _ := strconv.Atoi(part) 29 | nums = append(nums, int64(n)) 30 | } 31 | if check(int64(total), nums[0], nums[1:], false) { 32 | validA += int64(total) 33 | } 34 | if check(int64(total), nums[0], nums[1:], true) { 35 | validB += int64(total) 36 | } 37 | } 38 | fmt.Println(validA) 39 | fmt.Println(validB) 40 | } 41 | 42 | func check(total, cumulative int64, nums []int64, partB bool) bool { 43 | // Each op makes total bigger, so bail early if we overshot. 44 | if cumulative > total { 45 | return false 46 | } 47 | sum := cumulative + nums[0] 48 | product := cumulative * nums[0] 49 | cat := concat(cumulative, nums[0]) 50 | // No further numbers to add. 51 | if len(nums) == 1 { 52 | if sum == total || product == total || (partB && cat == total) { 53 | return true 54 | } 55 | return false 56 | } 57 | return check(total, sum, nums[1:], partB) || check(total, product, nums[1:], partB) || (partB && check(total, cat, nums[1:], partB)) 58 | } 59 | 60 | func concat(left, right int64) int64 { 61 | shift := 0 62 | digits := right 63 | for digits > 0 { 64 | digits = digits / 10 65 | shift++ 66 | } 67 | ret := left 68 | for range shift { 69 | ret *= 10 70 | } 71 | ret += right 72 | return ret 73 | } 74 | -------------------------------------------------------------------------------- /2024/day08.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strings" 8 | ) 9 | 10 | var inputFile = flag.String("inputFile", "inputs/day08.input", "Relative file path to use as input.") 11 | 12 | type coord struct { 13 | r, c int 14 | } 15 | 16 | func (c coord) add(o coord) coord { 17 | return coord{int(c.r + o.r), int(c.c + o.c)} 18 | } 19 | 20 | func (c coord) sub(o coord) coord { 21 | return coord{int(c.r - o.r), int(c.c - o.c)} 22 | } 23 | 24 | func (c coord) bounds(o coord) bool { 25 | return c.r >= 0 && c.c >= 0 && c.r <= o.r && c.c <= o.c 26 | } 27 | 28 | func main() { 29 | flag.Parse() 30 | bytes, err := ioutil.ReadFile(*inputFile) 31 | if err != nil { 32 | return 33 | } 34 | contents := string(bytes) 35 | split := strings.Split(contents, "\n") 36 | 37 | byType := make(map[rune][]coord) 38 | var maxR, maxC int 39 | for r, s := range split[:len(split)-1] { 40 | maxR = r 41 | maxC = len(s) - 1 42 | for c, v := range s { 43 | if v == '.' { 44 | continue 45 | } 46 | loc := coord{r, c} 47 | byType[v] = append(byType[v], loc) 48 | } 49 | } 50 | 51 | limits := coord{maxR, maxC} 52 | loci := make(map[coord]bool) 53 | lociInf := make(map[coord]bool) 54 | for _, list := range byType { 55 | for i, x := range list { 56 | for j, y := range list { 57 | if i == j { 58 | break 59 | } 60 | lociInf[x] = true 61 | lociInf[y] = true 62 | delta := y.sub(x) 63 | first := true 64 | for pos := y.add(delta); pos.bounds(limits); pos = pos.add(delta) { 65 | if first { 66 | loci[pos] = true 67 | first = false 68 | } 69 | lociInf[pos] = true 70 | } 71 | delta = x.sub(y) 72 | first = true 73 | for pos := x.add(delta); pos.bounds(limits); pos = pos.add(delta) { 74 | if first { 75 | loci[pos] = true 76 | first = false 77 | } 78 | lociInf[pos] = true 79 | } 80 | } 81 | } 82 | } 83 | fmt.Println(len(loci)) 84 | fmt.Println(len(lociInf)) 85 | } 86 | -------------------------------------------------------------------------------- /2024/day09.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "math" 8 | "slices" 9 | "strings" 10 | ) 11 | 12 | var inputFile = flag.String("inputFile", "inputs/day09.input", "Relative file path to use as input.") 13 | 14 | const empty uint16 = math.MaxUint16 15 | 16 | func main() { 17 | flag.Parse() 18 | bytes, err := ioutil.ReadFile(*inputFile) 19 | if err != nil { 20 | return 21 | } 22 | contents := string(bytes) 23 | split := strings.Split(contents, "\n") 24 | 25 | // File numbers range from 0 to 19,999. uint16 holds up to 65535 26 | var mem []uint16 27 | var file bool 28 | var maxFile uint16 29 | positions := make(map[uint16]int) 30 | lengths := make(map[uint16]int) 31 | for n, v := range split[0] { 32 | file = !file 33 | digit := int(v - '0') 34 | val := uint16(n / 2) 35 | if !file { 36 | val = empty 37 | } else { 38 | maxFile = val 39 | positions[val] = len(mem) 40 | lengths[val] = digit 41 | } 42 | mem = append(mem, slices.Repeat([]uint16{val}, digit)...) 43 | } 44 | 45 | a := slices.Clone(mem) 46 | // ss stands for "search sequence", of course! 47 | ss := len(a) 48 | for hole := range len(a) { 49 | // Look for a place to store. 50 | if fill := a[hole]; fill != empty { 51 | continue 52 | } 53 | // Stop when the two indices collide. 54 | if ss <= hole { 55 | break 56 | } 57 | // Find a non-empty fragment to use. 58 | for ss--; a[ss] == empty; ss-- { 59 | } 60 | // Swap. 61 | a[hole], a[ss] = a[ss], empty 62 | } 63 | fmt.Println(checksum(a)) 64 | 65 | b := slices.Clone(mem) 66 | for file := maxFile; file != empty; file-- { 67 | length := lengths[file] 68 | pos := positions[file] 69 | search: 70 | for i := range pos { 71 | for offset := range length { 72 | if b[i+offset] != empty { 73 | continue search 74 | } 75 | } 76 | copy(b[i:i+length], slices.Repeat([]uint16{file}, length)) 77 | copy(b[pos:pos+length], slices.Repeat([]uint16{empty}, length)) 78 | break 79 | } 80 | } 81 | fmt.Println(checksum(b)) 82 | } 83 | 84 | func checksum(mem []uint16) uint64 { 85 | var ret uint64 86 | for i, v := range mem { 87 | if v == empty { 88 | continue 89 | } 90 | ret += uint64(i) * uint64(v) 91 | } 92 | return ret 93 | } 94 | -------------------------------------------------------------------------------- /2024/day10.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strings" 8 | ) 9 | 10 | var inputFile = flag.String("inputFile", "inputs/day10.input", "Relative file path to use as input.") 11 | 12 | type coord struct { 13 | r, c int 14 | } 15 | 16 | type grid map[coord]int 17 | 18 | func main() { 19 | flag.Parse() 20 | bytes, err := ioutil.ReadFile(*inputFile) 21 | if err != nil { 22 | return 23 | } 24 | contents := string(bytes) 25 | split := strings.Split(contents, "\n") 26 | 27 | board := make(grid) 28 | for r, s := range split[:len(split)-1] { 29 | for c, v := range s { 30 | loc := coord{r, c} 31 | height := int(v - '0') 32 | board[loc] = height 33 | } 34 | } 35 | 36 | var sumA, sumB int 37 | for loc, height := range board { 38 | if height != 0 { 39 | continue 40 | } 41 | reachable := make(grid) 42 | rating := score(board, reachable, loc) 43 | sumA += len(reachable) 44 | sumB += rating 45 | } 46 | fmt.Println(sumA) 47 | fmt.Println(sumB) 48 | } 49 | 50 | func score(board, reachable grid, loc coord) int { 51 | height := board[loc] 52 | if height == 9 { 53 | reachable[loc] = 1 54 | return 1 55 | } 56 | toVisit := [4]coord{ 57 | {loc.r + 1, loc.c}, 58 | {loc.r - 1, loc.c}, 59 | {loc.r, loc.c + 1}, 60 | {loc.r, loc.c - 1}, 61 | } 62 | var rating int 63 | for _, neigh := range toVisit { 64 | if height+1 == board[neigh] { 65 | rating += score(board, reachable, neigh) 66 | } 67 | } 68 | return rating 69 | } 70 | -------------------------------------------------------------------------------- /2024/day11.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day11.input", "Relative file path to use as input.") 12 | 13 | type memo map[pair]uint64 14 | 15 | func (m memo) evolve(v int, gens int) uint64 { 16 | if gens == 0 { 17 | return 1 18 | } 19 | 20 | key := pair{v, gens} 21 | if ret := m[key]; ret != 0 { 22 | return ret 23 | } 24 | 25 | if v == 0 { 26 | ret := m.evolve(1, gens-1) 27 | m[key] = ret 28 | return ret 29 | } 30 | var digits int 31 | for test := v; test > 0; test /= 10 { 32 | digits++ 33 | } 34 | if digits%2 == 0 { 35 | cutoff := 1 36 | for range digits / 2 { 37 | cutoff *= 10 38 | } 39 | left := v / cutoff 40 | right := v % cutoff 41 | ret := m.evolve(left, gens-1) + m.evolve(right, gens-1) 42 | m[key] = ret 43 | return ret 44 | } 45 | ret := m.evolve(v*2024, gens-1) 46 | m[key] = ret 47 | return ret 48 | } 49 | 50 | type pair struct { 51 | initial, rounds int 52 | } 53 | 54 | func main() { 55 | flag.Parse() 56 | bytes, err := ioutil.ReadFile(*inputFile) 57 | if err != nil { 58 | return 59 | } 60 | contents := string(bytes) 61 | split := strings.Split(contents, "\n") 62 | 63 | var stones []int 64 | for _, s := range strings.Split(split[0], " ") { 65 | v, _ := strconv.Atoi(s) 66 | stones = append(stones, v) 67 | } 68 | 69 | cache := make(memo) 70 | var sumA, sumB uint64 71 | for _, v := range stones { 72 | sumA += cache.evolve(v, 25) 73 | } 74 | fmt.Println(sumA) 75 | for _, v := range stones { 76 | sumB += cache.evolve(v, 75) 77 | } 78 | fmt.Println(sumB) 79 | } 80 | -------------------------------------------------------------------------------- /2024/day12.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strings" 8 | ) 9 | 10 | var inputFile = flag.String("inputFile", "inputs/day12.input", "Relative file path to use as input.") 11 | 12 | type pair struct { 13 | r, c int 14 | } 15 | 16 | func (p pair) neighbours() [4]pair { 17 | return [4]pair{ 18 | {p.r - 1, p.c}, // up 19 | {p.r, p.c + 1}, // right 20 | {p.r + 1, p.c}, // down 21 | {p.r, p.c - 1}, // left 22 | } 23 | } 24 | 25 | func (p pair) diags() [4]pair { 26 | return [4]pair{ 27 | {p.r - 1, p.c + 1}, // up-right 28 | {p.r + 1, p.c + 1}, // down-right 29 | {p.r + 1, p.c - 1}, // down-left 30 | {p.r - 1, p.c - 1}, // up-left 31 | } 32 | } 33 | 34 | type garden map[pair]rune 35 | 36 | func main() { 37 | flag.Parse() 38 | bytes, err := ioutil.ReadFile(*inputFile) 39 | if err != nil { 40 | return 41 | } 42 | contents := string(bytes) 43 | split := strings.Split(contents, "\n") 44 | 45 | board := make(garden) 46 | for r, s := range split[:len(split)-1] { 47 | for c, v := range s { 48 | board[pair{r, c}] = v 49 | } 50 | } 51 | 52 | plots := make(map[pair]int) 53 | areas := make(map[int]int) 54 | perimeters := make(map[int]int) 55 | corners := make(map[int]int) 56 | plot := 1 57 | for loc, kind := range board { 58 | q := []pair{loc} 59 | for len(q) > 0 { 60 | work := q[0] 61 | q = q[1:] 62 | if plots[work] != 0 { 63 | continue 64 | } 65 | plots[work] = plot 66 | areas[plot]++ 67 | var different [4]bool 68 | for i, n := range work.neighbours() { 69 | if board[n] != kind { 70 | perimeters[plot]++ 71 | different[i] = true 72 | } else { 73 | q = append(q, n) 74 | } 75 | } 76 | diags := work.diags() 77 | for i := range len(different) { 78 | // outer corners 79 | if different[i] && different[(i+1)%4] { 80 | corners[plot]++ 81 | } 82 | // inner corners 83 | if !different[i] && !different[(i+1)%4] && board[diags[i]] != kind { 84 | corners[plot]++ 85 | } 86 | } 87 | } 88 | plot++ 89 | } 90 | 91 | var sumA, sumB int 92 | for k, area := range areas { 93 | sumA += area * perimeters[k] 94 | sumB += area * corners[k] 95 | } 96 | fmt.Println(sumA) 97 | fmt.Println(sumB) 98 | } 99 | -------------------------------------------------------------------------------- /2024/day18.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strconv" 8 | "strings" 9 | 10 | "github.com/lizthegrey/adventofcode/2022/heapq" 11 | ) 12 | 13 | var inputFile = flag.String("inputFile", "inputs/day18.input", "Relative file path to use as input.") 14 | 15 | type coord struct { 16 | x, y int 17 | } 18 | 19 | func (c coord) add(o coord) coord { 20 | return coord{c.x + o.x, c.y + o.y} 21 | } 22 | 23 | const maxCoord = 70 24 | 25 | func main() { 26 | flag.Parse() 27 | bytes, err := ioutil.ReadFile(*inputFile) 28 | if err != nil { 29 | return 30 | } 31 | contents := string(bytes) 32 | split := strings.Split(contents, "\n") 33 | 34 | var incoming []coord 35 | for _, s := range split[:len(split)-1] { 36 | parts := strings.Split(s, ",") 37 | x, _ := strconv.Atoi(parts[0]) 38 | y, _ := strconv.Atoi(parts[1]) 39 | incoming = append(incoming, coord{x, y}) 40 | } 41 | 42 | impassable := make(map[coord]bool) 43 | for i, loc := range incoming { 44 | impassable[loc] = true 45 | if i < 1024 { 46 | continue 47 | } 48 | steps := aStar(impassable, coord{0, 0}, coord{maxCoord, maxCoord}) 49 | if i == 1024 { 50 | fmt.Println(steps) 51 | } 52 | if steps == -1 { 53 | fmt.Printf("%d,%d\n", loc.x, loc.y) 54 | return 55 | } 56 | } 57 | } 58 | 59 | func aStar(impassable map[coord]bool, start, target coord) int { 60 | gScore := map[coord]int{ 61 | start: 0, 62 | } 63 | workList := heapq.New[coord]() 64 | workList.Upsert(start, start.heuristic(target)) 65 | for workList.Len() != 0 { 66 | // Pop the current node off the worklist. 67 | current := workList.PopSafe() 68 | 69 | if current == target { 70 | return gScore[current] 71 | } 72 | for _, n := range current.iterate(impassable) { 73 | proposedScore := gScore[current] + 1 74 | if previousScore, ok := gScore[n]; !ok || proposedScore < previousScore { 75 | gScore[n] = proposedScore 76 | workList.Upsert(n, proposedScore+n.heuristic(target)) 77 | } 78 | } 79 | } 80 | return -1 81 | } 82 | 83 | func (c coord) heuristic(target coord) int { 84 | return (target.x - c.x) + (target.y - c.y) 85 | } 86 | 87 | func (s coord) iterate(impassable map[coord]bool) []coord { 88 | var ret []coord 89 | for _, step := range []coord{{1, 0}, {-1, 0}, {0, 1}, {0, -1}} { 90 | next := s.add(step) 91 | if impassable[next] || next.x < 0 || next.x > maxCoord || next.y < 0 || next.y > maxCoord { 92 | continue 93 | } 94 | ret = append(ret, next) 95 | } 96 | return ret 97 | } 98 | -------------------------------------------------------------------------------- /2024/day19.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strings" 8 | ) 9 | 10 | var inputFile = flag.String("inputFile", "inputs/day19.input", "Relative file path to use as input.") 11 | 12 | func main() { 13 | flag.Parse() 14 | bytes, err := ioutil.ReadFile(*inputFile) 15 | if err != nil { 16 | return 17 | } 18 | contents := string(bytes) 19 | split := strings.Split(contents, "\n") 20 | 21 | var elements, patterns []string 22 | for i, s := range split[:len(split)-1] { 23 | if i == 0 { 24 | for _, v := range strings.Split(s, ", ") { 25 | elements = append(elements, v) 26 | } 27 | } 28 | if i < 2 { 29 | continue 30 | } 31 | patterns = append(patterns, s) 32 | } 33 | 34 | var possible, total uint64 35 | memo := make(map[string]uint64) 36 | for _, p := range patterns { 37 | num := test(memo, elements, p) 38 | total += num 39 | if num > 0 { 40 | possible++ 41 | } 42 | } 43 | fmt.Println(possible) 44 | fmt.Println(total) 45 | } 46 | 47 | func test(memo map[string]uint64, elements []string, p string) uint64 { 48 | if len(p) == 0 { 49 | return 1 50 | } 51 | if v, ok := memo[p]; ok { 52 | return v 53 | } 54 | var total uint64 55 | for _, e := range elements { 56 | if len(p) < len(e) { 57 | continue 58 | } 59 | if p[:len(e)] == e { 60 | total += test(memo, elements, p[len(e):]) 61 | } 62 | } 63 | memo[p] = total 64 | return total 65 | } 66 | -------------------------------------------------------------------------------- /2024/day22.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day22.input", "Relative file path to use as input.") 12 | 13 | type deltaKey [4]int8 14 | 15 | func main() { 16 | flag.Parse() 17 | bytes, err := ioutil.ReadFile(*inputFile) 18 | if err != nil { 19 | return 20 | } 21 | contents := string(bytes) 22 | split := strings.Split(contents, "\n") 23 | 24 | var nums []uint64 25 | for _, s := range split[:len(split)-1] { 26 | n, _ := strconv.Atoi(s) 27 | nums = append(nums, uint64(n)) 28 | } 29 | 30 | var sum uint64 31 | profits := make(map[deltaKey]int) 32 | for _, v := range nums { 33 | var deltas, prices []int8 34 | previous := int8(v % 10) 35 | for range 2000 { 36 | v = next(v) 37 | price := int8(v % 10) 38 | prices = append(prices, price) 39 | delta := price - previous 40 | deltas = append(deltas, delta) 41 | previous = price 42 | } 43 | sum += v 44 | 45 | candidates := make(map[deltaKey]int) 46 | for j := range len(deltas) - 4 { 47 | key := deltaKey{deltas[j], deltas[j+1], deltas[j+2], deltas[j+3]} 48 | if _, ok := candidates[key]; ok { 49 | continue 50 | } 51 | value := int(prices[j+3]) 52 | candidates[key] = value 53 | } 54 | for k, v := range candidates { 55 | profits[k] += v 56 | } 57 | } 58 | fmt.Println(sum) 59 | 60 | var best int 61 | for _, v := range profits { 62 | if v > best { 63 | best = v 64 | } 65 | } 66 | fmt.Println(best) 67 | } 68 | 69 | func next(a uint64) uint64 { 70 | a = mix(a, a<<6) 71 | a = prune(a) 72 | a = mix(a, a>>5) 73 | a = prune(a) 74 | a = mix(a, a<<11) 75 | a = prune(a) 76 | return a 77 | } 78 | 79 | func mix(a, b uint64) uint64 { 80 | return a ^ b 81 | } 82 | func prune(a uint64) uint64 { 83 | return a % 16777216 84 | } 85 | -------------------------------------------------------------------------------- /2024/day23.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "slices" 8 | "strings" 9 | ) 10 | 11 | var inputFile = flag.String("inputFile", "inputs/day23.input", "Relative file path to use as input.") 12 | 13 | type triad [3]string 14 | 15 | func main() { 16 | flag.Parse() 17 | bytes, err := ioutil.ReadFile(*inputFile) 18 | if err != nil { 19 | return 20 | } 21 | contents := string(bytes) 22 | split := strings.Split(contents, "\n") 23 | 24 | relations := make(map[string][]string) 25 | for _, s := range split[:len(split)-1] { 26 | parts := strings.Split(s, "-") 27 | a := parts[0] 28 | b := parts[1] 29 | relations[a] = append(relations[a], b) 30 | relations[b] = append(relations[b], a) 31 | } 32 | 33 | found := make(map[triad]bool) 34 | for a, v := range relations { 35 | if len(v) < 2 { 36 | continue 37 | } 38 | if a[0] != 't' { 39 | continue 40 | } 41 | for i := range len(v) - 1 { 42 | b := v[i] 43 | for j := i + 1; j < len(v); j++ { 44 | c := v[j] 45 | if slices.Contains(relations[b], c) { 46 | key := []string{a, b, c} 47 | slices.Sort(key) 48 | found[triad{key[0], key[1], key[2]}] = true 49 | } 50 | } 51 | } 52 | } 53 | fmt.Println(len(found)) 54 | 55 | var largest []string 56 | for a, v := range relations { 57 | cohort := expand(relations, []string{a}, v) 58 | if len(cohort) > len(largest) { 59 | largest = slices.Clone(cohort) 60 | } 61 | } 62 | slices.Sort(largest) 63 | fmt.Println(strings.Join(largest, ",")) 64 | } 65 | 66 | func expand(relations map[string][]string, cohort, candidates []string) []string { 67 | // If we're unable to expand, return just what we have. 68 | largest := cohort 69 | proposed := make([]string, len(cohort)+1) 70 | copy(proposed, cohort) 71 | outer: 72 | for i, c := range candidates { 73 | for _, v := range cohort { 74 | if !slices.Contains(relations[v], c) { 75 | continue outer 76 | } 77 | } 78 | proposed[len(cohort)] = c 79 | if res := expand(relations, proposed, candidates[i+1:]); len(res) > len(largest) { 80 | largest = slices.Clone(res) 81 | } 82 | } 83 | return largest 84 | } 85 | -------------------------------------------------------------------------------- /2024/day24.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strings" 8 | ) 9 | 10 | var inputFile = flag.String("inputFile", "inputs/day24.input", "Relative file path to use as input.") 11 | 12 | type gate struct { 13 | a, b, out string 14 | op string 15 | } 16 | 17 | func main() { 18 | flag.Parse() 19 | bytes, err := ioutil.ReadFile(*inputFile) 20 | if err != nil { 21 | return 22 | } 23 | contents := string(bytes) 24 | split := strings.Split(contents, "\n") 25 | 26 | var gateList bool 27 | var gates []gate 28 | values := make(map[string]bool) 29 | for _, s := range split[:len(split)-1] { 30 | if len(s) == 0 { 31 | gateList = true 32 | continue 33 | } 34 | if !gateList { 35 | parts := strings.Split(s, ": ") 36 | key := parts[0] 37 | val := parts[1] == "1" 38 | values[key] = val 39 | } else { 40 | parts := strings.Split(s, " ") 41 | a := parts[0] 42 | op := parts[1] 43 | b := parts[2] 44 | out := parts[4] 45 | gates = append(gates, gate{a, b, out, op}) 46 | } 47 | } 48 | 49 | process(gates, values) 50 | 51 | var result uint64 52 | for i := 0; ; i++ { 53 | key := fmt.Sprintf("z%02d", i) 54 | val, ok := values[key] 55 | if !ok { 56 | break 57 | } 58 | if val { 59 | result += 1 << i 60 | } 61 | } 62 | fmt.Println(result) 63 | 64 | // Part B is not brute forceable; there are 222 nPr 8 / (2^4 * 4!) possible swaps. 65 | // Very few people have solved half an hour in, so this one's going to be a toughie. 66 | } 67 | 68 | func process(gates []gate, values map[string]bool) { 69 | for { 70 | var updated bool 71 | for _, gate := range gates { 72 | if _, ok := values[gate.out]; ok { 73 | continue 74 | } 75 | a, ok := values[gate.a] 76 | if !ok { 77 | continue 78 | } 79 | b, ok := values[gate.b] 80 | if !ok { 81 | continue 82 | } 83 | updated = true 84 | switch gate.op { 85 | case "OR": 86 | values[gate.out] = a || b 87 | case "AND": 88 | values[gate.out] = a && b 89 | case "XOR": 90 | values[gate.out] = a != b 91 | } 92 | } 93 | if !updated { 94 | break 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /2024/day25.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "strings" 8 | ) 9 | 10 | var inputFile = flag.String("inputFile", "inputs/day25.input", "Relative file path to use as input.") 11 | 12 | func main() { 13 | flag.Parse() 14 | bytes, err := ioutil.ReadFile(*inputFile) 15 | if err != nil { 16 | return 17 | } 18 | contents := string(bytes) 19 | split := strings.Split(contents, "\n") 20 | 21 | var keys, locks [][5]int 22 | for i := 0; i < len(split[:len(split)-1]); i += 8 { 23 | var state [5]int 24 | for j := range 7 { 25 | for c, v := range split[i+j] { 26 | if v == '#' { 27 | state[c]++ 28 | } 29 | } 30 | } 31 | if split[i][0] == '#' { 32 | locks = append(locks, state) 33 | } else { 34 | keys = append(keys, state) 35 | } 36 | } 37 | 38 | var matches int 39 | for _, k := range keys { 40 | outer: 41 | for _, l := range locks { 42 | for i := range 5 { 43 | if k[i]+l[i] > 7 { 44 | continue outer 45 | } 46 | } 47 | matches++ 48 | } 49 | } 50 | fmt.Println(matches) 51 | } 52 | -------------------------------------------------------------------------------- /2024/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/lizthegrey/adventofcode/2024 2 | 3 | go 1.23 4 | 5 | require github.com/lizthegrey/adventofcode/2022 v0.0.0-20241215071341-3986bbd6e25f // indirect 6 | -------------------------------------------------------------------------------- /2024/go.sum: -------------------------------------------------------------------------------- 1 | github.com/lizthegrey/adventofcode/2022 v0.0.0-20241215071341-3986bbd6e25f h1:keHn3rOV6r0aLRH+prBALTQogj7NFviAArIG8zFlRhU= 2 | github.com/lizthegrey/adventofcode/2022 v0.0.0-20241215071341-3986bbd6e25f/go.mod h1:Q4sLWj8X5kQjol0CQb/NLq0sGK0veCWf8cUGK5Hm4H4= 3 | --------------------------------------------------------------------------------