Compare commits
28 Commits
0b851158dc
...
master
Author | SHA1 | Date | |
---|---|---|---|
3e9fe4153c | |||
f1d6903fba | |||
16784775c9 | |||
df30e9abc0 | |||
17ab6d7144 | |||
42c77627d1 | |||
f744ac6745 | |||
44fb58ff14 | |||
010a843471 | |||
d2b4bc2921 | |||
9821836b6c | |||
0cd38cfd03 | |||
d8ed532442 | |||
e1d3ca5ea2 | |||
0883efd35e | |||
353d5e92c9 | |||
9d406c2d22 | |||
80142a25fb | |||
e2ac00420d | |||
18bccde2f5 | |||
559d91b9a6 | |||
6ed45d319b | |||
1124022f4f | |||
f5ec48772e | |||
55fa312abf | |||
24b59f71fa | |||
9244b2e178 | |||
56cd171588 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
input
|
||||
input
|
||||
testinput
|
@@ -5,11 +5,16 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
// [...] they need you to find the two entries that sum to 2020 and then multiply those two numbers together.
|
||||
|
@@ -6,12 +6,17 @@ import (
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
//To try to debug the problem, they have created a list (your puzzle input) of passwords (according to the corrupted database) and the corporate policy when that password was set.
|
||||
|
@@ -5,11 +5,16 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
type slope struct {
|
||||
|
@@ -6,11 +6,16 @@ import (
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
type passport struct {
|
||||
|
@@ -5,11 +5,16 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
type boardingPass struct {
|
||||
|
@@ -5,11 +5,16 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
func union(s string) string {
|
||||
|
@@ -6,11 +6,16 @@ import (
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
// Bag is a bag that contains other bags
|
||||
|
@@ -6,11 +6,16 @@ import (
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
type opCode struct {
|
||||
|
@@ -6,11 +6,16 @@ import (
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
func checkNotSum(t int, s []int) bool {
|
||||
|
122
10/main.go
Normal file
122
10/main.go
Normal file
@@ -0,0 +1,122 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
// Find a chain that uses all of your adapters to connect the charging outlet to your device's built-in adapter
|
||||
// and count the joltage differences between the charging outlet, the adapters, and your device.
|
||||
// What is the number of 1-jolt differences multiplied by the number of 3-jolt differences?
|
||||
|
||||
func partOne() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
// wall
|
||||
adapters := []int{0}
|
||||
|
||||
// load
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
i, _ := strconv.Atoi(line)
|
||||
adapters = append(adapters, i)
|
||||
}
|
||||
|
||||
// sort
|
||||
sort.Slice(adapters, func(i, j int) bool {
|
||||
return adapters[i] < adapters[j]
|
||||
})
|
||||
|
||||
// device
|
||||
adapters = append(adapters, adapters[len(adapters)-1]+3)
|
||||
|
||||
one := 0
|
||||
three := 0
|
||||
|
||||
// count gaps
|
||||
for i, j := range adapters {
|
||||
if i < len(adapters)-1 {
|
||||
if adapters[i+1]-j == 3 {
|
||||
three = three + 1
|
||||
} else if adapters[i+1]-j == 1 {
|
||||
one = one + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println(three * one)
|
||||
}
|
||||
|
||||
// You glance back down at your bag and try to remember why you brought so many adapters; there must be more than a trillion valid ways to arrange them!
|
||||
// Surely, there must be an efficient way to count the arrangements.
|
||||
// What is the total number of distinct ways you can arrange the adapters to connect the charging outlet to your device?
|
||||
|
||||
func partTwo() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
// wall
|
||||
adapters := []int{0}
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
i, _ := strconv.Atoi(line)
|
||||
adapters = append(adapters, i)
|
||||
}
|
||||
|
||||
// sort
|
||||
sort.Slice(adapters, func(i, j int) bool {
|
||||
return adapters[i] < adapters[j]
|
||||
})
|
||||
|
||||
// device
|
||||
adapters = append(adapters, adapters[len(adapters)-1]+3)
|
||||
|
||||
combinations := 1
|
||||
ones := 0
|
||||
three := 0
|
||||
|
||||
// count gaps
|
||||
for i, j := range adapters {
|
||||
if i < len(adapters)-1 {
|
||||
if adapters[i+1]-j == 1 {
|
||||
// small steps...
|
||||
ones = ones + 1
|
||||
} else if adapters[i+1]-j == 3 {
|
||||
combinations = combinations + 1
|
||||
if ones == 2 {
|
||||
combinations = combinations * 1
|
||||
}
|
||||
if ones == 3 {
|
||||
combinations = combinations * 2
|
||||
}
|
||||
if ones == 4 {
|
||||
combinations = combinations * 4
|
||||
}
|
||||
if ones == 5 {
|
||||
combinations = combinations * 7
|
||||
}
|
||||
ones = 0
|
||||
three = three + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println(adapters, combinations)
|
||||
}
|
303
11/main.go
Normal file
303
11/main.go
Normal file
@@ -0,0 +1,303 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
type cell struct {
|
||||
taken int // 1: someone there; 0: empty
|
||||
seat bool // t: seat available; f: open floor
|
||||
}
|
||||
|
||||
type waitingRoom struct {
|
||||
seats [][]cell
|
||||
maxCol int
|
||||
maxRow int
|
||||
}
|
||||
|
||||
func (w *waitingRoom) parseRow(s string) {
|
||||
row := []cell{}
|
||||
|
||||
for _, r := range s {
|
||||
switch {
|
||||
case r == 'L':
|
||||
row = append(row, cell{taken: 0, seat: true})
|
||||
case r == '.':
|
||||
row = append(row, cell{taken: 0, seat: false})
|
||||
}
|
||||
}
|
||||
w.seats = append(w.seats, row)
|
||||
}
|
||||
|
||||
func (w *waitingRoom) countNeighborsPartOne(row, col int) int {
|
||||
c := 0
|
||||
if row-1 > -1 {
|
||||
c = c + w.seats[row-1][col].taken // north
|
||||
if col-1 > -1 {
|
||||
c = c + w.seats[row-1][col-1].taken // northwest
|
||||
}
|
||||
if col+1 < w.maxCol {
|
||||
c = c + w.seats[row-1][col+1].taken // northheast
|
||||
}
|
||||
}
|
||||
if row+1 < w.maxRow {
|
||||
c = c + w.seats[row+1][col].taken // south
|
||||
if col+1 < w.maxCol {
|
||||
c = c + w.seats[row+1][col+1].taken // southeast
|
||||
}
|
||||
if col-1 > -1 {
|
||||
c = c + w.seats[row+1][col-1].taken // southwest
|
||||
}
|
||||
}
|
||||
if col-1 > -1 {
|
||||
c = c + w.seats[row][col-1].taken // west
|
||||
}
|
||||
if col+1 < w.maxCol {
|
||||
c = c + w.seats[row][col+1].taken // east
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (w *waitingRoom) makeNextStepPartOne() *waitingRoom {
|
||||
newRoom := &waitingRoom{
|
||||
seats: make([][]cell, w.maxRow),
|
||||
maxCol: w.maxCol,
|
||||
maxRow: w.maxRow,
|
||||
}
|
||||
|
||||
for r := range w.seats {
|
||||
newRoom.seats[r] = make([]cell, newRoom.maxCol)
|
||||
for c := range w.seats[r] {
|
||||
if w.seats[r][c].seat && w.seats[r][c].taken == 0 && w.countNeighborsPartOne(r, c) == 0 {
|
||||
newRoom.seats[r][c].seat = w.seats[r][c].seat
|
||||
newRoom.seats[r][c].taken = 1
|
||||
} else if w.seats[r][c].seat && w.seats[r][c].taken == 1 && w.countNeighborsPartOne(r, c) >= 4 {
|
||||
newRoom.seats[r][c].seat = w.seats[r][c].seat
|
||||
newRoom.seats[r][c].taken = 0
|
||||
} else {
|
||||
newRoom.seats[r][c].seat = w.seats[r][c].seat
|
||||
newRoom.seats[r][c].taken = w.seats[r][c].taken
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newRoom
|
||||
}
|
||||
|
||||
func (w *waitingRoom) countNeighborsPartTwo(row, col int) int {
|
||||
c := 0
|
||||
trow := row
|
||||
tcol := col
|
||||
|
||||
// south
|
||||
for trow < w.maxRow-1 {
|
||||
trow = trow + 1
|
||||
if w.seats[trow][col].seat {
|
||||
c = c + w.seats[trow][col].taken
|
||||
break
|
||||
}
|
||||
}
|
||||
trow = row
|
||||
|
||||
// north
|
||||
for trow > 0 {
|
||||
trow = trow - 1
|
||||
if w.seats[trow][col].seat {
|
||||
c = c + w.seats[trow][col].taken
|
||||
break
|
||||
}
|
||||
}
|
||||
trow = row
|
||||
|
||||
// west
|
||||
for tcol > 0 {
|
||||
tcol = tcol - 1
|
||||
if w.seats[row][tcol].seat {
|
||||
c = c + w.seats[row][tcol].taken
|
||||
break
|
||||
}
|
||||
}
|
||||
tcol = col
|
||||
|
||||
// east
|
||||
for tcol < w.maxCol-1 {
|
||||
tcol = tcol + 1
|
||||
if w.seats[row][tcol].seat {
|
||||
c = c + w.seats[row][tcol].taken
|
||||
break
|
||||
}
|
||||
}
|
||||
tcol = col
|
||||
|
||||
// southeast
|
||||
for trow < w.maxRow-1 && tcol < w.maxCol-1 {
|
||||
trow = trow + 1
|
||||
tcol = tcol + 1
|
||||
if w.seats[trow][tcol].seat {
|
||||
c = c + w.seats[trow][tcol].taken
|
||||
break
|
||||
}
|
||||
}
|
||||
trow = row
|
||||
tcol = col
|
||||
|
||||
// southwest
|
||||
for trow < w.maxRow-1 && tcol > 0 {
|
||||
trow = trow + 1
|
||||
tcol = tcol - 1
|
||||
if w.seats[trow][tcol].seat {
|
||||
c = c + w.seats[trow][tcol].taken
|
||||
break
|
||||
}
|
||||
}
|
||||
trow = row
|
||||
tcol = col
|
||||
|
||||
// northeast
|
||||
for trow > 0 && tcol < w.maxCol-1 {
|
||||
trow = trow - 1
|
||||
tcol = tcol + 1
|
||||
if w.seats[trow][tcol].seat {
|
||||
c = c + w.seats[trow][tcol].taken
|
||||
break
|
||||
}
|
||||
}
|
||||
trow = row
|
||||
tcol = col
|
||||
|
||||
// northwest
|
||||
for trow > 0 && tcol > 0 {
|
||||
trow = trow - 1
|
||||
tcol = tcol - 1
|
||||
if w.seats[trow][tcol].seat {
|
||||
c = c + w.seats[trow][tcol].taken
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (w *waitingRoom) makeNextStepPartTwo() *waitingRoom {
|
||||
newRoom := &waitingRoom{
|
||||
seats: make([][]cell, w.maxRow),
|
||||
maxCol: w.maxCol,
|
||||
maxRow: w.maxRow,
|
||||
}
|
||||
|
||||
for r := range w.seats {
|
||||
newRoom.seats[r] = make([]cell, newRoom.maxCol)
|
||||
for c := range w.seats[r] {
|
||||
if w.seats[r][c].seat && w.seats[r][c].taken == 0 && w.countNeighborsPartTwo(r, c) == 0 {
|
||||
newRoom.seats[r][c].seat = w.seats[r][c].seat
|
||||
newRoom.seats[r][c].taken = 1
|
||||
} else if w.seats[r][c].seat && w.seats[r][c].taken == 1 && w.countNeighborsPartTwo(r, c) >= 5 {
|
||||
newRoom.seats[r][c].seat = w.seats[r][c].seat
|
||||
newRoom.seats[r][c].taken = 0
|
||||
} else {
|
||||
newRoom.seats[r][c].seat = w.seats[r][c].seat
|
||||
newRoom.seats[r][c].taken = w.seats[r][c].taken
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newRoom
|
||||
}
|
||||
|
||||
func (w *waitingRoom) countFilledSeats() int {
|
||||
count := 0
|
||||
for r := range w.seats {
|
||||
for c := range w.seats[r] {
|
||||
count = count + w.seats[r][c].taken
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func (w *waitingRoom) print() {
|
||||
for r := range w.seats {
|
||||
s := ""
|
||||
for c := range w.seats[r] {
|
||||
if !w.seats[r][c].seat {
|
||||
s = s + "."
|
||||
} else if w.seats[r][c].taken == 0 {
|
||||
s = s + "L"
|
||||
} else if w.seats[r][c].taken == 1 {
|
||||
s = s + "#"
|
||||
}
|
||||
}
|
||||
fmt.Println(s)
|
||||
}
|
||||
}
|
||||
|
||||
func checkStasis(old, new *waitingRoom) bool {
|
||||
for r := range old.seats {
|
||||
for c := range old.seats[r] {
|
||||
if old.seats[r][c].seat && old.seats[r][c].taken != new.seats[r][c].taken {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func partOne() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
oldRoom := &waitingRoom{}
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
oldRoom.parseRow(line)
|
||||
}
|
||||
|
||||
oldRoom.maxCol = len(oldRoom.seats[0])
|
||||
oldRoom.maxRow = len(oldRoom.seats)
|
||||
|
||||
for {
|
||||
newRoom := oldRoom.makeNextStepPartOne()
|
||||
if checkStasis(newRoom, oldRoom) {
|
||||
fmt.Println(newRoom.countFilledSeats())
|
||||
return
|
||||
}
|
||||
oldRoom = newRoom
|
||||
}
|
||||
}
|
||||
|
||||
func partTwo() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
oldRoom := &waitingRoom{}
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
oldRoom.parseRow(line)
|
||||
}
|
||||
|
||||
oldRoom.maxCol = len(oldRoom.seats[0])
|
||||
oldRoom.maxRow = len(oldRoom.seats)
|
||||
|
||||
for {
|
||||
newRoom := oldRoom.makeNextStepPartTwo()
|
||||
if checkStasis(newRoom, oldRoom) {
|
||||
fmt.Println(newRoom.countFilledSeats())
|
||||
return
|
||||
}
|
||||
oldRoom = newRoom
|
||||
}
|
||||
}
|
197
12/main.go
Normal file
197
12/main.go
Normal file
@@ -0,0 +1,197 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
type ship struct {
|
||||
horizontal int
|
||||
vertical int
|
||||
direction rune // E W N S
|
||||
}
|
||||
|
||||
// both horizontal/vertical are relative to ship
|
||||
type waypoint struct {
|
||||
horizontal int
|
||||
vertical int
|
||||
}
|
||||
|
||||
func parseLine(s string) (rune, int) {
|
||||
i, _ := strconv.Atoi(s[1:])
|
||||
return []rune(s)[0], i
|
||||
}
|
||||
|
||||
func movePartOne(s ship, action rune, vector int) ship {
|
||||
switch action {
|
||||
case 'R', 'L':
|
||||
s.direction = rotateShip(s.direction, vector, action)
|
||||
case 'N':
|
||||
s.vertical = s.vertical + vector
|
||||
case 'S':
|
||||
s.vertical = s.vertical - vector
|
||||
case 'E':
|
||||
s.horizontal = s.horizontal + vector
|
||||
case 'W':
|
||||
s.horizontal = s.horizontal - vector
|
||||
case 'F':
|
||||
s = movePartOne(s, s.direction, vector)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func movePartTwo(s ship, w waypoint, action rune, vector int) (ship, waypoint) {
|
||||
switch action {
|
||||
case 'R', 'L':
|
||||
w = rotateWaypoint(w, action, vector)
|
||||
case 'N':
|
||||
w.vertical = w.vertical + vector
|
||||
case 'S':
|
||||
w.vertical = w.vertical - vector
|
||||
case 'E':
|
||||
w.horizontal = w.horizontal + vector
|
||||
case 'W':
|
||||
w.horizontal = w.horizontal - vector
|
||||
case 'F':
|
||||
s.horizontal = s.horizontal + (w.horizontal * vector)
|
||||
s.vertical = s.vertical + (w.vertical * vector)
|
||||
}
|
||||
return s, w
|
||||
}
|
||||
|
||||
// for example E, 90, L => N; E, 180, R => W; E, 90, R => S
|
||||
func rotateShip(current rune, degrees int, rl rune) rune {
|
||||
// easy
|
||||
if degrees == 180 {
|
||||
switch current {
|
||||
case 'N':
|
||||
return 'S'
|
||||
case 'S':
|
||||
return 'N'
|
||||
case 'E':
|
||||
return 'W'
|
||||
case 'W':
|
||||
return 'E'
|
||||
}
|
||||
}
|
||||
// translate L to R
|
||||
if rl == 'L' {
|
||||
if degrees == 90 {
|
||||
degrees = 270
|
||||
} else {
|
||||
degrees = 90
|
||||
}
|
||||
}
|
||||
// the R set
|
||||
if degrees == 90 {
|
||||
switch current {
|
||||
case 'N':
|
||||
return 'E'
|
||||
case 'S':
|
||||
return 'W'
|
||||
case 'E':
|
||||
return 'S'
|
||||
case 'W':
|
||||
return 'N'
|
||||
}
|
||||
} else if degrees == 270 {
|
||||
switch current {
|
||||
case 'N':
|
||||
return 'W'
|
||||
case 'S':
|
||||
return 'E'
|
||||
case 'E':
|
||||
return 'N'
|
||||
case 'W':
|
||||
return 'S'
|
||||
}
|
||||
}
|
||||
|
||||
return current // we're made a terrible mistake somewhere
|
||||
}
|
||||
|
||||
func rotateWaypoint(w waypoint, action rune, vector int) waypoint {
|
||||
if vector == 180 {
|
||||
w.horizontal, w.vertical = -w.horizontal, -w.vertical
|
||||
} else if action == 'R' {
|
||||
if vector == 90 {
|
||||
w.horizontal, w.vertical = w.vertical, -w.horizontal
|
||||
}
|
||||
if vector == 270 {
|
||||
w.horizontal, w.vertical = -w.vertical, w.horizontal
|
||||
}
|
||||
} else if action == 'L' {
|
||||
if vector == 90 {
|
||||
w.horizontal, w.vertical = -w.vertical, w.horizontal
|
||||
}
|
||||
if vector == 270 {
|
||||
w.horizontal, w.vertical = w.vertical, -w.horizontal
|
||||
}
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
func manhattan(i, j int) int {
|
||||
return abs(i) + abs(j)
|
||||
}
|
||||
|
||||
func abs(x int) int {
|
||||
if x < 0 {
|
||||
return -x
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
func partOne() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
s := ship{
|
||||
horizontal: 0,
|
||||
vertical: 0,
|
||||
direction: 'E',
|
||||
}
|
||||
|
||||
for scanner.Scan() {
|
||||
dir, vec := parseLine(scanner.Text())
|
||||
s = movePartOne(s, dir, vec)
|
||||
}
|
||||
|
||||
fmt.Println(manhattan(s.horizontal, s.vertical))
|
||||
}
|
||||
|
||||
func partTwo() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
s := ship{
|
||||
horizontal: 0,
|
||||
vertical: 0,
|
||||
// no longer care about ship direction
|
||||
}
|
||||
w := waypoint{
|
||||
horizontal: 10,
|
||||
vertical: 1,
|
||||
}
|
||||
|
||||
for scanner.Scan() {
|
||||
dir, vec := parseLine(scanner.Text())
|
||||
s, w = movePartTwo(s, w, dir, vec)
|
||||
}
|
||||
|
||||
fmt.Println(manhattan(s.horizontal, s.vertical))
|
||||
}
|
89
13/main.go
Normal file
89
13/main.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
func partOne() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
busWaitTimes := map[int]int{}
|
||||
shortestWait := -1
|
||||
|
||||
// don't actually need loop logic for a two-line input
|
||||
scanner.Scan()
|
||||
ts := scanner.Text()
|
||||
currentTimestamp, _ := strconv.Atoi(ts)
|
||||
|
||||
scanner.Scan()
|
||||
buses := strings.Split(scanner.Text(), ",")
|
||||
for _, b := range buses {
|
||||
if b != "x" {
|
||||
j, _ := strconv.Atoi(b)
|
||||
busWaitTimes[j] = (((currentTimestamp / j) + 1) * j) - currentTimestamp
|
||||
if shortestWait == -1 || busWaitTimes[shortestWait] > busWaitTimes[j] {
|
||||
shortestWait = j
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println(busWaitTimes[shortestWait] * shortestWait)
|
||||
}
|
||||
|
||||
type bus struct {
|
||||
route float64
|
||||
offset float64
|
||||
}
|
||||
|
||||
func partTwo() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
buses := []bus{}
|
||||
|
||||
scanner.Scan() // discard first line
|
||||
scanner.Scan()
|
||||
line := strings.Split(scanner.Text(), ",")
|
||||
for i, b := range line {
|
||||
if b != "x" {
|
||||
j, _ := strconv.Atoi(b)
|
||||
buses = append(buses, bus{
|
||||
route: float64(j),
|
||||
offset: float64(i),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
t := float64(0)
|
||||
offset := buses[0].route
|
||||
loop:
|
||||
t = t + offset
|
||||
offset = 1
|
||||
for i := range buses {
|
||||
if math.Remainder(t+buses[i].offset, buses[i].route) != 0 {
|
||||
goto loop
|
||||
} else {
|
||||
offset = offset * buses[i].route
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println(int(t))
|
||||
}
|
148
14/main.go
Normal file
148
14/main.go
Normal file
@@ -0,0 +1,148 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
type bitset struct {
|
||||
bits []string
|
||||
}
|
||||
|
||||
func newBitset(i int) bitset {
|
||||
b := strings.Split(fmt.Sprintf("%036b", i), "")
|
||||
bs := bitset{
|
||||
bits: strings.Split("000000000000000000000000000000000000", ""),
|
||||
}
|
||||
for i, bt := range b {
|
||||
if bt != "0" {
|
||||
bs.bits[i] = bt
|
||||
}
|
||||
}
|
||||
return bs
|
||||
}
|
||||
|
||||
func (b bitset) ToInt() int {
|
||||
i, _ := strconv.ParseInt(strings.Join(b.bits, ""), 2, 64)
|
||||
return int(i)
|
||||
}
|
||||
|
||||
func parseMask(s string) string {
|
||||
return strings.Split(s, " = ")[1]
|
||||
}
|
||||
|
||||
func parseMemoryToAddrAndBitset(s string) (int, bitset) {
|
||||
a := strings.Split(s, " = ")
|
||||
addr, _ := strconv.Atoi(strings.TrimFunc(a[0], func(r rune) bool {
|
||||
if r == 'm' || r == 'e' || r == '[' || r == ']' {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}))
|
||||
i, _ := strconv.Atoi(a[1])
|
||||
return addr, newBitset(i)
|
||||
}
|
||||
|
||||
func parseMemoryToBitsetAndValue(s string) (bitset, int) {
|
||||
a := strings.Split(s, " = ")
|
||||
addr, _ := strconv.Atoi(strings.TrimFunc(a[0], func(r rune) bool {
|
||||
if r == 'm' || r == 'e' || r == '[' || r == ']' {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}))
|
||||
i, _ := strconv.Atoi(a[1])
|
||||
return newBitset(addr), i
|
||||
}
|
||||
|
||||
func applyMaskToValue(m string, b bitset) int {
|
||||
for i, r := range m {
|
||||
if r == '0' {
|
||||
b.bits[i] = "0"
|
||||
}
|
||||
if r == '1' {
|
||||
b.bits[i] = "1"
|
||||
}
|
||||
}
|
||||
return b.ToInt()
|
||||
}
|
||||
|
||||
func applyMaskToAddr(prefix []string, m string, attr bitset) []bitset {
|
||||
if len(m) == 0 {
|
||||
return []bitset{{bits: prefix}}
|
||||
}
|
||||
res := []bitset{}
|
||||
for _, r := range m {
|
||||
if r == '0' || r == 'X' {
|
||||
res = append(res, applyMaskToAddr(append(prefix[:], "0"), m[1:], attr)...)
|
||||
}
|
||||
if r == '1' || r == 'X' {
|
||||
res = append(res, applyMaskToAddr(append(prefix[:], "1"), m[1:], attr)...)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func partOne() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
mem := map[int]int{}
|
||||
var currentMask string
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if strings.HasPrefix(line, "mask") {
|
||||
currentMask = parseMask(line)
|
||||
} else {
|
||||
addr, value := parseMemoryToAddrAndBitset(line)
|
||||
mem[addr] = applyMaskToValue(currentMask, value)
|
||||
}
|
||||
}
|
||||
sum := 0
|
||||
for _, v := range mem {
|
||||
sum = sum + v
|
||||
}
|
||||
fmt.Println(sum)
|
||||
}
|
||||
|
||||
func partTwo() {
|
||||
f, _ := os.Open("testinput")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
mem := map[int]int{}
|
||||
var currentMask string
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if strings.HasPrefix(line, "mask") {
|
||||
currentMask = parseMask(line)
|
||||
} else {
|
||||
addr, value := parseMemoryToBitsetAndValue(line)
|
||||
for _, b := range applyMaskToAddr([]string{}, currentMask, addr) {
|
||||
mem[b.ToInt()] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sum := 0
|
||||
for _, v := range mem {
|
||||
sum = sum + v
|
||||
}
|
||||
fmt.Println(sum)
|
||||
}
|
85
15/main.go
Normal file
85
15/main.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
func partOne() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
// naive implementation
|
||||
scanner.Scan()
|
||||
numbers := strings.Split(scanner.Text(), ",")
|
||||
for {
|
||||
i := len(numbers) - 1
|
||||
lastNumber := numbers[i]
|
||||
// walk backward through the list, looking for a match
|
||||
for j := range numbers {
|
||||
if len(numbers) == 2020 {
|
||||
fmt.Println(numbers[2019])
|
||||
return
|
||||
}
|
||||
if i-j == 0 {
|
||||
// made it to the head of the list, no matches; append 0
|
||||
numbers = append(numbers, "0")
|
||||
break
|
||||
} else if numbers[i-j-1] == lastNumber {
|
||||
// a number matched; insert the difference between the last number and when we last saw it
|
||||
numbers = append(numbers, fmt.Sprintf("%d", len(numbers)-1-(i-j-1)))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func partTwo() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
// caching implementation. It's still slow, but it at least finishes...!
|
||||
scanner.Scan()
|
||||
numbers := strings.Split(scanner.Text(), ",")
|
||||
seen := make(map[int]int)
|
||||
|
||||
// load in numbers
|
||||
for i, v := range numbers[:len(numbers)-1] {
|
||||
j, _ := strconv.Atoi(v)
|
||||
seen[j] = i
|
||||
}
|
||||
|
||||
i := len(numbers) - 1
|
||||
lastNumber, _ := strconv.Atoi(numbers[i])
|
||||
for {
|
||||
if seen[lastNumber] == 30000000-1 {
|
||||
fmt.Println(lastNumber)
|
||||
return
|
||||
}
|
||||
if v, ok := seen[lastNumber]; ok {
|
||||
// a number matched; insert the difference between the last number and when we last saw it
|
||||
seen[lastNumber] = i
|
||||
lastNumber = i - v
|
||||
} else {
|
||||
// never seen that before
|
||||
seen[lastNumber] = i
|
||||
lastNumber = 0
|
||||
}
|
||||
i = i + 1
|
||||
}
|
||||
}
|
232
16/main.go
Normal file
232
16/main.go
Normal file
@@ -0,0 +1,232 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
type valuerange struct {
|
||||
min int
|
||||
max int
|
||||
}
|
||||
|
||||
type rule struct {
|
||||
name string
|
||||
ranges []valuerange
|
||||
field int
|
||||
}
|
||||
|
||||
type ticket struct {
|
||||
values []int
|
||||
valid bool
|
||||
}
|
||||
|
||||
func parseRule(s string) rule {
|
||||
q := strings.Split(s, ": ")
|
||||
ru := rule{
|
||||
name: q[0],
|
||||
ranges: make([]valuerange, 0, 2),
|
||||
}
|
||||
r := strings.Split(q[1], " or ")
|
||||
for _, rr := range r {
|
||||
mm := strings.Split(rr, "-")
|
||||
min, _ := strconv.Atoi(mm[0])
|
||||
max, _ := strconv.Atoi(mm[1])
|
||||
ru.ranges = append(ru.ranges, valuerange{min: min, max: max})
|
||||
}
|
||||
return ru
|
||||
}
|
||||
|
||||
func parseTicket(s string) ticket {
|
||||
t := ticket{
|
||||
values: []int{},
|
||||
}
|
||||
vs := strings.Split(s, ",")
|
||||
for _, v := range vs {
|
||||
vi, _ := strconv.Atoi(v)
|
||||
t.values = append(t.values, vi)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// return valid/invalid and the invalid value, if found
|
||||
func validateTicket(r []rule, t ticket) (bool, int) {
|
||||
for _, ta := range t.values {
|
||||
ok := false
|
||||
for _, ru := range r {
|
||||
outsideRule := (ta < ru.ranges[0].min || (ta > ru.ranges[0].max && ta < ru.ranges[1].min) || ta > ru.ranges[1].max)
|
||||
if ok || !outsideRule {
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
return false, ta
|
||||
}
|
||||
}
|
||||
return true, 0
|
||||
}
|
||||
|
||||
// returns a mapping of fields to possible rules
|
||||
func guessFields(rules []rule, tickets []ticket) map[int][]int {
|
||||
guesses := map[int][]int{}
|
||||
for ri, r := range rules {
|
||||
for i := range tickets[0].values { // loop over the fields on tickets, and...
|
||||
ok := true
|
||||
for _, t := range tickets { // loop over all the tickets...
|
||||
ta := t.values[i] // checking the current field value...
|
||||
outsideRule := (ta < r.ranges[0].min || (ta > r.ranges[0].max && ta < r.ranges[1].min) || ta > r.ranges[1].max)
|
||||
if !ok || outsideRule { // checking if any of the tickets fall outside our rule's range.
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
if ok { // if all tickets pass, we've got a hit
|
||||
guesses[i] = append(guesses[i], ri)
|
||||
}
|
||||
}
|
||||
}
|
||||
return guesses
|
||||
}
|
||||
|
||||
func reduce(g map[int][]int) (int, int) {
|
||||
for i, j := range g {
|
||||
if len(j) == 1 {
|
||||
return i, j[0]
|
||||
}
|
||||
}
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
func remove(a []int, rule int) []int {
|
||||
for i, j := range a {
|
||||
if j == rule {
|
||||
a[i] = a[len(a)-1]
|
||||
a[len(a)-1] = 0
|
||||
return a[:len(a)-1]
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func partOne() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
rules := []rule{}
|
||||
tickets := []ticket{}
|
||||
invalidityRate := 0
|
||||
|
||||
seenAllRules := false
|
||||
seenMyTicket := false
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
// check for end of rules list
|
||||
if line == "" && !seenAllRules {
|
||||
seenAllRules = true
|
||||
scanner.Scan() // clear out the following line
|
||||
continue
|
||||
}
|
||||
// parse rules until we run out
|
||||
if !seenAllRules {
|
||||
rules = append(rules, parseRule(line))
|
||||
continue
|
||||
}
|
||||
// parse own ticket, then clear for remaining tickets
|
||||
if !seenMyTicket {
|
||||
tickets = append(tickets, parseTicket(scanner.Text()))
|
||||
seenMyTicket = true
|
||||
scanner.Scan() // clear out the next two lines
|
||||
scanner.Scan()
|
||||
continue
|
||||
}
|
||||
// parse and validate other people's tickets
|
||||
t := parseTicket(line)
|
||||
ok, i := validateTicket(rules, t)
|
||||
if !ok {
|
||||
t.valid = false
|
||||
invalidityRate = invalidityRate + i
|
||||
} else {
|
||||
t.valid = true
|
||||
tickets = append(tickets, t)
|
||||
}
|
||||
}
|
||||
fmt.Println(invalidityRate)
|
||||
}
|
||||
|
||||
func partTwo() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
rules := []rule{}
|
||||
myTicket := ticket{}
|
||||
tickets := []ticket{}
|
||||
|
||||
seenAllRules := false
|
||||
seenMyTicket := false
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
// check for end of rules list
|
||||
if line == "" && !seenAllRules {
|
||||
seenAllRules = true
|
||||
scanner.Scan() // clear out the following line
|
||||
continue
|
||||
}
|
||||
// parse rules until we run out
|
||||
if !seenAllRules {
|
||||
rules = append(rules, parseRule(line))
|
||||
continue
|
||||
}
|
||||
// parse own ticket, then clear for remaining tickets
|
||||
if !seenMyTicket {
|
||||
myTicket = parseTicket(scanner.Text())
|
||||
tickets = append(tickets, myTicket)
|
||||
seenMyTicket = true
|
||||
scanner.Scan() // clear out the next two lines
|
||||
scanner.Scan()
|
||||
continue
|
||||
}
|
||||
// parse and validate other people's tickets
|
||||
t := parseTicket(line)
|
||||
ok, _ := validateTicket(rules, t)
|
||||
if ok {
|
||||
t.valid = true
|
||||
tickets = append(tickets, t)
|
||||
}
|
||||
}
|
||||
guesses := guessFields(rules, tickets)
|
||||
var lastRuleSolved int
|
||||
// iterate over the guesses list. on every iteration, only one field has only one rule remaining that's valid
|
||||
// save it off into the rules list, then remove that rule from all fields.
|
||||
// Luckily, we don't need any higher-order elimination logic.
|
||||
for i := 1; i < len(guesses); i++ {
|
||||
for i, g := range guesses {
|
||||
if len(g) == 1 {
|
||||
rules[g[0]].field = i
|
||||
lastRuleSolved = g[0]
|
||||
}
|
||||
}
|
||||
for i := range guesses {
|
||||
guesses[i] = remove(guesses[i], lastRuleSolved)
|
||||
}
|
||||
}
|
||||
result := 1
|
||||
// first 6 rules are our "departure" rules
|
||||
for _, r := range rules[:6] {
|
||||
result = result * myTicket.values[r.field]
|
||||
}
|
||||
fmt.Println(result)
|
||||
}
|
235
17/main.go
Normal file
235
17/main.go
Normal file
@@ -0,0 +1,235 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
type cell struct {
|
||||
x int // length
|
||||
y int // width
|
||||
z int // depth
|
||||
w int // spissitude
|
||||
}
|
||||
|
||||
type universe struct {
|
||||
cells map[cell]bool
|
||||
hyper bool // true: 4-dimensional; false: 3-dimensional
|
||||
}
|
||||
|
||||
func makeNeighborsList(c cell, hyper bool) []cell {
|
||||
cells := []cell{}
|
||||
if hyper {
|
||||
cells = append(cells, []cell{
|
||||
// outward...?
|
||||
// above
|
||||
{x: c.x + 1, y: c.y + 1, z: c.z + 1, w: c.w + 1},
|
||||
{x: c.x + 1, y: c.y, z: c.z + 1, w: c.w + 1},
|
||||
{x: c.x + 1, y: c.y - 1, z: c.z + 1, w: c.w + 1},
|
||||
{x: c.x, y: c.y + 1, z: c.z + 1, w: c.w + 1},
|
||||
{x: c.x, y: c.y, z: c.z + 1, w: c.w + 1},
|
||||
{x: c.x, y: c.y - 1, z: c.z + 1, w: c.w + 1},
|
||||
{x: c.x - 1, y: c.y + 1, z: c.z + 1, w: c.w + 1},
|
||||
{x: c.x - 1, y: c.y, z: c.z + 1, w: c.w + 1},
|
||||
{x: c.x - 1, y: c.y - 1, z: c.z + 1, w: c.w + 1},
|
||||
|
||||
// same layer
|
||||
{x: c.x + 1, y: c.y + 1, z: c.z, w: c.w + 1},
|
||||
{x: c.x + 1, y: c.y, z: c.z, w: c.w + 1},
|
||||
{x: c.x + 1, y: c.y - 1, z: c.z, w: c.w + 1},
|
||||
{x: c.x, y: c.y + 1, z: c.z, w: c.w + 1},
|
||||
{x: c.x, y: c.y, z: c.z, w: c.w + 1},
|
||||
{x: c.x, y: c.y - 1, z: c.z, w: c.w + 1},
|
||||
{x: c.x - 1, y: c.y + 1, z: c.z, w: c.w + 1},
|
||||
{x: c.x - 1, y: c.y, z: c.z, w: c.w + 1},
|
||||
{x: c.x - 1, y: c.y - 1, z: c.z, w: c.w + 1},
|
||||
|
||||
// below
|
||||
{x: c.x + 1, y: c.y + 1, z: c.z - 1, w: c.w + 1},
|
||||
{x: c.x + 1, y: c.y, z: c.z - 1, w: c.w + 1},
|
||||
{x: c.x + 1, y: c.y - 1, z: c.z - 1, w: c.w + 1},
|
||||
{x: c.x, y: c.y + 1, z: c.z - 1, w: c.w + 1},
|
||||
{x: c.x, y: c.y, z: c.z - 1, w: c.w + 1},
|
||||
{x: c.x, y: c.y - 1, z: c.z - 1, w: c.w + 1},
|
||||
{x: c.x - 1, y: c.y + 1, z: c.z - 1, w: c.w + 1},
|
||||
{x: c.x - 1, y: c.y, z: c.z - 1, w: c.w + 1},
|
||||
{x: c.x - 1, y: c.y - 1, z: c.z - 1, w: c.w + 1},
|
||||
// inward...?
|
||||
// above
|
||||
{x: c.x + 1, y: c.y + 1, z: c.z + 1, w: c.w - 1},
|
||||
{x: c.x + 1, y: c.y, z: c.z + 1, w: c.w - 1},
|
||||
{x: c.x + 1, y: c.y - 1, z: c.z + 1, w: c.w - 1},
|
||||
{x: c.x, y: c.y + 1, z: c.z + 1, w: c.w - 1},
|
||||
{x: c.x, y: c.y, z: c.z + 1, w: c.w - 1},
|
||||
{x: c.x, y: c.y - 1, z: c.z + 1, w: c.w - 1},
|
||||
{x: c.x - 1, y: c.y + 1, z: c.z + 1, w: c.w - 1},
|
||||
{x: c.x - 1, y: c.y, z: c.z + 1, w: c.w - 1},
|
||||
{x: c.x - 1, y: c.y - 1, z: c.z + 1, w: c.w - 1},
|
||||
|
||||
// same layer
|
||||
{x: c.x + 1, y: c.y + 1, z: c.z, w: c.w - 1},
|
||||
{x: c.x + 1, y: c.y, z: c.z, w: c.w - 1},
|
||||
{x: c.x + 1, y: c.y - 1, z: c.z, w: c.w - 1},
|
||||
{x: c.x, y: c.y + 1, z: c.z, w: c.w - 1},
|
||||
{x: c.x, y: c.y, z: c.z, w: c.w - 1},
|
||||
{x: c.x, y: c.y - 1, z: c.z, w: c.w - 1},
|
||||
{x: c.x - 1, y: c.y + 1, z: c.z, w: c.w - 1},
|
||||
{x: c.x - 1, y: c.y, z: c.z, w: c.w - 1},
|
||||
{x: c.x - 1, y: c.y - 1, z: c.z, w: c.w - 1},
|
||||
|
||||
// below
|
||||
{x: c.x + 1, y: c.y + 1, z: c.z - 1, w: c.w - 1},
|
||||
{x: c.x + 1, y: c.y, z: c.z - 1, w: c.w - 1},
|
||||
{x: c.x + 1, y: c.y - 1, z: c.z - 1, w: c.w - 1},
|
||||
{x: c.x, y: c.y + 1, z: c.z - 1, w: c.w - 1},
|
||||
{x: c.x, y: c.y, z: c.z - 1, w: c.w - 1},
|
||||
{x: c.x, y: c.y - 1, z: c.z - 1, w: c.w - 1},
|
||||
{x: c.x - 1, y: c.y + 1, z: c.z - 1, w: c.w - 1},
|
||||
{x: c.x - 1, y: c.y, z: c.z - 1, w: c.w - 1},
|
||||
{x: c.x - 1, y: c.y - 1, z: c.z - 1, w: c.w - 1},
|
||||
}...)
|
||||
}
|
||||
|
||||
// "normal" space
|
||||
cells = append(cells, []cell{
|
||||
// above
|
||||
{x: c.x + 1, y: c.y + 1, z: c.z + 1, w: c.w},
|
||||
{x: c.x + 1, y: c.y, z: c.z + 1, w: c.w},
|
||||
{x: c.x + 1, y: c.y - 1, z: c.z + 1, w: c.w},
|
||||
{x: c.x, y: c.y + 1, z: c.z + 1, w: c.w},
|
||||
{x: c.x, y: c.y, z: c.z + 1, w: c.w},
|
||||
{x: c.x, y: c.y - 1, z: c.z + 1, w: c.w},
|
||||
{x: c.x - 1, y: c.y + 1, z: c.z + 1, w: c.w},
|
||||
{x: c.x - 1, y: c.y, z: c.z + 1, w: c.w},
|
||||
{x: c.x - 1, y: c.y - 1, z: c.z + 1, w: c.w},
|
||||
|
||||
// same layer
|
||||
{x: c.x + 1, y: c.y + 1, z: c.z, w: c.w},
|
||||
{x: c.x + 1, y: c.y, z: c.z, w: c.w},
|
||||
{x: c.x + 1, y: c.y - 1, z: c.z, w: c.w},
|
||||
{x: c.x, y: c.y + 1, z: c.z, w: c.w},
|
||||
// <-- original cell
|
||||
{x: c.x, y: c.y - 1, z: c.z, w: c.w},
|
||||
{x: c.x - 1, y: c.y + 1, z: c.z, w: c.w},
|
||||
{x: c.x - 1, y: c.y, z: c.z, w: c.w},
|
||||
{x: c.x - 1, y: c.y - 1, z: c.z, w: c.w},
|
||||
|
||||
// below
|
||||
{x: c.x + 1, y: c.y + 1, z: c.z - 1, w: c.w},
|
||||
{x: c.x + 1, y: c.y, z: c.z - 1, w: c.w},
|
||||
{x: c.x + 1, y: c.y - 1, z: c.z - 1, w: c.w},
|
||||
{x: c.x, y: c.y + 1, z: c.z - 1, w: c.w},
|
||||
{x: c.x, y: c.y, z: c.z - 1, w: c.w},
|
||||
{x: c.x, y: c.y - 1, z: c.z - 1, w: c.w},
|
||||
{x: c.x - 1, y: c.y + 1, z: c.z - 1, w: c.w},
|
||||
{x: c.x - 1, y: c.y, z: c.z - 1, w: c.w},
|
||||
{x: c.x - 1, y: c.y - 1, z: c.z - 1, w: c.w},
|
||||
}...)
|
||||
|
||||
return cells
|
||||
}
|
||||
|
||||
func checkNeighbors(u universe, c cell) bool {
|
||||
sum := 0
|
||||
for _, c := range makeNeighborsList(c, u.hyper) {
|
||||
if u.cells[c] {
|
||||
sum = sum + 1
|
||||
}
|
||||
}
|
||||
if sum == 3 {
|
||||
return true
|
||||
} else if u.cells[c] && sum == 2 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func step(old universe) universe {
|
||||
new := universe{
|
||||
cells: make(map[cell]bool, len(old.cells)),
|
||||
hyper: old.hyper,
|
||||
}
|
||||
cellsToCheck := make([]cell, 0, len(old.cells)*80)
|
||||
for c := range old.cells {
|
||||
cellsToCheck = append(cellsToCheck, makeNeighborsList(c, old.hyper)...)
|
||||
}
|
||||
for _, c := range cellsToCheck {
|
||||
if checkNeighbors(old, c) {
|
||||
new.cells[c] = true
|
||||
}
|
||||
}
|
||||
return new
|
||||
}
|
||||
|
||||
func newUniverse(input []string, hyper bool) universe {
|
||||
u := universe{
|
||||
cells: make(map[cell]bool, len(input)),
|
||||
hyper: hyper,
|
||||
}
|
||||
|
||||
for i, r := range input {
|
||||
for j, c := range r {
|
||||
if c == '#' {
|
||||
u.cells[cell{z: 0, x: i, y: j, w: 0}] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return u
|
||||
}
|
||||
|
||||
func countActivesInUniverse(u universe) {
|
||||
fmt.Println(len(u.cells))
|
||||
}
|
||||
|
||||
func partOne() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
var input []string
|
||||
|
||||
for scanner.Scan() {
|
||||
input = append(input, scanner.Text())
|
||||
}
|
||||
|
||||
u := newUniverse(input, false)
|
||||
u = step(u)
|
||||
u = step(u)
|
||||
u = step(u)
|
||||
u = step(u)
|
||||
u = step(u)
|
||||
u = step(u)
|
||||
countActivesInUniverse(u)
|
||||
}
|
||||
|
||||
func partTwo() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
var input []string
|
||||
for scanner.Scan() {
|
||||
input = append(input, scanner.Text())
|
||||
}
|
||||
|
||||
u := newUniverse(input, true)
|
||||
u = step(u)
|
||||
u = step(u)
|
||||
u = step(u)
|
||||
u = step(u)
|
||||
u = step(u)
|
||||
u = step(u)
|
||||
countActivesInUniverse(u)
|
||||
}
|
180
18/main.go
Normal file
180
18/main.go
Normal file
@@ -0,0 +1,180 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
// an expression is a list of expressions and a list of operators that intersperse those expressions, OR a number.
|
||||
// as a sanity check, the length of the body list should always be one more than operators, unless both are 0,
|
||||
// in which case number must be set. Luckily, we don't have to worry about 0 (the "empty" state for numbers).
|
||||
type expression struct {
|
||||
body []expression
|
||||
operators []op
|
||||
number int
|
||||
}
|
||||
|
||||
// op can be one of two things.
|
||||
type op struct {
|
||||
plus bool
|
||||
multiply bool
|
||||
}
|
||||
|
||||
// 1 + 2 should parse to
|
||||
// expression{
|
||||
// body:[
|
||||
// expression{number: 1},
|
||||
// expression{number: 2},
|
||||
// ],
|
||||
// operators:[
|
||||
// op{plus: true},
|
||||
// ],
|
||||
// }
|
||||
|
||||
// (2 * 2) + 2 should parse to:
|
||||
// expression{
|
||||
// body:[
|
||||
// expression{
|
||||
// body: [
|
||||
// expression{number: 2},
|
||||
// expression{number: 2},
|
||||
// ],
|
||||
// operators: [
|
||||
// op{multiply: true},
|
||||
// ],
|
||||
// },
|
||||
// expression{number: 2},
|
||||
// ],
|
||||
// operators:[
|
||||
// op{plus:true}
|
||||
// ]
|
||||
// }
|
||||
|
||||
func parseExpression(e expression, s string) (expression, string) {
|
||||
for i := 0; i < len(s); {
|
||||
switch s[i] {
|
||||
case '(':
|
||||
sub, remainder := parseExpression(expression{}, s[i+1:])
|
||||
e.body = append(e.body, sub)
|
||||
i = len(s) - len(remainder) + 1 // skip everything we just parsed in the subexpression
|
||||
case ')':
|
||||
return e, s[i:]
|
||||
case '*':
|
||||
e.operators = append(e.operators, op{multiply: true})
|
||||
i = i + 1
|
||||
case '+':
|
||||
e.operators = append(e.operators, op{plus: true})
|
||||
i = i + 1
|
||||
case ' ':
|
||||
i = i + 1
|
||||
case '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
// luckily, it's all single-digit numbers and I don't need to track parser state
|
||||
// an ASCII byte number minus the rune value of 0 equals the integer itself: '1' - '0' == 49 - 48;
|
||||
// rune math returns a platform-specific int, so cast to generic int.
|
||||
e.body = append(e.body, expression{number: int(s[i] - '0')})
|
||||
i = i + 1
|
||||
}
|
||||
}
|
||||
return e, ""
|
||||
}
|
||||
|
||||
func printExpression(e expression) string {
|
||||
result := ""
|
||||
for i := 0; i < len(e.body); i = i + 1 {
|
||||
if e.body[i].number != 0 {
|
||||
result = result + fmt.Sprintf("%d ", e.body[i].number)
|
||||
} else {
|
||||
result = result + "(" + printExpression(e.body[i]) + ") "
|
||||
}
|
||||
if i < len(e.operators) {
|
||||
if e.operators[i].multiply {
|
||||
result = result + "* "
|
||||
} else if e.operators[i].plus {
|
||||
result = result + "+ "
|
||||
}
|
||||
}
|
||||
}
|
||||
return strings.TrimSpace(result)
|
||||
}
|
||||
|
||||
// evaluation starts at the root then descends into the sub-expressions.
|
||||
func evalLeftToRight(e expression) int {
|
||||
var result int
|
||||
if e.number != 0 {
|
||||
result = e.number
|
||||
} else {
|
||||
result = evalLeftToRight(e.body[0])
|
||||
}
|
||||
for i := 0; i < len(e.operators); i = i + 1 {
|
||||
if e.operators[i].multiply {
|
||||
result = result * evalLeftToRight(e.body[i+1])
|
||||
}
|
||||
if e.operators[i].plus {
|
||||
result = result + evalLeftToRight(e.body[i+1])
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// previous approach might not work...
|
||||
func evalPlusThenMultiplication(e expression) int {
|
||||
var result int
|
||||
if e.number != 0 {
|
||||
result = e.number
|
||||
} else {
|
||||
result = evalPlusThenMultiplication(e.body[0])
|
||||
}
|
||||
for i := 0; i < len(e.operators); i = i + 1 {
|
||||
if e.operators[i].multiply {
|
||||
result = result * evalPlusThenMultiplication(e.body[i+1])
|
||||
}
|
||||
if e.operators[i].plus {
|
||||
result = result + evalPlusThenMultiplication(e.body[i+1])
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func partOne() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
total := 0
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
e, _ := parseExpression(expression{}, line)
|
||||
total = total + evalLeftToRight(e)
|
||||
}
|
||||
|
||||
fmt.Println(total)
|
||||
}
|
||||
|
||||
func partTwo() {
|
||||
f, _ := os.Open("testinput")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
total := 0
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
e, _ := parseExpression(expression{}, line)
|
||||
fmt.Println(printExpression(e), "=", evalPlusThenMultiplication(e))
|
||||
}
|
||||
|
||||
fmt.Println(total)
|
||||
}
|
175
19/main.go
Normal file
175
19/main.go
Normal file
@@ -0,0 +1,175 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
// options: 20 => [][20]; 20 10 => [][20, 10]; 20 | 10 => [][20][10]; 20 10 | 39 12 => [][20, 10][39, 12];
|
||||
// token is only set for the two "bottom" rules of "a" and "b"
|
||||
// options and token will never both be set
|
||||
// ints all point to rule numbers in overall rule list
|
||||
type rule struct {
|
||||
name int
|
||||
options [][]int
|
||||
token rune
|
||||
}
|
||||
|
||||
func parseRule(s string) rule {
|
||||
r := rule{
|
||||
options: make([][]int, 0),
|
||||
}
|
||||
sp := strings.Split(s, ": ")
|
||||
r.name, _ = strconv.Atoi(sp[0])
|
||||
|
||||
// either it's a token rule...
|
||||
if sp[1] == `"a"` || sp[1] == `"b"` {
|
||||
r.token = []rune(strings.Trim(sp[1], `"`))[0]
|
||||
return r
|
||||
}
|
||||
|
||||
// ...or an options rule.
|
||||
opts := strings.Split(sp[1], " | ")
|
||||
for _, o := range opts {
|
||||
opt := []int{}
|
||||
for _, i := range strings.Split(o, " ") {
|
||||
ii, _ := strconv.Atoi(i)
|
||||
opt = append(opt, ii)
|
||||
}
|
||||
r.options = append(r.options, opt)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func validateString(rules []rule, currentRule int, s string) (bool, string) {
|
||||
activeRule := rules[currentRule]
|
||||
// if we're run out of string, the rule obviously cannot match
|
||||
if len(s) == 0 {
|
||||
return false, ""
|
||||
}
|
||||
// we've reached a leaf; check if the current head of the string matches our expected rune
|
||||
if len(activeRule.options) == 0 {
|
||||
return []rune(s)[0] == activeRule.token, s[1:]
|
||||
}
|
||||
// if there's only one option, attempt to apply the rules
|
||||
if len(activeRule.options) == 1 {
|
||||
option := activeRule.options[0]
|
||||
if len(option) == 1 {
|
||||
return validateString(rules, option[0], s)
|
||||
}
|
||||
if len(option) == 2 {
|
||||
ok, remainder := validateString(rules, option[0], s)
|
||||
if ok {
|
||||
return validateString(rules, option[1], remainder)
|
||||
}
|
||||
}
|
||||
if len(option) == 3 {
|
||||
ok, remainder := validateString(rules, option[0], s)
|
||||
if ok {
|
||||
ok, remainder = validateString(rules, option[1], remainder)
|
||||
}
|
||||
if ok {
|
||||
return validateString(rules, option[2], remainder)
|
||||
}
|
||||
}
|
||||
}
|
||||
// otherwise, we need to branch into our options and try and apply their rules
|
||||
if len(activeRule.options) == 2 {
|
||||
ok := false
|
||||
remainder := ""
|
||||
optionOne := activeRule.options[0]
|
||||
if len(optionOne) == 1 {
|
||||
ok, remainder = validateString(rules, optionOne[0], s)
|
||||
}
|
||||
if len(optionOne) == 2 {
|
||||
ok, remainder = validateString(rules, optionOne[0], s)
|
||||
if ok {
|
||||
ok, remainder = validateString(rules, optionOne[1], remainder)
|
||||
}
|
||||
}
|
||||
if len(optionOne) == 3 {
|
||||
ok, remainder = validateString(rules, optionOne[0], s)
|
||||
if ok {
|
||||
ok, remainder = validateString(rules, optionOne[1], remainder)
|
||||
}
|
||||
if ok {
|
||||
ok, remainder = validateString(rules, optionOne[2], remainder)
|
||||
}
|
||||
}
|
||||
if ok {
|
||||
return ok, remainder
|
||||
}
|
||||
optionTwo := activeRule.options[1]
|
||||
if len(optionTwo) == 1 {
|
||||
ok, _ = validateString(rules, optionTwo[0], s)
|
||||
}
|
||||
if len(optionTwo) == 2 {
|
||||
ok, remainder = validateString(rules, optionTwo[0], s)
|
||||
if ok {
|
||||
return validateString(rules, optionTwo[1], remainder)
|
||||
}
|
||||
}
|
||||
if len(optionTwo) == 3 {
|
||||
ok, remainder = validateString(rules, optionTwo[0], s)
|
||||
if ok {
|
||||
ok, remainder = validateString(rules, optionTwo[1], remainder)
|
||||
}
|
||||
if ok {
|
||||
return validateString(rules, optionTwo[2], remainder)
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, s
|
||||
}
|
||||
|
||||
func partOne() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
rules := make([]rule, 134)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if line == "" {
|
||||
break
|
||||
}
|
||||
rule := parseRule(line)
|
||||
rules[rule.name] = rule
|
||||
}
|
||||
|
||||
total := 0
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if ok, _ := validateString(rules, 0, line); ok {
|
||||
fmt.Println("VALID:", line)
|
||||
total = total + 1
|
||||
} else {
|
||||
fmt.Println("INVALID:", line)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println(total)
|
||||
}
|
||||
|
||||
func partTwo() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
for scanner.Scan() {
|
||||
// line := scanner.Text()
|
||||
}
|
||||
}
|
244
20/main.go
Normal file
244
20/main.go
Normal file
@@ -0,0 +1,244 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
type tile struct {
|
||||
id int
|
||||
neighbors map[int]int // key matches the edge key, so we can skip existing matches if we want
|
||||
edges map[int]string
|
||||
image map[int]string
|
||||
}
|
||||
|
||||
// edges are always defined clockwise starting from 0:north
|
||||
// as long as there's only ever one match for an edge, I don't _think_ I need to keep track of piece orientation...?
|
||||
func parseTile(header string, body []string) tile {
|
||||
id, _ := strconv.Atoi(strings.Trim(header, "Tile :"))
|
||||
t := tile{
|
||||
id: id,
|
||||
neighbors: make(map[int]int, 4),
|
||||
edges: make(map[int]string, 4),
|
||||
image: make(map[int]string, 8),
|
||||
}
|
||||
// make edges and extract picture data...
|
||||
left := []byte{}
|
||||
right := []byte{}
|
||||
for i := range body {
|
||||
left = append(left, body[i][0])
|
||||
right = append(right, body[i][9])
|
||||
// image has edges removed; still want 0-indexed for sanity later!
|
||||
if i != 0 && i != 9 {
|
||||
t.image[i-1] = body[i-1][1:9]
|
||||
}
|
||||
}
|
||||
t.edges[0] = body[0]
|
||||
t.edges[1] = string(right)
|
||||
t.edges[2] = body[9]
|
||||
t.edges[3] = string(left)
|
||||
return t
|
||||
}
|
||||
|
||||
// return is match, inverted
|
||||
func testEdge(one, two string) (bool, bool) {
|
||||
if one == two {
|
||||
return true, false
|
||||
}
|
||||
// go doesn't have a built-in "string reversal" method, so we just check it the hard way
|
||||
// 9 is our magic number for a 10-character string, which we know they all are.
|
||||
// This might not be 100% sound for unicode (byte != rune), but our string is ASCII so it's... fine.
|
||||
for i := range one {
|
||||
if one[i] != two[9-i] {
|
||||
return false, false
|
||||
}
|
||||
}
|
||||
return true, true
|
||||
}
|
||||
|
||||
// this is wasteful in that it happily checks edges that we've already determined neighbors for,
|
||||
// but it's performant enough that I don't really care to fix it
|
||||
func findPartners(id int, tiles map[int]tile) map[int]tile {
|
||||
currentTile := tiles[id]
|
||||
for tile := range tiles {
|
||||
if tile != id {
|
||||
for edgeID := range tiles[tile].edges {
|
||||
for i := range currentTile.edges {
|
||||
if ok, _ := testEdge(tiles[tile].edges[edgeID], currentTile.edges[i]); ok {
|
||||
tiles[tile].neighbors[edgeID] = tiles[id].id
|
||||
tiles[id].neighbors[i] = tiles[tile].id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return tiles
|
||||
}
|
||||
|
||||
// Starting from a corner, we can "walk" through the image by:
|
||||
// stepping to the next tile in a known direction
|
||||
// checking where _that_ tile thinks we came from
|
||||
// then re-orienting ourselves and stepping again.
|
||||
// For example, if we take a corner and go to its 0 neighbor,
|
||||
// and that neighbor thinks our previous tile was 3,
|
||||
// a "straight line" along the edge would be 1. Repeat as necessary until you hit an edge.
|
||||
// Once we hit an edge, return to the start of the row,
|
||||
// step to its 90° neighbor, then walk parallel to the initial edge.
|
||||
// Repeat until we reach the far corner.
|
||||
// Additionally, we need to "line up" the edges, so there's some alignment checking that has to happen.
|
||||
func mergeTiles(corner int, tiles map[int]tile) []string {
|
||||
// just to make the counting a little easier
|
||||
sideLength := int(math.Sqrt(float64(len(tiles))))
|
||||
result := make([]string, sideLength*8)
|
||||
headOfRow := tiles[corner]
|
||||
currentRowDirection := 0
|
||||
nextRowDirection := 0
|
||||
// what an absurd way to "pick" between two keys in a two-key map
|
||||
for i := range headOfRow.neighbors {
|
||||
if currentRowDirection == 0 {
|
||||
currentRowDirection = i
|
||||
} else if nextRowDirection == 0 {
|
||||
nextRowDirection = i
|
||||
}
|
||||
}
|
||||
currentTile := tiles[corner]
|
||||
image := imageFromEdge(currentTile, 0)
|
||||
for row, s := range image {
|
||||
result[row] = result[row] + s
|
||||
}
|
||||
for i := 0; i < sideLength; i = i + 1 {
|
||||
nextID := currentTile.neighbors[currentRowDirection]
|
||||
for e, id := range tiles[nextID].neighbors {
|
||||
if id == currentTile.id {
|
||||
_, invert := testEdge(currentTile.edges[currentRowDirection], tiles[nextID].edges[e])
|
||||
image := imageFromEdge(tiles[nextID], (e+1)%4)
|
||||
if !invert {
|
||||
for row, s := range image {
|
||||
result[row] = result[row] + s
|
||||
}
|
||||
} else {
|
||||
for row, s := range image {
|
||||
result[7-row] = result[7-row] + s
|
||||
}
|
||||
}
|
||||
currentRowDirection = (e + 2) % 4 // flip 180 around the compass
|
||||
}
|
||||
}
|
||||
currentTile = tiles[nextID]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// returns the image "rotated" so that the provided edge is on "top" (the 0 position)
|
||||
func imageFromEdge(t tile, edge int) map[int]string {
|
||||
if edge == 1 {
|
||||
newImage := make(map[int]string, 8)
|
||||
for i := 0; i < 8; i = i + 1 {
|
||||
b := []byte{t.image[0][7-i], t.image[1][7-i], t.image[2][7-i], t.image[3][7-i], t.image[4][7-i], t.image[5][7-i], t.image[6][7-i], t.image[7][7-i]}
|
||||
newImage[i] = string(b)
|
||||
}
|
||||
return newImage
|
||||
}
|
||||
if edge == 2 {
|
||||
newImage := make(map[int]string, 8)
|
||||
for i := 0; i < 8; i = i + 1 {
|
||||
b := []byte{t.image[7-i][7], t.image[7-i][6], t.image[7-i][5], t.image[7-i][4], t.image[7-i][3], t.image[7-i][2], t.image[7-i][1], t.image[7-i][0]}
|
||||
newImage[i] = string(b)
|
||||
}
|
||||
return newImage
|
||||
}
|
||||
if edge == 3 {
|
||||
newImage := make(map[int]string, 8)
|
||||
for i := 0; i < 8; i = i + 1 {
|
||||
b := []byte{t.image[7][i], t.image[6][i], t.image[5][i], t.image[4][i], t.image[3][i], t.image[2][i], t.image[1][i], t.image[0][i]}
|
||||
newImage[i] = string(b)
|
||||
}
|
||||
return newImage
|
||||
}
|
||||
// edge == 0 does nothing
|
||||
return t.image
|
||||
}
|
||||
|
||||
func partOne() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
buffer := []string{}
|
||||
tiles := make(map[int]tile)
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if line == "" {
|
||||
tile := parseTile(buffer[0], buffer[1:])
|
||||
tiles[tile.id] = tile
|
||||
buffer = []string{}
|
||||
} else {
|
||||
buffer = append(buffer, line)
|
||||
}
|
||||
}
|
||||
// catch that last tile
|
||||
// weirdly, I'd think the trailing newline in the input would have done this for us
|
||||
tile := parseTile(buffer[0], buffer[1:])
|
||||
tiles[tile.id] = tile
|
||||
|
||||
corners := 1
|
||||
for id := range tiles {
|
||||
tiles = findPartners(id, tiles)
|
||||
if len(tiles[id].neighbors) == 2 {
|
||||
corners = corners * id
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println(corners)
|
||||
}
|
||||
|
||||
func partTwo() {
|
||||
f, _ := os.Open("testinput")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
buffer := []string{}
|
||||
tiles := make(map[int]tile)
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if line == "" {
|
||||
tile := parseTile(buffer[0], buffer[1:])
|
||||
tiles[tile.id] = tile
|
||||
buffer = []string{}
|
||||
} else {
|
||||
buffer = append(buffer, line)
|
||||
}
|
||||
}
|
||||
// catch that last tile
|
||||
tile := parseTile(buffer[0], buffer[1:])
|
||||
tiles[tile.id] = tile
|
||||
|
||||
corner := 0
|
||||
for id := range tiles {
|
||||
tiles = findPartners(id, tiles)
|
||||
// grab a corner to start the merge from
|
||||
if len(tiles[id].neighbors) == 2 && corner == 0 {
|
||||
corner = id
|
||||
}
|
||||
}
|
||||
|
||||
for _, line := range mergeTiles(corner, tiles) {
|
||||
fmt.Println(line)
|
||||
}
|
||||
}
|
140
21/main.go
Normal file
140
21/main.go
Normal file
@@ -0,0 +1,140 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
type food struct {
|
||||
allergens []string
|
||||
ingredients []string
|
||||
}
|
||||
|
||||
func intersection(a, b []string) []string {
|
||||
// special-case starting out
|
||||
if len(a) == 0 {
|
||||
return b
|
||||
} else if len(b) == 0 {
|
||||
return a
|
||||
}
|
||||
result := []string{}
|
||||
for _, s := range b {
|
||||
for _, t := range a {
|
||||
if s == t {
|
||||
result = append(result, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func countSafeIngredients(f food, allergens map[string][]string) int {
|
||||
for c, i := range f.ingredients {
|
||||
for _, l := range allergens {
|
||||
for _, a := range l {
|
||||
if i == a {
|
||||
f.ingredients[c] = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
result := 0
|
||||
for _, i := range f.ingredients {
|
||||
if i != "" {
|
||||
result = result + 1
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func remove(ingredients []string, ingredient string) []string {
|
||||
for i, j := range ingredients {
|
||||
if j == ingredient {
|
||||
ingredients[i] = ingredients[len(ingredients)-1]
|
||||
ingredients[len(ingredients)-1] = ""
|
||||
return ingredients[:len(ingredients)-1]
|
||||
}
|
||||
}
|
||||
return ingredients
|
||||
}
|
||||
|
||||
func partOne() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
foods := []food{}
|
||||
allergens := map[string][]string{}
|
||||
|
||||
for scanner.Scan() {
|
||||
line := strings.Split(scanner.Text(), " (contains ")
|
||||
f := food{
|
||||
ingredients: strings.Split(line[0], " "),
|
||||
allergens: strings.Split(strings.TrimSuffix(line[1], ")"), ", "),
|
||||
}
|
||||
foods = append(foods, f)
|
||||
for _, a := range f.allergens {
|
||||
allergens[a] = intersection(f.ingredients, allergens[a])
|
||||
}
|
||||
}
|
||||
|
||||
sum := 0
|
||||
for _, f := range foods {
|
||||
sum = sum + countSafeIngredients(f, allergens)
|
||||
}
|
||||
fmt.Println(sum)
|
||||
}
|
||||
|
||||
func partTwo() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
foods := []food{}
|
||||
allergens := map[string][]string{}
|
||||
|
||||
for scanner.Scan() {
|
||||
line := strings.Split(scanner.Text(), " (contains ")
|
||||
f := food{
|
||||
ingredients: strings.Split(line[0], " "),
|
||||
allergens: strings.Split(strings.TrimSuffix(line[1], ")"), ", "),
|
||||
}
|
||||
foods = append(foods, f)
|
||||
for _, a := range f.allergens {
|
||||
allergens[a] = intersection(f.ingredients, allergens[a])
|
||||
}
|
||||
}
|
||||
|
||||
// hey, this is the same reduction logic as day 16!
|
||||
trueAllergens := map[string]string{}
|
||||
var lastAllergenSolved string
|
||||
for i := 0; i < len(allergens); i = i + 1 {
|
||||
for i, a := range allergens {
|
||||
if len(a) == 1 {
|
||||
trueAllergens[i] = a[0]
|
||||
lastAllergenSolved = a[0]
|
||||
}
|
||||
}
|
||||
for i := range allergens {
|
||||
allergens[i] = remove(allergens[i], lastAllergenSolved)
|
||||
}
|
||||
}
|
||||
|
||||
formatted := "" // in theory we shouldn't hard-code this allergen order...
|
||||
for _, a := range []string{"dairy", "eggs", "fish", "nuts", "peanuts", "sesame", "soy", "wheat"} {
|
||||
formatted = formatted + trueAllergens[a] + ","
|
||||
}
|
||||
fmt.Println(strings.TrimSuffix(formatted, ","))
|
||||
}
|
155
22/main.go
Normal file
155
22/main.go
Normal file
@@ -0,0 +1,155 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
type deck struct {
|
||||
cards []int
|
||||
}
|
||||
|
||||
type cacheKey struct {
|
||||
one int
|
||||
two int
|
||||
}
|
||||
|
||||
func countWinningDeck(deck []int) int {
|
||||
sum := 0
|
||||
for i := range deck {
|
||||
sum = sum + ((len(deck) - i) * deck[i])
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
// return values is who won/their score: true for player one, false for player two
|
||||
func playCombat(one, two []int) (bool, int) {
|
||||
round := 0
|
||||
for len(one) > 0 && len(two) > 0 {
|
||||
round = round + 1
|
||||
if one[0] > two[0] {
|
||||
one = append(one[1:], one[0], two[0])
|
||||
two = two[1:]
|
||||
} else if one[0] < two[0] {
|
||||
two = append(two[1:], two[0], one[0])
|
||||
one = one[1:]
|
||||
}
|
||||
}
|
||||
if len(one) == 0 {
|
||||
return false, countWinningDeck(two)
|
||||
}
|
||||
return true, countWinningDeck(one)
|
||||
}
|
||||
|
||||
func playRecursiveCombat(one, two []int) (bool, int) {
|
||||
loopCache := make(map[cacheKey]bool, 0)
|
||||
round := 0
|
||||
for len(one) > 0 && len(two) > 0 {
|
||||
// check for loops
|
||||
if loopCache[cacheKey{one: countWinningDeck(one), two: countWinningDeck(two)}] {
|
||||
return true, countWinningDeck(one)
|
||||
}
|
||||
loopCache[cacheKey{one: countWinningDeck(one), two: countWinningDeck(two)}] = true
|
||||
|
||||
// otherwise, play a round
|
||||
round = round + 1
|
||||
if len(one[1:]) >= one[0] && len(two[1:]) >= two[0] {
|
||||
// fuckin' go slice internals
|
||||
subDeckOne := make([]int, len(one))
|
||||
subDeckTwo := make([]int, len(two))
|
||||
copy(subDeckOne, one)
|
||||
copy(subDeckTwo, two)
|
||||
winner, _ := playRecursiveCombat(subDeckOne[1:one[0]+1], subDeckTwo[1:two[0]+1])
|
||||
if winner { // p1 wins
|
||||
one = append(one[1:], one[0], two[0])
|
||||
two = two[1:]
|
||||
} else { // p2 wins
|
||||
two = append(two[1:], two[0], one[0])
|
||||
one = one[1:]
|
||||
}
|
||||
} else if one[0] > two[0] {
|
||||
one = append(one[1:], one[0], two[0])
|
||||
two = two[1:]
|
||||
} else if one[0] < two[0] {
|
||||
two = append(two[1:], two[0], one[0])
|
||||
one = one[1:]
|
||||
}
|
||||
}
|
||||
if len(one) == 0 {
|
||||
return false, countWinningDeck(two)
|
||||
}
|
||||
return true, countWinningDeck(one)
|
||||
}
|
||||
|
||||
func partOne() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
// deal the decks
|
||||
playerone := deck{}
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if line == "" {
|
||||
break
|
||||
}
|
||||
i, err := strconv.Atoi(line)
|
||||
if err == nil {
|
||||
playerone.cards = append(playerone.cards, i)
|
||||
}
|
||||
}
|
||||
playertwo := deck{}
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
i, err := strconv.Atoi(line)
|
||||
if err == nil {
|
||||
playertwo.cards = append(playertwo.cards, i)
|
||||
}
|
||||
}
|
||||
|
||||
// play a game and determine a winner
|
||||
_, score := playCombat(playerone.cards, playertwo.cards)
|
||||
fmt.Println(score)
|
||||
}
|
||||
|
||||
func partTwo() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
// deal the decks
|
||||
playerone := deck{}
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if line == "" {
|
||||
break
|
||||
}
|
||||
i, err := strconv.Atoi(line)
|
||||
if err == nil {
|
||||
playerone.cards = append(playerone.cards, i)
|
||||
}
|
||||
}
|
||||
playertwo := deck{}
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
i, err := strconv.Atoi(line)
|
||||
if err == nil {
|
||||
playertwo.cards = append(playertwo.cards, i)
|
||||
}
|
||||
}
|
||||
|
||||
_, score := playRecursiveCombat(playerone.cards, playertwo.cards)
|
||||
fmt.Println(score)
|
||||
}
|
137
23/main.go
Normal file
137
23/main.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
func cupInList(cup int, cups []int) bool {
|
||||
for _, c := range cups {
|
||||
if c == cup {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func generateFullCupset(cups []int, totalCups int) []int {
|
||||
for i := len(cups); i < totalCups; i = i + 1 {
|
||||
cups = append(cups, i)
|
||||
}
|
||||
return cups
|
||||
}
|
||||
|
||||
func findTargetCupIndex(cup int, cups []int) int {
|
||||
for i, c := range cups {
|
||||
if c == cup {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return 0 // uh-oh
|
||||
}
|
||||
|
||||
func step(cups []int) []int {
|
||||
cupCount := len(cups)
|
||||
currentCup := cups[0]
|
||||
removedCups := cups[1:4]
|
||||
newList := make([]int, 0, cupCount)
|
||||
newList = append([]int{cups[0]}, cups[4:]...)
|
||||
var targetCup int
|
||||
if currentCup == 1 {
|
||||
targetCup = cupCount
|
||||
} else {
|
||||
targetCup = currentCup - 1
|
||||
}
|
||||
for cupInList(targetCup, removedCups) {
|
||||
targetCup = targetCup - 1
|
||||
if targetCup == 0 {
|
||||
targetCup = cupCount
|
||||
}
|
||||
}
|
||||
tc := findTargetCupIndex(targetCup, newList)
|
||||
result := make([]int, 0, cupCount)
|
||||
result = append(result, newList[1:tc+1]...)
|
||||
result = append(result, removedCups...)
|
||||
result = append(result, newList[tc+1:]...)
|
||||
result = append(result, newList[0])
|
||||
return result
|
||||
}
|
||||
|
||||
func runabout(cups []int) string {
|
||||
oneIndex := findTargetCupIndex(1, cups)
|
||||
result := ""
|
||||
for i := oneIndex + 1; i < len(cups); i = i + 1 {
|
||||
result = result + strconv.Itoa(cups[i])
|
||||
}
|
||||
for i := 0; i < len(cups[:oneIndex]); i = i + 1 {
|
||||
result = result + strconv.Itoa(cups[i])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func nextTwo(cups []int) (int, int) {
|
||||
oneIndex := findTargetCupIndex(1, cups)
|
||||
if oneIndex+1 > len(cups) {
|
||||
return cups[0], cups[1]
|
||||
}
|
||||
if oneIndex+2 > len(cups) {
|
||||
return cups[len(cups)], cups[0]
|
||||
}
|
||||
return cups[oneIndex+1], cups[oneIndex+2]
|
||||
}
|
||||
|
||||
func partOne() {
|
||||
f, _ := os.Open("testinput")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
cups := []int{}
|
||||
|
||||
scanner.Scan()
|
||||
for _, s := range strings.Split(scanner.Text(), "") {
|
||||
i, _ := strconv.Atoi(s)
|
||||
cups = append(cups, i)
|
||||
}
|
||||
|
||||
for i := 0; i < 100; i = i + 1 {
|
||||
cups = step(cups)
|
||||
}
|
||||
|
||||
fmt.Println(runabout(cups))
|
||||
|
||||
}
|
||||
|
||||
func partTwo() {
|
||||
f, _ := os.Open("testinput")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
cups := []int{}
|
||||
|
||||
scanner.Scan()
|
||||
for _, s := range strings.Split(scanner.Text(), "") {
|
||||
i, _ := strconv.Atoi(s)
|
||||
cups = append(cups, i)
|
||||
}
|
||||
|
||||
allCups := generateFullCupset(cups, 10000000)
|
||||
for i := 0; i < 10000000; i = i + 1 {
|
||||
allCups = step(allCups)
|
||||
fmt.Println(i)
|
||||
}
|
||||
|
||||
fmt.Println(nextTwo(allCups))
|
||||
}
|
186
24/main.go
Normal file
186
24/main.go
Normal file
@@ -0,0 +1,186 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
// Our grid is a hex, so let's represent that as follows:
|
||||
// x runs east-west, y runs north-south. Yes, these coordinates here are backward.
|
||||
// 2,-2 2,0 2,2 2,4
|
||||
// 1,-2 1,-1 1,1 1,2
|
||||
// 0,-2 0,0 0,2 0,4
|
||||
// -1,-2 -1,-1 -1,1 -1,2
|
||||
// -2,-2 -2,0 -2,2 -2,4
|
||||
// 0,0 -> e -> 0, 2
|
||||
// -> w -> 0,-2
|
||||
// -> se -> -1, 1
|
||||
// -> sw -> -1,-1
|
||||
// -> ne -> 1, 1
|
||||
// -> nw -> 1,-1
|
||||
|
||||
// 0 == white, 1 == black
|
||||
type tiles map[coord]int
|
||||
|
||||
type coord struct {
|
||||
x int
|
||||
y int
|
||||
}
|
||||
|
||||
func walkGrid(directions []string) (int, int) {
|
||||
var y, x int
|
||||
for _, d := range directions {
|
||||
switch d {
|
||||
case "e":
|
||||
x = x + 2
|
||||
case "se":
|
||||
y = y - 1
|
||||
x = x + 1
|
||||
case "sw":
|
||||
y = y - 1
|
||||
x = x - 1
|
||||
case "w":
|
||||
x = x - 2
|
||||
case "ne":
|
||||
y = y + 1
|
||||
x = x + 1
|
||||
case "nw":
|
||||
y = y + 1
|
||||
x = x - 1
|
||||
}
|
||||
}
|
||||
return y, x
|
||||
}
|
||||
|
||||
func parseLine(s string) []string {
|
||||
directions := []string{}
|
||||
for i := 0; i < len(s); {
|
||||
switch s[i] {
|
||||
case 'e':
|
||||
directions = append(directions, "e")
|
||||
i = i + 1
|
||||
case 's':
|
||||
if s[i+1] == 'e' {
|
||||
directions = append(directions, "se")
|
||||
i = i + 2
|
||||
} else if s[i+1] == 'w' {
|
||||
directions = append(directions, "sw")
|
||||
i = i + 2
|
||||
}
|
||||
case 'w':
|
||||
directions = append(directions, "w")
|
||||
i = i + 1
|
||||
case 'n':
|
||||
if s[i+1] == 'e' {
|
||||
directions = append(directions, "ne")
|
||||
i = i + 2
|
||||
} else if s[i+1] == 'w' {
|
||||
directions = append(directions, "nw")
|
||||
i = i + 2
|
||||
}
|
||||
}
|
||||
}
|
||||
return directions
|
||||
}
|
||||
|
||||
func generation(t tiles) tiles {
|
||||
newTiles := tiles{}
|
||||
seenWhiteTiles := tiles{}
|
||||
for coord, tile := range t {
|
||||
// walk black tile list
|
||||
if tile == 1 {
|
||||
neighbors := getNeighbors(coord)
|
||||
bn := 0
|
||||
for _, n := range neighbors {
|
||||
if t[n] == 0 {
|
||||
seenWhiteTiles[n] = seenWhiteTiles[n] + 1
|
||||
} else {
|
||||
bn = bn + 1
|
||||
}
|
||||
}
|
||||
// black tiles only survive if they have one or two black neighbors
|
||||
if bn == 1 || bn == 2 {
|
||||
newTiles[coord] = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
// white tiles turn black if they have two black neighbors
|
||||
for c, n := range seenWhiteTiles {
|
||||
if n == 2 {
|
||||
newTiles[c] = 1
|
||||
}
|
||||
}
|
||||
return newTiles
|
||||
}
|
||||
|
||||
func getNeighbors(c coord) []coord {
|
||||
return []coord{
|
||||
{x: c.x + 2, y: c.y}, // e
|
||||
{x: c.x - 2, y: c.y}, // w
|
||||
{x: c.x + 1, y: c.y + 1}, // ne
|
||||
{x: c.x + 1, y: c.y - 1}, // nw
|
||||
{x: c.x - 1, y: c.y + 1}, // se
|
||||
{x: c.x - 1, y: c.y - 1}, // sw
|
||||
}
|
||||
}
|
||||
|
||||
func countBlackTiles(t tiles) int {
|
||||
sum := 0
|
||||
for _, tile := range t {
|
||||
sum = sum + tile
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
func partOne() {
|
||||
f, _ := os.Open("testinput")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
t := tiles{}
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
x, y := walkGrid(parseLine(line))
|
||||
if t[coord{x: x, y: y}] == 0 {
|
||||
t[coord{x: x, y: y}] = 1
|
||||
} else {
|
||||
t[coord{x: x, y: y}] = 0
|
||||
}
|
||||
}
|
||||
fmt.Println(countBlackTiles(t))
|
||||
}
|
||||
|
||||
func partTwo() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
t := tiles{}
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
y, x := walkGrid(parseLine(line))
|
||||
if t[coord{x: x, y: y}] == 0 {
|
||||
t[coord{x: x, y: y}] = 1
|
||||
} else {
|
||||
t[coord{x: x, y: y}] = 0
|
||||
}
|
||||
}
|
||||
for day := 1; day <= 100; day = day + 1 {
|
||||
t = generation(t)
|
||||
}
|
||||
fmt.Println(countBlackTiles(t))
|
||||
|
||||
}
|
54
25/main.go
Normal file
54
25/main.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: n/a\n", duration)
|
||||
}
|
||||
|
||||
func partOne() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
scanner.Scan()
|
||||
doorPubKey, _ := strconv.Atoi(scanner.Text())
|
||||
scanner.Scan()
|
||||
cardPubKey, _ := strconv.Atoi(scanner.Text())
|
||||
|
||||
subjectNumber := 7
|
||||
value := 1
|
||||
magicPrime := 20201227
|
||||
loopCount := 1
|
||||
doorLoopCount := 0
|
||||
cardLoopCount := 0
|
||||
for {
|
||||
value = (value * subjectNumber) % magicPrime
|
||||
if value == doorPubKey {
|
||||
doorLoopCount = loopCount
|
||||
}
|
||||
if value == cardPubKey {
|
||||
cardLoopCount = loopCount
|
||||
}
|
||||
loopCount = loopCount + 1
|
||||
if doorLoopCount != 0 && cardLoopCount != 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
value = 1
|
||||
subjectNumber = cardPubKey
|
||||
for i := 0; i < doorLoopCount; i = i + 1 {
|
||||
value = (value * subjectNumber) % magicPrime
|
||||
}
|
||||
fmt.Println(value)
|
||||
}
|
37
main.go.tmpl
Normal file
37
main.go.tmpl
Normal file
@@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
func partOne() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
for scanner.Scan() {
|
||||
// line := scanner.Text()
|
||||
}
|
||||
}
|
||||
|
||||
func partTwo() {
|
||||
f, _ := os.Open("input")
|
||||
reader := bufio.NewReader(f)
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
for scanner.Scan() {
|
||||
// line := scanner.Text()
|
||||
}
|
||||
}
|
7
new.sh
Executable file
7
new.sh
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ "${1}" != "" ]; then
|
||||
mkdir -p ${1}
|
||||
touch ${1}/{input,testinput}
|
||||
cp -n main.go.tmpl ${1}/main.go
|
||||
fi
|
Reference in New Issue
Block a user