Things used in this project

Hardware components:
Neopixel strip
NeoPixel strip
×1
Teensy31
Teensy 3.1
×1
Tic-Tac-Toe Game
×1
Uxcell U18 Hall Effect Sensor
×1
Magnets
×1
5v 60A Power Supply
×1
Copper Conductive Tape
×1
OctoWS2811 Adapter Board
×1
Software apps and online services:
Ide web
Arduino IDE
Hand tools and fabrication machines:
09507 01
Soldering iron (generic)
Hand Drill
Hy gluegun
Hot glue gun (generic)

Code

tic-tac-toe-1.0.0C/C++
/****************************************
 * Written by:   Sam Kristoff
 * Version:      1.0 
****************************************/

#include <OctoWS2811.h>

//LED Variables
const int ledsPerStrip = 108;
DMAMEM int displayMemory[ledsPerStrip*6];
int drawingMemory[ledsPerStrip*6];
const int config = WS2811_GRB | WS2811_800kHz;

OctoWS2811 leds(ledsPerStrip, displayMemory, drawingMemory, config);

#define NUM_COLORS 7


#define BLACK  0x000000
#define RED    0xFF0000
#define GREEN  0x00FF00
#define BLUE   0x0000FF
#define YELLOW 0xFFFF00
#define PINK   0xFF1088
#define ORANGE 0xE05800
#define WHITE  0xFFFFFF

/*
// Less intense...
#define BLACK  0x000000
#define RED    0x160000
#define GREEN  0x001600
#define BLUE   0x000016
#define YELLOW 0x101400
#define PINK   0x120009
#define ORANGE 0x100400
#define WHITE  0x101010
*/

#define PLAYERWIN 0
#define COMPUTERWIN 1
#define TIE 2


int colors[NUM_COLORS] = {RED, GREEN, BLUE, YELLOW, PINK, ORANGE, WHITE};
int colorIndex = 0;
int playerColor = RED;
int computerColor = GREEN;
int drawColor = BLUE;

//Define game states
enum gameState_t {
  PREGAME,
  PLAYERTURN,
  COMPUTERTURN,
  GAMEOVER,
};

gameState_t gameState = PREGAME;


void setup() {
  Serial.begin(9600);
  Serial.println("Tic-Tac-Toe");
  leds.begin();
  setStripColor(BLACK);
  leds.show();  
  //delay(1000);

  //Set hall effect sensor pins as inputs.
  pinMode(4, INPUT);
  pinMode(5, INPUT);
  pinMode(6, INPUT);
  pinMode(7, INPUT);
  pinMode(8, INPUT);
  pinMode(9, INPUT);
  pinMode(10, INPUT);
  pinMode(11, INPUT);
  pinMode(12, INPUT);
}


unsigned int boardState = 0;

unsigned int playerCell = 0;
unsigned int computerCell = 0;

unsigned int playerCells = 0;
unsigned int computerCells = 0;

unsigned int previousBoardState = 0;

