diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 4601381..0000000 --- a/src/main.rs +++ /dev/null @@ -1,438 +0,0 @@ -use eframe::egui; - -fn main() -> eframe::Result<()> { - let options = eframe::NativeOptions{ - viewport: egui::ViewportBuilder::default() - .with_fullscreen(false) - .with_min_inner_size(egui::vec2(800.0, 600.0)) // Minimum width, height - .with_inner_size(egui::vec2(7680.0, 4320.0)), // Initial size - ..Default::default() - }; - eframe::run_native( - "Knightly", - options, - Box::new(|cc| { - let mut fonts = egui::FontDefinitions::default(); - fonts.font_data.insert( - "symbols".to_owned(), - egui::FontData::from_static(include_bytes!("../fonts/DejaVuSans.ttf")).into(), - ); - fonts - .families - .entry(egui::FontFamily::Proportional) - .or_default() - .insert(0, "symbols".to_owned()); - cc.egui_ctx.set_fonts(fonts); - Ok(Box::new(ChessApp::default())) - }), -) -} - -#[derive(Clone, Copy, PartialEq, Debug)] -enum Piece { - King(char), - Queen(char), - Rook(char), - Bishop(char), - Knight(char), - Pawn(char), - Empty, -} - -impl Piece { - fn symbol(&self) -> &'static str { - match self { - Piece::King('w') => "♔", - Piece::Queen('w') => "♕", - Piece::Rook('w') => "♖", - Piece::Bishop('w') => "♗", - Piece::Knight('w') => "♘", - Piece::Pawn('w') => "♙", - Piece::King('b') => "♚", - Piece::Queen('b') => "♛", - Piece::Rook('b') => "♜", - Piece::Bishop('b') => "♝", - Piece::Knight('b') => "♞", - Piece::Pawn('b') => "♟︎", - Piece::Empty => "", - _ => "", - } - } -} - -#[derive(PartialEq, Debug)] -enum Turn { - White, - Black, -} - -enum AppState { - MainMenu, - InGame, - Settings, -} - -struct ChessApp { - fullscreen: bool, - resolutions: Vec<(u32, u32)>, - selected_resolution: usize, - state: AppState, - board: [[Piece; 8]; 8], - selected: Option<(usize, usize)>, - turn: Turn, - pending_settings: PendingSettings, - server_port: String, -} - -#[derive(Default)] -struct PendingSettings { - fullscreen: bool, - selected_resolution: usize, - server_port: String, -} - -impl Default for ChessApp { - fn default() -> Self { - Self { - fullscreen: true, - resolutions: vec![ - (1280, 720), - (1600, 900), - (1920, 1080), - (2560, 1440), - (3840, 2160), - ], - selected_resolution: 2, // Default to 1920x1080 - state: AppState::MainMenu, - board: Self::starting_board(), - selected: None, - turn: Turn::White, - pending_settings: PendingSettings::default(), - server_port: "8080".to_string(), // Default port - } - } -} - -impl ChessApp { - fn starting_board() -> [[Piece; 8]; 8] { - use Piece::*; - [ - [ - Rook('b'), - Knight('b'), - Bishop('b'), - Queen('b'), - King('b'), - Bishop('b'), - Knight('b'), - Rook('b'), - ], - [Pawn('b'); 8], - [Empty; 8], - [Empty; 8], - [Empty; 8], - [Empty; 8], - [Pawn('w'); 8], - [ - Rook('w'), - Knight('w'), - Bishop('w'), - Queen('w'), - King('w'), - Bishop('w'), - Knight('w'), - Rook('w'), - ], - ] - } - - fn handle_click(&mut self, row: usize, col: usize) { - if let Some((r, c)) = self.selected { - let piece = self.board[r][c]; - self.board[r][c] = Piece::Empty; - self.board[row][col] = piece; - self.selected = None; - self.turn = if self.turn == Turn::White { - Turn::Black - } else { - Turn::White - }; - } else { - if self.board[row][col] != Piece::Empty { - self.selected = Some((row, col)); - } - } - } - - fn apply_settings(&mut self, ctx: &egui::Context) { - self.fullscreen = self.pending_settings.fullscreen; - self.selected_resolution = self.pending_settings.selected_resolution; - self.server_port = self.pending_settings.server_port.clone(); - - if let Some(resolution) = self.resolutions.get(self.selected_resolution) { - ctx.send_viewport_cmd(egui::ViewportCommand::InnerSize( - egui::Vec2::new(resolution.0 as f32, resolution.1 as f32) - )); - } - - ctx.send_viewport_cmd(egui::ViewportCommand::Fullscreen(self.fullscreen)); - } - - fn enter_settings(&mut self) { - self.pending_settings.fullscreen = self.fullscreen; - self.pending_settings.selected_resolution = self.selected_resolution; - self.pending_settings.server_port = self.server_port.clone(); - self.state = AppState::Settings; - } -} - -impl eframe::App for ChessApp { - fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) { - match self.state { - AppState::MainMenu => { - egui::CentralPanel::default().show(ctx, |ui| { - ui.vertical_centered(|ui| { - ui.heading("♞ Knightly ♞"); - ui.add_space(30.0); - - if ui.add_sized([300.0, 60.0], egui::Button::new("Play")).clicked() { - self.state = AppState::InGame; - } - ui.add_space(8.0); - - if ui.add_sized([300.0, 60.0], egui::Button::new("Settings")).clicked() { - self.enter_settings(); - } - ui.add_space(8.0); - - if ui - .add_sized([300.0, 60.0], egui::Button::new("Quit")) - .clicked() - { - std::process::exit(0); - } - }); - }); - } - - AppState::Settings => { - egui::CentralPanel::default().show(ctx, |ui| { - ui.vertical_centered(|ui| { - ui.heading("Settings"); - ui.add_space(30.0); - - // Fullscreen toggle - ui.horizontal(|ui| { - ui.label("Fullscreen:"); - if ui.checkbox(&mut self.pending_settings.fullscreen, "").changed() { - // If enabling fullscreen, we might want to disable resolution selection - } - }); - ui.add_space(10.0); - - // Resolution dropdown - ui.horizontal(|ui| { - ui.label("Resolution:"); - egui::ComboBox::new("resolution_combo", "") - .selected_text(format!( - "{}x{}", - self.resolutions[self.pending_settings.selected_resolution].0, - self.resolutions[self.pending_settings.selected_resolution].1 - )) - .show_ui(ui, |ui| { - for (i, &(width, height)) in self.resolutions.iter().enumerate() { - ui.selectable_value( - &mut self.pending_settings.selected_resolution, - i, - format!("{}x{}", width, height), - ); - } - }); - }); - ui.add_space(10.0); - - // Server port input field - ui.horizontal(|ui| { - ui.label("Local Server Port:"); - ui.add(egui::TextEdit::singleline(&mut self.pending_settings.server_port) - .desired_width(100.0) - .hint_text("8080")); - }); - ui.add_space(30.0); - - // Apply and Cancel buttons - ui.horizontal(|ui| { - if ui.add_sized([140.0, 40.0], egui::Button::new("Apply")).clicked() { - self.apply_settings(ctx); - self.state = AppState::MainMenu; - } - - if ui.add_sized([140.0, 40.0], egui::Button::new("Cancel")).clicked() { - self.state = AppState::MainMenu; - } - }); - }); - }); - } - - AppState::InGame => { - egui::TopBottomPanel::top("menu_bar").show(ctx, |ui| { - ui.horizontal(|ui| { - if ui.button("Main Menu").clicked() { - self.state = AppState::MainMenu; - } - if ui.button("Settings").clicked() { - self.enter_settings(); - } - if ui.button("New Game").clicked() { - *self = ChessApp::default(); - self.state = AppState::InGame; - } - ui.separator(); - ui.label(format!("Turn: {:?}", self.turn)); - }); - }); - - egui::CentralPanel::default().show(ctx, |ui| { - ui.vertical_centered(|ui| { - let full_avail = ui.available_rect_before_wrap(); - let board_tile = (full_avail.width().min(full_avail.height())) / 8.0; - let board_size = board_tile * 8.0; - - // Create a child UI at the board position - let (response, painter) = ui.allocate_painter( - egui::Vec2::new(board_size, board_size), - egui::Sense::click() - ); - - let board_rect = egui::Rect::from_center_size( - full_avail.center(), - egui::vec2(board_size, board_size) - ); - - // Draw the chess board - let tile_size = board_size / 8.0; - for row in 0..8 { - for col in 0..8 { - let color = if (row + col) % 2 == 0 { - egui::Color32::from_rgb(100, 97, 97) - } else { - egui::Color32::from_rgb(217, 217, 217) - }; - - let rect = egui::Rect::from_min_size( - egui::Pos2::new( - board_rect.min.x + col as f32 * tile_size, - board_rect.min.y + row as f32 * tile_size - ), - egui::Vec2::new(tile_size, tile_size) - ); - - painter.rect_filled(rect, 0.0, color); - - // Draw piece - let piece = self.board[row][col]; - if piece != Piece::Empty { - let symbol = piece.symbol(); - let font_id = egui::FontId::proportional(tile_size * 0.75); - painter.text( - rect.center(), - egui::Align2::CENTER_CENTER, - symbol, - font_id, - if matches!(piece, Piece::King('w') | Piece::Queen('w') | Piece::Rook('w') | Piece::Bishop('w') | Piece::Knight('w') | Piece::Pawn('w')) { - egui::Color32::WHITE - } else { - egui::Color32::BLACK - } - ); - } - - // Draw selection highlight - if self.selected == Some((row, col)) { - painter.rect_stroke( - rect, - 0.0, - egui::Stroke::new(3.0, egui::Color32::RED), - egui::StrokeKind::Inside - ); - } - - // Handle clicks - if ui.ctx().input(|i| i.pointer.primary_clicked()) { - let click_pos = ui.ctx().input(|i| i.pointer.interact_pos()).unwrap(); - if rect.contains(click_pos) { - self.handle_click(row, col); - } - } - } - } - }); - }); - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - #[test] - fn test_initial_board_setup() { - let app = ChessApp::default(); - assert!(matches!(app.board[0][0], Piece::Rook('b'))); - assert!(matches!(app.board[7][0], Piece::Rook('w'))); - - assert!(matches!(app.board[1][0], Piece::Pawn('b'))); - assert!(matches!(app.board[6][0], Piece::Pawn('w'))); - } - - #[test] - fn test_piece_symbols() { - assert_eq!(Piece::King('w').symbol(), "♔"); - assert_eq!(Piece::King('b').symbol(), "♚"); - assert_eq!(Piece::Empty.symbol(), ""); - } - - #[test] - fn test_piece_selection() { - let mut app = ChessApp::default(); - app.handle_click(6, 0); - assert_eq!(app.selected, Some((6, 0))); - app.handle_click(6, 0); - assert_eq!(app.selected, None); - } - - #[test] - fn test_piece_movement() { - let mut app = ChessApp::default(); - // Select and move a piece - app.handle_click(6, 0); // Select white pawn - app.handle_click(5, 0); // Move to empty square - assert_eq!(app.board[6][0], Piece::Empty); - assert!(matches!(app.board[5][0], Piece::Pawn('w'))); - } - - #[test] - fn test_turn_switching() { - let mut app = ChessApp::default(); - assert_eq!(app.turn, Turn::White); - app.handle_click(6, 0); // White selects - app.handle_click(5, 0); // White moves - assert_eq!(app.turn, Turn::Black); // Should now be Black's turn - } - - #[test] - fn test_settings_initialization() { - let app = ChessApp::default(); - assert_eq!(app.pending_settings.fullscreen, app.fullscreen); - assert_eq!(app.pending_settings.selected_resolution, app.selected_resolution); - assert_eq!(app.pending_settings.server_port, app.server_port); - } - - #[test] - fn test_server_port_default() { - let app = ChessApp::default(); - assert_eq!(app.server_port, "8080"); - } -} \ No newline at end of file