Day 4: Ceres Search
Megathread guidelines
- Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
- You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL
FAQ
- What is this?: Here is a post with a large amount of details: https://programming.dev/post/6637268
- Where do I participate?: https://adventofcode.com/
- Is there a leaderboard for the community?: We have a programming.dev leaderboard with the info on how to join in this post: https://programming.dev/post/6631465
3 points
*
Haskell
import Control.Arrow
import Data.Array.Unboxed
import Data.List
type Pos = (Int, Int)
type Board = Array Pos Char
data Dir = N | NE | E | SE | S | SW | W | NW
target = "XMAS"
parse s = listArray ((1, 1), (n, m)) [l !! i !! j | i <- [0 .. n - 1], j <- [0 .. m - 1]]
where
l = lines s
(n, m) = (length $ head l, length l)
move N = first pred
move S = first succ
move E = second pred
move W = second succ
move NW = move N . move W
move SW = move S . move W
move NE = move N . move E
move SE = move S . move E
check :: Board -> Pos -> Int -> Dir -> Bool
check b p i d =
i >= length target
|| ( inRange (bounds b) p
&& (b ! p) == (target !! i)
&& check b (move d p) (succ i) d
)
checkAllDirs :: Board -> Pos -> Int
checkAllDirs b p = length . filter (check b p 0) $ [N, NE, E, SE, S, SW, W, NW]
check2 :: Board -> Pos -> Bool
check2 b p =
all (inRange (bounds b)) moves && ((b ! p) == 'A') && ("SSMM" `elem` rotations)
where
rotations = rots $ (b !) <$> moves
moves = flip move p <$> [NE, SE, SW, NW]
rots xs = init $ zipWith (++) (tails xs) (inits xs)
part1 b = sum $ checkAllDirs b <$> indices b
part2 b = length . filter (check2 b) $ indices b
main = getContents >>= print . (part1 &&& part2) . parse
4 points
I struggled a lot more when doing list slices that I wouldโve liked to
Haskell
import Data.List qualified as List
collectDiagonal :: [String] -> Int -> Int -> String
collectDiagonal c y x
| length c > y && length (c !! y) > x = c !! y !! x : collectDiagonal c (y+1) (x+1)
| otherwise = []
part1 c = do
let forwardXMAS = map (length . filter (List.isPrefixOf "XMAS") . List.tails) $ c
let backwardXMAS = map (length . filter (List.isPrefixOf "XMAS") . List.tails . reverse) $ c
let downwardXMAS = map (length . filter (List.isPrefixOf "XMAS") . List.tails ) . List.transpose $ c
let upwardXMAS = map (length . filter (List.isPrefixOf "XMAS") . List.tails . reverse ) . List.transpose $ c
let leftSideDiagonals = map (\ y -> collectDiagonal c y 0) [0..length c]
let leftTopDiagonals = map (\ x -> collectDiagonal c 0 x) [1..(length . List.head $ c)]
let leftDiagonals = leftSideDiagonals ++ leftTopDiagonals
let rightSideDiagonals = map (\ y -> collectDiagonal (map List.reverse c) y 0) [0..length c]
let rightTopDiagonals = map (\ x -> collectDiagonal (map List.reverse c) 0 x) [1..(length . List.head $ c)]
let rightDiagonals = rightSideDiagonals ++ rightTopDiagonals
let diagonals = leftDiagonals ++ rightDiagonals
let diagonalXMAS = map (length . filter (List.isPrefixOf "XMAS") . List.tails) $ diagonals
let reverseDiagonalXMAS = map (length . filter (List.isPrefixOf "XMAS") . List.tails . reverse) $ diagonals
print . sum $ [sum forwardXMAS, sum backwardXMAS, sum downwardXMAS, sum upwardXMAS, sum diagonalXMAS, sum reverseDiagonalXMAS]
return ()
getBlock h w c y x = map (take w . drop x) . take h . drop y $ c
isXBlock b = do
let diagonal1 = collectDiagonal b 0 0
let diagonal2 = collectDiagonal (map List.reverse b) 0 0
diagonal1 `elem` ["SAM", "MAS"] && diagonal2 `elem` ["SAM", "MAS"]
part2 c = do
let lineBlocks = List.map (getBlock 3 3 c) [0..length c - 1]
let groupedBlocks = List.map (flip List.map [0..(length . head $ c) - 1]) lineBlocks
print . sum . map (length . filter isXBlock) $ groupedBlocks
return ()
main = do
c <- lines <$> getContents
part1 c
part2 c
return ()
4 points
*
Nim
Could be done more elegantly, but I havenโt bothered yet.
proc solve(input: string): AOCSolution[int, int] =
var lines = input.splitLines()
block p1:
# horiz
for line in lines:
for i in 0..line.high-3:
if line[i..i+3] in ["XMAS", "SAMX"]:
inc result.part1
for y in 0..lines.high-3:
#vert
for x in 0..lines[0].high:
let word = collect(for y in y..y+3: lines[y][x])
if word in [@"XMAS", @"SAMX"]:
inc result.part1
#diag \
for x in 0..lines[0].high-3:
let word = collect(for d in 0..3: lines[y+d][x+d])
if word in [@"XMAS", @"SAMX"]:
inc result.part1
#diag /
for x in 3..lines[0].high:
let word = collect(for d in 0..3: lines[y+d][x-d])
if word in [@"XMAS", @"SAMX"]:
inc result.part1
block p2:
for y in 0..lines.high-2:
for x in 0..lines[0].high-2:
let diagNW = collect(for d in 0..2: lines[y+d][x+d])
let diagNE = collect(for d in 0..2: lines[y+d][x+2-d])
if diagNW in [@"MAS", @"SAM"] and diagNE in [@"MAS", @"SAM"]:
inc result.part2
5 points
Haskell
Popular language this year :)
I got embarrassingly stuck on this one trying to be clever with list operations. Then I realized I should just use an arrayโฆ
import Data.Array.Unboxed (UArray)
import Data.Array.Unboxed qualified as A
import Data.Bifunctor
readInput :: String -> UArray (Int, Int) Char
readInput s =
let rows = lines s
n = length rows
in A.listArray ((1, 1), (n, n)) $ concat rows
s1 `eq` s2 = s1 == s2 || s1 == reverse s2
part1 arr = length $ filter isXmas $ concatMap lines $ A.indices arr
where
isXmas ps = all (A.inRange $ A.bounds arr) ps && map (arr A.!) ps `eq` "XMAS"
lines p = [take 4 $ iterate (bimap (+ di) (+ dj)) p | (di, dj) <- [(1, 0), (0, 1), (1, 1), (1, -1)]]
part2 arr = length $ filter isXmas innerPoints
where
innerPoints =
let ((i1, j1), (i2, j2)) = A.bounds arr
in [(i, j) | i <- [i1 + 1 .. i2 - 1], j <- [j1 + 1 .. j2 - 1]]
isXmas p = up p `eq` "MAS" && down p `eq` "MAS"
up (i, j) = map (arr A.!) [(i + 1, j - 1), (i, j), (i - 1, j + 1)]
down (i, j) = map (arr A.!) [(i - 1, j - 1), (i, j), (i + 1, j + 1)]
main = do
input <- readInput <$> readFile "input04"
print $ part1 input
print $ part2 input
2 points
C#
public class Day04 : Solver
{
private int width, height;
private char[,] data;
public void Presolve(string input) {
var lines = input.Trim().Split("\n").ToList();
height = lines.Count;
width = lines[0].Length;
data = new char[height, width];
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
data[i, j] = lines[i][j];
}
}
}
private static readonly string word = "XMAS";
public string SolveFirst()
{
int counter = 0;
for (int start_i = 0; start_i < height; start_i++) {
for (int start_j = 0; start_j < width; start_j++) {
if (data[start_i, start_j] != word[0]) continue;
for (int di = -1; di <= 1; di++) {
for (int dj = -1; dj <= 1; dj++) {
if (di == 0 && dj == 0) continue;
int end_i = start_i + di * (word.Length - 1);
int end_j = start_j + dj * (word.Length - 1);
if (end_i < 0 || end_j < 0 || end_i >= height || end_j >= width) continue;
for (int k = 1; k < word.Length; k++) {
if (data[start_i + di * k, start_j + dj * k] != word[k]) break;
if (k == word.Length - 1) counter++;
}
}
}
}
}
return counter.ToString();
}
public string SolveSecond()
{
int counter = 0;
for (int start_i = 1; start_i < height - 1; start_i++) {
for (int start_j = 1; start_j < width - 1; start_j++) {
if (data[start_i, start_j] != 'A') continue;
int even_mas_starts = 0;
for (int di = -1; di <= 1; di++) {
for (int dj = -1; dj <= 1; dj++) {
if (di == 0 && dj == 0) continue;
if ((di + dj) % 2 != 0) continue;
if (data[start_i + di, start_j + dj] != 'M') continue;
if (data[start_i - di, start_j - dj] != 'S') continue;
even_mas_starts++;
}
}
if (even_mas_starts == 2) counter++;
}
}
return counter.ToString();
}
}