#AoC 2023 | Day 13 | Solutions & Spoilers

256 messages · Page 1 of 1 (latest)

keen lance
#

Language Roulette is ... Elixir

misty wyvern
#
#..#..####.
#.#####..##
#.##..####.
##.#..####.
..#...#..#.
##...#....#
....##....#
##.##.#..#.
#..#.##..##
##.###.##.#
...#.#.##.#
.#.#.#.##.#
##.###.##.#
#..#.##..##```
what should be the output for this?
shadow linden
#

Which part?

misty wyvern
#

p1

small knoll
#

I get 8 (reflection on x=8) but then again my code doesn't work

#

🙃

misty wyvern
simple fox
#

easy day

#

just changed (a[:trim] == b[:trim]).all() to (a[:trim] != b[:trim]).sum() == 1

meager jacinth
#

well i had np.sum(~(data[i-m:i] == data[i+m-1:i-1:-1]))

#

and then i deleted the second one to let copilot to fill in np.sum(~(data[:, i-m:i]…

#

but didn't notice it also copy ret += 100 * i 💀

small knoll
#

2134/1154 sadge

#
def parse_raw(raw: str):
    return list(raw.split("\n\n")).mapped(Grid.from_string)

def reflect_x(grid: Grid, mirror_x: int) -> list[int]:
    reflect_range = min(mirror_x, grid[0].len() - mirror_x)
    return grid.data.mapped(
        lambda row: sum(
            a != b
            for a, b in zip(
                row[mirror_x - reflect_range : mirror_x],
                row[mirror_x : mirror_x + reflect_range][::-1],
            )
        )
    )

def reflect_y(grid: Grid, mirror_y: int) -> list[int]:
    reflect_range = min(mirror_y, grid.data.len() - mirror_y)
    return list(
        sum(a != b for a, b in zip(a, b))
        for a, b in zip(
            grid.data[mirror_y - reflect_range : mirror_y],
            grid.data[mirror_y : mirror_y + reflect_range][::-1],
        )
    )

def part_one(data=data):
    total = 0
    for i, grid in data.enumerated():
        for mirror_x in range(grid[0].len()):
            if reflect_x(grid, mirror_x).sum() == 0:
                total += mirror_x
        for mirror_y in range(grid.data.len()):
            if reflect_y(grid, mirror_y).sum() == 0:
                total += 100 * mirror_y
    return total

def part_two(data=data):
    total = 0
    for i, grid in data.enumerated():
        for mirror_x in range(grid[0].len()):
            if reflect_x(grid, mirror_x).sum() == 1:
                total += mirror_x
        for mirror_y in range(grid.data.len()):
            if reflect_y(grid, mirror_y).sum() == 1:
                total += 100 * mirror_y
    return total
obtuse cliff
#
from operator import eq

def find_reflection(block, ignore=None):
    for i in range(1, len(block)):
        if all(map(eq, block[:i][::-1], block[i:])):
            o = i*100
            if o == ignore:
                continue
            return o

    block = list(zip(*block))
    for i in range(1, len(block)):
        if all(map(eq, block[:i][::-1], block[i:])):
            o = i
            if o == ignore:
                continue
            return o

def p2_reflection(block):
    block = list(map(list, block))
    base = find_reflection(block)

    for line in block:
        for x, _ in enumerate(line):
            line[x] = "#."[".#".find(line[x])]
            r = find_reflection(block, base)
            line[x] = "#."[".#".find(line[x])]

            if r is not None and r != base:
                return r

    raise ValueError

p = q = 0
for i, block in enumerate(blocks):
    p += find_reflection(block)
    q += p2_reflection(block)

print(p, q)

aaa it took me too long to find out that the part 1 reflection can still be valid

small knoll
obtuse cliff
#

ah i added the restriction when i moved it into a function
it used to just add on directly to p1

#

that would be very annoying though
i'd probably plop the reflection lines into a set then use new_set - base_set

#

that would be very annoying though
by that i mean it wouldn't have been in the test cases again >:c

#

although i think i mainly assumed it bc it kept referring to the lines in the singular

the reflection in each pattern
although i do admit i am lucky lol

#

same thing with day 8 :D

misty wyvern
#

may help golf

meager jacinth
#

i initially interpreted multiple and started working on double reflections lol

#

like, the thing between two mirrors being symmetric

#

then looked in the input went 'why is it not one huge grid'

#

back at the question went 'ah one reflection each'

simple fox
#

cleaned up a bit:

def reflect_axis(pattern, smudge):
    for i in range(1, len(pattern)):
        a = pattern[:i][::-1]
        b = pattern[i:]
        trim = min(len(a), len(b))
        if (a[:trim] != b[:trim]).sum() == smudge:
            return i
    return 0

def reflections(pattern, smudge):
    return 100 * reflect_axis(pattern, smudge) + reflect_axis(pattern.T, smudge)

def part_one():
    return sum(reflections(pattern, 0) for pattern in DATA)

def part_two():
    return sum(reflections(pattern, 1) for pattern in DATA)
lucid spear
#

My solution for p2 using edit distance. I used a strict equality check for p1.

def transpose(pattern: list[str]) -> list[str]:
    """Transpose a pattern."""
    return ["".join(row) for row in zip(*pattern, strict=True)]


def hamming_distance(a: str, b: str) -> int:
    """Calculate the hamming distance between two strings."""
    return sum(1 for x, y in zip(a, b, strict=True) if x != y)


def find_reflection(pattern: list[str], allowed_smudges: int = 1) -> int:
    """Check a pattern for reflections."""
    for index, (a, b) in enumerate(pairwise(pattern)):
        smudges_remaining = allowed_smudges - hamming_distance(a, b)

        if smudges_remaining < 0:
            continue

        for offset in range(1, min(index + 1, len(pattern) - index - 1)):
            smudges_remaining -= hamming_distance(
                pattern[index + 1 + offset], pattern[index - offset],
            )

            if smudges_remaining < 0:
                break
        else:
            if smudges_remaining == 0:
                return index + 1

    return 0


def find_reflections(pattern: list[str]) -> int:
    """Check a pattern for horizontal and vertical reflections."""
    if horizontal := find_reflection(pattern):
        return horizontal * 100

    if vertical := find_reflection(transpose(pattern)):
        return vertical

    return 0


def calculate_solution(path: Path) -> int:
    """Calculate the solution."""
    return sum(find_reflections(pattern) for pattern in read_patterns(path))
hidden terrace
#

Need a little help. for this pattern:

#.##..##.
#...##...
...####..
.########
#..#..#..
#..#..#..
#...##...
.##.##.##
.###..###
#...##...
.###..###

There are both horizontal (at 9) and vertical (at 5) reflection points. what should I do in here?

mental axle
#

assuming you're on part 1, are you sure horizontal reflection at line 9 is valid? a mirror must be placed either between two rows or between two columns

hidden terrace
mental axle
#
from typing import Optional

HORZ_S = 100
VERT_S = 1

def count_mismatch(case: list[str], i: int) -> int:
    if i < len(case)//2:
        check = case[:i+i+2]
    else:
        check = case[i+i+2-len(case):]

    L = len(check)
    return sum(
        sum(int(a != b) for a, b in zip(check[j], check[L-j-1]))
        for j in range(0, L//2)
    )

def find_mirror(case: list[str], case_t: list[str], smudges = 0) -> Optional[int]:
    for m, c in ((HORZ_S, case), (VERT_S, case_t)):
        for i in range(len(c)-1):
            if count_mismatch(c, i) == smudges:
                return m * (i+1)

    print(*case, sep='\n')
    return None

with open(0) as f:
    cases = list(map(str.splitlines, f.read().split('\n\n')))
    cases_t = [ list(map(''.join, zip(*case))) for case in cases ]


print(sum(find_mirror(case, case_t) for case, case_t in zip(cases, cases_t)))
print(sum(find_mirror(case, case_t, 1) for case, case_t in zip(cases, cases_t)))
#

darn, I just realized I used a soft keyword

hollow tinsel
# hidden terrace oh thx, got the answer 🤦‍♂️

starting to get a too terse again, someone stop me, I'm not trying to golf.

rotate = lambda b: list(map(''.join, zip(*b)))
smudge = lambda board,i: sum(sum(a!=b for a,b in zip(row[:i][::-1],row[i:])) for row in board)

def mirror(board):
    return next((i for i in range(1,len(board[0])) if smudge(board,i) == 1),0)

boards = [b.splitlines() for b in open(aocinput).read().split('\n\n')]
print(sum(mirror(board) + 100*mirror(rotate(board))for board in boards))
mental axle
#

that feeling when you want to shorten it but retain the whitespaces and proper variable names

hollow tinsel
#

I swear, I find that readable. 🙂

#

there I made it shorter AND more readable.

mental axle
#

although you could argue the shortened version is less readable

small knoll
#

I can never remember how [::-1] works when you include slicing indices lol

#

Would it be [i::-1] or is that subtly different lol?

mental axle
#

off by 1 XD

#

yes

#

I think its [i-1::-1]

small knoll
#

(this is why I always just [][::-1])

mental axle
#

but it makes sense because [start:stop] start is inclusive and stop isn't

haughty maple
#
from pathlib import Path
from itertools import groupby


def grid_diff(g1, g2):
    return sum(a != b for r1, r2 in zip(g1, g2) for a, b in zip(r1, r2))


def find_hori_mirror(grid: list[str], diff=0):
    for i in range(1, len(grid)):
        left_slice, right_slice = grid[:i][::-1], grid[i:]
        size = min(len(left_slice), len(right_slice))
        if grid_diff(left_slice[:size], right_slice[:size]) == diff:
            return i
    return 0


def solve(notes, diff):
    return sum(
        100 * find_hori_mirror(note, diff)
        + find_hori_mirror(list(zip(*note[::-1])), diff)
        for note in notes
    )


with open(Path(__file__).parent / "input.txt") as f:
    notes = [list(g) for _, g in groupby(f.read().splitlines(), lambda t: bool(t))][::2]

print(solve(notes, 0), solve(notes, 1))
```I though the catch of part2 was gonna be that comparing strings directly was too slow... guessed wrong
woven lantern
#

This was a fun puzzle. I solved it super verbosely/dumbly, so it's fun now reading all the very clever ways.

#

And... the golf ways. @hollow tinsel XD

worldly pawn
#
def parser(raw_data: TextIO):
    # noinspection PyTypeChecker
    return [
        np.genfromtxt(StringIO(grid), delimiter=1, dtype=np.str_, comments=None) == '#'
        for grid in raw_data.read().split('\n\n')
    ]


def part_a_solver(data):
    return sum(
        next((
            col
            for col in range(1, grid.shape[1])
            if np.array_equal(
                np.flip(grid[:, col - (size := min(col, grid.shape[1] - col)):col], 1),
                grid[:, col: col + size]
            )), 0
        )
        or
        100 * next(
            row
            for row in range(1, grid.shape[0])
            if np.array_equal(
                np.flip(grid[row - (size := min(row, grid.shape[0] - row)):row], 0),
                grid[row: row + size]
            )
        )
        for grid in data
    )


def part_b_solver(data):
    return sum(
        next((
            col
            for col in range(1, grid.shape[1])
            if np.count_nonzero(
                np.flip(grid[:, col - (size := min(col, grid.shape[1] - col)):col], 1) !=
                grid[:, col: col + size]
            ) == 1), 0
        )
        or
        100 * next(
            row
            for row in range(1, grid.shape[0])
            if np.count_nonzero(
                np.flip(grid[row - (size := min(row, grid.shape[0] - row)):row], 0) !=
                grid[row: row + size]
            ) == 1
        )
        for grid in data
    )

just used numpy, but in hindsight doing a zip would likely be easier

mental axle
#
from typing import Optional

HORZ_S = 100
VERT_S = 1

def find_mirror(case: list[str], smudges = 0) -> Optional[int]:
    case_t = list(map(''.join, zip(*case)))
    for m, c in ((HORZ_S, case), (VERT_S, case_t)):
        for i in range(len(c)-1):
            if sum(
                sum(int(a != b) for a, b in zip(line1, line2))
                for line1, line2 in zip(c[i+1:], c[i::-1])
            ) == smudges:
                return m * (i+1)

    print(*case, sep='\n')
    return None

with open(0) as f:
    cases = list(map(str.splitlines, f.read().split('\n\n')))

for smudge in (0, 1):
    print(sum(find_mirror(case, smudge) for case in cases))

inspired by xelf to be terse but readable

vocal ether
#

realized i never sent my solution trollchad

#

Part 1

f = open('INPUT.IN')
def validate(matrix):
    for i in range(1, len(matrix)):
        l = min(i,len(matrix)-i)
        valid = all(matrix[i-k] == matrix[i+k-1] for k in range(1,l+1))
        if valid: return i
    return 0
def evaluate(matrix):
    transpose = [[matrix[i][j] for i in range(len(matrix))] for j in range(len(matrix[0]))]
    return validate(matrix) * 100 + validate(transpose)


s = 0
while k := f.readline():
    m = [list(k.strip('\n'))]
    while (newline := f.readline()) not in ['\n','']:
        newline = newline.strip('\n')
        m.append(list(newline))
    ans = evaluate(m)
    print(ans)
    s += ans

print(s)

Part 2

f = open('INPUT.IN')
def validate(matrix):
    for i in range(1, len(matrix)):
        l = min(i,len(matrix)-i)
        # valid = all(matrix[i-k] == matrix[i+k-1] for k in range(1,l+1))
        diff = 0
        for k in range(1,l+1):
            for j in range(len(matrix[i])):
                diff += (matrix[i-k][j] != matrix[i+k-1][j])
        if diff == 1: return i
    return 0
def evaluate(matrix):
    transpose = [[matrix[i][j] for i in range(len(matrix))] for j in range(len(matrix[0]))]
    return validate(matrix) * 100 + validate(transpose)


s = 0
while k := f.readline():
    m = [list(k.strip('\n'))]
    while (newline := f.readline()) not in ['\n','']:
        newline = newline.strip('\n')
        m.append(list(newline))
    ans = evaluate(m)
    print(ans)
    s += ans

print(s)
dapper bramble
#

Wanted to post this here

#
type Split<S extends string, Delim extends string> =
    S extends `${infer X}${Delim}${infer R}` ? [X, ...Split<R, Delim>]
    : Delim extends "" ? []
    : [S];

type To2dTuple<T extends string> =
    T extends `${infer X}\n${infer R}` ? [Split<X, "">, ...To2dTuple<R>]
    : [Split<T, "">];

type Reverse<T extends unknown[]> =
    T extends [infer X, ...infer Rest] ? [...Reverse<Rest>, X]
    : [];

type Transpose<T extends unknown[][], Col extends unknown[] = [], Result extends unknown[] = []> =
    Col["length"] extends T["length"] ? Transpose<T, [], [...Result, Col]>
    : T extends [][] ? Result
    : T extends [infer Row, ...infer Others extends unknown[][]]
    ? Row extends [infer X, ...infer Rest]
    ? Transpose<[...Others, Rest], [...Col, X], Result>
    : never
    : never;

type IsMirror<A extends unknown[], B extends unknown[]> =
    A extends [] ? false
    : B extends [] ? false
    : Reverse<A> extends [...B, ...infer _] ? true
    : Reverse<B> extends [...infer _, ...A] ? true
    : false;

type FindHorizontalMirror<Right extends unknown[], Left extends unknown[] = []> =
    IsMirror<Left, Right> extends true ? Left
    : Right extends [infer X, ...infer R]
    ? FindHorizontalMirror<R, [...Left, X]>
    : [];

type Mul<A extends unknown[], B extends unknown[]> =
    A extends [infer _, ...infer Rest] ? [...B, ...Mul<Rest, B>]
    : [];

type Two = [unknown, unknown];
type Five = [unknown, unknown, unknown, unknown, unknown];
type OneHundred = Mul<Two, Mul<Two, Mul<Five, Five>>>;

type SolveBoard<T extends unknown[][]> = [
    ...Mul<FindHorizontalMirror<T>, OneHundred>,
    ...FindHorizontalMirror<Transpose<T>>,
];

type SolveBoards<T extends string[]> =
    T extends [infer X extends string, ...infer R extends string[]]
    ? [...SolveBoard<To2dTuple<X>>, ...SolveBoards<R>]
    : [];

type Solve<T extends string> = SolveBoards<Split<T, "\n\n">>["length"];


// Testing

type Test = Solve<`#.##..##.
..#.##.#.
##......#
##......#
..#.##.#.
..##..##.
#.#.##.#.

#...##..#
#....#..#
..##..###
#####.##.
#####.##.
..##..###
#....#..#`>;
worldly pawn
#

right thread FubukiYay

dapper bramble
#

It doesn't work on the actual input though

#

it's too long and TypeScript doesn't like my sins

dapper bramble
worldly pawn
#

idk typescript but that type OneHundred looks cursed

dapper bramble
#

It is pretty cursed

#

idk

#

I can't think of another way to do arithmetic

#

maybe Peano

tall kelp
#

how do you know that for each one there is only one that could have been smudged? (part 2)

golden tide
#

part 1. ```py
with open("aoc13.txt") as f:
text = f.read()
blocks = text.split("\n\n")
blocklines = [block.split("\n") for block in blocks]
blockcollumns = []
count = 0
for block in blocklines:
blockcollumns.append(list(map("".join, zip(*block))))

def findsymetry(argument):
length = len(argument)
for i in range(1, len(argument), 1):
searchlenght = min(len(argument[:i]), len(argument[i:]))
if argument[:i][-searchlenght:] == argument[i:][:searchlenght][::-1]:
return i
return 0

for i in range(len(blocks)):
result = findsymetry(blockcollumns[i])
if result == 0:
result = 100 * findsymetry(blocklines[i])
count += result
print(count)```

worldly pawn
#

ie. has a unique answer

#

it's fine to assume the problem is well defined for aoc imo

tall kelp
#

it works for part 1

#

but the number outputted for part 2 is around 100 times larger than part 1

#

aoc says my answer is too high

#

is it overcounting in some way?

golden tide
#

part 2: so you can have 2 axis in the same field?

haughty maple
tall kelp
#

ok

#

my program is probably detecting more than one smudge

#

i don't know why

golden tide
tall kelp
# golden tide with what logic are you detecting a smudge?

i'm just iterating through the 2d array and seeing if changing it to the opposite one makes a reflection work in either the horizontal or vertical axis
at that point, i break out of both loops and go onto the next 2d array (the breaking out i just added and it reduced my output to something similar to part 1 but is still not right for part 2)

golden tide
#

wouldn't there always be 2 possible smudges?

mental axle
#

I think it's designed so that there's only one solution

golden tide
#

I was thinking does it matter if I change the smudge on the topside or the bottom. the reflection would still work

jolly sand
#
YEAR = 2023
DAY = 13
_U = 1
if _U:
    from aoc_lube import fetch, submit
    s = fetch(YEAR, DAY)
else: exec(r'''
s=r"""
#.##..##.
..#.##.#.
##......#
##......#
..#.##.#.
..##..##.
#.#.##.#.

#...##..#
#....#..#
..##..###
#####.##.
#####.##.
..##..###
#....#..#
"""
''');s=s.strip()

from itertools import islice

*r, = map(str.splitlines, s.split('\n\n'))

p1 = 0

tb = {}

for F in 100, 1:
 for I, x in enumerate(r):
  for i in range(len(x)-1):
   if x[i] == x[i := i + 1]:
    print("@@#!")
    print(x[i-1], x[i])
    i2 = i + i - 1
    for j in range(i - 1):
     if i2 - j < len(x) and x[j] != x[i2 - j]: break
     if i2 - j < len(x): print(x[j], x[i2 - j])
    else: break
  else: continue
  tb[I] = (F, i)
  p1 += i * F
 r = [[*zip(*x)] for x in r]

ff = lambda x, y: sum(a != b for a, b in zip(x, y))

new = tb.copy()

for F in 100, 1:
 for I, x in enumerate(r):
  #if I in found: continue
  for i in range(len(x)-1):
   c = ff(x[i], x[i := i + 1])
   if c <= 1:
    print("@@#!")
    print(F, I, i, x[i-1], x[i])
    i2 = i + i - 1
    for j in range(i - 1):
     if i2 - j < len(x):
      c += ff(x[j], x[i2 - j])
      if c>1:break
    else:
     if c==1:break
  else: continue
  t = (F, i)
  if t != tb[I]:
   print(t, tb[I])
   new[I] = t
 r = [[*zip(*x)] for x in r]

p2 = sum(x * y for x, y in new.values())

if _U:
    submit(YEAR, DAY, 1, lambda: p1)
    submit(YEAR, DAY, 2, lambda: p2)
else:
    print(p1)
    print(p2)
#

finally

#

no timeouts

lyric quiver
#
def calculate_reflection(p1, p2, y=-1, smudge=False):
    if e and len([u for u in range(len(d[e])) if d[e][u] != d[e - 1][u]]) < 2:
        for i in range(e, len(d) + 1):
            y += 2
            if i - y < 0 or i == len(d):
                if smudge: p2 += e * multiplier
                else:      p1 += e * multiplier
                break
            if d[i] != d[i - y]:
                if not smudge and len([u for u in range(len(d[i])) if d[i][u] != d[i - y][u]]) == 1: 
                    smudge = True
                else: break
    return p1, p2

with open("day13.txt", "r") as file:
    data = file.read().split("\n\n")
    p1 = p2 = 0
    for grid in data:
        h = [*map(list, grid.split())]
        v = [*map(list, zip(*h))]
        for d, multiplier in ((h, 100), (v, 1)):
            for e, x in enumerate(d):
                p1, p2 = calculate_reflection(p1, p2)
    print(p1, p2)```
real jewel
#
from itertools import product

from aoctk.data import Unbound2DGrid as U2DG
from aoctk.input import get_groups as gs


def mirror(m: U2DG, a: int, b: int, d: complex, t: int = 0) -> int:
    c, fm = (a + b) / 2 * d, {_ for _ in m if a <= (d.conjugate() * _).real <= b}
    return (
        int((a + b + 1) / 2 * (1 if d == 1 else 100))
        if len({_ - 2 * (d.conjugate() * (_ - c)).real * d for _ in fm} - fm) == t
        else 0
    )


def value(m: U2DG, t: int = 0) -> int:
    for (b, d), s in product(zip(m.size(), (1, 1j)), (2, -2)):
        for c in range(b % 2 if s > 0 else b - 1 - (b % 2), b - 1 if s > 0 else 0, s):
            if n := mirror(m, c if s > 0 else 0, b - 1 if s > 0 else c, d, t):
                return n


def solve(data: str, t: int) -> int:
    return sum(
        value(m, t)
        for m in (U2DG.from_group(g, filter=lambda _: _ != ".") for g in gs(data))
    )


def part_one(data="input.txt"):
    return solve(data, 0)


def part_two(data="input.txt"):
    return solve(data, 1)
real jewel
#

made solve a one-liner XD

def solve(data: str, t: int) -> int:
    return sum(
        next(
            dropwhile(
                lambda _: _ == 0,
                (
                    mirror(p, c if s > 0 else 0, b - 1 if s > 0 else c, d, t)
                    for (b, d), s in product(zip(p.size(), (1, 1j)), (2, -2))
                    for c in range(
                        b % 2 if s > 0 else b - 1 - (b % 2), b - 1 if s > 0 else 0, s
                    )
                ),
            )
        )
        for p in (U2DG.from_group(g, filter=lambda _: _ != ".") for g in gs(data))
    )
worldly pawn
#

one liner gang let's go

golden tide
#

I'm a bit confused here

cursive moat
mental axle
#

a challenge where you solve the puzzle in a different language each day, the language is selected before the puzzle is released and is announced in the thread

obsidian pewter
#

Setup

with open('day13/input.txt') as f:
    data = f.read().split('\n\n')

patterns = [[list(line) for line in pattern] for pattern in [pattern.splitlines() for pattern in data]]
ans = 0```

P1
```py
def solve(pattern):
        for index in range(len(pattern[1:-1])+1):
            if all(pair[0] == pair[1] for pair in zip(pattern[:index+1][::-1],pattern[index+1:])):
                return index+1

for pattern in patterns:
    val = solve(pattern)
    if val:
        ans += val*100
    else:
        ans += solve(list(zip(*pattern)))
print(ans)```
P2
```py
def solve(pattern,og):
        for index in range(len(pattern[1:-1])+1):
            if all(pair[0] == pair[1] for pair in zip(pattern[:index+1][::-1],pattern[index+1:])):
                if index+1 == og:
                    continue
                return index+1

for pattern in patterns:
    og_val = (solve(pattern,-1),0)
    if not og_val[0]:
        og_val = (solve(list(zip(*pattern)),-1),1) 
    for ri,row in enumerate(pattern):
        for index,char in enumerate(row):
            new_pattern = deepcopy(pattern)
            new_pattern[ri][index] = '#' if char == '.' else '.'
            val = solve(new_pattern,og_val[0] if not og_val[1] else -1)

            if val: 
                ans += val*100
                break
            else:
                val = solve(list(zip(*new_pattern)),og_val[0] if og_val[1] else -1)
                if val:
                    ans += val
                    break
        else:
            continue
        break
print(ans)

Quite proud of my part1! I thought it was quite elegant. Not so much my part2....
As always, lmk any obvious improvements!

golden tide
#
with open("aoc13.txt") as f:
    text = f.read()
blocks = text.split("\n\n")
blocklines = [block.split("\n") for block in blocks]
blockcollumns = []
count = 0
for block in blocklines:
    blockcollumns.append(list(map("".join, zip(*block))))


def findsymetry(argument):
    for i in range(1, len(argument), 1):
        searchlenght = min(len(argument[:i]), len(argument[i:]))
        leftside, rightside = argument[:i][-searchlenght:], argument[i:][:searchlenght][::-1]
        a = [x != y for x, y in zip(leftside, rightside, strict=True)]
        if sum(a) == 1 or a == [True]:
            index = a.index(True)
            b = [x != y for x, y in zip(leftside[index], rightside[index], strict=True)]
            if sum(b) == 1:
                return i
    return 0

for i in range(len(blocks)):
    result = findsymetry(blockcollumns[i])
    if result == 0:
        count += 100 * findsymetry(blocklines[i])
    else:
        count += result
print(count)``` my p2
#

I just cleaned it up a bit

#

I like that I didn't have to brute force it completely

#

and I forgot to delete some testing artefacts it seems.

mental axle
#
def find_mirror(case: list[str], smudges = 0) -> int:
    case_t = list(map(''.join, zip(*case)))
    for m, c in ((100, case), (1, case_t)):
        for i in range(1, len(c)):
            if sum(
                sum(int(a != b) for a, b in zip(line1, line2))
                for line1, line2 in zip(c[i:], c[i-1::-1])
            ) == smudges:
                return m * i

    raise ValueError

cases = list(map(str.splitlines, open(0).read().split('\n\n')))
print(*(sum(find_mirror(case, smudge) for case in cases) for smudge in (0, 1)), sep='\n')

importless common solver
so done with this now 😮‍💨

golden tide
mental axle
#

yes! #aoc-solution-hints message

golden tide
#

what do you think of my solution?

mental axle
#

although now that you understand what it's doing, there are several places you can simplify

#

by the way,

if sum(a) == 1 or a == [True]:
calling sum on [True] yields 1 as well

golden tide
#

oh yeah. fixed that now

golden tide
night root
#
###..##.######.##
..#..#.#......#.#
#.##.#..#.##.#..#
#.#.##.#..##..#.#
#..#...###..###..
#..#...###..###..
#.#.##.#..##..#.#
#.##.#..#.##.#..#
..#..#.#......#.#
###..##.######.##
...#.#..######..#
..###....#..#..#.
#.###....#..#....
##.#####.#..#.###
...#.....####....
..#...#.#.##.#.#.
...###.#......#.#```
#

where is the reflection point here

#

I'm sure I'm probably just being blind but I genuinely can't see it

golden tide
#

part 2 or 1?

night root
#

1

golden tide
#

row 5

night root
#

ah yes I was indeed being blind

#

cheers matey

steel shale
# night root where is the reflection point here
###..##.######.##
..#..#.#......#.#
#.##.#..#.##.#..#
#.#.##.#..##..#.#
#..#...###..###..
-----------------
#..#...###..###..
#.#.##.#..##..#.#
#.##.#..#.##.#..#
..#..#.#......#.#
###..##.######.##

...#.#..######..#
..###....#..#..#.
#.###....#..#....
##.#####.#..#.###
...#.....####....
..#...#.#.##.#.#.
...###.#......#.#
night root
#

danke

inland hearth
steel shale
#

I was using someone's part 2 code to verify my code's part 1 output 😅 well that's a few hours I will never get back

inland hearth
#

lol.

#

My code above looks scary, but will run easily with a single pip install duckdb.

steel shale
#

finally finished my day 13 part 1 solution, not sure how to approach part2 but I have a feeling that my approach did not give me a good baseline for part2. Any help appreciated (I don't really understand how to effectively find smudges)
https://paste.pythondiscord.com/PYLQ

worldly pawn
steel shale
# worldly pawn how are you finding reflections rn?

I find 2 neighbouring cols/rows that are the same, then I step away from them in both directions one index at a time and compare the rows/cols at the new indices, e.g. 1221 I would first find 22, then compare 11 etc. until I reach the edge. If I reach the edge, I found reflection, otherwise I continue scanning

worldly pawn
#

pithink hard to give a hint without spoiling it

inland hearth
#

So, smudge is just a row with one diff

steel shale
inland hearth
golden tide
#

i basically checked all mirror positions until i found a working one

#

for two I look for one who with one row difference

steel shale
golden tide
#

and then I check if the two rows with one difference have a difference of one between them

worldly pawn
steel shale
golden tide
#

I basically xor the left and right side of the mirror.

hollow tinsel
golden tide
#

i did it like that as well. tried slicing in one but couldn't wrap my head around it

hollow tinsel
# woven lantern And... the golf ways. <@255074198559391744> XD

I don't golf! I'm just terse!

rotate = lambda b: list(map(''.join, zip(*b)))
smudge = lambda board,i: sum(sum(a!=b for a,b in zip(row[:i][::-1],row[i:])) for row in board)
mirror = lambda board: next((i for i in range(1,len(board[0])) if smudge(board,i) == 1),0)
totals = lambda board: mirror(board) + 100*mirror(rotate(board))
print(sum(map(totals,map(str.splitlines,open(aocinput).read().split('\n\n')))))
inland hearth
#

So I pruned so I only looked at adjacent mirrors, then expanded 1 at a time

golden tide
#

i don't think the difference in time is significant here. I guess its a bit faster

hollow tinsel
golden tide
#

(your thing that is)

inland hearth
#

I never solved yesterday in sql

golden tide
#

i never solved yesterday.

#

i should try tough

inland hearth
#

I might just try porting someone else’s solution

#

I wrote an ugly version in Python, but it’s not transferable to sql.

#

But there are some far cleverer solutions that might work

hollow tinsel
golden tide
#

No. Will need to learn how to do recursion properly

woven lantern
golden tide
#

Yes but do I want to do that? No

hollow tinsel
hollow tinsel
woven lantern
hollow tinsel
golden tide
#

Simulate as in permutations?

hollow tinsel
#
print( [(i-1, ~-i) for i in range(5)] )
# [(-1, -1), (0, 0), (1, 1), (2, 2), (3, 3)]

~ is the bit inversion operator

fresh linden
#

Parts 1 + 2, part 2 wasn't the worst once I remembered that this is an AoC puzzle with the associated constraints, but it would be nice if they were given explicitly, ie. each pattern will only have one valid reflection line, each pattern with one smudge removed will have only one valid reflection line. py import numpy as np part_1 = 0 part_2 = 0 for index, line in enumerate(open(0).read().split("\n\n")): arr = np.array([*map(list, line.split("\n"))]) for mult in (1, 100): arr = arr.T for row in range(1, (arr.shape[0] + 1) // 2): top_end = arr[0:row] == arr[row:row*2][::-1] bottom_end = arr[arr.shape[0] - (row * 2):arr.shape[0] - row] == arr[arr.shape[0] - row:][::-1] if top_end.all(): part_1 += mult * row if np.sum(top_end) + 1 == top_end.shape[0] * top_end.shape[1]: part_2 += mult * row if bottom_end.all(): part_1 += mult * (arr.shape[0] - row) if np.sum(bottom_end) + 1 == bottom_end.shape[0] * bottom_end.shape[1]: part_2 += mult * (arr.shape[0] - row) print(part_1, part_2)

woven lantern
fresh linden
#

I'm just lazy, imagine turning on your brain for a coding challenge :p

hollow tinsel
hollow tinsel
fresh linden
#

There is something to be said about the test cases not having full coverage. For example, my first part 1 code worked for the tests but not actual input, since both test cases are bottom ended reflection with 1 discarded row, leaving top ended reflection calculations and non-1 row bottom end discards untested. This is mitigated by being able to see all the inputs, but it does also hamper shareability of test cases due to the larger size.

hollow tinsel
#

for example, an approach that seems within the rules that gets you the correct answer on the test examples, but the wrong answer on part 2: iterating over possible smudges and then searching for reflections. The only way I found out this was wrong was when the answer was wrong and had to try something completely different. (iterate over failed reflections and look to see if a smudge would fix it)

fresh linden
#

The root of the issue is whatever algorithm was used to generate the inputs/calculate the answer. If either of those are biased, then the resulting problem has to also be solved with that bias, in this case checking for smudged reflections in a certain order. That is where explicitly stating the problem constraints would help, but at that point why no just go to leetcode. The biases in the used algorithms should obviously be minimized as much as possible, but while that is really easy to say, it is basically impossible until the problem reaches a large audience.

hollow tinsel
#

What I would suggest is more example test cases, they should be easy enough to generate. Perhaps have them hidden until after you submit a failed answer.

fresh linden
#

That would help a lot on the days where the test cases and actual input are orders of magnitude different in size instead of just more, like days 10 and 8.

hollow tinsel
#

Like "What number comes next" 2, 3, ...
With no other test cases, it's hard to know. are we looking at digits in order? prime numbers, numbers with a specific binary pattern?

#

Ah well. Bracing for tonight's possibly harder puzzle. 🙂

slate light
#

That took way too long to debug...

hollow tinsel
woven lantern
# hollow tinsel The problem here was that the constraints were not discoverable unless you had a...

I can certainly understand the frustration with the test cases not being comprehensive.

On the other hand, I appreciate it as a part of discovery on the problem. Spelling out all the corner cases makes it easier to code towards an answer, but removes part of the puzzle solving.

Looking back at 2023 Day 1 as an example:

||If the test cases included twone or oneight, then some of the potential hiccups of part 2 are removed and the answer is much easier for people to solve.|| I really do look at uncovering corner cases as a part of the puzzle.

jolly sand
woven lantern
hollow tinsel
woven lantern
slate light
#

Didn't bother with an OOP solution today

pine mural
#

Elixir is quite nice

#
defmodule Aoc do
  def twoscan(line, f) do
    f.(line)
      |> Enum.chunk_every(2, 2, :discard)
      |> Enum.scan(fn x, acc -> Enum.concat(acc, x) end)
      |> Enum.map(&f.(&1))
  end

  def count(start, delta) do Stream.iterate(start, &(&1+delta)) end

  def segments(line) do
    Enum.concat(
      Enum.zip(count(1, 1), line |> twoscan(&(&1))),
      Enum.zip(count(length(line)-1, -1), line |> twoscan(&Enum.reverse/1))
    )
  end

  def mismatches(line) do
    Enum.zip(line, Enum.reverse(line)) |> Enum.count(fn {a, b} -> a != b end)
  end

  def transpose(a) do Enum.zip(a) |> Enum.map(&Tuple.to_list/1) end

  def find_mirror(lines, t) do
    lines
      |> Enum.map(&segments/1)
      |> transpose
      |> Enum.map(&Enum.unzip/1)
      |> Enum.filter(fn {_, x} -> Enum.map(x, &mismatches/1) |> Enum.sum == 2*t end)
      |> Enum.map(&elem(&1, 0) |> Enum.at(0))
      |> Enum.sum
  end

  def solve(lines, t) do
    (lines |> find_mirror(t)) + (lines |> transpose |> find_mirror(t)) * 100
  end
end

input = IO.read(:stdio, :all)
  |> String.trim
  |> String.split("\n\n")
  |> Enum.map(fn x -> x |> String.split("\n") |> Enum.map(&String.graphemes/1) end)

input |> Enum.map(fn x -> Aoc.solve(x, 0) end) |> Enum.sum |> IO.inspect(label: "Part 1")
input |> Enum.map(fn x -> Aoc.solve(x, 1) end) |> Enum.sum |> IO.inspect(label: "Part 2")
woven lantern
#

Pretty language!

pine mural
#

I've definitely added |> to my list of nice language features I want more languages to have

#

(it looks even prettier with ligatures)

woven lantern
#

screenshot?

pine mural
#

triangles

woven lantern
#

Very nice

pine mural
#

it's kinda fascinating how fast one can pick up enough of a language to solve a problem

#

but yeah, this was for sure one of my nicer experiences so far

a mostly functional language that feels very flexible when writing code, with a great stdlib

hollow tinsel
#

Nice. Congrats

pine mural
#

out of the ones so far this and Scala have been the highlights

hollow tinsel
#

seems like it might be a bit more fun to do several problems in a row on the same language so you have more time to explore it or build up some common functions like loading the data.

pine mural
#

oh, F# was also a nice surprise

#

.NET OCaml

pine mural
#

Some languages I would like to play with more

#

Some languages I'm just happy to be done with 😛

keen lance
#

I can pick fortran again for you =P

pine mural
#

fortran was indeed a thing

#

fortran does feel a lot like C in the sense of you getting nothing handed to you

#

and generics? forget it

#

I'm guessing C will show up sooner or later

woven lantern
woven lantern
pine mural
woven lantern
pine mural
#

I did rust last two years, but that kinda got samey

#

the beginning of the first year was very much me going "I know exactly what I need to write, but I don't know how to spell it in rust"

inland hearth
fresh linden
worldly pawn
fresh linden
hollow tinsel
#

There might be only one reflection per smudge. But there are multiple possible smudges for some patterns

hollow tinsel
woven lantern
hollow tinsel
woven lantern
cursive moat
#

Is there an O(n) tilt solution possible? maybe n being number of #s? I don't see how that is possible without counting Os. Which makes it O(n^2)

woven lantern
dull prism
#

brute force today

mental axle
#

welcome to day 13! you have time-travelled successfully

dull prism
#

hehe :)

lusty drum
#

are vertical rows with smudges a thing?

dull prism
#

I might check my real input on this point when I’m back at my desk

lusty drum
#

Based on that my code returns 0 matches with cords that will have matches if you change one vertical point im prettu sure they are there

dull prism
lusty drum
#

So basicly i figured by looking at my data that i currently have that some of them return 0 matches

#

And when i look at those paterns with 0 matches in my input i found out that they indeed dont have horizontal matches

#

Yet there is a option for a vertical match

dull prism
#

Right - that makes perfect sense to me

lusty drum
#

how do i need to handle this case:

..#..####..#.
.############
.############
..#..####..#.
....##..##...
###......#.##```
#

it can both have a horizontal and vertical line

#

or does every patern have to have a new line

young parcel
#

doesn't it just have a horizontal line of reflection?

lusty drum
young parcel
#

oh, you're doing part 2?

lusty drum
#

yep

young parcel
#

the puzzle says ||each mirror has exactly 1 smudge||

lusty drum
#

so i guess you have to always remove that smudge

young parcel
#

yes, you only consider cases where it's off by 1

lusty drum