add the 2nd day of work

This commit is contained in:
SamuWrob 2020-12-18 17:28:51 +01:00
parent 1ec96163ab
commit 6db66906da
17 changed files with 568 additions and 124 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
{"id":"../node_modules/@dzeio/dom-manager/dist/index.js","dependencies":[{"name":"/home/ubuntu/Bureau/cours/2eme année/contest/Puissance4/package.json","includedInParent":true,"mtime":1608219815779},{"name":"/home/ubuntu/Bureau/cours/2eme année/contest/Puissance4/node_modules/@dzeio/dom-manager/package.json","includedInParent":true,"mtime":1608219815443},{"name":"./DOMElement","loc":{"line":7,"column":45},"parent":"/home/ubuntu/Bureau/cours/2eme année/contest/Puissance4/node_modules/@dzeio/dom-manager/dist/index.js","resolved":"/home/ubuntu/Bureau/cours/2eme année/contest/Puissance4/node_modules/@dzeio/dom-manager/dist/DOMElement.js"},{"name":"./DOMFleetManager","loc":{"line":9,"column":50},"parent":"/home/ubuntu/Bureau/cours/2eme année/contest/Puissance4/node_modules/@dzeio/dom-manager/dist/index.js","resolved":"/home/ubuntu/Bureau/cours/2eme année/contest/Puissance4/node_modules/@dzeio/dom-manager/dist/DOMFleetManager.js"}],"generated":{"js":"\"use strict\";\r\nvar __importDefault = (this && this.__importDefault) || function (mod) {\r\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\r\n};\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nexports.DOMFleetManager = exports.DOMElement = void 0;\r\nconst DOMElement_1 = __importDefault(require(\"./DOMElement\"));\r\nexports.DOMElement = DOMElement_1.default;\r\nconst DOMFleetManager_1 = __importDefault(require(\"./DOMFleetManager\"));\r\nexports.DOMFleetManager = DOMFleetManager_1.default;\r\n"},"sourceMaps":{"js":{"mappings":[{"source":"../node_modules/@dzeio/dom-manager/dist/index.js","original":{"line":1,"column":0},"generated":{"line":1,"column":0}},{"source":"../node_modules/@dzeio/dom-manager/dist/index.js","original":{"line":2,"column":0},"generated":{"line":2,"column":0}},{"source":"../node_modules/@dzeio/dom-manager/dist/index.js","original":{"line":3,"column":0},"generated":{"line":3,"column":0}},{"source":"../node_modules/@dzeio/dom-manager/dist/index.js","original":{"line":4,"column":0},"generated":{"line":4,"column":0}},{"source":"../node_modules/@dzeio/dom-manager/dist/index.js","original":{"line":5,"column":0},"generated":{"line":5,"column":0}},{"source":"../node_modules/@dzeio/dom-manager/dist/index.js","original":{"line":6,"column":0},"generated":{"line":6,"column":0}},{"source":"../node_modules/@dzeio/dom-manager/dist/index.js","original":{"line":7,"column":0},"generated":{"line":7,"column":0}},{"source":"../node_modules/@dzeio/dom-manager/dist/index.js","original":{"line":8,"column":0},"generated":{"line":8,"column":0}},{"source":"../node_modules/@dzeio/dom-manager/dist/index.js","original":{"line":9,"column":0},"generated":{"line":9,"column":0}},{"source":"../node_modules/@dzeio/dom-manager/dist/index.js","original":{"line":10,"column":0},"generated":{"line":10,"column":0}},{"source":"../node_modules/@dzeio/dom-manager/dist/index.js","original":{"line":11,"column":0},"generated":{"line":11,"column":0}}],"sources":{"../node_modules/@dzeio/dom-manager/dist/index.js":"\"use strict\";\r\nvar __importDefault = (this && this.__importDefault) || function (mod) {\r\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\r\n};\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nexports.DOMFleetManager = exports.DOMElement = void 0;\r\nconst DOMElement_1 = __importDefault(require(\"./DOMElement\"));\r\nexports.DOMElement = DOMElement_1.default;\r\nconst DOMFleetManager_1 = __importDefault(require(\"./DOMFleetManager\"));\r\nexports.DOMFleetManager = DOMFleetManager_1.default;\r\n"},"lineCount":11}},"error":null,"hash":"d24788d11e56655232f1dc2b69591de9","cacheData":{"env":{}}}
{"id":"../node_modules/@dzeio/dom-manager/dist/index.js","dependencies":[{"name":"/home/ubuntu/Bureau/cours/2eme année/contest/Puissance4/package.json","includedInParent":true,"mtime":1608220555886},{"name":"/home/ubuntu/Bureau/cours/2eme année/contest/Puissance4/node_modules/@dzeio/dom-manager/package.json","includedInParent":true,"mtime":1608219815443},{"name":"./DOMElement","loc":{"line":7,"column":45},"parent":"/home/ubuntu/Bureau/cours/2eme année/contest/Puissance4/node_modules/@dzeio/dom-manager/dist/index.js","resolved":"/home/ubuntu/Bureau/cours/2eme année/contest/Puissance4/node_modules/@dzeio/dom-manager/dist/DOMElement.js"},{"name":"./DOMFleetManager","loc":{"line":9,"column":50},"parent":"/home/ubuntu/Bureau/cours/2eme année/contest/Puissance4/node_modules/@dzeio/dom-manager/dist/index.js","resolved":"/home/ubuntu/Bureau/cours/2eme année/contest/Puissance4/node_modules/@dzeio/dom-manager/dist/DOMFleetManager.js"}],"generated":{"js":"\"use strict\";\r\nvar __importDefault = (this && this.__importDefault) || function (mod) {\r\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\r\n};\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nexports.DOMFleetManager = exports.DOMElement = void 0;\r\nconst DOMElement_1 = __importDefault(require(\"./DOMElement\"));\r\nexports.DOMElement = DOMElement_1.default;\r\nconst DOMFleetManager_1 = __importDefault(require(\"./DOMFleetManager\"));\r\nexports.DOMFleetManager = DOMFleetManager_1.default;\r\n"},"sourceMaps":{"js":{"mappings":[{"source":"../node_modules/@dzeio/dom-manager/dist/index.js","original":{"line":1,"column":0},"generated":{"line":1,"column":0}},{"source":"../node_modules/@dzeio/dom-manager/dist/index.js","original":{"line":2,"column":0},"generated":{"line":2,"column":0}},{"source":"../node_modules/@dzeio/dom-manager/dist/index.js","original":{"line":3,"column":0},"generated":{"line":3,"column":0}},{"source":"../node_modules/@dzeio/dom-manager/dist/index.js","original":{"line":4,"column":0},"generated":{"line":4,"column":0}},{"source":"../node_modules/@dzeio/dom-manager/dist/index.js","original":{"line":5,"column":0},"generated":{"line":5,"column":0}},{"source":"../node_modules/@dzeio/dom-manager/dist/index.js","original":{"line":6,"column":0},"generated":{"line":6,"column":0}},{"source":"../node_modules/@dzeio/dom-manager/dist/index.js","original":{"line":7,"column":0},"generated":{"line":7,"column":0}},{"source":"../node_modules/@dzeio/dom-manager/dist/index.js","original":{"line":8,"column":0},"generated":{"line":8,"column":0}},{"source":"../node_modules/@dzeio/dom-manager/dist/index.js","original":{"line":9,"column":0},"generated":{"line":9,"column":0}},{"source":"../node_modules/@dzeio/dom-manager/dist/index.js","original":{"line":10,"column":0},"generated":{"line":10,"column":0}},{"source":"../node_modules/@dzeio/dom-manager/dist/index.js","original":{"line":11,"column":0},"generated":{"line":11,"column":0}}],"sources":{"../node_modules/@dzeio/dom-manager/dist/index.js":"\"use strict\";\r\nvar __importDefault = (this && this.__importDefault) || function (mod) {\r\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\r\n};\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nexports.DOMFleetManager = exports.DOMElement = void 0;\r\nconst DOMElement_1 = __importDefault(require(\"./DOMElement\"));\r\nexports.DOMElement = DOMElement_1.default;\r\nconst DOMFleetManager_1 = __importDefault(require(\"./DOMFleetManager\"));\r\nexports.DOMFleetManager = DOMFleetManager_1.default;\r\n"},"lineCount":11}},"error":null,"hash":"d24788d11e56655232f1dc2b69591de9","cacheData":{"env":{}}}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
node_modules
.cache
dist/
public/

