Connect 4 with NeoPixel Matrix RGB LED

Overview

by Ha Dao and Joseph Doughty

/media/uploads/Josahty/20180425_110740.jpg

This project implements the popular game Connect 4 on an mbed. Connect 4 is a two-player game where players take turns dropping markers of their respective color into a vertical board until one player has four in a row, column, or diagonal. Think tic-tac-toe, but with a bigger board and needing four to win instead of just three. If you're not familiar with how the game works, you can play a version of it online here.

Along with an mbed, this circuit uses an Adafruit NeoPixel NeoMatrix 8x8 - 64 RGB LED Pixel Matrix, a SparkFun 5-Way Tactile Switch Breakout, and a Wall Adapter Power Supply - 5V DC 2A (Barrel Jack). Take a moment to check out the Cookbook pages for the NeoMatrix and the Tactile Switch before beginning to build the circuit.

After you familiarize yourself with the components, you'll be well on your way to building the game shown in the video below! All you have to do is hook your parts up as directed by the Pin Connections segment, import the Connect_4_with_NeoPixel program into your compiler workspace, and then put it on your mbed!

When your circuit is assembled and your code is on the mbed, then you should be good to go. Hit the Reset button on your mbed to start the game. The game begins with the red player - move the joystick left or right to pick where you want to drop your marker, then center press the joystick to drop it. Next, the blue player gets to do the same. The red player and blue player alternate until a player has won, at which point the game ends and the winning four markers flash. To start a new game, just reset the mbed again!

If you have any questions, please comment on this page or email josahty@gmail.com - I'll be happy to help!

Demonstration Video

Components

Information

Note: The 5-Way Tactile Switch is no longer sold by SparkFun. However, there are several similar options available on the website.

Pin Connections

mbed5v Power SupplyNeoPixel Matrix
GNDGND
+5v+5v
pin 18DIN
mbedTactile Switch
U - up
D - down
pin 7L - left
pin 5R - right
pin 8C - center
GND-
+

Information

Since the up and down capability of the tactile switch is not used in this project, the corresponding pins don't need to be wired to the mbed.

The + pin of the tactile switch doesn't need to be powered since the project is using the mbed's internal pullups.

Code

Import programConnect_4_with_NeoPixel

Connect 4 game using the NeoPixel Matrix 8x8 RGB LED and a 5-way Tactile Navigation Switch.

main.cpp

// ======================================================================
// Program to create a Connect 4 game using a NeoPixel Matrix 8x8 RGB LED
// and a 5-way Tactile Navigation Switch.
// Ha Dao and Joseph Doughty
// April 25, 2018
// Georgia Institute of Technology, ECE4011 Final Project
//=======================================================================

#include "mbed.h"
#include "NeoStrip.h"
#include "PinDetect.h"
#include "Matrix.h"

#define N 64

NeoStrip strip(p18, N); // 8x8 neopixel matrix with each "pixel" LED addressable by its index, which range from 0 to 63 
Matrix board(8,7); // the Connect Four game board is a 6x7 matrix, the NeoPixel matrix is 8x8, created an 8x7 matrix to keep track of moves
             
PinDetect right(p5);
PinDetect left(p7);
PinDetect center(p8);
 
int pos = 0; //c urrent position of LED in Neopixel index
int row = 0; 
int col = 0;
int winmode = 0; //0 means the game hasn't yet been won, 1-4 are different ways to win

int red = 0xFF0000; // one player is red
int blue = 0x0000FF; // other player is blue
int color = red; // begin the game with the red player
int winningcolor = 0;

// clears the LED matrix by turning each one off
void alloff()
{
    for (int i = 0; i < N; i++)
        strip.setPixel(i, 0, 0, 0);
    strip.write();
}

// converts row and column indexes of a matrix to the corresponding index of the NeoPixel
int matrix2index(int r, int c) 
{
   return (r-1)*8 + c - 1;
}

