forked from VuqarAhadli/MiniOS-ESP
-
Notifications
You must be signed in to change notification settings - Fork 0
Add host-side games module and CLI commands; add release-binaries CI workflow #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| name: Release Rust Binaries | ||
|
|
||
| on: | ||
| push: | ||
| tags: | ||
| - "v*.*.*" | ||
| workflow_dispatch: | ||
|
|
||
| permissions: | ||
| contents: write | ||
|
|
||
| jobs: | ||
| build: | ||
| name: Build ${{ matrix.target }} | ||
| runs-on: ${{ matrix.os }} | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| include: | ||
| - os: ubuntu-latest | ||
| target: x86_64-unknown-linux-gnu | ||
| archive: tar.gz | ||
| - os: macos-13 | ||
| target: x86_64-apple-darwin | ||
| archive: tar.gz | ||
| - os: windows-latest | ||
| target: x86_64-pc-windows-msvc | ||
| archive: zip | ||
|
|
||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Install Rust toolchain | ||
| uses: dtolnay/rust-toolchain@stable | ||
| with: | ||
| targets: ${{ matrix.target }} | ||
|
|
||
| - name: Build minios binary | ||
| run: cargo build --release --locked --bin minios --target ${{ matrix.target }} | ||
|
|
||
| - name: Package Unix binary | ||
| if: runner.os != 'Windows' | ||
| shell: bash | ||
| run: | | ||
| set -euo pipefail | ||
| mkdir -p dist/package | ||
| cp "target/${{ matrix.target }}/release/minios" dist/package/minios | ||
| tar -C dist/package -czf "dist/minios-${{ matrix.target }}.tar.gz" minios | ||
|
|
||
| - name: Package Windows binary | ||
| if: runner.os == 'Windows' | ||
| shell: pwsh | ||
| run: | | ||
| New-Item -ItemType Directory -Force -Path dist/package | Out-Null | ||
| Copy-Item "target/${{ matrix.target }}/release/minios.exe" "dist/package/minios.exe" | ||
| Compress-Archive -Path "dist/package/minios.exe" -DestinationPath "dist/minios-${{ matrix.target }}.zip" -Force | ||
|
|
||
| - name: Upload binary artifact | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: minios-${{ matrix.target }} | ||
| path: dist/minios-${{ matrix.target }}.${{ matrix.archive }} | ||
| if-no-files-found: error | ||
|
|
||
| - name: Upload binaries to GitHub release | ||
| if: startsWith(github.ref, 'refs/tags/') | ||
| shell: bash | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| TAG_NAME: ${{ github.ref_name }} | ||
| run: | | ||
| set -euo pipefail | ||
| gh release view "${TAG_NAME}" >/dev/null 2>&1 || \ | ||
| gh release create "${TAG_NAME}" --title "${TAG_NAME}" --notes "Automated MiniOS Rust host CLI binaries." | ||
| gh release upload "${TAG_NAME}" dist/minios-${{ matrix.target }}.${{ matrix.archive }} --clobber | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,209 @@ | ||
| use std::time::{SystemTime, UNIX_EPOCH}; | ||
|
|
||
| const X_MAX: f32 = 320.0; | ||
| const Y_MAX: f32 = 230.0; | ||
|
|
||
| #[derive(Debug, Clone)] | ||
| struct Lcg { | ||
| state: u64, | ||
| } | ||
|
|
||
| impl Lcg { | ||
| fn seeded() -> Self { | ||
| let nanos = SystemTime::now() | ||
| .duration_since(UNIX_EPOCH) | ||
| .map(|duration| duration.as_nanos() as u64) | ||
| .unwrap_or(0x5eed_2024); | ||
| Self { | ||
| state: nanos ^ 0xa5a5_5a5a_1337_2024, | ||
| } | ||
| } | ||
|
|
||
| fn next_u32(&mut self) -> u32 { | ||
| self.state = self | ||
| .state | ||
| .wrapping_mul(6364136223846793005) | ||
| .wrapping_add(1442695040888963407); | ||
| (self.state >> 32) as u32 | ||
| } | ||
|
|
||
| fn next_range(&mut self, min: u32, max: u32) -> u32 { | ||
| debug_assert!(min <= max); | ||
| min + (self.next_u32() % (max - min + 1)) | ||
| } | ||
|
|
||
| fn next_unit(&mut self) -> f32 { | ||
| self.next_u32() as f32 / u32::MAX as f32 | ||
| } | ||
| } | ||
|
|
||
| #[derive(Debug, Clone)] | ||
| struct BallState { | ||
| x: f32, | ||
| y: f32, | ||
| speed_x: f32, | ||
| speed_y: f32, | ||
| } | ||
|
|
||
| /// 弹球游戏:host Rust 版本用文本帧模拟 C++ TFT 弹球动画。 | ||
| pub fn ball_game(radius: u16, num_balls: usize, trail: bool) -> Vec<String> { | ||
| const GRAVITY: f32 = 0.05; | ||
| const FLOOR_DAMP: f32 = 0.85; | ||
| const WALL_DAMP: f32 = 0.90; | ||
|
|
||
| let mut rng = Lcg::seeded(); | ||
| let radius_f = radius as f32; | ||
| let mut balls = (0..num_balls) | ||
| .map(|index| { | ||
| let angle = rng.next_unit() * std::f32::consts::TAU; | ||
| let speed = 1.5 + rng.next_unit() * 3.5; | ||
| BallState { | ||
| x: X_MAX / 2.0 + index as f32, | ||
| y: Y_MAX / 2.0, | ||
| speed_x: speed * angle.cos(), | ||
| speed_y: speed * angle.sin(), | ||
| } | ||
| }) | ||
| .collect::<Vec<_>>(); | ||
|
|
||
| for _ in 0..12 { | ||
| for ball in &mut balls { | ||
| ball.speed_y += GRAVITY; | ||
| ball.x += ball.speed_x; | ||
| ball.y += ball.speed_y; | ||
|
|
||
| if ball.x - radius_f <= 0.0 { | ||
| ball.speed_x = ball.speed_x.abs() * WALL_DAMP; | ||
| ball.x = radius_f; | ||
| } else if ball.x + radius_f >= X_MAX { | ||
| ball.speed_x = -ball.speed_x.abs() * WALL_DAMP; | ||
| ball.x = X_MAX - radius_f; | ||
| } | ||
|
|
||
| if ball.y - radius_f <= 0.0 { | ||
| ball.speed_y = ball.speed_y.abs() * WALL_DAMP; | ||
| ball.y = radius_f; | ||
| } else if ball.y + radius_f >= Y_MAX { | ||
| ball.speed_y = -ball.speed_y.abs() * FLOOR_DAMP; | ||
| ball.y = Y_MAX - radius_f; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| let mut lines = vec![ | ||
| format!("Ball / 弹球: radius={radius}, balls={num_balls}, trail={trail}"), | ||
| "Host preview: simulated 12 physics frames without TFT hardware.".to_string(), | ||
| ]; | ||
| for (index, ball) in balls.iter().enumerate() { | ||
| lines.push(format!( | ||
| "ball #{:02}: pos=({:.1},{:.1}) vel=({:.2},{:.2})", | ||
| index + 1, | ||
| ball.x, | ||
| ball.y, | ||
| ball.speed_x, | ||
| ball.speed_y | ||
| )); | ||
| } | ||
| lines | ||
| } | ||
|
|
||
| /// 乒乓游戏:host Rust 版本生成一局短回合预览。 | ||
| pub fn pingpong_game() -> Vec<String> { | ||
| let mut rng = Lcg::seeded(); | ||
| let mut x = X_MAX / 2.0; | ||
| let mut y = Y_MAX / 2.0; | ||
| let mut speed_x = if rng.next_u32() % 2 == 0 { 2.0 } else { -2.0 }; | ||
| let mut speed_y = if rng.next_u32() % 2 == 0 { 1.5 } else { -1.5 }; | ||
| let racket_x = 8.0; | ||
| let racket_y = 92.0; | ||
| let racket_len = 30.0; | ||
| let radius = 5.0; | ||
| let mut score = 0; | ||
|
|
||
| for _ in 0..48 { | ||
| x += speed_x; | ||
| y += speed_y; | ||
|
|
||
| if y - radius <= 0.0 || y + radius >= Y_MAX { | ||
| speed_y = -speed_y; | ||
| } | ||
|
|
||
| if x + radius >= X_MAX { | ||
| speed_x = -speed_x; | ||
| } | ||
|
|
||
| if x - radius <= racket_x + 5.0 && y >= racket_y && y <= racket_y + racket_len { | ||
| x = racket_x + 5.0 + radius; | ||
| speed_x = speed_x.abs(); | ||
| score += 1; | ||
| } | ||
|
|
||
| if x - radius < 0.0 { | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| vec![ | ||
| "PingPong / 乒乓: host preview".to_string(), | ||
| format!("score={score}, ball=({x:.1},{y:.1}), velocity=({speed_x:.1},{speed_y:.1})"), | ||
| "Use the C++ ESP32 firmware for live Serial controls and TFT animation.".to_string(), | ||
| ] | ||
| } | ||
|
|
||
| /// D20 骰子游戏:host Rust 版本执行一次二十面骰投掷。 | ||
| pub fn d20_game() -> Vec<String> { | ||
| let mut rng = Lcg::seeded(); | ||
| let value = rng.next_range(1, 20); | ||
| let note = match value { | ||
| 20 => "Lucky! / 幸运!", | ||
| 1 => "Unlucky :( / 不太走运 :(", | ||
| _ => "Roll complete / 投掷完成", | ||
| }; | ||
|
|
||
| vec![ | ||
| "D20 / 二十面骰子: roll".to_string(), | ||
| format!("Rolled: {value}"), | ||
| note.to_string(), | ||
| ] | ||
| } | ||
|
|
||
| /// 抛硬币游戏:host Rust 版本执行一次硬币翻转。 | ||
| pub fn coin_game() -> Vec<String> { | ||
| let mut rng = Lcg::seeded(); | ||
| let heads = rng.next_u32() % 2 == 0; | ||
| let result = if heads { | ||
| "Heads / 正面" | ||
| } else { | ||
| "Tails / 反面" | ||
| }; | ||
|
|
||
| vec![ | ||
| "Coin / 抛硬币: flip".to_string(), | ||
| format!("You got: {result}"), | ||
| ] | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
|
|
||
| #[test] | ||
| fn ball_preview_contains_requested_count() { | ||
| let lines = ball_game(10, 2, true); | ||
| assert!(lines[0].contains("弹球")); | ||
| assert_eq!( | ||
| lines | ||
| .iter() | ||
| .filter(|line| line.starts_with("ball #")) | ||
| .count(), | ||
| 2 | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn chance_games_include_chinese_labels() { | ||
| assert!(pingpong_game()[0].contains("乒乓")); | ||
| assert!(d20_game()[0].contains("二十面骰子")); | ||
| assert!(coin_game()[0].contains("抛硬币")); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,7 @@ | |
|
|
||
| pub mod display; | ||
| pub mod filesystem; | ||
| pub mod games; | ||
| pub mod kernel; | ||
| pub mod shell; | ||
| pub mod theme; | ||
|
|
||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.