Step 2 - Click Detection: Placing Symbols
Let's make the game interactive by detecting mouse clicks and placing symbols on the board!
Understanding Click-to-Grid Conversion
When you click the canvas:
- Get mouse position (mouseX, mouseY)
- Convert to grid position (which cell was clicked?)
- Place current player's symbol
- Switch to other player
Step 1: Add mousePressed Function
Add this function at the bottom of your code:
function mousePressed() {
// Convert mouse position to grid position
let i = floor(mouseX / cellSize);
let j = floor(mouseY / cellSize);
console.log("Clicked cell:", i, j);
}
Breaking it down
mousePressed()- Special p5.js function called once when mouse is clickedfloor(mouseX / cellSize)- Converts X pixel to column number- If mouseX = 50, and cellSize = 133.33, then 50 / 133.33 = 0.375
floor(0.375)= 0 (first column)- If mouseX = 200, then 200 / 133.33 = 1.5
floor(1.5)= 1 (second column)
floor(mouseY / cellSize)- Same for Y coordinate to get rowconsole.log()- Shows in console which cell was clicked
Test it: Click different cells and watch the console - you should see numbers 0-2!
Step 2: Check if Cell is Empty
Before placing a symbol, make sure the cell isn't already occupied:
function mousePressed() {
let i = floor(mouseX / cellSize);
let j = floor(mouseY / cellSize);
// Make sure click is within bounds
if (i >= 0 && i < 3 && j >= 0 && j < 3) {
// Check if cell is empty
if (board[i][j] === '') {
console.log("Cell is empty! Can place", currentPlayer);
} else {
console.log("Cell already has:", board[i][j]);
}
}
}
Breaking it down
if (i >= 0 && i < 3 && j >= 0 && j < 3)- Bounds checking- Makes sure click is inside the 3×3 grid
- Prevents errors if click is outside canvas
if (board[i][j] === '')- Check if cell is empty- Empty cells have empty string
'' - Only allow placing symbol in empty cells
- Prevents overwriting existing symbols
- Empty cells have empty string
Test it: Click on empty cells, then click the same cell again - console shows different messages!
Step 3: Place the Symbol
Now actually place the symbol on the board:
function mousePressed() {
let i = floor(mouseX / cellSize);
let j = floor(mouseY / cellSize);
if (i >= 0 && i < 3 && j >= 0 && j < 3) {
if (board[i][j] === '') {
// Place current player's symbol
board[i][j] = currentPlayer;
console.log(currentPlayer, "placed at", i, j);
}
}
}
Breaking it down
board[i][j] = currentPlayer- Sets cell to 'X' or 'O'- This modifies the board array
drawBoard()indraw()automatically shows the new symbol- Symbol appears immediately!
Test it: Click cells - X's should appear! But we can only place X's right now...
Step 4: Switch Players After Each Move
Let's alternate between X and O:
function mousePressed() {
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;
// Switch to other player
if (currentPlayer === 'X') {
currentPlayer = 'O';
} else {
currentPlayer = 'X';
}
}
}
}
Breaking it down
- After placing a symbol, we switch players
if (currentPlayer === 'X')- If X just playedcurrentPlayer = 'O'- Make it O's turn
else- If O just playedcurrentPlayer = 'X'- Make it X's turn
- Players automatically alternate!
Test it: Click cells - should alternate between red X's and blue O's!
Step 5: Display Current Player
Let's show whose turn it is:
function draw() {
background(255);
// 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
fill(0);
noStroke();
textSize(24);
textAlign(CENTER);
text("Current Player: " + currentPlayer, width / 2, 30);
}
Breaking it down
fill(0)- Black textnoStroke()- Remove outline from texttextSize(24)- Readable sizetextAlign(CENTER)- Center the text horizontallytext("Current Player: " + currentPlayer, width / 2, 30)- Shows "Current Player: X" or "Current Player: O"width / 2= center of canvas (200)30= 30 pixels from top
Test it: You should see "Current Player: X" at the top, changing after each move!
Step 6: Add Hover Effect
Let's show which cell you're about to click:
function draw() {
background(255);
// Highlight hovered cell
if (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); // Light gray, semi-transparent
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
fill(0);
noStroke();
textSize(24);
textAlign(CENTER);
text("Current Player: " + currentPlayer, width / 2, 30);
}
Breaking it down
Display current player:
fill(0);
noStroke();
textSize(24);
textAlign(CENTER);
text("Current Player: " + currentPlayer, width / 2, 30);
- Shows "Current Player: X" or "Current Player: O" at the top
width / 2= center of canvas (200)30= 30 pixels from top- Changes after each move
Hover effect:
if (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);
}
}
- Checks if mouse is over the canvas
- Converts mouse position to grid cell (i, j)
- If cell is empty, highlights it with semi-transparent gray
fill(200, 200, 200, 100)- Light gray with transparency (100 = alpha)- Hover effect draws BEFORE grid lines so lines stay on top
Test it: Move your mouse over empty cells - they should highlight!
Complete Step 2 Code
Here's everything together:
let cellSize = 400 / 3;
let board = [
['', '', ''],
['', '', ''],
['', '', '']
];
let currentPlayer = 'X';
function setup() {
createCanvas(400, 400);
}
function draw() {
background(255);
// Highlight hovered cell
if (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
fill(0);
noStroke();
textSize(24);
textAlign(CENTER);
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() {
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;
// Switch to other player
if (currentPlayer === 'X') {
currentPlayer = 'O';
} else {
currentPlayer = 'X';
}
}
}
}
What You Learned
In this step, you:
- ✅ Detected mouse clicks on the canvas
- ✅ Converted pixel coordinates to grid coordinates
- ✅ Checked if cells are empty before placing
- ✅ Placed symbols on the board
- ✅ Switched players automatically
- ✅ Displayed current player
- ✅ Added hover effect for better UX
What's Next?
You can now play the game! But there's no way to win yet - the game continues forever. Next, we'll add win detection to check if someone has won.
Coming up: Detecting winners and ties!
Next Step: Step 3 - Win Detection: Checking for Winners