mirror of https://github.com/dirtbags/moth.git
Initial effort to decouple from buildroot
This commit is contained in:
commit
00694e8472
|
@ -0,0 +1,2 @@
|
|||
*~
|
||||
*.pyc
|
|
@ -0,0 +1,53 @@
|
|||
BASE = /opt/ctf
|
||||
VAR = $(BASE)/var
|
||||
WWW = $(BASE)/www
|
||||
LIB = $(BASE)/lib
|
||||
BIN = $(BASE)/bin
|
||||
SBIN = $(BASE)/sbin
|
||||
BASE_URL = /ctf/
|
||||
|
||||
install:
|
||||
id ctf || useradd --system --shell /bin/false --home $(VAR) \
|
||||
--comment "Capture The Flag" ctf
|
||||
install --directory $(LIB) $(BIN) $(SBIN) $(LIB)/disabled
|
||||
install --directory --owner=ctf $(VAR)
|
||||
install --directory --owner=ctf $(WWW)
|
||||
install --directory --owner=ctf $(WWW)/puzzler
|
||||
install --directory --owner=ctf $(VAR)/points
|
||||
install --directory --owner=ctf $(VAR)/points/tmp
|
||||
install --directory --owner=ctf $(VAR)/points/cur
|
||||
install --directory --owner=ctf $(VAR)/flags
|
||||
|
||||
# Tanks has a lot of files.
|
||||
install --directory --owner=ctf $(VAR)/tanks
|
||||
install --directory --owner=ctf $(VAR)/tanks/results
|
||||
install --directory --owner=ctf $(VAR)/tanks/errors
|
||||
install --directory --owner=ctf $(VAR)/tanks/ai
|
||||
install --directory --owner=ctf $(VAR)/tanks/ai/players
|
||||
install --directory --owner=ctf $(VAR)/tanks/ai/house
|
||||
|
||||
echo 'VAR = "$(VAR)"' > ctf/paths.py
|
||||
echo 'WWW = "$(WWW)"' >> ctf/paths.py
|
||||
echo 'LIB = "$(LIB)"' >> ctf/paths.py
|
||||
echo 'BIN = "$(BIN)"' >> ctf/paths.py
|
||||
echo 'SBIN = "$(SBIN)"' >> ctf/paths.py
|
||||
echo 'BASE_URL = "$(BASE_URL)"' >> ctf/paths.py
|
||||
python setup.py install
|
||||
|
||||
install bin/pointscli $(BIN)
|
||||
install bin/in.pointsd bin/in.flagd \
|
||||
bin/scoreboard bin/run-tanks \
|
||||
bin/run-ctf $(SBIN)
|
||||
cp -r lib/* $(LIB)
|
||||
cp -r www/* $(WWW)
|
||||
rm -f $(WWW)/tanks/results
|
||||
ln -s $(VAR)/tanks/results $(WWW)/tanks/results
|
||||
cp template.html $(LIB)
|
||||
|
||||
./mkpuzzles.py --base=$(BASE_URL) --puzzles=puzzles \
|
||||
--htmldir=$(WWW)/puzzler --keyfile=$(LIB)/puzzler.keys
|
||||
|
||||
|
||||
uninstall:
|
||||
rm -rf $(VAR) $(WWW) $(LIB) $(BIN) $(SBIN)
|
||||
rmdir $(BASE) || true
|
|
@ -0,0 +1,265 @@
|
|||
#! /usr/bin/python
|
||||
|
||||
import badmath
|
||||
import time
|
||||
import os
|
||||
import traceback
|
||||
import pickle
|
||||
|
||||
import irc
|
||||
from ctf import teams
|
||||
from ctf.flagger import Flagger
|
||||
|
||||
class Gyopi(irc.Bot):
|
||||
STATE_FN = 'badmath.state'
|
||||
|
||||
SALT = 'this is questionable.'
|
||||
|
||||
MAX_ATTEMPT_RATE = 3
|
||||
NOBODY = '\002[nobody]\002'
|
||||
|
||||
def __init__(self, host, channels, dataPath, flagger):
|
||||
irc.Bot.__init__(self, host, ['gyopi', 'gyopi_', '_gyopi', '[gyopi]'], 'Gyopi', channels)
|
||||
|
||||
self._dataPath = dataPath
|
||||
|
||||
self._flag = flagger
|
||||
|
||||
try:
|
||||
self._loadState()
|
||||
except:
|
||||
self._lvl = 0
|
||||
self._flag.set_flag(teams.house)
|
||||
|
||||
self._tokens = []
|
||||
self._lastAttempt = {}
|
||||
self._affiliations = {}
|
||||
self._newPuzzle()
|
||||
|
||||
def err(self, exception):
|
||||
"""Save the traceback for later inspection"""
|
||||
irc.Bot.err(self, exception)
|
||||
t,v,tb = exception
|
||||
info = []
|
||||
while 1:
|
||||
info.append('%s:%d(%s)' %
|
||||
(os.path.basename(tb.tb_frame.f_code.co_filename),
|
||||
tb.tb_lineno,
|
||||
tb.tb_frame.f_code.co_name))
|
||||
tb = tb.tb_next
|
||||
if not tb:
|
||||
break
|
||||
del tb # just to be safe
|
||||
infostr = '[' + '] ['.join(info) + ']'
|
||||
self.last_tb = '%s %s %s' % (t, v, infostr)
|
||||
print(self.last_tb)
|
||||
|
||||
def cmd_JOIN(self, sender, forum, addl):
|
||||
"""On join, announce who has the flag."""
|
||||
if sender.name() in self.nicks:
|
||||
self._tellFlag(forum)
|
||||
self._tellPuzzle(forum)
|
||||
self.write(['TOPIC', '#badmath'], 'type !help')
|
||||
|
||||
def _newPuzzle(self):
|
||||
"""Create a new puzzle."""
|
||||
self._key, self._puzzle, self._banned = badmath.mkPuzzle(self._lvl)
|
||||
|
||||
def _loadState(self):
|
||||
"""Load the last state from the stateFile."""
|
||||
statePath = os.path.join(self._dataPath, self.STATE_FN)
|
||||
stateFile = open( statePath, 'rb' )
|
||||
state = pickle.load(stateFile)
|
||||
self._lvl = state['lvl']
|
||||
self._flag.set_flag( state['flag'] )
|
||||
self._lastAttempt = state['lastAttempt']
|
||||
self._affiliations = state['affiliations']
|
||||
self._puzzle = state['puzzle']
|
||||
self._key = state['key']
|
||||
self._banned = state['banned']
|
||||
self._tokens = state.get('tokens', [])
|
||||
|
||||
def _saveState(self):
|
||||
"""Write the current state to file."""
|
||||
state = {'lvl': self._lvl,
|
||||
'flag': self._flag.flag,
|
||||
'lastAttempt': self._lastAttempt,
|
||||
'affiliations': self._affiliations,
|
||||
'puzzle': self._puzzle,
|
||||
'key': self._key,
|
||||
'banned': self._banned,
|
||||
'tokens': self._tokens}
|
||||
|
||||
# Do the write as an atomic move operation
|
||||
statePath = os.path.join(self._dataPath, self.STATE_FN)
|
||||
stateFile = open(statePath + '.tmp', 'wb')
|
||||
pickle.dump(state, stateFile)
|
||||
stateFile.close()
|
||||
os.rename( statePath + '.tmp', statePath)
|
||||
|
||||
def _tellFlag(self, forum):
|
||||
"""Announce who owns the flag."""
|
||||
forum.msg('%s has the flag.' % (self._flag.flag))
|
||||
|
||||
def _tellPuzzle(self, forum):
|
||||
"""Announce the current puzzle."""
|
||||
forum.msg('Difficulty level is %d' % self._lvl)
|
||||
forum.msg('The problem is: %s' % ' '.join( map(str, self._puzzle)))
|
||||
|
||||
def _getStations(self):
|
||||
stations = {}
|
||||
file = open(os.path.join(STORAGE, 'stations.txt'))
|
||||
lines = file.readlines()
|
||||
for line in lines:
|
||||
try:
|
||||
name, file = line.split(':')
|
||||
except:
|
||||
continue
|
||||
stations[name] = file
|
||||
|
||||
return stations
|
||||
|
||||
def _giveToken(self, user, forum):
|
||||
"""Hand a Jukebox token to the user."""
|
||||
|
||||
token = self._jukebox.mkToken(user)
|
||||
|
||||
forum.msg('You get a jukebox token: %s' % token)
|
||||
forum.msg('Use this with the !set command to change the music.')
|
||||
forum.msg('This token is specific to your user name, and is only '
|
||||
'useable once.')
|
||||
|
||||
def _useToken(self, user, forum, token, station):
|
||||
"""Use the given token, and change the current station to station."""
|
||||
try:
|
||||
station = int(station)
|
||||
stations = self._getStations()
|
||||
assert station in stations
|
||||
except:
|
||||
forum.msg('%s: Invalid Station (%s)' % station)
|
||||
return
|
||||
|
||||
if token in self._tokens[user]:
|
||||
self._tokens[user].remove(token)
|
||||
|
||||
|
||||
def cmd_PRIVMSG(self, sender, forum, addl):
|
||||
text = addl[0]
|
||||
who = sender.name()
|
||||
if text.startswith('!'):
|
||||
parts = text[1:].split(' ', 1)
|
||||
cmd = parts[0]
|
||||
if len(parts) > 1:
|
||||
args = parts[1]
|
||||
else:
|
||||
args = None
|
||||
if cmd.startswith('r'):
|
||||
# Register
|
||||
if args:
|
||||
self._affiliations[who] = args
|
||||
team = self._affiliations.get(who, self.NOBODY)
|
||||
forum.msg('%s is playing for %s' % (who, team))
|
||||
elif cmd.startswith('w'):
|
||||
forum.msg('Teams:')
|
||||
for player in self._affiliations:
|
||||
forum.msg('%s: %s' % (player, self._affiliations[player]))
|
||||
elif cmd.startswith('embrace'):
|
||||
# Embrace
|
||||
forum.ctcp('ACTION', 'is devoid of emotion.')
|
||||
elif cmd.startswith('f'):
|
||||
# Flag
|
||||
self._tellFlag(forum)
|
||||
elif cmd.startswith('h'):
|
||||
# Help
|
||||
forum.msg('''Goal: Help me with my math homework, FROM ANOTHER DIMENSION! Order of operations is always left to right in that dimension, but the operators are alien.''')
|
||||
forum.msg('Order of operations is always left to right.')
|
||||
#forum.msg('Goal: The current winner gets to control the contest music.')
|
||||
forum.msg('Commands: !help, !flag, !register [TEAM], !solve SOLUTION,!? EQUATION, !ops, !problem, !who')
|
||||
elif cmd.startswith('prob'):
|
||||
self._tellPuzzle(forum)
|
||||
elif cmd.startswith('solve') and args:
|
||||
# Solve
|
||||
team = self._affiliations.get(who)
|
||||
lastAttempt = time.time() - self._lastAttempt.get(team, 0)
|
||||
#UN-COMMENT AFTER NMT CTF
|
||||
# self._lastAttempt[team] = time.time()
|
||||
answer = badmath.solve(self._key, self._puzzle)
|
||||
try:
|
||||
attempt = int(''.join(args).strip())
|
||||
except:
|
||||
forum.msg("%s: Answers are always integers.")
|
||||
if not team:
|
||||
forum.msg('%s: register first (!register TEAM).' % who)
|
||||
elif self._flag.flag == team:
|
||||
forum.msg('%s: Greedy, greedy.' % who)
|
||||
elif lastAttempt < self.MAX_ATTEMPT_RATE:
|
||||
forum.msg('%s: Wait at least %d seconds between attempts' %
|
||||
(team, self.MAX_ATTEMPT_RATE))
|
||||
elif answer == attempt:
|
||||
self._flag.set_flag( team )
|
||||
self._lvl = self._lvl + 1
|
||||
self._tellFlag(forum)
|
||||
self._newPuzzle()
|
||||
self._tellPuzzle(forum)
|
||||
# self._giveToken(who, sender)
|
||||
self._saveState()
|
||||
else:
|
||||
forum.msg('%s: That is not correct.' % who)
|
||||
|
||||
# Test a simple one op command.
|
||||
elif cmd.startswith('?'):
|
||||
if not args:
|
||||
forum.msg('%s: Give me an easier problem, and I\'ll '
|
||||
'give you the answer.' % who)
|
||||
return
|
||||
|
||||
try:
|
||||
tokens = badmath.parse(''.join(args))
|
||||
except (ValueError), msg:
|
||||
forum.msg('%s: %s' % (who, msg))
|
||||
return
|
||||
|
||||
if len(tokens) > 3:
|
||||
forum.msg('%s: You can only test one op at a time.' % who)
|
||||
return
|
||||
|
||||
for num in self._banned:
|
||||
if num in tokens:
|
||||
forum.msg('%s: You can\'t test numbers in the '
|
||||
'puzzle.' % who)
|
||||
return
|
||||
|
||||
try:
|
||||
result = badmath.solve(self._key, tokens)
|
||||
forum.msg('%s: %s -> %d' % (who, ''.join(args), result))
|
||||
except Exception, msg:
|
||||
forum.msg("%s: That doesn't work at all: %s" % (who, msg))
|
||||
|
||||
elif cmd == 'birdzerk':
|
||||
self._saveState()
|
||||
|
||||
elif cmd == 'traceback':
|
||||
forum.msg(self.last_tb or 'No traceback')
|
||||
|
||||
if __name__ == '__main__':
|
||||
import optparse
|
||||
|
||||
p = optparse.OptionParser()
|
||||
p.add_option('-i', '--irc', dest='ircHost', default='localhost',
|
||||
help='IRC Host to connect to.')
|
||||
p.add_option('-f', '--flagd', dest='flagd', default='localhost',
|
||||
help='Flag Server to connect to')
|
||||
p.add_option('-p', '--password', dest='password',
|
||||
default='badmath:::a41c6753210c0bdafd84b3b62d7d1666',
|
||||
help='Flag server password')
|
||||
p.add_option('-d', '--path', dest='path', default='/var/lib/ctf/badmath',
|
||||
help='Path to where we can store state info.')
|
||||
p.add_option('-c', '--channel', dest='channel', default='#badmath',
|
||||
help='Which channel to join')
|
||||
|
||||
opts, args = p.parse_args()
|
||||
channels = [opts.channel]
|
||||
|
||||
flagger = Flagger(opts.flagd, opts.password.encode('utf-8'))
|
||||
gyopi = Gyopi((opts.ircHost, 6667), channels, opts.path, flagger)
|
||||
irc.run_forever()
|
|
@ -0,0 +1,96 @@
|
|||
#! /usr/bin/python
|
||||
|
||||
import sys
|
||||
import optparse
|
||||
import hmac
|
||||
import time
|
||||
import select
|
||||
from ctf import teams, pointscli
|
||||
import os
|
||||
from urllib import quote
|
||||
|
||||
basedir = None
|
||||
flagsdir = None
|
||||
|
||||
key = 'My First Shared Secret (tm)'
|
||||
def hexdigest(s):
|
||||
return hmac.new(key, s.encode('utf-8')).hexdigest()
|
||||
|
||||
def auth():
|
||||
# Pretend to be in.tcpmuxd
|
||||
while True:
|
||||
line = sys.stdin.readline()
|
||||
if not line:
|
||||
return
|
||||
line = line.strip().lower()
|
||||
|
||||
if line == 'tcpmux':
|
||||
sys.stdout.write('+Okay, fine.\r\n')
|
||||
sys.stdout.flush()
|
||||
continue
|
||||
elif line == 'help':
|
||||
sys.stdout.write('tcpmux\r\n')
|
||||
elif ':::' in line:
|
||||
# Authentication
|
||||
cat, passwd = line.split(':::')
|
||||
if passwd == hexdigest(cat):
|
||||
return cat
|
||||
else:
|
||||
sys.stdout.write('-Blow me.\r\n')
|
||||
else:
|
||||
sys.stdout.write('-Blow me.\r\n')
|
||||
return
|
||||
|
||||
def award(cat, team):
|
||||
qcat = quote(cat, '')
|
||||
fn = os.path.join(flagsdir, qcat)
|
||||
f = open(fn, 'w')
|
||||
f.write(team)
|
||||
f.close()
|
||||
pointscli.award(cat, team, 1)
|
||||
print('+%s' % team)
|
||||
sys.stdout.flush()
|
||||
|
||||
def run():
|
||||
cat = auth()
|
||||
if not cat:
|
||||
return
|
||||
|
||||
now = time.time()
|
||||
next_award = now - (now % 60)
|
||||
flag = teams.house
|
||||
|
||||
while True:
|
||||
now = time.time()
|
||||
while now >= next_award:
|
||||
next_award += 60
|
||||
award(cat, flag)
|
||||
|
||||
timeout = next_award - now
|
||||
r, w, x = select.select([sys.stdin], [], [], timeout)
|
||||
if r:
|
||||
line = sys.stdin.readline()
|
||||
if not line:
|
||||
break
|
||||
new_flag = line.strip() or teams.house
|
||||
if new_flag != flag:
|
||||
# Award a point if the flag is captured
|
||||
flag = new_flag
|
||||
award(cat, flag)
|
||||
|
||||
def main():
|
||||
p = optparse.OptionParser(usage='%prog [options] FLAGSDIR')
|
||||
p.add_option('-a', '--auth', dest='cat', default=None,
|
||||
help='Generate authentication for the given category')
|
||||
opts, args = p.parse_args()
|
||||
if opts.cat:
|
||||
print('%s:::%s' % (opts.cat, hexdigest(opts.cat.encode('utf-8'))))
|
||||
elif len(args) != 1:
|
||||
p.error('Wrong number of arguments')
|
||||
else:
|
||||
global flagsdir
|
||||
flagsdir = args[0]
|
||||
run()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -0,0 +1,5 @@
|
|||
#! /bin/sh
|
||||
|
||||
ip=$(echo $UDPREMOTEADDR | cut -d: -f1)
|
||||
touch $1/$ip
|
||||
echo 'Hello.'
|
|
@ -0,0 +1,19 @@
|
|||
#! /bin/sh
|
||||
|
||||
##
|
||||
## This is meant to be run from inotifyd like so:
|
||||
##
|
||||
## inotifyd in.pointsd $base/cur:y
|
||||
##
|
||||
## inotifyd runs in.pointsd serially, so all we have to do is just echo
|
||||
## each file to the log, and then remove it. This allows a log message
|
||||
## to be entered by writing a file into tmp, moving it to cur, and then
|
||||
## moving along. Even if pointsd dies, everybody is still able to score
|
||||
## points asynchronously without losing anything: it'll just get picked
|
||||
## up when pointsd restarts.
|
||||
##
|
||||
|
||||
# Args: flag dir file
|
||||
fn=$2/$3
|
||||
|
||||
cat $fn >> $2/../log && rm $fn
|
|
@ -0,0 +1,136 @@
|
|||
#! /usr/bin/python
|
||||
|
||||
import os
|
||||
import optparse
|
||||
import asynchat
|
||||
import socket
|
||||
import asyncore
|
||||
from urllib import quote_plus as quote
|
||||
|
||||
import irc
|
||||
from ctf.flagger import Flagger
|
||||
|
||||
nobody = '\002[nobody]\002'
|
||||
|
||||
class Kevin(irc.Bot):
|
||||
def __init__(self, host, flagger, tokens, victims):
|
||||
irc.Bot.__init__(self, host,
|
||||
['kevin', 'kev', 'kevin_', 'kev_', 'kevinm', 'kevinm_'],
|
||||
'Kevin',
|
||||
['+kevin'])
|
||||
self.flagger = flagger
|
||||
self.tokens = tokens
|
||||
self.victims = victims
|
||||
self.affiliation = {}
|
||||
|
||||
def cmd_001(self, sender, forum, addl):
|
||||
self.write(['OPER', 'bot', 'BottyMcBotpants'])
|
||||
irc.Bot.cmd_001(self, sender, forum, addl)
|
||||
|
||||
def cmd_JOIN(self, sender, forum, addl):
|
||||
if sender.name == self.nick:
|
||||
self.write(['TOPIC', '#badmath'], 'type !help')
|
||||
self.tell_flag(forum)
|
||||
|
||||
def cmd_381(self, sender, forum, addl):
|
||||
# You are now an IRC Operator
|
||||
if self.nick != 'kevin':
|
||||
self.write(['KILL', 'kevin'], 'You are not kevin. I am kevin.')
|
||||
self.write(['NICK', 'kevin'])
|
||||
|
||||
def err(self, exception):
|
||||
"""Save the traceback for later inspection"""
|
||||
irc.Bot.err(self, exception)
|
||||
t,v,tb = exception
|
||||
info = []
|
||||
while 1:
|
||||
info.append('%s:%d(%s)' % (os.path.basename(tb.tb_frame.f_code.co_filename),
|
||||
tb.tb_lineno,
|
||||
tb.tb_frame.f_code.co_name))
|
||||
tb = tb.tb_next
|
||||
if not tb:
|
||||
break
|
||||
del tb # just to be safe
|
||||
infostr = '[' + '] ['.join(info) + ']'
|
||||
self.last_tb = '%s %s %s' % (t, v, infostr)
|
||||
print(self.last_tb)
|
||||
|
||||
def tell_flag(self, forum):
|
||||
forum.msg('%s has the flag.' % (self.flagger.flag or nobody))
|
||||
|
||||
def cmd_PRIVMSG(self, sender, forum, addl):
|
||||
text = addl[0]
|
||||
if text.startswith('!'):
|
||||
parts = text[1:].split(' ', 1)
|
||||
cmd = parts[0].lower()
|
||||
if len(parts) > 1:
|
||||
args = parts[1]
|
||||
else:
|
||||
args = None
|
||||
if cmd.startswith('r'):
|
||||
# Register
|
||||
who = sender.name()
|
||||
if args:
|
||||
self.affiliation[who] = args
|
||||
team = self.affiliation.get(who, nobody)
|
||||
forum.msg('%s is playing for %s' % (who, team))
|
||||
elif cmd.startswith('e'):
|
||||
# Embrace
|
||||
forum.ctcp('ACTION', 'hugs %s' % sender.name())
|
||||
elif cmd.startswith('f'):
|
||||
# Flag
|
||||
self.tell_flag(forum)
|
||||
elif cmd.startswith('h'):
|
||||
# Help
|
||||
forum.msg('Goal: Obtain a token with social engineering.')
|
||||
forum.msg('Commands: !help, !flag, !register [TEAM], !claim TOKEN, !victims, !embrace')
|
||||
elif cmd.startswith('c') and args:
|
||||
# Claim
|
||||
sn = sender.name()
|
||||
team = self.affiliation.get(sn)
|
||||
token = quote(args, safe='')
|
||||
fn = os.path.join(self.tokens, token)
|
||||
if not team:
|
||||
forum.msg('%s: register first (!register TEAM).' % sn)
|
||||
elif self.flagger.flag == team:
|
||||
forum.msg('%s: Greedy, greedy.' % sn)
|
||||
elif not os.path.exists(fn):
|
||||
forum.msg('%s: Token does not exist (possibly already claimed).' % sn)
|
||||
else:
|
||||
os.unlink(fn)
|
||||
self.flagger.set_flag(team)
|
||||
self.tell_flag(forum)
|
||||
elif cmd.startswith('v'):
|
||||
# Victims
|
||||
# Open the file each time, so it can change
|
||||
try:
|
||||
for line in open(self.victims):
|
||||
forum.msg(line.strip())
|
||||
except IOError:
|
||||
forum.msg('There are no victims!')
|
||||
elif cmd == 'traceback':
|
||||
forum.msg(self.last_tb or 'No traceback')
|
||||
|
||||
def main():
|
||||
p = optparse.OptionParser()
|
||||
p.add_option('-t', '--tokens', dest='tokens', default='./tokens',
|
||||
help='Directory containing tokens')
|
||||
p.add_option('-v', '--victims', dest='victims', default='victims.txt',
|
||||
help='File containing victims information')
|
||||
p.add_option('-i', '--ircd', dest='ircd', default='localhost',
|
||||
help='IRC server to connect to')
|
||||
p.add_option('-f', '--flagd', dest='flagd', default='localhost',
|
||||
help='Flag server to connect to')
|
||||
p.add_option('-p', '--password', dest='password',
|
||||
default='kevin:::7db3e44d53d4a466f8facd7b7e9aa2b7',
|
||||
help='Flag server password')
|
||||
p.add_option('-c', '--channel', dest='channel',
|
||||
help='Channel to join')
|
||||
opts, args = p.parse_args()
|
||||
|
||||
f = Flagger(opts.flagd, opts.password.encode('utf-8'))
|
||||
k = Kevin((opts.ircd, 6667), f, opts.tokens, opts.victims)
|
||||
irc.run_forever()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -0,0 +1,5 @@
|
|||
#! /usr/bin/python
|
||||
|
||||
from ctf import pointscli
|
||||
|
||||
pointscli.main()
|
|
@ -0,0 +1,285 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import socket
|
||||
import traceback
|
||||
import subprocess
|
||||
import random
|
||||
import httplib
|
||||
import optparse
|
||||
import cStringIO as io
|
||||
|
||||
from ctf import pointscli, html
|
||||
|
||||
ifconfig = '/sbin/ifconfig'
|
||||
udhcpc = '/sbin/udhcpc'
|
||||
|
||||
class BoundHTTPConnection(httplib.HTTPConnection):
|
||||
''' http.client.HTTPConnection doesn't support binding to a particular
|
||||
address, which is something we need. '''
|
||||
|
||||
def __init__(self, bindip, host, port=None, strict=None, timeout=None):
|
||||
httplib.HTTPConnection.__init__(self, host, port, strict)
|
||||
self.bindip = bindip
|
||||
self.timeout = timeout
|
||||
|
||||
def connect(self):
|
||||
''' Connect to the host and port specified in __init__, but
|
||||
also bind first. '''
|
||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.sock.bind((self.bindip, 0))
|
||||
self.sock.settimeout(self.timeout)
|
||||
self.sock.connect((self.host, self.port))
|
||||
|
||||
def random_mac():
|
||||
''' Set a random mac on the poll interface. '''
|
||||
retcode = subprocess.call((ifconfig, opts.iface, 'down'))
|
||||
mac = ':'.join([opts.mac_vendor] + ['%02x' % random.randint(0,255) for i in range(3)])
|
||||
retcode = subprocess.call((ifconfig, opts.iface, 'hw', 'ether', mac, 'up'))
|
||||
|
||||
def dhcp_request():
|
||||
''' Request a new IP on the poll interface. '''
|
||||
retcode = subprocess.call((udhcpc, '-i', opts.iface, '-q'))
|
||||
|
||||
def get_ip():
|
||||
''' Return the IP of the poll interface. '''
|
||||
ip_match = re.compile(r'inet addr:([0-9.]+)')
|
||||
p = subprocess.Popen((ifconfig, opts.iface), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
(out, err) = p.communicate()
|
||||
for line in out.splitlines():
|
||||
m = ip_match.search(line)
|
||||
if m is not None:
|
||||
return m.group(1).decode('utf-8')
|
||||
return '10.1.1.1'
|
||||
|
||||
def socket_poll(srcip, ip, port, msg, prot, max_recv=1):
|
||||
''' Connect via socket to the specified <ip>:<port> using the
|
||||
specified <prot>, send the specified <msg> and return the
|
||||
response or None if something went wrong. <max_recvs> specifies
|
||||
how many times to read from the socket (defaults to once). '''
|
||||
|
||||
# create a socket
|
||||
try:
|
||||
sock = socket.socket(socket.AF_INET, prot)
|
||||
except Exception, e:
|
||||
print('pollster: create socket failed (%s)' % e)
|
||||
traceback.print_exc()
|
||||
return None
|
||||
|
||||
sock.bind((srcip, 0))
|
||||
sock.settimeout(opts.timeout)
|
||||
|
||||
# connect
|
||||
try:
|
||||
sock.connect((ip, port))
|
||||
except socket.timeout, e:
|
||||
print('pollster: attempt to connect to %s:%d timed out (%s)' % (ip, port, e))
|
||||
traceback.print_exc()
|
||||
return None
|
||||
except Exception, e:
|
||||
print('pollster: attempt to connect to %s:%d failed (%s)' % (ip, port, e))
|
||||
traceback.print_exc()
|
||||
return None
|
||||
|
||||
# send something
|
||||
sock.send(msg)
|
||||
|
||||
# get a response
|
||||
resp = []
|
||||
try:
|
||||
# read from the socket until <max_recv> responses or read,
|
||||
# a timeout occurs, the socket closes, or some other exception
|
||||
# is raised
|
||||
for i in range(max_recv):
|
||||
data = sock.recv(1024)
|
||||
if len(data) == 0:
|
||||
break
|
||||
resp.append(data)
|
||||
|
||||
except socket.timeout, e:
|
||||
print('pollster: timed out waiting for a response from %s:%d (%s)' % (ip, port, e))
|
||||
traceback.print_exc()
|
||||
except Exception, e:
|
||||
print('pollster: receive from %s:%d failed (%s)' % (ip, port, e))
|
||||
traceback.print_exc()
|
||||
|
||||
sock.close()
|
||||
|
||||
if len(resp) == 0:
|
||||
return None
|
||||
|
||||
return ''.join(resp)
|
||||
|
||||
# PUT POLLS FUNCTIONS HERE
|
||||
# Each function should take an IP address and return a team name or None
|
||||
# if (a) the service is not up, (b) it doesn't return a valid team name.
|
||||
|
||||
def poll_fingerd(srcip, ip):
|
||||
''' Poll the fingerd service. Returns None or a team name. '''
|
||||
resp = socket_poll(srcip, ip, 79, 'flag\n', socket.SOCK_STREAM)
|
||||
if resp is None:
|
||||
return None
|
||||
return resp.split('\n')[0]
|
||||
|
||||
def poll_noted(srcip, ip):
|
||||
''' Poll the noted service. Returns None or a team name. '''
|
||||
resp = socket_poll(srcip, ip, 4000, 'rflag\n', socket.SOCK_STREAM)
|
||||
if resp is None:
|
||||
return None
|
||||
return resp.split('\n')[0]
|
||||
|
||||
def poll_catcgi(srcip, ip):
|
||||
''' Poll the cat.cgi web service. Returns None or a team name. '''
|
||||
|
||||
try:
|
||||
conn = BoundHTTPConnection(srcip, ip, timeout=opts.timeout)
|
||||
conn.request('GET', '/var/www/flag')
|
||||
except Exception, e:
|
||||
traceback.print_exc()
|
||||
return None
|
||||
|
||||
resp = conn.getresponse()
|
||||
if resp.status != 200:
|
||||
conn.close()
|
||||
return None
|
||||
|
||||
data = resp.read()
|
||||
conn.close()
|
||||
return data.split('\n')[0]
|
||||
|
||||
def poll_tftpd(srcip, ip):
|
||||
''' Poll the tftp service. Returns None or a team name. '''
|
||||
resp = socket_poll(srcip, ip, 69, '\x00\x01' + 'flag' + '\x00' + 'octet' + '\x00', socket.SOCK_DGRAM)
|
||||
if resp is None:
|
||||
return None
|
||||
|
||||
if len(resp) <= 5:
|
||||
return None
|
||||
|
||||
resp = resp.split('\n')[0]
|
||||
|
||||
# ack
|
||||
_ = socket_poll(srcip, ip, 69, '\x00\x04\x00\x01' + '\x00' * 14, socket.SOCK_DGRAM, 0)
|
||||
|
||||
return resp[4:].split('\n')[0]
|
||||
|
||||
# PUT POLL FUNCTIONS IN HERE OR THEY WONT BE POLLED
|
||||
POLLS = {
|
||||
'fingerd' : poll_fingerd,
|
||||
'noted' : poll_noted,
|
||||
'catcgi' : poll_catcgi,
|
||||
'tftpd' : poll_tftpd,
|
||||
}
|
||||
|
||||
|
||||
|
||||
p = optparse.OptionParser()
|
||||
p.add_option('-d', '--debug', action='store_true', dest='debug',
|
||||
default=False,
|
||||
help='Turn on debugging output')
|
||||
p.add_option('-s', '--interval', type='float', dest='interval',
|
||||
default=60,
|
||||
help='Time between polls, in seconds (default: %default)')
|
||||
p.add_option('-t', '--timeout', type='float', dest='timeout',
|
||||
default=0.5,
|
||||
help='Poll timeout, in seconds (default: %default)')
|
||||
p.add_option('-b', '--heartbeat-dir', dest='heartbeat_dir',
|
||||
default='/var/lib/ctf/heartbeat',
|
||||
help='Where in.heartbeatd writes its files')
|
||||
p.add_option('-r', '--results-page', dest='results',
|
||||
default='/tmp/services.html',
|
||||
help='Where to write results file')
|
||||
p.add_option('-i', '--interface', dest='iface',
|
||||
default='eth1',
|
||||
help='Interface to bind to')
|
||||
p.add_option('-m', '--mac-vendor', dest='mac_vendor',
|
||||
default='00:01:c0',
|
||||
help='MAC vendor to look for (default: %default)')
|
||||
opts, args = p.parse_args()
|
||||
|
||||
socket.setdefaulttimeout(opts.timeout)
|
||||
|
||||
ip_re = re.compile('(\d{1,3}\.){3}\d{1,3}')
|
||||
# loop forever
|
||||
while True:
|
||||
|
||||
random_mac()
|
||||
dhcp_request()
|
||||
|
||||
srcip = get_ip()
|
||||
|
||||
t_start = time.time()
|
||||
|
||||
# gather the list of IPs to poll
|
||||
ips = os.listdir(opts.heartbeat_dir)
|
||||
|
||||
out = io.StringIO()
|
||||
for ip in ips:
|
||||
# check file name format is ip
|
||||
if ip_re.match(ip) is None:
|
||||
continue
|
||||
|
||||
# remove the file
|
||||
fn = os.path.join(opts.heartbeat_dir, ip)
|
||||
try:
|
||||
os.remove(fn)
|
||||
except Exception, e:
|
||||
print('pollster: could not remove %s' % fn)
|
||||
traceback.print_exc()
|
||||
|
||||
results = {}
|
||||
|
||||
if opts.debug:
|
||||
print('ip: %s' % ip)
|
||||
|
||||
if out is not None:
|
||||
out.write('<h2>%s</h2>\n' % ip)
|
||||
out.write('<table class="pollster">\n<thead><tr><td>Service Name</td></td>')
|
||||
out.write('<td>Flag Holder</td></tr></thead>\n')
|
||||
|
||||
# perform polls
|
||||
for service,func in POLLS.items():
|
||||
try:
|
||||
team = func(srcip, ip).decode('utf-8')
|
||||
if len(team) == 0:
|
||||
team = 'dirtbags'
|
||||
except:
|
||||
team = 'dirtbags'
|
||||
|
||||
if opts.debug:
|
||||
print('\t%s - %s' % (service, team))
|
||||
|
||||
if out is not None:
|
||||
out.write('<tr><td>%s</td><td>%s</td>\n' % (service, team))
|
||||
|
||||
pointscli.award('svc.' + service, team, 1)
|
||||
|
||||
if out is not None:
|
||||
out.write('</table>\n')
|
||||
|
||||
if opts.debug:
|
||||
print('+-----------------------------------------+')
|
||||
|
||||
time_str = time.strftime('%a, %d %b %Y %H:%M:%S %Z')
|
||||
out.write('''
|
||||
<p>This page was generated on %s. That was <span id="diff">?</span> seconds ago.</p>
|
||||
<script type="text/javascript">
|
||||
var gen_time = new Date(%f);
|
||||
var cur_time = new Date();
|
||||
var diff = (cur_time.getTime() - gen_time.getTime())/1000;
|
||||
document.getElementById("diff").innerHTML = diff;
|
||||
</script>
|
||||
''' % (time_str, time.time()*1000))
|
||||
|
||||
t_end = time.time()
|
||||
exec_time = int(t_end - t_start)
|
||||
sleep_time = opts.interval - exec_time
|
||||
|
||||
html.write(opts.results,'Team Service Availability', out.getvalue())
|
||||
|
||||
# sleep until its time to poll again
|
||||
time.sleep(sleep_time)
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
#! /bin/sh
|
||||
|
||||
POINTS=var/points/log
|
||||
|
||||
cd $(dirname $0)/..
|
||||
|
||||
while true; do
|
||||
# Timestamp
|
||||
start=$(date +%s)
|
||||
next=$(expr $start + 60)
|
||||
|
||||
# If enabled, run tanks
|
||||
if ! [ -f var/disabled/tanks ]; then
|
||||
sbin/run-tanks -1 var/tanks
|
||||
fi
|
||||
|
||||
# Collect any new points
|
||||
for fn in var/points/cur/*; do
|
||||
[ -f $fn ] || continue
|
||||
cat $fn >> $POINTS || break
|
||||
rm $fn
|
||||
done
|
||||
|
||||
if [ -f $POINTS ]; then
|
||||
sbin/scoreboard -t www/scoreboard.html -j www/myplot.js < $POINTS
|
||||
fi
|
||||
|
||||
# Wait until the next minute
|
||||
now=$(date +%s)
|
||||
if [ $now -lt $next ]; then
|
||||
sleep $(expr $next - $now)
|
||||
fi
|
||||
done
|
|
@ -0,0 +1,59 @@
|
|||
#! /usr/bin/python
|
||||
|
||||
import optparse
|
||||
import os
|
||||
import shutil
|
||||
import socket
|
||||
import time
|
||||
from ctf import pointscli, teams, paths
|
||||
from tanks import Pflanzarr
|
||||
|
||||
MAX_HIST = 30
|
||||
HIST_STEP = 100
|
||||
|
||||
running = True
|
||||
|
||||
def run_tanks(basedir, turns):
|
||||
try:
|
||||
p = Pflanzarr.Pflanzarr(basedir)
|
||||
p.run(turns)
|
||||
winner = p.winner
|
||||
except Pflanzarr.NotEnoughPlayers:
|
||||
winner = teams.house
|
||||
pointscli.award('tanks', winner, 1)
|
||||
|
||||
winnerFile = open(os.path.join(basedir, 'winner'),'w')
|
||||
winnerFile.write(winner or teams.house)
|
||||
winnerFile.close()
|
||||
|
||||
# Fake being a flag, so the most recent winner shows up on the
|
||||
# scoreboard.
|
||||
try:
|
||||
open(os.path.join(paths.VAR, 'flags', 'tanks'), 'w').write(winner or teams.house)
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
|
||||
def main():
|
||||
parser = optparse.OptionParser('%prog [options] DATA_DIR')
|
||||
parser.add_option('-1', '--once',
|
||||
action='store_true', dest='once',
|
||||
help='Run only once')
|
||||
parser.add_option('-t', '--max-turns',
|
||||
type='int', dest='turns', default=500,
|
||||
help='Maximum number of turns per round')
|
||||
parser.add_option('-s', '--sleep-time',
|
||||
type='int', dest='sleep', default=60,
|
||||
help='Wait SLEEP seconds between turns (default %default)')
|
||||
opts, args = parser.parse_args()
|
||||
if (len(args) != 1):
|
||||
parser.error('Wrong number of arguments')
|
||||
|
||||
while running:
|
||||
run_tanks(args[0], opts.turns)
|
||||
if opts.once:
|
||||
break
|
||||
time.sleep(opts.sleep)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -0,0 +1,174 @@
|
|||
#! /usr/bin/python
|
||||
|
||||
import sys
|
||||
import codecs
|
||||
import time
|
||||
import optparse
|
||||
import string
|
||||
import os
|
||||
from urllib import unquote
|
||||
from ctf import teams, html, paths
|
||||
from codecs import open
|
||||
from sets import Set as set
|
||||
from cgi import escape
|
||||
|
||||
flags_dir = os.path.join(paths.VAR, 'flags')
|
||||
sys.stdin = codecs.getreader('utf-8')(sys.stdin)
|
||||
|
||||
def incdict(dict, key, amt=1):
|
||||
dict[key] = dict.get(key, 0) + amt
|
||||
|
||||
class Chart:
|
||||
def __init__(self):
|
||||
self.points_by_cat = {}
|
||||
self.points_by_cat_team = {}
|
||||
self.high_score = 0.001
|
||||
self.teams = set()
|
||||
self.cats = set()
|
||||
self.log = []
|
||||
|
||||
def add_points(self, when, cat, team, points):
|
||||
self.log.append((when, cat, team, points))
|
||||
self.teams.add(team)
|
||||
self.cats.add(cat)
|
||||
incdict(self.points_by_cat, cat, points)
|
||||
incdict(self.points_by_cat_team, (cat, team), points)
|
||||
|
||||
def team_points(self, team):
|
||||
points = 0
|
||||
for cat, tot in self.points_by_cat.items():
|
||||
if not tot:
|
||||
continue
|
||||
team_points = self.team_points_in_cat(cat, team)
|
||||
points += team_points / float(tot)
|
||||
return points
|
||||
|
||||
def team_points_in_cat(self, cat, team):
|
||||
return self.points_by_cat_team.get((cat, team), 0)
|
||||
|
||||
def write_js(self, f):
|
||||
start = self.log[0][0]
|
||||
end = self.log[-1][0]
|
||||
|
||||
# Calculate high score
|
||||
high_score = reduce(max, [self.team_points(t) for t in self.teams])
|
||||
|
||||
width = end - start
|
||||
height = high_score * 1.1
|
||||
|
||||
f.write('function draw(id) {\n')
|
||||
f.write(' p = new Plot(id, %d, %.3f);\n' % (width, height))
|
||||
for team in self.teams:
|
||||
f.write(' p.line("#%s",[' % teams.color(team))
|
||||
score = 0
|
||||
for when, cat, t, points in self.log:
|
||||
if t == team:
|
||||
cat_points = self.points_by_cat[cat]
|
||||
if not cat_points:
|
||||
continue
|
||||
pct = float(points) / cat_points
|
||||
score += pct
|
||||
f.write('[%d,%.2f],' % (when - start, score))
|
||||
f.write(']); // %s\n' % team)
|
||||
f.write('}')
|
||||
|
||||
def make_table(self):
|
||||
body = []
|
||||
body.append('<table class="scoreboard">')
|
||||
body.append('<tr>')
|
||||
body.append('<th>Overall</th>')
|
||||
for cat in self.cats:
|
||||
points = self.points_by_cat[cat]
|
||||
if not points:
|
||||
continue
|
||||
body.append('<th>')
|
||||
body.append(' %s (%d)' % (cat, points))
|
||||
try:
|
||||
fn = os.path.join(flags_dir, cat)
|
||||
team = open(fn).read().strip() or teams.house
|
||||
body.append(' <br/>')
|
||||
body.append(' <span style="color: #%s" title="flag holder">' % teams.color(team))
|
||||
body.append(' <!-- flag: %s --> %s\n' % (cat, escape(team[:15])))
|
||||
body.append(' </span>')
|
||||
except IOError:
|
||||
pass
|
||||
body.append('</th>')
|
||||
body.append('</tr>')
|
||||
|
||||
body.append('<tr>')
|
||||
body.append('<td><ol>')
|
||||
totals = []
|
||||
for team in self.teams:
|
||||
total = self.team_points(team)
|
||||
totals.append((total, team))
|
||||
totals.sort()
|
||||
totals.reverse()
|
||||
for total, team in totals:
|
||||
if total < 0.1:
|
||||
break
|
||||
body.append('<li><span style="color: #%s;">%s (%0.3f)</span></li>'
|
||||
% (teams.color(team), escape(team[:15]), total))
|
||||
body.append('</ol></td>')
|
||||
for cat in self.cats:
|
||||
total = self.points_by_cat[cat]
|
||||
if not total:
|
||||
continue
|
||||
body.append('<td>')
|
||||
scores = sorted([(self.team_points_in_cat(cat, team), team) for team in self.teams])
|
||||
for score, team in scores:
|
||||
if not score:
|
||||
continue
|
||||
color = teams.color(team)
|
||||
body.append('<div style="height: %f%%; overflow: hidden; background: #%s; color: black;">' % (float(score * 100)/total, color))
|
||||
body.append('<!-- category: %s --> %s: %d' % (cat, escape(team[:15]), score))
|
||||
body.append('</div>')
|
||||
body.append('</td>')
|
||||
body.append('</tr>')
|
||||
body.append('</table>')
|
||||
|
||||
return '\n'.join(body)
|
||||
|
||||
def main():
|
||||
p = optparse.OptionParser(usage='%prog [options] < logfile')
|
||||
p.add_option('-t', '--html', dest='html', default=None,
|
||||
help='Write a web page to HTML')
|
||||
p.add_option('-j', '--javascript', dest='js', default=None,
|
||||
help='Write javascript params to JS')
|
||||
|
||||
opts, args = p.parse_args()
|
||||
if args:
|
||||
return p.print_help()
|
||||
|
||||
chart = Chart()
|
||||
for line in sys.stdin:
|
||||
line = line.strip()
|
||||
try:
|
||||
date, qcat, qteam, points = line.split('\t')
|
||||
except ValueError:
|
||||
print 'Possible line corruption: %s' % (repr(line)[:40])
|
||||
cat = unquote(qcat)
|
||||
team = unquote(qteam)
|
||||
when = time.strptime(date, '%Y-%m-%dT%H:%M:%S')
|
||||
chart.add_points(time.mktime(when),
|
||||
cat,
|
||||
team,
|
||||
int(points))
|
||||
|
||||
if opts.html:
|
||||
hdr = ('<script type="application/javascript" src="plot.js"></script>'
|
||||
'<script type="application/javascript" src="myplot.js"></script>'
|
||||
'<meta http-equiv="refresh" content="60" />')
|
||||
body = chart.make_table()
|
||||
body += '\n<canvas id="history"></canvas>'
|
||||
html.write(opts.html,
|
||||
'Scoreboard',
|
||||
body,
|
||||
hdr=hdr,
|
||||
body_class='wide',
|
||||
onload="draw('history')")
|
||||
if opts.js:
|
||||
f = open(opts.js, 'w', encoding='utf-8')
|
||||
chart.write_js(f)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -0,0 +1,33 @@
|
|||
#! /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
|
|
@ -0,0 +1,35 @@
|
|||
#! /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))
|
|
@ -0,0 +1,6 @@
|
|||
VAR = "/opt/ctf/var"
|
||||
WWW = "/opt/ctf/www"
|
||||
LIB = "/opt/ctf/lib"
|
||||
BIN = "/opt/ctf/bin"
|
||||
SBIN = "/opt/ctf/sbin"
|
||||
BASE_URL = "/ctf/"
|
|
@ -0,0 +1,40 @@
|
|||
#! /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()
|
|
@ -0,0 +1,72 @@
|
|||
#! /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]
|
|
@ -0,0 +1,46 @@
|
|||
import math
|
||||
|
||||
class Function(object):
|
||||
"""Represents a single condition or action. This doc string is printed
|
||||
as user documentation. You should override it to say something useful."""
|
||||
|
||||
def __call__(self, tank):
|
||||
"""The __call__ method should be of this basic form. Actions
|
||||
should return None, conditions should return True or False. Actions
|
||||
should utilize the set* methods of tanks. Conditions can utilize the
|
||||
tanks get* methods."""
|
||||
pass
|
||||
|
||||
def _limitArgs(self, args, max):
|
||||
"""Raises a ValueError if there are more than max args."""
|
||||
if len(args) > max:
|
||||
raise ValueError("Too many arguments: %s" % ','.join(args))
|
||||
|
||||
def _checkRange(self, value, name, min=0, max=100):
|
||||
"""Check that the value is in the given range.
|
||||
Raises an exception with useful info for invalid values. Name is used to
|
||||
let the user know which value is wrong."""
|
||||
try:
|
||||
value = int(value)
|
||||
except:
|
||||
raise ValueError("Invalid %s value: %s" % (name, value))
|
||||
assert value >= min and value <= max, "Invalid %s. %ss must be in"\
|
||||
" the %s %d-%d" % \
|
||||
(name, name.capitalize(), value, min, max)
|
||||
|
||||
return value
|
||||
|
||||
def _convertAngle(self, value, name):
|
||||
"""Parse the given value as an angle in degrees, and return its value
|
||||
in radians. Raise useful errors.
|
||||
Name is used in the errors to describe the field."""
|
||||
try:
|
||||
angle = math.radians(value)
|
||||
except:
|
||||
raise ValueError("Invalid %s value: %s" % (name, value))
|
||||
|
||||
assert angle >= 0 and angle < 2*math.pi, "Invalid %s; "\
|
||||
"It be in the range 0 and 359." % name
|
||||
|
||||
return angle
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
import math
|
||||
|
||||
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 = 2*math.pi - theta
|
||||
|
||||
return r, theta
|
||||
|
||||
def reduceAngle(angle):
|
||||
"""Reduce the angle such that it is in 0 <= angle < 2pi"""
|
||||
|
||||
while angle >= math.pi*2:
|
||||
angle = angle - math.pi*2
|
||||
while angle < 0:
|
||||
angle = angle + math.pi*2
|
||||
|
||||
return angle
|
||||
|
||||
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)..
|
||||
"""
|
||||
|
||||
for angle in angle1, angle2:
|
||||
assert angle < 2*math.pi and angle >= 0, \
|
||||
'angleDiff: bad angle %s' % angle
|
||||
|
||||
diff = angle2 - angle1
|
||||
if diff > math.pi:
|
||||
diff = diff - 2*math.pi
|
||||
elif diff < -math.pi:
|
||||
diff = diff + 2*math.pi
|
||||
|
||||
return diff
|
||||
|
||||
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
|
|
@ -0,0 +1,399 @@
|
|||
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 = []
|
||||
for p in tmpPlayers:
|
||||
p = unquote(p)
|
||||
if (not (p.startswith('.')
|
||||
or p.endswith('#')
|
||||
or p.endswith('~'))
|
||||
and teams.exists(p)):
|
||||
players.append(p)
|
||||
|
||||
AIs = {}
|
||||
for player in players:
|
||||
AIs[player] = open(os.path.join(self._playerDir, player)).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 = math.sqrt(len(players) + 1)
|
||||
if int(cols) != cols:
|
||||
cols = cols + 1
|
||||
|
||||
cols = int(cols)
|
||||
cols = max(cols, 2)
|
||||
|
||||
rows = len(players)/cols
|
||||
if len(players) % cols != 0:
|
||||
rows = rows + 1
|
||||
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, testMode=True)
|
||||
if player == None:
|
||||
tank.program(random.choice(defaultAIs))
|
||||
else:
|
||||
tank.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 = [\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()
|
||||
deadThisTurn = set()
|
||||
|
||||
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')
|
||||
|
||||
# Draw the explosions
|
||||
for tank in self._deadTanks:
|
||||
tank.draw(hdr)
|
||||
|
||||
# Draw the live tanks.
|
||||
for tank in self._tanksByX:
|
||||
# Have the tank run its program, move, etc.
|
||||
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')
|
||||
|
||||
# Write everything out
|
||||
html.write(self.gameFilename,
|
||||
'Tanks round %d' % self.gameNum,
|
||||
body.getvalue(),
|
||||
hdr=hdr.getvalue(),
|
||||
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."""
|
||||
if tank.name == None:
|
||||
return
|
||||
|
||||
if tank._program.errors:
|
||||
print tank.name, 'has errors'
|
||||
|
||||
|
||||
fileName = os.path.join(self._errorDir, quote(tank.name, ''))
|
||||
file = open(fileName, 'w')
|
||||
for error in tank._program.errors:
|
||||
file.write(error)
|
||||
file.write('\n')
|
||||
file.close()
|
||||
|
||||
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, 'ai', 'players')
|
||||
|
||||
def _getDefaultAIs(self, basedir):
|
||||
"""Load all the house bot AIs."""
|
||||
defaultAIs = []
|
||||
|
||||
path = os.path.join(basedir, 'ai', 'house')
|
||||
files = os.listdir(path)
|
||||
for fn in files:
|
||||
if fn.startswith('.'):
|
||||
continue
|
||||
|
||||
fn = os.path.join(path, fn)
|
||||
file = open(fn)
|
||||
defaultAIs.append(file.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"
|
||||
|
||||
|
|
@ -0,0 +1,234 @@
|
|||
"""<H2>Introduction</H2>
|
||||
You are the proud new operator of a M-375 Pflanzarr Tank. Your tank is
|
||||
equipped with a powerful laser cannon, independently rotating turret
|
||||
section, up to 10 enemy detection sensors, and a standard issue NATO hull.
|
||||
Unfortunately, it lacks seats, and thus must rely own its own wits and your
|
||||
skills at designing those wits to survive.
|
||||
|
||||
<H2>Programming Your Tank</H2>
|
||||
Your tanks are programmed using the Super Useful Command and Kontrol language,
|
||||
the very best in laser tank AI languages. It includes amazing features such
|
||||
as comments (Started by a #, ended at EOL), logic, versatility, and
|
||||
semi-colons (all lines must end in one). As with all new military systems
|
||||
it utilizes only integers; we must never rest in our
|
||||
diligence against the communist floating point conspiracy. Whitespace is
|
||||
provided by trusted contractors, and should never interfere with operations.
|
||||
<P>
|
||||
Your program should be separated into Setup and AI commands. The definitions
|
||||
section lets you designated the behaviors of its sensors and memory.
|
||||
Each setup command must begin with a '>'. Placing setup commands after
|
||||
the first AI command is a violation of protocol.
|
||||
Here are some examples of correct setup commands:
|
||||
<pre class="docs">>addsensor(80, 90, 33);
|
||||
>addsensor(50, 0, 10, 1);
|
||||
>addtimer(3);</pre>
|
||||
|
||||
The AI section will act as the brain of your tank. Each AI line is
|
||||
separated into a group of conditions functions and a group of action
|
||||
functions. If all the conditions are satisfactory (true), all of the actions
|
||||
are given as orders. Conditions are separated by ampersands, actions separated
|
||||
by periods. Here are some examples of AI commands:
|
||||
<pre class="docs">
|
||||
sensor(1) & sensor(2) & fireready() : fire();
|
||||
sensor(0,0)&sin(5): move(40, 30) . turretcw(50);
|
||||
sensor(4) & random(4,5) : led(1).settoggle(0,1);</pre>
|
||||
|
||||
Your tank will check its program each turn, and attempt to the best of its
|
||||
abilities to carry out its orders (or die trying). Like any military mind,
|
||||
your tank may receive a plethora of often conflicting orders and information.
|
||||
This a SMART TANK, however. It knows that the proper thing to do with each
|
||||
subsystem is to have that subsystem follow only the last order given each turn.
|
||||
"""
|
||||
|
||||
import traceback
|
||||
import conditions
|
||||
import actions
|
||||
import setup
|
||||
|
||||
class Statement(object):
|
||||
"""Represents a single program statement. If all the condition Functions
|
||||
evaluate to True, the actions are all executed in order."""
|
||||
|
||||
def __init__(self, lineNum, line, conditions, actions):
|
||||
self.lineNum = lineNum
|
||||
self.line = line
|
||||
self._conditions = conditions
|
||||
self._actions = actions
|
||||
|
||||
def __call__(self, tank):
|
||||
success = True
|
||||
for condition in self._conditions:
|
||||
if not condition(tank):
|
||||
success = False
|
||||
break
|
||||
|
||||
if success:
|
||||
for action in self._actions:
|
||||
action(tank)
|
||||
|
||||
class Program(object):
|
||||
"""This parses and represents a Tank program."""
|
||||
CONDITION_SEP = '&'
|
||||
ACTION_SEP = '.'
|
||||
|
||||
def __init__(self, text):
|
||||
"""Initialize this program, parsing the given text."""
|
||||
self.errors = []
|
||||
|
||||
self._program, self._setup = self._parse(text)
|
||||
|
||||
def setup(self, tank):
|
||||
"""Execute all the setup actions."""
|
||||
for action in self._setup:
|
||||
try:
|
||||
action(tank)
|
||||
except Exception, msg:
|
||||
self.errors.append("Bad setup action, line %d, msg: %s" % \
|
||||
(action.lineNum, msg))
|
||||
|
||||
def __call__(self, tank):
|
||||
"""Execute this program on the given tank."""
|
||||
for statement in self._program:
|
||||
try:
|
||||
statement(tank)
|
||||
except Exception, msg:
|
||||
traceback.print_exc()
|
||||
self.errors.append('Error executing program. \n'
|
||||
'(%d) - %s\n'
|
||||
'msg: %s\n' %
|
||||
(statement.lineNum, statement.line, msg) )
|
||||
|
||||
def _parse(self, text):
|
||||
"""Parse the text of the given program."""
|
||||
program = []
|
||||
setup = []
|
||||
inSetup = True
|
||||
lines = text.split(';')
|
||||
lineNum = 0
|
||||
for line in lines:
|
||||
lineNum = lineNum + 1
|
||||
|
||||
originalLine = line
|
||||
|
||||
# Remove Comments
|
||||
parts = line.split('\n')
|
||||
for i in range(len(parts)):
|
||||
comment = parts[i].find('#')
|
||||
if comment != -1:
|
||||
parts[i] = parts[i][:comment]
|
||||
# Remove all whitespace
|
||||
line = ''.join(parts)
|
||||
line = line.replace('\r', '')
|
||||
line = line.replace('\t', '')
|
||||
line = line.replace(' ', '')
|
||||
|
||||
if line == '':
|
||||
continue
|
||||
|
||||
if line.startswith('>'):
|
||||
if inSetup:
|
||||
if '>' in line[1:] or ':' in line:
|
||||
self.errors.append('(%d) Missing semicolon: %s' %
|
||||
(lineNum, line))
|
||||
continue
|
||||
|
||||
try:
|
||||
setupAction = self._parseSection(line[1:], 'setup')[0]
|
||||
setupAction.lineNum = lineNum
|
||||
setup.append(setupAction)
|
||||
except Exception, msg:
|
||||
self.errors.append('(%d) Error parsing setup line: %s'
|
||||
'\nThe error was: %s' %
|
||||
(lineNum, originalLine, msg))
|
||||
|
||||
continue
|
||||
else:
|
||||
self.errors.append('(%d) Setup lines aren\'t allowed '
|
||||
'after the first command: %s' %
|
||||
(lineNum, originalLine))
|
||||
else:
|
||||
# We've hit the first non-blank, non-comment, non-setup
|
||||
# line
|
||||
inSetup = False
|
||||
|
||||
semicolons = line.count(':')
|
||||
if semicolons > 1:
|
||||
self.errors.append('(%d) Missing semicolon: %s' %
|
||||
(lineNum, line))
|
||||
continue
|
||||
elif semicolons == 1:
|
||||
conditions, actions = line.split(':')
|
||||
else:
|
||||
self.errors.append('(%d) Invalid Line, no ":" seperator: %s'%
|
||||
(lineNum, line) )
|
||||
|
||||
try:
|
||||
conditions = self._parseSection(conditions, 'condition')
|
||||
except Exception, msg:
|
||||
self.errors.append('(%d) %s - "%s"' %
|
||||
(lineNum, msg, line) )
|
||||
continue
|
||||
|
||||
try:
|
||||
actions = self._parseSection(actions, 'action')
|
||||
except Exception, msg:
|
||||
self.errors.append('(%d) %s - "%s"' %
|
||||
(lineNum, msg, originalLine) )
|
||||
continue
|
||||
program.append(Statement(lineNum, line, conditions, actions))
|
||||
|
||||
return program, setup
|
||||
|
||||
def _parseSection(self, section, sectionType):
|
||||
"""Parses either the action or condition section of each command.
|
||||
@param section: The text of the section of the command to be parsed.
|
||||
@param sectionType: The type of section to be parsed. Should be:
|
||||
'condition', 'action', or 'setup'.
|
||||
@raises ValueError: Raises ValueErrors when parsing errors occur.
|
||||
@returns: Returns a list of parsed section components (Function objects).
|
||||
"""
|
||||
|
||||
if sectionType == 'condition':
|
||||
parts = section.split(self.CONDITION_SEP)
|
||||
functions = conditions.conditions
|
||||
if section == '':
|
||||
return []
|
||||
elif sectionType == 'action':
|
||||
parts = section.split(self.ACTION_SEP)
|
||||
functions = actions.actions
|
||||
if section == '':
|
||||
raise ValueError("The action section cannot be empty.")
|
||||
elif sectionType == 'setup':
|
||||
parts = [section]
|
||||
functions = setup.setup
|
||||
else:
|
||||
raise ValueError('Invalid section Type - Contact Contest Admin')
|
||||
|
||||
parsed = []
|
||||
for part in parts:
|
||||
|
||||
pos = part.find('(')
|
||||
if pos == -1:
|
||||
raise ValueError("Missing open paren in %s: %s" %
|
||||
(sectionType, part) )
|
||||
funcName = part[:pos]
|
||||
|
||||
if funcName not in functions:
|
||||
raise ValueError("%s function %s is not accepted." %
|
||||
(sectionType.capitalize(), funcName) )
|
||||
|
||||
if part[-1] != ')':
|
||||
raise ValueError("Missing closing paren in %s: %s" %
|
||||
(condition, sectionType) )
|
||||
|
||||
args = part[pos+1:-1]
|
||||
if args != '':
|
||||
args = args.split(',')
|
||||
for i in range(len(args)):
|
||||
args[i] = int(args[i])
|
||||
else:
|
||||
args = []
|
||||
|
||||
parsed.append(functions[funcName](*args))
|
||||
|
||||
return parsed
|
|
@ -0,0 +1,479 @@
|
|||
import math
|
||||
import random
|
||||
from sets import Set as set
|
||||
|
||||
import GameMath as gm
|
||||
import Program
|
||||
|
||||
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
|
||||
SPEED = 7.0
|
||||
# Max acceleration, as a fraction of speed.
|
||||
ACCEL = 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,
|
||||
testMode=True):
|
||||
"""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.
|
||||
@param testMode: When True, extra debugging information is displayed. Namely,
|
||||
arcs for each sensor are drawn, which turn white when
|
||||
activated.
|
||||
"""
|
||||
|
||||
# Keep track of what turn number it is for this tank.
|
||||
self._turn = 0
|
||||
|
||||
self.name = name
|
||||
self._testMode = testMode
|
||||
|
||||
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
|
||||
# The frame of the death animation.
|
||||
self._deadFrame = 10
|
||||
# Death reason
|
||||
self.deathReason = 'survived'
|
||||
|
||||
def __repr__(self):
|
||||
return '<tank: %s, (%d, %d)>' % (self.name, self.pos[0], self.pos[1])
|
||||
|
||||
def get_turn(self):
|
||||
return self._turn
|
||||
turn = property(get_turn)
|
||||
|
||||
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 (percent).
|
||||
@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 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 addTimer(self, period):
|
||||
"""Add a timer with timeout period 'period'."""
|
||||
|
||||
if len(self._timers) >= self.SENSOR_LIMIT:
|
||||
raise ValueError('You can only have 10 timers')
|
||||
|
||||
self._timers.append(None)
|
||||
self._timerPeriods.append(period)
|
||||
|
||||
def resetTimer(self, key):
|
||||
"""Reset, and start the given timer, but only if it is inactive.
|
||||
If it is active, raise a ValueError."""
|
||||
if self._timer[key] is None:
|
||||
self._timer[key] = self._timerPeriods[key]
|
||||
else:
|
||||
raise ValueError("You can't reset an active timer (#%d)" % key)
|
||||
|
||||
def clearTimer(self, key):
|
||||
"""Clear the timer."""
|
||||
self._timer[key] = None
|
||||
|
||||
def checkTimer(self, key):
|
||||
"""Returns True if the timer has expired."""
|
||||
return self._timer[key] == 0
|
||||
|
||||
def _manageTimers(self):
|
||||
"""Decrement each active timer."""
|
||||
for i in range(len(self._timers)):
|
||||
if self._timers[i] is not None and \
|
||||
self._timers[i] > 0:
|
||||
self._timers[i] = self._timers[i] - 1
|
||||
|
||||
def program(self, text):
|
||||
"""Set the program for this tank."""
|
||||
|
||||
self._program = Program.Program(text)
|
||||
self._program.setup(self)
|
||||
|
||||
def execute(self):
|
||||
"""Execute this tanks program."""
|
||||
|
||||
# Decrement the active timers
|
||||
self._manageTimers()
|
||||
self.led = False
|
||||
|
||||
self._program(self)
|
||||
|
||||
self._move(self._nextMove[0], self._nextMove[1])
|
||||
self._moveTurret()
|
||||
if self._fireReady > 0:
|
||||
self._fireReady = self._fireReady - 1
|
||||
|
||||
self._turn = self._turn + 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.ACCEL < lSpeed:
|
||||
lSpeed = self._lastSpeed[0] + self.ACCEL
|
||||
elif self._lastSpeed[0] > lSpeed and \
|
||||
self._lastSpeed[0] - self.ACCEL > lSpeed:
|
||||
lSpeed = self._lastSpeed[0] - self.ACCEL
|
||||
|
||||
if self._lastSpeed[1] < rSpeed and \
|
||||
self._lastSpeed[1] + self.ACCEL < rSpeed:
|
||||
rSpeed = self._lastSpeed[1] + self.ACCEL
|
||||
elif self._lastSpeed[1] > rSpeed and \
|
||||
self._lastSpeed[1] - self.ACCEL > rSpeed:
|
||||
rSpeed = self._lastSpeed[1] - self.ACCEL
|
||||
|
||||
self._lastSpeed = lSpeed, rSpeed
|
||||
|
||||
# The simple case
|
||||
if lSpeed == rSpeed:
|
||||
fSpeed = self.SPEED*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.SPEED * lSpeed/100.0
|
||||
R = self.SPEED * rSpeed/100.0
|
||||
friction = .75 * abs(L-R)/(2.0*self.SPEED)
|
||||
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 draw(self, f):
|
||||
"""Output this tank's state as JSON.
|
||||
|
||||
[color, x, y, angle, turret_angle, led, fired]
|
||||
|
||||
"""
|
||||
|
||||
f.write(' [')
|
||||
f.write(str(int(self.isDead)));
|
||||
f.write(',')
|
||||
f.write(repr(self.color))
|
||||
f.write(',')
|
||||
f.write('%d' % self.pos[0])
|
||||
f.write(',')
|
||||
f.write('%d' % self.pos[1])
|
||||
f.write(',')
|
||||
f.write('%.2f' % self._angle)
|
||||
f.write(',')
|
||||
f.write('%.2f' % self._tAngle)
|
||||
f.write(',')
|
||||
f.write(str(int(self.led)))
|
||||
f.write(',')
|
||||
f.write('%d' % (self._fired and self.FIRE_RANGE) or 0)
|
||||
if not self.isDead:
|
||||
f.write(',[')
|
||||
for i in range(len(self._sensors)):
|
||||
dist, sensorAngle, width, tSens = self._sensors[i]
|
||||
|
||||
# If the angle is tied to the turret, add that.
|
||||
if tSens:
|
||||
sensorAngle = sensorAngle + self._tAngle
|
||||
|
||||
f.write('[')
|
||||
f.write(str(int(dist)))
|
||||
f.write(',')
|
||||
f.write('%.2f' % (sensorAngle - width/2));
|
||||
f.write(',')
|
||||
f.write('%.2f' % (sensorAngle + width/2));
|
||||
f.write(',')
|
||||
f.write(str(int(self._sensorState[i])))
|
||||
f.write('],')
|
||||
f.write(']')
|
||||
|
||||
f.write('],\n')
|
|
@ -0,0 +1,126 @@
|
|||
"""Define new action Functions here. They should inherit from the
|
||||
Function.Function class. To make an action usable, add it to the
|
||||
actions dictionary at the end of this file."""
|
||||
|
||||
import Function
|
||||
|
||||
class Move(Function.Function):
|
||||
"""move(left tread speed, right tread speed)
|
||||
Set the speeds for the tanks right and left treads. The speeds should
|
||||
be a number (percent power) between -100 and 100."""
|
||||
|
||||
def __init__(self, left, right):
|
||||
self._checkRange(left, 'left tread speed', min=-100)
|
||||
self._checkRange(right, 'right tread speed', min=-100)
|
||||
|
||||
self._left = left
|
||||
self._right = right
|
||||
|
||||
def __call__(self, tank):
|
||||
tank.setMove(self._left, self._right)
|
||||
|
||||
class TurretCounterClockwise(Function.Function):
|
||||
"""turretccw([percent speed])
|
||||
Rotate the turret counter clockwise as a percentage of the max speed."""
|
||||
def __init__(self, speed=100):
|
||||
self._checkRange(speed, 'turret percent speed')
|
||||
self._speed = speed/100.0
|
||||
def __call__(self, tank):
|
||||
tank.setTurretAngle(tank._tAngle - tank.TURRET_TURN_RATE*self._speed)
|
||||
|
||||
class TurretClockwise(Function.Function):
|
||||
"""turretcw([percent speed])
|
||||
Rotate the turret clockwise at a rate preportional to speed."""
|
||||
|
||||
def __init__(self, speed=100):
|
||||
self._checkRange(speed, 'turret percent speed')
|
||||
self._speed = speed/100.0
|
||||
def __call__(self, tank):
|
||||
tank.setTurretAngle(tank._tAngle + tank.TURRET_TURN_RATE*self._speed)
|
||||
|
||||
class TurretSet(Function.Function):
|
||||
"""turretset([angle])
|
||||
Set the turret to the given angle, in degrees, relative to the front of
|
||||
the tank. Angles increase counterclockwise.
|
||||
The angle can be left out; in that case, this locks the turret
|
||||
to it's current position."""
|
||||
|
||||
def __init__(self, angle=None):
|
||||
# Convert the angle to radians
|
||||
if angle is not None:
|
||||
angle = self._convertAngle(angle, 'turret angle')
|
||||
|
||||
self._angle = angle
|
||||
|
||||
def __call__(self, tank):
|
||||
tank.setTurretAngle(self._angle)
|
||||
|
||||
class Fire(Function.Function):
|
||||
"""fire()
|
||||
Attempt to fire the tanks laser cannon."""
|
||||
|
||||
def __call__(self, tank):
|
||||
tank.setFire()
|
||||
|
||||
class SetToggle(Function.Function):
|
||||
"""settoggle(key, state)
|
||||
Set toggle 'key' to 'state'.
|
||||
"""
|
||||
def __init__(self, key, state):
|
||||
self._key = key
|
||||
self._state = state
|
||||
def __call__(self, tank):
|
||||
tank.toggles[self._key] = self._state
|
||||
|
||||
class Toggle(Function.Function):
|
||||
"""toggle(key)
|
||||
Toggle the value of toggle 'key'.
|
||||
"""
|
||||
def __init__(self, key):
|
||||
self._key = key
|
||||
def __call__(self, tank):
|
||||
try:
|
||||
tank.toggles[self._key] = not tank.toggles[self._key]
|
||||
except IndexError:
|
||||
raise IndexError('Invalid toggle: %d' % self._key)
|
||||
|
||||
class LED(Function.Function):
|
||||
"""led(state)
|
||||
Set the tanks LED to state (true is on, false is off).
|
||||
The led is a light that appears behind the tanks turret.
|
||||
It remains on for a single turn."""
|
||||
def __init__(self, state=1):
|
||||
self._state = state
|
||||
def __call__(self, tank):
|
||||
tank.led = self._state
|
||||
|
||||
class StartTimer(Function.Function):
|
||||
"""starttimer(#)
|
||||
Start (and reset) the given timer, but only if it is inactive.
|
||||
"""
|
||||
def __init__(self, key):
|
||||
self._key = key
|
||||
def __call__(self, tank):
|
||||
tank.resetTimer(key)
|
||||
|
||||
class ClearTimer(Function.Function):
|
||||
"""cleartimer(#)
|
||||
Clear the given timer such that it is no longer active (inactive timers
|
||||
are always False)."""
|
||||
def __init__(self, key):
|
||||
self._key = key
|
||||
def __call__(self, tank):
|
||||
tank.clearTimer(self._key)
|
||||
|
||||
### When adding names to this dict, make sure they are lower case and alpha
|
||||
### numeric.
|
||||
actions = {'move': Move,
|
||||
'turretccw': TurretCounterClockwise,
|
||||
'turretcw': TurretClockwise,
|
||||
'turretset': TurretSet,
|
||||
'fire': Fire,
|
||||
'settoggle': SetToggle,
|
||||
'toggle': Toggle,
|
||||
'led': LED,
|
||||
'starttimer': StartTimer,
|
||||
'cleartimer': ClearTimer}
|
|
@ -0,0 +1,126 @@
|
|||
"""Define new condition functions here. Add it to the conditions dictionary
|
||||
at the end to make it usable by Program.Program. These should inherit from
|
||||
Function.Function."""
|
||||
|
||||
import Function
|
||||
import math
|
||||
import random
|
||||
|
||||
class Sense(Function.Function):
|
||||
"""sense(#, [invert])
|
||||
Takes a Sensor number as an argument.
|
||||
Returns True if the given sensor is currently activated, False otherwise.
|
||||
If the option argument invert is set to true then logic is inverted,
|
||||
and then sensor returns True when it is NOT activated, and False when
|
||||
it is. Invert is false by default.
|
||||
"""
|
||||
|
||||
def __init__(self, sensor, invert=0):
|
||||
self._sensor = sensor
|
||||
self._invert = invert
|
||||
|
||||
def __call__(self, tank):
|
||||
state = tank.getSensorState(self._sensor)
|
||||
if self._invert:
|
||||
return not state
|
||||
else:
|
||||
return state
|
||||
|
||||
class Toggle(Function.Function):
|
||||
"""toggle(#)
|
||||
Returns True if the given toggle is set, False otherwise. """
|
||||
def __init__(self, toggle):
|
||||
self._toggle = toggle
|
||||
def __call__(self, tank):
|
||||
return tank.toggles[toggle]
|
||||
|
||||
class TimerCheck(Function.Function):
|
||||
"""timer(#, [invert])
|
||||
Checks the state of timer # 'key'. Returns True if time has run out.
|
||||
If invert is given (and true), then True is returned if the timer has
|
||||
yet to expire.
|
||||
"""
|
||||
def __init__(self, key, invert=0):
|
||||
self._key = key
|
||||
self._invert = invert
|
||||
def __call__(self, tank):
|
||||
state = tank.checkTimer(self._key)
|
||||
if invert:
|
||||
return not state
|
||||
else:
|
||||
return state
|
||||
|
||||
class Random(Function.Function):
|
||||
"""random(n,m)
|
||||
Takes two arguments, n and m. Generates a random number between 1
|
||||
and m (inclusive) each time it's checked. If the random number is less
|
||||
than or equal
|
||||
to n, then the condition returns True. Returns False otherwise."""
|
||||
|
||||
def __init__(self, n, m):
|
||||
self._n = n
|
||||
self._m = m
|
||||
|
||||
def __call__(self, tank):
|
||||
if random.randint(1,self._m) <= self._n:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
class Sin(Function.Function):
|
||||
"""sin(T)
|
||||
A sin wave of period T (in turns). Returns True when the wave is positive.
|
||||
A wave with period 1 or 2 is always False (it's 0 each turn), only
|
||||
at periods of 3 or more does this become useful."""
|
||||
|
||||
def __init__(self, T):
|
||||
self._T = T
|
||||
|
||||
def __call__(self, tank):
|
||||
turn = tank.turn
|
||||
factor = math.pi/self._T
|
||||
if math.sin(turn * factor) > 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
class Cos(Function.Function):
|
||||
"""cos(T)
|
||||
A cos wave with period T (in turns). Returns True when the wave is
|
||||
positive. A wave of period 1 is always True. Period 2 is True every
|
||||
other turn, etc."""
|
||||
|
||||
def __init__(self, T):
|
||||
self._T = T
|
||||
|
||||
def __call__(self, tank):
|
||||
|
||||
turn = tank.turn
|
||||
factor = math.pi/self._T
|
||||
if math.cos(turn * factor) > 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
class FireReady(Function.Function):
|
||||
"""fireready()
|
||||
True when the tank can fire."""
|
||||
def __call__(self, tank):
|
||||
return tank.fireReady()
|
||||
|
||||
class FireNotReady(Function.Function):
|
||||
"""firenotready()
|
||||
True when the tank can not fire."""
|
||||
def __call__(self, tank):
|
||||
return not tank.fireReady()
|
||||
|
||||
### When adding names to this dict, make sure they are lower case and alpha
|
||||
### numeric.
|
||||
conditions = {'sense': Sense,
|
||||
'random': Random,
|
||||
'toggle': Toggle,
|
||||
'sin': Sin,
|
||||
'cos': Cos,
|
||||
'fireready': FireReady,
|
||||
'firenotready': FireNotReady,
|
||||
'timer': TimerCheck }
|
|
@ -0,0 +1,26 @@
|
|||
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)
|
||||
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
"""Each of these classes provides a function for configuring a tank.
|
||||
They should inherit from Function.Function.
|
||||
To make one available to the tank programmer, add it to the dictionary at
|
||||
the end of this file."""
|
||||
|
||||
import Function
|
||||
|
||||
class AddSensor(Function.Function):
|
||||
"""addsensor(range, angle, width, [turretAttached])
|
||||
Add a new sensor to the tank. Sensors are an arc (pie slice) centered on
|
||||
the tank that detect other tanks within their sweep.
|
||||
A sensor is 'on' if another tank is within this arc.
|
||||
Sensors are numbered, starting at 0, in the order they are added.
|
||||
<p>
|
||||
range - The range of the sensor, as a percent of the tanks max range.
|
||||
angle - The angle of the center of the sensor, in degrees.
|
||||
width - The width of the sensor, in percent (100 is a full circle).
|
||||
turretAttached - Normally, the angle is relative to the front of the
|
||||
tank. When this is set, the angle is relative to the current turret
|
||||
direction.
|
||||
<p>
|
||||
Sensors are drawn for each tank, but not in the way you might expect.
|
||||
Instead of drawing a pie slice (the actual shap of the sensor), an arc with
|
||||
the end points connected by a line is drawn. Sensors with 0 width don't show
|
||||
up, but still work.
|
||||
"""
|
||||
|
||||
def __init__(self, range, angle, width, turretAttached=False):
|
||||
|
||||
self._checkRange(range, 'sensor range')
|
||||
|
||||
self._range = range / 100.0
|
||||
self._width = self._convertAngle(width, 'sensor width')
|
||||
self._angle = self._convertAngle(angle, 'sensor angle')
|
||||
self._turretAttached = turretAttached
|
||||
|
||||
def __call__(self, tank):
|
||||
tank.addSensor(self._range, self._angle, self._width,
|
||||
self._turretAttached)
|
||||
|
||||
class AddToggle(Function.Function):
|
||||
"""addtoggle([state])
|
||||
Add a toggle to the tank. The state of the toggle defaults to 0 (False).
|
||||
These essentially act as a single bit of memory.
|
||||
Use the toggle() condition to check its state and the settoggle, cleartoggle,
|
||||
and toggle actions to change the state. Toggles are named numerically,
|
||||
starting at 0.
|
||||
"""
|
||||
def __init__(self, state=0):
|
||||
self._state = state
|
||||
|
||||
def __call__(self, tank):
|
||||
if len(tank.toggles) >= tank.SENSOR_LIMIT:
|
||||
raise ValueError('You can not have more than 10 toggles.')
|
||||
|
||||
tank.toggles.append(self._state)
|
||||
|
||||
class AddTimer(Function.Function):
|
||||
"""addtimer(timeout)
|
||||
Add a new timer (they're numbered in the order added, starting from 0),
|
||||
with the given timeout. The timeout is in number of turns. The timer
|
||||
is created in inactive mode. You'll need to do a starttimer() action
|
||||
to reset and start the timer. When the timer expires, the timer()
|
||||
condition will begin to return True."""
|
||||
def __init__(self, timeout):
|
||||
self._timeout = timeout
|
||||
def __call__(self, tank):
|
||||
tank.addTimer(timeout)
|
||||
|
||||
setup = {'addsensor': AddSensor,
|
||||
'addtoggle': AddToggle,
|
||||
'addtimer': AddTimer}
|
|
@ -0,0 +1,33 @@
|
|||
#! /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
|
|
@ -0,0 +1,35 @@
|
|||
#! /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))
|
|
@ -0,0 +1,6 @@
|
|||
VAR = "/opt/ctf/var"
|
||||
WWW = "/opt/ctf/www"
|
||||
LIB = "/opt/ctf/lib"
|
||||
BIN = "/opt/ctf/bin"
|
||||
SBIN = "/opt/ctf/sbin"
|
||||
BASE_URL = "/ctf/"
|
|
@ -0,0 +1,40 @@
|
|||
#! /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()
|
|
@ -0,0 +1,72 @@
|
|||
#! /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]
|
|
@ -0,0 +1,5 @@
|
|||
random(1,10): move(50, 100);
|
||||
random(1,10): move(100, 50);
|
||||
random(1,10): turretcw();
|
||||
random(1,10): turretccw();
|
||||
random(1,30): fire();
|
|
@ -0,0 +1,8 @@
|
|||
>addsensor(50, 0, 0, 1);
|
||||
>addsensor(70, 0, 50); # 1-Anti-collision sensor
|
||||
|
||||
: move(100, 100) . turretset(180);
|
||||
random(1, 8): move(70, 100);
|
||||
random(1, 8): move(100, 70);
|
||||
sense(0) : fire();
|
||||
sense(1) : move(-0, 100);
|
|
@ -0,0 +1,52 @@
|
|||
|
||||
>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();
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
# 3
|
||||
# 000 33
|
||||
# 2
|
||||
# 2
|
||||
# 2
|
||||
# 11111 4
|
||||
# 4
|
||||
# 4
|
||||
# @@/
|
||||
# @@@
|
||||
# @@@
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
>addsensor(50, 0, 05, 1); # 0 Fire Sensor
|
||||
>addsensor(30, 0, 50); # 1 Anti-collision sensor
|
||||
>addsensor(50, 0, 10); # 2 Anti-collision sensor
|
||||
>addsensor(100, 315, 100, 1); # 3 Turret ccw
|
||||
>addsensor(100, 45, 100, 1); # 4 Turret cw
|
||||
>addsensor(60, 180, 180, 0); # 5 Ass
|
||||
|
||||
##
|
||||
## Add "ears" so the tank is easy to pick out.
|
||||
##
|
||||
>addsensor(20, 90, 30, 0);
|
||||
>addsensor(20, 270, 30, 0);
|
||||
|
||||
# Can't fire
|
||||
: led(0) . move(80, 80) . turretset(0);
|
||||
random(1, 3): led(0) . move(60, 80) . turretset(0);
|
||||
random(2, 3): led(0) . move(80, 60) . turretset(0);
|
||||
|
||||
sense(0) : led(0) . move(10, 20) . turretset(0);
|
||||
sense(1) : led(0) . move(10, 10) . turretset(0);
|
||||
sense(2) : led(0) . move(10, 20) . turretset(0);
|
||||
sense(3) : led(0) . move(70, 50) . turretset(0);
|
||||
sense(4) : led(0) . move(50, 70) . turretset(0);
|
||||
sense(3) & sense(4): led(0) . move(-100, 20) . turretset(0);
|
||||
sense(5) : led(0) . move(100, 50) . turretset(0);
|
||||
|
||||
|
||||
# Can fire
|
||||
fireready() : led(1) . move(70, 70) . turretset(0);
|
||||
fireready() & random(2, 40): led(1) . move(40, 70) . turretset(0);
|
||||
fireready() & random(1, 40): led(1) . move(70, 40) . turretset(0);
|
||||
|
||||
fireready() & sense(3) : led(1) . move(0, 60) . turretccw(50);
|
||||
fireready() & sense(4) : led(1) . move(60, 0) . turretcw(50);
|
||||
fireready() & sense(3) & sense(4): led(1) . move(100, 100) . turretset();
|
||||
fireready() & sense(1) : led(1) . turretset(0) . move(10, 10);
|
||||
fireready() & sense(2) : led(1) . turretset(0) . move(10, 10);
|
||||
fireready() & sense(0) : led(1) . turretset() . fire();
|
||||
|
||||
fireready() & sense(5) : led(1) . move(100, 40);
|
|
@ -0,0 +1,22 @@
|
|||
>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();
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
>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();
|
|
@ -0,0 +1,8 @@
|
|||
>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)
|
|
@ -0,0 +1,9 @@
|
|||
>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();
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# 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();
|
|
@ -0,0 +1,140 @@
|
|||
#! /usr/bin/python
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import optparse
|
||||
import string
|
||||
import markdown
|
||||
from codecs import open
|
||||
|
||||
p = optparse.OptionParser()
|
||||
p.add_option('-t', '--template', dest='template', default='template.html',
|
||||
help='Location of HTML template')
|
||||
p.add_option('-b', '--base', dest='base', default='',
|
||||
help='Base URL for contest')
|
||||
p.add_option('-p', '--puzzles', dest='puzzles', default='puzzles',
|
||||
help='Directory containing puzzles')
|
||||
p.add_option('-w', '--htmldir', dest='htmldir', default='puzzler',
|
||||
help='Directory to write HTML puzzle tree')
|
||||
p.add_option('-k', '--keyfile', dest='keyfile', default='puzzler.keys',
|
||||
help='Where to write keys')
|
||||
|
||||
opts, args = p.parse_args()
|
||||
|
||||
keys = []
|
||||
|
||||
tmpl_f = open(opts.template, encoding='utf-8')
|
||||
template = string.Template(tmpl_f.read())
|
||||
tmpl_f.close()
|
||||
|
||||
js = '''
|
||||
<script type="text/javascript">
|
||||
function readCookie(key) {
|
||||
var s = key + '=';
|
||||
var toks = document.cookie.split(';');
|
||||
for (var i = 0; i < toks.length; i++) {
|
||||
var tok = toks[i];
|
||||
while (tok.charAt(0) == ' ') {
|
||||
tok = tok.substring(1, tok.length);
|
||||
}
|
||||
if (tok.indexOf(s) == 0) {
|
||||
return tok.substring(s.length, tok.length);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getTeamInfo() {
|
||||
team = readCookie('team');
|
||||
passwd = readCookie('passwd');
|
||||
if (team != null) {
|
||||
document.getElementById("form").t.value = team;
|
||||
}
|
||||
if (passwd != null) {
|
||||
document.getElementById("form").w.value = passwd;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
'''
|
||||
|
||||
for cat in os.listdir(opts.puzzles):
|
||||
dirname = os.path.join(opts.puzzles, cat)
|
||||
for points in os.listdir(dirname):
|
||||
pointsdir = os.path.join(dirname, points)
|
||||
if not os.path.isdir(pointsdir):
|
||||
continue
|
||||
|
||||
outdir = os.path.join(opts.htmldir, cat, points)
|
||||
try:
|
||||
os.makedirs(outdir)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
readme = ''
|
||||
files = []
|
||||
for fn in os.listdir(pointsdir):
|
||||
path = os.path.join(pointsdir, fn)
|
||||
if fn == 'key':
|
||||
for key in open(path, encoding='utf-8'):
|
||||
key = key.rstrip()
|
||||
keys.append((cat, points, key))
|
||||
elif fn == 'hint':
|
||||
pass
|
||||
elif fn == 'index.html':
|
||||
readme = open(path, encoding='utf-8').read()
|
||||
elif fn == 'index.mdwn':
|
||||
readme = open(path, encoding='utf-8').read()
|
||||
readme = markdown.markdown(readme)
|
||||
elif fn.endswith('~'):
|
||||
pass
|
||||
else:
|
||||
files.append((fn, path))
|
||||
|
||||
title = '%s for %s points' % (cat, points)
|
||||
|
||||
body = []
|
||||
if readme:
|
||||
body.append('<div class="readme">%s</div>\n' % readme)
|
||||
if files:
|
||||
body.append('<ul>\n')
|
||||
for fn, path in files:
|
||||
if os.path.isdir(path):
|
||||
shutil.rmtree(os.path.join(outdir, fn), ignore_errors=True)
|
||||
shutil.copytree(path, os.path.join(outdir, fn))
|
||||
else:
|
||||
shutil.copy(path, outdir)
|
||||
|
||||
if not fn.startswith(','):
|
||||
body.append('<li><a href="%s">%s</a></li>\n' % (fn, fn))
|
||||
body.append('</ul>\n')
|
||||
body.append('''
|
||||
<form id="form" action="%(base)spuzzler.cgi" method="post">
|
||||
<fieldset>
|
||||
<legend>Your answer:</legend>
|
||||
<input type="hidden" name="c" value="%(cat)s" />
|
||||
<input type="hidden" name="p" value="%(points)s" />
|
||||
Team: <input name="t" /><br />
|
||||
Password: <input type="password" name="w" /><br />
|
||||
Key: <input name="k" /><br />
|
||||
<input type="submit" />
|
||||
</fieldset>
|
||||
</form>
|
||||
''' % {'base': opts.base,
|
||||
'cat': cat,
|
||||
'points': points})
|
||||
|
||||
page = template.substitute(hdr=js,
|
||||
title=title,
|
||||
base=opts.base,
|
||||
links='',
|
||||
body_class='',
|
||||
onload = "getTeamInfo()",
|
||||
body=''.join(body))
|
||||
|
||||
f = open(os.path.join(outdir, 'index.html'), 'w', encoding='utf-8')
|
||||
f.write(page)
|
||||
|
||||
f = open(opts.keyfile, 'w', encoding='utf-8')
|
||||
for key in keys:
|
||||
f.write('%s\t%s\t%s\n' % key)
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
bletchley 50 extra special text
|
||||
bletchley 1000 It is a lovely day outside
|
||||
bletchley 150 jackalope wheeze
|
||||
bletchley 350 PC LOAD LETTER
|
||||
bletchley 300 jako561962
|
||||
bletchley 200 unequivocal
|
||||
bletchley 900 PEANUT BUTTER JELLY TIME
|
||||
bletchley 100 antediluvian
|
||||
bletchley 500 xez.3nt
|
||||
bletchley 250 DB1663<3
|
||||
net-re 4 PINHEAD CATASTROPHE
|
||||
net-re 2 great job
|
||||
net-re 25000 galloping gallimimus
|
||||
net-re 1000 a difficult key!
|
||||
net-re 7 60.0.13.65
|
||||
net-re 4000 gaucho moped fleet
|
||||
net-re 800 10.72.148.66
|
||||
net-re 5000 miniature commodore exercise
|
||||
net-re 300 pumpkins
|
||||
net-re 5 fishsticks
|
||||
net-re 30 RSTNFGEAID
|
||||
net-re 3000 galactic octopus
|
||||
net-re 200 particulate
|
||||
net-re 6 whatever.example.net
|
||||
net-re 2000 obtuse
|
||||
net-re 3 2,4,6,8,A,B,C,D,F
|
||||
net-re 400 lettuce
|
||||
net-re 100 chumbucket
|
||||
net-re 8 bacon
|
||||
net-re 1200 hotshot tomato
|
||||
net-re 20 squirrel
|
||||
net-re 1 163
|
||||
net-re 10 69.35
|
||||
net-re 700 fixate rasterize
|
||||
net-re 250 alice_test@hotmail.com
|
||||
survey 1000000 quux blorb frotz
|
||||
sequence 50 42
|
||||
sequence 2 111 1000
|
||||
sequence 35 13 21
|
||||
sequence 300 ┤
|
||||
sequence 25 36
|
||||
sequence 600 61 64 9b
|
||||
sequence 200 E G G
|
||||
sequence 16 a
|
||||
sequence 400 0101
|
||||
sequence 100 45
|
||||
sequence 8 100
|
||||
sequence 19 17
|
||||
sequence 1 6
|
||||
sequence 700 01 00 00 ca 0a
|
||||
sequence 450 05
|
||||
hispaniola 50 LANL GUYS
|
||||
hispaniola 5 3acd767f2717b84076cdcd18e882f01d
|
||||
hispaniola 125 ‽
|
||||
hispaniola 75 ⚑ ◢ ◕ ★ ♥ ◢ ♥ ⚑ ◕ ★ ♥ ◕ ★ ♥ ⚑ ★ ⚑ ◢ ♥ ◢ ◕ ◕ ◢ ★ ⚑
|
||||
hispaniola 15 -462766
|
||||
hispaniola 10 You're well on your way :)
|
||||
skynet 302 SkynetSasserVersionWithPingFast
|
||||
skynet 401 67678dj*&78
|
||||
skynet 102 beagle_beagle
|
||||
skynet 300 pecompact
|
||||
skynet 202 tftp
|
||||
skynet 203 FreeConsole
|
||||
skynet 200 402fcc
|
||||
skynet 301 4028de
|
||||
skynet 400 services.exe
|
||||
skynet 100 bbeagle.exe
|
||||
skynet 500 c:\windows\system32:lzx32.sys
|
||||
skynet 500
|
||||
skynet 501 kdD
|
||||
crypto 160 chronic failure
|
||||
crypto 220 open meadows
|
||||
crypto 230 quavering tendrils
|
||||
crypto 130 probable cause
|
||||
crypto 210 The Colour Out of Space
|
||||
crypto 150 flaming mastiff
|
||||
crypto 240 in the same vein
|
||||
crypto 140 giant chickens
|
||||
crypto 200 the squirrels crow at noon
|
||||
crypto 110 the s is for sucks
|
||||
crypto 120 Rat Fink
|
||||
crypto 180 The key for this puzzle is this sentence
|
||||
crypto 400 --------========Thanks for Pl@y|ng========--------
|
||||
crypto 100 caesar
|
||||
crypto 170 terrifying silence
|
||||
crypto 1 dirtbags
|
||||
forensics 50 C:\WINDOWS\system32\klog.sys
|
||||
forensics 200 winsecur.dll
|
||||
forensics 400 avatar.txt
|
||||
forensics 100 dll injection
|
||||
forensics 20 klog.txt
|
||||
forensics 10 lsass.exe
|
||||
forensics 250 Dirka Dirka
|
||||
compaq 50 4C
|
||||
compaq 150 This is our world now... the world of the electron and the switch, the beauty of the baud.
|
||||
compaq 350 Actually, Werner, we're all tickled to here you say that. Frankly, watchin' Donny beat Nazis to death is is the closest we ever get to goin' to the movies.
|
||||
compaq 600 Now think real hard. You been bird-doggin' this township awhile now. They wouldn't mind a corpse of you. Now, you can luxuriate in a nice jail cell, but if your hand touches metal, I swear by my pretty floral bonnet, I will end you.
|
||||
compaq 200 Gawain Ballard Tunisia
|
||||
compaq 400 lawful forths Amanda
|
||||
compaq 100 root:x:0:0:root:/root:/bin/bash
|
||||
compaq 500 codger launched jet
|
||||
hackme 806 That's all, folks.
|
||||
hackme 200 james
|
||||
hackme 614 james
|
||||
webapp 50 eVkIwHzOok
|
||||
webapp 40 765JBo4B54
|
||||
webapp 70 s4nNlaMScV
|
||||
webapp 30 BRrHdtdADI
|
||||
webapp 60 QJebByJaKX
|
||||
webapp 80 dmW5f9P54e
|
||||
webapp 20 uq4G4dXrpx
|
||||
webapp 10 ktFfb8R1Bw
|
|
@ -0,0 +1 @@
|
|||
antediluvian
|
Binary file not shown.
After Width: | Height: | Size: 87 B |
|
@ -0,0 +1,108 @@
|
|||
Safe to ex<b>e</b>cute.
|
||||
|
||||
<img width="20" height="20" src="data:image;base64,
|
||||
cjd7PTw9PDw8DQ0NDQ0NDQ8PDA0MDAwMzE5KQnYNDQ1lYmJiYmJiDTk5GRke
|
||||
HjY2Kg0UFBISEhImJiYNObm9tYEBBQ3tDQ0N7e3t7ejo6A0JCQkJCgoKCh4M
|
||||
DAwYmZ2VgQAEBRYWFhYFBQUFAQ0NDQwMDAwNDQ0NDQ0NDQ2NiYGBjYmBAQUF
|
||||
BYWBgQ0ICAgICBgYGBkNDQ2NiYmJCZ2ZBYURFR0dHBwcGAwMDAoKCgoKGhoN
|
||||
Dw8PD5ufn58LmZ2VAZWRmVFRUQ3FxcXFw8PDw8cNDQ0JCQkJISAgDSWkoKiA
|
||||
AQUNLQ0NDS0tLS0pKSkNCQkJCVi9ya2tDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQsN
|
||||
DQ0JCQkJJkojb0AsSGUJYA57AyNQPxEjIyMnJycNHR0dHRwcHBxbQxYWFhYW
|
||||
FhQUFA0PDw8PCgoKCgkNDQ0LCwsLDg4ODQ8PDw8MDAwMDA0NDQ0NDQ0MDAwN
|
||||
DQ0NDQ0NDQ0JDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDSMNDQ0NDQ0Np6amDR8fHx9d
|
||||
XV1dXQ0NDdDQ0NDCwsINPj4+PmLm4uruDQ0NHBwSEhMTEw0NDQ0NDQ0NDS0N
|
||||
DQ0YGBgYGBgYDQ0NDQ0tLS0tLVIYbjFjBmEIew9oGlk1VCdUMUJCUg1qB2gG
|
||||
WSpeP38LVAsLZw5sDyF+ET8JCXkMeAsLUhtUC3gMaAFvMHgLbgoKVQpmD21u
|
||||
MUI2VyVRDmMCZAoKTQFICkkWJCMTExMTERETExINDQ0NDQ0NDAwNDSkpKSk5
|
||||
OTk5OQ0NDR10HRAQEBINWVlZWVlZWVkFmJyUkpeXl/tuagUCAwMDc+bi6u0P
|
||||
Dw9a0za1WVG5bGxsbIQwMDAw2IaHh4dOjY1yRyOYnJRrTiazt7+/DQ0N8te7
|
||||
LioiSg0NDQ3kBPsE+wQoWM3JwamhoaGh5DTLNMv6F0nAIY5qmsqezKRw8/cF
|
||||
be1uamIzZQ1ljoqCaqVapVquPp3IQaT3Hx8fHx9W1xRndXV1J6wv8Q7xDotL
|
||||
Pz3CElUOxwSUBJTBSK2OYmrq11fCxs7OeXadgg95efo6Pq7SR0NLtGbHuy4J
|
||||
AYqaH824U5WQjRgcFBXcH5ZgNYRh4g4Gpzejp6+ISDwlnZ2dnZ0YzbmpKsbK
|
||||
ojKmogX6KqltffCGhk/OXs6bEvd0mJAT6RmadmoCYubi6uXHOMc48TJn7gta
|
||||
DF/cMDzU1NTUDVbXFMbX19c/4fMM83793SLdIq+evkG+QciNfVSEPMoL8/HI
|
||||
Dn1r4tpTpVpO/Hc6yuP0snOKiLF/9gx+42CkqPOt8jv4cfuuJ8KVw5B4eHgN
|
||||
DVbXFJWEhIQJjq5RrlHcZ0e4R/LbI+IaGJt3e/Z9gmls/AMXoO5t8wx5jmZI
|
||||
SEhIy8nFnsCfVpUFlcCEYTJg21vPy8NijRkdFf70eQ8PjObiHc1GRcY+wbT5
|
||||
ofoz8KUsyZpyDQ0NDVbXFA8eHg1dtQP9Av2k/zbOzs7Nzc3NzMzODUQwEHkK
|
||||
KktrB2IUcR1kRCBBOBhiF2MQeR14eHh4DQ0N8g3yDQ0NDQ3yDfINDQ0NDQ0N
|
||||
DQ0MDAwMKCgoDQEBAQF5+//3+g0NDTG1sbm9vb0NRcTAyM3Nzc0ZjIiAhoaG
|
||||
hvJzdwUPDw8PUVFRUVoNDQ0dHR0dCAgIDQ0NDQ0ODg4ObpiclJaWlpaGhoYN
|
||||
GRkZGQgICAgfDQ0NZefj6/r6+g1t7+vj8fHx8fkNDQ0eHh4eFhYWDfMM85zc
|
||||
XlpSrfINYmNjY2OTbJNiUNLW3t7e3t7eDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0N
|
||||
DQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ2ZDQkFBQUFBQUFBQWjj4uDNbez
|
||||
u7u7uw0NDQ0NgRURGRlKCUpwUHg/cSQNLR4wBCocPA4+DjsLPw87GzNhBGAt
|
||||
ZQRwUGNNeVdhIBgxMTF2NXZMbCViLHlQcENtWXc7GykZKR8vGysfLQVXMlZ2
|
||||
Pl8rCz4QJAo8ESkAAABKCUpwUHg/cSQNLR4wBCocPA4+DjsLPw87GzNhBGAt
|
||||
ZQRwUGNNeVdhIBkwMDB3NHdNbSViLHlQcENtWXc7GykZKR8vGysfLQVXMlZ2
|
||||
Pl8rCz4QJAo8ESgBAQFKCUpwUHg/cSQNLR4wBCocPA4+DjsLPw87GzNhBGAt
|
||||
ZQRwUGNNeVdhIBkwMDB3NHdNbSViLHlQcENtWXc7GykZKR8vGysfLQVXMlZ2
|
||||
Pl8rCz4QJAo8ESkAAAAjUClEMFEzMx1ueQt/Hnx8UiFJOnkLfx58fFI7VSFo
|
||||
GmpqRCpFMVR6TA5Hah5/GBg2Xmwfd3dZPUQqWSBgYE4qUz1OOkhII0QqX3EH
|
||||
YhBjCmIMDCJFK15wBmN/DGUKZDtJSWcVaAQqTjdZWXcFYGFPP1MnJwlgDmd5
|
||||
eVcjRj5KSmQCZApjY00/UDRVIWxsQidPEHYEZQhoaEYlUT5MPz8RaR1yAHNz
|
||||
XTdUJg0jRz5QMVw1VlYjRCtfX3EWeQ0jfRFlZUsvTjpbWyNBMkFBbwxjDmNo
|
||||
BnJycnJycnJyDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0N
|
||||
DRYWFg0MDAwMDg4ODhqMiICUlZWVhoaGDQ0NDQ0NDQ0NDA0NDQ0NDQ0uLi4N
|
||||
CgoKCggICAggjIiAqKmpqYmJiQ0NDQ0NDQ0NDQkNDQ0NDQ0NPDw8DQgICAgK
|
||||
CgoKQoyIgMjJycnl5eUNCQkJCQkJCQkNDQ0NCQkJCT4+Pg0GBgYGBAQEBHCM
|
||||
iID09fX1lZWVDQgICAgJCQkJDQ0NDR0dHR0iIiINDg4ODgwMDAzYjIiAVFVV
|
||||
VQsLCw0NDQ0NDQ0NDQwNDQ0NDQ0NSkpKDfIN8p2fn5+frY+Lg7Gzs7O/v78N
|
||||
CQkJCQkJCQkLDQ0NDw8PD1tbWw3zDPOcnp6ent6Pi4PDwcHB4eHhDQgICAgJ
|
||||
CQkJDQ0NDQ0NDQ1ubm4NBAQEBAYGBgZmj4uD4+Hh4enp6Q0JCQkJCQkJCQ0N
|
||||
DQ0FBQUFaWlpDQQEBAQGBgYGbo+Lg+vp6en5+fkNCQkJCQICAgIGDQ0NBQUF
|
||||
BXBwcA0MDAwMCgoKCnKPi4P7+fn57u7uDQ0NDQ0NDQ0NCQ0NDQ0NDQ19fX0N
|
||||
DAwMDAoKCgqaj4uDExERESEhIQ0NDQ0NDQ0NDQkNDQ0JCQkJcnJyDQwMDAwK
|
||||
CgoKyo+Lg0NBQUE9PDwNDQ0NDQ0NDQ0JDQ0NDQ0NDYyMjA0MDAwMCgoKCjaJ
|
||||
jYW5vb29p6enDQ0NDQ0NDQ0NCQ0NDQ0NDQ2KiooNDAwMDA4ODg5WiY2F3dnZ
|
||||
2fr6+g0NDQ0NDQ0NDQkNDQ0NDQ0NgoKCDQwMDAwODg4OcomNhfn9/f35+fkN
|
||||
DQ0NDQ0NDQ0JDQ0NDQ0NDZSUlA0MDAwMDw8PD4+ZnZUVERERGRkZDQ0NDQ0N
|
||||
DQ0NCQ0NDQ0NDQ2tra0NDAwMDA8PDw+HmZ2VHRkZGREREQ0NDQ0NDQ0NDQkN
|
||||
DQ0NDQ0NqqqqDQwMDAwPDw8Pn5mdlQUBAQEFBQUNDQ0NDQ0NDQ0JDQ0NDQ0N
|
||||
DaGhoQ0LCwsLCAgICJyZnZUBBQUFzc3NDQgICAgICAgIDA0NDQUFBQWwsLAN
|
||||
DAwMDA8PDw9TmJyUyM3NzcnJyQ0NDQ0NDQ0NDQkNDQ0JCQkJs7OzDQwMDAwP
|
||||
Dw8Pb5iclPTx8fHl5eUNDQ0NDQ0NDQ0JDQ0NCQkJCcrKyg0MDAwMDw8PD3uY
|
||||
nJTg5eXl6enpDQ0NDQ0NDQ0NCQ0NDQ0NDQ3ExMQNBQUFBQYGBgaGmJyUFBER
|
||||
ERUVFQ0NDQ0NDQ0NDQkNDQ0NDQ0Nw8PDDQwMDAwMDAwMDA0NDY2IiIiGh4cN
|
||||
DQ0NDQ0NDQ0MDQ0NDQ0NDRwcHA0ODg4ODg4ODg4NDQ2DhYWFUlJSDQ0NDQ0N
|
||||
DQ0NDA0NDQ0NDQ0MDAwNDw8PDw8PDw8PDQ0Nxc7Ozq6qqg0WFhYWOjo6Oj4N
|
||||
DQ0dHR0dFBQUDQ4ODg4ODg4ODg0NDSU1NTUBAwMNDQ0NDQ0NDQ0MDQ0NDQ0N
|
||||
DQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NGZicBQUFBQUGBgcHBw0NDSWkoKioqKgN
|
||||
Dg4MDAwMDAxEjIiAgICAgIODgA0NDQ0Nefj89PQNDQ0ODgoKCgoKDdlYXFRU
|
||||
VFRUVw0ICAgICAg6uLwFBQUFBQYGAAAADQ0NTc/Lw8PDww0ODgkJCQkJCWmP
|
||||
i4ODg4ODgICIDQ0NDQ1l5+Pr6w0NDQ4OBwcHBwcNdffz+/v7+/v4DQcHBwcH
|
||||
B5cVEQUFBQUFBgYNDQ0NDQ3NT0tDQ0NDDQ4OAgICAgICPomNhYWFhYWGhosN
|
||||
DQ0NDVXR1d3dDQ0NDg4AAAAAAA1x9fH5+fn5+foNAgICAgICghYSBQUFBQUG
|
||||
BhYWFg0NDYURFR0dHR0NDg4fHx8fHx+PmZ2VlZWVlZaWhA0NDQ0NmQ0JAQEN
|
||||
DQ0ODh0dHR0dDVHEwMjIyMjIyw0ZGRkZGRl57OgFBQUFBQYGExMTDQ0Neezo
|
||||
4ODg4A0ODhgYGBgYGJiYnJSUlJSUl5eADQ0NDQ0NDQ0NDQ0NDQ4OFhYWFhYN
|
||||
DQ0NDQ0NDQ0ODRQUFBQUFBQUFA0NDQ0NDg4UFBQNDQ0NDQ0NDQ0NDQ4OFRUU
|
||||
FBQU8I+Lg4ODg4OBgY0NHBwcHBwcHBwcDQ0NCQn4BxsbGw2NGR0VFRUVFRQN
|
||||
HR03Nzc3vysvBQUFBQUEBBUVLQ0NDZ0JDQUFBQUNDAweHltbW1snmJyUlJSU
|
||||
lJWVgw1ERERExFFVXVwNDQ0MDBsbTk5ODQWGgoqKioqKiA0BAWpqampW1dEF
|
||||
BQUFBQcHCwsaDQ0NDQ0NDQ0NDQ0JCfgHcHBwcPSZnZWVlZWVlJSEDYmJiYkF
|
||||
kZWdnQ0NDQwMHR2MjIwNcfXx+fn5+fn4DQICnZ2dnQ2ZnQUFBQUFBAQWFr0N
|
||||
DQ0VkZWdnZ2dDQ8PAwPCwsLCwg0NDQ0NDQ0JCfjyOjo6Oq46PjY2DQ0NHBwP
|
||||
D97e3g1V0dXd2dnZ2cgNAwPb29vbW8/LBQUFBQUVF+YZ8A0NDXXg5Ozs7OwN
|
||||
HB4ICP7+/v4qjoqCwMDAwNLS3g0LCgoKCgoKCqAMDAweHh4eCAkJDXX38/v7
|
||||
+/v76Q0HBxsaGhraWFwFBQUFBRcXGxs4DAwMjBgcFBQUFA0dH+4RJyYmJqaO
|
||||
ioLQ0NDQwsLODUtKSkrKX1tTUw0NDR0d7BNBQEANZebi6vLy8vLgDQEBVldX
|
||||
V1dXVw3Q0NDQwsLCwrYMDAyMGBwUFBQUDR0f7hGUlZWV4ZiclJSUlJS0tKIN
|
||||
nZycnKAkICgoDQ0NHx8SEoSFhQ2NGR0VFRUVFQUP/gGrqqqqKr+7BQUFBQUV
|
||||
FeQbqgwMDGz5/fX19fUNHBwJCc7Pz89LmJyUlJSUlISEdfI+Pz8/vysvJycN
|
||||
DQ0dH+4Rzs/PDVHV0dnd3d3dzA0DA+3s7OyYDQkFBQUFBRUVAwP4DAwMDAwM
|
||||
DAwMDA0tLS0tIiAgIKCZnZWVlZWVhYd28tfV1dXV1dXV1Q0NDS0tLS0tTi9h
|
||||
DVI1WDdZBnUBbB5qagl7D3wIfWsNI0BAH0ADVxhfAEwFVgJdAgJdUhZCDV8A
|
||||
TAVWAlINDVINRwRWCUVEF0McQ0MzHS0tbgFsHHAVYQRgTjw8YzxYN2gPYwxv
|
||||
DmI9WS1CMEMcbBlhYQd1FHkcQ2kccRxlZTplJnJCEE8KRABfAABfUhZCDV8A
|
||||
RQtPEFJSDVIURgdKD1BIBkIdQkIdQghLXwBFC08QT08QT2kGWT5SPV8+Ug1u
|
||||
GnUHdCtKP0dHeRxvGzVWVglNFEMCTwZFRRp8DFNlEhJNEnQdcxpFbB5sDXQr
|
||||
TiBERFINaRp1KkIjTSlhBARbBGgBYwBfbh1oN1E4Vj8/T3gMfz9/OHQ9fzxS
|
||||
YE5+fiFIJk87DVIhVTRGMjJtMmsCbAVaO0k7WiNSIVU0RjIybTJeZAZlOlkq
|
||||
XwBpB2QQEE8QcgFyLV55GGoeHnMSexUVUg1hCGoJViVRMH8LVDlYMV8fXxhB
|
||||
CEoJVmRKenolUjtVPEgXdgR2F3QrTiBERCBBNVRSIVU0RjIybQtiYwoKVQp6
|
||||
CG0EamQQTy5cLk82aQxjBwdYPVk4TC0tUhVZFlQVWQZJD0sYXQlWAkMBTQhS
|
||||
Ug1oBmJiPWILYwp+IUAyQCFYB34KaxltbTJ7NGt+Cm4HaTZDMFUxDVINaQh8
|
||||
HUIxRWweamo1fwlWBGFqA3AEYRNQPF0ufhtoaDdoGGoPZmMKfiFAMkAhWAd+
|
||||
CmsZbW0ybQpnYgxTIFQ1RzNsMw0=" alt="Santa's helpers binary" />
|
|
@ -0,0 +1 @@
|
|||
It is a lovely day outside
|
Binary file not shown.
|
@ -0,0 +1,2 @@
|
|||
Recovery, while not strictly necessary, may be tremendously helpful.
|
||||
|
|
@ -0,0 +1 @@
|
|||
jackalope wheeze
|
|
@ -0,0 +1 @@
|
|||
tkftsuiuqvaheohrnsnuoleyriod"eic"
|
|
@ -0,0 +1 @@
|
|||
unequivocal
|
|
@ -0,0 +1 @@
|
|||
27586126814341379597440261571645814840581961154587430529221052323
|
|
@ -0,0 +1 @@
|
|||
DB1663<3
|
|
@ -0,0 +1,22 @@
|
|||
Kolejne modele Panzerfausta, odpowiednio: 60, 100, 150, różnił
|
||||
kaliber głowicy i wielkość ładunku miotającego. Konstrukcja i
|
||||
mechanizm nie ulegał istotnym zmianom, z racji wzrastania zasięgu
|
||||
broni modyfikacjom ulegały nastawy celowników. Jedynie we wzorze 150
|
||||
wprowadzono (a był to już początek 1945 roku) wielokrotne użycie
|
||||
wyrzutni rurowej. Osiągnięto to przez umieszczenie ładunku
|
||||
miotającego w głowicy oraz przez wzmocnienie rury. W wyniku problemu z
|
||||
transportem model ów nie wszedł do walki. Model 250 (o teoretycznym
|
||||
zasięgu 250 m) z racji zakończenia wojny nie opuścił desek
|
||||
kreślarskich nigdy nie wchodząc nawet w fazę prototypową.
|
||||
|
||||
(61, 4)
|
||||
(47, 8)
|
||||
(19, 4)
|
||||
(37, 1)
|
||||
(51, 3)
|
||||
(67, 5)
|
||||
(9, 2)
|
||||
(26, 1)
|
||||
(2, 2)
|
||||
(26, 3)
|
||||
(50, 2)
|
|
@ -0,0 +1 @@
|
|||
jako561962
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
PC LOAD LETTER
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
extra special text
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
31 9 15 26 14 23 14 6 18 5 12 18 5 2 16 27 7 10 11 5 13 31 17 17 6 2 26 26 10 21 10 8 20 4
|
|
@ -0,0 +1 @@
|
|||
journals.uchicago
|
|
@ -0,0 +1 @@
|
|||
xez.3nt
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
PEANUT BUTTER JELLY TIME
|
|
@ -0,0 +1,53 @@
|
|||
You are doing a forensics evaluation of a linux box that you know has
|
||||
been compromised. You find a binary on the system and assume it was
|
||||
used by the attackers to hide data on box that that was exfiltrated.
|
||||
You dissamble the file and find the x86 assembly shown below (from Ida)
|
||||
- this function was used for obfuscation. You also find a file
|
||||
obfuscated by this tool. Using the key you find in this encoder code
|
||||
what is the unobfuscated first line of the file which starts with
|
||||
|
||||
8%%>p2pzpzp8%%>pe8%%>pe(#$e(+9"
|
||||
|
||||
HINT: The function was orginally defined as void convert_buf(unsigned
|
||||
char * buf, int len).
|
||||
|
||||
.text:08048474 ; =============== S U B R O U T I N E =======================================
|
||||
.text:08048474
|
||||
.text:08048474 ; Attributes: bp-based frame
|
||||
.text:08048474
|
||||
.text:08048474 public convert_buf
|
||||
.text:08048474 convert_buf proc near ; CODE XREF: main+B2p
|
||||
.text:08048474
|
||||
.text:08048474 cnt = dword ptr -4
|
||||
.text:08048474 buf = dword ptr 8
|
||||
.text:08048474 len = dword ptr 0Ch
|
||||
.text:08048474
|
||||
.text:08048474 push ebp
|
||||
.text:08048475 mov ebp, esp
|
||||
.text:08048477 sub esp, 10h
|
||||
.text:0804847A mov [ebp+cnt], 0
|
||||
.text:08048481 mov [ebp+cnt], 0
|
||||
.text:08048488 jmp short loc_80484A4
|
||||
.text:0804848A ; ---------------------------------------------------------------------------
|
||||
.text:0804848A
|
||||
.text:0804848A loc_804848A: ; CODE XREF: convert_buf+36j
|
||||
.text:0804848A mov eax, [ebp+cnt]
|
||||
.text:0804848D mov edx, eax
|
||||
.text:0804848F add edx, [ebp+buf]
|
||||
.text:08048492 mov eax, [ebp+cnt]
|
||||
.text:08048495 add eax, [ebp+buf]
|
||||
.text:08048498 movzx eax, byte ptr [eax]
|
||||
.text:0804849B xor eax, 4Ah
|
||||
.text:0804849E mov [edx], al
|
||||
.text:080484A0 add [ebp+cnt], 1
|
||||
.text:080484A4
|
||||
.text:080484A4 loc_80484A4: ; CODE XREF: convert_buf+14j
|
||||
.text:080484A4 mov eax, [ebp+cnt]
|
||||
.text:080484A7 cmp eax, [ebp+len]
|
||||
.text:080484AA jl short loc_804848A
|
||||
.text:080484AC leave
|
||||
.text:080484AD retn
|
||||
.text:080484AD convert_buf endp
|
||||
.text:080484AD
|
||||
.text:080484AE
|
||||
|
|
@ -0,0 +1 @@
|
|||
root:x:0:0:root:/root:/bin/bash
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
This is our world now... the world of the electron and the switch, the beauty of the baud.
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
Gawain Ballard Tunisia
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
Actually, Werner, we're all tickled to here you say that. Frankly, watchin' Donny beat Nazis to death is is the closest we ever get to goin' to the movies.
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
lawful forths Amanda
|
|
@ -0,0 +1,52 @@
|
|||
You are doing a forensics evaluation of a linux box that you know has
|
||||
been compromised. You find a binary on the system and assume it was
|
||||
used by the attackers to hide data on box that they were going to
|
||||
exfiltrate. You dissamble the file and find the following lines of x86
|
||||
assembly - this function was used to encode a buffer in place to
|
||||
obfuscate a file. What is the 1 byte key used to obfuscate the data (in
|
||||
hex)?
|
||||
|
||||
HINT: The function was orginally defined as void convert_buf(unsigned
|
||||
char * buf, int len). You can solve this puzzle by writing some code,
|
||||
or by using some of the advanced functions of some of the hex editors
|
||||
out there.
|
||||
|
||||
.text:08048474 ; =============== S U B R O U T I N E =======================================
|
||||
.text:08048474
|
||||
.text:08048474 ; Attributes: bp-based frame
|
||||
.text:08048474
|
||||
.text:08048474 public convert_buf
|
||||
.text:08048474 convert_buf proc near ; CODE XREF: main+B2p
|
||||
.text:08048474
|
||||
.text:08048474 cnt = dword ptr -4
|
||||
.text:08048474 buf = dword ptr 8
|
||||
.text:08048474 len = dword ptr 0Ch
|
||||
.text:08048474
|
||||
.text:08048474 push ebp
|
||||
.text:08048475 mov ebp, esp
|
||||
.text:08048477 sub esp, 10h
|
||||
.text:0804847A mov [ebp+cnt], 0
|
||||
.text:08048481 mov [ebp+cnt], 0
|
||||
.text:08048488 jmp short loc_80484A4
|
||||
.text:0804848A ; ---------------------------------------------------------------------------
|
||||
.text:0804848A
|
||||
.text:0804848A loc_804848A: ; CODE XREF: convert_buf+36j
|
||||
.text:0804848A mov eax, [ebp+cnt]
|
||||
.text:0804848D mov edx, eax
|
||||
.text:0804848F add edx, [ebp+buf]
|
||||
.text:08048492 mov eax, [ebp+cnt]
|
||||
.text:08048495 add eax, [ebp+buf]
|
||||
.text:08048498 movzx eax, byte ptr [eax]
|
||||
.text:0804849B xor eax, 4Ch
|
||||
.text:0804849E mov [edx], al
|
||||
.text:080484A0 add [ebp+cnt], 1
|
||||
.text:080484A4
|
||||
.text:080484A4 loc_80484A4: ; CODE XREF: convert_buf+14j
|
||||
.text:080484A4 mov eax, [ebp+cnt]
|
||||
.text:080484A7 cmp eax, [ebp+len]
|
||||
.text:080484AA jl short loc_804848A
|
||||
.text:080484AC leave
|
||||
.text:080484AD retn
|
||||
.text:080484AD convert_buf endp
|
||||
.text:080484AD
|
||||
.text:080484AE
|
|
@ -0,0 +1 @@
|
|||
4C
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
codger launched jet
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
Now think real hard. You been bird-doggin' this township awhile now. They wouldn't mind a corpse of you. Now, you can luxuriate in a nice jail cell, but if your hand touches metal, I swear by my pretty floral bonnet, I will end you.
|
|
@ -0,0 +1,16 @@
|
|||
<dl>
|
||||
<dt>Alice
|
||||
<dd>Welcome to Crypto. It works like this: I'll say something to Bob,
|
||||
and he'll say something back. Our communication will be encrypted in some
|
||||
manner, or at least obfuscated. Your job is to get the plaintext, and
|
||||
find the puzzle key.
|
||||
<dt>Bob
|
||||
<dd>Sometimes the plaintext from one puzzle will give you a hint (or the
|
||||
cryptogaphic key) for the next. When we give you such keys, we'll always
|
||||
do so in a straightforward manner. The puzzle key for each puzzle
|
||||
is always in what I say, and there shouldn't be any tricks involved in
|
||||
figuring out what it is.
|
||||
<dt>Alice<dd>Good Luck!
|
||||
<dt>Bob<dd>You'll need it. By the way, the key is 'dirtbags'.
|
||||
</dl>
|
||||
|
|
@ -0,0 +1 @@
|
|||
dirtbags
|
|
@ -0,0 +1,2 @@
|
|||
<dl><dt>Alice<dd> nyy unvy pnrfne.
|
||||
<dt>Bob<dd> pnrfne vf gur xrl </dl>
|
|
@ -0,0 +1 @@
|
|||
caesar
|
|
@ -0,0 +1,24 @@
|
|||
plaintext = [b'all hail caesar.', b'caesar is the key']
|
||||
|
||||
alpha = b'abcdefghijklmnopqrstuvwxyz'
|
||||
|
||||
def ceasar(text, r):
|
||||
out = bytearray()
|
||||
for t in text:
|
||||
if t in alpha:
|
||||
t = t - b'a'[0]
|
||||
t = (t + r)%26
|
||||
out.append(t + b'a'[0])
|
||||
else:
|
||||
out.append(t)
|
||||
return bytes(out)
|
||||
|
||||
encode = lambda text : ceasar(text, 13)
|
||||
decode = lambda text : ceasar(text, -13)
|
||||
|
||||
c = encode(plaintext[0])
|
||||
print('<dl><dt>Alice<dd>', str(c, 'utf-8'))
|
||||
assert decode(c) == plaintext[0]
|
||||
c = encode(plaintext[1])
|
||||
print('<dt>Bob<dd>', str(c, 'utf-8'), '</dl>')
|
||||
assert decode(c) == plaintext[1]
|
|
@ -0,0 +1,2 @@
|
|||
<dl><dt>Alice<dd> Vkbd ntg duun puwtvbauwg dbnjwu, hlv bv'd vku dtnu htdbe jpbfebjwud td lduq bf d-hxyud, t vuekfbmlu lduq bf ntfg nxqupf epgjvxcptjkbe twcxpbvnd. Xi exlpdu, bfdvutq xi wuvvup dlhdvbvlvbxf, gxl'pu qxbfc hgvu dlhdvbvlvbxf.
|
||||
<dt>Bob<dd> Vku fuyv vzx jlsswud tpu t hbv qbiiupufv; Ipumlufeg exlfvd (xi ektptevupd) zbww rldv puautw ptfqxn fxbdu. Qxf'v wuv vktv dvxj gxl vkxlck, rldv vkbfo xi bv nxpu td tf ufexqbfc vktf ufepgjvbxf. Xk, hg vku ztg, vku oug vkbd vbnu bd: 'vku d bd ixp dleod'. </dl>
|
|
@ -0,0 +1 @@
|
|||
the s is for sucks
|
|
@ -0,0 +1,49 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
plaintext = [b"This may seem relatively simple, but it's the same basic "
|
||||
b"principles as used in s-boxes, a technique used in many modern "
|
||||
b"cryptographic algoritms. Of course, instead of letter substitution, "
|
||||
b"you're doing byte substitution.",
|
||||
b"The next two puzzles are a bit different; Frequency counts (of characters) "
|
||||
b"will just reveal random noise. Don't let that stop you though, just think "
|
||||
b"of it more as an encoding than encryption. "
|
||||
b"Oh, by the way, the key this time is: 'the s is for sucks'."]
|
||||
|
||||
key = b"thequickbrownfxjmpdvlazygs"
|
||||
|
||||
def encode(text):
|
||||
ukey = key.upper()
|
||||
lkey = key.lower()
|
||||
assert len(set(key)) == 26, 'invalid key'
|
||||
assert key.isalpha(), 'non alpha character in key'
|
||||
out = bytearray()
|
||||
for t in text:
|
||||
if t in lkey:
|
||||
out.append(lkey[t - ord('a')])
|
||||
elif t in ukey:
|
||||
out.append(ukey[t - ord('A')])
|
||||
else:
|
||||
out.append(t)
|
||||
return bytes(out)
|
||||
|
||||
def decode(text):
|
||||
ukey = key.upper()
|
||||
lkey = key.lower()
|
||||
assert len(set(key)) == 26, 'invalid key'
|
||||
assert key.isalpha(), 'non alpha character in key'
|
||||
out = bytearray()
|
||||
for t in text:
|
||||
if t in lkey:
|
||||
out.append(ord('a') + lkey.index(bytes([t])))
|
||||
elif t in ukey:
|
||||
out.append(ord('A') + ukey.index(bytes([t])))
|
||||
else:
|
||||
out.append(t)
|
||||
return bytes(out)
|
||||
|
||||
c = encode(plaintext[0])
|
||||
print('<dl><dt>Alice<dd>', str(c, 'utf-8'))
|
||||
assert decode(c) == plaintext[0]
|
||||
c = encode(plaintext[1])
|
||||
print('<dt>Bob<dd>', str(c, 'utf-8'), '</dl>')
|
||||
assert decode(c) == plaintext[1]
|
|
@ -0,0 +1,3 @@
|
|||
<p>The 5 byte groupings are just a standard way of displaying cypher text. It has no bearing on the solution to the puzzle. This format will be used to display the cyphertext from now on.</p>
|
||||
<dl><dt>Alice<dd> YkJzY rEFAd iVsPW RXwxG PnRoX<BR>QcFZC YWDzg MzkzH SegHM rUqSu<BR>vmfRP KYcma GlBBT EtFRP MJYut<BR>vSPHE UblvT uNaRh hexTd JHnjg<BR>yHtIR gaPme DHMne CUsEk EVoSB<BR>JVZMk lcVJq cWgbd XwCAX ceGZQ<BR>JgDxh gYIaX MheoP cWLWe kmLBe<BR>tAPgG JWOEC mzTcY ZbJwu IgmnN<BR>ACgLP AYPNw wkBuW KSewJ oDNMM<BR>efDBk SoPCk CSCTX FsteA QjrPv<BR>nWTOD zHdhS auWhT koFCv UnFHe<BR>SGGWM OhrmU yjmLv zDFPP eQjox<BR>uXQdN AtKyu DNLNj EVZCX PtpDZ<BR>PlSOp soDgH rHrwm UeOIQ QdZJP<BR>RNMph sCgSA JbxYr OWBYe uSErY<BR>rCAmN CSJMY qmiIV wTSvv JDqQy<BR>UoroE HrwCo cfQwA kOsxU hoUIL<BR>aomUm ESRRn UMJVJ CwaVu NEoGi<BR>tpUZo pCilR puxUI CmSEe mtEqr<BR>vMvWP MFdSK EWEQr twWNU QBrwA<BR>hxAMP oqQFP ThZmm BcIBi QmdAj<BR>uaEPU eRICD KJurQ MYhZU vnXyG<BR>AxBsy BzwsO WnyXO SCbDn usYBx<BR>QDbra nYWxY PkYqw iPEQh KQMRE<BR>VEmKB YXCEf XlUoY SGznM vCQVK<BR>waRrX WeLbz VfumH DtiHl UqdYD<BR>mMBUG PNyIV ohezI tEuWG ukVpV<BR>NvVGz RSrBD RIUPb vHvvT RShwS<BR>ETNsI srrwB KuUjl IKuGm BzhlH<BR>XmYJF hCZFT EQDtq MZEiJ XvAjb<BR>pA
|
||||
<dt>Bob<dd> SkPic KVbSt sDOuK iNgow JQUZU<BR>NtXwh ZWTMy PDORL MnflO phjJj<BR>wOYeT oSLxX OdmaD ifyYT bBJnq<BR>ANdPs RFkRK ALTKw rzZRp xZwrh<BR>IeFSH vyObC XqQea RfoJG MifYf<BR>dZZXC eCPCL UMnjE DnTLD lmSEl<BR>McIhw TCQVt XqhNd xGIGo mMsEQ<BR>mGdwT isjMX fxGQt aqKBz MVJTF<BR>PkbbQ hmhIk yZxPK bThti JdZMK<BR>kwYaA VIPJx WBHAK PynLx SrGIe<BR>dgAjS nFuge CAerC jwmOv YVALv<BR>UAHND PafSD dXoTt eNwkg DNvbD<BR>JDjiA cjTbn aaUvk DRcSW JvwKb<BR>AEfGe bSscp HDnsV GztnS zwpIM<BR>szCKv GkwOJ CLzYS LSBYr lBles<BR>LLupN twhcC khkWh MQISc HYNLK<BR>DdiOQ pUuUg vFxqy OJmaF KYzkM<BR>ifvBL lLOww bgWKs ZrbzJ HFGMb<BR>rxSdZ QEpjO yAXjP ytVcr kxFcq<BR>VyfkY VYfFf paTEy NlNGx SHiiB<BR>GXBaF qzsZv FEHPx VQMBS HiXGV<BR>skOSj hIfIX nZduH khyGT xyKdY<BR>ny </dl>
|
|
@ -0,0 +1 @@
|
|||
Rat Fink
|
|
@ -0,0 +1,45 @@
|
|||
#!/usr/bin/python3
|
||||
"""This is non-obvious, so let me elaborate. The message is translated to
|
||||
binary with one character per binary bit. Lower case characters are 1's,
|
||||
and upper case is 0. The letters are chosen at random. Tricky, eh?"""
|
||||
|
||||
import random
|
||||
import crypto
|
||||
|
||||
lower = b'abcdefghijklmnopqrstuvwxyz'
|
||||
upper = lower.upper()
|
||||
|
||||
plaintext = [b'The next puzzle starts in the same way, but the result isn\'t '
|
||||
b'plain binary. Think OSI layer 1.',
|
||||
b'Yep, we\'re still dealing with just encodings, not encryption.'
|
||||
b'Rat Fink']
|
||||
|
||||
def encode(text):
|
||||
out = bytearray()
|
||||
mask = 0x80
|
||||
for t in text:
|
||||
for i in range(8):
|
||||
if t & mask:
|
||||
out.append(random.choice(lower))
|
||||
else:
|
||||
out.append(random.choice(upper))
|
||||
t = t << 1
|
||||
|
||||
return bytes(out)
|
||||
|
||||
def decode(text):
|
||||
out = bytearray()
|
||||
i = 0
|
||||
while i < len(text):
|
||||
c = 0
|
||||
mask = 0x80
|
||||
for j in range(8):
|
||||
if text[i] in lower:
|
||||
c = c + mask
|
||||
mask = mask >> 1
|
||||
i = i + 1
|
||||
out.append(c)
|
||||
return bytes(out)
|
||||
|
||||
print('<p>The 5 byte groupings are just a standard way of displaying cypher text. It has no bearing on the solution to the puzzle. This format will be used to display the cyphertext from now on.</p>')
|
||||
crypto.mkIndex(encode, decode, plaintext[0], plaintext[1], crypto.groups)
|
|
@ -0,0 +1,2 @@
|
|||
<dl><dt>Alice<dd> OsaZO qrVCc bBEwT uVaeD cUIgs<BR>XBoLu SiOcy GqSMq LuoKN xjYUn<BR>MepZO lOeYl PqOiU ycJqJ RxnOo<BR>DsXEj CehRl WXsXw mKCka ILpaZ<BR>qWkCf MEyTt CyJtf AsYaD TvyYB<BR>xKgGg ZjrNS qQdRm McJzG uiEuE<BR>DxsLt VvXkY IpcVy FGnfM iAdOL<BR>kTybM kCJaV rlDRw tQBpZ aoTNj<BR>RrSbB nXzSc fBcIE qcFOw AjeBO<BR>ncVnJ nLXyV nuToR YuWdt FFlQq<BR>SxWeW wKpqA bQBok KNkQg qIIdf<BR>JfQWc sQrFd JDzUo ErzCA wGoJt<BR>SzEyT zeIUw TkzQa DNzdN FsrIo<BR>FGllP kOrPn SFjwI uYcNJ yUldB<BR>SmUez OaYzO EpIkc ZGuGl dRvRC<BR>dcEYq FtoZD bjWpS yMXvR jvSvC<BR>PgAnx KOqUe GdUdM xNotY YuYxN<BR>nYmaD nZBgu MzCUo lMcVk ZjCWt<BR>jPaCF kRwgZ SnDuX rbZzK NcIvr<BR>FIwrR AtYaf MLsiX zWiVM rQlJh<BR>oGIxC sEjJc KfSsN dfBFr LfOiM<BR>eBkKe pUBsd CMzoY DltIW knBtT<BR>UbnJl DrPYs QlyJx VJvfO cBNlL<BR>zHqnP wZUob QQcWm tUVff PcUPb<BR>gUGwp PmYRe dCcDO oWfmF GhmXU<BR>iEjhR OoRcT rKhNu CovZo IeFVm<BR>kDIdO jDsjZ wVIye DFxOi UfVfw<BR>GgHCi MorGD hgDNp VrvPE lFwJy<BR>KdBrE qtMuX rPIqK zEuQh VivSb<BR>YrYKg NzgVN eKskT yJEuQ vbPZr<BR>cXLzm HlKsI YrhDl USeRf sEgDS<BR>kbBHr QdxXY tnIjT UffSu WgOlL<BR>KzuHh ZvPIw mEKte EUesW bIxUL<BR>qHxwX iNEyM llYGz GhGoL jLjGk<BR>hXpCr NNwoC XcYzS btNeL iUXyd<BR>GmYoS XrsSu FEvkD dGzDq RUkMq<BR>xPUpm JpJKi JxLfI lfEKz BpSvD<BR>yBeCo jBoPf LMjhP ZaIrZ dyQxJ<BR>YgcPP gYnMs DoqVm UBcEl dDXha<BR>GEarV zQvAd JJvXq vUMnH xbOUh<BR>UeJcF iHcCi vJpWa MGipL rRoTI<BR>mwUpJ UfdCA jEafV FlkJu VIypK<BR>cUDhI gXkbC yDAzf JuMRm CzRfR<BR>llBCk VuSiU iGvPj iXwKQ qSiMi<BR>ExjMP laRzU XlPmF dnNwW LzvGf<BR>JoTPe kIAiP sOalO pYeXY srYEx<BR>qNFlb FbUVn IjVzV qzTLl lUxIV<BR>iiBeP QfRfX juMfH UtrMn OWaXe<BR>NvkNi BlUyJ MeBjj OVeJt bBXaP<BR>gSeUl EiPsn HoSBe iCeUh HCfBg<BR>yEqTH gGmqX EcmPJ bdVxC ZsKtw<BR>PLmsZ YqnSm GYhSs kEAcX yUdLu<BR>lHNjY jJlCl BlAby KwVtD GieJW<BR>bJrWl vJeFK fyHaU oLnPD pBsiC<BR>JqSqH zXhDl CunRi GTxCi mTUaZ<BR>vGxkK bIFaK oaNXo iYNxu ZtLfH<BR>BfrDF oPjQv aRoHO dJkgD AzgGQ<BR>okHdT aNJdE jnGWw RyrBo CObdY<BR>vOKvg EDjbB bDNns DBsPk dRQzn<BR>LpCOp mQpEv EDoSx nPwHG lSxnS<BR>WgwBH cZufD EoIgG xOzCv LgmYa<BR>TvXJh OtaMg TYpzQ vJVei ZjVpU<BR>aLDrs UxZCi bPyFJ qfAIz hFdCG<BR>qArpV CyrVK uDmwA ToLfE pHvYi<BR>YmsMo WjXOh XzpXe QOhwW xEOcu<BR>KjDnR fLOhx KgWaC EqPob HPgHv<BR>oCbQc PWhyK MsWkZ iJwpO JdNpS<BR>vScNw YnzUj FEmjR sEeYq PBnsL<BR>sSFvK kkQxY UvVmV dgKNb CxEpV<BR>fXpZz kFiRM kqZMp tQcWE ijCxD<BR>WaNgv YDhtI QtvAx YjHxJ HnPtp<BR>CUrGn vTAgo IvQrI Is
|
||||
<dt>Bob<dd> DcfTV cQrxZ bTBnq HCjwG kNIie<BR>CoAyJ gRUqp PrCoZ IvImh WXiUy<BR>jRkJf FZlSa vNTxY yoCxI ShiID<BR>dXntL XyjPz JkOSm ZomOu UXqWz<BR>aYUvJ yVhEd DqPmg YbBPc SdUrt<BR>OaPTt bAoKZ eoChN iQzRL imGmV<BR>JjMhz EJeUp WpvNe LCgQv qKQco<BR>FMtLs zDGfE lXuKi QqWaw UtMjM<BR>GrjGg JrVVy fYpLC dyWNa SkuGW<BR>qoPtP jISrp SVnVg PrsVx FCdtW<BR>GpOzI gVeCg cEAyL wXtGi QjBce<BR>PyQCs YoVhC hjJPf VklPI lPoUm<BR>KfJdN veXvG ReoWE wiJsM TblSc<BR>QLeKr bXAvv GVwvS rGlUm UAnQa<BR>dBDkO lcBzT qDvGj MfZRw JsfML<BR>eHcTi OpEtP uKbvS CbAhW pMmBv<BR>YvzJX ceEFy yTBcB oIzjG gRTmc<BR>FDcAa YoHlg AoHHo CtDuC wmQTm<BR>mGmKb CCxiY QeIkO yTknZ XcHmZ<BR>jGqOo VkpAs WwFGs RlbYw QXluN<BR>sUUuq KoTdF nFEsc WtPfW UesOP<BR>jbYNc rBjMZ ajHjV zSPyF gyIcV<BR>CzRju MPhGc XipXc HcXOh MfrEz<BR>IFbTp yVXsQ wKyVw LbQzo ZkVDq<BR>SqBbq JWlUu kHmGL leKXy VpqEK<BR>mrLoU rLgTY pqBDj EhuCe OLiPo<BR>DvWrl PMjvM uBbTQ rOulG AfWyt<BR>FjQcL GuSkj EHkXl iGoXI sKobC<BR>TbdDG uViyC JvbRj XyFTy VlJyw<BR>KGoPq MdOjV fVxnS xNpGR vVyIc<BR>VnLpb RaFrU PeIgv YRcCb nAmYG<BR>soXgJ lCzFK rsZpJ KuNpZ npPOo<BR>CmmYy DXbMp VtQzb NFyiK uCLfU<BR>lUxpK MnHbq GsDPq gYpUM fGtHm<BR>mGxFQ bBfiA NlhXI wDyrH XfZnM<BR>xUzCe SryJj UExJm NssDa PObqH<BR>dOEmV vYzHg aNMbw IvVgK PbjQX<BR>kcWKv yGtPs VFkSb fYhAY ssYuY<BR>AtBgs FKdbO </dl>
|
|
@ -0,0 +1 @@
|
|||
probable cause
|
|
@ -0,0 +1,56 @@
|
|||
#!/usr/bin/python3
|
||||
'''This is the same as the previous, but it uses non-return to zero to encode
|
||||
the binary.'''
|
||||
|
||||
import random
|
||||
import crypto
|
||||
|
||||
lower = b'abcdefghijklmnopqrstuvwxyz'
|
||||
upper = lower.upper()
|
||||
|
||||
alice = b'The next one is in Morris Code. Unlike the previous two, '\
|
||||
b'they will actually need to determine some sort of key.'
|
||||
bob = b'Morris code with a key? That sounds bizarre. probable cause'
|
||||
|
||||
def encode(text):
|
||||
out = bytearray()
|
||||
mask = 0x80
|
||||
state = 0
|
||||
for t in text:
|
||||
for i in range(8):
|
||||
next = t & mask
|
||||
if not state and not next:
|
||||
out.append(random.choice(upper))
|
||||
out.append(random.choice(lower))
|
||||
elif not state and next:
|
||||
out.append(random.choice(lower))
|
||||
out.append(random.choice(upper))
|
||||
elif state and not next:
|
||||
out.append(random.choice(upper))
|
||||
out.append(random.choice(lower))
|
||||
elif state and next:
|
||||
out.append(random.choice(lower))
|
||||
out.append(random.choice(upper))
|
||||
state = next
|
||||
t = t << 1
|
||||
|
||||
return bytes(out)
|
||||
|
||||
def decode(text):
|
||||
out = bytearray()
|
||||
i = 0
|
||||
while i < len(text):
|
||||
c = 0
|
||||
mask = 0x80
|
||||
for j in range(8):
|
||||
a = 0 if text[i] in lower else 1
|
||||
b = 0 if text[i+1] in lower else 1
|
||||
assert a != b, 'bad encoding'
|
||||
if b:
|
||||
c = c + mask
|
||||
mask = mask >> 1
|
||||
i = i + 2
|
||||
out.append(c)
|
||||
return bytes(out)
|
||||
|
||||
crypto.mkIndex(encode, decode, alice, bob, crypto.groups)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue