Step 3 - Win Detection: Checking for Winners
Let's add the logic to detect when someone wins or when there's a tie!
Understanding Win Conditions
In Tic-Tac-Toe, there are 8 ways to win:
- 3 rows (horizontal)
- 3 columns (vertical)
- 2 diagonals
A player wins when they get 3 of their symbols in any of these lines.
Step 1: Add Game State Variables
First, let's track if the game is over:
let cellSize = 400 / 3;
let board = [
['', '', ''],
['', '', ''],
['', '', '']
];
let currentPlayer = 'X';
let gameOver = false;
let winner = '';
Breaking it down
gameOver = false- Tracks if game is finishedwinner = ''- Stores who won ('X', 'O', or 'tie')- We'll set these when we detect a win or tie
Step 2: Create Check Winner Function
Add this function to check all winning conditions:
function checkWinner() {
// Check rows
for (let j = 0; j < 3; j++) {
if (board[0][j] !== '' &&
board[0][j] === board[1][j] &&
board[1][j] === board[2][j]) {
return board[0][j];
}
}
// Check columns
for (let i = 0; i < 3; i++) {
if (board[i][0] !== '' &&
board[i][0] === board[i][1] &&
board[i][1] === board[i][2]) {
return board[i][0];
}
}
// Check diagonal (top-left to bottom-right)
if (board[0][0] !== '' &&
board[0][0] === board[1][1] &&
board[1][1] === board[2][2]) {
return board[0][0];
}
// Check diagonal (top-right to bottom-left)
if (board[2][0] !== '' &&
board[2][0] === board[1][1] &&
board[1][1] === board[0][2]) {
return board[2][0];
}
// No winner yet
return null;
}
Breaking it down
Check rows:
for (let i = 0; i < 3; i++) {
if (board[i][0] !== '' &&
board[i][0] === board[i][1] &&
board[i][1] === board[i][2]) {
return board[i][0];
}
}
- Loop through each row (i = 0, 1, 2)
board[i][0] !== ''- First cell in row is not emptyboard[i][0] === board[i][1]- First cell equals second cellboard[i][1] === board[i][2]- Second cell equals third cell- If all three match, return the symbol ('X' or 'O')
Check columns:
for (let j = 0; j < 3; j++) {
if (board[0][j] !== '' &&
board[0][j] === board[1][j] &&
board[1][j] === board[2][j]) {
return board[0][j];
}
}
- Loop through each column (j = 0, 1, 2)
- Check if all three cells in column match
- Same logic as rows, but checking vertically
Check diagonal (top-left to bottom-right):
if (board[0][0] !== '' &&
board[0][0] === board[1][1] &&
board[1][1] === board[2][2]) {
return board[0][0];
}
- Check positions: [0,0], [1,1], [2,2]
- This is the \ diagonal
Check diagonal (top-right to bottom-left):
if (board[2][0] !== '' &&
board[2][0] === board[1][1] &&
board[1][1] === board[0][2]) {
return board[2][0];
}
- Check positions: [2,0], [1,1], [0,2]
- This is the / diagonal
Return null if no winner:
- If none of the winning conditions are met, return
null
Step 3: Check for Tie
Add a function to check if the board is full:
function checkTie() {
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (board[i][j] === '') {
return false; // Found an empty cell
}
}
}
return true; // No empty cells = board is full
}
Breaking it down
- Loop through all cells
- If any cell is empty (
''), returnfalse- game can continue - If all cells are filled, return
true- board is full - A full board with no winner = tie game
Step 4: Call Win Detection After Each Move
Update the mousePressed() function to check for wins:
function mousePressed() {
// Don't allow moves if game is over
if (gameOver) return;
let i = floor(mouseX / cellSize);
let j = floor(mouseY / cellSize);
if (i >= 0 && i < 3 && j >= 0 && j < 3) {
if (board[i][j] === '') {
board[i][j] = currentPlayer;
// Check for winner
let result = checkWinner();
if (result !== null) {
gameOver = true;
winner = result;
console.log(winner, "wins!");
} else if (checkTie()) {
gameOver = true;
winner = 'tie';
console.log("It's a tie!");
} else {
// Switch to other player
if (currentPlayer === 'X') {
currentPlayer = 'O';
} else {
currentPlayer = 'X';
}
}
}
}
}
Breaking it down
if (gameOver) return;- Prevent moves after game ends- After placing a symbol:
checkWinner()- Check if current player won- If someone won:
gameOver = true- Stop the gamewinner = result- Store who won
- If no winner, check for tie:
checkTie()- Is board full?- If full:
winner = 'tie'
- If no winner and no tie:
- Switch to other player (game continues)
Test it: Play a game and try to get 3 in a row - check the console!
Step 5: Display Game Over Message
Update the draw() function to show the winner:
function draw() {
background(255);
// Highlight hovered cell (only if game not over)
if (!gameOver && mouseX >= 0 && mouseX < width && mouseY >= 0 && mouseY < height) {
let i = floor(mouseX / cellSize);
let j = floor(mouseY / cellSize);
if (i >= 0 && i < 3 && j >= 0 && j < 3) {
if (board[i][j] === '') {
fill(200, 200, 200, 100);
noStroke();
rect(i * cellSize, j * cellSize, cellSize, cellSize);
}
}
}
// Draw grid lines
stroke(0);
strokeWeight(4);
line(cellSize, 0, cellSize, height);
line(cellSize * 2, 0, cellSize * 2, height);
line(0, cellSize, width, cellSize);
line(0, cellSize * 2, width, cellSize * 2);
// Draw X's and O's
drawBoard();
// Display current player or game over message
fill(0);
noStroke();
textSize(24);
textAlign(CENTER);
if (gameOver) {
if (winner === 'tie') {
text("It's a Tie!", width / 2, 30);
} else {
text(winner + " Wins!", width / 2, 30);
}
} else {
text("Current Player: " + currentPlayer, width / 2, 30);
}
}
Breaking it down
Hover effect:
if (!gameOver && mouseX >= 0 && mouseX < width && mouseY >= 0 && mouseY < height) {
let i = floor(mouseX / cellSize);
let j = floor(mouseY / cellSize);
if (i >= 0 && i < 3 && j >= 0 && j < 3 && board[i][j] === '') {
fill(200, 200, 200, 100);
rect(i * cellSize, j * cellSize, cellSize, cellSize);
}
}
if (!gameOver && ...)- Only show hover effect if game is still active- Calculates which cell the mouse is over using
floor() - If cell is empty, highlights it with a semi-transparent gray rectangle
Game over message:
if (gameOver) {
if (winner === 'tie') {
text("It's a Tie!", width / 2, 30);
} else {
text(winner + " Wins!", width / 2, 30);
}
} else {
text("Current Player: " + currentPlayer, width / 2, 30);
}
- If game is over and it's a tie: show "It's a Tie!"
- If game is over with a winner: show "X Wins!" or "O Wins!"
- If game is still active: show "Current Player: X" or "Current Player: O"
Test it: Play a full game - the winner should display at the top!
Step 6: Add Visual Feedback for Winner
Let's highlight the winning line:
let winningLine = null; // Add at top with other variables
Update checkWinner() to store the winning line:
function checkWinner() {
// Check rows
for (let i = 0; i < 3; i++) {
if (board[i][0] !== '' &&
board[i][0] === board[i][1] &&
board[i][1] === board[i][2]) {
winningLine = {type: 'row', index: i};
return board[i][0];
}
}
// Check columns
for (let j = 0; j < 3; j++) {
if (board[0][j] !== '' &&
board[0][j] === board[1][j] &&
board[1][j] === board[2][j]) {
winningLine = {type: 'col', index: j};
return board[0][j];
}
}
// Check diagonal (top-left to bottom-right)
if (board[0][0] !== '' &&
board[0][0] === board[1][1] &&
board[1][1] === board[2][2]) {
winningLine = {type: 'diag', index: 0};
return board[0][0];
}
// Check diagonal (top-right to bottom-left)
if (board[2][0] !== '' &&
board[2][0] === board[1][1] &&
board[1][1] === board[0][2]) {
winningLine = {type: 'diag', index: 1};
return board[2][0];
}
return null;
}
Add function to draw the winning line:
function drawWinningLine() {
if (winningLine === null) return;
stroke(0, 255, 0); // Green line
strokeWeight(8);
if (winningLine.type === 'row') {
let y = winningLine.index * cellSize + cellSize / 2;
line(0, y, width, y);
} else if (winningLine.type === 'col') {
let x = winningLine.index * cellSize + cellSize / 2;
line(x, 0, x, height);
} else if (winningLine.type === 'diag') {
if (winningLine.index === 0) {
line(0, 0, width, height); // Top-left to bottom-right
} else {
line(width, 0, 0, height); // Top-right to bottom-left
}
}
}
Call it in draw() after drawing the board:
function draw() {
// ... existing code ...
// Draw X's and O's
drawBoard();
// Draw winning line if game is over
if (gameOver) {
drawWinningLine();
}
// Display current player or game over message
// ... rest of code ...
}
Breaking it down
winningLine = {type: 'row', index: i}- Store which row won- Types: 'row', 'col', or 'diag'
- Index: which row/column (0-2), or which diagonal (0 or 1)
drawWinningLine()draws a green line through the winning cells- Line drawn after symbols so it appears on top
Test it: Win a game - you should see a green line through the winning symbols!
Complete Step 3 Code
Here's everything together:
let cellSize = 400 / 3;
let board = [
['', '', ''],
['', '', ''],
['', '', '']
];
let currentPlayer = 'X';
let gameOver = false;
let winner = '';
let winningLine = null;
function setup() {
createCanvas(400, 400);
}
function draw() {
background(255);
// Highlight hovered cell
if (!gameOver && mouseX >= 0 && mouseX < width && mouseY >= 0 && mouseY < height) {
let i = floor(mouseX / cellSize);
let j = floor(mouseY / cellSize);
if (i >= 0 && i < 3 && j >= 0 && j < 3) {
if (board[i][j] === '') {
fill(200, 200, 200, 100);
noStroke();
rect(i * cellSize, j * cellSize, cellSize, cellSize);
}
}
}
// Draw grid lines
stroke(0);
strokeWeight(4);
line(cellSize, 0, cellSize, height);
line(cellSize * 2, 0, cellSize * 2, height);
line(0, cellSize, width, cellSize);
line(0, cellSize * 2, width, cellSize * 2);
// Draw X's and O's
drawBoard();
// Draw winning line if game is over
if (gameOver) {
drawWinningLine();
}
// Display current player or game over message
fill(0);
noStroke();
textSize(24);
textAlign(CENTER);
if (gameOver) {
if (winner === 'tie') {
text("It's a Tie!", width / 2, 30);
} else {
text(winner + " Wins!", width / 2, 30);
}
} else {
text("Current Player: " + currentPlayer, width / 2, 30);
}
}
function drawBoard() {
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
let x = i * cellSize + cellSize / 2;
let y = j * cellSize + cellSize / 2;
let symbol = board[i][j];
if (symbol === 'X') {
drawX(x, y);
} else if (symbol === 'O') {
drawO(x, y);
}
}
}
}
function drawX(x, y) {
let offset = cellSize / 4;
stroke(255, 0, 0);
strokeWeight(8);
line(x - offset, y - offset, x + offset, y + offset);
line(x + offset, y - offset, x - offset, y + offset);
}
function drawO(x, y) {
stroke(0, 0, 255);
strokeWeight(8);
noFill();
circle(x, y, cellSize / 2);
}
function mousePressed() {
if (gameOver) return;
let i = floor(mouseX / cellSize);
let j = floor(mouseY / cellSize);
if (i >= 0 && i < 3 && j >= 0 && j < 3) {
if (board[i][j] === '') {
board[i][j] = currentPlayer;
// Check for winner
let result = checkWinner();
if (result !== null) {
gameOver = true;
winner = result;
} else if (checkTie()) {
gameOver = true;
winner = 'tie';
} else {
// Switch to other player
if (currentPlayer === 'X') {
currentPlayer = 'O';
} else {
currentPlayer = 'X';
}
}
}
}
}
function checkWinner() {
// Check rows
for (let i = 0; i < 3; i++) {
if (board[i][0] !== '' &&
board[i][0] === board[i][1] &&
board[i][1] === board[i][2]) {
winningLine = {type: 'row', index: i};
return board[i][0];
}
}
// Check columns
for (let j = 0; j < 3; j++) {
if (board[0][j] !== '' &&
board[0][j] === board[1][j] &&
board[1][j] === board[2][j]) {
winningLine = {type: 'col', index: j};
return board[0][j];
}
}
// Check diagonal (top-left to bottom-right)
if (board[0][0] !== '' &&
board[0][0] === board[1][1] &&
board[1][1] === board[2][2]) {
winningLine = {type: 'diag', index: 0};
return board[0][0];
}
// Check diagonal (top-right to bottom-left)
if (board[2][0] !== '' &&
board[2][0] === board[1][1] &&
board[1][1] === board[0][2]) {
winningLine = {type: 'diag', index: 1};
return board[2][0];
}
return null;
}
function checkTie() {
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (board[i][j] === '') {
return false;
}
}
}
return true;
}
function drawWinningLine() {
if (winningLine === null) return;
stroke(0, 255, 0);
strokeWeight(8);
if (winningLine.type === 'row') {
let x = winningLine.index * cellSize + cellSize / 2;
line(x, 0, x, height); // Vertical line through column
} else if (winningLine.type === 'col') {
let y = winningLine.index * cellSize + cellSize / 2;
line(0, y, width, y); // Horizontal line through row
} else if (winningLine.type === 'diag') {
if (winningLine.index === 0) {
line(0, 0, width, height); // Top-left to bottom-right
} else {
line(width, 0, 0, height); // Top-right to bottom-left
}
}
}
What You Learned
In this step, you:
- ✅ Added win detection for all 8 possible winning lines
- ✅ Checked rows, columns, and diagonals
- ✅ Added tie detection when board is full
- ✅ Prevented moves after game ends
- ✅ Displayed winner or tie message
- ✅ Drew a green line through winning symbols
- ✅ Learned about comprehensive condition checking
What's Next?
The game is almost complete! But once a game ends, you can't play again without refreshing. Next, we'll add a restart button and some final polish.
Coming up: Restart functionality and final touches!
Next Step: Step 4 - Complete Code: Restart and Polish