Skip to content

Instantly share code, notes, and snippets.

@Joeccp
Last active January 5, 2024 04:23
Show Gist options
  • Save Joeccp/f5c8768709e295fd7a867dfc97b71085 to your computer and use it in GitHub Desktop.
Save Joeccp/f5c8768709e295fd7a867dfc97b71085 to your computer and use it in GitHub Desktop.
Generate shuffed 9x9 sudoku
"""
Sudoku generator
Algorithm shamelessly borrowed from https://stackoverflow.com/a/56581709/16747758 (By Alain T., released under CC-BY-SA 4.0)
"""
from itertools import product, batched, chain
from typing import *
from random import sample
def check_sudoku_valid(board) -> (bool, str):
"""
Check if the sudoku board is a valid board.
Note that a non-full board can be a valid board.
"""
# Check rows
for row_index, row in enumerate(board):
row_used: list = []
for cell in row:
if cell in row_used:
return False, f'Value {cell} repeated in row {row_index}'
row_used.append(cell)
# Check columns
for column_index in range(9):
column_used: list = []
for row_index in range(9):
cell = board[row_index][column_index]
if cell in column_used:
return False, f'Value {cell} repeated in column {column_index}'
column_used.append(cell)
# Check boxes
for i, j in product(range(3), repeat=2):
box_used: list = []
for k, l in product(range(3), repeat=2):
cell = board[i * 3 + k][j * 3 + l]
if cell in box_used:
return False, f'Value {cell} repeated in box {3 * i + j}'
box_used.append(cell)
return True
def generate_shuffled_board(board: list[list[int]] = None, shuffle_times: int = 5) -> list[list[int]]:
if board is None:
board: list[list[int]] = [
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[4, 5, 6, 7, 8, 9, 1, 2, 3],
[7, 8, 9, 1, 2, 3, 4, 5, 6],
[2, 3, 1, 5, 6, 4, 8, 9, 7],
[5, 6, 4, 8, 9, 7, 2, 3, 1],
[8, 9, 7, 2, 3, 1, 5, 6, 4],
[3, 1, 2, 6, 4, 5, 9, 7, 8],
[6, 4, 5, 9, 7, 8, 3, 1, 2],
[9, 7, 8, 3, 1, 2, 6, 4, 5]
]
assert check_sudoku_valid(board)
# Shuffle row group
row_grouped = list(batched(board, 3))
row_grouped = sample(row_grouped, 3)
board = list(chain.from_iterable(row_grouped))
assert check_sudoku_valid(board)
# Shuffle row 1-3
shuffled_rows = sample(board[0:3], 3)
board = [*shuffled_rows, *board[3:]]
assert check_sudoku_valid(board)
# Shuffle row 4-6
shuffled_rows = sample(board[3:6], 3)
board = [*board[0:3], *shuffled_rows, *board[6:9]]
assert check_sudoku_valid(board)
# Shuffle row 7-9
shuffled_rows = sample(board[6:9], 3)
board = [*board[0:6], *shuffled_rows]
assert check_sudoku_valid(board)
# Row <-> Column
board = list(zip(*board))
assert check_sudoku_valid(board)
# Shuffle row group
row_grouped = list(batched(board, 3))
row_grouped = sample(row_grouped, 3)
board = list(chain.from_iterable(row_grouped))
assert check_sudoku_valid(board)
# Shuffle row 1-3
shuffled_rows = sample(board[0:3], 3)
board = [*shuffled_rows, *board[3:]]
assert check_sudoku_valid(board)
# Shuffle row 4-6
shuffled_rows = sample(board[3:6], 3)
board = [*board[0:3], *shuffled_rows, *board[6:9]]
assert check_sudoku_valid(board)
# Shuffle row 7-9
shuffled_rows = sample(board[6:9], 3)
board = [*board[0:6], *shuffled_rows]
assert check_sudoku_valid(board)
# Column <-> Row
board = list(zip(*board))
assert check_sudoku_valid(board)
for _ in range(shuffle_times-1):
print(board)
board = generate_shuffled_board(board)
return board
for line in generate_shuffled_board():
print(line)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment