The landowner who played with his neighbour the other day was found and his cards were confiscated. The landowner can't fight anymore, but I still want to play with his neighbour.If you still want to fight the landowner, stamp: Fight the landlord with the next neighbor while the old king is not there
Wanting to break my mind finally reminds me of a game, Sudoku!What is Sudoku?Sudoku is a game that allows me to play with my neighbors while the old king is away!
Sudoku rules
1. Numbers 1-9 can only appear once per line.
2. Numbers 1-9 can only appear once in each column.
3. Numbers 1-9 can only appear once per 3x3 uterus.3x3 intrauterine A1-C3,A4-C6,A7-C9,D1-F3,D4-F6,D7-F9...
Sudoku Title Example
Rough ideas
1. Sudoku we use a two-dimensional list to store, where there is no value we use''empty characters to occupy places.(2-D array)
2. Get the data in each 3*3 uterus, each row, and each column, and store it.3. Get all the vacant locations, iterate through the vacant locations, try to place the data, and make a judgment. If the condition is satisfied, continue to place the next one.By analogy, if a condition is not met on the way, it is retrospective, returns to the last condition that was met, and makes another attempt.
Demonstration environment
- Operating system: windows10
- python version: python 3.7
- Code Editor: pycharm 2018.2
Specific code
1. Prefer us to create a class SudoKu.Write the constructor.
class SudoKu(): def __init__(self,sudo_ku_data): # Determine if the incoming Sudoku satisfies the format if not isinstance(sudo_ku_data,list): raise TypeError(f'sudo_ku_data params must a list, but {sudo_ku_data} is a {type(sudo_ku_data)}') if len(sudo_ku_data) != 9 or len(sudo_ku_data[0]) != 9: raise TypeError(f'sudo_ku_data params must a 9*9 list, but {sudo_ku_data} is a {len(sudo_ku_data)}*{len(sudo_ku_data[0])} list') self.sudo_ku = sudo_ku_data # Store existing data for each row self.every_row_data = {} # Existing numbers in each column self.every_column_data = {} # Every 3*3 digit in the uterus self.every_three_to_three_data = {} # Location of each vacancy self.vacant_position = [] # Numbers tried for each vacant location self.every_vacant_position_tried_values = {}
2. Write methods to add each row, column and uterus so that we can call them later
def _add_row_data(self,row,value): ''' //Adding data to self.every_row_data adds data that already exists on each row :param row: :param value: :return: ''' # If the current row does not exist, set() (empty set) is initialized with the current behavior key. if row not in self.every_row_data: self.every_row_data[row] = set() # If this value has already appeared on this line, it means that the correct Sudoku was not passed in if value in self.every_row_data[row]: raise TypeError(f'params {self.sudo_ku} is a invalid SudoKu') self.every_row_data[row].add(value) def _add_column_data(self,column,value): ''' //Add data to self.every_column_data, and the above function works the same way :param column: :param value: :return: ''' if column not in self.every_column_data: self.every_column_data[column] = set() if value in self.every_column_data[column]: raise TypeError(f'params {self.sudo_ku} is a invalid SudoKu') self.every_column_data[column].add(value) def _get_three_to_three_key(self,row,column): ''' //To find out in which 3*3 uterus the location is :param row: :param column: :return: ''' if row in [0,1,2]: if column in [0,1,2]: key = 1 elif column in [3,4,5]: key = 2 else: key = 3 elif row in [3,4,5]: if column in [0,1,2]: key = 4 elif column in [3,4,5]: key = 5 else: key = 6 else: if column in [0,1,2]: key = 7 elif column in [3,4,5]: key = 8 else: key = 9 return key def _add_three_to_three_data(self,row,column,value): ''' //Add data to self.every_three_to_three_data :param row: :param column: :param value: :return: ''' # Which 3*3 uterus do you get first key = self._get_three_to_three_key(row,column) # Then add rows and columns as above if key not in self.every_three_to_three_data: self.every_three_to_three_data[key] = set() if value in self.every_three_to_three_data[key]: raise TypeError(f'params {self.sudo_ku} is a invalid SudoKu') self.every_three_to_three_data[key].add(value)
3. Traverse Sudoku to initialize each kind of data
def _init(self): ''' //Initialize data based on incoming Sudoku :return: ''' for row,row_datas in enumerate(self.sudo_ku): for column,value in enumerate(row_datas): if value == '': # Add vacant locations self.vacant_position.append( (row,column) ) else: # Add row data self._add_row_data(row,value) # Add Column Data self._add_column_data(column,value) # Add uterine data self._add_three_to_three_data(row,column,value)
4. Write a function to determine if the value of a location is legal
def _judge_value_is_legal(self,row,column,value): ''' //Determine if the data placed by the party is legal :param row: :param column: :param value: :return: ''' # Whether value exists in this row of data if value in self.every_row_data[row]: return False # Does value exist in this column of data if value in self.every_column_data[column]: return False # Does value exist in this 3*3 uterus? key = self._get_three_to_three_key(row,column) if value in self.every_three_to_three_data[key]: return False return True
5. Write a calculated function that loops through the amount of data available at the current location to determine if this value can be placed
def _calculate(self, vacant_position): ''' //Calculate, start placing values on the individual :param vacant_position: :return: ''' # Get the current location row,column = vacant_position values = set(range(1,10)) # Create a unique key for the current location to hold data that the current location has tried key = str(row) + str(column) # If this key exists, set the values differently, since both are sets, just use - if key in self.every_vacant_position_tried_values: values = values - self.every_vacant_position_tried_values[key] # If this key does not exist, create an empty collection else: self.every_vacant_position_tried_values[key] = set() for value in values: # Add current data to data that has been tried at the current location self.every_vacant_position_tried_values[key].add(value) # If the current value is legal, it can be placed if self._judge_value_is_legal(row,column,value): print(f'set {vacant_position} value is {value}') # Update the data needed to determine if the data is valid self.every_column_data[column].add(value) self.every_row_data[row].add(value) key = self._get_three_to_three_key(row,column) self.every_three_to_three_data[key].add(value) # Modify the value of this location to value self.sudo_ku[row][column] = value # Return True and populated value return True,value return False,None
6. If there is no value at the current location to place, then go back, return to the last successful location, and retrieve the value, so we write a backtrace function
def _backtrack(self,current_vacant_position,previous_vacant_position,previous_value): ''' //To flash back :param current_vacant_position: Location where the current attempt failed :param previous_vacant_position: Last Success Location :param previous_value:Value of last success :return: ''' print(f"run backtracking... value is {previous_value},vacant position is {previous_vacant_position}") row,column = previous_vacant_position # Remove the last successful value from the data needed for judgment self.every_column_data[column].remove(previous_value) self.every_row_data[row].remove(previous_value) key = self._get_three_to_three_key(row,column) self.every_three_to_three_data[key].remove(previous_value) # And the last changed value goes back self.sudo_ku[row][column] = '' # Delete the value where the current attempt failed that has failed in the city because it is backtracked, so the next entry needs to be re-evaluated current_row,current_column = current_vacant_position key = str(current_row) + str(current_column) self.every_vacant_position_tried_values.pop(key)
7. So far, all our function functions have been written, and then we write a function to start looping through all the vacant locations.Then calculate.
def get_result(self): ''' //Sudoku after calculation :return: ''' # First initialize the data self._init() # Length of vacant position length = len(self.vacant_position) # Subscript for vacant location index = 0 # Store data you've tried tried_values = [] # If index is less than length, it is not calculated yet while index < length: # Get a vacant location vacant_position = self.vacant_position[index] # Includes the calculation function, returns whether it succeeds, if it succeeds, value is the value of success, if it fails, value is None is_success,value = self._calculate(vacant_position) # If successful, place the value in the tried_values list because the list is ordered. # index+1 tries the next location if is_success: tried_values.append(value) index += 1 # Failed, backtracked, and index-1, back to the last vacant location, we need to pass in the current failed location and the last successful location and value else: self._backtrack(vacant_position,self.vacant_position[index-1],tried_values.pop()) index -= 1 # If index <0 indicates that the number is invalid if index < 0: raise ValueError(f'{self.sudo_ku} is a invalid sudo ku') # Return Sudoku after calculation return self.sudo_ku
Effect Display
Call.Finally, the code is finished, so let's start harvesting
if __name__ == '__main__': sudo_ku_data = [ [5,3,'','',7,'','','',''], [6,'','',1,9,5,'','',''], ['',9,8,'','','','',6,''], [8,'','','',6,'','','',3], [4,'','',8,'',3,'','',1], [7,'','','',2,'','','',6], ['',6,'','','','',2,8,''], ['','','',4,1,9,'','',5], ['','','','',8,'','',7,9], ] # Get a calculated Sudoku sudo_ku = SudoKu(sudo_ku_data).get_result() print(sudo_ku) ################ # The results show that # ################ [5, 3, 4, 6, 7, 8, 9, 1, 2] [6, 7, 2, 1, 9, 5, 3, 4, 8] [1, 9, 8, 3, 4, 2, 5, 6, 7] [8, 5, 9, 7, 6, 1, 4, 2, 3] [4, 2, 6, 8, 5, 3, 7, 9, 1] [7, 1, 3, 9, 2, 4, 8, 5, 6] [9, 6, 1, 5, 3, 7, 2, 8, 4] [2, 8, 7, 4, 1, 9, 6, 3, 5] [3, 4, 5, 2, 8, 6, 1, 7, 9]
That's perfect. We're testing a rare Sudoku.
Input Sudoku is:
[ [8, '', '', '', '', '', '', '', 4], ['', 2, '', '', '', '', '', 7, ''], ['', '', 9, 1, '', 6, 5, '', ''], ['', '', 6, 2, '', 8, 9, '', ''], ['', 9, '', '', 3, '', '', 4, ''], ['', '', 2, 4, '', 7, 8, '', ''], ['', '', 7, 9, '', 5, 6, '', ''], ['', 8, '', '', '', '', '', 2, ''], [6, '', '', '', '', '', '', '', 9], ] ################ # The results show that # ################ [8, 6, 1, 5, 7, 2, 3, 9, 4] [5, 2, 4, 3, 8, 9, 1, 7, 6] [3, 7, 9, 1, 4, 6, 5, 8, 2] [4, 3, 6, 2, 5, 8, 9, 1, 7] [7, 9, 8, 6, 3, 1, 2, 4, 5] [1, 5, 2, 4, 9, 7, 8, 6, 3] [2, 4, 7, 9, 1, 5, 6, 3, 8] [9, 8, 5, 7, 6, 3, 4, 2, 1] [6, 1, 3, 8, 2, 4, 7, 5, 9]
Ha Ha Ha Ha, who can compare with me in the future?Expansion. jpg
Code has been completely uploaded to Github: https://github.com/MiracleYou...
For more fun and interesting Pythons, follow the "Python column"