Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,83 @@ Use GitHub Copilot to refactor the code for this game to add more advanced featu
- The game should be responsive and work well on both desktop and mobile devices.
- UI colors should be visually appealing and accessible.
- Completed and correct puzzles should display a congratulatory message with the time taken and hints used and ask for the user's name for Top 10 times.

# Refactor a Sudoku Game with GitHub Copilot

A modern Flask-based Sudoku game refactored from legacy Python code with GitHub Copilot.

The project adds Sudoku generation and validation, difficulty levels, hints, puzzle checking, a timer, a persistent Top 10 leaderboard, dark mode, responsive styling, and accessibility improvements.
## Features

### Sudoku Game
- Generates valid Sudoku puzzles.
- Ensures each generated puzzle has exactly one unique solution.
- Supports Easy, Medium, and Hard difficulty levels.
- Difficulty levels change the number of prefilled cells.
- Prefilled cells are locked and cannot be edited.

### Validation
- Provides immediate feedback for invalid entries.
- Check Puzzle button highlights incorrect entries.
- Detects when the puzzle has been correctly completed.
- Displays a congratulatory completion message.

### Hint System
- Hint button fills one correct empty cell.
- Hint-filled cells are visually distinguished.
- Hint-filled cells are locked.
- Tracks the number of hints used.

### Timer
- Starts when a new puzzle begins.
- Tracks elapsed solving time.
- Stops when the puzzle is completed.

### Top 10 Leaderboard
- Stores completed scores in browser localStorage.
- Records player name.
- Records completion time.
- Records difficulty level.
- Records number of hints used.
- Sorts scores by fastest completion time.
- Keeps only the top 10 scores.
- Scores persist between browser sessions.

### User Interface
- Light and dark mode.
- Responsive desktop and mobile layout.
- Alternating styles for the 3x3 Sudoku regions.
- Accessible and readable controls.
- Keyboard-friendly interface and visible focus states.
## Testing

Run the following command from the starter directory:

pytest

## How to Play

1. Select a difficulty level: Easy, Medium, or Hard.
2. Start a new puzzle.
3. Fill the empty Sudoku cells.
4. Prefilled cells cannot be edited.
5. Use the Check Puzzle button to identify incorrect entries.
6. Use Hint when assistance is needed.
7. The timer tracks the solving time.
8. Complete the puzzle correctly to finish the game.
9. Enter your name when prompted after completing a puzzle.
10. Completed scores are stored in the Top 10 leaderboard.
## Accessibility and Responsive Design

The application was reviewed for:

- Keyboard navigation
- Visible focus states
- Accessible button and control labels
- Readable text
- Color contrast
- Error and success feedback
- Dark mode readability
- Responsive desktop and mobile layouts

The Sudoku grid and controls are designed to remain usable across different screen sizes and themes.
Binary file added Screenshots/Screenshot 2026-08-27 121605.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Screenshots/Screenshot 2026-08-27 124457.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Screenshots/Screenshot 2026-08-27 124631.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Screenshots/Screenshot 2026-08-27 131754.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Screenshots/Screenshot 2026-08-27 131821.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Screenshots/Screenshot 2026-08-27 141225.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Screenshots/Screenshot 2026-08-27 141252.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Screenshots/Screenshot 2026-08-27 142544.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Screenshots/Screenshot 2026-08-27 143003.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Screenshots/Screenshot 2026-08-27 214725.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Screenshots/Screenshot 2026-08-27 214736.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
164 changes: 164 additions & 0 deletions instruction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# GitHub Copilot Instructions — Flask Sudoku Project

## Project Overview

This project is a Python Flask Sudoku game that is being refactored from legacy code into a modern, modular, maintainable application.

The application should provide:
- Sudoku puzzle generation
- Unique-solution validation
- Easy, Medium, and Hard difficulty levels
- Locked prefilled cells
- Immediate input validation
- Check Puzzle functionality
- Hint functionality
- Puzzle completion detection
- Timer
- Top 10 leaderboard
- Browser localStorage persistence
- Light and dark modes
- Responsive desktop and mobile layouts
- Accessible user interface

## General Coding Standards

- Use modern Python practices.
- Keep code readable, maintainable, and modular.
- Use clear and descriptive variable, function, and class names.
- Keep functions focused on a single responsibility.
- Avoid unnecessary duplication.
- Avoid unnecessary dependencies.
- Preserve existing functionality when refactoring.
- Do not modify unrelated files or features.
- Prefer simple and understandable solutions over unnecessarily complex implementations.
- Handle errors gracefully.
- Provide clear user-facing error and status messages.
- Add comments only where they improve understanding of non-obvious logic.

## Application Architecture

Keep responsibilities separated.

Prefer the following separation:

- Flask routes handle HTTP requests and responses.
- Sudoku generation handles puzzle creation.
- Sudoku solving handles solution finding and solution counting.
- Validation handles Sudoku rule validation and user input checking.
- Frontend HTML handles page structure.
- CSS handles styling, themes, responsiveness, and visual states.
- JavaScript handles client-side interaction and dynamic UI behavior.
- Leaderboard functionality handles localStorage persistence and score management.

Avoid putting all application logic into one large Flask file.

## Sudoku Requirements

- Every generated Sudoku puzzle must be valid.
- Every generated puzzle must have exactly one solution.
- The solution must be verified before the puzzle is presented to the player.
- Easy, Medium, and Hard must have different numbers of prefilled cells.
- Easy should provide more clues than Medium.
- Medium should provide more clues than Hard.
- Prefilled cells must be locked and must not be editable.
- Hint-filled cells must also become locked.
- User-entered values must be validated.
- Incorrect entries must receive clear visual feedback.
- Completed puzzles must be detected correctly.

## Game Features

### Difficulty

Support:
- Easy
- Medium
- Hard

Changing difficulty should start an appropriate new puzzle.

### Hint

- Fill one correct empty cell.
- Never overwrite a user's existing value.
- Visually distinguish the hinted cell.
- Lock the hinted cell.
- Track the number of hints used.

### Check Puzzle

- Check the current board against the correct solution.
- Highlight incorrect entries.
- Do not incorrectly mark valid entries.
- Use event delegation where required by the project.

### Timer

- Start the timer when a new puzzle begins.
- Display elapsed time clearly.
- Stop the timer when the puzzle is correctly completed.
- Reset the timer for a new puzzle.

### Leaderboard

Store the Top 10 scores in browser localStorage.

Each score should contain:
- Player name
- Completion time
- Difficulty
- Number of hints used

Sort scores by fastest completion time and keep only the best 10 scores.

Handle missing or corrupted localStorage data gracefully.

## Frontend and Accessibility

- Use semantic HTML where appropriate.
- Ensure controls have clear labels.
- Ensure buttons are keyboard accessible.
- Provide visible focus states.
- Maintain readable text and controls.
- Maintain sufficient color contrast.
- Do not rely only on color to communicate important information.
- Provide clear feedback for errors and successful actions.
- Ensure dark mode remains readable and accessible.
- Ensure the layout works on desktop, tablet, and mobile.
- Avoid horizontal scrolling where possible.
- Ensure the Sudoku grid does not shift when styles or states change.
- Use alternating visual styles for the 3x3 Sudoku regions.

## Testing

- Use pytest for Python tests where appropriate.
- Run tests after every major change.
- Do not remove or weaken tests simply to make them pass.
- Add tests for important Sudoku logic and new functionality where practical.
- Preserve existing behavior during refactoring.

## GitHub Copilot Usage

Before implementing a major change:
1. Analyze the existing code.
2. Propose an approach.
3. Explain which files will change.
4. Wait for approval before making significant changes.

When reviewing Copilot suggestions:
- Do not blindly accept generated code.
- Check whether the approach is necessary and maintainable.
- Reject unnecessary dependencies.
- Reject unnecessarily complex implementations.
- Prefer the simplest solution that satisfies the requirements.

Do not rebuild the entire application when only a focused change is required.

## Change Management

