fixed day 6 with optimized algorithm

This commit is contained in:
Louis Heredero 2024-12-08 01:52:05 +01:00
parent 9a1b3bedc2
commit 0d89355f86
Signed by: HEL
GPG Key ID: 8D83DE470F8544E7
4 changed files with 169 additions and 454 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 113 KiB

View File

@ -9,6 +9,6 @@
5:
stars: 2
6:
stars: 1
stars: 2
7:
stars: 2

View File

@ -1,385 +1,144 @@
#import "/src/utils.typ": *
#import "@preview/cetz:0.3.1": canvas, draw
#let empty = 0
#let up = 1
#let right = 2
#let down = 4
#let left = 8
#let obstacle = 16
#let possible-obstacle = 32
#let offsets = (
(0, -1),
(1, 0),
(0, 1),
(-1, 0)
str(up): (0, -1),
str(right): (1, 0),
str(down): (0, 1),
str(left): (-1, 0)
)
#let values = (
".": empty,
"#": obstacle,
"^": up,
"O": possible-obstacle
)
#let in-grid(x, y, w, h) = {
return 0 <= x and x < w and 0 <= y and y < h
}
#let puzzle1(grid, w, h) = {
let ox = 0
let oy = 0
let found-start = false
for y in range(h) {
for x in range(w) {
if grid.at(y).at(x) == "^" {
ox = x
oy = y
found-start = true
break
}
}
if found-start {
break
}
}
let path = ()
let x = ox
let y = oy
let dir = 0
let count = 1
let (dx, dy) = offsets.at(dir)
while in-grid(x, y, w, h) {
if grid.at(y).at(x) != "v" {
path.push((x, y, dir))
}
let x2 = x + dx
let y2 = y + dy
if not in-grid(x2, y2, w, h) {
break
}
for _ in range(4) {
let next = grid.at(y2).at(x2)
if next == "#" {
dir = calc.rem(dir + 1, 4)
(dx, dy) = offsets.at(dir)
x2 = x + dx
y2 = y + dy
} else {
break
}
}
x = x2
y = y2
}
return path
#let rotate(dir) = {
return if dir == left {up} else {dir.bit-lshift(1)}
}
/*
#let get-next-obstacle(grid, w, h, ox, oy, dir) = {
let (dx, dy) = offsets.at(dir)
#let puzzle1(grid, ox, oy, dir) = {
let w = grid.first().len()
let h = grid.len()
let path = ((ox, oy, dir),)
let (x, y) = (ox, oy)
while in-grid(x, y, w, h) {
if grid.at(y).at(x) == "#" {
return (x - dx, y - dy)
}
x += dx
y += dy
}
return none
}
#let puzzle1(grid, w, h) = {
let ox = 0
let oy = 0
let found-start = false
for y in range(h) {
for x in range(w) {
if grid.at(y).at(x) == "^" {
ox = x
oy = y
found-start = true
break
}
}
if found-start {
break
}
}
let path = ()
let x = ox
let y = oy
let dir = 0
let count = 1
let (dx, dy) = offsets.at(dir)
while in-grid(x, y, w, h) {
path.push((x, y, dir))
let x2 = x + dx
let y2 = y + dy
if not in-grid(x2, y2, w, h) {
break
}
for _ in range(4) {
let next = grid.at(y2).at(x2)
if next == "#" {
dir = calc.rem(dir + 1, 4)
(dx, dy) = offsets.at(dir)
x2 = x + dx
y2 = y + dy
} else {
break
}
}
x = x2
y = y2
}
return path
}
#let solve(input) = {
let grid = input.split("\n").map(l => l.clusters())
let w = grid.first().len()
let h = grid.len()
let obstacles = ()
for y in range(h) {
for x in range(w) {
if grid.at(y).at(x) == "#" {
obstacles.push((x, y))
}
}
}
let get-next-obstacle = get-next-obstacle.with(
grid, w, h
)
let path = puzzle1(grid, w, h)
let count = 0
for (ox, oy) in obstacles {
for odir in range(4) {
let dir = odir
let (dx, dy) = offsets.at(dir)
let (x, y) = (ox + dx, oy + dy)
if not in-grid(x, y, w, h) {
continue
}
let found = true
let in-path = false
let (min-x, min-y) = (w, h)
let (max-x, max-y) = (0, 0)
let corner-obstacles = ()
corner-obstacles.push((ox, oy))
for _ in range(3) {
let next = get-next-obstacle(x, y, calc.rem(dir + 3, 4))
dir = calc.rem(dir + 1, 4)
if next == none {
found = false
break
} else {
(x, y) = next
min-x = calc.min(min-x, x)
min-y = calc.min(min-y, y)
max-x = calc.max(max-x, x)
max-y = calc.max(max-y, y)
if corner-in-path(path, x, y, )
}
}
if found {
count += 1
}
}
}
return count
}*/
#let solve(input) = {
let grid = input.split("\n").map(l => l.clusters())
let w = grid.first().len()
let h = grid.len()
let ox = 0
let oy = 0
let found-start = false
for y in range(h) {
for x in range(w) {
if grid.at(y).at(x) == "^" {
ox = x
oy = y
found-start = true
break
}
}
if found-start {
break
}
}
let path = ()
let x = ox
let y = oy
let dir = 0
let count = 1
let (dx, dy) = offsets.at(dir)
while in-grid(x, y, w, h) {
path.push((x, y, dir))
if path.len() >= 3 {
let anchor = path.at(path.len() - 3)
if ((dx == 0 and y == anchor.last()) or
(dy == 0 and x == anchor.first())) {
}
}
let x2 = x + dx
let y2 = y + dy
if not in-grid(x2, y2, w, h) {
break
}
for _ in range(4) {
let next = grid.at(y2).at(x2)
if next == "#" {
dir = calc.rem(dir + 1, 4)
(dx, dy) = offsets.at(dir)
x2 = x + dx
y2 = y + dy
} else {
break
}
}
x = x2
y = y2
}
return path
}
#let has-loop(grid, w, h, ox, oy, tx, ty, dir) = {
let grid = grid
grid.at(ty).at(tx) = "#"
let x = ox
let y = oy
let visited = ()
let (dx, dy) = offsets.at(dir)
while in-grid(x, y, w, h) {
grid.at(y).at(x) = "v"
let e = (x, y, dir)
if e in visited {
return true
}
visited.push(e)
let x2 = x + dx
let y2 = y + dy
if not in-grid(x2, y2, w, h) {
break
}
for _ in range(4) {
let next = grid.at(y2).at(x2)
if next == "#" {
dir = calc.rem(dir + 1, 4)
(dx, dy) = offsets.at(dir)
x2 = x + dx
y2 = y + dy
} else {
break
}
}
x = x2
y = y2
}
return false
}
#let solve-bruteforce(input) = {
let grid = input.split("\n").map(l => l.clusters())
let w = grid.first().len()
let h = grid.len()
let ox = 0
let oy = 0
let found-start = false
for y in range(h) {
for x in range(w) {
if grid.at(y).at(x) == "^" {
ox = x
oy = y
found-start = true
break
}
}
if found-start {
break
}
}
let path = puzzle1(grid, w, h)
let count = 0
for (x, y, dir) in path {
let (dx, dy) = offsets.at(dir)
let (tx, ty) = (x + dx, y + dy)
if not in-grid(tx, ty, w, h) {
continue
}
if grid.at(ty).at(tx) != "." {
continue
}
if has-loop(grid, w, h, x, y, tx, ty, dir) {
grid.at(ty).at(tx) = "O"
count += 1
}
}
return count
}
#let rejoins-path(
grid, w, h, path,
ox, oy, tx, ty, dir,
path-grid, si
) = {
let grid = grid
grid.at(ty).at(tx) = "#"
let (x, y) = (ox, oy)
let dir = dir
let (dx, dy) = offsets.at(dir)
let visited = ()
while in-grid(x, y, w, h) {
let (x2, y2) = (x + dx, y + dy)
let (dx, dy) = (0, 0)
let (x2, y2) = (x, y)
while true {
(dx, dy) = offsets.at(str(dir))
(x2, y2) = (x + dx, y + dy)
grid.at(y).at(x) = grid.at(y).at(x).bit-or(dir)
path.push((x2, y2, dir))
if not in-grid(x2, y2, w, h) {
return false
break
}
let visits = path-grid.at(y2).at(x2)
if visits.at(str(dir), default: calc.inf) < si {
return true
// Not relevant
/*
if grid.at(y2).at(x2).bit-and(dir) != 0 {
loops = true
break
}
let e = (x, y, dir)
if e in visited {
return true
}
visited.push(e)
*/
if grid.at(y2).at(x2) == "#" {
dir = calc.rem(dir + 1, 4)
(dx, dy) = offsets.at(dir)
if grid.at(y2).at(x2) == obstacle {
dir = rotate(dir)
} else {
(x, y) = (x2, y2)
}
}
return path
}
#let add-obstacle(rows, cols, x, y) = {
let add-in-line(line, v) = {
if line.len() == 0 {
line.push(v)
return line
}
for (i, v2) in line.enumerate() {
if v < v2 {
line.insert(i, v)
return line
}
}
line.push(v)
return line
}
rows.at(y) = add-in-line(rows.at(y), x)
cols.at(x) = add-in-line(cols.at(x), y)
return (rows, cols)
}
#let walk-loops(rows, cols, ox, oy, dir) = {
let (x, y) = (ox, oy)
let w = cols.len()
let h = rows.len()
let visited = ()
while true {
let pos = (x, y, dir)
if pos in visited {
return true
}
visited.push(pos)
let line = if dir in (up, down) {cols.at(x)} else {rows.at(y)}
let v = if dir in (up, down) {y} else {x}
// No obstacle
if line.len() == 0 {
return false
}
// Leave grid
if dir in (up, left) {
if v < line.first() {
return false
}
} else if v > line.last() {
return false
}
let i = line.len() - 1
for (j, v2) in line.enumerate() {
if v < v2 {
i = j
if dir in (left, up) {
i -= 1
}
break
}
}
let v2 = line.at(i)
if dir == up {
y = v2 + 1
} else if dir == right {
x = v2 - 1
} else if dir == down {
y = v2 - 1
} else {
x = v2 + 1
}
dir = rotate(dir)
}
return false
}
@ -389,107 +148,63 @@
let w = grid.first().len()
let h = grid.len()
let path-grid = (
(
(:),
) * w,
) * h
let path = puzzle1(grid, w, h)
for (i, (x, y, dir)) in path.enumerate() {
path-grid.at(y).at(x).insert(str(dir), i)
let ox = 0
let oy = 0
for (y, line) in grid.enumerate() {
for (x, cell) in line.enumerate() {
let value = values.at(cell)
if value == up {
(ox, oy) = (x, y)
}
grid.at(y).at(x) = value
}
}
let cols = ()
let rows = ()
for y in range(h) {
let row = ()
for x in range(w) {
if grid.at(y).at(x) == obstacle {
row.push(x)
}
}
rows.push(row)
}
for x in range(w) {
let col = ()
for y in range(h) {
if grid.at(y).at(x) == obstacle {
col.push(y)
}
}
cols.push(col)
}
let path = puzzle1(grid, ox, oy, up)
let count = 0
let max = (h, w, h, w)
for (i, (x, y, dir)) in path.enumerate() {
let (dx, dy) = offsets.at(dir)
let (tx, ty) = (x + dx, y + dy)
if not in-grid(tx, ty, w, h) or grid.at(ty).at(tx) != "." {
for (x, y, _) in path.slice(1, path.len() - 1) {
if x == ox and y == oy {
continue
}
let dir2 = calc.rem(dir + 1, 4)
if rejoins-path(
grid, w, h, path,
x, y, tx, ty, dir2,
path-grid, i
) {
count += 1
grid.at(ty).at(tx) = "O"
let cell = grid.at(y).at(x)
if cell.bit-and(possible-obstacle) == 0 {
let (rows2, cols2) = add-obstacle(rows, cols, x, y)
if walk-loops(rows2, cols2, ox, oy, up) {
count += 1
grid.at(y).at(x) = cell.bit-or(possible-obstacle)
}
}
}
//return raw(block: true, grid.map(l => l.join("")).join("\n"))
return count
}
#let visualize(input) = {
let grid = input.split("\n").map(l => l.clusters())
let w = grid.first().len()
let h = grid.len()
canvas(length: 2em, {
let (ox, oy) = (0, 0)
for y in range(h) {
for x in range(w) {
let c = grid.at(y).at(x)
draw.circle(
(x, -y),
radius: if c == "#" {0.4} else {0.2},
fill: if c == "#" {
red
} else if c == "^" {
green
} else {
gray.lighten(40%)
}
)
if c == "^" {
(ox, oy) = (x, y)
}
}
}
let path = puzzle1(grid, w, h)
let count = 0
for (i, (x, y, dir)) in path.enumerate() {
if i != 0 {
let col = if i == 1 {green} else {blue}
draw.line(
(path.at(i - 1).first(), -path.at(i - 1).at(1)),
(x, -y),
stroke: col,
fill: col,
mark: (end: ">")
)
}
let (dx, dy) = offsets.at(dir)
let (tx, ty) = (x + dx, y + dy)
if not in-grid(tx, ty, w, h) {
continue
}
if grid.at(ty).at(tx) != "." {
continue
}
if has-loop(grid, w, h, x, y, tx, ty, dir) {
grid.at(ty).at(tx) = "O"
draw.circle(
(tx, -ty),
radius: 0.4,
stroke: blue
)
}
}
})
}
#show-puzzle(
6, 2,
solve,//solve-bruteforce,
example: 6,
visualize: visualize,
only-example: true
solve,
example: 6
)
//#solve(get-input(6))

Binary file not shown.