Sudoku Game in Unity
Sudoku Game in Unity - Complete Guide
Project Setup
1. Create New Unity Project
- Open Unity Hub
- Create new 2D project named "SudokuGame"
- Unity version: 2021.3 LTS or newer recommended
2. Project Structure
Create the following folder structure in Assets:
Assets/
├── Scripts/
├── Prefabs/
├── Scenes/
├── UI/
└── Materials/
Scripts
Script 1: SudokuCell.cs
This script handles individual cell behavior.
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class SudokuCell : MonoBehaviour
{
[Header("UI References")]
public TextMeshProUGUI numberText;
public Image background;
[Header("Colors")]
public Color normalColor = Color.white;
public Color selectedColor = new Color(0.73f, 0.87f, 0.98f, 1f);
public Color highlightedColor = new Color(1f, 0.98f, 0.77f, 1f);
public Color givenColor = new Color(0.96f, 0.96f, 0.96f, 1f);
public Color errorColor = new Color(1f, 0.8f, 0.8f, 1f);
private int row;
private int col;
private int value;
private bool isGiven;
private bool isSelected;
private bool hasError;
private SudokuManager manager;
public void Initialize(int r, int c, SudokuManager mgr)
{
row = r;
col = c;
manager = mgr;
value = 0;
isGiven = false;
isSelected = false;
hasError = false;
}
public void SetValue(int val, bool given = false)
{
value = val;
isGiven = given;
if (val == 0)
{
numberText.text = "";
}
else
{
numberText.text = val.ToString();
}
UpdateAppearance();
}
public int GetValue()
{
return value;
}
public bool IsGiven()
{
return isGiven;
}
public int GetRow() { return row; }
public int GetCol() { return col; }
public void Select()
{
isSelected = true;
UpdateAppearance();
}
public void Deselect()
{
isSelected = false;
UpdateAppearance();
}
public void SetHighlighted(bool highlighted)
{
if (!isSelected)
{
background.color = highlighted ? highlightedColor : (isGiven ? givenColor : normalColor);
}
}
public void SetError(bool error)
{
hasError = error;
UpdateAppearance();
}
private void UpdateAppearance()
{
if (isSelected)
{
background.color = selectedColor;
}
else if (hasError)
{
background.color = errorColor;
}
else if (isGiven)
{
background.color = givenColor;
numberText.color = Color.black;
}
else
{
background.color = normalColor;
numberText.color = new Color(0.09f, 0.46f, 0.82f, 1f); // Blue for user input
}
}
public void OnCellClicked()
{
if (!isGiven)
{
manager.SelectCell(this);
}
}
}
Script 2: SudokuGenerator.cs
This script generates Sudoku puzzles.
using System.Collections.Generic;
using UnityEngine;
public class SudokuGenerator
{
private System.Random random = new System.Random();
public int[,] GenerateSolution()
{
int[,] grid = new int[9, 9];
// Fill diagonal 3x3 boxes
for (int box = 0; box < 9; box += 3)
{
FillBox(grid, box, box);
}
// Solve the rest
SolveSudoku(grid);
return grid;
}
private void FillBox(int[,] grid, int row, int col)
{
List<int> nums = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Shuffle(nums);
int idx = 0;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
grid[row + i, col + j] = nums[idx++];
}
}
}
private void Shuffle<T>(List<T> list)
{
int n = list.Count;
while (n > 1)
{
n--;
int k = random.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
public bool SolveSudoku(int[,] grid)
{
int row = -1;
int col = -1;
bool isEmpty = false;
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
if (grid[i, j] == 0)
{
row = i;
col = j;
isEmpty = true;
break;
}
}
if (isEmpty) break;
}
if (!isEmpty) return true;
List<int> nums = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Shuffle(nums);
foreach (int num in nums)
{
if (IsValid(grid, num, row, col))
{
grid[row, col] = num;
if (SolveSudoku(grid))
return true;
grid[row, col] = 0;
}
}
return false;
}
public bool IsValid(int[,] grid, int num, int row, int col)
{
// Check row
for (int x = 0; x < 9; x++)
{
if (grid[row, x] == num)
return false;
}
// Check column
for (int x = 0; x < 9; x++)
{
if (grid[x, col] == num)
return false;
}
// Check 3x3 box
int boxRow = (row / 3) * 3;
int boxCol = (col / 3) * 3;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
if (grid[boxRow + i, boxCol + j] == num)
return false;
}
}
return true;
}
public int[,] CreatePuzzle(int[,] solution, int cellsToRemove)
{
int[,] puzzle = (int[,])solution.Clone();
int removed = 0;
while (removed < cellsToRemove)
{
int row = random.Next(9);
int col = random.Next(9);
if (puzzle[row, col] != 0)
{
puzzle[row, col] = 0;
removed++;
}
}
return puzzle;
}
}
Script 3: SudokuManager.cs
Main game manager script.
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class SudokuManager : MonoBehaviour
{
[Header("UI References")]
public Transform gridParent;
public GameObject cellPrefab;
public TMP_Dropdown difficultyDropdown;
public TextMeshProUGUI timerText;
public TextMeshProUGUI remainingText;
public TextMeshProUGUI mistakesText;
public TextMeshProUGUI hintsText;
[Header("Number Buttons")]
public Button[] numberButtons;
public Button clearButton;
[Header("Game Buttons")]
public Button newGameButton;
public Button checkButton;
public Button hintButton;
public Button clearAllButton;
public Button solveButton;
private SudokuCell[,] cells = new SudokuCell[9, 9];
private int[,] puzzle = new int[9, 9];
private int[,] solution = new int[9, 9];
private int[,] userGrid = new int[9, 9];
private SudokuCell selectedCell;
private SudokuGenerator generator;
private int mistakes = 0;
private int hintsUsed = 0;
private float elapsedTime = 0f;
private bool isTimerRunning = false;
void Start()
{
generator = new SudokuGenerator();
SetupGrid();
SetupButtons();
NewGame();
}
void Update()
{
if (isTimerRunning)
{
elapsedTime += Time.deltaTime;
UpdateTimer();
}
HandleKeyboardInput();
}
void SetupGrid()
{
for (int row = 0; row < 9; row++)
{
for (int col = 0; col < 9; col++)
{
GameObject cellObj = Instantiate(cellPrefab, gridParent);
SudokuCell cell = cellObj.GetComponent<SudokuCell>();
cell.Initialize(row, col, this);
cells[row, col] = cell;
}
}
}
void SetupButtons()
{
newGameButton.onClick.AddListener(NewGame);
checkButton.onClick.AddListener(CheckSolution);
hintButton.onClick.AddListener(GetHint);
clearAllButton.onClick.AddListener(ClearAllUserInputs);
solveButton.onClick.AddListener(SolvePuzzle);
for (int i = 0; i < numberButtons.Length; i++)
{
int num = i + 1;
numberButtons[i].onClick.AddListener(() => PlaceNumber(num));
}
clearButton.onClick.AddListener(() => PlaceNumber(0));
}
public void NewGame()
{
// Generate solution
solution = generator.GenerateSolution();
// Get difficulty
int cellsToRemove = GetCellsToRemove();
puzzle = generator.CreatePuzzle(solution, cellsToRemove);
userGrid = (int[,])puzzle.Clone();
// Display puzzle
DisplayPuzzle();
// Reset stats
mistakes = 0;
hintsUsed = 0;
elapsedTime = 0f;
isTimerRunning = true;
UpdateStats();
}
int GetCellsToRemove()
{
switch (difficultyDropdown.value)
{
case 0: return 35; // Easy
case 1: return 45; // Medium
case 2: return 52; // Hard
case 3: return 58; // Expert
default: return 45;
}
}
void DisplayPuzzle()
{
for (int row = 0; row < 9; row++)
{
for (int col = 0; col < 9; col++)
{
int value = puzzle[row, col];
cells[row, col].SetValue(value, value != 0);
}
}
if (selectedCell != null)
{
selectedCell.Deselect();
selectedCell = null;
}
}
public void SelectCell(SudokuCell cell)
{
// Deselect previous cell
if (selectedCell != null)
{
selectedCell.Deselect();
}
// Clear all highlights
ClearHighlights();
// Select new cell
selectedCell = cell;
cell.Select();
// Highlight related cells
HighlightRelatedCells(cell.GetRow(), cell.GetCol());
}
void ClearHighlights()
{
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
cells[i, j].SetHighlighted(false);
}
}
}
void HighlightRelatedCells(int row, int col)
{
// Highlight row, column, and 3x3 box
for (int i = 0; i < 9; i++)
{
cells[row, i].SetHighlighted(true); // Row
cells[i, col].SetHighlighted(true); // Column
}
// 3x3 box
int boxRow = (row / 3) * 3;
int boxCol = (col / 3) * 3;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
cells[boxRow + i, boxCol + j].SetHighlighted(true);
}
}
}
public void PlaceNumber(int num)
{
if (selectedCell == null)
{
Debug.Log("Please select a cell first!");
return;
}
if (selectedCell.IsGiven())
return;
int row = selectedCell.GetRow();
int col = selectedCell.GetCol();
selectedCell.SetError(false);
if (num == 0)
{
selectedCell.SetValue(0);
userGrid[row, col] = 0;
}
else
{
selectedCell.SetValue(num);
userGrid[row, col] = num;
// Check if it's wrong
if (num != solution[row, col])
{
selectedCell.SetError(true);
mistakes++;
}
}
UpdateStats();
CheckIfComplete();
}
public void CheckSolution()
{
bool hasErrors = false;
for (int row = 0; row < 9; row++)
{
for (int col = 0; col < 9; col++)
{
cells[row, col].SetError(false);
int userValue = userGrid[row, col];
int correctValue = solution[row, col];
if (userValue != 0 && userValue != correctValue)
{
cells[row, col].SetError(true);
hasErrors = true;
}
}
}
if (!hasErrors)
{
bool complete = IsGridComplete();
if (complete)
{
isTimerRunning = false;
ShowMessage($"Congratulations! Solved in {FormatTime((int)elapsedTime)}!");
}
else
{
ShowMessage("No mistakes so far! Keep going!");
}
}
else
{
ShowMessage("Some cells are incorrect (marked in red).");
}
}
public void GetHint()
{
// Find empty cells
for (int row = 0; row < 9; row++)
{
for (int col = 0; col < 9; col++)
{
if (puzzle[row, col] == 0 && userGrid[row, col] == 0)
{
// Fill with correct value
userGrid[row, col] = solution[row, col];
cells[row, col].SetValue(solution[row, col]);
cells[row, col].SetError(false);
hintsUsed++;
UpdateStats();
CheckIfComplete();
return;
}
}
}
ShowMessage("No hints available!");
}
public void SolvePuzzle()
{
for (int row = 0; row < 9; row++)
{
for (int col = 0; col < 9; col++)
{
if (!cells[row, col].IsGiven())
{
cells[row, col].SetValue(solution[row, col]);
cells[row, col].SetError(false);
userGrid[row, col] = solution[row, col];
}
}
}
isTimerRunning = false;
UpdateStats();
}
public void ClearAllUserInputs()
{
for (int row = 0; row < 9; row++)
{
for (int col = 0; col < 9; col++)
{
if (!cells[row, col].IsGiven())
{
cells[row, col].SetValue(0);
cells[row, col].SetError(false);
userGrid[row, col] = 0;
}
}
}
if (selectedCell != null)
{
selectedCell.Deselect();
selectedCell = null;
}
UpdateStats();
}
void UpdateStats()
{
int remaining = 0;
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
if (userGrid[i, j] == 0)
remaining++;
}
}
remainingText.text = remaining.ToString();
mistakesText.text = mistakes.ToString();
hintsText.text = hintsUsed.ToString();
}
void UpdateTimer()
{
timerText.text = FormatTime((int)elapsedTime);
}
string FormatTime(int seconds)
{
int mins = seconds / 60;
int secs = seconds % 60;
return $"{mins:00}:{secs:00}";
}
bool IsGridComplete()
{
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
if (userGrid[i, j] != solution[i, j])
return false;
}
}
return true;
}
void CheckIfComplete()
{
if (IsGridComplete())
{
isTimerRunning = false;
ShowMessage($"Congratulations! Solved in {FormatTime((int)elapsedTime)}!");
}
}
void ShowMessage(string message)
{
Debug.Log(message);
// You can implement a proper UI popup here
}
void HandleKeyboardInput()
{
if (selectedCell == null)
return;
// Number input
for (int i = 1; i <= 9; i++)
{
if (Input.GetKeyDown(KeyCode.Alpha0 + i) || Input.GetKeyDown(KeyCode.Keypad0 + i))
{
PlaceNumber(i);
}
}
// Clear
if (Input.GetKeyDown(KeyCode.Backspace) || Input.GetKeyDown(KeyCode.Delete))
{
PlaceNumber(0);
}
// Arrow key navigation
int row = selectedCell.GetRow();
int col = selectedCell.GetCol();
if (Input.GetKeyDown(KeyCode.UpArrow) && row > 0)
{
SelectCell(cells[row - 1, col]);
}
else if (Input.GetKeyDown(KeyCode.DownArrow) && row < 8)
{
SelectCell(cells[row + 1, col]);
}
else if (Input.GetKeyDown(KeyCode.LeftArrow) && col > 0)
{
SelectCell(cells[row, col - 1]);
}
else if (Input.GetKeyDown(KeyCode.RightArrow) && col < 8)
{
SelectCell(cells[row, col + 1]);
}
}
}
UI Setup Instructions
1. Create Canvas
- Right-click in Hierarchy → UI → Canvas
- Set Canvas Scaler to "Scale With Screen Size"
- Reference Resolution: 1920x1080
2. Create Sudoku Grid
- Create Empty GameObject under Canvas, name it "GridPanel"
- Add Grid Layout Group component:
- Cell Size: 60x60
- Spacing: 0, 0
- Constraint: Fixed Column Count = 9
- Add Content Size Fitter:
- Horizontal Fit: Preferred Size
- Vertical Fit: Preferred Size
3. Create Cell Prefab
- Create UI → Image, name it "SudokuCell"
- Add Button component (remove navigation)
- Add child: UI → TextMeshPro - Text
- Name it "NumberText"
- Font Size: 36
- Alignment: Center
- Add SudokuCell script to prefab
- Assign references in inspector
- Drag to Prefabs folder
- Delete from scene
4. Create Number Pad
- Create Panel under Canvas
- Add Grid Layout Group:
- Cell Size: 80x80
- Spacing: 10, 10
- Columns: 5
- Create 9 buttons (1-9) + 1 clear button
- Add TextMeshPro text to each button
5. Create Control Buttons
Create buttons for:
- New Game
- Check
- Hint
- Clear All
- Solve
6. Create Stats Panel
Create TextMeshPro texts for:
- Timer
- Remaining cells
- Mistakes
- Hints used
7. Create Difficulty Dropdown
- UI → Dropdown - TextMeshPro
- Add options: Easy, Medium, Hard, Expert
Assembly and Configuration
1. Setup SudokuManager GameObject
- Create empty GameObject, name it "SudokuManager"
- Add SudokuManager script
- Assign all references in inspector:
- Grid Parent → GridPanel
- Cell Prefab → SudokuCell prefab
- All UI elements
2. Add Box Borders
To create the thick 3x3 box borders:
- Create 2 horizontal and 2 vertical UI Images
- Set color to dark gray/black
- Position them to create the grid lines
- Set width/height to 3-4 pixels
Additional Features (Optional)
Sound Manager Script
using UnityEngine;
public class SoundManager : MonoBehaviour
{
public AudioClip buttonClick;
public AudioClip numberPlace;
public AudioClip errorSound;
public AudioClip winSound;
private AudioSource audioSource;
void Awake()
{
audioSource = GetComponent<AudioSource>();
}
public void PlayButtonClick()
{
audioSource.PlayOneShot(buttonClick);
}
public void PlayNumberPlace()
{
audioSource.PlayOneShot(numberPlace);
}
public void PlayError()
{
audioSource.PlayOneShot(errorSound);
}
public void PlayWin()
{
audioSource.PlayOneShot(winSound);
}
}
Build Settings
- File → Build Settings
- Add current scene
- Select platform (PC, Mac, Android, iOS)
- Player Settings:
- Company Name
- Product Name: Sudoku
- Default Icon
- Build and Run
Tips for Improvement
- Add animations for cell selection
- Implement undo/redo functionality
- Add save/load game state
- Create different themes
- Add achievement system
- Implement daily challenges
- Add multiplayer functionality
- Create tutorial mode
Common Issues & Solutions
Issue: Cells not responding to clicks
- Check EventSystem exists in scene
- Verify Button component on cells
- Check raycast target on Image
Issue: Grid layout not working
- Verify Grid Layout Group settings
- Check anchor points on parent
- Ensure proper cell size
Issue: Numbers not displaying
- Check TextMeshPro package installed
- Verify font asset assigned
- Check text size and color
Testing Checklist
- [ ] New game generates valid puzzle
- [ ] All difficulty levels work
- [ ] Cell selection works
- [ ] Number placement works
- [ ] Error detection works
- [ ] Hint system works
- [ ] Timer counts correctly
- [ ] Check solution validates correctly
- [ ] Solve button completes puzzle
- [ ] Clear all removes user input
- [ ] Keyboard controls work
- [ ] UI scales properly on different resolutions
This guide provides everything you need to build a complete Sudoku game in Unity!
Comments