Tic Tac Toe Game code - W2812B Adressable LED

Hi friends ,

Please, I need some help. I don´t have much experience with Arduino (in this case - Uno). I built a game based in W2812B adressable LED by Robert Polzer - 3x3 Games. It is a very nice tic tac toe game. It is possible play just the game - human x computer. I´d like to include in this game (code) also a second option : Human x Human. Thanks in advance.

#include <FastLED.h>
#include <Keypad.h>

// Definitions
#define DATA_PIN 2           // Data pin in Arduino
#define NUM_LEDS 9           // Set number of LEDs
#define BRIGHTNESS 255       // Ribbon Brightness (0-255)
const byte ROWS = 3;         // three lines
const byte COLS = 3;         // three columns

byte rowPins[ROWS] = { 9, 8, 7 };     // Connect the COL0, COL1, and COL2 keyboard to these Arduino pins.
byte colPins[COLS] = { 12, 11, 10 };
    
CRGB leds[NUM_LEDS];         // Array with RGB color values of individual LEDs
int field[NUM_LEDS];         // Matrix with hue values of individual LEDs (-1 = off, -2 = white)
byte key = 0;                // Key pressed

#define SPEED_V 1            // Victory Screen Speed

byte keys[ROWS][COLS] = {
  {1,2,3},
  {4,5,6},
  {7,8,9},
};

// Create Keyboard
Keypad kpd = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS );

//******************

void setup() {

  FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS); 
    playVictory();    

//******************

void loop() {

  playTicTacToe();
}

//******************
//      TIC TAC TOE GAME
//******************

void playTicTacToe() {  

  //Sprachausgabe "TicTacToe"
  
  int player = 2;  // START : 1 = HUMAN - 2 = COMPUTER 
  
  do {             // Loop as long as you want to continue
  
    clearField();  // Delete display
    showField();
    
    int board[] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; // create empty playing field
     
    for(int turn=0; turn < 9 && win(board) == 0; ++turn) {   // maximum 9 moves, as long as nobody has won yet
      if((turn + player)%2 == 0) {             // Computer is on the train
        
        if(turn != 0) {                        // not the first move -> calculate
          computerMove(board);
        }
        else {                                 // first move -> random field
          randomSeed(millis());
          board[random(0,9)] = 1;
        }
      }
      else {                                   // Player is on the move
        drawboard(board);
        playerMove(board);
      }
    }

//----------------
    
    switch(win(board)) {        // who won?
      case 0:                   // draw
        drawboard(board);
        delay(3000);
        break;
      case 1:                   // Computer won
        showWinner(board); 
        delay(3000);
        break;
      case -1:                  // Player has won
        playVictory();
        showWinner(board);
        delay(3000);
        break;
    }
    player = abs(player - 3);   // change of the first player

  }
  while (playAgain() == true);  // Once again ?
}

//**************
// COMPUTER MOVE
//**************

void computerMove(int board[]) 

{
  int moveval=-1;
  int score=-2;
  int i;
  for(i=0;i<9;++i)
  {
    if(board[i]==0)
    {
      board[i]=1;
      int tempScore=-minimax(board,-1);
      board[i]=0;
      if(tempScore>score)
      {
        score=tempScore;
        moveval=i;          // The field is assigned to the computer.
      }
    }
  }
  //  returns a score based on the minimax tree on a given node.

  board[moveval]=1;
}

//***********
// HUMAN MOVE
//***********

void playerMove(int board[]) {
  
  int moveval=0,flag=0;

    while (moveval == 0) {    // wait at the touch of a button
    moveval = kpd.getKey();
  }
  moveval--;                  // map buttons 1-9 to 0-8
  
  if(board[moveval]== 1 || board[moveval]== -1)
  {
    playerMove(board);        // Field already busy
  }
  else if(board[moveval]==0)
  {
    board[moveval]= -1;        // The field is assigned to the players.
  }
  drawboard(board);
}

//***********
//  WIN
//***********  
int win(int board[]) {
  
  // Definition of profit situations

  int wins[][8] = {{0,1,2},{3,4,5},{6,7,8},{0,3,6},{1,4,7},{2,5,8},{0,4,8},{2,4,6}};
  for(int i=0;i<8;++i) {   
   
    if(board[wins[i][0]] != 0 && board[wins[i][0]] == board[wins[i][1]] && board[wins[i][0]] == board[wins[i][2]])
      return board[wins[i][2]];  // one player has gained feedback, otherwise "0"
  }
  return 0;
}

//-----------------

int minimax(int board[],int player) {
  
  int winner=win(board);
  if(winner!=0)
    return winner*player;

  int moveval=-1;
  int score=-2;                  // Lost moves are preferred to no moves.
  for(int i=0;i<9;++i)
  {
    if(board[i]==0)
    {
      board[i]=player;          // Experience the change
      int thisScore=-minimax(board,player*-1);
      if(thisScore>score)
      {
        score=thisScore;
        moveval=i;
      }                       // Choose the worst for the opponent
      
      board[i]=0;             // Reset card after attempt
    }
  }
  if(moveval==-1) return 0;
  
  return score;
}

//************
//  Draw Board
//************
void drawboard(int board[]) {
  
  clearField();
  for (int i=0; i < NUM_LEDS; i++) {
    switch(board[i]) 
    
    {
      case 0:
        field[i] = -1;     // the empty field is black
        break;
      case -1:
        field[i] = 160;    // BLUE - PLAYER
        break;
      case 1:
        field[i] = 0;      // RED - COMPUTER
        break;
    }
  }
  showField();  
}

//************
// Show Winner
//************

