{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Reflections on symmetry\n", "\n", "- https://adventofcode.com/2023/day/13\n", "\n", "This is a very easy problem to solve with numpy arrays; we can use `numpy.split()` to divide the image in two parts, and `numpy.fliy()` to mirror one of them. I do so for the first split half, as that's easier to then slice (from zero to the smaller width).\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import typing as t\n", "from dataclasses import dataclass\n", "\n", "import numpy as np\n", "from numpy.typing import NDArray\n", "\n", "\n", "@dataclass\n", "class ValleyPattern:\n", " pattern: NDArray[np.bool_]\n", "\n", " @classmethod\n", " def from_text(cls, text: str) -> t.Self:\n", " return cls(np.array([[c == \"#\" for c in line] for line in text.splitlines()]))\n", "\n", " def _reflection(self, axis: t.Literal[0, 1], difference: int = 0) -> int:\n", " patt, size = self.pattern, self.pattern.shape[axis]\n", " for i in range(1, size):\n", " rev, fwd = np.split(patt, [i], axis=axis)\n", " idx = range(min(i, size - i))\n", " if (\n", " np.sum(\n", " fwd.take(idx, axis=axis)\n", " ^ np.flip(rev, axis=axis).take(idx, axis=axis)\n", " )\n", " == difference\n", " ):\n", " return i\n", " return 0\n", "\n", " def summary(self, difference: int = 0) -> int:\n", " return (\n", " self._reflection(1, difference)\n", " or 100 * self._reflection(0, difference)\n", " or 0\n", " )\n", "\n", "\n", "test_patterns = \"\"\"\\\n", "#.##..##.\n", "..#.##.#.\n", "##......#\n", "##......#\n", "..#.##.#.\n", "..##..##.\n", "#.#.##.#.\n", "\n", "#...##..#\n", "#....#..#\n", "..##..###\n", "#####.##.\n", "#####.##.\n", "..##..###\n", "#....#..#\n", "\"\"\".split(\n", " \"\\n\\n\"\n", ")\n", "test_valley_patterns = [ValleyPattern.from_text(blk) for blk in test_patterns]\n", "assert sum(p.summary() for p in test_valley_patterns) == 405" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Part 1: 33520\n" ] } ], "source": [ "import aocd\n", "\n", "pattern_blocks = aocd.get_data(day=13, year=2023).split(\"\\n\\n\")\n", "valley_patterns = [ValleyPattern.from_text(blk) for blk in pattern_blocks]\n", "print(\"Part 1:\", sum(p.summary() for p in valley_patterns))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 1 spot makes all the difference\n", "\n", "Insteod of looking for a simple equality, you can find the reflection with a single smudge by looking at the _difference_ between the two parts being equal to 1. Simple!\n", "\n", "I refactored part 1 to sum the XOR (boolean difference) of the two parts, and for part 2 the difference needs to be 1 instead of 0.\n" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "assert sum(p.summary(1) for p in test_valley_patterns) == 400" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Part 2: 34824\n" ] } ], "source": [ "print(\"Part 2:\", sum(p.summary(1) for p in valley_patterns))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "adventofcode-bRnAxXn--py3.12", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.0" } }, "nbformat": 4, "nbformat_minor": 2 }