diff --git a/Makefile b/Makefile index 10c7c0b..42e28f5 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -BINARIES = forftanks designer.cgi +BINARIES = forftanks upload.cgi HTML = forf.html procs.html intro.html designer.html WWW = style.css grunge.png designer.js figures.js tanks.js nav.html.inc diff --git a/TODO.txt b/TODO.txt index 14ab0ed..15f5369 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,14 +1,10 @@ BUGS ==== -* Fix "random" bug -* Pull fuzzie tree - Requests ======== -* Keep 12 hours of runs (720 runs) * Merge devdsp fixes (https://github.com/devdsp/tanks/commits/master) * Fix leaderboard * Read sensors from "sensors" and not "sensor[0-9]" diff --git a/contrib/tput b/contrib/tput new file mode 100755 index 0000000..6750cba --- /dev/null +++ b/contrib/tput @@ -0,0 +1,60 @@ +#!/usr/bin/env python +""" Here's an example tank, in RFC822ish format: + +From: Joe Cool +Password: swordfish +Tank-Name: Red Baron +Color: #c7e148 +Sensor-0: 50 0 7 1 +Sensor-1: 30 0 90 0 + +get-turret 12 + set-turret! ( Rotate turret ) +37 40 set-speed! ( Go in circles ) +0 sensor? { fire! } if ( Fire if turret sensor triggered ) +1 sensor? { -50 50 set-speed! } if ( Turn if collision sensor triggered ) +""" + +import argparse +import sys +import email +import requests + +def parse_args(): + parser = argparse.ArgumentParser(description="Upload forf tanks.", + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument('--infile', '-i', nargs='?', metavar='FILE', + help="filename containing tank program", + type=argparse.FileType('r'), default=sys.stdin) + parser.add_argument('--url', '-u', nargs='?', metavar='URL', + help="URL to submit your tank to", + default='http://woozle.org/tanks/designer.cgi') + return parser.parse_args() + +def read_tank(infile): + msg = email.message_from_file(infile) + headers = dict(msg.items()) + return headers, msg.get_payload() + +def post_tank(headers, code, url='http://woozle.org/tanks/designer.cgi'): + request = {} + request['token'] = headers.get('Password', '') + request['name'] = headers.get('Tank-Name', '') + request['author'] = headers.get('From', '') + request['color'] = headers.get('Color', '#c0c0c0') + for sn in xrange(0, 10): + sensor = 'Sensor-{}'.format(sn) + if sensor in headers: + sr, sa, sw, st = headers[sensor].split() + request['s{}r'.format(sn)] = sr + request['s{}a'.format(sn)] = sa + request['s{}w'.format(sn)] = sw + request['s{}t'.format(sn)] = st + request['program'] = code + r = requests.post(url, data=request, headers={'Accept': 'text/plain'}) + print(r.text) + r.raise_for_status() + +if __name__ == '__main__': + args = parse_args() + headers, code = read_tank(args.infile) + post_tank(headers, code, args.url) diff --git a/ctanks.c b/ctanks.c index 7d7c7c5..76574d8 100644 --- a/ctanks.c +++ b/ctanks.c @@ -317,7 +317,7 @@ tanks_move_tank(struct tanks_game *game, to be a penalty for having the treads go in opposite directions. This probably plays hell with precisely-planned tanks, which I find very ha ha. */ - friction = .75 * (fabsf(tank->speed.current[0] - tank->speed.current[1]) / 200); + friction = TANK_FRICTION * (fabsf(tank->speed.current[0] - tank->speed.current[1]) / 200); v[0] = tank->speed.current[0] * (1 - friction) * (TANK_TOP_SPEED / 100.0); v[1] = tank->speed.current[1] * (1 - friction) * (TANK_TOP_SPEED / 100.0); diff --git a/ctanks.h b/ctanks.h index 4086176..57cb4e7 100644 --- a/ctanks.h +++ b/ctanks.h @@ -14,6 +14,7 @@ #define TANK_MAX_ACCEL 35 #define TANK_MAX_TURRET_ROT (TAU/8) #define TANK_TOP_SPEED 7 +#define TANK_FRICTION 0.75 /* (tank radius + tank radius)^2 */ #define TANK_COLLISION_ADJ2 \ diff --git a/designer.cgi.c b/designer.cgi.c deleted file mode 100644 index 1430444..0000000 --- a/designer.cgi.c +++ /dev/null @@ -1,320 +0,0 @@ -/* - * This software has been authored by an employee or employees of Los - * Alamos National Security, LLC, operator of the Los Alamos National - * Laboratory (LANL) under Contract No. DE-AC52-06NA25396 with the U.S. - * Department of Energy. The U.S. Government has rights to use, - * reproduce, and distribute this software. The public may copy, - * distribute, prepare derivative works and publicly display this - * software without charge, provided that this Notice and any statement - * of authorship are reproduced on all copies. Neither the Government - * nor LANS makes any warranty, express or implied, or assumes any - * liability or responsibility for the use of this software. If - * software is modified to produce derivative works, such modified - * software should be clearly marked, so as not to confuse it with the - * version available from LANL. - */ - -#include -#include -#include -#include -#include -#include -#include - -char *BASE_PATH = ""; - -struct { - char *name; - size_t size; -} entries[] = { - {"name", 20}, - {"author", 40}, - {"color", 10}, - {"program", 8192}, - {NULL, 0} -}; - - -size_t inlen; - -int -read_char() -{ - if (inlen) { - inlen -= 1; - return getchar(); - } - return EOF; -} - -char -tonum(int c) -{ - if ((c >= '0') && (c <= '9')) { - return c - '0'; - } - if ((c >= 'a') && (c <= 'f')) { - return 10 + c - 'a'; - } - if ((c >= 'A') && (c <= 'F')) { - return 10 + c - 'A'; - } - return 0; -} - -char -read_hex() -{ - int a = read_char(); - int b = read_char(); - - return tonum(a)*16 + tonum(b); -} - -/* Read a key or a value. Since & and = aren't supposed to appear - outside of boundaries, we can use the same function for both. -*/ -size_t -read_item(char *str, size_t maxlen) -{ - int c; - size_t pos = 0; - - while (1) { - c = read_char(); - switch (c) { - case EOF: - case '=': - case '&': - str[pos] = '\0'; - return pos; - case '%': - c = read_hex(); - break; - case '+': - c = ' '; - break; - } - if (pos < maxlen - 1) { - str[pos] = c; - pos += 1; - } - } -} - -size_t -copy_item(char *filename, size_t maxlen) -{ - FILE *f; - char path[132]; - int c; - size_t pos = 0; - - snprintf(path, sizeof(path), - "%s%05d.%s", BASE_PATH, - getpid(), filename); - f = fopen(path, "w"); - if (! f) { - /* Just send it to the bit bucket */ - maxlen = 0; - } - - while (1) { - c = read_char(); - switch (c) { - case EOF: - case '=': - case '&': - if (f) fclose(f); - return pos; - case '%': - c = read_hex(); - break; - case '+': - c = ' '; - break; - } - if (pos < maxlen) { - fputc(c, f); - pos += 1; - } - } -} - -int -croak(char *msg) -{ - int i; - char path[132]; - - for (i = 0; entries[i].name; i += 1) { - snprintf(path, sizeof(path), - "%s%05d.%s", BASE_PATH, - getpid(), entries[i].name); - unlink(path); - } - - printf("Content-type: text/html\n\n"); - printf("\n"); - printf("\n"); - printf("Tank submission error\n"); - printf("

Tank submission error

\n"); - if (msg) { - printf("

%s.

\n", msg); - } else { - printf("

Something went wrong.

.\n"); - } - printf("

Sorry it didn't work out.

\n"); - printf("

You could go back and try again, though.

\n"); - printf("\n"); - - return 0; -} - -int -main(int argc, char *argv[]) -{ - int sensor[10][4]; - char key[20]; - char token[40]; - size_t len; - - memset(sensor, 0, sizeof(sensor)); - token[0] = '\0'; - - BASE_PATH = getenv("BASE_PATH"); - if (! BASE_PATH) { - BASE_PATH = ""; - } - - { - char *rm = getenv("REQUEST_METHOD"); - - if (! (rm && (0 == strcmp(rm, "POST")))) { - printf("405 Method not allowed\n"); - printf("Allow: POST\n"); - printf("Content-type: text/html\n"); - printf("\n"); - printf("

Method not allowed

\n"); - printf("

I only speak POST. Sorry.

\n"); - return 0; - } - - inlen = atoi(getenv("CONTENT_LENGTH")); - } - - while (inlen) { - len = read_item(key, sizeof(key)); - if (0 == strcmp(key, "token")) { - read_item(token, sizeof(token)); - } else if ((3 == len) && ('s' == key[0])) { - /* sensor dealie, key = "s[0-9][rawt]" */ - char val[5]; - int n = key[1] - '0'; - int i; - int p; - - read_item(val, sizeof(val)); - - if (! (n >= 0) && (n <= 9)) { - break; - } - i = atoi(val); - - switch (key[2]) { - case 'r': - p = 0; - break; - case 'a': - p = 1; - break; - case 'w': - p = 2; - break; - default: - p = 3; - i = (val[0] != '\0'); - break; - } - - sensor[n][p] = i; - } else { - int i; - - for (i = 0; entries[i].name; i += 1) { - if (0 == strcmp(key, entries[i].name)) { - len = copy_item(key, entries[i].size); - break; - } - } - } - } - - /* Sanitize token */ - { - char *p = token; - - while (*p) { - if (! isalnum(*p)) { - *p = '_'; - } - p += 1; - } - - if ('\0' == token[0]) { - token[0] = '_'; - token[1] = '\0'; - } - } - - /* Move files into their directory */ - { - char path[132]; - char dest[132]; - struct stat st; - int i; - - snprintf(path, sizeof(path), "%s%s/", BASE_PATH, token); - if (-1 == stat(path, &st)) return croak("Invalid token"); - if (! S_ISDIR(st.st_mode)) return croak("Invalid token"); - for (i = 0; entries[i].name; i += 1) { - snprintf(path, sizeof(path), - "%s%05d.%s", BASE_PATH, - getpid(), entries[i].name); - snprintf(dest, sizeof(dest), - "%s%s/%s", BASE_PATH, - token, entries[i].name); - rename(path, dest); - } - - for (i = 0; i < 10; i += 1) { - FILE *f; - - snprintf(dest, sizeof(dest), - "%s%s/sensor%d", BASE_PATH, - token, i); - f = fopen(dest, "w"); - if (! f) break; - - fprintf(f, "%d %d %d %d\n", - sensor[i][0], - sensor[i][1], - sensor[i][2], - sensor[i][3]); - fclose(f); - } - } - - printf("Content-type: text/html\n\n"); - printf("\n"); - printf("\n"); - printf("\n"); - printf("Tank submitted\n"); - printf("

Tank submitted

\n"); - printf("

You just uploaded a tank!

\n"); - printf("

Let's hope it doesn't suck.

\n"); - printf("\n"); - - return 0; -} diff --git a/run-tanks b/run-tanks index ffaa11f..062b38d 100755 --- a/run-tanks +++ b/run-tanks @@ -64,8 +64,7 @@ cat <>$fn EOF -summary.awk $tanks > summary.html.$$ -mv summary.html.$$ summary.html +summary.awk $tanks > summary.html.$$ && mv summary.html.$$ summary.html echo "done." diff --git a/summary.awk b/summary.awk index 77fbde5..88da22b 100755 --- a/summary.awk +++ b/summary.awk @@ -1,24 +1,24 @@ #! /usr/bin/awk -f function esc(s) { - gsub(/&/, "&", s); - gsub(//, ">", s); - return 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 ""; + print ""; + print " "; + print " Dirtbags Tanks"; + print " "; + print " "; + print " "; + print "

Dirtbags Tanks

"; print "

New here? Read the introduction.

"; print "

New round every minute.

"; @@ -68,10 +68,10 @@ BEGIN { } print " "; - while (getline < ENVIRON["NAV_HTML_INC"]) { - print; - } + while (getline < ENVIRON["NAV_HTML_INC"]) { + print; + } - print " "; - print ""; + print " "; + print ""; } diff --git a/upload.cgi.c b/upload.cgi.c new file mode 100644 index 0000000..7fc0ea6 --- /dev/null +++ b/upload.cgi.c @@ -0,0 +1,303 @@ +/* + * This software has been authored by an employee or employees of Los + * Alamos National Security, LLC, operator of the Los Alamos National + * Laboratory (LANL) under Contract No. DE-AC52-06NA25396 with the U.S. + * Department of Energy. The U.S. Government has rights to use, + * reproduce, and distribute this software. The public may copy, + * distribute, prepare derivative works and publicly display this + * software without charge, provided that this Notice and any statement + * of authorship are reproduced on all copies. Neither the Government + * nor LANS makes any warranty, express or implied, or assumes any + * liability or responsibility for the use of this software. If + * software is modified to produce derivative works, such modified + * software should be clearly marked, so as not to confuse it with the + * version available from LANL. + */ + +#include +#include +#include +#include +#include +#include +#include + +char *BASE_PATH = ""; + +struct { + char *name; + size_t size; +} entries[] = { + { + "name", 20}, { + "author", 80}, { + "color", 10}, { + "program", 16384}, { + NULL, 0} +}; + + +size_t inlen; + +int +read_char() +{ + if (inlen) { + inlen -= 1; + return getchar(); + } + return EOF; +} + +char +tonum(int c) +{ + if ((c >= '0') && (c <= '9')) { + return c - '0'; + } + if ((c >= 'a') && (c <= 'f')) { + return 10 + c - 'a'; + } + if ((c >= 'A') && (c <= 'F')) { + return 10 + c - 'A'; + } + return 0; +} + +char +read_hex() +{ + int a = read_char(); + int b = read_char(); + + return tonum(a) * 16 + tonum(b); +} + +/* + * Read a key or a value. Since & and = aren't supposed to appear outside of boundaries, we can use the same function for both. + */ +size_t +read_item(char *str, size_t maxlen) +{ + int c; + size_t pos = 0; + + while (1) { + c = read_char(); + switch (c) { + case EOF: + case '=': + case '&': + str[pos] = '\0'; + return pos; + case '%': + c = read_hex(); + break; + case '+': + c = ' '; + break; + } + if (pos < maxlen - 1) { + str[pos] = c; + pos += 1; + } + } +} + +size_t +copy_item(char *filename, size_t maxlen) +{ + FILE *f; + char path[132]; + int c; + size_t pos = 0; + + snprintf(path, sizeof(path), "%s%05d.%s", BASE_PATH, getpid(), filename); + f = fopen(path, "w"); + if (!f) { + /* + * Just send it to the bit bucket + */ + maxlen = 0; + } + + while (1) { + c = read_char(); + switch (c) { + case EOF: + case '=': + case '&': + if (f) + fclose(f); + return pos; + case '%': + c = read_hex(); + break; + case '+': + c = ' '; + break; + } + if (pos < maxlen) { + fputc(c, f); + pos += 1; + } + } +} + +int +croak(int code, char *msg) +{ + int i; + char path[132]; + + for (i = 0; entries[i].name; i += 1) { + snprintf(path, sizeof(path), "%s%05d.%s", BASE_PATH, getpid(), entries[i].name); + unlink(path); + } + + printf("Status: %d %s\n", code, msg); + printf("Content-type: text/plain\n"); + printf("\n"); + printf("Error: %s\n", msg); + + return 0; +} + +int +main(int argc, char *argv[]) +{ + int sensor[10][4]; + char key[20]; + char token[40]; + size_t len; + + memset(sensor, 0, sizeof(sensor)); + token[0] = '\0'; + + BASE_PATH = getenv("BASE_PATH"); + if (!BASE_PATH) { + BASE_PATH = ""; + } + + { + char *rm = getenv("REQUEST_METHOD"); + + if (!(rm && (0 == strcmp(rm, "POST")))) { + printf("405 Method not allowed\n"); + printf("Allow: POST\n"); + printf("Content-type: text/plain\n"); + printf("\n"); + printf("Error: I only speak POST\n"); + return 0; + } + + inlen = atoi(getenv("CONTENT_LENGTH")); + } + + while (inlen) { + len = read_item(key, sizeof(key)); + if (0 == strcmp(key, "token")) { + read_item(token, sizeof(token)); + } else if ((3 == len) && ('s' == key[0])) { + /* + * sensor dealie, key = "s[0-9][rawt]" + */ + char val[5]; + int n = key[1] - '0'; + int i; + int p; + + read_item(val, sizeof(val)); + + if (!(n >= 0) && (n <= 9)) { + break; + } + i = atoi(val); + + switch (key[2]) { + case 'r': + p = 0; + break; + case 'a': + p = 1; + break; + case 'w': + p = 2; + break; + default: + p = 3; + i = (val[0] != '\0'); + break; + } + + sensor[n][p] = i; + } else { + int i; + + for (i = 0; entries[i].name; i += 1) { + if (0 == strcmp(key, entries[i].name)) { + len = copy_item(key, entries[i].size); + break; + } + } + } + } + + /* + * Sanitize token + */ + { + char *p = token; + + while (*p) { + if (!isalnum(*p)) { + *p = '_'; + } + p += 1; + } + + if ('\0' == token[0]) { + token[0] = '_'; + token[1] = '\0'; + } + } + + /* + * Move files into their directory + */ + { + char path[132]; + char dest[132]; + struct stat st; + int i; + + snprintf(path, sizeof(path), "%s%s/", BASE_PATH, token); + if (-1 == stat(path, &st)) + return croak(422, "Invalid token"); + if (!S_ISDIR(st.st_mode)) + return croak(422, "Invalid token"); + for (i = 0; entries[i].name; i += 1) { + snprintf(path, sizeof(path), "%s%05d.%s", BASE_PATH, getpid(), entries[i].name); + snprintf(dest, sizeof(dest), "%s%s/%s", BASE_PATH, token, entries[i].name); + rename(path, dest); + } + + for (i = 0; i < 10; i += 1) { + FILE *f; + + snprintf(dest, sizeof(dest), "%s%s/sensor%d", BASE_PATH, token, i); + f = fopen(dest, "w"); + if (!f) + break; + + fprintf(f, "%d %d %d %d\n", sensor[i][0], sensor[i][1], sensor[i][2], sensor[i][3]); + fclose(f); + } + } + + printf("Content-type: text/plain\n"); + printf("\n"); + printf("OK: Tank submitted!\n"); + + return 0; +}