mirror of
https://github.com/Threnklyn/advent-of-code-go.git
synced 2026-05-18 19:13:27 +02:00
208 lines
3.9 KiB
Go
208 lines
3.9 KiB
Go
package main
|
|
|
|
import (
|
|
_ "embed"
|
|
"flag"
|
|
"fmt"
|
|
"math"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/alexchao26/advent-of-code-go/mathy"
|
|
"github.com/alexchao26/advent-of-code-go/util"
|
|
)
|
|
|
|
//go:embed input.txt
|
|
var input string
|
|
|
|
func init() {
|
|
// do this in init (not main) so test file has same input
|
|
input = strings.TrimRight(input, "\n")
|
|
if len(input) == 0 {
|
|
panic("empty input.txt file")
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
var part int
|
|
flag.IntVar(&part, "part", 1, "part 1 or 2")
|
|
flag.Parse()
|
|
fmt.Println("Running part", part)
|
|
|
|
ans := unstableDiffusion(input, part)
|
|
util.CopyToClipboard(fmt.Sprintf("%v", ans))
|
|
fmt.Println("Output:", ans)
|
|
}
|
|
|
|
var diffsSlice = [][][2]int{
|
|
// N
|
|
{
|
|
{-1, -1},
|
|
{-1, 0},
|
|
{-1, 1},
|
|
},
|
|
// S
|
|
{
|
|
{1, -1},
|
|
{1, 0},
|
|
{1, 1},
|
|
},
|
|
// W
|
|
{
|
|
{-1, -1},
|
|
{0, -1},
|
|
{1, -1},
|
|
},
|
|
// E
|
|
{
|
|
{-1, 1},
|
|
{0, 1},
|
|
{1, 1},
|
|
},
|
|
}
|
|
var targetDiff = [][2]int{
|
|
{-1, 0}, // N
|
|
{1, 0}, // S
|
|
{0, -1}, // W
|
|
{0, 1}, // E
|
|
}
|
|
|
|
func unstableDiffusion(input string, part int) int {
|
|
elfCoords := parseInput(input)
|
|
diffStartIndex := 0
|
|
|
|
// part 2
|
|
var lastState string
|
|
|
|
round := 1
|
|
|
|
for {
|
|
if part == 1 && round == 11 {
|
|
break
|
|
}
|
|
|
|
elfPlannedMoves := [][][2]int{}
|
|
elvesTargetingCoord := map[[2]int]int{}
|
|
|
|
for coords, val := range elfCoords {
|
|
if val == "#" {
|
|
nonZeroNeighbors := 0
|
|
for _, diffSlice := range diffsSlice {
|
|
for _, d := range diffSlice {
|
|
if elfCoords[[2]int{
|
|
coords[0] + d[0],
|
|
coords[1] + d[1],
|
|
}] == "#" {
|
|
nonZeroNeighbors++
|
|
}
|
|
}
|
|
}
|
|
|
|
if nonZeroNeighbors == 0 {
|
|
elfPlannedMoves = append(elfPlannedMoves, [][2]int{
|
|
coords, coords,
|
|
})
|
|
elvesTargetingCoord[coords]++
|
|
} else {
|
|
foundAMove := false
|
|
for i := 0; i < 4; i++ {
|
|
diffSliceIndex := i + diffStartIndex
|
|
diffSlice := diffsSlice[diffSliceIndex%4]
|
|
neighbors := 0
|
|
for _, d := range diffSlice {
|
|
if elfCoords[[2]int{
|
|
coords[0] + d[0],
|
|
coords[1] + d[1],
|
|
}] == "#" {
|
|
neighbors++
|
|
}
|
|
}
|
|
if neighbors == 0 {
|
|
nextCoords := coords
|
|
nextCoords[0] += targetDiff[diffSliceIndex%4][0]
|
|
nextCoords[1] += targetDiff[diffSliceIndex%4][1]
|
|
|
|
elfPlannedMoves = append(elfPlannedMoves, [][2]int{
|
|
coords, nextCoords,
|
|
})
|
|
elvesTargetingCoord[nextCoords]++
|
|
|
|
foundAMove = true
|
|
break
|
|
}
|
|
}
|
|
if !foundAMove {
|
|
elfPlannedMoves = append(elfPlannedMoves, [][2]int{
|
|
coords, coords,
|
|
})
|
|
elvesTargetingCoord[coords]++
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// reset coords, but only if elves are not blocked...
|
|
elfCoords = map[[2]int]string{}
|
|
|
|
for _, plannedMove := range elfPlannedMoves {
|
|
if elvesTargetingCoord[plannedMove[1]] > 1 {
|
|
// stay
|
|
elfCoords[plannedMove[0]] = "#"
|
|
} else {
|
|
// move
|
|
elfCoords[plannedMove[1]] = "#"
|
|
}
|
|
}
|
|
|
|
// rotate directions that are checked
|
|
diffStartIndex++
|
|
|
|
if part == 2 { // hash the state
|
|
allCoords := []string{}
|
|
for c := range elfCoords {
|
|
allCoords = append(allCoords, fmt.Sprint(c))
|
|
}
|
|
sort.Strings(allCoords)
|
|
thisState := fmt.Sprint(allCoords)
|
|
if lastState == thisState {
|
|
return round
|
|
}
|
|
lastState = thisState
|
|
}
|
|
|
|
round++
|
|
}
|
|
|
|
lowRow, highRow, lowCol, highCol := math.MaxInt16, math.MinInt16, math.MaxInt16, math.MinInt16
|
|
for coords := range elfCoords {
|
|
lowRow = mathy.MinInt(lowRow, coords[0])
|
|
highRow = mathy.MaxInt(highRow, coords[0])
|
|
lowCol = mathy.MinInt(lowCol, coords[1])
|
|
highCol = mathy.MaxInt(highCol, coords[1])
|
|
}
|
|
|
|
ans := 0
|
|
for r := lowRow; r <= highRow; r++ {
|
|
for c := lowCol; c <= highCol; c++ {
|
|
if elfCoords[[2]int{r, c}] != "#" {
|
|
ans++
|
|
}
|
|
}
|
|
}
|
|
|
|
return ans
|
|
}
|
|
|
|
func parseInput(input string) map[[2]int]string {
|
|
ans := map[[2]int]string{}
|
|
for r, line := range strings.Split(input, "\n") {
|
|
for c, v := range strings.Split(line, "") {
|
|
if v == "#" {
|
|
ans[[2]int{r, c}] = "#"
|
|
}
|
|
}
|
|
}
|
|
return ans
|
|
}
|