Output actual JSON, and modernize it a little

This commit is contained in:
Neale Pickett 2024-11-05 16:34:07 -07:00
parent 49152d5128
commit a3cb9f8fe1
24 changed files with 146 additions and 298 deletions

1
.gitignore vendored
View File

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

View File

@ -1,21 +1,13 @@
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
DESTDIR ?= $(HOME)
CFLAGS = -Wall
all: $(BINARIES) $(HTML)
BINARIES = forftanks upload.cgi
install:
install -d $(DESTDIR)/usr/bin
install run-tanks $(DESTDIR)/usr/bin
install forftanks $(DESTDIR)/usr/bin
all: $(BINARIES)
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
install: $(BINARIES)
install -d $(DESTDIR)$(PREFIX)/bin
install $(BINARIES) $(DESTDIR)$(PREFIX)/bin
forftanks: forftanks.o ctanks.o forf.o
forftanks: LDLIBS = -lm
@ -24,9 +16,6 @@ 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) $(HTML)
rm -f $(BINARIES)

View File

@ -8,15 +8,68 @@ 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)
Author
------
Current Maintainer
=====
Neale Pickett <neale@woozle.org>

View File

@ -1,5 +1,4 @@
#ifndef __CTANKS_H__
#define __CTANKS_H__
#pragma once
/* τ = 2π */
#define TAU 6.28318530717958647692
@ -107,6 +106,3 @@ 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

@ -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 Adam Glasgall wrote a C implementation, which was quick.
and then Anna 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
* Adam Glasgall: C Forf prototype
* Anna 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 @@
#ifndef __DUMP_H__
#pragma once
#include <stdio.h>
@ -28,5 +28,3 @@
#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,5 +1,4 @@
#ifndef __FORF_H__
#define __FORF_H__
#pragma once
#include <stdio.h>
#include <inttypes.h>
@ -146,5 +145,3 @@ int forf_eval_once(struct forf_env *env);
/** Evaluate the entire command stack */
int forf_eval(struct forf_env *env);
#endif

View File

@ -368,38 +368,11 @@ ft_read_tank(struct forftank *ftank,
void
print_header(FILE *f,
struct tanks_game *game,
struct forftank *forftanks,
struct tank *tanks,
int ntanks)
int seed)
{
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");
fprintf(f, "{\n");
fprintf(f, " \"seed\": %d,\n", seed);
fprintf(f, " \"field\": [%d,%d],\n", (int)game->size[0], (int)game->size[1]);
}
void
@ -408,24 +381,31 @@ print_rounds(FILE *f,
struct tank *tanks,
int ntanks)
{
int i;
int alive;
fprintf(f, " \"rounds\": [\n");
/* Run rounds */
alive = ntanks;
for (i = 0; (alive > 1) && (i < ROUNDS); i += 1) {
int j;
for (int i = 0; (alive > 1) && (i < ROUNDS); i += 1) {
if (i > 0) {
fprintf(f, ",\n");
}
tanks_run_turn(game, tanks, ntanks);
fprintf(f, "[\n");
fprintf(f, " [");
alive = ntanks;
for (j = 0; j < ntanks; j += 1) {
for (int 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);
@ -441,7 +421,7 @@ print_rounds(FILE *f,
alive -= 1;
flags |= 4;
}
fprintf(f, " [%d,%d,%.2f,%.2f,%d,%d],\n",
fprintf(f, "[%d,%d,%.2f,%.2f,%d,%d]",
(int)t->position[0],
(int)(t->position[1]),
t->angle,
@ -449,8 +429,10 @@ print_rounds(FILE *f,
flags,
sensors);
}
fprintf(f, "],\n");
fprintf(f, "]");
}
fprintf(f, "\n ],\n");
}
void
@ -459,20 +441,58 @@ print_standings(FILE *f,
struct tank *tanks,
int ntanks)
{
int i;
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]);
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;
}
}
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
main(int argc, char *argv[])
{
@ -483,6 +503,7 @@ main(int argc, char *argv[])
int order[MAX_TANKS];
int ntanks = 0;
int i;
int seed;
lenv[0].name = NULL;
lenv[0].proc = NULL;
@ -495,14 +516,18 @@ main(int argc, char *argv[])
/* We only need slightly random numbers */
{
char *s = getenv("SEED");
int seed = atoi(s?s:"");
seed = atoi(s?s:"");
if (! seed) {
seed = getpid();
}
srand(seed);
fprintf(stdout, "// SEED=%d\n", seed);
}
if ((argc < 2) || (argv[1][0] == '-')) {
fprintf(stderr, "usage: %s TANKDIR [TANKDIR...]\n", argv[0]);
return 1;
}
/* Every argument is a tank directory */
@ -515,11 +540,6 @@ 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;
@ -566,24 +586,10 @@ main(int argc, char *argv[])
}
}
print_header(stdout, &game, myftanks, mytanks, ntanks);
print_header(stdout, &game, seed);
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;
}

View File

@ -1,83 +0,0 @@
#! /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>";
}

View File

@ -1,77 +0,0 @@
#! /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>";
}

View File

@ -1,32 +0,0 @@
#! /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];
}
}
}