From e60d0e106389a7b1893650148a94401dfe2d3b6b Mon Sep 17 00:00:00 2001 From: Akumatic Date: Fri, 11 Dec 2020 13:18:31 +0100 Subject: [PATCH] increase speed by using a cache for positions --- 2020/11/code.py | 79 +++++++++++++++++++++++--------------------- 2020/11/test_code.py | 11 +++--- 2 files changed, 48 insertions(+), 42 deletions(-) diff --git a/2020/11/code.py b/2020/11/code.py index e59b28a..7bd191e 100644 --- a/2020/11/code.py +++ b/2020/11/code.py @@ -7,50 +7,54 @@ def readFile() -> list: with open(f"{__file__.rstrip('code.py')}input.txt", "r") as f: return [list(line.strip()) for line in f.readlines()] -def get_positions(seats: list, x: int, y: int, rx: range, ry: range, adjacent: bool) -> list: - if adjacent: # check only adjacent tiles - positions = [(x+i, y+j) for i in range(-1,2) for j in range(-1,2) if (i,j) != (0,0)] - - else: # check first seat available in direction - positions = [] - for dx in range(-1,2): - for dy in range(-1,2): - if dx == 0 and dy == 0: - continue - i, j = x, y - while i + dx in rx and j + dy in ry: - i += dx - j += dy - if seats[i][j] in ("L", "#"): - positions.append((i, j)) - break +def get_positions(seats: list, cache: dict, x: int, y: int): + idx = (x, y) + cache[idx] = {"adjacent": [], "next_seat": []} - return positions + for dx in range(-1, 2): + for dy in range(-1, 2): + if dx == 0 and dy == 0: + continue + # adjacent tiles + cache[idx]["adjacent"].append((x + dx, y + dy)) -def count(seats: list, x: int, y: int, seat: str, adjacent: bool) -> int: - rx, ry = range(len(seats)), range(len(seats[0])) - positions = get_positions(seats, x, y, rx, ry, adjacent) - return sum(1 for pos in positions if pos[0] in rx and pos[1] in ry and \ - seats[pos[0]][pos[1]] == seat) + # next available seat + i, j = x, y + while i + dx in cache["rx"] and j + dy in cache["ry"]: + i += dx + j += dy + if seats[i][j] in ("L", "#"): + cache[idx]["next_seat"].append((i, j)) + break -def iterate(seats: list, adjacent: bool = True) -> list: - size_outer = len(seats) - size_inner = len(seats[0]) +def count(seats: list, cache: dict, x: int, y: int, seat: str, selection: str) -> int: + return sum(1 for pos in cache[(x, y)][selection] if pos[0] in cache["rx"] and \ + pos[1] in cache["ry"] and seats[pos[0]][pos[1]] == seat) + +def create_cache(seats: list) -> dict: + cache = {"rx": range(len(seats)), "ry": range(len(seats[0]))} + for x in cache["rx"]: + for y in cache["ry"]: + get_positions(seats, cache, x, y) + return cache + +def iterate(seats: list, cache: dict, adjacent: bool = True) -> list: + selection = "adjacent" if adjacent else "next_seat" + tolerance = 4 if adjacent else 5 while True: tmp = [s[:] for s in seats] - for x in range(size_outer): - for y in range(size_inner): + for x in cache["rx"]: + for y in cache["ry"]: if seats[x][y] == ".": continue elif seats[x][y] == "L": - if count(seats, x, y, "#", adjacent) == 0: + if count(seats, cache, x, y, "#", selection) == 0: tmp[x][y] = "#" else: # seats[x][y] == "#": - tolerance = 4 if adjacent else 5 - if count(seats, x, y, "#", adjacent) >= tolerance: + if count(seats, cache, x, y, "#", selection) >= tolerance: tmp[x][y] = "L" if seats == tmp: @@ -58,15 +62,16 @@ def iterate(seats: list, adjacent: bool = True) -> list: else: seats = tmp -def part1(input: list) -> int: - seats = iterate(input) +def part1(input: list, cache) -> int: + seats = iterate(input, cache) return sum([row.count("#") for row in seats]) -def part2(input: list) -> int: - seats = iterate(input, adjacent=False) +def part2(input: list, cache) -> int: + seats = iterate(input, cache, adjacent=False) return sum([row.count("#") for row in seats]) if __name__ == "__main__": input = readFile() - print(f"Part 1: {part1(input)}") - print(f"Part 2: {part2(input)}") \ No newline at end of file + cache = create_cache(input) + print(f"Part 1: {part1(input, cache)}") + print(f"Part 2: {part2(input, cache)}") \ No newline at end of file diff --git a/2020/11/test_code.py b/2020/11/test_code.py index ce1bf72..b5ff69d 100644 --- a/2020/11/test_code.py +++ b/2020/11/test_code.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: MIT # Copyright (c) 2020 Akumatic -from code import part1, part2, iterate +from code import part1, part2, iterate, create_cache def test(): input = [['L', '.', 'L', 'L', '.', 'L', 'L', '.', 'L', 'L'], @@ -14,7 +14,8 @@ def test(): ['L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L'], ['L', '.', 'L', 'L', 'L', 'L', 'L', 'L', '.', 'L'], ['L', '.', 'L', 'L', 'L', 'L', 'L', '.', 'L', 'L']] - assert iterate(input) == [ + cache = create_cache(input) + assert iterate(input, cache) == [ ['#', '.', '#', 'L', '.', 'L', '#', '.', '#', '#'], ['#', 'L', 'L', 'L', '#', 'L', 'L', '.', 'L', '#'], ['L', '.', '#', '.', 'L', '.', '.', '#', '.', '.'], @@ -25,7 +26,7 @@ def test(): ['#', 'L', '#', 'L', '#', '#', 'L', '#', 'L', '#'], ['#', '.', 'L', 'L', 'L', 'L', 'L', 'L', '.', 'L'], ['#', '.', '#', 'L', '#', 'L', '#', '.', '#', '#']] - assert iterate(input, adjacent=False) == [ + assert iterate(input, cache, adjacent=False) == [ ['#', '.', 'L', '#', '.', 'L', '#', '.', 'L', '#'], ['#', 'L', 'L', 'L', 'L', 'L', 'L', '.', 'L', 'L'], ['L', '.', 'L', '.', 'L', '.', '.', '#', '.', '.'], @@ -36,8 +37,8 @@ def test(): ['L', 'L', 'L', '#', '#', '#', 'L', 'L', 'L', '#'], ['#', '.', 'L', 'L', 'L', 'L', 'L', '#', '.', 'L'], ['#', '.', 'L', '#', 'L', 'L', '#', '.', 'L', '#']] - assert part1(input) == 37 - assert part2(input) == 26 + assert part1(input, cache) == 37 + assert part2(input, cache) == 26 print("Passed tests for", input) if __name__ == "__main__":