Skip to content

Commit

Permalink
Merge pull request #11 from feuerrot/day12
Browse files Browse the repository at this point in the history
day 12
  • Loading branch information
feuerrot authored Dec 14, 2022
2 parents ed263f6 + be0bef2 commit 1374e43
Show file tree
Hide file tree
Showing 4 changed files with 409 additions and 0 deletions.
314 changes: 314 additions & 0 deletions 12.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,314 @@
package main

import (
"fmt"
"sort"
"strings"
"sync"
)

type AOC202212Node struct {
Height rune
PosX int
PosY int
NodeUp *AOC202212Node
NodeDown *AOC202212Node
NodeLeft *AOC202212Node
NodeRight *AOC202212Node
}

type AOC202212Way []*AOC202212Node

func (way AOC202212Way) String() string {
parts := []string{}
for _, node := range way {
parts = append(parts, fmt.Sprintf("N %d/%d H %d", node.PosX, node.PosY, node.Height))
}

return strings.Join(parts, " -> ")
}

func (n *AOC202212Node) NeighborReachable(neighbor *AOC202212Node) bool {
if neighbor == nil {
return false
}

selfHeight := n.Height
if selfHeight == 'S' {
selfHeight = 'a'
} else if selfHeight == 'E' {
selfHeight = 'z'
}
neighborHeight := neighbor.Height
if neighborHeight == 'S' {
neighborHeight = 'a'
} else if neighborHeight == 'E' {
neighborHeight = 'z'
}

return neighborHeight <= selfHeight+1
}

type AOC202212Map struct {
NodeStart *AOC202212Node
NodeEnd *AOC202212Node
SizeX int
SizeY int
Nodes [][]*AOC202212Node
Distance map[int][]*AOC202212Node
}

func (m *AOC202212Map) NodeLeft(x, y int) *AOC202212Node {
if x-1 < 0 {
return nil
}
return m.Nodes[y][x-1]
}

func (m *AOC202212Map) NodeRight(x, y int) *AOC202212Node {
if x >= m.SizeX {
return nil
}
return m.Nodes[y][x+1]
}

func (m *AOC202212Map) NodeUp(x, y int) *AOC202212Node {
if y-1 < 0 {
return nil
}
return m.Nodes[y-1][x]
}

func (m *AOC202212Map) NodeDown(x, y int) *AOC202212Node {
if y >= m.SizeY {
return nil
}
return m.Nodes[y+1][x]
}

func (m *AOC202212Map) FindWay(current *AOC202212Node, previous AOC202212Way) (AOC202212Way, int) {
search := []*AOC202212Node{
current.NodeUp,
current.NodeDown,
current.NodeLeft,
current.NodeRight,
}

sort.Slice(search, func(i, j int) bool {
if search[i] == nil || search[j] == nil {
return false
}
if search[i].PosX > search[j].PosX {
return true
}
if search[i].PosY > search[j].PosY {
return true
}

if search[i].Height != current.Height {
return true
}
return false
})

bestLen := -1
var bestChoice []*AOC202212Node
var wg sync.WaitGroup
var bcm sync.Mutex

CHOICE:
for _, choice := range search {
if choice == nil {
continue
}

if choice == m.NodeEnd {
return []*AOC202212Node{choice}, 1
}

for _, prev := range previous {
if choice == prev {
continue CHOICE
}
}

lchoice := choice
previous := previous
wg.Add(1)
go func() {
previous = append(previous, current)
nextWay, nextLen := m.FindWay(lchoice, previous)
bcm.Lock()
if nextWay != nil && (bestLen == -1 || nextLen < bestLen) {
bestChoice = nextWay
bestLen = nextLen
}
bcm.Unlock()
wg.Done()
}()
if len(previous) > 2 {
wg.Wait()
}
}

wg.Wait()

if bestChoice == nil {
return nil, 0
}

bestChoice = append(bestChoice, current)

return bestChoice, bestLen + 1
}

func (m *AOC202212Map) ParseDistances() {
found := []*AOC202212Node{m.NodeStart}
distance := map[int][]*AOC202212Node{
0: {m.NodeStart},
}

for i := 1; ; i++ {
curDist := []*AOC202212Node{}
for _, node := range distance[i-1] {
if node == nil {
continue
}
search := []*AOC202212Node{
node.NodeUp,
node.NodeDown,
node.NodeLeft,
node.NodeRight,
}

NEIGHBOR:
for _, neighbor := range search {
for _, prevFound := range found {
if neighbor == prevFound {
continue NEIGHBOR
}
}
curDist = append(curDist, neighbor)
found = append(found, neighbor)
}
}
if len(curDist) == 0 {
break
}
distance[i] = curDist
}
m.Distance = distance
}