int winner = -1;
void loop() {
   
  //Game State Machine
  switch(gameState) {
    case PREGAME:   
      //Initialize values
      previousBoardState = 0;
      playerCell = 0;
      computerCell = 0;

      playerCells = 0;
      computerCells = 0;
      
      winner = -1;
      
      boardState = readBoardValue();
      cycleCellColors(50);     
      Serial.print("boardState = "); Serial.println(boardState, BIN);
      if(boardState != 0){        
        //Clear the board, then highlight the cell the player chose
        setStripColor(BLACK);  
        playerCell = findPlayerMoveCell(boardState, previousBoardState);
        playerCells |= 0x0001 << playerCell;
        setCellColor(playerCell, playerColor);                
        previousBoardState = boardState;
        gameState = COMPUTERTURN;       
        Serial.println("Computer Turn");
      }
      break;
    case COMPUTERTURN:   
      delay(100);
      computerCell = computeMove(boardState);
      computerCells |= 0x0001 << computerCell;   
      setCellColor(computerCell, computerColor);      
      boardState = boardState | 0x0001 << computerCell;   
      previousBoardState = boardState;
      //Check for winner
      winner = checkForGameOver(playerCells, computerCells);
      if(winner >= 0){
          gameState = GAMEOVER;       
          Serial.println("Game Over");
      } 
      else 
      {
        gameState = PLAYERTURN;       
        Serial.println("Player Turn");
      }      
      break;
    case PLAYERTURN:
      boardState = boardState | readBoardValue();
      if(boardState != previousBoardState)
      {
        playerCell = findPlayerMoveCell(boardState, previousBoardState);
        playerCells |= 0x0001 << playerCell;
        setCellColor(playerCell, playerColor);      
        previousBoardState = boardState;

        //Check for winner
        winner = checkForGameOver(playerCells, computerCells);
        if(winner >= 0){
          gameState = GAMEOVER;       
          Serial.println("Game Over");
        } 
        else 
        {                  
          gameState = COMPUTERTURN;       
          Serial.println("Computer Turn");
          delay(1000);
        }        
      }
      break;
    case GAMEOVER:
      if(winner == TIE)
      {      
        setStripColor(drawColor);
        delay(1000);
      }
      if(winner == PLAYERWIN)
      {      
        setStripColor(playerColor);
        flashWinningLine(playerCells, computerCells, 2);
      }
      if(winner == COMPUTERWIN)
      {      
        setStripColor(computerColor);
        flashWinningLine(playerCells, computerCells, 2);
      }
      break;
    default:
      break;
  }  

  //If the board is ever empty reutrn to pre-game
  if(readBoardValue() == 0)
  {
    gameState = PREGAME;
  }
}

//Set the entire strip to the specified color
void setStripColor(int color)
{
  for (int i=0; i < leds.numPixels(); i++) {
    leds.setPixel(i, color);       
  }
  leds.show(); 
}

//Set the color of a single cell
void setCellColor(int cell, int color)
{  
  for (int i=0; i < 12; i++) {
    leds.setPixel((cell*12) + i -1, color);       
  }
  leds.show();
}

//Read board value
unsigned int readBoardValue(){
  unsigned int boardState = 0;
  for(int i=0; i<9; i++){
      int set = !digitalRead(i+4);
      boardState = boardState | (set << i); 
  }  
  return boardState;
}

//Find the cell number that the player just claimed
int findPlayerMoveCell(unsigned int boardState, unsigned int previousBoardState){
  unsigned int diff = boardState - previousBoardState;
  Serial.print("dif = ");  
  Serial.println(diff, BIN);  
  int index = 0;
  //Scan through bits until we find the one that is set
  while(((diff >> index) & 0x0001) == 0)
  {
    index++;
  }
  Serial.print("Player Chose ");  
  Serial.println(index);  
  return index;
}

//Calculate the comptuer's move and return the selected cell
unsigned int computeMove(unsigned int boardState){  
  Serial.println("Computing Move... ");  
  Serial.print("boardState = ");  
  Serial.println(boardState, BIN);  
  while(1) {    
    int cell = random(0, 9);
    Serial.print("Checking Cell ");  
    Serial.println(cell, DEC); 

    /*
    Serial.print("Shifted Value =  ");  
     Serial.println((boardState >> cell), BIN); 
      Serial.print("ANDED Value =  "); 
     Serial.println(((boardState >> cell) & 0x0001) == 0); 
    */ 
    if(((boardState >> cell) & 0x0001) == 0)
    {
      Serial.print("Computer Chose ");  
      Serial.println(cell); 
      return cell;
    }     
  }  
}

