rsnaker/game_logic/
playing_logic.rs1use crate::controls::direction::Direction;
2use crate::controls::speed::Speed;
3use crate::game_logic::fruits_manager::FruitsManager;
4use crate::game_logic::high_score::{HighScore, HighScoreManager};
5use crate::game_logic::state::{GameOverMenu, GameState, GameStatus};
6use crate::graphics::sprites::fruit::Fruit;
7use crate::graphics::sprites::map::Map;
8use crate::graphics::sprites::snake_body::SnakeBody;
9use std::sync::{Arc, RwLock};
10use std::thread::sleep;
11use std::time::{Duration, Instant};
12use tracing::{debug, error, info, trace, trace_span, warn};
13
14pub fn playing_logic_loop(
18 direction: &Arc<RwLock<Direction>>,
19 snake: &Arc<RwLock<SnakeBody>>,
20 gs: &Arc<RwLock<GameState>>,
21 carte: &Arc<RwLock<Map>>,
22 fruits_manager: &Arc<RwLock<FruitsManager>>,
23 (game_speed, snake_symbols, negative_size_fruits, snake_growth_factor): (
24 Speed,
25 String,
26 bool,
27 u16,
28 ),
29) {
30 let mut gsc;
31 debug!(fruits = ?fruits_manager.read().unwrap().get_fruits(), "Initial fruits on map");
32 loop {
33 let _span = trace_span!("logic_iteration").entered();
34 let loop_start = Instant::now();
35 gsc = gs.read().unwrap().status.clone();
38 match gsc {
40 GameStatus::Playing => {
41 let mut write_guard = snake.write().unwrap();
42 let position = write_guard.ramp(&direction.read().unwrap(), &carte.read().unwrap());
44 debug!(?position, "Snake moved");
45 let fruits = fruits_manager.read().unwrap().eat_some_fruits(position);
47 if let Some(fruits) = fruits {
49 debug!(count = fruits.len(), "Fruits eaten by snake");
50 let score_fruits = fruits.iter().map(Fruit::get_score).sum::<i32>();
51 let base_size_effect = fruits
52 .iter()
53 .map(Fruit::get_grow_snake)
54 .map(i32::from)
55 .sum::<i32>();
56 let size_effect = i16::try_from(
57 base_size_effect
58 .saturating_mul(i32::from(snake_growth_factor))
59 .clamp(i32::from(i16::MIN), i32::from(i16::MAX)),
60 )
61 .unwrap();
62 info!(?fruits, "Fruit characteristics");
63 if negative_size_fruits || size_effect > 0i16 {
65 write_guard.relative_size_change(size_effect);
66 }
67 let fruit_impact = score_fruits * i32::from(game_speed.score_modifier());
71 info!(
72 fruit_impact = fruit_impact,
73 "Fruit impact on score because of speed score modifier"
74 );
75 let mut state_guard_gs = gs.write().unwrap();
76 if fruit_impact < 0 {
78 state_guard_gs.score = state_guard_gs
79 .score
80 .saturating_sub(fruit_impact.unsigned_abs());
81 } else {
82 state_guard_gs.score = state_guard_gs
83 .score
84 .saturating_add(fruit_impact.unsigned_abs());
85 }
86 info!(
87 score = state_guard_gs.score,
88 snake_size = write_guard.body.len(),
89 "Current game metrics"
90 );
91 fruits_manager.write().unwrap().replace_fruits(&fruits);
92 debug!(fruits = ?fruits_manager.read().unwrap().get_fruits(), "Current fruits on map");
93 }
94 if write_guard.is_snake_eating_itself() {
96 let mut state_guard = gs.write().unwrap();
98 if (state_guard.life) >= 1 {
99 state_guard.life -= 1;
100 warn!(
101 remaining_life = state_guard.life,
102 "Ouch! Did that taste good? You bit yourself!",
103 );
104 }
105 if state_guard.life == 0 {
106 error!(
107 score = state_guard.score,
108 "FATAL: Snake succumbed to its injuries. Game Over!"
109 );
110 state_guard.status = GameStatus::GameOver(GameOverMenu::default());
112
113 if let Ok(manager) = HighScoreManager::new() {
115 let score_value: u32 = state_guard.score;
116 let rank = manager.save_score(&HighScore::new(
117 snake_symbols.clone(),
118 score_value,
119 game_speed.to_string(),
121 snake_growth_factor,
122 ));
123 state_guard.rank = rank.unwrap_or(None);
124 }
125 }
126 }
127 }
128 GameStatus::Restarting => {
129 sleep(Duration::from_secs(1));
131 gs.write().unwrap().reset();
132 snake.write().unwrap().reset();
133 *direction.write().unwrap() = Direction::Right;
134 fruits_manager.write().unwrap().reset();
135 }
137 GameStatus::ByeBye | GameStatus::Menu => break,
138 GameStatus::Paused | GameStatus::GameOver(_) => {}
139 }
140 let elapsed = loop_start.elapsed();
141 trace!(?elapsed, "Logic loop iteration finished");
142 sleep(Duration::from_millis(game_speed.ms_value()));
143 }
144}