use anyhow::Result;
use std::path::Path;
use life_backend::format;
use life_backend::{Board, Game, Position};
use i16 as I;
fn load_game
(path: P) -> Result>
where
P: AsRef,
{
let handler = format::open(path)?;
let rule = handler.rule();
let board = handler.live_cells().map(Position::try_from).collect::, _>>()?;
let game = Game::new(rule, board);
Ok(game)
}
fn print_game(game: &Game, generation: usize) {
let bbox = game.board().bounding_box();
let population = game.board().iter().count();
println!("Generation {generation}: bounding-box = {bbox}, population = {population}");
println!("{game}");
}
fn advance_game(mut game: Game, steps: usize) -> Game {
for _ in 0..steps {
game.advance();
}
game
}
fn advance_game_with_check(mut game: Game, steps: usize, init: &Board) -> Game {
for i in 0..steps {
if i > 0 {
assert_ne!(game.board(), init);
}
game.advance();
}
game
}
fn shift_board(board: &Board, relative_position: (I, I)) -> Board {
let board: Board<_> = board
.iter()
.map(|pos| Position(pos.0 + relative_position.0, pos.1 + relative_position.1))
.collect();
board
}
fn do_oscillator_test(path: P, period: usize) -> Result<()>
where
P: AsRef,
{
// Load the given file and create a game
let game = load_game(path)?;
print_game(&game, 0);
// Set the initial pattern to the variable
let init = game.board().to_owned();
// Advance the game to the target generation
let game = advance_game_with_check(game, period, &init);
print_game(&game, period);
// Check the result
let result = game.board();
assert_eq!(result, &init);
Ok(())
}
fn do_stilllife_test(path: P) -> Result<()>
where
P: AsRef,
{
do_oscillator_test(path, 1)
}
fn do_spaceship_test(path: P, period: usize, relative_position: (I, I)) -> Result<()>
where
P: AsRef,
{
// Load the given file and create a game
let game = load_game(path)?;
print_game(&game, 0);
// Set the expected pattern to the variable
let expected = shift_board(game.board(), relative_position);
// Advance the game to the target generation
let game = advance_game(game, period);
print_game(&game, period);
// Check the result
let result = game.board();
assert_eq!(result, &expected);
Ok(())
}
fn do_methuselah_test(path: P, steps: usize, expected_final_population: usize) -> Result<()>
where
P: AsRef,
{
// Load the given file and create a game
let game = load_game(path)?;
print_game(&game, 0);
// Advance the game to the target generation
let game = advance_game(game, steps);
print_game(&game, steps);
// Check the result
let result = game.board().iter().count();
assert_eq!(result, expected_final_population);
Ok(())
}
fn do_diehard_test(path: P, steps: usize) -> Result<()>
where
P: AsRef,
{
do_methuselah_test(path, steps, 0)
}
macro_rules! create_stilllife_test_function {
($function_name:ident, $relative_path_string:literal) => {
#[test]
fn $function_name() -> Result<()> {
let path = $relative_path_string;
do_stilllife_test(path)
}
};
}
macro_rules! create_oscillator_test_function {
($function_name:ident, $relative_path_string:literal, $period:expr) => {
#[test]
fn $function_name() -> Result<()> {
let path = $relative_path_string;
do_oscillator_test(path, $period)
}
};
}
macro_rules! create_spaceship_test_function {
($function_name:ident, $relative_path_string:literal, $period:expr, $relative_position:expr) => {
#[test]
fn $function_name() -> Result<()> {
let path = $relative_path_string;
do_spaceship_test(path, $period, $relative_position)
}
};
}
macro_rules! create_methuselah_test_function {
($function_name:ident, $relative_path_string:literal, $steps:expr, $expected_final_population:expr) => {
#[test]
fn $function_name() -> Result<()> {
let path = $relative_path_string;
do_methuselah_test(path, $steps, $expected_final_population)
}
};
($function_name:ident, $relative_path_string:literal, $steps:expr, $expected_final_population:expr, ignore = $reason:literal) => {
#[test]
#[ignore = $reason]
fn $function_name() -> Result<()> {
let path = $relative_path_string;
do_methuselah_test(path, $steps, $expected_final_population)
}
};
}
macro_rules! create_diehard_test_function {
($function_name:ident, $relative_path_string:literal, $steps:expr) => {
#[test]
fn $function_name() -> Result<()> {
let path = $relative_path_string;
do_diehard_test(path, $steps)
}
};
}
#[rustfmt::skip]
mod game {
use super::*;
// Still life tests
create_stilllife_test_function!(stilllife_block, "patterns/block.rle");
create_stilllife_test_function!(stilllife_boat, "patterns/boat.rle");
create_stilllife_test_function!(stilllife_spiral, "patterns/spiral.rle");
create_stilllife_test_function!(stilllife_34life_block, "patterns/34life_block.rle");
create_stilllife_test_function!(stilllife_34life_36bitfortress, "patterns/34life_36bitfortress.rle");
// Oscillator tests
create_oscillator_test_function!(oscillator_blinker, "patterns/blinker.rle", 2);
create_oscillator_test_function!(oscillator_toad, "patterns/toad.rle", 2);
create_oscillator_test_function!(oscillator_koksgalaxy, "patterns/koksgalaxy.rle", 8);
create_oscillator_test_function!(oscillator_pentadecathlon, "patterns/pentadecathlon.rle", 15);
create_oscillator_test_function!(oscillator_queenbeeshuttle, "patterns/transqueenbeeshuttle.rle", 30);
create_oscillator_test_function!(oscillator_twinbeesshuttle, "patterns/3blocktwinbeesshuttle.rle", 46);
create_oscillator_test_function!(oscillator_p60glidershuttle, "patterns/p60glidershuttle.rle", 60);
create_oscillator_test_function!(oscillator_centinal, "patterns/centinal.rle", 100);
create_oscillator_test_function!(oscillator_highlife_p7, "patterns/highlife_p7.rle", 7);
create_oscillator_test_function!(oscillator_highlife_p10, "patterns/highlife_p10.rle", 10);
create_oscillator_test_function!(oscillator_seeds_duoplet, "patterns/seeds_duoplet.rle", 2);
create_oscillator_test_function!(oscillator_seeds_anchor, "patterns/seeds_anchor.rle", 4);
create_oscillator_test_function!(oscillator_34life_z, "patterns/34life_z.rle", 2);
create_oscillator_test_function!(oscillator_34life_loaf, "patterns/34life_loaf.rle", 12);
create_oscillator_test_function!(oscillator_2x2_largedomino, "patterns/2x2_largedomino.rle", 2);
create_oscillator_test_function!(oscillator_2x2_largetetromino, "patterns/2x2_largetetromino.rle", 6);
// Spaceship tests
create_spaceship_test_function!(spaceship_glider, "patterns/glider.rle", 4, (1, 1));
create_spaceship_test_function!(spaceship_lwss, "patterns/lwss.rle", 4, (-2, 0));
create_spaceship_test_function!(spaceship_loafer, "patterns/loafer.rle", 7, (-1, 0));
create_spaceship_test_function!(spaceship_copperhead, "patterns/copperhead.rle", 10, (0, -1));
create_spaceship_test_function!(spaceship_highlife_bomber, "patterns/highlife_bomber.rle", 48, (8, 8));
create_spaceship_test_function!(spaceship_daynight_rocket, "patterns/daynight_rocket.rle", 40, (-20, 0));
create_spaceship_test_function!(spaceship_seeds_moon, "patterns/seeds_moon.rle", 1, (-1, 0));
create_spaceship_test_function!(spaceship_34life_glider, "patterns/34life_glider.rle", 3, (0, -1));
create_spaceship_test_function!(spaceship_2x2_crawler, "patterns/2x2_crawler.rle", 8, (1, -1));
// Methuselah tests
create_methuselah_test_function!(methuselah_rpentomino, "patterns/rpentomino.rle", 1103, 116);
create_methuselah_test_function!(methuselah_bheptomino, "patterns/bheptomino.rle", 148, 28);
create_methuselah_test_function!(methuselah_eheptomino, "patterns/eheptomino.rle", 343, 52);
create_methuselah_test_function!(methuselah_fheptomino, "patterns/fheptomino.rle", 437, 61);
create_methuselah_test_function!(methuselah_herschel, "patterns/herschel.rle", 128, 24);
create_methuselah_test_function!(methuselah_piheptomino, "patterns/piheptomino.rle", 173, 55);
create_methuselah_test_function!(methuselah_century, "patterns/century.rle", 103, 15);
create_methuselah_test_function!(methuselah_queenbee, "patterns/queenbee.rle", 191, 30);
create_methuselah_test_function!(methuselah_thunderbird, "patterns/thunderbird.rle", 243, 46);
create_methuselah_test_function!(methuselah_switchengine, "patterns/switchengine.rle", 3911, 842, ignore = "too long for testing");
create_methuselah_test_function!(methuselah_acorn, "patterns/acorn.rle", 5206, 633, ignore = "too long for testing");
create_methuselah_test_function!(methuselah_bunnies, "patterns/bunnies.rle", 17332, 1744, ignore = "too long for testing");
// Diehard tests
create_diehard_test_function!(diehard_diehard, "patterns/diehard.rle", 130);
}