int main() {
  
  config c;
  config_fscan(&c, stdin);
  srand(time(NULL));
  int max_depth = 1;
  int i, count;
  //Read the Config
  if(c.stage == 0) {
   //Adding Stage 
   count = 0;
   for(i = 1; i <= 12; i++) {
     count = count + config_self(&c, i) + config_opponent(&c, i);
   }	   
   //If count is less than 5 , crucial state of game go deep 
   if(count < 5) {
     max_depth = 3;
   } 
  } else if (c.stage == 1) {
    //Removing Stage 
    max_depth = 3;	
    count = 0;
    for(i = -15; i <=15 ; i++) {
      if(config_board(&c, i) > 0) {
	count = count + 1;
      }	
    }	
    //Depending on number of remaining weights change your depth 
    if(count < 14) {
      //Crucial Stage go to depth 
      max_depth = 15;
    } else if(count < 22) {
      max_depth = 4;	
    } else {
      printf("0,0\n0\n");	
    }

  }

  //Randomize this depth
  if(max_depth > 1) {
    if(rand() < RAND_MAX/10*DEPTH_RATIO) {
      DEPTH = max_depth - 1;			
    } else {
      DEPTH = max_depth;
    }	
  }
 
  //Make your next move now
  printf("%g\n", prune(&c, -DBL_MAX, DBL_MAX));
  return 0;
}
void setup() {

    // read the cause of the reset
    mcusr=MCUSR;
    MCUSR=0;


    pinMode(STATUS_LED, OUTPUT);
    pinMode(FAULT_LED, OUTPUT);

    digitalWrite(FAULT_LED, HIGH);
    digitalWrite(STATUS_LED, LOW);

    delay(500);

    initBitlash(115200);     // must be first to initialize serial port

    //start serial
    Serial.begin(115200);

    check_reset_cause();

    Serial.print(F("------------ Setup start - "));
    Serial.print(freeMemory());
    Serial.println(F("Ko  ------------"));

    list_debug();
    list_lib_version();
    /* memory allocation in function of the config stored in eeprom */

    Serial.println(F("---------- memory allocation  -----------"));

    if (NumberOfBoardIn8R8)
        IN8R8 = (In8R8*) malloc((NumberOfBoardIn8R8)*sizeof(In8R8));

        Serial.print(F("Create IN8R8: "));
        Serial.println(NumberOfBoardIn8R8);

    if (NumberOfBoardIn16)
        IN16 = (Input16*) malloc((NumberOfBoardIn16)*sizeof(Input16));

        Serial.print(F("Create IN16: "));
        Serial.println(NumberOfBoardIn16);

    if (NumberOfBoardR8)
        R8 = (Relay8*) malloc((NumberOfBoardR8)*sizeof(Relay8));

        Serial.print(F("Create R8: "));
        Serial.println(NumberOfBoardR8);

    if (NumberOfBoardIn4Dimmer4)
        IN4DIM4 = (In4Dimmer4*) malloc((NumberOfBoardIn4Dimmer4)*sizeof(In4Dimmer4));

        Serial.print(F("Create IN4DIM4 :"));
        Serial.println(NumberOfBoardIn4Dimmer4);

    if (NumberOfSwitch)
        SWITCH = (Switch*) malloc((NumberOfSwitch)*sizeof(Switch));

        Serial.print(F("Create SWITCH:"));
        Serial.println(NumberOfSwitch);

    if (NumberOfShutter)
        SHUTTER = (Shutter*) malloc((NumberOfShutter)*sizeof(Shutter));

        Serial.print(F("Create SHUTTER:"));
        Serial.println(NumberOfShutter);

    // create the instances of Lighting
    if (NumberOfLighting)
        LIGHTING = (Lighting*) malloc((NumberOfLighting)*sizeof(Lighting));

        Serial.print(F("Create LIGHTING:"));
        Serial.println(NumberOfLighting);

    // create the instances of Temperature
    if (NumberOfTemp)
        TEMPERATURE = (Temperature*) malloc((NumberOfTemp)*sizeof(Temperature));

        Serial.print(F("Create Temperature:"));
        Serial.println(NumberOfTemp);

    // create the instances of RF Device
    if (NumberOfRFDevice)
        RFDEVICE = (RFDevice*) malloc((NumberOfRFDevice)*sizeof(RFDevice));

        Serial.print(F("Create RFDevice: "));
        Serial.println(NumberOfRFDevice);

    pinMode(int_i2c,INPUT); // configure the pin of the i2c interrupt signal as input
    digitalWrite(int_i2c, HIGH); // active the internal pull-up

    setup_Udp();  // start Ethernet


    clock_setup(); // start pulse generator

    Serial.println(F("------------ setup i2c  ------------"));
    Wire.begin(); // start i2c
    I2c16.begin() ; // start i2c
    I2c16.timeOut(1000); /// à valider, permet de ne pas rester bloquer par une operation i2c
    Serial.println(F("---------- setup i2c end -----------"));


    setup_bitlash(); // setup bitlash

    if(NumberOfTemp>0){
        Serial.println(F("------------ setup DS2482  ------------"));
        DS2482_Init();  //initialize the DS2482 for OneWire
        Serial.println(F("---------- setup DS2482 end -----------"));
        delay(100);
    }
    if(NumberOfRFDevice>0){
        Serial.println(F("------------ setup RF module  ------------"));
    	Setup_RF();
        Serial.println(F("---------- setup RF module end -----------"));
    }     

    /* configuration des cartes */
    /// futurement sur lecture de l'eeprom
    config_board(); // cf custom.ino
    init_instance(); // cf custom.ino

    //~ Serial.println(F("---------- shutter initializ  -----------"));

    /// initialise les volets au démarrage (devrait être optionnel, par config) => desormais on relit l'epprom
    //~ if(NumberOfShutter){
        //~ for(int i=0; i<NumberOfShutter; i++){
            //~ SHUTTER[i].reset();
        //~ }
    //~ }



    digitalWrite(FAULT_LED, LOW);
    digitalWrite(STATUS_LED, HIGH);


    Serial.print(F("------------  Setup end - "));
    Serial.print(freeMemory());
    Serial.println(F("Ko   ------------"));

    main_period_prev_millis=millis();
    main_prev_millis=millis();
    other_prev_millis=millis();

}
double prune(config* c, double alpha, double beta)
{
  config next;
  int weight = INT_MAX, board = INT_MAX;
  double score;
  int i,j;

  if(c->stage == 0) {
   //Adding Stage 
   for(i = 1; i <= 12; i++) {
     //Check if you have this weight 
     if(config_self(c, i) == 1) {
	for(j = -15; j <= 15; j++) {	
	  //Try this board position
	  if(config_board(c, j) == 0) {
	     config_copy(c, &next);	 
	     config_selfPlace(&next, i, j);
	     if(!config_tip(&next)) {	
		
		//This move didn't cause tipping; Extract the score from each of my min nodes; Choose the child with max score
		score = prunemin(&next, 1, alpha, beta);
		if(score > alpha) {
		  alpha = score;
		  weight = i;
		  board = j;
		}
		//If the score was 1.0. Bingo found one.This is called "Solve Optimization over minimax"
 		if(alpha == 1.0) {
                  printf("%d,%d\n",weight ,board);
		  return alpha;
		}
	     }
	  }
 	}
     }
   }
   
   if(weight == INT_MAX && board == INT_MAX) {
     //No optimal answer found; i.e every move caused a tipping . I will loose just choose a placement
     alpha = -1.0;
     for(i = 1; i <= 12; i++) {
        if(config_self(c, 1) == 1) {
           for(j = -15; j <= 15; j++) {
             //Try this board position
             if(config_board(c, j) == 0) {
		weight = i;
		board = j;
		break;
	     } 
           }
	   break;
	}
     }	
   }
   
  } else if (c->stage == 1) {
   
    //Removing Stage
    for(j = -15; j <=15; j++) {
      if(config_board(c,j) > 0) {
	//There exist a weight at this position
        config_copy(c, &next);
	config_remove(&next, j);
	if(!config_tip(&next)) {
	  //Not tipping ; Compute a score for your min childs
	  score = prunemin(&next, 1, alpha, beta);
	  if(score > alpha) {
	    alpha = score;
	    weight = config_board(c, j);
	    board = j;
	  }
	  if(alpha == 1.0) {	 
	    printf("%d,%d\n", weight, board);
	    return alpha;
	  }
	}
      }
    }

    if(weight == INT_MAX && board == INT_MAX) {
     //No optimal answer found; i.e every move caused a tipping . I will loose just choose a placement
      alpha = -1.0;
      for(j = -15; j <= 15; j++) {
         //Try this board position
         if(config_board(c, j) > 0) {
                weight = config_board(c,j);
                board = j;
                break;
          }
        }
      }

  } else {
    return 0.0;
  }

  printf("%d,%d\n",weight,board);
  return alpha;
}
// Max and Min functions will call themselves recursively
double prunemin(config* c, int depth, double alpha, double beta)
{
  if(depth > DEPTH) {
    //Its time to approx at this level 
    return approx(c,0);
  }

  config next;
  double score = 0.0;
  int i,j,p = 0;

  if(c->stage == 0) {
    //Adding Stage      
    p = 0;
    for(i = 1; i <= 12 ; i = i+1) {
      if(config_self(c, i) == 1) {

        //Try this weight at all positions
        for(j = -15; j <= 15; j++) {
           if(config_board(c, j) == 0) {
             //There is no weight at this position
             config_copy(c, &next);
             config_selfPlace(&next, i , j);
             if(!config_tip(&next)) {
                p = 1; //Some Progress

                score = prunemax(c, depth+1, alpha, beta); //Get the scores from all your min childrens and take the max one

                if(score < beta) {
                  //Better score
                  beta = score;
                }

                if(alpha >= beta) {
                  // During the entire game for every node alpha should be less than beta. Remember this prunemax will be called prunemin
                  // and beta is best possible min value move that it could make. so if this alpha will be greater than that i will never 
                  // make this move. So Cut the search space and just return beta 
		  return alpha;
                }
                else if(beta == -1.0) {
                  //Found the best possible score; Return it to your opponent; Remember the goal is assume that your opponent will 
                  // make the best possible move and if this move makes me the winner I won't search further because my opponent will know
                  // this and thereby never make this move
                  return beta;
                }
             }
           }
        }
      }
    }


  } else if (c->stage == 1) {
     // Removing Stage
     p = 0;
    for(j = -15; j <=15; j++) {
      if(config_board(c,j) > 0) {
        //There exist a weight at this position
        config_copy(c, &next);
        config_remove(&next, j);
        if(!config_tip(&next)) {
          //Not tipping ; Compute a score for your min childs
          score = prunemax(&next, depth + 1, alpha, beta);
          if(score < beta) {
            beta = score;
          }

          if(alpha >= beta) {
            return alpha;
          }
          else if(beta == -1.0) {
            return beta;
          }

        }
      }
    }

  } else {
    //Game Ended
    beta = 0.0;
  }

  if(p == 0) {
    //I can't make a move so I loose
    beta = 1.0;
  }

  return beta;

}