g | x | w | all
Bytes Lang Time Link
nan230721T194624ZKoutaiba
288C gcc200211T094256Zceilingc
361C151018T142114ZJerry Je
449Java 11200213T133424ZKevin Cr
nanPerl 5200216T132505Zandytech
200Burlesque200211T151945ZDeathInc
509Rust190223T050932Zdon brig
328Python190221T162907Zsenox13
357R151005T083013Zplannapu
125Pyth151010T093410ZJakube
nan151008T184839Zedc65
277Octave151008T214523Zbeaker
288Ruby151007T140611ZCristian
140Pyth151005T112724ZPurkkaKo
178Dyalog APL151005T101758Zmarinus
nanPerl151005T073310ZEric Was

C++ - A non esoteric version

vector<int> getBytes(const string& in)
{
    vector<int> result;
    for (int i = 0; i < in.length(); )
    {
        string s = in.substr(i, 2);
        stringstream ss;
        ss << std::hex << s;
        unsigned n;
        ss >> n;
        result.push_back(n);
        i += 3;
    }
    return result;
}

// each step will be binary 00, 01, 10 or 11: 0, 1, 2 3
unique_ptr<int[]> getSteps(const vector<int>& bytes, int& size)
{
     auto dp = unique_ptr<int[]>(new int[bytes.size() * sizeof(int)]());
     for (int i = 0; i < bytes.size(); ++i)
     {
         int byte = bytes[i];
         for (int j = 0; j < sizeof(int); j++)
         {
             // mask the bits and then shift result into position ( << s) so it can be inserted to res
             //int res |= ( ( (byte >> s) & 3) << s);
             int pos = j + (4 * i);
             dp[pos] = ((byte >> (2 * j)) & 3); // shift byte by 0, 2, 4 , 6 and mask - store 2 bits of the byte in j = 0 to j = 3
         }
     }
     size = bytes.size() * sizeof(int);
     return dp;
}

vector<vector<int>> populateGridWithMovements(const unique_ptr<int[]>& steps, int size, pair<int, int>& end_pos)
{
    vector<vector<int>> field(9, vector<int>(17, 0));
    pair<int, int> start_position = { 4, 8 };
    map<int, pair<int, int>> direction_map = {
                                            {0, {-1, -1}},
                                            {1, {-1, 1}},
                                            {2, {1, -1}},
                                            {3, {1, 1}} };


    auto adjust_at_edges = [](int x, int y)
    {
        set<pair<int, int>> corners = { {0, 16}, {0, 0}, {8, 16}, {8, 0} };
        if (corners.find(make_pair(x, y)) != corners.end())
            return make_pair(x, y);
        // If on the right edge and the next move is 01, we simply slide one space vertically up
        // or at the top edge (0, 8) an next move is (up right: -1, -1) this would mean new pos is -1, 7
        // so we must remain at postion 0 but move only horizontally
        // we do this by taking std::max(x, 0) which is 0 and then the min between 0 and 8 which is 0
        // thereby remaining at 0. equally of we are at postion 8 and new postion is 9
        // std::max will return 9 but the ouer std::min acts as a ceiling so it return 8
        // inner max acts as a barrier to prevent going below 0 and outer as a barrier going above range 
        return make_pair<const int&, const int&>(min(std::max(x, 0), 8), min(max(y, 0), 16));  
    };
    
    std::pair<int, int> pos = start_position;
    for (int i = 0; i < size; ++i)
    {
        // get new postions/deltas to move to
       int dx, dy;
       std::tie(dx, dy)= direction_map[steps[i]];
       
       // pos has the existing position - add the deltas (based on the current step position) and adjust new pos if required 
       pos = adjust_at_edges(pos.first + dx, pos.second + dy);

       // pos now contains new position to move to (x + dx) adjusted so that if at the edge its slides forward
       field[pos.first][pos.second] += 1;
       
       if (i == size - 1)
       {
           // copy last position we moved to
           end_pos.first = pos.first;
           end_pos.second = pos.second;
       }
    }
    return field;
}

