This commit is contained in:
Hamcha 2024-12-06 18:59:30 +01:00
parent fc530ec5f8
commit 68d39d8896
5 changed files with 324 additions and 0 deletions

View file

@ -5,6 +5,7 @@ version = "1.0.0"
gleam_stdlib = ">= 0.34.0 and < 2.0.0"
stdin = ">= 1.1.4 and < 2.0.0"
gleam_yielder = ">= 1.1.0 and < 2.0.0"
parallel_map = ">= 2.1.0 and < 3.0.0"
[dev-dependencies]
gleeunit = ">= 1.0.0 and < 2.0.0"

View file

@ -2,9 +2,12 @@
# You typically do not need to edit this file
packages = [
{ name = "gleam_erlang", version = "0.33.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "A1D26B80F01901B59AABEE3475DD4C18D27D58FA5C897D922FCB9B099749C064" },
{ name = "gleam_otp", version = "0.14.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "5A8CE8DBD01C29403390A7BD5C0A63D26F865C83173CF9708E6E827E53159C65" },
{ name = "gleam_stdlib", version = "0.45.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "206FCE1A76974AECFC55AEBCD0217D59EDE4E408C016E2CFCCC8FF51278F186E" },
{ name = "gleam_yielder", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_yielder", source = "hex", outer_checksum = "8E4E4ECFA7982859F430C57F549200C7749823C106759F4A19A78AEA6687717A" },
{ name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" },
{ name = "parallel_map", version = "2.1.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib"], otp_app = "parallel_map", source = "hex", outer_checksum = "DE2BA9878728EF9EE34BE83FEDC7A18A1ABE4B2AC1E79C710E3E5D95F5E73404" },
{ name = "stdin", version = "1.1.4", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "stdin", source = "hex", outer_checksum = "04C04035F2A4CEEFB023837249649CD25F9A9CF5A45F9947C4D0462428A4677D" },
]
@ -12,4 +15,5 @@ packages = [
gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" }
gleam_yielder = { version = ">= 1.1.0 and < 2.0.0" }
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
parallel_map = { version = ">= 2.1.0 and < 3.0.0" }
stdin = { version = ">= 1.1.4 and < 2.0.0" }

10
sample/day6-sample.txt Normal file
View file

@ -0,0 +1,10 @@
....#.....
.........#
..........
..#.......
.......#..
..........
.#..^.....
........#.
#.........
......#...

137
src/day6_p1.gleam Normal file
View file

@ -0,0 +1,137 @@
import gleam/io
import gleam/iterator
import gleam/list.{filter, map}
import gleam/pair
import gleam/result
import gleam/string
import stdin.{stdin}
pub fn main() {
stdin()
|> read_input
|> traverse
|> io.debug
}
fn traverse(params: #(Map, #(Int, Int))) -> Int {
let #(map, guard) = params
traverse_loop([], map, guard, Up)
|> list.unique
|> list.length
}
fn traverse_loop(
visited: List(#(Int, Int)),
map: Map,
position: #(Int, Int),
direction: Direction,
) -> List(#(Int, Int)) {
case position {
#(x, _) if x < 0 -> visited
#(x, _) if x >= map.width -> visited
#(_, y) if y < 0 -> visited
#(_, y) if y >= map.height -> visited
_ -> {
let next = walk(position, direction)
case
map.obstacles
|> list.find(fn(x) { x == next })
{
Ok(_) -> traverse_loop(visited, map, position, turn(direction))
_ -> traverse_loop([position, ..visited], map, next, direction)
}
}
}
}
type Direction {
Up
Left
Right
Down
}
fn walk(pos: #(Int, Int), dir: Direction) -> #(Int, Int) {
let #(x, y) = pos
case dir {
Up -> #(x, y - 1)
Down -> #(x, y + 1)
Left -> #(x - 1, y)
Right -> #(x + 1, y)
}
}
fn turn(dir: Direction) -> Direction {
case dir {
Up -> Right
Right -> Down
Down -> Left
Left -> Up
}
}
type Map {
Map(width: Int, height: Int, obstacles: List(#(Int, Int)))
}
fn read_input(it: iterator.Iterator(String)) -> #(Map, #(Int, Int)) {
let maptxt =
it
|> iterator.to_list
|> map(string.trim)
|> filter(not(string.is_empty))
let assert Ok(width) = list.first(maptxt) |> result.map(string.length)
let height = list.length(maptxt)
let obstacles = maptxt |> list.index_fold([], find_obstacles)
let assert Ok(guard) =
maptxt
|> find_index(fn(line, y) {
use x <- result.try(
line
|> string.to_graphemes
|> find_index(fn(chr, x) {
case chr {
"^" -> Ok(x)
_ -> Error(Nil)
}
}),
)
Ok(#(x, y))
})
#(Map(width, height, obstacles), guard)
}
fn find_obstacles(
acc: List(#(Int, Int)),
line: String,
y: Int,
) -> List(#(Int, Int)) {
line
|> string.to_graphemes
|> list.index_fold(acc, fn(cur, chr, x) {
case chr {
"#" -> [#(x, y), ..cur]
_ -> cur
}
})
}
fn find_index(
lst: List(a),
apply: fn(a, Int) -> Result(b, Nil),
) -> Result(b, Nil) {
lst
|> list.index_map(pair.new)
|> list.find_map(fn(tup) {
let #(elem, index) = tup
apply(elem, index)
})
}
fn not(a: fn(a) -> Bool) {
fn(param: a) -> Bool { !a(param) }
}

172
src/day6_p2.gleam Normal file
View file

@ -0,0 +1,172 @@
import gleam/dict.{type Dict}
import gleam/int
import gleam/io
import gleam/iterator
import gleam/list.{filter, map}
import gleam/pair
import gleam/result
import gleam/string
import parallel_map
import stdin.{stdin}
pub fn main() {
stdin()
|> read_input
|> obstruct
|> io.debug
}
fn obstruct(params: #(Map, #(Int, Int))) -> Int {
let #(map, guard) = params
// Skip first step (guard starting spot)
let assert Finite([_, ..steps]) =
traverse_loop_check(dict.new(), map, guard, Up)
// Beware, this one takes a while...
steps
|> parallel_map.list_pmap(
fn(obstruction) {
let new_map = Map(map.width, map.height, [obstruction, ..map.obstacles])
traverse_loop_check(dict.new(), new_map, guard, Up)
},
parallel_map.MatchSchedulersOnline,
5000,
)
|> list.count(fn(x) { x == Ok(Loop) })
}
type Patrol {
Finite(visited: List(#(Int, Int)))
Loop
}
fn traverse_loop_check(
visited: Dict(#(#(Int, Int), Direction), Int),
map: Map,
position: #(Int, Int),
direction: Direction,
) -> Patrol {
case dict.has_key(visited, #(position, direction)) {
True -> Loop
False ->
case position {
#(x, _) if x < 0 -> Finite(visited |> to_steps)
#(x, _) if x >= map.width -> Finite(visited |> to_steps)
#(_, y) if y < 0 -> Finite(visited |> to_steps)
#(_, y) if y >= map.height -> Finite(visited |> to_steps)
_ -> {
let next = walk(position, direction)
case
map.obstacles
|> list.find(fn(x) { x == next })
{
Ok(_) ->
traverse_loop_check(visited, map, position, turn(direction))
_ ->
traverse_loop_check(
visited |> dict.insert(#(position, direction), 1),
map,
next,
direction,
)
}
}
}
}
}
fn to_steps(visited: Dict(#(a, _), _)) -> List(a) {
visited |> dict.keys |> list.map(pair.first) |> list.unique
}
type Direction {
Up
Left
Right
Down
}
fn walk(pos: #(Int, Int), dir: Direction) -> #(Int, Int) {
let #(x, y) = pos
case dir {
Up -> #(x, y - 1)
Down -> #(x, y + 1)
Left -> #(x - 1, y)
Right -> #(x + 1, y)
}
}
fn turn(dir: Direction) -> Direction {
case dir {
Up -> Right
Right -> Down
Down -> Left
Left -> Up
}
}
type Map {
Map(width: Int, height: Int, obstacles: List(#(Int, Int)))
}
fn read_input(it: iterator.Iterator(String)) -> #(Map, #(Int, Int)) {
let maptxt =
it
|> iterator.to_list
|> map(string.trim)
|> filter(not(string.is_empty))
let assert Ok(width) = list.first(maptxt) |> result.map(string.length)
let height = list.length(maptxt)
let obstacles = maptxt |> list.index_fold([], find_obstacles)
let assert Ok(guard) =
maptxt
|> find_index(fn(line, y) {
use x <- result.try(
line
|> string.to_graphemes
|> find_index(fn(chr, x) {
case chr {
"^" -> Ok(x)
_ -> Error(Nil)
}
}),
)
Ok(#(x, y))
})
#(Map(width, height, obstacles), guard)
}
fn find_obstacles(
acc: List(#(Int, Int)),
line: String,
y: Int,
) -> List(#(Int, Int)) {
line
|> string.to_graphemes
|> list.index_fold(acc, fn(cur, chr, x) {
case chr {
"#" -> [#(x, y), ..cur]
_ -> cur
}
})
}
fn find_index(
lst: List(a),
apply: fn(a, Int) -> Result(b, Nil),
) -> Result(b, Nil) {
lst
|> list.index_map(pair.new)
|> list.find_map(fn(tup) {
let #(elem, index) = tup
apply(elem, index)
})
}
fn not(a: fn(a) -> Bool) {
fn(param: a) -> Bool { !a(param) }
}