markbennettuk
Published © CC BY-NC-SA

Tic Tac Toe and Lights Out Game

A project to build an Arduino powered Tic Tac Toe and Lights Out game board.

IntermediateFull instructions provided5,938
Tic Tac Toe and Lights Out Game

Things used in this project

Hardware components

Standoff 25mm
×4
Screw to suit standoffs
×8
Pushbutton Switch, Momentary
Pushbutton Switch, Momentary
My buttons required a 5mm hole, adjust the plates to suit yours
×9
Resistor 100 ohm
Resistor 100 ohm
For the button circuit
×9
Shift Register- Serial to Parallel
Texas Instruments Shift Register- Serial to Parallel
×3
Dual colour LED
×9
Resistor 220 ohm
Resistor 220 ohm
For the LED circuit
×18
Jumper wires (generic)
Jumper wires (generic)
The number of jumper wires will depend on if you use 1 or 2 breadboards and how you wire up the resistors
×30
Breadboard (generic)
Breadboard (generic)
×1
Arduino UNO
Arduino UNO
×1
Rubber feet
×8

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free
Laser cutter (generic)
Laser cutter (generic)
Optional, DXFs included for laser cutting, hand cutting is also an option

Story

Read more

Custom parts and enclosures

Baseplate

DXF file of the baseplate, laser or hand cut from 2mm material.

Mounting plate

Plate for mounting the swithes and LEDs into, laser or hand cut from 2mm material.

Schematics

Circuit diagram

Code

Buttons

Arduino
Used to help set up the buttons array in the main program.
/*
  Tic Tac Toe Buttons
  Reads analogue input and outputs it to the serial monitor.
*/

float voltages[] = {4.92, 2.47, 1.65, 1.24, 0.99, 0.82, 0.70, 0.62, 0.55, 0.10}; // standard voltage for each button plus minimum value at the end
float boundaries[9]; // boundary voltages for button detection

int button; // number (0-8) of button pressed

void setup() {
  // set boundaries for button voltages
  for (int i = 0; i < 9; i++){
    boundaries[i] = voltages[i]-((voltages[i] - voltages[i + 1]) / 2.0);
  }
  // enable serial port for debug monitoring
  Serial.begin(9600);
}

void loop() {
  button = getButton();
  delay(500);
}

int getButton() {
  int sensorValue;
  float inputVoltage;
  button = -1;
  
  // read input value and convert to voltage
  sensorValue = analogRead(A0);
  inputVoltage = sensorValue * (5.0 / 1023.0);
  
  // convert to button number
  if (inputVoltage > 0.1){
    for (int i = 8; i >= 0; i--){
      if (boundaries[i] < inputVoltage){
        button = i;
      }
    }
  }

//  if (button > -1){
    Serial.print(inputVoltage);
    Serial.print("v = button ");
    Serial.println(button);
//  }

  return button;
}

Tic Tac Toe and Lights Out Game Code

Arduino
Main game code
// ####################################
// #                                  #
// #  Tic Tac Toe & Lights Out Games  #
// #                                  #
// #           for  Arduino           #
// #                                  #
// #           by Mark 2013           #
// #                                  #
// #  Details on Arduino Project Hub  #
// #                                  #
// ####################################

// global constants
const int player = 1;
const int computer = 2;

// global variables
// variables for buttons
// voltage for each button pressed, measured with multimeter, 10th value is minimum value for boundary setting
float voltages[] = {4.92, 2.47, 1.65, 1.24, 0.99, 0.82, 0.70, 0.62, 0.55, 0.10};
float boundaries[9]; // boundary voltages for button detection

int button; // last button pressed (0-8)

// light variables
int lights[] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; // 0 = off, 1 = red, 2 = green
int flash[] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; // used for flashing winning line in tic tac toe game

// game variables
int gameSelected = 0; // 0 = none, 1 = tic tac toe, 2 = lights out

// shift register variables
int latchPin = 8;
int clockPin = 12;
int dataPin = 11;

