# C# 7 - <s>414</s> 369 bytes <!-- language: lang-c# --> ___Edit__: switched to 1D looping, computing `i` and `j` on the fly_ using C=System.Console;class P{static void Main(){string D="",L;int W=0,H=0,i,j,z,k,q;for(;(L=C.ReadLine())!=null;H+=W=L.Length)D+=L+="\n";int[]P()=>new[]{i=z%W,j=z/W,-i,-j,i+j,j-i,-i-j,i-j};int[]B=null;for(z=H;z-->0;)if(D[z]%7<1)for(B=B??P(),k=8;k-->0;)if(B[k]<(q=P()[k]+1))B[k]=q;for (;++z<H;C.Write(q>8?'o':D[z]))for(q=k=8;k-->0;)q*=B[k]<P()[k]?0:B[k]==P()[k]?2:1;}} Complete program, takes input to standard in, prints it to standard out, uses `#`, `.`, and `o`. For each cell, it computes a 'profile' (which is the distance over 8 directions), and records a maximum of each of these. It then writes out the whole map again, and replaces any cell which is both on a boundary and not outside of any with a 'o'. The commented code below explains how it all works. As per my answer to http://codegolf.stackexchange.com/questions/50829/save-the-geese-from-extinction, this produces the smallest octagon (valid circumnavigation with largest area) which bounds the island. ___Note__: that for once in my life I'm using something from the current decade, and this code requires C# 7 to compile. If you do not have C# 7, there is one line that will need to be replaced, which is clearly marked in the code._ Example usage and output: type t7.txt | IslandGolf1.exe .........ooooooooooo.... ........o....#......o... .......o...#.#.##...#o.. ......o....#.#.###.##.o. .....o....########.##..o ....o.....############.o ...o.#....############.o ..o#.###.##############o .o##.##################o o.####################.o o..##################..o o.##################...o o...################...o o###################...o o#####################.o o.##################..o. o####################o.. o#...##############.o... o##...#############o.... o#.....###....#...o..... .o.....#.........o...... ..ooooooooooooooo....... Formatted and commented code: <!-- language: lang-c# --> using C=System.Console; class P { static void Main() { // \n 10 // # 35 // . 46 // o 111 string D="", // the whole map L; // initally each line of the map, later each line of output int W=0, // width H=0, // length (width * height) i, // x-index j, // y-index z, // position in map (decomposed into i and j by and for P) k, // bound index q; // bound distance, and later cell condition (0 -> outside, 8 -> inside, >8 -> on boudary) for(;(L=C.ReadLine())!=null; // read a line, while we can H+=W=L.Length) // record the width, and increment height D+=L+="\n"; // add a \n to the line (the rest of the code treats this as a . cell), and add the line to the map // create profile for point // converts 1d to 2d very cheaply // each entry describes the distance in one of the 8 directions: we want to maximise these to find the 'outer bounds' // these 8 bounds describe 8 lines, together an octogen int[]P()=>new[]{i=z%W,j=z/W,-i,-j,i+j,j-i,-i-j,i-j}; // new C#7 local method syntax (if you don't have C#7, you can test this code with the line below instead) //z=0;System.Func<int[]>P=()=>new[]{i=z%W,j=z/W,-i,-j,i+j,j-i,-i-j,i-j}; // old lambda syntax (must pre-assign z to make static checker happy) int[]B=null; // our current bounds, initially null (must only call P() when on a #) for(z=H;z-->0;) // for each cell if(D[z]%7<1) // if this cell is # for(B=B??P(), // init B when we first hit a # k=8;k-->0;) // for each bound if(B[k]<(q=P()[k]+1))B[k]=q; // update bound if necessary (add one so that we define the bound _outside_ the hashes) // z=-1 for (;++z<H; // for each cell C.Write(q>8?'o':D[z])) // print the cell (if q > 8, then we are on the bounds, otherwise, spit out whatever we were before) // check we are not 'outside' any of the bounds, and that we are 'on' atleast one of them for(q=k=8;k-->0;) // for each bound q*=B[k]<P()[k]?0: // outside bound (q=0) B[k]==P()[k]?2: // on bound (if q != 0, then q becomes > 8) 1; // inside (preserve q) } }