前回、「Arduinoアプリケーション開発手順 パート4」では、チックタックトーでプレーヤー間の交代を行うコードについて解説しました。今回は、完成したコードを提供し、必要に応じてコメントします。「勝ち」の条件を考えるのは、最後にゲームをリセットする方法を考えることほどには難しくありませんでした。繰り返しますが、読みやすくするためにコードの一部を提供し、完成したコードのzipフォルダを提供します。この最終的なプログラムは動作するものの、結局、いくつかの機能を追加しましたが、コードの大部分はそのままにしておきました。
セクション1:グローバル変数
... //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
セクション2:メインループ
セットアップのコードに変更はなく、デバッグに重要なのでSerial.begin(9600)をインクルードしたままにしています。
//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();
}
}
}
}
}
長くなってしまいましたが、良い関数をうまく使えばずっと短くできたと思うので、その点だけコメントします。ただし、テストやデバッグにおいて、特定の繰り返しコードを関数に変換するのが難しい場合があります。ある変数が更新されなかったり、何が原因でループが壊れたのかわからないという異常な問題に遭遇することもありました。バグの修正で一番難しいのは、何が原因かわからない場合です。
セクション3:追加機能
「turnLightUp()」関数以降はすべて新しいものだったので、以下のようなコードをコピーしただけです。
//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
}
}
checkPlayerCanMoveToWin() と checkMatrixAndReset() の 2 つの関数は、上記の残りの関数よりも開発が困難でした。引き分けがあるかどうかをプログラムでチェックする方法が必要でしたが、これは当初考えていたよりも困難でした。すでに各プレーヤーのライトが2つずつ点灯した後でも、1人が次の手を打てるかどうかを確認する方法があると思いましたが、技術的にはまだ設定されていない場所にはどこでも行けるため、これは機能しませんでした。そこで私は、7個が点灯した後、空いたスポットを一時的に1で埋め、「勝ち」の可能性がある回数を合計できると判断しました。カウントが0だった場合は引き分けになり、そうでない場合は、誰かが勝つ可能性のある手が存在することになります。次に、押したときにオンのままになるボタンを選んだため、すべてが適切にリセットされていることを確認したいと考えました。ただ、どのような「if」文を使えばよいのかを理解し、また、この段階ではwhileループが有効であることを理解するのに時間がかかりました。この関数は、リセットが必要なボタンに対応するライトを点滅させるだけです。最後の注意点は、シーケンスのためにつけたライトの名前を覚えておくか、またはもっとわかりやすい名前をつけることです。 R1が下の行ではなく上の行を参照していることを忘れてしまうため、シーケンスを何度も書き直す必要がありました。
全体としては、もっと良い関数を書けば行数を減らせたかもしれませんが、このプログラムはまさに私が望むとおりに動いてくれます。ただし、すべての関数がなければ、これはもっと面倒で、より多くのメモリを消費したかもしれません。現在、このプログラムは12,738バイトのストレージスペースとわずか229バイトのダイナミックメモリを使用しているだけです。ここにコード全体のzipフォルダがあります。
tttFull.zip(9.0 KB)
次のステップでは、LED表示を組み込んだボタンの筐体を作り、最終的なデザインに仕上げていく予定です。