pub use crate::controls::direction::Direction;
use crate::controls::main_menu::controls_main_switch_menu;
use crate::controls::playing_input::playing_input_loop;
pub use crate::controls::speed::Speed;
pub use crate::game_logic::fruits_manager::FruitsManager;
use crate::game_logic::game_options::GameOptions;
use crate::game_logic::playing_logic::playing_logic_loop;
pub use crate::game_logic::state::{GameState, GameStatus};
use crate::graphics::playing_render::playing_render_loop;
use crate::graphics::sprites::map::Map;
use crate::graphics::sprites::snake_body::SnakeBody;
use ratatui::text::Span;
use ratatui::DefaultTerminal;
use std::cmp::max;
use std::sync::{Arc, RwLock};
use std::thread;
pub struct Game<'a, 'b, 'c: 'b, 't: 'a + 'b + 'c> {
options: &'t GameOptions,
speed: Speed,
serpent: Arc<RwLock<SnakeBody<'a>>>,
direction: Arc<RwLock<Direction>>,
carte: Arc<RwLock<Map<'b>>>,
state: Arc<RwLock<GameState>>,
fruits_manager: Arc<RwLock<FruitsManager<'c, 'b>>>,
terminal: &'t mut DefaultTerminal,
}
impl<'a, 'b, 'c, 't> Game<'a, 'b, 'c, 't> {
#[must_use]
fn new(
options: &'t GameOptions,
serpent: SnakeBody<'a>,
carte: Map<'b>,
terminal: &'t mut DefaultTerminal,
) -> Game<'a, 'b, 'c, 't> {
let arc_carte = Arc::new(RwLock::new(carte));
let life = options.life;
let fruits_nb = options.nb_of_fruits;
let speed = options.speed;
Game {
options,
speed,
serpent: Arc::new(RwLock::new(serpent)),
direction: Arc::new(RwLock::new(Direction::Right)),
carte: arc_carte.clone(),
state: Arc::new(RwLock::new(GameState::new(life))),
fruits_manager: Arc::new(RwLock::new(FruitsManager::new(
fruits_nb,
arc_carte.clone(),
))),
terminal,
}
}
pub fn menu(mut options: GameOptions, mut terminal: DefaultTerminal) {
loop {
if controls_main_switch_menu(&mut terminal, &mut options) {
let case_size = u16::try_from(max(
Span::raw(&options.body_symbol).width(),
Span::raw(&options.head_symbol).width(),
))
.expect("Bad symbol size, use a real character");
let carte: Map = Map::new(case_size, terminal.get_frame().area());
let serpent: SnakeBody = SnakeBody::new(
&options.body_symbol,
&options.head_symbol,
options.snake_length,
GameOptions::initial_position(),
case_size,
);
let mut game = Game::new(&options, serpent, carte, &mut terminal);
game.start();
if game
.state
.read()
.expect("Panic in a previous thread, check previous error")
.status
== GameStatus::ByeBye
{
break;
}
} else {
break;
}
}
}
pub fn start(&mut self) {
let logic_snake = Arc::clone(&self.serpent);
let logic_gs = Arc::clone(&self.state);
let logic_dir = Arc::clone(&self.direction);
let carte = Arc::clone(&self.carte);
let fruits_manager = Arc::clone(&self.fruits_manager);
let input_gs = Arc::clone(&self.state);
let input_dir = Arc::clone(&self.direction);
let current_game_speed = self.speed;
let classic = self.options.classic_mode;
let snake_symbols = format!("{}{}", self.options.head_symbol, self.options.body_symbol);
thread::scope(|s| {
s.spawn(move || {
playing_logic_loop(
&logic_dir,
&logic_snake,
&logic_gs,
&carte,
&fruits_manager,
(current_game_speed, snake_symbols, classic),
);
});
s.spawn(move || {
playing_input_loop(&input_dir, &input_gs);
});
playing_render_loop(
&Arc::clone(&self.carte),
&Arc::clone(&self.fruits_manager),
&Arc::clone(&self.state),
&Arc::clone(&self.serpent),
self.options.caps_fps,
(self.speed.score_modifier(), self.speed.symbol()),
self.terminal,
);
});
}
}