/*===========================================================================

         main.c 

        Interrupting Cows - 2 defensive positions, 2 offensive ones, 
        players swtich depending on position in relation to the ball.   


        Gary Yngve (gary@cc) and Alison Smith (ans@cc)
        990414

===========================================================================*/

#include "players.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>

#define KICK_DIST 10



        #define NW      0
        #define N       1
        #define NE      2
        #define W       3

        #define E       5
        #define SW      6
        #define S       7
        #define SE      8

#define ABS(x) ((x)>0?(x):-(x))

/* 
players.h includes a rather unusual macro, UN().  This macro
will convert the names of the functions in this .c file to
unique names.  For instance, if this were the east team, UN(team_name)()
will become EASTteam_name().  This is necessary for the automatic
compiling and linking of arbitrary teams for tournaments.

Below are the functions player1() through player4().  Each
function is passed 4 parameters.  The incoming parameters are 
the robot's sensory information:  

        local_area[]    Reveals what's nearby.  Indexed by
                        N,NE,E,SE,S, etc. so that, for example,
                        local_area[S] tells you what is in the
                        cell to the south of the robot.  Possible 
                        values include: BOUNDARY, OPPONENT, TEAMMATE,
                        BALL and EMPTY.

        ball_direction  Compass heading to the ball: N, S, E, W, NE, etc.

        x, y            The robot's location.  y varies from 1 to 22,
                        x varies from 1 to 78.

The function should return an integer indicating either a direction to
move (N, NE, E, S, etc) or KICK to kick the ball.
*/



static int players[4][2];       /*contains location of each player*/

static float team[80][25];      /*contains board w/ player locations marked*/
static float opp[80][25];       /*opponent locations probabilities*/
static float ball[80][25];      /*ball location probabilities*/
static float temp[80][25];      /*temp board*/
static float kick[80][25];      /*kick trajectories*/
static float move[80][25];      /*move possibilities*/
static float temp2[80][25];     /*temp2 board*/
static float go1[80][25];
static float go2[80][25];
static float go10[80][25];
static float go11[80][25];
static float go12[80][25];

static int id=0;
static int oldx,oldy;

static int back(int x) {
  switch(x) {
    case N: return NE;
    case NE: return NE;
    case E: return E;
    case SE: return SE;
    case S: return SE;
    case SW: return SW;
    case W: return W;
    case NW: return NW;
  }
  return 0xDEADBEEF;
}

/*reflects direction across vertical axis, with exception of N and S*/
static int refdir(int x, int i,int old) {
  if (x==E && i < 11) return SW;
  if (x==E && i >=11) return NW;
  if (x==W && i < 11) return SE;
  if (x==W && i >=11) return NE;
  switch(x) {
    case N: return NW;
    case NE: return NW;
    case E: return W;
    case SE: return SW;
    case S: return SW;
    case SW: return SE;
    case W: return E;
    case NW: return NE;
  }
  return 0xDEADBEEF;
}

/*find larger distance and return - used when finding distance to ball*/
static int dist(int x1,int y1,int x2,int y2) {
  return ABS(x2-x1) > ABS(y2-y1) ? ABS(x2-x1) : ABS(y2-y1);
}

/* rescale the probabilities in the array to add up to max */

static void normalize(float arr[80][25], float max) {
  int i,j;
  float t;
  t=0;
  for(i=0;i<80;i++)
    for(j=0;j<25;j++)
      t+=arr[i][j];
  t/=max;
  if (t==0) 
    for(i=0;i<80;i++)
      for(j=1;j<24;j++)
        arr[i][j]=1.0/(80*23);
  else
    for(i=0;i<80;i++)
      for(j=0;j<25;j++)
        arr[i][j]/=t;
}

/* copy src to dest */

static void copy(float dest[80][25], float src[80][25]) {
  int i,j;
  for(i=0;i<80;i++)
    for(j=0;j<25;j++)
      dest[i][j]=src[i][j];
}

/* clear dest to all 0's */

static void clear(float dest[80][25]) {
  int i,j;
  for(i=0;i<80;i++)
    for(j=0;j<25;j++)
      dest[i][j]=0;
}



/* scale dest by src, element by element */

static void weight(float dest[80][25],float src[80][25]) {
  int i,j;
  for(i=0;i<80;i++)
    for(j=0;j<25;j++)
      dest[i][j]*=src[i][j];
}


static void fuzzyball2(int i, int j, float temp[80][25]) {
  temp[i][j]+=1.0/9.0*
    (ball[i][j]+ball[i][j+1]+ball[i][j-1]+ball[i-1][j-1]+ball[i-1][j]+ball[i-1][
j+1]
     +ball[i+1][j-1]+ball[i+1][j]+ball[i+1][j+1]);

}

