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 (
|
||||
"log"
|
||||
|
||||
"sunshine.industries/aoc2023/day15"
|
||||
"sunshine.industries/aoc2023/day16"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.Print("> starting run:")
|
||||
|
||||
result := day15.Run()
|
||||
log.Printf("day15 result: %d\n****\n", result)
|
||||
result := day16.Run()
|
||||
log.Printf("\n\nday16 result: %d\n****\n", result)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue