Skip to content

Commit

Permalink
day 04: initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
sreedevk committed Dec 4, 2024
1 parent abf0832 commit 028b21c
Show file tree
Hide file tree
Showing 3 changed files with 203 additions and 0 deletions.
14 changes: 14 additions & 0 deletions src/advent_of_code.gleam
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import ceres_search as day04
import gleam/int
import gleam/io
import gleam/result
Expand Down Expand Up @@ -47,4 +48,17 @@ pub fn main() {
}),
Nil,
)

// Day 04
result.unwrap(
result.map(read("data/day4.txt"), fn(data) {
io.println(
"[4] Ceres Search (Part 1): " <> int.to_string(day04.solve_a(data)),
)
io.println(
"[4] Ceres Search (Part 2): " <> int.to_string(day04.solve_b(data)),
)
}),
Nil,
)
}
168 changes: 168 additions & 0 deletions src/ceres_search.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import gleam/dict.{type Dict}
import gleam/int
import gleam/io
import gleam/list
import gleam/pair
import gleam/result
import gleam/string

type Point =
#(Int, Int)

type Grid =
Dict(#(Int, Int), String)

fn parse_grid(input: String) -> Grid {
input
|> string.trim()
|> string.split("\n")
|> list.map(string.to_graphemes)
|> list.index_map(fn(line, y) {
list.index_fold(line, dict.new(), fn(grid, char, x) {
dict.insert(grid, #(x, y), char)
})
})
|> list.reduce(dict.merge)
|> result.unwrap(dict.new())
}

fn forward(point: Point) -> List(Point) {
[
point,
#(pair.first(point) + 1, pair.second(point)),
#(pair.first(point) + 2, pair.second(point)),
#(pair.first(point) + 3, pair.second(point)),
]
}

fn backward(point: Point) -> List(Point) {
[
point,
#(pair.first(point) - 1, pair.second(point)),
#(pair.first(point) - 2, pair.second(point)),
#(pair.first(point) - 3, pair.second(point)),
]
}

fn upward(point: Point) -> List(Point) {
[
point,
#(pair.first(point), pair.second(point) - 1),
#(pair.first(point), pair.second(point) - 2),
#(pair.first(point), pair.second(point) - 3),
]
}

fn downward(point: Point) -> List(Point) {
[
point,
#(pair.first(point), pair.second(point) + 1),
#(pair.first(point), pair.second(point) + 2),
#(pair.first(point), pair.second(point) + 3),
]
}

fn upward_left(point: Point) -> List(Point) {
[
point,
#(pair.first(point) - 1, pair.second(point) - 1),
#(pair.first(point) - 2, pair.second(point) - 2),
#(pair.first(point) - 3, pair.second(point) - 3),
]
}

fn upward_right(point: Point) -> List(Point) {
[
point,
#(pair.first(point) + 1, pair.second(point) - 1),
#(pair.first(point) + 2, pair.second(point) - 2),
#(pair.first(point) + 3, pair.second(point) - 3),
]
}

fn downward_left(point: Point) -> List(Point) {
[
point,
#(pair.first(point) - 1, pair.second(point) + 1),
#(pair.first(point) - 2, pair.second(point) + 2),
#(pair.first(point) - 3, pair.second(point) + 3),
]
}

fn downward_right(point: Point) -> List(Point) {
[
point,
#(pair.first(point) + 1, pair.second(point) + 1),
#(pair.first(point) + 2, pair.second(point) + 2),
#(pair.first(point) + 3, pair.second(point) + 3),
]
}

fn neighbors(point: Point) -> List(List(Point)) {
[
forward(point),
backward(point),
upward(point),
downward(point),
upward_left(point),
upward_right(point),
downward_left(point),
downward_right(point),
]
}

fn x_neighbors(point: Point) -> List(List(Point)) {
[
[
#(pair.first(point) - 1, pair.second(point) - 1),
point,
#(pair.first(point) + 1, pair.second(point) + 1),
],
[
#(pair.first(point) + 1, pair.second(point) - 1),
point,
#(pair.first(point) - 1, pair.second(point) + 1),
],
]
}

fn fetch(points: List(Point), grid: Grid) -> String {
list.fold(points, "", fn(word, point) {
result.unwrap(dict.get(grid, point), "")
|> string.append(word)
})
}

fn word_count_at_point(grid: Grid, point: Point) -> Int {
neighbors(point)
|> list.map(fetch(_, grid))
|> list.count(fn(word) { word == "XMAS" })
}

fn point_has_x_mas(grid: Grid, point: Point) -> Bool {
x_neighbors(point)
|> list.map(fetch(_, grid))
|> list.all(fn(word) { word == "MAS" || word == "SAM" })
}

fn process_grid_xmas(grid: Grid) -> Int {
dict.keys(grid)
|> list.map(fn(point) { word_count_at_point(grid, point) })
|> list.reduce(int.add)
|> result.unwrap(0)
}

fn process_grid_x_mas(grid: Grid) -> Int {
dict.keys(grid)
|> list.count(fn(point) { point_has_x_mas(grid, point) })
}

pub fn solve_a(input: String) -> Int {
parse_grid(input)
|> process_grid_xmas()
}

pub fn solve_b(input: String) -> Int {
parse_grid(input)
|> process_grid_x_mas()
}
21 changes: 21 additions & 0 deletions test/ceres_search_test.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import ceres_search as day04
import gleam/string
import gleeunit/should

fn test_data() -> String {
string.join(
[
"MMMSXXMASM", "MSAMXMSMSA", "AMXSXMAAMM", "MSAMASMSMX", "XMASAMXAMM",
"XXAMMXXAMA", "SMSMSASXSS", "SAXAMASAAA", "MAMMMXMMMM", "MXMXAXMASX",
],
"\n",
)
}

pub fn solve_a_test() {
should.equal(day04.solve_a(test_data()), 18)
}

pub fn solve_b_test() {
should.equal(day04.solve_b(test_data()), 9)
}

0 comments on commit 028b21c

Please sign in to comment.