Php 递归函数无限循环

Php 递归函数无限循环,php,recursion,artificial-intelligence,Php,Recursion,Artificial Intelligence,我正在做一个个人项目,一个玩connect 4的机器人。因此,我编写的用于管理机器人移动的递归函数出现了严重错误。不会抛出错误,并且显示的任何调试信息都不会告诉我任何有用的信息。此外,我确信我的堆栈没有溢出,我的php.ini文件很好用。这个脚本只是运行(消耗了大量内存),永远不会返回。应该只发生大约2400个函数调用,所以这个脚本应该在一两秒钟后返回。这件事困扰了我好几天。我相信有些东西我还没有好好研究过。另外,我应该提到的是,游戏板是一个简单的2D阵列,用于模拟7 x 7板ai_move_h

我正在做一个个人项目,一个玩connect 4的机器人。因此,我编写的用于管理机器人移动的递归函数出现了严重错误。不会抛出错误,并且显示的任何调试信息都不会告诉我任何有用的信息。此外,我确信我的堆栈没有溢出,我的php.ini文件很好用。这个脚本只是运行(消耗了大量内存),永远不会返回。应该只发生大约2400个函数调用,所以这个脚本应该在一两秒钟后返回。这件事困扰了我好几天。我相信有些东西我还没有好好研究过。另外,我应该提到的是,游戏板是一个简单的2D阵列,用于模拟7 x 7板
ai_move_helper
是递归函数,我无法理解它为什么不能正常工作

// this class is a code igniter library
class ConnectFour
{
    public function __construct()
    {
        // these are expensive opperations this will give the server enough time
        set_time_limit(0);
        ini_set('memory_limit', '2048M');
    }

    public $board_width     = 7;
    public $board_length    = 7;
    public $game_array      = array();
    public $depth_limit     = 3;

            // this function gets a human made move from a CI controller ($game board)
            // and returns the board after the new AI move is applied
    public function ai_player_move($game_array, $active_players_move)
    {
        $this->game_array = $game_array;            
        $this->game_array = $this->calculate_ai_move($active_players_move);
        return $this->game_array;   
    }

    public function calculate_ai_move($active_players_move)
    {
        $move_weight_array = array();
        $prime_game_board = array();

        // we hardcast the active player because at this point we know it is the AI
        // here we also have to prime the move computer
        for($q = 0; $q < $this->board_length; $q++)
        {
            // MAGIC NUMBERS are the active players!!!
            $prime_game_board[] = $this->apply_prime_move($q, 2, $this->game_array);

            $move_weight_array[] = $this->ai_move_helper($prime_game_board[$q], 2, 0);  
        }

        //choose your move
        for($u = 0; $u < $this->board_length; $u)
        {
            if($move_weight_array[$u][0] == 1)
            {
                return $prime_game_board[$u];               
            }
        }

        // otherwise return a random acceptable move
        $random = rand(0, 6);
        return $prime_game_board[$random];

    }

    public function ai_move_helper($game_board, $active_player, $depth)
    {
        // build the object that will be needed at this level of recusion
        $depth = $depth + 1;
        $score_object                               = new stdClass;
        $move_array                                 = array();
        $game_boards_generated_at_this_level        = array(); 
        $new_game_boards_generated_at_this_level    = array();
        $return_end_state_detected                  = 0;
        $score_agregate                             = array();          

        if($this->depth_limit < $depth)
        {
            $score_agregate[0] = 0; 
            $score_agregate[1] = 0;
            return $score_agregate; 
        }

        $active_player = ($active_player == 1) ? 2 : 1;

        // check for possible moves
        for($i=0; $i < $this->board_width; $i++)
        {
            // calculate all of the possible recusions (all of the next moves)
            $game_boards_generated_at_this_level[$i] = $this->apply_ai_move($i, $active_player, $game_board);
            // this is the recusive level
            $score_agregate = $this->ai_move_helper($game_boards_generated_at_this_level[$i]->game_board, $active_player, $depth);              
        }

        // check to see if there are more moves of if it is time to return
        foreach($game_boards_generated_at_this_level as $game_state)
        {
            //compute the agragate of the scores only for player two (AI)
            if($active_player == 2)
            {
                $score_agregate[0] = $score_agregate[0] + $game_state->score_array[0]; 
                $score_agregate[1] = $score_agregate[1] + $game_state->score_array[1];              
            }
        }

        return $score_agregate;
    }

