Compare commits
10 Commits
9821836b6c
...
master
Author | SHA1 | Date | |
---|---|---|---|
3e9fe4153c | |||
f1d6903fba | |||
16784775c9 | |||
df30e9abc0 | |||
17ab6d7144 | |||
42c77627d1 | |||
f744ac6745 | |||
44fb58ff14 | |||
010a843471 | |||
d2b4bc2921 |
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