Created
August 5, 2024 18:33
-
-
Save nibzard/2c5ffcd1784f795042055af72815224f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| !pip install reportlab | |
| !pip install pypdf2 | |
| import random | |
| import math | |
| from numba import njit, cuda | |
| import numpy as np | |
| from reportlab.lib.pagesizes import A4 | |
| from reportlab.pdfgen import canvas | |
| from reportlab.lib.units import cm | |
| import io | |
| def get_box_size(size): | |
| if size in [4, 9, 16]: | |
| root = int(size ** 0.5) | |
| return root, root | |
| elif size == 6: | |
| return 2, 3 | |
| elif size == 12: | |
| return 3, 4 | |
| else: | |
| raise ValueError("Unsupported size. Supported sizes: 4, 6, 9, 12, 16.") | |
| @cuda.jit | |
| def fill_sudoku_kernel(grid, size, permutations, rows_per_box, cols_per_box): | |
| row, col = cuda.grid(2) | |
| if row < size and col < size: | |
| num = permutations[(row * cols_per_box + row // rows_per_box + col) % size] | |
| grid[row, col] = num | |
| def fill_sudoku(grid, size): | |
| permutations = np.random.permutation(size) + 1 # Random permutation of numbers 1 to size | |
| d_permutations = cuda.to_device(permutations) | |
| rows_per_box, cols_per_box = get_box_size(size) | |
| # For smaller grids like 6x6, use a smaller threads_per_block | |
| if size <= 9: | |
| threads_per_block = (8, 8) # Smaller block size for smaller grids | |
| else: | |
| threads_per_block = (16, 16) # Larger block size for larger grids | |
| # Calculate the number of blocks in the grid | |
| blocks_per_grid_x = max(1, math.ceil(size / threads_per_block[0])) | |
| blocks_per_grid_y = max(1, math.ceil(size / threads_per_block[1])) | |
| blocks_per_grid = (blocks_per_grid_x, blocks_per_grid_y) | |
| # Launch the kernel with the calculated grid size | |
| fill_sudoku_kernel[blocks_per_grid, threads_per_block](grid, size, d_permutations, rows_per_box, cols_per_box) | |
| cuda.synchronize() | |
| @njit | |
| def is_valid_move_cpu(grid, row, col, num, size, rows_per_box, cols_per_box): | |
| for x in range(size): | |
| if grid[row][x] == num or grid[x][col] == num: | |
| return False | |
| start_row, start_col = (row // rows_per_box) * rows_per_box, (col // cols_per_box) * cols_per_box | |
| for i in range(rows_per_box): | |
| for j in range(cols_per_box): | |
| if grid[start_row + i][start_col + j] == num: | |
| return False | |
| return True | |
| @njit | |
| def solve_sudoku_cpu(grid, size, rows_per_box, cols_per_box): | |
| for row in range(size): | |
| for col in range(size): | |
| if grid[row][col] == 0: | |
| for num in range(1, size + 1): | |
| if is_valid_move_cpu(grid, row, col, num, size, rows_per_box, cols_per_box): | |
| grid[row][col] = num | |
| if solve_sudoku_cpu(grid, size, rows_per_box, cols_per_box): | |
| return True | |
| grid[row][col] = 0 | |
| return False | |
| return True | |
| def generate_sudoku_cpu(size, difficulty): | |
| supported_sizes = [4, 6, 9, 12, 16] | |
| if size not in supported_sizes: | |
| raise ValueError(f"Unsupported size. Supported sizes: {supported_sizes}.") | |
| grid = np.zeros((size, size), dtype=np.int32) | |
| d_grid = cuda.to_device(grid) | |
| fill_sudoku(d_grid, size) # Fill grid with a randomized sequence | |
| grid = d_grid.copy_to_host() | |
| rows_per_box, cols_per_box = get_box_size(size) | |
| if not solve_sudoku_cpu(grid, size, rows_per_box, cols_per_box): | |
| raise ValueError("Failed to create a complete Sudoku grid.") | |
| original_grid = grid.copy() | |
| cells_to_remove = int(size * size * difficulty) | |
| while cells_to_remove > 0: | |
| row, col = random.randint(0, size-1), random.randint(0, size-1) | |
| if grid[row][col] != 0: | |
| grid[row][col] = 0 | |
| cells_to_remove -= 1 | |
| return grid, original_grid # Return both puzzle and solution | |
| def create_sudoku_pdf(puzzle, solution, filename="sudoku_puzzle_with_solution.pdf"): | |
| size = len(puzzle) | |
| rows_per_box, cols_per_box = get_box_size(size) | |
| # Set the page size to A4 | |
| page_width, page_height = A4 | |
| # Increase the margins for more comfortable viewing | |
| margin = 2 * cm | |
| # Adjust the maximum grid size to take the increased margins into account | |
| max_grid_size = min(page_width - 2 * margin, page_height - 2 * margin) | |
| # Calculate a smaller cell size so that the grid fits well within the margins | |
| cell_size = max_grid_size / size | |
| buffer = io.BytesIO() | |
| c = canvas.Canvas(buffer, pagesize=A4) | |
| c.setLineWidth(0.5) | |
| def draw_grid(x_offset, y_offset, grid, cell_size, font_size): | |
| grid_size = size * cell_size | |
| c.setLineWidth(2) | |
| c.rect(x_offset, y_offset, grid_size, grid_size) | |
| c.setLineWidth(0.5) | |
| for i in range(1, size): | |
| x = x_offset + i * cell_size | |
| y = y_offset + i * cell_size | |
| if i % cols_per_box == 0: | |
| c.setLineWidth(2) | |
| c.line(x, y_offset, x, y_offset + grid_size) | |
| c.setLineWidth(0.5) | |
| if i % rows_per_box == 0: | |
| c.setLineWidth(2) | |
| c.line(x_offset, y, x_offset + grid_size, y) | |
| c.setLineWidth(0.5) | |
| for row in range(size): | |
| for col in range(size): | |
| if grid[row][col] != 0: | |
| x = x_offset + (col + 0.5) * cell_size | |
| y = y_offset + (size - row - 0.5) * cell_size | |
| c.setFont("Helvetica", font_size) | |
| c.drawCentredString(x, y - 4, str(grid[row][col])) | |
| # Adjust y_offset based on margin and cell size | |
| y_offset = page_height - margin - size * cell_size | |
| draw_grid(margin, y_offset, puzzle, cell_size, font_size=cell_size*0.4) | |
| solution_cell_size = cell_size / 2.5 | |
| solution_grid_size = size * solution_cell_size | |
| solution_y_offset = y_offset - margin - solution_grid_size | |
| draw_grid(margin, solution_y_offset, solution, solution_cell_size, font_size=solution_cell_size*0.4) | |
| c.setFont("Helvetica-Bold", 14) | |
| c.drawString(margin, y_offset + size * cell_size + 10, "Sudoku Puzzle") | |
| c.drawString(margin, solution_y_offset + solution_grid_size + 10, "Solution") | |
| c.save() | |
| buffer.seek(0) | |
| return buffer | |
| # Example usage | |
| size = 16 | |
| difficulty = 0.7 | |
| puzzle, solution = generate_sudoku_cpu(size, difficulty) | |
| pdf_buffer = create_sudoku_pdf(puzzle, solution) | |
| with open("sudoku_puzzle_with_solution.pdf", "wb") as f: | |
| f.write(pdf_buffer.getvalue()) | |
| print("Sudoku puzzle with solution PDF has been saved to 'sudoku_puzzle_with_solution.pdf'") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment