Compare commits
12 Commits
d8ed532442
...
master
Author | SHA1 | Date | |
---|---|---|---|
3e9fe4153c | |||
f1d6903fba | |||
16784775c9 | |||
df30e9abc0 | |||
17ab6d7144 | |||
42c77627d1 | |||
f744ac6745 | |||
44fb58ff14 | |||
010a843471 | |||
d2b4bc2921 | |||
9821836b6c | |||
0cd38cfd03 |
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)
|
||||||
|
}
|
Reference in New Issue
Block a user