string print_randomart(const vector<vector<int>>& grid, const pair<int, int>& end_pos)
{
    // Symbols for the number of times a position is visited by the bishop
    // White space means that the position was never visited
    // S and E are the start and end positions
    string symbols = " .o+=*BOX@%&#/^SE";
    pair<int, int> start_position = { 4, 8 };
    int start_x, start_y;
    tie(start_x, start_y) = start_position;
    string result = "+---[CODINGAME]---+\n";
    for (int i = 0; i < grid.size(); ++i)
    {
        string row_symbols;
        for (int j = 0; j < grid[0].size(); ++j)
        {
            // wrap-around logic if the number of times visited is greater than 14, 
            // e.g. we use 'o' if a position is visited for 17 times. 17 % 15 = 2
            if(i == start_x && j == start_y)
            {
                row_symbols += "S";
            }
            else if(i == end_pos.first && j == end_pos.second)
            {
                row_symbols += "E";
            }
            else
            {
                row_symbols += symbols[grid[i][j] % 15];
            }
        }
        result += "|" + row_symbols + "|\n";
    }
    result += "+-----------------+";
    return result;
}

C (gcc), 333 326 314 292 288 bytes

f[17][9],i,x=8,y=4;main(n){for(;i++%4||read(0,&n,3)*sscanf(&n,"%x",&n);n/=4)f[x+=n%2?x<16:-!!x][y+=n&2?y<8:-!!y]+=f[x][y]<14;f[8][4]=15;for(f[x][y]=puts(n="+-----------------+")-4;++i<75;puts("|"))for(x=!printf("|");x<17;)putchar(" .o+=*BOX@%&#/^SE"[y=fmin(f[x++][i-66],16)]);puts(n);}

Try it online!

Golfed from openssh source code key_fingerprint_randomart().

Edit: Now a complete program.

Less golfed:

/* initialize field */
f[17][9],i,x=8,y=4;
main(n){
  /* process raw key */
  /* each byte conveys four 2-bit move commands */
  for(;i%4||read(0,&n,3)+sscanf(&n,"%x",&n);n/=4)
    /* evaluate 2 bit, rest is shifted later */
    /* assure we are still in bounds; augment the field */
    f[x+=n%2?x<16:-!!x][y+=n&2?y<8:-!!y]+=f[x][y]<14;

  /* mark starting point and end point; output upper border */
  f[8][4]=15;
  f[x][y]=puts(n="+-----------------+")-4

  /* output content */
  for(;++i<75;puts("|"))
    for(x=!printf("|");x<17;)
      /*
       * Chars to be used after each other every time the worm
       * intersects with itself.
       */
      putchar(" .o+=*BOX@%&#/^SE"[y=fmin(f[x++][i-66],16)]);

  /* output lower border */
  puts(n);
}

C 361

Thanks to @ceilingcat for some very nice pieces of golfing - now even shorter

#define H h[i]|=*++p-48-*p/59*39
#define L puts("+-----------------+")
#define F(m,s)for(m=0;m<s;m++)
h[16],m[17][9],i,j,n,x=8,y=4;main(w,v)char**v;{for(char*p=v[1]-1;i<16;p++,i++)H<<4,H;F(j,16)F(n,4)x+=h[j]>>n*2&1?x!=16:-!!x,y+=h[j]>>n*2&2?y!=8:-!!y,m[x][y]++;m[8][4]=15;m[x][y]=L-4;F(i,9){printf("|");F(j,17)printf(L" .o+=*BOX@%&#/^SE"+m[j][i]);puts("|");}L;}

Try it online!

Java 11, 460 453 452 449 bytes

interface M{static void main(String[]a){var m=new char[9][17];Integer x=4,y=8,i=0,s;for(var p:a[0].split(":"))for(p=x.toString(x.parseInt(p,16),2),i=4;i-->0;m[x-=s<2?x>0?1:0:x/8-1][y-=s%2>0?y/16-1:y>0?1:0]++)s=x.decode(("0".repeat(8-p.length())+p).split("(?<=\\G..)")[i]);String t="+"+"-".repeat(m[x][y]=16)+"-+\n",r=t+"|";for(m[4][8]=15;++i<153;r+=y>15?"|\n"+(x<8?"|":""):"")r+=" .o+=*BOX@%&#/^SE".charAt(m[x=i/17][y=i%17]);System.out.print(r+t);}}

