use crate::{draw_scaled_texture, Textures}; use super::tile::*; use super::Pos; use raylib::prelude::*; #[derive(Debug, Clone)] pub struct Board { rows: Vec>, width: usize, height: usize, } impl Board { pub fn parse(source: &str) -> Self { let mut rows = Vec::new(); let mut width = 0; for line in source.lines() { width = width.max(line.len()); let mut tiles = Vec::new(); for char in line.chars() { tiles.push(Tile::from_char(char)); } rows.push(tiles); } for line in &mut rows { line.resize(width, Tile::Blank); } Board::new(rows) } pub fn to_string(&self) -> String { let mut out = String::new(); for row in &self.rows { for tile in row { out.push(tile.to_char()) } out.push('\n'); } out } pub fn new_empty(width: usize, height: usize) -> Self { let rows = vec![vec![Tile::Blank; width]; height]; Self { rows, width, height, } } pub fn new(rows: Vec>) -> Self { Self { width: rows[0].len(), height: rows.len(), rows, } } pub fn count_tiles(&self) -> usize { let mut sum = 0; for row in &self.rows { for tile in row { match tile { Tile::Blank | Tile::Block => (), _ => sum += 1, } } } sum } fn in_bounds(&self, p: Pos) -> bool { p.x >= 0 && p.y >= 0 && p.x < self.width as isize && p.y < self.height as isize } pub fn get(&self, p: Pos) -> Option { if self.in_bounds(p) { Some(self.rows[p.y as usize][p.x as usize]) } else { None } } pub fn get_or_blank(&self, p: Pos) -> Tile { if self.in_bounds(p) { self.rows[p.y as usize][p.x as usize] } else { Tile::default() } } pub fn get_mut(&mut self, p: Pos) -> Option<&mut Tile> { if self.in_bounds(p) { Some(&mut self.rows[p.y as usize][p.x as usize]) } else { None } } pub fn get_blank_mut(&mut self, p: Pos) -> Option<&mut Tile> { if self.in_bounds(p) { let tile = &mut self.rows[p.y as usize][p.x as usize]; if tile == &Tile::Blank { return Some(tile); } } None } pub fn set(&mut self, p: Pos, tile: Tile) { if self.in_bounds(p) { self.rows[p.y as usize][p.x as usize] = tile; } } pub fn grow_to_include(&mut self, p: Pos) -> (isize, isize) { let mut offset_x = 0; let mut offset_y = 0; if p.x < 0 { let len = p.x.unsigned_abs() + 2; for row in &mut self.rows { let mut new_row = vec![Tile::Blank; len]; new_row.append(row); *row = new_row; } offset_x = len; self.width += len; } else if p.x as usize >= self.width { let new_width = p.x as usize + 3; for row in &mut self.rows { row.resize(new_width, Tile::Blank); } self.width = new_width; } if p.y < 0 { let len = p.y.unsigned_abs() + 2; let mut new_rows = vec![vec![Tile::Blank; self.width]; len]; new_rows.append(&mut self.rows); self.rows = new_rows; offset_y = len; self.height += len; } else if p.y as usize >= self.height { let new_height = p.y as usize + 3; self.rows.resize(new_height, vec![Tile::Blank; self.width]); self.height = new_height; } (offset_x as isize, offset_y as isize) } pub fn trim_size(&mut self) -> (usize, usize) { let (offset_x, offset_y); // top { let mut n = 0; while n < self.height && self.rows[n].iter().all(Tile::is_blank) { n += 1; } let trim_top = n.saturating_sub(2); for _ in 0..trim_top { self.rows.remove(0); } offset_y = trim_top; self.height -= trim_top; } // bottom { let mut n = 0; while n < self.height && self.rows[self.height - n - 1].iter().all(Tile::is_blank) { n += 1; } let trim_bottom = n.saturating_sub(2); for _ in 0..trim_bottom { self.rows.pop(); } self.height -= trim_bottom; } // left { let mut n = 0; while n < self.width && self.rows.iter().all(|row| row[n].is_blank()) { n += 1; } let trim_left = n.saturating_sub(2); for row in &mut self.rows { for _ in 0..trim_left { row.remove(0); } } offset_x = trim_left; self.width -= trim_left; } // right { let mut n = 0; while n < self.width && self.rows.iter().all(|r| r[self.width - n - 1].is_blank()) { n += 1; } let trim_right = n.saturating_sub(2); for row in &mut self.rows { for _ in 0..trim_right { row.pop(); } } self.width -= trim_right; } (offset_x, offset_y) } pub fn width(&self) -> usize { self.width } pub fn height(&self) -> usize { self.height } pub fn get_marbles(&self) -> Vec { let mut out = Vec::new(); for y in 0..self.height { for x in 0..self.width { if let Tile::Marble { value: _, dir: _ } = self.rows[y][x] { out.push((x, y).into()); } } } out } pub fn draw(&self, d: &mut RaylibDrawHandle, textures: &Textures, offset: Vector2, zoom: i32) { let tile_size = 16 << zoom; let start_x = (-offset.x as i32) / tile_size - 1; let tile_width = d.get_screen_width() / tile_size + 2; let start_y = (-offset.y as i32) / tile_size - 1; let tile_height = d.get_screen_height() / tile_size + 2; for x in start_x..(start_x + tile_width) { for y in start_y..(start_y + tile_height) { let tx = x as usize; let ty = y as usize; let px = x * tile_size + offset.x as i32; let py = y * tile_size + offset.y as i32; if self.in_bounds((tx, ty).into()) { let tile = self.rows[ty][tx]; let texname = tile.texture(); if texname.is_empty() { continue; } let texture = textures.get(&texname); draw_scaled_texture(d, texture, px, py, (1 << zoom) as f32); } else { d.draw_rectangle(px, py, tile_size, tile_size, Color::new(0, 0, 0, 80)); } } } } }