reversi-core
Overview
The core domain model and game logic for the Reversi game. This module is completely UI-agnostic and persistence-agnostic, exposing only pure Kotlin data types and functions that implement the Reversi game rules. It is designed to be used by any client (CLI, GUI, test code, etc.) without modification or dependency on any UI or storage framework.
The module follows clean architecture principles with explicit domain modeling, immutable data structures, and pure functions that return new instances rather than modifying state. This design makes it inherently testable and thread-safe.

Architecture
The module is organized into several focused packages:
Core Package — Main game types and orchestration
Board Package — Board model and piece management
Storage Package — Data Transfer Objects (DTOs) for persistence
Exceptions Package — Domain-specific exceptions
All transformations are deterministic and return new instances. The game state is immutable, making it easy to implement features like undo/redo, concurrent play, and state recovery.
Key Concepts
Game
The central type representing a Reversi game session. It is an immutable data class that carries:
The current board state
Player information and points
Game metadata (name, player piece types)
Storage references for persistence
Game mode information (local vs networked)
The Game type provides methods like play(), pass(), and refresh() to transform the game state while handling all validation and rules enforcement.
GameLogic
A singleton object containing pure game logic functions:
play()— Validates a move and returns the new board with captured pieces flippedgetAvailablePlays()— Computes all legal moves for a playerisValidMove()— Checks if a move is legal
These functions never modify state; they compute and return new board instances.
Board
An immutable representation of the 8x8 (or configurable) game grid:
Stores pieces as a list with coordinates
Provides safe accessors to query pieces by coordinate or linear index
Offers transformation methods:
addPiece(),changePiece(),startPieces()Implements Iterable for easy traversal
The board is designed to be efficient for both queries and transformations.
Coordinate
Represents a position on the board with 1-based row and column indexing (1-8). Provides:
Arithmetic operations (+ and -)
Boundary validation
Alternative column representation (letters A-H)
8 directional neighbors for move validation
Player
Represents a player with:
Piece type (BLACK or WHITE)
Current points (piece count)
Methods to update points from the board state
Ability to swap to the opposite color
Piece
Represents a single piece on the board with:
Coordinate (position)
Piece type (BLACK or WHITE)
Optional ghost piece flag (for UI highlighting available moves)
Serialization
The module provides serializers in the storage.serializers subpackage for converting domain types to text:
GameStateSerializer— Converts entire game state to/from textBoardSerializer— Converts board to/from row/col/symbol formatPlayerSerializer— Converts player dataPieceSerializer— Converts individual piecesPieceTypeSerializer— Converts piece symbols
This allows clean separation: the core module knows nothing about persistence, but provides the serializers that storage modules use to read/write games.
Configuration
The module reads configuration from reversi-core.properties via CoreConfig:
savesPath— Directory where games are saved (default: "saves")Board size and game parameters
Logging configuration
Exception Handling
Domain-specific exceptions with helpful messages:
InvalidPlayException— Attempted invalid moveInvalidGameException— Operation on uninitialized gameInvalidPieceInFileException— Malformed saved fileInvalidGameStateInFileException— Corrupted game state fileEndGameException— Game has endedInvalidNameAlreadyExists— Game name already used in storage
These exceptions help clients distinguish between logic errors and data corruption.
Design Principles
Immutability — All domain types are immutable; transformations return new instances
Purity — Game logic functions have no side effects and always return the same result for the same input
Simplicity — Types are focused and single-purpose; no god objects
Testability — No external dependencies; easy to unit test every function
Persistence-Agnostic — No I/O or storage logic; serializers are external
UI-Agnostic — No UI dependencies; works with any client
This design allows the core to be used in any context (CLI, GUI, mobile, server) without modification.