Minor cleanup to day 22
parent
e0997658d8
commit
b0a87880cc
|
@ -94,62 +94,21 @@ func main() {
|
|||
|
||||
func part1(inputBricks []Brick) int {
|
||||
slammedBricks := settleBricks(inputBricks)
|
||||
incoming, outgoing := buildBrickGraph(slammedBricks)
|
||||
removable := removableBricks(slammedBricks, incoming, outgoing)
|
||||
removable := removableBricks(slammedBricks)
|
||||
|
||||
return len(removable)
|
||||
}
|
||||
|
||||
func part2(inputBricks []Brick) int {
|
||||
slammedBricks := settleBricks(inputBricks)
|
||||
incoming, outgoing := buildBrickGraph(slammedBricks)
|
||||
total := 0
|
||||
for i := range slammedBricks {
|
||||
stableNodes := map[int]struct{}{}
|
||||
lastFalling := map[int]struct{}{}
|
||||
for {
|
||||
reachableNodes := outgoing.ReachableFromExcluding(i, stableNodes)
|
||||
for reachable := range reachableNodes {
|
||||
for _, parentOfReachable := range incoming[reachable] {
|
||||
if _, ok := reachableNodes[parentOfReachable]; !ok && parentOfReachable != i {
|
||||
// If any reachable node is accessible from another subgraph, it is "stable"
|
||||
stableNodes[reachable] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
falling := outgoing.ReachableFromExcluding(i, stableNodes)
|
||||
shouldBreak := mapKeysEqual(falling, lastFalling)
|
||||
lastFalling = falling
|
||||
if shouldBreak {
|
||||
break
|
||||
}
|
||||
|
||||
// We must repeat this process until we reach a state where no more stable nodes are found
|
||||
// There are some cases where a node might be stable, but the children of said stable node
|
||||
// must also be considered invalidated (think of it as second-order stability)
|
||||
}
|
||||
|
||||
total += len(lastFalling)
|
||||
total += numBricksFallingByRemoval(slammedBricks, i)
|
||||
}
|
||||
|
||||
return total
|
||||
}
|
||||
|
||||
func mapKeysEqual[T comparable, U any](m1, m2 map[T]U) bool {
|
||||
if len(m1) != len(m2) {
|
||||
return false
|
||||
}
|
||||
|
||||
for key := range m1 {
|
||||
if _, ok := m2[key]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func settleBricks(bricks []Brick) []Brick {
|
||||
sorted := slices.Clone(bricks)
|
||||
sortByHeight(sorted)
|
||||
|
@ -214,7 +173,9 @@ func buildBrickGraph(bricks []Brick) (incoming, outgoing BrickGraph) {
|
|||
return
|
||||
}
|
||||
|
||||
func removableBricks(allBricks []Brick, incoming BrickGraph, outgoing BrickGraph) []int {
|
||||
func removableBricks(allBricks []Brick) []int {
|
||||
incoming, outgoing := buildBrickGraph(allBricks)
|
||||
|
||||
removable := []int{}
|
||||
for i := range allBricks {
|
||||
dependents := outgoing[i]
|
||||
|
@ -237,6 +198,38 @@ func removableBricks(allBricks []Brick, incoming BrickGraph, outgoing BrickGraph
|
|||
return removable
|
||||
}
|
||||
|
||||
func numBricksFallingByRemoval(allBricks []Brick, removeBrick int) int {
|
||||
if removeBrick >= len(allBricks) {
|
||||
panic("cannot remove brick not in bricks list")
|
||||
}
|
||||
|
||||
incoming, outgoing := buildBrickGraph(allBricks)
|
||||
stableNodes := map[int]struct{}{}
|
||||
lastFalling := map[int]struct{}{}
|
||||
for {
|
||||
reachableNodes := outgoing.ReachableFromExcluding(removeBrick, stableNodes)
|
||||
for reachable := range reachableNodes {
|
||||
for _, parentOfReachable := range incoming[reachable] {
|
||||
if _, ok := reachableNodes[parentOfReachable]; !ok && parentOfReachable != removeBrick {
|
||||
// If any reachable node is accessible from another subgraph, it is "stable"
|
||||
stableNodes[reachable] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
falling := outgoing.ReachableFromExcluding(removeBrick, stableNodes)
|
||||
if mapKeysEqual(falling, lastFalling) {
|
||||
return len(lastFalling)
|
||||
}
|
||||
|
||||
lastFalling = falling
|
||||
|
||||
// We must repeat this process until we reach a state where no more stable nodes are found
|
||||
// There are some cases where a node might be stable, but the children of said stable node
|
||||
// must also be considered invalidated (think of it as second-order stability)
|
||||
}
|
||||
}
|
||||
|
||||
func sortByHeight(bricks []Brick) {
|
||||
slices.SortFunc(bricks, func(brick1, brick2 Brick) int {
|
||||
min1Z := brick1.LowestPoint()
|
||||
|
@ -341,3 +334,17 @@ func tryParse[T any](items []string, parse func(string) (T, error)) ([]T, error)
|
|||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func mapKeysEqual[T comparable, U any](m1, m2 map[T]U) bool {
|
||||
if len(m1) != len(m2) {
|
||||
return false
|
||||
}
|
||||
|
||||
for key := range m1 {
|
||||
if _, ok := m2[key]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue