Skip to content
Closed
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
Binary file added starter/__pycache__/app.cpython-310.pyc
Binary file not shown.
Binary file added starter/__pycache__/generator.cpython-310.pyc
Binary file not shown.
Binary file added starter/__pycache__/routes.cpython-310.pyc
Binary file not shown.
Binary file added starter/__pycache__/solver.cpython-310.pyc
Binary file not shown.
Binary file added starter/__pycache__/sudoku_logic.cpython-310.pyc
Binary file not shown.
Binary file added starter/__pycache__/validator.cpython-310.pyc
Binary file not shown.
31 changes: 5 additions & 26 deletions starter/app.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from flask import Flask, render_template, jsonify, request
import sudoku_logic
from flask import Flask

from routes import bp, set_current_store

app = Flask(__name__)

Expand All @@ -9,31 +10,9 @@
'solution': None
}

@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})
set_current_store(CURRENT)
app.register_blueprint(bp)

@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})

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

from solver import fill_board

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)]


def remove_cells(board, clues):
attempts = SIZE * SIZE - clues
while attempts > 0:
row = random.randrange(SIZE)
col = random.randrange(SIZE)
if board[row][col] != EMPTY:
board[row][col] = EMPTY
attempts -= 1


def generate_puzzle(clues=35):
board = create_empty_board()
fill_board(board)
solution = deep_copy(board)
remove_cells(board, clues)
puzzle = deep_copy(board)
return puzzle, solution
15 changes: 15 additions & 0 deletions starter/instruction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copilot Instructions
Use Python best practices.
Keep functions small.
Use Flask Blueprints where appropriate.
Use type hints.
Avoid duplicated code.
Write reusable modules.
Follow PEP8.
Add comments only when necessary.
Use Bootstrap or clean CSS.
Prefer readability over clever code.
Use localStorage for leaderboard.
Make responsive layouts.
Support dark mode.
Create unit tests with pytest.
1 change: 1 addition & 0 deletions starter/requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
Flask>=2.0
pytest>=8.0
51 changes: 51 additions & 0 deletions starter/routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from flask import Blueprint, jsonify, render_template, request

from generator import generate_puzzle
from validator import find_incorrect_cells

bp = Blueprint('main', __name__)

CURRENT = None


def set_current_store(store):
global CURRENT
CURRENT = store


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


@bp.route('/new')
def new_game():
difficulty = request.args.get('difficulty', '').lower()
clues = request.args.get('clues')

if clues is None:
clue_map = {
'easy': 45,
'medium': 35,
'hard': 25,
}
clues = clue_map.get(difficulty, 35)
else:
clues = int(clues)

puzzle, solution = generate_puzzle(clues)
CURRENT['puzzle'] = puzzle
CURRENT['solution'] = solution
return jsonify({'puzzle': puzzle, 'solution': solution})


@bp.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 = find_incorrect_cells(board, solution)
return jsonify({'incorrect': incorrect})
Binary file added starter/screenshots/screenshots/Check button.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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 starter/screenshots/screenshots/initial_tests.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 starter/screenshots/screenshots/rejection.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 starter/screenshots/screenshots/timer.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 starter/screenshots/screenshots/validation.png
34 changes: 34 additions & 0 deletions starter/solver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import random

SIZE = 9
EMPTY = 0


def is_safe(board, row, col, num):
for x in range(SIZE):
if board[row][x] == num or board[x][col] == num:
return False

start_row = row - row % 3
start_col = col - col % 3
for i in range(3):
for j in range(3):
if board[start_row + i][start_col + j] == num:
return False
return True


def fill_board(board):
for row in range(SIZE):
for col in range(SIZE):
if board[row][col] == EMPTY:
possible = list(range(1, SIZE + 1))
random.shuffle(possible)
for candidate in possible:
if is_safe(board, row, col, candidate):
board[row][col] = candidate
if fill_board(board):
return True
board[row][col] = EMPTY
return False
return True
Loading