package day10 import ( "errors" "fmt" "log" "os" "slices" "strings" ) func Run() int { fmt.Println("hello day 10") // filename := "day10/example2noisy" filename := "day10/input" fieldMap := Read(filename) fmt.Println(fieldMap.BeastCoord) // fmt.Println(fieldMap.String()) // fmt.Printf("%+v\n", fieldMap.Cells) // log.Printf(">> does Equals work? {1,2} == {1,2} is %t\n", (Coord{1,2} == Coord{1,2})) // log.Printf(">> does Index work? {1,2} in [{2,2}, {1,2}] is %d \n", slices.Index([]Coord{{2,2}, {1,2}}, Coord{1,2})) // fieldMap.checkDirectionFromBeast(Coord{1,2}) beastNeighborCoords := fieldMap.Cells[fieldMap.BeastCoord].Neighbords len := 0 for _, coord := range beastNeighborCoords { log.Printf("checking neighbor %v\n", coord) isCycle, curLen := fieldMap.checkDirectionFromBeast(coord) if isCycle { log.Printf("found cycle through %v\n", coord) len = curLen break } } fmt.Println("beore marking:") fieldMap.markMainLoop() fmt.Println("after marking:") fmt.Println(fieldMap.String()) // now main loop is closed with regards to 'S' neighbors return (len / 2) + (len % 2) } // so do i work with just [][]rune ? // func Next(from Coord, through Coord) (Coord, error) ? // and here check that 'from' has exit into 'through' // and check that 'through' has entrance from 'from' // so, i guess i could do 'exit direction' and 'entrance direction' // then compare 'exit direction' with what's available on 'from' // // or i can just have function 'canExit(from, to Coord)' and canEnter(from, to Coord) // i suppose it would be nice to just create Cell(Coord, Type) and // cell would map 'from' to 'to' type Cell struct { Coord Coord Tile rune Neighbords []Coord IsOnMainPath bool } func (c *Cell)String() string { if c.Tile == 'S' { return "S" } if !c.IsOnMainPath { return " " } switch c.Tile { case '7': return "⌝" case 'J': return "⌟" case 'F': return "⌜" case 'L': return "⌞" case '.': return " " default: return string(c.Tile) } } type Coord struct { X, Y int } func (c Coord)Equal(other Coord) bool { return c.X == other.X && c.Y == other.Y } type Direction int const (UP Direction = iota DOWN LEFT RIGHT) func (c Coord)Shift(d Direction) Coord { x, y := c.X, c.Y result := Coord{} switch d { case UP: result = Coord{x, y-1} case DOWN: result = Coord{x, y+1} case LEFT: result = Coord{x-1, y} case RIGHT: result = Coord{x+1, y} } return result } type Map struct { Cells map[Coord]Cell Height, Width int BeastCoord Coord } func (m *Map)String() string { result := "" for y := 0; y < m.Height; y++ { for x := 0; x < m.Width; x++ { cell := m.Cells[Coord{x, y}] result += cell.String() } result += "\n" } return result } func (m *Map)markMainLoop() { start := m.Cells[m.BeastCoord] start.IsOnMainPath = true m.Cells[m.BeastCoord] = start previous := start currentCell := m.Cells[previous.Neighbords[0]] // log.Printf("starting marking of main loop from %+v through %+v\n", start, currentCell) for currentCell.Tile != 'S' { currentCell.IsOnMainPath = true m.Cells[currentCell.Coord] = currentCell // log.Printf("marking loop on %+v (%s)\n", currentCell, currentCell.String()) nextCoord, err := currentCell.Next(previous.Coord) // log.Printf("next coord will be %v %s\n", nextCoord, err) if err != nil { return } previous = currentCell currentCell = m.Cells[nextCoord] } } // call for each direction from beast. // will run the path until it loops back at best, or terminates func (m *Map)checkDirectionFromBeast(through Coord) (isCycle bool, len int) { // defer log.Printf("about to return check from beast %v, isCycle : %t. len is %d", through, isCycle, len) len = 1 previous := m.Cells[m.BeastCoord] currentCell, found := m.Cells[through] // log.Printf("check direction init for %+v\n", currentCell) for found && currentCell.Tile != 'S' { // log.Printf("check direction loop for %+v (%s)\n", currentCell, currentCell.String()) len += 1 nextCoord, err := currentCell.Next(previous.Coord) // log.Printf("next coord will be %v %s\n", nextCoord, err) if err != nil { return } previous = currentCell currentCell, found = m.Cells[nextCoord] } if currentCell.Tile == 'S' { // log.Printf("found cycle, len is %d\n", len) isCycle = true // let's close the loop now. beastCell := m.Cells[m.BeastCoord] beastCell.Neighbords = []Coord{previous.Coord, through} m.Cells[m.BeastCoord] = beastCell // log.Printf("cells are not %+v", m.Cells) } return } func (m *Map)isValidCoord(c Coord) bool { if c.X < 0 || c.Y < 0 || c.X >= m.Height || c.Y >= m.Width { return false } return true } func Read(filename string) Map { result := Map{} bytes, err := os.ReadFile(filename) if err != nil { panic(fmt.Sprint("cannot read file ", filename)) } lines := strings.Split(string(bytes), "\n") result.Height = len(lines) result.Width = len(lines[0]) result.Cells = map[Coord]Cell{} for y, line := range lines { for x, symb := range line { coord := Coord{X: x, Y: y} if symb == 'S' { result.BeastCoord = coord } cell := Cell{ Coord: coord, Tile: symb, } cell.Neighbords = cell.GetNeighbors() result.Cells[coord] = cell } } return result } func (c *Cell) GetNeighbors() []Coord { result := make([]Coord, 0) for _, direction := range c.OutDirections() { result = append(result, c.Coord.Shift(direction)) } return result } // doesn't check whether 'from' has exit into c // only whether c can accept conntion from that direction // - check if 'from' is in neighbors // if it is - then take another neighbor // wouldn't work for 'S' but we don't need it to func (c *Cell) Next(from Coord) (to Coord, err error) { if len(c.Neighbords) != 2 { return Coord{}, errors.New(fmt.Sprintf("not 2 neighbors: cannot get next from %v through %c", from, c)) } i := slices.Index(c.Neighbords, from) if i == -1 { return Coord{}, errors.New(fmt.Sprintf("cannot find next from %v through %+v", from, c)) } otherIndex := 1 - i return c.Neighbords[otherIndex], nil } // x from left to right; y from top to bottom func (c *Cell) OutDirections() []Direction { switch c.Tile { case '|': return []Direction{UP, DOWN} case '-': return []Direction{LEFT, RIGHT} case 'L': return []Direction{UP, RIGHT} case 'J': return []Direction{UP, LEFT} case 'F': return []Direction{RIGHT, DOWN} case '7': return []Direction{LEFT, DOWN} case 'S': // all return []Direction{UP, DOWN, LEFT, RIGHT} default: return []Direction{} } }