/*set position of the ball on the ball board*/
static void setballpos(int x, int y) {
  int i,j;

  clear(ball);
  ball[x][y]=1;
}

/*set opponent position on the board*/
static void setopppos(int x, int y) {
  int i,j;
  opp[x][y]=1;
}

/*guess probable position of the ball*/
static void guessballpos(int x, int y, int dir) {
  int i,j;
  int ball_direction;
  float temp_angle; 

  clear(temp2);
  for(i=1;i<79;i++)
    for(j=1;j<24;j++) {
      fuzzyball2(i,j,temp2);

    } 
  copy(ball,temp2);
  for(i=1;i<79;i++)
    for(j=1;j<24;j++) 
      fuzzyball2(i,j,temp2);

  for(i=0;i<80;i++) {temp2[i][0]=0;temp2[i][23]=0;}
  for(i=0;i<23;i++) {temp2[0][i]=0;temp2[79][i]=0;}

  copy(ball,temp2);
  normalize(ball,1);
  clear(temp2);

  for(i=0;i<80;i++)
    for(j=0;j<25;j++) {    
      if (ball[i][j]>0) {
        temp_angle = atan2((i - x), (j - y));
        temp_angle += PI;
        temp_angle = 360.0*temp_angle/(2.0*PI);
        ball_direction = N;
        if (temp_angle > 22.5+0*45)
          ball_direction = NW;
        if (temp_angle > 22.5+1*45)
          ball_direction = W;
        if (temp_angle > 22.5+2*45)
          ball_direction = SW;
        if (temp_angle > 22.5+3*45)
          ball_direction = S;
        if (temp_angle > 22.5+4*45)
          ball_direction = SE;
        if (temp_angle > 22.5+5*45)
          ball_direction = E;
        if (temp_angle > 22.5+6*45)
          ball_direction = NE;
        if (temp_angle > 22.5+7*45)
          ball_direction = N;
      
        if (ball_direction==dir)
          temp2[i][j]=1;
      }
    }
  weight(ball,temp2);
  normalize(ball,1);  
}

static void ballvalue(int *x, int *y) {
  int i,j; 
  float t,r;
  r=drand48();
  t=0;
  for(i=0;i<80;i++)
     for(j=0;j<25;j++) {
       t+=ball[i][j];
       if (r<t) {*x=i;*y=j;return;}
     }
}

/*finds new direction of the ball*/
static int newballdir(int px, int py, int bx, int by) {
  float temp_angle;
  int i,j,x,y;
  int ball_direction;
  x=px;y=py;i=bx;j=by;  
  
  temp_angle = atan2((i - x), (j - y));
  temp_angle += PI;
  temp_angle = 360.0*temp_angle/(2.0*PI);
  ball_direction = N;
  if (temp_angle > 22.5+0*45)
    ball_direction = NW;
  if (temp_angle > 22.5+1*45)
    ball_direction = W;
  if (temp_angle > 22.5+2*45)
    ball_direction = SW;
  if (temp_angle > 22.5+3*45)
    ball_direction = S;
  if (temp_angle > 22.5+4*45)
    ball_direction = SE;
  if (temp_angle > 22.5+5*45)
    ball_direction = E;
  if (temp_angle > 22.5+6*45)
    ball_direction = NE;
  if (temp_angle > 22.5+7*45)
    ball_direction = N;

  return ball_direction;
}

/*updates variables for each player turn*/
static void update(int local_area[9],int ball_direction, int x, int y) {
  int i,j;
  clear(temp2);
  for(i=0;i<9;i++) {
    switch(i) {
      case N: temp[x][y-1]=local_area[N];
        break;
      case NE: temp[x+1][y-1]=local_area[NE];
        break;
      case E: temp[x+1][y]=local_area[E];
        break;
      case SE: temp[x+1][y+1]=local_area[SE];
        break;
      case S: temp[x][y+1]=local_area[S];
        break;
      case SW: temp[x-1][y+1]=local_area[SW];
        break;
      case W: temp[x-1][y]=local_area[W];
        break;
      case NW: temp[x-1][y-1]=local_area[NW];
        break;
      default: temp[x][y]=local_area[i];
        break;
    }
  }

  guessballpos(x,y,ball_direction);
  for(i=x-1;i<x+2;i++)
    for(j=y-1;j<y+2;j++) {
      if (temp[i][j]==BALL) setballpos(i,j);
      if (temp[i][j]==OPPONENT) setopppos(i,j);
    }
}