// when the game is won, flashes the four LEDs corresponding to the winning move
// this will continue until the game is reset via the mbed reset button
void display_winner(int mode, int r, int c, int wincolor)
{
    switch (mode)
    {
        case 1:
            while (1)
                {
                   strip.setPixel(matrix2index(r,c), wincolor);
                   strip.setPixel(matrix2index(r,c+1), wincolor);
                   strip.setPixel(matrix2index(r,c+2), wincolor);
                   strip.setPixel(matrix2index(r,c+3), wincolor);
                   strip.write();
                   wait(0.2);
                   strip.setPixel(matrix2index(r,c), 0, 0, 0);
                   strip.setPixel(matrix2index(r,c+1), 0, 0, 0);
                   strip.setPixel(matrix2index(r,c+2), 0, 0, 0);
                   strip.setPixel(matrix2index(r,c+3), 0, 0, 0);
                   strip.write();
                   wait(0.2);
                }
        case 2:
            while (1)
                {
                   strip.setPixel(matrix2index(r,c), wincolor);
                   strip.setPixel(matrix2index(r+1,c), wincolor);
                   strip.setPixel(matrix2index(r+2,c), wincolor);
                   strip.setPixel(matrix2index(r+3,c), wincolor);
                   strip.write();
                   wait(0.2);
                   strip.setPixel(matrix2index(r,c), 0, 0, 0);
                   strip.setPixel(matrix2index(r+1,c), 0, 0, 0);
                   strip.setPixel(matrix2index(r+2,c), 0, 0, 0);
                   strip.setPixel(matrix2index(r+3,c), 0, 0, 0);
                   strip.write();
                   wait(0.2);
                }
        case 3:
            while (1)
                {
                   strip.setPixel(matrix2index(r,c), wincolor);
                   strip.setPixel(matrix2index(r+1,c+1), wincolor);
                   strip.setPixel(matrix2index(r+2,c+2), wincolor);
                   strip.setPixel(matrix2index(r+3,c+3), wincolor);
                   strip.write();
                   wait(0.2);
                   strip.setPixel(matrix2index(r,c), 0, 0, 0);
                   strip.setPixel(matrix2index(r+1,c+1), 0, 0, 0);
                   strip.setPixel(matrix2index(r+2,c+2), 0, 0, 0);
                   strip.setPixel(matrix2index(r+3,c+3), 0, 0, 0);
                   strip.write();
                   wait(0.2);
                }
        case 4:
            while (1)
                {
                   strip.setPixel(matrix2index(r,c), wincolor);
                   strip.setPixel(matrix2index(r+1,c-1), wincolor);
                   strip.setPixel(matrix2index(r+2,c-2), wincolor);
                   strip.setPixel(matrix2index(r+3,c-3), wincolor);
                   strip.write();
                   wait(0.2);
                   strip.setPixel(matrix2index(r,c), 0, 0, 0);
                   strip.setPixel(matrix2index(r+1,c-1), 0, 0, 0);
                   strip.setPixel(matrix2index(r+2,c-2), 0, 0, 0);
                   strip.setPixel(matrix2index(r+3,c-3), 0, 0, 0);
                   strip.write();
                   wait(0.2);
                }
        default:
            break;
    }
}

// checks to see if the game has been won; a game is won if either player has
// four markers in a row horizontally, diagonally, or vertically
void check_winner()
{
    //checks for four markers in a row
    for (int r = 1; r < 9; r++)    
    {
        for (int c = 1; c < 6; c++)
        {
            if (board.getNumber(r,c) != 0 && board.getNumber(r,c) == board.getNumber(r,c+1) && board.getNumber(r,c+1) == board.getNumber(r,c+2) && board.getNumber(r,c+2) == board.getNumber(r,c+3))
            {//have winner
                row = r; col = c; winmode = 1; winningcolor = color;
                return; //avoid the uncessary of continue to check
            }
        }
    }
    //checks for four markers in a column
    for (int c = 1; c < 9; c++)
    {
        for (int r = 1; r < 6; r++)
        {
            if (board.getNumber(r,c) != 0 && board.getNumber(r,c) == board.getNumber(r+1,c) && board.getNumber(r+1,c) == board.getNumber(r+2,c) && board.getNumber(r+2,c) == board.getNumber(r+3,c))
            {//have winner
                row = r; col = c; winmode = 2; winningcolor = color;
                return;
            }
        }
    }
    //checks for four markers in a forward (right-leaning) diagonal
    for (int r = 3; r < 6; r++)
    {
        for (int c = 1; c < 5; c++)
        {
            if (board.getNumber(r,c) != 0 && board.getNumber(r,c) == board.getNumber(r+1,c+1) && board.getNumber(r+1,c+1) == board.getNumber(r+2,c+2) && board.getNumber(r+2,c+2) == board.getNumber(r+3,c+3))
            {//have winner
                row = r; col = c; winmode = 3; winningcolor = color;
                return;
            }   
        }
    }
    //checks for four markers in a reverse (left-leaning) diagonal
    for (int r = 3; r < 6; r++)
    {
        for (int c = 4; c < 8; c++)
        {
            if (board.getNumber(r,c) != 0 && board.getNumber(r,c) == board.getNumber(r+1,c-1) && board.getNumber(r+1,c-1) == board.getNumber(r+2,c-2) && board.getNumber(r+2,c-2) == board.getNumber(r+3,c-3))
            {//have winner
                row = r; col = c; winmode = 4; winningcolor = color;
                return;
            }   
        }
    }
}

