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