import {Component, OnInit} from '@angular/core'; import {Player} from '../../models/doppelkopf/player'; import * as announcements from '../../models/doppelkopf/enums/announcement'; import * as solos from '../../models/doppelkopf/enums/solo'; import {Team} from '../../models/doppelkopf/enums/team'; @Component({ selector: 'app-add-game', templateUrl: './add-game.component.html', styleUrls: ['./add-game.component.scss'] }) export class AddGameComponent implements OnInit { potentialPlayers: Player[] = []; actualPlayers: Player[] = []; selectedAnnouncements: announcements.Announcement[] = []; soloPlayed?: solos.Solo; currentPage: number = 0; constructor() { this.generateDemoData(); } ngOnInit(): void { } /** * Generates demo data to test the UI */ generateDemoData() { this.potentialPlayers.push({ firebonkId: 1, uuid: 'abc-def-ghi-j', firstName: 'Patrick', team: Team.CONTRA, gamePoints: 0, finalCardScore: 0, // foxesCaught: 1, // wonLastTrickWithCharlie: true }); this.potentialPlayers.push({ firebonkId: 2, uuid: 'abc-def-ghi-k', firstName: 'Julian', team: Team.CONTRA, gamePoints: 0, finalCardScore: 0 }); this.potentialPlayers.push({ firebonkId: 3, uuid: 'abc-def-ghi-l', firstName: 'Yannick', team: Team.CONTRA, gamePoints: 0, finalCardScore: 0 }); this.potentialPlayers.push({ firebonkId: 4, uuid: 'abc-def-ghi-m', firstName: 'Janina', team: Team.CONTRA, gamePoints: 0, finalCardScore: 0 }); this.potentialPlayers.push({ firebonkId: 5, uuid: 'abc-def-ghi-n', firstName: 'Moritz', team: Team.CONTRA, gamePoints: 0, finalCardScore: 0 }); this.actualPlayers.push(...this.potentialPlayers.slice(0, 4)); } /** * Switches the entry mask UI to the next page */ switchToNextPage(): void { if (this.currentPage >= 4) { this.calculateCurrentScores(); } if(this.currentPage === 3 && !this.checkIfSolo()) { // If we don't play a solo, we can skip the solo page this.currentPage++; } this.currentPage++; } /** * ____ __ * / __ \/ /___ ___ _____ __________ * / /_/ / / __ `/ / / / _ \/ ___/ ___/ * / ____/ / /_/ / /_/ / __/ / (__ ) * /_/ /_/\__,_/\__, /\___/_/ /____/ * /____/ */ /** * Toggles if the given player is should be an active player for the current game * @param player The player to toggle the activity for */ togglePlayer(player: Player): void { let index = this.actualPlayers.indexOf(player); if (index !== -1) { this.actualPlayers.splice(index, 1); } else { this.actualPlayers.push(player); } } /** * Checks, if the given player is currently marked as active for this game * @param player The player to check the activity status for * @returns boolean If the player is active */ isPlayerActive(player: Player): boolean { return this.actualPlayers.indexOf(player) !== -1; } /** * Returns, if the amount of currently active players is greater or less than 4 */ not4Players() { return this.actualPlayers.length !== 4; } /** * Returns the names of the active players as a comma-separated string */ getPlayerNamesAsString(): string { let playerNames = ''; for (let player of this.actualPlayers) { playerNames += player.firstName + ', '; } // Remove last ", " return playerNames.substring(0, playerNames.length - 2); } /** * ___ __ * / | ____ ____ ____ __ ______ ________ ____ ___ ___ ____ / /______ * / /| | / __ \/ __ \/ __ \/ / / / __ \/ ___/ _ \/ __ `__ \/ _ \/ __ \/ __/ ___/ * / ___ |/ / / / / / / /_/ / /_/ / / / / /__/ __/ / / / / / __/ / / / /_(__ ) * /_/ |_/_/ /_/_/ /_/\____/\__,_/_/ /_/\___/\___/_/ /_/ /_/\___/_/ /_/\__/____/ */ /** * Toggles the activity status for the given announcement and also activates / deactivates other announcements * to conform to the rules. * @param announcement The announcement to toggle activity for */ toggleAnnouncement(announcement: announcements.Announcement): void { let index = this.selectedAnnouncements.indexOf(announcement); if (index !== -1) { this.selectedAnnouncements.splice(index, 1); this.deactivateLowerAnnouncements(announcement); } else { this.selectedAnnouncements.push(announcement); this.activateHigherAnnouncements(announcement); } } /** * Deactivates all lower announcements. E.g.: When RE is deactivated, RE_NO_NINETY and all lower also get deactivated * @param announcement The announcement that has been deactivated */ deactivateLowerAnnouncements(announcement: announcements.Announcement) { // First for RE switch (announcement) { case announcements.Announcement.RE: this.deactivateAnnouncement(announcements.Announcement.RE_NO_NINETY); this.deactivateAnnouncement(announcements.Announcement.RE_NO_SIXTY); this.deactivateAnnouncement(announcements.Announcement.RE_NO_THIRTY); break; case announcements.Announcement.RE_NO_NINETY: this.deactivateAnnouncement(announcements.Announcement.RE_NO_SIXTY); this.deactivateAnnouncement(announcements.Announcement.RE_NO_THIRTY); break; case announcements.Announcement.RE_NO_SIXTY: this.deactivateAnnouncement(announcements.Announcement.RE_NO_THIRTY); break; } // Now for CONTRA switch (announcement) { case announcements.Announcement.CONTRA: this.deactivateAnnouncement(announcements.Announcement.CONTRA_NO_NINETY); this.deactivateAnnouncement(announcements.Announcement.CONTRA_NO_SIXTY); this.deactivateAnnouncement(announcements.Announcement.CONTRA_NO_THIRTY); break; case announcements.Announcement.CONTRA_NO_NINETY: this.deactivateAnnouncement(announcements.Announcement.CONTRA_NO_SIXTY); this.deactivateAnnouncement(announcements.Announcement.CONTRA_NO_THIRTY); break; case announcements.Announcement.CONTRA_NO_SIXTY: this.deactivateAnnouncement(announcements.Announcement.CONTRA_NO_THIRTY); break; } } /** * Deactivates the given announcement if it is active * @param announcement The announcement to deactivate */ deactivateAnnouncement(announcement: announcements.Announcement) { let index = this.selectedAnnouncements.indexOf(announcement); if (index !== -1) { this.selectedAnnouncements.splice(index, 1); } } /** * Activates all higher announcements. E.g.: When RE_NO_NINETY is activated, RE also gets activated * @param announcement The announcement that has been activated */ activateHigherAnnouncements(announcement: announcements.Announcement) { // First for RE switch (announcement) { case announcements.Announcement.RE_NO_THIRTY: this.activateAnnouncement(announcements.Announcement.RE_NO_SIXTY); this.activateAnnouncement(announcements.Announcement.RE_NO_NINETY); this.activateAnnouncement(announcements.Announcement.RE); break; case announcements.Announcement.RE_NO_SIXTY: this.activateAnnouncement(announcements.Announcement.RE_NO_NINETY); this.activateAnnouncement(announcements.Announcement.RE); break; case announcements.Announcement.RE_NO_NINETY: this.activateAnnouncement(announcements.Announcement.RE); break; } // Now for CONTRA switch (announcement) { case announcements.Announcement.CONTRA_NO_THIRTY: this.activateAnnouncement(announcements.Announcement.CONTRA_NO_SIXTY); this.activateAnnouncement(announcements.Announcement.CONTRA_NO_NINETY); this.activateAnnouncement(announcements.Announcement.CONTRA); break; case announcements.Announcement.CONTRA_NO_SIXTY: this.activateAnnouncement(announcements.Announcement.CONTRA_NO_NINETY); this.activateAnnouncement(announcements.Announcement.CONTRA); break; case announcements.Announcement.CONTRA_NO_NINETY: this.activateAnnouncement(announcements.Announcement.CONTRA); break; } } /** * Activates the given announcement if it is inactive * @param announcement The announcement to activate */ activateAnnouncement(announcement: announcements.Announcement) { let index = this.selectedAnnouncements.indexOf(announcement); if (index === -1) { this.selectedAnnouncements.push(announcement); } } /** * Checks if the given announcement is already marked as selected * @param announcement The announcement to check the status for */ isAnnouncementActive(announcement: announcements.Announcement): boolean { return this.selectedAnnouncements.indexOf(announcement) !== -1; } /** * Returns a list of all possible announcements */ getAllPossibleAnnouncements(): announcements.Announcement[] { return announcements.getAllAnnouncementValues(); } /** * Checks, if the currently selected announcements are a valid set of announcements */ checkAnnouncementsValid(): boolean { return announcements.checkValidity(this.selectedAnnouncements); } /** * Returns the two highest announcements for this game (one for RE, one for CONTRA, if applicable) */ getHighestAnnouncements(): string { return announcements.returnTwoHighestAnnouncements(this.selectedAnnouncements); } /** * Checks if the given announcement has been selected * @param announcement The announcement to check */ checkAnnouncementActive(announcement: announcements.Announcement): boolean { let index = this.selectedAnnouncements.indexOf(announcement); return index !== -1; } /** * ______ * /_ __/__ ____ _____ ___ _____ * / / / _ \/ __ `/ __ `__ \/ ___/ * / / / __/ /_/ / / / / / (__ ) * /_/ \___/\__,_/_/ /_/ /_/____/ */ /** * Toggles the players team * @param player The player to toggle the team for */ toggleElderPlayer(player: Player): void { if (player.team === Team.RE) { player.team = Team.CONTRA; } else { player.team = Team.RE; } } /** * Checks if the player is an elder * @param player The player to check */ isPlayerElder(player: Player): boolean { return player.team === Team.RE; } /** * Checks if the current team assignment is valid */ checkValidTeamAssignment(): boolean { let numberOfElderPlayers: number = 0; for (let player of this.actualPlayers) { if (player.team === Team.RE) { numberOfElderPlayers++; } } return !(numberOfElderPlayers !== 1 && numberOfElderPlayers !== 2); } /** * _____ * / ___/_________ ________ _____ * \__ \/ ___/ __ \/ ___/ _ \/ ___/ * ___/ / /__/ /_/ / / / __(__ ) * /____/\___/\____/_/ \___/____/ */ /** * Checks if the sum of all points is exactly 240 */ calculatePointSum(): number { let totalScore: number = 0; for (let player of this.actualPlayers) { totalScore += player.finalCardScore ?? 0; } return totalScore; } /** * Generates a string that explains if the total point sum is too low / too high */ getScoreDifferenceText(): string { let difference = this.calculatePointSum() - 240; if (difference > 0) { return difference + ' more than expected'; } else { difference *= -1; return difference + ' less than expected'; } } /** * ______ _____ * / ____/___ _____ ___ ___ / ___/_________ ________ * / / __/ __ `/ __ `__ \/ _ \ \__ \/ ___/ __ \/ ___/ _ \ * / /_/ / /_/ / / / / / / __/ ___/ / /__/ /_/ / / / __/ * \____/\__,_/_/ /_/ /_/\___/ /____/\___/\____/_/ \___/ */ /** * Calculates the game points for all players */ calculateCurrentScores(): void { let gameScore: number = 0; let winningTeam = this.getWinningTeamAndScore().team; let winningTeamScore = this.getWinningTeamAndScore().score; let unfulfilledAnnouncementPoints = this.checkUnfulfilledAnnouncements(winningTeam, winningTeamScore); let isSoloPlay = this.checkIfSolo(); // We are going to calculate the points for the winning team and then set all players points accordingly if (unfulfilledAnnouncementPoints === 0) { gameScore += this.calculateNormalScore(winningTeamScore, winningTeam); } else { gameScore += unfulfilledAnnouncementPoints; winningTeam = winningTeam === Team.RE ? Team.CONTRA : Team.RE; } // Double Score in case of announcement if (this.checkAnnouncementActive(announcements.Announcement.RE)) { gameScore *= 2; } if (this.checkAnnouncementActive(announcements.Announcement.CONTRA)) { gameScore *= 2; } // Bonus points if (!isSoloPlay) { gameScore += this.getFinalFoxPoints(winningTeam); gameScore += this.getCharliePoints(winningTeam); } this.setGameScores(gameScore, winningTeam, isSoloPlay); } /** * Calculate the score according to card points and announcements in case of a "normal" game, so without unfulfilled announcements * @param winningTeamScore * @param winningTeam * @private */ private calculateNormalScore(winningTeamScore: number, winningTeam: Team): number { let gameScore = 1; // 1 Point for Winning // Won by how much? if (winningTeamScore > 210) { gameScore += 3; } else if (winningTeamScore > 180) { gameScore += 2; } else if (winningTeamScore > 150) { gameScore += 1; } // Announcements if (winningTeam === Team.RE) { if (this.checkAnnouncementActive(announcements.Announcement.RE_NO_NINETY) && winningTeamScore > 150) { gameScore++; } if (this.checkAnnouncementActive(announcements.Announcement.RE_NO_SIXTY) && winningTeamScore > 180) { gameScore++; } if (this.checkAnnouncementActive(announcements.Announcement.RE_NO_THIRTY) && winningTeamScore > 210) { gameScore++; } } if (winningTeam === Team.CONTRA) { if (this.checkAnnouncementActive(announcements.Announcement.CONTRA_NO_NINETY) && winningTeamScore > 150) { gameScore++; } if (this.checkAnnouncementActive(announcements.Announcement.CONTRA_NO_SIXTY) && winningTeamScore > 180) { gameScore++; } if (this.checkAnnouncementActive(announcements.Announcement.CONTRA_NO_THIRTY) && winningTeamScore > 210) { gameScore++; } } return gameScore; } /** * Calculates the team card score of the given players team * @param player The player to calculate the teams points for */ getTeamScore(player: Player): number { let totalTeamScore: number = player.finalCardScore ?? 0; for (let otherPlayer of this.actualPlayers) { if (otherPlayer !== player && otherPlayer.team === player.team) { totalTeamScore += otherPlayer.finalCardScore ?? 0; } } return totalTeamScore; } /** * Calculates which team won and returns the Team Name and the achieved score */ getWinningTeamAndScore(): { team: Team, score: number } { let elderPoints: number = 0; let otherPoints: number = 0; for (let player of this.actualPlayers) { if (player.team === Team.RE) { elderPoints += player.finalCardScore ?? 0; } else { otherPoints += player.finalCardScore ?? 0; } } if (elderPoints > otherPoints) { return { team: Team.RE, score: elderPoints }; } return { team: Team.CONTRA, score: otherPoints }; } /** * Sets the game scores for all players * @param score The score to set * @param winningTeam The team that won */ setGameScores(score: number, winningTeam: Team, isSolo: boolean) { for (let player of this.actualPlayers) { if (player.team === winningTeam) { if(isSolo) { player.gamePoints = score * 3; } else { player.gamePoints = score; } } else { player.gamePoints = -score; } } } /** * Calculates the points that each team got by catching foxes */ calculateFoxPoints(): { re: number, contra: number } { let reFoxesCaught: number = 0; let contraFoxesCaught: number = 0; for (let player of this.actualPlayers) { if (player.team === Team.RE) { reFoxesCaught += player.foxesCaught ?? 0; } else { contraFoxesCaught += player.foxesCaught ?? 0; } } return { re: reFoxesCaught, contra: contraFoxesCaught }; } /** * Calculates the final fox points for the winning team * @param winningTeam The winning team */ getFinalFoxPoints(winningTeam: Team): number { let finalPoints = 0; if (winningTeam === Team.RE) { finalPoints += this.calculateFoxPoints().re; finalPoints -= this.calculateFoxPoints().contra; } else { finalPoints += this.calculateFoxPoints().contra; finalPoints -= this.calculateFoxPoints().re; } return finalPoints; } /** * Returns the extra points for a when a charlie made the final trick. * If the winning team played the charlie, they get one point. If the losing team played the charlie, the winning team loses one point. * @param winningTeam The winning team */ getCharliePoints(winningTeam: Team): number { for (let player of this.actualPlayers) { if (player.wonLastTrickWithCharlie) { return player.team === winningTeam ? 1 : -1; } } return 0; } /** * Checks if the winning team has made announcements that have not been fulfilled. * If so, returns the points that the "losing" team gets for these unfulfilled announcements * @param normalWinningTeam The team that would have won under normal circumstances * @param normalWinningTeamScore The card score of said team */ checkUnfulfilledAnnouncements(normalWinningTeam: Team, normalWinningTeamScore: number): number { let gamePoints = 0; if (normalWinningTeam === Team.RE) { if (this.checkAnnouncementActive(announcements.Announcement.RE_NO_NINETY) && normalWinningTeamScore < 151) { gamePoints++; } if (this.checkAnnouncementActive(announcements.Announcement.RE_NO_SIXTY) && normalWinningTeamScore < 181) { gamePoints++; } if (this.checkAnnouncementActive(announcements.Announcement.RE_NO_THIRTY) && normalWinningTeamScore < 211) { gamePoints++; } } if (normalWinningTeam === Team.CONTRA) { if (this.checkAnnouncementActive(announcements.Announcement.CONTRA_NO_NINETY) && normalWinningTeamScore < 151) { gamePoints++; } if (this.checkAnnouncementActive(announcements.Announcement.CONTRA_NO_SIXTY) && normalWinningTeamScore < 181) { gamePoints++; } if (this.checkAnnouncementActive(announcements.Announcement.CONTRA_NO_THIRTY) && normalWinningTeamScore < 211) { gamePoints++; } } return gamePoints; } /** * _____ __ * / ___/____ / /___ * \__ \/ __ \/ / __ \ * ___/ / /_/ / / /_/ / * /____/\____/_/\____/ */ /** * Checks, according to the assigned Teams, if this is a solo play */ checkIfSolo(): boolean { let numberOfElders: number = 0; for(let player of this.actualPlayers) { if(player.team === Team.RE) { numberOfElders++; } } return numberOfElders === 1; } isSoloActive(solo: solos.Solo): boolean { return this.soloPlayed === solo; } getAllPossibleSolos(): solos.Solo[] { return solos.getAllSoloValues(); } toggleSolo(solo: solos.Solo): void { this.soloPlayed = solo; } }