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::DefaultTerminal;
13use ratatui::text::Span;
14use std::cmp::max;
15use std::sync::{Arc, RwLock};
16use std::thread;
17use tracing::{debug, info, trace};
18
19pub struct Game<'a, 'b, 'c: 'b, 't: 'a + 'b + 'c> {
23 options: &'t GameOptions,
25 speed: Speed,
27 serpent: Arc<RwLock<SnakeBody<'a>>>,
29 direction: Arc<RwLock<Direction>>,
31 carte: Arc<RwLock<Map<'b>>>,
34 state: Arc<RwLock<GameState>>,
36 fruits_manager: Arc<RwLock<FruitsManager<'c, 'b>>>,
38 terminal: &'t mut DefaultTerminal,
40}
41impl<'a, 'b, 'c, 't> Game<'a, 'b, 'c, 't> {
42 #[must_use]
43 fn new(
44 options: &'t GameOptions,
45 serpent: SnakeBody<'a>,
46 carte: Map<'b>,
47 terminal: &'t mut DefaultTerminal,
48 ) -> Game<'a, 'b, 'c, 't> {
49 let arc_carte = Arc::new(RwLock::new(carte));
50 let life = options.life;
51 let fruits_nb = options.nb_of_fruits;
52 let speed = options.speed;
53 Game {
54 options,
55 speed,
56 serpent: Arc::new(RwLock::new(serpent)),
57 direction: Arc::new(RwLock::new(Direction::Right)),
58 carte: arc_carte.clone(),
59 state: Arc::new(RwLock::new(GameState::new(life))),
60 fruits_manager: Arc::new(RwLock::new(FruitsManager::new(
61 fruits_nb,
62 arc_carte.clone(),
63 options.fruit_timer,
64 f32::from(options.fruit_duration_seconds),
65 ))),
66 terminal,
67 }
68 }
69 pub fn menu(mut options: GameOptions, mut terminal: DefaultTerminal) {
76 info!("Welcome dear player ! Make your choice on Main menu !");
77 loop {
80 if controls_main_switch_menu(&mut terminal, &mut options) {
83 info!("Let's play! 🐍 (Run has been entered in the menu, starting the game...)");
84 let case_size = u16::try_from(max(
87 Span::raw(&options.body_symbol).width(),
88 Span::raw(&options.head_symbol).width(),
89 ))
91 .expect("Bad symbol size, use a real character");
92 let carte: Map = Map::new(case_size, terminal.get_frame().area());
93 let serpent: SnakeBody = SnakeBody::new(
94 &options.body_symbol,
95 &options.head_symbol,
96 options.snake_length,
97 GameOptions::initial_position(),
98 case_size,
99 );
100 info!(Snake_lenght = ?options.snake_length, Snake_head = ?options.head_symbol, Snake_body = ?options.body_symbol, "Snake info for starting");
101 trace!(?serpent);
102 debug!(?options);
103 let mut game = Game::new(&options, serpent, carte, &mut terminal);
104 game.start();
105 if game
106 .state
107 .read()
108 .expect("Panic in a previous thread, check previous error")
109 .status
110 == GameStatus::ByeBye
111 {
112 break;
113 }
114 } else {
115 break;
116 }
117 }
118 info!("Good bye, come back soon, snaker is waiting for you 🐍");
119 }
120 pub fn start(&mut self) {
122 debug!("Starting game threads");
123 let logic_snake = Arc::clone(&self.serpent);
129 let logic_gs = Arc::clone(&self.state);
130 let logic_dir = Arc::clone(&self.direction);
131 let carte = Arc::clone(&self.carte);
132 let fruits_manager = Arc::clone(&self.fruits_manager);
133
134 let input_gs = Arc::clone(&self.state);
136 let input_dir = Arc::clone(&self.direction);
137
138 let current_game_speed = self.speed;
142 let negative_size_fruits = self.options.negative_size_fruits;
143 let snake_growth_factor = self.options.snake_growth_factor;
144 let snake_symbols = format!("{}{}", self.options.head_symbol, self.options.body_symbol);
145 thread::scope(|s| {
147 thread::Builder::new()
149 .name("t_game_logic".to_string())
151 .spawn_scoped(s, move || {
152 playing_logic_loop(
153 &logic_dir,
154 &logic_snake,
155 &logic_gs,
156 &carte,
157 &fruits_manager,
158 (
159 current_game_speed,
160 snake_symbols,
161 negative_size_fruits,
162 snake_growth_factor,
163 ),
164 );
165 })
166 .expect("Unable to create the thread");
167 thread::Builder::new()
169 .name("t_input".to_string())
171 .spawn_scoped(s, move || {
172 playing_input_loop(&input_dir, &input_gs);
173 })
174 .expect("Unable to create the thread");
175
176 playing_render_loop(
178 &Arc::clone(&self.carte),
179 &Arc::clone(&self.fruits_manager),
180 &Arc::clone(&self.state),
181 &Arc::clone(&self.serpent),
182 self.options.caps_fps,
183 (self.speed.score_modifier(), self.speed.symbol()),
184 self.terminal,
185 );
186 });
187 }
188}