mirror of https://github.com/dirtbags/tanks.git
Compare commits
No commits in common. "b1ab26e41ff9ef052e10281e17aab7157c1c9ab2" and "142195b5d898dd5997201ca7335a7ad0dee5b3e8" have entirely different histories.
b1ab26e41f
...
142195b5d8
|
@ -8,7 +8,6 @@ points
|
|||
forftanks
|
||||
|
||||
designer.cgi
|
||||
upload.cgi
|
||||
|
||||
forf.html
|
||||
summary.html
|
||||
|
|
25
Makefile
25
Makefile
|
@ -1,13 +1,21 @@
|
|||
DESTDIR ?= $(HOME)
|
||||
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
|
||||
|
||||
CFLAGS = -Wall
|
||||
|
||||
BINARIES = forftanks upload.cgi
|
||||
all: $(BINARIES) $(HTML)
|
||||
|
||||
all: $(BINARIES)
|
||||
install:
|
||||
install -d $(DESTDIR)/usr/bin
|
||||
install run-tanks $(DESTDIR)/usr/bin
|
||||
install forftanks $(DESTDIR)/usr/bin
|
||||
|
||||
install: $(BINARIES)
|
||||
install -d $(DESTDIR)$(PREFIX)/bin
|
||||
install $(BINARIES) $(DESTDIR)$(PREFIX)/bin
|
||||
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
|
||||
|
||||
forftanks: forftanks.o ctanks.o forf.o
|
||||
forftanks: LDLIBS = -lm
|
||||
|
@ -16,6 +24,9 @@ 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)
|
||||
rm -f $(BINARIES) $(HTML)
|
||||
|
|
59
README.md
59
README.md
|
@ -8,68 +8,15 @@ 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)
|
||||
|
||||
|
||||
Current Maintainer
|
||||
=====
|
||||
Author
|
||||
------
|
||||
|
||||
Neale Pickett <neale@woozle.org>
|
||||
|
|
6
ctanks.h
6
ctanks.h
|
@ -1,4 +1,5 @@
|
|||
#pragma once
|
||||
#ifndef __CTANKS_H__
|
||||
#define __CTANKS_H__
|
||||
|
||||
/* τ = 2π */
|
||||
#define TAU 6.28318530717958647692
|
||||
|
@ -106,3 +107,6 @@ 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__ */
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<li>Paul Ferrell: Initial implementation</li>
|
||||
<li>Aaron McPhall: Math optimizations</li>
|
||||
<li>Nick Moffitt: Suggestion to use FORTH-like language</li>
|
||||
<li>Anna Glasgall: C Forf prototype</li>
|
||||
<li>Adam Glasgall: C Forf prototype</li>
|
||||
<li>Adam Thomas: Various patches</li>
|
||||
<li>Various Australians: Creation of some mind-blowing tanks</li>
|
||||
<li>Alyssa Milburn: JS Tanks, Tank Debugger, many more other helpful contributions</li>
|
||||
|
|
|
@ -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 Anna Glasgall wrote a C implementation, which was quick.
|
||||
and then Adam 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
|
||||
|
|
|
@ -6,7 +6,7 @@ title: thanks
|
|||
* Paul Ferrell: Initial implementation
|
||||
* Aaron McPhall: Math optimizations
|
||||
* Nick Moffitt: Suggestion to use FORTH-like language
|
||||
* Anna Glasgall: C Forf prototype
|
||||
* Adam 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
4
dump.h
|
@ -1,4 +1,4 @@
|
|||
#pragma once
|
||||
#ifndef __DUMP_H__
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
|
@ -28,3 +28,5 @@
|
|||
#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
5
forf.h
|
@ -1,4 +1,5 @@
|
|||
#pragma once
|
||||
#ifndef __FORF_H__
|
||||
#define __FORF_H__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
|
@ -145,3 +146,5 @@ int forf_eval_once(struct forf_env *env);
|
|||
|
||||
/** Evaluate the entire command stack */
|
||||
int forf_eval(struct forf_env *env);
|
||||
|
||||
#endif
|
||||
|
|
148
forftanks.c
148
forftanks.c
|
@ -368,11 +368,38 @@ ft_read_tank(struct forftank *ftank,
|
|||
void
|
||||
print_header(FILE *f,
|
||||
struct tanks_game *game,
|
||||
int seed)
|
||||
struct forftank *forftanks,
|
||||
struct tank *tanks,
|
||||
int ntanks)
|
||||
{
|
||||
fprintf(f, "{\n");
|
||||
fprintf(f, " \"seed\": %d,\n", seed);
|
||||
fprintf(f, " \"field\": [%d,%d],\n", (int)game->size[0], (int)game->size[1]);
|
||||
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");
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -381,31 +408,24 @@ print_rounds(FILE *f,
|
|||
struct tank *tanks,
|
||||
int ntanks)
|
||||
{
|
||||
int i;
|
||||
int alive;
|
||||
|
||||
fprintf(f, " \"rounds\": [\n");
|
||||
|
||||
/* Run rounds */
|
||||
alive = ntanks;
|
||||
for (int i = 0; (alive > 1) && (i < ROUNDS); i += 1) {
|
||||
if (i > 0) {
|
||||
fprintf(f, ",\n");
|
||||
}
|
||||
for (i = 0; (alive > 1) && (i < ROUNDS); i += 1) {
|
||||
int j;
|
||||
|
||||
tanks_run_turn(game, tanks, ntanks);
|
||||
fprintf(f, " [");
|
||||
fprintf(f, "[\n");
|
||||
alive = ntanks;
|
||||
for (int j = 0; j < ntanks; j += 1) {
|
||||
for (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);
|
||||
|
@ -421,7 +441,7 @@ print_rounds(FILE *f,
|
|||
alive -= 1;
|
||||
flags |= 4;
|
||||
}
|
||||
fprintf(f, "[%d,%d,%.2f,%.2f,%d,%d]",
|
||||
fprintf(f, " [%d,%d,%.2f,%.2f,%d,%d],\n",
|
||||
(int)t->position[0],
|
||||
(int)(t->position[1]),
|
||||
t->angle,
|
||||
|
@ -429,10 +449,8 @@ print_rounds(FILE *f,
|
|||
flags,
|
||||
sensors);
|
||||
}
|
||||
fprintf(f, "]");
|
||||
fprintf(f, "],\n");
|
||||
}
|
||||
|
||||
fprintf(f, "\n ],\n");
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -441,56 +459,18 @@ print_standings(FILE *f,
|
|||
struct tank *tanks,
|
||||
int ntanks)
|
||||
{
|
||||
int i;
|
||||
|
||||
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;
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -503,7 +483,6 @@ main(int argc, char *argv[])
|
|||
int order[MAX_TANKS];
|
||||
int ntanks = 0;
|
||||
int i;
|
||||
int seed;
|
||||
|
||||
lenv[0].name = NULL;
|
||||
lenv[0].proc = NULL;
|
||||
|
@ -516,18 +495,14 @@ main(int argc, char *argv[])
|
|||
/* We only need slightly random numbers */
|
||||
{
|
||||
char *s = getenv("SEED");
|
||||
seed = atoi(s?s:"");
|
||||
int seed = atoi(s?s:"");
|
||||
|
||||
if (! seed) {
|
||||
seed = getpid();
|
||||
}
|
||||
|
||||
srand(seed);
|
||||
}
|
||||
|
||||
if ((argc < 2) || (argv[1][0] == '-')) {
|
||||
fprintf(stderr, "usage: %s TANKDIR [TANKDIR...]\n", argv[0]);
|
||||
return 1;
|
||||
fprintf(stdout, "// SEED=%d\n", seed);
|
||||
}
|
||||
|
||||
/* Every argument is a tank directory */
|
||||
|
@ -540,6 +515,11 @@ 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;
|
||||
|
@ -586,10 +566,24 @@ main(int argc, char *argv[])
|
|||
}
|
||||
}
|
||||
|
||||
print_header(stdout, &game, seed);
|
||||
print_header(stdout, &game, myftanks, mytanks, ntanks);
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
#! /usr/bin/awk -f
|
||||
|
||||
BEGIN {
|
||||
FS = "\t";
|
||||
}
|
||||
|
||||
function esc(s) {
|
||||
gsub(/&/, "&", 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] = "<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>";
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
#! /usr/bin/awk -f
|
||||
|
||||
function esc(s) {
|
||||
gsub(/&/, "&", s);
|
||||
gsub(/</, "<", s);
|
||||
gsub(/>/, ">", 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>";
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
#! /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];
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue