If you've ever played Spacewar!, you know it was a fun game. If you haven't, know this: it was (and is) one of the very first and most important computer games. And it's still fun! The clone I grew up on is this one, which is, apparently and unfortunately, Windows only. So I recreated it!
The KotH is hosted here: PPCG - Spacewar! King of the Hill. I encourage you to play as a human against at least one other bot to get a feel for how the game works.
The Game
- One frame is 30 milliseconds (thus, about 33 frames per second).
- The field is 800 pixels wide and 600 pixels tall.
- The field is toroidal, meaning that spaceships and missiles that move outside of the field reappear on the opposite side.
- There are two spaceships, red and blue.
- Red is positioned at x=50 and random y between 50, (field height - 50) pixels.
- Blue is positioned at x=(field width - 50) and random y between 50, (field height - 50) pixels.
- Both face x = (field width)/2.
- The available controls are:
- Turn left - 5 degrees per frame counterclockwise.
- Turn right - 5 degrees per frame clockwise.
- Fire missile - travels at an extra 10 pixels per frame in addition to the velocity of the ship, in the direction the ship was pointing.
- Fire engine - accelerates the spaceship at 0.30 pixels per frame in the direction the spaceship is pointing.
- Hyperspace jump - teleports to some random coordinates in the field, with a 25% chance of exploding. These random coordinates may be on top of the sun.
- The maximum speed for ships is 15 pixels per frame under engine power and 40 pixels per frame when gravity-boosted.
- When traveling faster than 15 pixels per frame, engine thrust may only change direction or slow down.
- Regarding missiles:
- Missiles travel in a straight line.
- Missiles may be fired at a maximum rate of 1 per 0.1 seconds.
- Missiles have a lifetime of 2.25 seconds.
- Ships have a maximum of 20 missiles each.
- Missiles are point particles internally.
- There is a sun in the very center that is extremely hazardous to your ship. The slightest contact is fatal. This sun also destroys missiles.
- The sun has gravity. The resultant acceleration is 5000/(distance^2) pixels/frame^2, where distance is in pixels. Spaceships and missiles are affected.
- Both ships have three strike zones: the nose, the left wing, and the right wing.
- A hit on the nose is instant death.
- A hit on either wing reduces the spaceship's turn rate and engine acceleration by half.
- If both wings are destroyed, the spaceship cannot be maneuvered and can only fire missiles.
- Ships may collide with each other.
- A nose-nose impact is fatal for both ships.
- A nose-wing impact destroys the wing.
- A wing-wing impact destroys both wings.
- Dead ships are solid and frozen until they explode 1 second later.
- After at least one ship has died, the field is reset 3 seconds later. Until then, the sun and any remaining missiles are still hazardous.
The original game also has deadly and indestructible asteroids, but I won't include those.
The Rules
- Your bot must be written in JavaScript.
- Your bot should limit its decision to about 10 milliseconds. If I notice consistent lag because of your bot, I'll disqualify it and let you know so you can fix it.
- Bots will have access to the following:
- Field width and field height
- Sun position and radius
- The position, rotation, velocity, shape, missile stock, and in-hyperspace status of both ships
- The position and velocity of all missiles
- When prompted, your bot should return a list of strings.
- These strings should be one of the following:
turn left
,turn right
,fire engine
,fire missile
,hyperspace
. Any other string will be ignored. - If there are any duplicates, only the first will be noted.
hyperspace
takes precedence over all others.turn left
andturn right
at the same time will have no effect.fire engine
will have no effect if the ship only has the nose or is dead.fire missile
will have no effect if a missile was fired too recently.
- These strings should be one of the following:
- In a change from the usual, your bot is allowed to exploit the behavior of other bots. I want to encourage a metagame.
- Bots may not emulate other bots. (I.e., no mind-reading.)
- Bots may not set any variables used by the game and physics code. (I.e., no cheating.)
Bot Implementation Details
I will be storing your bot in its own JavaScript file that is automatically included, with the filename bot_<name>.js
. So don't put any spaces or characters that would interfere with this or with naming a function in JavaScript. That's because you should define the following functions: <name>_setup(team)
and <name>_getActions(gameInfo, botVars)
. Further down the page, there exist textareas for the userbot, which you can edit to test your code.
<name>_setup(team)
This function is for you to define any variables that you want to persist. team
will be either "red"
or "blue"
. This function must return an object. Define variables like so:
var vars = {};
vars['example'] = "example";
return vars;
This vars
object will be passed in to the other function:
<name>_getActions(gameInfo, botVars)
botVars
is the object returned by <name>_setup(team)
. gameInfo
is an object containing the following variables:
redScore
blueScore
timeLeft
fieldWidth
fieldHeight
sun_x
sun_y
sun_r //sun's radius
gravityStrength //acceleration in pixels/frame^2 at 1 pixel away from the sun's center
engineThrust //acceleration in pixels/frame^2
speedLimit //maximum speed under engine power
maxSpeed //maximum speed from gravity boosts
red_x
red_y
red_rot //rotation in degrees
red_xv //x velocity
red_yv //y velocity
red_shape //one of "full ship", "left wing", "right wing", "nose only"
red_missileStock //the number of missiles red has left
red_inHyperspace //true if red is in hyperspace
red_exploded //until red explodes, it is still solid and hazardous
red_alive
// likewise for blue //
numMissiles
missiles //this is a list of objects, each with the following variables
x
y
xv
yv
Your bot has full access to these. I'm pretty sure that you can write to them and not affect the original variables, but don't do it anyway. A note on rotations: ships point in the +y direction, downwards, so anything that you want to align with the ship needs to be offset by 90 degrees. Also, positive rotation is clockwise.
This function must return a list of strings, representing your bot's actions. For example, ["turn right","thrust"]
. More details on this are in the Rules section.
Additional Details
You may also make use of the following:
LineIntersection(L1, L2)
L1 and L2 are two-element arrays of two-element arrays. That is, L1 := [[x1,y1],[x2,y2]]
and L2 := [[u1,v1],[u2,v2]]
. This function computes the intersection of two lines and returns this: [[x,y], [a,b]]
. [x,y]
are the coordinates of the intersection point and [a,b]
is a pair of ratios that express how far along each line the intersection point is. As in, a = 0.25
would mean that the intersection point is a quarter of the way from [x1,y1]
to [x2,y2]
, and likewise for b
. If there is no intersection, an empty array is returned.
window["shipShapes"]
var shipShapes = {
'full ship': [[-8,16],[0,-8],[8,16]],
'left wing': [[-8,16],[0,-8],[4,4],[0,8],[0,16]],
'right wing':[[-4,4],[0,-8],[8,16],[0,16],[0,8]],
'nose only': [[-4,4],[0,-8],[4,4],[0,8]]
};
These are the coordinates of the ships' polygons. To make getting the current coordinates easier, you may also use...
getShipCoords(<color>)
getShipCoords("red")
will return the current coordinates of the vertices of Red's ship, and likewise for getShipCoords("blue")
and Blue. These coordinates are in a list like so: [[x1,y1],[x2,y2],[x3,y3],...]
. Polygons are implicitly closed, so there is a line between the first and last coordinate pairs.
You may not access or alter any other variables or functions in use by the game/website. And definitely don't name your functions the same. I don't foresee that this will be a problem, but if your bot breaks the game code, that's one possibility. There is no logging or catching of exceptions.
Winning
- Every pairing of bots shall be played at least 10 times, both ways. (So, at least 20 games total.)
- Aim to have the highest win/loss ratios overall. If your bot does super well against one other bot, but loses against the other three, that's not as good as winning against two and losing against two (as a general rule of thumb).
- For every bot, the ratios (wins+1)/(losses+1) will be calculated, then the mean and standard deviation of these ratios will be calculated. A higher mean will have priority, and in case means are within 1 unit of each other, the lower variance will have priority.
- Scoring will begin either in a week from today or after three days of no new submissions. This is so I don't have to repeat any pairing of bots.
Above all, have fun!
Leaderboard (2016-01-08 05:15):
# Name Mean StdDev
1. Helios 13.625 6.852
2. EdgeCase 8.335 8.155
3. OpponentDodger 8.415 8.186
4. OrbitBot 5.110 6.294
5. SunAvoider 5.276 6.772
6. DangitBobby 3.320 4.423
7. SprayAndPray 3.118 4.642
8. Engineer 3.903 6.315
9. RighthandedSpasms 1.805 2.477
10. AttackAndComeBack 2.521 2.921
11. PanicAttack 2.622 3.102
12. FullSpeedAhead 2.058 3.295
13. UhhIDKWhatToCallThisBot 2.555 3.406
14. MissilesPlusScore 0.159 0.228
15. Hyper 0.236 0.332
16. RandUmmm 0.988 1.329
17. Kamikaze 0.781 1.793
Note: This is subject to change as I run more games. Plus, the ordering of ranks 9-13 bothers me, so I may tweak the scoring method to better match one's intuition of how they should be ranked.
(Means and standard deviations were rounded to three decimal digits. Also, Hyper
should be HYPER
but that messes up the highlighting. :P)
LineIntersection
on non-intersecting segments returns an empty array. \$\endgroup\$