day16, example
This commit is contained in:
parent
d09a8af5db
commit
cb973d60cc
|
@ -0,0 +1,10 @@
|
||||||
|
.|...\....
|
||||||
|
|.-.\.....
|
||||||
|
.....|-...
|
||||||
|
........|.
|
||||||
|
..........
|
||||||
|
.........\
|
||||||
|
..../.\\..
|
||||||
|
.-.-/..|..
|
||||||
|
.|....-|.\
|
||||||
|
..//.|....
|
|
@ -0,0 +1,258 @@
|
||||||
|
package day16
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Run() int {
|
||||||
|
fmt.Println("hello from day 16")
|
||||||
|
log.Println("starting")
|
||||||
|
field := ReadField("day16/example")
|
||||||
|
fmt.Println(field.String())
|
||||||
|
field.StartTraversal()
|
||||||
|
|
||||||
|
fmt.Println(field.ShowEnergyzed())
|
||||||
|
|
||||||
|
return field.CountEnergized()
|
||||||
|
}
|
||||||
|
|
||||||
|
// have shared field
|
||||||
|
// running traversal recursive function per ray
|
||||||
|
// exit if going out of field or visiting cell that already had light in this direction
|
||||||
|
// (i.e encountering loop)
|
||||||
|
|
||||||
|
type CellType rune
|
||||||
|
|
||||||
|
const (
|
||||||
|
Empty CellType = '.'
|
||||||
|
SplitterNS = '|'
|
||||||
|
SplitterEW = '-'
|
||||||
|
MirrorBackslash = '\\'
|
||||||
|
MirrorSlash = '/'
|
||||||
|
)
|
||||||
|
|
||||||
|
type Direction int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Upward Direction = iota
|
||||||
|
Downward
|
||||||
|
Leftward
|
||||||
|
Rightward
|
||||||
|
)
|
||||||
|
|
||||||
|
type Cell struct {
|
||||||
|
CellType CellType
|
||||||
|
KnownBeams map[Direction]any
|
||||||
|
}
|
||||||
|
|
||||||
|
type Field struct {
|
||||||
|
cells [][]*Cell
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Field)isValid(mp MovementPoint) bool {
|
||||||
|
if mp.Row < 0 || mp.Col < 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if mp.Row >= len(f.cells) || len(f.cells) == 0 || mp.Col >= len(f.cells[0]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadField(filename string) Field {
|
||||||
|
result := Field{}
|
||||||
|
bytes, err := os.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprint("cannot read file: ", filename))
|
||||||
|
}
|
||||||
|
text := string(bytes)
|
||||||
|
text = strings.TrimSpace(text)
|
||||||
|
for _, line := range strings.Split(text, "\n") {
|
||||||
|
rowCells := make([]*Cell, 0)
|
||||||
|
for _, symb := range line {
|
||||||
|
rowCells = append(rowCells, &Cell{
|
||||||
|
CellType: CellType(symb),
|
||||||
|
KnownBeams: make(map[Direction]any),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
result.cells = append(result.cells, rowCells)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Field)String() string {
|
||||||
|
result := "\n"
|
||||||
|
for _, row := range f.cells {
|
||||||
|
for _, cell := range row {
|
||||||
|
result += string(cell.CellType)
|
||||||
|
}
|
||||||
|
result += "\n"
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Field)ShowEnergyzed() string {
|
||||||
|
result := "\n"
|
||||||
|
for _, row := range f.cells {
|
||||||
|
for _, cell := range row {
|
||||||
|
if len(cell.KnownBeams) > 0 {
|
||||||
|
result += "#"
|
||||||
|
} else {
|
||||||
|
result += string(cell.CellType)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
result += "\n"
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type MovementPoint struct {
|
||||||
|
Row, Col int
|
||||||
|
Direction Direction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Field)StartTraversal() {
|
||||||
|
reportedVisits := make(chan MovementPoint)
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
go f.RecordVisits(reportedVisits)
|
||||||
|
startPoint := MovementPoint {
|
||||||
|
Row: 0, Col: 0, Direction: Rightward,
|
||||||
|
}
|
||||||
|
wg.Add(1)
|
||||||
|
go f.TraverseFrom(startPoint, reportedVisits, &wg)
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
close(reportedVisits)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Field)CountEnergized() (result int) {
|
||||||
|
for _, row := range f.cells {
|
||||||
|
for _, cell := range row {
|
||||||
|
if len(cell.KnownBeams) > 0 {
|
||||||
|
result += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Field)RecordVisits(reportedPoints <-chan MovementPoint) {
|
||||||
|
for point := range reportedPoints {
|
||||||
|
cell := f.cells[point.Row][point.Col]
|
||||||
|
log.Printf("recording visit %+v to %+v at row %d col %d\n", point, cell, point.Row, point.Col)
|
||||||
|
cell.KnownBeams[point.Direction] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// starting at point, mark as visited
|
||||||
|
// move (concurrently if required) into next points
|
||||||
|
// ends - when out of the field, or if encountering a cycle
|
||||||
|
func (f *Field)TraverseFrom(current MovementPoint, reportVisits chan<- MovementPoint, wg *sync.WaitGroup) {
|
||||||
|
log.Printf("> starting traverse through %+v", current)
|
||||||
|
if !f.isValid(current) {
|
||||||
|
log.Println("invalid current ", current, " should be impossible")
|
||||||
|
wg.Done()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cell := f.cells[current.Row][current.Col]
|
||||||
|
_, knownDirection := cell.KnownBeams[current.Direction]
|
||||||
|
if knownDirection {
|
||||||
|
log.Printf("found cycle at %+v in %+v", current, cell)
|
||||||
|
wg.Done()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
reportVisits <- current
|
||||||
|
|
||||||
|
nextPoints := NextPoints(f, current)
|
||||||
|
log.Printf("for current %+v next are: %+v\n", current, nextPoints)
|
||||||
|
switch len(nextPoints) {
|
||||||
|
case 0:
|
||||||
|
wg.Done()
|
||||||
|
return
|
||||||
|
case 1:
|
||||||
|
f.TraverseFrom(nextPoints[0], reportVisits, wg)
|
||||||
|
return
|
||||||
|
case 2:
|
||||||
|
wg.Add(1)
|
||||||
|
go f.TraverseFrom(nextPoints[0], reportVisits, wg)
|
||||||
|
f.TraverseFrom(nextPoints[1], reportVisits, wg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func NextPoints(f *Field, current MovementPoint) []MovementPoint {
|
||||||
|
cell := f.cells[current.Row][current.Col]
|
||||||
|
nextDirections := cell.CellType.NextDirections(current.Direction)
|
||||||
|
nextCells := make([]MovementPoint, 0)
|
||||||
|
for _, direction := range nextDirections {
|
||||||
|
nextMovementPoint := current.ApplyDirection(direction)
|
||||||
|
if f.isValid(nextMovementPoint) {
|
||||||
|
nextCells = append(nextCells, nextMovementPoint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nextCells
|
||||||
|
}
|
||||||
|
|
||||||
|
// value receiver, can safely modify incoming mp
|
||||||
|
// doesn't know about Field dimentions
|
||||||
|
func (mp MovementPoint)ApplyDirection(d Direction) MovementPoint {
|
||||||
|
switch d {
|
||||||
|
case Upward:
|
||||||
|
mp.Row -= 1
|
||||||
|
case Downward:
|
||||||
|
mp.Row += 1
|
||||||
|
case Leftward:
|
||||||
|
mp.Col -= 1
|
||||||
|
case Rightward:
|
||||||
|
mp.Col += 1
|
||||||
|
}
|
||||||
|
mp.Direction = d
|
||||||
|
return mp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ct CellType) NextDirections(currentDirection Direction) (nextDirections []Direction) {
|
||||||
|
switch ct {
|
||||||
|
case Empty:
|
||||||
|
nextDirections = []Direction{currentDirection}
|
||||||
|
case SplitterNS:
|
||||||
|
if currentDirection == Rightward || currentDirection == Leftward {
|
||||||
|
nextDirections = []Direction{Upward, Downward}
|
||||||
|
} else {
|
||||||
|
nextDirections = []Direction{currentDirection}
|
||||||
|
}
|
||||||
|
case SplitterEW:
|
||||||
|
if currentDirection == Downward || currentDirection == Upward {
|
||||||
|
nextDirections = []Direction{Leftward, Rightward}
|
||||||
|
} else {
|
||||||
|
nextDirections = []Direction{currentDirection}
|
||||||
|
}
|
||||||
|
case MirrorBackslash:
|
||||||
|
// mirror symbol is \
|
||||||
|
directionMappings := map[Direction]Direction{
|
||||||
|
Leftward: Upward,
|
||||||
|
Rightward: Downward,
|
||||||
|
Upward: Leftward,
|
||||||
|
Downward: Rightward,
|
||||||
|
}
|
||||||
|
nextDirections = []Direction{directionMappings[currentDirection]}
|
||||||
|
case MirrorSlash:
|
||||||
|
// mirrow symbol is /
|
||||||
|
directionMappings := map[Direction]Direction{
|
||||||
|
Leftward: Downward,
|
||||||
|
Rightward: Upward,
|
||||||
|
Upward: Rightward,
|
||||||
|
Downward: Leftward,
|
||||||
|
}
|
||||||
|
nextDirections = []Direction{directionMappings[currentDirection]}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
6
main.go
6
main.go
|
@ -3,12 +3,12 @@ package main
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"sunshine.industries/aoc2023/day15"
|
"sunshine.industries/aoc2023/day16"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
log.Print("> starting run:")
|
log.Print("> starting run:")
|
||||||
|
|
||||||
result := day15.Run()
|
result := day16.Run()
|
||||||
log.Printf("day15 result: %d\n****\n", result)
|
log.Printf("\n\nday16 result: %d\n****\n", result)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue