def solve(img, model, sc_func, click_func): index = 1 attempted_words = {} prev_search_word = "" # This is just for logging. while True: # DSF-ish traversal of possible passwords. characters = password_features.get_password(img, model) match = word_matching_prefix(characters[:index]) attemp_prefix = characters[:index-1] attempts = get_attempts(attemp_prefix, attempted_words) if attempts > 5: index -= 1 while match is not None: if match != prev_search_word: log(f"Attempting to write '{match}'", LOG_DEBUG, "Password") prev_search_word = match if index == 5: return True # Match found. index += 1 match = word_matching_prefix(characters[:index]) if index == 0: # No passwords were found. break if attempts == 0: attempted_words[attemp_prefix] = 1 else: attempted_words[attemp_prefix] += 1 get_next_char(index-1, click_func) sc = sc_func()[0] img = features_util.convert_to_cv2(sc) return False
def monitor(self): _, SH = get_screen_size() lo = (30, 30, 30) hi = (255, 255, 255) self.change_event.set() while self.is_active: sc = screenshot(0, SH - 200, 200, 200) img = features_util.convert_to_cv2(sc) rgb = features_util.split_channels(img) if self.lights_on: if not features_util.color_in_range(self.pixel, rgb, lo, hi): if self.bomb_exploded(rgb): # We died :( self.is_active = False self.exit_after_explosion() else: log("Lights in the room are turned off. Pausing execution temporarily..." ) self.lights_on = False self.change_event.clear() sleep(1) else: if features_util.color_in_range(self.pixel, rgb, lo, hi): log("Lights in the room are turned back on. Resuming...") self.change_event.set() self.lights_on = True sleep(1) sleep(0.25)
def identify_side_features(sides, model): flattened = [] for side in sides: for img in side: converted = dataset_util.reshape(convert_to_cv2(img), config.MODULE_INPUT_DIM[1:]) flattened.append(converted) preds = module_classifier.predict(model, array(flattened)) predictions = classifier_util.get_best_prediction(preds) return predictions
def solve_whos_on_first(image, char_model, mod_pos): mod_x, mod_y = mod_pos for i in range(3): coords = whos_first_solver.solve(image, char_model) y, x = coords win_util.click(x + mod_x, y + mod_y) if i < 2: sleep(3.5) image = convert_to_cv2(screenshot_module()[0])
def solve_memory(image, char_model, mod_pos): mod_x, mod_y = mod_pos history = [] for i in range(5): coords, label, position = memory_solver.solve(image, char_model, history) history.append((label, position)) y, x = coords win_util.click(x + mod_x, y + mod_y) if i < 4: sleep(3.5) image = convert_to_cv2(screenshot_module()[0])
def solve_simon(image, mod_pos, side_features): mod_x, mod_y = mod_pos num = 1 while not simon_solver.is_solved(image): btn_coords = simon_solver.solve(image, screenshot_module, side_features, num) for coords in btn_coords: button_y, button_x = coords win_util.click(mod_x + button_x, mod_y + button_y) sleep(0.5) num += 1 SC, _, _ = screenshot_module() image = convert_to_cv2(SC)
def solve_wire_sequence(image, mod_pos): mod_x, mod_y = mod_pos button_x, button_y = 128 + mod_x, 248 + mod_y color_hist = [0, 0, 0] for i in range(4): wires_to_cut, color_hist, coords = wire_seq_solver.solve(image, color_hist) for j, cut in enumerate(wires_to_cut): if cut: y, x = coords[j] win_util.click(mod_x + x, mod_y + y) sleep(0.5) win_util.click(button_x, button_y) if i < 3: sleep(1.8) image = convert_to_cv2(screenshot_module()[0])
def solve_needy_modules(modules, needy_indices, curr_module, duration): SW, SH = win_util.get_screen_size() prev_index = curr_module timestamp = None for index in needy_indices: if (index > 5) ^ (prev_index > 5): # Flip the bomb, if needed. inspect_bomb.flip_bomb(SW, SH) sleep(0.75) win_util.mouse_up(SW // 2, SH // 2, btn="right") sleep(0.5) mod_index = index if index < 6 else index - 6 label = modules[index] select_module(mod_index) add_overlay_properties("module_info", (label)) SC, x, y = screenshot_module() mod_pos = (x, y) cv2_img = convert_to_cv2(SC) mod_name = module_classifier.LABELS[label] log(f"Solving {mod_name}...") try: if label == 20: # Needy Vent Gas. solve_needy_vent(cv2_img, mod_pos) elif label == 21: # Needy Discharge Capacitor. solve_needy_discharge(cv2_img, mod_pos) elif label == 22: # Solve Knob. solve_needy_knob(cv2_img, mod_pos) except KeyboardInterrupt: handle_module_exception(mod_name, cv2_img) raise KeyboardInterrupt except Exception: handle_module_exception(mod_name, cv2_img) if timestamp is None: cooldown_time = 5 timestamp = time() + cooldown_time sleep(0.5) deselect_module() prev_index = index # Flip the bomb back to it's original state, if needed. if (curr_module > 5) ^ (prev_index > 5): inspect_bomb.flip_bomb(SW, SH) sleep(0.75) win_util.mouse_up(SW // 2, SH // 2, btn="right") sleep(0.5) return timestamp
def solve_button(image, mod_pos, side_features, character_model, duration): mod_x, mod_y = mod_pos hold = button_solver.solve(image, side_features, character_model) button_x, button_y = mod_x + 125, mod_y + 175 if not hold: log(f"Tapping button.", config.LOG_DEBUG, "Button") win_util.click(button_x, button_y) sleep(0.5) else: log(f"Holding button...", config.LOG_DEBUG, "Button") win_util.mouse_move(button_x, button_y) win_util.mouse_down(button_x, button_y) sleep(0.9) # 48 frames until strip lights up. SC, _, _ = screenshot_module() image = convert_to_cv2(SC) pixel = (184, 255) release_time = button_solver.get_release_time(image, pixel) log(f"Release button at {release_time}", config.LOG_DEBUG, "Button") release_mouse_at(release_time, duration, button_x, button_y)
def process_module_data(images, predictions=None): IMAGES_CAPTURED = 0 for i, img in enumerate(images): cv2_img = convert_to_cv2(img) path = None if predictions is not None: label = predictions[i] if label in INCLUDED_LABELS: label_name = clean_file_path(classifier.LABELS[label]) path = f"../resources/training_images/{label_name}/" if not os.path.exists(path): os.mkdir(path) else: path = f"../resources/training_images/" if path is not None: num_images = len(glob(path + "*.png")) imwrite(f"{path}{num_images:03d}.png", cv2_img) IMAGES_CAPTURED += 1 log(f"Captured {IMAGES_CAPTURED} module images.")
def extract_side_features(sides, labels, character_model): index = 0 features = {"indicators": [], "parallel_port": False, "batteries": 0} for side in sides: for img in side: cv2_img = convert_to_cv2(img) mod_name = module_classifier.LABELS[labels[index]] try: if labels[index] == 1: # Single battery (large or small). features["batteries"] += 1 elif labels[index] == 2: # Double batteries. features["batteries"] += 2 elif labels[index] == 3: # Serial number. serial_num = get_serial_number(cv2_img, character_model) if serial_num is None: log(f"Serial number could not be determined.", config.LOG_WARNING) index += 1 continue log(f"Serial number: {serial_num}", verbose=config.LOG_DEBUG) features["serial_number"] = serial_num serial_features = extract_serial_number_features( serial_num) features.update(serial_features) elif labels[index] == 5: # Parallel port. features["parallel_port"] = True elif labels[index] == 6: # Indicator of some kind. lit, text = get_indicator_features(cv2_img, character_model) desc = "lit_" + text if lit else "unlit_" + text features["indicators"].append(desc) except KeyboardInterrupt: handle_module_exception(mod_name, cv2_img) raise KeyboardInterrupt except Exception: handle_module_exception(mod_name, cv2_img) index += 1 return features
def process_bomb_data(images): IMAGES_CAPTURED = 0 predictions = [] for img in images: cv2_img = convert_to_cv2(img) path = f"../resources/training_images/" if AUTO_LABEL: resized = dataset_util.reshape(cv2_img, config.MODULE_INPUT_DIM[1:]) pred = classifier.predict(MODEL, resized) label = classifier_util.get_best_prediction(pred)[0] predictions.append(label) if label in INCLUDED_LABELS: if label < 7: label_name = clean_file_path(classifier.LABELS[label]) path = f"{path}{label_name}/" if DATA_TYPE != "modules": num_images = len(glob(path + "*.png")) imwrite(f"{path}{num_images:03d}.png", cv2_img) IMAGES_CAPTURED += 1 log(f"Captured {IMAGES_CAPTURED} bomb images.") return predictions
def solve_modules(modules, side_features, character_model, symbol_model, duration): # Get list of indexes of needy modules (all modules an index over 19). needy_indices = list( filter(lambda i: modules[i] > 19, [x for x in range(len(modules))])) needy_timestamp = duration[0] module_durations = [2, 5, 2, 12, 10, 2, 14, 8, 8, 8, 20] log(f"Needy modules: {len(needy_indices)}", config.LOG_DEBUG) solved_modules = 0 num_modules = len(list(filter(lambda x: 8 < x < 20, modules))) module = 0 while module < len(modules): label = modules[module] LIGHT_MONITOR.wait_for_light() # If the room is dark, wait for light. mod_index = module if module < 6 else module - 6 bomb_solved = solved_modules == num_modules mod_duration = 2 if label > 19 else module_durations[label - 9] critical = needy_modules_critical(len(needy_indices), needy_timestamp, mod_duration) if not bomb_solved and critical: # Needy modules need attention! Solve them, and continue where we left off. needy_timestamp = solve_needy_modules(modules, needy_indices, module, needy_timestamp) if 8 < label < 20: select_module(mod_index) SC, x, y = screenshot_module() #add_overlay_properties("module_selected", (x, y, mod_index)) mod_pos = (x, y) cv2_img = convert_to_cv2(SC) mod_name = module_classifier.LABELS[label] log(f"Solving {mod_name}...") try: if label == 9: # Wires. solve_wires(cv2_img, mod_pos, side_features) elif label == 10: # Button. solve_button(cv2_img, mod_pos, side_features, character_model, duration) elif label == 11: # Symbols. solve_symbols(cv2_img, mod_pos, symbol_model) elif label == 12: # Simon Says. solve_simon(cv2_img, mod_pos, side_features) elif label == 13: # Wire Sequence. solve_wire_sequence(cv2_img, mod_pos) elif label == 14: # Complicated Wires. solve_complicated_wires(cv2_img, mod_pos, side_features) elif label == 15: # Memory Game. solve_memory(cv2_img, character_model, mod_pos) elif label == 16: # Who's on First? solve_whos_on_first(cv2_img, character_model, mod_pos) elif label == 17: # Maze. solve_maze(cv2_img, mod_pos) elif label == 18: # Password. solve_password(cv2_img, character_model, mod_pos) elif label == 19: # Morse. solve_morse(cv2_img, mod_pos) solved_modules += 1 except KeyboardInterrupt: # Bomb 'sploded. handle_module_exception(mod_name, cv2_img) raise KeyboardInterrupt except Exception: # If an exception happened while lights were off, we try again. if not LIGHT_MONITOR.lights_on: log("Exception while light was off. We try again in a bit!" ) deselect_module() continue handle_module_exception(mod_name, cv2_img) sleep(0.1) deselect_module() if module == 5 and solved_modules != num_modules: # We have gone through all modules on one side of the bomb, flip it over and continue. SW, SH = win_util.get_screen_size() inspect_bomb.flip_bomb(SW, SH) sleep(0.75) win_util.mouse_up(SW // 2, SH // 2, btn="right") sleep(0.5) module += 1 if solved_modules == num_modules: log("We did it! We live to defuse another bomb!") else: log("Some modules could not be disarmed, it seems we are doomed...") raise KeyboardInterrupt # We failed.