This document outlines the detailed implementation plan for step 2.2 of the Conway's Game of Life improvements: Save/Load Game States functionality.
- Modular structure: Well-organized modules (game.zig, config.zig, cli.zig, etc.)
- Configuration system: Already has TOML-based config loading/saving
- CLI parsing: Supports named flags and positional arguments
- Grid representation: Simple
[]u8array with helper functions - Memory management: Uses arena allocator for temporary allocations
- CLI module: Extend with
--saveand--loadflags - Game module: Add serialization/deserialization functions
- Main loop: Add save/load trigger points
- Config system: Reuse existing file I/O patterns
Advantages: Human-readable, consistent with existing config system, familiar format
File extension: .cgol
# Conway's Game of Life Save File
version = "1.0"
[metadata]
saved_at = "2025-09-23T14:30:00Z"
description = "Gosper glider gun at generation 150"
original_pattern = "gosperglidergun.rle"
[game_state]
current_generation = 150
rows = 40
cols = 60
generations = 1000
delay_ms = 100
[grid_data]
encoding = "rle_compressed"
data = "40b$5bo$3bo$4b2o$..."Advantages: Smaller file size, faster I/O
File extension: .cgol
const SaveFileHeader = struct {
magic: [4]u8 = "CGOL",
version: u16 = 1,
rows: u32,
cols: u32,
generation: u64,
generations: u64,
delay_ms: u64,
metadata_size: u32,
grid_data_size: u32,
};// In a new module: src/saveload.zig
const SavedGameState = struct {
// Metadata
version: []const u8,
saved_at: []const u8,
description: ?[]const u8,
original_pattern: ?[]const u8,
// Game state
current_generation: u64,
rows: usize,
cols: usize,
generations: u64,
delay_ms: u64,
// Grid data (compressed)
grid_data: []const u8,
pub fn deinit(self: *SavedGameState, allocator: std.mem.Allocator) void {
// Cleanup allocated strings
}
};
const SaveLoadError = error{
InvalidFileFormat,
UnsupportedVersion,
CorruptedData,
IncompatibleGridSize,
FileNotFound,
PermissionDenied,
};// Compress grid using RLE for efficient storage
fn compressGrid(grid: []const u8, rows: usize, cols: usize, allocator: std.mem.Allocator) ![]u8 {
// Implementation: Count consecutive dead/alive cells
// Format: "40b$5bo$3bo$4b2o$..." (standard RLE format)
}
fn decompressGrid(compressed: []const u8, grid: []u8, rows: usize, cols: usize) !void {
// Implementation: Parse RLE and populate grid
}# Save current state
cgol --save checkpoint.cgol --generations 1000 --delay 100
# Load saved state
cgol --load checkpoint.cgol
# Save with metadata
cgol --save "glider_gen_50.cgol" --description "Glider pattern at generation 50"
# Auto-save every N generations
cgol --auto-save-every 100 --save-prefix "backup_"
# List available save files
cgol --list-savespub const CliArgs = struct {
// Existing fields...
force_prompt: bool = false,
rows: ?usize = null,
cols: ?usize = null,
generations: ?u64 = null,
delay_ms: ?u64 = null,
pattern_file: ?[]const u8 = null,
show_help: bool = false,
// New save/load fields
save_file: ?[]const u8 = null,
load_file: ?[]const u8 = null,
save_description: ?[]const u8 = null,
auto_save_every: ?u64 = null,
save_prefix: ?[]const u8 = null,
list_saves: bool = false,
};Estimated time: 2-3 hours
- Define data structures (
SavedGameState, error types) - Implement grid compression/decompression functions
- Create TOML serialization/deserialization functions
- Add file I/O functions with error handling
- Add new save/load command line options
- Update help text with new options
- Add validation for save/load file paths
- Handle conflicting options (save + load together)
// Primary interface functions
pub fn saveGameState(
filepath: []const u8,
grid: []const u8,
rows: usize,
cols: usize,
generation: u64,
config: GameConfig,
description: ?[]const u8,
allocator: std.mem.Allocator
) SaveLoadError!void
pub fn loadGameState(
filepath: []const u8,
allocator: std.mem.Allocator
) SaveLoadError!SavedGameState
pub fn listSaveFiles(
directory: []const u8,
allocator: std.mem.Allocator
) ![][]const u8Estimated time: 1-2 hours
- Check for
--loadflag and load state before simulation - Handle grid allocation based on loaded dimensions
- Initialize generation counter from loaded state
- Restore configuration from saved state
- Save on explicit
--saveflag - Auto-save functionality during main loop
- Save on graceful exit (SIGINT handling)
- Save before pattern loading (backup current state)
- Graceful handling of corrupted save files
- Fallback to default initialization on load failure
- User-friendly error messages for save/load issues
Estimated time: 2-3 hours
- Keyboard shortcuts during simulation:
- 's': Quick save with timestamp
- 'l': List and load from available saves
- 'S': Save with custom name prompt
- Integration with existing input handling
- Configurable auto-save intervals
- Rotating backup system (keep last N saves)
- Background save to avoid simulation interruption
- Progress indication for large grid saves
- Pattern detection and classification
- Statistical summaries (population, stability)
- Save preview/thumbnail generation
- File validation and repair utilities
Estimated time: 1-2 hours
test "save and load grid state" {
// Test basic save/load functionality
}
test "grid compression and decompression" {
// Test RLE compression preserves data
}
test "handle corrupted save files" {
// Test error handling for malformed files
}
test "auto-save functionality" {
// Test periodic saving during simulation
}- Test full CLI workflow with save/load
- Test large grid performance
- Test edge cases (empty grids, single cells)
- Test file permissions and disk space handling
- Update CLAUDE.md with new commands
- Add save/load examples to README
- Document file format specification
- Create troubleshooting guide
src/
├── saveload.zig # New: Save/load functionality
└── ...existing files...
saves/ # New: Default save directory
├── .gitkeep
└── README.md # Instructions for save files
tests/
├── saveload_test.zig # New: Save/load tests
└── ...existing tests...
// In constants.zig
pub const SAVE_FILE_EXTENSION = ".cgol";
pub const SAVE_FILE_VERSION = "1.0";
pub const SAVE_DIR_DEFAULT = "saves";
pub const AUTO_SAVE_PREFIX = "auto_";
pub const MAX_SAVE_DESCRIPTION_LEN = 256;
pub const MAX_SAVES_TO_LIST = 50;- CLI Level: Validate file paths and permissions
- File Level: Check file format and version compatibility
- Data Level: Validate grid dimensions and generation numbers
- Memory Level: Handle allocation failures gracefully
- Corrupted files: Attempt partial recovery, fallback to defaults
- Version mismatches: Provide migration utilities
- Disk space: Warn before saving, cleanup old auto-saves
- Permissions: Suggest alternative save locations
- Lazy loading: Only decompress grid when needed
- Streaming: Process large grids in chunks
- Compression: Use efficient RLE for sparse grids
- Caching: Keep recently loaded states in memory
- Arena allocator: For temporary save/load operations
- Careful cleanup: Ensure no memory leaks in error paths
- Grid reuse: Avoid unnecessary allocations during load
- Path validation: Prevent directory traversal attacks
- Size limits: Prevent excessive memory consumption
- Format validation: Strict parsing to prevent crashes
- Sandboxing: Restrict save/load to designated directories
- Save current game state to file with metadata
- Load saved game state and resume simulation
- Handle grid compression for efficient storage
- Support auto-save functionality
- Provide CLI interface for all operations
- Robust error handling for all failure modes
- Performance suitable for grids up to 1000x1000
- Memory efficient (no unnecessary copies)
- Human-readable TOML save format for debugging
- Comprehensive test coverage (>90%)
- Intuitive command line interface
- Helpful error messages and suggestions
- Fast save/load operations (<1 second for typical grids)
- Seamless integration with existing workflow
- Clear documentation and examples
Total Estimated Time: 6-10 hours
- Phase 1 (Core Infrastructure): 2-3 hours
- Phase 2 (Main Loop Integration): 1-2 hours
- Phase 3 (Advanced Features): 2-3 hours
- Phase 4 (Testing & Documentation): 1-2 hours
- Memory usage: Large grids may consume significant memory
- File I/O performance: Slow on some filesystems
- Compression efficiency: RLE may not be optimal for all patterns
- Progressive loading: Load grids in chunks if needed
- Format options: Support both TOML and binary formats
- Compression alternatives: Consider other algorithms if RLE insufficient
- Create saveload.zig module with basic data structures
- Implement TOML serialization for SavedGameState
- Add CLI parsing for save/load options
- Integrate with main loop for basic save/load functionality
- Add comprehensive testing and error handling
- Document the new features and update examples
This implementation plan provides a solid foundation for adding robust save/load functionality to the Conway's Game of Life simulator while maintaining the existing code quality and architectural principles.