Skip to content

Commit

Permalink
day 06: optimized the solution
Browse files Browse the repository at this point in the history
  • Loading branch information
sreedevk committed Dec 6, 2024
1 parent cf92e9a commit 8dac45d
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 64 deletions.
3 changes: 1 addition & 2 deletions src/advent_of_code.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,7 @@ pub fn main() {
"[6] Guard Gallivant (Part 1): " <> int.to_string(day06.solve_a(data)),
)
io.println(
"[6] Guard Gallivant (Part 2): skipped execution (runtime 9m30s)",
// <> int.to_string(day06.solve_b(data)),
"[6] Guard Gallivant (Part 2): " <> int.to_string(day06.solve_b(data)),
)
}),
Nil,
Expand Down
111 changes: 49 additions & 62 deletions src/guard_gallivant.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@ import gleam/dict.{type Dict}
import gleam/list
import gleam/option.{type Option, Some}
import gleam/otp/task
import gleam/pair
import gleam/pair.{first as pf, second as ps}
import gleam/result
import gleam/string
import utils/list as li

type Entity {
Guard
Obstacle
UserObst
Path
Unknown(String)
}
Expand All @@ -35,10 +34,7 @@ type Boundaries =
#(Int, Int)

type State =
#(Point, Direction, Grid, List(History), Boundaries)

type AdvState =
#(State, Bool)
#(Point, Direction, Grid, List(History), Boundaries, Bool)

fn parse_column(line, y) {
use grid, char, x <- list.index_fold(line, dict.new())
Expand All @@ -60,48 +56,33 @@ fn parse_grid(input: String) -> Grid {
|> result.unwrap(dict.new())
}

fn is_guard_position(position) -> Bool {
case position {
#(_, entity) if entity == Guard -> True
fn is_guard_position(pos: Point, g: Grid) -> Bool {
case dict.get(g, pos) {
Ok(Guard) -> True
_ -> False
}
}

fn guard_position(grid: Grid) -> Result(Point, Nil) {
dict.to_list(grid)
|> list.find(is_guard_position)
|> result.map(fn(set) { pair.first(set) })
}

fn init_state(g: Grid) -> State {
let assert Ok(gp) = guard_position(g)
#(gp, Up, g, [#(gp, Up)], #(max_x(g), max_y(g)))
}

fn init_adv_state(grid: Grid) -> AdvState {
#(init_state(grid), False)
}

fn max_x(grid: Grid) -> Int {
li.max(list.map(dict.keys(grid), pair.first))
}
let assert Ok(gp) = list.find(dict.keys(g), is_guard_position(_, g))
let max_x = li.max(list.map(dict.keys(g), pf))
let max_y = li.max(list.map(dict.keys(g), ps))

fn max_y(grid: Grid) -> Int {
li.max(list.map(dict.keys(grid), pair.second))
#(gp, Up, g, [#(gp, Up)], #(max_x, max_y), False)
}

fn update_coordinates(position: Point, direction: Direction) -> Point {
case direction {
Up -> #(pair.first(position), pair.second(position) - 1)
Down -> #(pair.first(position), pair.second(position) + 1)
Left -> #(pair.first(position) - 1, pair.second(position))
Right -> #(pair.first(position) + 1, pair.second(position))
fn incr_pos(pos: Point, dir: Direction) -> Point {
case dir {
Up -> #(pf(pos), ps(pos) - 1)
Down -> #(pf(pos), ps(pos) + 1)
Left -> #(pf(pos) - 1, ps(pos))
Right -> #(pf(pos) + 1, ps(pos))
}
}

fn is_obstacle(position: Point, grid: Grid) -> Bool {
case dict.get(grid, position) {
Ok(Obstacle) | Ok(UserObst) -> True
Ok(Obstacle) -> True
_ -> False
}
}
Expand All @@ -116,7 +97,7 @@ fn rotate_cw(d: Direction) -> Direction {
}

fn next_coordinates(pos: Point, dir: Direction, g: Grid) -> #(Point, Direction) {
let np = update_coordinates(pos, dir)
let np = incr_pos(pos, dir)
case is_obstacle(np, g) {
True -> next_coordinates(pos, rotate_cw(dir), g)
False -> #(np, dir)
Expand All @@ -130,34 +111,40 @@ fn guard_exited(pos: Point, bounds: #(Int, Int)) -> Bool {
}

fn emulate(state: State) -> State {
let #(gp, gd, g, vs, bounds) = state
let #(gp, gd, g, vs, bounds, _) = state
let #(ngp, ngd) = next_coordinates(gp, gd, g)
case guard_exited(ngp, bounds) {
True -> state
False -> emulate(#(ngp, ngd, g, list.append(vs, [#(ngp, ngd)]), bounds))
False ->
emulate(#(ngp, ngd, g, list.append(vs, [#(ngp, ngd)]), bounds, False))
}
}

fn visited_count(st: State) -> Int {
let #(_, _, _, hist, _) = st
let #(_, _, _, hist, _, _) = st
hist
|> list.map(fn(x) { pair.first(x) })
|> list.map(pf)
|> list.unique()
|> list.length()
}

fn iterate(astate: AdvState) -> AdvState {
let #(state, _) = astate
let #(gp, gd, g, vs, bounds) = state
fn visited_paths(st: State) -> #(Grid, List(Point)) {
let #(_, _, g, hist, _, _) = st

#(g, list.unique(list.map(hist, pf)))
}

fn iterate(state: State) -> State {
let #(gp, gd, g, vs, bounds, _) = state
let #(ngp, ngd) = next_coordinates(gp, gd, g)
let ngh = list.append(vs, [#(ngp, ngd)])

case guard_exited(ngp, bounds) {
True -> #(state, False)
True -> state
False ->
case li.contains(vs, #(ngp, ngd)) {
True -> #(#(ngp, ngd, g, ngh, bounds), True)
False -> iterate(#(#(ngp, ngd, g, ngh, bounds), False))
True -> #(ngp, ngd, g, ngh, bounds, True)
False -> iterate(#(ngp, ngd, g, ngh, bounds, False))
}
}
}
Expand All @@ -166,28 +153,25 @@ fn add_obst(g: Grid, pt: Point) -> Grid {
dict.upsert(g, pt, fn(ent: Option(Entity)) {
case ent {
Some(Guard) -> Guard
_ -> UserObst
_ -> Obstacle
}
})
}

fn computer_obst_chunk_var(g: Grid, points: List(Point)) -> List(Bool) {
use obs_pos <- list.map(points)
add_obst(g, obs_pos)
|> init_adv_state
|> iterate
|> pair.second
fn computer_obst(g: Grid, point: Point) {
task.async(fn() {
case iterate(init_state(add_obst(g, point))) {
#(_, _, _, _, _, r) -> r
}
})
}

fn computer_obst_all_var(g: Grid) -> Int {
dict.keys(g)
|> list.sized_chunk(5000)
|> list.map(fn(pos_chunk) {
task.async(fn() { computer_obst_chunk_var(g, pos_chunk) })
})
|> list.map(task.await_forever)
|> list.flatten
|> list.count(fn(x) { x })
fn computer_obst_all_var(info) -> Int {
let #(g, original_path) = info

original_path
|> list.map(computer_obst(g, _))
|> list.count(task.await_forever)
}

pub fn solve_a(input: String) -> Int {
Expand All @@ -199,5 +183,8 @@ pub fn solve_a(input: String) -> Int {

pub fn solve_b(input: String) -> Int {
parse_grid(input)
|> init_state
|> emulate
|> visited_paths
|> computer_obst_all_var
}

0 comments on commit 8dac45d

Please sign in to comment.