int checkForGameOver(unsigned int playerCells, unsigned int computerCells) 
{ 
  Serial.print("Player Cells = ");
  Serial.println(playerCells, BIN);
  Serial.print("Computer Cells = ");
  Serial.println(computerCells, BIN);
  if((playerCells & 0b000000111) == 0b000000111 ||
      (playerCells & 0b000111000) == 0b000111000 ||
      (playerCells & 0b111000000) == 0b111000000 ||
      (playerCells & 0b001100001) == 0b001100001 ||
      (playerCells & 0b010010010) == 0b010010010 ||
      (playerCells & 0b100001100) == 0b100001100 ||
      (playerCells & 0b100010001) == 0b100010001 ||
      (playerCells & 0b001010100) == 0b001010100 )
   {
    return PLAYERWIN;
   }
   
   if((computerCells & 0b000000111) == 0b000000111 ||
      (computerCells & 0b000111000) == 0b000111000 ||
      (computerCells & 0b111000000) == 0b111000000 ||
      (computerCells & 0b001100001) == 0b001100001 ||
      (computerCells & 0b010010010) == 0b010010010 ||
      (computerCells & 0b100001100) == 0b100001100 ||
      (computerCells & 0b100010001) == 0b100010001 ||
      (computerCells & 0b001010100) == 0b001010100 )
   {
    return COMPUTERWIN;
   }

   if((playerCells | computerCells) == 0b111111111)
   {
    return TIE;
   }
   
   return -1;
}

int flashWinningLine(unsigned int playerCells, unsigned int computerCells , int numFlashes) {
  int winner =  checkForGameOver(playerCells, computerCells);
  int winningColor = BLACK;
  unsigned int winningBoard = 0;
  if(winner == PLAYERWIN){
     winningColor = playerColor;
     winningBoard = playerCells;
  } else if(winner == COMPUTERWIN){
     winningColor = computerColor;
     winningBoard = computerCells;
  }

  int cell0 = 0;
  int cell1 = 0;
  int cell2 = 0;

  if((winningBoard & 0b000000111) == 0b000000111)  {
    cell0 = 0;
    cell1 = 1;
    cell2 = 2;
  } else if ((winningBoard & 0b000111000) == 0b000111000)  {
    cell0 = 3;
    cell1 = 4;
    cell2 = 5;
  } else if ((winningBoard & 0b111000000) == 0b111000000)  {
    cell0 = 6;
    cell1 = 7;
    cell2 = 8;
  } else if ((winningBoard & 0b001100001) == 0b001100001)  {
    cell0 = 0;
    cell1 = 5;
    cell2 = 6;
  } else if ((winningBoard & 0b010010010) == 0b010010010)  {
    cell0 = 1;
    cell1 = 4;
    cell2 = 7;
  } else if ((winningBoard & 0b100001100) == 0b100001100)  {
    cell0 = 2;
    cell1 = 3;
    cell2 = 8;
  } else if ((winningBoard & 0b100010001) == 0b100010001)  {
    cell0 = 0;
    cell1 = 4;
    cell2 = 8;
  } else if ((winningBoard & 0b001010100) == 0b001010100)  {
    cell0 = 2;
    cell1 = 4;
    cell2 = 6;
  }  

  //Flash Colors
  for(int i=0; i<numFlashes; i++){
    setCellColor(cell0, winningColor);
    setCellColor(cell1, winningColor);
    setCellColor(cell2, winningColor);
    delay(250);
  
    setCellColor(cell0, BLACK);
    setCellColor(cell1, BLACK);
    setCellColor(cell2, BLACK);
    delay(250);
  }  
}


//---------- Cycle cell colors ---------
//Cycle through all cells and all colors in order.
#define NUM_CYCLE_COLORS 7

int cycleCellIndex = 0;
int cycleColors[NUM_COLORS] = {RED, GREEN, BLUE, YELLOW, PINK, ORANGE, WHITE};
int cycleColorIndex = 0;

void cycleCellColors(int cellDelay) {
  for (int i=0; i < 12; i++) {
   setCellColor(cycleCellIndex, cycleColors[cycleColorIndex]);
   cycleCellIndex = (cycleCellIndex + 1)  % 9;
   cycleColorIndex = (cycleColorIndex + 1) % NUM_CYCLE_COLORS;
   delay(cellDelay);
   leds.show();
  }
}

Credits

Photo
Sam Kristoff
21 projects • 6 followers
Computer Engineer and maker located in Seattle WA. Engineering Manager at Digilent and founder of LabVIEW MakerHub.
Contact

Replications

Did you replicate this project? Share it!

I made one

Love this project? Think it could be improved? Tell us what you think!

Give feedback

Comments

Sign up / LoginProjectsPlatformsTopicsContestsLiveAppsBetaBlog