Compare commits

..

No commits in common. "b1ab26e41ff9ef052e10281e17aab7157c1c9ab2" and "142195b5d898dd5997201ca7335a7ad0dee5b3e8" have entirely different histories.

25 changed files with 299 additions and 147 deletions

1
.gitignore vendored
View File

@ -8,7 +8,6 @@ points
forftanks
designer.cgi
upload.cgi
forf.html
summary.html

View File

@ -1,13 +1,21 @@
DESTDIR ?= $(HOME)
BINARIES = forftanks upload.cgi
HTML = forf.html procs.html intro.html designer.html debugger.html
WWW = style.css grunge.png designer.js figures.js tanks.js nav.html.inc jstanks.js
CFLAGS = -Wall
BINARIES = forftanks upload.cgi
all: $(BINARIES) $(HTML)
all: $(BINARIES)
install:
install -d $(DESTDIR)/usr/bin
install run-tanks $(DESTDIR)/usr/bin
install forftanks $(DESTDIR)/usr/bin
install: $(BINARIES)
install -d $(DESTDIR)$(PREFIX)/bin
install $(BINARIES) $(DESTDIR)$(PREFIX)/bin
install -d $(DESTDIR)/usr/lib/tanks
install designer.cgi $(DESTDIR)/usr/lib/tanks
install $(HTML) $(DESTDIR)/usr/lib/tanks
install $(WWW) $(DESTDIR)/usr/lib/tanks
cp -r examples $(DESTDIR)/usr/lib/tanks/examples
forftanks: forftanks.o ctanks.o forf.o
forftanks: LDLIBS = -lm
@ -16,6 +24,9 @@ forftanks.o: forf.h ctanks.h
forf.o: forf.c forf.h
ctanks.o: ctanks.h
%.html: %.html.m4 nav.html.inc
m4 $< > $@
clean:
rm -f *.o next-round round-*.html
rm -f $(BINARIES)
rm -f $(BINARIES) $(HTML)

View File

