From a3cb9f8fe17c6c258f93c2c68f5534c8d232446e Mon Sep 17 00:00:00 2001 From: Neale Pickett Date: Tue, 5 Nov 2024 16:34:07 -0700 Subject: [PATCH] Output actual JSON, and modernize it a little --- .gitignore | 1 + Makefile | 25 +--- README.md | 59 +++++++- debugger.html.m4 => contrib/debugger.html.m4 | 0 designer.html.m4 => contrib/designer.html.m4 | 0 designer.js => contrib/designer.js | 0 evolve.ml => contrib/evolve.ml | 0 jstanks.js => contrib/jstanks.js | 0 ctanks.h | 6 +- forf.html.m4 => docs/forf.html.m4 | 0 forf.txt => docs/forf.md | 0 docs/history.md | 2 +- intro.html.m4 => docs/intro.html.m4 | 0 nav.html.inc => docs/nav.html.inc | 0 procs.html.m4 => docs/procs.html.m4 | 0 docs/thanks.md | 2 +- dump.h | 4 +- forf.h | 5 +- forftanks.c | 148 ++++++++++--------- rank.awk | 83 ----------- summary.awk | 77 ---------- winner.awk | 32 ---- style.css => www/style.css | 0 tanks.js => www/tanks.js | 0 24 files changed, 146 insertions(+), 298 deletions(-) rename debugger.html.m4 => contrib/debugger.html.m4 (100%) rename designer.html.m4 => contrib/designer.html.m4 (100%) rename designer.js => contrib/designer.js (100%) rename evolve.ml => contrib/evolve.ml (100%) rename jstanks.js => contrib/jstanks.js (100%) rename forf.html.m4 => docs/forf.html.m4 (100%) rename forf.txt => docs/forf.md (100%) rename intro.html.m4 => docs/intro.html.m4 (100%) rename nav.html.inc => docs/nav.html.inc (100%) rename procs.html.m4 => docs/procs.html.m4 (100%) delete mode 100755 rank.awk delete mode 100755 summary.awk delete mode 100755 winner.awk rename style.css => www/style.css (100%) rename tanks.js => www/tanks.js (100%) diff --git a/.gitignore b/.gitignore index ce47879..cfe294b 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ points forftanks designer.cgi +upload.cgi forf.html summary.html diff --git a/Makefile b/Makefile index 9b9400f..71e847c 100644 --- a/Makefile +++ b/Makefile @@ -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) diff --git a/README.md b/README.md index 6f21959..bbe9c54 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/debugger.html.m4 b/contrib/debugger.html.m4 similarity index 100% rename from debugger.html.m4 rename to contrib/debugger.html.m4 diff --git a/designer.html.m4 b/contrib/designer.html.m4 similarity index 100% rename from designer.html.m4 rename to contrib/designer.html.m4 diff --git a/designer.js b/contrib/designer.js similarity index 100% rename from designer.js rename to contrib/designer.js diff --git a/evolve.ml b/contrib/evolve.ml similarity index 100% rename from evolve.ml rename to contrib/evolve.ml diff --git a/jstanks.js b/contrib/jstanks.js similarity index 100% rename from jstanks.js rename to contrib/jstanks.js diff --git a/ctanks.h b/ctanks.h index 57cb4e7..cbab3cf 100644 --- a/ctanks.h +++ b/ctanks.h @@ -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__ */ diff --git a/forf.html.m4 b/docs/forf.html.m4 similarity index 100% rename from forf.html.m4 rename to docs/forf.html.m4 diff --git a/forf.txt b/docs/forf.md similarity index 100% rename from forf.txt rename to docs/forf.md diff --git a/docs/history.md b/docs/history.md index 8e44909..a1e1515 100644 --- a/docs/history.md +++ b/docs/history.md @@ -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 diff --git a/intro.html.m4 b/docs/intro.html.m4 similarity index 100% rename from intro.html.m4 rename to docs/intro.html.m4 diff --git a/nav.html.inc b/docs/nav.html.inc similarity index 100% rename from nav.html.inc rename to docs/nav.html.inc diff --git a/procs.html.m4 b/docs/procs.html.m4 similarity index 100% rename from procs.html.m4 rename to docs/procs.html.m4 diff --git a/docs/thanks.md b/docs/thanks.md index 8e30390..2e9600c 100644 --- a/docs/thanks.md +++ b/docs/thanks.md @@ -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 diff --git a/dump.h b/dump.h index 4553643..7cdfb2a 100644 --- a/dump.h +++ b/dump.h @@ -1,4 +1,4 @@ -#ifndef __DUMP_H__ +#pragma once #include @@ -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 diff --git a/forf.h b/forf.h index 689db10..d231086 100644 --- a/forf.h +++ b/forf.h @@ -1,5 +1,4 @@ -#ifndef __FORF_H__ -#define __FORF_H__ +#pragma once #include #include @@ -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 diff --git a/forftanks.c b/forftanks.c index 30237df..489021a 100644 --- a/forftanks.c +++ b/forftanks.c @@ -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,18 +441,56 @@ 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 @@ -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; } diff --git a/rank.awk b/rank.awk deleted file mode 100755 index ee9615d..0000000 --- a/rank.awk +++ /dev/null @@ -1,83 +0,0 @@ -#! /usr/bin/awk -f - -BEGIN { - FS = "\t"; -} - -function esc(s) { - gsub(/&/, "&", s); - gsub(//, ">", 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] = "Unnamed"; - } - - 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] " (" name[killer[id]] ")"; - } - # XXX: track points a different way - # print score[id] >> (path[id] "/points"); - } - - # Output the table - print ""; - print ""; - 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(""); - printf(""); - printf(""); - printf(""); - printf(""); - printf("\n"); - printf("\n"); - } - } - } - print "
NameScoreCause of DeathLast Error
# " name[me] "" score[me] "" reason[me] "" lasterr[me] "
"; -} diff --git a/summary.awk b/summary.awk deleted file mode 100755 index d98975e..0000000 --- a/summary.awk +++ /dev/null @@ -1,77 +0,0 @@ -#! /usr/bin/awk -f - -function esc(s) { - gsub(/&/, "&", s); - gsub(//, ">", s); - return s; -} - -BEGIN { - ngames = 20; - getline rounds < "next-round"; - - print ""; - print ""; - print " "; - print " Dirtbags Tanks"; - print " "; - print " "; - print " "; - print "

Dirtbags Tanks

"; - - print "

New here? Read the introduction.

"; - print "

New round every minute.

"; - print "

Rankings

"; - print "

Over the last " ngames" games only.

"; - print "
    "; - 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] = "Unnamed" - } - - 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("
  1. # %s (%d points)
  2. \n", color[id], names[id], scores[id]); - } - } - } - print "
"; - - print "

Rounds

"; - print "
    "; - for (i = rounds - 1; (i >= rounds - 721) && (i > 0); i -= 1) { - printf("
  • %04d
  • \n", i, i); - } - print "
"; - - while (getline < ENVIRON["NAV_HTML_INC"]) { - print; - } - - print " "; - print ""; -} diff --git a/winner.awk b/winner.awk deleted file mode 100755 index 6a32a91..0000000 --- a/winner.awk +++ /dev/null @@ -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]; - } - } -} \ No newline at end of file diff --git a/style.css b/www/style.css similarity index 100% rename from style.css rename to www/style.css diff --git a/tanks.js b/www/tanks.js similarity index 100% rename from tanks.js rename to www/tanks.js