Rešitev

Tole imamo še od prej.

import numpy as np
import re

instr = np.array([[line.startswith("on")] +
                  [int(x) for x in re.findall("-?\d+", line)]
                  for line in open("example2.txt")])

instr[:, [2, 4, 6]] += 1

Zdaj zberimo vse možne koordinate x, vse možne koordinate y in vse možne koordinate z. Koordinate x najdemo v stolpcih 1 in 2. Vzemimo takšno matriko, sploščimo jo in sestavimo množico njenih elementov. Podobno za y in z.

xs = set(instr[:, 1:3].flatten())
ys = set(instr[:, 3:5].flatten())
zs = set(instr[:, 5:7].flatten())

Os x bo razrezana na vseh številkah, ki se pojavijo v xs. Uredimo te meje in jih zložimo v tabele. Da so v tabelah, nam bo prišlo prav malo kasneje.

xa = np.array(sorted(xs))
ya = np.array(sorted(ys))
za = np.array(sorted(zs))

Zdaj pa pripravimo slovarje, s katerimi bomo za vsako mejo izvedeli njihovo zaporedno številko.

xs = {x: i for i, x in enumerate(xa)}
ys = {x: i for i, x in enumerate(ya)}
zs = {x: i for i, x in enumerate(za)}

Dimenzije tabele, ki predstavlja stanje reaktorja bodo enake številu blokov. 1 nam ni potrebno prišteti, ker zadnje število vedno predstavlja le gornjo mejo; bloka s to številko nikoli ne bomo prižgali ali ugasnili.

reactor = np.zeros((len(xs), len(ys), len(zs)), dtype=bool)

Zdaj pa gremo čez navodila ter prižigamo in ugašamo bloke v podanih intervalih (številk).

for on, x0, x1, y0, y1, z0, z1 in instr:
    reactor[xs[x0]:xs[x1], ys[y0]:ys[y1], zs[z0]:zs[z1]] = on

Koordinate iz navodil preslikujemo v številke blokov prek slovarjev xs, ys in zs.

Stanje reaktorja zdaj poznamo: vemo, kateri bloki so prižgani. Da ugotovimo število prižganih celic, pa potrebujemo število celic v vsakem bloku. To dobimo takole:

volumes = \
    (xa[1:] - xa[:-1])[:, None, None] \
    * (ya[1:] - ya[:-1])[:, None] \
    * (za[1:] - za[:-1])

xa[1:] - xa[:-1] so razmiki med zaporednimi koordinatami v xs - to so širine blokov po smeri x. Podobno za ostale dimenzije. Dimenziji x dodamo še dve osi, y še eno in to zmnožimo skupaj. Zdaj bo volumes[n][m][k] dimenzija bloka, ki se začne na n-ti koordinati x, m-ti koordinati y in k-ti koordinati z. Torej tistega bloka, katerega stanje opisuje reactor[n][m][k]. Prostornine vseh prižganih blokov dobimo z volumes[reactor[:-1, :-1, :-1]]. -1 moramo odšteti zaradi gornjih mej (spet!). Očitno je xa[1:] - xa[:-1], kolikor je dolg volumes za 1 krajši od len(xs), kolikor je dolg reaktor.

Rezultat, ki ga iščemo, je torej

np.sum(volumes[reactor[:-1, :-1, :-1]])
2758514936282235

Za preglednost in občutek je tu še celotna rešitev.

xs = set(instr[:, 1:3].flatten())
ys = set(instr[:, 3:5].flatten())
zs = set(instr[:, 5:7].flatten())

xa = np.array(sorted(xs))
ya = np.array(sorted(ys))
za = np.array(sorted(zs))

xs = {x: i for i, x in enumerate(xa)}
ys = {x: i for i, x in enumerate(ya)}
zs = {x: i for i, x in enumerate(za)}

reactor = np.zeros((len(xs), len(ys), len(zs)), dtype=bool)
for on, x0, x1, y0, y1, z0, z1 in instr:
    reactor[xs[x0]:xs[x1], ys[y0]:ys[y1], zs[z0]:zs[z1]] = on

volumes = \
    (xa[1:] - xa[:-1])[:, None, None] \
    * (ya[1:] - ya[:-1])[:, None] \
    * (za[1:] - za[:-1])

np.sum(volumes[reactor[:-1, :-1, :-1]])
2758514936282235

Razen imenitnega zaključka z računanjem prostornin je to bolj vaja iz razmišljanja in Pythona kot iz numpy-ja.