diff --git a/TikTakToe/TikTakToeGameModel.cpp b/TikTakToe/TikTakToeGameModel.cpp index c57de10..bdb76c8 100644 --- a/TikTakToe/TikTakToeGameModel.cpp +++ b/TikTakToe/TikTakToeGameModel.cpp @@ -26,36 +26,104 @@ TikTakToeGameModel TikTakToeGameModel::gameModel; void TikTakToeGameModel::playersMove(const std::string& player, int cellID) { if (player == players[whosNext]) { int cellValue = 0; + if(board[cellID] == 0){ + if (player == "red") { + cellValue = 1; + } else if (player == "blue") { + cellValue = -1; + } + board[cellID] = cellValue; + + checkIfGameCompleted(); - if (player == "red") { - cellValue = 1; - } else if (player == "blue") { - cellValue = -1; - } - - board[cellID] = cellValue; - - if (whosNext >= 1) { - whosNext = 0; - } else { - whosNext += 1; + if (whosNext >= 1) { + whosNext = 0; + } else { + whosNext += 1; + } } } } void TikTakToeGameModel::resetBoard() { whosNext = 0; + gameFinished= false; for (int i = 0; i < 9; i++) { board[i] = 0; } } +void TikTakToeGameModel::checkIfGameCompleted() { + //Check for a winner + if(checkForWinner()){ + gameFinished = true; + winner = players[whosNext]; + } + + //Check for a draw + if(checkIfBoardFull() && !gameFinished) { + gameFinished = true; + draw = true; + } +} + + +bool TikTakToeGameModel::checkForWinner() { + //Vertical + for(int i = 0; i < 3; i++) { + if(board[i] != 0 && board[0 + i] == board [3 + i] && board[3 + i] == board[6 + i]){ + return true; + } + } + + //Horizontal + for(int i = 0; i < 3; i++) { + if(board[i * 3] != 0 && board[i * 3] == board [i * 3 + 1] && board[i * 3 + 1] == board[i * 3 + 2]){ + return true; + } + } + + //Diagonal left to right + if(board[0] != 0 && board[0] == board[4] && board[4] == board[8]) { + return true; + } + + //Diagonal right to left + if(board[2] != 0 && board[2] == board[4] && board[4] == board[6]) { + return true; + } + + return false; +} + + +bool TikTakToeGameModel::checkIfBoardFull() { + for(int i = 0; i < 9; i++) { + if(board[i] == 0) { + return false; + } + } + return true; +} + + nlohmann::json TikTakToeGameModel::updateClientState() { nlohmann::json message; message["type"] = "update"; message["whosTurn"] = players[whosNext]; message["board"] = board; + + if(gameFinished) { + if(draw) { + message["gameOver"] = "Nobody won, it's a draw!"; + } else { + message["gameOver"] = "Congratulations, player " + winner + " won!"; + } + } + else { + message["gameOver"] = false; + } return message; } diff --git a/TikTakToe/TikTakToeGameModel.h b/TikTakToe/TikTakToeGameModel.h index 7abda23..c974181 100644 --- a/TikTakToe/TikTakToeGameModel.h +++ b/TikTakToe/TikTakToeGameModel.h @@ -29,6 +29,9 @@ class TikTakToeGameModel { public: void playersMove(const std::string& player, int cellID); void resetBoard(); + void checkIfGameCompleted(); + bool checkForWinner(); + bool checkIfBoardFull(); nlohmann::json updateClientState(); static TikTakToeGameModel& getGameModel(); @@ -38,6 +41,9 @@ class TikTakToeGameModel { int whosNext = 0; int numPlayers = 0; int board[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; + bool gameFinished = false; //if one player won, or all fields have been filled + bool draw = false; //all fields are filled, but no player won + std::string winner = ""; friend class TikTakToeSubProtocol; diff --git a/TikTakToe/TikTakToeSubProtocol.cpp b/TikTakToe/TikTakToeSubProtocol.cpp index c681069..60dd6d4 100644 --- a/TikTakToe/TikTakToeSubProtocol.cpp +++ b/TikTakToe/TikTakToeSubProtocol.cpp @@ -88,17 +88,14 @@ void TikTakToeSubProtocol::onMessageEnd() { if (action["type"] == "move") { gameModel.playersMove(action["playerID"], action["cellID"]); - nlohmann::json message = gameModel.updateClientState(); - - /* // also possible - forEachClient([&message](SubProtocol* client) { - client->sendMessage(message.dump()); - }); - */ + } - sendBroadcast(message.dump()); - VLOG(0) << "SendMessage Dump: " << message.dump(); + if (action["type"] == "reset-game") { + gameModel.resetBoard(); } + nlohmann::json message = gameModel.updateClientState(); + sendBroadcast(message.dump()); + VLOG(0) << "SendMessage Dump: " << message.dump(); data.clear(); } diff --git a/TikTakToe/public/index.html b/TikTakToe/public/index.html index 4551366..50997c4 100644 --- a/TikTakToe/public/index.html +++ b/TikTakToe/public/index.html @@ -29,6 +29,8 @@

Websocket Tic Tac Toe

Player Name
Player Turn
+
Status: Game in Progress
+
diff --git a/TikTakToe/public/js/app.js b/TikTakToe/public/js/app.js index 41ac7a2..ba5a647 100644 --- a/TikTakToe/public/js/app.js +++ b/TikTakToe/public/js/app.js @@ -9,6 +9,7 @@ if (location.protocol == "https:") { var gameState = { whosTurn: undefined, playerID: undefined, + gameOver: undefined, board: [0, 0, 0, 0, 0, 0, 0, 0, 0] }; @@ -21,6 +22,7 @@ var initializeBoard = () => { // This function updates the board and whos turn it is. var updateBoard = () => { + let myTurn = gameState.whosTurn === gameState.playerID ? "My Turn" : "Opponents Turn" document.querySelector('.game-info__players-turn').innerText = myTurn; @@ -36,13 +38,19 @@ var updateBoard = () => { element.classList.add('game-board__cell--red') } } + + if(gameState.gameOver) { + document.querySelector('.game-info__status').innerText = gameState.gameOver; + document.querySelector('.game-board').style.pointerEvents = "none"; + } } // This responds to the server push messages ws.addEventListener('message', (message) => { + let action = JSON.parse(message.data); let loadingEl = document.querySelector('.game-loading'); - + switch(action.type) { case 'setup': gameState = action.playerData; @@ -53,6 +61,7 @@ ws.addEventListener('message', (message) => { loadingEl.style.display = 'block'; gameState.whosTurn = action.whosTurn; gameState.board = action.board; + gameState.gameOver = action.gameOver; updateBoard(); loadingEl.style.display = 'none'; break; @@ -75,3 +84,18 @@ ws.addEventListener('open', () => { ws.send(JSON.stringify(message)); }) }) + +ws.addEventListener('open', () => { + document + .querySelector('.reset-game') + .addEventListener('click', (event) => { + document.querySelector('.game-board').style.pointerEvents = "auto"; + document.querySelector('.game-info__status').innerText = "Status: Game in Progress"; + let element = event.target; + let message = { + type: 'reset-game', + playerID: gameState.playerID, + } + setTimeout(function(){ ws.send(JSON.stringify(message)); }, 1000); + }) +}) diff --git a/TikTakToe/public/stylesheets/app.css b/TikTakToe/public/stylesheets/app.css index 659c1f1..e0ef356 100644 --- a/TikTakToe/public/stylesheets/app.css +++ b/TikTakToe/public/stylesheets/app.css @@ -27,6 +27,30 @@ font-size: 1.5rem; text-align: center; } +.game-info__status { + font-weight: bold; + font-size: 1.5rem; + text-align: center; + padding-top: 20px; +} +.reset-game { + display: block; + text-align: center; + margin: 20px auto; + box-shadow:inset 0px 1px 0px 0px #ffffff; + background:linear-gradient(to bottom, #f9f9f9 5%, #e9e9e9 100%); + background-color:#f9f9f9; + border-radius:6px; + border:1px solid #dcdcdc; + cursor:pointer; + color:#666666; + font-family:Arial; + font-size:15px; + font-weight:bold; + padding:6px 24px; + text-decoration:none; + text-shadow:0px 1px 0px #ffffff; +} .game-board { width: 500px;