import std; void main() { File("input", "r").byLineCopy.array.calculateNumberOfOccupiedSeatsStable.writeln; } auto calculateNewSeatState(const char seat, ulong numberOfSeenOccupiedSeats) { if (seat == '#' && numberOfSeenOccupiedSeats >= 5) { return 'L'; } else if (seat == 'L' && numberOfSeenOccupiedSeats == 0) { return '#'; } else { return seat; } } auto generateIndexes(ptrdiff_t posY, ptrdiff_t posX, ptrdiff_t y, ptrdiff_t x) { return recurrence!((state, n) => tuple(state[n - 1][0] + y, state[n - 1][1] + x))( tuple(posY, posX)).drop(1); } auto isValidPosition(Tuple!(ptrdiff_t, ptrdiff_t) pos, size_t height, size_t width) { return pos[0] >= 0 && pos[0] < height && pos[1] >= 0 && pos[1] < width; } auto findFirstSeatInDirection(const char[][] seats, const Tuple!(ptrdiff_t, ptrdiff_t) direction, const Tuple!(ptrdiff_t, ptrdiff_t) initialPosition, const size_t height, const size_t width) { return generateIndexes(initialPosition[0], initialPosition[1], direction[0], direction[1]).until!( pos => !pos.isValidPosition(height, width)) .map!(pos => seats[pos[0]][pos[1]]) .find!(seat => seat != '.'); } auto calculateNextSeatOccupation(const char[][] seats) { if (seats.empty || seats.front.empty) { return seats; } auto height = seats.length; auto width = seats.front.length; return seats.enumerate.map!((indexedRow) { ptrdiff_t i = indexedRow.index; auto row = indexedRow.value; return row.byChar.enumerate.map!((indexedSeat) { ptrdiff_t j = indexedSeat.index; auto seat = indexedSeat.value; auto numberOfSeenOccupiedSeats = [ tuple(0L, 1L), tuple(-1L, 1L), tuple(-1L, 0L), tuple(-1L, -1L), tuple(0L, -1L), tuple(1L, -1L), tuple(1L, 0L), tuple(1L, 1L) ].map!(direction => findFirstSeatInDirection(seats, direction, tuple(i, j), height, width)) .filter!(lineOfSight => !lineOfSight.empty) .map!(lineOfSeight => lineOfSeight.front) .filter!(it => it == '#') .count; return seat.calculateNewSeatState(numberOfSeenOccupiedSeats); }).array; }).array; } unittest { auto input = `L.LL.LL.LL LLLLLLL.LL L.L.L..L.. LLLL.LL.LL L.LL.LL.LL L.LLLLL.LL ..L.L..... LLLLLLLLLL L.LLLLLL.L L.LLLLL.LL`.dup.split("\n"); assert(input.calculateNextSeatOccupation.join("\n") == `#.##.##.## #######.## #.#.#..#.. ####.##.## #.##.##.## #.#####.## ..#.#..... ########## #.######.# #.#####.##`); } unittest { auto input = `#.##.##.## #######.## #.#.#..#.. ####.##.## #.##.##.## #.#####.## ..#.#..... ########## #.######.# #.#####.##`.dup.split("\n"); assert(input.calculateNextSeatOccupation.join("\n") == `#.LL.LL.L# #LLLLLL.LL L.L.L..L.. LLLL.LL.LL L.LL.LL.LL L.LLLLL.LL ..L.L..... LLLLLLLLL# #.LLLLLL.L #.LLLLL.L#`); } auto calculateNumberOfOccupiedSeatsStable(const char[][] seats) { return recurrence!((seats, n) => seats[n - 1].calculateNextSeatOccupation)(seats).slide(2) .map!array .find!((pair) => pair[0] == pair[1]) .front .front .join .filter!(seat => seat == '#') .count; } unittest { auto input = `L.LL.LL.LL LLLLLLL.LL L.L.L..L.. LLLL.LL.LL L.LL.LL.LL L.LLLLL.LL ..L.L..... LLLLLLLLLL L.LLLLLL.L L.LLLLL.LL`.dup.split("\n"); assert(input.calculateNumberOfOccupiedSeatsStable == 26); }