View File

@ -1,9 +1,16 @@
import WebSocket from "ws";
import Listener from "./Listener";
/**
* Creation et Export de la classe Connection hérité de Listener
*/
export default class Connection extends Listener<{
message: (message: string | Record<string, any>) => void
close: () => void
}> {
/**
* Creation du constructeur, qui reçoit des messages de type string et qui les transforme en Json si possible
*
*/
public constructor(
private ws: WebSocket
) {
@ -15,8 +22,14 @@ export default class Connection extends Listener<{
this.emit('message', message.toString())
}
})
ws.once('close', () => {
this.emit('close')
})
}
/**
*
* Envoie du message reçu et modifié vers le serveur
*/
public send(message: string | Record<string, any>) {
if (typeof message === 'string') {
return this.ws.send(message)

View File

@ -2,27 +2,36 @@ import WebSocket from "ws";
import Connection from "./Connection";
import Listener from "./Listener";
/**
* Creation et export de la class Server hérité de Listener
*
*/
export default class Server extends Listener<{
connection: (connection: Connection) => void
open: () => void
}> {
private clients: Array<Connection> = []
private server: WebSocket.Server
/**
* Créer un serveur WebSocket
* Ecoute les emissions lors de l'ouverture d'une connection
* émet l'information de nouvelle connection lors d'une nouvelle connection
*/
public constructor(options: WebSocket.ServerOptions) {
super()
const server = new WebSocket.Server(options, () => this.emit('open'))
server.on('connection', (ws) => {
const connection = new Connection(ws)
connection.on('close', () => {
const index = this.clients.indexOf(connection)
this.clients.splice(index, 1)
})
this.emit('connection', connection)
})
this.server = server
}
/**
* Permet d'envoyer un message à tout les serveurs reliés
*/
public broadcast(message: string) {
this.clients.forEach((ws) => ws.send(message))
this.server.clients.forEach((ws) => ws.send(message))
}
}

View File

@ -1,20 +1,48 @@
import { DOMElement, DOMFleetManager } from '@dzeio/dom-manager'
import { textChangeRangeIsUnchanged } from 'typescript'
/**
* Création et export de la classe game
*/
export default class Game {
private pointsJ1 = 0
private pointsJ2 = 0
public onReset?: () => void
public onMultiStart?: () => void
public multiplayerSessionId?: number
public isWaitingForPlayerMove = false
public playerColor: 'red' | 'yellow' = 'red'
public gameType: 'single' | 'multi' = 'single'
public onWin?: (color?: 'red' | 'yellow') => void
private table: DOMElement<HTMLTableElement>
private columns: Array<Array<DOMElement>> = []
private gameStarted = false
private ws?: WebSocket
/**
* Création du constructeur permettant de créer le tableau de jeu
*/
public constructor(
table: HTMLTableElement
table: HTMLTableElement,
rows: number = 6,
cols: number = 7
) {
this.table = new DOMElement(table)
for (let i = 0; i < rows; i++) {
const row = new DOMElement('tr')
for (let j = 0; j < cols; j++) {
row.item.appendChild(new DOMElement('td').item)
}
row.place('asChildOf', this.table)
}
this.setupGeneral()
}
/**
* Mise en place du tableau, et de différents attributs nécessaires
*/
public setupGeneral() {
// Clear la table
this.columns = []
@ -50,29 +78,138 @@ export default class Game {
// Setup la base du jeux
}
/**
* Remise a zéro du jeu lors d'un redemarrage de partie
*/
public resetGame() {
this.cleanMultiplayer()
if (this.onReset) {
this.onReset()
}
public setRestartButton(btn: DOMElement) {
btn.on('click', () => {
this.setupGeneral()
this.startSinglePlayer()
this.pointsJ1 = 0
this.pointsJ2 = 0
this.updatePoints()
this.gameStarted = false
this.clearTable()
}
/**
* Remise a zéro du plateau de jeu lors d'un redemarrage de partie
*/
public clearTable() {
const rows = new DOMFleetManager('tr', this.table)
rows.each((item, rowIndex) => {
const cells = new DOMFleetManager('td', item)
// cellIndex = 0-6
cells.each((cell, cellIndex) => {
cell
.text(' ')
.data('color', null)
.data('winner', null)
})
})
}
/**
* Lancement d'une partie en joueur vs ia
*/
public startSinglePlayer() {
this.gameStarted = true
this.gameType = 'single'
this.setPlayerTurn(true)
}
/**
*
* Gestion des différentes requetes en fonction des actions de l'utilisateur
*/
public onWebSocket = (event: MessageEvent) => {
const json = JSON.parse(event.data)
if ('joined' in json && json.joined === true) {
this.multiplayerSessionId = json.sessionId
}
if ('xPos' in json && !this.isWaitingForPlayerMove) {
this.makeMove(json.xPos, this.getEnnemyColor())
this.setPlayerTurn(true)
}
if ('gameStarted' in json) {
console.log('Game Started in multiplayer')
if (this.onMultiStart) {
this.onMultiStart()
}
this.gameType = 'multi'
this.gameStarted = true
this.isWaitingForPlayerMove = json.startGame
}
if ('gameStopped' in json) {
console.log('The other player has left the session')
this.resetGame()
}
if ('continue' in json) {
this.continueGame()
}
}
/**
* Fermeture d'une session multijoueur
*/
public cleanMultiplayer() {
this.ws?.close()
}
/**
* Création d'une session multijoueur
*/
public setupMultiplayer() {
this.ws = new WebSocket(`ws://${window.location.hostname}:8080`)
this.ws.onmessage = this.onWebSocket
}
/**
* Création d'une partie multijoueur
*/
public createMultiplayerGame() {
if (!this.ws) {
throw new Error('WebSocket Error')
}
this.ws.send(JSON.stringify({ type: 'request' }))
return new Promise<number>((res, rej) => {
setTimeout(() => {
if (!this.multiplayerSessionId) {
return
}
res(this.multiplayerSessionId)
}, 100)
})
}
public isWaitingForPlayerMove = false
public playerColor: 'red' | 'yellow' = 'red'
public gameType: 'single' | 'multi' = 'single'
/**
*
* Cette fonction permet de rejoindre la partie créer par un utilisateur
*/
public startSinglePlayer() {
this.gameStarted = true
this.isWaitingForPlayerMove = true
public joinMultiplayerGame(sessionId: number) {
if (!this.ws) {
throw new Error('WebSocket Error')
}
this.ws.send(JSON.stringify({ type: 'join', id: sessionId }))
}
/**
* Attribution des couleurs
*/
private getEnnemyColor() {
return this.playerColor === 'red' ? 'yellow' : 'red'
}
/**
* Gestion du tour du joueur
*/
public setPlayerTurn(player: boolean) {
const playerShower = DOMElement.get('.playerColor')
if (!playerShower) {
return
}
playerShower.text(player ? this.playerColor : this.playerColor === 'red' ? 'yellow' : 'red')
playerShower.text(player ? this.playerColor : this.getEnnemyColor())
if (player) {
this.isWaitingForPlayerMove = true
} else {
@ -84,26 +221,32 @@ export default class Game {
}
}
}
public setupMultiplayer() { }
/**
* Gestion de l'état des actions du joueur
*/
public onPlayerMove(cell: DOMElement, xPos: number) {
console.log(this.playerColor)
if (this.isWaitingForPlayerMove) {
this.isWaitingForPlayerMove = !this.makeMove(xPos, this.playerColor)
if (this.isWaitingForPlayerMove) {
return
}
if (this.gameType === 'single' && this.gameStarted) {
if (this.gameStarted) {
this.setPlayerTurn(false)
}
if (this.ws && this.multiplayerSessionId) {
this.ws.send(JSON.stringify({ type: 'proxy', id: this.multiplayerSessionId, xPos }))
}
}
}
/**
* Make a move and return and true if the move was done and false if the move was not done
* Action de placement de jeton dans le jeu
*/
public makeMove(xPos: number, color: 'red' | 'yellow'): boolean {
console.log(color)
let cellToFill: DOMElement | undefined
let yPos = 0
for (let i = 0; i < this.columns[xPos].length; i++) {
@ -118,7 +261,6 @@ export default class Game {
}
}
console.log('cellToFill', cellToFill)
if (!cellToFill) {
return false
}
@ -127,19 +269,84 @@ export default class Game {
return true
}
/**
* Gestion des conditions de victoire
*
*/
public checkWinner(x: number, y: number) {
const win = this.checkDirection(x, y, 'horizontal') || this.checkDirection(x, y, 'vertical') || this.checkDirection(x, y, 'diagonal-left') || this.checkDirection(x, y, 'diagonal-right')
const isFull = this.isFull()
if (win === false) {
console.log('FALSE')
if (isFull) {
if (this.onWin) {
this.onWin(undefined)
}
this.gameStarted = false
console.log('Egalité')
return
}
console.log('no winner currently')
return false
}
this.gameStarted = false
console.log(win)
const clr = win[0].data('color') as 'red'
if (this.onWin) {
this.onWin(clr || undefined)
}
if (clr === this.getEnnemyColor()) {
this.pointsJ2++
} else {
this.pointsJ1++
}
this.updatePoints()
win.forEach((item) => {
console.log(item.data('winner', 'true'))
})
this.gameStarted = false
}
/**
* Gestion de colonne remplie
*/
public isFull() {
for (const col of this.columns) {
for (const cell of col) {
const clr = cell.data('color')
if (!clr) {
return false
}
}
}
return true
}
/**
* Gestion du tableau de jeu lors de relance de partie
*/
public continueGame() {
this.clearTable()
if (this.ws && this.gameType === 'multi' && !this.gameStarted) {
this.ws.send(JSON.stringify({ type: 'proxy', id: this.multiplayerSessionId, continue: true }))
}
this.gameStarted = true
if (this.gameType === 'single' && !this.isWaitingForPlayerMove) {
this.makeIATakeTurn()
}
}
/**
* Attribution des points
*/
private updatePoints() {
DOMElement.get('.j1p')?.text(this.pointsJ1 + '')
DOMElement.get('.j2p')?.text(this.pointsJ2 + '')
}
/**
* Verification des conditions de victoires
*/
public checkDirection(x: number, y: number, direction: 'horizontal' | 'vertical' | 'diagonal-left' | 'diagonal-right'): Array<DOMElement> | false {
console.log('Starting Check', direction)
const color = this.columns[x][y].data('color')
@ -161,8 +368,7 @@ export default class Game {
newY = typeof wentReverse !== 'undefined' ? y + i - wentReverse : y - i
}
console.log('index', i, 'y', newY, 'Y exist', this.isYCorrect(newY))
console.log('index', i, 'x', newX, 'X exist', this.isXCorrect(newX))
if (!this.isYCorrect(newY) || !this.isXCorrect(newX)) {
if (typeof wentReverse === 'undefined') {
@ -173,7 +379,6 @@ export default class Game {
}
const element = this.columns[newX][newY]
console.log('element color', element.data('color'), 'color wanted', color)
if (element.data('color') !== color) {
if (typeof wentReverse === 'undefined') {
@ -186,14 +391,22 @@ export default class Game {
}
return items
}
/**
* Verification des placement de jetons verticaux
*/
private isXCorrect(x: number) {
return x >= 0 && x < this.columns.length
}
/**
* Verification des placement de jetons horizontaux
*/
private isYCorrect(y: number) {
return y >= 0 && y < this.columns[0].length
}
/**
* Gestion de l'IA
*/
public makeIATakeTurn() {
let turnDone = false
@ -201,16 +414,17 @@ export default class Game {
const pos = getRandomInt(0, this.columns.length - 1)
turnDone = this.makeMove(pos, 'red')
}
this.setPlayerTurn(true)
}
}
/**
* Création de number Aléatoire
*/
function getRandomInt(min: number, max: number) {
return Math.floor(Math.random() * ((max + 1) - min)) + min
}
// const cell = new DOMElement('tr')
// cell.data('color') // return 'red | 'yello' pour get
// cell.data('color', 'red') //return void pour set

View File

@ -2,71 +2,42 @@
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Puissance 4</title>
</head>
<body>
<div>Tour du joueur <span class="playerColor"></span></div>
<button class="restartBtn">Recommencer</button>
<h1>Puissance 4</h1>
<div class="chooseSetup">
<button class="vsAI">Jouer contre une IA</button>
<button class="vsPlayer">Jouer contre un joueur en ligne</button>
</div>
<div id="usermessage" style="display: none">Donnez ce code au deuxieme joueur</div>
<div class="playerOption" style="display: none">
<button class="createSession">Créer une Session</button>
<input type="number" id="sessionID" required min="100" max="999" />
<button class="joinSession">Rejoindre une Session</button>
<button id="backInMenu">retour au menu</button>
</div>
<div class="giveUp" style="display: none">
<button class="restartBtn">Arrêter</button>
<button class="continueBtn" style="display:none">Nouvelle Manche</button>
</div>
<div class="tableContainer">
<table>
<tr>
<td>1</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>1</td>
<td>1</td>
</tr>
<tr>
<td>2</td>
<td>2</td>
<td>2</td>
<td>2</td>
<td>2</td>
<td>2</td>
<td>2</td>
</tr>
<tr>
<td>3</td>
<td>3</td>
<td>3</td>
<td>3</td>
<td>3</td>
<td>3</td>
<td>3</td>
</tr>
<tr>
<td>4</td>
<td>4</td>
<td>4</td>
<td>4</td>
<td>4</td>
<td>4</td>
<td>4</td>
</tr>
<tr>
<td>5</td>
<td>5</td>
<td>5</td>
<td>5</td>
<td>5</td>
<td>5</td>
<td>5</td>
</tr>
<tr>
<td>6</td>
<td>6</td>
<td>6</td>
<td>6</td>
<td>6</td>
<td>6</td>
<td>6</td>
</tr>
</table>
</div>
<div class="stateOfGame">
<div class="victoire"></div>
<div>Tour du joueur <span class="playerColor"></span></div>
<div>Vos points <span class="j1p">0</span></div>
<div>Points Adversaire <span class="j2p">0</span></div>
</div>
</body>
<script src="./main.ts"></script>

View File

@ -3,6 +3,9 @@ import './style.css'
import Game from './Game'
import { DOMElement } from '@dzeio/dom-manager'
/**
* Mise en place de l'environnement de jeu
*/
const table = document.querySelector('table')
if (!table) {
@ -10,16 +13,134 @@ if (!table) {
}
const game = new Game(table)
const restartBtn = DOMElement.get('.restartBtn')
if (restartBtn) {
game.setRestartButton(restartBtn)
}
game.playerColor = 'yellow'
game.startSinglePlayer()
/**
* Récupération des éléments nécéssaires
*/
const btnWrapper = DOMElement.get('.chooseSetup')
const multiplayerOptions = DOMElement.get('.playerOption')
const input = DOMElement.get<HTMLInputElement>('#sessionID')
const giveUpWrapper = DOMElement.get('.giveUp')
const btnJoinGame = DOMElement.get('.joinSession')
const btnCreateGame = DOMElement.get('.createSession')
const btnBackInMenu = DOMElement.get('#backInMenu')
const userMessage = DOMElement.get('#usermessage')
const messageVictoire = DOMElement.get('.victoire')
const continueBtn = DOMElement.get('.continueBtn')
const ws = new WebSocket('ws://localhost:8080')
ws.onmessage = (event) => {
console.log(event.data)
/**
* Comportement du DOM lors du reset de la partie
*/
game.onReset = () => {
btnWrapper?.style('display', '')
multiplayerOptions?.style('display', 'none')
giveUpWrapper?.style('display', 'none')
messageVictoire?.text('')
continueBtn?.style('display', 'none')
}
/**
* Comportement du DOM lors d'une victoire
*/
game.onWin = (winner) => {
console.log(winner)
if (!winner) {
messageVictoire?.text('Égalité !')
} else {
messageVictoire?.text(`Le Joueur ${winner} à gagné !`)
}
continueBtn?.style('display', '')
}
/**
* Comportement du DOM lors du lancement d'une partie
*/
game.onMultiStart = () => {
giveUpWrapper?.style('display', '')
multiplayerOptions?.style('display', 'none')
userMessage?.style('display', 'none')
}
/**
* Comportement du DOM et du jeu lorsqu'un utilisateur rejoins une partie
*/
btnJoinGame?.on('click', () => {
const numberSession = input?.item.valueAsNumber || 0
game.joinMultiplayerGame(numberSession)
})
/**
* Comportement du DOM et du jeu lorsqu'un utilisateur créer une partie
*/
btnCreateGame?.on('click', async () => {
const id = await game.createMultiplayerGame()
if (!input) {
return
}
input.item.valueAsNumber = id
input.attr('readOnly', true)
userMessage?.style('display', '')
btnCreateGame?.style('display', 'none')
btnJoinGame?.style('display', 'none')
})
/**
* Comportement du DOM et du jeu lorsqu'un utilisateur relance une partie
*/
continueBtn?.on('click', () => {
game.continueGame()
messageVictoire?.text('')
continueBtn?.style('display', 'none')
})
/**
* Comportement du DOMlorsqu'un utilisateur retourne sur le menu
*/
DOMElement.get('#backInMenu', multiplayerOptions)?.on('click', () => {
btnWrapper?.style('display', 'flex')
btnBackInMenu?.style('display', 'none')
btnCreateGame?.style('display', 'flex')
userMessage?.style('display', 'none')
multiplayerOptions?.style('display', 'none')
game.cleanMultiplayer()
})
/**
* Comportement du DOM et du jeu lorsqu'un utilisateur lance une partie contre l'IA
*/
DOMElement.get('.vsAI', btnWrapper)?.on('click', () => {
game.startSinglePlayer()
btnWrapper?.style('display', 'none')
giveUpWrapper?.style('display', '')
userMessage?.style('display', 'none')
})
/**
* Comportement du DOM et du jeu lorsqu'un utilisateur souhaite lancer une partie en multijoueur
*/
DOMElement.get('.vsPlayer', btnWrapper)?.on('click', () => {
multiplayerOptions?.style('display', 'flex')
btnJoinGame?.style('display', 'flex')
game.setupMultiplayer()
btnWrapper?.style('display', 'none')
multiplayerOptions?.style('display', '')
if (input?.item) {
input.item.value = ''
}
btnBackInMenu?.style('display', 'flex')
})
/**
* Comportement du DOM et du jeu lorsqu'un utilisateur recommence une partie
*/
const restartBtn = DOMElement.get('.restartBtn')?.on('click', () => {
game.resetGame()
})
window.game = game

View File

@ -8,12 +8,25 @@ body {
display: flex;
align-items: center;
justify-content: center;
height: 40rem;
}
table {
height: 45rem;
border: 2px solid;
margin-top: 10rem;
}
.chooseSetup,
.playerOption,
.giveUp {
display: flex;
justify-content: center;
}
h1 {
text-align: center;
}
tr {
@ -21,10 +34,19 @@ tr {
border: 1px solid;
}
#usermessage {
text-align: center;
}
#backInMenu {
display: none
}
td {
border-radius: 100%;
border: 1px solid;
height: 10px;
width: 20px;
height: 7rem;
width: 7rem;
}
td[data-color="red"] {
@ -38,3 +60,11 @@ td[data-color="yellow"] {
td[data-winner="true"] {
background: gold
}
.stateOfGame {
display: flex;
justify-content: center;
align-items: center;
padding-top: 10rem;
flex-direction: column;
}

1
launch.bat Normal file
View File

@ -0,0 +1 @@
npm run start

1
launch.sh Executable file
View File

@ -0,0 +1 @@
npm run start

103
main.ts
View File

@ -2,20 +2,30 @@ import express from 'express'
import WebSocket from './Websocket'
import Connection from './Connection'
/**
* Création de l'interface Comm
*/
interface Comm {
type?: 'request' | 'join' | 'proxy'
id?: number
xPos?: number
continue?: boolean
}
/**
* Mise en place du framework express
*/
const app = express();
app.use(express.static('public'))
/**
* Création du serveur
*/
var server = app.listen(3000, function () {
const wsServer = new WebSocket({ port: 8080 })
/**
*Lecture des différents messages reçu en fonctions de leurs types
*/
wsServer.on('connection', (conn) => {
conn.send({ ok: true })
conn.on('message', (message: Comm | string) => {
@ -31,30 +41,103 @@ var server = app.listen(3000, function () {
joinSession(conn, message.id)
}
if (message?.type === 'proxy' && message?.xPos) {
proxyRequest(conn, message.xPos)
if (message?.type === 'proxy' && typeof message.xPos === 'number' && message?.id) {
proxyRequest(conn, message.id, message.xPos)
}
if (message?.type === 'proxy' && typeof message.xPos === 'number' && message?.id) {
proxyRequest(conn, message.id, message.xPos)
}
if (message?.type === 'proxy' && message.continue && message?.id) {
proxyContinue(conn, message.id)
}
})
})
console.log(`Example app listening at http://localhost:3000`);
});
const currentSessions: Array<Array<Connection>> = []
/**
*
* Création de session de jeu et identification de celle ci par un code
*/
function handleRequest(conn: Connection) {
// Générer un nombre aléatoire entre 100 et 999 et on vérifie qu'il n'y a pas déja de parti avec cette ID
let n: number
do {
n = getRandomInt(100, 999)
} while (currentSessions[n] !== undefined);
// Rajouter la connection a cette partie
// renvoie au client l'ID
joinSession(conn, n)
}
/**
*
* Cette fonction permet de joindre une session créer par handleRequest
*
*
*/
function joinSession(conn: Connection, session: number) {
// Rajouter la connection a cette partie
if (!currentSessions[session]) {
currentSessions[session] = []
}
if (currentSessions[session].length >= 2) {
return conn.send({ joined: false, reason: 'lobby full' })
}
currentSessions[session].push(conn)
conn.once('close', () => {
console.log('closing session')
const index = currentSessions[session].indexOf(conn)
currentSessions[session].splice(index, 1)
currentSessions[session].forEach((c) => c.send(JSON.stringify({ gameStopped: true })))
if (currentSessions[session].length === 0) {
delete currentSessions[session]
}
})
// renvoie au client l'ID
conn.send({ sessionId: session, joined: true })
if (currentSessions[session].length === 2) {
for (const connection of currentSessions[session]) {
connection.send({ gameStarted: true, startGame: connection !== conn })
}
}
}
/**
* Permet d'envoyer le mouvement fait par le joueur actif
*/
function proxyRequest(conn: Connection, session: number, xPos: number) {
// Renvoyer a tout les clients du meme ID le xPos
for (const connection of currentSessions[session]) {
if (conn === connection) {
continue
}
connection.send({ xPos })
}
}
function proxyRequest(conn: Connection, xPos: number) {
// renvoyer a tout les clients du meme ID le xPos
/**
* Création de la nouvelle manche, sans redémarrer la partie
*
*/
function proxyContinue(conn: Connection, session: number) {
// Renvoyer a tout les clients du meme ID le xPos
for (const connection of currentSessions[session]) {
if (conn === connection) {
continue
}
connection.send({ continue: true })
}
}
/**
*Création d'un chiffre aléatoire
*/
function getRandomInt(min: number, max: number) {
return Math.floor(Math.random() * ((max + 1) - min)) + min
}

View File

@ -1 +1 @@
<!DOCTYPE html><html lang="fr"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Puissance 4</title><link rel="stylesheet" href="/main.491056a1.css"></head><body> <div>Tour du joueur <span class="playerColor"></span></div> <button class="restartBtn">Recommencer</button> <div class="tableContainer"> <table> <tr> <td>1</td> <td>1</td> <td>1</td> <td>1</td> <td>1</td> <td>1</td> <td>1</td> </tr> <tr> <td>2</td> <td>2</td> <td>2</td> <td>2</td> <td>2</td> <td>2</td> <td>2</td> </tr> <tr> <td>3</td> <td>3</td> <td>3</td> <td>3</td> <td>3</td> <td>3</td> <td>3</td> </tr> <tr> <td>4</td> <td>4</td> <td>4</td> <td>4</td> <td>4</td> <td>4</td> <td>4</td> </tr> <tr> <td>5</td> <td>5</td> <td>5</td> <td>5</td> <td>5</td> <td>5</td> <td>5</td> </tr> <tr> <td>6</td> <td>6</td> <td>6</td> <td>6</td> <td>6</td> <td>6</td> <td>6</td> </tr> </table> </div> </body><script src="/main.6ae8f54c.js"></script></html>
<!DOCTYPE html><html lang="fr"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Puissance 4</title><link rel="stylesheet" href="/main.0dd87d91.css"></head><body> <h1>Puissance 4</h1> <div class="chooseSetup"> <button class="vsAI">contre ia</button> <button class="vsPlayer">multijoueur</button> </div> <div id="usermessage"></div> <div class="playerOption" style="display:none"> <button class="createSession">Create Session</button> <input type="number" id="sessionID" required="" min="100" max="999"> <button class="joinSession">Join Session</button> <button id="backInMenu">retour au menu</button> </div> <div class="giveUp" style="display:none"> <button class="restartBtn">Abandonner</button> <button class="continueBtn">Nouvelle Manche</button> </div> <div class="tableContainer"> <table> </table> </div> <div class="stateOfGame"> <div>Tour du joueur <span class="playerColor"></span></div> <div>Vos points <span class="j1p">0</span></div> <div>Points Adversaire <span class="j2p">0</span></div> </div> </body><script src="/main.41fb9c97.js"></script></html>