mirror of https://github.com/dirtbags/moth.git
Lua register.cgi
This commit is contained in:
parent
ab842bab4d
commit
d3707d912e
|
@ -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
|
|
35
ctf/html.py
35
ctf/html.py
|
@ -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))
|
|
|
@ -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()
|
|
72
ctf/teams.py
72
ctf/teams.py
|
@ -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]
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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();
|
|
||||||
|
|
|
@ -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
|
|
|
@ -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();
|
|
||||||
|
|
|
@ -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();
|
|
|
@ -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)
|
|
|
@ -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();
|
|
||||||
|
|
|
@ -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();
|
|
|
@ -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
|
|
|
@ -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'))
|
|
|
@ -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)
|
|
3720
site-packages/png.py
3720
site-packages/png.py
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
|
@ -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">← Prev</a> |' %
|
|
||||||
(self.gameNum - 1))
|
|
||||||
body.write(' <a href="%04d.html">Next →</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"
|
|
||||||
|
|
||||||
|
|
100
tanks/Program.py
100
tanks/Program.py
|
@ -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()
|
|
||||||
|
|
426
tanks/Tank.py
426
tanks/Tank.py
|
@ -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))
|
|
|
@ -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)
|
|
||||||
|
|
||||||
|
|
290
tanks/forf.py
290
tanks/forf.py
|
@ -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()
|
|
BIN
www/grunge.png
BIN
www/grunge.png
Binary file not shown.
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 10 KiB |
139
www/register.cgi
139
www/register.cgi
|
@ -1,63 +1,106 @@
|
||||||
#! /usr/bin/python
|
#! /usr/bin/lua
|
||||||
|
|
||||||
import cgitb; cgitb.enable()
|
function decode(str)
|
||||||
import cgi
|
local hexdec = function(h)
|
||||||
import os
|
return string.char(tonumber(h, 16))
|
||||||
import fcntl
|
end
|
||||||
import string
|
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():
|
for key, val in string.gfind(query, "([^&=]+)=([^&=]+)") do
|
||||||
f = cgi.FieldStorage()
|
ret[string.lower(decode(key))] = decode(val)
|
||||||
|
end
|
||||||
|
|
||||||
team = f.getfirst('team', '')
|
return ret
|
||||||
pw = f.getfirst('pw')
|
end
|
||||||
confirm_pw = f.getfirst('confirm_pw')
|
|
||||||
|
|
||||||
tmpl = string.Template('''
|
function escape(str)
|
||||||
<p>
|
str = string.gsub(str, "&", "&")
|
||||||
Pick a short team name: you'll be typing it a lot.
|
str = string.gsub(str, "<", "<")
|
||||||
</p>
|
str = string.gsub(str, ">", ">")
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
|
||||||
<form method="post" action="register.cgi">
|
function djbhash(s)
|
||||||
<fieldset>
|
local hash = 5380
|
||||||
<legend>Registration information:</legend>
|
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>
|
function head(title)
|
||||||
<input type="text" name="team" />
|
print("Content-type: text/html")
|
||||||
<span class="error">$team_error</span><br />
|
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>
|
function foot()
|
||||||
<input type="password" name="pw" />
|
print(" </body>")
|
||||||
<br />
|
print("</html>")
|
||||||
|
os.exit()
|
||||||
|
end
|
||||||
|
|
||||||
<label>Confirm Password:</label>
|
if (os.getenv("REQUEST_METHOD") ~= "POST") then
|
||||||
<input type="password" name="confirm_pw" />
|
print("405 Method not allowed")
|
||||||
<span class="error">$pw_match_error</span><br />
|
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?
|
inlen = tonumber(os.getenv("CONTENT_LENGTH"))
|
||||||
body = tmpl.substitute(team_error='',
|
if (inlen > 200) then
|
||||||
pw_match_error='')
|
head("Bad team name")
|
||||||
elif teams.exists(team):
|
print("<p>That's a bit on the long side, don't you think?</p>")
|
||||||
body = tmpl.substitute(team_error='Team team already taken',
|
foot()
|
||||||
pw_match_error='')
|
end
|
||||||
elif pw != confirm_pw:
|
formdata = io.read(inlen)
|
||||||
body = tmpl.substitute(team_error='',
|
f = decode_query(formdata)
|
||||||
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))
|
|
||||||
|
|
||||||
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__':
|
if io.open(hash) then
|
||||||
import sys, codecs
|
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()
|
|
@ -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>
|
Loading…
Reference in New Issue