    public function apply_ai_move($move, $active_players_move, $board_to_use)
    {
        $board_for_function     = array();
        $location_of_new_pieces = 0;
        $return_object          = new stdClass;

        // this makes sure that this function is being called with the right board
        if(!empty($board_to_use))
        {
            $board_for_function = $board_to_use;
        } else {
            $board_for_function = $this->game_array;
        }

        // check that this move is possible
        if(!$this->move_possible($move, $board_for_function))
        {
            $return_object->game_board      = NULL;
            $return_object->score_array     = NULL;
            return $return_object;
        }

        // this part of the function applies a valid move
        foreach($board_for_function[$move] as $column_key => $column_space)
        {
            // check if you are at the edge of an empty row
            if(!array_key_exists(($location_of_new_pieces + 1), $board_for_function[$move]) && $column_space == '_')
            {
                $board_for_function[$move][$location_of_new_pieces] = ($active_players_move == 1) ? 'x' : 'o';
                break;
            }

            // check if the next place has stuff in it too
            if($column_space != '_')
            {
                // check the edge of the board to make sure that exists
                if(array_key_exists(($location_of_new_pieces - 1), $board_for_function))
                {
                    $board_for_function[$move][$location_of_new_pieces - 1] = ($active_players_move == 1) ? 'x' : 'o';
                    break;
                } else {
                    echo "well fuck...1"; exit;
                }
            }

            $location_of_new_pieces++;
        }

        $return_object->game_board = $board_for_function;

        // now check if this state is a win loss or draw
        $test_for_complete = $this->check_for_winner_or_draw($board_for_function, $active_players_move);

        // this is a draw
        if($test_for_complete == -1)
        {
            $return_object->score_array = array(0, 1);
        } else if($test_for_complete > 3) {
            $return_object->score_array = array(1, 0);
        } else {
            $return_object->score_array = array(0, 0);
        }

        return $return_object;
    }

    public function apply_prime_move($move, $active_players_move, $board_to_use)
    {
        $location_of_new_pieces = 0;

        foreach($board_to_use[$move] as $column_key => $column_space)
        {
            // check if you are at the edge of an empty row
            if(!array_key_exists(($location_of_new_pieces + 1), $board_to_use[$move]) && $column_space == '_')
            {
                $board_to_use[$move][$location_of_new_pieces] = ($active_players_move == 1) ? 'x' : 'o';
                break;
            }

            // check if the next place has stuff in it too
            if($column_space != '_')
            {
                // check the edge of the board to make sure that exists
                if(array_key_exists(($location_of_new_pieces - 1), $board_to_use))
                {
                    $board_to_use[$move][$location_of_new_pieces - 1] = ($active_players_move == 1) ? 'x' : 'o';
                    break;
                } else {
                    echo "well fuck...1"; exit;
                }
            }

            $location_of_new_pieces++;
        }

        return $board_to_use;
    }

    public function move_possible($move, $game_board)
    {
        // check that this move is not going to fall out of the board
        if($game_board[$move][0] != "_")
        {
            return FALSE;
        } else {
            return TRUE;
        }
    }

    public function check_for_winner_or_draw($game_array, $active_player_move)
    {
        $present_possible_winner    = "";
        $count_to_win               = 0;
        $game_not_a_draw            = FALSE;

        for($i = 0; $i < $this->board_length; $i++)
        {
            for($j = 0; $j < $this->board_width; $j++)
            {
                // start checking for a winner
                if($game_array[$i][$j] != "_")
                {
                    $present_possible_winner = $game_array[$i][$j]; 

                    // check for a winner horizontally
                    for($x = 0; $x < 4; $x++)
                    {
                        if($j+$x < $this->board_width)
                        {
                            if($game_array[$i][$j+$x] == $present_possible_winner)
                            {
                                $count_to_win = $count_to_win + 1;
                            }
                        }
                    }

                    if($count_to_win > 3)
                    {
                        return $present_possible_winner;    // this player has won
                    } else {
                        $count_to_win = 0;
                    }

                    // check for a winner horizontally
                    for($y = 0; $y < 4; $y++)
                    {
                        if($i+$y < $this->board_width)
                        {
                            if($game_array[$i+$y][$j] == $present_possible_winner)
                            {
                                $count_to_win = $count_to_win + 1;
                            }
                        }
                    }

                    if($count_to_win > 3)
                    {
                        return $present_possible_winner;    // this player has won
                    } else {
                        $count_to_win = 0;
                    }

                    // check for a winner up to down diagonal
                    for($z = 0; $z < 4; $z++)
                    {
                        if(($i+$z < $this->board_width) && ($j+$z < $this->board_length))
                        {
                            if($game_array[$i+$z][$j+$z] == $present_possible_winner)
                            {
                                $count_to_win = $count_to_win + 1;
                            }
                        }
                    }

                    if($count_to_win > 3)
                    {
                        return $present_possible_winner;    // this player has won
                    } else {
                        $count_to_win = 0;
                    }

                    // check for a winner down to up diagonal
                    for($w = 0; $w < 4; $w++)
                    {
                        if(($i+$w < $this->board_width) && ($j-$w >= 0))
                        {
                            if($game_array[$i+$w][$j-$w] == $present_possible_winner)
                            {
                                $count_to_win = $count_to_win + 1;
                            }
                        }
                    }

                    if($count_to_win > 3)
                    {
                        return $present_possible_winner;    // this player has won
                    } else {
                        $count_to_win = 0;
                    }
                }
            }
        }


        // check for a drawed game and return accordingly
        for($i = 0; $i < $this->board_length; $i++)
        {
            for($j = 0; $j < $this->board_width; $j++)
            {
                if($game_array[$i][$j] == "_")
                {
                    $game_not_a_draw = TRUE;
                }
            }
        }

        if(!$game_not_a_draw)
        {
            return -1;
        }

        return 0;
    }


