2009-10-06 11:50:21 -06:00
|
|
|
"""<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,
|
2009-10-09 08:47:19 -06:00
|
|
|
the very best in laser tank AI languages. It includes amazing features such
|
2009-10-06 11:50:21 -06:00
|
|
|
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">
|
2010-01-19 15:31:15 -07:00
|
|
|
sense(1) & sense(2) & fireready() : fire();
|
|
|
|
sense(0,0)&sin(5): move(40, 30) . turretcw(50);
|
|
|
|
sense(4) & random(4,5) : led(1).settoggle(0,1);</pre>
|
2009-10-06 11:50:21 -06:00
|
|
|
|
2010-01-19 15:31:15 -07:00
|
|
|
Your tank will execute its program each turn(frame), and attempt to
|
|
|
|
the best of its
|
2009-10-06 11:50:21 -06:00
|
|
|
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.
|
2010-01-19 15:31:15 -07:00
|
|
|
|
|
|
|
<pre class="docs">
|
|
|
|
#Setup commands define your tank when your program compiles
|
|
|
|
>addsensor(50, 0, 5, 1); # 0-Fire Sensor
|
|
|
|
>addsensor(30, 0, 180); # 1-Anti-collision sensor
|
|
|
|
|
|
|
|
# These commands execute each frame.
|
|
|
|
# Blank condition sections are true.
|
|
|
|
: move(90, 100).
|
|
|
|
turretset(0);
|
|
|
|
sense(0) : fire();
|
|
|
|
sense(1) : move(-100, 100)
|
|
|
|
</pre>
|
2009-10-06 11:50:21 -06:00
|
|
|
"""
|
|
|
|
|
|
|
|
import conditions
|
|
|
|
import actions
|
|
|
|
import setup
|
|
|
|
|
2010-01-19 15:31:15 -07:00
|
|
|
import traceback
|
|
|
|
|
2009-10-06 11:50:21 -06:00
|
|
|
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:
|
2010-01-19 15:31:15 -07:00
|
|
|
self.errors.append("Bad setup action, line %d, msg: %s, %s" % \
|
|
|
|
(action.lineNum, msg, traceback.format_exc()))
|
2009-10-06 11:50:21 -06:00
|
|
|
|
|
|
|
def __call__(self, tank):
|
|
|
|
"""Execute this program on the given tank."""
|
|
|
|
for statement in self._program:
|
|
|
|
try:
|
|
|
|
statement(tank)
|
|
|
|
except Exception, msg:
|
|
|
|
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
|