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

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;
}