Full Code of aochagavia/rocket_wasm for AI

master 95c902703d72 cached
26 files
33.4 KB
9.2k tokens
108 symbols
1 requests
Download .txt
Repository: aochagavia/rocket_wasm
Branch: master
Commit: 95c902703d72
Files: 26
Total size: 33.4 KB

Directory structure:
gitextract_okp2mtnu/

├── .gitattributes
├── .gitignore
├── Cargo.toml
├── LICENSE.md
├── html/
│   ├── index.html
│   └── program.wasm
├── post_build.py
├── readme.md
└── src/
    ├── controllers/
    │   ├── collisions.rs
    │   ├── input.rs
    │   ├── mod.rs
    │   └── time.rs
    ├── game_state.rs
    ├── geometry/
    │   ├── mod.rs
    │   ├── point.rs
    │   ├── size.rs
    │   └── traits.rs
    ├── lib.rs
    ├── models/
    │   ├── bullet.rs
    │   ├── enemy.rs
    │   ├── mod.rs
    │   ├── particle.rs
    │   ├── player.rs
    │   ├── vector.rs
    │   └── world.rs
    └── util.rs

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitattributes
================================================
# Auto detect text files and perform LF normalization
* text=auto

================================================
FILE: .gitignore
================================================
target
.vscode
.idea
html/rocket.wasm


================================================
FILE: Cargo.toml
================================================
[package]
name = "rocket"
version = "1.0.0"
authors = ["Adolfo Ochagavía <aochagavia92@gmail.com>"]

[lib]
crate-type = ["cdylib"]

[dependencies]
clippy = { version = "0.0.118", optional = true }
itertools-num = "0.1.1"
lazy_static = "1.0"
rand = "0.3.18"
pcg_rand = "0.7.1"

[patch.crates-io]
rand = { git = "https://github.com/aochagavia/rand.git", branch = "wasm" }

[features]
default = []


================================================
FILE: LICENSE.md
================================================
The MIT License (MIT)

Copyright (c) 2015 Adolfo Ochagavía

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


================================================
FILE: html/index.html
================================================
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <style>
      html, body {
        width: 100%;
        height: 100%;
        margin: 0px;
        border: 0;
        overflow: hidden; /*  Disable scrollbars */
        display: block;  /* No floating content on sides */
      }
      </style>
  </head>

  <body>
    <canvas id="canvas" style='position:absolute; left:0px; top:0px; width: 100%'></canvas>

<script>


  // Returns an object containing resources that will be used later for drawing
  function resources() {
    let res = {
      player: document.createElement('canvas'),
      enemy: document.createElement('canvas'),
      bullet: document.createElement('canvas'),
      particle: document.createElement('canvas')
    }

    // Particle
    res.particle.width = 20;
    res.particle.height = 20;
    let pCtx = res.particle.getContext('2d');
    pCtx.fillStyle = "darkviolet";
    pCtx.beginPath();
    pCtx.arc(10, 10, 10, 0, 2 * Math.PI);
    pCtx.fill();

    // Bullet
    res.bullet.width = 6;
    res.bullet.height = 6;
    let bCtx = res.bullet.getContext('2d');
    bCtx.fillStyle = "blue";
    bCtx.beginPath();
    bCtx.arc(3, 3, 3, 0, 2 * Math.PI);
    bCtx.fill();

    // Enemy
    res.enemy.width = 20;
    res.enemy.height = 20;
    let eCtx = res.enemy.getContext('2d');
    eCtx.fillStyle = "yellow";
    eCtx.beginPath();
    eCtx.arc(10, 10, 10, 0, 2 * Math.PI);
    eCtx.fill();

    // Player
    res.player.width = 20;
    res.player.height = 16;
    let plCtx = res.player.getContext('2d');
    plCtx.fillStyle = "red";
    plCtx.beginPath();
    plCtx.lineTo(20, 8);
    plCtx.lineTo(0, 16);
    plCtx.lineTo(0, 0);
    plCtx.fill();

    return res;
  }

  // We create this here because it will be used from within `imports`
  let canvas = document.getElementById('canvas');

  // Returns an object containing functions that will be linked to our wasm model
  // This means that they can be called from Rust
  function imports() {
    const res = resources();
    var ctx = canvas.getContext("2d");

    function clear_screen() {
      ctx.fillStyle = "black";
      ctx.fillRect(0, 0, canvas.width, canvas.height);
    }

    function draw_player(x, y, angle) {
      ctx.translate(x, y);
      ctx.rotate(angle);
      ctx.translate(0, -8);
      ctx.drawImage(res.player, 0, 0);
      ctx.setTransform(1, 0, 0, 1, 0, 0);

      ctx.fillStyle = "black";
      //ctx.fillRect(x - 17, y - 12, 4, 4);
    }

    function draw_enemy(x, y) {
      ctx.drawImage(res.enemy, x - 10, y - 10);
    }

    function draw_bullet(x, y) {
      ctx.drawImage(res.bullet, x - 3, y - 3);
    }

    function draw_particle(x, y, radius) {
      ctx.drawImage(res.particle, x - radius, y - radius, 2 * radius, 2 * radius);
    }

    function draw_score(x) {
      ctx.fillStyle = "orange";
      ctx.textBaseline = "top";
      ctx.font = "20px sans-serif";
      ctx.fillText('Score: ' + x, 10, 10)
    }

    // The real loading and running of our wasm starts here
    let imports = { clear_screen, draw_player, draw_enemy, draw_bullet, draw_particle, draw_score };
    imports.Math_atan = Math.atan;
    imports.sin = Math.sin;
    imports.cos = Math.cos;
    return imports;
  }

  // Fetch and instantiate our wasm module
  fetch("program.wasm").then(response =>
    response.arrayBuffer()
  ).then(bytes =>
    WebAssembly.instantiate(bytes, { env: imports() })
  ).then(results => {
    let module = {};
    let mod = results.instance;
    module.update = mod.exports.update;
    module.toggle_shoot = mod.exports.toggle_shoot;
    module.toggle_boost = mod.exports.toggle_boost;
    module.toggle_turn_left = mod.exports.toggle_turn_left;
    module.toggle_turn_right = mod.exports.toggle_turn_right;
    module.resize = mod.exports.resize;
    module.draw = mod.exports.draw;

    // Input processing
    function processKey(key, b) {
      switch (key) {
        case "ArrowLeft":
          module.toggle_turn_left(b);
          break;
        case "ArrowRight":
          module.toggle_turn_right(b);
          break;
        case "ArrowUp":
          module.toggle_boost(b);
          break;
        case " ":
          module.toggle_shoot(b);
          break;
      }
    }
    document.addEventListener('keydown', e => processKey(e.key, true));
    document.addEventListener('keyup', e => processKey(e.key, false));

    // Resizing
    function resize() {
      // We make the canvas somewhat smaller to get some zooming
      canvas.width = window.innerWidth * 0.8;
      canvas.height = window.innerHeight * 0.8;
      module.resize(canvas.width, canvas.height);
    }
    window.addEventListener('resize', () => {
      resize();
    });

    // Game loop
    let start = null;
    let prevTimestamp = null;
    let drawAndUpdate = (timestamp) => {
      // Initialization
      if (!prevTimestamp) {
        start = timestamp;
        prevTimestamp = timestamp;
        requestAnimationFrame(drawAndUpdate);
        return;
      }

      // Update and draw
      let progress = (timestamp - prevTimestamp) / 1000;
      module.update(progress);
      module.draw();

      // Some bookkeeping
      prevTimestamp = timestamp;
      requestAnimationFrame(drawAndUpdate);
    };

    resize();
    drawAndUpdate();
  });
  </script>
  </body>
