Le jeu de Connect 4 ramène des souvenirs d'antan. Ce jeu classique a sûrement laissé une impression sur tous ceux qui l'ont joué. Dans cet article, nous allons créer une version multijoueur de Connect 4 à l'aide de Node.js et Socket.io..
Ce tutoriel suppose que vous avez installé Node.js et npm. Pour gérer les dépendances frontales, nous utiliserons Bower pour récupérer les packages et Grunt pour gérer les tâches. Ouvrez un terminal et installez Bower et Grunt globalement en exécutant:
$ sudo npm installer -g bower grunt-cli
Remarque: Grunt nécessite des versions de Node.js> = 0.8.0. Au moment de la rédaction de cet article, les dépôts d'Ubuntu possédaient une version plus ancienne de Node. Assurez-vous que vous utilisez le PPA de Chris Lea si vous êtes sur Ubuntu. Pour les autres distributions / systèmes d'exploitation, veuillez vous reporter aux documents d'installation de Node.js pour obtenir la dernière version..
Avec Bower et Grunt-cli installés, créons un répertoire pour le projet et récupérons les fichiers Twitter Bootstrap et Alertify.js (pour gérer les notifications d'alerte) à l'aide de Bower..
$ mkdir connect4 $ cd connect4 $ bower installer bootstrap alertify.js
Maintenant, configurons un répertoire pour gérer nos ressources personnalisées. Nous l'appellerons les atouts
et stocker nos fichiers personnalisés Less et JavaScript à l'intérieur.
$ mkdir -p assets / javascript, stylesheets $ touch assets / javascript /frontend.js
actifs / feuilles de style /styles.less
assets / stylesheets / variables.less
Pour servir les actifs compilés, nous allons créer un répertoire nommé statique
avec des sous-répertoires nommés javascript
et feuilles de style
.
$ mkdir -p statique / javascript, feuilles de style
Ouvrir assets / stylesheets / styles.less
et importer variables.less
et les requis Moins de fichiers de bootstrap.
// Variables principales et mixins @import "… /… /bower_components/bootstrap/less/variables.less"; @import "… /… /bower_components/bootstrap/less/mixins.less"; // Reset @import "… /… /bower_components/bootstrap/less/normalize.less"; @import "… /… /bower_components/bootstrap/less/print.less"; // Core CSS @import "… /… /bower_components/bootstrap/less/scaffolding.less"; @import "… /… /bower_components/bootstrap/less/type.less"; @import "… /… /bower_components/bootstrap/less/code.less"; @import "… /… /bower_components/bootstrap/less/grid.less"; @import "… /… /bower_components/bootstrap/less/tables.less"; @import "… /… /bower_components/bootstrap/less/forms.less"; @import "… /… /bower_components/bootstrap/less/buttons.less"; // Composants @import "… /… /bower_components/bootstrap/less/component-animations.less"; @import "… /… /bower_components/bootstrap/less/glyphicons.less"; @import "… /… /bower_components/bootstrap/less/dropdowns.less"; @import "… /… /bower_components/bootstrap/less/navbar.less"; @import "… /… /bower_components/bootstrap/less/jumbotron.less"; @import "… /… /bower_components/bootstrap/less/alerts.less"; @import "… /… /bower_components/bootstrap/less/panels.less"; @import "… /… /bower_components/bootstrap/less/wells.less"; // Classes utilitaires @import "… /… /bower_components/bootstrap/less/utilities.less"; @import "… /… /bower_components/bootstrap/less/responsive-utilities.less"; // Variables personnalisées @import "variables.less"; // Alertify @import (less) "… /… /bower_components/alertify.js/themes/alertify.core.css"; @import (less) "… /… /bower_components/alertify.js/themes/alertify.default.css"; // Styles personnalisés
Ceci fait, configurons le Gruntfile.js
compiler les fichiers Less en CSS et combiner tous les fichiers JavaScript en un seul fichier. La structure de base de la Gruntfile.js
fichier avec quelques tâches, ressemblerait à ceci:
// Gruntfile module.exports = function (grunt) // Initialisation de l'objet de configuration grunt.initConfig (// Configuration de tâche moins: //…, concat: //…, regardez: //… ); // Charger les plugins // Définir les tâches;
Nous allons définir trois tâches pour gérer les actifs. La première consistera à compiler tous les fichiers Less en CSS. La seconde consistera à concaténer tous les fichiers JavaScript en un et enfin, la dernière tâche consistera à surveiller les modifications apportées aux fichiers. La tâche de surveillance serait la tâche par défaut et peut être exécutée en tapant grognement
à la racine du projet, une fois la configuration du fichier grunt terminée.
Configurons une tâche pour compiler tous les fichiers Less en fichiers CSS dans le statiques / feuilles de style
annuaire.
less: développement: options: compresser: true,, fichiers: "./static/stylesheets/styles.css": "./assets/stylesheets/styles.less
",,
Nous allons maintenant configurer une autre tâche pour concaténer tous les fichiers JS en un seul..
concat: options: separator: ';',, js: src: ['./bower_components/jquery/jquery.js', './bower_components/bootstrap/dist/js/bootstrap.js', '. /bower_components/alertify.js/lib/alertify.js ',' ./assets/javascript/frontend.js
'], dest:' ./static/javascript/frontend.js
',,,
Enfin, configurons la tâche de surveillance pour surveiller les modifications apportées à nos fichiers et pour exécuter les tâches requises lors de la sauvegarde..
watch: js: files: ['./bower_components/jquery/jquery.js', './bower_components/bootstrap/dist/js/bootstrap.js', './bower_components/alertify.js/lib/alertify. js ','.'assets/javascript/frontend.js
'], tâches: [' concat: js '], moins: fichiers: [' ./assets/stylesheets/*.less '], tâches: [' moins '],
Ceci fait, nous allons charger les plugins npm requis et enregistrer la tâche par défaut.
// Charger les plugins grunt.loadNpmTasks ('grunt-contrib-concat'); grunt.loadNpmTasks ('grunt-contrib-less'); grunt.loadNpmTasks ('grunt-contrib-watch'); // Définition des tâches grunt.registerTask ('default', ['watch']);
Passons maintenant à la gestion des dépendances dorsales à l’aide de npm. Pour ce projet, nous utiliserons le framework Express avec le moteur de templates Jade et Socket.io. Installez les dépendances localement en exécutant la commande suivante:
$ npm installer express jade socket.io async grunt grunt-contrib-concat grunt-contrib-less grunt-contrib-watch
La structure de répertoire devrait maintenant être semblable à ceci:
Maintenant que nos dépendances sont configurées, il est temps de passer à l’interface utilisateur de notre jeu..
Continuons en créant un fichier nommé server.js
et servir du contenu en utilisant Express.
var express = require ('express'); var async = require ('async'); var app = express () var io = require ('socket.io'). listen (app.listen (8000)); app.use ('/ static', express.static (__ dirname + '/ static')); app.get ('/', fonction (req, res) res.render ('index.jade');); app.get ('/ landingPage', fonction (req, res) res.render ('landing.jade');); console.log ('Écoute sur le port 8000');
Nous utilisons Jade Templating Engine pour gérer les modèles. Par défaut, Express recherche les vues à l’intérieur du vues
annuaire. Faisons le vues
répertoire et créer des fichiers Jade pour la mise en page, l'index et la page de remerciement.
$ mkdir -p vues $ tactile vues / layout.jade, index.jade, landing.jade
Ensuite, éditons la mise en page de notre projet, notre page d’index et notre page de destination (landing.jade
).
doctype html html (lang = "en") titre de la tête Connect 4 link (rel = 'stylesheet', href = "static / stylesheets / styles.css") body #wrap nav.navbar.navbar-default (role = 'navigation' ) .container-fluid .navbar-header a.navbar-brand (href = '#') Connect 4 contenu du bloc #footer .container p.text-muted | Développé par | Gaurav Narula pour Nettuts bloque le script javascript (src = '/ socket.io/socket.io.js') le script (src = 'statique / javascript /frontend.js
')
étend le contenu du bloc de disposition .container .row .col-xs-3 .p1-score p 0 # board.col-xs-6 table.center-table .form-group label (for = "shareUrl"). col-sm- URL de partage 3.control-label.share-label: entrée .col-sm-9 (type = 'text' en lecture seule) .form-control .col-xs-3 .p2-score p 0
étend le contenu du bloc de disposition .jumbotron .containerJe vous remercie!
Merci d'avoir joué! Nous espérons que vous avez apprécié le jeu!
bloquer javascript
Notez que nous servons socket.io.js
, bien que cela ne soit défini nulle part dans le statique
annuaire. C'est parce que le socket.io
module gère automatiquement le service de lasocket.io.js
dossier client.
Maintenant que nous avons la configuration HTML, passons à la définition des styles. Nous allons commencer par écraser certaines variables bootstrap avec les valeurs de notre choix à l'intérieur assets / stylesheets / variables.less
.
@ body-bg: # F1F1F1; @ text-color: # 717171; @ titres-couleur: # 333; @ marque-primaire: # 468847; @ succès de la marque: # 3A87AD; @ avertissement de marque: # FFC333; @ danger de marque: # FB6B5B; @ navbar-default-bg: # 25313E; @ navbar-default-color: #ADBECE; @ navbar-default-link-color: @ navbar-default-color; @ navbar-default-link-hover-color: # 333;
Ensuite, nous allons ajouter des styles personnalisés à styles.less
.
// Styles personnalisés / * Pied de page collant * / html, body height: 100%; / * Wrapper pour que le contenu de la page abaisse le pied de page * / #wrap min-height: 100%; hauteur: auto; marge: 0 auto -60px; remplissage: 0 0 60px; #footer height: 60px; couleur de fond: # 65BD77; > .container padding-left: 15px; padding-right: 15px; .container .text-muted margin: 20px 0; couleur: #fff; // Grid table border-collapse: separ; espacement des bordures: 10px 10px; table tr margin: 10px; table tr td largeur: 50px; hauteur: 50px; bordure: solide 1px # 3A87AD; .center-table margin: 0 auto! important; float: aucun! important; .p1-score, .p2-score padding: 185px 0; largeur: 50px; hauteur: 50px; taille de police: 25px; hauteur de ligne: 50px; couleur: #fff; text-align: center; .p1-score float: right; p arrière-plan: # FFC333; .current border: 5px solid darken (# FFC333, 10%); .p2-score p background: # FB6B5B; .current border: 5px solid darken (# FB6B5B, 10%); .share-label line-height: 34px; text-align: right;
Ceci fait, ajoutons du code JavaScript dans actifs / javascript /
pour créer la grille et ajouter frontend.js
ligne de données
et colonne de données
attributs avec les valeurs appropriées dynamiquement.
$ (document) .ready (fonction () pour (var i = 0; i < 6; i++) $('#board table').append("); for(var j = 0; j < 7; j++) $('#board tr').last().append("); $('#board td').last().addClass('box').attr('data-row', i).attr('data-column', j); );
Cela couvre la configuration du frontend. Compilons les actifs et lançons le serveur.
$ grunt less concat: js $ node server.js
Si vous avez suivi, la page d'index devrait ressembler à ceci:
Pointe: Exécuter le grognement
commande sur la racine du projet sur un terminal séparé. Cela appellerait la tâche par défaut qui est surveillée. Cela concatènerait tous les fichiers JS ou compilerait tous les fichiers Less à chaque sauvegarde..
L'objectif de Connect 4 est de connecter quatre "blocs" consécutifs horizontalement, verticalement ou en diagonale. Socket.io nous permet de créer pièces
que les clients peuvent rejoindre. Considérez-les comme des canaux IRC.
Nous utiliserons cette fonctionnalité dans le jeu, de sorte que seuls deux joueurs puissent se trouver dans une salle et que celle-ci soit détruite à la fermeture de l'un d'entre eux. Nous allons créer un objet qui gardera une trace de toutes les salles et, à son tour, de tous les états du jeu. Commençons par créer une fonction dans server.js
créer des noms de pièce aléatoires.
function generateRoom (longueur) var haystack = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; var room = "; pour (var i = 0; i < length; i++) room += haystack.charAt(Math.floor(Math.random() * 62)); return room; ;
La fonction attend la longueur du nom de salle aléatoire que nous souhaitons générer. Le nom de la salle est généré en concaténant des caractères aléatoires à partir du meule de foin
chaîne. Modifions notre itinéraire pour la page d'index afin d'inclure l'URL de partage et créons un nouvel itinéraire pour servir le contenu en cas d'accès à une salle particulière..
app.get ('/', fonction (req, res) share = generateRoom (6); res.render ('index.jade', shareURL: req.protocol + ': //' + req.get (' hôte ') + req.path + partage, partage: partage);); app.get ('/: room ([A-Za-z0-9] 6)', fonction (req, res) share = req.params.room; res.render ('index.jade', shareURL: req.protocol + ': //' + + req.get ('hôte') + '/' + share, share: share););
Dans le code ci-dessus, nous générons l'ID de partage à l'aide du generateRoom ()
fonction que nous avons définie précédemment et transmettons l’ID de partage et l’URL en tant que paramètres au modèle. La seconde route attend un paramètre nommé room qui est limité par une expression régulière. La regex autorise une chaîne ne contenant que des caractères alphanumériques, de longueur six. Encore une fois, nous passons le shareURL
et id en tant que paramètres du modèle. Ajoutons quelques attributs à l’élément d’entrée de notre index afin que nous puissions y accéder de manière frontend.js
plus tard.
input (type = 'text', data-room = share, name = "shareUrl", value = shareURL ReadOnly) .form-control
Ensuite, éditons frontend.js
pour vous connecter au serveur Socket.io, rejoindre la salle et attribuer des propriétés au lecteur actuel.
var socket = io.connect ('localhost'); function Joueur (room, pid) this.room = room; this.pid = pid; var room = $ ('input'). data ('room'); var player = new Player (room, ","); socket.on ('connect', function () socket.emit ('join', room: room);); socket.on ('assign', fonction (data) player.color = data.color; player.pid = data.pid; if (player.pid == 1) $ ('. p1-score p'). addClass ('actuel'); else $ ('. p2-score p'). addClass ('actuel'););
Notez que nous avons créé un objet appelé joueur
faire référence au joueur côté client. Lors de la connexion, l’événement de jointure est appelé sur le serveur qui émet à son tour l’émission assigner sur le serveur afin d’attribuer certaines propriétés au lecteur. Nous pouvons maintenant procéder à la définition du code dans le backend pour gérer le joindre
un événement.
// un objet pour contenir tous les joueurs. La clé indique le numéro de salle var games = ; io.sockets.on ('connexion', fonction (socket) socket.on ('rejoindre', fonction (données) if (data.room dans les jeux) if (typeof games [data.room] .player2! = "undefined") socket.emit ('leave'); return; socket.join (data.room); socket.set ('room', data.room); socket.set ('color', '# FB6B5B '); socket.set (' pid ', -1); jeux [data.room] .player2 = socket // Définir l'adversaire socket.set (' adversaire ', jeux [data.room] .player1); jeux [données .room] .player1.set ('adversaire', jeux [data.room] .player2); // Définit turn socket.set ('turn', false); socket.get ('opposant', fonction (err, opposant ) opposant.set ('turn', true);); socket.emit ('assign', pid: 2); else socket.join (data.room); socket.set ('room' , data.room); socket.set ('color', '# FFC333'); socket.set ('pid', 1); socket.set ('turn', false); jeux [data.room] = player1: prise, plateau: [[0,0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0], [0,0,0,0,0, 0,0], [0,0,0,0,0,0,0], [0,0,0,0,0,0,0], [0,0,0,0,0,0, 0]],; socket.emit ('assign', pid: 1);););
Remarque: Les gestionnaires d’événements Socket.io sur le backend doivent être ajoutés à la io.sockets.on ('connexion', fonction (socket)
bloc de code. De même, les gestionnaires d’événements et le code JavaScript frontal doivent être à l’intérieur du $ (document) .ready (fonction ()
bloc de code.
Dans le code ci-dessus, nous avons défini le gestionnaire d’événements pour le joindre
événement qui est émis par le frontend. Il vérifie si la salle donnée existe déjà et si le joueur deux n'a pas déjà été attribué et, dans l'affirmative, affecte le client actuel au joueur deux. Sinon, il assigne le client actuel au premier joueur et initialise le tableau. Nous émettons le laisser
événement sur le frontend pour les clients qui tentent de rejoindre une partie en cours. Nous définissons également certaines propriétés sur le socket en utilisant socket.set ()
. Ceux-ci incluent l’identifiant de la pièce, la couleur, le pid et la variable de virage. Les propriétés définies de cette manière peuvent être extraites du rappel de socket.get ()
. Ensuite, ajoutons le laisser
gestionnaire d'événements sur le frontend.
socket.on ('leave', function () window.location = '/ landingPage';);
le laisser
Le gestionnaire d’événements redirige simplement le client vers la page de destination. Nous allons maintenant émettre un événement qui avertit les deux joueurs si le jeu est prêt à commencer. Ajoutons du code à la si
condition de notre événement de jointure côté serveur.
if (data.room dans les jeux) //… ajoute au code existant // Notify les jeux [data.room] .player1.emit ('notify', connected: 1, turn: true); socket.emit ('notify', connected: 1, turn: false);
Nous devons définir un notifier
événement dans l'interface qui traite de la notification. Alert.js
fournit un moyen élégant de gérer toutes les notifications. Ajoutons le gestionnaire d'événement de notification dans frontend.js
.
socket.on ('notify', fonction (data) if (data.connected == 1) if (data.turn) alertify.success ('Joueurs connectés! Votre tour'); sinon alertify.success ('Joueurs connectés ! Le tour de votre adversaire '););
Il est temps d’essayer nos progrès jusqu’à présent. Lancez le serveur localement et accédez à localhost et à l'URL de partage dans deux fenêtres distinctes. Si vous avez suivi, vous devriez recevoir une alerte dans le coin inférieur droit, comme indiqué dans l'image ci-dessous:
Nous allons maintenant ajouter du code qui émet un événement lorsque les blocs sont cliqués. Pour cette partie, nous devons vérifier si le clic a été effectué par le bon joueur. C'est là que le tour
la propriété que nous avons définie sur la prise entrerait en jeu. Ajoutez le code suivant à frontend.js
.
$ ('. box'). click (function () // trouver la boîte pour déposer le disque dans var click = rangée: $ (this) .data ('rangée'), colonne: $ (this) .data ('colonne'); socket.emit ('cliquez', cliquez););
Le code ci-dessus définit un gestionnaire d'événements sur toutes les cellules du tableau. Une chose à noter est que la grille dans Connect 4 est similaire à l’ajout de briques dans un mur, c’est-à-dire qu’on ne peut pas remplir une paire particulière (rangée, colonne) si la paire (rangée-1, colonne) n’est pas remplie. Par conséquent, nous devons d'abord obtenir la paire (rangée, colonne) de la cellule sur laquelle vous avez cliqué, puis trouver un moyen de déterminer la cellule à remplir. Ceci est fait dans le backend, dans le gestionnaire d’événements pour Cliquez sur
.
socket.on ('click', fonction (données) async.parallel ([socket.get.bind (this, 'turn'), socket.get.bind (this, 'opposant'), socket.get.bind ( ceci, 'room'), socket.get.bind (this, 'pid')], fonction (err, results) if (résultats [0]) socket.set ('turn', false); results [1 ] .set ('turn', true); var i = 5; while (i> = 0) if (jeux [résultats [2]]. board [i] [colonne de données] == 0) break; i--; if (i> = 0 && data.column> = 0) jeux [résultats [2]]. board [i] [données.colonne] = résultats [3]; socket.get ('color ', function (err, color) socket.emit (' drop ', rangée: i, colonne: data.column, color: color); résultats [1] .emit (' drop ', rangée: i, column: data.column, color: color);); else console.log ('à son tour');););
Le gestionnaire d'événements ci-dessus utilise le module asynchrone pour extraire simultanément les propriétés du socket. Cela évite l’imbrication de rappels lors d’utilisations successives de socket.get ()
. le résultats
variable est un tableau avec des éléments dans le même ordre que le socket.get ()
appels. résultats [0]
, fait donc référence à tour
etc.
Une fois les propriétés extraites, nous échangeons les tournants et déterminons la paire (rangée, colonne) à remplir. Nous faisons cela dans la boucle while en partant de la rangée inférieure (rangée cinq) et en remontant jusqu’à ce que la valeur du tableau situé à (rangée, colonne) soit égale à zéro (ce qui implique qu’elle n’a pas été jouée). Nous assignons ensuite le pid (un ou un négatif) à l’élément du tableau et émettons le laissez tomber
événement sur les deux joueurs. Ajoutons le laissez tomber
gestionnaire d'événements sur frontend.js
et présenter une animation qui nous donne un effet de chute.
socket.on ('drop', fonction (data) var row = 0; stopVal = setInterval (function () if (row == data.row) clearInterval (stopVal); fillBox (row, data.column, data. couleur); rangée ++;, 25);); function fillBox (rangée, colonne, couleur) $ ('[data-row = "' + (row-1) + '"] [data-column = "' + column +" "]"). css ('background " , "); $ ('[data-row ="' + row + ""] [data-column = "" + column + "" ""). css ("background", couleur);
Nous implémentons l'animation de chute en utilisant JavaScript setInterval ()
méthode. À partir de la dernière ligne (ligne zéro), nous continuons à appeler fillBox ()
à des intervalles de 25 secondes jusqu'à ce que la valeur de rangée
est égal à la valeur de data.row
. le fillBox
function efface l’arrière-plan de l’élément précédent, dans la même colonne, et attribue un arrière-plan à l’élément actuel. Nous arrivons ensuite au cœur du jeu, en implémentant les conditions de gain et de tirage. Nous allons couvrir cela dans le backend.
// Fonction helper function getPair (ligne, colonne, étape) l = []; pour (var i = 0; i < 4; i++) l.push([row, column]); row += step[0]; column += step[1]; return l; // a list to hold win cases var check = [];
Nous commençons par définir une fonction d'assistance qui renvoie quatre paires (rangée, colonne) horizontalement, verticalement ou en diagonale. La fonction attend la ligne et la colonne en cours et un tableau qui détermine l'incrément des valeurs de ligne et de colonne. Par exemple, un appel à getPair (1,1, [1,1])
retournerais [[1,1], [2,2], [3,3], [4,4]]
qui se trouve être la bonne diagonale. De cette façon, nous pouvons obtenir des paires respectives en sélectionnant des valeurs appropriées pour la étape
tableau. Nous avons également déclaré une liste contenant toutes les fonctions permettant de rechercher des gains. Commençons par la fonction qui recherche les gains horizontalement et verticalement.
check.push (fonction check_horizontal (room, row, startColumn, callback) pour (var i = 1; i < 5; i++) var count = 0; var column = startColumn + 1 - i; var columnEnd = startColumn + 4 - i; if(columnEnd > 6 || colonne < 0) continue; var pairs = getPair(row, column, [0,1]); for(var j = column; j < columnEnd + 1; j++) count += games[room]['board'][row][j]; if(count == 4) callback(1, pairs); else if(count == -4) callback(2, pairs); ); check.push(function check_vertical(room, startRow, column, callback) for(var i = 1; i < 5; i++) var count = 0; var row = startRow + 1 - i; var rowEnd = startRow + 4 - i; if(rowEnd > 5 || rangée < 0) continue; var pairs = getPair(row, column, [1,0]); for(var j = row; j < rowEnd + 1; j++) count += games[room]['board'][j][column]; if(count == 4) callback(1, pairs); else if(count == -4) callback(2, pairs); );
Passons en revue la fonction ci-dessus, étape par étape. La fonction attend quatre paramètres pour la pièce, la ligne, la colonne et le rappel de réussite. Pour vérifier une victoire horizontalement, la cellule sur laquelle vous avez cliqué peut contribuer à une condition gagnante de quatre façons au maximum. Par exemple, la cellule en (5, 3) peut entraîner une victoire dans l’une des quatre combinaisons suivantes: [[5,3], [5,4], [5,5], [5,6]], [[5,2], [5,3], [5,4], [5,5] ], [[5,1], [5,2], [5,3], [5,4]], [[5,0], [5,1], [5,2], [5, 3], [5,4]]
. Le nombre de combinaisons peut être inférieur pour les conditions aux frontières. L’algorithme ci-dessus traite le problème en calculant la colonne la plus à gauche (variable colonne
) et la colonne la plus à droite (variable colonneFin
) dans chacune des quatre combinaisons possibles.
Si la colonne la plus à droite est supérieure à six, elle sort de la grille et cette passe peut être ignorée. La même chose sera faite si la colonne la plus à gauche est inférieure à zéro. Cependant, si les cas de limite tombent dans la grille, nous calculons les paires (rangée, colonne) en utilisant getPair ()
fonction d'assistance que nous avons définie précédemment, puis ajoutons les valeurs des éléments sur le tableau. Rappelez-vous que nous avons attribué une valeur plus un au tableau pour le joueur un et une valeur négative pour le joueur deux. Par conséquent, quatre cellules consécutives d'un joueur doivent donner un nombre de quatre ou de quatre négatives, respectivement. Le rappel est appelé en cas de victoire et deux paramètres sont passés, l'un pour le joueur (un ou deux) et l'autre pour les paires gagnantes. La fonction qui traite de la vérification verticale est assez similaire à celle horizontale, sauf qu'elle vérifie les majuscules des rangées plutôt que des colonnes..
Passons maintenant à la définition des vérifications pour les diagonales gauche et droite.
check.push (fonction check_leftDiagonal (room, startRow, startColumn, callback) pour (var i = 1; i < 5; i++) var count = 0; var row = startRow + 1 - i; var rowEnd = startRow + 4 - i; var column = startColumn + 1 - i; var columnEnd = startColumn + 4 - i; if(column < 0 || columnEnd > 6 || rowEnd> 5 || rangée < 0) continue; var pairs = getPair(row, column, [1,1]); for(var j = 0; j < pairs.length; j++) count += games[room]['board'][pairs[j][0]][pairs[j][1]]; if(count == 4) callback(1, pairs); else if(count == -4) callback(2, pairs); ); check.push(function check_rightDiagonal(room, startRow, startColumn, callback) for(var i = 1; i < 5; i++) var count = 0; var row = startRow + 1 - i; var rowEnd = startRow + 4 - i; var column = startColumn -1 + i; var columnEnd = startColumn - 4 + i; if(column < 0 || columnEnd > 6 || rowEnd> 5 || rangée < 0) continue; var pairs = getPair(row, column, [1,-1]); for(var j = 0; j < pairs.length; j++) count += games[room]['board'][pairs[j][0]][pairs[j][1]]; if(count == 4) callback(1, pairs); else if(count == -4) callback(2, pairs); );
Les vérifications des diagonales sont assez similaires à celles des vérifications horizontale et verticale. La seule différence est que dans le cas des diagonales, nous vérifions les cas de bord pour les lignes et les colonnes. Enfin, nous définissons une fonction pour vérifier les tirages.
// Fonction de vérification de la fonction draw check_draw (room, callback) pour (var val dans les jeux [room] ['board'] [0]) if (val == 0) return; rappeler();
Vérifier les tirages est plutôt trivial. Un tirage au sort est évident si toutes les cellules de la rangée supérieure ont été remplies et que personne n’a gagné. Ainsi, nous excluons un tirage au sort si l’une des cellules de la rangée supérieure n’a pas été jouée et appelons le rappel autrement..
Avec les conditions gagnantes et les conditions de tirage triées, nous devons maintenant utiliser ces fonctions dans l’événement click et émettre une réinitialiser
événement sur le frontend pour indiquer aux clients la fin de la partie. Modifions l'événement click pour gérer ces conditions.
if (i> = 0 && data.column> = 0) / * Code précédent ignoré * / var win = false; check.forEach (fonction (méthode) méthode (résultats [2], i, colonne de données, fonction (joueur, paires) win = true; if (joueur == 1) jeux [résultats [2]]. joueur1 .emit ('reset', texte: 'Vous avez gagné!', 'inc': [1,0], mettez en surbrillance: paires); jeux [résultats [2]]. player2.emit ('reset', text : 'You Lost!', 'Inc': [1,0], mettez en surbrillance: paires); jeux [résultats [2]]. Joueur1.emit ('reset', texte: 'You Lost!' , 'inc': [0,1], mettez en surbrillance: paires); jeux [résultats [2]]. player2.emit ('reset', texte: 'You Won!', 'vous avez gagné!', 'inc': [0,1 ], mettez en surbrillance: paires); jeux [résultats [2]]. board = [[0,0,0,0,0,0,0], [0,0,0,0,0,0,0,0 ], [0,0,0,0,0,0,0], [0,0,0,0,0,0,0], [0,0,0,0,0,0,0], [0,0,0,0,0,0,0]];);); si (gagnant) retourne; check_draw (résultats [2], function () jeux [résultats [2]]. board = [[0,0,0,0,0,0,0], [0,0,0,0,0, 0,0], [0,0,0,0,0,0,0], [0,0,0,0,0,0,0], [0,0,0,0,0,0, 0], [0,0,0,0,0,0,0]]; io.sockets.in (résultats [2]). Emit ('reset', 'text': 'Game Drawn', 'inc ': [0,0]););
Dans le code ci-dessus, nous recherchons une victoire horizontalement, verticalement et en diagonale. En cas de victoire, nous émettons le réinitialiser
événement sur le frontend avec un message approprié pour les deux joueurs. le surligner
propriété contient les paires gagnantes et la inc
propriété indique le score d’incrément pour les deux joueurs. Par exemple, [1,0]
signifierait augmenter le score d'un joueur par un et le score du joueur deux par zéro.
Continuons en manipulant le réinitialiser
événement sur le frontend.
socket.on ('reset', fonction (data) if (data.highlight) setTimeout (function () data.highlight.forEach (function (pair) ('[data-row = "' + pair [ 0] + '"] [data-column ="' + pair [1] + '"]"). Css (' background-color ',' # 65BD77 '););), 500); setTimeout ( function () $ ('td'). css ('background-color', ") alertify.confirm (data.text, fonction (e) if (e) socket.emit ('continue'); autre window.location = '/ landingPage';), 1200) // Définir les scores p1 = parseInt ($ ('. p1-score p'). html ()) + data ['inc'] [0 ]; $ ('.p1-score p'). html (p1); p2 = parseInt ($ ('. p2-score p'). html ()) + data ['inc'] [1]; $ ( '.p2-score p'). html (p2););
dans le réinitialiser
gestionnaire d'événements, nous mettons en évidence les paires gagnantes après 500 ms. La raison du délai est de permettre à l’animation de déposer de se terminer. Ensuite, nous réinitialisons le tableau en arrière-plan et ouvrons une boîte de dialogue de confirmation alertify avec le texte envoyé à partir du backend. Si l’utilisateur décide de continuer, nous émettons le message continuer
événement sur le côté serveur ou bien rediriger le client vers la page de destination. Nous procédons ensuite à l’augmentation des scores du joueur en incrémentant le s