@ -8,68 +8,15 @@ written by other players. Dirtbags Tanks is frequently a component of
[Dirtbags Capture The Flag](https://dirtbags.github.io/contest/).
Running it
============
forftanks TANKDIR [TANKDIR...]
`forftanks` will run a round with every tank provided as an argument.
It outputs a JSON object describing the round.
Output fields
------
`seed`
: Seed used by the random number generator.
You can specify your own seed with the environment variable `SEED.
If the same seed is used with the same tanks,
you will get the same output.
`field`
: Dimensions of the play field.
`tanks`
: Description of each tank.
`rounds`
: List of frames for each round.
Each frame is a list of tank state for each tank.
Tank state is described below.
Tank state
--------
Tank state is packed more tightly than most modern JSON APIs.
Tank state is a tuple of (x position, y position, orientation angle, turret angle, flags, sensor bits).
x position, y position
: Tank's position on the play field.
orientation angle
: Tank's orientation on the play field, in radians.
turret angle
: Turret angle, relative to the tank, in radians.
flags
: Logical or of 1 (firing), 2 (LED on), and 4 (dead)
sensor bits
: Bit field of sensor state (1 = triggered)
Documentation
============
-------------
* [Homepage](https://dirtbags.github.io/tanks/)
* [History](docs/history.md)
* [Running](docs/running.md)
Current Maintainer
=====
Author
------
Neale Pickett <neale@woozle.org>

View File

@ -1,4 +1,5 @@
#pragma once
#ifndef __CTANKS_H__
#define __CTANKS_H__
/* τ = 2π */
#define TAU 6.28318530717958647692
@ -106,3 +107,6 @@ int tank_get_sensor(struct tank *tank, int sensor_num);
/** Set the LED state */
void tank_set_led(struct tank *tank, int active);
#endif /* __CTANKS_H__ */

View File

@ -17,7 +17,7 @@
<li>Paul Ferrell: Initial implementation</li>
<li>Aaron McPhall: Math optimizations</li>
<li>Nick Moffitt: Suggestion to use FORTH-like language</li>
<li>Anna Glasgall: C Forf prototype</li>
<li>Adam Glasgall: C Forf prototype</li>
<li>Adam Thomas: Various patches</li>
<li>Various Australians: Creation of some mind-blowing tanks</li>
<li>Alyssa Milburn: JS Tanks, Tank Debugger, many more other helpful contributions</li>

View File

@ -52,7 +52,7 @@ 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 wrote a Python implementation of Forf, which was slow,
and then Anna Glasgall wrote a C implementation, which was quick.
and then Adam Glasgall wrote a C implementation, which was quick.
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

View File

@ -6,7 +6,7 @@ title: thanks
* Paul Ferrell: Initial implementation
* Aaron McPhall: Math optimizations
* Nick Moffitt: Suggestion to use FORTH-like language
* Anna Glasgall: C Forf prototype
* Adam Glasgall: C Forf prototype
* Adam Thomas: Various patches
* Various Australians: Creation of some mind-blowing tanks
* Alyssa Milburn: JS Tanks, Tank Debugger, many more other helpful contributions

4
dump.h
View File

@ -1,4 +1,4 @@
#pragma once
#ifndef __DUMP_H__
#include <stdio.h>
@ -28,3 +28,5 @@
#define TEK_line(x1, y1, x2, y2) TEK("\035%c%c%c%c%c%c%c%c", TEK_coord(x1, y1), TEK_coord(x2, y2))
#define TEK_point(x, y) TEK("\034%c%c%c%c", TEK_coord(x, y))
#define TEK_text(x, y, s) TEK("\035%c%c%c%c\037%s", TEK_coord(x, y), s)
#endif

5
forf.h
View File

@ -1,4 +1,5 @@
#pragma once
#ifndef __FORF_H__
#define __FORF_H__
#include <stdio.h>
#include <inttypes.h>
@ -145,3 +146,5 @@ int forf_eval_once(struct forf_env *env);
/** Evaluate the entire command stack */
int forf_eval(struct forf_env *env);
#endif

View File

@ -368,11 +368,38 @@ ft_read_tank(struct forftank *ftank,
void
print_header(FILE *f,
struct tanks_game *game,
int seed)
struct forftank *forftanks,
struct tank *tanks,
int ntanks)
{
fprintf(f, "{\n");
fprintf(f, " \"seed\": %d,\n", seed);
fprintf(f, " \"field\": [%d,%d],\n", (int)game->size[0], (int)game->size[1]);
int i, j;
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) {
struct sensor *s = &(tanks[i].sensors[j]);
if (! s->range) {
fprintf(f, "0,");
} else {
fprintf(f, "[%d,%.2f,%.2f,%d],",
(int)(s->range),
s->angle,
s->width,
s->turret);
}
}
fprintf(f, "]],\n");
}
fprintf(f, "],[\n");
}
void
print_footer(FILE *f)
{
fprintf(f, "]]\n");
}
void
@ -381,31 +408,24 @@ print_rounds(FILE *f,
struct tank *tanks,
int ntanks)
{
int i;
int alive;
fprintf(f, " \"rounds\": [\n");
/* Run rounds */
alive = ntanks;
for (int i = 0; (alive > 1) && (i < ROUNDS); i += 1) {
if (i > 0) {
fprintf(f, ",\n");
}
for (i = 0; (alive > 1) && (i < ROUNDS); i += 1) {
int j;
tanks_run_turn(game, tanks, ntanks);
fprintf(f, " [");
fprintf(f, "[\n");
alive = ntanks;
for (int j = 0; j < ntanks; j += 1) {
for (j = 0; j < ntanks; j += 1) {
struct tank *t = &(tanks[j]);
int k;
int flags = 0;
int sensors = 0;
if (j > 0) {
fprintf(f, ",");
}
for (k = 0; k < TANK_MAX_SENSORS; k += 1) {
if (t->sensors[k].triggered) {
sensors |= (1 << k);
@ -421,7 +441,7 @@ print_rounds(FILE *f,
alive -= 1;
flags |= 4;
}
fprintf(f, "[%d,%d,%.2f,%.2f,%d,%d]",
fprintf(f, " [%d,%d,%.2f,%.2f,%d,%d],\n",
(int)t->position[0],
(int)(t->position[1]),
t->angle,
@ -429,10 +449,8 @@ print_rounds(FILE *f,
flags,
sensors);
}
fprintf(f, "]");
fprintf(f, "],\n");
}
fprintf(f, "\n ],\n");
}
void
@ -441,56 +459,18 @@ print_standings(FILE *f,
struct tank *tanks,
int ntanks)
{
int i;
fprintf(f, " \"tanks\": [\n");
for (int i = 0; i < ntanks; i += 1) {
int killer = -1;
for (int j = 0; j < ntanks; j += 1) {
if (tanks[i].killer == &(tanks[j])) {
killer = j;
for (i = 0; i < ntanks; i += 1) {
/* &tank path cause &killer parse_error_pos lasterr */
fprintf(f, "%p\t%s\t%s\t%p\t%d\t%s\n",
&(tanks[i]),
ftanks[i].path,
tanks[i].cause_death,
tanks[i].killer,
ftanks[i].error_pos,
forf_error_str[ftanks[i].env.error]);
}
}
if (i > 0) {
fprintf(f, ",\n");
}
fprintf(f, " {\n");
fprintf(f, " \"color\": \"%s\",\n", ftanks[i].color);
fprintf(f, " \"path\": \"%s\",\n", ftanks[i].path);
fprintf(f, " \"death\": \"%s\",\n", tanks[i].cause_death);
fprintf(f, " \"killer\": %d,\n", killer);
fprintf(f, " \"errorPos\": %d,\n", ftanks[i].error_pos);
fprintf(f, " \"error\": \"%s\",\n", forf_error_str[ftanks[i].env.error]);
fprintf(f, " \"sensors\": [\n");
for (int j = 0; j < TANK_MAX_SENSORS; j += 1) {
struct sensor *s = &(tanks[i].sensors[j]);
if (j > 0) {
fprintf(f, ",\n");
}
if (! s->range) {
fprintf(f, " null");
} else {
fprintf(f, " {\"range\":%d,\"angle\":%.2f,\"width\":%.2f,\"turret\":%s}",
(int)(s->range),
s->angle,
s->width,
s->turret?"true":"false");
}
}
fprintf(f, "\n ]");
fprintf(f, "\n }");
}
fprintf(f, "\n ],\n");
}
void
print_footer(FILE *f)
{
fprintf(f, " \"\": null\n"); // sentry, so everything prior can end with a comma
fprintf(f, "}\n");
}
int
@ -503,7 +483,6 @@ main(int argc, char *argv[])
int order[MAX_TANKS];
int ntanks = 0;
int i;
int seed;
lenv[0].name = NULL;
lenv[0].proc = NULL;
@ -516,18 +495,14 @@ main(int argc, char *argv[])
/* We only need slightly random numbers */
{
char *s = getenv("SEED");
seed = atoi(s?s:"");
int seed = atoi(s?s:"");
if (! seed) {
seed = getpid();
}
srand(seed);
}
if ((argc < 2) || (argv[1][0] == '-')) {
fprintf(stderr, "usage: %s TANKDIR [TANKDIR...]\n", argv[0]);
return 1;
fprintf(stdout, "// SEED=%d\n", seed);
}
/* Every argument is a tank directory */
@ -540,6 +515,11 @@ main(int argc, char *argv[])
}
}
if (0 == ntanks) {
fprintf(stderr, "No usable tanks!\n");
return 1;
}
/* Calculate the size of the game board */
{
int x, y;
@ -586,10 +566,24 @@ main(int argc, char *argv[])
}
}
print_header(stdout, &game, seed);
print_header(stdout, &game, myftanks, mytanks, ntanks);
print_rounds(stdout, &game, mytanks, ntanks);
print_standings(stdout, myftanks, mytanks, ntanks);
print_footer(stdout);
/* Output standings to fd3.
*
* fd 3 is normally closed, so this won't normally do anything.
* To output to fd3 from the shell, you'll need to do something like this:
*
* ./run-tanks 3>standing
**/
{
FILE *standings = fdopen(3, "w");
if (standings) {
print_standings(standings, myftanks, mytanks, ntanks);
}
}
return 0;
}

83
rank.awk Executable file
View File

@ -0,0 +1,83 @@
#! /usr/bin/awk -f
BEGIN {
FS = "\t";
}
function esc(s) {
gsub(/&/, "&amp;", s);
gsub(/</, "&lt;", s);
gsub(/>/, "&gt;", s);
return s;
}
{
id = $1;
ntanks += 1;
tanks[id] = id;
if ($4 == "(nil)") {
score[id] += 1;
} else {
reason[id] = $3;
killer[id] = $4;
kills[$4] += 1;
score[$4] += 1;
}
path[id] = $2;
if ($5) {
lasterr[id] = $6 " around char " $5;
} else {
lasterr[id] = $6;
}
if (1 == getline < (path[id] "/name")) {
name[id] = esc($0);
} else {
name[id] = "<i>Unnamed</i>";
}
getline < (path[id] "/color");
if (/^#[0-9A-Fa-f]+$/) {
color[id] = $0;
} else {
color[id] = "#c0c0c0";
}
}
END {
# Fill in who killed whom
for (id in tanks) {
if (score[id] > topscore) {
winner = id;
topscore = score[id];
}
if (killer[id]) {
reason[id] = reason[id] " (<span style='color:" color[killer[id]] ";'>" name[killer[id]] "</span>)";
}
# XXX: track points a different way
# print score[id] >> (path[id] "/points");
}
# Output the table
print "<table id=\"results\">";
print "<tr><th>Name</th><th>Score</th><th>Cause of Death</th><th>Last Error</th></tr>";
for (i = ntanks; i >= 0; i -= 1) {
for (me in tanks) {
if (score[me] == i) {
if (me == winner) {
style = "style=\"font-weight: bold; background-color: #666666\"";
} else {
style = "";
}
printf("<tr " style ">");
printf("<td><span class=\"swatch\" style=\"background-color: " color[me] "\">#</span> " name[me] "</td>");
printf("<td>" score[me] "</td>");
printf("<td>" reason[me] "</td>");
printf("<td>" lasterr[me] "</td>");
printf("</tr>\n");
printf("<!-- score " path[me] " " i " -->\n");
}
}
}
print "</table>";
}

77
summary.awk Executable file
View File

@ -0,0 +1,77 @@
#! /usr/bin/awk -f
function esc(s) {
gsub(/&/, "&amp;", s);
gsub(/</, "&lt;", s);
gsub(/>/, "&gt;", s);
return s;
}
BEGIN {
ngames = 20;
getline rounds < "next-round";
print "<!DOCTYPE html>";
print "<html>";
print " <head>";
print " <title>Dirtbags Tanks</title>";
print " <link rel=\"stylesheet\" href=\"style.css\" type=\"text/css\">";
print " </head>";
print " <body>";
print " <h1>Dirtbags Tanks</h1>";
print " <p>New here? Read the <a href=\"intro.html\">introduction</a>.</p>";
print " <p>New round every minute.</p>";
print " <h2>Rankings</h2>";
print " <p>Over the last " ngames" games only.</p>";
print " <ol>";
for (i = rounds - ngames - 1; i > 0 && i < rounds; i += 1) {
fn = sprintf("round-%04d.html", i)
while (getline < fn) {
if ($2 == "score") {
scores[$3] += $4
if (scores[$3] > topscore) {
topscore = scores[$3]
}
}
}
}
for (id in scores) {
if (1 == getline < (id "/name")) {
names[id] = esc($0)
} else {
names[id] = "<i>Unnamed</i>"
}
getline < (id "/color")
if (/^#[0-9A-Fa-f]+$/) {
color[id] = $0
} else {
color[id] = "#c0c0c0"
}
}
for (s = topscore; s >= 0; s -= 1) {
for (id in scores) {
if (scores[id] == s) {
printf("<li><span class=\"swatch\" style=\"background-color: %s;\">#</span> %s (%d points)</li>\n", color[id], names[id], scores[id]);
}
}
}
print " </ol>";
print " <h2>Rounds</h2>";
print " <ul>";
for (i = rounds - 1; (i >= rounds - 721) && (i > 0); i -= 1) {
printf("<li><a href=\"round-%04d.html\">%04d</a></li>\n", i, i);
}
print " </ul>";
while (getline < ENVIRON["NAV_HTML_INC"]) {
print;
}
print " </body>";
print "</html>";
}

32
winner.awk Executable file
View File

@ -0,0 +1,32 @@
#! /usr/bin/awk -f
BEGIN {
FS = "\t";
}
{
tanks[$1] = $2;
if ($4 == "(nil)") {
p = $1;
} else {
p = $4;
}
score[p] += 1;
if (score[p] == topscore) {
winners += 1;
} else if (score[p] > topscore) {
winners = 1;
topscore = score[p];
}
}
END {
if (winners > 1) {
exit;
}
for (id in tanks) {
if (score[id] == topscore) {
print tanks[id];
}
}
}