/mine_clearing_simulator

Runs and scores Starfleet Academy's student's mine clearing exercises

Primary LanguageHTML

Exercise

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|
   |    |
  /| |  |\
 / | |  | \
/__|_|__|__\
   /_\/_\
   ######
  ########
   ######
    ####
    ####
     ##
     ##   
     ##
     ##

Instructions

Usage:

linux

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
      

windows

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
      

results

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.

test inputs

There is a default directory with a set of test files you can use at this locations

test script

starfleet/tests/test_input/student_minesweeping_script.steps
      

or:

starfleet\tests\test_input\student_minesweeping_script.steps
      

test field

starfleet/tests/test_input/cuboid.dat

or

starfleet\tests\test_input\cuboid.dat

unit tests

There is a suite of tests that can be run with py.test from top level project dir.

$ py.text
      

there is a test coverage website built in.

You can find it under htmlcov directory.

Run the coverage yourself with:

bin\test_coverage.bat

todo: make a .sh script for this.

logging

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.

Work in progress..

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.

Example run

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)
      

working

cuboid

mine layout coordinate system rendering

vessel

step execution navigation targeting firiing decent

step

parsing and lexing instructions hit tracking

computer

calculations

smallest rectangle relative ship centering on dimensions

grid

shrinking and growing face rendering

simulator

execution of steps state machine

test suite

unit test suite can be run with py.test

fluent expectation based tests

test coverage reports in html

builds website

facilities

command line execution scripts for both linux and windows argument options parser with defaults

logger
output to local files, seperating info from errors
configured with local yaml file

todo

scoring (not implemented)

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

known issues

there’s a bug with shrininking and growing of space around the ship.

should be fixed by tweaking the simulation.recomput_cuboid() method

status

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.

notables

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

developed with:

emacs
ipython
py.test, nose, sure (spec-based semantic assertions)

todo:

diagram system arch
diagram domain models
diagram program flow

publication

pdf

todo:..

html

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.

Describe design

requirements

pip install:

nose mockito sure pytest pytest-cov

running

input

script file

contains the initial cuboid definition

field file

contains the student’s mine sweeping solution steps

simulator program

serves as the executor of the script and field files

components

domain

represent’s the entities within the simulation

input

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
cuboid

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     
point (x,y,z)

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 and firing patterns

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

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

vessel (ship)

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
score

todo:…

output

todo…

composition

cuboid

contains 3d a coordinate system of points

point’s will be recomputed with each step

grid

in charge of face rendering and adjustment’s to position

vessel

occupies a point (has a slot for a point)

step

represent’s an action performed by the simulator

simulation

the entry point for executing the simulation.

driver for input, execution, and output

computer

hold’s calculation logic for geometric positioning and navigation

scoring

calulate’s the results of the simulation’s runs

also, prints out the results formatted file