def y_wing_scan(board): for x in range(9): for y in range(9): if np.sum(board[x][y]) != 2: continue [az, bz] = list(np.nonzero(board[x][y])[0]) xy_units = units(x, y) # get all pairs of unit cells concatenated_units = sorted( list(set(xy_units[0] + xy_units[1] + xy_units[2]))) concatenated_units.remove((x, y)) pairs = [] for (ax, ay) in concatenated_units: for (bx, by) in concatenated_units: if (ax, ay) == (bx, by): continue if np.sum(board[ax][ay]) == 2 and np.sum( board[bx][by]) == 2: if board[ax][ay][az] == 1 and board[bx][by][bz] == 1: union = board[ax][ay] + board[bx][by] if np.sum(union > 0) == 3: if np.argmax(union) != az and np.argmax( union) != bz: # found a valid pair pairs.append([(ax, ay), (bx, by), np.argmax(union)]) if len(pairs) > 0: removed = False for [(ax, ay), (bx, by), cz] in pairs: units_a = set( [cell for unit in units(ax, ay) for cell in unit]) units_b = set( [cell for unit in units(bx, by) for cell in unit]) for (cx, cy) in sorted(list(units_a.intersection(units_b))): if (cx, cy) in [(ax, ay), (bx, by), (x, y)]: continue if board[cx][cy][cz] == 1: board[cx][cy][cz] = 0 removed = True if removed: relevant_cells = [(x, y), (ax, ay), (bx, by), (cx, cy)] relevant_candidates = [[cz]] return (True, relevant_cells, np.array(relevant_candidates)) return False, None, None
def hidden_single_scan(board): for x in range(9): for y in range(9): if np.sum(board[x][y]) == 1: continue xy_units = units(x, y) for z in range(9): if board[x][y][z] == 0: continue for unit in xy_units: hidden_candidate = True for (searchX, searchY) in unit: if (searchX, searchY) == (x, y): continue if board[searchX][searchY][z] == 1: hidden_candidate = False break if hidden_candidate: board[x][y] = np.zeros((9)) board[x][y][z] = 1 update_guesses(board, x, y) relevant_cells = [(x, y)] relevant_candidates = [ np.nonzero(board[a][b])[0] for (a, b) in relevant_cells ] return (True, relevant_cells, np.array(relevant_candidates)) return False, None, None
def xyz_wing_scan(board): for x in range(9): for y in range(9): if np.sum(board[x][y]) != 3: continue xyz = np.nonzero(board[x][y])[0] xy_units = units(x, y) # get all pairs of unit cells return False, None, None
def naked_triples_scan(board): for x in range(9): for y in range(9): # find 3 cells with a total of 3 numbers # every cell has to have at least 2 guesses if np.sum(board[x][y]) != 2 and np.sum(board[x][y]) != 3: continue xy_units = units(x, y) for unit in xy_units: found_triple = False for (x1, y1) in unit: if np.sum(board[x1][y1]) != 2 and np.sum( board[x1][y1]) != 3: continue for (x2, y2) in unit: if np.sum(board[x2][y2]) != 2 and np.sum( board[x2][y2]) != 3: continue if len(set([(x, y), (x1, y1), (x2, y2)])) < 3: # make sure cells are unique continue # found 3 unique cells in this unit with 2-3 guesses intersection = board[x][y] + board[x1][y1] + board[x2][ y2] if np.sum(intersection >= 1) == 3: found_triple = True break else: # double loop breaking continue break if found_triple: removed = False for (searchX, searchY) in unit: if (searchX, searchY) == (x, y) or (searchX, searchY) == ( x1, y1) or (searchX, searchY) == (x2, y2): continue for zz in range(9): if intersection[zz] > 0: if board[searchX][searchY][zz] == 1: removed = True board[searchX][searchY][zz] = 0 if removed: relevant_cells = [(x, y), (x1, y1), (x2, y2)] relevant_candidates = [ np.nonzero(board[a][b])[0] for (a, b) in relevant_cells ] return (True, relevant_cells, np.array(relevant_candidates)) return False, None, None
def naked_single_scan(board): for x in range(9): for y in range(9): if np.sum(board[x][y]) != 1: continue xy_units = units(x, y) removed = False candidate = np.argmax(board[x][y]) for unit in xy_units: for (searchX, searchY) in unit: if (searchX, searchY) == (x, y): continue if board[searchX][searchY][candidate] == 1: board[searchX][searchY][candidate] = 0 removed = True if removed: relevant_cells = [(x, y)] relevant_candidates = [ np.nonzero(board[a][b])[0] for (a, b) in relevant_cells ] return (True, relevant_cells, np.array(relevant_candidates)) return False, None, None
def naked_pairs_scan(board): for x in range(9): for y in range(9): if np.sum(board[x][y]) != 2: continue xy_units = units(x, y) for unit in xy_units: found_pair = False for (x1, y1) in unit: if (x1, y1) == (x, y): continue if np.array_equal(board[x][y], board[x1][y1]): found_pair = True break if found_pair: removed = False for (searchX, searchY) in unit: if (searchX, searchY) == (x, y) or (searchX, searchY) == (x1, y1): continue for zz in range(9): if board[x][y][zz] == 1: if board[searchX][searchY][zz] == 1: removed = True board[searchX][searchY][zz] = 0 if removed: relevant_cells = [(x, y), (x1, y1)] relevant_candidates = [ np.nonzero(board[a][b])[0] for (a, b) in relevant_cells ] return (True, relevant_cells, np.array(relevant_candidates)) return False, None, None
def x_wing_scan(board): for x in range(9): for y in range(9): for z in range(9): if np.sum(board[x][y]) == 1: continue if board[x][y][z] == 0: continue similar_in_unit = [[], []] # [0] contains all cells in the same row with candidate z # [1] contains all cells in the same col with candidate z xy_units = units(x, y) for i in [0, 1]: for (x1, y1) in xy_units[i]: if board[x1][y1][z] == 1: similar_in_unit[i].append((x1, y1)) if len(similar_in_unit[0]) == 2 and len( similar_in_unit[1]) >= 2: # the two coords in [0] (row) are locked in a pair a = 0 b = 1 elif len(similar_in_unit[1]) == 2 and len( similar_in_unit[0]) >= 2: a = 1 b = 0 else: continue opposite_coord = similar_in_unit[a][similar_in_unit[a].index( (x, y)) - 1][a] # time to look at each coordinate to find another locked pair with the same similar axis for (x1, y1) in similar_in_unit[b]: if (x1, y1) != (x, y): other_units = units(x1, y1) other_similar = [] for (x2, y2) in other_units[ a]: # looking at this other cell's row if board[x2][y2][z] == 1: other_similar.append((x2, y2)) if len(other_similar) == 2: # found another locked pair, time to check if x coordinates match up # we know that one x coordinate will, just have to check if the second one does opposite_corner = other_similar[ other_similar.index((x1, y1)) - 1] if opposite_corner[a] == opposite_coord: # found an x wing x_wing = [similar_in_unit[a], other_similar] # go through columns and remove # print('!!!') # print(x_wing, z + 1) removed = False unit_a = units(*x_wing[0][0])[b] unit_b = units(*x_wing[0][1])[b] for (searchX, searchY) in unit_a: if (searchX, searchY) in x_wing[0] or ( searchX, searchY) in x_wing[1]: continue if board[searchX][searchY][z] == 1: removed = True board[searchX][searchY][z] = 0 for (searchX, searchY) in unit_b: if (searchX, searchY) in x_wing[0] or ( searchX, searchY) in x_wing[1]: continue if board[searchX][searchY][z] == 1: removed = True board[searchX][searchY][z] = 0 if removed: relevant_cells = x_wing[0] + x_wing[1] relevant_candidates = [ np.nonzero(board[a][b])[0] for (a, b) in relevant_cells ] return (True, relevant_cells, np.array(relevant_candidates)) return False, None, None
def intersection_scan(board): for x in range(9): for y in range(9): if np.sum(board[x][y]) == 1: continue xy_units = units(x, y) for z in range(9): if board[x][y][z] == 0: continue # pointing pairs and triples for i in range(3): candidate_coords = [] for (x1, y1) in xy_units[i]: if np.sum(board[x1][y1]) == 1: continue if board[x1][y1][z] == 1: candidate_coords.append((x1, y1)) if len(candidate_coords) <= 3: removed = False if i == 2: # pointing pairs within the same box # check if the candidates line up on a row or column if all( map(lambda coord: coord in xy_units[0], candidate_coords)): # row lineup for (searchX, searchY) in xy_units[0]: if not (searchX, searchY) in candidate_coords: if board[searchX][searchY][z] == 1: board[searchX][searchY][z] = 0 removed = True elif all( map(lambda coord: coord in xy_units[1], candidate_coords)): # col lineup for (searchX, searchY) in xy_units[1]: if not (searchX, searchY) in candidate_coords: if board[searchX][searchY][z] == 1: board[searchX][searchY][z] = 0 removed = True else: # candidates on a row or column # check if they are in the same box if all( map(lambda coord: coord in xy_units[2], candidate_coords)): for (searchX, searchY) in xy_units[2]: if not (searchX, searchY) in candidate_coords: if board[searchX][searchY][z] == 1: board[searchX][searchY][z] = 0 removed = True if removed: # relevant_candidates = [np.nonzero(board[a][b])[0] for (a, b) in candidate_coords] return (True, candidate_coords, np.array([[z]])) return False, None, None
def naked_quads_scan(board): for x in range(9): for y in range(9): # find 3 cells with a total of 3 numbers # every cell has to have at least 2 guesses if np.sum(board[x][y]) not in [2, 3, 4]: continue xy_units = units(x, y) for unit in xy_units: found_quad = False # TODO get all four-groups of cells that fit criterion? # rather than nested looping for (x1, y1) in unit: if np.sum(board[x1][y1]) not in [2, 3, 4]: continue for (x2, y2) in unit: if np.sum(board[x2][y2]) not in [2, 3, 4]: continue for (x3, y3) in unit: if np.sum(board[x3][y3]) not in [2, 3, 4]: continue if len(set([(x, y), (x1, y1), (x2, y2), (x3, y3)])) < 4: # make sure cells are unique continue # found 4 unique cells in this unit with 2-4 guesses intersection = board[x][y] + board[x1][y1] + board[ x2][y2] + board[x3][y3] if np.sum(intersection >= 1) == 4: found_quad = True break else: # double loop breaking continue break else: # double loop breaking continue break if found_quad: removed = False for (searchX, searchY) in unit: if (searchX, searchY) == (x, y) or ( searchX, searchY) == (x1, y1) or (searchX, searchY) == ( x2, y2) or (searchX, searchY) == (x3, y3): continue for zz in range(9): if intersection[zz] > 0: if board[searchX][searchY][zz] == 1: removed = True board[searchX][searchY][zz] = 0 if removed: relevant_cells = [(x, y), (x1, y1), (x2, y2), (x3, y3)] relevant_candidates = [ np.nonzero(board[a][b])[0] for (a, b) in relevant_cells ] return (True, relevant_cells, np.array(relevant_candidates)) return False, None, None