From 44de1377caec9a395487a38aad8dbdd3f4470a9d Mon Sep 17 00:00:00 2001 From: efim Date: Sat, 23 Dec 2023 09:16:04 +0000 Subject: [PATCH] day23, part1 --- day23/aLongWalk.go | 22 +++++++++ day23/example | 23 +++++++++ day23/field.go | 112 ++++++++++++++++++++++++++++++++++++++++++++ day23/field_test.go | 31 ++++++++++++ day23/paths.go | 57 ++++++++++++++++++++++ day23/paths_test.go | 19 ++++++++ main.go | 6 +-- 7 files changed, 267 insertions(+), 3 deletions(-) create mode 100644 day23/aLongWalk.go create mode 100644 day23/example create mode 100644 day23/field.go create mode 100644 day23/field_test.go create mode 100644 day23/paths.go create mode 100644 day23/paths_test.go diff --git a/day23/aLongWalk.go b/day23/aLongWalk.go new file mode 100644 index 0000000..18dad7e --- /dev/null +++ b/day23/aLongWalk.go @@ -0,0 +1,22 @@ +package day23 + +import ( + "fmt" +) + +// length of longest scenic route +func Run() int { + fmt.Println("day 23") + filename := "day23/example" + field := ReadField(filename) + finalPaths := RunAllScenicPaths(field) + + max := 0 + for _, path := range finalPaths { + if path.visited.Cardinality() > max { + max = path.visited.Cardinality() + } + } + return max +} + diff --git a/day23/example b/day23/example new file mode 100644 index 0000000..ea945a4 --- /dev/null +++ b/day23/example @@ -0,0 +1,23 @@ +#.##################### +#.......#########...### +#######.#########.#.### +###.....#.>.>.###.#.### +###v#####.#v#.###.#.### +###.>...#.#.#.....#...# +###v###.#.#.#########.# +###...#.#.#.......#...# +#####.#.#.#######.#.### +#.....#.#.#.......#...# +#.#####.#.#.#########v# +#.#...#...#...###...>.# +#.#.#v#######v###.###v# +#...#.>.#...>.>.#.###.# +#####v#.#.###v#.#.###.# +#.....#...#...#.#.#...# +#.#########.###.#.#.### +#...###...#...#...#.### +###.###.#.###v#####v### +#...#...#.#.>.>.#.>.### +#.###.###.#.###.#.#v### +#.....###...###...#...# +#####################.# diff --git a/day23/field.go b/day23/field.go new file mode 100644 index 0000000..361bba8 --- /dev/null +++ b/day23/field.go @@ -0,0 +1,112 @@ +package day23 + +import ( + "fmt" + "os" + "strings" +) + +type Coord struct { + Row, Col int +} + +type CellType rune + +const ( + Path CellType = '.' + Tree CellType = '#' + SlideDown CellType = 'v' + SlideUp CellType = '^' + SlideLeft CellType = '<' + SlideRight CellType = '>' +) + +type Field struct { + MaxRow, MaxCol int + Cells map[Coord]CellType + StartCol, EndCol int +} + +func (f *Field) Neighbors(c Coord) (neighbors []Coord) { + symb, exists := f.Cells[c] + if !exists { + panic(fmt.Sprintf("coord %+v not found in field", c)) + } + + var coords []Coord + switch symb { + case Path: + coords = []Coord{ + {Row: c.Row + 1, Col: c.Col}, + {Row: c.Row - 1, Col: c.Col}, + {Row: c.Row, Col: c.Col + 1}, + {Row: c.Row, Col: c.Col - 1}, + } + case Tree: + panic(fmt.Sprintf("attempting to get neighbors of a tree at %+v", c)) + case SlideDown: + coords = []Coord{{Row: c.Row + 1, Col: c.Col}} + case SlideUp: + coords = []Coord{{Row: c.Row - 1, Col: c.Col}} + case SlideLeft: + coords = []Coord{{Row: c.Row, Col: c.Col - 1}} + case SlideRight: + coords = []Coord{{Row: c.Row, Col: c.Col + 1}} + } + + for _, coord := range coords { + neighborSymb, found := f.Cells[coord] + if !found || neighborSymb == Tree { + continue + } + neighbors = append(neighbors, coord) + } + return +} + +func (f *Field) String() (result string) { + result += "\n" + for row := 0; row <= f.MaxRow; row++ { + for col := 0; col <= f.MaxCol; col++ { + if row == 0 && col == f.StartCol { + result += "S" + continue + } + if row == f.MaxRow && col == f.EndCol { + result += "E" + continue + } + + symb := f.Cells[Coord{Row: row, Col: col}] + result += string(symb) + } + result += "\n" + } + return +} + +func ReadField(filename string) (result Field) { + bytes, err := os.ReadFile(filename) + if err != nil { + panic(err) + } + lines := strings.Split(strings.TrimSpace(string(bytes)), "\n") + result.MaxRow = len(lines) - 1 + result.MaxCol = len(lines[0]) - 1 + rows := make(map[Coord]CellType) + + for rowNum, row := range lines { + for colNum, symb := range row { + rows[Coord{Row: rowNum, Col: colNum}] = CellType(symb) + if rowNum == 0 && symb == rune(Path) { + result.StartCol = colNum + } + if rowNum == result.MaxRow && symb == rune(Path) { + result.EndCol = colNum + } + } + } + result.Cells = rows + + return +} diff --git a/day23/field_test.go b/day23/field_test.go new file mode 100644 index 0000000..725b966 --- /dev/null +++ b/day23/field_test.go @@ -0,0 +1,31 @@ +package day23 + +import "testing" + +func TestReadField(t *testing.T) { + filename := "example" + field := ReadField(filename) + t.Log(field.String()) +} + +func TestStartNeighbors(t *testing.T) { + filename := "example" + field := ReadField(filename) + startNeighbors := field.Neighbors(Coord{Row: 0, Col: field.StartCol}) + t.Log(startNeighbors) +} + +// 5,3 +func TestForkNeighbors(t *testing.T) { + filename := "example" + field := ReadField(filename) + startNeighbors := field.Neighbors(Coord{Row: 5, Col: 3}) + t.Log(startNeighbors) +} + +func TestSlideNeighbors(t *testing.T) { + filename := "example" + field := ReadField(filename) + startNeighbors := field.Neighbors(Coord{Row: 6, Col: 3}) + t.Log(startNeighbors) +} diff --git a/day23/paths.go b/day23/paths.go new file mode 100644 index 0000000..f502ea0 --- /dev/null +++ b/day23/paths.go @@ -0,0 +1,57 @@ +package day23 + +import ( + mapset "github.com/deckarep/golang-set/v2" +) + +type PathEnd struct { + end Coord + visited mapset.Set[Coord] +} + +func ExtendPath(p PathEnd, f Field) (nextPaths []PathEnd) { + endPointNeighbors := f.Neighbors(p.end) + for _, potentialNewEnd := range endPointNeighbors { + if !p.visited.Contains(potentialNewEnd) { + nextVisited := p.visited.Clone() + nextVisited.Add(p.end) + nextPaths = append(nextPaths, PathEnd{ + end: potentialNewEnd, + visited: nextVisited, + }) + } + } + + return +} + +// return paths that end on End +func RunAllScenicPaths(f Field) (result []PathEnd) { + pathsToFurther := []PathEnd{ + {end: Coord{Row: 0, Col: f.StartCol}, visited: mapset.NewSet[Coord]()}, + } + + theEndCoord := Coord{Row: f.MaxRow, Col: f.EndCol} + + for len(pathsToFurther) > 0 { + curCheckedPath := pathsToFurther[0] + pathsToFurther = pathsToFurther[1:] + + if curCheckedPath.end == theEndCoord { + result = append(result, curCheckedPath) + continue + } + + nextSteps := ExtendPath(curCheckedPath, f) + + // log.Printf("for %+v next steps %+v\n", curCheckedPath, pathsToFurther) + // log.Printf("remaining paths to check len is %d", len(pathsToFurther)) + // log.Println(pathsToFurther) + + if len(nextSteps) > 0 { + pathsToFurther = append(pathsToFurther, nextSteps...) + } + } + + return +} diff --git a/day23/paths_test.go b/day23/paths_test.go new file mode 100644 index 0000000..69a5eae --- /dev/null +++ b/day23/paths_test.go @@ -0,0 +1,19 @@ +package day23 + +import "testing" + +func TestRunAllPaths(t *testing.T) { + filename := "example" + field := ReadField(filename) + finalPaths := RunAllScenicPaths(field) + t.Log(finalPaths) + + max := 0 + for _, path := range finalPaths { + if path.visited.Cardinality() > max { + max = path.visited.Cardinality() + } + } + t.Logf("max path len is %d", max) + +} diff --git a/main.go b/main.go index e37738e..a95f887 100644 --- a/main.go +++ b/main.go @@ -4,15 +4,15 @@ import ( "log" "time" - "sunshine.industries/aoc2023/day22" + "sunshine.industries/aoc2023/day23" ) func main() { startTime := time.Now() log.Print("> starting run:") - result := day22.Run() - log.Printf("\n\nday22 result: %d\n****\n", result) + result := day23.Run() + log.Printf("\n\nday23 result: %d\n****\n", result) endTime := time.Now() diff := endTime.Sub(startTime) log.Printf("execution took %s", diff.String())