def possibilities(node): """Compte le nombre de choix possible.""" state = numpify_state(node.state) possibilities = 0 for i, j in zip(*np.where(state == 0)): line = state[i] column = state[:,j] square = state[i//3*3:i//3*3+3,j//3*3:j//3*3+3] possibilities += len(reduce(np.setdiff1d, [np.arange(1, 10), line, column, square.flatten()])) return possibilities
def remaining_blanks(node): """Heuristique basée sur le nombre de cases vides.""" state = numpify_state(node.state) return state[state == 0].size blanks = 3 * 729 for i, j in zip(*np.where(state == 0)): line = state[i] column = state[:,j] square = state[i//3*3:i//3*3+3,j//3*3:j//3*3+3] blanks -= line[line == 0].size * column[column == 0].size * square[square == 0].flatten().size return blanks
def conflicts(node): """Heuristique qui compte le nombre de conflits dans une grille pour un Sudoku remplit aléatoirement.""" state = numpify_state(node.state) conflicts = 0 for i in xrange(9): for j in xrange(9): value = state[i][j] line = state[i] column = state[:,j] conflicts += line[line == value].size + column[column == value].size - 2 return conflicts
def value(self, state): """ The value of a state is determined by the sum of remaining possibilities for each cell in the grid. """ state = numpify_state(state) possibilities = 729 for i, j in zip(*np.where(state == 0)): line = state[i] column = state[:,j] square = state[i//3*3:i//3*3+3,j//3*3:j//3*3+3] possibilities -= len(reduce(np.setdiff1d, [np.arange(1, 10), line, column, square.flatten()])) return possibilities
def actions(self, state): """ les actions sont déterminées en retournant les possibilitiés qui manquent simultanément dans une ligne, colonne et grille correspondantes à une case. La position de la case et la nouvelle valeur possible est retournée sous forme d'un triplet (i, j, k). """ state = numpify_state(state) for i, j in zip(*np.where(vlen(state) > 1)): for k in state[i, j]: yield i, j, k
def non_inferable_cells(node): """ Compte le nombre de cases qui ne peuvent pas être inférées, soit les cases qui ont plus d'une possibilité. """ state = numpify_state(node.state) non_inferable = 0 for i, j in zip(*np.where(state == 0)): line = state[i] column = state[:,j] square = state[i//3*3:i//3*3+3,j//3*3:j//3*3+3] possibilities = len(reduce(np.setdiff1d, [np.arange(1, 10), line, column, square.flatten()])) if possibilities > 1: non_inferable += 1 return non_inferable
def __init__(self, initial, super_branch = True): """Remplit la grille avec des valeurs qui respectent les carrés.""" state = numpify_state(initial) # préserve les positions initiales self.initial_positions = list(zip(*np.where(state == 0))) # initialise la grille en respect des carrés for i, j in zip(*np.where(state == 0)): square = state[i//3*3:i//3*3+3,j//3*3:j//3*3+3] possibilities = np.setdiff1d(np.arange(1, 10), square.flatten()) state.itemset((i, j), possibilities[0]) self.initial = tuple(state.flatten()) self.super_branch = super_branch
def goal_test(self, state): """ Détermine si une configuration donnée est valide en supposant les carrés valides. Seul les lignes et les colonnes sont vérifiées. """ state = numpify_state(state) for line in state: if np.bincount(line).max() > 1: return False for column in state.T: if np.bincount(column).max() > 1: return False return True
def result(self, state, action): """ Calcule la configuration résultante à appliquer une action sur une configuration. Le nouvel état est une copie modifiée de l'état passé en argument. """ state = numpify_state(state) i, j, k = action state.itemset((i, j), frozenset([k])) # normalize normalized_state = normalize_state(state) return tuple(normalized_state.flatten())
def value(self, state): """ La valeur d'un état est déterminé par le nombre de cases non-conflictuelles, considérant qu'il n'y a pas de conflits sur les carrés. """ state = numpify_state(state) conflicts = 0 for i in xrange(9): for j in xrange(9): if (i, j) not in self.initial_positions: value = state[i][j] line = state[i] column = state[:,j] conflicts += line[line == value].size + column[column == value].size - 2 # on cherche à minimiser les conflits (au plus 81 * 4 = 324) return -1*conflicts
def actions(self, state): """ les actions sont déterminées en retournant les possibilitiés qui manquent simultanément dans une ligne, colonne et grille correspondantes à une case. La position de la case et la nouvelle valeur possible est retournée sous forme d'un triplet (i, j, k). """ state = numpify_state(state) for i, j in zip(*np.where(state == 0)): line = state[i] column = state[:,j] square = state[i//3*3:i//3*3+3,j//3*3:j//3*3+3] for k in xrange(1, 10): # valide la nouvelle configuration et s'assurant qu'une même # valeur non-nulle n'apparait pas plus d'une fois dans la ligne, # colonne et carré correspondant if k not in line and k not in column and k not in square: yield i, j, k