Skip to content

dutc/neocrisis

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

neocrisis !

a collaborative coding game from NYC Python

Teaches: automation, JSON, REST APIs, Python, curl, httpie.

The earth is in danger!

We've detected multiple near earth objects (‘NEO’s) on a collision course with the earth. But we're not defenseless. Surrounding the earth we've launched multiple orbital defense platforms. It is your job to control these platforms and defend earth.

RULES

There are multiple objects (‘rocks’) spiralling toward the earth. Your orbital defense platforms (‘ODP’s) have telescopes and railguns. The telescopes can image the sky one ‘octant’ at a time. The railguns can fire ‘slugs’ to shoot down the ‘rocks.’

  • the earth is a point mass located at (0, 0, 0) in (x, y, z) space
  • orbital defense platforms are located at (0, 0, 0) in (x, y, z) space
    • they orbit quickly enough that they can fire in any direction at any time
    • they orbit quickly enough that they can image the sky in any direction at any time
  • each rock moves on a simple trajectory
    • these trajectories are ballistic and inertialess (no thrust)
    • these trajectories will spiral around the earth until impact
    • they are unaffected by gravitational forces
    • the trajectories are modelled by linear equations in spherical coördinates
  • each slug moves on a linear (straight line) trajectory from the earth
    • slugs can be aimed in any direction
    • these trajectories are ballistic and inertialess (no thrust)
    • slugs cannot turn; they move in straight lines only
    • the trajectories are modelled by linear equations in spherical coördinates
  • slugs collide only with other rocks
    • slugs cannot collide with other slugs, and rocks cannot collide with other rocks
    • if the rock has a mass of 1, then collision with a slug vaporizes both the rock and slug immediately
    • if the rock has a mass greater than 1, then collision with a slug vaporizes the slug and rock but a rock fragment with smaller mass will be created

The orbital defense platforms can be controlled via JSON REST APIs. There are two endpoints.

The server can be found at: http://neocrisis.xyz

SELF-HELP QUICK START

# install https://httpie.org/
$ http get http://neocrisis.xyz/help/
$ http get http://neocrisis.xyz/telescope/help/
$ http get http://neocrisis.xyz/railgun/help/

JSON API

Instructions for how to request or send info to the server is below.

Verb endpoint URL params description
GET /info gives information about the orbital weapon station
GET /telescope/<int:octant> octant from [1, 8] images the specified octant (Ⅰ-Ⅷ) of the night sky and returns NEOs it sees
POST /railgun name, string (optional)
target, string
phi, number
theta, number
fired, string (optional)
fires a slug named name intending to hit target at the specified angles theta and phi, optionally specifying the future fired time at which to fire the slug as HH:MM:SS (for precise timing purposes)

The /telescope endpoint returns a JSON structure that looks like: { "objects": [ obj, … ] }

Each obj is a map with the following fields:

field description
type the tye of object: rock or slug
name the name of the object
mass the mass of the object
fired when the rock was fired at the earth or when the slug was fired into space
pos the spherical coördinates of the object at observation time
cpos the Caresian coördinates of the object at observation time
obs_time the observation time
octant the octant (Ⅰ, Ⅱ, Ⅲ, Ⅳ, Ⅴ, Ⅵ, Ⅶ, Ⅷ) as an integer in which the object was spotted
age the age of the object (how long it has been around since fired) in seconds

REST API EXAMPLE