void showWinner(int board[]) {
  
  clearField();

  int wins[][8] = {{0,1,2},{3,4,5},{6,7,8},{0,3,6},{1,4,7},{2,5,8},{0,4,8},{2,4,6}};
  for(int i=0;i<8;++i) {   // consult all 8 profit situations
   
    if(board[wins[i][0]] != 0 && board[wins[i][0]] == board[wins[i][1]] && board[wins[i][0]] == board[wins[i][2]]) {
      for(int j=0;j<3;j++) {
        switch(board[wins[i][j]]) {
         
          case -1:
            field[wins[i][j]] = 160;
            break;
          case 1:
            field[wins[i][j]] = 0;
            break;
        }
      }
      showField();
      break;
    }
  }
}

// ********
// Clear Field
//*********
void clearField() {
  
  for (int i=0; i < NUM_LEDS; i++) {
    field[i] = -1;
  }
}

//**********
// Show Field
//**********

// Output all fields on LED display
void showField() {   
  
  // Assign field color to individual LEDs

  for (int i=0; i < NUM_LEDS; i++) {
    if (field[i] >= 0) {
      leds[i] = CHSV(field[i], 255, BRIGHTNESS); // Tint "color" when active
    }
    else 
    {
      if (field[i] == -1) {
        leds[i] = CRGB::Black;                   // black if -1
      }  
      else 
      {
        leds[i] = CRGB::White;                   // 
      }
    }
  }
  FastLED.show();                                // activate the LEDs
}

//*************
// Dimmout Display
//*************

void dimmoutDisplay() {

  for (int hell=255; hell >= 0; hell--) {
    
    for (int i=0; i < NUM_LEDS; i++) {
      if (field[i] >= 0) {
        leds[i] = CHSV(field[i], 255, hell); // Tint "color" when active
      }
      else {
        if (field[i] == -1) {
          leds[i] = CRGB::Black;                   // black if -1
        }  
        else {
          leds[i] = CHSV(0, 0, hell);              // 
        }
      }
    }
    FastLED.show();                                // activate the LEDs
    delay(5);
  }  
}

//************
// Multi-Color Field
//************

void playVictory() {  
  
  int i, j;
  
  for (j=0; j < 256*5 ; j++) {      // a total of 5 passes
    for (i=0; i < NUM_LEDS; i++) 
    
    {
      leds[i] = CHSV(((i * 256 / NUM_LEDS) + j) % 256, 255, BRIGHTNESS);
    } 
       
    FastLED.show();  
    delay(SPEED_V);
  }

  clearField();  
  delay(500);
  showField();  
}

//************
// Play it Again ???
//************

boolean playAgain() {
  
  clearField();
  field[0] = 0;
  field[8] = 96;
  showField();
  
  // Interrogated Keys (1-9)
  byte key = 0;                     // wait at the touch of a button
  while (key != 1  && key != 9) {   // until key 1 or 9 is pressed
    key = kpd.getKey();
  }
  
  if(key == 1) {
    dimmoutDisplay();
    return false;

  }
  dimmoutDisplay();  
  return true;
}

Okay, so what have you tried?

This is a forum for help to learn programming. Not to edit code on request.

The function used to input the player's answer is playerMove()

In playTicTacToe(), these lines are for the computer's turn:

        if(turn != 0) {                        // not the first move -> calculate
          computerMove(board);
        }
        else {                                 // first move -> random field
          randomSeed(millis());
          board[random(0,9)] = 1;
        }

You should consider changing these lines with a call to playerMove(), but you'll need to somehow take into account that the second player is different from the first player (assign different colors to different players)

OK, Septillion , I am sorry, maybe I did not clear. I have changed some lines but it did not work. The reason of my post was if someone could give me a "way" , I mean, which lines I should change. Anyway Lesept gave me a way to make some changes. Thanks Lesept for reply an as soon I get it, I will post here.

tic_tac_toe_leds.jpg

Your image (nice):
tic_tac_toe_leds.jpg

Many thanks Lesept ! I built it to play with my daughter but it is too hard to play against computer, then my ideia is make this change to become this nice game easier to play (human x human). I hope to get it in the next days.

Good luck, and come back if you need help. I'm not often on this part of the forum, so PM me if you need

Looking at the code quickly, it seems to set the board values to -1 for human and 1 for computer. It might be as simple as passing that value into playerMove().

    for(int turn=0; turn < 9 && win(board) == 0; ++turn) {   // maximum 9 moves, as long as nobody has won yet
                                  // Player is on the move
        int val = (turn%2) ? 1 : -1;  // change board value based on turn
        drawboard(board);
        playerMove(board, val);
      }
    }

Then set the board value to whatever is passed in.

void playerMove(int board[], int val) {
 
  int moveval=0,flag=0;

    while (moveval == 0) {    // wait at the touch of a button
    moveval = kpd.getKey();
  }
  moveval--;                  // map buttons 1-9 to 0-8
 
  if(board[moveval]== 1 || board[moveval]== -1)
  {
    playerMove(board);        // Field already busy
  }
  else if(board[moveval]==0)
  {
    board[moveval]= val;        // used passed value for board
  }
  drawboard(board);
}

Or it might not be that simple, but that's the direction I would take trying to change it.

Hi Blue Eyes !
Next weekend on my free time I will try this direction too. Anyway, I thank you for your attention. I hope to give good news here in the next post.

Hi !
I want to tell that finally the Tic Tac Toe Game is working very well. After "tips" from Blue Eyes and Lesept the Tic Tac Toe game has both options : Human x Human and Human x Computer. I am using a momentary latching switch to choose wich mode I can play .Thank you very much for your help.