Finalise high score implementation

Fixes #87.
This commit is contained in:
Florine W. Dekker 2020-09-02 20:32:28 +02:00
parent 53dd327786
commit e64d20f3bd
Signed by: FWDekker
GPG Key ID: B1B567AF58D6EE0F
5 changed files with 47 additions and 11 deletions

View File

@ -62,6 +62,7 @@ form button.cancel {
.overlay { .overlay {
padding: 6rem; padding: 6rem;
min-width: 33%;
max-height: 80%; max-height: 80%;
overflow: auto; overflow: auto;

View File

@ -3,6 +3,7 @@
*/ */
export class Difficulty { export class Difficulty {
readonly name: string; readonly name: string;
readonly description: string | null;
readonly width: number; readonly width: number;
readonly height: number; readonly height: number;
readonly mineCount: number; readonly mineCount: number;
@ -13,13 +14,16 @@ export class Difficulty {
* Constructs a new difficulty. * Constructs a new difficulty.
* *
* @param name the name of the difficulty * @param name the name of the difficulty
* @param description the properties of the difficulty
* @param width the width of the field in this difficulty * @param width the width of the field in this difficulty
* @param height the height of the field in this difficulty * @param height the height of the field in this difficulty
* @param mineCount the number of mines in this difficulty * @param mineCount the number of mines in this difficulty
* @param solvable whether the field is guaranteed to be solvable in this difficulty * @param solvable whether the field is guaranteed to be solvable in this difficulty
*/ */
constructor(name: string, width: number, height: number, mineCount: number, solvable: boolean) { constructor(name: string, description: string | null, width: number, height: number, mineCount: number,
solvable: boolean) {
this.name = name; this.name = name;
this.description = description;
this.width = width; this.width = width;
this.height = height; this.height = height;
this.mineCount = mineCount; this.mineCount = mineCount;
@ -31,10 +35,10 @@ export class Difficulty {
* The default difficulty levels. * The default difficulty levels.
*/ */
export const difficulties: Difficulty[] = [ export const difficulties: Difficulty[] = [
new Difficulty("Beginner (9x9, 10 mines)", 9, 9, 10, true), new Difficulty("Beginner", "9x9, 10 mines", 9, 9, 10, true),
new Difficulty("Intermediate (16x16, 40 mines)", 16, 16, 40, true), new Difficulty("Intermediate", "16x16, 40 mines", 16, 16, 40, true),
new Difficulty("Expert (30x16, 99 mines)", 30, 16, 99, true), new Difficulty("Expert", "30x16, 99 mines", 30, 16, 99, true),
new Difficulty("Custom", 0, 0, 0, false) new Difficulty("Custom", null, 0, 0, 0, false)
]; ];
/** /**
@ -64,4 +68,4 @@ export const findDifficulty = function(width: number, height: number, mineCount:
} }
return customDifficulty; return customDifficulty;
} };

View File

@ -87,7 +87,7 @@ export class Game {
difficulties.forEach(it => { difficulties.forEach(it => {
const option = document.createElement("option"); const option = document.createElement("option");
option.value = it.name; option.value = it.name;
option.innerHTML = it.name; option.innerHTML = `${it.name}${it.description !== null ? ` (${it.description})` : ""}`;
this.difficultySelect.add(option); this.difficultySelect.add(option);
}); });
this.difficultySelect.addEventListener( this.difficultySelect.addEventListener(

View File

@ -1,3 +1,4 @@
import {formatTime} from "./Common";
import {difficulties, Difficulty} from "./Difficulty"; import {difficulties, Difficulty} from "./Difficulty";
import {LocalStorage, Storage} from "./Storage"; import {LocalStorage, Storage} from "./Storage";
@ -16,15 +17,29 @@ export class HighScores {
private readonly storage: Storage; private readonly storage: Storage;
/**
* Constructs a new container for high scores.
*
* @param storage the underlying object to store high scores in
*/
constructor(storage: Storage = new LocalStorage("/tools/minesweeper//high-scores")) { constructor(storage: Storage = new LocalStorage("/tools/minesweeper//high-scores")) {
this.storage = storage; this.storage = storage;
} }
/**
* Clears all high scores.
*/
clear(): void { clear(): void {
this.storage.clear(); this.storage.clear();
} }
/**
* Adds a new high score.
*
* @param difficulty the difficulty to associate the score to
* @param score the score to store
*/
addScore(difficulty: Difficulty, score: Score): void { addScore(difficulty: Difficulty, score: Score): void {
const scores = this.storage.getArray(difficulty.name, []); const scores = this.storage.getArray(difficulty.name, []);
scores.push(score); scores.push(score);
@ -32,19 +47,35 @@ export class HighScores {
this.storage.setArray(difficulty.name, scores.slice(0, HighScores.scoresToStore)); this.storage.setArray(difficulty.name, scores.slice(0, HighScores.scoresToStore));
} }
/**
* Returns all high scores associated with the given difficulty.
*
* @param difficulty the difficulty to return the high scores for
* @returns all high scores associated with the given difficulty
*/
getScores(difficulty: Difficulty): Score[] { getScores(difficulty: Difficulty): Score[] {
return this.storage.getArray(difficulty.name, []); return this.storage.getArray(difficulty.name, []);
} }
/**
* Generates an HTML report of all high scores.
*
* @returns an HTML report of all high scores
*/
generateHtmlReport(): string { generateHtmlReport(): string {
let report = ""; let report = "";
for (let i = 0; i < difficulties.length; i++) { for (let i = 0; i < difficulties.length; i++) {
const difficulty = difficulties[i]; const difficulty = difficulties[i];
report += `<h3>${difficulty.name}</h3>`;
const highScores = this.getScores(difficulty); const highScores = this.getScores(difficulty);
if (highScores.length === 0) {
report += "<i>You have no high scores for this difficulty yet.</i><br /><br />";
continue;
}
report += "" + report += "" +
`<h3>${difficulty.name}</h3> `<table>
<table>
<tr> <tr>
<th>Time (seconds)</th> <th>Time (seconds)</th>
<th>Deaths</th> <th>Deaths</th>
@ -53,7 +84,7 @@ export class HighScores {
const score = highScores[j]; const score = highScores[j];
report += "" + report += "" +
`<tr> `<tr>
<td>${score.time / 1000}</td> <td>${formatTime(Math.floor(score.time / 1000), true, false)}</td>
<td>${score.deaths}</td> <td>${score.deaths}</td>
</tr>`; </tr>`;
} }

View File

@ -178,7 +178,7 @@ export class Statistics implements Statistics {
${this.lossesRedone > 0 ? `<tr><th>Losses redone</th><td>${this.lossesRedone}</td></tr>` : ``} ${this.lossesRedone > 0 ? `<tr><th>Losses redone</th><td>${this.lossesRedone}</td></tr>` : ``}
<tr> <tr>
<th>Time spent</th> <th>Time spent</th>
<td>${formatTime(Math.floor(+this.timeSpent / 1000), true, true)}</td> <td>${formatTime(Math.floor(this.timeSpent / 1000), true, true)}</td>
</tr> </tr>
</table> </table>