Previously in Arduino Application Development Procedure Part-4, I provided commentary on my code for taking turns between players in Tic-Tac-Toe. In this stage, I will provide the finished code and some commentary if needed. Figuring out winning wasn’t as difficult as figuring out how to reset the game at the end. Again, I will be providing sections of code for readability and will provide a zipped folder of the finished code. Even though this final program works, in the end, I added a few more features but kept a majority of the code.
Section One: Global Variables
... //I am just shortening the initial code in this snippet, everything below flagTurn9P1 is new, nothing else changed
boolean flagTurn1P1 = 0; //variable for turn1 player1, will help with logic so I don't override past turns
boolean flagTurn2P2 = 0; //you can only ever have 9 turns max in a game of tic-tac-toe, need to keep track of who took a turn and where it took place; aslo specific turns exist for specific players in paper tic-tac-toe, so I named them appropriately
boolean flagTurn3P1 = 0;
boolean flagTurn4P2 = 0;
boolean flagTurn5P1 = 0;
boolean flagTurn6P2 = 0;
boolean flagTurn7P1 = 0;
boolean flagTurn8P2 = 0;
boolean flagTurn9P1 = 0;
//new lines below:
boolean p1Win = 0; //flag for checking if p1 wins
boolean p2Win = 0; //flag for checking if p2 wins
boolean gameDraw = 0; //flag for checking if there is a draw
int movePossibleWinsCt = 0; //count for possible wins
int randSeq = 0; //variable for selecting which sequence plays if a player wins
int seqCt = 0; //variable for making the sequence only repeat a few times
Section Two: Main Loop
Nothing changed in the setup code, I still kept Serial.begin(9600) included because it was important to debugging.
//changed the first if statement to include p1Win, p2Win, and game draw flags for resetting purposes
if (ttt1[0][0] == 0 && ttt1[0][1] == 0 && ttt1[0][2] == 0 && ttt1[1][0] == 0 && ttt1[1][1] == 0 && ttt1[1][2] == 0 && ttt1[2][0] == 0 && ttt1[2][1] == 0 && ttt1[2][2] == 0 && lightCount == 0 && playerSw == 0 && p1Win == 0 && p2Win == 0 && gameDraw == 0) {
updateMatrix(); //update the appropriate 3x3 matrix by reading the input for buttons for all positions, see updateMatrix function below
randSeq = random(1, 5); //generate a new random number as long as ttt1 is empty
startSeq();
} else { //as soon as one of the values changes the game has begun
if (lightCount == 0) { //run the update light FCN once because player one will have made a move, check if no lights are lit yet to run the first function, see updateFirstMove below
updateFirstMove();
} else if (lightCount == 1 && flagTurn2P2 != 1 && playerSw == 1) { //check if the light count is 1, make sure the flag for Player 2 for this turn is not set yet and make sure the right player is playing; 1RED ON
buttonPressAndMatrixUp(playerSw, lightCount); //check if buttons are pressed, update appropriate matrices, uses current player and lightCount as arguments, see function below
randSeq = random(1, 5); //generate a new random number if light count = 1, do this before one person can win each time
} else if (lightCount == 2 && flagTurn3P1 != 1 && playerSw == 0) { //each turn has a pattern, check current light count, make sure appropriate turn flag is not set yet, and make sure right player is playing; 1RED 1BLUE ON 2TOT
buttonPressAndMatrixUp(playerSw, lightCount);
randSeq = random(1, 5);
} else if (lightCount == 3 && flagTurn4P2 != 1 && playerSw == 1) { //2RED 1BLUE 3TOT
buttonPressAndMatrixUp(playerSw, lightCount);
randSeq = random(1, 5);
} else if (lightCount == 4 && flagTurn5P1 != 1 && playerSw == 0) { //2RED 2BLUE 4TOT
buttonPressAndMatrixUp(playerSw, lightCount);
randSeq = random(1, 5);
} else if (lightCount == 5 && flagTurn6P2 != 1 && playerSw == 1) { //this is when player 1 may have made a move to win; 3RED 2BLUE 5TOT
//NEW CODE BELOW
p1Win = checkP1Win(); //after count updated to 5, it is possible a player may have one, update p1Win flag
if (p1Win == 0) { //if they haven't, continue the regular sequence
buttonPressAndMatrixUp(playerSw, lightCount);
} else if (p1Win == 1) { //check if the first player won
turnAllLEDOff(); //turn all the LEDs off (blue and red)
playerSw = 0; //set the right player (last turn switches the player)
if (randSeq == 1) { //check which random number 1-4 randSeq last equaled
while (seqCt < 3) { //only repeat the sequence three times
winSeq1(playerSw); //each win sequence takes the player who won to change light colors, I just made up four different sequences, see below function for reference
seqCt++; //increase the seq count by 1
}
if (seqCt == 3) { //after the sequence finishes (seqCt will equal 3), check if any button is left pressed via checkPressedEnd function
while (checkPressedEnd()) { //as long as something is still pressed, the following functions are called
checkMatrixAndReset(ttt1, 0); //checks ttt1 to see if there are still values that equal 1
checkMatrixAndReset(ttt2, 1); //checks ttt2
}
resetGame(); //function that only works if every light is off and no matrix positions equal 1
}
} else if (randSeq == 2) { //each random sequence gets the same code repeated above, even draw conditions seen below get the same code
while (seqCt < 3) {
winSeq2(playerSw);
seqCt++;
}
if (seqCt == 3) {
while (checkPressedEnd()) {
checkMatrixAndReset(ttt1, 0);
checkMatrixAndReset(ttt2, 1);
}
resetGame();
}
} else if (randSeq == 3) {
while (seqCt < 3) {
winSeq3(playerSw);
seqCt++;
}
if (seqCt == 3) {
while (checkPressedEnd()) {
checkMatrixAndReset(ttt1, 0);
checkMatrixAndReset(ttt2, 1);
}
resetGame();
}
} else if (randSeq == 4) {
while (seqCt < 3) {
winSeq4(playerSw);
seqCt++;
}
if (seqCt == 3) {
while (checkPressedEnd()) {
checkMatrixAndReset(ttt1, 0);
checkMatrixAndReset(ttt2, 1);
}
resetGame();
}
}
}
} else if (lightCount == 6 && flagTurn7P1 != 1 && playerSw == 0) { //3RED 3BLUE 6TOT
p2Win = checkP2Win(); //check if the next player wins
if (p2Win == 0) { //if not, keep doing regular sequence
buttonPressAndMatrixUp(playerSw, lightCount);
} else if (p2Win == 1) { //if win, do something
turnAllLEDOff();
playerSw = 1;
if (randSeq == 1) {
while (seqCt < 3) {
winSeq1(playerSw);
seqCt++;
}
if (seqCt == 3) {
while (checkPressedEnd()) {
checkMatrixAndReset(ttt1, 0);
checkMatrixAndReset(ttt2, 1);
}
resetGame();
}
} else if (randSeq == 2) {
while (seqCt < 3) {
winSeq2(playerSw);
seqCt++;
}
if (seqCt == 3) {
while (checkPressedEnd()) {
checkMatrixAndReset(ttt1, 0);
checkMatrixAndReset(ttt2, 1);
}
resetGame();
}
} else if (randSeq == 3) {
while (seqCt < 3) {
winSeq3(playerSw);
seqCt++;
}
if (seqCt == 3) {
while (checkPressedEnd()) {
checkMatrixAndReset(ttt1, 0);
checkMatrixAndReset(ttt2, 1);
}
resetGame();
}
} else if (randSeq == 4) {
while (seqCt < 3) {
winSeq4(playerSw);
seqCt++;
}
if (seqCt == 3) {
while (checkPressedEnd()) {
checkMatrixAndReset(ttt1, 0);
checkMatrixAndReset(ttt2, 1);
}
resetGame();
}
}
}
} else if (lightCount == 7 && flagTurn8P2 != 1 && playerSw == 1) { //4RED 3BLUE 7TOT
p1Win = checkP1Win(); //again check if next player won
movePossibleWinsCt = checkPlayerCanMoveToWin(lightCount, playerSw); //also check if any moves can be played next turn that results in a win
if (p1Win == 0 && movePossibleWinsCt > 0) { //if there are moves to win by either player and p1 hasn't won, continue the sequence
buttonPressAndMatrixUp(playerSw, lightCount);
} else if (p1Win == 1) { //if the player1 wins, do something
turnAllLEDOff();
playerSw = 0;
if (randSeq == 1) {
while (seqCt < 3) {
winSeq1(playerSw);
seqCt++;
}
if (seqCt == 3) {
while (checkPressedEnd()) {
checkMatrixAndReset(ttt1, 0);
checkMatrixAndReset(ttt2, 1);
}
resetGame();
}
} else if (randSeq == 2) {
while (seqCt < 3) {
winSeq2(playerSw);
seqCt++;
}
if (seqCt == 3) {
while (checkPressedEnd()) {
checkMatrixAndReset(ttt1, 0);
checkMatrixAndReset(ttt2, 1);
}
resetGame();
}
} else if (randSeq == 3) {
while (seqCt < 3) {
winSeq3(playerSw);
seqCt++;
}
if (seqCt == 3) {
while (checkPressedEnd()) {
checkMatrixAndReset(ttt1, 0);
checkMatrixAndReset(ttt2, 1);
}
resetGame();
}
} else if (randSeq == 4) {
while (seqCt < 3) {
winSeq4(playerSw);
seqCt++;
}
if (seqCt == 3) {
while (checkPressedEnd()) {
checkMatrixAndReset(ttt1, 0);
checkMatrixAndReset(ttt2, 1);
}
resetGame();
}
}
} else if (movePossibleWinsCt == 0) { //if there are no possible wins, do something for a draw, 7 moves it is possible to determine if there are moves left
gameDraw = 1; //update gameDraw flag to 1, will do something with this in the "reset" phase
turnAllLEDOff(); //turn all the LEDs off
while (seqCt < 3) {
drawSeq(); //special sequence of lights for a draw
seqCt++;
}
if (seqCt == 3) {
while (checkPressedEnd()) {
checkMatrixAndReset(ttt1, 0);
checkMatrixAndReset(ttt2, 1);
}
resetGame();
}
}
} else if (lightCount == 8 && flagTurn9P1 != 1 && playerSw == 0) { //4RED 4BLUE 8TOT
p2Win = checkP2Win(); //check if next player won
movePossibleWinsCt = 0; //reset movePossibleWinsCt back to 0 (may have changed above)
movePossibleWinsCt = checkPlayerCanMoveToWin(lightCount, playerSw); //check if possible after 8 lights if one can win
if (p2Win == 0 && movePossibleWinsCt > 0) { //if p2 still hasn't won and it is possible for p1 to win, continue sequence
buttonPressAndMatrixUp(playerSw, lightCount);
} else if (p2Win == 1) { //do something if p2 wins
turnAllLEDOff();
playerSw = 1;
if (randSeq == 1) {
while (seqCt < 3) {
winSeq1(playerSw);
seqCt++;
}
if (seqCt == 3) {
while (checkPressedEnd()) {
checkMatrixAndReset(ttt1, 0);
checkMatrixAndReset(ttt2, 1);
}
resetGame();
}
} else if (randSeq == 2) {
while (seqCt < 3) {
winSeq2(playerSw);
seqCt++;
}
if (seqCt == 3) {
while (checkPressedEnd()) {
checkMatrixAndReset(ttt1, 0);
checkMatrixAndReset(ttt2, 1);
}
resetGame();
}
} else if (randSeq == 3) {
while (seqCt < 3) {
winSeq3(playerSw);
seqCt++;
}
if (seqCt == 3) {
while (checkPressedEnd()) {
checkMatrixAndReset(ttt1, 0);
checkMatrixAndReset(ttt2, 1);
}
resetGame();
}
} else if (randSeq == 4) {
while (seqCt < 3) {
winSeq4(playerSw);
seqCt++;
}
if (seqCt == 3) {
while (checkPressedEnd()) {
checkMatrixAndReset(ttt1, 0);
checkMatrixAndReset(ttt2, 1);
}
resetGame();
}
}
} else if (movePossibleWinsCt == 0) { //if no possible moves to win, do somehting for a draw
gameDraw = 1;
turnAllLEDOff();
while (seqCt < 3) {
drawSeq();
seqCt++;
}
if (seqCt == 3) {
while (checkPressedEnd()) {
checkMatrixAndReset(ttt1, 0);
checkMatrixAndReset(ttt2, 1);
}
resetGame();
}
}
} else if (lightCount == 9) { //last possible light count to check
p1Win = checkP1Win(); //again, someone might make a mistake by chance, check if p1Wins
if (p1Win == 0) { //if p1 doesn't win by mistake, set movePossibleWinsCt to 0
movePossibleWinsCt = 0;
} else if (p1Win == 1) { //do something if p1 wins which is most likely to happen
turnAllLEDOff();
playerSw = 0;
if (randSeq == 1) {
while (seqCt < 3) {
winSeq1(playerSw);
seqCt++;
}
if (seqCt == 3) {
while (checkPressedEnd()) {
checkMatrixAndReset(ttt1, 0);
checkMatrixAndReset(ttt2, 1);
}
resetGame();
}
} else if (randSeq == 2) {
while (seqCt < 3) {
winSeq2(playerSw);
seqCt++;
}
if (seqCt == 3) {
while (checkPressedEnd()) {
checkMatrixAndReset(ttt1, 0);
checkMatrixAndReset(ttt2, 1);
}
resetGame();
}
} else if (randSeq == 3) {
while (seqCt < 3) {
winSeq3(playerSw);
seqCt++;
}
if (seqCt == 3) {
while (checkPressedEnd()) {
checkMatrixAndReset(ttt1, 0);
checkMatrixAndReset(ttt2, 1);
}
resetGame();
}
} else if (randSeq == 4) {
while (seqCt < 3) {
winSeq4(playerSw);
seqCt++;
}
if (seqCt == 3) {
while (checkPressedEnd()) {
checkMatrixAndReset(ttt1, 0);
checkMatrixAndReset(ttt2, 1);
}
resetGame();
}
}
} else if (movePossibleWinsCt == 0) { //again, it is possible to draw if someone made a mistake
gameDraw = 1;
turnAllLEDOff();
while (seqCt < 3) {
drawSeq();
seqCt++;
}
if (seqCt == 3) {
while (checkPressedEnd()) {
checkMatrixAndReset(ttt1, 0);
checkMatrixAndReset(ttt2, 1);
}
resetGame();
}
}
}
}
}
My only comment on this lengthy section is I could have made it much shorter by using better functions. However, sometimes turning certain repeated code into functions is a difficult process in testing and debugging. I would run into unusual problems where certain variables would not get updated and some loops broke without me knowing what broke them. The hardest bug to fix is one that you don’t know what is causing it.
Section Three: Additional Functions
Everything after the “turnLightUp()” function was new, so I just copied the code I have below.
//simple function that returns true or false (1 or 0) if p1 wins, doesn't accept any input parameters
boolean checkP1Win() {
//setup variables for rows and columns and local variable for setting if winning
int r1 = 0;
int r2 = 1;
int r3 = 2;
int c1 = 0;
int c2 = 1;
int c3 = 2;
boolean p1WinFlag = 0;
//the if statement below literally just checks all the valid ways you can win at TIC-TAC-TOE; 3 lights are required and the || (or symbol) separates each three light check; parenthesis between the || are REQUIRED
if ((ttt1[r1][c1] == 1 && ttt1[r1][c2] == 1 && ttt1[r1][c3] == 1) || (ttt1[r2][c1] == 1 && ttt1[r2][c2] == 1 && ttt1[r2][c3] == 1) || (ttt1[r3][c1] == 1 && ttt1[r3][c2] == 1 && ttt1[r3][c3] == 1) || (ttt1[r1][c1] == 1 && ttt1[r2][c1] == 1 && ttt1[r3][c1] == 1) || (ttt1[r1][c2] == 1 && ttt1[r2][c2] == 1 && ttt1[r3][c2] == 1) || (ttt1[r1][c3] == 1 && ttt1[r2][c3] == 1 && ttt1[r3][c3] == 1) || (ttt1[r1][c1] == 1 && ttt1[r2][c2] == 1 && ttt1[r3][c3] == 1) || (ttt1[r1][c3] == 1 && ttt1[r2][c2] == 1 && ttt1[r3][c1] == 1)) {
p1WinFlag = 1; //update the p1WinFlag to 1
return p1WinFlag; //return the value of p1WinFlag (will need a variable in the main loop to set it equal to
} else { //if none of the valid win conditions are met, return the default value of p1WinFlag : 0
return p1WinFlag;
}
}
//this function is the same as the previous one, it just checks ttt2 because it belongs to player 2
boolean checkP2Win() {
int r1 = 0;
int r2 = 1;
int r3 = 2;
int c1 = 0;
int c2 = 1;
int c3 = 2;
boolean p2WinFlag = 0;
if ((ttt2[r1][c1] == 1 && ttt2[r1][c2] == 1 && ttt2[r1][c3] == 1) || (ttt2[r2][c1] == 1 && ttt2[r2][c2] == 1 && ttt2[r2][c3] == 1) || (ttt2[r3][c1] == 1 && ttt2[r3][c2] == 1 && ttt2[r3][c3] == 1) || (ttt2[r1][c1] == 1 && ttt2[r2][c1] == 1 && ttt2[r3][c1] == 1) || (ttt2[r1][c2] == 1 && ttt2[r2][c2] == 1 && ttt2[r3][c2] == 1) || (ttt2[r1][c3] == 1 && ttt2[r2][c3] == 1 && ttt2[r3][c3] == 1) || (ttt2[r1][c1] == 1 && ttt2[r2][c2] == 1 && ttt2[r3][c3] == 1) || (ttt2[r1][c3] == 1 && ttt2[r2][c2] == 1 && ttt2[r3][c1] == 1)) {
p2WinFlag = 1;
return p2WinFlag;
} else {
return p2WinFlag;
}
}
//following function simulates valid "plays" between players to see if there are possible ways to win still (this allows a Draw condition); accepts light count and player
/*
Note: I had tried to check if players could make any move based on if there were already two lights in any valid sequence and one "empty" spot, this did not work because
it would always return true because there are always lights that are not for the next turn (not at 9 obviously). I scrapped that idea and thought: "Hey why not temporarily
fill up the matrix of each player to see if one of them can win? That would be easier."
*/
int checkPlayerCanMoveToWin(int lc, bool pl) {
boolean tempWinP1 = 0; //set a flag for a temporary Win Status for player 1
boolean tempWinP2 = 0; //set a flag for temporary win status for player 2
int countWins = 0; //this variable counts the number of times any player can potentially win, will return what this value is; in main loop, if this is greater than 0 then there isn't a draw, if it is equal, then it is a draw because no player can win
for (int i = 0; i < rows; i++) { //loop through the matrices again using row and column "for()" method
for (int j = 0; j < columns; j++) {
if (ttt1[i][j] == 1 || ttt2[i][j] == 1) { //again need to fill empty positions in either matrix, don't overwrite existing ones and don't put a 1 where a different matrix already equals 1
} else if (ttt1[i][j] == 0 && ttt2[i][j] == 0) { //redundant check to make sure both spots in matrix 1 and 2 equal 0
if (lc == 7 && pl == 1) { //after 7 lights it is possible to determine a "draw" state; 6 lights has too many ways and is indeterminate, make sure right player is referenced
ttt1[i][j] = 1; //temporarily set matrix 1 position equal to 1
ttt2[i][j] = 1; //player2 can also potentially still win, set matrix 2 position equal to 1
tempWinP1 = checkP1Win(); //update tempWinP1 by using function checkP1Win after ttt1 has been updated
tempWinP2 = checkP2Win(); //do the same thing with tempWinP2
if (tempWinP1 == 1 || tempWinP2 == 1) { //if either tempWin flag equals 1, increase the count by 1
countWins++;
} else { //otherwise, don't update the countAmount
countWins = countWins;
}
ttt1[i][j] = 0; //reset the position back to 0, don't want to mess up the regular turn sequence
ttt2[i][j] = 0; //same thing for player 2
tempWinP1 = 0; //reset the flag for p1
tempWinP2 = 0; //same thing for p2
} else if (lc == 8 && pl == 0) { //check if the light count is 8 (only one player can make a move which is player 1)
ttt1[i][j] = 1; //temporarily set ttt1 position to 1
tempWinP1 = checkP1Win(); //check if P1 can win
if (tempWinP1 == 1) { //if they can, increase count by 1
countWins++;
} else { //if they can't, keep it the same
countWins = countWins;
}
ttt1[i][j] = 0; //reset ttt1 position back to 0
tempWinP1 = 0; //reset flag back to 0
}
}
}
}
return countWins; //return the amount that countWin has either incremented by or stayed the same
countWins = 0; //reset countWin in the function to start from scratch the next time the function is used
}
//turn all LEDs off for light sequences
void turnAllLEDOff() {
digitalWrite(RR1C1, LOW);
digitalWrite(RR1C2, LOW);
digitalWrite(RR1C3, LOW);
digitalWrite(RR2C1, LOW);
digitalWrite(RR2C2, LOW);
digitalWrite(RR2C3, LOW);
digitalWrite(RR3C1, LOW);
digitalWrite(RR3C2, LOW);
digitalWrite(RR3C3, LOW);
digitalWrite(BR1C1, LOW);
digitalWrite(BR1C2, LOW);
digitalWrite(BR1C3, LOW);
digitalWrite(BR2C1, LOW);
digitalWrite(BR2C2, LOW);
digitalWrite(BR2C3, LOW);
digitalWrite(BR3C1, LOW);
digitalWrite(BR3C2, LOW);
digitalWrite(BR3C3, LOW);
}
//light sequence one, change light colors based on player that won
void winSeq1(bool pl) {
if (pl == 0) {
digitalWrite(RR1C1, HIGH);
delay(100);
digitalWrite(RR2C2, HIGH);
delay(100);
digitalWrite(RR3C3, HIGH);
delay(100);
digitalWrite(RR3C2, HIGH);
digitalWrite(RR2C3, HIGH);
delay(100);
digitalWrite(RR3C2, LOW);
digitalWrite(RR2C3, LOW);
delay(100);
digitalWrite(RR3C1, HIGH);
digitalWrite(RR1C3, HIGH);
delay(500);
digitalWrite(RR1C1, LOW);
digitalWrite(RR1C3, LOW);
digitalWrite(RR2C2, LOW);
digitalWrite(RR3C1, LOW);
digitalWrite(RR3C3, LOW);
delay(500);
digitalWrite(RR1C1, HIGH);
digitalWrite(RR1C3, HIGH);
digitalWrite(RR2C2, HIGH);
digitalWrite(RR3C1, HIGH);
digitalWrite(RR3C3, HIGH);
delay(500);
digitalWrite(RR1C1, LOW);
digitalWrite(RR1C3, LOW);
digitalWrite(RR2C2, LOW);
digitalWrite(RR3C1, LOW);
digitalWrite(RR3C3, LOW);
delay(500);
digitalWrite(RR1C1, HIGH);
digitalWrite(RR1C3, HIGH);
digitalWrite(RR2C2, HIGH);
digitalWrite(RR3C1, HIGH);
digitalWrite(RR3C3, HIGH);
delay(500);
digitalWrite(RR1C1, LOW);
digitalWrite(RR1C3, LOW);
digitalWrite(RR2C2, LOW);
digitalWrite(RR3C1, LOW);
digitalWrite(RR3C3, LOW);
delay(1000);
} else if (pl == 1) {
digitalWrite(BR1C1, HIGH);
delay(200);
digitalWrite(BR1C2, HIGH);
digitalWrite(BR2C1, HIGH);
delay(200);
digitalWrite(BR1C3, HIGH);
digitalWrite(BR3C1, HIGH);
delay(200);
digitalWrite(BR2C3, HIGH);
digitalWrite(BR3C2, HIGH);
delay(200);
digitalWrite(BR3C3, HIGH);
delay(500);
digitalWrite(BR1C1, LOW);
digitalWrite(BR1C2, LOW);
digitalWrite(BR1C3, LOW);
digitalWrite(BR2C1, LOW);
digitalWrite(BR2C3, LOW);
digitalWrite(BR3C1, LOW);
digitalWrite(BR3C2, LOW);
digitalWrite(BR3C3, LOW);
delay(500);
digitalWrite(BR1C1, HIGH);
digitalWrite(BR1C2, HIGH);
digitalWrite(BR1C3, HIGH);
digitalWrite(BR2C1, HIGH);
digitalWrite(BR2C3, HIGH);
digitalWrite(BR3C1, HIGH);
digitalWrite(BR3C2, HIGH);
digitalWrite(BR3C3, HIGH);
delay(500);
digitalWrite(BR1C1, LOW);
digitalWrite(BR1C2, LOW);
digitalWrite(BR1C3, LOW);
digitalWrite(BR2C1, LOW);
digitalWrite(BR2C3, LOW);
digitalWrite(BR3C1, LOW);
digitalWrite(BR3C2, LOW);
digitalWrite(BR3C3, LOW);
delay(500);
digitalWrite(BR1C1, HIGH);
digitalWrite(BR1C2, HIGH);
digitalWrite(BR1C3, HIGH);
digitalWrite(BR2C1, HIGH);
digitalWrite(BR2C3, HIGH);
digitalWrite(BR3C1, HIGH);
digitalWrite(BR3C2, HIGH);
digitalWrite(BR3C3, HIGH);
delay(500);
digitalWrite(BR1C1, LOW);
digitalWrite(BR1C2, LOW);
digitalWrite(BR1C3, LOW);
digitalWrite(BR2C1, LOW);
digitalWrite(BR2C3, LOW);
digitalWrite(BR3C1, LOW);
digitalWrite(BR3C2, LOW);
digitalWrite(BR3C3, LOW);
delay(1000);
}
}
//light sequence 2, change light colors based on player that won
void winSeq2(bool pl) {
int randL = 0;
int rep = 0;
if (pl == 0) {
while (rep < 20) {
randL = random(1, 10);
switch (randL) {
case 1:
digitalWrite(RR1C1, HIGH);
delay(100);
digitalWrite(RR1C1, LOW);
delay(100);
break;
case 2:
digitalWrite(RR1C2, HIGH);
delay(100);
digitalWrite(RR1C2, LOW);
delay(100);
break;
case 3:
digitalWrite(RR1C3, HIGH);
delay(100);
digitalWrite(RR1C3, LOW);
delay(100);
break;
case 4:
digitalWrite(RR2C1, HIGH);
delay(100);
digitalWrite(RR2C1, LOW);
delay(100);
break;
case 5:
digitalWrite(RR2C2, HIGH);
delay(100);
digitalWrite(RR2C2, LOW);
delay(100);
break;
case 6:
digitalWrite(RR2C3, HIGH);
delay(100);
digitalWrite(RR2C3, LOW);
delay(100);
break;
case 7:
digitalWrite(RR3C1, HIGH);
delay(100);
digitalWrite(RR3C1, LOW);
delay(100);
break;
case 8:
digitalWrite(RR3C2, HIGH);
delay(100);
digitalWrite(RR3C2, LOW);
delay(100);
break;
case 9:
digitalWrite(RR3C3, HIGH);
delay(100);
digitalWrite(RR3C3, LOW);
delay(100);
break;
default:
break;
}
rep++;
}
delay(500);
digitalWrite(RR2C2, HIGH);
delay(500);
digitalWrite(RR2C2, LOW);
delay(1000);
} else if (pl == 1) {
while (rep < 20) {
randL = random(1, 10);
switch (randL) {
case 1:
digitalWrite(BR1C1, HIGH);
delay(100);
digitalWrite(BR1C1, LOW);
delay(100);
break;
case 2:
digitalWrite(BR1C2, HIGH);
delay(100);
digitalWrite(BR1C2, LOW);
delay(100);
break;
case 3:
digitalWrite(BR1C3, HIGH);
delay(100);
digitalWrite(BR1C3, LOW);
delay(100);
break;
case 4:
digitalWrite(BR2C1, HIGH);
delay(100);
digitalWrite(BR2C1, LOW);
delay(100);
break;
case 5:
digitalWrite(BR2C2, HIGH);
delay(100);
digitalWrite(BR2C2, LOW);
delay(100);
break;
case 6:
digitalWrite(BR2C3, HIGH);
delay(100);
digitalWrite(BR2C3, LOW);
delay(100);
break;
case 7:
digitalWrite(BR3C1, HIGH);
delay(100);
digitalWrite(BR3C1, LOW);
delay(100);
break;
case 8:
digitalWrite(BR3C2, HIGH);
delay(100);
digitalWrite(BR3C2, LOW);
delay(100);
break;
case 9:
digitalWrite(BR3C3, HIGH);
delay(100);
digitalWrite(BR3C3, LOW);
delay(100);
break;
default:
break;
}
rep++;
}
delay(500);
digitalWrite(BR2C2, HIGH);
delay(500);
digitalWrite(BR2C2, LOW);
delay(1000);
}
}
//light sequence 3, change light color based on player that won
void winSeq3(bool pl) {
if (pl == 1) {
digitalWrite(RR3C3, HIGH);
digitalWrite(BR1C1, HIGH);
delay(200);
digitalWrite(BR2C1, HIGH);
delay(200);
digitalWrite(BR1C1, LOW);
digitalWrite(BR2C2, HIGH);
delay(200);
digitalWrite(BR2C3, HIGH);
digitalWrite(BR2C1, LOW);
delay(200);
digitalWrite(BR1C3, HIGH);
digitalWrite(BR2C2, LOW);
delay(200);
digitalWrite(BR1C2, HIGH);
digitalWrite(BR2C3, LOW);
delay(200);
digitalWrite(BR2C2, HIGH);
digitalWrite(BR1C3, LOW);
delay(200);
digitalWrite(BR3C2, HIGH);
digitalWrite(BR1C2, LOW);
delay(200);
digitalWrite(BR3C3, HIGH);
digitalWrite(BR2C2, LOW);
delay(200);
digitalWrite(RR3C3, LOW);
digitalWrite(BR3C1, HIGH);
delay(200);
digitalWrite(BR2C3, HIGH);
delay(200);
digitalWrite(BR1C3, HIGH);
delay(200);
digitalWrite(BR1C2, HIGH);
digitalWrite(BR2C2, HIGH);
delay(200);
digitalWrite(BR2C1, HIGH);
digitalWrite(BR1C1, HIGH);
delay(200);
digitalWrite(BR3C1, HIGH);
delay(500);
turnAllLEDOff();
delay(1000);
} else if (pl == 0) {
digitalWrite(BR3C3, HIGH);
digitalWrite(RR1C1, HIGH);
delay(200);
digitalWrite(RR2C1, HIGH);
delay(200);
digitalWrite(RR1C1, LOW);
digitalWrite(RR2C2, HIGH);
delay(200);
digitalWrite(RR2C3, HIGH);
digitalWrite(RR2C1, LOW);
delay(200);
digitalWrite(RR1C3, HIGH);
digitalWrite(RR2C2, LOW);
delay(200);
digitalWrite(RR1C2, HIGH);
digitalWrite(RR2C3, LOW);
delay(200);
digitalWrite(RR2C2, HIGH);
digitalWrite(RR1C3, LOW);
delay(200);
digitalWrite(RR3C2, HIGH);
digitalWrite(RR1C2, LOW);
delay(200);
digitalWrite(RR3C3, HIGH);
digitalWrite(RR2C2, LOW);
delay(200);
digitalWrite(BR3C3, LOW);
digitalWrite(RR3C1, HIGH);
delay(200);
digitalWrite(RR2C3, HIGH);
delay(200);
digitalWrite(RR1C3, HIGH);
delay(200);
digitalWrite(RR1C2, HIGH);
digitalWrite(RR2C2, HIGH);
delay(200);
digitalWrite(RR2C1, HIGH);
digitalWrite(RR1C1, HIGH);
delay(200);
digitalWrite(RR3C1, HIGH);
delay(500);
turnAllLEDOff();
delay(1000);
}
}
//light sequence 3, change color based on player that won
void winSeq4(bool pl) {
if (pl == 1) {
digitalWrite(BR1C3, HIGH);
digitalWrite(BR2C2, HIGH);
digitalWrite(BR3C1, HIGH);
delay(300);
digitalWrite(BR1C3, LOW);
digitalWrite(BR2C2, LOW);
digitalWrite(BR3C1, LOW);
digitalWrite(BR1C1, HIGH);
digitalWrite(BR2C1, HIGH);
digitalWrite(BR1C2, HIGH);
delay(300);
digitalWrite(BR1C1, LOW);
digitalWrite(BR2C1, LOW);
digitalWrite(BR1C2, LOW);
digitalWrite(BR2C3, HIGH);
digitalWrite(BR3C2, HIGH);
digitalWrite(BR3C3, HIGH);
delay(300);
digitalWrite(BR2C3, LOW);
digitalWrite(BR3C2, LOW);
digitalWrite(BR3C3, LOW);
digitalWrite(BR1C1, HIGH);
digitalWrite(BR2C2, HIGH);
digitalWrite(BR3C3, HIGH);
delay(300);
digitalWrite(BR1C1, LOW);
digitalWrite(BR2C2, LOW);
digitalWrite(BR3C3, LOW);
digitalWrite(BR1C3, HIGH);
digitalWrite(BR1C2, HIGH);
digitalWrite(BR2C3, HIGH);
delay(300);
digitalWrite(BR1C3, LOW);
digitalWrite(BR1C2, LOW);
digitalWrite(BR2C3, LOW);
digitalWrite(BR2C1, HIGH);
digitalWrite(BR3C1, HIGH);
digitalWrite(BR3C2, HIGH);
delay(300);
digitalWrite(BR2C1, LOW);
digitalWrite(BR3C1, LOW);
digitalWrite(BR3C2, LOW);
delay(1000);
} else if (pl == 0) {
digitalWrite(RR1C3, HIGH);
digitalWrite(RR2C2, HIGH);
digitalWrite(RR3C1, HIGH);
delay(300);
digitalWrite(RR1C3, LOW);
digitalWrite(RR2C2, LOW);
digitalWrite(RR3C1, LOW);
digitalWrite(RR1C1, HIGH);
digitalWrite(RR2C1, HIGH);
digitalWrite(RR1C2, HIGH);
delay(300);
digitalWrite(RR1C1, LOW);
digitalWrite(RR2C1, LOW);
digitalWrite(RR1C2, LOW);
digitalWrite(RR2C3, HIGH);
digitalWrite(RR3C2, HIGH);
digitalWrite(RR3C3, HIGH);
delay(300);
digitalWrite(RR2C3, LOW);
digitalWrite(RR3C2, LOW);
digitalWrite(RR3C3, LOW);
digitalWrite(RR1C1, HIGH);
digitalWrite(RR2C2, HIGH);
digitalWrite(RR3C3, HIGH);
delay(300);
digitalWrite(RR1C1, LOW);
digitalWrite(RR2C2, LOW);
digitalWrite(RR3C3, LOW);
digitalWrite(RR1C3, HIGH);
digitalWrite(RR1C2, HIGH);
digitalWrite(RR2C3, HIGH);
delay(300);
digitalWrite(RR1C3, LOW);
digitalWrite(RR1C2, LOW);
digitalWrite(RR2C3, LOW);
digitalWrite(RR2C1, HIGH);
digitalWrite(RR3C1, HIGH);
digitalWrite(RR3C2, HIGH);
delay(300);
digitalWrite(RR2C1, LOW);
digitalWrite(RR3C1, LOW);
digitalWrite(RR3C2, LOW);
delay(1000);
}
}
//only one sequence for drawing, no specific player applies
void drawSeq() {
digitalWrite(RR1C1, HIGH);
delay(300);
digitalWrite(RR1C2, HIGH);
digitalWrite(RR2C1, HIGH);
delay(300);
digitalWrite(BR3C3, HIGH);
delay(300);
digitalWrite(BR3C2, HIGH);
digitalWrite(BR2C3, HIGH);
delay(300);
digitalWrite(RR1C3, HIGH);
delay(200);
digitalWrite(RR2C2, HIGH);
delay(200);
digitalWrite(RR3C1, HIGH);
delay(200);
digitalWrite(RR1C3, LOW);
digitalWrite(RR2C2, LOW);
digitalWrite(RR3C1, LOW);
digitalWrite(BR3C1, HIGH);
delay(200);
digitalWrite(BR2C2, HIGH);
delay(200);
digitalWrite(BR1C3, HIGH);
delay(200);
digitalWrite(BR1C3, LOW);
digitalWrite(BR2C2, LOW);
digitalWrite(BR3C1, LOW);
digitalWrite(RR3C1, HIGH);
delay(200);
digitalWrite(RR2C2, HIGH);
delay(200);
digitalWrite(RR1C3, HIGH);
delay(200);
digitalWrite(BR1C3, HIGH);
delay(200);
digitalWrite(BR2C2, HIGH);
delay(200);
digitalWrite(BR3C1, HIGH);
delay(2000);
turnAllLEDOff();
delay(1000);
}
//do something while waiting for input, small sequence that shows red X and blue O, repeats until someone plays (small delay to wait for)
void startSeq(){
digitalWrite(RR1C1, HIGH);
digitalWrite(RR2C2, HIGH);
digitalWrite(RR3C3, HIGH);
digitalWrite(RR1C3, HIGH);
digitalWrite(RR3C1, HIGH);
delay(250);
turnAllLEDOff();
delay(250);
digitalWrite(BR1C1, HIGH);
digitalWrite(BR1C2, HIGH);
digitalWrite(BR1C3, HIGH);
digitalWrite(BR2C1, HIGH);
digitalWrite(BR2C3, HIGH);
digitalWrite(BR3C1, HIGH);
digitalWrite(BR3C2, HIGH);
digitalWrite(BR3C3, HIGH);
delay(250);
turnAllLEDOff();
delay(250);
}
//function that simply returns true or false if there are still buttons pressed after the game is finished (reads all inputs and checks if they are active)
boolean checkPressedEnd() {
if (digitalRead(R1C1IN) == 1 || digitalRead(R1C2IN) == 1 || digitalRead(R1C3IN) == 1 || digitalRead(R2C1IN) == 1 || digitalRead(R2C2IN) == 1 || digitalRead(R2C3IN) == 1 || digitalRead(R3C1IN) == 1 || digitalRead(R3C2IN) == 1 || digitalRead(R3C3IN) == 1) {
return true;
} else {
return false;
}
}
//function that checks each position in the matrix specified by the first parameter and flashes the corresponding light until the button is pressed to set it back to zero, accepts player since player is tied to light color
void checkMatrixAndReset(bool mat[rows][columns], bool pl) {
for (int r = 0; r < rows; r++) { //loop through rows and columns again of the specified matrix
for (int c = 0; c < columns; c++) {
if (r == 0 && c == 0 && mat[r][c] == 1) { //check each position and check if the position equals 1
while (mat[r][c] == 1) { //if that position equals one, it will loop until it no longer is equal to 1
if (pl == 0) { //check player by pl input
digitalWrite(RR1C1, HIGH);
delay(100);
digitalWrite(RR1C1, LOW);
delay(100); //blink the correct light on and off
ttt1[r][c] = digitalRead(R1C1IN); //set the right matrix position equal to digitalRead of the corresponding position, as soon as it reads 0, it will end the while loop
} else if (pl == 1) { //do the same thing for player 2 color
digitalWrite(BR1C1, HIGH);
delay(100);
digitalWrite(BR1C1, LOW);
delay(100);
ttt2[r][c] = digitalRead(R1C1IN);
}
}
} else if (r == 0 && c == 1 && mat[r][c] == 1) { //repeat the same thing for each position, it skips ones that are already 0
while (mat[r][c] == 1) {
if (pl == 0) {
digitalWrite(RR1C2, HIGH);
delay(100);
digitalWrite(RR1C2, LOW);
delay(100);
ttt1[r][c] = digitalRead(R1C2IN);
} else if (pl == 1) {
digitalWrite(BR1C2, HIGH);
delay(100);
digitalWrite(BR1C2, LOW);
delay(100);
Serial.println(digitalRead(R1C2IN));
ttt2[r][c] = digitalRead(R1C2IN);
}
}
} else if (r == 0 && c == 2 && mat[r][c] == 1) {
while (mat[r][c] == 1) {
if (pl == 0) {
digitalWrite(RR1C3, HIGH);
delay(100);
digitalWrite(RR1C3, LOW);
delay(100);
ttt1[r][c] = digitalRead(R1C3IN);
} else if (pl == 1) {
digitalWrite(BR1C3, HIGH);
delay(100);
digitalWrite(BR1C3, LOW);
delay(100);
ttt2[r][c] = digitalRead(R1C3IN);
}
}
} else if (r == 1 && c == 0 && mat[r][c] == 1) {
while (mat[r][c] == 1) {
if (pl == 0) {
digitalWrite(RR2C1, HIGH);
delay(100);
digitalWrite(RR2C1, LOW);
delay(100);
ttt1[r][c] = digitalRead(R2C1IN);
} else if (pl == 1) {
digitalWrite(BR2C1, HIGH);
delay(100);
digitalWrite(BR2C1, LOW);
delay(100);
ttt2[r][c] = digitalRead(R2C1IN);
}
}
} else if (r == 1 && c == 1 && mat[r][c] == 1) {
while (mat[r][c] == 1) {
if (pl == 0) {
digitalWrite(RR2C2, HIGH);
delay(100);
digitalWrite(RR2C2, LOW);
delay(100);
ttt1[r][c] = digitalRead(R2C2IN);
} else if (pl == 1) {
digitalWrite(BR2C2, HIGH);
delay(100);
digitalWrite(BR2C2, LOW);
delay(100);
ttt2[r][c] = digitalRead(R2C2IN);
}
}
} else if (r == 1 && c == 2 && mat[r][c] == 1) {
while (mat[r][c] == 1) {
if (pl == 0) {
digitalWrite(RR2C3, HIGH);
delay(100);
digitalWrite(RR2C3, LOW);
delay(100);
ttt1[r][c] = digitalRead(R2C3IN);
} else if (pl == 1) {
digitalWrite(BR2C3, HIGH);
delay(100);
digitalWrite(BR2C3, LOW);
delay(100);
ttt2[r][c] = digitalRead(R2C3IN);
}
}
} else if (r == 2 && c == 0 && mat[r][c] == 1) {
while (mat[r][c] == 1) {
if (pl == 0) {
digitalWrite(RR3C1, HIGH);
delay(100);
digitalWrite(RR3C1, LOW);
delay(100);
ttt1[r][c] = digitalRead(R3C1IN);
} else if (pl == 1) {
digitalWrite(BR3C1, HIGH);
delay(100);
digitalWrite(BR3C1, LOW);
delay(100);
ttt2[r][c] = digitalRead(R3C1IN);
}
}
} else if (r == 2 && c == 1 && mat[r][c] == 1) {
while (mat[r][c] == 1) {
if (pl == 0) {
digitalWrite(RR3C2, HIGH);
delay(100);
digitalWrite(RR3C2, LOW);
delay(100);
ttt1[r][c] = digitalRead(R3C2IN);
} else if (pl == 1) {
digitalWrite(BR3C2, HIGH);
delay(100);
digitalWrite(BR3C2, LOW);
delay(100);
ttt2[r][c] = digitalRead(R3C2IN);
}
}
} else if (r == 2 && c == 2 && mat[r][c] == 1) {
while (mat[r][c] == 1) {
if (pl == 0) {
digitalWrite(RR3C3, HIGH);
delay(100);
digitalWrite(RR3C3, LOW);
delay(100);
ttt1[r][c] = digitalRead(R3C3IN);
} else if (pl == 1) {
digitalWrite(BR3C3, HIGH);
delay(100);
digitalWrite(BR3C3, LOW);
delay(100);
ttt2[r][c] = digitalRead(R3C3IN);
}
}
}
}
}
}
//final function to reset the entire game, it checks if all the matrix positions for both players are empty (0's)
void resetGame() {
int r1 = 0;
int r2 = 1;
int r3 = 2;
int c1 = 0;
int c2 = 1;
int c3 = 2;
//if both matrices are filled with 0's, reset every global variable that controls the playing sequence back to their default values
if (ttt1[r1][c1] == 0 && ttt1[r1][c2] == 0 && ttt1[r1][c3] == 0 && ttt1[r2][c1] == 0 && ttt1[r2][c2] == 0 && ttt1[r2][c3] == 0 && ttt1[r3][c1] == 0 && ttt1[r3][c2] == 0 && ttt1[r3][c3] == 0 && ttt2[r1][c1] == 0 && ttt2[r1][c2] == 0 && ttt2[r1][c3] == 0 && ttt2[r2][c1] == 0 && ttt2[r2][c2] == 0 && ttt2[r2][c3] == 0 && ttt2[r3][c1] == 0 && ttt2[r3][c2] == 0 && ttt2[r3][c3] == 0) {
playerSw = 0;
lightCount = 0;
flagTurn1P1 = 0;
flagTurn2P2 = 0;
flagTurn3P1 = 0;
flagTurn4P2 = 0;
flagTurn5P1 = 0;
flagTurn6P2 = 0;
flagTurn7P1 = 0;
flagTurn8P2 = 0;
flagTurn9P1 = 0;
p1Win = 0;
p2Win = 0;
gameDraw = 0;
movePossibleWinsCt = 0;
randSeq = 0;
seqCt = 0;
} else {
//don't do anything if all matrix positions aren't 1
}
}
Two functions were harder to develop: checkPlayerCanMoveToWin() and checkMatrixAndReset() than the remaining ones above. I needed a way to program checking if there was a draw, this was harder than I first thought. I thought there was a way to see if one person could make any move after two lights per player were already lit. This didn’t work because technically a person could move anywhere that hasn’t already been set. So I determined that after seven lights it was possible to temporarily fill empty spots with one’s and add up the number of times a “win” was possible. If the count was zero, the game results in a draw, otherwise, there are possible moves for someone to win. Next, I wanted to make sure that everything was reset properly since I chose buttons that stayed on when pressed. It just took me a while to figure what “if” statements to use and that while loops were useful in this stage. The function simply blinks the light that corresponds to the button that needs to be reset. One final note is remember what names you give the lights for sequencing or make them more obvious names. I had to re-write sequences a lot because I would forget R1 refers to the top row, not the bottom row.
Overall, I could have saved lines by writing better functions, but this program works exactly the way I want it to. However, I’d say that this could have been a lot more tedious without all the functions and may have taken up more memory. Currently, the program only uses 12,738 bytes of storage space and only 229 bytes of dynamic memory. Here is the zipped folder of the entire code:
tttFull.zip (9.0 KB)
The next step is creating an enclosure for buttons with LED indication built into them to make a final design.