From 8ea2ff7dda03ccfb85af1524d7dd07d091b738e3 Mon Sep 17 00:00:00 2001 From: Neale Pickett Date: Thu, 5 Dec 2024 10:49:03 -0700 Subject: [PATCH] It broke the web site. Revert "Clean up docs. I hope this doesn't break the web site." This reverts commit a2c4164a65d6a7233e9f37da89e7fd66ad0ec3ed. --- docs/TODO.md | 8 - docs/_config.yml | 6 + docs/_layouts/default.html | 29 + docs/_site/TODO.html | 36 + docs/_site/assets/css/dirtbags.css | 151 +++ docs/_site/assets/images/grunge.png | Bin 0 -> 6761 bytes docs/_site/assets/js/figures.js | 5 + docs/_site/assets/js/tanks.js | 310 +++++ docs/_site/history.html | 117 ++ docs/_site/index.html | 70 + docs/_site/running.html | 89 ++ docs/_site/standie.svg | 260 ++++ docs/_site/thanks.html | 37 + docs/_site/token.svg | 1897 +++++++++++++++++++++++++++ docs/assets/css/dirtbags.css | 151 +++ docs/assets/images/grunge.png | Bin 0 -> 6761 bytes docs/assets/js/figures.js | 5 + docs/assets/js/tanks.js | 310 +++++ docs/forf.html.m4 | 13 + docs/history.md | 19 +- docs/intro.html.m4 | 147 +++ docs/intro.md | 72 - docs/nav.html.inc | 10 + docs/procs.html.m4 | 74 ++ docs/procs.md | 47 - docs/running.md | 73 ++ 26 files changed, 3808 insertions(+), 128 deletions(-) create mode 100644 docs/_config.yml create mode 100644 docs/_layouts/default.html create mode 100644 docs/_site/TODO.html create mode 100644 docs/_site/assets/css/dirtbags.css create mode 100644 docs/_site/assets/images/grunge.png create mode 100644 docs/_site/assets/js/figures.js create mode 100644 docs/_site/assets/js/tanks.js create mode 100644 docs/_site/history.html create mode 100644 docs/_site/index.html create mode 100644 docs/_site/running.html create mode 100644 docs/_site/standie.svg create mode 100644 docs/_site/thanks.html create mode 100644 docs/_site/token.svg create mode 100644 docs/assets/css/dirtbags.css create mode 100644 docs/assets/images/grunge.png create mode 100644 docs/assets/js/figures.js create mode 100644 docs/assets/js/tanks.js create mode 100644 docs/forf.html.m4 create mode 100644 docs/intro.html.m4 delete mode 100644 docs/intro.md create mode 100644 docs/nav.html.inc create mode 100644 docs/procs.html.m4 delete mode 100644 docs/procs.md create mode 100644 docs/running.md diff --git a/docs/TODO.md b/docs/TODO.md index e80c02b..6fd40ff 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -10,13 +10,5 @@ Requests ======== * Merge devdsp fixes (https://github.com/devdsp/tanks/commits/master) - -Done -==== - * Fix leaderboard - -Won't Fix -======= - * Read sensors from "sensors" and not "sensor[0-9]" diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 0000000..199bf78 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1,6 @@ +defaults: + - + scope: + path: "" + values: + layout: "default" diff --git a/docs/_layouts/default.html b/docs/_layouts/default.html new file mode 100644 index 0000000..94e08b5 --- /dev/null +++ b/docs/_layouts/default.html @@ -0,0 +1,29 @@ + + + + + + + +{% if page.scripts %}{% for js_file in page.scripts %} + +{% endfor %}{% endif %} + {{ site.title | default: site.github.repository_name }} by {{ site.github.owner_name }} + + + +

{{ page.title | default: site.title }}

+ +{{ content }} + + + + diff --git a/docs/_site/TODO.html b/docs/_site/TODO.html new file mode 100644 index 0000000..af7313a --- /dev/null +++ b/docs/_site/TODO.html @@ -0,0 +1,36 @@ + + + + + + + + + by + + + +

To-Do List

+ +

BUGS

+ +

Requests

+ + + + + + + diff --git a/docs/_site/assets/css/dirtbags.css b/docs/_site/assets/css/dirtbags.css new file mode 100644 index 0000000..6a86e06 --- /dev/null +++ b/docs/_site/assets/css/dirtbags.css @@ -0,0 +1,151 @@ +/**** document ****/ + +html { + background: #eed url(/assets/images/grunge.png) repeat-x; +} + +body { + font-family: sans-serif; + color: #112; + padding: 10px; + max-width: 700px; +} + +/**** heading ****/ + +#title { + text-transform: lowercase; + font-size: 1.6em; + padding: 3px; + margin: 0 0 1em 2em; +} + +#title:before { + color: #999; + letter-spacing: -0.1em; + content: "Dirtbags: "; +} + +/*** left side bar ***/ + +nav h2 { + display: none; +} + +nav ul { + list-style: none; + text-align: center; +} + +nav li { + display: inline; +} + +nav li a { + text-transform: lowercase; + font-size: 0.9em; +} + +nav li + li:before { + content: " | "; +} + + +/**** body ****/ + +a img { + border: 0px; +} + +a { + text-decoration: none; + color: #2a4; + font-weight: bold; +} + +a:hover { + color: #ddc; + background: #2a4; +} + + +h1, h2, h3 { + color: #464; + letter-spacing: -0.05em; +} + +.readme { + background-color: #ccc; + margin: 1em; +} + +pre { + background-color: #ccc; + border: solid #888 2px; + padding: 0.25em; +} + + +th { + vertical-align: top; + text-align: center; +} +td { + vertical-align: top; + text-align: right; +} + +p { + line-height: 1.4em; + margin-bottom: 20px; +} + +hr { + border: 1px solid #444; +} + +dt { + white-space: pre; + background-color: #333; + padding: 5px; + border: 2px solid green; + border-bottom: none; + font-weight: bold; +} + +dd { + border: 2px solid green; + margin: 0px; + padding: 5px; + background-color: #282828; +} + + +/**** special cases ****/ + +.wide { + max-width: inherit; +} + +.figure { + margin: 0.5em 1em; + float: right; + font-size: small; + text-align: center; +} + +.scoreboard { + background: #222; +} + +.scoreboard td { + height: 400px; +} + +#battlefield { + border: 2px solid green; +} + +.solved { + text-decoration: line-through; +} diff --git a/docs/_site/assets/images/grunge.png b/docs/_site/assets/images/grunge.png new file mode 100644 index 0000000000000000000000000000000000000000..9a8c41a59bdff7795373467cb677a9a49cf8b04e GIT binary patch literal 6761 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 A maxlen) { + maxlen = s[0]; + } + } + } + + // Set up our state, for later interleaved draw requests + this.set_state = function(x, y, rotation, turret, flags, sensor_state) { + this.x = x; + this.y = y; + this.rotation = rotation; + this.turret = turret; + if (flags & 1) { + this.fire = 5; + } + this.led = flags & 2; + this.dead = flags & 4; + this.sensor_state = sensor_state; + } + + this.draw_crater = function() { + if (!this.dead) { + return; + } + + var points = 7; + var angle = Math.PI / points; + + ctx.save(); + ctx.translate(this.x, this.y); + ctx.rotate(this.rotation); + + if (this.fire == 5) { + ctx.save(); + ctx.rotate(this.turret); + // one frame of cannon fire + this.draw_cannon(); + this.fire = 0; + ctx.restore(); + } + + ctx.lineWidth = 2; + ctx.strokeStyle = craterStroke; + ctx.fillStyle = craterFill; + ctx.beginPath(); + ctx.moveTo(12, 0); + for (i = 0; i < points; i += 1) { + ctx.rotate(angle); + ctx.lineTo(6, 0); + ctx.rotate(angle); + ctx.lineTo(12, 0); + } + ctx.closePath() + ctx.stroke(); + ctx.fill(); + + ctx.restore(); + } + + this.draw_sensors = function() { + if (this.dead) { + return; + } + ctx.save(); + ctx.translate(this.x, this.y); + ctx.rotate(this.rotation); + + ctx.lineWidth = 1; + for (i in this.sensors) { + var s = this.sensors[i]; + var adj = this.turret * s[3]; + + if (this.sensor_state & (1 << i)) { + // Sensor is triggered + ctx.strokeStyle = "#000"; + } else { + ctx.strokeStyle = sensorStroke; + } + ctx.beginPath(); + ctx.moveTo(0, 0); + ctx.arc(0, 0, s[0], s[1] + adj, s[2] + adj, false); + ctx.closePath(); + ctx.stroke(); + } + + ctx.restore(); + } + + this.draw_tank = function() { + if (this.dead) { + return; + } + ctx.save(); + ctx.translate(this.x, this.y); + ctx.rotate(this.rotation); + + ctx.fillStyle = color; + ctx.fillRect(-5, -4, 10, 8); + ctx.fillStyle = "#777"; + ctx.fillRect(-7, -9, 15, 5); + ctx.fillRect(-7, 4, 15, 5); + ctx.rotate(this.turret); + if (this.fire) { + this.draw_cannon(); + this.fire -= 1; + } else { + if (this.led) { + ctx.fillStyle = "#f00"; + } else { + ctx.fillStyle = "#000"; + } + ctx.fillRect(0, -1, 10, 2); + } + + ctx.restore(); + } + + this.draw_cannon = function() { + ctx.fillStyle = ("rgba(255,255,64," + this.fire/5 + ")"); + ctx.fillRect(0, -1, 45, 2); + } + + this.draw_wrap_sensors = function() { + var orig_x = this.x; + var orig_y = this.y; + for (x = this.x - width; x < width + maxlen; x += width) { + for (y = this.y - height; y < height + maxlen; y += height) { + if ((-maxlen < x) && (x < width + maxlen) && + (-maxlen < y) && (y < height + maxlen)) { + this.x = x; + this.y = y; + this.draw_sensors(); + } + } + } + this.x = orig_x; + this.y = orig_y; + } +} + +var loop_id; +var updateFunc = null; +function togglePlayback() { + if ($("#playing").prop("checked")) { + loop_id = setInterval(updateFunc, 66); + } else { + clearInterval(loop_id); + loop_id = null; + } + $("#pauselabel").toggleClass("ui-icon-play ui-icon-pause"); +} + +function start(id, game) { + var canvas = document.getElementById(id); + var ctx = canvas.getContext('2d'); + + canvas.width = game[0][0]; + canvas.height = game[0][1]; + // game[2] is tank descriptions + var turns = game[2]; + + // Set up tanks + var tanks = new Array(); + for (i in game[1]) { + var desc = game[1][i]; + tanks[i] = new Tank(ctx, game[0][0], game[0][1], desc[0], desc[1]); + } + + var frame = 0; + var lastframe = 0; + var fps = document.getElementById('fps'); + + function update_fps() { + fps.innerHTML = (frame - lastframe); + lastframe = frame; + } + + function drawFrame(idx) { + canvas.width = canvas.width; + turn = turns[idx]; + + // Update and draw craters first + for (i in turn) { + t = turn[i]; + if (!t) { + // old data, force-kill it + tanks[i].fire = 0; + tanks[i].dead = 5; + } else { + tanks[i].set_state(t[0], t[1], t[2], t[3], t[4], t[5]); + } + tanks[i].draw_crater(); + } + // Then sensors + for (i in turn) { + tanks[i].draw_wrap_sensors(); + } + // Then tanks + for (i in turn) { + tanks[i].draw_tank() + } + + document.getElementById('frameid').innerHTML = idx; + } + + function update() { + var idx = frame % (turns.length + 20); + var turn; + + frame += 1; + if (idx >= turns.length) { + return; + } + + drawFrame(idx); + + $('#seekslider').slider('value', idx); + } + + function seekToFrame(newidx) { + var idx = frame % (turns.length + 20); + if (idx !== newidx) { + frame = newidx; + drawFrame(newidx); + } + // make sure we're paused + if ($("#playing").prop("checked")) { + $("#playing").prop("checked", false); + togglePlayback(); + } + } + + updateFunc = update; + loop_id = setInterval(update, 66); + //loop_id = setInterval(update, 400); + if (fps) { + setInterval(update_fps, 1000); + } + + if (id === "battlefield") { + $("#game_box").append('

0

'); + $('#playing').button(); + var slider = $('#seekslider'); + slider.slider({ max: turns.length-1, slide: function(event, ui) { seekToFrame(ui.value); } }); + + var spacing = 100 / turns.length; + var deaths = []; + for (i in turns[0]) { + deaths.push(false); + } + var percent = 0; + for (var f = 0; f < turns.length; f++) { + var turn = turns[f]; + if (percent < (spacing * f)) { + percent = spacing * f; + } + for (var i = 0; i < turn.length; i++) { + if (deaths[i]) { continue; } + if (!turn[i] || (turn[i][4] & 4)) { + deaths[i] = true; + // http://stackoverflow.com/questions/8648963/add-tick-marks-to-jquery-slider + $('').css('left', percent + '%').css('background-color', game[1][i][0]).appendTo(slider); + percent++; + break; + } + } + } + } +} + diff --git a/docs/_site/history.html b/docs/_site/history.html new file mode 100644 index 0000000..58284ab --- /dev/null +++ b/docs/_site/history.html @@ -0,0 +1,117 @@ + + + + + + + + + + + + + by + + + +

History

+ +

This is a port of the “Tanks” program written by Paul Ferrell +pflarr@clanspum.net in 2009-2010. Paul created the entire game based +off a brief description I provided him of +Crobots +and a vague desire to +“make something fun for high school kids to learn some programming.” We +ran Paul’s Tanks as part of a 100-attendee computer security contest in +February of 2010 and by all accounts it was a huge success. It even +made the nightly news.

+ +

Paul’s version was written in Python and provided a custom language +called “Bullet”, which looked like this:

+ +
>addsensor(50, 0, 5, 1);        # 0
+>addsensor(100, 90, 150, 1);    # 1
+>addsensor(100, 270, 150, 1);   # 2
+>addsensor(100, 0, 359, 0);     # 3
+
+# Default movement if nothing is detected
+            : move(70, 70) . turretccw();
+random(2, 3): move(40, 70) . turretccw();
+random(1, 3): move(70, 40) . turretccw();
+
+# We found something!!
+sense(3): move(0, 0);
+sense(1): turretcw();
+sense(2): turretccw();
+sense(0): fire();
+
+
+ + + + + + +
"Chashtank" cleans up.
+ + +
+ +

Nick Moffitt played with this original version and convinced me (Neale) +that something like Forth would be a better language. I added some code +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.

+ +

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 +240 MHz MIPS CPU, 16MB RAM, and a 4MB flash disk, along with an +802.11b/g radio, 4-port 10/100 switch, and an additional 10/100 “uplink” +port; it’s sold as a home wireless router. I had originally intended to +run it off a lantern battery just for fun, but eventually thought better +of it: doing so would be wasteful for no good reason.

+ +

Aaron McPhall amcphall@mcphall.org, my summer intern at the time, got +OpenWRT and Python onto the box and benchmarked it at about 60 seconds +for a 16-tank game, after he had profiled the code and optimized a lot +of the math. That wasn’t bad, it meant we could run a reasonably-sized +game at one turn per minute, which we knew from past experience was +about the right rate. But it required a USB thumb drive to hold Python, +and when we used the Python Forf implementation, the run-time shot up to +several minutes. I began this C port while Adam Glasgall, another fool +on the previously-mentioned IRC channel, started work on a C version of +a Forf interpreter.

+ +

This C version with Forf runs about 6 times faster than the Python +version with Bullet, on the WL-500gU. A 1GHz Intel Atom runs a 16-tank +game in about 0.2 seconds.

+ +

What’s so great about Forf?

+ +

Nothing’s great about Forf. It’s a crummy language that only does +integer math. For this application it’s a good choice, for the +following reasons:

+ +
    +
  • No library dependencies, not even malloc
  • +
  • Runs in fixed size memory
  • +
  • Not Turing-complete, I think (impossible to make endless loops)
  • +
  • Lends itself to genetic algorithms
  • +
+ + + + + diff --git a/docs/_site/index.html b/docs/_site/index.html new file mode 100644 index 0000000..5852315 --- /dev/null +++ b/docs/_site/index.html @@ -0,0 +1,70 @@ + + + + + + + + + + + + + by + + + +

Tanks

+ + + + +
"Ant Lion" nails "Rabbit With Gun".
+ + +
+ +

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.

+ +

Each tank has a turret-mounted laser, two treads, up to ten sensors, +and a diagnostic LED. Sensors are used to detect when other tanks are +inside a given arc. In the examples on this page, “triggered” sensors +turn black. Most tanks will take some action if a sensor is +triggered, such as changing speed of the treads, turning the turret, +or firing.

+ +

Tanks are programmed in Forf, a stack-based language similar to +PostScript.

+ +

Other Versions

+ +

Everybody loves tanks! +If you’ve made a port or done something fun with it, +please let me know and I’ll link it here!

+ +
    +
  • Class Lab at New Mexico Tech, by Russell White
  • +
+ +

Author

+ +

Neale Pickett neale@woozle.org

+ + + + + diff --git a/docs/_site/running.html b/docs/_site/running.html new file mode 100644 index 0000000..67a9e6d --- /dev/null +++ b/docs/_site/running.html @@ -0,0 +1,89 @@ + + + + + + + + + by + + + +

Running Tanks

+ +

Unfortunately, it’s kind of a mess right now. +I know there are a few forks of this code, +and I would love it if someone proposed a merge to clearly illustrate how to run tanks.

+ +

When I spin up a new tanks game, +typically I run something like:

+ +
while sleep 60; do ./round.sh */; done
+
+
+ +

This assumes all your tanks directories are in the same place as round.sh.

+ +

Included programs

+ +

I tried to stick with the Unix philosophy of one program per task. I +also tried to avoid doing any string processing in C. The result is a +hodgepodge of C, Bourne shell, and awk, but at least each piece is +fairly simple to audit.

+ +

round.sh tank1 tank2 …

+ +

Runs a single round, awards points with rank.awk, and creates a new +summary.html with summary.awk. This is the main interface that you want +to run from cron or whatever.

+ +

forftanks tank1 tank2 …

+ +

A program to run a round of tanks and output a JSON description of the +game. This is what tanks.js uses to render a game graphically. +The object printed contains:

+ +
[[game-width, game-height],
+ [[tank1-color, 
+  [[sensor1range, sensor1angle, sensor1width, sensor1turret],
+   ...]],
+  ...],
+ [[
+  [tank1x, tank1y, tank1angle, tank1sensangle, 
+   tank1flags, tank1sensors],
+  ...],
+ ...]]
+
+
+ +

If file descriptor 3 is open for writes, it also outputs the results of +the round to fd3.

+ +

rank.awk

+ +

Processes the fd3 output of forftanks to award points and output an +HTML results table.

+ +

summary.awk tank1 tank2

+ +

Creates summary.html, linking to all rounds and showing overall +standing.

+ +

designer.cgi

+ +

Accepts form input and writes a tank.

+ + + + + + diff --git a/docs/_site/standie.svg b/docs/_site/standie.svg new file mode 100644 index 0000000..76e6875 --- /dev/null +++ b/docs/_site/standie.svg @@ -0,0 +1,260 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + dirtbags + + + + + + + + + + + + + + diff --git a/docs/_site/thanks.html b/docs/_site/thanks.html new file mode 100644 index 0000000..53d1beb --- /dev/null +++ b/docs/_site/thanks.html @@ -0,0 +1,37 @@ + + + + + + + + + by + + + +

thanks

+ +
    +
  • Tom Poindexter: Inspiration (Author of CRobots)
  • +
  • Paul Ferrell: Initial implementation
  • +
  • Aaron McPhall: Math optimizations
  • +
  • Nick Moffitt: Suggestion to use FORTH-like language
  • +
  • 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/docs/_site/token.svg b/docs/_site/token.svg new file mode 100644 index 0000000..4cb6bb4 --- /dev/null +++ b/docs/_site/token.svg @@ -0,0 +1,1897 @@ + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + dirtbags! + + + + + + + + + + + + + essid: tankshttp://192.168.1.1/password: + + + + + + + + + + + dirtbags! + + + + + + + + + + + + + essid: tankshttp://192.168.1.1/password: + + + + + + + + + + + dirtbags! + + + + + + + + + + + + + essid: tankshttp://192.168.1.1/password: + + + + + + + + + + + dirtbags! + + + + + + + + + + + + + essid: tankshttp://192.168.1.1/password: + + + + + + + + + + + dirtbags! + + + + + + + + + + + + + essid: tankshttp://192.168.1.1/password: + + + + + + + + + + + dirtbags! + + + + + + + + + + + + + essid: tankshttp://192.168.1.1/password: + + + + + + + + + + + dirtbags! + + + + + + + + + + + + + essid: tankshttp://192.168.1.1/password: + + + + + + + + + + + dirtbags! + + + + + + + + + + + + + essid: tankshttp://192.168.1.1/password: + + + + + + + + + + + dirtbags! + + + + + + + + + + + + + essid: tankshttp://192.168.1.1/password: + + + + + + + + + + + dirtbags! + + + + + + + + + + + + + essid: tankshttp://192.168.1.1/password: + + + diff --git a/docs/assets/css/dirtbags.css b/docs/assets/css/dirtbags.css new file mode 100644 index 0000000..6a86e06 --- /dev/null +++ b/docs/assets/css/dirtbags.css @@ -0,0 +1,151 @@ +/**** document ****/ + +html { + background: #eed url(/assets/images/grunge.png) repeat-x; +} + +body { + font-family: sans-serif; + color: #112; + padding: 10px; + max-width: 700px; +} + +/**** heading ****/ + +#title { + text-transform: lowercase; + font-size: 1.6em; + padding: 3px; + margin: 0 0 1em 2em; +} + +#title:before { + color: #999; + letter-spacing: -0.1em; + content: "Dirtbags: "; +} + +/*** left side bar ***/ + +nav h2 { + display: none; +} + +nav ul { + list-style: none; + text-align: center; +} + +nav li { + display: inline; +} + +nav li a { + text-transform: lowercase; + font-size: 0.9em; +} + +nav li + li:before { + content: " | "; +} + + +/**** body ****/ + +a img { + border: 0px; +} + +a { + text-decoration: none; + color: #2a4; + font-weight: bold; +} + +a:hover { + color: #ddc; + background: #2a4; +} + + +h1, h2, h3 { + color: #464; + letter-spacing: -0.05em; +} + +.readme { + background-color: #ccc; + margin: 1em; +} + +pre { + background-color: #ccc; + border: solid #888 2px; + padding: 0.25em; +} + + +th { + vertical-align: top; + text-align: center; +} +td { + vertical-align: top; + text-align: right; +} + +p { + line-height: 1.4em; + margin-bottom: 20px; +} + +hr { + border: 1px solid #444; +} + +dt { + white-space: pre; + background-color: #333; + padding: 5px; + border: 2px solid green; + border-bottom: none; + font-weight: bold; +} + +dd { + border: 2px solid green; + margin: 0px; + padding: 5px; + background-color: #282828; +} + + +/**** special cases ****/ + +.wide { + max-width: inherit; +} + +.figure { + margin: 0.5em 1em; + float: right; + font-size: small; + text-align: center; +} + +.scoreboard { + background: #222; +} + +.scoreboard td { + height: 400px; +} + +#battlefield { + border: 2px solid green; +} + +.solved { + text-decoration: line-through; +} diff --git a/docs/assets/images/grunge.png b/docs/assets/images/grunge.png new file mode 100644 index 0000000000000000000000000000000000000000..9a8c41a59bdff7795373467cb677a9a49cf8b04e GIT binary patch literal 6761 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 A maxlen) { + maxlen = s[0]; + } + } + } + + // Set up our state, for later interleaved draw requests + this.set_state = function(x, y, rotation, turret, flags, sensor_state) { + this.x = x; + this.y = y; + this.rotation = rotation; + this.turret = turret; + if (flags & 1) { + this.fire = 5; + } + this.led = flags & 2; + this.dead = flags & 4; + this.sensor_state = sensor_state; + } + + this.draw_crater = function() { + if (!this.dead) { + return; + } + + var points = 7; + var angle = Math.PI / points; + + ctx.save(); + ctx.translate(this.x, this.y); + ctx.rotate(this.rotation); + + if (this.fire == 5) { + ctx.save(); + ctx.rotate(this.turret); + // one frame of cannon fire + this.draw_cannon(); + this.fire = 0; + ctx.restore(); + } + + ctx.lineWidth = 2; + ctx.strokeStyle = craterStroke; + ctx.fillStyle = craterFill; + ctx.beginPath(); + ctx.moveTo(12, 0); + for (i = 0; i < points; i += 1) { + ctx.rotate(angle); + ctx.lineTo(6, 0); + ctx.rotate(angle); + ctx.lineTo(12, 0); + } + ctx.closePath() + ctx.stroke(); + ctx.fill(); + + ctx.restore(); + } + + this.draw_sensors = function() { + if (this.dead) { + return; + } + ctx.save(); + ctx.translate(this.x, this.y); + ctx.rotate(this.rotation); + + ctx.lineWidth = 1; + for (i in this.sensors) { + var s = this.sensors[i]; + var adj = this.turret * s[3]; + + if (this.sensor_state & (1 << i)) { + // Sensor is triggered + ctx.strokeStyle = "#000"; + } else { + ctx.strokeStyle = sensorStroke; + } + ctx.beginPath(); + ctx.moveTo(0, 0); + ctx.arc(0, 0, s[0], s[1] + adj, s[2] + adj, false); + ctx.closePath(); + ctx.stroke(); + } + + ctx.restore(); + } + + this.draw_tank = function() { + if (this.dead) { + return; + } + ctx.save(); + ctx.translate(this.x, this.y); + ctx.rotate(this.rotation); + + ctx.fillStyle = color; + ctx.fillRect(-5, -4, 10, 8); + ctx.fillStyle = "#777"; + ctx.fillRect(-7, -9, 15, 5); + ctx.fillRect(-7, 4, 15, 5); + ctx.rotate(this.turret); + if (this.fire) { + this.draw_cannon(); + this.fire -= 1; + } else { + if (this.led) { + ctx.fillStyle = "#f00"; + } else { + ctx.fillStyle = "#000"; + } + ctx.fillRect(0, -1, 10, 2); + } + + ctx.restore(); + } + + this.draw_cannon = function() { + ctx.fillStyle = ("rgba(255,255,64," + this.fire/5 + ")"); + ctx.fillRect(0, -1, 45, 2); + } + + this.draw_wrap_sensors = function() { + var orig_x = this.x; + var orig_y = this.y; + for (x = this.x - width; x < width + maxlen; x += width) { + for (y = this.y - height; y < height + maxlen; y += height) { + if ((-maxlen < x) && (x < width + maxlen) && + (-maxlen < y) && (y < height + maxlen)) { + this.x = x; + this.y = y; + this.draw_sensors(); + } + } + } + this.x = orig_x; + this.y = orig_y; + } +} + +var loop_id; +var updateFunc = null; +function togglePlayback() { + if ($("#playing").prop("checked")) { + loop_id = setInterval(updateFunc, 66); + } else { + clearInterval(loop_id); + loop_id = null; + } + $("#pauselabel").toggleClass("ui-icon-play ui-icon-pause"); +} + +function start(id, game) { + var canvas = document.getElementById(id); + var ctx = canvas.getContext('2d'); + + canvas.width = game[0][0]; + canvas.height = game[0][1]; + // game[2] is tank descriptions + var turns = game[2]; + + // Set up tanks + var tanks = new Array(); + for (i in game[1]) { + var desc = game[1][i]; + tanks[i] = new Tank(ctx, game[0][0], game[0][1], desc[0], desc[1]); + } + + var frame = 0; + var lastframe = 0; + var fps = document.getElementById('fps'); + + function update_fps() { + fps.innerHTML = (frame - lastframe); + lastframe = frame; + } + + function drawFrame(idx) { + canvas.width = canvas.width; + turn = turns[idx]; + + // Update and draw craters first + for (i in turn) { + t = turn[i]; + if (!t) { + // old data, force-kill it + tanks[i].fire = 0; + tanks[i].dead = 5; + } else { + tanks[i].set_state(t[0], t[1], t[2], t[3], t[4], t[5]); + } + tanks[i].draw_crater(); + } + // Then sensors + for (i in turn) { + tanks[i].draw_wrap_sensors(); + } + // Then tanks + for (i in turn) { + tanks[i].draw_tank() + } + + document.getElementById('frameid').innerHTML = idx; + } + + function update() { + var idx = frame % (turns.length + 20); + var turn; + + frame += 1; + if (idx >= turns.length) { + return; + } + + drawFrame(idx); + + $('#seekslider').slider('value', idx); + } + + function seekToFrame(newidx) { + var idx = frame % (turns.length + 20); + if (idx !== newidx) { + frame = newidx; + drawFrame(newidx); + } + // make sure we're paused + if ($("#playing").prop("checked")) { + $("#playing").prop("checked", false); + togglePlayback(); + } + } + + updateFunc = update; + loop_id = setInterval(update, 66); + //loop_id = setInterval(update, 400); + if (fps) { + setInterval(update_fps, 1000); + } + + if (id === "battlefield") { + $("#game_box").append('

0

'); + $('#playing').button(); + var slider = $('#seekslider'); + slider.slider({ max: turns.length-1, slide: function(event, ui) { seekToFrame(ui.value); } }); + + var spacing = 100 / turns.length; + var deaths = []; + for (i in turns[0]) { + deaths.push(false); + } + var percent = 0; + for (var f = 0; f < turns.length; f++) { + var turn = turns[f]; + if (percent < (spacing * f)) { + percent = spacing * f; + } + for (var i = 0; i < turn.length; i++) { + if (deaths[i]) { continue; } + if (!turn[i] || (turn[i][4] & 4)) { + deaths[i] = true; + // http://stackoverflow.com/questions/8648963/add-tick-marks-to-jquery-slider + $('').css('left', percent + '%').css('background-color', game[1][i][0]).appendTo(slider); + percent++; + break; + } + } + } + } +} + diff --git a/docs/forf.html.m4 b/docs/forf.html.m4 new file mode 100644 index 0000000..65a4de5 --- /dev/null +++ b/docs/forf.html.m4 @@ -0,0 +1,13 @@ + + + + Forf Manual + + + + +syscmd(markdown forf.txt) +include(nav.html.inc) + + + diff --git a/docs/history.md b/docs/history.md index 3873dbf..d94efc3 100644 --- a/docs/history.md +++ b/docs/history.md @@ -1,4 +1,9 @@ -# History +--- +title: History +scripts: + - assets/js/tanks.js + - assets/js/figures.js +--- This is a port of the "Tanks" program written by Paul Ferrell in 2009-2010. Paul created the entire game based @@ -29,6 +34,18 @@ called "Bullet", which looked like this: sense(2): turretccw(); sense(0): fire(); + + + + + +
"Chashtank" cleans up.
+ + +
+ Nick Moffitt played with this original version and convinced me (Neale) that something like Forth would be a better language. I added some code to accept a scaled-down version of PostScript. The IRC channel we diff --git a/docs/intro.html.m4 b/docs/intro.html.m4 new file mode 100644 index 0000000..6d31e1f --- /dev/null +++ b/docs/intro.html.m4 @@ -0,0 +1,147 @@ + + + + Tanks Introduction + + + + + + +

Tanks Introduction

+ + + + + + +
"ChashTank" dominates this short round.
+ + +
+ +

+ 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 it fares against other tanks while + running your program. +

+ +

+ Each tank has a turret-mounted laser, two treads, up to ten + sensors, and a diagnostic LED. Sensors are used to detect when + other tanks are inside a given arc. In the examples on this page, + "triggered" sensors turn black. Most tanks will take some action + if a sensor is triggered, such as changing speed of the treads, + turning the turret, or firing. +

+ +

+ Tanks are programmed in Forf, a stack-based language similar to + PostScript. Please read the Forf manual + to learn more about forf, and the Tanks + procedure reference for a description of Tanks extensions. +

+ +

Quick Start for the Impatient

+ + + + + + +
"Crashmaster" pwns the lame default tank provided in this + section.
+ + +
+ +

+ To get started, head over to the Tank + Debugger and enter the following example tank. This tank will + move around, turn the turret, and fire if there's something in + front of it. +

+ +
+Sensor 0: 50 0 7 ☑
+Sensor 1: 30 0 90 ☐
+
+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 )
+    
+ +

+ Obviously, this tank could be improved. Studying the examples on + this page should give you ideas about how to make a better tank. + Don't forget the Forf manual and the + Tank procedure reference. +

+ +

Tank Specifications

+ + + + + + +
"Ant Lion" nails "Rabbit With Gun".
+ + +
+ +

+ Your PF-255 autonomous tank is built to the exacting + specifications sent to our factory in New Khavistan. All + distances are in meters, angles in degrees. +

+ +
+
Tank size
+
+ The targettable area of the tank—the part which can be hit by a + cannon—is a circle about 7½ meters in radius. +
+ +
Speed
+
+ Each tread can have a speed between -100 and 100. This is in + percentage of total speed for that tread, where total speed is + roughly 7 meters per turn. +
+ +
Sensors
+
+ Each sensor has a maximum range of 100 meters. Of course, you + don't have to use the full range. Sensors may be attached to + the turret (they turn along with the turret), or left fixed to + the tank. +
+ +
Turret
+
+ Turret angle can be set between -359° and 359°, with 0° directly + in front of the tank. Be aware that it takes time for the + turret to swing around: the turret can swing about 45° per turn. +
+ +
Cannon range and recharging
+
+ When the cannon is fired, it obliterates everything for 50 + meters in front of it. It takes around 20 turns for your cannon + to recharge after it's been fired, so only shoot when you feel + like you're going to hit something. +
+
+ + Good luck blowing everybody up! +include(nav.html.inc) + diff --git a/docs/intro.md b/docs/intro.md deleted file mode 100644 index add5033..0000000 --- a/docs/intro.md +++ /dev/null @@ -1,72 +0,0 @@ -# Tanks Introduction - -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 it fares against other tanks while -running your program. - -Each tank has a turret-mounted laser, two treads, up to ten -sensors, and a diagnostic LED. Sensors are used to detect when -other tanks are inside a given arc. In the examples on this page, -"triggered" sensors turn black. Most tanks will take some action -if a sensor is triggered, such as changing speed of the treads, -turning the turret, or firing. - -Tanks are programmed in Forf, a stack-based language similar to -PostScript. Please read the [Forf manual](forf.md) to learn more -about forf, and the [Tanks procedure reference](procs.html) for a -description of Tanks extensions. - -## Quick Start for the Impatient - -To get started, head over to the designer for your game, -and use the following example tank. This tank will -move around, turn the turret, and fire if there's something in -front of it. - - Sensor 0: 50 0 7 ☑ - Sensor 1: 30 0 90 ☐ - - 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 ) - -Obviously, this tank could be improved. -Watch other tanks in your game to get ideas about how to improve yours. -Don't forget the [Forf manual](forf.md) and the -[Tank procedure reference](procs.html). - -## Tank Specifications - -Your PF-255 autonomous tank is built to the exacting -specifications sent to our factory in New Khavistan. All -distances are in meters, angles in degrees. - - -Tank size -: The targettable area of the tank—the part which can be hit by a cannon—is a circle about 7½ meters in radius. - -Speed -: Each tread can have a speed between -100 and 100. This is in - percentage of total speed for that tread, where total speed is - roughly 7 meters per turn. - -Sensors -: Each sensor has a maximum range of 100 meters. Of course, you - don't have to use the full range. Sensors may be attached to - the turret (they turn along with the turret), or left fixed to - the tank. - -Turret -: Turret angle can be set between -359° and 359°, with 0° directly - in front of the tank. Be aware that it takes time for the - turret to swing around: the turret can swing about 45° per turn. - -Cannon range and recharging -: When the cannon is fired, it obliterates everything for 50 - meters in front of it. It takes around 20 turns for your cannon - to recharge after it's been fired, so only shoot when you feel - like you're going to hit something. - -Good luck blowing everybody up! diff --git a/docs/nav.html.inc b/docs/nav.html.inc new file mode 100644 index 0000000..b2ea44f --- /dev/null +++ b/docs/nav.html.inc @@ -0,0 +1,10 @@ + diff --git a/docs/procs.html.m4 b/docs/procs.html.m4 new file mode 100644 index 0000000..2510df0 --- /dev/null +++ b/docs/procs.html.m4 @@ -0,0 +1,74 @@ + + + + Tanks Procedure Reference + + + + +

Tanks Procedure Reference

+ +

+ Each tank's program is run once per turn. The data and command + stacks are reset at the beginning of each turn, but memory is not, + so you can carry data over in memory registers if you want. See + the Forf manual for more information about + the base language. +

+ +

+ For tank specifications (sensor range, maximum speeds, etc.), see + the introduction. +

+ + +

Limits

+ +

+ Forf Tanks has a data stack size of 200, and a command stack size + of 500. This means your program cannot have more than 200 data + items, or 500 instructions, including 2 instructions for each + substack. +

+ +

+ Forf Tanks provides 10 memory registers (0-9) which persist across + invocations of your tank's program. +

+ + +

Additional Procedures

+ +
+
fire-ready?
+
Returns 1 if the tank can fire, 0 if not.
+ +
fire!
+
Fires the cannon.
+ +
l r set-speed!
+
Sets the speed of the left and right treads (range: -100 to + 100).
+ +
get-turret
+
Returns the current angle of the turret.
+ +
a set-turret!
+
Set the turret to a degrees.
+ +
n sensor?
+
Returns 1 if sensor n is triggered, 0 if not.
+ +
s set-led!
+
Turns off the LED if s is 0, on for any other value.
+ +
n random
+
Returns a random number in the range [0, n). That is, between + 0 and n-1, inclusive.
+
+ +include(nav.html.inc) + + + + diff --git a/docs/procs.md b/docs/procs.md deleted file mode 100644 index c003cb5..0000000 --- a/docs/procs.md +++ /dev/null @@ -1,47 +0,0 @@ -# Tanks Procedure Reference - -Each tank's program is run once per turn. The data and command -stacks are reset at the beginning of each turn, but memory is not, -so you can carry data over in memory registers if you want. See -the [Forf manual](forf.md) for more information about -the base language. - -For tank specifications (sensor range, maximum speeds, etc.), see -the [introduction](intro.html). - -## Limits - -Forf Tanks has a data stack size of 200, and a command stack size -of 500. This means your program cannot have more than 200 data -items, or 500 instructions, including 2 instructions for each -substack. - -Forf Tanks provides 10 memory registers (0-9) which persist across -invocations of your tank's program. - - -## Additional Procedures - -fire-ready? -: Returns 1 if the tank can fire, 0 if not. - -fire! -: Fires the cannon. - -l r set-speed! -: Sets the speed of the left and right treads (range: -100 to 100). - -get-turret -: Returns the current angle of the turret, in degrees. - -a set-turret! -: Begin moving the turret to a degrees. - -n sensor? -: Returns 1 if sensor n is triggered, 0 if not. - -s set-led! -: Turns off the LED if s is 0, on for any other value. - -n random -: Returns a random number in the range [0, n). That is, between 0 and n-1, inclusive. diff --git a/docs/running.md b/docs/running.md new file mode 100644 index 0000000..66dd857 --- /dev/null +++ b/docs/running.md @@ -0,0 +1,73 @@ +--- +title: Running Tanks +--- + +Unfortunately, it's kind of a mess right now. +I know there are a few forks of this code, +and I would love it if someone proposed a merge to clearly illustrate how to run tanks. + +When I spin up a new tanks game, +typically I run something like: + + while sleep 60; do ./run-tanks */; done + +This assumes all your tanks directories are in the same place as `run-tanks`. + + + +Included programs +----------------- + +I tried to stick with the Unix philosophy of one program per task. I +also tried to avoid doing any string processing in C. The result is a +hodgepodge of C, Bourne shell, and awk, but at least each piece is +fairly simple to audit. + + +### run-tanks tank1 tank2 ... + +Runs a single round, awards points with rank.awk, and creates a new +summary.html with summary.awk. This is the main interface that you want +to run from cron or whatever. + + +### forftanks tank1 tank2 ... + +A program to run a round of tanks and output a JSON description of the +game. This is what tanks.js uses to render a game graphically. +The object printed contains: + + [[game-width, game-height], + [[tank1-color, + [[sensor1range, sensor1angle, sensor1width, sensor1turret], + ...]], + ...], + [[ + [tank1x, tank1y, tank1angle, tank1sensangle, + tank1flags, tank1sensors], + ...], + ...]] + +If file descriptor 3 is open for writes, it also outputs the results of +the round to fd3. + + +### rank.awk + +Processes the fd3 output of forftanks to award points and output an +HTML results table. + + +### summary.awk tank1 tank2 + +Creates summary.html, linking to all rounds and showing overall +standing. + + +### designer.cgi + +Accepts form input and writes a tank. + + + +