
#include "Player.hh"

using namespace std;


/**
 * Write the name of your player and save this file
 * with the same name and .cc extension.
 */
#define PLAYER_NAME Enric


const vector<Dir> all_dirs = {
  DEFAULT, SLOW, FAST,
  UP, SLOW_UP, FAST_UP,
  DOWN, SLOW_DOWN, FAST_DOWN};


struct PLAYER_NAME : public Player {


    /**
     * Factory: returns a new instance of this class.
     * Do not modify this function.
     */
    static Player* factory() {
        return new PLAYER_NAME;
    }


    /**
     * Attributes for your player can be defined here.
     */


  /* Returns whether the first cell ahead from p which has a non-empty
     type corresponds to a car of another player */
  bool found_target(Pos p) {
    int i =  first(p);
    int j = second(p) + 1;
    while (within_window({i, j}, round()+1) and
           cell({i, j}).type == EMPTY)
      ++j;

    return
      cell({i, j}).type == CAR and
      player_of(cell({i, j}).cid) != me();
  }


  /* Returns whether a position is ok as an intermediate cell */
  bool ok(Pos p, Dir d, int rnd) {

    Pos pd = p + d;
    if (not within_window(pd, round()+1)) return false;
    if (not within_window(pd, rnd      )) return false;    

    for (Dir dir : dir2all.at(d)) {
      Pos pdir = p + dir;
      CType t = cell(pdir).type;
      if (t == MISSILE or t == CAR or t == TYRE)
        return false;
    }

    for (int j = number_universe_columns() + second(pd) - 2; j < second(pd); ++j) {
      CType tj = cell({first(pd), j}).type;
      if (tj == CAR and player_of(cell({first(pd), j}).cid) != me()) return false;
    }

    for (int j = number_universe_columns() + second(pd) - 2; j < second(pd); ++j) {
      CType tj = cell({first(pd), j}).type;
      if (tj == MISSILE) return false;
    }

    return true;
  }


  template<typename T>
  T& at(vector<vector<T>>& mat, Pos p) {
    int i = first(p);
    int u = number_universe_columns();
    int w = number_window_columns();
    int r = round() % u;
    int j = second(p) % u;
    return j >= r ? mat[i][j-r] : mat[i][u - r + j];
  }


  /* Computes the shortest path from position ini to the closest cell
     of type in the set goals.
     Returns whether some goal was found.
  */
  bool shortest_path(Pos ini,
                     const set<CType>& goals,
                     vector<Pos>& path) {

    my_assert(within_window(ini, round()));

    const Pos UNDEF = {-1, -1};
    const int    oo = numeric_limits<int>::max();

    int rnd = round() % number_universe_columns();

    int r = number_rows();
    int c = number_window_columns() + 1;
    vector<vector<Pos>> par(r, vector<Pos>(c, UNDEF));
    vector<vector<int>> dst(r, vector<int>(c, +oo));
    at(par, ini) = ini;
    at(dst, ini) = 0;
    queue<Pos> q;
    q.push(ini);
    while (not q.empty()) {
      Pos p = q.front();
      q.pop();
      if (goals.contains(cell(p).type)) {
        Pos pant = at(par, p);
        while (pant != p) {
          path.push_back(p);
          p = pant;
          pant = at(par, p);
          my_assert(p != UNDEF);
        }
        return true;
      }
      for (Dir d : all_dirs) {
        Pos pd = p+d;
        if (ok(p, d, round() + 1 + at(dst, p)) and
            at(par, pd) == UNDEF) {
          at(par, pd) = p;
          at(dst, pd) = 1 + at(dst, p);
          q.push(pd);
        }
      }
    }
    return false;
  }


    /**
     * Play method.
     *
     * This method will be invoked once per each round.
     * You have to read the board here to place your actions
     * for this round.
     *
     */
    virtual void play() {

      for (Car_Id cid = begin(me()); cid != end(me()); ++cid) {
        Car s = car(cid);
        if (s.alive) {

          // if (randomize(0, 1) == 0) {
          //   move(cid, DEFAULT);
          //   continue;
          // }

          if (found_target(s.pos)) {
            shoot(cid);
            continue;
          }
          vector<Pos> path;
          set<CType> goals = {MISSILE_BONUS, WATER_BONUS,GAS_BONUS};
          bool found = shortest_path(s.pos, goals, path);
          if (found) {
            Dir d = path.back() - s.pos;
            my_assert(dir_ok(d));
            move(cid, d);
            continue;
          }

          bool danger =
            cell(s.pos + DEFAULT).type == TYRE or
            cell(s.pos + FAST   ).type == TYRE;
          if (s.nb_miss > 0 and danger) {
            shoot(cid);
            continue;
          }

        }
      }
    }
};


/**
 * Do not modify the following line.
 */
RegisterPlayer(PLAYER_NAME);
