From ee832719cd1b10c6833a45fbe3751e8c84fc2e8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Cort=C3=A9s?= Date: Sun, 6 Dec 2020 03:31:53 -0300 Subject: [PATCH] removing input from git --- .gitignore | 1 + 2020/day_5/binary_boarding.cc | 8 +- 2020/day_5/visual.py | 449 ++++++++++++++++++++++++++++++++++ 3 files changed, 456 insertions(+), 2 deletions(-) create mode 100644 2020/day_5/visual.py diff --git a/.gitignore b/.gitignore index cba7efc..7d97223 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ a.out +input diff --git a/2020/day_5/binary_boarding.cc b/2020/day_5/binary_boarding.cc index b031217..445cb08 100644 --- a/2020/day_5/binary_boarding.cc +++ b/2020/day_5/binary_boarding.cc @@ -15,6 +15,11 @@ std::vector read_input_file() { return data; } +template +T peek_back(T it) { + return --it; +} + int parse_id(std::string ticket) { std::replace(ticket.begin(), ticket.end(), 'F', '0'); std::replace(ticket.begin(), ticket.end(), 'L', '0'); @@ -28,8 +33,7 @@ int solve_a(const std::set &ids) { return *ids.rbegin(); } int solve_b(const std::set &ids) { for(auto it = ++ids.begin(); it != ids.end(); it++){ - auto prev = it; --prev; - if(*prev + 2 == *it) return *it - 1; + if(*peek_back(it) + 2 == *it) return *it - 1; } return 0; } diff --git a/2020/day_5/visual.py b/2020/day_5/visual.py new file mode 100644 index 0000000..954bc00 --- /dev/null +++ b/2020/day_5/visual.py @@ -0,0 +1,449 @@ + +# Sorry, all out of meatballs, but the spaghetti in here is good nonetheless + +import curses +import itertools +import random +from time import sleep +import threading + +BOX_URD = 0x251C +BOX_UDL = 0x2524 +BOX_LR = 0x2500 +BOX_UD = 0x2502 +BOX_UR = 0x2514 + +TOTAL_SIZE = (132, 20) + +PLANE_FRAME = ((0, 0), (132, 14)) +ACTIVE_FRAME = ((0, 13), (66, 5)) +RESULT_FRAME = ((66, 13), (66, 5)) +STATUS_FRAME = ((0, 18), (100, 2)) + +SPEC_SCROLLER_OFFSETX = 15 +ACTIVE_INFO_NUM_FIELD = 16 +ACTIVE_POST_NUM_FIELD = 24 +ACTIVE_INFO_KEY_OFFSETX = 32 +ACTIVE_INFO_VALUE_OFFSETX = 42 +RESULT_VALUE_OFFSETX = 16 +ACTIVE_INFO_VALUE_MAXLN = ACTIVE_FRAME[1][0] - ACTIVE_INFO_VALUE_OFFSETX - 3 +RESULT_VALUE_MAXLN = RESULT_FRAME[1][0] - RESULT_VALUE_OFFSETX - 3 +STATUS_MAXLN = STATUS_FRAME[1][0] - 4 + +COLOR_LIGHT_GREEN = 0xa +COLOR_LIGHT_AQUA = 0xb +COLOR_LIGHT_YELLOW = 0xe +COLOR_WHITE = 7 +COLOR_GRAY = 8 +COLOR_FULLWHITE = 0xF + +VISUALIZE_BIN_SEARCH_FIRST = 16 + +class StatusThread(threading.Thread): + ANIM_LENGTH = 7 + + def __init__(self, status_win): + super().__init__() + self.status_win = status_win + self._stlen = 0 + self.draw_anim = False + self.stop_flag = threading.Event() + self.animcycler = itertools.cycle( + (" ▙█", " █▟", " █▙", " █▛", " █▜", " ▛█", " ▜█", " ▟█") + ) + + def set_status(self, status, draw_anim=True): + self.draw_anim = draw_anim + self._stlen = min(len(status), STATUS_MAXLN - self.ANIM_LENGTH) + self.status_win.addstr(0, 2, f"{status: <{STATUS_MAXLN}}") + self.status_win.refresh() + + def join(self): + self.stop_flag.set() + super().join() + + def run(self): + while not self.stop_flag.is_set(): + if self.draw_anim: + self.status_win.addstr(0, 2 + self._stlen, next(self.animcycler)) + self.status_win.refresh() + sleep(.2) + + +def new_border_and_win(ws): + """ + Returns two curses windows, one serving as the border, the other as the + inside from these *_FRAME tuples above. + """ + return ( + curses.newwin(ws[1][1], ws[1][0], ws[0][1], ws[0][0]), + curses.newwin(ws[1][1] - 2, ws[1][0] - 2, ws[0][1] + 1, ws[0][0] + 1), + ) + +def new_win(tup): + """ + Returns a curses window made from the (x,y),(w,h) tuples above + """ + return curses.newwin(tup[1][1], tup[1][0], tup[0][1], tup[0][0]) + +class PlaneVisualization(): + def __init__(self, input_): + self.seat_specs = input_.splitlines() + + def run(self): + self.init_curses() + my, mx = self.screen.getmaxyx() + if (mx, my) < TOTAL_SIZE: + self.screen.addstr("Terminal too small!") + self.screen.refresh() + sleep(1) + self.stop_curses() + return + + self.setup_windows() + + self.visualize() + + self.stop_curses() + + def init_curses(self): + self.screen = curses.initscr() + curses.start_color() + curses.init_pair(1, COLOR_GRAY, curses.COLOR_BLACK) # Darkest non-black color on black bg + curses.init_pair(2, COLOR_FULLWHITE, curses.COLOR_BLACK) # Full white on black bg + curses.init_pair(3, COLOR_WHITE, COLOR_GRAY) # Full white on black bg + curses.init_pair(4, COLOR_LIGHT_GREEN, curses.COLOR_BLACK) # Green on black bg + curses.init_pair(5, COLOR_LIGHT_AQUA, curses.COLOR_BLUE ) # aqua on black bg, used to highlight max + curses.init_pair(6, COLOR_LIGHT_YELLOW,curses.COLOR_BLACK) + curses.init_pair(7, curses.COLOR_RED, curses.COLOR_BLACK) + curses.noecho() + curses.cbreak() + curses.curs_set(0) + + def stop_curses(self): + curses.curs_set(1) + curses.nocbreak() + curses.echo() + curses.endwin() + + def setup_windows(self): + ## Frame creation + plane_b, self.plane_win = new_border_and_win(PLANE_FRAME) + active_b, self.active_win = new_border_and_win(ACTIVE_FRAME) + result_b, self.result_win = new_border_and_win(RESULT_FRAME) + self.status_win = new_win(STATUS_FRAME) + + ## Borders + plane_b.border() + active_b.border(0, 0, 0, 0, BOX_URD, 0, BOX_URD, 0) + result_b.border(0, 0, 0, 0, 0, BOX_UDL, 0, 0) + self.status_win.addstr(0, 0, "│") + self.status_win.addstr(1, 0, "└") + self.status_win.addstr(1, 1, "─"*98) + + for win in (plane_b, active_b, result_b, self.status_win): + win.refresh() + + @staticmethod + def seat_to_screen_pos(row, col): + """ + Given a seat coordinate from 0, 0 (x, y) -> (row, col), + returns actual screen coordinate corresponding to the seat in + self.plane_win + """ + return (1 + row, 1 + col + (col > 2) + (col > 4)) + + def vis_seat_search(self, seat_ids): + """ + Takes existing/occupied seat_ids as a set, returns the free seat or + None if not locatable. Also visualizations. + """ + for seat_id in range(128*8): + prev = seat_id - 1 + next_ = seat_id + 1 + + # Draw C, N, P markers + x, y = self.seat_to_screen_pos(seat_id >> 3, seat_id & 7) + self.plane_win.addstr(y, x, "C", curses.color_pair(5)) + + if prev >= 0: + x, y = self.seat_to_screen_pos(prev >> 3, prev & 7) + self.plane_win.addstr(y, x, "P", curses.color_pair(6)) + + if next_ < 1024: + x, y = self.seat_to_screen_pos(next_ >> 3, next_ & 7) + self.plane_win.addstr(y, x, "N", curses.color_pair(6)) + + self.plane_win.refresh() + + self.active_win.addstr(0, ACTIVE_INFO_NUM_FIELD, f"{seat_id: >5}") + self.active_win.addstr(1, ACTIVE_INFO_NUM_FIELD, f"{prev: >5}") + self.active_win.addstr(2, ACTIVE_INFO_NUM_FIELD, f"{next_: >5}") + + for i, seat_id_ in enumerate((seat_id, prev, next_)): + if seat_id_ in seat_ids: + self.active_win.addstr( + i, ACTIVE_POST_NUM_FIELD, "Exists ", + curses.color_pair(7 if i == 0 else 4) + ) + else: + self.active_win.addstr( + i, ACTIVE_POST_NUM_FIELD, "Does not exist", + curses.color_pair(4 if i == 0 else 7) + ) + + self.active_win.refresh() + + if seat_id not in seat_ids and (prev in seat_ids) and (next_ in seat_ids): + return seat_id + + # Reset P marker + if prev >= 0: + x, y = self.seat_to_screen_pos(prev >> 3, prev & 7) + self.plane_win.addstr(y, x, "[" if prev in seat_ids else " ", curses.color_pair(1)) + + sleep(1.01 - (seat_id/1024)**.005) + + return None + + def vis_binsearch(self, iter, spec, _pos_remember, cmax): + """ + This function sucks, but works + """ + #Draw initial binsearch rect + delay = .25 - ((iter/VISUALIZE_BIN_SEARCH_FIRST)**.2)*.24 + for i in range(8): + self.plane_win.chgat( + self.seat_to_screen_pos(-1, i)[1], + 1, 128, curses.color_pair(3) + ) + self.plane_win.refresh() + if iter == 0: + sleep(5) + + # Binsearch rows + row_min = 0 + row_max = 127 + step = 64 + for i, c in enumerate(spec[:7]): + if c == "F": + row_max -= step + else: + row_min += step + for j in range(10): + self.plane_win.chgat( + 1 + j, + 1 + (row_max + 1 if c == "F" else row_min - step), + step, + curses.color_pair(1), + ) + for x, y in _pos_remember: + self.plane_win.chgat(y, x, 1, curses.color_pair(2)) + if cmax is not None: + self.plane_win.chgat(cmax[1], cmax[0], 1, curses.color_pair(5)) + self.active_win.chgat(1, SPEC_SCROLLER_OFFSETX + 2 + i, 1, curses.color_pair(4)) + self.plane_win.refresh() + self.active_win.refresh() + step //= 2 + sleep(delay) + row = row_min + + # Binsearch columns + col_min = 0 + col_max = 7 + step = 4 + for i, c in enumerate(spec[7:]): + if c == "L": + col_max -= step + else: + col_min += step + offset = col_max + 1 if c == "L" else col_min - step + for j in range(step): + tx, ty = self.seat_to_screen_pos(row, offset + j) + self.plane_win.chgat(ty, tx, 1, curses.color_pair(1)) + for x, y in _pos_remember: + self.plane_win.chgat(y, x, 1, curses.color_pair(2)) + if cmax is not None: + self.plane_win.chgat(cmax[1], cmax[0], 1, curses.color_pair(5)) + self.active_win.chgat(1, SPEC_SCROLLER_OFFSETX + 2 + 7 + i, 1, curses.color_pair(4)) + self.plane_win.refresh() + self.active_win.refresh() + step //= 2 + sleep(delay) + col = col_min + _pos_remember.append(self.seat_to_screen_pos(row, col)) + + def visualize(self): + ## Text for Part 1 + self.active_win.addstr(0, 1, f"{0: ^{SPEC_SCROLLER_OFFSETX - 2}}") + self.active_win.addstr(0, SPEC_SCROLLER_OFFSETX, "| |") + self.active_win.addstr(1, 1, "Current seat: > <") + self.active_win.addstr(2, 1, f"{len(self.seat_specs): ^{SPEC_SCROLLER_OFFSETX - 2}}") + self.active_win.addstr(2, SPEC_SCROLLER_OFFSETX, "| |") + self.active_win.addstr(0, ACTIVE_INFO_KEY_OFFSETX, "Row:") + self.active_win.addstr(1, ACTIVE_INFO_KEY_OFFSETX, "Column:") + self.active_win.addstr(2, ACTIVE_INFO_KEY_OFFSETX, "Id:") + self.result_win.addstr(1, 1, "Current max:") + + ## Seats + self.plane_win.attron(curses.color_pair(1)) + for line in range(8): + self.plane_win.addstr( + self.seat_to_screen_pos(-1, line)[1], + 1, "["*128, curses.color_pair(1) + ) + self.plane_win.refresh() + self.plane_win.attron(curses.color_pair(0)) + + status_thread = StatusThread(self.status_win) + status_thread.start() + + # Begin + # Part 1 + SPEC_AMT = len(self.seat_specs) + # Holds position of the first VISUALIZE_BIN_SEARCH_FIRST seats so they + # are properly redrawn during binary search. + seat_ids = set() + _pos_remember = [] + cmax = cmax_pos = row = col = seat_id = None + + self.active_win.addstr(0, ACTIVE_INFO_VALUE_OFFSETX, str(row)) + self.active_win.addstr(1, ACTIVE_INFO_VALUE_OFFSETX, str(col)) + self.active_win.addstr(2, ACTIVE_INFO_VALUE_OFFSETX, str(seat_id)) + self.result_win.addstr(1, RESULT_VALUE_OFFSETX, str(cmax)) + self.active_win.refresh() + self.result_win.refresh() + + status_thread.set_status("Part 1: Finding maximum seat id") + for i, spec in enumerate(self.seat_specs): + # Draw Seat Specs + if i != 0: + self.active_win.addstr( + 0, SPEC_SCROLLER_OFFSETX + 2, + self.seat_specs[i - 1], curses.color_pair(1) + ) + self.active_win.addstr(1, SPEC_SCROLLER_OFFSETX + 2, spec) + if i != SPEC_AMT - 1: + self.active_win.addstr( + 2, SPEC_SCROLLER_OFFSETX + 2, + self.seat_specs[i + 1], curses.color_pair(1) + ) + else: + self.active_win.addstr(2, SPEC_SCROLLER_OFFSETX + 2, " ") + # Draw current index + self.active_win.addstr(0, 1, f"{i: ^{SPEC_SCROLLER_OFFSETX - 2}}") + self.active_win.refresh() + + # Visualize binary subdivision for first three specs + if i < VISUALIZE_BIN_SEARCH_FIRST: + self.vis_binsearch(i, spec, _pos_remember, cmax_pos) + + # Mega cheap, get seat_id, row and col + seat_id = int(spec.replace("F", "0").replace("B", "1").replace("L", "0").replace("R", "1"), 2) + row, col = seat_id >> 3, seat_id & 7 + + seat_ids.add(seat_id) + + # Color current seat in brighter color + tx, ty = self.seat_to_screen_pos(row, col) + self.plane_win.chgat(ty, tx, 1, curses.color_pair(2)) + self.plane_win.refresh() + + # New maximum id? + if cmax is None or seat_id > cmax: + cmax = seat_id + # Decolor old maximum seat + if cmax_pos is not None: + self.plane_win.chgat(cmax_pos[1], cmax_pos[0], 1, curses.color_pair(2)) + cmax_pos = self.seat_to_screen_pos(row, col) + self.plane_win.chgat(cmax_pos[1], cmax_pos[0], 1, curses.color_pair(5)) + self.result_win.addstr(1, 1, f"Current max: {cmax: <{RESULT_VALUE_MAXLN}}") + self.result_win.refresh() + self.plane_win.refresh() + + self.active_win.addstr(0, ACTIVE_INFO_VALUE_OFFSETX, f"{row: <{ACTIVE_INFO_VALUE_MAXLN}}") + self.active_win.addstr(1, ACTIVE_INFO_VALUE_OFFSETX, f"{col: <{ACTIVE_INFO_VALUE_MAXLN}}") + self.active_win.addstr(2, ACTIVE_INFO_VALUE_OFFSETX, f"{seat_id: <{ACTIVE_INFO_VALUE_MAXLN}}") + self.active_win.refresh() + + sleep(.008) + + status_thread.set_status("Maximum found!", False) + + # Blink max repeatedly + for _ in range(5): + self.result_win.addstr( + 1, 1, f"Current max: {cmax: <{RESULT_VALUE_MAXLN}}", curses.color_pair(4)) + self.result_win.refresh() + sleep(.4) + self.result_win.addstr( + 1, 1, f"Current max: {' ': <{RESULT_VALUE_MAXLN}}",) + self.result_win.refresh() + sleep(.3) + + # Purge info windows + self.result_win.clear() + self.active_win.clear() + self.result_win.refresh() + self.active_win.refresh() + + status_thread.set_status("Removing seats") + BACKDRAG = 8 + # Thanos snap seats + for x in range(128 + BACKDRAG): + for y in range(8): + for t in range(BACKDRAG): + tx, ty = self.seat_to_screen_pos(x - t, y) + if 0 < tx < 129 and random.randint(1, BACKDRAG*10-10) < t*10+1: + self.plane_win.addstr(ty, tx, " ") + self.plane_win.refresh() + sleep(.006 + x/6000) + + status_thread.set_status("Drawing existing seats") + # Part 2 + # Add new text fields to lower windows + self.active_win.addstr(0, 1, "Current seat [ ]: ") + self.active_win.addstr(1, 1, "Previous seat [ ]: ") + self.active_win.addstr(2, 1, "Next seat [ ]: ") + self.result_win.addstr(1, 1, "Your seat: ?") + + self.active_win.refresh() + self.result_win.refresh() + + # Draw all existing seats + for i, seat_id in enumerate(seat_ids): + row, col = seat_id >> 3, seat_id & 7 + x, y = self.seat_to_screen_pos(row, col) + self.plane_win.addstr(y, x, "[", curses.color_pair(1)) + self.plane_win.refresh() + sleep(.001) + + status_thread.set_status("Part 2: Searching for own seat") + own_seat = self.vis_seat_search(seat_ids) + + if own_seat is None: + status_thread.set_status("Failed to find seat!", False) + else: + # Blink seat number repeatedly + status_thread.set_status("Seat found!", False) + for _ in range(5): + self.result_win.addstr( + 1, 1, f"Your seat: {own_seat: <{RESULT_VALUE_MAXLN}}", curses.color_pair(4)) + self.result_win.refresh() + sleep(.4) + self.result_win.addstr( + 1, 1, f"Your seat: {' ': <{RESULT_VALUE_MAXLN}}",) + self.result_win.refresh() + sleep(.3) + + sleep(1) + + status_thread.join() + +pz_in = open('input').read() +pv = PlaneVisualization(pz_in) +try: + pv.run() +except Exception as e: + PlaneVisualization.stop_curses(None) + raise e from None