</html>


================================================
FILE: post_build.py
================================================
from shutil import copyfile
from subprocess import call

copyfile('target/wasm32-unknown-unknown/release/rocket.wasm', 'html/rocket.wasm')
call(['wasm-gc', 'html/rocket.wasm', 'html/program.wasm'])


================================================
FILE: readme.md
================================================
Rocket on WASM
==============

An adapted version of the [Rocket](https://github.com/aochagavia/rocket) game, running on WASM!

[Read the blog post](https://ochagavia.nl/blog/rocket-a-rust-game-running-on-wasm/)
about the development of this WASM version (includes an embedded canvas where you can play the game).

## Screenshot

![Screenshot](screenshots/gameplay1.png)

## How to play

As you can see in the screenshot, you are the red rocket and have to save the world from
the yellow invaders. To do so, you can use the following controls:

Keyboard                | Action
----------------------- | ------------
<kbd>&uparrow;</kbd>    | Boost
<kbd>&leftarrow;</kbd>  | Rotate left
<kbd>&rightarrow;</kbd> | Rotate right
<kbd>Space</kbd>        | Shoot

## Compiling and running

Follow the steps on the [hellorust website](https://www.hellorust.com/setup/wasm-target/)
in order to set up everything. Besides the `wasm32-unknown-unknown` target, the `post_build.py`
script requires python 2.7 and `wasm-gc`.

After setting things up, you should be able to compile the code using the commands below:

```
cargo build --release --target wasm32-unknown-unknown
python post_build.py
```

The generated wasm will be copied to the `html` directory and `wasm-gc`ed.

```
python -m SimpleHTTPServer
```

Try opening http://localhost:8000/ on your browser to check whether it works.

## Related projects

* [Running Rocket in a Python environment through WebAssembly](https://almarklein.org/python_and_webassembly.html)


================================================
FILE: src/controllers/collisions.rs
================================================
use game_state::GameState;
use geometry::{Collide, Position};
use util;

const SCORE_PER_ENEMY: u32 = 10;

pub struct CollisionsController;

impl CollisionsController {
    pub fn handle_collisions(state: &mut GameState) {
        CollisionsController::handle_bullet_collisions(state);
        CollisionsController::handle_player_collisions(state);
    }

    /// Handles collisions between the bullets and the enemies
    ///
    /// When an enemy is reached by a bullet, both the enemy and the bullet
    /// will be removed. Additionally, the score of the player will be increased.
    fn handle_bullet_collisions(state: &mut GameState) {
        let old_enemy_count = state.world.enemies.len();

        // We introduce a scope to shorten the lifetime of the borrows below
        {
            let bullets = &mut state.world.bullets;
            let enemies = &mut state.world.enemies;
            let particles = &mut state.world.particles;

            // Note: this is O(n * m) where n = amount of bullets and n = amount of enemies
            // This is pretty bad, but we don't care because n and m are small
            util::fast_retain(bullets, |bullet| {
                // Remove the first enemy that collides with a bullet (if any)
                // Add an explosion on its place
                if let Some((index, position)) = enemies.iter().enumerate()
                    .find(|&(_, enemy)| enemy.collides_with(bullet))
                    .map(|(index, enemy)| (index, enemy.position()))
                    {
                        util::make_explosion(particles, &position, 10);
                        enemies.remove(index);
                        false
                    } else {
                    true
                }
            });
        }

        let killed_enemies = (old_enemy_count - state.world.enemies.len()) as u32;
        state.score += SCORE_PER_ENEMY * killed_enemies;
    }

    /// Handles collisions between the player and the enemies
    fn handle_player_collisions(state: &mut GameState) {
        if state.world.enemies.iter().any(|enemy| state.world.player.collides_with(enemy)) {
            // Make an explosion where the player was
            let ppos = state.world.player.position();
            util::make_explosion(&mut state.world.particles, &ppos, 8);

            state.reset();
        }
    }
}


================================================
FILE: src/controllers/input.rs
================================================
/// Active actions (toggled by user input)
#[derive(Default)]
pub struct Actions {
    pub rotate_left: bool,
    pub rotate_right: bool,
    pub boost: bool,
    pub shoot: bool
}


================================================
FILE: src/controllers/mod.rs
================================================
//! This module contains the game logic
//!
//! There are three main controllers: collisions, input and time

mod collisions;
mod input;
mod time;

pub use self::collisions::CollisionsController;
pub use self::input::Actions;
pub use self::time::TimeController;


================================================
FILE: src/controllers/time.rs
================================================
use std::f64;
use rand::Rng;

use super::Actions;
use game_state::GameState;
use geometry::{Advance, Position, Point};
use models::{Bullet, Enemy, Particle, Vector};
use util;

// Constants related to time
const BULLETS_PER_SECOND: f64 = 100.0;
const BULLET_RATE: f64 = 1.0 / BULLETS_PER_SECOND;

const ENEMY_SPAWNS_PER_SECOND: f64 = 1.0;
const ENEMY_SPAWN_RATE: f64 = 1.0 / ENEMY_SPAWNS_PER_SECOND;

const TRAIL_PARTICLES_PER_SECOND: f64 = 20.0;
const TRAIL_PARTICLE_RATE: f64 = 1.0 / TRAIL_PARTICLES_PER_SECOND;

// Constants related to movement
// Speed is measured in pixels per second
// Rotation speed is measured in radians per second
const ADVANCE_SPEED: f64 = 200.0;
const BULLET_SPEED: f64 = 500.0;
const ENEMY_SPEED: f64 = 100.0;
const ROTATE_SPEED: f64 = 2.0 * f64::consts::PI;

const PLAYER_GRACE_AREA: f64 = 200.0;

/// Timers to handle creation of bullets, enemies and particles
pub struct TimeController<T: Rng> {
    /// A random number generator
    rng: T,
    current_time: f64,
    last_tail_particle: f64,
    last_shoot: f64,
    last_spawned_enemy: f64
}

impl<T: Rng> TimeController<T> {
    pub fn new(rng: T) -> TimeController<T> {
        TimeController {
            rng,
            current_time: 0.0,
            last_tail_particle: 0.0,
            last_shoot: 0.0,
            last_spawned_enemy: 0.0
        }
    }

    /// Updates the game
    ///
    /// `dt` is the amount of seconds that have passed since the last update
    pub fn update_seconds(&mut self, dt: f64, actions: &Actions, state: &mut GameState) {
        self.current_time += dt;

        // Update rocket rotation
        if actions.rotate_left {
            *state.world.player.direction_mut() += -ROTATE_SPEED * dt;
        }
        if actions.rotate_right {
            *state.world.player.direction_mut() += ROTATE_SPEED * dt;
        };

        // Set speed and advance the player with wrap around
        let speed = if actions.boost { 2.0 * ADVANCE_SPEED } else { ADVANCE_SPEED };
        state.world.player.advance_wrapping(dt * speed, state.world.size);

        // Update particles
        for particle in &mut state.world.particles {
            particle.update(dt);
        }

        // Remove old particles
        util::fast_retain(&mut state.world.particles, |p| p.ttl > 0.0);

        // Add new particles at the player's position, to leave a trail
        if self.current_time - self.last_tail_particle > TRAIL_PARTICLE_RATE {
            self.last_tail_particle = self.current_time;
            state.world.particles.push(Particle::new(state.world.player.vector.clone().invert(),
                                                    0.5));
        }

        // Add bullets
        if actions.shoot && self.current_time - self.last_shoot > BULLET_RATE {
            self.last_shoot = self.current_time;
            state.world.bullets.push(Bullet::new(Vector::new(state.world.player.front(),
                                                            state.world.player.direction())));
        }

        // Advance bullets
        for bullet in &mut state.world.bullets {
            bullet.update(dt * BULLET_SPEED);
        }

        // Remove bullets outside the viewport
        {
            // Shorten the lifetime of size
            let size = &state.world.size;
            util::fast_retain(&mut state.world.bullets, |b| size.contains(b.position()));
        }

        // Spawn enemies at random locations
        if self.current_time - self.last_spawned_enemy > ENEMY_SPAWN_RATE {
            self.last_spawned_enemy = self.current_time;

            let player_pos: &Vector = &state.world.player.vector;
            let mut enemy_pos;
            // We loop here, just in case the new enemy random position is exactly equal
            // to the players current position, this would break our calculations below
            loop {
                enemy_pos = Vector::random(&mut self.rng, state.world.size);
                if enemy_pos.position != player_pos.position {
                    break;
                }
            }
            // Check if the newly spawned enemy is inside the player's grace area,
            // if so, we push its spawn point to the edge of the area
            if enemy_pos.position.intersect_circle(&player_pos.position, PLAYER_GRACE_AREA) {
                let length: f64 = enemy_pos.position.squared_distance_to(&player_pos.position).sqrt();
                let dp: Point = enemy_pos.position - player_pos.position;
                enemy_pos.position = player_pos.position + dp / length * PLAYER_GRACE_AREA;
            }

            let new_enemy = Enemy::new(enemy_pos);
            state.world.enemies.push(new_enemy);
        }

        // Move enemies in the player's direction
        for enemy in &mut state.world.enemies {
            enemy.update(dt * ENEMY_SPEED, state.world.player.position());
        }
    }
}


================================================
FILE: src/game_state.rs
================================================
use pcg_rand::Pcg32Basic;
use rand::SeedableRng;

use geometry::{Position, Size};
use models::World;

/// The data structure that contains the state of the game
pub struct GameState {
    /// The world contains everything that needs to be drawn
    pub world: World,
    /// The current score of the player
    pub score: u32
}

impl GameState {
    /// Returns a new `GameState` containing a `World` of the given `Size`
    pub fn new(size: Size) -> GameState {
        let mut rng = Pcg32Basic::from_seed([42, 42]);
        GameState {
            world: World::new(&mut rng, size),
            score: 0
        }
    }

    /// Reset our game-state
    pub fn reset(&mut self) {
        let mut rng = Pcg32Basic::from_seed([42, 42]);

        // Reset player position
        *self.world.player.x_mut() = self.world.size.random_x(&mut rng);
        *self.world.player.y_mut() = self.world.size.random_y(&mut rng);

        // Reset score
        self.score = 0;

        // Remove all enemies and bullets
        self.world.bullets.clear();
        self.world.enemies.clear();
    }
}


================================================
FILE: src/geometry/mod.rs
================================================
mod point;
mod size;
mod traits;

pub use self::point::Point;
pub use self::size::Size;
pub use self::traits::{Position, Advance, Collide};

================================================
FILE: src/geometry/point.rs
================================================
use rand::Rng;

use super::Size;

use std::ops::{Add, Sub, Mul, Div};

/// A `Point` represents a position in space
#[derive(Clone, Default, Copy)]
pub struct Point {
    pub x: f64,
    pub y: f64
}

impl Point {
    /// Returns a new `Point` with the given coordinates
    pub fn new(x: f64, y: f64) -> Point {
        Point { x: x, y: y }
    }

    /// Returns a random `Point` within the given bounds (exclusive)
    pub fn random<R: Rng>(rng: &mut R, bounds: Size) -> Point {
        Point {
            x: rng.gen_range(0.0, bounds.width),
            y: rng.gen_range(0.0, bounds.height)
        }
    }

    /// Returns the squared distance from this point to the given one
    pub fn squared_distance_to(&self, target: &Point) -> f64 {
        (self.x - target.x) * (self.x - target.x)
            + (self.y - target.y) * (self.y - target.y)
    }

    /// Rotates the point through the origin in the given angle (radians)
    pub fn rotate(mut self, radians: f64) -> Point {
        let radius = (self.x * self.x + self.y * self.y).sqrt();
        let point_angle = (self.y / self.x).atan();
        let final_angle = point_angle + radians;
        self.x = final_angle.cos() * radius;
        self.y = final_angle.sin() * radius;
        self
    }

    /// Translates the point by another point
    pub fn translate(mut self, other: &Point) -> Point {
        self.x += other.x;
        self.y += other.y;
        self
    }

    /// Checks if this point is contained in a circle
    pub fn intersect_circle(self, center: &Point, radius: f64) -> bool {
        (self.x - center.x).powi(2) +
            (self.y - center.y).powi(2) < radius.powi(2)
    }
}

/// Implements '==' for Point, as well as its inverse '!='
impl PartialEq for Point {
    fn eq (&self, _rhs: &Self) -> bool {
        (self.x == _rhs.x) && (self.y == _rhs.y)
    }
}

/// Implements the '+' operator for Point + Point
impl Add for Point {
    type Output = Point;

    fn add(self, _rhs: Point) -> Point {
        Point {
            x: self.x + _rhs.x,
            y: self.y + _rhs.y,
        }
    }
}

/// Implements the '+' operator for Point + f64
impl Add<f64> for Point {
    type Output = Point;

    fn add(self, _rhs: f64) -> Point {
        Point {
            x: self.x + _rhs,
            y: self.y + _rhs,
        }
    }
}

/// Implements the '-' operator for Point - Point
impl Sub for Point {
    type Output = Point;

    fn sub(self, _rhs: Point) -> Point {
        Point {
            x: self.x - _rhs.x,
            y: self.y - _rhs.y,
        }
    }
}

/// Implements the '-' operator for Point - f64
impl Sub<f64> for Point {
    type Output = Point;

    fn sub(self, _rhs: f64) -> Point {
        Point {
            x: self.x - _rhs,
            y: self.y - _rhs,
        }
    }
}

/// Implements the '*' operator for Point * Point
impl Mul for Point {
    type Output = Point;

    fn mul(self, _rhs: Point) -> Point {
        Point {
            x: self.x * _rhs.x,
            y: self.y * _rhs.y,
        }
    }
}

/// Implements the '*' operator for Point * f64
impl Mul<f64> for Point {
    type Output = Point;

    fn mul(self, _rhs: f64) -> Point {
        Point {
            x: self.x * _rhs,
            y: self.x * _rhs,
        }
    }
}

/// Implements the '/' operator for Point / Point
impl Div for Point {
    type Output = Point;

    fn div(self, _rhs: Point) -> Point {
        assert!(_rhs.x != 0f64);
        assert!(_rhs.y != 0f64);
        Point {
            x: self.x / _rhs.x,
            y: self.y / _rhs.y,
        }
    }
}

/// Implements the '/' operator for Point / f64:
impl Div<f64> for Point {
    type Output = Point;

    fn div(self, _rhs: f64) -> Point {
        assert!(_rhs != 0f64);
        Point {
            x: self.x / _rhs,
            y: self.y / _rhs,
        }
    }
}


================================================
FILE: src/geometry/size.rs
================================================
use rand::Rng;

use super::Point;

/// A `Size` represents a region in space
#[derive(Clone, Copy, Default)]
pub struct Size {
    pub width: f64,
    pub height: f64
}

impl Size {
    /// Returns a new `Size` of the given dimensions
    pub fn new(width: f64, height: f64) -> Size {
        Size { width: width, height: height }
    }

    /// Returns true if the `Point` is contained in this `Size` or false otherwise
    pub fn contains(&self, point: Point) -> bool {
        0.0 <= point.x && point.x <= self.width
            && 0.0 <= point.y && point.y <= self.height
    }

    /// Returns a random x coordinate within the bounds of this `Size`
    pub fn random_x<R: Rng>(&self, rng: &mut R) -> f64 {
        rng.gen_range(0.0, self.width)
    }

    /// Returns a random y coordinate within the bounds of this `Size`
    pub fn random_y<R: Rng>(&self, rng: &mut R) -> f64 {
        rng.gen_range(0.0, self.height)
    }
}


================================================
FILE: src/geometry/traits.rs
================================================
//! Traits used by the models

use std::f64;

use super::{Point, Size};

/// A trait for objects that occupy a position in space
pub trait Position {
    /// Returns the x coordinate of the object
    fn x(&self) -> f64;

    /// Returns a mutable reference to the x coordinate
    fn x_mut(&mut self) -> &mut f64;

    /// Returns the y coordinate of the object
    fn y(&self) -> f64;

    /// Returns a mutable reference to the y coordinate
    fn y_mut(&mut self) -> &mut f64;

    /// Returns the position of the object
    fn position(&self) -> Point {
        Point::new(self.x(), self.y())
    }
}

/// A trait for objects that have can move in a given direction
pub trait Advance: Position {
    /// Returns the direction of the object, measured in radians
    ///
    /// Note: 0.0 points to the right and a positive number means a clockwise
    /// rotation
    fn direction(&self) -> f64;

    /// Returns a mutable reference to the direction of the object
    fn direction_mut(&mut self) -> &mut f64;

    /// Changes the direction of the vector to point to the given target
    fn point_to(&mut self, target: Point) {
        let m = (self.y() - target.y) / (self.x() - target.x);

        *self.direction_mut() = if target.x > self.x() {
            m.atan()
        } else {
            m.atan() + f64::consts::PI
        };
    }

    /// Advances the object in the given amount of units, according to its direction
    fn advance(&mut self, units: f64) {
        *self.x_mut() += self.direction().cos() * units;
        *self.y_mut() += self.direction().sin() * units;
    }

    /// Similar to `Advance::advance`, but the final position will be wrapped
    /// around the given bounds
    fn advance_wrapping(&mut self, units: f64, bounds: Size) {
        self.advance(units);

        fn wrap(k: &mut f64, bound: f64) {
            if *k < 0.0 {
                *k += bound;
            } else if *k >= bound {
                *k -= bound;
            }
        }

        wrap(self.x_mut(), bounds.width);
        wrap(self.y_mut(), bounds.height);
    }
}

/// A trait that provides collision detection for objects with a position and a radius
///
/// For collision purposes, all objects are treated as circles
pub trait Collide: Position {
    /// Returns the radius of the object
    fn radius(&self) -> f64;

    /// Returns the diameter of the objects
    fn diameter(&self) -> f64 {
        self.radius() * 2.0
    }

    /// Returns true if the two objects collide and false otherwise
    fn collides_with<O: Collide>(&self, other: &O) -> bool {
        let radii = self.radius() + other.radius();
        self.position().squared_distance_to(&other.position()) < radii * radii
    }
}


================================================
FILE: src/lib.rs
================================================
extern crate itertools_num;
#[macro_use]
extern crate lazy_static;
extern crate rand;
extern crate pcg_rand;

mod controllers;
mod game_state;
mod geometry;
mod models;
mod util;

use std::os::raw::{c_double, c_int};
use std::sync::Mutex;

use pcg_rand::Pcg32Basic;
use rand::SeedableRng;

use self::game_state::GameState;
use self::geometry::Size;
use self::controllers::{Actions, TimeController, CollisionsController};

lazy_static! {
    static ref DATA: Mutex<GameData> = Mutex::new(new_game_data(1024.0, 600.0));
}

struct GameData {
    state: GameState,
    actions: Actions,
    time_controller: TimeController<Pcg32Basic>
}

fn new_game_data(width: f64, height: f64) -> GameData {
    GameData {
        state: GameState::new(Size::new(width, height)),
        actions: Actions::default(),
        time_controller: TimeController::new(Pcg32Basic::from_seed([42, 42]))
    }
}

// These functions are provided by the runtime
extern "C" {
    fn clear_screen();
    fn draw_player(_: c_double, _: c_double, _: c_double);
    fn draw_enemy(_: c_double, _: c_double);
    fn draw_bullet(_: c_double, _: c_double);
    fn draw_particle(_: c_double, _: c_double, _: c_double);
    fn draw_score(_: c_double);
}

#[no_mangle]
pub extern "C" fn resize(width: c_double, height: c_double) {
    *DATA.lock().unwrap() = new_game_data(width, height);
}

#[no_mangle]
pub unsafe extern "C" fn draw() {
    use geometry::{Advance, Position};
    let data = &mut DATA.lock().unwrap();
    let world = &data.state.world;

    clear_screen();
    for particle in &world.particles {
        draw_particle(particle.x(), particle.y(), 5.0 * particle.ttl);
    }

    for bullet in &world.bullets {
        draw_bullet(bullet.x(), bullet.y());
    }

    for enemy in &world.enemies {
        draw_enemy(enemy.x(), enemy.y());
    }

    draw_player(world.player.x(), world.player.y(), world.player.direction());
    draw_score(data.state.score as f64);
}

#[no_mangle]
pub extern "C" fn update(time: c_double) {
    let data: &mut GameData = &mut DATA.lock().unwrap();
    data.time_controller.update_seconds(time, &data.actions, &mut data.state);
    CollisionsController::handle_collisions(&mut data.state);
}

fn int_to_bool(i: c_int) -> bool {
    i != 0
}

#[no_mangle]
pub extern "C" fn toggle_shoot(b: c_int) {
    let data = &mut DATA.lock().unwrap();
    data.actions.shoot = int_to_bool(b);
}

#[no_mangle]
pub extern "C" fn toggle_boost(b: c_int) {
    let data = &mut DATA.lock().unwrap();
    data.actions.boost = int_to_bool(b);
}

#[no_mangle]
pub extern "C" fn toggle_turn_left(b: c_int) {
    let data = &mut DATA.lock().unwrap();
    data.actions.rotate_left = int_to_bool(b);
}

#[no_mangle]
pub extern "C" fn toggle_turn_right(b: c_int) {
    let data = &mut DATA.lock().unwrap();
    data.actions.rotate_right = int_to_bool(b);
}


================================================
FILE: src/models/bullet.rs
================================================
use super::Vector;
use geometry::{Advance, Collide};

/// Bullets are spawned when the player shoots
///
/// When an enemy is reached by a bullet, it will explode
pub struct Bullet {
    vector: Vector
}

derive_position_direction!(Bullet);

impl Bullet {
    /// Create a bullet with the given vector
    pub fn new(vector: Vector) -> Bullet {
        Bullet { vector: vector }
    }

    /// Update the bullet's position
    pub fn update(&mut self, units: f64) {
        self.advance(units);
    }
}

impl Collide for Bullet {
    fn radius(&self) -> f64 { 3.0 }
}


================================================
FILE: src/models/enemy.rs
================================================
use geometry::Point;
use super::Vector;
use geometry::{Advance, Collide};

/// Enemies follow the player in order to cause a collision and let him explode
pub struct Enemy {
    vector: Vector
}

derive_position_direction!(Enemy);

impl Enemy {
    /// Create a enemy with the given vector
    pub fn new(vector: Vector) -> Enemy {
        Enemy { vector: vector }
    }

    /// Update the enemy
    pub fn update(&mut self, speed: f64, player_position: Point) {
        // Point to the player
        self.point_to(player_position);
        self.advance(speed);
    }
}

impl Collide for Enemy {
    fn radius(&self) -> f64 { 10.0 }
}


================================================
FILE: src/models/mod.rs
================================================
// macro_use needs to go first so the macro is visible for the other modules
#[macro_use]
mod vector;

mod bullet;
mod enemy;
mod particle;
mod player;
mod world;

pub use self::bullet::Bullet;
pub use self::enemy::Enemy;
pub use self::particle::Particle;
pub use self::player::{Player, POLYGON as PLAYER_POLYGON};
pub use self::vector::Vector;
pub use self::world::World;


================================================
FILE: src/models/particle.rs
================================================
use super::Vector;
use geometry::Advance;

/// A model representing a particle
///
/// Particles are visible objects that have a time to live and move around
/// in a given direction until their time is up. They are spawned when the
/// player or an enemy is killed
pub struct Particle {
    pub vector: Vector,
    pub ttl: f64
}

derive_position_direction!(Particle);

impl Particle {
    /// Create a particle with the given vector and time to live in seconds
    pub fn new(vector: Vector, ttl: f64) -> Particle {
        Particle { vector: vector, ttl: ttl }
    }

    /// Update the particle
    pub fn update(&mut self, elapsed_time: f64) {
        self.ttl -= elapsed_time;
        let speed = 500.0 * self.ttl * self.ttl;
        self.advance(elapsed_time * speed);
    }
}


================================================
FILE: src/models/player.rs
================================================
use rand::Rng;

use geometry::{Point, Size};
use super::Vector;
use geometry::{Advance, Collide, Position};

/// The `Player` is the rocket controlled by the user
#[derive(Default)]
pub struct Player {
    pub vector: Vector
}

derive_position_direction!(Player);

/// The player is represented as the polygon below
pub const POLYGON: &'static [[f64; 2]] = &[
    [0.0, -8.0],
    [20.0, 0.0],
    [0.0, 8.0]
];

impl Player {
    /// Create a new `Player` with a random position and direction
    pub fn random<R: Rng>(rng: &mut R, bounds: Size) -> Player {
        Player { vector: Vector::random(rng, bounds) }
    }

    /// Returns the front of the rocket
    pub fn front(&self) -> Point {
        Point::new(POLYGON[1][0], POLYGON[1][1])
            .rotate(self.direction())
            .translate(&self.position())
    }
}

impl Collide for Player {
    fn radius(&self) -> f64 { 6.0 }
}


================================================
FILE: src/models/vector.rs
================================================
use std::f64;

use rand::Rng;

use geometry::{Point, Size};

/// A `Vector`
#[derive(Clone, Default)]
pub struct Vector {
    /// The position of the vector
    pub position: Point,
    /// The direction angle, in radians
    pub direction: f64
}

impl Vector {
    /// Returns a new `Vector`
    pub fn new(position: Point, direction: f64) -> Vector {
        Vector { position: position, direction: direction }
    }

    /// Returns a random `Vector` within the given bounds
    pub fn random<R: Rng>(rng: &mut R, bounds: Size) -> Vector {
        Vector::new(Point::random(rng, bounds), rng.gen())
    }

    /// Consumes the vector and returns a new one with inverted direction
    pub fn invert(mut self) -> Vector {
        self.direction -= f64::consts::PI;
        self
    }
}

/// A macro to implement `Position` and `Direction` for any type that has a field named `vector`
#[macro_export]
macro_rules! derive_position_direction {
    ($t:ty) => {
        impl ::geometry::Position for $t {
            fn x(&self) -> f64 { self.vector.position.x }
            fn x_mut(&mut self) -> &mut f64 { &mut self.vector.position.x }
            fn y(&self) -> f64 { self.vector.position.y }
            fn y_mut(&mut self) -> &mut f64 { &mut self.vector.position.y }
        }

        impl ::geometry::Advance for $t {
            fn direction(&self) -> f64 {
                self.vector.direction
            }

            fn direction_mut(&mut self) -> &mut f64 {
                &mut self.vector.direction
            }
        }
    }
}


================================================
FILE: src/models/world.rs
================================================
use rand::Rng;

use geometry::Size;
use models::{Bullet, Enemy, Particle, Player};

/// A model that contains the other models and renders them
pub struct World {
    pub player: Player,
    pub particles: Vec<Particle>,
    pub bullets: Vec<Bullet>,
    pub enemies: Vec<Enemy>,
    pub size: Size
}

impl World {
    /// Returns a new world of the given size
    pub fn new<R: Rng>(rng: &mut R, size: Size) -> World {
        World {
            player: Player::random(rng, size),
            particles: Vec::with_capacity(1000),
            bullets: vec![],
            enemies: vec![],
            size: size
        }
    }
}


================================================
FILE: src/util.rs
================================================
use geometry::Point;
use models::{Particle, Vector};

/// Optimized version of `Vec::retain`
///
/// We achieve better performance by renouncing to keep the original order of the `Vec`
pub fn fast_retain<T, F>(vec: &mut Vec<T>, mut f: F)
where F: FnMut(&T) -> bool {
    let mut i = 0;
    while i < vec.len() {
        if !f(&vec[i]) {
            vec.swap_remove(i);
        }

        i += 1;
    }
}

/// Generates a new explosion of the given intensity at the given position.
/// This works best with values between 5 and 25
pub fn make_explosion(particles: &mut Vec<Particle>, position: &Point, intensity: u8) {
    use itertools_num;
    for rotation in itertools_num::linspace(0.0, 2.0 * ::std::f64::consts::PI, 30) {
        for ttl in (1..intensity).map(|x| (x as f64) / 10.0) {
            particles.push(Particle::new(Vector::new(position.clone(), rotation), ttl));
        }
    }
}
Download .txt
gitextract_okp2mtnu/

├── .gitattributes
├── .gitignore
├── Cargo.toml
├── LICENSE.md
├── html/
│   ├── index.html
│   └── program.wasm
├── post_build.py
├── readme.md
└── src/
    ├── controllers/
    │   ├── collisions.rs
    │   ├── input.rs
    │   ├── mod.rs
    │   └── time.rs
    ├── game_state.rs
    ├── geometry/
    │   ├── mod.rs
    │   ├── point.rs
    │   ├── size.rs
    │   └── traits.rs
    ├── lib.rs
    ├── models/
    │   ├── bullet.rs
    │   ├── enemy.rs
    │   ├── mod.rs
    │   ├── particle.rs
    │   ├── player.rs
    │   ├── vector.rs
    │   └── world.rs
    └── util.rs
Download .txt
SYMBOL INDEX (108 symbols across 15 files)

FILE: src/controllers/collisions.rs
  constant SCORE_PER_ENEMY (line 5) | const SCORE_PER_ENEMY: u32 = 10;
  type CollisionsController (line 7) | pub struct CollisionsController;
    method handle_collisions (line 10) | pub fn handle_collisions(state: &mut GameState) {
    method handle_bullet_collisions (line 19) | fn handle_bullet_collisions(state: &mut GameState) {
    method handle_player_collisions (line 51) | fn handle_player_collisions(state: &mut GameState) {

FILE: src/controllers/input.rs
  type Actions (line 3) | pub struct Actions {

FILE: src/controllers/time.rs
  constant BULLETS_PER_SECOND (line 11) | const BULLETS_PER_SECOND: f64 = 100.0;
  constant BULLET_RATE (line 12) | const BULLET_RATE: f64 = 1.0 / BULLETS_PER_SECOND;
  constant ENEMY_SPAWNS_PER_SECOND (line 14) | const ENEMY_SPAWNS_PER_SECOND: f64 = 1.0;
  constant ENEMY_SPAWN_RATE (line 15) | const ENEMY_SPAWN_RATE: f64 = 1.0 / ENEMY_SPAWNS_PER_SECOND;
  constant TRAIL_PARTICLES_PER_SECOND (line 17) | const TRAIL_PARTICLES_PER_SECOND: f64 = 20.0;
  constant TRAIL_PARTICLE_RATE (line 18) | const TRAIL_PARTICLE_RATE: f64 = 1.0 / TRAIL_PARTICLES_PER_SECOND;
  constant ADVANCE_SPEED (line 23) | const ADVANCE_SPEED: f64 = 200.0;
  constant BULLET_SPEED (line 24) | const BULLET_SPEED: f64 = 500.0;
  constant ENEMY_SPEED (line 25) | const ENEMY_SPEED: f64 = 100.0;
  constant ROTATE_SPEED (line 26) | const ROTATE_SPEED: f64 = 2.0 * f64::consts::PI;
  constant PLAYER_GRACE_AREA (line 28) | const PLAYER_GRACE_AREA: f64 = 200.0;
  type TimeController (line 31) | pub struct TimeController<T: Rng> {
  function new (line 41) | pub fn new(rng: T) -> TimeController<T> {
  function update_seconds (line 54) | pub fn update_seconds(&mut self, dt: f64, actions: &Actions, state: &mut...

FILE: src/game_state.rs
  type GameState (line 8) | pub struct GameState {
    method new (line 17) | pub fn new(size: Size) -> GameState {
    method reset (line 26) | pub fn reset(&mut self) {

FILE: src/geometry/point.rs
  type Point (line 9) | pub struct Point {
    method new (line 16) | pub fn new(x: f64, y: f64) -> Point {
    method random (line 21) | pub fn random<R: Rng>(rng: &mut R, bounds: Size) -> Point {
    method squared_distance_to (line 29) | pub fn squared_distance_to(&self, target: &Point) -> f64 {
    method rotate (line 35) | pub fn rotate(mut self, radians: f64) -> Point {
    method translate (line 45) | pub fn translate(mut self, other: &Point) -> Point {
    method intersect_circle (line 52) | pub fn intersect_circle(self, center: &Point, radius: f64) -> bool {
    type Output (line 79) | type Output = Point;
    method add (line 81) | fn add(self, _rhs: f64) -> Point {
    type Output (line 103) | type Output = Point;
    method sub (line 105) | fn sub(self, _rhs: f64) -> Point {
    type Output (line 127) | type Output = Point;
    method mul (line 129) | fn mul(self, _rhs: f64) -> Point {
    type Output (line 153) | type Output = Point;
    method div (line 155) | fn div(self, _rhs: f64) -> Point {
  method eq (line 60) | fn eq (&self, _rhs: &Self) -> bool {
  type Output (line 67) | type Output = Point;
  method add (line 69) | fn add(self, _rhs: Point) -> Point {
  type Output (line 91) | type Output = Point;
  method sub (line 93) | fn sub(self, _rhs: Point) -> Point {
  type Output (line 115) | type Output = Point;
  method mul (line 117) | fn mul(self, _rhs: Point) -> Point {
  type Output (line 139) | type Output = Point;
  method div (line 141) | fn div(self, _rhs: Point) -> Point {

FILE: src/geometry/size.rs
  type Size (line 7) | pub struct Size {
    method new (line 14) | pub fn new(width: f64, height: f64) -> Size {
    method contains (line 19) | pub fn contains(&self, point: Point) -> bool {
    method random_x (line 25) | pub fn random_x<R: Rng>(&self, rng: &mut R) -> f64 {
    method random_y (line 30) | pub fn random_y<R: Rng>(&self, rng: &mut R) -> f64 {

FILE: src/geometry/traits.rs
  type Position (line 8) | pub trait Position {
    method x (line 10) | fn x(&self) -> f64;
    method x_mut (line 13) | fn x_mut(&mut self) -> &mut f64;
    method y (line 16) | fn y(&self) -> f64;
    method y_mut (line 19) | fn y_mut(&mut self) -> &mut f64;
    method position (line 22) | fn position(&self) -> Point {
  type Advance (line 28) | pub trait Advance: Position {
    method direction (line 33) | fn direction(&self) -> f64;
    method direction_mut (line 36) | fn direction_mut(&mut self) -> &mut f64;
    method point_to (line 39) | fn point_to(&mut self, target: Point) {
    method advance (line 50) | fn advance(&mut self, units: f64) {
    method advance_wrapping (line 57) | fn advance_wrapping(&mut self, units: f64, bounds: Size) {
  type Collide (line 76) | pub trait Collide: Position {
    method radius (line 78) | fn radius(&self) -> f64;
    method diameter (line 81) | fn diameter(&self) -> f64 {
    method collides_with (line 86) | fn collides_with<O: Collide>(&self, other: &O) -> bool {

FILE: src/lib.rs
  type GameData (line 27) | struct GameData {
  function new_game_data (line 33) | fn new_game_data(width: f64, height: f64) -> GameData {
  function clear_screen (line 43) | fn clear_screen();
  function draw_player (line 44) | fn draw_player(_: c_double, _: c_double, _: c_double);
  function draw_enemy (line 45) | fn draw_enemy(_: c_double, _: c_double);
  function draw_bullet (line 46) | fn draw_bullet(_: c_double, _: c_double);
  function draw_particle (line 47) | fn draw_particle(_: c_double, _: c_double, _: c_double);
  function draw_score (line 48) | fn draw_score(_: c_double);
  function resize (line 52) | pub extern "C" fn resize(width: c_double, height: c_double) {
  function draw (line 57) | pub unsafe extern "C" fn draw() {
  function update (line 80) | pub extern "C" fn update(time: c_double) {
  function int_to_bool (line 86) | fn int_to_bool(i: c_int) -> bool {
  function toggle_shoot (line 91) | pub extern "C" fn toggle_shoot(b: c_int) {
  function toggle_boost (line 97) | pub extern "C" fn toggle_boost(b: c_int) {
  function toggle_turn_left (line 103) | pub extern "C" fn toggle_turn_left(b: c_int) {
  function toggle_turn_right (line 109) | pub extern "C" fn toggle_turn_right(b: c_int) {

FILE: src/models/bullet.rs
  type Bullet (line 7) | pub struct Bullet {
    method new (line 15) | pub fn new(vector: Vector) -> Bullet {
    method update (line 20) | pub fn update(&mut self, units: f64) {
  method radius (line 26) | fn radius(&self) -> f64 { 3.0 }

FILE: src/models/enemy.rs
  type Enemy (line 6) | pub struct Enemy {
    method new (line 14) | pub fn new(vector: Vector) -> Enemy {
    method update (line 19) | pub fn update(&mut self, speed: f64, player_position: Point) {
  method radius (line 27) | fn radius(&self) -> f64 { 10.0 }

FILE: src/models/particle.rs
  type Particle (line 9) | pub struct Particle {
    method new (line 18) | pub fn new(vector: Vector, ttl: f64) -> Particle {
    method update (line 23) | pub fn update(&mut self, elapsed_time: f64) {

FILE: src/models/player.rs
  type Player (line 9) | pub struct Player {
    method random (line 24) | pub fn random<R: Rng>(rng: &mut R, bounds: Size) -> Player {
    method front (line 29) | pub fn front(&self) -> Point {
  constant POLYGON (line 16) | pub const POLYGON: &'static [[f64; 2]] = &[
  method radius (line 37) | fn radius(&self) -> f64 { 6.0 }

FILE: src/models/vector.rs
  type Vector (line 9) | pub struct Vector {
    method new (line 18) | pub fn new(position: Point, direction: f64) -> Vector {
    method random (line 23) | pub fn random<R: Rng>(rng: &mut R, bounds: Size) -> Vector {
    method invert (line 28) | pub fn invert(mut self) -> Vector {

FILE: src/models/world.rs
  type World (line 7) | pub struct World {
    method new (line 17) | pub fn new<R: Rng>(rng: &mut R, size: Size) -> World {

FILE: src/util.rs
  function fast_retain (line 7) | pub fn fast_retain<T, F>(vec: &mut Vec<T>, mut f: F)
  function make_explosion (line 21) | pub fn make_explosion(particles: &mut Vec<Particle>, position: &Point, i...
Condensed preview — 26 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (37K chars).
[
  {
    "path": ".gitattributes",
    "chars": 65,
    "preview": "# Auto detect text files and perform LF normalization\n* text=auto"
  },
  {
    "path": ".gitignore",
    "chars": 38,
    "preview": "target\n.vscode\n.idea\nhtml/rocket.wasm\n"
  },
  {
    "path": "Cargo.toml",
    "chars": 395,
    "preview": "[package]\nname = \"rocket\"\nversion = \"1.0.0\"\nauthors = [\"Adolfo Ochagavía <aochagavia92@gmail.com>\"]\n\n[lib]\ncrate-type = "
  },
  {
    "path": "LICENSE.md",
    "chars": 1083,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2015 Adolfo Ochagavía\n\nPermission is hereby granted, free of charge, to any person "
  },
  {
    "path": "html/index.html",
    "chars": 5321,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <style>\n      html, body {\n        width: 100%;"
  },
  {
    "path": "post_build.py",
    "chars": 198,
    "preview": "from shutil import copyfile\nfrom subprocess import call\n\ncopyfile('target/wasm32-unknown-unknown/release/rocket.wasm', '"
  },
  {
    "path": "readme.md",
    "chars": 1516,
    "preview": "Rocket on WASM\n==============\n\nAn adapted version of the [Rocket](https://github.com/aochagavia/rocket) game, running on"
  },
  {
    "path": "src/controllers/collisions.rs",
    "chars": 2366,
    "preview": "use game_state::GameState;\nuse geometry::{Collide, Position};\nuse util;\n\nconst SCORE_PER_ENEMY: u32 = 10;\n\npub struct Co"
  },
  {
    "path": "src/controllers/input.rs",
    "chars": 181,
    "preview": "/// Active actions (toggled by user input)\n#[derive(Default)]\npub struct Actions {\n    pub rotate_left: bool,\n    pub ro"
  },
  {
    "path": "src/controllers/mod.rs",
    "chars": 262,
    "preview": "//! This module contains the game logic\n//!\n//! There are three main controllers: collisions, input and time\n\nmod collis"
  },
  {
    "path": "src/controllers/time.rs",
    "chars": 4904,
    "preview": "use std::f64;\nuse rand::Rng;\n\nuse super::Actions;\nuse game_state::GameState;\nuse geometry::{Advance, Position, Point};\nu"
  },
  {
    "path": "src/game_state.rs",
    "chars": 1088,
    "preview": "use pcg_rand::Pcg32Basic;\nuse rand::SeedableRng;\n\nuse geometry::{Position, Size};\nuse models::World;\n\n/// The data struc"
  },
  {
    "path": "src/geometry/mod.rs",
    "chars": 139,
    "preview": "mod point;\nmod size;\nmod traits;\n\npub use self::point::Point;\npub use self::size::Size;\npub use self::traits::{Position,"
  },
  {
    "path": "src/geometry/point.rs",
    "chars": 3836,
    "preview": "use rand::Rng;\n\nuse super::Size;\n\nuse std::ops::{Add, Sub, Mul, Div};\n\n/// A `Point` represents a position in space\n#[de"
  },
  {
    "path": "src/geometry/size.rs",
    "chars": 933,
    "preview": "use rand::Rng;\n\nuse super::Point;\n\n/// A `Size` represents a region in space\n#[derive(Clone, Copy, Default)]\npub struct "
  },
  {
    "path": "src/geometry/traits.rs",
    "chars": 2713,
    "preview": "//! Traits used by the models\n\nuse std::f64;\n\nuse super::{Point, Size};\n\n/// A trait for objects that occupy a position "
  },
  {
    "path": "src/lib.rs",
    "chars": 2840,
    "preview": "extern crate itertools_num;\n#[macro_use]\nextern crate lazy_static;\nextern crate rand;\nextern crate pcg_rand;\n\nmod contro"
  },
  {
    "path": "src/models/bullet.rs",
    "chars": 568,
    "preview": "use super::Vector;\nuse geometry::{Advance, Collide};\n\n/// Bullets are spawned when the player shoots\n///\n/// When an ene"
  },
  {
    "path": "src/models/enemy.rs",
    "chars": 637,
    "preview": "use geometry::Point;\nuse super::Vector;\nuse geometry::{Advance, Collide};\n\n/// Enemies follow the player in order to cau"
  },
  {
    "path": "src/models/mod.rs",
    "chars": 373,
    "preview": "// macro_use needs to go first so the macro is visible for the other modules\n#[macro_use]\nmod vector;\n\nmod bullet;\nmod e"
  },
  {
    "path": "src/models/particle.rs",
    "chars": 784,
    "preview": "use super::Vector;\nuse geometry::Advance;\n\n/// A model representing a particle\n///\n/// Particles are visible objects tha"
  },
  {
    "path": "src/models/player.rs",
    "chars": 897,
    "preview": "use rand::Rng;\n\nuse geometry::{Point, Size};\nuse super::Vector;\nuse geometry::{Advance, Collide, Position};\n\n/// The `Pl"
  },
  {
    "path": "src/models/vector.rs",
    "chars": 1546,
    "preview": "use std::f64;\n\nuse rand::Rng;\n\nuse geometry::{Point, Size};\n\n/// A `Vector`\n#[derive(Clone, Default)]\npub struct Vector "
  },
  {
    "path": "src/models/world.rs",
    "chars": 631,
    "preview": "use rand::Rng;\n\nuse geometry::Size;\nuse models::{Bullet, Enemy, Particle, Player};\n\n/// A model that contains the other "
  },
  {
    "path": "src/util.rs",
    "chars": 896,
    "preview": "use geometry::Point;\nuse models::{Particle, Vector};\n\n/// Optimized version of `Vec::retain`\n///\n/// We achieve better p"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the aochagavia/rocket_wasm GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 26 files (33.4 KB), approximately 9.2k tokens, and a symbol index with 108 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!