Anthill Server and Sample Bots

Server Implementation

Download the Anthill-Server.

To test your Anthill-Bot it might be convenient for you to use our Anthill-Server. Note that it is work in progress and in case you find any mistakes, please report them either via the Forum or directly to sandro@soi.ch.

The server is written in C++ and should work under both Linux and Windows (and Mac, for that matter). However, the focus lies on Linux. In order to compile it has to be linked against the NCurses library (resp. its windows port PDCurses), which is used to produce a terminal-based-gui-visualization.

Hopefully the following information will help you getting the server running, if you have any problems, however, do not hesitate to either write in the Forum or send an email to sandro@soi.ch

Compilation under Linux

Make sure you have installed gcc and install the ncurses-library. Under Ubuntu this can be done by:

sudo apt-get install build-essential libncurses5-dev

Then compile the server with

g++ -o server server.cpp -lncurses

Compilation under Windows

We managed to compile the server under Windows using different Versions of Microsof Visual Studio (if you have any reports with other compilers, please contact us). First download PDCurses and extract it in some folder. Then start Visual Studio, create an empty project and add server.cpp as an «existing item». Also add curses.h, such that the header-file is found during compilation. Moreover make sure that you link against the pdcurses.lib by adding it to «Project - Properties- Configuration Properties - Linker - Input - Additional Dependencies». Moreover when testing make sure you have set up the arguments correctly (this can be done in «Project - Properties - Configuration Properties - Debugging - Command Arguments») and that the pdcurses.dll lies in the same folder as your executable (this is probably the Debug folder).

After that you should be good to go.

Usage

The server is started with

./server MODE W H K N Z S "commands for starting bot1" "commands for starting bot2" ...
where MODE is:

  • 0 for Simulation mode (simulate game and print ranking).
  • 1 for Step-By-Step Simulation
  • 2 for Terminal-GUI-Mode.

and the other values are desribed in the task description.

So a sample execution could be

./server 2 50 60 3 20 18 1 ./bot1 "java bot2" "python bot3.py" "ruby bot4.rb"
assuming the bots are in the same folder and one of the bots is compiled to bot1.

Note that in either MODE a file with log_file.txt is created which stores debug-information and potential errors. If anything goes wrong please consult the log_file.txt first and also append it to your potential emails/Forum-posts.

Sample Bot(s)

Besides the Server implementation we provide you with a sample bot to get started with. The bot acts not very intelligently, it just walks around randomly until it sees its own hill, at which point it marks the current position with a scent and proceeds walking towards the hill. Moreover if it does not see the hill but a position marked with the scent, it walks towards it.

We implemented this strategy in different programming languages, to get you started easily and to show you that the programming language does not really matter.

If your favorite language is missing just request it in the forum and we will try to port the sample bot to your requested language (as long as it is doable in some reasonable effort, so porting to some Assembly-Language or Brainfuck might be out of scope. This of course does not mean that you are not allowed to hand-in in Brainfuck ).

Currently we have ported the sample bot to the following languages: Ada, C, C++, D, Fortran90, Go, Haskell, Java, Python, Ruby.

Also note that we are not experts in all those languages. So it is very well possible that there are some errors or at least non-best-practices in there. If you happen to find one, send an email to sandro@soi.ch.

Ada - Version

-- sample bot in ada
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Ada.Numerics.Discrete_Random;
with Ada.Characters.Handling; use Ada.Characters.Handling;

procedure Bot is
   type View is range -3..3;
   type Move is (N,S,W,E,H,M,J); subtype Direction is Move range N..E;

   package Rand is new Ada.Numerics.Discrete_Random(Direction); use Rand;
   Gen: Generator;

   function MoveTowards (Y, X: View) return Move is
   begin
      if    Y<0 then return N;
      elsif Y>0 then return S;
      elsif X<0 then return W;
      elsif X>0 then return E;
      else           return H; end if;
   end MoveTowards;

   -- field of vision
   Map1: array (View, View) of Character;
   Map2: array (View, View) of Natural;

   NextMove, LastMove: Move;
   HillDiscovered, MarkerDiscovered: Boolean;
   Marker: Natural;
   GoalX, GoalY: View;
   Width, Height, K, Teams, Size, Ants, Z, Dx, Dy: Integer;
   I, Hill: Character;
