From 3da1a03b345c30415e6677055a9ec78d3f2830f6 Mon Sep 17 00:00:00 2001 From: oabrivard Date: Tue, 19 Dec 2023 00:50:23 +0100 Subject: [PATCH] Completed both parts of Day 18 puzzle with help for Part 2 (pointers to Shoelace algo and to Pick's theorem --- day18/day18.go | 516 +++++++++++++++++++++++++++++++++++++ day18/day18_test.go | 116 +++++++++ day18/input.txt | 606 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1238 insertions(+) create mode 100644 day18/day18.go create mode 100644 day18/day18_test.go create mode 100644 day18/input.txt diff --git a/day18/day18.go b/day18/day18.go new file mode 100644 index 0000000..920f2bd --- /dev/null +++ b/day18/day18.go @@ -0,0 +1,516 @@ +package day18 + +import ( + "fmt" + "math" + "strconv" + "strings" +) + +func matrixSize(lines []string) (int, int, int, int) { + currRow, currCol := 0, 0 + maxRow, maxCol := 0, 0 + minRow, minCol := math.MaxInt32, math.MaxInt32 + + for _, line := range lines { + fields := strings.Fields(line) + dir := fields[0][0] + dist, _ := strconv.Atoi(fields[1]) + + switch dir { + case 'R': + currCol += dist + case 'L': + currCol -= dist + case 'D': + currRow += dist + case 'U': + currRow -= dist + } + + if currCol < minCol { + minCol = currCol + } + + if currRow < minRow { + minRow = currRow + } + + if currCol > maxCol { + maxCol = currCol + } + + if currRow > maxRow { + maxRow = currRow + } + } + + fmt.Println("(", minRow, ",", minCol, ") - (", maxRow, ",", maxCol, ")") + return minCol, minRow, maxRow, maxCol +} + +func floodFill(matrix [][]int64, row int, col int, prevColor int64, newColor int64, height int, width int) { + if row < 0 || row >= height || col < 0 || col >= width { + return + } + + if matrix[row][col] != prevColor { + return + } + + if matrix[row][col] == newColor { + return + } + + matrix[row][col] = newColor + + floodFill(matrix, row+1, col, prevColor, newColor, height, width) + floodFill(matrix, row-1, col, prevColor, newColor, height, width) + floodFill(matrix, row, col+1, prevColor, newColor, height, width) + floodFill(matrix, row, col-1, prevColor, newColor, height, width) +} + +func Part1(lines []string, printMatrix bool) int64 { + + minCol, minRow, maxRow, maxCol := matrixSize(lines) + height, width := maxRow-minRow+1, maxCol-minCol+1 + + matrix := make([][]int64, height) + + for i := range matrix { + matrix[i] = make([]int64, width) + } + + dRow := minRow * -1 + dCol := minCol * -1 + + currRow := dRow + currCol := dCol + + // dig path + for _, line := range lines { + fields := strings.Fields(line) + dir := fields[0][0] + dist, _ := strconv.Atoi(fields[1]) + color, _ := strconv.ParseInt(fields[2][2:len(fields[2])-3], 16, 64) + + switch dir { + case 'R': + for col := 1; col <= dist; col++ { + matrix[currRow][currCol+col] = color + } + currCol += dist + case 'L': + for col := 1; col <= dist; col++ { + matrix[currRow][currCol-col] = color + } + currCol -= dist + case 'D': + for row := 1; row <= dist; row++ { + matrix[currRow+row][currCol] = color + } + currRow += dist + case 'U': + for row := 1; row <= dist; row++ { + matrix[currRow-row][currCol] = color + } + currRow -= dist + } + + } + + floodFill(matrix, dRow+1, dCol+1, 0, 1, height, width) + + //print matrix + if printMatrix { + for _, l := range matrix { + for _, c := range l { + v := 0 + if c > 0 { + v = 1 + } + fmt.Print(v, " ") + } + fmt.Println("") + } + } + + result := int64(0) + for _, l := range matrix { + for _, c := range l { + if c > 0 { + result += 1 + } + } + } + + return result +} + +/* +type Fraction struct { + num int64 + den int64 +} + +type ETentry struct { + yMin int64 + yMax int64 + x_of_yMin int64 + inv_slope Fraction +} + +type AETentry struct { + y int64 + yMax int64 + x int64 + inv_slope Fraction +} + +func buildEdgeTable(lines []string) []*ETentry { + result := []*ETentry{} + + currCol, currRow := int64(0), int64(0) + for _, line := range lines { + fields := strings.Fields(line) + dist, _ := strconv.ParseInt(fields[2][2:7], 16, 64) + dir := fields[2][7:8] + + prevCol, prevRow := currCol, currRow + + switch dir { + case "0": + currCol += dist + case "2": + currCol -= dist + case "1": + currRow += dist + case "3": + currRow -= dist + } + + et := ETentry{} + if currRow < prevRow { + et.yMin = currRow + et.yMax = prevRow + et.x_of_yMin = currCol + } else { + et.yMin = prevRow + et.yMax = currRow + et.x_of_yMin = prevCol + } + et.inv_slope = Fraction{ + num: currCol - prevCol, + den: currRow - prevRow, + } + + result = append(result, &et) + } + + slices.SortFunc(result, func(a, b *ETentry) int { + if a.yMin < b.yMin { + return -1 + } else if a.yMin > b.yMin { + return 1 + } else { + return 0 + } + }) + + return result +} + +// Function to implement scan-line polygon filling +func scanFill2(x, y []int64, edges int64, matrix [][]int64) int64 { + result := int64(0) + var i, j int64 + xmin := int64(math.MaxInt64) + xmax := int64(0) + + // Find the minimum and maximum x-coordinates of the polygon + for i = 0; i < edges; i++ { + if x[i] < xmin { + xmin = x[i] + } + if x[i] > xmax { + xmax = x[i] + } + } + + // Scan each scan-line within the polygon's vertical extent + for i = xmin; i <= xmax; i++ { + interPoints := make([]int64, 0, edges) + count := int64(0) + + for j = 0; j < edges; j++ { + next := (j + 1) % edges + + // Check if the current edge intersects with the scan line + if (y[j] > i && y[next] <= i) || (y[next] > i && y[j] <= i) { + interPoint := x[j] + (i-y[j])*(x[next]-x[j])/(y[next]-y[j]) + interPoints = append(interPoints, interPoint) + count++ + } + } + + // Sort the intersection points in ascending order + sort.Slice(interPoints, func(i, j int) bool { return i < j }) + + // Fill the pixels between pairs of intersection points + for j = 0; j < count; j += 2 { + //line(interPoints[j], i, interPoints[j+1], i) + for r := interPoints[j] + 1; r < interPoints[j+1]; r++ { + matrix[r][i] = 1 + } + line := interPoints[j+1] - interPoints[j] + 1 + result += line + } + } + + return result +} + +// Edge represents an edge of the polygon +type Edge struct { + x1, y1, x2, y2 int64 +} + +// NewEdge creates a new Edge given two points +func NewEdge(x1, y1, x2, y2 int64) Edge { + if y1 > y2 { + return Edge{x1, y1, x2, y2} + } + return Edge{x2, y2, x1, y1} +} + +// scanLineFill performs the scanline polygon fill algorithm +func scanLineFill(x, y []int64, matrix [][]int64) { + edges := make([]Edge, len(x)) + for i := 0; i < len(x); i++ { + nextIndex := (i + 1) % len(x) + edges[i] = NewEdge(x[i], y[i], x[nextIndex], y[nextIndex]) + } + + minY := edges[0].y2 + maxY := edges[0].y1 + for _, e := range edges { + if e.y1 > maxY { + maxY = e.y1 + } + if e.y2 < minY { + minY = e.y2 + } + } + + for y := minY; y <= maxY; y++ { + var xIntersections []int64 + for _, e := range edges { + if y > e.y2 && y <= e.y1 { + if e.y1 != e.y2 { + xIntersect := e.x2 + (y-e.y2)*(e.x1-e.x2)/(e.y1-e.y2) + xIntersections = append(xIntersections, xIntersect) + } + } + } + slices.Sort(xIntersections) + for i := 0; i < len(xIntersections); i += 2 { + if i+1 < len(xIntersections) { + for x := xIntersections[i]; x < xIntersections[i+1]; x++ { + matrix[x][y] = 1 + } + } + } + } +} + +type Point struct { + X, Y int64 +} + +// Edge represents an edge of a polygon +type Edge2 struct { + ymax, xofymin, slopeInverse float64 +} + +// Function to fill a polygon using the scanline algorithm +func ScanlineFill(matrix [][]int, polygon []Point, fillCol int) { + if len(polygon) < 3 { + return + } + + // Function to create edges from the polygon vertices + createEdges := func(polygon []Point) []Edge2 { + var edges []Edge2 + for i := 0; i < len(polygon); i++ { + p1 := polygon[i] + p2 := polygon[(i+1)%len(polygon)] + if p1.Y == p2.Y { + continue // Skip horizontal edges + } + + edge := Edge2{} + if p1.Y < p2.Y { + edge.ymax = float64(p2.Y) + edge.xofymin = float64(p1.X) + edge.slopeInverse = float64(p2.X-p1.X) / float64(p2.Y-p1.Y) + } else { + edge.ymax = float64(p1.Y) + edge.xofymin = float64(p2.X) + edge.slopeInverse = float64(p1.X-p2.X) / float64(p1.Y-p2.Y) + } + edges = append(edges, edge) + } + return edges + } + + edges := createEdges(polygon) + + // Find the min and max Y values + minY := polygon[0].Y + maxY := polygon[0].Y + for _, p := range polygon { + if p.Y < minY { + minY = p.Y + } + if p.Y > maxY { + maxY = p.Y + } + } + + // Initialize Active Edge Table (AET) + var AET []Edge2 + + // Iterate through each scanline + for y := minY; y <= maxY; y++ { + // Remove edges where ymax is the current y + for i := 0; i < len(AET); i++ { + if AET[i].ymax == float64(y) { + AET = append(AET[:i], AET[i+1:]...) + i-- + } + } + + // Add edges where ymin is the current y + for _, e := range edges { + if e.xofymin == float64(y) { + AET = append(AET, e) + } + } + + // Sort AET on xofymin + sort.Slice(AET, func(i, j int) bool { + return AET[i].xofymin < AET[j].xofymin + }) + + // Fill pixels between pairs of intersections + for i := 0; i < len(AET); i += 2 { + if len(AET) > 1 { + for x := AET[i].xofymin; x <= AET[i+1].xofymin; x++ { + //img.Set(int(x), y, fillCol) + matrix[int(x)][y] = fillCol + } + + } + } + + // Update xofymin for edges in AET + for i := range AET { + AET[i].xofymin += AET[i].slopeInverse + } + } +} +*/ + +type Point struct { + X, Y float64 +} + +// Function to calculate the area of a polygon using the Shoelace algorithm +func ShoelaceArea(polygon []Point) float64 { + if len(polygon) < 3 { + return 0.0 // Not a polygon + } + + var area float64 = 0.0 + j := len(polygon) - 1 // The last vertex is the 'previous' one to the first + + for i := 0; i < len(polygon); i++ { + area += (polygon[j].X + polygon[i].X) * (polygon[j].Y - polygon[i].Y) + j = i // j is previous vertex to i + } + + return 0.5 * abs(area) +} + +// Helper function to calculate the absolute value +func abs(x float64) float64 { + if x < 0 { + return -x + } + return x +} + +func Part2(lines []string) int64 { + minCol, minRow, maxRow, maxCol := matrixSize(lines) + height, width := maxRow-minRow+1, maxCol-minCol+1 + + matrix := make([][]int, height) + + for i := range matrix { + matrix[i] = make([]int, width) + } + + x := []int64{} + y := []int64{} + points := []Point{} + + currCol, currRow := int64(0), int64(0) + x = append(x, currRow) + y = append(y, currCol) + points = append(points, Point{ + X: float64(currRow), + Y: float64(currCol), + }) + edges := int64(0) + totalDig := int64(0) + for _, line := range lines { + fields := strings.Fields(line) + dist, _ := strconv.ParseInt(fields[2][2:7], 16, 64) + dir := fields[2][7:8] + + switch dir { + case "0": + currCol += dist + case "2": + currCol -= dist + case "1": + currRow += dist + case "3": + currRow -= dist + } + + totalDig += int64(dist) + x = append(x, currRow) + y = append(y, currCol) + points = append(points, Point{ + X: float64(currRow), + Y: float64(currCol), + }) + edges++ + } + + result := int64(ShoelaceArea(points)) + //ScanlineFill(matrix, points, 1) //+ totalDig + fmt.Println(result) + fmt.Println(totalDig) + + /* + for _, l := range matrix { + for _, c := range l { + fmt.Print(c, " ") + } + fmt.Println("") + } + */ + return result + totalDig/2 + 1 +} diff --git a/day18/day18_test.go b/day18/day18_test.go new file mode 100644 index 0000000..f00cbdb --- /dev/null +++ b/day18/day18_test.go @@ -0,0 +1,116 @@ +package day18 + +import ( + "testing" + + "gitea.paas.celticinfo.fr/oabrivard/aoc2023/utils" +) + +func TestMatrixSize(t *testing.T) { + lines := []string{ + "R 6 (#70c710)", + "D 5 (#0dc571)", + "L 2 (#5713f0)", + "D 2 (#d2c081)", + "R 2 (#59c680)", + "D 2 (#411b91)", + "L 5 (#8ceee2)", + "U 2 (#caa173)", + "L 1 (#1b58a2)", + "U 2 (#caa171)", + "R 2 (#7807d2)", + "U 3 (#a77fa3)", + "L 2 (#015232)", + "U 2 (#7a21e3)", + } + + minCol, minRow, maxRow, maxCol := matrixSize(lines) + height, width := maxRow-minRow+1, maxCol-minCol+1 + result := height * width + + if result != 70 { + t.Fatalf("expected 70, got %v", result) + } +} + +func TestMatrixSizeWithInput(t *testing.T) { + lines := utils.ReadLines("input.txt") + + minCol, minRow, maxRow, maxCol := matrixSize(lines) + height, width := maxRow-minRow+1, maxCol-minCol+1 + result := height * width + + if result != 88464 { + t.Fatalf("expected 88464, got %v", result) + } +} + +func TestPart1(t *testing.T) { + lines := []string{ + "R 6 (#70c710)", + "D 5 (#0dc571)", + "L 2 (#5713f0)", + "D 2 (#d2c081)", + "R 2 (#59c680)", + "D 2 (#411b91)", + "L 5 (#8ceee2)", + "U 2 (#caa173)", + "L 1 (#1b58a2)", + "U 2 (#caa171)", + "R 2 (#7807d2)", + "U 3 (#a77fa3)", + "L 2 (#015232)", + "U 2 (#7a21e3)", + } + + result := Part1(lines, true) + + if result != 62 { + t.Fatalf("expected 62, got %v", result) + } +} + +func TestPart1WithInput(t *testing.T) { + lines := utils.ReadLines("input.txt") + + result := Part1(lines, false) + + if result != 41019 { + t.Fatalf("expected 41019, got %v", result) + } +} + +func TestPart2(t *testing.T) { + lines := []string{ + "R 6 (#70c710)", + "D 5 (#0dc571)", + "L 2 (#5713f0)", + "D 2 (#d2c081)", + "R 2 (#59c680)", + "D 2 (#411b91)", + "L 5 (#8ceee2)", + "U 2 (#caa173)", + "L 1 (#1b58a2)", + "U 2 (#caa171)", + "R 2 (#7807d2)", + "U 3 (#a77fa3)", + "L 2 (#015232)", + "U 2 (#7a21e3)", + } + + result := Part2(lines) + + if result != 952408144115 { + t.Fatalf("expected 952408144115, got %v", result) + } +} + +func TestPart2WithInput(t *testing.T) { + lines := utils.ReadLines("input.txt") + + result := Part2(lines) + + if result != 96116995735219 { + t.Fatalf("expected 96116995735219, got %v", result) + } +} diff --git a/day18/input.txt b/day18/input.txt new file mode 100644 index 0000000..d7ef926 --- /dev/null +++ b/day18/input.txt @@ -0,0 +1,606 @@ +R 3 (#6289d0) +U 2 (#6a5a63) +R 3 (#49a250) +U 9 (#290bb3) +R 6 (#8ec710) +D 8 (#838413) +R 4 (#387a10) +D 3 (#32f593) +R 4 (#3a8d10) +U 7 (#a252a3) +R 3 (#500100) +U 5 (#03c113) +R 4 (#28fc32) +U 6 (#6da763) +R 4 (#033882) +D 8 (#0aa563) +R 2 (#3c2ae2) +D 3 (#79df03) +R 5 (#7a1652) +D 8 (#79df01) +R 6 (#27e8a2) +U 7 (#65f7a3) +R 3 (#911302) +U 5 (#58f723) +R 8 (#6289d2) +U 4 (#93bed3) +L 11 (#5d5922) +U 3 (#5552d3) +R 9 (#7d6f62) +U 3 (#17eec3) +L 8 (#a06f82) +U 3 (#068273) +L 2 (#1aadb0) +U 4 (#7f20e3) +L 7 (#6895c0) +U 3 (#141553) +L 5 (#2ae520) +U 5 (#141551) +R 5 (#87e540) +U 6 (#1d5593) +L 6 (#2f8912) +U 2 (#43b2e3) +L 3 (#79f0f2) +D 8 (#43b2e1) +L 3 (#27a892) +D 3 (#274813) +L 3 (#64eb42) +D 3 (#77cf23) +L 3 (#9ac052) +D 3 (#5d6873) +L 5 (#1e96b2) +D 6 (#1b83b3) +L 5 (#981b40) +U 4 (#13e0e1) +L 11 (#367430) +D 4 (#8b4bc3) +L 5 (#4c6d40) +U 9 (#8b4bc1) +R 2 (#3eefd0) +U 3 (#13e0e3) +R 6 (#583170) +U 4 (#a413d3) +R 6 (#356a00) +U 6 (#a413d1) +R 8 (#4d0710) +U 4 (#151eb3) +R 3 (#838e10) +U 6 (#34b913) +R 4 (#2ef720) +U 5 (#4a8523) +R 7 (#34ca90) +D 5 (#077023) +R 4 (#747f00) +U 3 (#077021) +R 8 (#1a3150) +U 6 (#4096d3) +R 10 (#44a9e2) +U 6 (#2a7c03) +L 10 (#7ed102) +U 4 (#46ea63) +R 3 (#2acd60) +U 3 (#9799c3) +L 4 (#050c60) +U 6 (#44c4b3) +L 3 (#01e230) +U 5 (#50ee91) +L 3 (#3bfdd0) +U 3 (#1c9603) +L 7 (#2aa6d2) +U 4 (#71db13) +L 8 (#2aa6d0) +D 7 (#31c943) +L 9 (#4ccc60) +D 4 (#2ca231) +R 9 (#1402e0) +D 5 (#939821) +L 4 (#668400) +D 2 (#4d4fa1) +L 8 (#210230) +U 8 (#3e2041) +L 4 (#085470) +U 3 (#404733) +L 6 (#00c2b0) +U 4 (#5be0c3) +L 4 (#2b5df0) +U 9 (#5fde53) +R 4 (#83a0d0) +U 9 (#795c63) +R 3 (#814530) +U 4 (#7f37e3) +R 5 (#266850) +D 5 (#659c43) +R 7 (#266852) +U 5 (#6b5593) +R 9 (#a9e230) +D 7 (#4d39a1) +R 9 (#1a48b0) +D 6 (#4f83f1) +R 4 (#26ebe0) +D 5 (#742f43) +R 5 (#5443a0) +U 5 (#742f41) +R 6 (#3e64a0) +U 2 (#1f6681) +R 9 (#b99422) +U 5 (#520eb1) +L 6 (#27d510) +U 6 (#0bba61) +L 4 (#3ad3c0) +U 5 (#92bca1) +L 7 (#3ad3c2) +U 5 (#200a11) +L 6 (#4493c0) +U 3 (#25dc51) +L 5 (#6a5560) +U 6 (#772071) +L 4 (#264bf0) +D 3 (#2719e1) +L 5 (#90a152) +D 6 (#1cbb11) +L 4 (#4493c2) +D 3 (#452e71) +L 4 (#27d512) +D 2 (#5dd971) +L 8 (#1c4610) +U 6 (#096103) +R 5 (#8438f0) +U 6 (#7351f3) +R 4 (#057e10) +U 6 (#0164e3) +R 6 (#4f7390) +U 3 (#2721c3) +R 4 (#60ab50) +U 6 (#4fb023) +R 10 (#35dad0) +U 2 (#4fb021) +R 5 (#331620) +U 7 (#157f73) +R 9 (#618be0) +U 2 (#36ac23) +R 4 (#4c6400) +U 9 (#36ac21) +R 3 (#43b840) +U 3 (#7159c3) +L 7 (#44d610) +U 5 (#155933) +R 7 (#0991f2) +U 5 (#577cc3) +R 4 (#5c9bf2) +U 3 (#05f3e3) +R 7 (#4861f2) +U 4 (#6c7323) +R 7 (#6268b2) +D 6 (#1cb853) +R 2 (#23b182) +D 3 (#34c9c3) +L 7 (#01d432) +D 8 (#0f5423) +L 2 (#84c4f0) +D 3 (#3f1e13) +R 9 (#0cc5a0) +D 5 (#57adb3) +R 9 (#016840) +D 4 (#677883) +R 7 (#4b8150) +D 7 (#584523) +R 8 (#5bfe40) +D 3 (#11d461) +L 8 (#129982) +D 7 (#51ede1) +R 3 (#129980) +D 9 (#5bfb61) +R 3 (#20ef20) +D 3 (#4d9bc3) +R 6 (#3c2e70) +D 3 (#43eb41) +L 6 (#64a590) +D 2 (#19ee91) +L 4 (#17c8d0) +D 7 (#975651) +L 6 (#4d78f0) +D 2 (#5af991) +L 3 (#15f1b0) +D 4 (#581221) +R 6 (#254700) +D 6 (#26e793) +R 4 (#1b6f00) +D 4 (#681fc3) +L 4 (#364020) +D 8 (#511d31) +L 6 (#548950) +D 6 (#3dea21) +R 6 (#04fc90) +D 3 (#552a51) +R 4 (#0e1762) +D 10 (#2561b1) +R 5 (#597e80) +D 8 (#702b43) +R 3 (#16eb00) +D 4 (#702b41) +R 4 (#5ad290) +D 3 (#427b11) +R 8 (#1e5d32) +D 7 (#0416e1) +R 4 (#2daa92) +U 5 (#28bc11) +R 3 (#4019e2) +U 5 (#8e2311) +R 8 (#052a72) +U 3 (#528623) +L 8 (#1ea522) +U 5 (#0cbf83) +R 6 (#1ea520) +U 5 (#5bb063) +R 6 (#39f002) +U 6 (#64e7e1) +R 8 (#2f0402) +U 3 (#3d83d3) +R 6 (#70b5f2) +U 2 (#7f4ba3) +R 6 (#090b72) +U 5 (#366de1) +R 4 (#4b84c2) +U 5 (#6b7cb1) +R 3 (#763d12) +D 6 (#1ae4e1) +R 6 (#0e3542) +U 6 (#560411) +R 5 (#83a042) +U 3 (#3bc341) +R 3 (#4b7352) +U 8 (#577fc1) +R 7 (#626e42) +U 4 (#13f5c1) +R 5 (#a04f62) +U 6 (#13f5c3) +R 6 (#096972) +U 2 (#2d6be1) +R 4 (#0767e0) +U 7 (#5be161) +R 2 (#7d3600) +U 8 (#440821) +R 3 (#0fc190) +U 3 (#61c671) +L 3 (#07b370) +U 10 (#430571) +L 4 (#6f6970) +U 5 (#43a653) +L 8 (#1a5260) +U 3 (#43a651) +R 8 (#7107e0) +U 7 (#10b3d1) +L 8 (#024af2) +U 7 (#16ed01) +L 4 (#b255c2) +U 2 (#099513) +L 8 (#13d682) +U 4 (#099511) +R 3 (#b0f4a2) +U 4 (#16ed03) +R 3 (#1d6ac2) +U 4 (#3295c1) +R 8 (#1aa6b2) +U 4 (#125d83) +R 7 (#1dfb02) +D 4 (#1bf363) +R 3 (#2656e2) +D 8 (#7d7303) +R 7 (#2656e0) +D 8 (#211f43) +R 3 (#4592c2) +D 7 (#292ae3) +R 7 (#816e92) +U 7 (#3898b3) +R 5 (#4f4e62) +D 5 (#86c423) +R 3 (#7e59f2) +D 9 (#27b271) +R 3 (#1a02b2) +D 6 (#03ce41) +L 3 (#363970) +D 9 (#b91bb1) +L 2 (#363972) +D 3 (#16cfd1) +R 5 (#37ea82) +D 3 (#0a2cd1) +L 7 (#92d872) +D 7 (#4c1711) +L 4 (#01cbd2) +D 7 (#4bc171) +R 5 (#8efa50) +D 5 (#7b2d31) +R 6 (#0e9850) +D 5 (#74ef91) +R 7 (#7a37e0) +D 3 (#6154e1) +R 3 (#7baf00) +U 5 (#17a443) +R 8 (#532e00) +U 6 (#17a441) +R 10 (#8e0410) +U 4 (#6154e3) +R 5 (#6fca00) +U 8 (#4b1103) +L 6 (#833220) +U 5 (#1e3033) +L 3 (#54ef30) +U 8 (#1e3031) +L 2 (#9e7660) +U 4 (#4b1101) +L 3 (#064ae0) +D 9 (#2b6491) +L 2 (#83b470) +D 3 (#2b6493) +L 3 (#5a3a10) +D 5 (#6bbba1) +L 4 (#29afc0) +U 10 (#235101) +R 4 (#4a0f40) +U 12 (#617f71) +R 2 (#62e590) +U 5 (#4d3611) +R 7 (#4077e2) +U 3 (#4763f1) +L 5 (#81d5b2) +U 5 (#6e1fd1) +L 5 (#1107a2) +U 5 (#020fa1) +L 5 (#22b5f2) +U 3 (#1d3841) +R 6 (#13dff2) +U 6 (#9501a1) +R 6 (#4176b2) +D 6 (#24aeb1) +R 3 (#1d44c2) +U 3 (#a77e53) +R 5 (#00ad12) +D 4 (#3179e3) +R 3 (#42c9e2) +D 8 (#65dbb1) +R 2 (#9b6442) +D 7 (#5bbda1) +R 4 (#706b80) +U 12 (#6b1e41) +R 4 (#2c2250) +D 9 (#5ef081) +R 7 (#4d2c80) +D 6 (#7e52d3) +R 5 (#4004a0) +D 2 (#340173) +R 4 (#638190) +D 8 (#a0e873) +R 7 (#58e510) +D 4 (#3f5041) +L 11 (#14be32) +D 2 (#350111) +L 3 (#14be30) +D 3 (#5f9dd1) +L 3 (#1a6850) +D 4 (#7f4d91) +R 7 (#067bf0) +D 7 (#600cf1) +R 2 (#5ff142) +U 7 (#10c9f1) +R 8 (#63a502) +D 7 (#3e29c1) +R 6 (#58c402) +D 6 (#653be1) +R 4 (#66ee92) +D 8 (#62d4d3) +L 3 (#7532f2) +D 3 (#62d4d1) +L 4 (#0c8252) +D 7 (#438151) +L 6 (#63ce50) +D 4 (#53bee1) +L 3 (#274190) +D 5 (#540a93) +L 8 (#843930) +U 5 (#540a91) +L 6 (#38f5f0) +U 4 (#5d2bc1) +L 3 (#35e8d0) +D 4 (#571381) +L 2 (#22aed2) +D 4 (#51c4d1) +L 4 (#22aed0) +D 2 (#1a6ca1) +L 8 (#141aa0) +D 4 (#16f0c1) +L 5 (#69f5a0) +D 3 (#50b081) +L 11 (#4722c2) +D 3 (#4cae83) +L 2 (#864f82) +D 2 (#62b293) +L 10 (#083dd2) +U 5 (#9863e3) +L 6 (#3bfff2) +D 2 (#160693) +L 3 (#9b2bf2) +U 4 (#471371) +L 8 (#181952) +D 4 (#53e531) +L 3 (#292882) +D 5 (#190d41) +R 7 (#68fbb2) +D 4 (#190d43) +R 3 (#87a972) +U 4 (#53e533) +R 7 (#081782) +D 6 (#8adf41) +R 8 (#532c40) +D 4 (#92bed1) +R 4 (#5019e0) +U 9 (#92bed3) +R 5 (#782dc0) +D 9 (#68fdd1) +R 4 (#1e8a90) +D 3 (#22db01) +R 7 (#4f5c22) +D 7 (#5a9031) +R 7 (#4dc440) +D 8 (#1adad1) +R 2 (#1c5ed0) +D 4 (#296441) +R 8 (#7e80c0) +D 5 (#6ccdd1) +L 5 (#5ea2a2) +D 4 (#4f7151) +L 9 (#654fc2) +D 3 (#2d7751) +L 6 (#654fc0) +D 4 (#3d0d31) +L 5 (#3fc180) +D 4 (#687ed1) +R 5 (#5c6c60) +D 3 (#08e123) +R 5 (#369da0) +D 2 (#5b9343) +R 8 (#4a19d2) +D 5 (#2fa453) +L 6 (#4a19d0) +D 9 (#3124e3) +L 2 (#369da2) +U 9 (#126ea3) +L 5 (#47d330) +D 3 (#9a27c1) +L 5 (#65efc0) +D 6 (#3d8471) +L 5 (#105740) +U 11 (#458fb1) +L 3 (#160aa0) +U 3 (#1f5a11) +L 6 (#2eec42) +U 8 (#0936f1) +L 2 (#a2bc52) +U 12 (#0936f3) +L 3 (#197e52) +D 8 (#7cff41) +L 4 (#0b1b52) +D 6 (#38ab61) +L 4 (#7a1082) +D 5 (#4cc4e1) +L 4 (#098560) +D 5 (#4cefe1) +R 7 (#098562) +D 5 (#7e3c91) +R 9 (#8210d2) +D 3 (#1f5193) +L 7 (#4ddcc2) +D 6 (#331863) +L 2 (#256540) +U 6 (#478ea3) +L 7 (#064712) +D 3 (#61ebe3) +L 6 (#064710) +D 4 (#1ba8f3) +L 4 (#256542) +D 5 (#1abc53) +L 4 (#4ddcc0) +D 6 (#21e323) +L 6 (#5a96e2) +D 5 (#25a201) +L 7 (#376d92) +U 5 (#5cd0f1) +L 3 (#110232) +D 5 (#2c23e1) +L 5 (#110230) +D 4 (#3d4e61) +R 4 (#4f94a2) +D 5 (#4a2761) +L 4 (#3bf242) +D 6 (#1c13d1) +L 3 (#788182) +D 3 (#2e4db3) +L 9 (#152c12) +D 2 (#1d6453) +L 3 (#251a52) +D 3 (#9a5fc3) +R 3 (#251a50) +D 2 (#466ca3) +R 9 (#6be6a2) +D 5 (#25a203) +L 7 (#318bb2) +D 4 (#64d183) +L 7 (#78f622) +D 2 (#247fd3) +L 8 (#78f620) +D 3 (#75b6a3) +L 2 (#57f542) +D 5 (#277e03) +L 4 (#773422) +D 6 (#92c4a3) +R 4 (#773420) +D 7 (#3e42a3) +L 7 (#800962) +U 8 (#3be443) +L 3 (#2d3bf2) +U 13 (#2dc9b3) +L 4 (#8dce92) +D 9 (#69adf1) +L 2 (#57b742) +D 3 (#30bbf3) +L 7 (#30f722) +D 4 (#85ad43) +L 9 (#8246b0) +D 4 (#2181f3) +L 7 (#796432) +D 5 (#087563) +L 8 (#3c7b92) +D 4 (#8767e3) +R 5 (#04a3c0) +D 3 (#78ea63) +R 8 (#04a3c2) +D 3 (#4d8193) +L 6 (#b5dfc0) +D 2 (#0b8243) +L 7 (#804ef0) +D 4 (#471af1) +L 6 (#14a462) +D 8 (#64f7e1) +L 7 (#1d1290) +U 4 (#6788a1) +L 4 (#1d1292) +U 9 (#07ed81) +L 2 (#14a460) +U 3 (#25f601) +L 6 (#1f4cb0) +U 9 (#41ce71) +L 3 (#a1dff0) +U 8 (#137563) +L 2 (#362e22) +U 4 (#6eb543) +L 6 (#6cccc2) +U 5 (#3f5983) +L 6 (#249900) +U 4 (#3ef663) +L 3 (#0a8c50) +U 4 (#5decc3) +L 10 (#0a8c52) +U 2 (#452b53) +L 9 (#249902) +U 6 (#2a3c03) +R 4 (#89f592) +U 3 (#4ba633) +R 8 (#2cc4e2) +U 4 (#263133) +L 9 (#913952) +U 2 (#0fe923) +L 3 (#059172) +U 6 (#7c4313) +L 10 (#73f222) +U 4 (#5559d3) +L 2 (#3cb6b2) +U 8 (#2c70f1) +L 5 (#5ffb22) +D 9 (#0bf671) +L 6 (#4d3512) +U 9 (#ac5f51) +L 4 (#27dc72) +U 5 (#ac5f53) +L 3 (#490342) +U 8 (#0bf673) +L 3 (#6fad62) +U 8 (#6e2403)