-11 bytes thanks to @ceilingcat.

Try it online.

Explanation:

interface M{              // Class
  static void main(String[]a){
                          //  Mandatory main method
    var m=new char[9][17];//   Create a character-matrix of size 9 by 17
    Integer x=4,y=8,      //   Current position, starting at 4,8
            i=0,s;        //   Temp integers
    for(var p:a[0].split(":"))
                          //   Loop over the argument-String, split by ":"
      for(p=x.toString(x.parseInt(p,16),
                          //    Convert the current part from hexadecimal String to integer
                       2),//    And then from integer to binary String
          i=4;i-->0       //    Inner loop `i` in the range (4,0]:
          ;               //      After every iteration:
           m[x-=          //       Update the `x` position:
                s<2?      //        If `s` is 0 or 1:
                 x>0?     //         If we're not out of bounds yet at the top:
                  1       //          Go up
                 :        //         Else:
                  0       //          Stay at the top border
                :         //        Else (s is 10 or 11):
                 x/8      //         If we're not out of bounds yet at the bottom:
                  -1]     //          Go down
                          //         Else:
                          //          Stay at the bottom border
            [y-=          //       Update the `y` position:
                s%2>0?    //        If `s` is odd (1 or 11):
                 y/16     //         If we're not out of bounds yet at the right side:
                  -1      //          Go right
                          //         Else:
                          //          Stay at the right border
                :         //        Else (s is 0 or 10):
                 y>0?     //         If we're not out of bounds yet at the left side:
                  1       //          Go left
                 :        //         Else:
                  0]      //          Stay at the left border
                    ++)   //       And increase the value at x,y cell by 1
        s=                //     Set `s` to:
          x.decode(("0".repeat(8-p.length())+p)
                          //      Left-pad the binary-String `p` with 0s up to length 8
                   .split("(?<=\\G..)")
                          //      Split it into parts of size 2
                   [i]    //      Get the `i`'th part
                      );  //      And convert it from binary-String to integer
                          //   After the nested loops are done:
    String t="+"+"-".repeat(m[x][y]=16
                          //   Set the value at position x,y to 16
                      )+"-+\n",
                          //   Push a temp String for the top/bottom borders
           r=t+"|";       //   Result-String, starting with the top border + one "|"
    for(m[4][8]=15;       //   Set the value at position 4,8 to 15
        ;++i<153          //   Loop over all cells:
        ;                 //     After every iteration:
         r+=              //      Append the following to the result-String:
            y>15?         //       If we're done with a row:
             "|\n"        //        Append "|" and a newline
             +(x<8?       //        And unless it's the last row:
                "|"       //         Also append a "|"
               :          //        If it is the last row:
                "")       //         Append nothing
            :             //       And if we're not done with the row yet:
             "")          //        Append nothing
      r+=                 //    Append the following to the result-String:
         " .o+=*BOX@%&#/^SE".charAt(m[x=i/17][y=i%17]);
                          //     The `i`'th character of the String " .o+=*BOX@%&#/^SE"
                          //     where `i` is the current value of cell x,y
    System.out.print(r    //   And finally output this result-String,
      +t);}}              //   including its bottom border

Perl 5, 283 276 266 254 + 1 (-p) = 255 bytes

