2016-06-20 22:55:35 +02:00
< ? php
/*
2016-06-21 17:07:42 +02:00
* Bot for connectfour https :// botsarena . tinad . fr / connectFour
* by Gnieark https :// blog - du - grouik . tinad . fr / june 2016
* GNU GPL License
2016-06-20 22:55:35 +02:00
*/
header ( 'Access-Control-Allow-Origin: *' );
header ( 'Access-Control-Allow-Methods: GET, POST' );
header ( 'Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept' );
2016-06-21 17:07:42 +02:00
header ( 'Content-Type: application/json' );
2016-06-20 22:55:35 +02:00
2016-06-21 16:10:08 +02:00
//connexion mysql
require_once ( " config.php " );
2016-06-23 23:06:13 +02:00
if ( ! $lnMySQL = mysqli_connect ( $config [ 'mysql_host' ], $config [ 'mysql_user' ], $config [ 'mysql_pass' ])) {
2016-06-21 16:10:08 +02:00
error ( 500 , 'database connexion failed' );
die ;
}
mysqli_select_db ( $lnMySQL , $config [ 'mysql_database' ]);
mysqli_set_charset ( $lnMySQL , 'utf8' );
2016-06-24 17:31:22 +02:00
//purge old games
mysqli_query ( $lnMySQL , " DELETE FROM battleship_current WHERE time < (NOW() - INTERVAL 10 MINUTE) " );
2016-06-21 16:10:08 +02:00
2016-06-27 14:27:42 +02:00
$trace = " " ;
2016-06-21 16:10:08 +02:00
function hash_map ( $map , $me , $opponent ){
$hashMap = " " ;
foreach ( $map as $line ){
foreach ( $line as $cell ){
switch ( $cell ){
case $me :
$hashMap .= " 1 " ;
break ;
2016-06-21 17:07:42 +02:00
case $opponent :
2016-06-21 16:10:08 +02:00
$hashMap .= " 2 " ;
break ;
default :
$hashMap .= " 0 " ;
break ;
}
}
}
2016-06-24 17:00:52 +02:00
return base_convert ( $hashMap , 3 , 16 );
2016-06-21 16:10:08 +02:00
}
2016-06-23 23:56:18 +02:00
function remenber_previous_lap_is_a_bullshit (){
2016-06-27 14:43:14 +02:00
global $params , $lnMySQL , $trace ;
$trace .= " |I Learn it " ;
2016-06-23 23:56:18 +02:00
mysqli_query ( $lnMySQL ,
" INSERT INTO battleshipLearn(map, dont_play_col)
SELECT battleship_current . map , battleship_current . play_at
FROM battleship_current
WHERE battleship_current . game_id = '".mysqli_real_escape_string($lnMySQL,$params[' game - id '])."'
AND battleship_current . player_index = '".mysqli_real_escape_string($lnMySQL,$params[' player - index '])."' ; "
);
2016-06-21 16:10:08 +02:00
2016-06-23 23:56:18 +02:00
}
2016-06-21 16:10:08 +02:00
2016-06-21 17:07:42 +02:00
function play ( $map , $colToPlay , $me , $opponent , $gameid , $player_index ){
2016-06-27 14:27:42 +02:00
global $lnMySQL , $trace ;
2016-06-21 16:10:08 +02:00
2016-06-21 17:07:42 +02:00
//save the lap on the database and then send the play response
2016-06-23 23:10:44 +02:00
mysqli_query ( $lnMySQL ,
2016-06-21 17:07:42 +02:00
" INSERT INTO battleship_current (game_id,player_index,map,play_at) VALUES
( '".mysqli_real_escape_string($lnMySQL,$gameid)."' ,
'".mysqli_real_escape_string($lnMySQL,$player_index)."' ,
'".hash_map($map,$me,$opponent)."' ,
'".$colToPlay."' )
ON DUPLICATE KEY UPDATE
map = '".hash_map($map,$me,$opponent)."' ,
play_at = '".$colToPlay."' ; " );
2016-06-27 14:27:42 +02:00
echo '{"play":' . $colToPlay . ',"trace":"' . $trace . '"}' ;
2016-06-21 17:07:42 +02:00
die ;
2016-06-21 16:10:08 +02:00
}
2016-06-20 22:55:35 +02:00
function can_win ( $line , $myChar , $depth = 0 ){
//retourne la position du caractere a remplacer dans la ligne pour gagner
$arr = array ();
if ( $depth == 0 ){
if ( strpos ( $line , " + " . $myChar . $myChar . $myChar ) !== false ){
$arr [] = strpos ( $line , " + " . $myChar . $myChar . $myChar );
}
if ( strpos ( $line , $myChar . " + " . $myChar . $myChar ) !== false ){
$arr [] = strpos ( $line , $myChar . " + " . $myChar . $myChar ) + 1 ;
}
if ( strpos ( $line , $myChar . $myChar . " + " . $myChar ) !== false ){
$arr [] = strpos ( $line , $myChar . $myChar . " + " . $myChar ) + 2 ;
}
if ( strpos ( $line , $myChar . $myChar . $myChar . " + " ) !== false ){
$arr [] = strpos ( $line , $myChar . $myChar . $myChar . " + " ) + 3 ;
}
} else {
if ( strpos ( $line , " + " . $myChar . $myChar . " + " ) !== false ){
$arr [] = strpos ( $line , " + " . $myChar . $myChar . " + " );
$arr [] = strpos ( $line , " + " . $myChar . $myChar . " + " ) + 3 ;
}
if ( strpos ( $line , " + " . $myChar . " + " . $myChar ) !== false ){
$arr [] = strpos ( $line , " + " . $myChar . " + " . $myChar );
$arr [] = strpos ( $line , " + " . $myChar . " + " . $myChar ) + 2 ;
}
if ( strpos ( $line , $myChar . " + " . $myChar . " + " ) !== false ){
$arr [] = strpos ( $line , $myChar . " + " . $myChar . " + " ) + 1 ;
$arr [] = strpos ( $line , $myChar . " + " . $myChar . " + " ) + 3 ;
}
}
return $arr ;
}
function can_loose ( $line , $hisChar , $depth = 0 ){
//je pourrai perdre aux 2 prochains tours de jeu
// retourne la place du caractere à remplacer pour éviter ça
$arr = array ();
if ( $depth == 0 ){
if ( strpos ( $line , " + " . $hisChar . $hisChar . $hisChar ) !== false ){
$arr [] = strpos ( $line , " + " . $hisChar . $hisChar . $hisChar );
}
if ( strpos ( $line , $hisChar . " + " . $hisChar . $hisChar ) !== false ){
$arr [] = strpos ( $line , $hisChar . " + " . $hisChar . $hisChar ) + 1 ;
}
if ( strpos ( $line , $hisChar . $hisChar . " + " . $hisChar ) !== false ){
$arr [] = strpos ( $line , $hisChar . $hisChar . " + " . $hisChar ) + 2 ;
}
if ( strpos ( $line , $hisChar . $hisChar . $hisChar . " + " ) !== false ){
$arr [] = strpos ( $line , $hisChar . $hisChar . $hisChar . " + " ) + 3 ;
}
} else {
if ( strpos ( $line , " + " . $hisChar . $hisChar . " + " ) !== false ){
$arr [] = strpos ( $line , " + " . $hisChar . $hisChar . " + " );
$arr [] = strpos ( $line , " + " . $hisChar . $hisChar . " + " ) + 3 ;
}
if ( strpos ( $line , " + " . $hisChar . " + " . $hisChar . " + " ) !== false ){
$arr [] = strpos ( $line , " + " . $hisChar . " + " . $hisChar . " + " );
$arr [] = strpos ( $line , " + " . $hisChar . " + " . $hisChar . " + " ) + 2 ;
$arr [] = strpos ( $line , " + " . $hisChar . " + " . $hisChar . " + " ) + 4 ;
}
}
return $arr ;
}
function array_merge_and_increment_rigth ( $arr1 , $arr2 , $incr ){
foreach ( $arr2 as $v ){
$arr1 [] = $v + $incr ;
}
return $arr1 ;
}
function analize ( $line , $me , $opponent , $isVertical , $decalageX ){
/*
* Etudie les lignes fournies
* Joue si une case est gagnante
* Sinon , " peuple " des variables
* qui permettront de prendre une décision
*/
static $colForNoLose = array ();
static $colForNoLose1 = array ();
static $canWinDepth1 = array ();
2016-06-23 22:49:05 +02:00
global $params ;
2016-06-20 22:55:35 +02:00
if ( count ( can_win ( $line , $me , 0 )) > 0 ){
if ( $isVertical ){
2016-06-23 22:49:05 +02:00
play ( $params [ 'board' ], $decalageX , $me , $opponent , $params [ 'game-id' ], $params [ 'player-index' ]);
2016-06-20 22:55:35 +02:00
} else {
2016-06-23 22:49:05 +02:00
play ( $params [ 'board' ],( can_win ( $line , $me , 0 )[ 0 ] + $decalageX ), $me , $opponent , $params [ 'game-id' ], $params [ 'player-index' ]);
2016-06-20 22:55:35 +02:00
}
die ;
}
if ( count ( can_loose ( $line , $opponent , 0 )) > 0 ){
if ( $isVertical ){
$colForNoLose [] = $decalageX ;
} else {
$colForNoLose = array_merge_and_increment_rigth ( $colForNoLose , can_loose ( $line , $opponent , 0 ), $decalageX );
}
}
if ( count ( can_loose ( $line , $opponent , 1 )) > 0 ){
if ( $isVertical ){
$colForNoLose1 [] = $decalageX ;
} else {
$colForNoLose1 = array_merge_and_increment_rigth ( $colForNoLose1 , can_loose ( $line , $opponent , 1 ), $decalageX );
}
}
if ( count ( can_win ( $line , $opponent , 1 )) > 0 ){
if ( $isVertical ){
$canWinDepth1 [] = $decalageX ;
} else {
$canWinDepth1 = array_merge_and_increment_rigth ( $canWinDepth1 , can_win ( $line , $me , 1 ), $decalageX );
}
}
return array (
'colForNoLose' => $colForNoLose ,
'colForNoLose1' => $colForNoLose1 ,
'canWinDepth1' => $canWinDepth1
);
}
function should_opponent_win_if_i_play_at ( $map , $me , $opponent , $colToPlay ){
2016-06-27 09:36:35 +02:00
//j'ouvre l'a possibilité à l'adversaire de jouer au dessus de mon pion
// est-ce une connerie?
if (( $map [ 4 ][ $colToPlay ] == $me ) OR ( $map [ 4 ][ $colToPlay ] == $opponent )){
//top of the grid
return false ;
}
for ( $y = 0 ; (( $map [ $y ][ $colToPlay ] <> " + " ) && ( $map [ $y ][ $colToPlay ] <> " - " )); $y ++ ){
}
$map [ $y ][ $colToPlay ] = $me ;
$map [ $y + 1 ][ $colToPlay ] = " $opponent " ;
$y ++ ;
if ( isset ( $map [ $y + 1 ][ $colToPlay ])){
$map [ $y + 1 ][ $colToPlay ] = " + " ;
}
//tester les lignes qui passent pas $y+1,$colToPlay
$loseStr = $opponent . $opponent . $opponent . $opponent ;
//horizontale
$line = " " ;
for ( $x = 0 ; $x < 7 ; $x ++ ){
$line .= $map [ $y ][ $x ];
}
if ( strpos ( $line , $loseStr ) !== false ){
return true ;
}
//diagonal /
$line = " " ;
if ( $colToPlay > $y ){
$kx = $colToPlay - $y ;
$ky = 0 ;
} else {
$kx = 0 ;
$ky = $y - $colToPlay ;
}
while ( isset ( $map [ $ky ][ $kx ])){
$line .= $map [ $ky ][ $kx ];
$kx ++ ;
$ky ++ ;
}
if ( strpos ( $line , $loseStr ) !== false ){
return true ;
}
//diagional \
$line = " " ;
$kx = $colToPlay ;
$ky = $y ;
while ( isset ( $map [ $ky - 1 ][ $kx + 1 ])){
$kx ++ ;
$ky -- ;
}
2016-06-20 22:55:35 +02:00
2016-06-27 09:36:35 +02:00
while ( isset ( $map [ $ky ][ $kx ])){
$line .= $map [ $ky ][ $kx ];
$kx -- ;
$ky ++ ;
}
if ( strpos ( $line , $loseStr ) !== false ){
return true ;
}
return false ;
2016-06-20 22:55:35 +02:00
}
//replace "" by " ", it will simplify my code.
$in = str_replace ( '""' , '"-"' , file_get_contents ( 'php://input' ));
$params = json_decode ( $in , TRUE );
switch ( $params [ 'action' ]){
case " init " :
echo '{"name":"Gnieark"}' ;
break ;
case " play-turn " :
//find $opponent and clean grid
for ( $x = 0 ; $x < 7 ; $x ++ ){
for ( $y = 0 ; $y < 6 ; $y ++ ){
//find opponent
if (( $params [ 'board' ][ $y ][ $x ] <> " - " ) && ( $params [ 'board' ][ $y ][ $x ] <> $params [ 'you' ] )){
$opponent = $params [ 'board' ][ $y ][ $x ];
}
//tester si la case est jouable (s'il y a un support en dessous)
if ( $params [ 'board' ][ $y ][ $x ] == " - " ){
//AND (($y==0) OR ($params['board'][$y - 1][$x] !== "-"))
//){
//la case est jouable, je la marque par un "+"
if ( $y == 0 ){
$params [ 'board' ][ $y ][ $x ] = " + " ;
} elseif (( $params [ 'board' ][ $y - 1 ][ $x ] !== " - " ) AND ( $params [ 'board' ][ $y - 1 ][ $x ] !== " + " )){
$params [ 'board' ][ $y ][ $x ] = " + " ;
} else {}
}
}
}
if (( ! isset ( $opponent )) && ( $params [ 'you' ] == " X " )){
$opponent = " O " ;
} elseif ( ! isset ( $opponent )){
$opponent = " X " ;
}
//transformer la grille en lignes horizontales, verticales et diagonales
2016-06-21 16:10:08 +02:00
2016-06-20 22:55:35 +02:00
//verticales
for ( $x = 0 ; $x < 7 ; $x ++ ){
$colStr = " " ;
for ( $y = 0 ; $y < 6 ; $y ++ ){
$colStr .= $params [ 'board' ][ $y ][ $x ];
}
$choice = analize ( $colStr , $params [ 'you' ], $opponent , true , $x );
}
//horizontales
for ( $y = 0 ; $y < 6 ; $y ++ ){
$lnStr = " " ;
for ( $x = 0 ; $x < 7 ; $x ++ ){
$lnStr .= $params [ 'board' ][ $y ][ $x ];
}
$choice = analize ( $lnStr , $params [ 'you' ], $opponent , false , 0 );
}
//tester seulement les diagonales >= 4 cases
for ( $k = 0 ; $k < 4 ; $k ++ ){
//diagonale /
$diagStr = " " ;
for ( $x = $k , $y = 0 ; isset ( $params [ 'board' ][ $y ][ $x ]); $x ++ , $y ++ ){
$diagStr .= $params [ 'board' ][ $y ][ $x ];
}
$choice = analize ( $diagStr , $params [ 'you' ], $opponent , false , $k );
//diagonale \
$diagStr = " " ;
for ( $x = $k , $y = 5 ; isset ( $params [ 'board' ][ $y ][ $x ]); $x ++ , $y -- ){
$diagStr .= $params [ 'board' ][ $y ][ $x ];
}
$choice = analize ( $diagStr , $params [ 'you' ], $opponent , false , $k );
}
for ( $k = 0 ; $k < 3 ; $k ++ ){
//diagonale /
$diagStr = " " ;
for ( $x = 0 , $y = $k ; isset ( $params [ 'board' ][ $y ][ $x ]); $x ++ , $y ++ ){
$diagStr .= $params [ 'board' ][ $y ][ $x ];
}
$choice = analize ( $diagStr , $params [ 'you' ], $opponent , false , 0 );
}
for ( $k = 3 ; $k < 6 ; $k ++ ){
//diagonales \
$diagStr = " " ;
for ( $x = 0 , $y = $k ; isset ( $params [ 'board' ][ $y ][ $x ]); $x ++ , $y -- ){
$diagStr .= $params [ 'board' ][ $y ][ $x ];
}
$choice = analize ( $diagStr , $params [ 'you' ], $opponent , false , 0 );
}
2016-06-21 16:10:08 +02:00
2016-06-20 22:55:35 +02:00
//si j'arrive là, je ne gagne pas à ce tour
//liste des cases possible moins celles à éviter
2016-06-24 17:31:22 +02:00
//Est-ce que cette combinaison est sauvegardée comme perdante?
$rs = mysqli_query ( $lnMySQL , " SELECT dont_play_col FROM battleshipLearn WHERE map=' " . hash_map ( $params [ 'board' ], $params [ 'you' ], $opponent ) . " ' " );
$learnedCells = array ();
while ( $r = mysqli_fetch_row ( $rs )){
$learnedCells [] = $r [ 0 ];
}
2016-06-27 14:27:42 +02:00
2016-06-20 22:55:35 +02:00
$colAvailable = array ();
for ( $i = 0 ; $i < 7 ; $i ++ ){
if ((( $params [ 'board' ][ 5 ][ $i ] == " + " ) OR ( $params [ 'board' ][ 5 ][ $i ] == " - " ))
2016-06-24 17:31:22 +02:00
AND ( ! should_opponent_win_if_i_play_at ( $params [ 'board' ], $params [ 'you' ], $opponent , $i ))
AND ( ! in_array ( $i , $learnedCells )))
2016-06-20 22:55:35 +02:00
{
$colAvailable [] = $i ;
}
}
if ( count ( $colAvailable ) == 0 ){
2016-06-27 14:45:12 +02:00
$trace .= " |no colsAvailable " ;
2016-06-20 22:55:35 +02:00
//on risque de perdre au prochain tour
2016-06-23 23:56:18 +02:00
remenber_previous_lap_is_a_bullshit ();
2016-06-20 22:55:35 +02:00
for ( $i = 0 ; $i < 7 ; $i ++ ){
if (( $params [ 'board' ][ 5 ][ $i ] == " + " ) OR ( $params [ 'board' ][ 5 ][ $i ] == " - " )){
$colAvailable [] = $i ;
}
}
}
2016-06-27 09:36:35 +02:00
if ( count ( array_unique ( $choice [ 'colForNoLose' ])) > 1 ){
//opponent has to places to win
remenber_previous_lap_is_a_bullshit ();
}
2016-06-27 14:40:23 +02:00
$trace .= " |learnedCells: " . implode ( " , " , $learnedCells ) . " |colAva: " . implode ( " , " , $colAvailable ) . " |colforNoLose: " . implode ( " , " , $choice [ 'colForNoLose' ]) . " |col for no loose depth1: " . implode ( " , " , $choice [ 'colForNoLose1' ]);
2016-06-21 17:07:42 +02:00
if ( count ( $choice [ 'colForNoLose' ]) > 0 ){
2016-06-20 22:55:35 +02:00
//intersection entre $choice['colForNoLose'] et $colAvailable
$intersection = array_intersect ( $choice [ 'colForNoLose' ], $colAvailable );
if ( count ( $intersection ) > 0 ){
shuffle ( $intersection );
2016-06-23 22:49:05 +02:00
play ( $params [ 'board' ], $intersection [ 0 ], $params [ 'you' ], $opponent , $params [ 'game-id' ], $params [ 'player-index' ]);
2016-06-20 22:55:35 +02:00
die ;
} else {
//on pourra perdre au prochain tour, tant pis
2016-06-23 23:56:18 +02:00
remenber_previous_lap_is_a_bullshit ();
2016-06-20 22:55:35 +02:00
shuffle ( $choice [ 'colForNoLose' ]);
2016-06-23 22:49:05 +02:00
play ( $params [ 'board' ], $choice [ 'colForNoLose' ][ 0 ], $params [ 'you' ], $opponent , $params [ 'game-id' ], $params [ 'player-index' ]);
2016-06-20 22:55:35 +02:00
die ;
}
}
2016-06-21 17:07:42 +02:00
2016-06-20 22:55:35 +02:00
$colForNoLose1 = array_unique ( $choice [ 'colForNoLose1' ]);
$canWinDepth1 = array_unique ( $choice [ 'canWinDepth1' ]);
$intersection = array_intersect ( $colForNoLose1 , $colAvailable , $canWinDepth1 );
if ( count ( $intersection ) > 0 ){
shuffle ( $intersection );
2016-06-23 22:49:05 +02:00
play ( $params [ 'board' ], $intersection [ 0 ], $params [ 'you' ], $opponent , $params [ 'game-id' ], $params [ 'player-index' ]);
2016-06-20 22:55:35 +02:00
die ;
}
$intersection = array_intersect ( $colForNoLose1 , $colAvailable );
if ( count ( $intersection ) > 0 ){
shuffle ( $intersection );
2016-06-23 22:49:05 +02:00
play ( $params [ 'board' ], $intersection [ 0 ], $params [ 'you' ], $opponent , $params [ 'game-id' ], $params [ 'player-index' ]);
2016-06-20 22:55:35 +02:00
die ;
}
$intersection = array_intersect ( $colForNoLose1 , $canWinDepth1 );
if ( count ( $intersection ) > 0 ){
shuffle ( $intersection );
2016-06-23 22:49:05 +02:00
play ( $params [ 'board' ], $intersection [ 0 ], $params [ 'you' ], $opponent , $params [ 'game-id' ], $params [ 'player-index' ]);
2016-06-20 22:55:35 +02:00
die ;
}
//still there? random
shuffle ( $colAvailable );
2016-06-23 22:49:05 +02:00
play ( $params [ 'board' ], $colAvailable [ 0 ], $params [ 'you' ], $opponent , $params [ 'game-id' ], $params [ 'player-index' ]);
2016-06-20 22:55:35 +02:00
break ;
default :
break ;
2016-06-23 23:06:13 +02:00
}