146 lines
2.5 KiB
Go
146 lines
2.5 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"slices"
|
|
)
|
|
|
|
func main() {
|
|
debug := flag.Bool("debug", false, "Debug.")
|
|
flag.Parse()
|
|
|
|
f := os.Stdin
|
|
if flag.NArg() > 0 && flag.Arg(0) != "-" {
|
|
var err error
|
|
f, err = os.Open(flag.Arg(0))
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer f.Close()
|
|
}
|
|
|
|
diagram, err := readDiagram(f)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
splits := diagram.Propagate()
|
|
var timelines int
|
|
for _, row := range slices.Backward(diagram) {
|
|
for _, c := range row {
|
|
if c.Marker != BeamMarker {
|
|
continue
|
|
}
|
|
timelines += c.WaysToReach
|
|
}
|
|
break
|
|
}
|
|
|
|
if *debug {
|
|
for _, row := range diagram {
|
|
for _, c := range row {
|
|
if c.Marker == BeamMarker {
|
|
if c.WaysToReach < 10 {
|
|
fmt.Print(c.WaysToReach)
|
|
} else {
|
|
b := 'A' + c.WaysToReach - 10
|
|
if b > 'Z' {
|
|
b = int(c.Marker)
|
|
}
|
|
fmt.Printf("%c", b)
|
|
}
|
|
} else {
|
|
fmt.Printf("%c", c.Marker)
|
|
}
|
|
}
|
|
fmt.Println()
|
|
}
|
|
}
|
|
|
|
fmt.Println("beam split", splits, "times into", timelines, "timelines")
|
|
}
|
|
|
|
type Marker byte
|
|
|
|
const (
|
|
BeamEmitterMarker Marker = 'S'
|
|
BeamMarker Marker = '|'
|
|
SplitterMarker Marker = '^'
|
|
EmptyMarker Marker = '.'
|
|
)
|
|
|
|
type Cell struct {
|
|
Marker Marker
|
|
WaysToReach int
|
|
}
|
|
|
|
type diagram [][]Cell
|
|
|
|
func (d diagram) Propagate() (splits int) {
|
|
splitoff := func(x, y, dx int) {
|
|
x1 := x + dx
|
|
if x1 < 0 || x1 >= len(d[y]) {
|
|
return // out of range
|
|
}
|
|
switch d[y][x1].Marker {
|
|
case EmptyMarker:
|
|
d[y][x1] = Cell{
|
|
Marker: BeamMarker,
|
|
WaysToReach: d[y-1][x].WaysToReach,
|
|
}
|
|
case BeamMarker:
|
|
d[y][x1].WaysToReach += d[y-1][x].WaysToReach
|
|
}
|
|
}
|
|
|
|
propagateCell := func(x, y int) {
|
|
switch d[y-1][x].Marker {
|
|
case BeamEmitterMarker:
|
|
d[y-1][x].WaysToReach = 1
|
|
case BeamMarker:
|
|
default:
|
|
return
|
|
}
|
|
// we have a beam above
|
|
|
|
switch d[y][x].Marker {
|
|
case EmptyMarker:
|
|
d[y][x] = Cell{
|
|
Marker: BeamMarker,
|
|
WaysToReach: d[y-1][x].WaysToReach,
|
|
}
|
|
case BeamMarker:
|
|
d[y][x].WaysToReach += d[y-1][x].WaysToReach
|
|
case SplitterMarker:
|
|
splitoff(x, y, -1)
|
|
splitoff(x, y, +1)
|
|
splits++
|
|
}
|
|
}
|
|
|
|
for y := 1; y < len(d); y++ {
|
|
for x := range d[y] {
|
|
propagateCell(x, y)
|
|
}
|
|
}
|
|
return splits
|
|
}
|
|
|
|
func readDiagram(r io.Reader) (diagram, error) {
|
|
var diagram diagram
|
|
sc := bufio.NewScanner(r)
|
|
for sc.Scan() {
|
|
cells := make([]Cell, 0, len(sc.Bytes()))
|
|
for _, b := range sc.Bytes() {
|
|
cells = append(cells, Cell{Marker: Marker(b)})
|
|
}
|
|
diagram = append(diagram, cells)
|
|
}
|
|
return diagram, sc.Err()
|
|
}
|