Doppelkopf-Stats-Frontend/src/app/components/add-game/add-game.component.ts
Patrick Mueller 1027011447
All checks were successful
Jenkins Production Deployment
Adding solo page to add game component
2022-09-16 12:22:32 +02:00

655 lines
18 KiB
TypeScript

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