Skip to content

Commit

Permalink
Day 4: Over engineered!
Browse files Browse the repository at this point in the history
  • Loading branch information
shaunburdick committed Dec 4, 2024
1 parent 12b3a6b commit 05460f7
Show file tree
Hide file tree
Showing 4 changed files with 437 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Each day will be setup as a separate folder.
- [Day 1](/day-1/) - Historian Hysteria
- [Day 2](/day-2/) - Red-Nosed Reports
- [Day 3](/day-3/) - Mull It Over
- [Day 4](/day-4/) - Ceres Search

## Environment Setup

Expand Down
82 changes: 82 additions & 0 deletions day-4/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Day 4 - Ceres Search

## Part 1

"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?

## Part 2

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-MASes 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?
189 changes: 189 additions & 0 deletions day-4/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
package main

import (
_ "embed"
"flag"
"fmt"
"log"
"strings"

file "github.com/shaunburdick/advent-of-code-2024/lib"
)

var input string

func init() {
// do this in init (not main) so test file has same input
inputFile, err := file.LoadRelativeFile("input.txt")
if err != nil {
log.Println(err)
}

input = strings.TrimRight(inputFile, "\n")
}

func main() {
var part int
flag.IntVar(&part, "part", 1, "part 1 or 2")
flag.Parse()
fmt.Println("Running part", part)

if part == 1 {
ans := part1(input)
fmt.Println("Output:", ans)
} else {
ans := part2(input)
fmt.Println("Output:", ans)
}
}

const MAS = "MAS"
const XMAS = "X" + MAS

func part1(input string) int {
parsed := parseInput(input)
foundWords := 0
// start traversing the grid
for y, row := range parsed {
for x := range row {
coord := Coords{x, y}
foundWords += xmasSearch(parsed, "", coord, AllDirections)
}
}

return foundWords
}

func part2(input string) int {
parsed := parseInput(input)
grid := Grid{parsed}
foundMas := 0
// start traversing the grid
for y, row := range grid.Data {
if y == 0 || y == len(grid.Data)-1 {
// skip edges
continue
}

for x, char := range row {
if x == 0 || x == len(row)-1 {
// skip edges
continue
}
if char == 'A' {
coord := Coords{x, y}
wing1 := string(grid.CharAt(coord.Direction(NorthWest))) + "A" + string(grid.CharAt(coord.Direction(SouthEast)))
wing2 := string(grid.CharAt(coord.Direction(NorthEast))) + "A" + string(grid.CharAt(coord.Direction(SouthWest)))

if (wing1 == "MAS" || wing1 == "SAM") && (wing2 == "MAS" || wing2 == "SAM") {
foundMas += 1
}
}
}
}

return foundMas
}

type Grid struct {
Data []string
}

func (g Grid) CharAt(c Coords) rune {
return rune(g.Data[c.y][c.x])
}

type Coords struct {
x int
y int
}

type Direction int

const (
NorthWest Direction = iota
North
NorthEast
West
East
SouthWest
South
SouthEast
)

var AllDirections []Direction = []Direction{
NorthWest,
North,
NorthEast,
West,
East,
SouthWest,
South,
SouthEast,
}

// Generate a list of valid coordinates from the existing one
// Will only return valid positive values
func (c Coords) Directions() []Coords {
directions := []Coords{}

for _, dir := range AllDirections {
newCoord := c.Direction(dir)
// check for bounds
if newCoord.x >= 0 && newCoord.y >= 0 {
directions = append(directions, newCoord)
}
}

return directions
}

func (c Coords) Direction(d Direction) Coords {
switch d {
case NorthWest:
return Coords{x: c.x - 1, y: c.y - 1}
case North:
return Coords{x: c.x, y: c.y - 1}
case NorthEast:
return Coords{x: c.x + 1, y: c.y - 1}
case West:
return Coords{x: c.x - 1, y: c.y}
case East:
return Coords{x: c.x + 1, y: c.y}
case SouthWest:
return Coords{x: c.x - 1, y: c.y + 1}
case South:
return Coords{x: c.x, y: c.y + 1}
case SouthEast:
return Coords{x: c.x + 1, y: c.y + 1}
default:
return c
}
}

func xmasSearch(grid []string, currentWord string, coords Coords, directions []Direction) int {
foundWords := 0
newWord := currentWord + string(grid[coords.y][coords.x])
if newWord == XMAS {
// we match!
return 1
} else if len(newWord) >= len(XMAS) || !strings.HasPrefix(XMAS, newWord) {
// we don't match!
return 0
} else {
// we partially match, keep looking!
for _, direction := range directions {
newCoord := coords.Direction(direction)
// check for bounds
if newCoord.x >= 0 && newCoord.y >= 0 && newCoord.y < len(grid) && newCoord.x < len(grid[newCoord.y]) {
foundWords += xmasSearch(grid, newWord, newCoord, []Direction{direction})
}
}
}

return foundWords
}

func parseInput(input string) (ans []string) {
return strings.Split(input, "\n")
}
Loading

0 comments on commit 05460f7

Please sign in to comment.