/
asmody
/
exercism
Обзор
Документация
Войти
/
asmody
/
exercism
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
rust/bowling/src/lib.rs
244 строки
7 KB
Victor Asmody Sakhnov
Rust: bowling
06 апр 2025, 12:28
06 апр 2025, 12:28
94ab710
Код
Авторство
О чём код?
// First, let's define our error types #[derive(Debug, PartialEq)] pub enum Error { NotEnoughPinsLeft, GameComplete, } // Define a Frame struct to represent each frame #[derive(Debug, Default)] struct Frame { rolls: Vec<u16>, is_complete: bool, } impl Frame { fn new() -> Self { Frame { rolls: Vec::new(), is_complete: false, } } fn add_roll(&mut self, pins: u16) -> Result<(), Error> { // Check if we've already thrown twice in a normal frame if self.rolls.len() >= 2 { return Err(Error::GameComplete); } // Check if we have enough pins left let pins_already_down = if self.rolls.is_empty() { 0 } else { self.rolls[0] }; if pins_already_down + pins > 10 { return Err(Error::NotEnoughPinsLeft); } self.rolls.push(pins); // Check if frame is complete (strike or second roll) if pins == 10 || self.rolls.len() == 2 { self.is_complete = true; } Ok(()) } fn is_strike(&self) -> bool { !self.rolls.is_empty() && self.rolls[0] == 10 } fn is_spare(&self) -> bool { self.rolls.len() == 2 && self.rolls[0] + self.rolls[1] == 10 } } // The main BowlingGame struct pub struct BowlingGame { frames: Vec<Frame>, current_frame_index: usize, bonus_rolls: Vec<u16>, } impl BowlingGame { pub fn new() -> Self { BowlingGame { frames: vec![Frame::new()], current_frame_index: 0, bonus_rolls: Vec::new(), } } pub fn roll(&mut self, pins: u16) -> Result<(), Error> { // Check if pins is valid (0-10) if pins > 10 { return Err(Error::NotEnoughPinsLeft); } // Check if game is already complete if self.is_game_complete() { return Err(Error::GameComplete); } // If we're in the 10th frame and it's complete, we're handling bonus rolls if self.current_frame_index == 9 && self.frames[9].is_complete { self.handle_bonus_roll(pins)?; } else { // Regular frame roll let frame = &mut self.frames[self.current_frame_index]; frame.add_roll(pins)?; // Move to next frame if current frame is complete if frame.is_complete && self.current_frame_index < 9 { self.current_frame_index += 1; self.frames.push(Frame::new()); } } Ok(()) } fn handle_bonus_roll(&mut self, pins: u16) -> Result<(), Error> { let tenth_frame = &self.frames[9]; // For a strike in 10th frame, we get 2 bonus rolls if tenth_frame.is_strike() { if self.bonus_rolls.len() >= 2 { return Err(Error::GameComplete); } // First bonus after strike can be anything from 0-10 if self.bonus_rolls.is_empty() { self.bonus_rolls.push(pins); return Ok(()); } // Second bonus depends on first bonus let first_bonus = self.bonus_rolls[0]; // If first bonus was a strike, second can be 0-10 if first_bonus == 10 { if pins > 10 { return Err(Error::NotEnoughPinsLeft); } } else { // If first bonus wasn't a strike, make sure we don't exceed 10 pins total if first_bonus + pins > 10 { return Err(Error::NotEnoughPinsLeft); } } self.bonus_rolls.push(pins); } // For a spare in 10th frame, we get 1 bonus roll else if tenth_frame.is_spare() { if self.bonus_rolls.len() >= 1 { return Err(Error::GameComplete); } if pins > 10 { return Err(Error::NotEnoughPinsLeft); } self.bonus_rolls.push(pins); } // Neither strike nor spare, no bonus rolls allowed else { return Err(Error::GameComplete); } Ok(()) } pub fn score(&self) -> Option<u16> { // Game is not complete - cannot calculate score if !self.is_game_over() { return None; } let mut total_score = 0; let mut roll_index = 0; let all_rolls = self.get_all_rolls(); // Calculate score for each of the 10 frames for _ in 0..10 { // Strike if all_rolls[roll_index] == 10 { // Strike bonus: the value of the next two rolls total_score += 10 + all_rolls[roll_index + 1] + all_rolls[roll_index + 2]; roll_index += 1; } // Spare else if all_rolls[roll_index] + all_rolls[roll_index + 1] == 10 { // Spare bonus: the value of the next roll total_score += 10 + all_rolls[roll_index + 2]; roll_index += 2; } // Open frame else { total_score += all_rolls[roll_index] + all_rolls[roll_index + 1]; roll_index += 2; } } Some(total_score) } // Helper method to check if the game is over (all frames played including bonus rolls) fn is_game_over(&self) -> bool { // Need 10 complete frames if self.current_frame_index < 9 || !self.frames[9].is_complete { return false; } // Check if we need bonus rolls and if they're complete let tenth_frame = &self.frames[9]; if tenth_frame.is_strike() { // After a strike in 10th frame, we need 2 bonus rolls return self.bonus_rolls.len() == 2; } else if tenth_frame.is_spare() { // After a spare in 10th frame, we need 1 bonus roll return self.bonus_rolls.len() == 1; } // No bonus rolls needed true } // Helper method to check if no more rolls are allowed fn is_game_complete(&self) -> bool { // If game is over with all required bonus rolls if self.is_game_over() { return true; } // If we haven't even finished frame 10 yet if self.current_frame_index < 9 { return false; } false } // Helper to flatten all rolls into a single array fn get_all_rolls(&self) -> Vec<u16> { let mut all_rolls = Vec::new(); // Add all regular frame rolls for frame in &self.frames { for &roll in &frame.rolls { all_rolls.push(roll); } } // Add bonus rolls for &roll in &self.bonus_rolls { all_rolls.push(roll); } all_rolls } }