// binary LED values
int byte1 = 0;
int byte2 = 0;
int byte3 = 0;

void setup() {
  // set pins for shift registers
  pinMode(latchPin, OUTPUT);
  pinMode(dataPin, OUTPUT);  
  pinMode(clockPin, OUTPUT);

  // set boundaries for button voltages
  for (int i = 0; i < 9; i++){
    boundaries[i] = voltages[i]-((voltages[i] - voltages[i + 1]) / 2.0);
  }

  // enable serial port
  Serial.begin(9600);
}

void loop() {
  button = getButtonPush();
  // menu selection
  if (gameSelected == 0){
    displayMenu();
    if (button == 0 || button == 2 || button == 3 || button == 5 || button == 6 || button == 8){
      gameSelected = 1;
    }
    if (button == 1 || button == 4 || button == 7){
      gameSelected = 2;
    }
  }
// game 1, tic tac toe
  if (gameSelected == 1){
    playTickTackToe();
  }
  
// game 2, lights out
  if (gameSelected == 2){
    playLightsOut();
  }
  refreshDisplay();
  delay(50);
}

int getButtonPush() {
  int sensorValue;
  float inputVoltage;
  int pressed = -1;
  
  // read input value and convert to voltage
  sensorValue = analogRead(A0);
  inputVoltage = sensorValue * (5.0 / 1023.0);
  
  // convert to button number
  if (inputVoltage > 0.1){
    for (int i = 8; i >= 0; i--){
      if (boundaries[i] < inputVoltage){
        pressed = i;
      }
    }
    // wait until button is released before returning
    while (inputVoltage > 0.1){
      sensorValue = analogRead(A0);
      inputVoltage = sensorValue * (5.0 / 1023.0);
    }
  }
  return pressed;
}

void displayMenu() {
  // set led pattern for menu
  lights[0] = 1;
  lights[1] = 2;
  lights[2] = 1;
  lights[3] = 1;
  lights[4] = 2;
  lights[5] = 1;
  lights[6] = 1;
  lights[7] = 2;
  lights[8] = 1;
}

void refreshDisplay() {
  byte1 = 0;
  byte2 = 0;
  byte3 = 0;
  if (lights[0] == 1){byte1 = byte1 ^ B00000001;}
  if (lights[1] == 1){byte1 = byte1 ^ B00000010;}
  if (lights[2] == 1){byte1 = byte1 ^ B00000100;}
  if (lights[3] == 1){byte1 = byte1 ^ B00001000;}
  if (lights[4] == 1){byte1 = byte1 ^ B00010000;}
  if (lights[5] == 1){byte1 = byte1 ^ B00100000;}
  if (lights[6] == 1){byte1 = byte1 ^ B01000000;}
  if (lights[7] == 1){byte1 = byte1 ^ B10000000;}
  if (lights[8] == 1){byte2 = byte2 ^ B00000001;}

  if (lights[0] == 2){byte2 = byte2 ^ B00000010;}
  if (lights[1] == 2){byte2 = byte2 ^ B00000100;}
  if (lights[2] == 2){byte2 = byte2 ^ B00001000;}
  if (lights[3] == 2){byte2 = byte2 ^ B00010000;}
  if (lights[4] == 2){byte2 = byte2 ^ B00100000;}
  if (lights[5] == 2){byte2 = byte2 ^ B01000000;}
  if (lights[6] == 2){byte2 = byte2 ^ B10000000;}
  if (lights[7] == 2){byte3 = byte3 ^ B00000001;}
  if (lights[8] == 2){byte3 = byte3 ^ B00000010;}

  setLEDs();
}

void setLEDs(){
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, byte3);
  shiftOut(dataPin, clockPin, MSBFIRST, byte2);
  shiftOut(dataPin, clockPin, MSBFIRST, byte1);
  digitalWrite(latchPin, HIGH);
}

