Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(2024): day 4 - Ceres Search 🕵️‍♂️ #13

Merged
merged 2 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 85 additions & 2 deletions 2024/Day04/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,86 @@
# --- Day 0: Template ---
# --- Day 4: Ceres Search ---

To be used as template for the other days.
"Looks like the Chief's not here. Next!" One of The Historians pulls out a device and pushes the only button on it. After a brief flash, you recognize the interior of the Ceres monitoring station!

As the search for the Chief continues, a small Elf who lives on the station tugs on your shirt; she'd like to know if you could help her with her word search (your puzzle input). She only has to find one word: **XMAS**.

This word search allows words to be horizontal, vertical, diagonal, written backwards, or even overlapping other words. It's a little unusual, though, as you don't merely need to find one instance of **XMAS** - you need to find all of them. Here are a few ways **XMAS** might appear, where irrelevant characters have been replaced with .:

```
..X...
.SAMX.
.A..A.
XMAS.S
.X....
```

The actual word search will be full of letters instead. For example:

```
MMMSXXMASM
MSAMXMSMSA
AMXSXMAAMM
MSAMASMSMX
XMASAMXAMM
XXAMMXXAMA
SMSMSASXSS
SAXAMASAAA
MAMMMXMMMM
MXMXAXMASX
```

In this word search, **XMAS** occurs a total of `18` times; here's the same word search again, but where letters not involved in any **XMAS** have been replaced with .:

```
....XXMAS.
.SAMXMS...
...S..A...
..A.A.MS.X
XMASAMX.MM
X.....XA.A
S.S.S.S.SS
.A.A.A.A.A
..M.M.M.MM
.X.X.XMASX
```

Take a look at the little Elf's word search. How many times does **XMAS** appear?

> Your puzzle answer was `2662`.

## --- Part Two ---

The Elf looks quizzically at you. Did you misunderstand the assignment?

Looking for the instructions, you flip over the word search to find that this isn't actually an **XMAS** puzzle; it's an **X-MAS** puzzle in which you're supposed to find two **MAS** in the shape of an **X**. One way to achieve that is like this:

```
M.S
.A.
M.S
```

Irrelevant characters have again been replaced with . in the above diagram. Within the **X**, each **MAS** can be written forwards or backwards.

Here's the same example from before, but this time all of the **X-MAS**es have been kept instead:

```
.M.S......
..A..MSMS.
.M.S.MAA..
..A.ASMSM.
.M.S.M....
..........
S.S.S.S.S.
.A.A.A.A..
M.M.M.M.M.
..........
```

In this example, an **X-MAS** appears `9` times.

Flip the word search from the instructions back over to the word search side and try again. How many times does an **X-MAS** appear?

> Your puzzle answer was `2034`.

Both parts of this puzzle are complete! They provide two gold stars: `**`
53 changes: 53 additions & 0 deletions 2024/Day04/ceres_search.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from utils.directions import ALL_DIRECTIONS


class CeresSearch:
TARGET_WORD = "XMAS"

def __init__(self, lines: list[str]):
self.lines = lines
self.rows = len(lines)
self.cols = len(lines[0]) if lines else 0

def dfs(self, x: int, y: int, idx: int, direction: tuple[int, int]) -> int:
if not (0 <= x < self.rows and 0 <= y < self.cols):
return 0
if self.lines[x][y] != self.TARGET_WORD[idx]:
return 0
if idx == len(self.TARGET_WORD) - 1:
return 1

dx, dy = direction
return self.dfs(x + dx, y + dy, idx + 1, direction)

def search_xmas(self) -> int:
count = 0
for i in range(self.rows):
for j in range(self.cols):
if self.lines[i][j] == self.TARGET_WORD[0]:
for direction in ALL_DIRECTIONS:
count += self.dfs(i, j, 0, direction)

return count

def is_x_mas(self, x: int, y: int) -> bool:
corners = [
self.lines[x - 1][y - 1],
self.lines[x - 1][y + 1],
self.lines[x + 1][y - 1],
self.lines[x + 1][y + 1],
]
return corners in [
["M", "S", "M", "S"],
["S", "M", "S", "M"],
["M", "M", "S", "S"],
["S", "S", "M", "M"],
]

def search_x_mas_patterns(self) -> int:
return sum(
1
for i in range(1, self.rows - 1)
for j in range(1, self.cols - 1)
if self.lines[i][j] == "A" and self.is_x_mas(i, j)
)
Loading
Loading