Sudoku by friends?python seconds solution only knows how to cut down?

Keywords: Python github Pycharm less

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"

Posted by dannys on Tue, 06 Aug 2019 16:05:37 -0700