Simplify setup

This commit is contained in:
Neale Pickett 2024-12-05 15:36:41 -07:00
parent 4fd9aa541b
commit 144d988e5c
43 changed files with 88 additions and 62 deletions

4
.dockerignore Normal file
View File

@ -0,0 +1,4 @@
contrib
docs
rounds
tanks

7
Dockerfile Normal file
View File

@ -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" ]

108
README.md
View File

@ -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

View File

@ -0,0 +1 @@
#ccffff

View File

@ -0,0 +1 @@
( This tank just sits around! )

View File

@ -0,0 +1 @@
#ffccff

1
examples/easy-twit/color Normal file
View File

@ -0,0 +1 @@
#ffffcc

1
examples/easy-twit/name Normal file
View File

@ -0,0 +1 @@
Twit

View File

@ -1 +0,0 @@
berzerker

View File

@ -1 +0,0 @@
( This program shouldn't even parse

View File

@ -0,0 +1 @@
#ccccff

View File

@ -0,0 +1 @@
#ffcccc

View File

@ -0,0 +1 @@
#ccffcc

View File

@ -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;
}

View File

@ -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)

View File

@ -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)
}