$x=8;$y=4;for$n(map{hex}split(/:/)){$n&2?$y<8&&$y++:$y&&$y--,$n&1?$x<16&&$x++:$x&&$x--,$a[$x][$y]++,$n>>=2 for 1..4}$a[8][4]=15;$a[$x][$y]=16;map{$y=$_;$s.="|";$s.=substr' .o+=*BOX@%&#/^SE',$a[$_][$y],1 for 0..16;$s.="|
"}0..8;$l='-'x17;$_="+$l+
$s+$l+
"

Uses -p to take a line of input into $_, and to print the (modified) value of $_ at the end.

Try it online!

Ungolfed:

# Implicit from running with perl -p:
# read line from standard input
#while($_ = <STDIN>) {

# Initialise starting coordinates
$x=8;
$y=4;

# Split input line at colons, convert each element (2-digit hex value) to integer,
iterate over the list of integers
for $n (map { hex($_) } split(/:/))
{
    # for each integer, iterate 4 times, looking at 2 of the 8 bits each time
    for(1..4) 
    {
        # if second-last bit is set
        if($n & 2) {
            # move down, unless we are already at the bottom
            $y++ if($y < 8);
        }
        else {
            # move up, unless we are already at the top
            $y-- if($y);
        }

        # if last bit is set
        if($n & 1) {
            # move right, unless already at the right edge
            $x++ if($x<16);
        }
        else {
            # move left, unless already at the left edge
            $x-- if($x);
        }

        # Increment the counter for this position
        $a[$x][$y]++;

        # Shift number right two bits for next iteration
        $n = $n >> 2
    }
}

# Set fixed values (15 and 16) for start and end positions respectively
$a[8][4] = 15;
$a[$x][$y] = 16;


# Iterate over the rows of the grid, building a string containing the output rows
for(0..8)
{
    # save the y-coordinate for use in inner loop below
    $y = $_;

    # append the line start
    $s .= "|";

    # iterate over the columns of the row
    for(0..16)
    {
        # get the value for this position
        $v = $a[$_][$y];

        # map the value to the appropriate character
        $s .= substr(' .o+=*BOX@%&#/^SE', $v, 1)
    }

    # append the line end
    $s .= "|\n"
}

# build the hyphen lines for the top and bottom rows
$l = '-' x 17;

# build the full output string and assign to $_
$_ = "+$l+\n$s+$l+\n"

# Implicit from running under perl -p
#print $_;
#}

Burlesque, 200 bytes

':;;m{b6b28'0P[2cob2^p}m{{-1 1}Jcpj!!}{4 8}+]qr{?+q0cy{>.}Z]{8 16}cy{<.}Z]}pal_g_jf:u[j" .o+=*BOX@%&#/^"XXbcjq!!Z]z[{(D!)_+p^}m[' 17.*9.*)XXje!j'SD!j'ED!"|\n|"IC'|+]'|[+\['+'-17.*_+<-"+\n".+J<-#rCL\[Q

Try it online!

':;;                # Split at colons
m{                  # Map over these
 b6b2               # Read as hex, then to binary
 8'0P[              # Pad to 8 bit
 2co                # Chunks of 2
 b2                 # Read as binary
 ^p}                # Push each
m{                  # Map over these
 {-1 1}Jcp          # Construct direction array {{- -}{- +}{+ -}{+ +}}
 j!!                # Select the block indexed by binary val
}
{4 8}+]             # Push starting pos
qr{                 # Boxed reduce
 ?+                 # Add together
 q0cy{>.}Z]         # If < 0 -> 0
 {8 16}cy{<.}Z]     # If > box -> boxwidth
