File size: 6,516 Bytes
507acad
9ecb8cf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
507acad
 
 
 
 
 
 
 
 
 
 
9ecb8cf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# file: battlewords/local_storage.py
"""
Storage module for BattleWords game.

Provides functionality for:
1. Saving/loading game results to local JSON files
2. Managing high scores and leaderboards
3. Sharing game IDs via query strings
"""

from __future__ import annotations
from dataclasses import dataclass, field, asdict
from typing import List, Dict, Optional, Any
from datetime import datetime
import json
import os
from pathlib import Path

@dataclass
class GameResult:
    game_id: str
    wordlist: str
    game_mode: str
    score: int
    tier: str
    elapsed_seconds: int
    words_found: List[str]
    completed_at: str
    player_name: Optional[str] = None

    def to_dict(self) -> Dict[str, Any]:
        return asdict(self)

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "GameResult":
        return cls(**data)

@dataclass
class HighScoreEntry:
    player_name: str
    score: int
    tier: str
    wordlist: str
    game_mode: str
    elapsed_seconds: int
    completed_at: str
    game_id: str

    def to_dict(self) -> Dict[str, Any]:
        return asdict(self)

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "HighScoreEntry":
        return cls(**data)

class GameStorage:
    def __init__(self, storage_dir: Optional[str] = None):
        if storage_dir is None:
            storage_dir = os.path.join(
                os.path.expanduser("~"),
                ".battlewords",
                "data"
            )
        self.storage_dir = Path(storage_dir)
        self.storage_dir.mkdir(parents=True, exist_ok=True)
        self.results_file = self.storage_dir / "game_results.json"
        self.highscores_file = self.storage_dir / "highscores.json"

    def save_result(self, result: GameResult) -> bool:
        try:
            results = self.load_all_results()
            results.append(result.to_dict())
            with open(self.results_file, 'w', encoding='utf-8') as f:
                json.dump(results, f, indent=2, ensure_ascii=False)
            self._update_highscores(result)
            return True
        except Exception as e:
            print(f"Error saving result: {e}")
            return False

    def load_all_results(self) -> List[Dict[str, Any]]:
        if not self.results_file.exists():
            return []
        try:
            with open(self.results_file, 'r', encoding='utf-8') as f:
                return json.load(f)
        except Exception as e:
            print(f"Error loading results: {e}")
            return []

    def get_results_by_game_id(self, game_id: str) -> List[GameResult]:
        all_results = self.load_all_results()
        matching = [
            GameResult.from_dict(r)
            for r in all_results
            if r.get("game_id") == game_id
        ]
        return sorted(matching, key=lambda x: x.score, reverse=True)

    def _update_highscores(self, result: GameResult) -> None:
        highscores = self.load_highscores()
        entry = HighScoreEntry(
            player_name=result.player_name or "Anonymous",
            score=result.score,
            tier=result.tier,
            wordlist=result.wordlist,
            game_mode=result.game_mode,
            elapsed_seconds=result.elapsed_seconds,
            completed_at=result.completed_at,
            game_id=result.game_id
        )
        highscores.append(entry.to_dict())
        highscores.sort(key=lambda x: x["score"], reverse=True)
        highscores = highscores[:100]
        with open(self.highscores_file, 'w', encoding='utf-8') as f:
            json.dump(highscores, f, indent=2, ensure_ascii=False)

    def load_highscores(
        self,
        wordlist: Optional[str] = None,
        game_mode: Optional[str] = None,
        limit: int = 10
    ) -> List[HighScoreEntry]:
        if not self.highscores_file.exists():
            return []
        try:
            with open(self.highscores_file, 'r', encoding='utf-8') as f:
                scores = json.load(f)
            if wordlist:
                scores = [s for s in scores if s.get("wordlist") == wordlist]
            if game_mode:
                scores = [s for s in scores if s.get("game_mode") == game_mode]
            scores.sort(key=lambda x: x["score"], reverse=True)
            return [HighScoreEntry.from_dict(s) for s in scores[:limit]]
        except Exception as e:
            print(f"Error loading highscores: {e}")
            return []

    def get_player_stats(self, player_name: str) -> Dict[str, Any]:
        all_results = self.load_all_results()
        player_results = [
            GameResult.from_dict(r)
            for r in all_results
            if r.get("player_name") == player_name
        ]
        if not player_results:
            return {
                "games_played": 0,
                "total_score": 0,
                "average_score": 0,
                "best_score": 0,
                "best_tier": None
            }
        scores = [r.score for r in player_results]
        return {
            "games_played": len(player_results),
            "total_score": sum(scores),
            "average_score": sum(scores) / len(scores),
            "best_score": max(scores),
            "best_tier": max(player_results, key=lambda x: x.score).tier,
            "fastest_time": min(r.elapsed_seconds for r in player_results)
        }

def save_json_to_file(data: dict, directory: str, filename: str = "settings.json") -> str:
    """
    Save a dictionary as a JSON file with a specified filename in the given directory.
    Returns the full path to the saved file.
    """
    os.makedirs(directory, exist_ok=True)
    file_path = os.path.join(directory, filename)
    with open(file_path, "w", encoding="utf-8") as f:
        json.dump(data, f, indent=2, ensure_ascii=False)
    return file_path

def generate_game_id_from_words(words: List[str]) -> str:
    import hashlib
    sorted_words = sorted([w.upper() for w in words])
    word_string = "".join(sorted_words)
    hash_obj = hashlib.sha256(word_string.encode('utf-8'))
    return hash_obj.hexdigest()[:8].upper()

def parse_game_id_from_url() -> Optional[str]:
    try:
        import streamlit as st
        params = st.query_params
        return params.get("game_id")
    except Exception:
        return None

def create_shareable_url(game_id: str, base_url: Optional[str] = None) -> str:
    if base_url is None:
        base_url = "https://huggingface.co/spaces/Surn/BattleWords"
    return f"{base_url}?game_id={game_id}"