diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..ffce85f --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +contrib +docs +rounds +tanks diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d004db5 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ +FROM golang:1.22 +WORKDIR /app +COPY *.go *.c *.h Makefile www /app +RUN make +RUN mkdir rounds +COPY examples /app/tanks +ENTRYPOINT [ "./tanksd" ] diff --git a/README.md b/README.md index bbe9c54..5109268 100644 --- a/README.md +++ b/README.md @@ -4,69 +4,63 @@ Tanks Dirtbags Tanks is a game in which you pit your coding abilities against other hackers. You write a program for your tank, set it out on the battlefield, and watch how your program fares against tanks -written by other players. Dirtbags Tanks is frequently a component of -[Dirtbags Capture The Flag](https://dirtbags.github.io/contest/). +written by other players. Running it +======== + + $ make # build source code + $ mkdir rounds # set up storage + $ cp -r examples tanks # install some tanks + $ mkdir tanks/mytank # provision your own tank + $ ./tanksd + +At this point you can connect to http://localhost:8080/ and watch all the +built-in tanks destroy your motionless tank. +You can work on your tank with the token `mytank`. + + +Administering a server +================== + +The `tanks` directory +---------------- +The `tanks` directory has tank definitions. +Each subdirectory is a token that can be used to upload a tank. +Empty subdirectories are okay. + +If you were running a 9-person event, +and had a Bash shell, +you could run something like this to set up `tanks` subdirectories: + + $ cd tanks + $ for i in $(seq 9); do mkdir $(printf "%04x$04x" $RANDOM $RANDOM); done + +`forftanks` uses the inode of each tank subdirectory +to uniquely identify the tank. +So if you want to change somebody's token, +you should `mv` the subdirectory. +If you `cp` it, or remove it and create a new one, +the scoreboard won't know it's the same tank. + + +The `rounds` directory +----------------- + +The `rounds` directory has internal state, +containing an `index.json` file, +and a bunch of game files. +You can delete individual games if you need to, for some reason; +tanksd will notice your change when it runs the next game. + + +More Documentation ============ - 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/) +* [Homepage](https://dirtbags.net/tanks/) * [History](docs/history.md) -* [Running](docs/running.md) +* [Thanks](docs/thanks.md) Current Maintainer diff --git a/examples/easy/berzerker/author b/examples/easy-brick/author similarity index 100% rename from examples/easy/berzerker/author rename to examples/easy-brick/author diff --git a/examples/easy-brick/color b/examples/easy-brick/color new file mode 100644 index 0000000..fa6adc7 --- /dev/null +++ b/examples/easy-brick/color @@ -0,0 +1 @@ +#ccffff diff --git a/examples/easy/brick/name b/examples/easy-brick/name similarity index 100% rename from examples/easy/brick/name rename to examples/easy-brick/name diff --git a/examples/easy-brick/program b/examples/easy-brick/program new file mode 100644 index 0000000..24a2734 --- /dev/null +++ b/examples/easy-brick/program @@ -0,0 +1 @@ +( This tank just sits around! ) diff --git a/examples/easy/brick/author b/examples/easy-rabbitwithgun/author similarity index 100% rename from examples/easy/brick/author rename to examples/easy-rabbitwithgun/author diff --git a/examples/easy-rabbitwithgun/color b/examples/easy-rabbitwithgun/color new file mode 100644 index 0000000..1b77fdd --- /dev/null +++ b/examples/easy-rabbitwithgun/color @@ -0,0 +1 @@ +#ffccff diff --git a/examples/easy/rabbitwithgun/name b/examples/easy-rabbitwithgun/name similarity index 100% rename from examples/easy/rabbitwithgun/name rename to examples/easy-rabbitwithgun/name diff --git a/examples/easy/rabbitwithgun/program b/examples/easy-rabbitwithgun/program similarity index 100% rename from examples/easy/rabbitwithgun/program rename to examples/easy-rabbitwithgun/program diff --git a/examples/easy/rabbitwithgun/sensor0 b/examples/easy-rabbitwithgun/sensor0 similarity index 100% rename from examples/easy/rabbitwithgun/sensor0 rename to examples/easy-rabbitwithgun/sensor0 diff --git a/examples/easy/rabbitwithgun/sensor1 b/examples/easy-rabbitwithgun/sensor1 similarity index 100% rename from examples/easy/rabbitwithgun/sensor1 rename to examples/easy-rabbitwithgun/sensor1 diff --git a/examples/easy/rabbitwithgun/author b/examples/easy-twit/author similarity index 100% rename from examples/easy/rabbitwithgun/author rename to examples/easy-twit/author diff --git a/examples/easy-twit/color b/examples/easy-twit/color new file mode 100644 index 0000000..cf2e147 --- /dev/null +++ b/examples/easy-twit/color @@ -0,0 +1 @@ +#ffffcc diff --git a/examples/easy-twit/name b/examples/easy-twit/name new file mode 100644 index 0000000..225046e --- /dev/null +++ b/examples/easy-twit/name @@ -0,0 +1 @@ +Twit diff --git a/examples/easy/berzerker/program b/examples/easy-twit/program similarity index 100% rename from examples/easy/berzerker/program rename to examples/easy-twit/program diff --git a/examples/easy/berzerker/name b/examples/easy/berzerker/name deleted file mode 100644 index 16db0a1..0000000 --- a/examples/easy/berzerker/name +++ /dev/null @@ -1 +0,0 @@ -berzerker diff --git a/examples/easy/brick/program b/examples/easy/brick/program deleted file mode 100644 index bd33719..0000000 --- a/examples/easy/brick/program +++ /dev/null @@ -1 +0,0 @@ -( This program shouldn't even parse \ No newline at end of file diff --git a/examples/medium/simpleton/author b/examples/medium-simpleton/author similarity index 100% rename from examples/medium/simpleton/author rename to examples/medium-simpleton/author diff --git a/examples/medium-simpleton/color b/examples/medium-simpleton/color new file mode 100644 index 0000000..58297af --- /dev/null +++ b/examples/medium-simpleton/color @@ -0,0 +1 @@ +#ccccff diff --git a/examples/medium/simpleton/name b/examples/medium-simpleton/name similarity index 100% rename from examples/medium/simpleton/name rename to examples/medium-simpleton/name diff --git a/examples/medium/simpleton/program b/examples/medium-simpleton/program similarity index 100% rename from examples/medium/simpleton/program rename to examples/medium-simpleton/program diff --git a/examples/medium/simpleton/sensor0 b/examples/medium-simpleton/sensor0 similarity index 100% rename from examples/medium/simpleton/sensor0 rename to examples/medium-simpleton/sensor0 diff --git a/examples/medium/simpleton/sensor1 b/examples/medium-simpleton/sensor1 similarity index 100% rename from examples/medium/simpleton/sensor1 rename to examples/medium-simpleton/sensor1 diff --git a/examples/medium/sittingduckwithteeth/author b/examples/medium-sittingduckwithteeth/author similarity index 100% rename from examples/medium/sittingduckwithteeth/author rename to examples/medium-sittingduckwithteeth/author diff --git a/examples/medium-sittingduckwithteeth/color b/examples/medium-sittingduckwithteeth/color new file mode 100644 index 0000000..225ad78 --- /dev/null +++ b/examples/medium-sittingduckwithteeth/color @@ -0,0 +1 @@ +#ffcccc diff --git a/examples/medium/sittingduckwithteeth/name b/examples/medium-sittingduckwithteeth/name similarity index 100% rename from examples/medium/sittingduckwithteeth/name rename to examples/medium-sittingduckwithteeth/name diff --git a/examples/medium/sittingduckwithteeth/program b/examples/medium-sittingduckwithteeth/program similarity index 100% rename from examples/medium/sittingduckwithteeth/program rename to examples/medium-sittingduckwithteeth/program diff --git a/examples/medium/sittingduckwithteeth/sensor0 b/examples/medium-sittingduckwithteeth/sensor0 similarity index 100% rename from examples/medium/sittingduckwithteeth/sensor0 rename to examples/medium-sittingduckwithteeth/sensor0 diff --git a/examples/medium/sittingduckwithteeth/sensor1 b/examples/medium-sittingduckwithteeth/sensor1 similarity index 100% rename from examples/medium/sittingduckwithteeth/sensor1 rename to examples/medium-sittingduckwithteeth/sensor1 diff --git a/examples/medium/sittingduckwithteeth/sensor2 b/examples/medium-sittingduckwithteeth/sensor2 similarity index 100% rename from examples/medium/sittingduckwithteeth/sensor2 rename to examples/medium-sittingduckwithteeth/sensor2 diff --git a/examples/medium/sweeper/author b/examples/medium-sweeper/author similarity index 100% rename from examples/medium/sweeper/author rename to examples/medium-sweeper/author diff --git a/examples/medium-sweeper/color b/examples/medium-sweeper/color new file mode 100644 index 0000000..036bd6b --- /dev/null +++ b/examples/medium-sweeper/color @@ -0,0 +1 @@ +#ccffcc diff --git a/examples/medium/sweeper/name b/examples/medium-sweeper/name similarity index 100% rename from examples/medium/sweeper/name rename to examples/medium-sweeper/name diff --git a/examples/medium/sweeper/program b/examples/medium-sweeper/program similarity index 100% rename from examples/medium/sweeper/program rename to examples/medium-sweeper/program diff --git a/examples/medium/sweeper/sensor0 b/examples/medium-sweeper/sensor0 similarity index 100% rename from examples/medium/sweeper/sensor0 rename to examples/medium-sweeper/sensor0 diff --git a/examples/medium/sweeper/sensor1 b/examples/medium-sweeper/sensor1 similarity index 100% rename from examples/medium/sweeper/sensor1 rename to examples/medium-sweeper/sensor1 diff --git a/examples/medium/sweeper/sensor2 b/examples/medium-sweeper/sensor2 similarity index 100% rename from examples/medium/sweeper/sensor2 rename to examples/medium-sweeper/sensor2 diff --git a/examples/medium/sweeper/sensor3 b/examples/medium-sweeper/sensor3 similarity index 100% rename from examples/medium/sweeper/sensor3 rename to examples/medium-sweeper/sensor3 diff --git a/forftanks.c b/forftanks.c index 34a40f8..e6fd916 100644 --- a/forftanks.c +++ b/forftanks.c @@ -275,6 +275,19 @@ void ft_read_sensors(struct tank *tank, char *path) { } } +// Crudely clean a string for json output +void clean(char *s) { + for (; *s; s++) { + switch (*s) { + case '\0' ... '\037': + case '"': + case '\\': + *s = '\0'; + return; + } + } +} + int ft_read_tank(struct forftank *ftank, struct tank *tank, struct forf_lexical_env *lenv, char *path) { int ret; @@ -296,6 +309,7 @@ int ft_read_tank(struct forftank *ftank, struct tank *tank, if (!ret) { snprintf(ftank->name, sizeof(ftank->name), "i:%x", ftank->uid); } + clean(ftank->name); /* What is your quest? */ ft_tank_init(ftank, tank, lenv); @@ -312,6 +326,7 @@ int ft_read_tank(struct forftank *ftank, struct tank *tank, if (!ret) { strncpy(ftank->color, "#808080", sizeof(ftank->color)); } + clean(ftank->color); return 1; } diff --git a/www/replay.mjs b/www/replay.mjs index 89ec7c0..abbb7fe 100644 --- a/www/replay.mjs +++ b/www/replay.mjs @@ -122,7 +122,6 @@ class Replay { } let tbody = this.stats.querySelector("tbody") - console.log(this.stats, tbody) tbody.replaceChildren() let byKills = Object.keys(TankNames) @@ -177,7 +176,6 @@ class Replay { let game = this.games[fn] this.player.load(game) - console.log(fn) this.dateOutput.value = dateOfFn(fn) diff --git a/www/tank.mjs b/www/tank.mjs index acf7f33..eac797e 100644 --- a/www/tank.mjs +++ b/www/tank.mjs @@ -55,6 +55,8 @@ export class Tank { this.ctx.translate(this.x, this.y) this.ctx.rotate(this.rotation) + // If the cannon was fired in the same turn we were shot, + // render the laser for one frame. if (this.fire == 5) { this.ctx.save() this.ctx.rotate(this.turret) @@ -145,7 +147,7 @@ export class Tank { } draw_cannon() { - this.ctx.fillStyle = ("hsl(0, 100%, 100%, " + this.fire/5 + ")") + this.ctx.fillStyle = ("hsl(0, 100%, 50%, " + this.fire/5 + ")") this.ctx.fillRect(0, -1, 45, 2) }