pa                  # Apply on inits
l_g_j               # Take start and end for later
f:                  # Calculate frequency of everything but head and tail
u[j                 # Unzip to {pos} {counts}
" .o+=*BOX@%&#/^"XXbcj # Magic string
q!!Z]               # Select element based on count
z[                  # Rezip to {pos icon}
{(D!)_+p^}m[        # Add D! (set at pos) to the each
' 17.*9.*)XX        # Create 17x9 array of spaces
je!                 # Evaluate block of ({pos icon D!}, ...)
j'SD!j'ED!          # Set start and end to S & E
"|\n|"IC            # Intercalate surrounding v-bars 
'|+]'|[+            # Add first and last
\[                  # Concatenate
'+'-17.*_+<-"+\n".+ # Create top-bar
J<-                 # Duplicate and reverse (putting newline at the front
#r                  # Rotate stack
CL                  # Collect stack as list
\[                  # Concatenate
Q                   # Pretty print

Rust - 509 bytes

fn b(s:&str)->String{let(mut v,mut b)=([[0;11];20],[9,5]);v[19]=[19;11];for i in 0..16{let mut c=usize::from_str_radix(&s[i*3..i*3+2],16).unwrap();for k in 0..4{for j in 0..2{v[j*18][i%9+1]=18;v[i+k][j*10]=[17,3][(i+k+17)%18/17];b[j]=match(if c&(j+1)==j+1{b[j]+1}else{b[j]-1},j,){(0,_)=>1,(18,0)=>17,(10,1)=>9,x@_=>x.0 as usize,}}v[b[0]][b[1]]+=1;c>>=2;}}v[9][5]=15;v[b[0]][b[1]]=16;(0..220).fold("\n".to_string(),|s,i|{format!("{}{}",s," .o+=*BOX@%&#/^SE-|\n".chars().nth(v[i%20][i/20] as usize).unwrap())})}

Large but... almost close to C. As usual there are many bytes used up due to the way Rust does not automagically cast types into each other. But there is also probably room for improvement.... could probably use some ideas from other solutions.

ungolfed version is on the Rust Playground online

Python, 381328

-51 thanks to @JonathanFrech

def h(f):
 s=[f'{int(o,16)>>s&3:02b}'for o in f.split(':')for s in(0,2,4,6)];r=[0]*153;p=76;w=17
 for d in s:r[p]+=1;p+=(-(p%w!=0),p%w!=16)[int(d[1])]+(-w*(p//w!=0),w*(p//w!=8))[int(d[0])]
 r[76]=15;r[p]=16;b='+'+'-'*w+'+';print(b);i=0
 while i<153:print(f"|{''.join(' .o+=*BOX@%&#/^SE'[c]for c in r[i:i+w])}|");i+=w
 print(b)

Slightly ungolfed for the sake of explanation:

def h_(f):
 #Alias 17 because it gets used enough times for this to save bytes
 w=17

 #Input parsing
 s=[f'{int(o,16)>>s&3:02b}'for o in f.split(':')for s in(0,2,4,6)]

 #Room setup
 r=[0]*153
 p=76

 #Apply movements
 for d in s:
  r[p]+=1
  p+=(-(p%w!=0),p%w!=16)[int(d[1])]+(-w*(p//w!=0),w*(p//w!=8))[int(d[0])]
 r[76]=15 #Set start position
 r[p]=16 #Set end position

 #Display result
 b='+'+'-'*w+'+'
 print(b)
 i=0
 while i<153:
  print(f"|{''.join(' .o+=*BOX@%&#/^SE'[c]for c in r[i:i+w])}|")
  i+=w
 print(b)

This mess of a line:

r[p]+=1;p+=(-(p%w!=0),p%w!=16)[int(d[1])]+(-w*(p//w!=0),w*(p//w!=8))[int(d[0])]

​ is functionally equivalent to this:

if int(d[0]): #Down, Y+
  if p//17!=8:
    p+=17
else: #Up, Y-
  if p//17!=0:
    p-=17
​
if int(d[1]): #Right, X+
  if p%17!=16:
    p+=1
else: #Left, X-
  if p%17!=0:
    p-=1

but nests all of the conditionals in this style of golf shortcut: (false_value,true_value)[condition] Hopefully the rest is fairly self-explanatory

Tests

h('16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48')
+-----------------+
|        .        |
|       + .       |
|      . B .      |
|     o * +       |
|    X * S        |
|   + O o . .     |
|    .   E . o    |
|       . . o     |
|        . .      |
+-----------------+

h("b6:dd:b7:1f:bc:25:31:d3:12:f4:92:1c:0b:93:5f:4b")
+-----------------+
|            o.o  |
|            .= E.|
|             .B.o|
|              .= |
|        S     = .|
|       . o .  .= |
|        . . . oo.|
|             . o+|
|              .o.|
+-----------------+

h("05:1e:1e:c1:ac:b9:d1:1c:6a:60:ce:0f:77:6c:78:47")
+-----------------+
|       o=.       |
|    o  o++E      |
|   + . Ooo.      |
|    + O B..      |
|     = *S.       |
|      o          |
|                 |
|                 |
|                 |
+-----------------+
```

R, 465 459 410 393 382 357 bytes

f=function(a){s=strsplit;C=matrix(as.integer(sapply(strtoi(el(s(a,":")),16),intToBits)[1:8,]),2);C[!C]=-1;n=c(17,9);R=array(0,n);w=c(9,5);for(i in 1:64){w=w+C[,i];w[w<1]=1;w[w>n]=n[w>n];x=w[1];y=w[2];R[x,y]=R[x,y]+1};R[]=el(s(" .o+=*BOX@%&#/^",""))[R+1];R[9,5]="S";R[x,y]="E";z="+-----------------+\n";cat(z);for(i in 1:9)cat("|",R[,i],"|\n",sep="");cat(z)}

With indentations and newlines:

f=function(a){
    s=strsplit
    C=matrix(as.integer(sapply(strtoi(el(s(a,":")),16),intToBits)[1:8,]),2)
    C[!C]=-1
    n=c(17,9)
    R=array(0,n)
    w=c(9,5)
    for(i in 1:64){
        w=w+C[,i]
        w[w<1]=1
        w[w>n]=n[w>n]
        x=w[1]
        y=w[2]
        R[x,y]=R[x,y]+1
    }
    R[]=el(s(" .o+=*BOX@%&#/^",""))[R+1]
    R[9,5]="S"
    R[x,y]="E"
    z="+-----------------+\n"
    cat(z)
    for(i in 1:9)cat("|",R[,i],"|\n",sep="")
    cat(z)
}

Usage:

> f("16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48")
+-----------------+
|        .        |
|       + .       |
|      . B .      |
|     o * +       |
|    X * S        |
|   + O o . .     |
|    .   E . o    |
|       . . o     |
|        . .      |
+-----------------+
> f("37:e4:6a:2d:48:38:1a:0a:f3:72:6d:d9:17:6b:bd:5e")
+-----------------+
|                 |
|                 |
|          .      |
|     .   o       |
|o . o . S +      |
|.+ + = . B .     |
|o + + o B o E    |
| o .   + . o     |
|         .o      |
+-----------------+

Pyth, 125 bytes

Jj*17\-"++"JVc9XXsm@"^ .o+=*BOX@%&#/"hdrS+*U9U17K.u.e@S[0b*8hk)1.b+tNyYNYsm_c4.[08jxsM^.HM16 2d2cz\:,4 8 8ieK17\E76\SjN"||")J

Try it online: Demonstration or Test-Suite

I wrote a few days ago, but didn't post it, because I wasn't really happy about it.

Explanation:

The basic idea is the following. I start with the pair (4, 8). In each move (m1,m2) I go from the (x, y) to (x-1+2*m1, y-1+2*m2). To make sure, that these coordinates don't go outside the boarders, I'll make some lists, sort them and return the middle element: (sorted(0,8,newx)[1], sorted(0,16,newy)[1]).

I keep track of all positions. To this list of positions I add a list of all possible positions, sort them and run-length-encode them. Which gives me a number for each position. With this number I can choose the coorect char, and at the end overwrite the chars of the start and end position.

JavaScript (ES6) 249 208

Edit Added missing border

Test running the snippet below in any EcmaScript 6 compliant browser

B=f=>f.replace(/\w+/g,b=>{for(b=`0x1${b}`;b-1;b>>=2)++g[p=(q=(p=(q=p+~-(b&2)*18)>0&q<162?q:p)+b%2*2-1)%18?q:p]},p=81,z=`+${'-'.repeat(17)}+`,g=Array(162).fill(0))&&g.map((v,q)=>q?q-81?q-p?q%18?' .o+=*BOX@%&#/^'[v]:`|
|`:'E':'S':z+`
|`).join``+`|
`+z

// TEST
console.log=x=>O.innerHTML+=x+'\n'

;['37:e4:6a:2d:48:38:1a:0a:f3:72:6d:d9:17:6b:bd:5e'
,'16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48'
,'b6:dd:b7:1f:bc:25:31:d3:12:f4:92:1c:0b:93:5f:4b'
,'05:1e:1e:c1:ac:b9:d1:1c:6a:60:ce:0f:77:6c:78:47'  
].forEach(t=>console.log(t+'\n'+B(t)+'\n'))


// Less golfed

BB=f=>(
  p = 81,
  g = Array(162).fill(0),
  f.replace(/\w+/g, b => {
    for(b = `0x1${b}`;b != 1; b >>= 2)
      q = p+~-(b&2)*18,
      p = q>0&q<162?q:p,
      p = (q=p+b%2*2-1)%18?q:p,
      ++g[p]
  }),
  g.map((v,q) => q-81?q-p?q%18?' .o+=*BOX@%&#/^'[v]:'\n':'E':'S')
  .join``
)
pre { font-family: menlo,consolas; font-size:13px }
<pre id=O></pre>

Octave, 277

d=reshape(rot90(dec2bin(hex2dec(strsplit(input('','s'),':'))))>'0',2,[])*2-1;p=[9;5];for m=1:64 p=[max(min(p(:,1)+d(:,m),[17;9]),1) p];end;A=' .o+=*BOX@%&#/^SE';F=A(sparse(p(2,:),p(1,:),1,9,17)+1);F(5,9)='S';F(p(2,1),p(1,1))='E';[a='+-----------------+';b=['|||||||||']' F b;a]

Explanation:

%// convert the input to binary and rearrange it to be
%//   an array of vectors: [x_displacement; y_displacement]
d=reshape(rot90(dec2bin(hex2dec(strsplit(input('','s'),':'))))>'0',2,[])*2-1;

%// start position array with vector for the start position
p=[9;5];
%// for each move, add displacement, clamping to valid values
for m=1:64 p=[max(min(p(:,1)+d(:,m),[17;9]),1) p];end;

%// alphabet for our fingerprint
A=' .o+=*BOX@%&#/^SE';

%// create a sparse matrix, accumulating values for duplicate
%// positions, and replace counts with symbols
F=A(sparse(p(2,:),p(1,:),1,9,17)+1);

%// correct the start and end symbols and construct the final output
F(5,9)='S';F(p(2,1),p(1,1))='E';
[a='+-----------------+';b=['|||||||||']' F b;a]

Sample run:

>> bish
b6:dd:b7:1f:bc:25:31:d3:12:f4:92:1c:0b:93:5f:4b
ans =

+-----------------+
|            o.o  |
|            .= E.|
|             .B.o|
|              .= |
|        S     = .|
|       . o .  .= |
|        . . . oo.|
|             . o+|
|              .o.|
+-----------------+

Ruby 288

->k{w=17
r=[z=?++?-*w+?+]
(0...w*9).each_slice(w).map{|o|r<<?|+o.map{|x|c=76
q=0
k.split(?:).flat_map{|b|(0..7).map{|i|b.to_i(16)[i]}}.each_slice(2){|h,v|v<1?(c>w&&c-=w):c<w*8&&c+=w
c+=h<1?c%w>0?-1:0:c%w<16?1:0
c==x&&q+=1}
x==76?'S':c==x ?'E':' .o+=*BOX@%&#/^'[q]}.join+?|}
(r+[z]).join'
'}

Try it online: http://ideone.com/QOHAnM

The readable version (the one I started golfing from) is here: http://ideone.com/XR64km

Pyth, 145 143 140

Jm*17]09A,4K8FYcz\:V4AmhtS[0^2d+@,HGdtyv@+_.BiY16*7\0+-4dyN),3 4 X@JGHh@@JGH; X@J4K15 X@JGH16
=Y++\+*17\-\+VJ++\|s@L" .o+=*BOX@%&#/^SE"N\|)Y

Try it online.

Pyth isn't really good at challenges with iteration. I'm expecting CJam to beat it easily.

Dyalog APL (178)

{⎕ML←3⋄F←9 17⍴0⋄5 9{(⍺⌷F)+←1⋄×⍴⍵:(1 1⌈9 17⌊⍺-1 1-2×↑⍵)∇1↓⍵⋄(⍺⌷F)←16⋄F[5;9]←15⋄K⍪(M,' .o+=*BOX@%&#/^SE'[1+F],M←'|')⍪K←'+','+',⍨17⍴'-'}⊃,/{↓⊖4 2⍴⍉(4/2)⊤¯1+⍵⍳⍨⎕D,'abcdef'}¨⍵⊂⍨':'≠⍵}

This is a function that takes the string as its right argument, and returns a character matrix containing the ASCII art representation, e.g.:

      F←{⎕ML←3⋄F←9 17⍴0⋄5 9{(⍺⌷F)+←1⋄×⍴⍵:(1 1⌈9 17⌊⍺-1 1-2×↑⍵)∇1↓⍵⋄(⍺⌷F)←16⋄F[5;9]←15⋄K⍪(M,' .o+=*BOX@%&#/^SE'[1+F],M←'|')⍪K←'+','+',⍨17⍴'-'}⊃,/{↓⊖4 2⍴⍉(4/2)⊤¯1+⍵⍳⍨⎕D,'abcdef'}¨⍵⊂⍨':'≠⍵}


      F '16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48'
+-----------------+
|        .        |
|       + .       |
|      . B .      |
|     o * +       |
|    X * S        |
|   + O o . .     |
|    .   E . o    |
|       . . o     |
|        . .      |
+-----------------+
      F 'b6:dd:b7:1f:bc:25:31:d3:12:f4:92:1c:0b:93:5f:4b'
+-----------------+
|            o.o  |
|            .= E.|
|             .B.o|
|              .= |
|        S     = .|
|       . o .  .= |
|        . . . oo.|
|             . o+|
|              .o.|
+-----------------+

Explanation:

Perl, 300 + 1 (-n) = 301 bytes

perl -ne 'sub b{$b=$_[0]+$_[1];$_[0]=$b<0?0:$b>$_[2]?$_[2]:$b}$v=pack"(H2)*",/\w\w/g;($x,$y)=(8,4);$a[b($y,($_&2)-1,8)*17+b($x,($_&1)*2-1,16)]++for map{vec$v,$_,2}0..63;@a[76,$y*17+$x]=(15,16);$c=" .o+=*BOX@%&#/^SE";print$d="+".("-"x17)."+\n",(map{+"|",(map{substr$c,$_,1}@a[$_*17..($_+1)*17-1]),"|\n"}0..8),$d'

This answer is disgusting, but it's also the first one for this puzzle, so it'll do for now.

-n to take a line of input on STDIN and fill $_.

# b($v, -1 or 1, max) modifies $v within 0..max
sub b{$b=$_[0]+$_[1];$_[0]=$b<0?0:$b>$_[2]?$_[2]:$b}

# turn $_ into a binary string
$v=pack"(H2)*",/\w\w/g;

# initialize cursor
($x,$y)=(8,4);

# find an element of single-dimensional buffer @a
$a[
    # y += (bitpair & 2) - 1, within 8
    b($y,($_&2)-1,8) * 17
    # x += (bitpair & 1) * 2 - 1, within 17
  + b($x,($_&1)*2-1,16)
# and increment it
]++
# for each bit pair (in the right order!)
  for map{vec$v,$_,2}0..63;

# overwrite the starting and ending positions
@a[76,$y*17+$x]=(15,16);

# ascii art lookup table
$c=" .o+=*BOX@%&#/^SE";

# output
print
  # the top row, saving it for later
  $d="+".("-"x17)."+\n",
  # each of the eight middle rows
  (map{+
    # converting each character in @a in this row as appropriate
    "|",(map{substr$c,$_,1}@a[$_*17..($_+1)*17-1]),"|\n"
  }0..8),
  # the bottom row
  $d