#include <stdio.h>
#include <string.h>

#include "prologin.h"
#include "enumere.h"
#include "eval.h"
#include "utils.h"

struct position DEPLACEMENT[4] =
{ {  1,  0 },
  {  0,  1 },
  { -1,  0 },
  {  0, -1 } };

#define TQ (TAILLE_BANQUISE * TAILLE_BANQUISE * TAILLE_BANQUISE * TAILLE_BANQUISE)

/* Score de chaque case */
double score_pos[TAILLE_BANQUISE][TAILLE_BANQUISE];

/* Remlpir le tableau score_pos */
void calc_score_pos()
{
    static struct bfs_scorepos q[TQ];
    memset(q, 0, sizeof(q));
    memset(score_pos, 0, sizeof(score_pos));

    int qpush = 0;
    int qpop = 0;

    for(int i = 0; i < TAILLE_BANQUISE; i++)
    {
        for(int j = 0; j < TAILLE_BANQUISE; j++)
        {
            struct position p;
            p.ligne = i, p.colonne = j;

            if(type_case(p) == LIBRE)
                score_pos[i][j] = -(1e5);
            else
                score_pos[i][j] = +1e5;
        }
    }

    struct alien_info_array laliens = liste_aliens();
    for(size_t i = 0; i < laliens.length; i++)
    {
        struct alien_info* ialien = laliens.datas + i;

        double isc = S_BASE_ALIEN;

        int visible = alien_visible(ialien);
        if(visible != 0)
        {
            int agcase = agent_sur_case(ialien->pos);

            if(agcase != moi())
            {
                if(agcase == adversaire())
                {
                    isc *= K_ALIEN_ADVERSAIRE;
                }
                else
                    isc *= K_ALIEN_VIDE;

                isc += K_SCORE_ALIEN * ialien->points_capture;

                if(visible > 0)
                    isc += K_VIS_CURR * visible;
                else if(visible < 0)
                    isc = K_VIS_SOON - K_VIS_CURR * (visible * visible);

                q[qpush].pos = ialien->pos;
                q[qpush].score = isc;
                qpush = (qpush + 1) % TQ;

            }
        }

        if(ialien->points_capture <= 0 && alien_sur_case(ialien->pos))
            isc = -(1e5);

        score_pos[ialien->pos.ligne][ialien->pos.colonne] = isc;
    }

    /* Boucle du BFS */
    while(qpop != qpush)
    {
        struct bfs_scorepos* act = q + qpop;
        qpop = (qpop + 1) % TQ;

        for(int i = 0; i < 4; i++)
        {
            struct position* idep = DEPLACEMENT + i;

            struct bfs_scorepos* nouv = q + qpush;
            nouv->pos.ligne   = act->pos.ligne + idep->ligne;
            nouv->pos.colonne = act->pos.colonne + idep->colonne;
            nouv->score       = act->score;
            nouv->score      -= K_COUTACT * COUT_DEPLACEMENT;

            if(type_case(nouv->pos) == LIBRE
            && nouv->score > score_pos[nouv->pos.ligne][nouv->pos.colonne])
            {
                /* Ajouter un deplacement simple */
                score_pos[nouv->pos.ligne][nouv->pos.colonne] = nouv->score;
                qpush = (qpush + 1) % TQ;
            }

            nouv = q + qpush;
            nouv->pos.ligne   = act->pos.ligne   + idep->ligne;
            nouv->pos.colonne = act->pos.colonne + idep->colonne;
            nouv->score       = act->score;
            nouv->score      -= K_COUTACT * COUT_GLISSADE;

            while(type_case(nouv->pos) == LIBRE
               && agent_sur_case(nouv->pos) == -1)
            {
                nouv->pos.ligne   += idep->ligne;
                nouv->pos.colonne += idep->colonne;
            }

            nouv->pos.ligne   -= idep->ligne;
            nouv->pos.colonne -= idep->colonne;

            if(type_case(nouv->pos) == LIBRE
            && nouv->score > score_pos[nouv->pos.ligne][nouv->pos.colonne])
            {
                /* Ajouter une glissage */
                score_pos[nouv->pos.ligne][nouv->pos.colonne] = nouv->score;
                qpush = (qpush + 1) % TQ;
            }
        }
    }

    /* Post traitement */
    for(size_t i = 0; i < laliens.length; i++)
    {
        struct alien_info* ialien = laliens.datas + i;
        struct position*   iapos  = &ialien->pos;

        if(alien_sur_case(*iapos))
        {
            for(int j = 0; j < 4; j++)
            {
                struct position* idep = DEPLACEMENT + j;

                struct position pdir;
                pdir.ligne   = iapos->ligne   + idep->ligne;
                pdir.colonne = iapos->colonne + idep->colonne;

                struct position pinv;
                pinv.ligne   = iapos->ligne   - idep->ligne;
                pinv.colonne = iapos->colonne - idep->colonne;

                double* isc = &score_pos[pdir.ligne][pdir.colonne];

                if(agent_sur_case(*iapos) == moi())
                {
                    if(agent_sur_case(pinv) == adversaire())
                        *isc += K_DEF_BLOQ;
                    else if(agent_sur_case(pinv) == -1)
                        *isc += K_DEF_BLOQPREV;
                    else
                        *isc = 0;
                }
                else if(agent_sur_case(*iapos) == adversaire())
                {
                    if(agent_sur_case(pinv) != -1
                    || type_case(pinv) != LIBRE)
                        *isc = 0;
                }
            }
        }
    }
}

double eval()
{
    struct position pagent = position_agent(moi(), agent_simul);

    double sc = score_pos[pagent.ligne][pagent.colonne];

    if(alien_sur_case(pagent))
    {
        for(int i = 0; i < 4; i++)
        {
            struct position* idep = DEPLACEMENT + i;

            struct position pdir;
            pdir.ligne   = pagent.ligne   + idep->ligne;
            pdir.colonne = pagent.colonne + idep->colonne;

            struct position pinv;
            pinv.ligne   = pagent.ligne   - idep->ligne;
            pinv.colonne = pagent.colonne - idep->colonne;

            if(agent_sur_case(pdir) == adversaire()
            && type_case(pinv) == LIBRE
            && agent_sur_case(pinv) != moi())
                sc -= K_PETREPOUSSE;
        }
    }

    for(int i = 0; i < 4; i++)
    {
        struct position* idep = DEPLACEMENT + i;

        struct position pdir;
        pdir.ligne   = pagent.ligne   + idep->ligne;
        pdir.colonne = pagent.colonne + idep->colonne;

        if(alien_sur_case(pdir) && agent_sur_case(pdir) != adversaire())
        {
            sc++;
        }
    }

    return sc;
}
