What a mess! 18-20, no Part 2 solve. Oh well!

This commit is contained in:
🐙PiperYxzzy
2023-12-21 07:26:06 +02:00
parent 9e2c2637e8
commit bcda89984d
13 changed files with 2172 additions and 90 deletions

View File

@@ -2,6 +2,8 @@ package main
import (
"fmt"
"math"
"sort"
"strconv"
"strings"
@@ -18,35 +20,164 @@ func main() {
// 3. with: false (part1), and user input
// 4. with: true (part2), and user input
// the return value of each run is printed to stdout
type Node struct {
Y int
X int
Next *Node
Prev *Node
}
func printNodes(nodes []*Node) {
maxY := 0
maxX := 0
minY := math.MaxInt
minX := math.MaxInt
for _, n := range nodes {
if maxY < n.Y {
maxY = n.Y
}
if minY > n.Y {
minY = n.Y
}
if maxX < n.X {
maxX = n.X
}
if minX > n.X {
minX = n.X
}
}
print := false
if print {
gridToPrint := make([][]rune, 0)
for y := 0; y <= maxY-minY; y++ {
gridLine := make([]rune, 0)
for x := 0; x <= maxX-minX; x++ {
gridLine = append(gridLine, ' ')
}
gridToPrint = append(gridToPrint, gridLine)
}
for y := minY; y <= maxY; y++ {
for x := minX; x <= maxX; x++ {
for _, n := range nodes {
//fmt.Printf("Checking node %v\n", n)
if n.X == x && n.Y == y {
if n.Prev.X == n.X {
// From previous, went up/down
if n.Prev.Y < n.Y {
// Has gone down
if n.Next.X > n.X {
// And right
gridToPrint[y-minY][x-minX] = '└'
} else {
// And left
gridToPrint[y-minY][x-minX] = '┘'
}
} else {
// Has gone up
if n.Next.X > n.X {
// And right
gridToPrint[y-minY][x-minX] = '┌'
} else {
// And left
gridToPrint[y-minY][x-minX] = '┐'
}
}
} else if n.Prev.Y == n.Y {
// From previous, went left/right
if n.Prev.X > n.X {
// Has gone left
if n.Next.Y > n.Y {
// And down
gridToPrint[y-minY][x-minX] = '┌'
} else {
// And up
gridToPrint[y-minY][x-minX] = '└'
}
} else {
// Has gone right
if n.Next.Y > n.Y {
// And down
gridToPrint[y-minY][x-minX] = '┐'
} else {
// And up
gridToPrint[y-minY][x-minX] = '┘'
}
}
} else {
panic(fmt.Sprintf("Could not align %v with Prev: %v", n, n.Prev))
}
}
}
}
}
// Now connect
for y := range gridToPrint {
for x := range gridToPrint[y] {
if gridToPrint[y][x] == '┌' || gridToPrint[y][x] == '└' {
xRun := x + 1
for gridToPrint[y][xRun] == ' ' {
gridToPrint[y][xRun] = '─'
xRun++
}
}
if gridToPrint[y][x] == '┌' || gridToPrint[y][x] == '┐' {
yRun := y + 1
for gridToPrint[yRun][x] == ' ' {
gridToPrint[yRun][x] = '│'
yRun++
}
}
}
}
for y := range gridToPrint {
for x := range gridToPrint[y] {
fmt.Printf("%v", string(gridToPrint[y][x]))
}
fmt.Println()
}
}
}
func abs(a int) int {
return int(math.Abs(float64(a)))
}
var rad90 float64 = 90 * math.Pi / 180
func rotate(nodes []*Node) {
for _, n := range nodes {
// rotate all points by 90 degrees
newX := int(math.Round(float64(n.X)*math.Cos(rad90) - float64(n.Y)*math.Sin(rad90)))
newY := int(math.Round(float64(n.X)*math.Sin(rad90) + float64(n.Y)*math.Cos(rad90)))
n.X, n.Y = newX, newY
}
}
func run(part2 bool, input string) any {
// when you're ready to do part 2, remove this "not implemented" block
if part2 {
// More complex
// shrink wrap?
return "not implemented"
}
// solve part 1 here
// Get max dimensions (sumR + sumD)
// Shrinkwrap approach
// or just make a big ass grid
maxV := 1200
maxH := 1200
grid := make([][]string, 0)
for v := 0; v < maxV; v++ {
gridLine := make([]string, 0)
for h := 0; h < maxH; h++ {
gridLine = append(gridLine, ".")
}
grid = append(grid, gridLine)
}
v := maxV / 2
h := maxH / 2
// Dig out trench
nodes := make([]*Node, 0)
x := 0
y := 0
for _, l := range strings.Split(input, "\n") {
if l == "" {
continue
@@ -54,79 +185,298 @@ func run(part2 bool, input string) any {
spl := strings.Split(l, " ")
order := spl[0]
order := rune(spl[0][0])
dist, _ := strconv.Atoi(spl[1])
colour := spl[2]
//colour := spl[2]
for i := 0; i < dist; i++ {
if order == "R" {
h += 1
} else if order == "L" {
h -= 1
} else if order == "U" {
v -= 1
} else if order == "D" {
v += 1
} else {
panic("bad direction")
}
grid[v][h] = colour
node := &Node{Y: y, X: x}
nodes = append(nodes, node)
if order == 'R' {
x += dist
} else if order == 'L' {
x -= dist
} else if order == 'U' {
y -= dist
} else if order == 'D' {
y += dist
} else {
panic("bad order")
}
}
// Fill out outside
outside := [][]int{{0, 0}}
for len(outside) > 0 {
o := outside[0]
outside = outside[1:]
// Connect nodes (currently in order
if grid[o[0]][o[1]] == "." {
grid[o[0]][o[1]] = " "
v = o[0]
h = o[1]
if v > 0 {
outside = append(outside, []int{v - 1, h})
}
if h > 0 {
outside = append(outside, []int{v, h - 1})
}
if v < len(grid)-1 {
outside = append(outside, []int{v + 1, h})
}
if h < len(grid[0])-1 {
outside = append(outside, []int{v, h + 1})
}
for n := range nodes {
if n == 0 {
nodes[n].Next = nodes[1]
nodes[n].Prev = nodes[len(nodes)-1]
} else if n == len(nodes)-1 {
nodes[n].Next = nodes[0]
nodes[n].Prev = nodes[n-1]
} else {
nodes[n].Next = nodes[n+1]
nodes[n].Prev = nodes[n-1]
}
}
// Finally count fill-in
digout := 0
for v := range grid {
for h := range grid {
if grid[v][h] != " " {
digout += 1
firstNode := nodes[0]
lastNode := nodes[len(nodes)-1]
firstNode.Prev = lastNode
lastNode.Next = firstNode
// Begin downward collapse
printNodes(nodes)
totalArea := 0
for len(nodes) > 0 {
remove := make([]*Node, 0)
// try to find an "unbreakable, top-layer node"
/*
imagine:
............
.##########.
.##########.
.##########.
.####..####.
.####..####.
.####..####.
.####.......
............
*/
// Find the topmost node (i.e. lowest Y)
sort.Slice(nodes, func(i, j int) bool {
return nodes[i].Y < nodes[j].Y
})
n1 := nodes[0]
var n2, n3, n1adj, n2adj *Node
if n1.Next.Y == n1.Y {
n2 = n1.Next
n1adj = n1.Prev
n2adj = n2.Next
} else {
n2 = n1.Prev
n1adj = n1.Next
n2adj = n2.Prev
}
// Confirm that this is "unbreakable", else rotate and continue
if n1adj.Y < n2adj.Y {
n3 = n1adj
} else {
n3 = n2adj
}
// If there are any other nodes inside this box, reject
unbreakable := true
maxY := n3.Y
var minX, maxX int
if n1.X < n2.X {
minX = n1.X
maxX = n2.X
} else {
minX = n2.X
maxX = n1.X
}
for _, n := range nodes {
if n == n1 || n == n2 || n == n3 {
continue
}
if n.X > minX && n.X < maxX && n.Y < maxY {
unbreakable = false
}
}
}
print := false
if print {
for v := range grid {
for h := range grid[v] {
if len(grid[v][h]) == 1 {
fmt.Print(grid[v][h])
if !unbreakable {
// try to find any other unbreakables
exclusionX := [][]int{{n1.X, n2.X}}
excludeX:
for _, n := range nodes {
n1 = n
if n1.Next.Y == n1.Y {
n2 = n1.Next
n1adj = n1.Prev
n2adj = n2.Next
} else {
fmt.Print("#")
n2 = n1.Prev
n1adj = n1.Next
n2adj = n2.Prev
}
if n1adj.Y < n1.Y || n2adj.Y < n1.Y {
continue excludeX
}
for _, ex := range exclusionX {
if ex[0] > ex[1] {
if n1.X <= ex[0] && n1.X >= ex[1] || n2.X <= ex[0] && n2.X >= ex[1] {
continue excludeX
}
} else {
if n1.X >= ex[0] && n1.X <= ex[1] || n2.X >= ex[0] && n2.X <= ex[1] {
continue excludeX
}
}
}
// Confirm that this is "unbreakable", else rotate and continue
if n1adj.Y < n2adj.Y {
n3 = n1adj
} else {
n3 = n2adj
}
unbreakable = true
maxY := n3.Y
var minX, maxX int
if n1.X < n2.X {
minX = n1.X
maxX = n2.X
} else {
minX = n2.X
maxX = n1.X
}
for _, n := range nodes {
if n == n1 || n == n2 || n == n3 {
continue
}
if n.X > minX && n.X < maxX && n.Y < maxY {
unbreakable = false
}
}
if !unbreakable {
exclusionX = append(exclusionX, []int{n1.X, n2.X})
} else {
break excludeX
}
}
fmt.Println()
if !unbreakable {
rotate(nodes)
printNodes(nodes)
continue
}
}
// We now have our "top" bar (n1-n2)
// Find the minimum to shift it by
// Now for the clever bit
xLen := abs(n1.X-n2.X) + 1
fmt.Printf("Bar len: %v (%v %v %v) \n", xLen, n1, n2, n3)
var addY int
if n1adj.Y < n2adj.Y {
addY = n1adj.Y
} else {
addY = n2adj.Y
}
addY -= n1.Y
fmt.Printf("Shift down by: %v\n", addY)
areaCollapse := xLen * addY
totalArea += areaCollapse
// And collapse the nodes
n1.Y += addY
n2.Y += addY
if n1.Y == n1adj.Y && n1.X == n1adj.X {
// merge into
remove = append(remove, n1)
if n1.Next == n1adj {
n1adj.Prev = n1.Prev
n1.Prev.Next = n1adj
} else if n1.Prev == n1adj {
n1adj.Next = n1.Next
n1.Next.Prev = n1adj
} else {
panic("Matched 1 but could not reassign")
}
}
if n2.Y == n2adj.Y && n2.X == n2adj.X {
remove = append(remove, n2)
if n2.Next == n2adj {
n2adj.Prev = n2.Prev
n2.Prev.Next = n2adj
} else if n2.Prev == n2adj {
n2adj.Next = n2.Next
n2.Next.Prev = n2adj
} else {
panic("Matched 2 but could not reassign")
}
}
// finally, find any sharp corners (3 nodes in a row with the same Y)
if n1adj.Next.Y == n1adj.Y && n1adj.Prev.Y == n1adj.Y {
if (n1adj.X > n1adj.Next.X && n1adj.X > n1adj.Prev.X) || (n1adj.X < n1adj.Next.X && n1adj.X < n1adj.Prev.X) {
diffNextPrev := abs(n1adj.Next.X - n1adj.Prev.X)
diffBoth := abs(n1adj.X-n1adj.Next.X) + abs(n1adj.X-n1adj.Prev.X)
areaCount := (diffBoth - diffNextPrev) / 2
fmt.Printf("Offcut area: %v\n", areaCount)
totalArea += areaCount
}
remove = append(remove, n1adj)
n1adj.Next.Prev = n1adj.Prev
n1adj.Prev.Next = n1adj.Next
}
if n2adj.Next.Y == n2adj.Y && n2adj.Prev.Y == n2adj.Y {
if (n2adj.X > n2adj.Next.X && n2adj.X > n2adj.Prev.X) || (n2adj.X < n2adj.Next.X && n2adj.X < n2adj.Prev.X) {
diffNextPrev := abs(n2adj.Next.X - n2adj.Prev.X)
diffBoth := abs(n2adj.X-n2adj.Next.X) + abs(n2adj.X-n2adj.Prev.X)
areaCount := (diffBoth - diffNextPrev) / 2
fmt.Printf("Offcut area: %v\n", areaCount)
totalArea += areaCount
}
remove = append(remove, n2adj)
n2adj.Next.Prev = n2adj.Prev
n2adj.Prev.Next = n2adj.Next
}
fmt.Printf("%v\n", totalArea)
remainingNodes := make([]*Node, 0)
for _, n := range nodes {
keep := true
for _, r := range remove {
if r == n {
keep = false
}
}
if keep {
remainingNodes = append(remainingNodes, n)
}
}
nodes = remainingNodes
//if totalArea == 48081 {
//}
//
printNodes(nodes)
//time.Sleep(500 * time.Millisecond)
}
return digout
// Expect 62
return totalArea + 1
}