def get_wire_colors(img, coords): """ Get a list of colors for each wire. Colors can be either: -1 (no wire), 0 (blue), 1 (white), 2 (red), or 3 (blue + red). """ wires = [-1] * 6 colors = config.WIRE_COLOR_RANGE[2:] rgb = features_util.split_channels(img) # Radius around each wire pixel to search for colors. search_radius = 9 # Minimum threshold for number of pixels to be of a specific color. color_threshold = 6 for i, (y, x) in enumerate(coords): # Look through each wire pixel. valid_colors = [0] * len(colors) # Track occurences of each color. for k, (lo, hi) in enumerate(colors): # Look through possible colors. for m in range(y - search_radius, y + search_radius): # Look in a radius around a pixel. for n in range(x - search_radius, x + search_radius): if features_util.color_in_range((m, n), rgb, lo, hi): if valid_colors[k] < color_threshold: valid_colors[k] += 1 if valid_colors[COLOR.Blue.value] == color_threshold: # Wire is blue. if valid_colors[ COLOR.Red.value] == color_threshold: # Wire is also red. wires[i] = COLOR.Both.value # Both colors. else: wires[i] = COLOR.Blue.value elif valid_colors[ COLOR.Red.value] == color_threshold: # Wire is just red. wires[i] = COLOR.Red.value elif valid_colors[ COLOR.White.value] == color_threshold: # Wire is white. wires[i] = COLOR.White.value return wires
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 get_strip_color(img, pixel): colors = config.BUTTON_COLOR_RANGE for i, (low, high) in enumerate(colors): rgb = feature_util.split_channels(img) if feature_util.color_in_range(pixel, rgb, low, high): return i return -1
def get_wire_colors(img, coords): wires = [-1] * 6 radius = 5 Color = Enum("Colors", {"Blue": 0, "White": 1, "Red": 2, "Both": 3}) colors = config.WIRE_COLOR_RANGE[2:] rgb = features_util.split_channels(img) color_threshold = 5 for i, (y, x) in enumerate(coords): valid_colors = [0] * len(colors) for k, (lo, hi) in enumerate(colors): for m in range(y - radius, y + radius): for n in range(x - radius, x + radius): if features_util.color_in_range((m, n), rgb, lo, hi): if valid_colors[k] < color_threshold: valid_colors[k] += 1 if valid_colors[Color.Blue.value] == color_threshold: if valid_colors[Color.Red.value] == color_threshold: wires[i] = Color.Both.value # Both colors. else: wires[i] = Color.Blue.value elif valid_colors[Color.Red.value] == color_threshold: wires[i] = Color.Red.value elif valid_colors[Color.White.value] == color_threshold: wires[i] = Color.White.value return wires
def get_next_color(img, sc_func, ranges, features, curr_match): coords = [(140, 95), (74, 155), (204, 156), (140, 216)] colors = ["Red", "Blue", "Green", "Yellow"] button_coords = [] pause_between_blinks = 0.35 colors_matched = 0 timestamp = 0 while True: sc, _, _ = sc_func() rgb = feature_util.split_channels( cv2.cvtColor(array(sc), cv2.COLOR_RGB2BGR)) for i, (low, high) in enumerate(ranges): coord = coords[i] if feature_util.color_in_range( coord, rgb, low, high) and time() - timestamp > pause_between_blinks: colors_matched += 1 press_color = get_response_color(i, features) button_coords.append(coords[press_color]) timestamp = time() if colors_matched == curr_match: log(f"Color: {colors[i]}. Press: {colors[press_color]}", LOG_DEBUG, "Simon Says") return button_coords sleep(0.1) # Lamps are lit for 0.25 seconds.
def get_button_color(image): color_ranges = config.BUTTON_COLOR_RANGE pixel = 210, 120 for i, (low, high) in enumerate(color_ranges): rgb = feature_util.split_channels(image) if feature_util.color_in_range(pixel, rgb, low, high): return i return -1
def get_led_states(img): coords = [(195, 77), (219, 94), (236, 120), (236, 179), (219, 205), (195, 222), (204, 57), (236, 78), (258, 112), (258, 187), (236, 220), (204, 242)] lit_low = (130, 190, 25) lit_high = (220, 255, 100) rgb = features_util.split_channels(img) states = [] for y, x in coords: states.append( features_util.color_in_range((y, x), rgb, lit_low, lit_high)) return states
def get_indicator_ratio(img): min_y, max_y = 125, 246 x = 66 min_rgb = config.DISCHARGE_MIN_RED max_rgb = config.DISCHARGE_MAX_RED rgb = features_util.split_channels(img) filled_height = 0 for y in range(min_y, max_y): if features_util.color_in_range((y, x), rgb, min_rgb, max_rgb): filled_height += 1 total_height = max_y - min_y return filled_height / total_height
def is_active(img): cropped = crop_image(img) h, w = cropped.shape[:2] y = h // 2 step_x = w // 12 start_x = (w // 10) min_red = (170, 0, 0) max_red = (255, 70, 70) rgb = features_util.split_channels(cropped) for i in range(1, 11): x = int(start_x + i * step_x) pixel = (y, x) if features_util.color_in_range(pixel, rgb, min_red, max_red): return True return False
def get_dial_orientation(img): dial_y_min, dial_x_min, dial_y_max, dial_x_max = 130, 118, 194, 182 rgb = features_util.split_channels(img) mid_y = (dial_y_min + dial_y_max) / 2 mid_x = (dial_x_min + dial_x_max) / 2 offset = 16 coords = [(mid_y - offset, mid_x), (mid_y, mid_x + offset), (mid_y + offset, mid_x), (mid_y, mid_x - offset)] red_min = (190, 30, 0) red_max = (255, 85, 40) for i, (y, x) in enumerate(coords): if features_util.color_in_range((int(y), int(x)), rgb, red_min, red_max): return i return -1
def solve(img, features): wire_coords = [(140, 49), (140, 74), (140, 126), (140, 168), (140, 190), (140, 226)] wires = get_wire_colors(img, wire_coords) stars = get_stars(img) lights = get_lights(features_util.split_channels(img)) log(desc_wires(wires, stars, lights), LOG_DEBUG, "Complicated Wires") wires_to_cut = [] for (star, light, wire) in zip(stars, lights, wires): if wire == -1: wires_to_cut.append(False) continue solve_label = get_wire_label(star, light, wire) wires_to_cut.append(parse_label(solve_label, features)) log(f"Wires to cut: {wires_to_cut}", LOG_DEBUG, "Complicated Wires") return wires_to_cut, wire_coords
def solve(img, features): wire_coords = [(140, 49), (140, 74), (140, 126), (140, 168), (140, 190), (140, 226)] wires = get_wire_colors(img, wire_coords) stars = get_stars(img) lights = get_lights(features_util.split_channels(img)) log(desc_wires(wires, stars, lights), LOG_DEBUG, "Complicated Wires") wires_to_cut = [] for (star, light, wire) in zip(stars, lights, wires): if wire == -1: wires_to_cut.append(False) continue # Get label from Venn Diagram, # based on the color of the wire and whether it has a star or lit LED. solve_label = get_wire_label(star, light, wire) # Determine whether to cut the wire, based on label and other bomb features. wires_to_cut.append(parse_label(solve_label, features)) log(f"Wires to cut: {wires_to_cut}", LOG_DEBUG, "Complicated Wires") return wires_to_cut, wire_coords
def determine_alignment(image, bbox, lit): if lit: return lit mid_x = (bbox[2] + bbox[3]) // 2 q = abs(bbox[3] - bbox[2]) // 4 q1_x = (bbox[2] + q) q3_x = abs(bbox[2] - bbox[3]) - q + bbox[2] x_coords = [q1_x, mid_x, q3_x] y_coords = [bbox[1] - 30, bbox[1] - 60, bbox[0] + 30, bbox[0] + 60] for x in x_coords: for i, y in enumerate(y_coords): h, _ = image.shape[:2] if y >= h or y < 0: continue min_c, max_c = ((25, 25, 25), (45, 45, 45)) rgb = features_util.split_channels(image) pixel = (y, x) if features_util.color_in_range(pixel, rgb, min_c, max_c): return 1 if i < 2 else -1 return 0
def get_wire_colors(img): Colors = Enum("Colors", {"Red": 0, "Blue": 1, "Black": 2}) coords = [ # Top to bottom. (91, 86), (102, 100), (102, 80), (127, 106), (138, 90), (146, 90), (174, 91), (173, 111), (184, 106) ] colors = [ # Red, Blue & Black. ((139, 0, 0), (255, 99, 71)), ((20, 20, 120), (130, 130, 255)), ((0, 0, 0), (10, 10, 10)) ] rgb = features_util.split_channels(img) wires = [-1] * 3 destinations = [-1] * 3 coords_to_cut = [-1] * 3 for i, pixel in enumerate(coords): for color, (lo, hi) in enumerate(colors): if features_util.color_in_range(pixel, rgb, lo, hi): coords_to_cut[i // 3] = coords[i] wires[i // 3] = color destinations[i // 3] = i % 3 return wires, destinations, coords_to_cut
def get_start_and_end(img): red_l = (200, 0, 0) red_h = (255, 30, 40) white_l = (190, 190, 190) white_h = (255, 255, 255) rgb = features_util.split_channels(img) start_x = 17 start_y = 16 gap = 25 start = None end = None for j in range(0, 6): y = start_y + j * gap for i in range(0, 6): x = start_x + i * gap pixel = (y, x) r, g, b = rgb if features_util.color_in_range(pixel, rgb, white_l, white_h): start = (i, j) elif features_util.color_in_range(pixel, rgb, red_l, red_h): end = (i, j) assert start is not None assert end is not None return start, end
def is_solved(image): rgb = feature_util.split_channels(cv2.cvtColor(image, cv2.COLOR_RGB2BGR)) pixel = (18, 270) lit_low = (0, 200, 0) lit_high = (50, 255, 50) return feature_util.color_in_range(pixel, rgb, lit_low, lit_high)
def solve(img, features): h, w, c = img.shape # Coordinates of wire endpoints. coords = [(40, 70), (80, 74), (118, 74), (157, 74), (198, 70), (240, 72)] # Colords of wires. color_ranges = config.WIRE_COLOR_RANGE Colors = Enum("Colors", { "Black": 0, "Yellow": 1, "Blue": 2, "White": 3, "Red": 4 }) num_wires = 0 color_hist = [0, 0, 0, 0, 0] wire_hist = [-1, -1, -1, -1, -1, -1] rgb = features_util.split_channels(img) for i, coord in enumerate(coords): for j, (low, high) in enumerate(color_ranges): if features_util.color_in_range(coord, rgb, low, high): color_hist[j] += 1 wire_hist[i] = j num_wires += 1 log(f"Wire {i+1} is {Colors(j)}", LOG_DEBUG, "Wires") break serial_odd = features.get("last_serial_odd", None) if serial_odd is None: log("WARNING: Serial number information not provided", LOG_WARNING, "Wires") raise ValueError # Something went wrong. if num_wires == 3: if color_hist[Colors.Red.value] == 0: # There are no red wires. return get_nth_wire(wire_hist, 1), coords # Cut second wire. last_wire = get_nth_wire(wire_hist, -1) if wire_hist[last_wire] == Colors.White.value: # Last wire is white. return get_nth_wire(wire_hist, -1), coords # Cut last wire if color_hist[Colors.Blue.value] > 1: # More than one blue wire. return get_nth_wire( wire_hist, -1, Colors.Blue.value), coords # Cut last blue wire. return get_nth_wire(wire_hist, -1), coords # Cut the last wire. if num_wires == 4: if color_hist[ Colors.Red. value] > 1 and serial_odd: # More than one red wire + serial number odd. return get_nth_wire(wire_hist, -1, Colors.Red.value), coords # Cut last red wire. last_wire = get_nth_wire(wire_hist, -1) # Last wire is yellow + no red wires. if wire_hist[last_wire] == Colors.Yellow.value and color_hist[ Colors.Red.value] == 0: return get_nth_wire(wire_hist, 0), coords # Cut the first wire. if color_hist[Colors.Blue.value] == 1: # Exactly one blue wire. return get_nth_wire(wire_hist, 0), coords # Cut the first wire. if color_hist[Colors.Yellow.value] > 1: # More than one yellow wire. return get_nth_wire(wire_hist, -1, Colors.Red.value), coords # Cut last red wire. return get_nth_wire(wire_hist, 1), coords # Cut the second wire. if num_wires == 5: last_wire = get_nth_wire(wire_hist, -1) # Last wire is black + serial number odd. if wire_hist[last_wire] == Colors.Black.value and serial_odd: return get_nth_wire(wire_hist, 3), coords # Cut the fourth wire. # One red wire + more than one yellow. if color_hist[ Colors.Red.value] == 1 and color_hist[Colors.Yellow.value] > 1: return get_nth_wire(wire_hist, 0), coords # Cut the first wire. if color_hist[Colors.Black.value] == 0: # No black wires. return get_nth_wire(wire_hist, 1), coords # Cut the second wire. return get_nth_wire(wire_hist, 0), coords # Cut the first wire. if num_wires == 6: if color_hist[ Colors.Yellow. value] == 0 and serial_odd: # No yellow wires + serial number odd. return get_nth_wire(wire_hist, 2), coords # Cut the third wire. # One yellow + more than one white. if color_hist[Colors.Yellow. value] == 1 and color_hist[Colors.White.value] > 1: return get_nth_wire(wire_hist, 3), coords # Cut the fourth wire. if color_hist[Colors.Red.value] == 0: # No red wires. return get_nth_wire(wire_hist, -1), coords # Cut last wire. return get_nth_wire(wire_hist, 3), coords # Cut the fourth wire. log("WARNING: Invalid number of wires.", LOG_WARNING, "Wires") raise ValueError # Something went wrong.
def solve(img, features): # Coordinates of wire endpoints. coords = [(40, 70), (80, 74), (118, 74), (157, 74), (198, 70), (240, 72)] # Colors of wires. color_ranges = config.WIRE_COLOR_RANGE Colors = Enum("Colors", { "Black": 0, "Yellow": 1, "Blue": 2, "White": 3, "Red": 4 }) num_wires = 0 color_hist = [0, 0, 0, 0, 0] # Histogram of color occurences. wire_colors = [-1, -1, -1, -1, -1, -1] # Color of each wire. -1 means wire is missing. rgb = features_util.split_channels(img) radius = 5 # Look through coordinates for each wire and check if a color matches that pixel. for i, coord in enumerate(coords): valid_colors = [0] * len(color_ranges) max_color_index = 0 max_color_count = 0 for j, (low, high) in enumerate( color_ranges): # Run through each possible color range. for m in range(coord[0] - radius, coord[0] + radius): # Look in a 5px radius around pixel. for n in range(coord[1] - radius, coord[1] + radius): if features_util.color_in_range((m, n), rgb, low, high): # Color detected. valid_colors[ j] += 1 # Record occurence of current color. if valid_colors[ j] > max_color_count: # Track most seen color. max_color_count = valid_colors[j] max_color_index = j if max_color_count > 10: # Assume no wire is present if too few pixels matched a color. color_hist[max_color_index] += 1 # Record occurence of wire color. wire_colors[ i] = max_color_index # Record what color the ith wire is. num_wires += 1 log(f"Wire {i+1} is {Colors(max_color_index)}", LOG_DEBUG, "Wires") serial_odd = features.get("last_serial_odd", None) if num_wires > 3 and serial_odd is None: # Serial number was not previous detected correctly. # If we depend on this info to solve wires, raise an error. log("WARNING: Serial number information not provided", LOG_WARNING, "Wires") raise ValueError if num_wires == 3: if color_hist[Colors.Red.value] == 0: # There are no red wires. return get_nth_wire(wire_colors, 1), coords # Cut second wire. last_wire = get_nth_wire(wire_colors, -1) if wire_colors[last_wire] == Colors.White.value: # Last wire is white. return get_nth_wire(wire_colors, -1), coords # Cut last wire if color_hist[Colors.Blue.value] > 1: # More than one blue wire. return get_nth_wire( wire_colors, -1, Colors.Blue.value), coords # Cut last blue wire. return get_nth_wire(wire_colors, -1), coords # Cut the last wire. if num_wires == 4: if color_hist[ Colors.Red. value] > 1 and serial_odd: # More than one red wire + serial number odd. return get_nth_wire(wire_colors, -1, Colors.Red.value), coords # Cut last red wire. last_wire = get_nth_wire(wire_colors, -1) # Last wire is yellow + no red wires. if wire_colors[last_wire] == Colors.Yellow.value and color_hist[ Colors.Red.value] == 0: return get_nth_wire(wire_colors, 0), coords # Cut the first wire. if color_hist[Colors.Blue.value] == 1: # Exactly one blue wire. return get_nth_wire(wire_colors, 0), coords # Cut the first wire. if color_hist[Colors.Yellow.value] > 1: # More than one yellow wire. return get_nth_wire(wire_colors, -1, Colors.Red.value), coords # Cut last red wire. return get_nth_wire(wire_colors, 1), coords # Cut the second wire. if num_wires == 5: last_wire = get_nth_wire(wire_colors, -1) # Last wire is black + serial number odd. if wire_colors[last_wire] == Colors.Black.value and serial_odd: return get_nth_wire(wire_colors, 3), coords # Cut the fourth wire. # One red wire + more than one yellow. if color_hist[ Colors.Red.value] == 1 and color_hist[Colors.Yellow.value] > 1: return get_nth_wire(wire_colors, 0), coords # Cut the first wire. if color_hist[Colors.Black.value] == 0: # No black wires. return get_nth_wire(wire_colors, 1), coords # Cut the second wire. return get_nth_wire(wire_colors, 0), coords # Cut the first wire. if num_wires == 6: if color_hist[ Colors.Yellow. value] == 0 and serial_odd: # No yellow wires + serial number odd. return get_nth_wire(wire_colors, 2), coords # Cut the third wire. # One yellow + more than one white. if color_hist[Colors.Yellow. value] == 1 and color_hist[Colors.White.value] > 1: return get_nth_wire(wire_colors, 3), coords # Cut the fourth wire. if color_hist[Colors.Red.value] == 0: # No red wires. return get_nth_wire(wire_colors, -1), coords # Cut last wire. return get_nth_wire(wire_colors, 3), coords # Cut the fourth wire. log("WARNING: Invalid number of wires.", LOG_WARNING, "Wires") raise ValueError # Something went wrong.
def solve(img, screenshot_func): pixel = (43, 108) rgb = features_util.split_channels(img) dot_pause = 0.1 # 15 frames = 0.25 seconds. dash_pause = 0.6 # 47 frames ~ 0.75 seconds. letter_pause = 1 # 63 frames ~ 1 second word_pause = 2.5 # 194 frames ~ 3.3 seconds sleep_duration = 0.05 duration = 0 solved_from_prefix = True for i in range( 2 ): # Run twice to ensure the whole sequence of letters are recorded. if not solved_from_prefix: log("Solved Morse in first round!", LOG_DEBUG, "Morse") break lit = is_lit(pixel, rgb) checkpoint = time() letters = "" symbols = "" while True: sleep(sleep_duration) screenshot, _, _ = screenshot_func() rgb = features_util.split_channels( cv2.cvtColor(array(screenshot), cv2.COLOR_RGB2BGR)) if is_lit(pixel, rgb) != lit: # Check if light has changed state. lit = not lit if lit: duration = time() - checkpoint # Record length of gap. checkpoint = time() # Record time of light being lit. if duration >= letter_pause: letter = LETTERS.get(symbols, '') log(f"LETTER: {letter}", LOG_DEBUG, "Morse") letters += letter if i == 0 and len( letters) > 1 and get_word_from_substring( letters[1:]) is not None: # Terminate if we can already guess from a substring of the word. solved_from_prefix = False # Indicate word was solved in first round. break if i == 1 and get_word_from_prefix( letters) is not None: break # Terminate if we can already guess from a prefix of the word. symbols = "" if duration >= word_pause: pos_str = "START" if i == 0 else "END" log(f"=== {pos_str} OF WORD ===", LOG_DEBUG, "Morse") break else: duration = time() - checkpoint # Record length of flash. checkpoint = time() # Record time of light being unlit. if duration >= dash_pause: symbols += "-" elif duration >= dot_pause: symbols += "." # Return amount of times to press morse button. presses = (get_word_from_prefix(letters) if solved_from_prefix else get_word_from_substring(letters)) log(f"Word: {WORDS[presses]}", LOG_DEBUG, "Morse") return presses, FREQUENCIES[presses]