Thilan Dissanayaka Pet Projects May 06

Tic-Tac-Toe Game with Atmega 256 MicroController

In this blog, I’ll walk you through how I built a Tic-Tac-Toe game using an AVR microcontroller, a 4x3 keypad, and a 3x3 grid of LEDs.

This project is a fun way to combine embedded programming, game logic, and hardware interaction into a complete system that brings a classic game to life using simple electronics.

Project Overview

The objective of this project was to create an interactive Tic-Tac-Toe game where:

Players enter their moves using a 4x3 keypad.

The current game state is visualized using a 3x3 LED grid, with each cell lighting up red or green depending on the player's turn.

The game identifies the winner and plays a simple LED animation to celebrate the victory.

Hardware Components

Here’s what I used for the setup:

  • ATmega microcontroller (running at 8 MHz)

  • 4x3 Keypad for user input

  • 3x3 LED grid for game display

  • Resistors, wires, and breadboard for wiring

  • Power supply (USB/5V battery or programmer)

Software Workflow

Let’s break down the development process step-by-step:

Step 1: Setting Up the Keypad

The keypad serves as the input device where each button represents a cell (1–9) on the Tic-Tac-Toe board. I used PORTK for rows and PINK for columns on the ATmega.

void initKeypad() {
    DDRK = 0x0F;    // Lower nibble (rows) as output, upper nibble (columns) as input
    PORTK |= 0xF0;  // Enable internal pull-ups for the inputs
}

The keypad() function handles:

Polling each row/column to detect key presses

Debouncing to eliminate accidental multiple presses

Mapping key values to grid positions (1–9)

Step 2: Initializing the LED Grid The LED grid is controlled through multiple ports. Each cell corresponds to one LED, and different pins on PORTA, PORTC, PORTD, and PORTG are used.

void initGrid() {
    DDRA = 0xFF;
    DDRC = 0xFF;
    DDRD = 0xFF;
    DDRG = 0xFF;
    // Set all grid control ports as output
}

Each LED is controlled individually based on the game state using a helper function like controlLed(row, col, color, state).

♻️ Step 3: Implementing Game Logic The game logic is responsible for:

Keeping track of the player turn (RED or GREEN)

Preventing overwriting already marked cells

Updating the LED grid

void play(int index) {
    int row = (index - 1) / 3;
    int col = (index - 1) % 3;

    if (grid[row][col] == 0) {          // Check if the cell is empty
        grid[row][col] = turn;          // Mark the cell
        controlLed(row, col, turn, ON); // Light up the LED
        turn = (turn == RED) ? GREEN : RED; // Switch turns
    }
}

🏁 Step 4: Checking for a Winner After every move, we check rows, columns, and diagonals to see if a player has won.

int checkWinner() {
    for (int i = 0; i < 3; i++) {
        // Check rows
        if (grid[i][0] != 0 &&
            grid[i][0] == grid[i][1] &&
            grid[i][1] == grid[i][2])
            return grid[i][0];

        // Check columns
        if (grid[0][i] != 0 &&
            grid[0][i] == grid[1][i] &&
            grid[1][i] == grid[2][i])
            return grid[0][i];
    }

    // Check diagonals
    if (grid[0][0] != 0 &&
        grid[0][0] == grid[1][1] &&
        grid[1][1] == grid[2][2])
        return grid[0][0];

    if (grid[0][2] != 0 &&
        grid[0][2] == grid[1][1] &&
        grid[1][1] == grid[2][0])
        return grid[0][2];

    return 0; // No winner
}

🎉 Step 5: Winning Animation When a player wins, a simple animation flashes the LEDs in the winning row/column/diagonal.

void animation(int winner) {
    for (int i = 0; i < 3; i++) {
        controlLed(0, i, winner, ON);
        _delay_ms(300);
        controlLed(0, i, winner, OFF);
        _delay_ms(300);
    }
}
ALSO READ
Exploiting a Stack Buffer Overflow on Windows
May 17 Exploit development

In a previous tutorial we discusses how we can exploit a buffer overflow vulnerability on a Linux machine. I wen through all theories in depth and explained each step. Now today we are going to....

Database Indexing: Speeding Up Your Queries Like a Pro
Apr 26 Database Systems

In the world of databases, speed matters. Whether you're powering an e-commerce store, a social media app, or a business dashboard — users expect data to load instantly. That’s where database....

GDB reverse engineering tutorial
Mar 23 Low level Development

hiii, I selected an interesting topic to discuss. Here, we are going to disassemble a binary file and take a look at what it does. This process is called reverse engineering. Let's run the program....

Building a Web3 CLI Tool for the Ballerina Language: From Idea to Reality
Apr 26 WSO2

🚀 Excited to finally share my journey of building a web3 CLI tool for Ballerina! This tool bridges the gap between Ethereum smart contracts and the Ballerina programming language by automatically....

Adapter Pattern explained simply
Apr 26 Software Architecture

Ever needed to connect two incompatible interfaces without changing their source code? That’s exactly where the **Adapter Pattern** shines! The Adapter Pattern is a structural design pattern....

CI/CD concepts - Interview preparation guide
Jan 05 Interview Guides

## What is CI/CD? CI/CD stands for Continuous Integration and Continuous Delivery/Deployment. CI is the practice of automatically integrating code changes from multiple contributors into a....