aboutsummaryrefslogtreecommitdiffstats
path: root/public/projects/js-small-apps/rock-paper-scissors/lib
diff options
context:
space:
mode:
Diffstat (limited to 'public/projects/js-small-apps/rock-paper-scissors/lib')
-rw-r--r--public/projects/js-small-apps/rock-paper-scissors/lib/class-game.js296
-rw-r--r--public/projects/js-small-apps/rock-paper-scissors/lib/class-player.js60
-rw-r--r--public/projects/js-small-apps/rock-paper-scissors/lib/class-rps-game.js230
-rw-r--r--public/projects/js-small-apps/rock-paper-scissors/lib/rps-instance.js70
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();
+ }
+}