2020-08-08 02:41:21 +02:00
|
|
|
import {formatTime} from "./Common";
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A storage for game statistics.
|
|
|
|
*/
|
|
|
|
export interface Statistics {
|
|
|
|
actionsUndone: number;
|
|
|
|
actionsRedone: number;
|
|
|
|
lossesUndone: number;
|
2020-08-09 17:38:39 +02:00
|
|
|
timeSpent: number;
|
2020-08-08 02:41:21 +02:00
|
|
|
|
|
|
|
gamesStarted: number;
|
|
|
|
gamesLost: number;
|
|
|
|
gamesWon: number;
|
2020-08-09 14:28:25 +02:00
|
|
|
gamesWonWithoutLosing: number;
|
2020-08-08 02:41:21 +02:00
|
|
|
|
|
|
|
squaresChorded: number;
|
|
|
|
squaresChordedLeadingToLoss: number;
|
|
|
|
squaresFlagged: number;
|
2020-08-12 18:52:02 +02:00
|
|
|
squaresMarked: number;
|
2020-08-08 02:41:21 +02:00
|
|
|
squaresUncovered: number;
|
|
|
|
|
2020-08-09 17:38:39 +02:00
|
|
|
hintsRequested: number;
|
|
|
|
solverUsages: number;
|
2020-08-08 02:41:21 +02:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Resets the statistics.
|
|
|
|
*/
|
|
|
|
clear(): void;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Stores game statistics in the browser's localstorage.
|
|
|
|
*/
|
|
|
|
export class LocalStatistics implements Statistics {
|
|
|
|
private static readonly storageKey = "/tools/minesweeper//statistics";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reads the object stored in local storage.
|
|
|
|
*
|
|
|
|
* @return the object stored in local storage, or an empty object if there is nothing in the local storage
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
private read(): { [key: string]: string } {
|
|
|
|
return JSON.parse(localStorage.getItem(LocalStatistics.storageKey) ?? "{}");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Writes the given object to local storage.
|
|
|
|
*
|
|
|
|
* @param statistics the object to write to local storage
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
private write(statistics: { [key: string]: string }): void {
|
|
|
|
localStorage.setItem(LocalStatistics.storageKey, JSON.stringify(statistics));
|
|
|
|
}
|
|
|
|
|
|
|
|
clear(): void {
|
|
|
|
localStorage.removeItem(LocalStatistics.storageKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
get actionsUndone(): number {
|
|
|
|
return +(this.read()["actionsUndone"] ?? 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
set actionsUndone(value: number) {
|
|
|
|
const statistics = this.read();
|
|
|
|
statistics["actionsUndone"] = "" + value;
|
|
|
|
this.write(statistics);
|
|
|
|
}
|
|
|
|
|
|
|
|
get actionsRedone(): number {
|
|
|
|
return +(this.read()["actionsRedone"] ?? 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
set actionsRedone(value: number) {
|
|
|
|
const statistics = this.read();
|
|
|
|
statistics["actionsRedone"] = "" + value;
|
|
|
|
this.write(statistics);
|
|
|
|
}
|
|
|
|
|
|
|
|
get lossesUndone(): number {
|
|
|
|
return +(this.read()["lossesUndone"] ?? 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
set lossesUndone(value: number) {
|
|
|
|
const statistics = this.read();
|
|
|
|
statistics["lossesUndone"] = "" + value;
|
|
|
|
this.write(statistics);
|
|
|
|
}
|
|
|
|
|
2020-08-09 17:38:39 +02:00
|
|
|
get timeSpent(): number {
|
|
|
|
return +(this.read()["timeSpent"] ?? 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
set timeSpent(value: number) {
|
|
|
|
const statistics = this.read();
|
|
|
|
statistics["timeSpent"] = "" + value;
|
|
|
|
this.write(statistics);
|
|
|
|
}
|
|
|
|
|
2020-08-08 02:41:21 +02:00
|
|
|
get gamesStarted(): number {
|
|
|
|
return +(this.read()["gamesStarted"] ?? 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
set gamesStarted(value: number) {
|
|
|
|
const statistics = this.read();
|
|
|
|
statistics["gamesStarted"] = "" + value;
|
|
|
|
this.write(statistics);
|
|
|
|
}
|
|
|
|
|
|
|
|
get gamesLost(): number {
|
|
|
|
return +(this.read()["gamesLost"] ?? 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
set gamesLost(value: number) {
|
|
|
|
const statistics = this.read();
|
|
|
|
statistics["gamesLost"] = "" + value;
|
|
|
|
this.write(statistics);
|
|
|
|
}
|
|
|
|
|
|
|
|
get gamesWon(): number {
|
|
|
|
return +(this.read()["gamesWon"] ?? 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
set gamesWon(value: number) {
|
|
|
|
const statistics = this.read();
|
|
|
|
statistics["gamesWon"] = "" + value;
|
|
|
|
this.write(statistics);
|
|
|
|
}
|
|
|
|
|
2020-08-09 14:28:25 +02:00
|
|
|
get gamesWonWithoutLosing(): number {
|
|
|
|
return +(this.read()["gamesWonWithoutLosing"] ?? 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
set gamesWonWithoutLosing(value: number) {
|
|
|
|
const statistics = this.read();
|
|
|
|
statistics["gamesWonWithoutLosing"] = "" + value;
|
|
|
|
this.write(statistics);
|
|
|
|
}
|
|
|
|
|
2020-08-08 02:41:21 +02:00
|
|
|
get squaresChorded(): number {
|
|
|
|
return +(this.read()["squaresChorded"] ?? 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
set squaresChorded(value: number) {
|
|
|
|
const statistics = this.read();
|
|
|
|
statistics["squaresChorded"] = "" + value;
|
|
|
|
this.write(statistics);
|
|
|
|
}
|
|
|
|
|
|
|
|
get squaresChordedLeadingToLoss(): number {
|
|
|
|
return +(this.read()["squaresChordedLeadingToLoss"] ?? 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
set squaresChordedLeadingToLoss(value: number) {
|
|
|
|
const statistics = this.read();
|
|
|
|
statistics["squaresChordedLeadingToLoss"] = "" + value;
|
|
|
|
this.write(statistics);
|
|
|
|
}
|
|
|
|
|
|
|
|
get squaresFlagged(): number {
|
|
|
|
return +(this.read()["squaresFlagged"] ?? 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
set squaresFlagged(value: number) {
|
|
|
|
const statistics = this.read();
|
|
|
|
statistics["squaresFlagged"] = "" + value;
|
|
|
|
this.write(statistics);
|
|
|
|
}
|
|
|
|
|
2020-08-12 18:52:02 +02:00
|
|
|
get squaresMarked(): number {
|
|
|
|
return +(this.read()["squaresMarked"] ?? 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
set squaresMarked(value: number) {
|
|
|
|
const statistics = this.read();
|
|
|
|
statistics["squaresMarked"] = "" + value;
|
|
|
|
this.write(statistics);
|
|
|
|
}
|
|
|
|
|
2020-08-08 02:41:21 +02:00
|
|
|
get squaresUncovered(): number {
|
|
|
|
return +(this.read()["squaresUncovered"] ?? 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
set squaresUncovered(value: number) {
|
|
|
|
const statistics = this.read();
|
|
|
|
statistics["squaresUncovered"] = "" + value;
|
|
|
|
this.write(statistics);
|
|
|
|
}
|
|
|
|
|
2020-08-09 17:38:39 +02:00
|
|
|
get hintsRequested(): number {
|
|
|
|
return +(this.read()["hintsRequested"] ?? 0);
|
2020-08-08 02:41:21 +02:00
|
|
|
}
|
|
|
|
|
2020-08-09 17:38:39 +02:00
|
|
|
set hintsRequested(value: number) {
|
2020-08-08 02:41:21 +02:00
|
|
|
const statistics = this.read();
|
2020-08-09 17:38:39 +02:00
|
|
|
statistics["hintsRequested"] = "" + value;
|
|
|
|
this.write(statistics);
|
|
|
|
}
|
|
|
|
|
|
|
|
get solverUsages(): number {
|
|
|
|
return +(this.read()["solverUsages"] ?? 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
set solverUsages(value: number) {
|
|
|
|
const statistics = this.read();
|
|
|
|
statistics["solverUsages"] = "" + value;
|
2020-08-08 02:41:21 +02:00
|
|
|
this.write(statistics);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generates an HTML report of the current statistics.
|
|
|
|
*/
|
|
|
|
generateHtmlReport(): string {
|
|
|
|
return "" +
|
|
|
|
`<h3>Time and history</h3>
|
|
|
|
<table>
|
|
|
|
<tr>
|
|
|
|
<th>Actions undone</th>
|
2020-08-09 02:01:37 +02:00
|
|
|
<td>${this.actionsUndone}</td>
|
2020-08-08 02:41:21 +02:00
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<th>Actions redone</th>
|
2020-08-09 02:01:37 +02:00
|
|
|
<td>${this.actionsRedone}</td>
|
2020-08-08 02:41:21 +02:00
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<th>Losses undone</th>
|
2020-08-09 02:01:37 +02:00
|
|
|
<td>${this.lossesUndone}</td>
|
2020-08-08 02:41:21 +02:00
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<th>Time spent</th>
|
2020-08-09 02:01:37 +02:00
|
|
|
<td>${formatTime(Math.floor(+this.timeSpent / 1000), true, true)}</td>
|
2020-08-08 02:41:21 +02:00
|
|
|
</tr>
|
|
|
|
</table>
|
|
|
|
|
2020-08-12 16:32:50 +02:00
|
|
|
<h3>Wins and losses</h3>
|
2020-08-08 02:41:21 +02:00
|
|
|
<table>
|
|
|
|
<tr>
|
|
|
|
<th>Games started</th>
|
2020-08-09 02:01:37 +02:00
|
|
|
<td>${this.gamesStarted}</td>
|
2020-08-08 02:41:21 +02:00
|
|
|
</tr>
|
|
|
|
<tr>
|
2020-08-12 16:32:50 +02:00
|
|
|
<th>Games completed</th>
|
2020-08-09 02:01:37 +02:00
|
|
|
<td>${this.gamesWon}</td>
|
2020-08-08 02:41:21 +02:00
|
|
|
</tr>
|
2020-08-09 14:28:25 +02:00
|
|
|
<tr>
|
2020-08-12 16:32:50 +02:00
|
|
|
<th>Games completed without losing</th>
|
2020-08-09 14:28:25 +02:00
|
|
|
<td>${this.gamesWonWithoutLosing}</td>
|
|
|
|
</tr>
|
2020-08-12 16:32:50 +02:00
|
|
|
<tr>
|
|
|
|
<th>Games completed with at least one loss</th>
|
|
|
|
<td>${this.gamesLost}</td>
|
|
|
|
</tr>
|
2020-08-08 02:41:21 +02:00
|
|
|
</table>
|
|
|
|
|
|
|
|
<h3>Steps taken</h3>
|
|
|
|
<table>
|
|
|
|
<tr>
|
|
|
|
<th>Squares chorded</th>
|
2020-08-09 02:01:37 +02:00
|
|
|
<td>${this.squaresChorded}</td>
|
2020-08-08 02:41:21 +02:00
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<th>Squares chorded leading to loss</th>
|
2020-08-09 02:01:37 +02:00
|
|
|
<td>${this.squaresChordedLeadingToLoss}</td>
|
2020-08-08 02:41:21 +02:00
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<th>Squares flagged</th>
|
2020-08-09 02:01:37 +02:00
|
|
|
<td>${this.squaresFlagged}</td>
|
2020-08-08 02:41:21 +02:00
|
|
|
</tr>
|
2020-08-12 18:52:02 +02:00
|
|
|
<tr>
|
|
|
|
<th>Squares marked</th>
|
|
|
|
<td>${this.squaresMarked}</td>
|
|
|
|
</tr>
|
2020-08-08 02:41:21 +02:00
|
|
|
<tr>
|
|
|
|
<th>Squares uncovered</th>
|
2020-08-09 02:01:37 +02:00
|
|
|
<td>${this.squaresUncovered}</td>
|
2020-08-08 02:41:21 +02:00
|
|
|
</tr>
|
2020-08-09 17:38:39 +02:00
|
|
|
</table>
|
|
|
|
|
|
|
|
<h3>Solver usage</h3>
|
|
|
|
<table>
|
|
|
|
<tr>
|
|
|
|
<th>Hints requested</th>
|
|
|
|
<td>${this.hintsRequested}</td>
|
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<th>Solver usages</th>
|
|
|
|
<td>${this.solverUsages}</td>
|
|
|
|
</tr>
|
2020-08-08 02:41:21 +02:00
|
|
|
</table>`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Stores game statistics in memory, right here in the fields of this object.
|
|
|
|
*/
|
|
|
|
export class MemoryStatistics implements Statistics {
|
|
|
|
actionsUndone: number = 0;
|
|
|
|
actionsRedone: number = 0;
|
|
|
|
lossesUndone: number = 0;
|
2020-08-09 17:38:39 +02:00
|
|
|
timeSpent: number = 0;
|
2020-08-08 02:41:21 +02:00
|
|
|
gamesLost: number = 0;
|
|
|
|
gamesStarted: number = 0;
|
|
|
|
gamesWon: number = 0;
|
2020-08-09 14:28:25 +02:00
|
|
|
gamesWonWithoutLosing: number = 0;
|
2020-08-08 02:41:21 +02:00
|
|
|
squaresChorded: number = 0;
|
|
|
|
squaresChordedLeadingToLoss: number = 0;
|
|
|
|
squaresFlagged: number = 0;
|
2020-08-12 18:52:02 +02:00
|
|
|
squaresMarked: number = 0;
|
2020-08-08 02:41:21 +02:00
|
|
|
squaresUncovered: number = 0;
|
2020-08-09 17:38:39 +02:00
|
|
|
hintsRequested: number = 0;
|
|
|
|
solverUsages: number = 0;
|
2020-08-08 02:41:21 +02:00
|
|
|
|
|
|
|
|
|
|
|
clear() {
|
|
|
|
this.actionsUndone = 0;
|
|
|
|
this.actionsRedone = 0;
|
|
|
|
this.lossesUndone = 0;
|
2020-08-09 17:38:39 +02:00
|
|
|
this.timeSpent = 0;
|
2020-08-08 02:41:21 +02:00
|
|
|
this.gamesLost = 0;
|
|
|
|
this.gamesStarted = 0;
|
|
|
|
this.gamesWon = 0;
|
2020-08-09 14:28:25 +02:00
|
|
|
this.gamesWonWithoutLosing = 0;
|
2020-08-08 02:41:21 +02:00
|
|
|
this.squaresChorded = 0;
|
|
|
|
this.squaresChordedLeadingToLoss = 0;
|
|
|
|
this.squaresFlagged = 0;
|
2020-08-12 18:52:02 +02:00
|
|
|
this.squaresMarked = 0;
|
2020-08-08 02:41:21 +02:00
|
|
|
this.squaresUncovered = 0;
|
2020-08-09 17:38:39 +02:00
|
|
|
this.hintsRequested = 0;
|
|
|
|
this.solverUsages = 0;
|
2020-08-08 02:41:21 +02:00
|
|
|
}
|
|
|
|
}
|