#include <bits/stdc++.h>

//{{{ uf.hpp
#ifndef INCLUDE_UF_HPP
#define INCLUDE_UF_HPP
#include <vector>
#include <algorithm>
namespace orislib {
namespace ds {
//{{{ UF merge, same, root, size, count
struct UF {
  std::vector<int> data;
  int cnt;
  UF(int n) : data(n, -1), cnt(n) {}
  bool merge(int a, int b){
    a = root(a);
    b = root(b);
    if (a != b){
      cnt--;
      if (data[b] < data[a]) std::swap(a, b);
      data[a] += data[b];
      data[b] = a;
    }
    return a != b;
  }
  bool same(int a, int b){ return root(a) == root(b); }
  int root(int a){ return data[a] >= 0 ? data[a] = root(data[a]) : a; }
  int size(int a){ return -data[root(a)]; }
  int count(){ return cnt; }
};
//}}}
//{{{ UF2d merge, same, size, count
struct UF2d {
  UF uf;
  const int W, H;
  UF2d(int w, int h) : uf(w * h), W(w), H(h) {}
  bool merge(int ax, int ay, int bx, int by){ return uf.merge(ay * W + ax, by * W + bx); }
  bool same(int ax, int ay, int bx, int by){ return uf.same(ay * W + ax, by * W + bx); }
  int size(int x, int y){ return uf.size(y * W + x); }
  int count(){ return uf.count(); }
};
//}}}
}
}
#endif
//}}}

using namespace std;
using namespace orislib::ds;

typedef long long ll;
typedef long double ld;
typedef tuple<int, int> duo;
typedef tuple<short, short, short> tri;
const int dx[] = {0, 0, 0, 1, -1, 1, 1, -1, -1, 2, -2, 0, 0};
const int dy[] = {0, 1, -1, 0, 0, 1, -1, 1, -1, 0, 0, 2, -2};
const int Mod = 1000000000 + 0;
//{{{ templates
#define TT_ template<typename T>inline
#define TTF_ template<typename T,typename F>inline
TT_ T sq(T x){return x*x;}
TT_ T In(){T x;cin>>x;return x;}
TT_ void Out(T&x){cout<<x;}
TT_ void sort(T&v){sort(begin(v),end(v));}
TT_ void revs(T&v){reverse(begin(v),end(v));}
TT_ void uniq(T&v){sort(v);v.erase(unique(begin(v),end(v)),end(v));}
TT_ int ubnd(T&v,typename T::value_type&x){return upper_bound(begin(v),end(v),x)-begin(v);}
TT_ int lbnd(T&v,typename T::value_type&x){return lower_bound(begin(v),end(v),x)-begin(v);}
TTF_ void inpt(T&v,int n,F f){for(v.reserve(n);n--;v.emplace_back(f()));}
TTF_ void show(T&v,F f,string d=" ",string e="\n"){int i=0;for(auto&x:v)i++&&(cout<<d),f(x);cout<<e;}
TT_ typename T::iterator minel(T&v){return min_element(begin(v),end(v));}
TT_ typename T::iterator maxel(T&v){return max_element(begin(v),end(v));}

inline void fast_io(){ios::sync_with_stdio(0);cin.tie(0);}
inline int in(){int x;scanf("%d",&x);return x;}
inline ll pow_mod(ll a,ll k,ll m){ll r=1;for(;k>0;a=a*a%m,k>>=1)if(k&1)r=r*a%m;return r;}
inline ll mod_inv(ll a,ll p){return pow_mod(a,p-2,p);}
//}}} priority_queue queue deque front stringstream max_element min_element insert count make_tuple

const int len[] = {5, 9, 13};
int N;
bool valid[13][13 * 13 + 1];


struct Board {
  Board(){
    cnt[0] = cnt[1] = cnt[2] = 0;
  }
  void read(){
    tot = 0;
    for (int i = 0; i < 10; i++){
      for (int j = 0; j < 10; j++){
        field[i][j] = in();
        tot += field[i][j];
      }
    }
  }
  bool drop_ink(int x, int y, int c){
    cnt[c]++;
    tot -= len[c];
    if (tot < 0) return false;
    if (!valid[N - (cnt[0] + cnt[1] + cnt[2])][tot]) return false;
    for (int d = 0, l = len[c]; d < l; d++){
      int nx = x + dx[d], ny = y + dy[d];
      if (invalid(nx, ny)) return false;
      if (!field[ny][nx]) return false;
      field[ny][nx]--;
    }
    log.emplace_back(x, y, c + 1);
    return true;
  }
  bool invalid(int x, int y){
    return x < 0 || x >= 10 || y < 0 || y >= 10;
  }
  bool operator<(const Board& b) const {
    return evl < b.evl;
  }
  void eval(){
    UF2d uf(10, 10);
    int mini = 128;
    int maxi = 0;
    for (int i = 0; i < 10; i++){
      for (int j = 0; j < 10; j++){
        if (!field[i][j]) continue;
        for (int d = 0; d < 5; d++){
          int nx = j + dx[d], ny = i + dy[d];
          if (invalid(nx, ny)) continue;
          if (!field[ny][nx]) continue;
          uf.merge(j, i, nx, ny);
        }
        maxi = max(maxi, (int)field[i][j]);
      }
    }
    for (int i = 0; i < 10; i++){
      for (int j = 0; j < 10; j++){
        if (field[i][j]) mini = min(mini, uf.size(j, i));
      }
    }
    if (mini < 5) evl = 1 << 29;
    else evl = tot;
  }
  int evl;
  int tot;
  int cnt[3];
  unsigned char field[10][10];
  vector<tri> log;
};

struct Zhash {
  Zhash(){
    mt19937 eng;
    for (int i = 0; i < 10; i++){
      for (int j = 0; j < 10; j++){
        for (int s = 0; s < 256; s++){
          rands[i][j][s] = eng();
        }
      }
    }
  }
  int hash(Board& b){
    int h = 0;
    for (int i = 0; i < 10; i++){
      for (int j = 0; j < 10; j++){
        h ^= rands[i][j][b.field[i][j]];
      }
    }
    return h;
  }
  int rands[10][10][256];
};

int main()
{
  memset(valid, 0, sizeof(valid));
  valid[0][0] = true;
  for (int i = 1; i <= 12; i++){
    for (int j = 0; j <= 12 * 13; j++){
      if (!valid[i - 1][j]) continue;
      valid[i][j + 5] = valid[i][j + 9] = valid[i][j + 13] = true;
    }
  }
  const int BEAM_WIDTH = 2000;
  Zhash z;
  N = in();
  Board b;
  b.read();
  vector<Board> beam;
  beam.push_back(b);
  set<int> vis;
  vis.insert(z.hash(b));
  for (int t = 1; t <= N; t++){
    vector<Board> next_beam;
    for (const auto& bi : beam){
      for (int c = 0; c < 3; c++){
        for (int y = 0; y < 9; y++){
          for (int x = 0; x < 9; x++){
            Board bj = bi;
            if (!bj.drop_ink(x, y, c)) continue;
            int h = z.hash(bj);
            if (vis.count(h)) continue;
            vis.insert(h);
            bj.eval();
            next_beam.push_back(bj);
          }
        }
      }
    }
    sort(next_beam);
    if (next_beam.size() > BEAM_WIDTH){
      next_beam.erase(next_beam.begin() + BEAM_WIDTH, next_beam.end());
    }
    swap(beam, next_beam);
  }
  for (const auto& ans : beam[0].log){
    cout << get<0>(ans) << " " << get<1>(ans) << " " << get<2>(ans) << endl;
  }
  return 0;
}