socha 0.2.2

Communication layer for the software challenge germany 2025
Documentation
use rand::{rngs::ThreadRng, Rng};
use socha::neutral::{PiranhaField, Size, Team};

fn get_random_field(rng: &mut ThreadRng) -> PiranhaField {
    let num = rng.random_range(0..100);
    if num < 30 {
        PiranhaField::Fish {
            team: rand_team(rng),
            size: rand_size(rng),
        }
    } else if num < 35 {
        PiranhaField::Squid
    } else {
        PiranhaField::Empty
    }
}

fn rand_team(rng: &mut ThreadRng) -> Team {
    if rng.random_bool(0.5) {
        Team::One
    } else {
        Team::Two
    }
}

fn rand_size(rng: &mut ThreadRng) -> Size {
    let num = rng.random_range(0..2);
    if num == 0 {
        Size::S
    } else if num == 1 {
        Size::M
    } else {
        Size::L
    }
}

#[cfg(test)]
pub mod tests {
    use rand::{rngs::ThreadRng, Rng};
    use socha::internal::{Board, GameState, MoveChange, Row};

    use crate::{get_random_field, rand_team};
    fn random_board(rng: &mut ThreadRng) -> Board {
        Board {
            rows: std::array::from_fn(|_| Row {
                fields: std::array::from_fn(|_| get_random_field(rng)),
            }),
        }
    }

    #[test]
    fn fuzz_make_unmake_games() {
        let mut rng = rand::rng();

        for game_idx in 0..100 {
            println!("Running fuzz game {}", game_idx);

            let board = random_board(&mut rng);
            let mut state = GameState::new_with_board(board, rand_team(&mut rng));

            let initial_board = state.board.clone();

            let mut move_stack: Vec<MoveChange> = Vec::new();
            for _ in 0..200 {
                let moves = state.possible_moves();

                if moves.is_empty() {
                    println!("stopping early");
                    break;
                }

                let mv = moves[rng.random_range(0..moves.len())];

                let change = state.make_move(mv);
                move_stack.push(change);
            }

            while let Some(change) = move_stack.pop() {
                state.unmake_move(change);
            }
            assert_eq!(state.board, initial_board, " board mismatch");
        }
    }
}