Files
termdoku/internal/savegame/persistence.go

92 lines
1.9 KiB
Go

package savegame
import (
"encoding/json"
"errors"
"io/fs"
"os"
"path/filepath"
"time"
"termdoku/internal/game"
)
// SavedGame represents a saved game state
type SavedGame struct {
Board game.Grid `json:"board"`
Solution game.Grid `json:"solution"`
Given [9][9]bool `json:"given"`
Difficulty string `json:"difficulty"`
Elapsed int64 `json:"elapsed"` // seconds
StartTime time.Time `json:"startTime"`
HintsUsed int `json:"hintsUsed"`
Notes map[string][]uint8 `json:"notes"` // JSON keys must be strings
SavedAt time.Time `json:"savedAt"`
AutoCheck bool `json:"autoCheck"`
TimerEnabled bool `json:"timerEnabled"`
}
// path returns the save file path
func path() (string, error) {
h, err := os.UserHomeDir()
if err != nil {
return "", err
}
return filepath.Join(h, ".termdoku", "savegame.json"), nil
}
// Save writes a game state to disk
func Save(sg SavedGame) error {
p, err := path()
if err != nil {
return err
}
if err := os.MkdirAll(filepath.Dir(p), 0o755); err != nil {
return err
}
data, err := json.MarshalIndent(sg, "", " ")
if err != nil {
return err
}
return os.WriteFile(p, data, 0o644)
}
// Load reads a game state from disk
func Load() (SavedGame, error) {
var sg SavedGame
p, err := path()
if err != nil {
return sg, err
}
b, err := os.ReadFile(p)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
return sg, errors.New("no saved game found")
}
return sg, err
}
if err := json.Unmarshal(b, &sg); err != nil {
return sg, err
}
return sg, nil
}
// Exists checks if a saved game exists
func Exists() bool {
p, err := path()
if err != nil {
return false
}
_, err = os.Stat(p)
return err == nil
}
// Delete removes the saved game
func Delete() error {
p, err := path()
if err != nil {
return err
}
return os.Remove(p)
}