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 node_modules
.cache .cache
dist/ dist/
public/

View File

@ -1,9 +1,16 @@
import WebSocket from "ws"; import WebSocket from "ws";
import Listener from "./Listener"; import Listener from "./Listener";
/**
* Creation et Export de la classe Connection hérité de Listener
*/
export default class Connection extends Listener<{ export default class Connection extends Listener<{
message: (message: string | Record<string, any>) => void 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( public constructor(
private ws: WebSocket private ws: WebSocket
) { ) {
@ -15,8 +22,14 @@ export default class Connection extends Listener<{
this.emit('message', message.toString()) 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>) { public send(message: string | Record<string, any>) {
if (typeof message === 'string') { if (typeof message === 'string') {
return this.ws.send(message) return this.ws.send(message)

View File

@ -2,27 +2,36 @@ import WebSocket from "ws";
import Connection from "./Connection"; import Connection from "./Connection";
import Listener from "./Listener"; import Listener from "./Listener";
/**
* Creation et export de la class Server hérité de Listener
*
*/
export default class Server extends Listener<{ export default class Server extends Listener<{
connection: (connection: Connection) => void connection: (connection: Connection) => void
open: () => 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) { public constructor(options: WebSocket.ServerOptions) {
super() super()
const server = new WebSocket.Server(options, () => this.emit('open')) const server = new WebSocket.Server(options, () => this.emit('open'))
server.on('connection', (ws) => { server.on('connection', (ws) => {
const connection = new 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.emit('connection', connection)
}) })
this.server = server
} }
/**
* Permet d'envoyer un message à tout les serveurs reliés
*/
public broadcast(message: string) { 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 { DOMElement, DOMFleetManager } from '@dzeio/dom-manager'
import { textChangeRangeIsUnchanged } from 'typescript' import { textChangeRangeIsUnchanged } from 'typescript'
/**
* Création et export de la classe game
*/
export default class 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 table: DOMElement<HTMLTableElement>
private columns: Array<Array<DOMElement>> = [] private columns: Array<Array<DOMElement>> = []
private gameStarted = false private gameStarted = false
private ws?: WebSocket
/**
* Création du constructeur permettant de créer le tableau de jeu
*/
public constructor( public constructor(
table: HTMLTableElement table: HTMLTableElement,
rows: number = 6,
cols: number = 7
) { ) {
this.table = new DOMElement(table) 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() this.setupGeneral()
} }
/**
* Mise en place du tableau, et de différents attributs nécessaires
*/
public setupGeneral() { public setupGeneral() {
// Clear la table // Clear la table
this.columns = [] this.columns = []
@ -50,29 +78,138 @@ export default class Game {
// Setup la base du jeux // 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) { this.pointsJ1 = 0
btn.on('click', () => { this.pointsJ2 = 0
this.setupGeneral() this.updatePoints()
this.startSinglePlayer()
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() { public joinMultiplayerGame(sessionId: number) {
this.gameStarted = true if (!this.ws) {
this.isWaitingForPlayerMove = true 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) { public setPlayerTurn(player: boolean) {
const playerShower = DOMElement.get('.playerColor') const playerShower = DOMElement.get('.playerColor')
if (!playerShower) { if (!playerShower) {
return return
} }
playerShower.text(player ? this.playerColor : this.playerColor === 'red' ? 'yellow' : 'red') playerShower.text(player ? this.playerColor : this.getEnnemyColor())
if (player) { if (player) {
this.isWaitingForPlayerMove = true this.isWaitingForPlayerMove = true
} else { } 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) { public onPlayerMove(cell: DOMElement, xPos: number) {
console.log(this.playerColor)
if (this.isWaitingForPlayerMove) { if (this.isWaitingForPlayerMove) {
this.isWaitingForPlayerMove = !this.makeMove(xPos, this.playerColor) this.isWaitingForPlayerMove = !this.makeMove(xPos, this.playerColor)
if (this.isWaitingForPlayerMove) { if (this.isWaitingForPlayerMove) {
return return
} }
if (this.gameType === 'single' && this.gameStarted) { if (this.gameStarted) {
this.setPlayerTurn(false) 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 { public makeMove(xPos: number, color: 'red' | 'yellow'): boolean {
console.log(color)
let cellToFill: DOMElement | undefined let cellToFill: DOMElement | undefined
let yPos = 0 let yPos = 0
for (let i = 0; i < this.columns[xPos].length; i++) { for (let i = 0; i < this.columns[xPos].length; i++) {
@ -118,7 +261,6 @@ export default class Game {
} }
} }
console.log('cellToFill', cellToFill)
if (!cellToFill) { if (!cellToFill) {
return false return false
} }
@ -127,19 +269,84 @@ export default class Game {
return true return true
} }
/**
* Gestion des conditions de victoire
*
*/
public checkWinner(x: number, y: number) { 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 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) { 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 return false
} }
this.gameStarted = false
console.log(win) 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) => { win.forEach((item) => {
console.log(item.data('winner', 'true')) 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 { public checkDirection(x: number, y: number, direction: 'horizontal' | 'vertical' | 'diagonal-left' | 'diagonal-right'): Array<DOMElement> | false {
console.log('Starting Check', direction) console.log('Starting Check', direction)
const color = this.columns[x][y].data('color') 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 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 (!this.isYCorrect(newY) || !this.isXCorrect(newX)) {
if (typeof wentReverse === 'undefined') { if (typeof wentReverse === 'undefined') {
@ -173,7 +379,6 @@ export default class Game {
} }
const element = this.columns[newX][newY] const element = this.columns[newX][newY]
console.log('element color', element.data('color'), 'color wanted', color)
if (element.data('color') !== color) { if (element.data('color') !== color) {
if (typeof wentReverse === 'undefined') { if (typeof wentReverse === 'undefined') {
@ -186,14 +391,22 @@ export default class Game {
} }
return items return items
} }
/**
* Verification des placement de jetons verticaux
*/
private isXCorrect(x: number) { private isXCorrect(x: number) {
return x >= 0 && x < this.columns.length return x >= 0 && x < this.columns.length
} }
/**
* Verification des placement de jetons horizontaux
*/
private isYCorrect(y: number) { private isYCorrect(y: number) {
return y >= 0 && y < this.columns[0].length return y >= 0 && y < this.columns[0].length
} }
/**
* Gestion de l'IA
*/
public makeIATakeTurn() { public makeIATakeTurn() {
let turnDone = false let turnDone = false
@ -201,16 +414,17 @@ export default class Game {
const pos = getRandomInt(0, this.columns.length - 1) const pos = getRandomInt(0, this.columns.length - 1)
turnDone = this.makeMove(pos, 'red') turnDone = this.makeMove(pos, 'red')
} }
this.setPlayerTurn(true)
} }
} }
/**
* Création de number Aléatoire
*/
function getRandomInt(min: number, max: number) { function getRandomInt(min: number, max: number) {
return Math.floor(Math.random() * ((max + 1) - min)) + min 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"> <html lang="fr">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Puissance 4</title> <title>Puissance 4</title>
</head> </head>
<body> <body>
<div>Tour du joueur <span class="playerColor"></span></div> <h1>Puissance 4</h1>
<button class="restartBtn">Recommencer</button>
<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"> <div class="tableContainer">
<table> <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> </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> </div>
</body> </body>
<script src="./main.ts"></script> <script src="./main.ts"></script>

View File

@ -3,6 +3,9 @@ import './style.css'
import Game from './Game' import Game from './Game'
import { DOMElement } from '@dzeio/dom-manager' import { DOMElement } from '@dzeio/dom-manager'
/**
* Mise en place de l'environnement de jeu
*/
const table = document.querySelector('table') const table = document.querySelector('table')
if (!table) { if (!table) {
@ -10,16 +13,134 @@ if (!table) {
} }
const game = new Game(table) const game = new Game(table)
const restartBtn = DOMElement.get('.restartBtn')
if (restartBtn) {
game.setRestartButton(restartBtn)
}
game.playerColor = 'yellow' 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) => { * Comportement du DOM lors du reset de la partie
console.log(event.data) */
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; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 40rem;
} }
table { table {
height: 45rem;
border: 2px solid; border: 2px solid;
margin-top: 10rem;
}
.chooseSetup,
.playerOption,
.giveUp {
display: flex;
justify-content: center;
}
h1 {
text-align: center;
} }
tr { tr {
@ -21,10 +34,19 @@ tr {
border: 1px solid; border: 1px solid;
} }
#usermessage {
text-align: center;
}
#backInMenu {
display: none
}
td { td {
border-radius: 100%;
border: 1px solid; border: 1px solid;
height: 10px; height: 7rem;
width: 20px; width: 7rem;
} }
td[data-color="red"] { td[data-color="red"] {
@ -38,3 +60,11 @@ td[data-color="yellow"] {
td[data-winner="true"] { td[data-winner="true"] {
background: gold 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 WebSocket from './Websocket'
import Connection from './Connection' import Connection from './Connection'
/**
* Création de l'interface Comm
*/
interface Comm { interface Comm {
type?: 'request' | 'join' | 'proxy' type?: 'request' | 'join' | 'proxy'
id?: number id?: number
xPos?: number xPos?: number
continue?: boolean
} }
/**
* Mise en place du framework express
*/
const app = express(); const app = express();
app.use(express.static('public')) app.use(express.static('public'))
/**
* Création du serveur
*/
var server = app.listen(3000, function () { var server = app.listen(3000, function () {
const wsServer = new WebSocket({ port: 8080 }) const wsServer = new WebSocket({ port: 8080 })
/**
*Lecture des différents messages reçu en fonctions de leurs types
*/
wsServer.on('connection', (conn) => { wsServer.on('connection', (conn) => {
conn.send({ ok: true }) conn.send({ ok: true })
conn.on('message', (message: Comm | string) => { conn.on('message', (message: Comm | string) => {
@ -31,30 +41,103 @@ var server = app.listen(3000, function () {
joinSession(conn, message.id) joinSession(conn, message.id)
} }
if (message?.type === 'proxy' && message?.xPos) { if (message?.type === 'proxy' && typeof message.xPos === 'number' && message?.id) {
proxyRequest(conn, message.xPos) 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`); 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) { 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 // 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 // Rajouter la connection a cette partie
joinSession(conn, n)
// renvoie au client l'ID
} }
/**
*
* Cette fonction permet de joindre une session créer par handleRequest
*
*
*/
function joinSession(conn: Connection, session: number) { function joinSession(conn: Connection, session: number) {
// Rajouter la connection a cette partie // 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 // 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>