You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

229 lines
5.3 KiB
Go

package day10
import (
"fmt"
"log"
)
const (
EAST = 'E'
SOUTH = 'S'
WEST = 'W'
NORTH = 'N'
)
type Move struct {
row int
col int
origin rune
count int
}
//var checked = map[Move]bool{}
func findStart(lines []string) (*Move, *Move, *Move) {
var start *Move
OuterLoop:
for row, l := range lines {
for col, c := range l {
if c == 'S' {
start = &Move{row, col, 0, 0}
break OuterLoop
}
}
}
m := make([]*Move, 2)
i := 0
if start.row >= 1 && (lines[start.row-1][start.col] == '7' || lines[start.row-1][start.col] == '|' || lines[start.row-1][start.col] == 'F') {
m[i] = &Move{start.row - 1, start.col, 'S', 1}
i++
}
if start.row <= len(lines)-2 && (lines[start.row+1][start.col] == 'L' || lines[start.row+1][start.col] == '|' || lines[start.row+1][start.col] == 'J') {
m[i] = &Move{start.row + 1, start.col, 'N', 1}
i++
}
if start.col >= 1 && (lines[start.row][start.col-1] == '-' || lines[start.row][start.col-1] == 'L' || lines[start.row][start.col-1] == 'F') {
m[i] = &Move{start.row, start.col - 1, 'E', 1}
i++
}
width := len(lines[start.row])
if start.col <= width-2 && (lines[start.row][start.col+1] == '-' || lines[start.row][start.col+1] == 'J' || lines[start.row][start.col+1] == '7') {
m[i] = &Move{start.row, start.col + 1, 'W', 1}
i++
}
return start, m[0], m[1]
}
func nextMove(m *Move, lines []string) *Move {
switch lines[m.row][m.col] {
case '|': // vertical pipe connecting north and south.
if m.origin == 'N' {
return &Move{m.row + 1, m.col, 'N', m.count + 1}
} else {
return &Move{m.row - 1, m.col, 'S', m.count + 1}
}
case '-': // horizontal pipe connecting east and west.
if m.origin == 'W' {
return &Move{m.row, m.col + 1, 'W', m.count + 1}
} else {
return &Move{m.row, m.col - 1, 'E', m.count + 1}
}
case 'L': // 90-degree bend connecting north and east.
if m.origin == 'N' {
return &Move{m.row, m.col + 1, 'W', m.count + 1}
} else {
return &Move{m.row - 1, m.col, 'S', m.count + 1}
}
case 'J': // 90-degree bend connecting north and west.
if m.origin == 'N' {
return &Move{m.row, m.col - 1, 'E', m.count + 1}
} else {
return &Move{m.row - 1, m.col, 'S', m.count + 1}
}
case '7': // 90-degree bend connecting south and west.
if m.origin == 'S' {
return &Move{m.row, m.col - 1, 'E', m.count + 1}
} else {
return &Move{m.row + 1, m.col, 'N', m.count + 1}
}
case 'F': // a 90-degree bend connecting south and east.
if m.origin == 'S' {
return &Move{m.row, m.col + 1, 'W', m.count + 1}
} else {
return &Move{m.row + 1, m.col, 'N', m.count + 1}
}
case 'S': // is ground; there is no pipe in this tile.
return m
case '.': // is ground; there is no pipe in this tile.
log.Fatalf("Should not find ground %v : ", m)
default:
log.Fatalf("Impossible move %v : ", m)
}
return nil
}
func FindDistance(lines []string) int {
/*
Iterative DFS
-------------
let S be a stack
S.push(v)
while S is not empty do
v = S.pop()
if v is not labeled as discovered then
label v as discovered
for all edges from v to w in G.adjacentEdges(v) do
S.push(w)
stack := utils.NewStack[*Move](140 * 140)
stack.Push(findStart(lines))
for stack.HasElement() {
lastMove := stack.Pop()
}
*/
start, m1, m2 := findStart(lines)
fmt.Println("Starting at ", *start)
for {
if m1.col == m2.col && m1.row == m2.row {
fmt.Println("Finished at ", *m1)
return m1.count
} else {
m1 = nextMove(m1, lines)
m2 = nextMove(m2, lines)
}
}
}
func isInPolygon(row int, col int, lines [][]rune) bool {
count := 0
last := rune(0)
for i := col; i < len(lines[row]); i++ {
c := lines[row][i]
switch c {
case 'S', '|', 'L', 'J', '7', 'F': // skip '-' and do not count it as part of the polygon border
if last == 'F' && c == 'J' || last == 'L' && c == '7' { // count these as a single border of the polygon
last = c
continue
}
last = c
count++
}
}
return count%2 == 1
}
func CountInPolygon(lines []string) int {
writeableLines := make([][]rune, len(lines))
for i := range lines {
writeableLines[i] = []rune(lines[i])
}
// delimit the loop with a specific rune different from the ones used in the problem ('*')
start, m1, m2 := findStart(lines)
fmt.Println("Starting at ", *start)
writeableLines[start.row][start.col] = '*'
writeableLines[m1.row][m1.col] = '*'
writeableLines[m2.row][m2.col] = '*'
for {
if m1.col == m2.col && m1.row == m2.row {
fmt.Println("Finished at ", *m1)
break
} else {
m1 = nextMove(m1, lines)
m2 = nextMove(m2, lines)
writeableLines[m1.row][m1.col] = '*'
writeableLines[m2.row][m2.col] = '*'
}
}
// mark all tile that are not part of the loop as a tile that can count in the total with rune '.'
for row, l := range writeableLines {
for col, c := range l {
if c != '*' {
writeableLines[row][col] = '.'
}
}
}
// reproduce the original loop in the grid with the '.' correctly set up
for row, l := range writeableLines {
for col, c := range l {
if c == '*' {
writeableLines[row][col] = []rune(lines[row])[col]
}
}
}
result := 0
for row, l := range writeableLines {
for col, c := range l {
if c == '.' {
if isInPolygon(row, col, writeableLines) {
result++
}
}
}
}
return result
}