-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
97ff614
commit b1dc904
Showing
7 changed files
with
380 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# Day 06 | ||
|
||
First part was ok. Simulate the walk of the guard and count the number of squares they visit. | ||
No fancy tricks, just the input parsing was painful as usual. | ||
|
||
The second part could definitely be implemented better. Currently, I find the guard's path from the | ||
first part, and attempt to place a block at every point of the path. For every such block I then | ||
simulate the guard's path again to see if a cycle forms. | ||
|
||
This could perhaps be optimised by finding all paths that lead to starting position, then | ||
simulating a single walk of the guard and counting places where we overlap the already walked path | ||
or the tree that leads to the starting position. | ||
|
||
If n is the side length of the map, 130 for my input, my current implementation runs in | ||
(walk_length * n^2) time, which in the worst case is n^4. It takes around 5s on my machine. The | ||
solution with first finding the states which lead to the initial position should be a single maze | ||
search and a single simulation, so should run in n^2 time. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
name = "day06" | ||
version = "1.0.0" | ||
|
||
# Fill out these fields if you intend to generate HTML documentation or publish | ||
# your project to the Hex package manager. | ||
# | ||
# description = "" | ||
# licences = ["Apache-2.0"] | ||
# repository = { type = "github", user = "", repo = "" } | ||
# links = [{ title = "Website", href = "" }] | ||
# | ||
# For a full reference of all the available options, you can have a look at | ||
# https://gleam.run/writing-gleam/gleam-toml/. | ||
|
||
[dependencies] | ||
gleam_stdlib = ">= 0.34.0 and < 2.0.0" | ||
simplifile = ">= 2.2.0 and < 3.0.0" | ||
|
||
[dev-dependencies] | ||
gleeunit = ">= 1.0.0 and < 2.0.0" |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# This file was generated by Gleam | ||
# You typically do not need to edit this file | ||
|
||
packages = [ | ||
{ name = "filepath", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "67A6D15FB39EEB69DD31F8C145BB5A421790581BD6AA14B33D64D5A55DBD6587" }, | ||
{ name = "gleam_stdlib", version = "0.45.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "206FCE1A76974AECFC55AEBCD0217D59EDE4E408C016E2CFCCC8FF51278F186E" }, | ||
{ name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" }, | ||
{ name = "simplifile", version = "2.2.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "0DFABEF7DC7A9E2FF4BB27B108034E60C81BEBFCB7AB816B9E7E18ED4503ACD8" }, | ||
] | ||
|
||
[requirements] | ||
gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" } | ||
gleeunit = { version = ">= 1.0.0 and < 2.0.0" } | ||
simplifile = { version = ">= 2.2.0 and < 3.0.0" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
....#..... | ||
.........# | ||
.......... | ||
..#....... | ||
.......#.. | ||
.......... | ||
.#..^..... | ||
........#. | ||
#......... | ||
......#... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
import gleam/bool | ||
import gleam/io | ||
import gleam/list | ||
import gleam/result | ||
import gleam/set | ||
import gleam/string | ||
import simplifile | ||
|
||
fn force_unwrap(r: Result(a, e)) -> a { | ||
result.lazy_unwrap(r, fn() { panic }) | ||
} | ||
|
||
type Coord { | ||
Coord(row: Int, col: Int) | ||
} | ||
|
||
type Map { | ||
Map(rows: Int, cols: Int, map: set.Set(Coord)) | ||
} | ||
|
||
type Dir { | ||
Up | ||
Right | ||
Down | ||
Left | ||
} | ||
|
||
fn dir_from_string(str: String) { | ||
case str { | ||
"^" -> Ok(Up) | ||
">" -> Ok(Right) | ||
"v" -> Ok(Down) | ||
"<" -> Ok(Left) | ||
_ -> Error(Nil) | ||
} | ||
} | ||
|
||
fn right(from dir: Dir) -> Dir { | ||
case dir { | ||
Up -> Right | ||
Right -> Down | ||
Down -> Left | ||
Left -> Up | ||
} | ||
} | ||
|
||
fn step(dir: Dir) -> Coord { | ||
case dir { | ||
Up -> Coord(-1, 0) | ||
Right -> Coord(0, 1) | ||
Down -> Coord(1, 0) | ||
Left -> Coord(0, -1) | ||
} | ||
} | ||
|
||
fn add_coord(a: Coord, b: Coord) -> Coord { | ||
Coord(a.row + b.row, a.col + b.col) | ||
} | ||
|
||
type Guard { | ||
Guard(pos: Coord, dir: Dir) | ||
} | ||
|
||
fn result_when(condition: Bool, give good_val: a) -> Result(a, Nil) { | ||
case condition { | ||
True -> Ok(good_val) | ||
False -> Error(Nil) | ||
} | ||
} | ||
|
||
fn parse_input(input: String) -> #(Map, Guard) { | ||
let lines = | ||
input | ||
|> string.split("\n") | ||
|
||
let els = | ||
lines | ||
|> list.index_map(fn(row, y) { | ||
list.index_map(string.split(row, ""), fn(el, x) { #(el, Coord(y, x)) }) | ||
}) | ||
|> list.flatten | ||
|
||
let map = | ||
els | ||
|> list.filter_map(fn(x) { result_when(x.0 == "#", give: x.1) }) | ||
|> set.from_list | ||
|
||
let height = list.length(lines) | ||
let width = string.length(list.first(lines) |> force_unwrap) | ||
let map = Map(height, width, map) | ||
|
||
let assert [guard] = | ||
els | ||
|> list.filter_map(fn(x) { | ||
dir_from_string(x.0) |> result.map(fn(dir) { Guard(x.1, dir) }) | ||
}) | ||
|
||
#(map, guard) | ||
} | ||
|
||
fn map_contains(map: Map, coord: Coord) -> Bool { | ||
coord.row >= 0 | ||
&& coord.col >= 0 | ||
&& coord.row < map.rows | ||
&& coord.col < map.cols | ||
} | ||
|
||
fn guard_step(map: Map, guard: Guard) -> Guard { | ||
let step = add_coord(guard.pos, step(guard.dir)) | ||
use <- bool.guard( | ||
when: !set.contains(map.map, step), | ||
return: Guard(step, guard.dir), | ||
) | ||
|
||
guard_step(map, Guard(guard.pos, right(guard.dir))) | ||
} | ||
|
||
fn get_visited( | ||
map: Map, | ||
guard: Guard, | ||
visited: set.Set(Coord), | ||
) -> set.Set(Coord) { | ||
use <- bool.guard(when: !map_contains(map, guard.pos), return: visited) | ||
let visited = set.insert(visited, guard.pos) | ||
|
||
get_visited(map, guard_step(map, guard), visited) | ||
} | ||
|
||
fn check_loop(map: Map, guard: Guard, visited: set.Set(Guard)) -> Bool { | ||
use <- bool.guard(when: !map_contains(map, guard.pos), return: False) | ||
use <- bool.guard(when: set.contains(visited, guard), return: True) | ||
let visited = set.insert(visited, guard) | ||
|
||
check_loop(map, guard_step(map, guard), visited) | ||
} | ||
|
||
pub fn part1(input: String) -> Int { | ||
let #(map, guard) = parse_input(input) | ||
get_visited(map, guard, set.new()) |> set.size | ||
} | ||
|
||
pub fn part2(input: String) -> Int { | ||
let #(map, guard) = parse_input(input) | ||
let walk = get_visited(map, guard, set.new()) | ||
|
||
walk | ||
|> set.filter(fn(coord) { coord != guard.pos }) | ||
|> set.filter(fn(coord) { | ||
check_loop(Map(..map, map: set.insert(map.map, coord)), guard, set.new()) | ||
}) | ||
|> set.size | ||
} | ||
|
||
pub fn main() { | ||
let input = | ||
simplifile.read("input.txt") | ||
|> result.lazy_unwrap(fn() { panic }) | ||
|> string.trim | ||
|
||
io.println("Part 1: " <> string.inspect(part1(input))) | ||
io.println("Part 2: " <> string.inspect(part2(input))) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import gleam/result | ||
import gleam/string | ||
import gleeunit | ||
import gleeunit/should | ||
import simplifile | ||
|
||
import day06 | ||
|
||
fn input() -> String { | ||
simplifile.read("small-in.txt") | ||
|> result.lazy_unwrap(fn() { panic }) | ||
|> string.trim | ||
} | ||
|
||
pub fn main() { | ||
gleeunit.main() | ||
} | ||
|
||
pub fn part1_test() { | ||
day06.part1(input()) | ||
|> should.equal(41) | ||
} | ||
|
||
pub fn part2_test() { | ||
day06.part2(input()) | ||
|> should.equal(6) | ||
} |