void playTickTackToe() {
  int gameState = 1;
  clearBoard();
  while (gameState == 1) {
    button = -1;
    refreshDisplay();
    button = getButtonPush();
    if (lights[button] == 0) {
      lights[button] = 1;
      gameState = checkForWin();
      refreshDisplay();
      if (gameState == 1) {
        delay(500);
        computerMove();
        refreshDisplay();
        gameState = checkForWin();
      }
    }
  }
  showWinner(gameState);
  clearBoard();
  refreshDisplay();
  delay(2000);
  gameState = 0;
  gameSelected = 0;
}

void showWinner(int gameState) {
  flash[0] = lights[0];
  flash[1] = lights[1];
  flash[2] = lights[2];
  flash[3] = lights[3];
  flash[4] = lights[4];
  flash[5] = lights[5];
  flash[6] = lights[6];
  flash[7] = lights[7];
  flash[8] = lights[8];
  if (lights[0] == player && lights[1] == player && lights[2] == player) { flash[0] = 0; flash[1] = 0; flash[2] = 0; }
  if (lights[3] == player && lights[4] == player && lights[5] == player) { flash[3] = 0; flash[4] = 0; flash[5] = 0; }
  if (lights[6] == player && lights[7] == player && lights[8] == player) { flash[6] = 0; flash[7] = 0; flash[8] = 0; }
  if (lights[0] == player && lights[3] == player && lights[6] == player) { flash[0] = 0; flash[3] = 0; flash[6] = 0; }
  if (lights[1] == player && lights[4] == player && lights[7] == player) { flash[1] = 0; flash[4] = 0; flash[7] = 0; }
  if (lights[2] == player && lights[5] == player && lights[8] == player) { flash[2] = 0; flash[5] = 0; flash[8] = 0; }
  if (lights[0] == player && lights[4] == player && lights[8] == player) { flash[0] = 0; flash[4] = 0; flash[8] = 0; }
  if (lights[2] == player && lights[4] == player && lights[6] == player) { flash[2] = 0; flash[4] = 0; flash[6] = 0; }
  if (lights[0] == computer && lights[1] == computer && lights[2] == computer) { flash[0] = 0; flash[1] = 0; flash[2] = 0; }
  if (lights[3] == computer && lights[4] == computer && lights[5] == computer) { flash[3] = 0; flash[4] = 0; flash[5] = 0; }
  if (lights[6] == computer && lights[7] == computer && lights[8] == computer) { flash[6] = 0; flash[7] = 0; flash[8] = 0; }
  if (lights[0] == computer && lights[3] == computer && lights[6] == computer) { flash[0] = 0; flash[3] = 0; flash[6] = 0; }
  if (lights[1] == computer && lights[4] == computer && lights[7] == computer) { flash[1] = 0; flash[4] = 0; flash[7] = 0; }
  if (lights[2] == computer && lights[5] == computer && lights[8] == computer) { flash[2] = 0; flash[5] = 0; flash[8] = 0; }
  if (lights[0] == computer && lights[4] == computer && lights[8] == computer) { flash[0] = 0; flash[4] = 0; flash[8] = 0; }
  if (lights[2] == computer && lights[4] == computer && lights[6] == computer) { flash[2] = 0; flash[4] = 0; flash[6] = 0; }
  swapLightsAndFlash();
  refreshDisplay();
  for (int i = 0; i < 20; i++) {
    delay(200);
    swapLightsAndFlash();
    refreshDisplay();
  }
}

void swapLightsAndFlash() {
  int temp[] = {lights[0], lights[1], lights[2], lights[3], lights[4], lights[5], lights[6], lights[7], lights[8]};
  lights[0] = flash[0];
  lights[1] = flash[1];
  lights[2] = flash[2];
  lights[3] = flash[3];
  lights[4] = flash[4];
  lights[5] = flash[5];
  lights[6] = flash[6];
  lights[7] = flash[7];
  lights[8] = flash[8];
  flash[0] = temp[0];
  flash[1] = temp[1];
  flash[2] = temp[2];
  flash[3] = temp[3];
  flash[4] = temp[4];
  flash[5] = temp[5];
  flash[6] = temp[6];
  flash[7] = temp[7];
  flash[8] = temp[8];
}

