rsnaker/game_logic/
playing_thread_manager.rs1pub use crate::controls::direction::Direction;
2use crate::controls::main_menu::controls_main_switch_menu;
3use crate::controls::playing_input::playing_input_loop;
4pub use crate::controls::speed::Speed;
5pub use crate::game_logic::fruits_manager::FruitsManager;
6use crate::game_logic::game_options::GameOptions;
7use crate::game_logic::playing_logic::playing_logic_loop;
8pub use crate::game_logic::state::{GameState, GameStatus};
9use crate::graphics::playing_render::playing_render_loop;
10use crate::graphics::sprites::map::Map;
11use crate::graphics::sprites::snake_body::SnakeBody;
12use ratatui::text::Span;
13use ratatui::DefaultTerminal;
14use std::cmp::max;
15use std::sync::{Arc, RwLock};
16use std::thread;
17
18pub struct Game<'a, 'b, 'c: 'b, 't: 'a + 'b + 'c> {
22 options: &'t GameOptions,
24 speed: Speed,
26 serpent: Arc<RwLock<SnakeBody<'a>>>,
28 direction: Arc<RwLock<Direction>>,
30 carte: Arc<RwLock<Map<'b>>>,
33 state: Arc<RwLock<GameState>>,
35 fruits_manager: Arc<RwLock<FruitsManager<'c, 'b>>>,
37 terminal: &'t mut DefaultTerminal,
39}
40impl<'a, 'b, 'c, 't> Game<'a, 'b, 'c, 't> {
41 #[must_use]
42 fn new(
43 options: &'t GameOptions,
44 serpent: SnakeBody<'a>,
45 carte: Map<'b>,
46 terminal: &'t mut DefaultTerminal,
47 ) -> Game<'a, 'b, 'c, 't> {
48 let arc_carte = Arc::new(RwLock::new(carte));
49 let life = options.life;
50 let fruits_nb = options.nb_of_fruits;
51 let speed = options.speed;
52 Game {
53 options,
54 speed,
55 serpent: Arc::new(RwLock::new(serpent)),
56 direction: Arc::new(RwLock::new(Direction::Right)),
57 carte: arc_carte.clone(),
58 state: Arc::new(RwLock::new(GameState::new(life))),
59 fruits_manager: Arc::new(RwLock::new(FruitsManager::new(
60 fruits_nb,
61 arc_carte.clone(),
62 ))),
63 terminal,
64 }
65 }
66 pub fn menu(mut options: GameOptions, mut terminal: DefaultTerminal) {
73 loop {
76 if controls_main_switch_menu(&mut terminal, &mut options) {
79 let case_size = u16::try_from(max(
82 Span::raw(&options.body_symbol).width(),
83 Span::raw(&options.head_symbol).width(),
84 ))
86 .expect("Bad symbol size, use a real character");
87 let carte: Map = Map::new(case_size, terminal.get_frame().area());
88 let serpent: SnakeBody = SnakeBody::new(
89 &options.body_symbol,
90 &options.head_symbol,
91 options.snake_length,
92 GameOptions::initial_position(),
93 case_size,
94 );
95 let mut game = Game::new(&options, serpent, carte, &mut terminal);
96 game.start();
97 if game
98 .state
99 .read()
100 .expect("Panic in a previous thread, check previous error")
101 .status
102 == GameStatus::ByeBye
103 {
104 break;
105 }
106 } else {
107 break;
108 }
109 }
110 }
111 pub fn start(&mut self) {
113 let logic_snake = Arc::clone(&self.serpent);
119 let logic_gs = Arc::clone(&self.state);
120 let logic_dir = Arc::clone(&self.direction);
121 let carte = Arc::clone(&self.carte);
122 let fruits_manager = Arc::clone(&self.fruits_manager);
123
124 let input_gs = Arc::clone(&self.state);
126 let input_dir = Arc::clone(&self.direction);
127
128 let current_game_speed = self.speed;
132 let classic = self.options.classic_mode;
133 let snake_symbols = format!("{}{}", self.options.head_symbol, self.options.body_symbol);
134 thread::scope(|s| {
136 s.spawn(move || {
138 playing_logic_loop(
139 &logic_dir,
140 &logic_snake,
141 &logic_gs,
142 &carte,
143 &fruits_manager,
144 (current_game_speed, snake_symbols, classic),
145 );
146 });
147 s.spawn(move || {
149 playing_input_loop(&input_dir, &input_gs);
150 });
151
152 playing_render_loop(
154 &Arc::clone(&self.carte),
155 &Arc::clone(&self.fruits_manager),
156 &Arc::clone(&self.state),
157 &Arc::clone(&self.serpent),
158 self.options.caps_fps,
159 (self.speed.score_modifier(), self.speed.symbol()),
160 self.terminal,
161 );
162 });
163 }
164}