            // this is a private debugging function that I wrote for this script
    public function debug($value = NULL, $name = NULL, $exit = NULL)
    {
        if(!empty($name))
        {
            echo $name . "<br />";              
        }
        echo "<pre>";
        var_dump($value);
        echo "</pre>";

        if($exit)
        {
            exit;
        }
    }

}
//此类是一个代码点火器库
第四类
{
公共函数构造()
{
//这些都是昂贵的操作,这将给服务器足够的时间
设置时间限制(0);
ini设置(“内存限制”,“2048M”);
}
公共$board_宽度=7;
公共$board_长度=7;
public$game_array=array();
公共$depth_limit=3;
//此函数从CI控制器($game board)获取人工移动
//并在应用新AI移动后返回板
公共功能ai玩家移动($game阵列,$active玩家移动)
{
$this->game\u array=$game\u array;
$this->game\u array=$this->calculate\u ai\u move($active\u players\u move);
返回$this->game\u数组;
}
公共功能计算ai移动($active\u players\u move)
{
$move_weight_array=array();
$prime_game_board=array();
//我们硬选了主动玩家,因为在这一点上,我们知道它是人工智能
//在这里,我们还必须启动移动计算机
对于($q=0;$q<$this->board_length;$q++)
{
//魔术数字是活跃的玩家!!!
$prime_game_board[]=$this->apply_prime_move($q,2,$this->game_array);
$move\u weight\u array[]=$this->ai\u move\u helper($prime\u game\u board[$q],2,0);
}
//选择你的行动
对于($u=0;$u<$this->board_length;$u)
{
如果($move_weight_array[$u][0]==1)
{
返回$prime_game_board[$u];
}
}
//否则返回一个可接受的随机移动
$random=rand(0,6);
返回$prime_game_board[$random];
}
公共功能ai_移动_助手($game_board,$active_player,$depth)
{
//构建此重现级别所需的对象
$depth=$depth+1;
$score_object=新的stdClass;
$move_array=array();
$game_boards_在_此_级别=数组()生成的_;
$new_game_boards_在_this_level=array()生成的_;
$return\u end\u state\u detected=0;
$score_agregate=array();
如果($this->depth\u limit<$depth)
{
$score_agregate[0]=0;
$score_agregate[1]=0;
返回$score_agegate;
}
$active_player=($active_player==1)?2:1;
//检查可能的移动
对于($i=0;$i<$this->board_width;$i++)
{
//计算所有可能的重复(所有下一步动作)
$game\u boards\u在\u this\u级别生成的\u[$i]=$this->apply\u ai\u move($i,$active\u player,$game\u board);
//这是复发级别
$score\u agregate=$this->ai\u move\u helper($game\u boards\u generated\u at\u this\u level[$i]->game\u board$active\u player$depth);
}
//检查是否有更多的动作,如果是时候返回
foreach($game\u boards\u在这个级别生成的\u作为$game\u state)
{
//仅为玩家2(AI)计算分数的agragate
如果($active_player==2)
{
$score_agregate[0]=$score_agregate[0]+$game_state->score_array[0];
$score_agregate[1]=$score_agregate[1]+$game_state->score_array[1];
}
}
返回$score_agegate;
}
公共功能应用移动($move,$active,$player,$move,$board,to,use)
{
$board_for_函数=数组();
$location\u of_new\u片=0;
$return\u object=新的stdClass;
//这样可以确保使用右板调用此函数
如果(!空($board_to_use))
{
$board_for_function=$board_to_use;
}否则{
$board\u for_function=$this->game\u数组;
}
//检查此移动是否可行
如果(!$this->move_可能($move,$board_用于_功能))
{
$return\u object->game\u board=NULL;
$return\u object->score\u array=NULL;
return$return\u对象;
}
//函数的这一部分应用有效移动
foreach($column\u key=>$column\u space的函数[$move]的board\u)
{
//检查您是否位于空行的边缘
如果(!array_key_)存在(($location_of_new_fixes+1),$board_for_function[$move])&&&$column_space==''''
{
$board_for_function[$move][$location_of_new_parties]=($active_players_move==1)?'x':'o';
打破
}
//检查下一个地方是否也有东西
if($column\u space!='\u'))
{
//检查电路板的边缘,确保其存在
如果(数组\u键\u存在($location\u of_new\u片段-1,$board\u for_函数))
{
$board_用于_函数[$move][$lo
 //choose your move
    for($u = 0; $u < $this->board_length; $u)
    {
        if($move_weight_array[$u][0] == 1)
        {
            return $prime_game_board[$u];               
        }
    }
 //choose your move
    for($u = 0; $u < $this->board_length; $u++)
    {
        if($move_weight_array[$u][0] == 1)
        {
            return $prime_game_board[$u];               
        }
    }