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) }