"""Day 11.""" with open("input") as f: CHART = tuple(tuple(x) for x in f.read().rstrip().split("\n")) EMPTY = 'L' OCCUPIED = '#' FLOOR = '.' MIN = 0 MAX = len(CHART[0]) - 1 def clamp(num, minimium, maximium): """Clamp a number in between a range.""" if num <= minimium: return minimium if num >= maximium: return maximium return num def get_neighbours(x, y): """Get neighbour cords for a point.""" pairs = set() for i in range(x - 1, x + 2): for j in range(y - 1, y + 2): pairs.add( ( clamp(i, MIN, MAX), clamp(j, MIN, MAX) ) ) # Remove self pairs.remove((x, y)) return pairs def get_new_seat(seat, neighbours, occupied_limit): """Get the new seat.""" occupied_sum = sum(1 if x == OCCUPIED else 0 for x in neighbours) if seat == EMPTY and not any(x == OCCUPIED for x in neighbours): return OCCUPIED elif seat == OCCUPIED and occupied_sum >= occupied_limit: return EMPTY else: return seat def iterate_over_chart_part1(chart, p=False): """Iterate over a chart for part 1.""" new_map = [] for y, row in enumerate(chart): new_map.append([]) for x, col in enumerate(row): seat = chart[y][x] neighbours = tuple(chart[pair[1]][pair[0]] for pair in get_neighbours(x, y)) new_map[y].append(get_new_seat(seat, neighbours, 4)) return new_map def iterate_over_chart_part2(chart, p=False): """Iterate over a chart for part 2.""" new_map = [] for y, row in enumerate(chart): new_map.append([]) for x, col in enumerate(row): seat = chart[y][x] neighbours = find_first_seat(x, y, chart) new_map[y].append(get_new_seat(seat, neighbours, 5)) return new_map def print_chart(chart): """Pretty print a chart.""" for y in chart: line = "" for x in y: line += x print(line) def are_equal(chart1, chart2): """Check if two charts are equal.""" for idx, _ in enumerate(chart1): if chart1[idx] != chart2[idx]: return False return True def count_occupied(chart): """Count occupied seats in a chart.""" count = 0 for row in chart: for x in row: if x == OCCUPIED: count += 1 return count # Part 1 initial = CHART count = 0 while True: new_chart = iterate_over_chart_part1(initial) if are_equal(new_chart, initial): print(count_occupied(new_chart)) print("done") break else: # print_chart(new_chart) print(count) count += 1 initial = new_chart def find_first_seat(x, y, chart): """Find the first seat.""" seen_seats = [] # print("top") for new_y in range(y - 1, MIN - 1, -1): seat = chart[new_y][x] if seat in [OCCUPIED, EMPTY]: seen_seats.append(seat) break # print("top right") for idx, new_y in enumerate(range(y - 1, MIN - 1, -1)): new_x = x + idx + 1 if new_x > MAX: break seat = chart[new_y][new_x] if seat in [OCCUPIED, EMPTY]: seen_seats.append(seat) break # print("right") for idx in range(len(chart[y][x:]) - 1): new_x = x + idx + 1 seat = chart[y][new_x] if seat in [OCCUPIED, EMPTY]: seen_seats.append(seat) break # print("down right") for idx, new_x in enumerate(range(x + 1, MAX + 1)): new_y = y + idx + 1 if new_y > MAX: break seat = chart[new_y][new_x] if seat in [OCCUPIED, EMPTY]: seen_seats.append(seat) break # print("down") for new_y in range(y + 1, MAX + 1): seat = chart[new_y][x] if seat in [OCCUPIED, EMPTY]: seen_seats.append(seat) break # print("down left") for idx, new_x in enumerate(range(x - 1, MIN - 1, -1)): new_y = y + idx + 1 if new_y > MAX: break seat = chart[new_y][new_x] if seat in [OCCUPIED, EMPTY]: seen_seats.append(seat) break # print("left") for idx in range(len(chart[y][:x + 1]) - 1): new_x = x - idx - 1 seat = chart[y][new_x] if seat in [OCCUPIED, EMPTY]: seen_seats.append(seat) break # print("top left") for idx, new_x in enumerate(range(x - 1, MIN - 1, -1)): new_y = y - idx - 1 if new_y < MIN: break seat = chart[new_y][new_x] if seat in [OCCUPIED, EMPTY]: seen_seats.append(seat) break return seen_seats # Part 2 initial = CHART count = 0 while True: new_chart = iterate_over_chart_part2(initial) if are_equal(new_chart, initial): print(count_occupied(new_chart)) print("done") break else: # print_chart(new_chart) print(count) count += 1 initial = new_chart