Mystery Game #14
← Back to Games List
// Sudoku Game Implementation
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// Game settings
const gridSize = 9;
const cellSize = 50;
const padding = 2;
const thickLine = 4;
canvas.width = gridSize * cellSize + padding * 2;
canvas.height = canvas.width;
// Game state
let board = Array(gridSize).fill().map(() => Array(gridSize).fill(0));
let initialBoard = Array(gridSize).fill().map(() => Array(gridSize).fill(0));
let selectedCell = null;
let gameOver = false;
// Generate a valid Sudoku board
function generateBoard() {
// Clear board
board = Array(gridSize).fill().map(() => Array(gridSize).fill(0));
// Fill diagonal 3x3 boxes first (they're independent)
for (let box = 0; box < 3; box++) {
const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9];
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
const num = nums.splice(Math.floor(Math.random() * nums.length), 1)[0];
board[box * 3 + i][box * 3 + j] = num;
}
}
}
// Fill rest of the board
solveBoard();
// Remove numbers to create puzzle
const cellsToRemove = 45; // Adjust difficulty here
for (let i = 0; i < cellsToRemove; i++) {
let row, col;
do {
row = Math.floor(Math.random() * gridSize);
col = Math.floor(Math.random() * gridSize);
} while (board[row][col] === 0);
board[row][col] = 0;
}
// Save initial board state
initialBoard = board.map(row => [...row]);
}
// Solve the board using backtracking
function solveBoard() {
const emptyCell = findEmptyCell();
if (!emptyCell) return true;
const [row, col] = emptyCell;
const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9];
shuffle(nums);
for (const num of nums) {
if (isValidMove(row, col, num)) {
board[row][col] = num;
if (solveBoard()) return true;
board[row][col] = 0;
}
}
return false;
}
// Find an empty cell
function findEmptyCell() {
for (let row = 0; row < gridSize; row++) {
for (let col = 0; col < gridSize; col++) {
if (board[row][col] === 0) {
return [row, col];
}
}
}
return null;
}
// Check if a move is valid
function isValidMove(row, col, num) {
// Check row
for (let x = 0; x < gridSize; x++) {
if (board[row][x] === num) return false;
}
// Check column
for (let y = 0; y < gridSize; y++) {
if (board[y][col] === num) return false;
}
// Check 3x3 box
const boxRow = Math.floor(row / 3) * 3;
const boxCol = Math.floor(col / 3) * 3;
for (let y = 0; y < 3; y++) {
for (let x = 0; x < 3; x++) {
if (board[boxRow + y][boxCol + x] === num) return false;
}
}
return true;
}
// Shuffle array
function shuffle(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}
// Handle mouse click
canvas.addEventListener('click', (e) => {
if (gameOver) {
initGame();
return;
}
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
const col = Math.floor(x / cellSize);
const row = Math.floor(y / cellSize);
if (row >= 0 && row < gridSize && col >= 0 && col < gridSize) {
if (initialBoard[row][col] === 0) {
selectedCell = [row, col];
}
}
});
// Handle keyboard input
document.addEventListener('keydown', (e) => {
if (!selectedCell || gameOver) return;
const [row, col] = selectedCell;
if (initialBoard[row][col] !== 0) return;
if (e.key >= '1' && e.key <= '9') {
const num = parseInt(e.key);
if (isValidMove(row, col, num)) {
board[row][col] = num;
// Check if board is complete
if (!findEmptyCell()) {
gameOver = true;
}
}
} else if (e.key === 'Backspace' || e.key === 'Delete') {
board[row][col] = 0;
}
});
function draw() {
// Clear canvas
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw grid
ctx.strokeStyle = 'black';
// Draw thin lines
ctx.lineWidth = 1;
for (let i = 0; i <= gridSize; i++) {
ctx.beginPath();
ctx.moveTo(i * cellSize, 0);
ctx.lineTo(i * cellSize, canvas.height);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(0, i * cellSize);
ctx.lineTo(canvas.width, i * cellSize);
ctx.stroke();
}
// Draw thick lines
ctx.lineWidth = thickLine;
for (let i = 0; i <= gridSize; i += 3) {
ctx.beginPath();
ctx.moveTo(i * cellSize, 0);
ctx.lineTo(i * cellSize, canvas.height);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(0, i * cellSize);
ctx.lineTo(canvas.width, i * cellSize);
ctx.stroke();
}
// Draw numbers
ctx.font = '24px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
for (let row = 0; row < gridSize; row++) {
for (let col = 0; col < gridSize; col++) {
if (board[row][col] !== 0) {
// Different colors for initial and player numbers
ctx.fillStyle = initialBoard[row][col] !== 0 ? 'black' : 'blue';
ctx.fillText(
board[row][col],
col * cellSize + cellSize/2,
row * cellSize + cellSize/2
);
}
}
}
// Draw selected cell
if (selectedCell) {
const [row, col] = selectedCell;
ctx.strokeStyle = 'red';
ctx.lineWidth = 2;
ctx.strokeRect(
col * cellSize + 1,
row * cellSize + 1,
cellSize - 2,
cellSize - 2
);
}
// Draw game over
if (gameOver) {
ctx.fillStyle = 'rgba(0, 255, 0, 0.3)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'black';
ctx.font = '40px Arial';
ctx.fillText('Solved!', canvas.width/2, canvas.height/2);
ctx.font = '20px Arial';
ctx.fillText('Click to Play Again', canvas.width/2, canvas.height/2 + 40);
}
}
function initGame() {
generateBoard();
selectedCell = null;
gameOver = false;
}
// Game loop
function gameLoop() {
draw();
requestAnimationFrame(gameLoop);
}
// Start game
initGame();
gameLoop();
# Sudoku Game Implementation
import pygame
import random
from dataclasses import dataclass
from typing import List, Tuple, Optional
import numpy as np
@dataclass
class Cell:
row: int
col: int
class SudokuGame:
def __init__(self):
pygame.init()
# Game settings
self.grid_size = 9
self.cell_size = 50
self.padding = 2
self.thick_line = 4
self.width = self.grid_size * self.cell_size + self.padding * 2
self.height = self.width
# Initialize display
self.screen = pygame.display.set_mode((self.width, self.height))
pygame.display.set_caption('Sudoku')
self.clock = pygame.time.Clock()
# Game state
self.board = np.zeros((self.grid_size, self.grid_size), dtype=int)
self.initial_board = np.zeros((self.grid_size, self.grid_size), dtype=int)
self.selected_cell: Optional[Cell] = None
self.game_over = False
self.init_game()
def generate_board(self):
"""Generate a valid Sudoku board"""
# Clear board
self.board.fill(0)
# Fill diagonal 3x3 boxes first (they're independent)
for box in range(3):
nums = list(range(1, 10))
random.shuffle(nums)
for i in range(3):
for j in range(3):
self.board[box * 3 + i][box * 3 + j] = nums.pop()
# Fill rest of the board
self.solve_board()
# Remove numbers to create puzzle
cells_to_remove = 45 # Adjust difficulty here
for _ in range(cells_to_remove):
while True:
row = random.randrange(self.grid_size)
col = random.randrange(self.grid_size)
if self.board[row][col] != 0:
self.board[row][col] = 0
break
# Save initial board state
self.initial_board = self.board.copy()
def solve_board(self) -> bool:
"""Solve the board using backtracking"""
empty_cell = self.find_empty_cell()
if not empty_cell:
return True
row, col = empty_cell
nums = list(range(1, 10))
random.shuffle(nums)
for num in nums:
if self.is_valid_move(row, col, num):
self.board[row][col] = num
if self.solve_board():
return True
self.board[row][col] = 0
return False
def find_empty_cell(self) -> Optional[Tuple[int, int]]:
"""Find an empty cell"""
for row in range(self.grid_size):
for col in range(self.grid_size):
if self.board[row][col] == 0:
return (row, col)
return None
def is_valid_move(self, row: int, col: int, num: int) -> bool:
"""Check if a move is valid"""
# Check row
if num in self.board[row]:
return False
# Check column
if num in self.board[:, col]:
return False
# Check 3x3 box
box_row = (row // 3) * 3
box_col = (col // 3) * 3
box = self.board[box_row:box_row+3, box_col:box_col+3]
if num in box:
return False
return True
def handle_input(self) -> bool:
"""Handle mouse and keyboard input"""
for event in pygame.event.get():
if event.type == pygame.QUIT:
return False
if event.type == pygame.MOUSEBUTTONDOWN:
if self.game_over:
self.init_game()
else:
mouse_x, mouse_y = event.pos
col = mouse_x // self.cell_size
row = mouse_y // self.cell_size
if (0 <= row < self.grid_size and
0 <= col < self.grid_size and
self.initial_board[row][col] == 0):
self.selected_cell = Cell(row, col)
if event.type == pygame.KEYDOWN:
if not self.selected_cell or self.game_over:
continue
row, col = self.selected_cell.row, self.selected_cell.col
if self.initial_board[row][col] != 0:
continue
if event.unicode.isdigit() and event.unicode != '0':
num = int(event.unicode)
if self.is_valid_move(row, col, num):
self.board[row][col] = num
if not self.find_empty_cell():
self.game_over = True
elif event.key in (pygame.K_BACKSPACE, pygame.K_DELETE):
self.board[row][col] = 0
return True
def draw(self):
"""Draw the game state"""
# Clear screen
self.screen.fill((255, 255, 255)) # White background
# Draw grid
# Thin lines
for i in range(self.grid_size + 1):
pygame.draw.line(self.screen, (0, 0, 0),
(i * self.cell_size, 0),
(i * self.cell_size, self.height))
pygame.draw.line(self.screen, (0, 0, 0),
(0, i * self.cell_size),
(self.width, i * self.cell_size))
# Thick lines
for i in range(0, self.grid_size + 1, 3):
pygame.draw.line(self.screen, (0, 0, 0),
(i * self.cell_size, 0),
(i * self.cell_size, self.height),
self.thick_line)
pygame.draw.line(self.screen, (0, 0, 0),
(0, i * self.cell_size),
(self.width, i * self.cell_size),
self.thick_line)
# Draw numbers
font = pygame.font.Font(None, 36)
for row in range(self.grid_size):
for col in range(self.grid_size):
if self.board[row][col] != 0:
color = (0, 0, 0) if self.initial_board[row][col] != 0 else (0, 0, 255)
text = font.render(str(self.board[row][col]), True, color)
text_rect = text.get_rect(
center=(col * self.cell_size + self.cell_size//2,
row * self.cell_size + self.cell_size//2))
self.screen.blit(text, text_rect)
# Draw selected cell
if self.selected_cell:
pygame.draw.rect(self.screen, (255, 0, 0),
(self.selected_cell.col * self.cell_size + 1,
self.selected_cell.row * self.cell_size + 1,
self.cell_size - 2,
self.cell_size - 2), 2)
# Draw game over
if self.game_over:
overlay = pygame.Surface((self.width, self.height))
overlay.fill((0, 255, 0))
overlay.set_alpha(76) # 30% opacity
self.screen.blit(overlay, (0, 0))
font = pygame.font.Font(None, 64)
text = font.render('Solved!', True, (0, 0, 0))
text_rect = text.get_rect(center=(self.width//2, self.height//2))
self.screen.blit(text, text_rect)
font = pygame.font.Font(None, 36)
text = font.render('Click to Play Again', True, (0, 0, 0))
text_rect = text.get_rect(
center=(self.width//2, self.height//2 + 40))
self.screen.blit(text, text_rect)
pygame.display.flip()
def init_game(self):
"""Initialize or reset game state"""
self.generate_board()
self.selected_cell = None
self.game_over = False
def run(self):
"""Main game loop"""
running = True
while running:
running = self.handle_input()
self.draw()
self.clock.tick(60)
pygame.quit()
if __name__ == '__main__':
game = SudokuGame()
game.run()
Play Game