Skip to content

Commit

Permalink
[Day 17] Improve performance: It is enough to store the position and …
Browse files Browse the repository at this point in the history
…direction type (vertical/horizontal) of each node
  • Loading branch information
goggle committed Jan 4, 2024
1 parent d7d60ac commit c3826b6
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 94 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ This Julia package contains my solutions for [Advent of Code 2023](https://adven
| 14 | [:white_check_mark:](https://adventofcode.com/2023/day/14) | 56.620 ms | 22.56 MiB | [:white_check_mark:](https://github.com/goggle/AdventOfCode2023.jl/blob/master/src/day14.jl) |
| 15 | [:white_check_mark:](https://adventofcode.com/2023/day/15) | 2.647 ms | 1.49 MiB | [:white_check_mark:](https://github.com/goggle/AdventOfCode2023.jl/blob/master/src/day15.jl) |
| 16 | [:white_check_mark:](https://adventofcode.com/2023/day/16) | 33.972 ms | 53.35 MiB | [:white_check_mark:](https://github.com/goggle/AdventOfCode2023.jl/blob/master/src/day16.jl) |
| 17 | [:white_check_mark:](https://adventofcode.com/2023/day/17) | 5.126 s | 467.29 MiB | [:white_check_mark:](https://github.com/goggle/AdventOfCode2023.jl/blob/master/src/day17.jl) |
| 17 | [:white_check_mark:](https://adventofcode.com/2023/day/17) | 50.170 ms | 2.72 MiB | [:white_check_mark:](https://github.com/goggle/AdventOfCode2023.jl/blob/master/src/day17.jl) |
| 18 | [:white_check_mark:](https://adventofcode.com/2023/day/18) | 630.330 μs | 484.14 KiB | [:white_check_mark:](https://github.com/goggle/AdventOfCode2023.jl/blob/master/src/day18.jl) |
| 19 | [:white_check_mark:](https://adventofcode.com/2023/day/19) | 2.702 ms | 1.85 MiB | [:white_check_mark:](https://github.com/goggle/AdventOfCode2023.jl/blob/master/src/day19.jl) |
| 20 | [:white_check_mark:](https://adventofcode.com/2023/day/20) | 65.710 ms | 28.76 MiB | [:white_check_mark:](https://github.com/goggle/AdventOfCode2023.jl/blob/master/src/day20.jl) |
Expand Down
142 changes: 49 additions & 93 deletions src/day17.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,110 +3,66 @@ module Day17
using AdventOfCode2023
using DataStructures

abstract type AbstractDirection end

struct Start <: AbstractDirection end
struct Up <: AbstractDirection end
struct Right <: AbstractDirection end
struct Down <: AbstractDirection end
struct Left <: AbstractDirection end

struct Vertex
cost::Int
entry_dir::AbstractDirection
streak::Int
coords::CartesianIndex{2}
end

direction(::Up) = CartesianIndex(-1, 0)
direction(::Right) = CartesianIndex(0, 1)
direction(::Down) = CartesianIndex(1, 0)
direction(::Left) = CartesianIndex(0, -1)
nextdirs(::Start, ::Int) = (Down(), Right())
nextdirs(::Up, streak::Int) = streak == 3 ? (Left(), Right()) : (Left(), Up(), Right())
nextdirs(::Right, streak::Int) = streak == 3 ? (Up(), Down()) : (Up(), Right(), Down())
nextdirs(::Down, streak::Int) = streak == 3 ? (Right(), Left()) : (Right(), Down(), Left())
nextdirs(::Left, streak::Int) = streak == 3 ? (Down(), Up()) : (Down(), Left(), Up())

nextdirs2(::Start, ::Int) = (Down(), Right())
function nextdirs2(::Up, streak::Int)
streak < 4 && return (Up(),)
streak < 10 && return (Left(), Up(), Right())
return (Left(), Right())
end

function nextdirs2(::Right, streak::Int)
streak < 4 && return (Right(),)
streak < 10 && return (Up(), Right(), Down())
return (Up(), Down())
end

function nextdirs2(::Down, streak::Int)
streak < 4 && return (Down(),)
streak < 10 && return (Right(), Down(), Left())
return (Right(), Left())
end

function nextdirs2(::Left, streak::Int)
streak < 4 && return (Left(),)
streak < 10 && return (Down(), Left(), Up())
return (Down(), Up())
end

to_number(dir::Up) = 1
to_number(dir::Right) = 2
to_number(dir::Down) = 3
to_number(dir::Left) = 4


function day17(input::String = readInput(joinpath(@__DIR__, "..", "data", "day17.txt")))
data = map(x -> parse(Int, x[1]), reduce(vcat, permutedims.(map(x -> split(x, ""), split(input)))))
return [solve(data), solvep2(data)]
return [solve_v2(data, 1, 3), solve_v2(data, 4, 10)]
end

function solve(data::Matrix{Int})
dist = typemax(Int) * ones(Int, size(data)..., 4, 3)
dist[1,1,:,:] .= 0
queue = PriorityQueue{Vertex,Int}()
enqueue!(queue, Vertex(0, Start(), 0, CartesianIndex(1, 1)), 0)
while !isempty(queue)
v = dequeue!(queue)
for ndir nextdirs(v.entry_dir, v.streak)
nidx = v.coords + direction(ndir)
if checkbounds(Bool, data, nidx)
streak = v.entry_dir == ndir ? v.streak + 1 : 1
ncost = v.cost + data[nidx[1], nidx[2]]
if dist[nidx[1], nidx[2], to_number(ndir), streak] > ncost
dist[nidx[1], nidx[2], to_number(ndir), streak] = ncost
enqueue!(queue, Vertex(ncost, ndir, streak, nidx), ncost)
function solve_v2(data::Matrix{Int}, smin::Int, smax::Int)
xend, yend = size(data)
pq = PriorityQueue{Tuple{Int,Int,Int},Int}()
cost = typemax(Int16) * ones(Int16, size(data)..., 2)
cost[1,1,:] .= 0
enqueue!(pq, (1, 1, 1), 0)
enqueue!(pq, (1, 1, 2), 0)
while !isempty(pq)
x, y, direction = dequeue!(pq)
if x == xend && y == yend
return Int(min(cost[xend,yend,1], cost[xend,yend,2]))
end

if direction == 1 # we moved horizontal, so now we need to move vertical
steps = 0
for i 1:smax
x - i < 1 && break
steps += data[x - i, y]
if i >= smin && cost[x-i,y,2] > steps + cost[x,y,1]
cost[x-i,y,2] = steps + cost[x,y,1]
pq[(x - i, y, 2)] = cost[x - i, y, 2]
end
end

steps = 0
for i 1:smax
x + i > xend && break
steps += data[x + i, y]
if i >= smin && cost[x+i,y,2] > steps + cost[x,y,1]
cost[x+i,y,2] = steps + cost[x,y,1]
pq[(x + i, y, 2)] = cost[x + i, y, 2]
end
end
else
steps = 0
for j 1:smax
y - j < 1 && break
steps += data[x, y - j]
if j >= smin && cost[x, y - j, 1] > steps + cost[x, y, 2]
cost[x, y - j, 1] = steps + cost[x, y, 2]
pq[(x, y - j, 1)] = cost[x, y - j, 1]
end
end
end
end
return minimum(dist[end, end, :, :])
end

function solvep2(data::Matrix{Int})
dist = typemax(Int) * ones(Int, size(data)..., 4, 10)
dist[1,1,:,:] .= 0
queue = PriorityQueue{Vertex,Int}()
enqueue!(queue, Vertex(0, Start(), 0, CartesianIndex(1, 1)), 0)
while !isempty(queue)
v = dequeue!(queue)
for ndir nextdirs2(v.entry_dir, v.streak)
ndir != v.entry_dir && !checkbounds(Bool, data, v.coords + 4*direction(ndir)) && continue
nidx = v.coords + direction(ndir)
if checkbounds(Bool, data, nidx)
streak = v.entry_dir == ndir ? v.streak + 1 : 1
ncost = v.cost + data[nidx[1], nidx[2]]
if dist[nidx[1], nidx[2], to_number(ndir), streak] > ncost
dist[nidx[1], nidx[2], to_number(ndir), streak] = ncost
enqueue!(queue, Vertex(ncost, ndir, streak, nidx), ncost)
steps = 0
for j 1:smax
y + j > yend && break
steps += data[x, y + j]
if j >= smin && cost[x, y + j, 1] > steps + cost[x, y, 2]
cost[x, y + j, 1] = steps + cost[x, y, 2]
pq[(x, y + j, 1)] = cost[x, y + j, 1]
end
end
end
end
return minimum(dist[end, end, :, :])
end

end # module

0 comments on commit c3826b6

Please sign in to comment.