day16, example

This commit is contained in:
efim 2023-12-16 07:09:42 +00:00
parent d09a8af5db
commit cb973d60cc
3 changed files with 271 additions and 3 deletions

10
day16/example Normal file
View File

@ -0,0 +1,10 @@
.|...\....
|.-.\.....
.....|-...
........|.
..........
.........\
..../.\\..
.-.-/..|..
.|....-|.\
..//.|....

258
day16/floorWillBeLava.go Normal file
View File

@ -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
}

View File

@ -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)
}