Lua register.cgi

This commit is contained in:
Neale Pickett 2010-09-01 17:39:46 -06:00
parent ab842bab4d
commit d3707d912e
28 changed files with 109 additions and 6330 deletions

View File

View File

@ -1,33 +0,0 @@
#! /usr/bin/python
import asynchat
import asyncore
import socket
class Flagger(asynchat.async_chat):
"""Use to connect to flagd and submit the current flag holder."""
def __init__(self, addr, auth):
asynchat.async_chat.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.connect((addr, 1))
self.push(auth + '\n')
self.flag = None
def handle_read(self):
# We don't care.
msg = self.recv(4096)
def handle_error(self):
# If we lose the connection to flagd, nobody can score any
# points. Terminate everything.
asyncore.close_all()
asynchat.async_chat.handle_error(self)
def set_flag(self, team):
if team:
eteam = team.encode('utf-8')
else:
eteam = ''
self.push(eteam + '\n')
self.flag = team

View File

@ -1,35 +0,0 @@
#! /usr/bin/python
import os
import string
import sys
from codecs import open
from paths import *
template_fn = os.path.join(LIB, 'template.html')
template = string.Template(open(template_fn, encoding='utf-8').read())
base = BASE_URL
css = base + 'ctf.css'
def substitute(title, body, base=base, hdr='', body_class='', onload='', links=''):
return template.substitute(title=title,
hdr=hdr,
body_class=body_class,
base=base,
links=links,
onload=onload,
body=body)
def serve(title, body, **kwargs):
out = substitute(title, body, **kwargs)
print 'Content-type: text/html'
print 'Content-length: %d' % len(out)
print
sys.stdout.write(out)
sys.stdout.flush()
def write(filename, title, body, **kwargs):
f = open(filename, 'w', encoding='utf-8')
f.write(substitute(title, body, **kwargs))

View File

@ -1,40 +0,0 @@
#! /usr/bin/python
from urllib import quote
import teams
import time
import os
import paths
pointsdir = os.path.join(paths.VAR, 'points')
def award(cat, team, points):
if not team:
team = teams.house
now = time.strftime('%Y-%m-%dT%H:%M:%S')
pid = os.getpid()
qcat = quote(cat, '')
qteam = quote(team, '')
basename = '%s.%d.%s.%s' % (now, pid, qcat, qteam)
# FAT can't handle :
basename = basename.replace(':', '.')
tmpfn = os.path.join(pointsdir, 'tmp', basename)
curfn = os.path.join(pointsdir, 'cur', basename)
f = open(tmpfn, 'w')
f.write('%s\t%s\t%s\t%d\n' % (now, cat, team, points))
f.close()
os.rename(tmpfn, curfn)
def main():
import optparse
p = optparse.OptionParser('%prog CATEGORY TEAM POINTS')
opts, args = p.parse_args()
if len(args) != 3:
p.error('Wrong number of arguments')
cat, team, points = args
points = int(points)
award(cat, team, points)
if __name__ == '__main__':
main()

View File

@ -1,72 +0,0 @@
#! /usr/bin/python
import fcntl
import time
import os
from urllib import quote, unquote
import paths
house = 'dirtbags'
passwdfn = os.path.join(paths.VAR, 'passwd')
team_colors = ['F0888A', '88BDF0', '00782B', '999900', 'EF9C00',
'F4B5B7', 'E2EFFB', '89CA9D', 'FAF519', 'FFE7BB',
'BA88F0', '8DCFF4', 'BEDFC4', 'FFFAB2', 'D7D7D7',
'C5B9D7', '006189', '8DCB41', 'FFCC00', '898989']
teams = {}
built = 0
def build_teams():
global teams, built
if not os.path.exists(passwdfn):
return
if os.path.getmtime(passwdfn) <= built:
return
teams = {}
try:
f = open(passwdfn)
for line in f:
line = line.strip()
if not line:
continue
team, passwd, color = map(unquote, line.strip().split('\t'))
teams[team] = (passwd, color)
except IOError:
pass
built = time.time()
def validate(team):
build_teams()
def chkpasswd(team, passwd):
validate(team)
if teams.get(team, [None, None])[0] == passwd:
return True
else:
return False
def exists(team):
validate(team)
if team == house:
return True
return team in teams
def add(team, passwd):
build_teams()
color = team_colors[len(teams)%len(team_colors)]
assert team not in teams, "Team already exists."
f = open(passwdfn, 'a')
fcntl.lockf(f, fcntl.LOCK_EX)
f.seek(0, 2)
f.write('%s\t%s\t%s\n' % (quote(team, ''),
quote(passwd, ''),
quote(color, '')))
def color(team):
validate(team)
t = teams.get(team)
if not t:
return '888888'
return t[1]

View File

@ -1,18 +0,0 @@
Name: berzerker
Author: Neale
2 random
0 =
{ 50 100 move }
{ 100 50 move }
ifelse
4 random
0 =
{ 360 random setturret }
if
30 random
0 =
{ fire }
if

View File

@ -1,31 +0,0 @@
Name: Rabbit With Gun
Author: Neale
Sensor: 50 0 0 1
Sensor: 70 0 50 0
100 100 move
( Always set turret to 180 degrees )
180 setturret
( Vary walk 1/8 of the time )
8 random
0 =
{
2 random
0 =
{ 70 100 move }
{ 100 70 move }
ifelse
}
if
( If you see something, shoot it )
0 sensoractive
{ fire }
if
( Turn, if trouble lies ahead )
1 sensoractive
{ 0 100 move }
if

View File

@ -1,52 +0,0 @@
>addsensor(50, 0, 10, 1);
>addsensor(35, 0, 90, 0);
>addsensor(100, 30, 59, 0);
>addsensor(100, 330, 59, 0);
>addsensor(70, 180, 180);
>addsensor(100, 90, 59, 0);
>addsensor(100, 270, 59, 0);
>addsensor(100, 0, 5, 1);
>addsensor(55, 50, 89, 0);
>addsensor(55, 310, 89, 0);
# move back and forth
: move(90,90).turretset(0);
random(2,6) : move(95,75).turretset(0);
#random(1,6) : move(75,95).turretset(0);
# rear sensor
sense(4) : move(90, 90);
# far right front sensor
sense(5) : move(100,-100);
# far left front sensor
sense(6) : move(-100,100);
# right front sensor
sense(2) : move(80,-80);
# left front sensor
sense(3) : move(-80,80);
# immediate front sensor in firing range
sense(0) & firenotready() : move(-50, -50);
: turretset(0);
# near far right front sensor
sense(8) : move(60,-60);
sense(9) : move(-60, 60);
fireready() : led();
# front far sensor
sense(7) & fireready() : move(100,100);
# collison sensor
sense(1) : move(-100, -100);
: turretset(0);
sense(0) & fireready() : fire();

View File

