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.
230 lines
5.3 KiB
Go
230 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)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Point in Polygon algorithm
|
|
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
|
|
}
|