From e24effa8fd3574e9d396be0e1a47f101c27c0a60 Mon Sep 17 00:00:00 2001 From: DJ O'Leary Date: Mon, 3 Mar 2025 00:56:33 +0100 Subject: perf: Merge Advent of Code year specific repos --- 2024/OCaml/lib/day_04/part_01.ml | 84 ++++++++++++++++++++++++++++++ 2024/OCaml/lib/day_04/php/Grid.php | 67 ++++++++++++++++++++++++ 2024/OCaml/lib/day_04/php/Input.php | 25 +++++++++ 2024/OCaml/lib/day_04/php/Position.php | 77 ++++++++++++++++++++++++++++ 2024/OCaml/lib/day_04/php/part_01.php | 94 ++++++++++++++++++++++++++++++++++ 5 files changed, 347 insertions(+) create mode 100644 2024/OCaml/lib/day_04/part_01.ml create mode 100644 2024/OCaml/lib/day_04/php/Grid.php create mode 100644 2024/OCaml/lib/day_04/php/Input.php create mode 100644 2024/OCaml/lib/day_04/php/Position.php create mode 100644 2024/OCaml/lib/day_04/php/part_01.php (limited to '2024/OCaml/lib/day_04') diff --git a/2024/OCaml/lib/day_04/part_01.ml b/2024/OCaml/lib/day_04/part_01.ml new file mode 100644 index 0000000..63c1cfd --- /dev/null +++ b/2024/OCaml/lib/day_04/part_01.ml @@ -0,0 +1,84 @@ +(* Day 4: Part 1 *) + +type character = { index : int; character : char } +type direction = N | NE | E | SE | S | SW | W | NW + +let all_directions = [ N; NE; E; SE; S; SW; W; NW ] +let needle = "XMAS" + +(** [calculate_row_offset lines] is the offset used to move between the rows of the concatenated variant of [lines] as though it were a grid. + Requires: values of [lines] all have the same length *) +let calculate_row_offset lines = + match lines with [] -> 0 | h :: _ -> String.length h + +let join_lines lines = + let concat_trim acc line = acc ^ String.trim line in + List.fold_left concat_trim "" lines + +let explode s = List.init (String.length s) (String.get s) +let is_in_bounds length index = index >= 0 && index < length + +let get_at_index line index = + let length = String.length line in + if not (is_in_bounds length index) then "." + else + let character = String.get line index in + Char.escaped character + +let get_direction_index row_offset direction index = + match direction with + | N -> index - row_offset + | NE -> index - row_offset + 1 + | E -> index + 1 + | SE -> index + row_offset + 1 + | S -> index + row_offset + | SW -> index + row_offset - 1 + | W -> index - 1 + | NW -> index - row_offset - 1 + +let get_direction_string get_char get_offset_index index = + let first = get_char index in + let second_index = get_offset_index index in + let second = get_char second_index in + let third_index = get_offset_index second_index in + let third = get_char third_index in + let fourth_index = get_offset_index third_index in + let fourth = get_char fourth_index in + first ^ second ^ third ^ fourth + +let check_direction get_from_line get_offset_index index direction = + get_direction_string get_from_line (get_offset_index direction) index = needle + +let calculate_num_of_matches line row_offset character = + let get_from_line_at_index = get_at_index line in + let get_offset_direction_index = get_direction_index row_offset in + let check_direction_from_index = + check_direction get_from_line_at_index get_offset_direction_index + character.index + in + all_directions + |> List.map check_direction_from_index + |> List.map (fun x -> match x with true -> 1 | false -> 0) + |> List.fold_left ( + ) 0 + +(* Tried: + 2544 -> too high + 1272 (previous halved) -> too low *) +let solve lines = + let row_offset = calculate_row_offset lines in + let line = join_lines lines in + let chars = explode line in + let characters = List.mapi (fun i c -> { index = i; character = c }) chars in + let xs = + List.filter + (fun c -> match c.character with 'X' -> true | _ -> false) + characters + in + let calculate_num_of_surrounding_matches_in_line = + calculate_num_of_matches line row_offset + in + let num_of_matches = + List.map calculate_num_of_surrounding_matches_in_line xs + |> List.fold_left ( + ) 0 + in + num_of_matches diff --git a/2024/OCaml/lib/day_04/php/Grid.php b/2024/OCaml/lib/day_04/php/Grid.php new file mode 100644 index 0000000..c0382df --- /dev/null +++ b/2024/OCaml/lib/day_04/php/Grid.php @@ -0,0 +1,67 @@ + array_reduce( + array: $row, + callback: fn(string $acc, string $character): string => $acc . $character, + initial: "", + ); + + return array_reduce( + array: $this->data, + callback: fn(string $acc, array $row): string => $acc . $rowToLine($row) . PHP_EOL, + initial: "", + ); + } + + public function getCharacterAtPosition(Position $position): string + { + if ($this->isInBounds($position)) { + return $this->data[$position->y][$position->x]; + } + + return "."; + } + + /** + * @param Position[] $positions + */ + public function getStringFromPositions(array $positions): string + { + $characters = array_map( + callback: fn(Position $position): string => $this->getCharacterAtPosition($position), + array: $positions, + ); + + $string = array_reduce( + array: $characters, + callback: fn(string $acc, string $character): string => $acc . $character, + initial: "", + ); + + return $string; + } + + public function isInBounds(Position $position): bool + { + if (!array_key_exists($position->y, $this->data)) { + return false; + } + + if (!array_key_exists($position->x, $this->data[$position->y])) { + return false; + } + + return true; + } +} diff --git a/2024/OCaml/lib/day_04/php/Input.php b/2024/OCaml/lib/day_04/php/Input.php new file mode 100644 index 0000000..f7e03c1 --- /dev/null +++ b/2024/OCaml/lib/day_04/php/Input.php @@ -0,0 +1,25 @@ +fileName, use_include_path: true); + $this->contents = $contents === false ? "" : $contents; + } + + public function toGrid(): Grid + { + $lines = explode("\n", $this->contents); + $gridArray = array_map( + callback: fn(string $line): array => str_split(trim($line)), + array: $lines + ); + + return new Grid($gridArray); + } +} diff --git a/2024/OCaml/lib/day_04/php/Position.php b/2024/OCaml/lib/day_04/php/Position.php new file mode 100644 index 0000000..182da7e --- /dev/null +++ b/2024/OCaml/lib/day_04/php/Position.php @@ -0,0 +1,77 @@ +x}, {$this->y})"; + } + + /** + * Not validated + */ + public function getNorth(): self + { + return new self($this->x, $this->y - 1); + } + + /** + * Not validated + */ + public function getNorthEast(): self + { + return new self($this->x + 1, $this->y - 1); + } + + /** + * Not validated + */ + public function getEast(): self + { + return new self($this->x + 1, $this->y); + } + + /** + * Not validated + */ + public function getSouthEast(): self + { + return new self($this->x + 1, $this->y + 1); + } + + /** + * Not validated + */ + public function getSouth(): self + { + return new self($this->x, $this->y + 1); + } + + /** + * Not validated + */ + public function getSouthWest(): self + { + return new self($this->x - 1, $this->y + 1); + } + + /** + * Not validated + */ + public function getWest(): self + { + return new self($this->x - 1, $this->y); + } + + /** + * Not validated + */ + public function getNorthWest(): self + { + return new self($this->x - 1, $this->y - 1); + } +} diff --git a/2024/OCaml/lib/day_04/php/part_01.php b/2024/OCaml/lib/day_04/php/part_01.php new file mode 100644 index 0000000..250da0c --- /dev/null +++ b/2024/OCaml/lib/day_04/php/part_01.php @@ -0,0 +1,94 @@ + require "{$className}.php"); + +// Assumes executed from workspace root +$input = new Input('input/day_04.txt'); +if ($input->contents === "") { + throw new RuntimeException("File not found"); +} + +$grid = $input->toGrid(); + +$matchCount = 0; +foreach ($grid->data as $y => $row) { + foreach ($row as $x => $char) { + if ($char !== 'X') { + continue; + } + + $start = new Position($x, $y); + + $possibleMatches = []; + + $possibleMatches["north"] = $grid->getStringFromPositions([ + $start, + $start->getNorth(), + $start->getNorth()->getNorth(), + $start->getNorth()->getNorth()->getNorth(), + ]); + + $possibleMatches["east"] = $grid->getStringFromPositions([ + $start, + $start->getEast(), + $start->getEast()->getEast(), + $start->getEast()->getEast()->getEast(), + ]); + + $possibleMatches["south"] = $grid->getStringFromPositions([ + $start, + $start->getSouth(), + $start->getSouth()->getSouth(), + $start->getSouth()->getSouth()->getSouth(), + ]); + + $possibleMatches["west"] = $grid->getStringFromPositions([ + $start, + $start->getWest(), + $start->getWest()->getWest(), + $start->getWest()->getWest()->getWest(), + ]); + + $possibleMatches["north-east"] = $grid->getStringFromPositions([ + $start, + $start->getNorthEast(), + $start->getNorthEast()->getNorthEast(), + $start->getNorthEast()->getNorthEast()->getNorthEast(), + ]); + + $possibleMatches["north-west"] = $grid->getStringFromPositions([ + $start, + $start->getNorthWest(), + $start->getNorthWest()->getNorthWest(), + $start->getNorthWest()->getNorthWest()->getNorthWest(), + ]); + + $possibleMatches["south-east"] = $grid->getStringFromPositions([ + $start, + $start->getSouthEast(), + $start->getSouthEast()->getSouthEast(), + $start->getSouthEast()->getSouthEast()->getSouthEast(), + ]); + + $possibleMatches["south-west"] = $grid->getStringFromPositions([ + $start, + $start->getSouthWest(), + $start->getSouthWest()->getSouthWest(), + $start->getSouthWest()->getSouthWest()->getSouthWest(), + ]); + + $matchCount += array_reduce( + array: $possibleMatches, + callback: fn(int $acc, string $possibleMatch): int => match ($possibleMatch) { + "XMAS" => $acc + 1, + default => $acc, + }, + initial: 0, + ); + } +} + +// ANSWER: 2517 +echo $matchCount; -- cgit v1.2.3