- Make small, focused changes.
- Avoid unrelated modifications.
- Preserve completed features when adding new functionality.
- Run tests after changes.
- Verify the application manually after major changes.
- If a change causes a regression, identify and fix the root cause instead of removing functionality.
Binary file added starter/__pycache__/app.cpython-314.pyc
Binary file not shown.
Binary file added starter/__pycache__/board.cpython-314.pyc
Binary file not shown.
Binary file added starter/__pycache__/game.cpython-314.pyc
Binary file not shown.
Binary file added starter/__pycache__/generator.cpython-314.pyc
Binary file not shown.
Binary file added starter/__pycache__/routes.cpython-314.pyc
Binary file not shown.
Binary file added starter/__pycache__/solver.cpython-314.pyc
Binary file not shown.
Binary file added starter/__pycache__/sudoku_logic.cpython-314.pyc
Binary file not shown.
Binary file added starter/__pycache__/validation.cpython-314.pyc
Binary file not shown.
33 changes: 6 additions & 27 deletions starter/app.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,18 @@
from flask import Flask, render_template, jsonify, request
from flask import Flask

from routes import create_routes
import sudoku_logic

app = Flask(__name__)

# Keep a simple in-memory store for current puzzle and solution
CURRENT = {
'puzzle': None,
'solution': None
'solution': None,
'hints_used': 0,
}

@app.route('/')
def index():
return render_template('index.html')

@app.route('/new')
def new_game():
clues = int(request.args.get('clues', 35))
puzzle, solution = sudoku_logic.generate_puzzle(clues)
CURRENT['puzzle'] = puzzle
CURRENT['solution'] = solution
return jsonify({'puzzle': puzzle})

@app.route('/check', methods=['POST'])
def check_solution():
data = request.json
board = data.get('board')
solution = CURRENT.get('solution')
if solution is None:
return jsonify({'error': 'No game in progress'}), 400
incorrect = []
for i in range(sudoku_logic.SIZE):
for j in range(sudoku_logic.SIZE):
if board[i][j] != solution[i][j]:
incorrect.append([i, j])
return jsonify({'incorrect': incorrect})
app.register_blueprint(create_routes(CURRENT, sudoku_logic))

if __name__ == '__main__':
app.run(debug=True)
13 changes: 13 additions & 0 deletions starter/board.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import copy


SIZE = 9
EMPTY = 0


def deep_copy(board):
return copy.deepcopy(board)


def create_empty_board():
return [[EMPTY for _ in range(SIZE)] for _ in range(SIZE)]
62 changes: 62 additions & 0 deletions starter/game.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import time

from validation import find_incorrect_cells


_GAME_TIMES = {}


def start_game(current, clues, generate_puzzle):
puzzle, solution = generate_puzzle(clues)
current['puzzle'] = puzzle
current['solution'] = solution
current['hints_used'] = 0
_GAME_TIMES[id(current)] = {
'started_at': time.monotonic(),
'elapsed_seconds': None,
}
return puzzle


def complete_game(current):
timing = _GAME_TIMES.get(id(current))
if timing is None:
return None
if timing['elapsed_seconds'] is None:
timing['elapsed_seconds'] = int(time.monotonic() - timing['started_at'])
return timing['elapsed_seconds']


def check_board(current, board):
solution = current.get('solution')
if solution is None:
return None

incorrect = find_incorrect_cells(board, solution)
puzzle = current.get('puzzle')
if puzzle is not None:
for row in range(len(puzzle)):
for col in range(len(puzzle[row])):
if puzzle[row][col] != 0 and board[row][col] != puzzle[row][col]:
if [row, col] not in incorrect:
incorrect.append([row, col])
return incorrect


def get_hint(current, board):
puzzle = current.get('puzzle')
solution = current.get('solution')
if puzzle is None or solution is None:
return None

for row in range(len(puzzle)):
for col in range(len(puzzle[row])):
if puzzle[row][col] == 0 and board[row][col] == 0:
current['hints_used'] = current.get('hints_used', 0) + 1
return {
'row': row,
'col': col,
'value': solution[row][col],
'hints_used': current['hints_used'],
}
return None
Loading