@ -1,55 +0,0 @@
Name: crashmaster
Author: Neale
Sensor: 50 0 8 1
Sensor: 30 0 50 0
Sensor: 50 0 10 0
Sensor: 100 315 100 1
Sensor: 100 45 100 1
Sensor: 60 180 180 0
( Mem 0: Turn number )
0 fetch
1 +
0 store
( Mem 1: Move turret (procedure) )
{
getturret
-
setturret
} 1 store
0 fetch 30 % 10 / ( [0..2], changes every 10 turns )
dup 0 = { 80 80 move } if
dup 1 = { 60 80 move } if
dup 2 = { 80 60 move } if
pop
0 setturret
fireready
{
( Behavior for when we can shoot )
0 sensoractive { fire } if
1 sensoractive { 10 10 move 0 setturret } if
2 sensoractive { 10 10 move 0 setturret } if
3 sensoractive { 0 60 move -50 1 call } if
4 sensoractive { 60 0 move 50 1 call } if
3 sensoractive 4 sensoractive & { 100 100 move getturret setturret } if
5 sensoractive { 100 40 move } if
}
{
( Behavior for when we can't shoot )
setled
0 sensoractive { 10 20 move } if
1 sensoractive { 10 10 move } if
2 sensoractive { 10 20 move } if
3 sensoractive { 70 50 move } if
4 sensoractive { 50 70 move } if
3 sensoractive 4 sensoractive & { -100 20 move } if
5 sensoractive { 100 50 move } if
}
ifelse

View File

@ -1,22 +0,0 @@
>addsensor(55, 0, 5, 1);
>addsensor(40, 0, 30);
>addsensor(80, 30, 59, 0);
>addsensor(80, 330, 59, 0);
>addsensor(70, 180, 180);
>addsensor(80, 90, 59, 0);
>addsensor(80, 270, 59, 0);
# : move(70,80);
# random(3,6) : move(80,70);
: move(65,85);
random(2,6) : move(90,65);
sense(2) : move(80,10).turretcw(100);
sense(3) : move(10,80).turretccw(100);
sense(4) : move(90, 90);
sense(5) : move(90,10).turretcw(100);
sense(6) : move(10,90).turretccw(100);
sense(0) & fireready() : turretset().move(90,90).fire();
sense(1) : move(-100, -100);
: turretset(0);
fireready() : led();

View File

@ -1,31 +0,0 @@
>addsensor(50, 0, 45, 1); # 0-Fire Sensor
>addsensor(30, 0, 180); # 1-Anti-collision sensor
>addsensor(100, 40, 60, 1); # 2 turret clockwise
>addsensor(100, 320, 60, 1); # 3 turret ccw
>addsensor(80, 180, 160); # 4 Coward
>addsensor(100, 0, 0, 1); # 5-Fire Sensor2
>addsensor(100, 0, 0); # 6-Chase Sensor
>addsensor(75, 75, 30); # 7-quick turn right
>addsensor(75, 285, 30); # 8-quick turn left
# Commands
: move(70, 75).
turretset(0);
random(1, 10): move(75, 75).
turretset(0);
sense(2) : turretcw(50).
move(85, 70);
sense(2) & sense(0): turretcw(25).
move(85, 70);
sense(3) : turretccw(50).
move(70, 85);
sense(3) & sense(0) : turretccw(25).
move(70, 85);
sense(5) & sense(7) : move(70, 30);
sense(5) & sense(8) : move(30, 70);
#sense(5) : turretset();
sense(0) & sense(5) : fire();
sense(6) & sense(5) & fireready(): move(100,100);
sense(4) : move(100,100);
sense(1) : move(-50, 25);
fireready() : led();

View File

@ -1,8 +0,0 @@
>addsensor(50, 0, 5, 1); # 0-Fire Sensor
>addsensor(30, 0, 50); # 1-Anti-collision sensor
# Commands
: move(90, 100).
turretset(0);
sense(0) : fire();
sense(1) : move(-100, 100)

View File

@ -1,9 +0,0 @@
>addsensor(50, 0, 10, 1); # 0-Fire Sensor
>addsensor(100, 90, 150, 1);
>addsensor(100, 270, 150, 1);
: turretcw(75);
sense(0): fire();
sense(1): turretcw();
sense(2): turretccw();

View File

@ -1,18 +0,0 @@
# Just sit there and sweep the field until it finds something to shoot.
# Uses a long-range sensor on the left and right to hone in.
>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();

View File

@ -1,125 +0,0 @@
import random
import math
# Not defined in older Python's math libs
def multiply(a, b):
return a * b
def factorial(n):
return reduce(multiply, range(1, n+1))
OPS = [lambda a, b: a + b,
lambda a, b: a - b,
lambda a, b: a * b,
lambda a, b: a // b,
lambda a, b: a % b,
lambda a, b: a ^ b,
lambda a, b: a | b,
lambda a, b: a & b,
lambda a, b: max(a,b),
lambda a, b: min(a,b),
lambda a, b: a+b//2,
lambda a, b: ~b,
lambda a, b: a + b + 3,
lambda a, b: max(a,b)//2,
lambda a, b: min(a,b)*3,
lambda a, b: a % 2,
lambda a, b: int(math.degrees(b + a)),
lambda a, b: ~(a & b),
lambda a, b: ~(a ^ b),
lambda a, b: a + b - a%b,
lambda a, b: (a > 0) and (factorial(a)//factorial(a-b)) or 0,
lambda a, b: (b%a) * (a%b),
lambda a, b: factorial(a)%b,
lambda a, b: int(math.sin(a)*b),
lambda a, b: b + a%2,
lambda a, b: a - 1 + b%3,
lambda a, b: a & 0xaaaa,
lambda a, b: a == b and 5 or 6,
lambda a, b: b % 17,
lambda a, b: int( cos( math.radians(b) ) * a )]
SYMBOLS = '.,<>?/!@#$%^&*()_+="~|;:'
MAX = 100
PLAYER_DIR = ''
def mkPuzzle(lvl):
"""Make a puzzle. The puzzle is a simple integer math equation. The trick
is that the math operators don't do what you might expect, and what they do
is randomized each time (from a set list of functions). The equation is
evaluated left to right, with no other order of operations.
The level determins both the length of the puzzle, and what functions are
enabled. The number of operators is half the level+2, and the number of
functions enabled is equal to the level.
returns the key, puzzle, and the set of numbers used.
"""
ops = OPS[:lvl + 1]
length = (lvl + 2)//2
key = {}
bannedNums = set()
puzzle = []
for i in range(length):
num = random.randint(1,MAX)
bannedNums.add(num)
puzzle.append( num )
symbol = random.choice(SYMBOLS)
if symbol not in key:
key[symbol] = random.randint(0, len(ops) - 1)
puzzle.append( symbol )
num = random.randint(1,MAX)
bannedNums.add(num)
puzzle.append( num )
return key, puzzle, bannedNums
def parse(puzzle):
"""Parse a puzzle string. If the string contains symbols not in
SYMBOLS, a ValueError is raised."""
parts = [puzzle]
for symbol in SYMBOLS:
newParts = []
for part in parts:
if symbol in part:
terms = part.split(symbol)
newParts.append( terms.pop(0))
while terms:
newParts.append(symbol)
newParts.append( terms.pop(0) )
else:
newParts.append(part)
parts = newParts
finalParts = []
for part in parts:
part = part.strip()
if part in SYMBOLS:
finalParts.append( part )
else:
try:
finalParts.append( int(part) )
except:
raise ValueError("Invalid symbol: %s" % part)
return finalParts
def solve(key, puzzle):
puzzle = list(puzzle)
stack = puzzle.pop(0)
while puzzle:
symbol = puzzle.pop(0)
nextVal = puzzle.pop(0)
op = OPS[key[symbol]]
stack = op(stack, nextVal)
return stack

View File

@ -1,40 +0,0 @@
#! /usr/bin/python
import png
from array import array
class Canvas:
def __init__(self, width, height, bg=(0,0,0)):
self.width = width
self.height = height
# Build the canvas using arrays, which are way quick
row = array('B')
for i in xrange(self.width):
row.extend(bg)
self.bytes = array('B')
for i in xrange(self.height):
self.bytes.extend(row)
def get(self, x, y):
offs = ((y*self.width)+x)*3
return self.bytes[offs:offs+3]
def set(self, x, y, pixel):
offs = ((y*self.width)+x)*3
for i in range(3):
self.bytes[offs+i] = pixel[i]
def write(self, f):
p = png.Writer(self.width, self.height)
p.write_array(f, self.bytes)
if __name__ == '__main__':
width = 800
height = 600
c = Canvas(width, height)
for x in range(width):
c.set(x, x % height, (x%256,(x*2)%256,(x*3)%256))
c.write(open('foo.png', 'wb'))

View File

@ -1,541 +0,0 @@
#! /usr/bin/python
import asynchat
import asyncore
import socket
import sys
import traceback
import time
channel_prefixes = '+#&'
class IRCHandler(asynchat.async_chat):
"""IRC Server connection.
This is the one you want to derive your connection classes from.
"""
debug = False
heartbeat_interval = 1 # seconds per heartbeat
def __init__(self, host=None, nick=None, gecos=None):
asynchat.async_chat.__init__(self)
self.line = ''
self.timers = []
self.last_heartbeat = 0
self.set_terminator('\r\n')
if host:
self.open_connection(host, nick, gecos)
def dbg(self, msg):
if self.debug:
print(msg)
def open_connection(self, host, nick, gecos):
self.nick = nick
self.gecos = gecos
self.host = host
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.connect(host)
def handle_connect(self):
self.write(['NICK', self.nick])
self.write(['USER', self.nick, '+iw', self.nick], self.gecos)
def connect(self, host):
self.waiting = False
asynchat.async_chat.connect(self, host)
def heartbeat(self):
"""Invoke all timers."""
if not self.timers:
return
timers, self.timers = self.timers, []
now = time.time()
for t, cb in timers:
if t > now:
self.timers.append((t, cb))
else:
cb()
def add_timer(self, secs, callback):
"""After secs seconds, call callback"""
self.timers.append((time.time() + secs, callback))
def readable(self):
"""Called by asynchat to see if we're readable.
We hook our heartbeat in here.
"""
now = time.time()
if now > self.last_heartbeat + self.heartbeat_interval:
self.heartbeat()
self.last_heartbeat = now
if self.connected:
return asynchat.async_chat.readable(self)
else:
return False
def collect_incoming_data(self, data):
"""Called by asynchat when data arrives"""
self.line += data
def found_terminator(self):
"""Called by asynchat when it finds the terminating character.
"""
line = self.line.decode('utf-8')
self.line = ''
self.parse_line(line)
def write(self, args, text=None):
"""Send out an IRC command
This function helps to prevent you from shooting yourself in the
foot, by forcing you to send commands that are in a valid format
(although it doesn't check the validity of the actual commands).
As we all know, IRC commands take the form
:COMMAND ARG1 ARG2 ARG3 ... :text string
where 'text string' is optional. Well, that's exactly how this
function works. Args is a list of length at least one, and text
string is a string.
write(['PRIVMSG', nick], 'Hello 12')
will send
PRIVMSG nick :Hello 12
"""
cmdstr = ' '.join(args)
if text:
cmdstr = '%s :%s' % (cmdstr, text)
self.dbg('-> %s' % cmdstr)
try:
line = '%s\n' % cmdstr
self.send(line.encode('utf-8'))
except socket.error:
pass
def parse_line(self, line):
"""Parse a server-provided line
This does all the magic of parsing those ill-formatted IRC
messages. It will also decide if a PRIVMSG or NOTICE is using
CTCP (the client-to-client protocol, which by convention is any
of the above messages with ^A on both ends of the text.
This function goes on to invoke self.eval_triggers on the parsed
data like this:
self.eval_triggers(operation, arguments, text)
where operation and text are strings, and arguments is a list.
It returns the same tuple (op, args, text).
"""
if (line[0] == ':'):
with_uname = 1
line = line [1:]
else:
with_uname = 0
try:
[args, text] = line.split(' :', 1)
args = args.split()
except ValueError:
args = line.split()
text = ''
if (with_uname != 1):
op = args[0]
elif ((args[1] in ["PRIVMSG", "NOTICE"]) and
(text and (text[0] == '\001') and (text[-1] == '\001'))):
op = "C" + args[1]
text = text[1:-1]
else:
op = args[1]
self.dbg("<- %s %s %s" % (op, args, text))
self.handle(op, args, text)
return (op, args, text)
def handle(self, op, args, text):
"""Take action on a server message
Right now, just invokes
self.do_[op](args, text)
where [op] is the operation passed in.
This is a good method to overload if you want a really advanced
client supporting bindings.
"""
try:
method = getattr(self, "do_" + lower(op))
except AttributeError:
self.dbg("Unhandled: %s" % (op, args, text))
return
method(args, text)
class Recipient:
"""Abstract recipient object"""
def __init__(self, interface, name):
self._interface = interface
self._name = name
def __repr__(self):
return 'Recipient(%s)' % self.name()
def name(self):
return self._name
def is_channel(self):
return False
def write(self, cmd, addl):
"""Write a raw IRC command to our interface"""
self._interface.write(cmd, addl)
def cmd(self, cmd, text):
"""Send a command to ourself"""
self.write([cmd, self._name], text)
def msg(self, text):
"""Tell the recipient something"""
self.cmd("PRIVMSG", text)
def notice(self, text):
"""Send a notice to the recipient"""
self.cmd("NOTICE", text)
def ctcp(self, command, text):
"""Send a CTCP command to the recipient"""
return self.msg("\001%s %s\001" % (command.upper(), text))
def act(self, text):
"""Send an action to the recipient"""
return self.ctcp("ACTION", text)
def cnotice(self, command, text):
"""Send a CTCP notice to the recipient"""
return self.notice("\001%s %s\001" % (command.upper(), text))
class Channel(Recipient):
def __repr__(self):
return 'Channel(%s)' % self.name()
def is_channel(self):
return True
class User(Recipient):
def __init__(self, interface, name, user, host, op=False):
Recipient.__init__(self, interface, name)
self.user = user
self.host = host
self.op = op
def __repr__(self):
return 'User(%s, %s, %s)' % (self.name(), self.user, self.host)
def recipient(interface, name):
if name[0] in channel_prefixes:
return Channel(interface, name)
else:
return User(interface, name, None, None)
class SmartIRCHandler(IRCHandler):
"""This is like the IRCHandler, except it creates Recipient objects
for IRC messages. The intent is to make it easier to write stuff
without knowledge of the IRC protocol.
"""
def recipient(self, name):
return recipient(self, name)
def err(self, exception):
if self.debug:
traceback.print_exception(*exception)
def handle(self, op, args, text):
"""Parse more, creating objects and stuff
makes a call to self.handle_op(sender, forum, addl)
sender is always a Recipient object; if you want to reply
privately, you can send your reply to sender.
forum is a Recipient object corresponding with the forum over
which the message was carried. For user-to-user PRIVMSG and
NOTICE commands, this is the same as sender. For those same
commands sent to a channel, it is the channel. Thus, you can
always send a reply to forum, and it will be sent back in an
appropriate manner (ie. the way you expect).
addl is a tuple, containing additional information which might
be relelvant. Here's what it will contain, based on the server
operation:
op | addl
---------+----------------
PRIVMSG | text of the message
NOTICE | text of the notice
CPRIVMSG | CTCP command, text of the command
CNOTICE | CTCP response, text of the response
KICK * | victim of kick, kick text
MODE * | all mode args
JOIN * | empty
PART * | empty
QUIT | quit message
PING | ping text
NICK ! | old nickname
others | all arguments; text is last element
* The forum in these items is the channel to which the action
pertains.
! The sender for the NICK command is the *new* nickname. This
is so you can send messages to the sender object and they'll
go to the right place.
"""
try:
sender = User(self, *unpack_nuhost(args))
except ValueError:
sender = None
forum = None
addl = ()
if op in ("PRIVMSG", "NOTICE"):
# PRIVMSG ['neale!~user@127.0.0.1', 'PRIVMSG', '#hydra'] firebot, foo
# PRIVMSG ['neale!~user@127.0.0.1', 'PRIVMSG', 'firebot'] firebot, foo
try:
forum = self.recipient(args[2])
if not forum.is_channel():
forum = sender
addl = (text,)
except IndexError:
addl = (text, args[1])
elif op in ("CPRIVMSG", "CNOTICE"):
forum = self.recipient(args[2])
splits = text.split(" ")
if splits[0] == "DCC":
op = "DC" + op
addl = (splits[1],) + tuple(splits[2:])
else:
addl = (splits[0],) + tuple(splits[1:])
elif op in ("KICK",):
forum = self.recipient(args[2])
addl = (self.recipient(args[3]), text)
elif op in ("MODE",):
forum = self.recipient(args[2])
addl = args[3:]
elif op in ("JOIN", "PART"):
try:
forum = self.recipient(args[2])
except IndexError:
forum = self.recipient(text)
elif op in ("QUIT",):
addl = (text,)
elif op in ("PING", "PONG"):
# PING ['PING'] us.boogernet.org.
# PONG ['irc.foonet.com', 'PONG', 'irc.foonet.com'] 1114199424
addl = (text,)
elif op in ("NICK",):
# NICK ['brad!~brad@10.168.2.33', 'NICK'] bradaway
#
# The sender is the new nickname here, in case you want to
# send something to the sender.
# Apparently there are two different standards for this
# command.
if text:
sender = self.recipient(text)
else:
sender = self.recipient(args[2])
addl = (unpack_nuhost(args)[0],)
elif op in ("INVITE",):
# INVITE [u'pflarr!~pflarr@www.clanspum.net', u'INVITE', u'gallium', u'#mysterious']
# INVITE [u'pflarr!~pflarr@www.clanspum.net', u'INVITE', u'gallium'] #mysterious
if len(args) > 3:
forum = self.recipient(args[3])
else:
forum = self.recipient(text)
else:
try:
int(op)
except ValueError:
self.dbg("WARNING: unknown server code: %s" % op)
addl = tuple(args[2:]) + (text,)
try:
self.handle_cooked(op, sender, forum, addl)
except SystemExit:
raise
except:
self.err(sys.exc_info())
def handle_cooked(self, op, sender, forum, addl):
try:
func = getattr(self, 'cmd_' + op.upper())
except AttributeError:
self.unhandled(op, sender, forum, addl)
return
func(sender, forum, addl)
def cmd_PING(self, sender, forum, addl):
self.write(['PONG'], addl[0])
def unhandled(self, op, sender, forum, addl):
"""Handle all the stuff that had no handler.
This is a special handler in that it also gets the server code
as the first argument.
"""
self.dbg("unhandled: %s" % ((op, sender, forum, addl),))
class Bot(SmartIRCHandler):
"""A simple bot.
This automatically joins the channels you pass to the constructor,
tries to use one of the nicks provided, and reconnects if it gets
booted. You can use this as a base for more sophisticated bots.
"""
def __init__(self, host, nicks, gecos, channels):
self.nicks = nicks
self.channels = channels
self.waiting = True
self._spool = []
SmartIRCHandler.__init__(self, host, nicks[0], gecos)
def despool(self, target, lines):
"""Slowly despool a bunch of lines to a target
Since the IRC server will block all output if we send it too
fast, use this to send large multi-line responses.
"""
self._spool.append((target, list(lines)))
def heartbeat(self):
SmartIRCHandler.heartbeat(self)
# Despool data
if self._spool:
# Take the first one on the queue, and put it on the end
which = self._spool[0]
del self._spool[0]
self._spool.append(which)
# Despool a line
target, lines = which
if lines:
line = lines[0]
target.msg(line)
del lines[0]
else:
self._spool.remove(which)
def announce(self, text):
for c in self.channels:
self.write(['PRIVMSG', c], text)
def err(self, exception):
SmartIRCHandler.err(self, exception)
self.announce('*bzert*')
def cmd_001(self, sender, forum, addl):
# Welcome to IRC
self.nick = addl[0]
for c in self.channels:
self.write(['JOIN'], c)
def cmd_433(self, sender, forum, addl):
# Nickname already in use
self.nicks.append(self.nicks.pop(0))
self.write(['NICK', self.nicks[0]])
def cmd_NICK(self, sender, forum, addl):
if addl[0] == self.nick:
self.nick = sender.name()
print(self.nick)
def writable(self):
if not self.waiting:
return asynchat.async_chat.writable(self)
else:
return False
def write(self, *args):
SmartIRCHandler.write(self, *args)
def close(self, final=False):
SmartIRCHandler.close(self)
if not final:
self.dbg("Connection closed, reconnecting...")
self.waiting = True
self.connected = 0
# Wait a bit and reconnect
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.add_timer(23, lambda : self.connect(self.host))
def handle_close(self):
self.close()
##
## Miscellaneous IRC functions
##
def unpack_nuhost(nuhost):
"""Unpack nick!user@host
Frequently, the first argument in a server message is in
nick!user@host format. You can just pass your whole argument list
to this function and get back a tuple containing:
(nick, user, host)
"""
try:
[nick, uhost] = nuhost[0].split('!', 1)
[user, host] = uhost.split('@', 1)
except ValueError:
raise ValueError("not in nick!user@host format")
return (nick, user, host)
def run_forever(timeout=2.0):
"""Run your clients forever.
Just a handy front-end to asyncore.loop, so you don't have to import
asyncore yourself.
"""
asyncore.loop(timeout)

File diff suppressed because it is too large Load Diff

View File

@ -1,196 +0,0 @@
import math
pi2 = math.pi * 2
def rotatePoint(point, angle):
"""Assuming 0,0 is the center, rotate the given point around it."""
x,y = point
r = math.sqrt(x**2 + y**2)
if r == 0:
return 0, 0
theta = math.acos(x/r)
if y < 0:
theta = -theta
theta = theta + angle
return int(round(r*math.cos(theta))), int(round(r*math.sin(theta)))
def rotatePoly(points, angle):
"""Rotate the given list of points around 0,0 by angle."""
return [ rotatePoint(point, angle) for point in points ]
def displace(point, disp, limits):
"""Displace point by disp, wrapping around limits."""
x = (point[0] + disp[0])
while x >= limits[0]:
x = x - limits[0]
while x < 0:
x = x + limits[0]
y = (point[1] + disp[1])
while y >= limits[1]:
y = y - limits[1]
while y < 0:
y = y + limits[1]
return x,y
def displacePoly(points, disp, limits, coordSequence=False):
"""Displace each point (x,y) in 'points' by 'disp' (x,y). The limits of
the drawing space are assumed to be at x=0, y=0 and x=limits[0],
y=limits[1]. If the poly overlaps the edge of the drawing space, the
poly is duplicated on each side.
@param coordSequence: If true, the coordinates are returned as a sequence -
x1, y1, x2, y2, ... This is need by some PIL drawing
commands.
@returns: A list of polys, displaced by disp
"""
xDup = 0; yDup = 0
maxX, maxY = limits
basePoints = []
for point in points:
x,y = int(point[0] + disp[0]), int(point[1] + disp[1])
# Check if duplication is needed on each axis
if x > maxX:
# If this is negative, then we need to duplicate in the negative
# direction.
xDup = -1
elif x < 0:
xDup = 1
if y > maxY:
yDup = -1
elif y < 0:
yDup = 1
basePoints.append( (x,y) )
polys = [basePoints]
if xDup:
polys.append([(x + maxX*xDup, y) for x,y in basePoints] )
if yDup:
polys.append([(x, maxY*yDup + y) for x,y in basePoints] )
if xDup and yDup:
polys.append([(x+maxX*xDup, maxY*yDup+y) for x,y in basePoints])
# Switch coordinates to sequence mode.
# (x1, y1, x2, y2) instead of ((x1, y1), (x2, y2))
if coordSequence:
seqPolys = []
for poly in polys:
points = []
for point in poly:
points.extend(point)
seqPolys.append(points)
polys = seqPolys
return polys
def polar2cart(r, theta):
"""Return the cartesian coordinates for r, theta."""
x = r*math.cos(theta)
y = r*math.sin(theta)
return x,y
def minShift(center, point, limits):
"""Get the minimum distances between the two points, given that the board
wraps at the givin limits."""
dx = point[0] - center[0]
if dx < -limits[0]/2.0:
dx = point[0] + limits[0] - center[0]
elif dx > limits[0]/2.0:
dx = point[0] - (center[0] + limits[0])
dy = point[1] - center[1]
if dy < - limits[1]/2.0:
dy = point[1] + limits[1] - center[1]
elif dy > limits[1]/2.0:
dy = point[1] - (limits[1] + center[1])
return dx, dy
def relativePolar(center, point, limits):
"""Returns the angle, from zero, to the given point assuming this
center is the origin. Take into account wrapping round the limits of the board.
@returns: r, theta
"""
dx, dy = minShift(center, point, limits)
r = math.sqrt(dx**2 + dy**2)
theta = math.acos(dx/r)
if dy < 0:
theta = pi2 - theta
return r, theta
def reduceAngle(angle):
"""Reduce the angle such that it is in 0 <= angle < 2pi"""
return angle % pi2
def angleDiff(angle1, angle2):
"""Returns the difference between the two angles. They are assumed
to be in radians, and must be in the range 0 <= angle < 2*pi.
@raises AssertionError: The angles given must be in the range 0 <= angle < 2pi
@returns: The minimum distance between the two angles; The distance
is negative if angle2 leads angle1 (clockwise)..
"""
ret = (angle2 - angle1) % pi2
if ret > math.pi:
ret -= pi2
return ret
def getDist(point1, point2):
"""Returns the distance between point1 and point2."""
dx = point2[0] - point1[0]
dy = point2[1] - point1[1]
return math.sqrt(dx**2 + dy**2)
def segmentCircleCollision(segment, center, radius):
"""Return True if the given circle touches the given line segment.
@param segment: A list of two points [(x1,y1), (x2, y2)] that define
the line segment.
@param center: The center point of the circle.
@param radius: The radius of the circle.
@returns: True if the the circle touches the line segment, False otherwise.
"""
a = getDist(segment[0], center)
c = getDist(segment[1], center)
base = getDist(segment[0], segment[1])
# If we're close enough to the end points, then we're close
# enough to the segment.
if a < radius or c < radius:
return True
# First we find the are of the triangle formed by the line segment
# and point. I use Heron's formula for the area. Using this, we'll
# find the distance d from the point to the line. We'll later make
# sure that the collision is with the line segment, and not just the
# line.
s = (a + c + base)/2
A = math.sqrt(s*(s - a)*(s - c)*(s - base))
d = 2*A/base
# print s, a, c, A, d, radius
# If the distance from the point to the line is more than the
# target radius, this isn't a hit.
if d > radius:
return False
# If the distance from an endpoint to the intersection between
# our line segment and the line perpendicular to it that passes through
# the point is longer than the line segment, then this isn't a hit.
elif math.sqrt(a**2 - d**2) > base or \
math.sqrt(c**2 - d**2) > base:
return False
else:
# The triangle is acute, that means we're close enough.
return True

View File

@ -1,392 +0,0 @@
import fcntl
import math
import os
import random
import cgi
from sets import Set as set
from ctf import teams, html, paths
from cStringIO import StringIO
from urllib import unquote, quote
import Tank
class NotEnoughPlayers(Exception):
pass
class Pflanzarr:
SPACING = 150
def __init__(self, dir):
"""Initialize a new game of Pflanzarr.
@param dir: The data directory."""
# Setup the game environment
self._setupDirectories(dir)
# Figure out what game number this is.
self.gameNum = self._getGameNum()
self.gameFilename = os.path.join(self._resultsDir, '%04d.html' % self.gameNum)
tmpPlayers = os.listdir(self._playerDir)
players = []
AIs = {}
for fn in tmpPlayers:
p = unquote(fn)
if (not (p.startswith('.')
or p.endswith('#')
or p.endswith('~'))
and teams.exists(p)):
players.append(p)
AIs[p] = open(os.path.join(self._playerDir, fn)).read()
defaultAIs = self._getDefaultAIs(dir)
if len(players) < 1:
raise NotEnoughPlayers()
# The one is added to ensure that there is at least one house
# bot.
cols = int(math.ceil(math.sqrt(len(players) + 1)))
cols = max(cols, 2)
rows = int(math.ceil(len(players)/float(cols)))
rows = max(rows, 2)
self._board = (cols*self.SPACING, rows*self.SPACING)
while len(players) < cols*rows:
players.append(None)
self._tanks = []
for i in range(cols):
for j in range(rows):
startX = i*self.SPACING + self.SPACING/2
startY = j*self.SPACING + self.SPACING/2
player = random.choice(players)
players.remove(player)
color = '#' + teams.color(player)
tank = Tank.Tank( player, (startX, startY), color,
self._board)
if player == None:
tank.set_program(random.choice(defaultAIs))
else:
tank.set_program(AIs[player])
self._tanks.append(tank)
# We only want to make these once, so we do it here.
self._tanksByX = list(self._tanks)
self._tanksByY = list(self._tanks)
self._deadTanks = set()
def run(self, maxTurns=None):
kills = {}
for tank in self._tanks:
kills[tank] = set()
# Open HTML output
hdr = StringIO()
hdr.write('<script type="application/javascript" src="../tanks.js"></script>\n'
'<script type="application/javascript">\n')
hdr.write('turns = [%d, %d,[\n' % self._board)
# Describe tanks
for tank in self._tanks:
tank.describe(hdr)
hdr.write('],\n')
hdr.write('[\n')
turn = 0
lastTurns = 3
while ((maxTurns is None) or turn < maxTurns) and lastTurns > 0:
if len(self._tanks) - len(self._deadTanks) < 2:
lastTurns = lastTurns - 1
near = self._getNear()
liveTanks = set(self._tanks).difference(self._deadTanks)
for tank in liveTanks:
# Shoot now, if we said to shoot last turn
dead = tank.fire( near[tank] )
kills[tank] = kills[tank].union(dead)
self._killTanks(dead, 'Shot by %s' % cgi.escape(tank.name or teams.house))
for tank in liveTanks:
# We also check for collisions here, while we're at it.
dead = tank.sense( near[tank] )
kills[tank] = kills[tank].union(dead)
self._killTanks(dead, 'Collision')
hdr.write(' [\n')
for tank in self._tanks:
tank.draw(hdr)
hdr.write(' ],\n')
# Have the live tanks do their turns
for tank in self._tanksByX:
tank.execute()
turn = turn + 1
# Removes tanks from their own kill lists.
for tank in kills:
if tank in kills[tank]:
kills[tank].remove(tank)
for tank in self._tanks:
self._outputErrors(tank)
hdr.write(']];\n')
hdr.write('</script>\n')
# Decide on the winner
winner = self._chooseWinner(kills)
self.winner = winner.name
# Now generate HTML body
body = StringIO()
body.write(' <canvas id="battlefield" width="%d" height="%d">\n' % self._board)
body.write(' Sorry, you need an HTML5-capable browser to see this.\n'
' </canvas>\n'
' <p>\n')
if self.gameNum > 0:
body.write(' <a href="%04d.html">&larr; Prev</a> |' %
(self.gameNum - 1))
body.write(' <a href="%04d.html">Next &rarr;</a> |' %
(self.gameNum + 1))
body.write(' <span id="fps">0</span> fps\n'
' </p>\n'
' <table class="results">\n'
' <tr>\n'
' <th>Team</th>\n'
' <th>Kills</th>\n'
' <th>Cause of Death</th>\n'
' </tr>\n')
tanks = self._tanks[:]
tanks.remove(winner)
tanks[0:0] = [winner]
for tank in tanks:
if tank is winner:
rowStyle = ('style="font-weight: bold; '
'color: #000; '
'background-color: %s;"' % tank.color)
else:
rowStyle = 'style="background-color:%s; color: #000;"' % tank.color
if tank.name:
name = cgi.escape(tank.name)
else:
name = teams.house
body.write('<tr %s><td>%s</td><td>%d</td><td>%s</td></tr>' %
(rowStyle,
name,
len(kills[tank]),
cgi.escape(tank.deathReason)))
body.write(' </table>\n')
links='''<h3>Tanks</h3>
<ul>
<li><a href="../docs.html">Docs</a></li>
<li><a href="../results.cgi">Results</a></li>
<li><a href="../submit.html">Submit</a></li>
<li><a href="../errors.cgi">My Errors</a></li>
</ul>
'''
# Write everything out
html.write(self.gameFilename,
'Tanks round %d' % self.gameNum,
body.getvalue(),
hdr=hdr.getvalue(),
links=links,
onload='start(turns);')
def _killTanks(self, tanks, reason):
for tank in tanks:
if tank in self._tanksByX:
self._tanksByX.remove(tank)
if tank in self._tanksByY:
self._tanksByY.remove(tank)
tank.die(reason)
self._deadTanks = self._deadTanks.union(tanks)
def _chooseWinner(self, kills):
"""Choose a winner. In case of a tie, live tanks prevail, in case
of further ties, a winner is chosen at random. This outputs the winner
to the winners file and outputs a results table html file."""
tanks = list(self._tanks)
def winSort(t1, t2):
"""Sort by # of kill first, then by life status."""
result = cmp(len(kills[t1]), len(kills[t2]))
if result != 0:
return result
if t1.isDead and not t2.isDead:
return -1
elif not t1.isDead and t2.isDead:
return 1
else:
return 0
tanks.sort(winSort)
tanks.reverse()
# Get the list of potential winners
winners = []
for i in range(len(tanks)):
if len( kills[tanks[0]] ) == len( kills[tanks[i]] ) and \
tanks[0].isDead == tanks[i].isDead:
winners.append(tanks[i])
else:
break
winner = random.choice(winners)
return winner
def _outputErrors(self, tank):
"""Output errors for each team."""
out = tank.program.get_output()
print 'Errors %r: %r' % (tank, out)
if tank.name == None:
return
fileName = os.path.join(self._errorDir, quote(tank.name, ''))
open(fileName, 'w').write(tank.program.get_output())
def _getNear(self):
"""A dictionary of the set of tanks nearby each tank. Nearby is
defined as within the square centered the tank with side length equal
twice the sensor range. Only a few tanks within the set (those in the
corners of the square) should be outside the sensor range."""
self._tanksByX.sort(lambda t1, t2: cmp(t1.pos[0], t2.pos[0]))
self._tanksByY.sort(lambda t1, t2: cmp(t1.pos[1], t2.pos[1]))
nearX = {}
nearY = {}
for tank in self._tanksByX:
nearX[tank] = set()
nearY[tank] = set()
numTanks = len(self._tanksByX)
offset = 1
for index in range(numTanks):
cTank = self._tanksByX[index]
maxRange = cTank.SENSOR_RANGE + cTank.RADIUS + 1
near = set([cTank])
for i in [(j + index) % numTanks for j in range(1, offset)]:
near.add(self._tanksByX[i])
while offset < numTanks:
nTank = self._tanksByX[(index + offset) % numTanks]
if (index + offset >= numTanks and
self._board[0] + nTank.pos[0] - cTank.pos[0] < maxRange):
near.add(nTank)
offset = offset + 1
elif (index + offset < numTanks and
nTank.pos[0] - cTank.pos[0] < maxRange ):
near.add(nTank)
offset = offset + 1
else:
break
if offset > 1:
offset = offset - 1
for tank in near:
nearX[tank] = nearX[tank].union(near)
offset = 1
for index in range(numTanks):
cTank = self._tanksByY[index]
maxRange = cTank.SENSOR_RANGE + cTank.RADIUS + 1
near = set([cTank])
for i in [(j + index) % numTanks for j in range(1, offset)]:
near.add(self._tanksByY[i])
while offset < numTanks:
nTank = self._tanksByY[(index + offset) % numTanks]
if (index + offset < numTanks and
nTank.pos[1] - cTank.pos[1] < maxRange):
near.add(nTank)
offset = offset + 1
elif (index + offset >= numTanks and
self._board[1] + nTank.pos[1] - cTank.pos[1] < maxRange):
near.add(nTank)
offset = offset + 1
else:
break
if offset > 1:
offset = offset - 1
for tank in near:
nearY[tank] = nearY[tank].union(near)
near = {}
for tank in self._tanksByX:
near[tank] = nearX[tank].intersection(nearY[tank])
near[tank].remove(tank)
return near
def _setupDirectories(self, dir):
"""Setup all the directories needed by the game."""
if not os.path.exists(dir):
os.mkdir(dir)
self._dir = dir
# Don't run more than one game at the same time.
self._lockFile = open(os.path.join(dir, '.lock'), 'a')
try:
fcntl.flock(self._lockFile, fcntl.LOCK_EX|fcntl.LOCK_NB)
except:
sys.exit(1)
# Setup all the directories we'll need.
self._resultsDir = os.path.join(dir, 'results')
self._errorDir = os.path.join(dir, 'errors')
self._playerDir = os.path.join(dir, 'players')
def _getDefaultAIs(self, basedir):
"""Load all the house bot AIs."""
defaultAIs = []
path = os.path.join(basedir, 'house')
files = os.listdir(path)
for fn in files:
if fn.startswith('.'):
continue
fn = os.path.join(path, fn)
f = open(fn)
defaultAIs.append(f.read())
return defaultAIs
def _getGameNum(self):
"""Figure out what game number this is from the past games played."""
games = os.listdir(self._resultsDir)
games.sort()
if games:
fn = games[-1]
s, _ = os.path.splitext(fn)
return int(s) + 1
else:
return 0
if __name__ == '__main__':
import sys, traceback
try:
p = Pflanzarr(sys.argv[1])
p.run(int(sys.argv[3]))
except:
traceback.print_exc()
print "Usage: Pflanzarr.py dataDirectory #turns"

View File

@ -1,100 +0,0 @@
#! /usr/bin/python
import forf
import random
import rfc822
from cStringIO import StringIO
from math import pi
def deg2rad(deg):
return float(deg) * pi / 180
def rad2deg(rad):
return int(rad * 180 / pi)
class Environment(forf.Environment):
def __init__(self, tank, stdout):
forf.Environment.__init__(self)
self.tank = tank
self.stdout = stdout
def err(self, msg):
self.stdout.write('Error: %s\n' % msg)
def msg(self, msg):
self.stdout.write('%s\n' % msg)
def cmd_random(self, data):
high = data.pop()
ret = random.randrange(high)
data.push(ret)
def cmd_fireready(self, data):
ret = self.tank.fireReady()
data.push(ret)
def cmd_sensoractive(self, data):
sensor = data.pop()
try:
ret = int(self.tank.getSensorState(sensor))
except KeyError:
ret = 0
data.push(ret)
def cmd_getturret(self, data):
rad = self.tank.getTurretAngle()
deg = rad2deg(rad)
data.push(deg)
def cmd_setled(self, data):
self.tank.setLED()
def cmd_fire(self, data):
self.tank.setFire()
def cmd_move(self, data):
right = data.pop()
left = data.pop()
self.tank.setMove(left, right)
def cmd_setturret(self, data):
deg = data.pop()
rad = deg2rad(deg)
self.tank.setTurretAngle(rad)
class Program:
def __init__(self, tank, source):
self.tank = tank
self.stdout = StringIO()
self.env = Environment(self.tank, self.stdout)
code_str = self.read_source(StringIO(source))
self.env.parse_str(code_str)
def get_output(self):
return self.stdout.getvalue()
def read_source(self, f):
"""Read in a tank program, establish sensors, and return code.
Tank programs are stored as rfc822 messages. The header
block includes fields for sensors (Sensor:)
and other crap which may be used later.
"""
message = rfc822.Message(f)
print 'reading tank %s' % message['Name']
sensors = message.getallmatchingheaders('Sensor')
for s in sensors:
k, v = s.strip().split(':')
r, angle, width, turret = [int(p) for p in v.split()]
r = float(r) / 100
angle = deg2rad(angle)
width = deg2rad(width)
self.tank.addSensor(r, angle, width, turret)
return message.fp.read()
def run(self):
self.env.eval()

View File

@ -1,426 +0,0 @@
import math
import random
from sets import Set as set
import GameMath as gm
import Program
from cStringIO import StringIO
class Tank(object):
# How often, in turns, that we can fire.
FIRE_RATE = 20
# How far the laser shoots from the center of the tank
FIRE_RANGE = 45.0
# The radius of the tank, from the center of the turret.
# This is what is used for collision and hit detection.
RADIUS = 7.5
# Max speed, in pixels
MAXSPEED = 7.0
# Max acceleration, as a fraction of speed.
MAXACCEL = 35
# Sensor range, in pixels
SENSOR_RANGE = 90.0
# Max turret turn rate, in radians
TURRET_TURN_RATE = math.pi/10
# The max number of sensors/timers/toggles
SENSOR_LIMIT = 10
def __init__(self, name, pos, color, boardSize, angle=None, tAngle=None):
"""Create a new tank.
@param name: The name name of the tank. Stored in self.name.
@param pos: The starting position of the tank (x,y)
@param color: The color of the tank.
@param boardSize: The size of the board. (maxX, maxY)
@param angle: The starting angle of the tank, defaults to random.
@param tAngle: The starting turretAngle of the tank, defaults to random.
"""
self.name = name
assert len(pos) == 2 and pos[0] > 0 and pos[1] > 0, \
'Bad starting position: %s' % str(pos)
self.pos = pos
# The last speed of each tread (left, right)
self._lastSpeed = 0.0, 0.0
# The next speed that the tank should try to attain.
self._nextMove = 0,0
# When set, the led is drawn on the tank.
self.led = False
assert len(boardSize) == 2 and boardSize[0] > 0 and boardSize[1] > 0
# The limits of the playfield (maxX, maxY)
self._limits = boardSize
# The current angle of the tank.
if angle is None:
self._angle = random.random()*2*math.pi
else:
self._angle = angle
# The current angle of the turret
if tAngle is None:
self._tAngle = random.random()*2*math.pi
else:
self._tAngle = tAngle
self.color = color
# You can't fire until fireReady is 0.
self._fireReady = self.FIRE_RATE
# Means the tank will fire at it's next opportunity.
self._fireNow = False
# True when the tank has fired this turn (for drawing purposes)
self._fired = False
# What the desired turret angle should be (from the front of the tank).
# None means the turret should stay stationary.
self._tGoal = None
# Holds the properties of each sensor
self._sensors = []
# Holds the state of each sensor
self._sensorState = []
# The tanks toggle memory
self.toggles = []
# The tanks timers
self._timers = []
# Is this tank dead?
self.isDead = False
# Death reason
self.deathReason = 'survived'
# Something to log to
self.stdout = StringIO()
def __repr__(self):
return '<tank: %s, (%d, %d)>' % (self.name, self.pos[0], self.pos[1])
def fire(self, near):
"""Shoots, if ordered to and able. Returns the set of tanks
destroyed."""
killed = set()
if self._fireReady > 0:
# Ignore the shoot order
self._fireNow = False
if self._fireNow:
self._fireNow = False
self._fireReady = self.FIRE_RATE
self._fired = True
firePoint = gm.polar2cart(self.FIRE_RANGE,
self._angle + self._tAngle)
for tank in near:
enemyPos = gm.minShift(self.pos, tank.pos, self._limits)
if gm.segmentCircleCollision(((0,0), firePoint), enemyPos,
self.RADIUS):
killed.add(tank)
else:
self._fired = False
return killed
def addSensor(self, range, angle, width, attachedTurret=False):
"""Add a sensor to this tank.
@param angle: The angle, from the tanks front and going clockwise,
of the center of the sensor. (radians)
@param width: The width of the sensor (radians).
@param range: The range of the sensor (percent)
@param attachedTurret: If set, the sensor moves with the turret.
"""
assert range >=0 and range <= 1, "Invalid range value."
if len(self._sensors) >= self.SENSOR_LIMIT:
raise ValueError("You can only have 10 sensors.")
range = range * self.SENSOR_RANGE
if attachedTurret:
attachedTurret = True
else:
attachedTurret = False
self._sensors.append((range, angle, width, attachedTurret))
self._sensorState.append(False)
def getSensorState(self, key):
return self._sensorState[key]
def setMove(self, left, right):
"""Parse the speed values given, and set them for the next move."""
self._nextMove = left, right
def getTurretAngle(self):
return self._tAngle
def setTurretAngle(self, angle=None):
"""Set the desired angle of the turret. No angle means the turret
should remain stationary."""
if angle is None:
self._tGoal = None
else:
self._tGoal = gm.reduceAngle(angle)
def setFire(self):
"""Set the tank to fire, next turn."""
self._fireNow = True
def fireReady(self):
"""Returns True if the tank can fire now."""
return self._fireReady == 0
def setLED(self):
self.led = True
def set_program(self, text):
"""Set the program for this tank."""
self.program = Program.Program(self, text)
def execute(self):
"""Execute this tanks program."""
self.led = False
self.program.run()
self._move(self._nextMove[0], self._nextMove[1])
self._moveTurret()
if self._fireReady > 0:
self._fireReady = self._fireReady - 1
def sense(self, near):
"""Detect collisions and trigger sensors. Returns the set of
tanks collided with, plus this one. We do both these steps at once
mainly because all the data is available."""
near = list(near)
polar = []
for tank in near:
polar.append(gm.relativePolar(self.pos, tank.pos, self._limits))
for sensorId in range(len(self._sensors)):
# Reset the sensor
self._sensorState[sensorId] = False
dist, sensorAngle, width, tSens = self._sensors[sensorId]
# Adjust the sensor angles according to the tanks angles.
sensorAngle = sensorAngle + self._angle
# If the angle is tied to the turret, add that too.
if tSens:
sensorAngle = sensorAngle + self._tAngle
while sensorAngle >= 2*math.pi:
sensorAngle = sensorAngle - 2*math.pi
for i in range(len(near)):
r, theta = polar[i]
# Find the difference between the object angle and the sensor.
# The max this can be is pi, so adjust for that.
dAngle = gm.angleDiff(theta, sensorAngle)
rCoord = gm.polar2cart(dist, sensorAngle - width/2)
lCoord = gm.polar2cart(dist, sensorAngle + width/2)
rightLine = ((0,0), rCoord)
leftLine = ((0,0), lCoord)
tankRelPos = gm.minShift(self.pos, near[i].pos, self._limits)
if r < (dist + self.RADIUS):
if abs(dAngle) <= (width/2) or \
gm.segmentCircleCollision(rightLine, tankRelPos,
self.RADIUS) or \
gm.segmentCircleCollision(leftLine, tankRelPos,
self.RADIUS):
self._sensorState[sensorId] = True
break
# Check for collisions here, since we already have all the data.
collided = set()
for i in range(len(near)):
r, theta = polar[i]
if r < (self.RADIUS + near[i].RADIUS):
collided.add(near[i])
# Add this tank (a collision kills both, after all
if collided:
collided.add(self)
return collided
def die(self, reason):
self.isDead = True
self.deathReason = reason
def _moveTurret(self):
if self._tGoal is None or self._tAngle == self._tGoal:
return
diff = gm.angleDiff(self._tGoal, self._tAngle)
if abs(diff) < self.TURRET_TURN_RATE:
self._tAngle = self._tGoal
elif diff > 0:
self._tAngle = gm.reduceAngle(self._tAngle - self.TURRET_TURN_RATE)
else:
self._tAngle = gm.reduceAngle(self._tAngle + self.TURRET_TURN_RATE)
def _move(self, lSpeed, rSpeed):
assert abs(lSpeed) <= 100, "Bad speed value: %s" % lSpeed
assert abs(rSpeed) <= 100, "Bad speed value: %s" % rSpeed
# Handle acceleration
if self._lastSpeed[0] < lSpeed and \
self._lastSpeed[0] + self.MAXACCEL < lSpeed:
lSpeed = self._lastSpeed[0] + self.MAXACCEL
elif self._lastSpeed[0] > lSpeed and \
self._lastSpeed[0] - self.MAXACCEL > lSpeed:
lSpeed = self._lastSpeed[0] - self.MAXACCEL
if self._lastSpeed[1] < rSpeed and \
self._lastSpeed[1] + self.MAXACCEL < rSpeed:
rSpeed = self._lastSpeed[1] + self.MAXACCEL
elif self._lastSpeed[1] > rSpeed and \
self._lastSpeed[1] - self.MAXACCEL > rSpeed:
rSpeed = self._lastSpeed[1] - self.MAXACCEL
self._lastSpeed = lSpeed, rSpeed
# The simple case
if lSpeed == rSpeed:
fSpeed = self.MAXSPEED*lSpeed/100
x = fSpeed*math.cos(self._angle)
y = fSpeed*math.sin(self._angle)
# Adjust our position
self._reposition((x,y), 0)
return
# The works as follows:
# The tank drives around in a circle of radius r, which is some
# offset on a line perpendicular to the tank. The distance it travels
# around the circle varies with the speed of each tread, and is
# such that each side of the tank moves an equal angle around the
# circle.
L = self.MAXSPEED * lSpeed/100.0
R = self.MAXSPEED * rSpeed/100.0
friction = .75 * abs(L-R)/(2.0*self.MAXSPEED)
L = L * (1 - friction)
R = R * (1 - friction)
# Si is the speed of the tread on the inside of the turn,
# So is the speed on the outside of the turn.
# dir is to note the direction of rotation.
if abs(L) > abs(R):
Si = R; So = L
dir = 1
else:
Si = L; So = R
dir = -1
# The width of the tank...
w = self.RADIUS * 2
# This is the angle that will determine the circle the tank travels
# around.
# theta = math.atan((So - Sl)/w)
# This is the distance from the outer tread to the center of the
# circle formed by it's movement.
r = w*So/(So - Si)
# The fraction of the circle traveled is equal to the speed of
# the outer tread over the circumference of the circle.
# Ft = So/(2*pi*r)
# The angle traveled is equal to the Fraction traveled * 2 * pi
# This reduces to a simple: So/r
# We multiply it by dir to adjust for the direction of rotation
theta = So/r * dir
# These are the offsets from the center of the circle, given that
# the tank is turned in some direction. The tank is facing
# perpendicular to the circle
# So far everything has been relative to the outer tread. At this
# point, however, we need to move relative to the center of the
# tank. Hence the adjustment in r.
x = -math.cos( self._angle + math.pi/2*dir ) * (r - w/2.0)
y = -math.sin( self._angle + math.pi/2*dir ) * (r - w/2.0)
# Now we just rotate the tank's position around the center of the
# circle to get the change in coordinates.
mx, my = gm.rotatePoint((x,y), theta)
mx = mx - x
my = my - y
# Finally, we shift the tank relative to the playing field, and
# rotate it by theta.
self._reposition((mx, my), theta)
def _reposition(self, move, angleChange):
"""Move the tank by x,y = move, and change it's angle by angle.
I assume the tanks move slower than the boardSize."""
x = self.pos[0] + move[0]
y = self.pos[1] + move[1]
self._angle = self._angle + angleChange
if x < 0:
x = self._limits[0] + x
elif x > self._limits[0]:
x = x - self._limits[0]
if y < 0:
y = self._limits[1] + y
elif y > self._limits[1]:
y = y - self._limits[1]
self.pos = round(x), round(y)
while self._angle < 0:
self._angle = self._angle + math.pi * 2
while self._angle > math.pi * 2:
self._angle = self._angle - math.pi * 2
def describe(self, f):
"""Output a description of this tank"""
f.write(' ["%s",[' % self.color)
for i in range(len(self._sensors)):
dist, sensorAngle, width, tSens = self._sensors[i]
f.write('[%d,%.2f,%.2f,%d],' % (dist, sensorAngle, width, tSens))
f.write(']],\n')
def draw(self, f):
"""Output this tank's state as JSON.
[color, x, y, angle, turret_angle, led, fired]
"""
if self.isDead:
f.write(' 0,\n')
else:
flags = (self._fired << 0) | (self.led << 1)
sensors = 0
for i in range(len(self._sensorState)):
sensors |= self._sensorState[i] << i
f.write(' [%d,%d,%.2f,%.2f,%d,%d],\n' % (self.pos[0],
self.pos[1],
self._angle,
self._tAngle,
flags,
sensors))

View File

View File

@ -1,26 +0,0 @@
import xml.sax.saxutils
def mkDocTable(objects):
objects.sort(lambda o1, o2: cmp(o1.__doc__, o2.__doc__))
for object in objects:
if object.__doc__ is None:
print '<table><tr><th>%s<tr><td colspan=2>Bad object</table>' % \
xml.sax.saxutils.escape(str(object))
continue
text = object.__doc__
lines = text.split('\n')
head = lines[0].strip()
head = xml.sax.saxutils.escape(head)
body = []
for line in lines[1:]:
line = line.strip() #xml.sax.saxutils.escape( line.strip() )
line = line.replace('.', '.<BR>')
body.append(line)
body = '\n'.join(body)
print '<DL><DT><DIV class="tab">%s</DIV></DT><DD>%s</DD></DL>' % (head, body)
#print '<tr><th>%s<th>Intentionally blank<th><tr><td colspan=3>%s' % (head, body)

View File

@ -1,290 +0,0 @@
#! /usr/bin/python
"""A shitty FORTH interpreter
15:58 <SpaceHobo> WELCOME TO FORF!
15:58 <SpaceHobo> *PUNCH*
"""
import operator
class ParseError(Exception):
pass
class Overflow(Exception):
pass
class Underflow(Exception):
pass
class Stack:
def __init__(self, init=None, size=50):
self.size = size
self.stack = init or []
def __str__(self):
if not self.stack:
return '{}'
guts = ' '.join(repr(i) for i in self.stack)
return '{ %s }' % guts
__repr__ = __str__
def push(self, *values):
for val in values:
if len(self.stack) == self.size:
raise Overflow()
self.stack.append(val)
def extend(self, other):
self.stack.extend(other.stack)
def dup(self):
return Stack(init=self.stack[:], size=self.size)
def pop(self):
if not self.stack:
raise Underflow()
return self.stack.pop()
def mpop(self, n):
return [self.pop() for i in range(n)]
def __nonzero__(self):
return bool(self.stack)
class Environment:
def __init__(self, ticks=2000, codelen=500):
self.ticks = ticks
self.codelen = codelen
self.registers = [0] * 10
self.unfuncs = {'~' : operator.inv,
'!' : operator.not_,
'abs': operator.abs,
}
self.binfuncs = {'+' : operator.add,
'-' : operator.sub,
'*' : operator.mul,
'/' : operator.div,
'%' : operator.mod,
'**': operator.pow,
'&' : operator.and_,
'|' : operator.or_,
'^' : operator.xor,
'<<': operator.lshift,
'>>': operator.rshift,
'>' : operator.gt,
'>=': operator.ge,
'<' : operator.lt,
'<=': operator.le,
'=' : operator.eq,
'<>': operator.ne,
'!=': operator.ne,
}
self.data = Stack()
def get(self, s):
unfunc = self.unfuncs.get(s)
if unfunc:
return self.apply_unfunc(unfunc)
binfunc = self.binfuncs.get(s)
if binfunc:
return self.apply_binfunc(binfunc)
try:
return getattr(self, 'cmd_' + s)
except AttributeError:
return None
def apply_unfunc(self, func):
"""Apply a unary function"""
def f(data):
a = data.pop()
data.push(int(func(a)))
return f
def apply_binfunc(self, func):
"""Apply a binary function"""
def f(data):
a = data.pop()
b = data.pop()
data.push(int(func(b, a)))
return f
def run(self, s):
self.parse_str(s)
self.eval()
def parse_str(self, s):
tokens = s.strip().split()
tokens.reverse() # so .parse can tokens.pop()
self.code = self.parse(tokens)
def parse(self, tokens, token=0, depth=0):
if depth > 4:
raise ParseError('Maximum recursion depth exceeded at token %d' % token)
code = []
while tokens:
val = tokens.pop()
token += 1
f = self.get(val)
if f:
code.append(f)
elif val == '(':
# Comment
while val != ')':
val = tokens.pop()
token += 1
elif val == '{}':
# Empty block
code.append(Stack())
elif val == '{':
block = self.parse(tokens, token, depth+1)
code.append(block)
elif val == '}':
break
else:
# The only literals we support are ints
try:
code.append(int(val))
except ValueError:
raise ParseError('Invalid literal at token %d (%s)' % (token, val))
if len(code) > self.codelen:
raise ParseError('Code stack overflow')
# Reverse so we can .pop()
code.reverse()
return Stack(code, size=self.codelen)
def eval(self):
ticks = self.ticks
code_orig = self.code.dup()
while self.code and ticks:
ticks -= 1
val = self.code.pop()
try:
if callable(val):
val(self.data)
else:
self.data.push(val)
except Underflow:
self.err('Stack underflow at proc %r' % (val))
except Overflow:
self.err('Stack overflow at proc %r' % (val))
if self.code:
self.err('Ran out of ticks!')
self.code = code_orig
def err(self, msg):
print 'Error: %s' % msg
def msg(self, msg):
print msg
##
## Commands
##
def cmd_print(self, data):
a = data.pop()
self.msg(a)
def cmd_dumpstack(self, data):
a = data.pop()
self.msg('(dumpstack %d) %r' % (a, data.stack))
def cmd_dumpmem(self, data):
a = data.pop()
self.msg('(dumpmem %d) %r' % (a, self.registers))
def cmd_exch(self, data):
a, b = data.mpop(2)
data.push(a, b)
def cmd_dup(self, data):
a = data.pop()
data.push(a, a)
def cmd_pop(self, data):
data.pop()
def cmd_store(self, data):
a, b = data.mpop(2)
self.registers[a % 10] = b
def cmd_fetch(self, data):
a = data.pop()
data.push(self.registers[a % 10])
##
## Evaluation commands
##
def eval_block(self, block):
try:
self.code.extend(block)
except TypeError:
# If it's not a block, just append it
self.code.push(block)
def cmd_if(self, data):
block = data.pop()
cond = data.pop()
if cond:
self.eval_block(block)
def cmd_ifelse(self, data):
elseblock = data.pop()
ifblock = data.pop()
cond = data.pop()
if cond:
self.eval_block(ifblock)
else:
self.eval_block(elseblock)
def cmd_eval(self, data):
# Interestingly, this is the same as "1 exch if"
block = data.pop()
self.eval_block(block)
def cmd_call(self, data):
# Shortcut for "fetch eval"
self.cmd_fetch(data)
self.cmd_eval(data)
def repl():
env = Environment()
while True:
try:
s = raw_input('>8[= =] ')
except (KeyboardInterrupt, EOFError):
print
break
try:
env.run(s)
print env.data
except ParseError, err:
print r' \ nom nom nom, %s!' % err
print r' \ bye bye!'
if __name__ == '__main__':
import sys
import time
try:
import readline
except ImportError:
pass
if len(sys.argv) > 1:
s = open(sys.argv[1]).read()
env = Environment()
begin = time.time()
env.run(s)
end = time.time()
elapsed = end - begin
print 'Evaluated in %.2f seconds' % elapsed
else:
print 'WELCOME TO FORF!'
print '*PUNCH*'
repl()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -1,63 +1,106 @@
#! /usr/bin/python
#! /usr/bin/lua
import cgitb; cgitb.enable()
import cgi
import os
import fcntl
import string
function decode(str)
local hexdec = function(h)
return string.char(tonumber(h, 16))
end
str = string.gsub(str, "+", " ")
return string.gsub(str, "%%(%x%x)", hexdec)
end
from ctf import teams, html
function decode_query(query)
local ret = {}
def main():
f = cgi.FieldStorage()
for key, val in string.gfind(query, "([^&=]+)=([^&=]+)") do
ret[string.lower(decode(key))] = decode(val)
end
team = f.getfirst('team', '')
pw = f.getfirst('pw')
confirm_pw = f.getfirst('confirm_pw')
return ret
end
tmpl = string.Template('''
<p>
Pick a short team name: you'll be typing it a lot.
</p>
function escape(str)
str = string.gsub(str, "&", "&amp;")
str = string.gsub(str, "<", "&lt;")
str = string.gsub(str, ">", "&gt;")
return str
end
<form method="post" action="register.cgi">
<fieldset>
<legend>Registration information:</legend>
function djbhash(s)
local hash = 5380
for i=0,string.len(s) do
local c = string.byte(string.sub(s, i, i+1))
hash = math.mod(((hash * 32) + hash + c), 2147483647)
end
return string.format("%08x", hash)
end
<label>Team Name:</label>
<input type="text" name="team" />
<span class="error">$team_error</span><br />
function head(title)
print("Content-type: text/html")
print("")
print("<!DOCTYPE html>")
print("<html>")
print(" <head>")
print(" <title>")
print(title)
print(" </title")
print(' <link rel="stylesheet" href="ctf.css" type="text/css">')
print(" </head>")
print(" <body>")
print(" <h1>")
print(title)
print(" </h1>")
end
<label>Password:</label>
<input type="password" name="pw" />
<br />
function foot()
print(" </body>")
print("</html>")
os.exit()
end
<label>Confirm Password:</label>
<input type="password" name="confirm_pw" />
<span class="error">$pw_match_error</span><br />
if (os.getenv("REQUEST_METHOD") ~= "POST") then
print("405 Method not allowed")
print("Allow: POST")
print("Content-type: text/html")
print()
print("<h1>Method not allowed</h1>")
print("<p>I only speak POST. Sorry.</p>")
end
<input type="submit" value="Register" />
</fieldset>
</form>''')
if not (team and pw and confirm_pw): # If we're starting from the beginning?
body = tmpl.substitute(team_error='',
pw_match_error='')
elif teams.exists(team):
body = tmpl.substitute(team_error='Team team already taken',
pw_match_error='')
elif pw != confirm_pw:
body = tmpl.substitute(team_error='',
pw_match_error='Passwords do not match')
else:
teams.add(team, pw)
body = ('<p>Congratulations, <samp>%s</samp> is now registered. Go <a href="/">back to the front page</a> and start playing!</p>' % cgi.escape(team))
inlen = tonumber(os.getenv("CONTENT_LENGTH"))
if (inlen > 200) then
head("Bad team name")
print("<p>That's a bit on the long side, don't you think?</p>")
foot()
end
formdata = io.read(inlen)
f = decode_query(formdata)
html.serve('Team Registration', body)
team = f["t"]
if (not team) or (team == "dirtbags") then
head("Bad team name")
print("<p>Go back and try again.</p>")
foot()
end
hash = djbhash(team)
if __name__ == '__main__':
import sys, codecs
if io.open(hash) then
head("Team name taken")
print("<p>Either someone's already using that team name,")
print("or you found a hash collision. Either way, you're")
print("going to have to pick something else.</p>")
foot()
end
sys.stdout = codecs.getwriter('utf-8')(sys.stdout)
f = io.open(hash, "w"):write(team)
main()
head("Team registered")
print("<p>Team name: <samp>")
print(escape(team))
print("</samp></p>")
print("<p>Team token: <samp>")
print(hash)
print("</samp></p>")
print("<p><b>Save your team token somewhere</b>!")
print("You will need it to claim points.</p>")
foot()

16
www/register.html Normal file
View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<title>Team Registration</title>
<link rel="stylesheet" href="ctf.css" type="text/css">
</head>
<body>
<h1>Team Registration</h1>
<form method="post" action="register.cgi">
<label>Team Name:</label>
<input type="text" name="t">
<input type="submit" value="Register">
</form>
</body>
</html>