From 50fed29e6217a9cfc50322aa774df14f0fb1e898 Mon Sep 17 00:00:00 2001 From: eguneys Date: Tue, 2 Jan 2024 20:53:16 +0300 Subject: [PATCH] sofo.demo --- src/game.ts | 36 +++++--- src/solitaire.ts | 5 +- src/solitaire_back.ts | 23 ++++- src/solitaire_game.ts | 2 +- src/statistics.ts | 190 ++++++++++++++++++++++++++++++++++++++++++ src/store.ts | 41 +++++++++ 6 files changed, 278 insertions(+), 19 deletions(-) create mode 100644 src/statistics.ts diff --git a/src/game.ts b/src/game.ts index cc95824..e3c3040 100644 --- a/src/game.ts +++ b/src/game.ts @@ -27,7 +27,8 @@ import Sound from './sound' import Trans, { languages } from './trans' import { limit_settings, cards_settings, SolitaireStore, GeneralStore } from './store' - +import { SolitaireResultsStore } from './store' +import { GameResults, OverallResults } from './statistics' type RectData = { @@ -2055,17 +2056,22 @@ class OverallStatistics extends Play { center: true }) + let overall_results = new OverallResults( + SolitaireResultsStore.results, + new GameResults([]), + new GameResults([])) + let h = 100 this.make(TransText, Vec2.make(700, 200), { - key: 'total_games_played%100%', + key: `total_games_played%${overall_results.total_played}%`, width: 820, height: 200, center: true }) this.make(TransText, Vec2.make(700, 200 + h * 1.2), { - key: 'games_won%20%', + key: `games_won%${overall_results.total_wins}%`, width: 820, height: 200, center: true @@ -2078,7 +2084,7 @@ class OverallStatistics extends Play { center: true }) - let top_highscores = [10, 20, 30] + let top_highscores = overall_results.top_5_highscores top_highscores.forEach((top, i) => { this.make(TransText, Vec2.make(200, 200 + h * 4 + h * i * 1.5 + 80), { @@ -2088,7 +2094,7 @@ class OverallStatistics extends Play { no_trans: true }) this.make(TransText, Vec2.make(700, 200 + h * 4 + h * i * 1.5 + 80), { - key: `${top}`, + key: `${top.multiplied_score}`, width: 80, height: 100, no_trans: true, @@ -2111,6 +2117,8 @@ class OverallStatistics extends Play { class SolitaireStatistics extends Play { + height!: number + _init() { this.make(TransText, Vec2.make(700, 0), { @@ -2120,17 +2128,19 @@ class SolitaireStatistics extends Play { center: true }) + let results = SolitaireResultsStore.results + let h = 100 this.make(TransText, Vec2.make(700, 200), { - key: 'total_games_played%100%', + key: `total_games_played%${results.total_played}%`, width: 820, height: 200, center: true }) this.make(TransText, Vec2.make(700, 200 + h * 1.2), { - key: 'games_won%20%', + key: `games_won%${results.total_wins}%`, width: 820, height: 200, center: true @@ -2143,7 +2153,7 @@ class SolitaireStatistics extends Play { center: true }) - let top_highscores = [10, 20, 30] + let top_highscores = results.top_5_highscores top_highscores.forEach((top, i) => { this.make(TransText, Vec2.make(200, 200 + h * 4 + h * i * 1.5 + 80), { @@ -2153,7 +2163,7 @@ class SolitaireStatistics extends Play { no_trans: true }) this.make(TransText, Vec2.make(700, 200 + h * 4 + h * i * 1.5 + 80), { - key: `${top}`, + key: `${top.multiplied_score}`, width: 80, height: 100, no_trans: true, @@ -2441,8 +2451,8 @@ export class MainMenu2 extends Play { _ = this.make(Anim, Vec2.make(200, 150), { name: 'main_bg' }) - let lisotaire = this.make(Text, Vec2.make(16, 32), { text: 'lisotaire', color: Color.hex(0x202431)}) - this.make(Text, Vec2.make(16 + lisotaire.width, 32), { text: '.com', color: Color.hex(0xb4beb4)}) + let sofo = this.make(Text, Vec2.make(16, 32), { text: 'sofo', color: Color.hex(0x202431)}) + this.make(Text, Vec2.make(16 + sofo.width, 32), { text: '.demo', color: Color.hex(0xb4beb4)}) let card_x = 200 @@ -2584,8 +2594,8 @@ class SceneTransition extends Play { //this.current = this._make(CardShowcase, Vec2.zero, {}) // this.current = this._make(MainMenu, Vec2.zero, {}) - this.current = this._make(Statistics2, Vec2.zero, {}) - // this.current = this._make(MainMenu2, Vec2.zero, {}) + //this.current = this._make(Statistics2, Vec2.zero, {}) + this.current = this._make(MainMenu2, Vec2.zero, {}) //this.current = this._make(HowtoPlay2, Vec2.zero, {}) //this.current = this._make(Settings2, Vec2.zero, {}) //this.current = this._make(SolitairePlay, Vec2.zero, {}) diff --git a/src/solitaire.ts b/src/solitaire.ts index 7d3ac41..119f73f 100644 --- a/src/solitaire.ts +++ b/src/solitaire.ts @@ -28,6 +28,8 @@ import { SolitaireGame } from './solitaire_game' import { make_solitaire_back } from './solitaire_back' import { card_sort_key, Settings } from 'lsolitaire' import { Nine } from './nine' +import { SolitaireResultsStore } from './store' +import { SolitaireGameResult } from './statistics' let rnd_screen_poss = [...Array(50).keys()].map(() => v_random().mul(v_screen.scale(0.8))) @@ -691,9 +693,10 @@ export class SolitairePlay extends Play { } } - const on_game_over = (score: number) => { + const on_game_over = (_: Settings, score: number) => { this.game_over_confetti_pop() game_over_dialog = this.make(GameOverDialog, Vec2.make(0, 0), { score }) + SolitaireResultsStore.add_result(SolitaireGameResult.from_win(_, score)) } make_solitaire_back(game, on_score, on_new_game, on_game_over).then(back_res => { diff --git a/src/solitaire_back.ts b/src/solitaire_back.ts index c81819a..1fff14a 100644 --- a/src/solitaire_back.ts +++ b/src/solitaire_back.ts @@ -1,7 +1,8 @@ import { SolitaireGame } from './solitaire_game' import { Settings, Cards, Solitaire, SolitairePov, Game as OSolitaireGame, GamePov, IMoveType, TableuToTableu, IMove, TableuToFoundation, WasteToFoundation } from 'lsolitaire' -import { SolitaireStore } from './store' +import { SolitaireResultsStore, SolitaireStore } from './store' import { arr_random } from './util' +import { SolitaireGameResult } from './statistics' export type BackRes = { game_pov: GamePov, @@ -14,7 +15,7 @@ export type BackRes = { export const make_solitaire_back = async (game: SolitaireGame, on_score: (_: number) => void, on_new_game: (_: Settings) => void, - on_game_over: (_: number) => void): Promise => { + on_game_over: (_: Settings, score: number) => void): Promise => { let back = solitaire_back let game_pov = await back.get_pov() @@ -39,7 +40,7 @@ export const make_solitaire_back = async (game: SolitaireGame, back.get_pov().then(_ => { if (_.game.is_finished) { - on_game_over(_.score) + on_game_over(_.game.settings, _.score) } }) } @@ -67,14 +68,28 @@ export const make_solitaire_back = async (game: SolitaireGame, } }, async new_game() { + + + back.get_pov().then(_ => { + let score = _.score + + if (score > 0) { + if (!_.game.is_finished) { + SolitaireResultsStore.add_result(SolitaireGameResult.from_loss(_.game.settings, score)) + } + } + }) + + solitaire_back = new SolitaireBack() back = solitaire_back game_pov = await solitaire_back.get_pov() - + game.new_game() back.get_pov().then(_ => on_score(_.score)) back.get_pov().then(_ => on_new_game(_.game.settings)) + } } diff --git a/src/solitaire_game.ts b/src/solitaire_game.ts index 9dafda8..42b6f51 100644 --- a/src/solitaire_game.ts +++ b/src/solitaire_game.ts @@ -14,7 +14,7 @@ import { HitStock, Recycle, TableuToFoundation, FoundationToTableu, } from 'lsolitaire' -import { SolitaireStore } from './store' +import { SolitaireStore, SolitaireResultsStore } from './store' import { ticks } from './shared' import { appr } from './lerp' diff --git a/src/statistics.ts b/src/statistics.ts new file mode 100644 index 0000000..808a860 --- /dev/null +++ b/src/statistics.ts @@ -0,0 +1,190 @@ +import { Settings, TurningCards, TurningLimit } from 'lsolitaire' + +const Solitaire_Multipliers = { + 'threecards': 1, + 'onecard': 1.5, + 'nolimit': 1, + 'threepass': 1.5, + 'onepass': 2.2 +} + +export class SpiderGameResult implements AGameResult { + + get multiplied_score() { + return this.score + } + + constructor(readonly win: boolean, readonly score: number) {} + + get fen() { + return '' + } + +} + +export class FreecellGameResult implements AGameResult { + + get multiplied_score() { + return this.score + } + + constructor(readonly win: boolean, readonly score: number) {} + + get fen() { + return '' + } + +} + +export class SolitaireGameResult implements AGameResult { + + get turning_cards_multiplier() { + return Solitaire_Multipliers[this.turning_cards] + } + + get turning_limit_multiplier() { + return Solitaire_Multipliers[this.turning_limit] + } + + get multiplied_score() { + return this.score * (this.turning_cards_multiplier + this.turning_limit_multiplier) + } + + get fen() { + return [this.win, this.score, this.turning_cards, this.turning_limit].join('/') + } + + static from_fen = (fen: string) => { + let [win, score, turning_cards, turning_limit] = fen.split('/') + return new SolitaireGameResult(win === 'true', + parseInt(score), + turning_cards as TurningCards, turning_limit as TurningLimit) + } + + static from_win = (settings: Settings, score: number) => { + return new SolitaireGameResult(true, score, settings.cards, settings.limit) + } + + static from_loss = (settings: Settings, score: number) => { + return new SolitaireGameResult(false, score, settings.cards, settings.limit) + } + + + + constructor( + readonly win: boolean, + readonly score: number, + readonly turning_cards: TurningCards, + readonly turning_limit: TurningLimit + ) {} +} + +interface AGameResult { + win: boolean, + multiplied_score: number, + fen: string +} + +export class GameResults { + + get fen() { + return this.list.map(_ => _.fen).join('$') + } + + static from_fen = (fen: string, from_fen: (_: string) => A) => { + return new GameResults(fen==='' ? []:fen.split('$').map(_ => from_fen(_))) + } + + constructor(readonly list: A[]) {} + + push(_: A) { + this.list.push(_) + } + + clear() { + this.list.length = 0 + } + + get total_wins() { + return this.list.filter(_ => _.win).length + } + + get total_played() { + return this.list.length + } + + get top_5_highscores() { + return this.list + .sort((a, b) => b.multiplied_score - a.multiplied_score) + .slice(0, 5) + } +} + +export class OverallResults { + + constructor( + readonly solitaire: GameResults, + readonly freecell: GameResults, + readonly spider: GameResults + ) {} + + + get total_wins() { + return this.freecell_total_wins + this.spider_total_wins + this.solitaire_total_wins + } + + get total_played() { + return this.freecell_total_played + this.spider_total_played + this.solitaire_total_played + } + + get top_5_highscores() { + return [ + ...this.freecell_top_5_highscores, + ...this.spider_top_5_highscores, + ...this.solitaire_top_5_highscores, + ] + .sort((a, b) => b.multiplied_score - a.multiplied_score) + .slice(0, 5) + } + + get freecell_total_wins() { + return this.freecell.total_wins + } + + get freecell_total_played() { + return this.freecell.total_played + } + + get freecell_top_5_highscores() { + return this.freecell.top_5_highscores + } + + + + get spider_total_wins() { + return this.spider.total_wins + } + + get spider_total_played() { + return this.spider.total_played + } + + get spider_top_5_highscores() { + return this.spider.top_5_highscores + } + + + + get solitaire_total_wins() { + return this.solitaire.total_wins + } + + get solitaire_total_played() { + return this.solitaire.total_played + } + + get solitaire_top_5_highscores() { + return this.solitaire.top_5_highscores + } + +} \ No newline at end of file diff --git a/src/store.ts b/src/store.ts index d5bc306..0c52168 100644 --- a/src/store.ts +++ b/src/store.ts @@ -2,6 +2,7 @@ import { Language } from './trans' import { TurningLimit, TurningCards, Settings, Solitaire, Cards } from 'lsolitaire' import { StoredJsonProp, storedJsonProp } from './storage' +import { GameResults, SolitaireGameResult } from './statistics' export let limit_settings: Array = ['nolimit', 'threepass', 'onepass'] export let cards_settings: Array = ['threecards', 'onecard'] @@ -104,3 +105,43 @@ let general = new GeneralStore() export { solitaire as SolitaireStore } export { general as GeneralStore } + + + +class SolitaireResultsStore { + + _results!: StoredJsonProp + + set results(_: GameResults) { + this._results(_.fen) + } + + get results() { + return GameResults.from_fen(this._results(), SolitaireGameResult.from_fen) + } + + add_result(_: SolitaireGameResult) { + let results = this.results + + results.push(_) + + + this.results = results + } + + clear_results() { + let results = this.results + + results.clear() + + + this.results = results + } + + constructor() { + this._results = storedJsonProp('solitaire_results', () => new GameResults([]).fen) + } +} + +let solitaire_results = new SolitaireResultsStore() +export { solitaire_results as SolitaireResultsStore } \ No newline at end of file