begin
   Reset (Gen);

   -- read game specifications
   Get(Width); Get(Height); Get(K); Get(Ants); Get(Z); Get(Teams); Get(Size);
   Get(I); Get(I); Hill := To_Upper (I); Marker := Character'Pos (I);
   Skip_Line;

   loop
      -- read last move
      Get(LastMove);
      if LastMove = J then
        -- jump, read offset
        Get(Dx); Get(Dy);
      end if;,

      -- read field of vision
      HillDiscovered := False;
      for Y in View loop
         for X in View loop
            Get (Map1(Y, X));
            if Map1(Y, X) = Hill then
               GoalX := X; GoalY := Y;
               HillDiscovered := True;
            end if;
         end loop;
         Skip_Line;
      end loop;

      MarkerDiscovered := False;
      for Y in View loop
         for X in View loop
            Get (Map2 (Y, X));
            if not HillDiscovered and Map2(Y, X) = Marker then
               GoalX := X; GoalY := Y;
               MarkerDiscovered := True;
            end if;
         end loop;
      end loop;

      Skip_Line;

      -- test if we are finished
      exit when Map1(0,0)='.';

      if HillDiscovered or MarkerDiscovered then
         -- goal in sight
         -- test whether current position needs to be marked
         if Map2(0,0)=Marker or MarkerDiscovered then
            -- then move towards the hill
            NextMove := MoveTowards (GoalY, GoalX);
         else
            -- otherwise mark the position
            NextMove := M;
         end if;
      else
         -- neither marked position nor hill in sight
         -- choose a random move
         NextMove := Random (Gen);
      end if;

      -- output move
      Put (Move'Image (NextMove));
      if NextMove = M then
         Put (Marker);
      end if;
      New_Line;
      Flush;
   end loop;
end Bot;

C - Version

#include <stdio.h>
#include <stdlib.h>
char directions[]={'N','S','W','E'};

char move_towards(int y, int x) {
  if (y<3) return directions[0];
  else if (y>3) return directions[1];
  else if (x<3) return directions[2];
  else if (x>3) return directions[3];
  return 'H';
}

char move_random() {
  return directions[rand()%4];
}

int main() {
  int W,H,K,N,Z,V,S,map2[10][10],scent,goaly,goalx,y,x;
  char I,buf[10],map1[10][10],move,see_goal;
 
  scanf("%i%i%i%i%i%i%i %c ", &W, &H, &K, &N, &Z, &V, &S, &I);

  while(1) {
    // Read last move.
    gets(buf);

    // Read first map.
    for (y=0;y<7;y++)
      gets(map1[y]);

    // Read second map.
    for (y=0;y<7;y++) {
      for (x=0;x<7;x++) {
        scanf("%i", &map2[y][x]);
      }
    }
   
    // Read newline.
    gets(buf);

    // Test if we are finished.
    if (map1[3][3]=='.')
      return 0;
   
    // Determine move.
    scent=-1;
   
    // Step 1: Look for our hill.
    see_goal = 0;
    for (y=0;y<7;y++) {
      for (x=0;x<7;x++) {
        if (map1[y][x]==toupper(I)) {
          goalx = x;
          goaly = y;
          see_goal=1;
        }
      }
    }

    if (see_goal) {
      // Hill in sight.      
      // Test if current position alread marked.
      if (map2[3][3]==(int)I) {
        // Then move towards hill.
        move = move_towards(goaly,goalx);
      } else {
        // Otherwise mark position.
        move='M';
        scent=(int)I;
      }
    } else {
      // Hill not in sight.
      // Test if marked position in sight.
      for (y=0;y<7;y++) {
        for (x=0;x<7;x++) {
          if (map2[y][x]==(int)I) {
            goalx = x;
            goaly = y;
            see_goal=1;
          }
        }
      }

      if (see_goal) {
        // Marked position in sight.
        // Move towards marked position.
        move = move_towards(goaly,goalx);
      } else {
        // No marked position in sight.
        // Choose a random move.
        move = move_random();
      }
    }
   
    // Send Move.
    if (move == 'M') {
      printf("%c %i\n",move,scent);
    } else {
      printf("%c\n",move);
    }
    fflush(stdout);
  }
   
  return 0;
}

C++ - Version

#include <iostream>
#include <vector>
#include <string>
#include <stdlib.h>
using namespace std;
char directions[]={'N','S','W','E'};

char move_towards(int y, int x) {
  if (y<3) return directions[0];
  else if (y>3) return directions[1];
  else if (x<3) return directions[2];
  else if (x>3) return directions[3];
  return 'H';
}

char move_random() {
  return directions[rand()%4];
}

int main() {
  int W,H,K,N,Z,V,S;
  char I;
  string tmp;
 
  cin >> W >> H >> K >> N >> Z >> V >> S >> I;

  // Read newline.
  getline(cin, tmp);

  while(1) {
    // Read last move. Not extracting distance information for J-Moves.
    string last_move;
    getline(cin, last_move);

    // Read first map.
    vector<string> map1;
    for (int y=0;y<7;y++) {
      getline(cin, tmp);
      map1.push_back(tmp);
    }

    // Read second map.
    vector<vector<int> > map2(7, vector<int>(7));
    for (int y=0;y<7;y++) {
      for (int x=0;x<7;x++) {
        cin >> map2[y][x];
      }
    }

    // Test if we are finished.
    if (map1.at(3).at(3)=='.') {
      return 0;
    }

    // Read newline.
    getline(cin, tmp);
   
    // Determine move.
    int scent,goaly,goalx;
    scent=-1;
   
    // Step 1: Look for our hill.
    bool see_goal=false;
    for (int y=0;y<7;y++) {
      for (int x=0;x<7;x++) {
        if (map1[y][x]==toupper(I)) {
          goalx = x;
          goaly = y;
          see_goal=true;
        }
      }
    }
   
    char move;
    if (see_goal) {
      // Hill in sight.      
      // Test if current position alread marked.
      if (map2[3][3]==(int)I) {
        // Then move towards hill.
        move = move_towards(goaly,goalx);
      } else {
        // Otherwise mark position.
        move='M';
        scent=(int)I;
      }
    } else {
      // Hill not in sight.
      // Test if marked position in sight.
      for (int y=0;y<7;y++) {
        for (int x=0;x<7;x++) {
          if (map2[y][x]==(int)I) {
            goalx = x;
            goaly = y;
            see_goal=1;
          }
        }
      }

      if (see_goal) {
        // Marked position in sight.
        // Move towards marked position.
        move = move_towards(goaly,goalx);
      } else {
        // No marked position in sight.
        // Choose a random move.
        move = move_random();
      }
    }
   
    // Send Move.
    // endl automatically flushes.
    if (move == 'M') {
      cout << move << " " << scent << endl;
    } else {
      cout << move << endl;
    }
  }

  return 0;
}

D - Version

import std.stdio, std.random, std.conv, std.string;

char moveTowards(size_t y, size_t x) {
        return y<3 ? 'N' :
               y>3 ? 'S' :
               x<3 ? 'W' :
               x>3 ? 'E' :
                     'H' ;
}

char moveRandom() {
        return "NSWE"[uniform(0,$)];
}

void main() {
        int W,H,K,N,Z,V,S;
        char I;
        scanf("%d%d%d%d%d%d%d %c ",&W,&H,&K,&N,&Z,&V,&S,&I);
        loop: for(;; stdout.flush()) {
                // read last move
                readln();
               
                // read first map
                char[7][7] map1;
                foreach(ref l; map1) l=readln()[0..7];
               
                // read second map
                int[7][7] map2;
                foreach(ref l; map2) l=to!(int[])(readln().strip().split());
               
                // test if we are finished
                if(map1[3][3] == '.') return;
               
                // look for hill
                foreach(y, l; map1) {
                        foreach(x, v; l) {
                                if(v == I+('A'-'a')) {
                                        // seen hill, test if position already marked
                                        if(map2[3][3] == I) {
                                                // then move towards hill
                                                writeln(moveTowards(y,x));
                                        } else {
                                                // otherwise mark position
                                                writeln("M ", to!(int)(I));
                                        }
                                        continue loop;
                                }
                        }
                }
               
                // hill not in sight
                // test if marked position in sight
                foreach(y, l; map2) {
                        foreach(x, v; l) {
                                if(v == I) {
                                        // marked position in sight
                                        // move towards marked position
                                        writeln(moveTowards(y,x));
                                        continue loop;
                                }
                        }
                }
               
                // no marked position in sight
                // choose a random move
                writeln(moveRandom());
        }
}

Fortran - Version

! sample bot in FORTRAN 90
program bot
  implicit none
  character :: moveTowards, randomMove

  integer :: Width, Height, K, Teams, Size, Ants, Z
  character :: I, Hill, lastMove, nextMove
  logical :: hillDiscovered, markerDiscovered
  character, dimension(7,7) :: map1
  integer, dimension(7,7) :: map2
  integer :: x, y, goalX, goalY, marker, dx, dy

  call random_seed

  ! read game specifications
  read(*,*) Width, Height, K, Ants, Z, Teams, Size, I
  marker = iachar(I); Hill = achar(iachar(I)-32)

  do
     read(*,*) LastMove
     if (LastMove=='J') then
        ! jump, get offset
        read(*,*) dx, dy
     end if

     ! read field of vision
     hillDiscovered = .false.
     do y=1,7
        read(*,'(7A1)') map1(y,1:7)
        do x=1,7
           if (map1(y,x)==Hill) then
              hillDiscovered = .true.
              goalX = x; goalY = y
           end if
        end do;
     end do

     markerDiscovered = .false.
     do y=1,7
        read(*,*) map2(y,1:7)
        do x=1,7
           if (.not. hillDiscovered .and. map2(y,x)==marker) then
              markerDiscovered = .true.
              goalX = x; goalY = y
           end if
        end do
     end do

     ! test if we are finished
     if (map1(4,4)=='.') exit
     
     markerDiscovered = .false.
     if (hillDiscovered .or. markerDiscovered) then
        ! goal in sight, test whether current position needs to be marked
        if (map2(4,4)==marker .or. markerDiscovered) then
           ! then move towards the hill
           nextMove = moveTowards(goalY,goalX)
        else
           ! otherwise mark the position
           nextMove = 'M'
        end if
     else
        ! neither marked position nor hill in sight, choose a random move
        nextMove = randomMove()
     end if

     ! output move
     if (nextMove == 'M') then
        write(*,'(A1,1X,I3)') 'M', marker
     else
        write(*,'(A1)') nextMove
     end if
     call flush
  end do
end program bot

character function moveTowards(y, x)
  implicit none
  integer, intent(in) :: x, y

  if     (y<4) then; moveTowards = 'N'
  elseif (y>4) then; moveTowards = 'S'
  elseif (x<4) then; moveTowards = 'W'
  elseif (x>4) then; moveTowards = 'E'
  else             ; moveTowards = 'N'; end if
  return
end function moveTowards

character function randomMove()
  implicit none
  character, dimension(4) :: move = (/'N', 'S', 'W', 'E' /)
  real :: r; call random_number(r);
  randomMove = move(int(3*r+1))
  return
end function randomMove

Go - Version

// sample bot in go
package main
import ("fmt"; "rand")

func moveTowards (y,x int) byte {
  switch {
        case y<3: return 'N'
        case y>3: return 'S'
        case x<3: return 'W'
        case x>3: return 'E'
  }
  return 'H'
}

func moveRandom() byte {
  return "NSWE"[rand.Intn(4)]
}

func main() {
  var ( W,H,K,N,V,Z,S,goalX,goalY,marker,dx,dy int
        I,nextMove,lastMove,Hill byte
        m1 [7]string
        m2 [7][7]int
        seeHill, seeMarker bool
      )
 
  fmt.Scanf ("%d%d%d%d%d%d%d %c\n", &W,&H,&K,&N,&Z,&V,&S,&I)
  Hill = (I+'A')-'a'
  marker = int(I)

  for {
     // read last move
     fmt.Scanf ("%c", &lastMove)
     if lastMove == 'J' {
        // jump, read offset
        fmt.Scan (&dx, &dy)
     }
     
     // read first map    
     seeHill = false
     for y:=0;y<7;y++ {
       fmt.Scan  (&m1[y])
       for x:=0;x<7;x++ {
          // look for the hill
          if m1[y][x]==Hill {
             seeHill = true
             goalX = x; goalY = y
          }
       }
     }

     // read second map
     seeMarker = false
     for y:=0;y<7;y++ {
        for x:=0;x<7;x++ {
           fmt.Scan (&m2[y][x])
           // look for markers
           if !seeHill && m2[y][x]==marker {
              seeMarker = true
              goalX = x; goalY = y
           }
        }
     }

     fmt.Scanln()
     
     // test whether we are finished
     if m1[3][3]=='.' {
        break
     }

     if seeHill || seeMarker {
        // goal in sight, test whether current position needs to be marked
        if  m2[3][3]==marker || seeMarker {
           nextMove = moveTowards (goalY, goalX)
        } else {
           nextMove = 'M'
        }
     } else {
        // neither marked position nor hill in sight, choose random move
        nextMove = moveRandom()
     }
               
     // send move
     if nextMove == 'M' {
        fmt.Printf ("M %d\n", marker)
     } else {
        fmt.Printf ("%c\n", nextMove)
     }
  }
}

Haskell - Version

import System.IO
import Data.List
import Data.Char
import System.Random

moveTowards y x | y < 3 = 'N'
                | y > 3 = 'S'
                | x < 3 = 'W'
                | x > 3 = 'E'
moveTowards _ _         = 'H'

moveRandom x = "NSWE"!!x

find2D i yss = let loop y [] = Nothing
                   loop y (xs:xss) = case elemIndex i xs of
                                       Just x -> Just (y,x)
                                       _ -> loop (y+1) xss
               in loop 0 yss

bot i (lines, r) = let maps = drop 1 lines
                       map1 = take 7 maps
                       map2 = map (map (\x -> read x :: Int)) (map words (drop 7 maps))
                       pos1 = find2D (toUpper i) map1
                       pos2 = find2D (ord i) map2
                       move = case pos1 of
                                Just (y,x) -> if map2!!3!!3 == ord i then (moveTowards y x):"\n"
                                              else "M " ++ show (ord i) ++"\n"
                                _ -> case pos2 of
                                       Just (y,x) -> (moveTowards y x):"\n"
                                       _ -> (moveRandom r):"\n"
                   in if map1!!3!!3 /= '.' then move else ""

chunks n [] = []
chunks n xs = (take n xs):(chunks n (drop n xs))

concatWhile p (xs:xss) | p xs = xs ++ concatWhile p xss
concatWhile _ _ = []

feed f rs input = concatWhile (/="") (map f (zip (chunks 15 (lines input)) rs))

main = do
  hSetBuffering stdout LineBuffering
  l <- getLine
  gen <- newStdGen
  let rs = randomRs (0, 3) gen
  let w = words l
  let i = w!!7!!0
  interact (feed (bot i) rs)

Java - Version

import java.util.*;
import java.lang.*;
import java.io.*;
class bot {
    public static Random r;
   
    public static char[] directions={'N','S','W','E'};

    public static char move_towards(int y, int x) {
        if (y<3) return directions[0];
        else if (y>3) return directions[1];
        else if (x<3) return directions[2];
        else if (x>3) return directions[3];
        return 'H';
    }
   
    public static char move_random() {
        return directions[r.nextInt(4)];
    }

    public static char toUpper(char x) {
        if (x>='a' && x<='z') return (char)((int)x+'A'-'a');
        return x;
    }
   
    public static void main(String[] args) throws IOException {
        r = new Random(123);
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
       
        int W,H,K,N,Z,V,S;
        int[][] map2 = new int[7][7];
        char I;
        String[] map1 = new String[7];
       
        String line = in.readLine();
        Scanner s = new Scanner(line).useDelimiter(" ");
        W=s.nextInt();
        H=s.nextInt();
        K=s.nextInt();
        N=s.nextInt();
        Z=s.nextInt();
        V=s.nextInt();
        S=s.nextInt();
        I=line.charAt(line.length()-1);

        while (true) {
            // Read last move. Ignoring distance-information for J-Moves.
            line = in.readLine();
            char last_move = line.charAt(0);

            // Read first map.
            for (int y=0;y<7;y++)
                map1[y]=in.readLine();

            // Read second map.
            for (int y=0;y<7;y++) {
                line = in.readLine();
                s = new Scanner(line).useDelimiter(" ");
                for (int x=0;x<7;x++) {
                    map2[y][x]=s.nextInt();
                }
            }

            // Test if we are finished.
            if (map1[3].charAt(3)=='.')
                return;
           
            // Determine move.
            char move;
            int scent=-1;
   
            // Step 1: Look for our hill.
            int goalx,goaly;
            goalx=goaly=0;
            boolean see_goal = false;
            for (int y=0;y<7;y++) {
                for (int x=0;x<7;x++) {
                    if (map1[y].charAt(x)==toUpper(I)) {
                        goalx = x;
                        goaly = y;
                        see_goal=true;
                    }
                }
            }

            if (see_goal) {
                // Hill in sight.      
                // Test if current position alread marked.
                if (map2[3][3]==(int)I) {
                    // Then move towards hill.
                    move = move_towards(goaly,goalx);
                } else {
                    // Otherwise mark position.
                    move='M';
                    scent=(int)I;
                }
            } else {
                // Hill not in sight.
                // Test if marked position in sight.
                for (int y=0;y<7;y++) {
                    for (int x=0;x<7;x++) {
                        if (map2[y][x]==(int)I) {
                            goalx = x;
                            goaly = y;
                            see_goal=true;
                        }
                    }
                }
               
                if (see_goal) {
                    // Marked position in sight.
                    // Move towards marked position.
                    move = move_towards(goaly,goalx);
                } else {
                    // No marked position in sight.
                    // Choose a random move.
                    move = move_random();
                }
            }
           
            // Send Move.
            if (move == 'M') {
                System.out.println(move + " " + scent);
            } else {
                System.out.println(move);
            }
            System.out.flush();
        }
    }
}

Python - Version

import sys
import random

directions=['N','S','W','E']
def move_towards(y,x):
  global directions
  if y<3:
    return directions[0]
  elif y>3:
    return directions[1]
  elif x<3:
    return directions[2]
  elif x>3:
    return directions[3]
  return 'H'

def move_random():
  return directions[random.randint(0,3)]

l = sys.stdin.readline()
[W,H,K,N,Z,V,S,I] = l[:-1].split(' ')

while True:
  # Read last move. Ignoring jumping information for J-Moves.
  last_move = sys.stdin.readline()[0]
  # Read first map.
  map1 = [[c for c in sys.stdin.readline().strip()] for y in xrange(7)]
  # Read second map.
  map2 = [[int(c) for c in sys.stdin.readline().strip().split(' ')] for y in xrange(7)]

  # Test if we are finished.
  if map1[3][3] == '.':
    exit(0)

  # Determine move.
  see_goal = False
  for y,l in enumerate(map1):
    for x,v in enumerate(l):
      if v == I.upper():
        goaly,goalx=y,x
        see_goal = True
        break

  if see_goal:
    # Hill in sight.      
    # Test if current position alread marked.
    if map2[3][3] == ord(I):
      # Then move towards hill.
      move = move_towards(goaly,goalx)
    else:
      # Otherwise mark position.
      move='M'
      scent=ord(I)
  else:
    # Hill not in sight.
    # Test if marked position in sight.
    for y,l in enumerate(map2):
      for x,v in enumerate(l):
        if v == ord(I):
          goaly,goalx=y,x
          see_goal = True
          break
    if see_goal:
      # Marked position in sight.
      # Move towards marked position.
      move = move_towards(goaly,goalx)
    else:
      # No marked position in sight.
      # Choose a random move.
      move = move_random()
  # Send Move.
  if move == 'M':
    print move,scent
  else:
    print move
  sys.stdout.flush()

Ruby - Version

$directions=['N','S','W','E']
def move_towards(y,x)
  if y<3
    return $directions[0]
  elsif y>3
    return $directions[1]
  elsif x<3
    return $directions[2]
  elsif x>3
    return $directions[3]
  end
  return 'H'
end

def move_random()
  return $directions[rand(4)]
end

srand(123)
l = $stdin.readline()
W,H,K,N,Z,V,S,I = l.split(' ')[0], l.split(' ')[1].to_i, l.split(' ')[2].to_i, l.split(' ')[3].to_i, l.split(' ')[4].to_i, l.split(' ')[5].to_i, l.split(' ')[6].to_i, l.split(' ')[7][0]  

while true
  # Read last move. Ignoring jumping information for J-Moves.
  last_move = $stdin.readline()[0]

  # Read first map.
  map1 = Array.new
  (0..6).each {map1 << $stdin.readline().strip.each_char.to_a}

  # Read second map.
  map2 = Array.new
  (0..6).each {|y| map2 << $stdin.readline().strip.split(' ').each {|v| v.to_i }}

  # Test if we are finished.
  if map1[3][3] == '.'
    exit(0)
  end

  goaly,goalx,see_goal=-1,-1,false
  # Determine move.
  map1.each_with_index { |row,y|
    row.each_with_index { |el,x|
      if el==I.chr.to_s.capitalize[0].chr
        goaly,goalx=y,x
        see_goal=true
      end
    }
  }

  if see_goal
    # Hill in sight.      
    # Test if current position alread marked.
    if map2[3][3].to_i == I.to_i
      # Then move towards hill.
      move = move_towards(goaly,goalx)
    else
      # Otherwise mark position.
      move='M'
      scent=I.to_i
    end
   else
    # Hill not in sight.
    # Test if marked position in sight.
    map2.each_with_index { |row,y|
      row.each_with_index { |el,x|
        if el.to_i==I.to_i
          goaly,goalx=y,x
          see_goal=true
        end
      }
    }

    if see_goal
      # Marked position in sight.
      # Move towards marked position.
      move = move_towards(goaly,goalx)
    else
      # No marked position in sight.
      # Choose a random move.
      move = move_random()
    end
  end

  if move == 'M'
    print move, " ", scent
  else
    print move
  end
  print "\n"
  $stdout.flush()
end