func (m *AOC202212Map) SolvePart1() int {
distance := []int{}
for i := range m.Distance {
distance = append(distance, i)
}

sort.Ints(distance)
for _, i := range distance {
for _, node := range m.Distance[i] {
if node == m.NodeEnd {
return i
}
}
}

return 0
}

func AOC202212ParseMap(input string) (AOC202212Map, error) {
rtn := AOC202212Map{
Nodes: make([][]*AOC202212Node, 0),
}

for y, line := range strings.Split(input, "\n") {
if y > rtn.SizeY {
rtn.SizeY = y
}
lineNodes := make([]*AOC202212Node, 0)
for x, char := range line {
if x > rtn.SizeX {
rtn.SizeX = x
}
node := &AOC202212Node{
Height: char,
PosX: x,
PosY: y,
}
if char == 'S' {
rtn.NodeStart = node
//node.Height = 'a'
} else if char == 'E' {
rtn.NodeEnd = node
//node.Height = 'z'
}
lineNodes = append(lineNodes, node)
}
rtn.Nodes = append(rtn.Nodes, lineNodes)
}

for y, row := range rtn.Nodes {
for x, node := range row {
if node.NeighborReachable(rtn.NodeLeft(x, y)) {
rtn.Nodes[y][x].NodeLeft = rtn.NodeLeft(x, y)
}
if node.NeighborReachable(rtn.NodeRight(x, y)) {
rtn.Nodes[y][x].NodeRight = rtn.NodeRight(x, y)
}
if node.NeighborReachable(rtn.NodeUp(x, y)) {
rtn.Nodes[y][x].NodeUp = rtn.NodeUp(x, y)
}
if node.NeighborReachable(rtn.NodeDown(x, y)) {
rtn.Nodes[y][x].NodeDown = rtn.NodeDown(x, y)
}
}
}

return rtn, nil
}

func AOC2022121(input string) (string, error) {
parsedMap, err := AOC202212ParseMap(input)
if err != nil {
return "", err
}
parsedMap.ParseDistances()
len := parsedMap.SolvePart1()
return fmt.Sprintf("%d", len), nil
}

func AOC2022122(input string) (string, error) {
parsedMap, err := AOC202212ParseMap(input)
if err != nil {
return "", err
}

aNodes := []*AOC202212Node{}
for y := range parsedMap.Nodes {
for x := range parsedMap.Nodes[y] {
if parsedMap.Nodes[y][x].Height == 'a' {
aNodes = append(aNodes, parsedMap.Nodes[y][x])
}
}
}

// Distance from S
parsedMap.ParseDistances()
startLen := parsedMap.SolvePart1()

for _, node := range aNodes {
parsedMap.NodeStart = node
parsedMap.ParseDistances()
nodeLen := parsedMap.SolvePart1()
if nodeLen == 0 {
continue
}
if nodeLen < startLen {
startLen = nodeLen
}
}

return fmt.Sprintf("%d", startLen), nil
}
53 changes: 53 additions & 0 deletions 12_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package main

import "testing"

func TestAOC202212Part1(t *testing.T) {
test := struct {
input string
posStart []int
posEnd []int
}{
input: `Sabqponm
abcryxxl
accszExk
acctuvwj
abdefghi`,
posStart: []int{0, 0},
posEnd: []int{5, 2},
}

parsedMap, err := AOC202212ParseMap(test.input)
if err != nil {
t.Fatalf("AOC202212ParseMap() err: %v", err)
}

nS := parsedMap.NodeStart
if nS.PosX != test.posStart[0] || nS.PosY != test.posStart[1] {
t.Errorf("mapStart: %d/%d, testStart: %+v", parsedMap.NodeStart.PosX, parsedMap.NodeStart.PosY, test.posStart)
}

nE := parsedMap.NodeEnd
if nE.PosX != test.posEnd[0] || nE.PosY != test.posEnd[1] {
t.Errorf("mapEnd: %d/%d, testEnd: %+v", parsedMap.NodeEnd.PosX, parsedMap.NodeEnd.PosY, test.posEnd)
}

//if parsedMap.Nodes[2][0].NodeRight != nil {
// t.Errorf("Node[2][0] unexpected right neighbor: %+v", parsedMap.Nodes[0][1].NodeRight)
//}

//if parsedMap.Nodes[0][0].NodeDown != parsedMap.Nodes[1][0] {
// t.Errorf("Node[0][0] unexpected missing downer neighbor: %+v", parsedMap.Nodes[0][0])
//}

//way, len := parsedMap.FindWay(parsedMap.NodeStart, []*AOC202212Node{})
//if len != 31 {
// t.Errorf("parsedMap.FindWay() unexpected len %d: Way: %s", len, way)
//}

parsedMap.ParseDistances()
len := parsedMap.SolvePart1()
if len != 31 {
t.Errorf("parsedMap.SolvePart1() differs:\nwant: %d\ngot: %d", 31, len)
}
}
Loading

0 comments on commit 1374e43

Please sign in to comment.