/*-----------------------------------------------------

        player1()

-----------------------------------------------------*/
int UN(player1)(int local_area[9], int ball_direction, int x, int y)
{
/*
UN(player1)(int local_area[9] . . .) becomes
EASTplayer(int local_area[9] . . .) or
WESTplayer(int local_area[9] . . .) depending on whether
this team is compiled to play on the east or west.
*/

  int i,j,k,d;
  int ne,se,sw,nw,n,s,e,w,newdir;
  players[id][0]=x;players[id][1]=y;
  id=0;

  update(local_area,ball_direction,x,y);

  ballvalue(&i,&j);

/*if ball is in view*/

/*if ball is to the North, there is an opponent NE and E, do nothing*/
  if (local_area[N] == BALL && local_area[NE]==OPPONENT && local_area[E]==OPPONENT) return (DO_NOTHING);


/*if ball is N, there is an opponent NE and a teammate E, go NW
 *(to receive kick)*/
  if (local_area[N] == BALL && local_area[NE]==OPPONENT && local_area[E]==TEAMMATE && local_area[NW]==EMPTY) return (NW);

/*if ball is N, there is an opponent NE, a teammate E, and NW is full, go W
 *(receive kick*/
  if (local_area[N] == BALL && local_area[NE]==OPPONENT && local_area[E]==TEAMMATE && local_area[NW]!=EMPTY) return (W);

/*if ball is N, NE is teammate and E is opponent, go W (receive kick)*/
  if (local_area[N] == BALL && local_area[NE]==TEAMMATE && local_area[E]==OPPONENT && local_area[W]==EMPTY) return (W);

/*if ball is N, NE is teammate, E is opponent, and W is full, go SW
 *(team should kick)*/
  if (local_area[N] == BALL && local_area[NE]==TEAMMATE && local_area[E]==OPPONENT && local_area[W]!=EMPTY) return (SW);
 
/*if ball is N, NE is teammate, E is teammate, go W (make a break for it)*/ 
  if (local_area[N] == BALL && local_area[NE]==TEAMMATE && local_area[E]==TEAMMATE && local_area[W]==EMPTY) return (W);

/*if ball is N, NE is teammate, E is teammate, and W is full, go SW*/
  if (local_area[N] == BALL && local_area[NE]==TEAMMATE && local_area[E]==TEAMMATE && local_area[W]!=EMPTY) return (SW);

/*if ball is N, E is full and NE is empty, go NE - get around the ball*/
  if (local_area[N] == BALL && local_area[NE]==EMPTY && local_area[E]!=EMPTY)
    return (NE);

/*if ball is N, NE is full and E is empty, go E - get around the ball (unless
 *you are near a wall)*/
  if (local_area[N] == BALL && local_area[NE]!=EMPTY && local_area[E]==EMPTY && y!=2)
    return (E);

/*if ball is N, NE is full and E is empty but you are near a wall - do nothing*/
  if (local_area[N] == BALL && local_area[NE]!=EMPTY && local_area[E]==EMPTY && y==2)
    return (NE);

/*if ball is N, NE is empty and E is empty but NW is full, go NE*/
  if (local_area[N] == BALL && local_area[NE]==EMPTY && local_area[E]==EMPTY
      && local_area[NW]!=EMPTY) return (NE);

/*if ball is N, NE is empty, E is empty and so is NW, go NE*/
  if (local_area[N] == BALL && local_area[NE]==EMPTY && local_area[E]==EMPTY
      && local_area[NW]==EMPTY) 
    return (NE);




/*if ball is NE, and E is empty, move*/
  if (local_area[NE] == BALL && local_area[E]==EMPTY) return(E);
/*if ball is NE amd E has opponent, stay where you are (help to block opponent*/
  if (local_area[NE] == BALL && local_area[E]==OPPONENT) return(DO_NOTHING);
/*if ball is NE and E has teammate, go W for break away*/
  if (local_area[NE] == BALL && local_area[E]==TEAMMATE && 
      local_area[W]==EMPTY) return(W);
/*if ball is NE, E has teammate, adn you can't go W, so SW to get out of way*/
  if (local_area[NE] == BALL && local_area[E]==TEAMMATE && 
      local_area[W]!=EMPTY) return(SW); /* so we don't block kick */




/*if ball is South -mirror moves if ball is north*/
  if (local_area[S] == BALL && local_area[SE]==OPPONENT && local_area[E]==OPPONENT) return (DO_NOTHING);

  if (local_area[S] == BALL && local_area[SE]==OPPONENT && local_area[E]==TEAMMATE && local_area[SW]==EMPTY) return (SW);
/*changed from NW to SW ans 041499*/
  if (local_area[S] == BALL && local_area[SE]==OPPONENT && local_area[E]==TEAMMATE && local_area[SW]!=EMPTY) return (W);

  if (local_area[S] == BALL && local_area[SE]==TEAMMATE && local_area[E]==OPPONENT && local_area[W]==EMPTY) return (W);
  if (local_area[S] == BALL && local_area[SE]==TEAMMATE && local_area[E]==OPPONENT && local_area[W]!=EMPTY) return (NW);
  
  if (local_area[S] == BALL && local_area[SE]==TEAMMATE && local_area[E]==TEAMMATE && local_area[W]==EMPTY) return (W);
  if (local_area[S] == BALL && local_area[SE]==TEAMMATE && local_area[E]==TEAMMATE && local_area[W]!=EMPTY) return (NW);

  if (local_area[S] == BALL && local_area[SE]==EMPTY && local_area[E]!=EMPTY)
    return (SE);
/*changed from E to Se by ans 990414*/
  if (local_area[S] == BALL && local_area[SE]!=EMPTY && local_area[E]==EMPTY
      && y!=21)
    return (E);
  if (local_area[S] == BALL && local_area[SE]!=EMPTY && local_area[E]==EMPTY && y==21)
    return (SE);
/*gary - are these above reversed? and should we reorder two below? ans*/

  if (local_area[S] == BALL && local_area[SE]==EMPTY && local_area[E]==EMPTY
      && local_area[SW]!=EMPTY) return (SE);

  if (local_area[S] == BALL && local_area[SE]==EMPTY && local_area[E]==EMPTY
      && local_area[SW]==EMPTY) 
    return (SE);
/*i may have changed this from SW to SE - but i can't remmeber ans 990419*/



/*if ball is SE, mirror NE actions*/
  if (local_area[SE] == BALL && local_area[E]==EMPTY) return(E);
  if (local_area[SE] == BALL && local_area[E]==OPPONENT) return(DO_NOTHING);
  if (local_area[SE] == BALL && local_area[E]==TEAMMATE && 
      local_area[W]==EMPTY) return(W);
  if (local_area[SE] == BALL && local_area[E]==TEAMMATE && 
      local_area[W]!=EMPTY) return(NW); /* so we don't block kick */




/*if ball is E, SE is empty but SW is full, check for team to east of you -
 * if there and W is empty, go W for break away.
 *if there and W is full, go NW.
 *if there and both W and NW are full, go SW.
 *if not there, then go NE, if ull, go SE, if full, do nothing*/
 if (local_area[E] == BALL && local_area[SE]==EMPTY && local_area[SW]!=EMPTY
     && team[x+2][y]+team[x+2][y+1]+team[x+2][y-1]>0 && local_area[W]==EMPTY)
   return (W);
 if (local_area[E] == BALL && local_area[SE]==EMPTY && local_area[SW]!=EMPTY
     && team[x+2][y]+team[x+2][y+1]+team[x+2][y-1]>0 && local_area[NW]==EMPTY)
   return (NW);
 if (local_area[E] == BALL && local_area[SE]==EMPTY && local_area[SW]!=EMPTY
     && team[x+2][y]+team[x+2][y+1]+team[x+2][y-1]>0 && local_area[NW]!=EMPTY
     && local_area[W]!=EMPTY)
   return (SW);
 
  if (local_area[E] == BALL && local_area[NE]==EMPTY) return NE;
  if (local_area[E] == BALL && local_area[SE]==EMPTY) return SE;
  if (local_area[E] == BALL) return DO_NOTHING;

/*
 * If already on the west, kick!
 */

if (local_area[SW] == BALL) return(KICK);
if (local_area[W] == BALL) return(KICK);
if (local_area[NW] == BALL) return(KICK);

/*determine where youare in relation to ball and teammates*/
d=0;
if (x<players[0][0]) d++;
if (x<players[1][0]) d++;
if (x<players[2][0]) d++;
if (x<players[3][0]) d++;

/*if you are the closest, go to the ball - east side*/
if (dist(x,y,i,j)<=dist(i,j,players[0][0],players[0][1]) &&
    dist(x,y,i,j)<=dist(i,j,players[1][0],players[1][1]) &&
    dist(x,y,i,j)<=dist(i,j,players[2][0],players[2][1]) &&
    dist(x,y,i,j)<=dist(i,j,players[3][0],players[3][1])) 
  return (back(ball_direction));


/*furthest plays part of goalie*/
/*if furthest and to west of the ball, go to east of ball direction*/
if (d==0 && x<i) return (back(ball_direction));
/*if furthest and at east end of field, go directly to ball*/
if (d==0 && i>55) return (ball_direction);
/*else if furthest*/
if (d==0) {
    /*if greater than kick distance (plus some) go to ball*/
    if (dist(x,y,i,j)>KICK_DIST+4) return(ball_direction);
    /*if less then kick distance, go to reflection of ball_direction*/
    if (dist(x,y,i,j)<=KICK_DIST+4) return(refdir(ball_direction,j,oldy));
}

/*if third and west of ball, go to east of ball direction*/
if (d==1 && x<i) return (back(ball_direction));
/*if third and at east end of field, go to ball*/
if (d==1 && i>55) return (ball_direction);
/*else if third*/
if (d==1) { 
  /*if greater than almost kick distance, go to ball*/
  if (dist(x,y,i,j)>KICK_DIST/2+4) return(ball_direction);
  /*if less than kick distance, go to south side of ball-direction*/
  if (dist(x,y,i,j)<=KICK_DIST/2+4) return(refdir(ball_direction,22-j,24-oldy));
}

/*if second*/
if (d==2) {  
  /*if greater than about half of kick distance, go to ball*/
  if (dist(x,y,i,j)>KICK_DIST/2+2) return(ball_direction);
  /*else, go to south reflection of ball direction*/
  if (dist(x,y,i,j)<=KICK_DIST/2+2) return(refdir(ball_direction,22-j,24-oldy));
}

/*if most forward*/
if (d==3) {
    /*if greater than kick distance, go to ball direction*/
    if (dist(x,y,i,j)>KICK_DIST+2) return(ball_direction);
    /*if less than kick distance, go to reflection of ball direction*/
    if (dist(x,y,i,j)<=KICK_DIST+2) return(refdir(ball_direction,j,oldy));
}
/*gary this makes no sense - ans*/

/*if all fail, go to ball*/
return(ball_direction);


}