void computerMove() {
  int bias[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
  int bestMove = -1;
  int topBias = 0;
  // can computer win
  bias[0] = bias[0] + getBiasWin(0, 1, 2);
  bias[0] = bias[0] + getBiasWin(0, 4, 8);
  bias[0] = bias[0] + getBiasWin(0, 3, 6);
  bias[1] = bias[1] + getBiasWin(1, 0, 2);
  bias[1] = bias[1] + getBiasWin(1, 4, 7);
  bias[2] = bias[2] + getBiasWin(2, 0, 1);
  bias[2] = bias[2] + getBiasWin(2, 4, 6);
  bias[2] = bias[2] + getBiasWin(2, 5, 8);
  bias[3] = bias[3] + getBiasWin(3, 0, 6);
  bias[3] = bias[3] + getBiasWin(3, 4, 5);
  bias[4] = bias[4] + getBiasWin(4, 0, 8);
  bias[4] = bias[4] + getBiasWin(4, 1, 7);
  bias[4] = bias[4] + getBiasWin(4, 2, 6);
  bias[4] = bias[4] + getBiasWin(4, 3, 5);
  bias[5] = bias[5] + getBiasWin(5, 2, 8);
  bias[5] = bias[5] + getBiasWin(5, 3, 4);
  bias[6] = bias[6] + getBiasWin(6, 0, 3);
  bias[6] = bias[6] + getBiasWin(6, 2, 4);
  bias[6] = bias[6] + getBiasWin(6, 7, 8);
  bias[7] = bias[7] + getBiasWin(7, 1, 4);
  bias[7] = bias[7] + getBiasWin(7, 6, 8);
  bias[8] = bias[8] + getBiasWin(8, 0, 4);
  bias[8] = bias[8] + getBiasWin(8, 2, 5);
  bias[8] = bias[8] + getBiasWin(8, 6, 7);
  // can computer stop the human from winning
  bias[0] = bias[0] + getBiasStop(0, 1, 2);
  bias[0] = bias[0] + getBiasStop(0, 4, 8);
  bias[0] = bias[0] + getBiasStop(0, 3, 6);
  bias[1] = bias[1] + getBiasStop(1, 0, 2);
  bias[1] = bias[1] + getBiasStop(1, 4, 7);
  bias[2] = bias[2] + getBiasStop(2, 0, 1);
  bias[2] = bias[2] + getBiasStop(2, 4, 6);
  bias[2] = bias[2] + getBiasStop(2, 5, 8);
  bias[3] = bias[3] + getBiasStop(3, 0, 6);
  bias[3] = bias[3] + getBiasStop(3, 4, 5);
  bias[4] = bias[4] + getBiasStop(4, 0, 8);
  bias[4] = bias[4] + getBiasStop(4, 1, 7);
  bias[4] = bias[4] + getBiasStop(4, 2, 6);
  bias[4] = bias[4] + getBiasStop(4, 3, 5);
  bias[5] = bias[5] + getBiasStop(5, 2, 8);
  bias[5] = bias[5] + getBiasStop(5, 3, 4);
  bias[6] = bias[6] + getBiasStop(6, 0, 3);
  bias[6] = bias[6] + getBiasStop(6, 2, 4);
  bias[6] = bias[6] + getBiasStop(6, 7, 8);
  bias[7] = bias[7] + getBiasStop(7, 1, 4);
  bias[7] = bias[7] + getBiasStop(7, 6, 8);
  bias[8] = bias[8] + getBiasStop(8, 0, 4);
  bias[8] = bias[8] + getBiasStop(8, 2, 5);
  bias[8] = bias[8] + getBiasStop(8, 6, 7);
  // add a small random value to each possible move
  for (int i = 0; i < 9; i++) {
    if (lights[i] == 0) {
      bias[i] = bias[i] + random(1, 5);
    }
  }
  // get best move
  for (int i = 0; i < 9; i++) {
    if (bias[i] > topBias) {
      topBias = bias[i];
      bestMove = i;
    }
  }
  // make move
  if (bestMove != -1) {
    lights[bestMove] = computer;
  }
}

int getBiasWin(int thisPosition, int checkPosition1, int checkPosition2) {
  if (lights[thisPosition] == 0 && lights[checkPosition1] == computer && lights[checkPosition2] == computer) {
    return 100;
  }
  else {
    return 0;
  }
}

int getBiasStop(int thisPosition, int checkPosition1, int checkPosition2) {
  if (lights[thisPosition] == 0 && lights[checkPosition1] == player && lights[checkPosition2] == player) {
    return 20;
  }
  else {
    return 0;
  }
}

int checkForWin() {
  int winState = 1;
  // check for player win
  if (lights[0] == player && lights[1] == player && lights[2] == player) { winState = 11; }
  if (lights[3] == player && lights[4] == player && lights[5] == player) { winState = 11; }
  if (lights[6] == player && lights[7] == player && lights[8] == player) { winState = 11; }
  if (lights[0] == player && lights[3] == player && lights[6] == player) { winState = 11; }
  if (lights[1] == player && lights[4] == player && lights[7] == player) { winState = 11; }
  if (lights[2] == player && lights[5] == player && lights[8] == player) { winState = 11; }
  if (lights[0] == player && lights[4] == player && lights[8] == player) { winState = 11; }
  if (lights[2] == player && lights[4] == player && lights[6] == player) { winState = 11; }
  // check for computer win
  if (lights[0] == computer && lights[1] == computer && lights[2] == computer) { winState = 12; }
  if (lights[3] == computer && lights[4] == computer && lights[5] == computer) { winState = 12; }
  if (lights[6] == computer && lights[7] == computer && lights[8] == computer) { winState = 12; }
  if (lights[0] == computer && lights[3] == computer && lights[6] == computer) { winState = 12; }
  if (lights[1] == computer && lights[4] == computer && lights[7] == computer) { winState = 12; }
  if (lights[2] == computer && lights[5] == computer && lights[8] == computer) { winState = 12; }
  if (lights[0] == computer && lights[4] == computer && lights[8] == computer) { winState = 12; }
  if (lights[2] == computer && lights[4] == computer && lights[6] == computer) { winState = 12; }
  // check for draw
  int draw = 1;
  for (int i = 0; i < 9; i++) {
    if (lights[i] == 0) {
      draw = 0;
    }
  }
  if (draw == 1) {
    winState = 13;
  }
  return winState;
}

void clearBoard() {
  for (int i = 0; i < 9; i++) {
    lights[i] = 0;
  }
}

void playLightsOut() {
  int gameState = 1;
  int lit = 0;
  resetBoard();
  while (gameState == 1) {
    button = -1;
    lit = 0;
    refreshDisplay(); 
    button = getButtonPush();
    if (lights[button] == 1) {
      switch (button) {
        case 0:
          lights[0] = abs(lights[0] - 1);
          lights[1] = abs(lights[1] - 1);
          lights[3] = abs(lights[3] - 1);
          lights[4] = abs(lights[4] - 1);
          break;
        case 1:
          lights[0] = abs(lights[0] - 1);
          lights[1] = abs(lights[1] - 1);
          lights[2] = abs(lights[2] - 1);
          break;
        case 2:
          lights[1] = abs(lights[1] - 1);
          lights[2] = abs(lights[2] - 1);
          lights[4] = abs(lights[4] - 1);
          lights[5] = abs(lights[5] - 1);
          break;
        case 3:
          lights[0] = abs(lights[0] - 1);
          lights[3] = abs(lights[3] - 1);
          lights[6] = abs(lights[6] - 1);
          break;
        case 4:
          lights[1] = abs(lights[1] - 1);
          lights[3] = abs(lights[3] - 1);
          lights[4] = abs(lights[4] - 1);
          lights[5] = abs(lights[5] - 1);
          lights[7] = abs(lights[7] - 1);
          break;
        case 5:
          lights[2] = abs(lights[2] - 1);
          lights[5] = abs(lights[5] - 1);
          lights[8] = abs(lights[8] - 1);
          break;
        case 6:
          lights[3] = abs(lights[3] - 1);
          lights[4] = abs(lights[4] - 1);
          lights[6] = abs(lights[6] - 1);
          lights[7] = abs(lights[7] - 1);
          break;
        case 7:
          lights[6] = abs(lights[6] - 1);
          lights[7] = abs(lights[7] - 1);
          lights[8] = abs(lights[8] - 1);
          break;
        case 8:
          lights[4] = abs(lights[4] - 1);
          lights[5] = abs(lights[5] - 1);
          lights[7] = abs(lights[7] - 1);
          lights[8] = abs(lights[8] - 1);
      }
    }
    for (int i = 0; i < 9; i++) {
      if (lights[i] == 1) {
        lit++;
      }
    }
    if (lit == 0) {
      gameState = 0;
      gameSelected = 0;
      delay(200);
      victoryDisplay();
    }
    delay(50);
  }
}

void resetBoard() {
  int lit = 0;
  randomSeed(analogRead(5));
  while (lit < 3 || lit > 5) {
    lit = 0;
    for (int i = 0; i < 9; i++) {
      lights[i] = random(2);
      if (lights[i] == 1) {
        lit++;
      }
    }
  }
}

void victoryDisplay() {
  victoryItem(0, 0, 0, 0, 0, 0, 0, 0, 0);
  for (int i = 0; i < 7; i++) {
    victoryItem(0, 1, 0, 0, 1, 0, 0, 1, 0);
    victoryItem(1, 0, 0, 0, 1, 0, 0, 0, 1);
    victoryItem(0, 0, 0, 1, 1, 1, 0, 0, 0);
    victoryItem(0, 0, 1, 0, 1, 0, 1, 0, 0);
  }
  for (int i = 0; i < 4; i++) {
    victoryItem(2, 2, 2, 0, 0, 0, 0, 0, 0);
    victoryItem(0, 0, 0, 2, 2, 2, 0, 0, 0);
    victoryItem(0, 0, 0, 0, 0, 0, 2, 2, 2);
    victoryItem(0, 0, 0, 2, 2, 2, 0, 0, 0);
  }
  for (int i = 0; i < 4; i++) {
    victoryItem(1, 0, 0, 1, 0, 0, 1, 0, 0);
    victoryItem(0, 1, 0, 0, 1, 0, 0, 1, 0);
    victoryItem(0, 0, 1, 0, 0, 1, 0, 0, 1);
    victoryItem(0, 1, 0, 0, 1, 0, 0, 1, 0);
  }
  for (int i = 0; i < 7; i++) {
    victoryItem(2, 0, 0, 0, 2, 0, 0, 0, 2);
    victoryItem(0, 2, 0, 0, 2, 0, 0, 2, 0);
    victoryItem(0, 0, 2, 0, 2, 0, 2, 0, 0);
    victoryItem(0, 0, 0, 2, 2, 2, 0, 0, 0);
  }
}

void victoryItem(int i0, int i1, int i2, int i3, int i4, int i5, int i6, int i7, int i8) {
  lights[0] = i0;
  lights[1] = i1;
  lights[2] = i2;
  lights[3] = i3;
  lights[4] = i4;
  lights[5] = i5;
  lights[6] = i6;
  lights[7] = i7;
  lights[8] = i8;
  refreshDisplay();
  delay(100);
}

Credits

markbennettuk
4 projects • 19 followers
Contact

Comments

Please log in or sign up to comment.