diff --git a/.github/workflows/hypatia-scan.yml b/.github/workflows/hypatia-scan.yml index 6fec8a5..82fcb2b 100644 --- a/.github/workflows/hypatia-scan.yml +++ b/.github/workflows/hypatia-scan.yml @@ -79,7 +79,7 @@ jobs: echo "- Medium: $MEDIUM" >> $GITHUB_STEP_SUMMARY - name: Upload findings artifact - uses: actions/upload-artifact@65c79d7f54e76e4e3c7a8f34db0f4ac8b515c478 # v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: hypatia-findings path: hypatia-findings.json diff --git a/benches/proof_of_work_bench.rs b/benches/proof_of_work_bench.rs index ac21aa3..45b64ac 100644 --- a/benches/proof_of_work_bench.rs +++ b/benches/proof_of_work_bench.rs @@ -17,20 +17,50 @@ use proof_of_work::{BoardState, GoalCondition, Level, LogicPiece}; /// Create a populated board with assumptions, gates, goals, and wires. fn populated_board() -> BoardState { let pieces = vec![ - LogicPiece::Assumption { formula: "P".into(), position: (2, 5) }, - LogicPiece::Assumption { formula: "Q".into(), position: (2, 3) }, - LogicPiece::Assumption { formula: "R".into(), position: (1, 7) }, - LogicPiece::Goal { formula: "S".into(), position: (15, 5) }, - LogicPiece::Goal { formula: "T".into(), position: (15, 10) }, + LogicPiece::Assumption { + formula: "P".into(), + position: (2, 5), + }, + LogicPiece::Assumption { + formula: "Q".into(), + position: (2, 3), + }, + LogicPiece::Assumption { + formula: "R".into(), + position: (1, 7), + }, + LogicPiece::Goal { + formula: "S".into(), + position: (15, 5), + }, + LogicPiece::Goal { + formula: "T".into(), + position: (15, 10), + }, LogicPiece::AndIntro { position: (5, 4) }, LogicPiece::OrIntro { position: (8, 6) }, LogicPiece::ImpliesIntro { position: (10, 4) }, LogicPiece::NotIntro { position: (12, 8) }, - LogicPiece::Wire { from: (3, 5), to: (5, 4) }, - LogicPiece::Wire { from: (3, 3), to: (5, 4) }, - LogicPiece::Wire { from: (6, 4), to: (8, 6) }, - LogicPiece::Wire { from: (9, 6), to: (10, 4) }, - LogicPiece::Wire { from: (11, 4), to: (15, 5) }, + LogicPiece::Wire { + from: (3, 5), + to: (5, 4), + }, + LogicPiece::Wire { + from: (3, 3), + to: (5, 4), + }, + LogicPiece::Wire { + from: (6, 4), + to: (8, 6), + }, + LogicPiece::Wire { + from: (9, 6), + to: (10, 4), + }, + LogicPiece::Wire { + from: (11, 4), + to: (15, 5), + }, ]; BoardState::with_pieces(20, 20, pieces) } @@ -43,7 +73,9 @@ fn verifiable_level() -> Level { description: "P AND Q implies R".into(), theorem: "(assert (=> (and P Q) R))".into(), initial_state: BoardState::new(10, 10), - goal_state: GoalCondition::ProveFormula { formula: "R".into() }, + goal_state: GoalCondition::ProveFormula { + formula: "R".into(), + }, } } @@ -51,9 +83,18 @@ fn verifiable_level() -> Level { /// both assumptions and the goal). fn valid_proof_pieces() -> Vec { vec![ - LogicPiece::Assumption { formula: "P".into(), position: (2, 5) }, - LogicPiece::Assumption { formula: "Q".into(), position: (2, 3) }, - LogicPiece::Goal { formula: "R".into(), position: (5, 4) }, + LogicPiece::Assumption { + formula: "P".into(), + position: (2, 5), + }, + LogicPiece::Assumption { + formula: "Q".into(), + position: (2, 3), + }, + LogicPiece::Goal { + formula: "R".into(), + position: (5, 4), + }, LogicPiece::AndIntro { position: (3, 4) }, ] } @@ -154,10 +195,19 @@ fn bench_validate_board(c: &mut Criterion) { fn bench_validate_piece_placement(c: &mut Criterion) { let board = populated_board(); let pieces = [ - LogicPiece::Assumption { formula: "X".into(), position: (0, 0) }, - LogicPiece::Wire { from: (3, 3), to: (7, 7) }, + LogicPiece::Assumption { + formula: "X".into(), + position: (0, 0), + }, + LogicPiece::Wire { + from: (3, 3), + to: (7, 7), + }, LogicPiece::AndIntro { position: (99, 99) }, // out of bounds - LogicPiece::Goal { formula: "".into(), position: (4, 4) }, // empty formula + LogicPiece::Goal { + formula: "".into(), + position: (4, 4), + }, // empty formula ]; c.bench_function("validate_piece_placement_4_pieces", |b| { @@ -178,9 +228,7 @@ fn bench_is_ready_for_verification(c: &mut Criterion) { let board = populated_board(); c.bench_function("is_ready_for_verification", |b| { - b.iter(|| { - black_box(validation::is_ready_for_verification(&board)) - }); + b.iter(|| black_box(validation::is_ready_for_verification(&board))); }); } @@ -253,9 +301,18 @@ fn bench_verify_level_solution(c: &mut Criterion) { fn bench_verify_level_solution_invalid(c: &mut Criterion) { let level = verifiable_level(); let pieces = vec![ - LogicPiece::Assumption { formula: "P".into(), position: (0, 0) }, - LogicPiece::Assumption { formula: "Q".into(), position: (0, 19) }, - LogicPiece::Goal { formula: "R".into(), position: (19, 19) }, + LogicPiece::Assumption { + formula: "P".into(), + position: (0, 0), + }, + LogicPiece::Assumption { + formula: "Q".into(), + position: (0, 19), + }, + LogicPiece::Goal { + formula: "R".into(), + position: (19, 19), + }, LogicPiece::AndIntro { position: (10, 10) }, // too far from everything ]; @@ -272,11 +329,20 @@ fn bench_verify_level_solution_invalid(c: &mut Criterion) { /// Benchmark serialization of a LogicPiece to SMT format. fn bench_piece_to_smt(c: &mut Criterion) { let pieces = [ - LogicPiece::Assumption { formula: "P".into(), position: (2, 5) }, - LogicPiece::Goal { formula: "Q".into(), position: (8, 4) }, + LogicPiece::Assumption { + formula: "P".into(), + position: (2, 5), + }, + LogicPiece::Goal { + formula: "Q".into(), + position: (8, 4), + }, LogicPiece::AndIntro { position: (5, 5) }, LogicPiece::ImpliesIntro { position: (3, 3) }, - LogicPiece::ForallIntro { position: (1, 1), variable: "x".into() }, + LogicPiece::ForallIntro { + position: (1, 1), + variable: "x".into(), + }, ]; c.bench_function("piece_to_smt_5_pieces", |b| { diff --git a/src/editor/ui.rs b/src/editor/ui.rs index 510d1f7..b042cea 100644 --- a/src/editor/ui.rs +++ b/src/editor/ui.rs @@ -4,7 +4,9 @@ use bevy::prelude::*; use bevy_egui::{egui, EguiContexts}; -use super::{EditorEntity, EditorPieceType, EditorState, EditorTool, SaveLevelEvent, TestLevelEvent}; +use super::{ + EditorEntity, EditorPieceType, EditorState, EditorTool, SaveLevelEvent, TestLevelEvent, +}; use crate::game::{GoalCondition, LogicPiece}; use crate::levels::LevelPackManager; use crate::states::GameState; @@ -124,10 +126,7 @@ pub fn editor_ui_system( for piece_type in piece_types { let selected = editor.selected_piece == Some(piece_type); - if ui - .selectable_label(selected, piece_type.name()) - .clicked() - { + if ui.selectable_label(selected, piece_type.name()).clicked() { editor.selected_piece = Some(piece_type); editor.tool = EditorTool::Place; } @@ -173,10 +172,7 @@ pub fn editor_ui_system( ui.separator(); ui.label("Name:"); - if ui - .text_edit_singleline(&mut editor.level.name) - .changed() - { + if ui.text_edit_singleline(&mut editor.level.name).changed() { editor.dirty = true; } @@ -191,10 +187,7 @@ pub fn editor_ui_system( ui.add_space(5.0); ui.label("Theorem (SMT-LIB2):"); - if ui - .text_edit_singleline(&mut editor.level.theorem) - .changed() - { + if ui.text_edit_singleline(&mut editor.level.theorem).changed() { editor.dirty = true; } @@ -207,14 +200,20 @@ pub fn editor_ui_system( ui.horizontal(|ui| { ui.label("Width:"); - if ui.add(egui::DragValue::new(&mut width).range(3..=20)).changed() { + if ui + .add(egui::DragValue::new(&mut width).range(3..=20)) + .changed() + { editor.set_grid_size(width as u32, height as u32); } }); ui.horizontal(|ui| { ui.label("Height:"); - if ui.add(egui::DragValue::new(&mut height).range(3..=20)).changed() { + if ui + .add(egui::DragValue::new(&mut height).range(3..=20)) + .changed() + { editor.set_grid_size(width as u32, height as u32); } }); @@ -396,10 +395,7 @@ pub fn editor_input_system( } /// Spawn editor grid visualization -pub fn spawn_editor_grid( - mut commands: Commands, - editor: Res, -) { +pub fn spawn_editor_grid(mut commands: Commands, editor: Res) { let half_width = editor.grid_width as f32 / 2.0; let half_height = editor.grid_height as f32 / 2.0; diff --git a/src/game/validation.rs b/src/game/validation.rs index 214dedd..41fa865 100644 --- a/src/game/validation.rs +++ b/src/game/validation.rs @@ -10,11 +10,20 @@ use super::{BoardState, GoalCondition, Level, LogicPiece}; #[derive(Debug, Clone, PartialEq, Eq)] pub enum ValidationError { /// Piece is placed outside board boundaries. - OutOfBounds { x: u32, y: u32, max_x: u32, max_y: u32 }, + OutOfBounds { + x: u32, + y: u32, + max_x: u32, + max_y: u32, + }, /// Two pieces occupy the same position. OverlappingPieces { position: (u32, u32) }, /// Wire endpoints are invalid. - InvalidWire { from: (u32, u32), to: (u32, u32), reason: String }, + InvalidWire { + from: (u32, u32), + to: (u32, u32), + reason: String, + }, /// No goals defined on the board. NoGoals, /// No assumptions defined on the board. @@ -62,7 +71,10 @@ impl ValidationResult { } /// Validate a piece placement on the board. -pub fn validate_piece_placement(board: &BoardState, piece: &LogicPiece) -> Result<(), ValidationError> { +pub fn validate_piece_placement( + board: &BoardState, + piece: &LogicPiece, +) -> Result<(), ValidationError> { let (x, y) = piece.position(); // Check bounds @@ -118,7 +130,11 @@ pub fn validate_piece_placement(board: &BoardState, piece: &LogicPiece) -> Resul }); } // Basic formula validation: must start with alphanumeric or parenthesis - if !formula.chars().next().map_or(false, |c| c.is_alphanumeric() || c == '(') { + if !formula + .chars() + .next() + .map_or(false, |c| c.is_alphanumeric() || c == '(') + { return Err(ValidationError::InvalidFormula { formula: formula.clone(), reason: "Formula must start with identifier or parenthesis".to_string(), @@ -161,8 +177,14 @@ pub fn validate_board(board: &BoardState) -> ValidationResult { } // Check for at least one assumption and one goal - let has_assumptions = board.pieces.iter().any(|p| matches!(p, LogicPiece::Assumption { .. })); - let has_goals = board.pieces.iter().any(|p| matches!(p, LogicPiece::Goal { .. })); + let has_assumptions = board + .pieces + .iter() + .any(|p| matches!(p, LogicPiece::Assumption { .. })); + let has_goals = board + .pieces + .iter() + .any(|p| matches!(p, LogicPiece::Goal { .. })); if !has_assumptions { errors.push(ValidationError::NoAssumptions); @@ -221,7 +243,10 @@ pub fn validate_level(level: &Level) -> ValidationResult { match &level.goal_state { GoalCondition::ConnectNodes { start, end } => { if start.0 >= level.initial_state.width || start.1 >= level.initial_state.height { - warnings.push(format!("Goal start node {:?} is outside board bounds", start)); + warnings.push(format!( + "Goal start node {:?} is outside board bounds", + start + )); } if end.0 >= level.initial_state.width || end.1 >= level.initial_state.height { warnings.push(format!("Goal end node {:?} is outside board bounds", end)); @@ -296,11 +321,16 @@ mod tests { #[test] fn test_out_of_bounds() { let mut board = make_test_board(); - board.pieces.push(LogicPiece::OrIntro { position: (15, 15) }); + board + .pieces + .push(LogicPiece::OrIntro { position: (15, 15) }); let result = validate_board(&board); assert!(!result.is_valid); - assert!(result.errors.iter().any(|e| matches!(e, ValidationError::OutOfBounds { .. }))); + assert!(result + .errors + .iter() + .any(|e| matches!(e, ValidationError::OutOfBounds { .. }))); } #[test] @@ -310,7 +340,10 @@ mod tests { let result = validate_board(&board); assert!(!result.is_valid); - assert!(result.errors.iter().any(|e| matches!(e, ValidationError::OverlappingPieces { .. }))); + assert!(result + .errors + .iter() + .any(|e| matches!(e, ValidationError::OverlappingPieces { .. }))); } #[test] @@ -326,7 +359,10 @@ mod tests { let result = validate_board(&board); assert!(!result.is_valid); - assert!(result.errors.iter().any(|e| matches!(e, ValidationError::NoAssumptions))); + assert!(result + .errors + .iter() + .any(|e| matches!(e, ValidationError::NoAssumptions))); } #[test] diff --git a/src/game_systems.rs b/src/game_systems.rs index e9973f8..1e722a7 100644 --- a/src/game_systems.rs +++ b/src/game_systems.rs @@ -5,8 +5,8 @@ use bevy::prelude::*; use crate::game::{ - CurrentLevel, GameEntity, Level, LogicPiece, PlaceablePiece, PlayerCursor, PlayerPlaced, - PlayerStats, SelectedPieceType, BoardState, GoalCondition, PieceBundle, + BoardState, CurrentLevel, GameEntity, GoalCondition, Level, LogicPiece, PieceBundle, + PlaceablePiece, PlayerCursor, PlayerPlaced, PlayerStats, SelectedPieceType, }; use crate::states::GameState; @@ -81,11 +81,7 @@ pub fn spawn_pieces( custom_size: Some(Vec2::new(78.0, 78.0)), ..default() }, - Transform::from_xyz( - (x as f32 - 4.5) * 80.0, - (y as f32 - 4.5) * 80.0, - -1.0, - ), + Transform::from_xyz((x as f32 - 4.5) * 80.0, (y as f32 - 4.5) * 80.0, -1.0), GameEntity, )); } @@ -131,12 +127,7 @@ pub fn handle_input( windows: Query<&Window>, camera_query: Query<(&Camera, &GlobalTransform)>, piece_query: Query< - ( - Entity, - &LogicPiece, - &Transform, - Option<&PlayerPlaced>, - ), + (Entity, &LogicPiece, &Transform, Option<&PlayerPlaced>), Without, >, mut commands: Commands, diff --git a/src/levels/ui.rs b/src/levels/ui.rs index e4b4aa7..c4779dc 100644 --- a/src/levels/ui.rs +++ b/src/levels/ui.rs @@ -151,8 +151,15 @@ pub fn level_select_ui_system( egui::ScrollArea::vertical() .id_salt("level_list") .show(&mut columns[1], |ui| { - for (level_idx, _level_id, name, description, is_selected, is_completed, best_time) in - levels + for ( + level_idx, + _level_id, + name, + description, + is_selected, + is_completed, + best_time, + ) in levels { ui.group(|ui| { ui.horizontal(|ui| { diff --git a/src/main.rs b/src/main.rs index 55c8837..e4d2c14 100644 --- a/src/main.rs +++ b/src/main.rs @@ -49,40 +49,31 @@ fn main() { let mut app = App::new(); app - // Core Bevy plugins - .add_plugins(DefaultPlugins.set(WindowPlugin { - primary_window: Some(Window { - title: "Proof of Work - Logic Puzzle Game".into(), - resolution: (1280, 720).into(), - resizable: true, + // Core Bevy plugins + .add_plugins(DefaultPlugins.set(WindowPlugin { + primary_window: Some(Window { + title: "Proof of Work - Logic Puzzle Game".into(), + resolution: (1280, 720).into(), + resizable: true, + ..default() + }), ..default() - }), - ..default() - })) - - // Egui plugin for UI - .add_plugins(EguiPlugin::default()) - - // Initialize game state - .init_state::() - - // Player stats resource - .insert_resource(PlayerStats::default()) - - // Selected piece type resource - .insert_resource(SelectedPieceType::default()) - - // Level pack manager - .insert_resource(LevelPackManager::new( - PathBuf::from("./data/packs") - )) - - // Editor state - .insert_resource(EditorState::default()) - - // Editor events (messages in Bevy 0.17) - .add_message::() - .add_message::(); + })) + // Egui plugin for UI + .add_plugins(EguiPlugin::default()) + // Initialize game state + .init_state::() + // Player stats resource + .insert_resource(PlayerStats::default()) + // Selected piece type resource + .insert_resource(SelectedPieceType::default()) + // Level pack manager + .insert_resource(LevelPackManager::new(PathBuf::from("./data/packs"))) + // Editor state + .insert_resource(EditorState::default()) + // Editor events (messages in Bevy 0.17) + .add_message::() + .add_message::(); // Insert Steam as a resource (if available) #[cfg(feature = "steam")] @@ -107,68 +98,74 @@ fn main() { } app - // Startup systems (run once at launch) - .add_systems(Startup, (setup_camera, levels::ui::init_level_packs)) - - // Systems that run every frame in MainMenu state - .add_systems(Update, ( - ui::main_menu_system, - ui::handle_menu_input, - ).run_if(in_state(GameState::MainMenu))) - - // Level select state - .add_systems(Update, levels::ui::level_select_ui_system.run_if(in_state(GameState::LevelSelect))) - .add_systems(OnExit(GameState::LevelSelect), levels::ui::save_level_progress) - - // Editor state - .add_systems(OnEnter(GameState::Editor), editor::ui::spawn_editor_grid) - .add_systems(Update, ( - editor::ui::editor_ui_system, - editor::ui::editor_input_system, - editor::ui::update_editor_pieces, - editor::ui::handle_test_level, - editor::ui::handle_save_level, - ).run_if(in_state(GameState::Editor))) - .add_systems(OnExit(GameState::Editor), editor::ui::cleanup_editor) - - // Systems when entering Playing state - .add_systems(OnEnter(GameState::Playing), ( - game_systems::load_level, - game_systems::spawn_pieces, - ).chain()) - - // Systems that run every frame in Playing state - .add_systems(Update, ( - game_systems::handle_input, - game_systems::update_board, - game_systems::update_piece_positions, - game_systems::check_connections, - game_systems::check_solution, - ui::update_hud, - ).run_if(in_state(GameState::Playing))); + // Startup systems (run once at launch) + .add_systems(Startup, (setup_camera, levels::ui::init_level_packs)) + // Systems that run every frame in MainMenu state + .add_systems( + Update, + (ui::main_menu_system, ui::handle_menu_input).run_if(in_state(GameState::MainMenu)), + ) + // Level select state + .add_systems( + Update, + levels::ui::level_select_ui_system.run_if(in_state(GameState::LevelSelect)), + ) + .add_systems( + OnExit(GameState::LevelSelect), + levels::ui::save_level_progress, + ) + // Editor state + .add_systems(OnEnter(GameState::Editor), editor::ui::spawn_editor_grid) + .add_systems( + Update, + ( + editor::ui::editor_ui_system, + editor::ui::editor_input_system, + editor::ui::update_editor_pieces, + editor::ui::handle_test_level, + editor::ui::handle_save_level, + ) + .run_if(in_state(GameState::Editor)), + ) + .add_systems(OnExit(GameState::Editor), editor::ui::cleanup_editor) + // Systems when entering Playing state + .add_systems( + OnEnter(GameState::Playing), + (game_systems::load_level, game_systems::spawn_pieces).chain(), + ) + // Systems that run every frame in Playing state + .add_systems( + Update, + ( + game_systems::handle_input, + game_systems::update_board, + game_systems::update_piece_positions, + game_systems::check_connections, + game_systems::check_solution, + ui::update_hud, + ) + .run_if(in_state(GameState::Playing)), + ); // Steam callbacks (if available) #[cfg(feature = "steam")] app.add_systems(Update, steam_callbacks.run_if(in_state(GameState::Playing))); app - // Systems when entering LevelComplete state - .add_systems(OnEnter(GameState::LevelComplete), on_level_complete) - - // Systems that run in LevelComplete state - .add_systems(Update, ( - ui::show_completion_screen, - ui::handle_completion_input, - ).run_if(in_state(GameState::LevelComplete))) - - // Systems when exiting Playing state - .add_systems(OnExit(GameState::Playing), game_systems::cleanup_level) - - // Run the app - .run(); + // Systems when entering LevelComplete state + .add_systems(OnEnter(GameState::LevelComplete), on_level_complete) + // Systems that run in LevelComplete state + .add_systems( + Update, + (ui::show_completion_screen, ui::handle_completion_input) + .run_if(in_state(GameState::LevelComplete)), + ) + // Systems when exiting Playing state + .add_systems(OnExit(GameState::Playing), game_systems::cleanup_level) + // Run the app + .run(); } - // Startup systems fn setup_camera(mut commands: Commands) { commands.spawn(Camera2d); diff --git a/src/network/client.rs b/src/network/client.rs index 1787045..1ee9da1 100644 --- a/src/network/client.rs +++ b/src/network/client.rs @@ -1,8 +1,8 @@ // SPDX-License-Identifier: PMPL-1.0-or-later +use super::{LeaderboardEntry, ProofSubmission, ServerResponse}; +use crate::verification::ExportedProof; use reqwest::Client; use serde::{Deserialize, Serialize}; -use crate::verification::ExportedProof; -use super::{ProofSubmission, ServerResponse, LeaderboardEntry}; const SERVER_URL: &str = "https://api.proofofwork.game"; @@ -16,25 +16,29 @@ impl NetworkClient { pub fn new(api_key: String) -> Self { Self { client: Client::builder() - .timeout(std::time::Duration::from_secs(30)) - .build() - .expect("Failed to create HTTP client"), + .timeout(std::time::Duration::from_secs(30)) + .build() + .expect("Failed to create HTTP client"), api_key, } } - pub async fn submit_proof(&self, proof: ExportedProof) -> Result> { + pub async fn submit_proof( + &self, + proof: ExportedProof, + ) -> Result> { let submission = ProofSubmission { proof: proof.clone(), signature: Self::sign_proof(&proof, &self.api_key), }; - let response = self.client - .post(&format!("{}/api/v1/proofs", SERVER_URL)) - .header("Authorization", format!("Bearer {}", self.api_key)) - .json(&submission) - .send() - .await?; + let response = self + .client + .post(&format!("{}/api/v1/proofs", SERVER_URL)) + .header("Authorization", format!("Bearer {}", self.api_key)) + .json(&submission) + .send() + .await?; if !response.status().is_success() { return Err(format!("Server returned error: {}", response.status()).into()); @@ -44,14 +48,18 @@ impl NetworkClient { Ok(server_response) } - pub async fn get_leaderboard(&self, limit: Option) -> Result, Box> { + pub async fn get_leaderboard( + &self, + limit: Option, + ) -> Result, Box> { let limit = limit.unwrap_or(100); - let response = self.client - .get(&format!("{}/api/v1/leaderboard", SERVER_URL)) - .query(&[("limit", limit)]) - .send() - .await?; + let response = self + .client + .get(&format!("{}/api/v1/leaderboard", SERVER_URL)) + .query(&[("limit", limit)]) + .send() + .await?; if !response.status().is_success() { return Err(format!("Server returned error: {}", response.status()).into()); @@ -61,12 +69,15 @@ impl NetworkClient { Ok(leaderboard) } - pub async fn get_player_stats(&self) -> Result> { - let response = self.client - .get(&format!("{}/api/v1/player/stats", SERVER_URL)) - .header("Authorization", format!("Bearer {}", self.api_key)) - .send() - .await?; + pub async fn get_player_stats( + &self, + ) -> Result> { + let response = self + .client + .get(&format!("{}/api/v1/player/stats", SERVER_URL)) + .header("Authorization", format!("Bearer {}", self.api_key)) + .send() + .await?; if !response.status().is_success() { return Err(format!("Server returned error: {}", response.status()).into()); @@ -77,11 +88,11 @@ impl NetworkClient { } fn sign_proof(proof: &ExportedProof, api_key: &str) -> String { - use sha2::{Sha256, Digest}; + use sha2::{Digest, Sha256}; let mut hasher = Sha256::new(); - let proof_json = serde_json::to_string(proof) - .unwrap_or_else(|_| format!("{:?}", proof.proof_smt2)); + let proof_json = + serde_json::to_string(proof).unwrap_or_else(|_| format!("{:?}", proof.proof_smt2)); hasher.update(proof_json); hasher.update(api_key.as_bytes()); diff --git a/src/steam/mod.rs b/src/steam/mod.rs index 171dad5..10d20b9 100644 --- a/src/steam/mod.rs +++ b/src/steam/mod.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: PMPL-1.0-or-later -use steamworks::*; use std::sync::Arc; +use steamworks::*; pub struct SteamManager { client: Arc, @@ -13,7 +13,7 @@ impl SteamManager { let (client, single) = Client::init()?; Ok(Self { client: Arc::new(client), - single, + single, }) } diff --git a/src/verification/mod.rs b/src/verification/mod.rs index ba22629..c81ac05 100644 --- a/src/verification/mod.rs +++ b/src/verification/mod.rs @@ -78,7 +78,7 @@ fn is_adjacent(a: (u32, u32), b: (u32, u32)) -> bool { /// For the vertical slice: check if pieces form a valid proof #[cfg(feature = "z3-verify")] pub fn verify_level_solution(_level: &Level, pieces: &[LogicPiece]) -> bool { - use z3::ast::{Ast, Bool}; + use z3::ast::Bool; use z3::{Config, Context, Solver}; let cfg = Config::new(); diff --git a/tests/aspect_test.rs b/tests/aspect_test.rs index 3d1ee5d..20d833f 100644 --- a/tests/aspect_test.rs +++ b/tests/aspect_test.rs @@ -48,7 +48,10 @@ fn aspect_large_formula_string() { assert_eq!(board.piece_count(), 1); // Verify formula is stored - if let Some(LogicPiece::Assumption { formula: stored, .. }) = board.piece_at(0, 0) { + if let Some(LogicPiece::Assumption { + formula: stored, .. + }) = board.piece_at(0, 0) + { assert_eq!(stored.len(), 10000); } } @@ -67,7 +70,10 @@ fn aspect_html_injection_in_formula() { assert!(board.place_piece(piece)); // Verify HTML string is stored verbatim - if let Some(LogicPiece::Assumption { formula: stored, .. }) = board.piece_at(2, 2) { + if let Some(LogicPiece::Assumption { + formula: stored, .. + }) = board.piece_at(2, 2) + { assert_eq!(stored, formula); } } @@ -274,7 +280,10 @@ fn aspect_large_board_with_1000_pieces() { } } - assert_eq!(placed_count, 100, "Should place 100 pieces (10x10 grid of 100x100 board)"); + assert_eq!( + placed_count, 100, + "Should place 100 pieces (10x10 grid of 100x100 board)" + ); assert_eq!(board.piece_count(), placed_count); } @@ -283,7 +292,9 @@ fn aspect_spatial_query_on_large_board() { let mut board = BoardState::new(200, 200); // Place piece in center - assert!(board.place_piece(LogicPiece::AndIntro { position: (100, 100) })); + assert!(board.place_piece(LogicPiece::AndIntro { + position: (100, 100) + })); // Query with small radius let nearby = board.pieces_near(100, 100, 5); @@ -338,7 +349,10 @@ fn aspect_chinese_characters_in_formula() { let mut board = BoardState::new(10, 10); assert!(board.place_piece(piece)); - if let Some(LogicPiece::Assumption { formula: stored, .. }) = board.piece_at(1, 1) { + if let Some(LogicPiece::Assumption { + formula: stored, .. + }) = board.piece_at(1, 1) + { assert_eq!(stored, formula); } } @@ -355,7 +369,10 @@ fn aspect_arabic_characters_in_formula() { let mut board = BoardState::new(10, 10); assert!(board.place_piece(piece)); - if let Some(LogicPiece::Goal { formula: stored, .. }) = board.piece_at(2, 2) { + if let Some(LogicPiece::Goal { + formula: stored, .. + }) = board.piece_at(2, 2) + { assert_eq!(stored, formula); } } @@ -372,7 +389,10 @@ fn aspect_emoji_in_formula() { let mut board = BoardState::new(10, 10); assert!(board.place_piece(piece)); - if let Some(LogicPiece::Assumption { formula: stored, .. }) = board.piece_at(3, 3) { + if let Some(LogicPiece::Assumption { + formula: stored, .. + }) = board.piece_at(3, 3) + { assert_eq!(stored, formula); } } @@ -389,7 +409,10 @@ fn aspect_mixed_scripts_in_formula() { let mut board = BoardState::new(10, 10); assert!(board.place_piece(piece)); - if let Some(LogicPiece::Assumption { formula: stored, .. }) = board.piece_at(4, 4) { + if let Some(LogicPiece::Assumption { + formula: stored, .. + }) = board.piece_at(4, 4) + { assert_eq!(stored, formula); } } @@ -398,11 +421,11 @@ fn aspect_mixed_scripts_in_formula() { fn aspect_whitespace_variants_in_formula() { // Test various whitespace characters let formulas = vec![ - "P Q".to_string(), // Regular space - "P\tQ".to_string(), // Tab - "P\nQ".to_string(), // Newline - "P\u{00A0}Q".to_string(), // Non-breaking space - "P\u{2003}Q".to_string(), // Em space + "P Q".to_string(), // Regular space + "P\tQ".to_string(), // Tab + "P\nQ".to_string(), // Newline + "P\u{00A0}Q".to_string(), // Non-breaking space + "P\u{2003}Q".to_string(), // Em space ]; let mut board = BoardState::new(10, 10); diff --git a/tests/e2e_test.rs b/tests/e2e_test.rs index 2bf3477..9bf9e75 100644 --- a/tests/e2e_test.rs +++ b/tests/e2e_test.rs @@ -106,7 +106,10 @@ fn e2e_place_and_remove_pieces() { // Verify removal was complete let after_removal = board.piece_at(5, 5); - assert!(after_removal.is_none(), "Position should be empty after removal"); + assert!( + after_removal.is_none(), + "Position should be empty after removal" + ); } #[test] @@ -236,7 +239,12 @@ fn e2e_editor_place_pieces_save_cycle() { // Verify each piece is at expected position for piece in &pieces_to_place { let (x, y) = piece.position(); - assert!(board.is_occupied(x, y), "Piece at ({},{}) should be occupied", x, y); + assert!( + board.is_occupied(x, y), + "Piece at ({},{}) should be occupied", + x, + y + ); } } @@ -491,9 +499,7 @@ fn e2e_pieces_at_board_boundaries() { let mut board = BoardState::new(width, height); // Place pieces at all four corners - assert!(board.place_piece(LogicPiece::AndIntro { - position: (0, 0), - })); + assert!(board.place_piece(LogicPiece::AndIntro { position: (0, 0) })); assert!(board.place_piece(LogicPiece::OrIntro { position: (width - 1, 0), })); diff --git a/tests/property_test.rs b/tests/property_test.rs index 05a00b6..4551bec 100644 --- a/tests/property_test.rs +++ b/tests/property_test.rs @@ -7,8 +7,8 @@ //! - Spatial query consistency //! - Formula preservation -use proptest::prelude::*; use proof_of_work::{BoardState, LogicPiece}; +use proptest::prelude::*; // ============================================================================ // Strategy definitions for property-based testing @@ -26,8 +26,7 @@ fn position_strategy(width: u32, height: u32) -> impl Strategy impl Strategy { - r"[A-Z][A-Z0-9]*( (AND|OR|IMPLIES|NOT) [A-Z][A-Z0-9]*)*" - .prop_map(|s| s.to_string()) + r"[A-Z][A-Z0-9]*( (AND|OR|IMPLIES|NOT) [A-Z][A-Z0-9]*)*".prop_map(|s| s.to_string()) } // ============================================================================