Core game (src/core/)

Pure game logic with no I/O and no rendering. Every other layer depends on this one; this layer depends on nothing else in the package.

Types and constants (struct.jl)

Reversi.ZOBRIST_TABLEConstant
ZOBRIST_TABLE

128-entry table of random UInt64 values indexed by [row, col, color_idx] (color_idx 1 = BLACK, 2 = WHITE) used for incremental Zobrist hashing.

source
Reversi.PositionType
Position

Represents a position on the Reversi board (row, col). Supports construction from standard Reversi notation (e.g. "e4").

source
Reversi.PositionMethod
Position(s::AbstractString) -> Position

Parse a standard Reversi notation string (e.g. "e4") into a Position. Column letters ah map to columns 1–8; row digits 18 map to rows 1–8.

Examples

Position("e4")  # Position(4, 5)
Position("a1")  # Position(1, 1)
source
Reversi.ReversiGameType
ReversiGame

Represents the state of a Reversi game using two UInt64 bitboards.

Fields:

  • black::UInt64 – bitmask of squares occupied by Black
  • white::UInt64 – bitmask of squares occupied by White
  • current_player::IntBLACK or WHITE
  • pass_count::Int – consecutive passes (≥ 2 → game over)
  • hash::UInt64 – incremental Zobrist hash of the current position
source
Base.copyMethod
copy(game::ReversiGame) -> ReversiGame

Fast field-by-field copy of a ReversiGame. Prefer this over deepcopy since all fields are value types (integers).

source
Reversi.compute_full_hashMethod
compute_full_hash(game::ReversiGame) -> UInt64

Compute the Zobrist hash of game from scratch. Useful for debugging.

source
Reversi.position_to_stringMethod
position_to_string(pos::Position) -> String

Convert a Position to standard Reversi notation (e.g. Position(4, 5)"e4").

source
Reversi.update_hashMethod
update_hash(current_hash, row, col, color) -> UInt64

Toggle one piece of color at (row, col) in the hash (add or remove).

source

Rules and bitboard logic (rules.jl)

Reversi.board_to_matrixMethod
board_to_matrix(game::ReversiGame; flip_for_current=true) -> Array{Float32, 2}

Encode the 8x8 board as a matrix of Float32. If flip_for_current=true, the current_player's pieces are 1.0f0 and the opponent's are -1.0f0. Empty squares are 0.0f0.

source
Reversi.compute_flipsMethod
compute_flips(pos, player, opponent) -> UInt64

Return a bitmask of opponent pieces that would be flipped when player places at the single-bit position pos.

source
Reversi.get_pieceMethod
get_piece(game, row, col) -> Int

Return BLACK, WHITE, or EMPTY for the given square.

source
Reversi.is_game_overMethod
is_game_over(game) -> Bool

Return true when the game has ended (two consecutive passes, board full, or neither player has any legal move).

source
Reversi.is_valid_moveFunction
is_valid_move(game, row, col[, player]) -> Bool

Check if placing a piece at (row, col) is legal for player.

source
Reversi.legal_moves_bbMethod
legal_moves_bb(player, opponent) -> UInt64

Return a bitmask of all squares where player can legally place a piece (Kogge-Stone Dumb7Fill in each of the 8 directions).

source
Reversi.make_move!Method
make_move!(game, s) -> Bool

Make a move specified in standard Reversi notation (e.g. "e4").

source
Reversi.make_move!Method
make_move!(game, row, col) -> Bool

Place a piece at (row, col) for the current player. Updates the board and the Zobrist hash. Returns true on success, false if the move is illegal.

source
Reversi.mobilityFunction
mobility(game[, player]) -> Int

Return the number of legal moves available to player (defaults to game.current_player). A commonly used feature in evaluation.

source
Reversi.next_stateMethod
next_state(game, move) -> ReversiGame

Return a new ReversiGame resulting from applying move to a copy of game. The original game is not modified.

source
Reversi.pass!Method
pass!(game; force=false)

Pass the turn to the opponent.

If force=false (default), throws ArgumentError when the current player still has at least one legal move — callers must only pass when genuinely stuck. Set force=true to skip the check (useful for replaying external records or tests).

source
Reversi.valid_movesFunction
valid_moves(game[, player]) -> Vector{Position}

Return all valid moves for player (defaults to game.current_player).

source

Player interface (player.jl)

Reversi.HumanPlayerType
HumanPlayer <: Player

A human player that can receive moves from any interface (GUI, terminal, etc.) via its internal move_channel.

Use get_move(player, game) to wait for the next move.

source
Reversi.PlayerType
Player

Abstract type for all player implementations. Subtype and implement get_move(player, game) to create a new player.

source
Reversi.get_moveFunction
get_move(player::Player, game::ReversiGame) -> Union{Position, Nothing}

Get the next move from a player. Returns nothing to indicate a pass.

source
Reversi.get_terminal_inputMethod
get_terminal_input(game::ReversiGame; hints=true) -> Union{Position, Nothing}

A helper for CLI frontends to get move input from the terminal using readline. Can be used to put! a move into a HumanPlayer's channel.

source

Classical AI players (ai_players.jl)

Five non-ML players built on the bitboard primitives:

  • HeuristicPlayer — static positional weight table
  • CornerPlayer — corner-first then positional fallback
  • MobilityPlayer — maximises own mobility minus opponent's
  • MinimaxPlayer(depth) — alpha-beta search on piece-count diff
  • MCTSPlayer(iterations) — UCB1 + random rollout MCTS
Reversi.CornerPlayerType
CornerPlayer <: Player

Picks any available corner move first, then falls back to HeuristicPlayer's positional weights.

source
Reversi.HeuristicPlayerType
HeuristicPlayer <: Player

A player that picks the move with the highest positional weight. Corners are strongly preferred; corner-adjacent squares are penalised.

source
Reversi.MCTSPlayerType
MCTSPlayer(iterations::Int=200, c::Float64=1.414) <: Player

Monte Carlo Tree Search with UCB1 selection and random rollouts. iterations controls the number of simulations per move.

source
Reversi.MinimaxPlayerType
MinimaxPlayer(depth::Int=3) <: Player

Alpha-beta search using a piece-count difference evaluation. depth controls the search depth in plies.

source
Reversi.MobilityPlayerType
MobilityPlayer <: Player

Picks the move that leaves the opponent with the fewest legal replies while keeping its own mobility high.

source

Notes

Bitboard layout

Bit index i = (row-1)*8 + (col-1) (0-based), so bit 0 = (1,1) (a1) and bit 63 = (8,8) (h8).

Hash invariant

After every make_move! or pass! call, game.hash == compute_full_hash(game) holds. Tests verify this property throughout random full games.

Copying game state

g2 = copy(game)      # fast: copies 5 integer fields
g2 = deepcopy(game)  # slower: avoid in hot loops

pass! semantics

pass!(game)               # throws if current player has legal moves
pass!(game; force=true)   # always passes — use only in replay / tests