diff options
Diffstat (limited to 'public/projects/js-small-apps/rock-paper-scissors/lib')
4 files changed, 656 insertions, 0 deletions
diff --git a/public/projects/js-small-apps/rock-paper-scissors/lib/class-game.js b/public/projects/js-small-apps/rock-paper-scissors/lib/class-game.js new file mode 100644 index 0000000..92898a1 --- /dev/null +++ b/public/projects/js-small-apps/rock-paper-scissors/lib/class-game.js @@ -0,0 +1,296 @@ +import Player from "./class-player.js"; + +/** + * Game class. + */ +class Game { + #name = "My Game"; + #language = "en-US"; + #playerId = 0; + #players = []; + #roundWinners = []; + #roundLosers = []; + #gameWinners = []; + #gameLosers = []; + #state = "paused"; + #turn; + #currentTurn = 1; + #maxTurn = 0; + #currentRound = 1; + #maxRound = 0; + + /** + * Initialize a new Game instance. + * @param {String} name - The game name. + * @param {Object[]} players - The players. + * @param {String} players[].username - The player username. + * @param {Boolean} players[].ia - True to set the player as an IA. + */ + constructor(name, players) { + this.#name = name; + players.forEach((player) => { + this.#players.push( + new Player(++this.#playerId, player.username, player.ia) + ); + }); + } + + set name(string) { + this.#name = string; + } + + get name() { + return this.#name; + } + + set language(languageCode) { + this.#language = languageCode; + } + + get language() { + return this.#language; + } + + set players(array) { + array.forEach((player) => + this.#players.push( + new Player(++this.#playerId, player.username, player.ia) + ) + ); + } + + get players() { + return this.#players; + } + + set roundWinners(array) { + if (array.length > 0) { + array.forEach((player) => this.#roundWinners.push(player)); + } else { + this.#roundWinners = []; + } + } + + get roundWinners() { + return this.#roundWinners; + } + + set roundLosers(array) { + if (array.length > 0) { + array.forEach((player) => this.#roundLosers.push(player)); + } else { + this.#roundLosers = []; + } + } + + get roundLosers() { + return this.#roundLosers; + } + + set gameWinners(array) { + if (array.length > 0) { + array.forEach((player) => this.#gameWinners.push(player)); + } else { + this.#gameWinners = []; + } + } + + get gameWinners() { + return this.#gameWinners; + } + + set gameLosers(array) { + if (array.length > 0) { + array.forEach((player) => this.#gameLosers.push(player)); + } else { + this.#gameLosers = []; + } + } + + get gameLosers() { + return this.#gameLosers; + } + + set state(string) { + this.#state = string; + } + + get state() { + return this.#state; + } + + set turn(generator) { + this.#turn = generator; + } + + get turn() { + return this.#turn; + } + + set currentTurn(int) { + this.#currentTurn = int; + } + + get currentTurn() { + return this.#currentTurn; + } + + set maxTurn(int = this.getPlayersNumber()) { + this.#maxTurn = int; + } + + get maxTurn() { + return this.#maxTurn; + } + + set currentRound(int) { + this.#currentRound = int; + } + + get currentRound() { + return this.#currentRound; + } + + set maxRound(int) { + this.#maxRound = int; + } + + get maxRound() { + return this.#maxRound; + } + + newPlayer(username) { + this.players.push(new Player(++this.#playerId, username)); + } + + getPlayersNumber() { + return this.players.length; + } + + getPlayer(number) { + return this.players[number - 1]; + } + + getCurrentPlayer() { + return this.getPlayer(this.currentTurn); + } + + getNextPlayer() { + if (this.currentTurn < this.maxTurn) { + return this.getPlayer(this.currentTurn + 1); + } else { + return this.getPlayer(1); + } + } + + isFirstTurn() { + return this.currentTurn === 1; + } + + isNewRound() { + return this.currentRound > 1 && this.isFirstTurn(); + } + + isGameOver() { + return this.state === "ended"; + } + + resetScore() { + this.players.forEach((player) => { + player.score = 0; + }); + } + + newGame() { + this.roundWinners = []; + this.roundLosers = []; + this.gameWinners = []; + this.gameLosers = []; + this.state = "paused"; + this.turn = this.#generateTurns(); + this.currentTurn = 1; + this.currentRound = 1; + this.maxTurn = this.getPlayersNumber(); + this.resetScore(); + } + + setPlayerChoice(choice) { + if (this.state === "running") { + this.getCurrentPlayer().choice = choice; + } + } + + *#generateTurns() { + this.currentRound = 1; + while (this.maxRound ? this.currentRound <= this.maxRound : true) { + this.currentTurn = 1; + while (this.currentTurn <= this.maxTurn) { + yield this.currentTurn; + this.currentTurn++; + } + this.currentRound++; + } + this.stop(); + this.setGameWinners(); + this.setGameLosers(); + return; + } + + /** + * Get a random choice from an array of choices. + * @param {Array} array - The choices. + * @returns {*} A random choice. + */ + getRandomChoice(array) { + const randomIndex = Math.floor(Math.random() * array.length); + return array[randomIndex]; + } + + getOrderedScores() { + let scores = []; + this.players.forEach((player) => { + scores.push(player.score); + }); + scores.sort((a, b) => a - b); + + return scores; + } + + setGameWinners() { + const scores = this.getOrderedScores(); + const highestScore = scores.pop(); + const winners = this.players.filter( + (player) => player.score === highestScore + ); + this.gameWinners = winners; + } + + setGameLosers() { + const scores = this.getOrderedScores(); + const lowestScore = scores.shift(); + const losers = this.players.filter( + (player) => player.score === lowestScore + ); + this.gameLosers = losers; + } + + resume() { + this.state = "running"; + } + + pause() { + this.state = "paused"; + } + + stop() { + this.state = "ended"; + } + + launch() { + this.newGame(); + this.resume(); + this.turn.next(); + } +} + +export default Game; diff --git a/public/projects/js-small-apps/rock-paper-scissors/lib/class-player.js b/public/projects/js-small-apps/rock-paper-scissors/lib/class-player.js new file mode 100644 index 0000000..8935581 --- /dev/null +++ b/public/projects/js-small-apps/rock-paper-scissors/lib/class-player.js @@ -0,0 +1,60 @@ +/** + * Player class. + */ +class Player { + #id; + #username = "Anonymous"; + #choice = ""; + #score = 0; + #ia = false; + + /** + * Initialize a new Player instance. + * @param {Integer} id - The player id. + * @param {String} username - The player username. + * @param {Boolean} ia - True to set player as an IA. + */ + constructor(id, username, ia) { + this.#id = id; + this.#username = username; + this.#ia = ia; + } + + get id() { + return this.#id; + } + + set username(name) { + this.#username = name; + } + + get username() { + return this.#username; + } + + set choice(choice) { + this.#choice = choice; + } + + set score(score) { + this.#score = score; + } + + get choice() { + return this.#choice; + } + + get score() { + return this.#score; + } + + set ia(boolean) { + this.#ia = boolean; + } + + get ia() { + return this.#ia; + } +} + +export default Player; diff --git a/public/projects/js-small-apps/rock-paper-scissors/lib/class-rps-game.js b/public/projects/js-small-apps/rock-paper-scissors/lib/class-rps-game.js new file mode 100644 index 0000000..fe517db --- /dev/null +++ b/public/projects/js-small-apps/rock-paper-scissors/lib/class-rps-game.js @@ -0,0 +1,230 @@ +import Game from "./class-game.js"; + +/** + * RPSGame class. + */ +class RPSGame extends Game { + #choices = ["rock", "paper", "scissors"]; + #buttons = { rock: "", paper: "", scissors: "", newGame: "" }; + #p1Scoring = { name: "", value: "" }; + #p2Scoring = { name: "", value: "" }; + #messages = ""; + #messageIterator; + #timeoutId; + + /** + * Initialize a new RPSGame instance. + * @param {Object[]} players - An array of player object. + * @param {String} players[].username - The player username. + * @param {Boolean} players[].ia - True to set the player as an IA. + * @param {Object} buttons - The buttons HTMLElement. + * @param {HTMLElement} buttons.rock - Button Element for rock choice. + * @param {HTMLElement} buttons.paper - Button Element for paper choice. + * @param {HTMLElement} buttons.scissors - Button Element for scissors choice. + * @param {HTMLElement} buttons.newGame - Button Element to start new game. + * @param {Object} p1Scoring - The player 1 scoring display. + * @param {HTMLElement} p1Scoring.name - Element to display player 1 name. + * @param {HTMLElement} p1Scoring.value - Element to display player 1 score. + * @param {Object} p2Scoring - The player 2 scoring display. + * @param {HTMLElement} p2Scoring.name - Element to display player 2 name. + * @param {HTMLElement} p2Scoring.value - Element to display player 2 score. + * @param {HTMLElement} messages - Element to display turn/game results. + */ + constructor( + players, + buttons = { rock: "", paper: "", scissors: "", newGame: "" }, + p1Scoring = { name: "", value: "" }, + p2Scoring = { name: "", value: "" }, + messages + ) { + super("Rock Paper Scissors", players); + this.#buttons = buttons; + this.#p1Scoring = p1Scoring; + this.#p2Scoring = p2Scoring; + this.#messages = messages; + } + + get messages() { + return this.#messages; + } + + set messageIterator(generator) { + this.#messageIterator = generator; + } + + get messageIterator() { + return this.#messageIterator; + } + + #updatePlayers() { + this.#p1Scoring.name.textContent = this.getPlayer(1).username; + this.#p2Scoring.name.textContent = this.getPlayer(2).username; + } + + #updateScore() { + this.#p1Scoring.value.textContent = this.getPlayer(1).score; + this.#p2Scoring.value.textContent = this.getPlayer(2).score; + } + + async #createMessage(msg, delay = 0) { + return new Promise( + (resolve) => + (this.#timeoutId = setTimeout(() => { + resolve(msg); + }, delay)) + ); + } + + async *#generateMessages() { + let msg; + msg = yield await this.#createMessage("New game, let's play!"); + + while (this.state === "running") { + for (let index = 0; index < this.getPlayersNumber(); index++) { + if (this.getCurrentPlayer().ia) { + msg = yield this.#createMessage( + `${this.getCurrentPlayer().username} is playing...`, + this.isFirstTurn() ? 1200 : 200 + ); + } else { + msg = yield this.#createMessage( + `${this.getCurrentPlayer().username}'s turn...`, + this.isFirstTurn() ? 1200 : 900 + ); + } + } + msg = yield this.#createMessage( + msg, + this.getCurrentPlayer().ia ? 1000 : 500 + ); + if (!this.isGameOver()) { + msg = yield this.#createMessage("New round...", 1500); + } + } + const winnersList = this.gameWinners.map((winner) => winner.username); + const losersList = this.gameLosers.map((loser) => loser.username); + msg = yield this.#createMessage( + `Winner: ${winnersList.join(", ")} / Loser: ${losersList.join(", ")}` + ); + } + + async printNextMessage(msg = null) { + if (!this.messageIterator) { + this.messageIterator = this.#generateMessages(); + } + this.messages.textContent = await this.messageIterator + .next(msg) + .then((object) => object.value); + } + + async #setTurnIssue() { + const choices = `${this.getPlayer(1).choice}-${this.getPlayer(2).choice}`; + let turnWinner; + let turnLoser; + let even = false; + let msg; + + switch (choices) { + case "rock-paper": + case "paper-scissors": + case "scissors-rock": + turnWinner = this.getPlayer(2); + turnLoser = this.getPlayer(1); + break; + case "paper-rock": + case "rock-scissors": + case "scissors-paper": + turnWinner = this.getPlayer(1); + turnLoser = this.getPlayer(2); + break; + default: + even = true; + break; + } + + if (!even) { + this.turnWinners = [turnWinner]; + this.turnLosers = [turnLoser]; + turnWinner.score++; + msg = `${turnWinner.username} wins! ${turnWinner.choice} beats ${turnLoser.choice}.`; + } else { + msg = `No winner. ${this.getPlayer(1).choice} equals to ${ + this.getPlayer(2).choice + }.`; + } + await this.printNextMessage(msg); + this.#updateScore(); + await this.printNextMessage(); + } + + async #getIAAction() { + if (this.currentTurn % 2 === 0) { + await this.#setTurnIssue(); + this.turn.next(); + await this.printNextMessage(); + if (!this.isGameOver()) { + this.getCurrentPlayer().choice = this.getRandomChoice(this.#choices); + } + } else { + this.turn.next(); + await this.printNextMessage(); + this.getCurrentPlayer().choice = this.getRandomChoice(this.#choices); + await this.#setTurnIssue(); + } + this.turn.next(); + await this.printNextMessage(); + } + + async listen() { + for (const [name, element] of Object.entries(this.#buttons)) { + element.addEventListener("click", async (event) => { + event.preventDefault(); + switch (name) { + case "rock": + case "paper": + case "scissors": + if (this.state === "running") { + this.setPlayerChoice(name); + if (this.currentTurn % 2 === 0) { + await this.#setTurnIssue(); + } + if (this.getNextPlayer().ia) { + await this.#getIAAction(); + } else { + this.turn.next(); + await this.printNextMessage(); + } + } + break; + case "newGame": + this.messageIterator = null; + clearTimeout(this.#timeoutId); + await this.launch(); + default: + break; + } + }); + } + } + + async launch() { + super.launch(); + this.#updatePlayers(); + this.#updateScore(); + await this.printNextMessage(); + await this.printNextMessage(); + + if (this.getCurrentPlayer().ia) { + this.getCurrentPlayer().choice = this.getRandomChoice(this.#choices); + this.turn.next(); + await this.printNextMessage(); + } + } + + async init() { + await this.launch(); + this.listen(); + } +} + +export default RPSGame; diff --git a/public/projects/js-small-apps/rock-paper-scissors/lib/rps-instance.js b/public/projects/js-small-apps/rock-paper-scissors/lib/rps-instance.js new file mode 100644 index 0000000..73c5888 --- /dev/null +++ b/public/projects/js-small-apps/rock-paper-scissors/lib/rps-instance.js @@ -0,0 +1,70 @@ +import RPS_Game from "./rps-game.js"; + +export default class RPS_Instance extends RPS_Game { + #buttons = {}; + #scoring = { player1: '', player2: '' }; + #resultBox = ''; + #resultMsg = ''; + + constructor(player1, player2, buttons, scoring, result) { + super(player1, player2); + this.#buttons = buttons; + this.#scoring = scoring; + this.#resultBox = result; + this.#resultMsg = result.innerHTML; + } + + updateScoring() { + for (const [name, element] of Object.entries(this.#scoring)) { + if ( 'player1' === name ) { + element.innerHTML = this.player1Score; + } + + if ( 'player2' === name ) { + element.innerHTML = this.player2Score; + } + } + } + + updateResult(result = '') { + let txt; + if ( result === 'player1' ) { + txt = this.player1Choice + ' beats ' + this.player2Choice + ".<br>You win!" + this.#resultBox.style.color = 'green'; + } else if ( result === 'player2' ) { + txt = this.player1Choice + ' loses to ' + this.player2Choice + ".<br>You lose..." + this.#resultBox.style.color = 'red'; + } else if ( result === 'even' ) { + txt = this.player1Choice + ' equals to ' + this.player2Choice + ".<br>No winner."; + this.#resultBox.style.color = 'black'; + } else { + txt = this.#resultMsg; + this.#resultBox.style.color = ''; + } + + this.#resultBox.innerHTML = txt; + } + + listen() { + for (const [name, element] of Object.entries(this.#buttons)) { + element.addEventListener('click', () => { + if ( 'reset' === name ) { + this.reset(); + this.updateResult(); + this.updateScoring(); + } else { + this.player1Choice = name; + const result = this.calculateScore(); + this.setScore(result); + this.updateResult(result); + this.updateScoring(); + } + }) + } + } + + init() { + this.reset(); + this.listen(); + } +} |
