day10, All Is Broken

This commit is contained in:
efim
2023-12-10 14:47:40 +00:00
parent 0b6c521b5b
commit e4afe55a1f
3 changed files with 207 additions and 37 deletions

View File

@@ -12,7 +12,7 @@ import (
func Run() int {
fmt.Println("hello day 10")
// filename := "day10/example2noisy"
filename := "day10/input"
filename := "day10/example5"
fieldMap := Read(filename)
fmt.Println(fieldMap.BeastCoord)
// fmt.Println(fieldMap.String())
@@ -35,12 +35,15 @@ func Run() int {
}
}
fmt.Println("beore marking:")
// fmt.Println("beore marking:")
fieldMap.markMainLoop()
fmt.Println("after marking:")
fmt.Println(fieldMap.String())
// fmt.Println("after marking loop:")
// fmt.Println(fieldMap.String())
fmt.Println("beore marking closest Outer:")
// now main loop is closed with regards to 'S' neighbors
fieldMap.initialMarkOuter()
fmt.Println("after marking closest Outer:")
fmt.Println(fieldMap.String())
return (len / 2) + (len % 2)
}
@@ -58,52 +61,72 @@ func Run() int {
// cell would map 'from' to 'to'
type Cell struct {
Coord Coord
Tile rune
Neighbords []Coord
Coord Coord
Tile rune
Neighbords []Coord
IsOnMainPath bool
IsOuter bool
}
func (c *Cell)String() string {
func (c *Cell) String() string {
if c.Tile == 'S' {
return "S"
}
if c.IsOuter {
return "O"
}
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)
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 {
func (c Coord) Equal(other Coord) bool {
return c.X == other.X && c.Y == other.Y
}
type Direction int
const (UP Direction = iota
const (
UP Direction = iota
DOWN
LEFT
RIGHT)
RIGHT
)
func (d Direction)String() string {
names := []string{"UP", "DOWN", "LEFT", "RIGHT"}
return names[d]
}
func (c Coord)Shift(d Direction) Coord {
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}
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
}
@@ -113,7 +136,8 @@ type Map struct {
Height, Width int
BeastCoord Coord
}
func (m *Map)String() string {
func (m *Map) String() string {
result := ""
for y := 0; y < m.Height; y++ {
for x := 0; x < m.Width; x++ {
@@ -125,7 +149,7 @@ func (m *Map)String() string {
return result
}
func (m *Map)markMainLoop() {
func (m *Map) markMainLoop() {
start := m.Cells[m.BeastCoord]
start.IsOnMainPath = true
m.Cells[m.BeastCoord] = start
@@ -136,7 +160,7 @@ func (m *Map)markMainLoop() {
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)
nextCoord, _, err := currentCell.Next(previous.Coord)
// log.Printf("next coord will be %v %s\n", nextCoord, err)
if err != nil {
return
@@ -146,9 +170,114 @@ func (m *Map)markMainLoop() {
}
}
func (m *Map) initialMarkOuter() {
// for start point let's take my highest on main path and one above
// and will have a runner pointer to the cell on the outside
var outerRunner Cell
var pathCunner Cell
outer:
for y := 0; y < m.Height; y++ {
for x := 0; x < m.Width; x++ {
if cell := m.Cells[Coord{x, y}]; cell.IsOnMainPath {
pathCunner = cell
outerRunner = m.Cells[Coord{x, y - 1}]
break outer
}
}
}
startPoint := pathCunner
previous := startPoint
firstDirection := startPoint.OutDirections()[0]
nextCoord := previous.Coord.Shift(firstDirection)
currentCell := m.Cells[nextCoord]
var exitingPreviousBy Direction = firstDirection
stepsToDo := 1
for currentCell.Coord != startPoint.Coord {
// looping once. and need to operae on the outer runner
// and i don't have the direction? well, i guess i could use direction
outerRunner = m.markOuterAndMove(previous, outerRunner, exitingPreviousBy)
var err error
nextCoord, exitingPreviousBy, err = currentCell.Next(previous.Coord)
if err != nil {
panic("initial mark cycle can't get next")
}
previous = currentCell
currentCell = m.Cells[nextCoord]
stepsToDo -= 1
if stepsToDo == 0 {
break
}
}
}
func (m *Map) markOuter(outerPointerCoord Coord) {
if !m.isValidCoord(outerPointerCoord) {
return
}
outerPointer := m.Cells[outerPointerCoord]
if outerPointer.IsOnMainPath {
return
}
outerPointer.IsOuter = true
m.Cells[outerPointer.Coord] = outerPointer
}
// move both inner path Cell and OuterCell through direction from inner path cell
// and i need to know direction from which we came into 'pathPointer'
func (m *Map) markOuterAndMove(pathPointer Cell, outerPointer Cell, exitingCurrentBy Direction) Cell {
// mark &save outer, get moves from pathPointer & direct
// do 1 or 2 moves and on each mark & save
m.markOuter(outerPointer.Coord)
outerPointerMovements := outerPointerMovements[pathPointer.Tile][exitingCurrentBy]
log.Printf("moving outer from %s exited via %s with moves %+v\n", pathPointer.String(), exitingCurrentBy.String(), outerPointerMovements)
coord := outerPointer.Coord
for _, movement := range outerPointerMovements {
coord = coord.Shift(movement)
m.markOuter(coord)
}
newPointer := m.Cells[coord]
return newPointer
}
// yeah, this is not enough. if we move down from | we could be directly up to -
// so we need TurnLeft & TurnRight things
var outerPointerMovements map[rune]map[Direction][]Direction = map[rune]map[Direction][]Direction{
'|': {
UP: {UP},
DOWN: {DOWN},
},
'-': {
LEFT: {LEFT},
RIGHT: {RIGHT},
},
'L': {
DOWN: {DOWN, RIGHT},
LEFT: {RIGHT, UP},
},
'J': {
DOWN: {DOWN, LEFT},
RIGHT: {RIGHT, UP},
},
'F': {
DOWN: {LEFT, DOWN},
RIGHT: {UP, RIGHT},
},
'7': {
RIGHT: {RIGHT, DOWN},
UP: {UP, LEFT},
},
}
// 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) {
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]
@@ -157,7 +286,7 @@ func (m *Map)checkDirectionFromBeast(through Coord) (isCycle bool, len int) {
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)
nextCoord, _, err := currentCell.Next(previous.Coord)
// log.Printf("next coord will be %v %s\n", nextCoord, err)
if err != nil {
return
@@ -178,7 +307,7 @@ func (m *Map)checkDirectionFromBeast(through Coord) (isCycle bool, len int) {
return
}
func (m *Map)isValidCoord(c Coord) bool {
func (m *Map) isValidCoord(c Coord) bool {
if c.X < 0 || c.Y < 0 || c.X >= m.Height || c.Y >= m.Width {
return false
}
@@ -228,18 +357,25 @@ func (c *Cell) GetNeighbors() []Coord {
// - 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) {
func (c *Cell) Next(from Coord) (to Coord, cameFrom Direction, 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))
return Coord{}, 0, 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))
return Coord{}, 0, errors.New(fmt.Sprintf("cannot find next from %v through %+v", from, c))
}
var nextDirection Direction
for _, direction := range c.OutDirections() {
if c.Coord.Shift(direction) != from {
nextDirection = direction
}
}
otherIndex := 1 - i
return c.Neighbords[otherIndex], nil
return c.Neighbords[otherIndex], nextDirection, nil
}
// x from left to right; y from top to bottom