Use curl or http (https://github.com/jakubroztocil/httpie/) to experiment with this API. Examples:

Take an image of octant one (Ⅰ) to search for objects.

$ http GET http://neocrisis.xyz/telescope/1
HTTP/1.0 200 OK
Content-Length: 20
Content-Type: application/json
Date: Wed, 4 Jul 2018 09:00:00 EST
Server: Werkzeug/0.14.1 Python/3.6.6

{
    "objects": [
        {
            "age": 3600,
            "cpos": {
                "x": 0.0,
                "y": 0.0,
                "z": 100.0,
            },
            "fired": "2018-07-04T08:00:00.000000-04:00",
            "mass": 2,
            "name": "99942 apophis",
            "obs_time": "2018-07-04T09:00:00.000000-04:00",
            "octant": 1,
            "pos": {
                "phi": 0.0,
                "r": 100.0,
                "theta": 0.0
            },
            "type": "rock"
        }
    ]
}

The above output shows one Near Earth Object: an asteroid (note the type ‘rock’) named ‘99942 apophis.’ It's on a direct course to hit the earth, unlike the real 99942 Apophis awhich has only a 2.7% chance of hitting earth on April 13, 2029 (https://en.wikipedia.org/wiki/99942_Apophis)

Next, we can fire a railgun slug at 99942 apophis. To better keep track of this new object, we can name it. We can name it anything we like. We'll name it ‘100 @ apophis’ to indicate what we intended to aim at.

$ http POST http://neocrisis.xyz/railgun name='100 @ apophis' theta:=0 phi:=0
HTTP/1.0 200 OK
Content-Length: 75
Content-Type: application/json
Date: Wed, 4 Jul 2018 09:00:01 EST
Server: Werkzeug/0.14.1 Python/3.6.6

{
    "slug": {
        "name": "100 @ apophis",
        "phi": 0.0,
        "theta": 0.0
    }
}

Image octant one (Ⅰ) again to see both the rock and the railgun slug we fired at it.

$ http GET http://neocrisis.xyz/telescope/1
HTTP/1.0 200 OK
Content-Length: 20
Content-Type: application/json
Date: Wed, 4 Jul 2018 09:00:05 EST
Server: Werkzeug/0.14.1 Python/3.6.6

{
    "objects": [
        {
            "age": 3600,
            "cpos": {
                "x": 0.0,
                "y": 0.0,
                "z": 95.0,
            },
            "fired": "2018-07-04T08:00:00.000000-04:00",
            "mass": 2,
            "name": "99942 apophis",
            "obs_time": "2018-07-04T09:00:05.000000-04:00",
            "octant": 1,
            "pos": {
                "phi": 0.0,
                "r": 95.0,
                "theta": 0.0
            },
            "type": "rock"
        },
        {
            "age": 4,
            "cpos": {
                "x": 0.0,
                "y": 0.0,
                "z": 4.0,
            },
            "fired": "2018-07-04T09:00:01.000000-04:00",
            "mass": 1,
            "name": "100 @ apophis",
            "obs_time": "2018-07-04T09:00:05.000000-04:00",
            "octant": 1,
            "pos": {
                "phi": 0.0,
                "r": 4.0,
                "theta": 0.0
            },
            "type": "slug"
        }
    ]
}

TRAJECTORIES, OCTANTS, and SPHERICAL COÖRDINATES

See docs/trajectories.pdf for more information.

The position of slugs and rocks can be described in terms of Cartesian coördinates (x, y, z) or spherical coördinates (r, θ, φ).

Our convention for Cartesian coördinates (x, y, z) is as follows:

Cartesian coördinate measures range & units directional convention
x distance [0, ∞) meters from the earth's core toward the 0° Prime Meridian (Greenwich, UK)
y distance [0, ∞) meters from the earth's core toward the 90° meridian (Memphis, TN)
z distance [0, ∞) meters from the earth's core toward 0° latitutde (North Pole)

Note that our convention is left-handed, as opposed to the traditional right-handed coordniate system seen the graphics below. This is because our positive y direction is west, instead of east; in other words, their +y is our -y.

Our convention for spherical coördinates (r, θ, φ) is as follows:

spherical coördinate measures range & units directional convention
r ‘radius’ distance [0, ∞) meters from the earth's core toward outer space
θ ‘theta’, ‘azimuth’ angle [0, 2π] radians east-to-west (longitudinally) from the 0° Prime Meridian (Greenwich, UK) toward the 90° meridian (Memphis, TN)
φ ‘phi’, ‘inclination’ angle [0, π] radians north-to-south (latitudinally) from the North Pole to the South Pole

spherical coördinates

Our convention for octant numbers is as follows:

number roman numeral x-sign y-sign z-sign
1 I + + +
2 II - + +
3 III - - +
4 IV + - +
5 V + + -
6 VI - + -
7 VII - - -
8 VIII + - -

octant numbers

The trajectories of rocks and slugs follow the below equations:

NEO params r phi theta
slugs v velocity
phi fixed at fire time
theta fixed at fire time
r = v × t phi theta
rocks v velocity
r initial radius
mφ phi-slope
bφ phi-intercept
mθ theta-slope
bθ theta-intercept
r = v × t + r₀ phi = mφ × t + bφ theta = mθ × t + bθ

The position of an object is determined only by its parameters and t, time.

MATH HELP and HINTS

Hints:

  • the above equations are independent of each other: r, phi, and theta do not affect each other.
  • for the rocks, each equation has only two unknowns; therefore, you only need to take two measurements to determine the unknowns

To compute the trajectory of a rock, take two measurements of its position in spherical coördinates (r, θ, φ).

  1. Two measurements are taken at t1 and t2.
    • call them (r1, phi1, theta1) and (r2, phi2, theta2)
  2. We want to solve for vrock and r0. Note: unlike a real asteroid, these asteroids' distances to the earth vary linearly. You need only basic algebra to determine a firing solution. No calculus is needed. They do not accelerate: their velocities are constant at all times.
    • we know that r1 = vrock × t1 + r0 and r2 = vrock × t2 + r0
    • therefore, v = (r1 - r2) ÷ (t1 - t2)
    • therefore, r0 = r1 - vrock × t1 or r0 = r2 - vrock × t2
    • Remember, velocity is a vector (signed) quantity! Since the rock is moving TOWARDS the origin (in other words, rrock(t) is becoming smaller over time), its velocity will be negative
  3. We also want to solve for mφ and bφ.
    • we know that phi1 = mφ × t1 + bφ and phi2 = mφ × t2 + bφ
    • therefore, mφ = (phi1 - phi2) ÷ (t1 - t2)
    • therefore, bφ = phi1 - mφ × t1 or bφ = phi2 - mφ × t2
  4. Finally, we want to solve for mθ and bθ.
    • we know that theta1 = mθ × t1 + bθ and theta2 = mθ × t2 + bθ
    • therefore, mθ = (phi1 - phi2) ÷ (t1 - t2)
    • therefore, bθ = phi1 - mθ × t1 or bθ = phi2 - mθ × t2
  5. We need to compute direction in which to fire the slug; its phi and theta angles
    • we're given the slug's vslug velocity
    • we know that at collision time tcollide, that rrock and rslug will be the same: rslug = rrock = rcollide
    • we know rcollide = vrock × tcollide + r0 and rslug, collide = vslug × tcollide
    • therefore, tcollide = r0 ÷ (vrock - vslug)
    • knowing the collision time, we can determine the firing angles
    • therefore, phicollide = mφ × tcollide + bφ and thetacollide = m0 × tcollide + b0

Altogether:

    # solve for v and r0

    v = (r1 - r2) ÷ (t1 - t2)
    r0 = r1 - v × t1
    r0 = r2 - v × t2

    # solve for mφ and bφ 

    mφ = (phi1 - phi2) ÷ (t1 - t2)
    bφ = phi1 - mφ × t1, bφ = phi2 - mφ × t2

    # solve for mθ and bθ 

    mθ = (phi1 - phi2) ÷ (t1 - t2)
    bθ = phi1 - mθ × t1, bθ = phi2 - mθ × t2

    # solve for tcollide 

    tcollide = r0 ÷ (vrock - vslug)

    # solve for phicollide and thetacollide 

    phicollide = mφ × tcollide + bφ
    thetacollide = m0 × tcollide + b0

CODE and IMPLEMENTATOIN

game engine

The game engine lives entirely in the data model of a Postgres database.

There are two schemas:

  • game which contains the game core data
  • api which contains views used by the API

The major tables are:

  • game.rocks which contains all rocks for a given game (incl. those that have collided with a slug or the earth)
  • game.slugs which contains all slugs for a given game (incl. those that have collided with a rock)
  • game.collisions which contains all collisions between all rocks and all slugs (rocks × slugs); the collision column represents the state of the collision
  • game.hits which contains all of the hits (every computed hit of a slug and a rock)

The collision composite type represents the result of a collision computation. Its fields include:

  • t, the time at when the collision would have occurred (or null if no collision was possible)
  • pos, the position at which the collision would have occured (or null)
  • mdiff, the difference in position between the slug and rock if there was a miss (or null if there was a hit)
  • miss, an enum field with the reason for the miss — r didn't match, theta didn't match, and/or phi didn't match (or null if there was a hit)

The game.collisions and game.hits tables are populated by triggers.

  • upon insert/update to game.rocks or game.slugs, recompute all collisions and insert/update in game.collisions
  • upon insert/update/delete to game.collisions, recompute all hits and delete/insert in game.hits
  • upon insert to game.hits, compute and rock fragments and insert into game.rocks (potentially “cascading” triggers)

The major views in api are:

  • api.rocks whch contains all rocks (incl. those that have collided) with positions and other derived fields computed
  • api.slugs whch contains all slugs (incl. those that have collided) with positions and other derived fields computed
  • api.all_neos which contains a union of all rocks and slugs (incl. those that have collided)
  • api.neos which contains a union of all active rocks and slugs (not incl. those that have collided)
  • api.collisions which contains all collisions (whether hits or missed) inner-joined nicely with game.rocks and game.slugs to include display names
  • api.hits which contains all collisions (whether hits or missed) inner-joined nicely with game.rocks and game.slugs to include display names

REST API

The REST API is a single-file flask (https://github.com/pallets/flask) app.

You can find it at api/api.py.

sample scripts

You can find some sample scripts in examples/.

MEETUPS

neocrisis is a collaborative coding game created by the folks behind NYC Python! It has been played at the following events:

Submit a PR to list any events where you've played NEO Crisis.

CREDITS and COPYRIGHT

About

Save the earth with REST APIs!

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published