我正在进行一个个人项目,一个玩四子棋的机器人。所以我编写的管理机器人移动的递归函数出现了严重问题。没有抛出任何错误,我能看到的任何调试信息也没有告诉我任何有用的东西。此外,我确定我没有溢出堆栈,我的php.ini文件也设置正确。这个脚本只是运行(消耗合理的内存量)并且永远不会返回。应该只发生大约2400次函数调用,所以这个脚本应该在一两秒内返回。这让我困惑了好几天。我认为有些东西我没有充分研究。另外,我应该提到游戏棋盘是一个简单的二维数组,用来模拟一个7×7的棋盘。ai_move_helper
是递归函数,我就是搞不明白为什么它不能正常工作。
// 这个类是一个CodeIgniter库class ConnectFour{ public function __construct() { // 这些是昂贵的操作,这将给服务器足够的时间 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; // 这个函数从CI控制器获取人类的移动($game board) // 并返回应用新AI移动后的棋盘 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(); // 我们硬编码活跃玩家,因为此时我们知道是AI // 这里我们还需要启动移动计算器 for($q = 0; $q < $this->board_length; $q++) { // MAGIC NUMBERS是活跃玩家!!! $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); } //选择你的移动 for($u = 0; $u < $this->board_length; $u) { if($move_weight_array[$u][0] == 1) { return $prime_game_board[$u]; } } // 否则返回一个随机的可接受移动 $random = rand(0, 6); return $prime_game_board[$random]; } public function ai_move_helper($game_board, $active_player, $depth) { // 构建在这个递归层级需要的对象 $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; // 检查可能的移动 for($i=0; $i < $this->board_width; $i++) { // 计算所有可能的递归(所有下一步移动) $game_boards_generated_at_this_level[$i] = $this->apply_ai_move($i, $active_player, $game_board); // 这是递归层级 $score_agregate = $this->ai_move_helper($game_boards_generated_at_this_level[$i]->game_board, $active_player, $depth); } // 检查是否有更多移动或者是否是返回的时候 foreach($game_boards_generated_at_this_level as $game_state) { //只为玩家二(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; // 这确保了这个函数是以正确的棋盘被调用 if(!empty($board_to_use)) { $board_for_function = $board_to_use; } else { $board_for_function = $this->game_array; } // 检查这个移动是否可能 if(!$this->move_possible($move, $board_for_function)) { $return_object->game_board = NULL; $return_object->score_array = NULL; return $return_object; } // 这个函数的一部分应用一个有效的移动 foreach($board_for_function[$move] as $column_key => $column_space) { // 检查是否在空行的边缘 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; } // 检查下一个位置是否也有东西 if($column_space != '_') { // 检查棋盘的边缘以确保存在 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; // 现在检查这个状态是否是赢、输或平局 $test_for_complete = $this->check_for_winner_or_draw($board_for_function, $active_players_move); // 这是平局 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) { // 检查是否在空行的边缘 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; } // 检查下一个位置是否也有东西 if($column_space != '_') { // 检查棋盘的边缘以确保存在 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) { // 检查这个移动是否会掉出棋盘 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++) { // 开始检查赢家 if($game_array[$i][$j] != "_") { $present_possible_winner = $game_array[$i][$j]; // 水平检查赢家 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; // 这个玩家赢了 } else { $count_to_win = 0; } // 垂直检查赢家 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; // 这个玩家赢了 } else { $count_to_win = 0; } // 检查从上到下的对角线赢家 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; // 这个玩家赢了 } else { $count_to_win = 0; } // 检查从下到上的对角线赢家 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; // 这个玩家赢了 } else { $count_to_win = 0; } } } } // 检查是否为平局并相应返回 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; } // 这是一个我为这个脚本编写的私有调试函数 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; } }}
回答:
我找到了:计算AI移动的函数中有一个无限循环…
//选择你的移动 for($u = 0; $u < $this->board_length; $u) { if($move_weight_array[$u][0] == 1) { return $prime_game_board[$u]; } }
应该改为
//选择你的移动 for($u = 0; $u < $this->board_length; $u++) { if($move_weight_array[$u][0] == 1) { return $prime_game_board[$u]; } }
我得继续工作了…