/// Инициализация логирования pub fn define_log_layer() -> Option { use std::fs::File; use tracing::Level; use tracing_subscriber::{Layer, filter::Targets, fmt::layer}; let targets = Targets::new().with_default(Level::TRACE); let stdout = layer() .compact() .with_thread_names(true) .with_file(false) .with_line_number(false) .with_filter(targets.clone()) .boxed(); let file = match File::options().create(true).append(true).open("snake-game.log") { Ok(file) => file, Err(e) => { eprintln!("No access to log file: {:?}", e); return Some(stdout); } }; let log_file = layer() .compact() .with_ansi(false) .with_thread_names(true) .with_file(false) .with_line_number(false) .with_writer(file) .with_filter(targets.clone()) .boxed(); Some(Box::new(vec![stdout, log_file])) } /// Разбиение изменения координаты по границам сетки /// Элементы выходного вектора представляют пары значений, где первое обозначает промежуточное значение координаты, /// а второе - изменение координаты относительно предыдущего значения (дельта). /// Если движение начинается на границе сетки и оно не нулевое (начальное и конечное значение координвты отличаются), /// то размер выходного вектора всегда больше 1 и второе значение первой пары (дельта) равно 0. /// На основе этой логики происходит в дальнейшем распознавание момента прохождения узла сетки и генерируется соответствующее событие. pub fn split_by_grid(src: f32, dst: f32, grid: f32) -> Vec<(f32, f32)> { if dst == src { return vec![(dst, 0.0)]; } if grid <= 0.0 { return vec![(dst, dst - src)]; }; let mut output = Vec::with_capacity(2); if src < dst { let src_grid = src / grid; let dst_grid = dst / grid; let src_grid_right = src_grid.ceil(); let dst_grid_left = dst_grid.floor(); let mut src: f32 = src; for step in src_grid_right as i32..=dst_grid_left as i32 { let step = step as f32 * grid; output.push((step, step - src)); src = step; } if src != dst { output.push((dst, dst - src)); } } else { let src_grid = src / grid; let dst_grid = dst / grid; let dst_grid_right = dst_grid.ceil(); let dst_grid_right = if dst_grid_right == dst { dst_grid_right + 1.0 } else { dst_grid_right }; let src_grid_left = src_grid.floor(); let mut dst: f32 = dst; for step in dst_grid_right as i32..=src_grid_left as i32 { let step = step as f32 * grid; output.push((dst, dst - step)); dst = step; } output.push((dst, dst - src)); output.reverse(); } output } #[cfg(test)] mod test { use crate::tools::*; #[test] fn test_split_by_grid() { // движение вправо assert_eq!(split_by_grid(1.0, 4.0, 5.0), vec![(4.0, 3.0)]); assert_eq!(split_by_grid(3.0, 5.0, 5.0), vec![(5.0, 2.0)]); assert_eq!(split_by_grid(3.0, 6.0, 5.0), vec![(5.0, 2.0), (6.0, 1.0)]); assert_eq!(split_by_grid(3.0, 10.0, 5.0), vec![(5.0, 2.0), (10.0, 5.0)]); assert_eq!(split_by_grid(3.0, 11.0, 5.0), vec![(5.0, 2.0), (10.0, 5.0), (11.0, 1.0)]); assert_eq!(split_by_grid(-2.0, 3.0, 5.0), vec![(0.0, 2.0), (3.0, 3.0)]); assert_eq!(split_by_grid(-6.0, 7.0, 5.0), vec![(-5.0, 1.0), (0.0, 5.0), (5.0, 5.0), (7.0, 2.0)]); assert_eq!(split_by_grid(-6.0, -2.0, 5.0), vec![(-5.0, 1.0), (-2.0, 3.0)]); // движение влево assert_eq!(split_by_grid(4.0, 1.0, 5.0), vec![(1.0, -3.0)]); assert_eq!(split_by_grid(5.0, 3.0, 5.0), vec![(5.0, 0.0), (3.0, -2.0)]); assert_eq!(split_by_grid(6.0, 3.0, 5.0), vec![(5.0, -1.0), (3.0, -2.0)]); assert_eq!(split_by_grid(10.0, 3.0, 5.0), vec![(10.0, 0.0), (5.0, -5.0), (3.0, -2.0)]); assert_eq!(split_by_grid(11.0, 3.0, 5.0), vec![(10.0, -1.0), (5.0, -5.0), (3.0, -2.0)]); assert_eq!(split_by_grid(3.0, -2.0, 5.0), vec![(0.0, -3.0), (-2.0, -2.0)]); assert_eq!(split_by_grid(7.0, -6.0, 5.0), vec![(5.0, -2.0), (0.0, -5.0), (-5.0, -5.0), (-6.0, -1.0)]); assert_eq!(split_by_grid(-2.0, -6.0, 5.0), vec![(-5.0, -3.0), (-6.0, -1.0)]); // сценарии с нулевой сеткой assert_eq!(split_by_grid(1.0, 4.0, 0.0), vec![(4.0, 3.0)]); assert_eq!(split_by_grid(4.0, 1.0, 0.0), vec![(1.0, -3.0)]); // сценарии со стартом в узле сетки assert_eq!(split_by_grid(0.0, 1.5, 5.0), vec![(0.0, 0.0), (1.5, 1.5)]); assert_eq!(split_by_grid(5.0, 7.0, 5.0), vec![(5.0, 0.0), (7.0, 2.0)]); assert_eq!(split_by_grid(0.0, -1.5, 5.0), vec![(0.0, 0.0), (-1.5, -1.5)]); assert_eq!(split_by_grid(5.0, 3.0, 5.0), vec![(5.0, 0.0), (3.0, -2.0)]); // сценарии с нулевым движением assert_eq!(split_by_grid(5.0, 5.0, 5.0), vec![(5.0, 0.0)]); assert_eq!(split_by_grid(0.0, 0.0, 5.0), vec![(0.0, 0.0)]); assert_eq!(split_by_grid(0.0, 0.0, 0.0), vec![(0.0, 0.0)]); } }