In this exercise, I am choosing to take a literate programming approach. I will be describing the design and writing blocks of executable code using emacs org-mode babel, which provides the means to both document and run essential pieces of the similator’s code inside this README.org document itself.
/\ |==| | | | | | | /____\ | | |Joel| | IX| | | /| | |\ / | | | \ /__|_|__|__\ /_\/_\ ###### ######## ###### #### #### ## ## ## ##
Usage:
long:
bin/simulator.sh --field path/of/field/file --script path/of/script/file
short:
bin/simulator.sh -f path/of/field/file --s path/of/script/file
example:
bin/simulator.sh -f starfleet/tests/test_input/cuboid.dat -s starfleet/tests/test_input/student_minesweeping_script.steps
long:
bin\similator.bat --field path\of\field\file -script path\of\script\file\
short:
bin/simulator.sh -f path\of\field\file -s path\of\script\file
example:
bin\simulator.bat -f starfleet\tests\test_input\cuboid.dat -s starfleet\tests\test_input\student_minesweeping_script.steps
The results file will be output in the same directory as the script file. It will be named the same except that there will be a .out extension on it. This file will be overwritten each time a simulation is run.
There is a default directory with a set of test files you can use at this locations
starfleet/tests/test_input/student_minesweeping_script.steps
or:
starfleet\tests\test_input\student_minesweeping_script.steps
starfleet/tests/test_input/cuboid.dat
or
starfleet\tests\test_input\cuboid.dat
There is a suite of tests that can be run with py.test from top level project dir.
$ py.text
You can find it under htmlcov directory.
Run the coverage yourself with:
bin\test_coverage.bat
todo: make a .sh script for this.
Currently the logs are buing output to the directory where the commands are executing.
There are a bunch of diagnostics that I put out to console for the time being. I’m still debugging and working on the core system, so it may look a bit crazy when you run it from the cli.
Just pick up the output file to see the clean results matching the format desired.
The simulator program is currently under development. It’s basic engine is in place to handle falling through the cuboid, shooting mines, and redrawing the cuboid according to the rules. This includes trimming the grid incrementally as mines are taken out and also keeping the space centered around the ship.
Here is a successful run of the program in it’s current state.
Field file:
..N.. ..... W...E ..... ..S..
Script file:
north delta south west gamma east east gamma west south delta
Ouput file:
FIELD FILE: ..N.. ..... W...E ..... ..S.. SCRIPT FILE: north delta south west gamma east east gamma west south delta OUPUT: step 1 north delta south west gamma east east gamma west south delta north ..N.. ..... W...E ..... ..S.. step 2 ..N.. ..... W...E ..... ..S.. delta south ..... ..... V...D ..... ..R.. step 3 ..... ..... V...D ..... ..R.. west U...C ..... ..Q.. step 4 U...C ..... ..Q.. gamma east ... ... ..B ... P.. step 5 ... ... ..B ... P.. east ... ... ..A ... O.. step 6 ... ... ..A ... O.. gamma west . . . . N step 7 . . . . N south . . M step 8 . . M delta . pass/fail (stubbed)
mine layout coordinate system rendering
step execution navigation targeting firiing decent
parsing and lexing instructions hit tracking
smallest rectangle relative ship centering on dimensions
shrinking and growing face rendering
execution of steps state machine
fluent expectation based tests
builds website
command line execution scripts for both linux and windows argument options parser with defaults
better validations hit mine marking
NEED TO PUT IN MODULE DEPS INTO SETUP.PY as it is, you’ll have to figure out based on what breaks
should be fixed by tweaking the simulation.recomput_cuboid() method
I’m close, but would like to continue work on the system to knock out the remaining features. If you want to go ahead and begin evaluating the system, please go forward.
lots of comprehension and lambda kung-fu for general purpose algorithms liberal use of generator streams and map,reduce,filtration pythonic functional idioms preferred over imperatives test-driven design methodology followed domain responsibilities are cleanly segmented and appropriately placed
todo:..
This readme is an executable emacs org file. It can both run the code and be publised as HTML. Github automatically understands .org files, so we’ll use this document to start with.
nose mockito sure pytest pytest-cov
contains the initial cuboid definition
contains the student’s mine sweeping solution steps
serves as the executor of the script and field files
represent’s the entities within the simulation
the input is collected from a cuboid file and the student’s script file. we put this information into data structures that are appropriate for the job. for this purpose we’ll need a lexer and a parser.
first we’ll consume the cuboid file
cuboid = open("./cuboid.dat", "r").read()
return cuboid
next we’ll get the steps that the student submitted to the simulator.
steps = open("./student_minesweeping_script.steps", "r").read().split("\n")
return steps
a cuboid is our data structure that represents our 3d coordinate system. you can print it and it will render it’s 2d view for outputting. here is a descriptive riff of the general mechanism behind the cuboid.
cuboid = """ .Z.
...
Z.Z
...
.Z. """
# compute height and width
width = len(list(cuboid.split()[0].strip()))
height = len(cuboid.split("\n"))
# build a mapping char to value
z_map = {c:i+1 for i,c
in enumerate([chr(c)
for c in range(ord('a'), ord('z')+1)] + [chr(c)
for c in range(ord('A'), ord('Z')+1)])}
#compute depth
import re
# find the mines and order them from deepest to most shallow
mine_chars = list(set(re.findall(r'[a-zA-Z]',cuboid)))
deepest_mine = reduce(lambda highest,current: current if z_map[current] > z_map[highest] else highest, mine_chars)
depth = z_map[deepest_mine]
# generate a cubic data structure of correct dimensions
cube_space = [[['.' for z in range(depth)]
for y in range(height)]
for x in range(width)]
# compute the mine coordinates in cubic space
for y,line in enumerate(cuboid.strip().split("\n")):
for x,char in enumerate(list(line.strip())):
if char in mine_chars:
z = z_map[char]-1
cube_space[x][y][z] = char
print(x,y,z)
return cube_space
points within the cuboid are represented as tuples
first we need to be able to find the center point of the x,y plane, in order to place the ship at it’s location
def find_center(cuboid):
width = len(cuboid)
height = len(cuboid[0])
center_point = ((width / 2) + (width % 2), (height / 2) + (height % 2))
return center_point
center_point = find_center(cuboid)
return center_point
We also need to be able to recomput the size of the x,y plane based upon the location of the ship and the mines
"todo"
movement within the cuboid corresponds done by lexing each string instruction to a map of lambdas. this allows us to easily connect language to action in our system.
firing patterns are just tuples of 2d coordinate offsets. they are assumed to go all the way to the bottom of the z-axis.
decent_rate = 1
navigation = (('north', lambda x,y: (x, y+1)),
('south', lambda x,y: (x, y-1)),
('east', lambda x,y: (x+1, y)),
('west', lambda x,y: (x-1, y)))
firing_patterns = (('alpha',((-1, -1), (-1, 1), (1, -1), (1, 1))),
('beta',((-1, 0), (0, -1), (0, 1), (1, 0))),
('gamma',((-1, 0), (0, 0), (1, 0))),
('delta',((0, -1), (0, 0), (0, 1))))
distance is tracked between points
this is used to find the center of the cuboid and to determine if photon torpedo firing_patterns actually hit the mines
there is a hit tracking mechanism that computes a hit based on distance, postion of points, and the firing pattern
the ship will have characteristics and behaviors.
characteristics:
position (x,y,z) firing_patterns
behaviors:
step fire move fall (happens on completion of a step)
class Vessel(Entity):
decent_rate = -1
def __init__(self, name="Enterprise"):
self.name = name
# initialize defaults
self.decent_level = 0
self.x,self.y,self.z = 0,0,0
self.steps = []
def step(self, step, cuboid):
print "running step: " + step.instructions
#run the step's operations
for operation in step.operations:
op,instruction,arg = operation
if op == "fire":
pattern = arg
step.hits = self.fire(pattern, cuboid, step)
if op == "move":
movement_calculator = arg[1]
x,y = movement_calculator(self.x,self.y)
self.move(x, y, cuboid)
self.steps.append(step)
return step
def fire(self, pattern, cuboid, step=None):
name, offsets = pattern
print "firing ", name, offsets
hits = [mine for mine in cuboid.mines
if self.hit_p(mine, offsets, cuboid)]
return hits
def hit_p(self, mine, shots, cuboid):
''' predicate to determine hits '''
mx,my,mz = mine[0]
for shot in shots:
ox, oy = shot
rx = self.x + ox
ry = self.y + oy
if rx == mx and ry == my:
return True
return False
def move(self, x, y, cuboid):
print "moving ship to new coordinates"
self.decent_level = self.decent_level - decent_rate
self.x, self.y, self.z = x, y, self.decent_level
print "new coordinates: ", self.get_coordinates()
print "ship now at decent level: ", self.decent_level
def get_coordinates(self):
return (self.x,self.y,self.z)
def render(self):
return self.name
todo:…
todo…
contains 3d a coordinate system of points
point’s will be recomputed with each step
in charge of face rendering and adjustment’s to position
occupies a point (has a slot for a point)
represent’s an action performed by the simulator
the entry point for executing the simulation.
driver for input, execution, and output
hold’s calculation logic for geometric positioning and navigation
calulate’s the results of the simulation’s runs
also, prints out the results formatted file