6 releases (breaking)

Uses new Rust 2024

new 0.6.0 Jul 6, 2025
0.5.0 May 22, 2025
0.4.0 May 21, 2025
0.3.2 May 20, 2025
0.1.0 May 19, 2025

#82 in Games

Download history 116/week @ 2025-05-14 323/week @ 2025-05-21 1/week @ 2025-05-28 3/week @ 2025-06-04 118/week @ 2025-07-02

119 downloads per month

MIT/Apache

120KB
1.5K SLoC

secondbest

secondbest is a Rust library for implementing the "Second Best" strategy game. This library provides a complete implementation of the game rules, board representation, and game state management.

Game Overview

"Second Best" is a strategic board game played on 8 positions arranged in a circle. Players take turns placing pieces of their color (Black or White) or moving pieces already on the board. The game features a unique mechanic where a player can declare "second best" after their opponent's move, forcing the opponent to choose a different action.

Game Setup

  • The game starts with an empty board of 8 positions arranged in a circle (N, NE, E, SE, S, SW, W, NW)
  • Each position can hold up to 3 stacked pieces
  • Black plays first, then players alternate turns
  • On each turn, a player must either place a new piece or move an existing one of their color

"Second Best" Mechanic

The unique feature of this game is the "second best" declaration:

  • After an opponent makes their move (their first action in the turn), a player can declare "second best"
  • This forces the opponent to choose a different action (their "second best" choice)
  • A player can only declare "second best" after the opponent's first action in a turn, not after their second action
  • The "second best" declaration can only be used once per turn

Winning Conditions

A player wins when they achieve one of the following:

  • Vertical Line-up: Stack 3 of their pieces in a single position
  • Horizontal Line-up: Have their pieces at the top of 4 consecutive positions
  • Priority Win: If both players achieve a winning condition simultaneously, the player who made the last move wins
  • No Moves: If a player has no legal moves available, they lose

Note: Winning conditions are only checked after all "second best" declarations have been used.

Architecture

The library is organized into several modules:

  • board: Core board representation and operations

    • BitBoard: Low-level bit manipulation for efficient board state representation
    • Board: High-level API for board manipulation
    • Position: Enumeration of the 8 board positions (N, NE, E, SE, S, SW, W, NW)
    • Color: Player colors (B for Black, W for White)
    • Action: Game actions (Put or Move)
  • game: Game state management

    • Game: Main game state controller
    • GameResult: Game outcome representation
  • error: Error handling

    • Various error types for different operations

Basic Usage

use secondbest::prelude::*;

// Create a new game
let mut game = Game::new();

// Apply actions (place pieces and move them)
game.apply_action(Action::Put(Position::N, Color::B)).unwrap();
game.apply_action(Action::Put(Position::S, Color::W)).unwrap();

// Check game state
if game.can_declare_second_best() {
    game.declare_second_best().unwrap();
}

// Check for a winner
match game.result() {
    GameResult::Finished { winner } => println!("Winner: {:?}", winner),
    GameResult::InProgress => println!("Game still in progress"),
}

Advanced Usage

For lower-level control, you can work directly with the Board API:

use secondbest::prelude::*;

let mut board = Board::default();

// Place pieces
board.put(Position::N, Color::B).unwrap();

// Check board state
let pieces_at_north = board.get_pieces_at(Position::N);
let black_piece_count = board.count_pieces(Color::B);

// Check for winning conditions
if board.lines_up(Color::B) {
    println!("Black has won!");
}

Error Handling

The library provides detailed error types for various operations:

use secondbest::prelude::*;
use secondbest::error::GameError;

let mut game = Game::new();
let result = game.apply_action(Action::Put(Position::N, Color::B));

match result {
    Ok(()) => println!("Action applied successfully"),
    Err(GameError::IllegalAction(_)) => println!("Illegal action"),
    Err(GameError::GameAlreadyOver) => println!("Game is already over"),
    Err(GameError::BoardError(_)) => println!("Board error occurred"),
    Err(_) => println!("Other error occurred"),
}

License: MIT OR Apache-2.0

Dependencies

~205–640KB
~15K SLoC