fixed day 6 with optimized algorithm
This commit is contained in:
		
							
								
								
									
										
											BIN
										
									
								
								progress.png
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								progress.png
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 113 KiB  | 
@@ -9,6 +9,6 @@
 | 
				
			|||||||
5:
 | 
					5:
 | 
				
			||||||
  stars: 2
 | 
					  stars: 2
 | 
				
			||||||
6:
 | 
					6:
 | 
				
			||||||
  stars: 1
 | 
					  stars: 2
 | 
				
			||||||
7:
 | 
					7:
 | 
				
			||||||
  stars: 2
 | 
					  stars: 2
 | 
				
			||||||
@@ -1,385 +1,144 @@
 | 
				
			|||||||
#import "/src/utils.typ": *
 | 
					#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 = (
 | 
					#let offsets = (
 | 
				
			||||||
  (0, -1),
 | 
					  str(up): (0, -1),
 | 
				
			||||||
  (1, 0),
 | 
					  str(right): (1, 0),
 | 
				
			||||||
  (0, 1),
 | 
					  str(down): (0, 1),
 | 
				
			||||||
  (-1, 0)
 | 
					  str(left): (-1, 0)
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let values = (
 | 
				
			||||||
 | 
					  ".": empty,
 | 
				
			||||||
 | 
					  "#": obstacle,
 | 
				
			||||||
 | 
					  "^": up,
 | 
				
			||||||
 | 
					  "O": possible-obstacle
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#let in-grid(x, y, w, h) = {
 | 
					#let in-grid(x, y, w, h) = {
 | 
				
			||||||
  return 0 <= x and x < w and 0 <= y and y < h
 | 
					  return 0 <= x and x < w and 0 <= y and y < h
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#let puzzle1(grid, w, h) = {
 | 
					#let rotate(dir) = {
 | 
				
			||||||
  let ox = 0
 | 
					  return if dir == left {up} else {dir.bit-lshift(1)}
 | 
				
			||||||
  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 puzzle1(grid, ox, oy, dir) = {
 | 
				
			||||||
  let y = oy
 | 
					  let w = grid.first().len()
 | 
				
			||||||
  let dir = 0
 | 
					  let h = grid.len()
 | 
				
			||||||
  let count = 1
 | 
					  let path = ((ox, oy, dir),)
 | 
				
			||||||
  let (dx, dy) = offsets.at(dir)
 | 
					  let (x, y) = (ox, oy)
 | 
				
			||||||
  while in-grid(x, y, w, h) {
 | 
					  
 | 
				
			||||||
    if grid.at(y).at(x) != "v" {
 | 
					  let (dx, dy) = (0, 0)
 | 
				
			||||||
      path.push((x, y, dir))
 | 
					  let (x2, y2) = (x, y)
 | 
				
			||||||
    }
 | 
					  while true {
 | 
				
			||||||
    let x2 = x + dx
 | 
					    (dx, dy) = offsets.at(str(dir))
 | 
				
			||||||
    let y2 = y + dy
 | 
					    (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) {
 | 
					    if not in-grid(x2, y2, w, h) {
 | 
				
			||||||
      break
 | 
					      break
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for _ in range(4) {
 | 
					    // Not relevant
 | 
				
			||||||
      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 get-next-obstacle(grid, w, h, ox, oy, dir) = {
 | 
					    if grid.at(y2).at(x2).bit-and(dir) != 0 {
 | 
				
			||||||
  let (dx, dy) = offsets.at(dir)
 | 
					      loops = true
 | 
				
			||||||
  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
 | 
					      break
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    }
 | 
					    */
 | 
				
			||||||
    if found-start {
 | 
					 | 
				
			||||||
      break
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  let path = ()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let x = ox
 | 
					    if grid.at(y2).at(x2) == obstacle {
 | 
				
			||||||
  let y = oy
 | 
					      dir = rotate(dir)
 | 
				
			||||||
  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)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if not in-grid(x2, y2, w, h) {
 | 
					 | 
				
			||||||
      return false
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let visits = path-grid.at(y2).at(x2)
 | 
					 | 
				
			||||||
    if visits.at(str(dir), default: calc.inf) < si {
 | 
					 | 
				
			||||||
      return true
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    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)
 | 
					 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      (x, y) = (x2, y2)
 | 
					      (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
 | 
					  return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -389,107 +148,63 @@
 | 
				
			|||||||
  let w = grid.first().len()
 | 
					  let w = grid.first().len()
 | 
				
			||||||
  let h = grid.len()
 | 
					  let h = grid.len()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let path-grid = (
 | 
					  let ox = 0
 | 
				
			||||||
    (
 | 
					  let oy = 0
 | 
				
			||||||
      (:),
 | 
					  for (y, line) in grid.enumerate() {
 | 
				
			||||||
    ) * w,
 | 
					    for (x, cell) in line.enumerate() {
 | 
				
			||||||
  ) * h
 | 
					      let value = values.at(cell)
 | 
				
			||||||
 | 
					      if value == up {
 | 
				
			||||||
  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 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) != "." {
 | 
					 | 
				
			||||||
      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"
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  //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)
 | 
					        (ox, oy) = (x, y)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					      grid.at(y).at(x) = value
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    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 cols = ()
 | 
				
			||||||
      let (tx, ty) = (x + dx, y + dy)
 | 
					  let rows = ()
 | 
				
			||||||
      if not in-grid(tx, ty, w, h) {
 | 
					
 | 
				
			||||||
 | 
					  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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (x, y, _) in path.slice(1, path.len() - 1) {
 | 
				
			||||||
 | 
					    if x == ox and y == oy {
 | 
				
			||||||
      continue
 | 
					      continue
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
      if grid.at(ty).at(tx) != "." {
 | 
					    let cell = grid.at(y).at(x)
 | 
				
			||||||
        continue
 | 
					    if cell.bit-and(possible-obstacle) == 0 {
 | 
				
			||||||
      }
 | 
					      let (rows2, cols2) = add-obstacle(rows, cols, x, y)
 | 
				
			||||||
      if has-loop(grid, w, h, x, y, tx, ty, dir) {
 | 
					
 | 
				
			||||||
        grid.at(ty).at(tx) = "O"
 | 
					      if walk-loops(rows2, cols2, ox, oy, up) {
 | 
				
			||||||
        draw.circle(
 | 
					        count += 1
 | 
				
			||||||
          (tx, -ty),
 | 
					        grid.at(y).at(x) = cell.bit-or(possible-obstacle)
 | 
				
			||||||
          radius: 0.4,
 | 
					 | 
				
			||||||
          stroke: blue
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  })
 | 
					  }
 | 
				
			||||||
 | 
					  return count
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#show-puzzle(
 | 
					#show-puzzle(
 | 
				
			||||||
  6, 2,
 | 
					  6, 2,
 | 
				
			||||||
  solve,//solve-bruteforce,
 | 
					  solve,
 | 
				
			||||||
  example: 6,
 | 
					  example: 6
 | 
				
			||||||
  visualize: visualize,
 | 
					 | 
				
			||||||
  only-example: true
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					 | 
				
			||||||
//#solve(get-input(6))
 | 
					 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								src/main.pdf
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/main.pdf
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							
		Reference in New Issue
	
	Block a user