#include <iostream>
#include <vector>
#include <ShiftRegister74HC595.h>
using namespace std;
#define BUTTON1_PIN 12 // esp pin connected to the button
#define BUTTON2_PIN 33 // esp pin connected to the button
// Joystick pins
#define JOY1_X_PIN 27
#define JOY1_Y_PIN 14
#define JOY2_X_PIN 26
#define JOY2_Y_PIN 25
// Two shift register objects for 8 leds
ShiftRegister74HC595 < 1 > redLeds(23, 19, 21); // same latch pins output signal will be sent to one at one time
ShiftRegister74HC595 < 1 > blueLeds(5, 15, 21);
// The last LED connected to the board directly
const int redLed8Pin = 4;
const int greenLed8Pin = 2;
int x1Value = 0; // To store value of the X axis
int y1Value = 0; // To store value of the Y axis
int x2Value = 0; // To store value of the X axis
int y2Value = 0; // To store value of the Y axis
bool isButton1Pressed;
bool isButton2Pressed;
bool xFields[9] = {false, false, false, false, false, false, false, false, false};
bool oFields[9] = {false, false, false, false, false, false, false, false, false};
char board[3][3] = {{' ', ' ', ' '}, {' ', ' ', ' '}, {' ', ' ', ' '}};
void lightLeds();
bool checkWinner(char board[][3], char player);
vector < pair < int, int >> checkWinningCoordinates();
bool isBoardFull(char board[][3]);
void flashLeds(pair < int, int > coordinates[]);
void player1Movement();
void player2Movement();
void resetGame();
int selectedLED = 0;
bool currentPlayer = true;
void lightLeds() {
// Display the selected LED by lighting up the corresponding shift register output
for (int i = 0; i < 9; i++) {
xFields[i] = false;
oFields[i] = false;
int row = i / 3;
int col = i % 3;
if (board[row][col] == 'X') {
xFields[i] = true;
} else if (board[row][col] == 'O') {
oFields[i] = true;
} else if (board[row][col] == '!') {
xFields[i] = true;
oFields[i] = true;
}
if (i == 8) {
break;
}
if (xFields[i]) {
blueLeds.set(i, HIGH);
} else {
blueLeds.set(i, LOW);
}
if (oFields[i]) {
redLeds.set(i, HIGH);
} else {
redLeds.set(i, LOW);
}
}
if (xFields[8]) {
digitalWrite(greenLed8Pin, HIGH);
} else {
digitalWrite(greenLed8Pin, LOW);
}
if (oFields[8]) {
digitalWrite(redLed8Pin, HIGH);
} else {
digitalWrite(redLed8Pin, LOW);
}
}
bool checkWinner(char board[][3], char player) {
for (int row = 0; row < 3; row++) {
if (board[row][0] == board[row][1] && board[row][0] == board[row][2] && board[row][0] == player) {
return true;
}
}
for (int col = 0; col < 3; col++) {
if (board[0][col] == board[1][col] && board[0][col] == board[2][col] && board[0][col] == player) {
return true;
}
}
if (board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] == player) {
return true;
}
if (board[0][2] == board[1][1] && board[0][2] == board[2][0] && board[0][2] == player) {
return true;
}
return false;
}
vector < pair < int, int >> checkWinningCoordinates() {
vector < pair < int, int >> coordinates;
//checking for row combinations a.b.c d.e.f g.h.i
for (int row = 0; row < 3; row++) {
if (board[row][0] == board[row][1] && board[row][0] == board[row][2] && board[row][0] != ' '){
for (int i = 0; i < 3; i++) {
coordinates.push_back(make_pair(row, i));
}
return coordinates;
}
}
//checking for column combinations a.d.g b.e.h c.f.i
for (int col = 0; col < 3; col++) {
if (board[0][col] == board[1][col] && board[0][col] == board[2][col] && board[0][col] != ' ') {
for (int i = 0; i < 3; i++) {
coordinates.push_back(make_pair(i, col));
}
return coordinates;
}
}
//checking for right diagonal a.e.i
if (board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] != ' ') {
for (int i = 0; i < 3; i++) {
coordinates.push_back(make_pair(i, i));
}
return coordinates;
}
//checking for left diagonal c.e.i
if (board[0][2] == board[1][1] && board[0][2] == board[2][0] && board[0][2] != ' ') {
for (int i = 0; i < 3; i++) {
coordinates.push_back(make_pair(i, 2 - i));
}
return coordinates;
}
return coordinates;
}
//if the board is full and
bool isBoardFull(char board[][3]) {
for (int i = 0; i < 9; i++) {
int row = i / 3;
int col = i % 3;
if (board[row][col] == ' ') {
return false;
}
else
return true;
}
}
//flashing the winning combination
void flashLeds(vector < pair < int, int >> coordinates) {
char signs[3];
for (int i = 0; i < coordinates.size(); i++) {
signs[i] = board[coordinates[i].first][coordinates[i].second];
board[coordinates[i].first][coordinates[i].second] = '!';
}
lightLeds();
delay(250);
for (int i = 0; i < coordinates.size(); i++) {
board[coordinates[i].first][coordinates[i].second] = signs[i];
}
lightLeds();
delay(250);
resetGame();
}
void player1Movement() {
// read analog X and Y analog values for Player 1
x1Value = analogRead(JOY1_X_PIN);
y1Value = analogRead(JOY1_Y_PIN);
// Check if the button is pressed for Player 1
isButton1Pressed = digitalRead(BUTTON1_PIN) == LOW;
// Player1 flags
if (isButton1Pressed) {
Serial.println("Mid");
xFields[4] = 1;
selectedLED = 4;
delay(200);
} else {
if (y1Value > 3000) {
Serial.println("Left");
xFields[3] = 1;
selectedLED = 3;
} else if (y1Value < 1000) {
Serial.println("Right");
xFields[5] = 1;
selectedLED = 5;
}
if (x1Value > 3000) {
Serial.println("Up");
xFields[2] = 1;
selectedLED = 1;
} else if (x1Value < 1000) {
Serial.println("Down");
xFields[7] = 1;
selectedLED = 7;
}
if (x1Value > 3000 && y1Value < 1000) {
Serial.println("Up/Right");
xFields[8] = 1;
selectedLED = 2;
} else if (x1Value < 1000 && y1Value < 1000) {
Serial.println("Down/Right");
xFields[2] = 1;
selectedLED = 8;
} else if (x1Value > 3000 && y1Value > 3000) {
Serial.println("Up/Left");
xFields[0] = 1;
selectedLED = 0;
} else if (x1Value < 1000 && y1Value > 3000) {
Serial.println("Down/Left");
xFields[6] = 1;
selectedLED = 6;
}
delay(200);
}
}
void player2Movement() {
// read analog X and Y analog values for Player 2
x2Value = analogRead(JOY2_X_PIN);
y2Value = analogRead(JOY2_Y_PIN);
// Check if the button is pressed for Player 2
isButton2Pressed = digitalRead(BUTTON2_PIN) == LOW;
// Player2 flags
if (isButton2Pressed) {
Serial.println("Mid");
oFields[4] = 1;
selectedLED = 4;
delay(200);
} else {
if (y2Value > 3000) {
Serial.println("Up");
oFields[2] = 1;
selectedLED = 1;
} else if (y2Value < 1000) {
Serial.println("Down");
oFields[7] = 1;
selectedLED = 7;
}
if (x2Value > 3000) {
Serial.println("Left");
oFields[3] = 1;
selectedLED = 3;
} else if (x2Value < 1000) {
Serial.println("Right");
oFields[5] = 1;
selectedLED = 5;
}
if (x2Value > 3000 && y2Value < 1000) {
Serial.println("Down/Left");
oFields[6] = 1;
selectedLED = 6;
} else if (x2Value < 1000 && y2Value < 1000) {
Serial.println("Down/Right");
oFields[2] = 1;
selectedLED = 8;
} else if (x2Value > 3000 && y2Value > 3000) {
Serial.println("Up/Left");
oFields[0] = 1;
selectedLED = 0;
} else if (x2Value < 1000 && y2Value > 3000) {
Serial.println("Up/Right");
oFields[8] = 1;
selectedLED = 2;
}
delay(200);
}
}
void resetGame() {
// Clear the board
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
board[i][j] = ' ';
}
}
}
void setup() {
pinMode(JOY1_X_PIN, INPUT);
pinMode(JOY1_Y_PIN, INPUT);
pinMode(JOY2_X_PIN, INPUT);
pinMode(JOY2_Y_PIN, INPUT);
pinMode(BUTTON1_PIN, INPUT_PULLUP);
pinMode(BUTTON2_PIN, INPUT_PULLUP);
pinMode(redLed8Pin, OUTPUT);
pinMode(greenLed8Pin, OUTPUT);
redLeds.setAllLow();
blueLeds.setAllLow();
Serial.begin(9600);
}
void loop() {
if (currentPlayer) {
player1Movement(); // Call Player 1 movement function
if (board[selectedLED / 3][selectedLED % 3] == ' ') {
board[selectedLED / 3][selectedLED % 3] = 'X';
}
lightLeds();
} else {
player2Movement(); // Call Player 2 movement function
if (board[selectedLED / 3][selectedLED % 3] == ' ') {
board[selectedLED / 3][selectedLED % 3] = 'O';
}
lightLeds();
if (checkWinner(board, 'O')) {
flashLeds(checkWinningCoordinates());
}
}
// Switch turns
currentPlayer = !currentPlayer;
if (checkWinner(board, 'X')) {
flashLeds(checkWinningCoordinates());
} else if (isBoardFull(board)) {
resetGame();
}
delay(200);
}