From 91cdc969721225355875861858c40c99f78d497a Mon Sep 17 00:00:00 2001 From: Namonay Date: Mon, 22 Apr 2024 19:13:08 +0200 Subject: [PATCH] morpion: add: Frontend checks, QOL changes and winning conditions --- .../js/api/game/tictactoe/TicTacToeGame.js | 105 +++++++++--------- .../static/js/sound/tictactoe/play-move.mp3 | Bin 0 -> 5224 bytes .../static/js/views/TicTacToeOnlineView.js | 1 - games/consumers.py | 9 +- games/objects/tictactoe/TicTacToeGame.py | 19 ++-- 5 files changed, 75 insertions(+), 59 deletions(-) create mode 100644 frontend/static/js/sound/tictactoe/play-move.mp3 diff --git a/frontend/static/js/api/game/tictactoe/TicTacToeGame.js b/frontend/static/js/api/game/tictactoe/TicTacToeGame.js index 73b5a56..49d6d18 100644 --- a/frontend/static/js/api/game/tictactoe/TicTacToeGame.js +++ b/frontend/static/js/api/game/tictactoe/TicTacToeGame.js @@ -18,12 +18,12 @@ class TicTacToe this.canvas = canvas this.context = this.canvas.getContext("2d"); this.sign; + this.currentMorpion = 4; this.turn; } async init() { - console.log(this.game_id); await this.game.join(); this.canvas.addEventListener("mousedown", (event, morpion = this) => this.onClick(event, morpion)); } @@ -37,22 +37,46 @@ class TicTacToe async onReceive(messageData) { console.log(messageData) - if (messageData.detail == "x" || messageData.detail == "o") + switch (messageData.detail) { - this.sign = messageData.detail; - this.turn = messageData.detail == "x"; - } - else if (messageData.detail == "game_start") - this.game.started = true; - else if (messageData.targetMorpion && messageData.targetCase) - { - this.map[messageData.targetMorpion][messageData.targetCase] = (this.sign == "x") ? 1 : 0; - this.printSign(messageData.targetMorpion, messageData.targetCase, (this.sign == "x") ? "o" : "x"); - if (this.checkWin() != -1) - printWin(); + case 'x': + case 'o': + this.sign = messageData.detail; + this.turn = messageData.detail == "x"; + if (this.turn) + this.setOutline(4, false); + break; + + case 'game_start': + this.game.started = true; + this.game.finished = false; + break; + + case 'game_move': + this.map[messageData.targetMorpion][messageData.targetCase] = (this.sign == "x") ? 1 : 0; + this.printSign(messageData.targetMorpion, messageData.targetCase, (this.sign == "x") ? "o" : "x"); + this.setOutline(this.currentMorpion, false); + break; + + case 'game_end': + this.game.finished = true; + this.canvas.removeEventListener("mousedown", (event, morpion = this) => this.onClick(event, morpion)); + this.printWin(messageData.winning_sign); + break; } } + printWin(winning_sign) + { + this.context.beginPath(); + this.context.fillStyle = "white"; + this.context.fillRect(this.width / 2 - 200, this.height - this.gap + 10, 400, 80); + this.context.closePath(); + this.context.beginPath(); + this.context.fillStyle = (winning_sign == "o") ? "red" : "green"; + this.context.fillText((winning_sign == "o") ? "Winner is : O" : "Winner is : X", this.width / 2 - 30, this.height - this.gap / 2, 140); + this.context.closePath(); + } checkWin() { for (let i = 0; i < 9; i++) @@ -85,6 +109,10 @@ class TicTacToe let y = event.offsetY; let targetMorpion, targetCase; + if (this.game.finished) + { + return; + } targetMorpion = morpion.findPlace(x, this) + morpion.findPlace(y, this) * 3; if (morpion.findPlace(x, this) < 0 || morpion.findPlace(y, this) < 0) return -1; @@ -92,8 +120,8 @@ class TicTacToe if (morpion.checkCase(targetMorpion, targetCase)) { + morpion.setOutline(this.currentMorpion, true); morpion.sendCase(targetMorpion, targetCase); - morpion.setOutline(); } else morpion.incorrectCase(); @@ -101,19 +129,20 @@ class TicTacToe checkCase(targetMorpion, targetCase) { - return (this.map[targetMorpion][targetCase] == -1 && this.turn == true); + return (this.map[targetMorpion][targetCase] == -1 && this.turn == true && targetMorpion == this.currentMorpion); } incorrectCase() { - console.log("bozo"); + } sendCase(targetMorpion, targetCase) { this.map[targetMorpion][targetCase] = (this.sign == "x") ? 0 : 1; + this.currentMorpion = targetCase; this.printSign(targetMorpion, targetCase, this.sign); - console.log(this.game.send, targetMorpion, targetCase) + console.log(targetMorpion, targetCase) this.game.send(JSON.stringify({"targetMorpion" : targetMorpion, "targetCase" : targetCase, "sign" : this.sign})); console.log(this.turn); this.turn = !this.turn; @@ -172,21 +201,25 @@ class TicTacToe return -1; } - setOutline() + setOutline(targetMorpion, clear) { - if (this.turn) + let targetX = (this.gap + targetMorpion % 3 * this.rectsize * 3); + let targetY = (this.gap + Math.floor(targetMorpion / 3) * this.rectsize * 3); + if (this.game.finished) + return; + if (!clear) { this.context.beginPath(); this.context.strokeStyle = (this.sign == "x") ? "green" : "red"; - this.context.roundRect(0, 0, this.canvas.width, this.canvas.height, 25); + this.context.rect(targetX, targetY, this.rectsize * 3, this.rectsize * 3) this.context.stroke(); this.context.closePath(); } else { this.context.beginPath(); - this.context.strokeStyle = "#1a1a1d"; - this.context.roundRect(0, 0, this.canvas.width, this.canvas.height, 25); + this.context.strokeStyle = `rgb(230 230 230)`; + this.context.rect(targetX, targetY, this.rectsize * 3, this.rectsize * 3) this.context.stroke(); this.context.closePath(); } @@ -244,34 +277,6 @@ class TicTacToe } this.context.closePath(); } - - selectCase(x, y) - { - case_morpion = Math.floor(x / this.rectsize) + Math.floor((y / this.rectsize)) * 3 - case_square = Math.floor(x / Math.floor(this.rectsize / 3)) % this.rectsize + Math.floor(y / this.rectsize) % this.rectsize - // ask server if case_morpion == playing_case && case_square == empty - - } - - setOutline() - { - if (this.turn) - { - this.context.beginPath(); - this.context.strokeStyle = (this.sign == "x") ? "green" : "red"; - this.context.roundRect(0, 0, this.canvas.width, this.canvas.height, 25); - this.context.stroke(); - this.context.closePath(); - } - else - { - this.context.beginPath(); - this.context.strokeStyle = "#1a1a1d"; - this.context.roundRect(0, 0, this.canvas.width, this.canvas.height, 25); - this.context.stroke(); - this.context.closePath(); - } - } } export { TicTacToe }; \ No newline at end of file diff --git a/frontend/static/js/sound/tictactoe/play-move.mp3 b/frontend/static/js/sound/tictactoe/play-move.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..1713db075b05221ed24cdd14b0024b31633016d8 GIT binary patch literal 5224 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 literal 0 HcmV?d00001 diff --git a/frontend/static/js/views/TicTacToeOnlineView.js b/frontend/static/js/views/TicTacToeOnlineView.js index 129e6a5..2379cd8 100644 --- a/frontend/static/js/views/TicTacToeOnlineView.js +++ b/frontend/static/js/views/TicTacToeOnlineView.js @@ -19,7 +19,6 @@ export class TicTacToeOnlineView extends AbstractView this.Morpion = new TicTacToe(this.height, this.width, 60, 60, document.getElementById("Morpion"), this.game_id); this.Morpion.DrawSuperMorpion(); await this.Morpion.init(); - this.Morpion.setOutline(); } async leavePage() diff --git a/games/consumers.py b/games/consumers.py index 4fd1094..825ffe5 100644 --- a/games/consumers.py +++ b/games/consumers.py @@ -45,6 +45,8 @@ class TicTacToeWebSocket(WebsocketConsumer): self.member.send(self.member.sign) if (self.game._everbody_is_here() and self.game.model.started == False): + if (self.game.time != -1): + self.game.broadcast("opponent_joined") self.game.broadcast("game_start") self.game.model.start() @@ -54,7 +56,12 @@ class TicTacToeWebSocket(WebsocketConsumer): if (data.get("targetMorpion") is not None and data.get("targetCase") is not None): if (self.game.add(data, self.member) == False): return - self.game.broadcast("", data, [self.member]) + if (data.get("catchup") is not None and self.game.model.finished == False and self.game.model.finished == True): + self.member.send("catchup", {"Morpion": self.game._map, "turn": self.game.turn}) + if (self.game.checkWin() != False): + print(self.game.checkWin()) + self.game.broadcast("game_end", {"winning_sign": self.member.sign}) + self.game.broadcast("game_move", data, [self.member]) pass def disconnect(self, event): diff --git a/games/objects/tictactoe/TicTacToeGame.py b/games/objects/tictactoe/TicTacToeGame.py index fe123e5..7befea5 100644 --- a/games/objects/tictactoe/TicTacToeGame.py +++ b/games/objects/tictactoe/TicTacToeGame.py @@ -18,6 +18,10 @@ class TicTacToeGame(AGame): self.players: list[TicTacToePlayer] = [TicTacToePlayer(player, None, self, ["x", "o"][i]) for i, player in enumerate(players)] + self.time = -1 + + self.turn = 'x' + self._map = [[-1 for _ in range(9)] for _ in range(9)] def _everbody_is_here(self): @@ -44,14 +48,15 @@ class TicTacToeGame(AGame): if (self.checkMove(newmove, player)): self._map[newmove.get("targetMorpion")][newmove.get("targetCase")] = newmove.get("sign") - player.currentMorpion = int(newmove.get("targetMorpion")) + player.currentMorpion = int(newmove.get("targetCase")) + self.turn = newmove.get("sign") return True return False def checkMove(self, newmove, player): print(int(newmove.get("targetMorpion")), player.currentMorpion) - if (int(newmove.get("targetMorpion")) != player.currentMorpion): + if (int(newmove.get("targetMorpion")) != player.currentMorpion or newmove.get("sign") != self.turn): return False if (self._map[newmove.get("targetMorpion")][newmove.get("targetCase")] != -1): @@ -62,16 +67,16 @@ class TicTacToeGame(AGame): def checkWin(self): for tab in self._map: for i in range(3): - if tab[i] == tab[i + 3] == tab[i + 6]: + if tab[i] != -1 and tab[i] == tab[i + 3] and tab[i + 3] == tab[i + 6]: return tab[i] for i in range(0, 9, 3): - if tab[i] == tab[i + 1] == tab[i + 2]: + if tab[i] != -1 and tab[i] == tab[i + 1] and tab[i + 1] == tab[i + 2]: return tab[i] - if tab[0] == tab[4] == tab[8]: + if tab[0] != -1 and tab[0] == tab[4] and tab[4] == tab[8]: return tab[0] - if tab[6] == tab[4] == tab[2]: + if tab[6] != -1 and tab[6] == tab[4] and tab[4] == tab[2]: return tab[6] - return None + return False def _spectator_join(self, user_id: int, socket: WebsocketConsumer):