mirror of https://github.com/dirtbags/tanks.git
Documentation and a little cleanup
This commit is contained in:
parent
fc98369eba
commit
e932242cfa
|
@ -1,11 +1,17 @@
|
|||
*~
|
||||
*#
|
||||
*.o
|
||||
|
||||
round-*.html
|
||||
next-round
|
||||
|
||||
run-tanks
|
||||
forf.c
|
||||
forf.h
|
||||
forf.html
|
||||
designer.cgi
|
||||
next-round
|
||||
|
||||
forf.html
|
||||
summary.html
|
||||
designer.html
|
||||
intro.html
|
||||
procs.html
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
History
|
||||
=======
|
||||
|
||||
This is a port of the "Tanks" program written by Paul Ferrell
|
||||
<pflarr@clanspum.net> in 2009-2010. Paul created the entire game based
|
||||
off a brief description I provided him of Crobots and a vague desire to
|
||||
"make something fun for high school kids to learn some programming." We
|
||||
ran Paul's Tanks as part of a 100-attendee computer security contest in
|
||||
February of 2010 and by all accounts it was a huge success. It even
|
||||
made the nightly news.
|
||||
|
||||
Paul's version was written in Python and provided a custom language
|
||||
called "Bullet", which looked like this:
|
||||
|
||||
>addsensor(50, 0, 5, 1); # 0
|
||||
>addsensor(100, 90, 150, 1); # 1
|
||||
>addsensor(100, 270, 150, 1); # 2
|
||||
>addsensor(100, 0, 359, 0); # 3
|
||||
|
||||
# Default movement if nothing is detected
|
||||
: move(70, 70) . turretccw();
|
||||
random(2, 3): move(40, 70) . turretccw();
|
||||
random(1, 3): move(70, 40) . turretccw();
|
||||
|
||||
# We found something!!
|
||||
sense(3): move(0, 0);
|
||||
sense(1): turretcw();
|
||||
sense(2): turretccw();
|
||||
sense(0): fire();
|
||||
|
||||
Nick Moffitt played with this original version and convinced me (Neale)
|
||||
that something like Forth would be a better language. I added some code
|
||||
to accept a scaled-down version of PostScript. The IRC channel we
|
||||
frequent collectively agreed to give this new language the derisive name
|
||||
"Forf", which should ideally be followed by punching someone after it is
|
||||
spoken aloud.
|
||||
|
||||
I decided to take Tanks to Def Con in July 2010, and just for bragging
|
||||
rights, to have it run on an Asus WL-500gU. This is a $50 device with a
|
||||
240 MHz MIPS CPU, 16MB RAM, and a 4MB flash disk, along with an
|
||||
802.11b/g radio, 4-port 10/100 switch, and an additional 10/100 "uplink"
|
||||
port; it's sold as a home wireless router. I had originally intended to
|
||||
run it off a lantern battery just for fun, but eventually thought better
|
||||
of it: doing so would be wasteful for no good reason.
|
||||
|
||||
Aaron McPhall <amcphall@mcphall.org>, my summer intern at the time, got
|
||||
OpenWRT and Python onto the box and benchmarked it at about 60 seconds
|
||||
for a 16-tank game, after he had profiled the code and optimized a lot
|
||||
of the math. That wasn't bad, it meant we could run a reasonably-sized
|
||||
game at one turn per minute, which we knew from past experience was
|
||||
about the right rate. But it required a USB thumb drive to hold Python,
|
||||
and when we used the Python Forf implementation, the run-time shot up to
|
||||
several minutes. I began this C port while Adam Glasgall, another fool
|
||||
on the previously-mentioned IRC channel, started work on a C version of
|
||||
a Forf interpreter.
|
||||
|
||||
This C version with Forf runs about 6 times faster than the Python
|
||||
version with Bullet, on the WL-500gU. A 1GHz Intel Atom runs a 16-tank
|
||||
game in about 0.2 seconds.
|
||||
|
||||
|
||||
What's so great about Forf?
|
||||
---------------------------
|
||||
|
||||
Nothing's great about Forf. It's a crummy language that only does
|
||||
integer math. For this application it's a good choice, for the
|
||||
following reasons:
|
||||
|
||||
* No library dependencies, not even malloc
|
||||
* Runs in fixed size memory
|
||||
* Not Turing-complete, I think (impossible to make endless loops)
|
||||
* Lends itself to genetic algorithms
|
||||
|
10
Makefile
10
Makefile
|
@ -1,7 +1,9 @@
|
|||
BINARIES = run-tanks designer.cgi
|
||||
HTML = forf.html procs.html intro.html designer.html
|
||||
|
||||
CFLAGS = -Wall
|
||||
|
||||
all: html run-tanks designer.cgi
|
||||
html: forf.html procs.html intro.html
|
||||
all: $(BINARIES) $(HTML)
|
||||
|
||||
run-tanks: run-tanks.o ctanks.o forf.o
|
||||
run-tanks: LDFLAGS = -lm
|
||||
|
@ -22,5 +24,5 @@ forf/%:
|
|||
git submodule update --init
|
||||
|
||||
clean:
|
||||
rm -f run-tanks designer.cgi *.o forf.c forf.h
|
||||
rm -f next-round round-*.html summary.html forf.html
|
||||
rm -f *.o forf.c forf.h next-round round-*.html
|
||||
rm -f $(BINARIES) $(HTML)
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
CTanks
|
||||
======
|
||||
|
||||
This is a programming game, inspired by Crobots by Tom Poindexter
|
||||
(http://www.nyx.net/~tpoindex/crob.html). Players write programs for
|
||||
tanks, with the intent to seek out and destroy other tanks.
|
||||
|
||||
Output is a JSON object, and some scripts are provided to wrap the
|
||||
object up in a web page.
|
||||
|
||||
|
||||
Included programs
|
||||
-----------------
|
||||
|
||||
I tried to stick with the Unix philosophy of one program per task. I
|
||||
also tried to avoid doing any string processing in C. The result is a
|
||||
hodgepodge of C, Bourne shell, and awk, but at least each piece is
|
||||
fairly simple to audit.
|
||||
|
||||
|
||||
### round.sh tank1 tank2 ...
|
||||
|
||||
Runs a single round, awards points with rank.awk, and creates a new
|
||||
summary.html with summary.awk. This is the main interface that you want
|
||||
to run from cron or whatever.
|
||||
|
||||
|
||||
### run-tanks tank1 tank2 ...
|
||||
|
||||
A program to run a round of tanks and output a JSON description of the
|
||||
game. This is what tanks.js uses to render a game graphically.
|
||||
The object printed contains:
|
||||
|
||||
[[game-width, game-height],
|
||||
[[tank1-color,
|
||||
[[sensor1range, sensor1angle, sensor1width, sensor1turret],
|
||||
...]],
|
||||
...],
|
||||
[[
|
||||
[tank1x, tank1y, tank1angle, tank1sensangle,
|
||||
tank1flags, tank1sensors],
|
||||
...],
|
||||
...]]
|
||||
|
||||
If file descriptor 3 is open for writes, it also outputs the results of
|
||||
the round to fd3.
|
||||
|
||||
|
||||
### rank.awk
|
||||
|
||||
Processes the fd3 output of run-tanks to award points and output an
|
||||
HTML results table.
|
||||
|
||||
|
||||
### summary.awk tank1 tank2
|
||||
|
||||
Creates summary.html, linking to all rounds and showing overall
|
||||
standing.
|
||||
|
||||
|
||||
### designer.cgi
|
||||
|
||||
Accepts form input and writes a tank.
|
||||
|
||||
|
||||
|
||||
Building from source
|
||||
--------------------
|
||||
|
||||
You should be able to just run "make". The C is supposedly ANSI C, and
|
||||
might even compile on Windows. I've built it on Linux, with glibc and
|
||||
uClibc, big- and little-endian.
|
||||
|
||||
|
||||
|
||||
History
|
||||
-------
|
||||
|
||||
This is a port of the "Tanks" program written by Paul Ferrell
|
||||
<pflarr@clanspum.net> in 2009-2010. Paul created the entire game based
|
||||
off a brief description I provided him of Crobots and a vague desire to
|
||||
"make something fun for high school kids to learn some programming." We
|
||||
ran Paul's Tanks as part of a 100-attendee computer security contest in
|
||||
February of 2010 and by all accounts it was a huge success. It even
|
||||
made the nightly news.
|
||||
|
||||
Paul's version was written in Python and provided a custom language
|
||||
called "Bullet", which looked like this:
|
||||
|
||||
>addsensor(50, 0, 5, 1); # 0
|
||||
>addsensor(100, 90, 150, 1); # 1
|
||||
>addsensor(100, 270, 150, 1); # 2
|
||||
>addsensor(100, 0, 359, 0); # 3
|
||||
|
||||
# Default movement if nothing is detected
|
||||
: move(70, 70) . turretccw();
|
||||
random(2, 3): move(40, 70) . turretccw();
|
||||
random(1, 3): move(70, 40) . turretccw();
|
||||
|
||||
# We found something!!
|
||||
sense(3): move(0, 0);
|
||||
sense(1): turretcw();
|
||||
sense(2): turretccw();
|
||||
sense(0): fire();
|
||||
|
||||
Nick Moffitt played with this original version and convinced me (Neale)
|
||||
that something like Forth would be a better language. I added some code
|
||||
to accept a scaled-down version of PostScript. The IRC channel we
|
||||
frequent collectively agreed to give this new language the derisive name
|
||||
"Forf", which should ideally be followed by punching someone after it is
|
||||
spoken aloud.
|
||||
|
||||
I decided to take Tanks to Def Con in July 2010, and just for bragging
|
||||
rights, to have it run on an Asus WL-500gU. This is a $50 device with a
|
||||
240 MHz MIPS CPU, 16MB RAM, and a 4MB flash disk, along with an
|
||||
802.11b/g radio, 4-port 10/100 switch, and an additional 10/100 "uplink"
|
||||
port; it's sold as a home wireless router. I had originally intended to
|
||||
run it off a lantern battery just for fun, but eventually thought better
|
||||
of it: doing so would be wasteful for no good reason.
|
||||
|
||||
Aaron McPhall <amcphall@mcphall.org>, my summer intern at the time, got
|
||||
OpenWRT and Python onto the box and benchmarked it at about 60 seconds
|
||||
for a 16-tank game, after he had profiled the code and optimized a lot
|
||||
of the math. That wasn't bad, it meant we could run a reasonably-sized
|
||||
game at one turn per minute, which we knew from past experience was
|
||||
about the right rate. But it required a USB thumb drive to hold Python,
|
||||
and when we used the Python Forf implementation, the run-time shot up to
|
||||
several minutes. I began this C port while Adam Glasgall, another fool
|
||||
on the previously-mentioned IRC channel, started work on a C version of
|
||||
a Forf interpreter.
|
||||
|
||||
This C version with Forf runs about 6 times faster than the Python
|
||||
version with Bullet, on the WL-500gU. A 1GHz Intel Atom runs a 16-tank
|
||||
game in about 0.2 seconds.
|
||||
|
||||
|
||||
What's so great about Forf?
|
||||
---------------------------
|
||||
|
||||
Nothing's great about Forf. It's a crummy language that only does
|
||||
integer math. For this application it's a good choice, for the
|
||||
following reasons:
|
||||
|
||||
* No library dependencies, not even malloc
|
||||
* Runs in fixed size memory
|
||||
* Not Turing-complete, I think (impossible to make endless loops)
|
||||
* Lends itself to genetic algorithms
|
||||
|
||||
|
||||
Author
|
||||
------
|
||||
|
||||
Neale Pickett <neale@woozle.org>
|
156
designer.html
156
designer.html
|
@ -1,156 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Tank Designer</title>
|
||||
<link rel="stylesheet" href="dirtbags.css" type="text/css">
|
||||
<style type="text/css">
|
||||
#preview {
|
||||
float: right;
|
||||
}
|
||||
#sensors input {
|
||||
width: 5em;
|
||||
}
|
||||
#program textarea {
|
||||
width: 100%;
|
||||
min-height: 20em;
|
||||
}
|
||||
td {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
<script type="application/javascript" src="tanks.js"></script>
|
||||
<script type="application/javascript" src="designer.js"></script>
|
||||
<script type="application/javascript">
|
||||
window.onload = design;
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Tank Designer</h1>
|
||||
<div id="preview"><canvas id="design"></canvas><p id="debug"></p></div>
|
||||
|
||||
<p>
|
||||
Before you can get going with a tank, you need a token. If you
|
||||
need a token, just ask one of the dirtbags.
|
||||
</p>
|
||||
|
||||
<form action="designer.cgi" method="post">
|
||||
<fieldset id="metadata">
|
||||
<legend>Information</legend>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Token:</td>
|
||||
<td><input name="token"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Tank name:</td>
|
||||
<td><input name="name"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Author:</td>
|
||||
<td><input name="author"> (eg. Joe Cool
|
||||
<joe@cool.cc>)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Color:</td>
|
||||
<td><input name="color" type="color" value="#c0c0c0"
|
||||
onchange="update();"> (eg. #c7e148)</td>
|
||||
</tr>
|
||||
</table>
|
||||
</fieldset>
|
||||
|
||||
<fieldset id="sensors">
|
||||
<legend>Sensors</legend>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>#</td>
|
||||
<td>Range</td>
|
||||
<td>Angle</td>
|
||||
<td>Width</td>
|
||||
<td>Turret?</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>0</td>
|
||||
<td><input name="s0r" type="number" min="0" max="100" onchange="update();"></td>
|
||||
<td><input name="s0a" type="number" min="0" max="360" onchange="update();"></td>
|
||||
<td><input name="s0w" type="number" min="0" max="360" onchange="update();"></td>
|
||||
<td><input name="s0t" type="checkbox" onchange="update();"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td><input name="s1r" type="number" min="0" max="100" onchange="update();"></td>
|
||||
<td><input name="s1a" type="number" min="0" max="360" onchange="update();"></td>
|
||||
<td><input name="s1w" type="number" min="0" max="360" onchange="update();"></td>
|
||||
<td><input name="s1t" type="checkbox" onchange="update();"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td><input name="s2r" type="number" min="0" max="100" onchange="update();"></td>
|
||||
<td><input name="s2a" type="number" min="0" max="360" onchange="update();"></td>
|
||||
<td><input name="s2w" type="number" min="0" max="360" onchange="update();"></td>
|
||||
<td><input name="s2t" type="checkbox" onchange="update();"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td><input name="s3r" type="number" min="0" max="100" onchange="update();"></td>
|
||||
<td><input name="s3a" type="number" min="0" max="360" onchange="update();"></td>
|
||||
<td><input name="s3w" type="number" min="0" max="360" onchange="update();"></td>
|
||||
<td><input name="s3t" type="checkbox" onchange="update();"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>4</td>
|
||||
<td><input name="s4r" type="number" min="0" max="100" onchange="update();"></td>
|
||||
<td><input name="s4a" type="number" min="0" max="360" onchange="update();"></td>
|
||||
<td><input name="s4w" type="number" min="0" max="360" onchange="update();"></td>
|
||||
<td><input name="s4t" type="checkbox" onchange="update();"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>5</td>
|
||||
<td><input name="s5r" type="number" min="0" max="100" onchange="update();"></td>
|
||||
<td><input name="s5a" type="number" min="0" max="360" onchange="update();"></td>
|
||||
<td><input name="s5w" type="number" min="0" max="360" onchange="update();"></td>
|
||||
<td><input name="s5t" type="checkbox" onchange="update();"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>6</td>
|
||||
<td><input name="s6r" type="number" min="0" max="100" onchange="update();"></td>
|
||||
<td><input name="s6a" type="number" min="0" max="360" onchange="update();"></td>
|
||||
<td><input name="s6w" type="number" min="0" max="360" onchange="update();"></td>
|
||||
<td><input name="s6t" type="checkbox" onchange="update();"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>7</td>
|
||||
<td><input name="s7r" type="number" min="0" max="100" onchange="update();"></td>
|
||||
<td><input name="s7a" type="number" min="0" max="360" onchange="update();"></td>
|
||||
<td><input name="s7w" type="number" min="0" max="360" onchange="update();"></td>
|
||||
<td><input name="s7t" type="checkbox" onchange="update();"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>8</td>
|
||||
<td><input name="s8r" type="number" min="0" max="100" onchange="update();"></td>
|
||||
<td><input name="s8a" type="number" min="0" max="360" onchange="update();"></td>
|
||||
<td><input name="s8w" type="number" min="0" max="360" onchange="update();"></td>
|
||||
<td><input name="s8t" type="checkbox" onchange="update();"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>9</td>
|
||||
<td><input name="s9r" type="number" min="0" max="100" onchange="update();"></td>
|
||||
<td><input name="s9a" type="number" min="0" max="360" onchange="update();"></td>
|
||||
<td><input name="s9w" type="number" min="0" max="360" onchange="update();"></td>
|
||||
<td><input name="s9t" type="checkbox" onchange="update();"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</fieldset>
|
||||
|
||||
<fieldset id="program">
|
||||
<legend>Program</legend>
|
||||
<textarea name="program"></textarea>
|
||||
</fieldset>
|
||||
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,98 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Tank Designer</title>
|
||||
<link rel="stylesheet" href="dirtbags.css" type="text/css">
|
||||
<style type="text/css">
|
||||
#preview {
|
||||
float: right;
|
||||
}
|
||||
#sensors input {
|
||||
width: 5em;
|
||||
}
|
||||
#program textarea {
|
||||
width: 100%;
|
||||
min-height: 20em;
|
||||
}
|
||||
td {
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
<script type="application/javascript" src="tanks.js"></script>
|
||||
<script type="application/javascript" src="designer.js"></script>
|
||||
<script type="application/javascript">
|
||||
window.onload = design;
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Tank Designer</h1>
|
||||
<div id="preview"><canvas id="design"></canvas><p id="debug"></p></div>
|
||||
|
||||
<p>
|
||||
Before you can get going with a tank, you need a password. If you
|
||||
need a password, just ask one of the dirtbags.
|
||||
</p>
|
||||
|
||||
<form action="designer.cgi" method="post">
|
||||
<fieldset id="metadata">
|
||||
<legend>Information</legend>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Token:</td>
|
||||
<td><input name="token"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Tank name:</td>
|
||||
<td><input name="name"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Author:</td>
|
||||
<td><input name="author"> (eg. Joe Cool
|
||||
<joe@cool.cc>)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Color:</td>
|
||||
<td><input name="color" type="color" value="#c0c0c0"
|
||||
onchange="update();"> (eg. #c7e148)</td>
|
||||
</tr>
|
||||
</table>
|
||||
</fieldset>
|
||||
|
||||
<fieldset id="sensors">
|
||||
<legend>Sensors</legend>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>#</td>
|
||||
<td>Range</td>
|
||||
<td>Angle</td>
|
||||
<td>Width</td>
|
||||
<td>Turret?</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
define(sensor,
|
||||
`ifelse(`$1',`10',,`
|
||||
<tr>
|
||||
<td>$1</td>
|
||||
<td><input name="s$1r" type="number" min="0" max="100" onchange="update();"></td>
|
||||
<td><input name="s$1a" type="number" min="0" max="360" onchange="update();"></td>
|
||||
<td><input name="s$1w" type="number" min="0" max="360" onchange="update();"></td>
|
||||
<td><input name="s$1t" type="checkbox" onchange="update();"></td>
|
||||
</tr>
|
||||
sensor(incr($1))')')
|
||||
sensor(0)
|
||||
</tbody>
|
||||
</table>
|
||||
</fieldset>
|
||||
|
||||
<fieldset id="program">
|
||||
<legend>Program</legend>
|
||||
<textarea name="program"></textarea>
|
||||
</fieldset>
|
||||
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
include(nav.html.inc)
|
||||
</body>
|
||||
</html>
|
|
@ -353,8 +353,8 @@ print_header(FILE *f,
|
|||
{
|
||||
int i, j;
|
||||
|
||||
fprintf(f, "[[%d,%d,%d],[\n",
|
||||
(int)game->size[0], (int)game->size[1], TANK_CANNON_RANGE);
|
||||
fprintf(f, "[[%d,%d],[\n",
|
||||
(int)game->size[0], (int)game->size[1]);
|
||||
for (i = 0; i < ntanks; i += 1) {
|
||||
fprintf(f, " [\"%s\",[", forftanks[i].color);
|
||||
for (j = 0; j < TANK_MAX_SENSORS; j += 1) {
|
||||
|
|
Loading…
Reference in New Issue