// move the player marker to the right
void right_hit_callback (void) { 
    strip.setPixel(pos, 0, 0, 0); // turn off the current LED
    if (pos < 6) pos = pos + 1; // only move to the right if not at "screen" edge
}

// move the player marker to the left
void left_hit_callback (void) { 
    strip.setPixel(pos, 0, 0, 0); // turn off the current LED
    if (pos > 0) pos = pos - 1; // only move to the left if not at "screen" edge
}

// drop the player marker straight down the current column
void center_hit_callback (void) { 
    strip.setPixel(pos, 0, 0, 0); // turn off the current LED
    // show marker at lowest unoccupied position in the selected column
    col = pos + 1;
    for(row = 8; row > 1; row-- )
    {
        if (board.getNumber(row,col) == 0) break; // break upon finding lowest unoccupied position
    }
    //convert to neopixel index to turn of that neopixel, but only if not all rows in 6x7 board are occupied 
    if (row > 2) strip.setPixel(matrix2index(row,col), color);
    else return;

    if (color == red) // if it's red player's turn,
    {
        board.add( row, col, 1.0); // update matrix to have a matrix to check winner
        check_winner(); // check board state to see if the game has been won
        color = blue; // switch to the blue player
    }
    else // else, if it's blue player's turn,
    {
        board.add( row, col, 2.0); // update matrix to have a matrix to check winner
        check_winner(); // check board state to see if the game has been won
        color = red; // switch to the red player
    }
}

int main() {
   // initialize the matrix to 0 so that all rows and columns are unoccupied
   board << 0 << 0 << 0 << 0 << 0 << 0 << 0   
         << 0 << 0 << 0 << 0 << 0 << 0 << 0
         << 0 << 0 << 0 << 0 << 0 << 0 << 0
         << 0 << 0 << 0 << 0 << 0 << 0 << 0
         << 0 << 0 << 0 << 0 << 0 << 0 << 0
         << 0 << 0 << 0 << 0 << 0 << 0 << 0
         << 0 << 0 << 0 << 0 << 0 << 0 << 0
         << 0 << 0 << 0 << 0 << 0 << 0 << 0;
             
    // use internal pullups for pushbutton
    right.mode(PullUp);    
    left.mode(PullUp);    
    center.mode(PullUp);
     
    // delay for initial pullup to take effect
    wait(.05);
    
    // setup interrupt callback functions for a pb hit
    right.attach_deasserted(&right_hit_callback); // used to move player marker
    left.attach_deasserted(&left_hit_callback);  // used to move player marker
    center.attach_deasserted(&center_hit_callback); // used to drop player marker
                     
    // start sampling inputs using interrupts
    right.setSampleFrequency();
    left.setSampleFrequency();
    center.setSampleFrequency();
    wait(.01);
    
    float bright = 0.2; // 20% brightness is plenty for indoor visibility
    strip.setBrightness(bright);    // set default brightness
    alloff(); // initialize all of the NeoPixel LEDs to be off
    
    while(1)
    {
        // if the game has been won, display the winning move
        display_winner(winmode,row,col,winningcolor);
        
        // flash the LED corresponding to the currently selected player marker location
        strip.setPixel(pos, color);
        strip.write();
        wait(0.2);
        strip.setPixel(pos, 0, 0, 0);
        strip.write();
        wait(0.2);
    }    
}


Please log in to post comments.