/*-----------------------------------------------------

        player2()

-----------------------------------------------------*/
int UN(player2)(int local_area[9], int ball_direction, int x, int y)
{
/*
 * Just do the same thing as player1
 */
id = 1;
return(UN(player1)(local_area, ball_direction, x, y));
}

/*-----------------------------------------------------

        player3()

-----------------------------------------------------*/
int UN(player3)(int local_area[9], int ball_direction, int x, int y)
{
/*
 * Just do the same thing as player1
 */
id=2;
return(UN(player1)(local_area, ball_direction, x, y));
}

/*-----------------------------------------------------

        player4()

-----------------------------------------------------*/
int UN(player4)(int local_area[9], int ball_direction, int x, int y)
{
/*
 * Just do the same thing as player1
 */
id=3;
return(UN(player1)(local_area, ball_direction, x, y));
}

/*-----------------------------------------------------

        team_name()

        Every team must have a name.  Make sure it
        is exactly 20 characters.  Pad with spaces.

-----------------------------------------------------*/
char *UN(team_name)()
{
char    *s;

/*  "####################\0" <--- 20 characters */
s = " Interrupting Cows  \0";
return(s);
}

/*-----------------------------------------------------

        initialize_game()

        This function is called only once per
        game, before play begins.  You can leave it
        empty, or put code here to initialize your
        variables, etc.

-----------------------------------------------------*/
void UN(initialize_game)()
{

}

/*-----------------------------------------------------

        initialize_point()

        This function is called once per point, just
        before play begins for that point.
        You can leave it empty, or put code here to initialize 
        your variables, etc.

-----------------------------------------------------*/
void UN(initialize_point)()
{
  int i;
  clear(ball);
  clear(opp);
  for(i=1;i<23;i++) {
    ball[39][i]=1.0/23;
    opp[35][i]=4.0/23;
  }
}

/*-----------------------------------------------------

        lost_point()

        If your team loses a point, this function is
        called, otherwise, if you win the point, won_point()
        is called.  You can leave it empty, or put code here 
        for negative re-inforcement, etc.

-----------------------------------------------------*/
void UN(lost_point)()
{
}

/*-----------------------------------------------------

        won_point()

        If your team wins a point, this function is
        called, otherwise, if you lose the point, lost_point()
        is called.  You can leave it empty, or put code here 
        for positive re-inforcement, etc.

-----------------------------------------------------*/
void UN(won_point)()
{
}

/*-----------------------------------------------------

        game_over()

        This function is called once at the end of
        the match.  You can leave it empty, or put code 
        here to save things to a file, etc.

-----------------------------------------------------*/
void UN(game_over)()
{
}



