Compare commits
No commits in common. "849fc74a1521bbe325d471941b1f3846ab58d923" and "ed95f875ad46c219b39e583bae279e25ca58aa9a" have entirely different histories.
849fc74a15
...
ed95f875ad
458
2020/day20.go
458
2020/day20.go
|
@ -1,458 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"regexp"
|
|
||||||
|
|
||||||
"git.annabunch.es/annabunches/adventofcode/2020/lib/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
MATCH_NONE = iota
|
|
||||||
MATCH_TOP
|
|
||||||
MATCH_BOTTOM
|
|
||||||
MATCH_LEFT
|
|
||||||
MATCH_RIGHT
|
|
||||||
)
|
|
||||||
|
|
||||||
type Tile struct {
|
|
||||||
id int
|
|
||||||
data []string
|
|
||||||
rotation int
|
|
||||||
flippedX bool
|
|
||||||
flippedY bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewTile() *Tile {
|
|
||||||
return &Tile{
|
|
||||||
data: make([]string, 0),
|
|
||||||
rotation: 0,
|
|
||||||
flippedX: false,
|
|
||||||
flippedY: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tile) print() {
|
|
||||||
for _, line := range t.data {
|
|
||||||
fmt.Println(line)
|
|
||||||
}
|
|
||||||
fmt.Println()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tile) rotate() {
|
|
||||||
newData := make([]string, len(t.data))
|
|
||||||
for i := len(t.data) - 1; i >= 0; i-- {
|
|
||||||
for j, char := range t.data[i] {
|
|
||||||
newData[j] += string(char)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.data = newData
|
|
||||||
|
|
||||||
t.rotation++
|
|
||||||
if t.rotation > 3 {
|
|
||||||
t.rotation = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tile) flipX() {
|
|
||||||
newData := make([]string, len(t.data))
|
|
||||||
|
|
||||||
for i := 0; i < len(newData); i++ {
|
|
||||||
newData[i] = t.data[len(t.data)-1-i]
|
|
||||||
}
|
|
||||||
|
|
||||||
t.data = newData
|
|
||||||
t.flippedX = !t.flippedX
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tile) flipY() {
|
|
||||||
newData := make([]string, len(t.data))
|
|
||||||
|
|
||||||
for i := 0; i < len(newData); i++ {
|
|
||||||
for j := 0; j < len(t.data[i]); j++ {
|
|
||||||
newData[i] += string(t.data[i][len(t.data)-1-j])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
t.data = newData
|
|
||||||
t.flippedY = !t.flippedY
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tile) reset() {
|
|
||||||
for t.rotation != 0 {
|
|
||||||
t.rotate()
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.flippedX {
|
|
||||||
t.flipX()
|
|
||||||
}
|
|
||||||
if t.flippedY {
|
|
||||||
t.flipY()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseInput(input []string) map[int]*Tile {
|
|
||||||
tileMap := make(map[int]*Tile)
|
|
||||||
|
|
||||||
re := regexp.MustCompile("^Tile ([0-9]+):$")
|
|
||||||
id := 0
|
|
||||||
tile := NewTile()
|
|
||||||
for _, line := range input {
|
|
||||||
if re.MatchString(line) {
|
|
||||||
id = util.MustAtoi(re.FindStringSubmatch(line)[1])
|
|
||||||
tile.id = id
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if line == "" {
|
|
||||||
tileMap[id] = tile
|
|
||||||
tile = NewTile()
|
|
||||||
id = 0
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
tile.data = append(tile.data, line)
|
|
||||||
}
|
|
||||||
|
|
||||||
// if needed, add the last one (might be missing our final blank line)
|
|
||||||
if id != 0 {
|
|
||||||
tileMap[id] = tile
|
|
||||||
}
|
|
||||||
|
|
||||||
return tileMap
|
|
||||||
}
|
|
||||||
|
|
||||||
func matchTiles(oldTile, newTile *Tile) int {
|
|
||||||
for i := 0; i < 4; i++ {
|
|
||||||
check := subMatchTiles(oldTile, newTile)
|
|
||||||
if check != MATCH_NONE {
|
|
||||||
return check
|
|
||||||
}
|
|
||||||
newTile.rotate()
|
|
||||||
}
|
|
||||||
|
|
||||||
newTile.reset()
|
|
||||||
newTile.flipX()
|
|
||||||
for i := 0; i < 4; i++ {
|
|
||||||
check := subMatchTiles(oldTile, newTile)
|
|
||||||
if check != MATCH_NONE {
|
|
||||||
return check
|
|
||||||
}
|
|
||||||
newTile.rotate()
|
|
||||||
}
|
|
||||||
|
|
||||||
newTile.reset()
|
|
||||||
newTile.flipY()
|
|
||||||
for i := 0; i < 4; i++ {
|
|
||||||
check := subMatchTiles(oldTile, newTile)
|
|
||||||
if check != MATCH_NONE {
|
|
||||||
return check
|
|
||||||
}
|
|
||||||
newTile.rotate()
|
|
||||||
}
|
|
||||||
|
|
||||||
newTile.reset()
|
|
||||||
newTile.flipX()
|
|
||||||
newTile.flipY()
|
|
||||||
for i := 0; i < 4; i++ {
|
|
||||||
check := subMatchTiles(oldTile, newTile)
|
|
||||||
if check != MATCH_NONE {
|
|
||||||
return check
|
|
||||||
}
|
|
||||||
newTile.rotate()
|
|
||||||
}
|
|
||||||
|
|
||||||
return MATCH_NONE
|
|
||||||
}
|
|
||||||
|
|
||||||
func subMatchTiles(oldTile, newTile *Tile) int {
|
|
||||||
// check top
|
|
||||||
if oldTile.data[0] == newTile.data[0] {
|
|
||||||
return MATCH_TOP
|
|
||||||
}
|
|
||||||
// check bottom
|
|
||||||
if oldTile.data[len(oldTile.data)-1] == newTile.data[len(newTile.data)-1] {
|
|
||||||
return MATCH_BOTTOM
|
|
||||||
}
|
|
||||||
// check left
|
|
||||||
match := true
|
|
||||||
for i := 0; i < len(oldTile.data); i++ {
|
|
||||||
if oldTile.data[i][0] != newTile.data[i][0] {
|
|
||||||
match = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if match {
|
|
||||||
return MATCH_LEFT
|
|
||||||
}
|
|
||||||
|
|
||||||
// check right
|
|
||||||
match = true
|
|
||||||
for i := 0; i < len(oldTile.data); i++ {
|
|
||||||
if oldTile.data[i][len(oldTile.data)-1] != newTile.data[i][len(newTile.data)-1] {
|
|
||||||
match = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if match {
|
|
||||||
return MATCH_RIGHT
|
|
||||||
}
|
|
||||||
|
|
||||||
return MATCH_NONE
|
|
||||||
}
|
|
||||||
|
|
||||||
func arrangeTiles(tiles map[int]*Tile) map[[2]int]*Tile {
|
|
||||||
grid := make(map[[2]int]*Tile)
|
|
||||||
looseTiles := make([]*Tile, 0)
|
|
||||||
for _, v := range tiles {
|
|
||||||
looseTiles = append(looseTiles, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// arbitrarily place a first tile
|
|
||||||
grid[[2]int{0, 0}] = looseTiles[0]
|
|
||||||
looseTiles = looseTiles[1:]
|
|
||||||
|
|
||||||
for len(looseTiles) > 0 {
|
|
||||||
for coord, tile := range grid {
|
|
||||||
if _, ok := grid[[2]int{coord[0] + 1, coord[1]}]; ok {
|
|
||||||
if _, ok := grid[[2]int{coord[0] - 1, coord[1]}]; ok {
|
|
||||||
if _, ok := grid[[2]int{coord[0], coord[1] + 1}]; ok {
|
|
||||||
if _, ok := grid[[2]int{coord[0], coord[1] - 1}]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, loose := range looseTiles {
|
|
||||||
matched := matchTiles(tile, loose)
|
|
||||||
// On a match, flip appropriately and set coords
|
|
||||||
// check for already present tiles as well - skip if that's the case
|
|
||||||
var newCoords [2]int
|
|
||||||
willFlipX := true
|
|
||||||
switch matched {
|
|
||||||
case MATCH_TOP:
|
|
||||||
newCoords = [2]int{coord[0], coord[1] + 1}
|
|
||||||
case MATCH_BOTTOM:
|
|
||||||
newCoords = [2]int{coord[0], coord[1] - 1}
|
|
||||||
case MATCH_LEFT:
|
|
||||||
newCoords = [2]int{coord[0] - 1, coord[1]}
|
|
||||||
willFlipX = false
|
|
||||||
case MATCH_RIGHT:
|
|
||||||
newCoords = [2]int{coord[0] + 1, coord[1]}
|
|
||||||
willFlipX = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if matched != MATCH_NONE {
|
|
||||||
if _, ok := grid[newCoords]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if willFlipX {
|
|
||||||
loose.flipX()
|
|
||||||
} else {
|
|
||||||
loose.flipY()
|
|
||||||
}
|
|
||||||
|
|
||||||
grid[newCoords] = loose
|
|
||||||
|
|
||||||
if i == len(looseTiles)-1 {
|
|
||||||
looseTiles = looseTiles[:i]
|
|
||||||
} else {
|
|
||||||
looseTiles = append(looseTiles[:i], looseTiles[i+1:]...)
|
|
||||||
}
|
|
||||||
break // to avoid sequencing issues
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return grid
|
|
||||||
}
|
|
||||||
|
|
||||||
func findCorners(grid map[[2]int]*Tile) []*Tile {
|
|
||||||
corners := make([]*Tile, 0)
|
|
||||||
|
|
||||||
// min and max coord values
|
|
||||||
minX, minY, maxX, maxY := findLimits(grid)
|
|
||||||
|
|
||||||
corners = append(corners, grid[[2]int{minX, minY}])
|
|
||||||
corners = append(corners, grid[[2]int{maxX, minY}])
|
|
||||||
corners = append(corners, grid[[2]int{minX, maxY}])
|
|
||||||
corners = append(corners, grid[[2]int{maxX, maxY}])
|
|
||||||
return corners
|
|
||||||
}
|
|
||||||
|
|
||||||
func findLimits(grid map[[2]int]*Tile) (int, int, int, int) {
|
|
||||||
var minX, minY, maxX, maxY int
|
|
||||||
for coord, _ := range grid {
|
|
||||||
if coord[0] > maxX {
|
|
||||||
maxX = coord[0]
|
|
||||||
}
|
|
||||||
if coord[0] < minX {
|
|
||||||
minX = coord[0]
|
|
||||||
}
|
|
||||||
if coord[1] > maxY {
|
|
||||||
maxY = coord[1]
|
|
||||||
}
|
|
||||||
if coord[1] < minY {
|
|
||||||
minY = coord[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return minX, minY, maxX, maxY
|
|
||||||
}
|
|
||||||
|
|
||||||
func stripBorder(tile *Tile) []string {
|
|
||||||
ret := make([]string, 0)
|
|
||||||
for i := 1; i < len(tile.data)-1; i++ {
|
|
||||||
ret = append(ret, tile.data[i][1:len(tile.data)-1])
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func combineTiles(grid map[[2]int]*Tile) *Tile {
|
|
||||||
bigTile := NewTile()
|
|
||||||
minX, minY, maxX, maxY := findLimits(grid)
|
|
||||||
|
|
||||||
for j := maxY; j >= minY; j-- {
|
|
||||||
row := make([]string, 8)
|
|
||||||
for i := minX; i <= maxX; i++ {
|
|
||||||
var tile *Tile
|
|
||||||
tile, ok := grid[[2]int{i, j}]
|
|
||||||
if !ok {
|
|
||||||
log.Panicf("Couldn't find tile: %d, %d", i, j)
|
|
||||||
}
|
|
||||||
|
|
||||||
data := stripBorder(tile)
|
|
||||||
for i, line := range data {
|
|
||||||
row[i] = row[i] + line
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bigTile.data = append(bigTile.data, row...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return bigTile
|
|
||||||
}
|
|
||||||
|
|
||||||
var monsterPattern = []string{
|
|
||||||
" # ",
|
|
||||||
"# ## ## ###",
|
|
||||||
" # # # # # # ",
|
|
||||||
}
|
|
||||||
|
|
||||||
func subFilterMonsters(image *Tile) int {
|
|
||||||
numMonsters := 0
|
|
||||||
for i := 0; i < len(image.data)-2; i++ {
|
|
||||||
for j := 0; j < len(image.data[0])-19; j++ {
|
|
||||||
if monsterCheck(image, i, j) {
|
|
||||||
removeMonster(image, i, j)
|
|
||||||
numMonsters++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return numMonsters
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeMonster(image *Tile, i, j int) {
|
|
||||||
for y := 0; y < 3; y++ {
|
|
||||||
for x := 0; x < 20; x++ {
|
|
||||||
if monsterPattern[y][x] == '#' {
|
|
||||||
if j+x == len(image.data[i+y])-1 {
|
|
||||||
image.data[i+y] = image.data[i+y][:j+x] + "O"
|
|
||||||
} else {
|
|
||||||
image.data[i+y] = image.data[i+y][:j+x] + "O" + image.data[i+y][j+x+1:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func monsterCheck(image *Tile, i, j int) bool {
|
|
||||||
data := image.data
|
|
||||||
|
|
||||||
for y := 0; y < 3; y++ {
|
|
||||||
for x := 0; x < 20; x++ {
|
|
||||||
if data[i+y][j+x] == '.' && monsterPattern[y][x] == '#' {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func filterMonsters(image *Tile) int {
|
|
||||||
numMonsters := 0
|
|
||||||
|
|
||||||
for i := 0; i < 4; i++ {
|
|
||||||
numMonsters = subFilterMonsters(image)
|
|
||||||
if numMonsters > 0 {
|
|
||||||
return numMonsters
|
|
||||||
}
|
|
||||||
image.rotate()
|
|
||||||
}
|
|
||||||
|
|
||||||
image.reset()
|
|
||||||
image.flipX()
|
|
||||||
for i := 0; i < 4; i++ {
|
|
||||||
numMonsters = subFilterMonsters(image)
|
|
||||||
if numMonsters > 0 {
|
|
||||||
return numMonsters
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
image.reset()
|
|
||||||
image.flipY()
|
|
||||||
for i := 0; i < 4; i++ {
|
|
||||||
numMonsters = subFilterMonsters(image)
|
|
||||||
if numMonsters > 0 {
|
|
||||||
return numMonsters
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
image.reset()
|
|
||||||
image.flipX()
|
|
||||||
image.flipY()
|
|
||||||
for i := 0; i < 4; i++ {
|
|
||||||
numMonsters = subFilterMonsters(image)
|
|
||||||
if numMonsters > 0 {
|
|
||||||
return numMonsters
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return numMonsters
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
step := os.Args[1]
|
|
||||||
values := util.InputParserStrings(os.Args[2])
|
|
||||||
|
|
||||||
tileMap := parseInput(values)
|
|
||||||
grid := arrangeTiles(tileMap)
|
|
||||||
|
|
||||||
switch step {
|
|
||||||
case "1":
|
|
||||||
corners := findCorners(grid)
|
|
||||||
product := 1
|
|
||||||
for _, tile := range corners {
|
|
||||||
product *= tile.id
|
|
||||||
}
|
|
||||||
fmt.Println(product)
|
|
||||||
case "2":
|
|
||||||
image := combineTiles(grid)
|
|
||||||
monsters := filterMonsters(image)
|
|
||||||
if monsters == 0 {
|
|
||||||
log.Panicf("Found no monsters")
|
|
||||||
}
|
|
||||||
count := 0
|
|
||||||
for _, line := range image.data {
|
|
||||||
for _, char := range line {
|
|
||||||
if char == '#' {
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Println(count)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"git.annabunch.es/annabunches/adventofcode/2020/lib/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
func findLoopSize(pubKey, subject int) int {
|
|
||||||
value := 1
|
|
||||||
for i := 0; ; i++ {
|
|
||||||
value *= subject
|
|
||||||
value %= 20201227
|
|
||||||
|
|
||||||
if value == pubKey {
|
|
||||||
return i + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func transformLoop(pubKey, loopSize int) int {
|
|
||||||
value := 1
|
|
||||||
for i := 0; i < loopSize; i++ {
|
|
||||||
value *= pubKey
|
|
||||||
value %= 20201227
|
|
||||||
}
|
|
||||||
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
step := os.Args[1]
|
|
||||||
values := util.InputParserInts(os.Args[2])
|
|
||||||
|
|
||||||
keyPub := values[0]
|
|
||||||
doorPub := values[1]
|
|
||||||
|
|
||||||
keyLoop := findLoopSize(keyPub, 7)
|
|
||||||
doorLoop := findLoopSize(doorPub, 7)
|
|
||||||
|
|
||||||
encryptionKey1 := transformLoop(keyPub, doorLoop)
|
|
||||||
encryptionKey2 := transformLoop(doorPub, keyLoop)
|
|
||||||
|
|
||||||
switch step {
|
|
||||||
case "1":
|
|
||||||
fmt.Println(encryptionKey1)
|
|
||||||
fmt.Println(encryptionKey2)
|
|
||||||
case "2":
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user