This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
I am working on a personal project, a bot that plays connect 4. So something is seriously wrong with the recursive function I have written to manage the bots move. No errors are thrown, and any debug info I can be shown does not tell me anything useful. Additionally, I am sure that I have not overflown my stack and that my php.ini file is good to go. This script just runs (consumes a sane amount of memory) and never returns. Only about 2400 function calls should be happening, so this script should return after only a second or two. This has stumped me for days. I believe there is something I have not properly researched. Additonally I should mention that the game board is a simple 2D array to simulate a 7 x 7 board. ai_move_helper is the recursive function and I just cannot figure out why it will not function correctly.
// 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;
}
}
}
Well I found it: The calculate ai move had an infinite loop in it...
//choose your move
for($u = 0; $u < $this->board_length; $u)
{
if($move_weight_array[$u][0] == 1)
{
return $prime_game_board[$u];
}
}
Should be
//choose your move
for($u = 0; $u < $this->board_length; $u++)
{
if($move_weight_array[$u][0] == 1)
{
return $prime_game_board[$u];
}
}
Back to work for me...
Related
I had a job interview test and the question I got was about making a function which would return the number of ways a number could be generated by using numbers from a certain set and any number in the set can be used N times.
It is like if I have the number 10 and I want to find out how many ways 10 can be generated using [2,3,5]
2+2+2+2+2 = 10
5+3+2 = 10
2+2+3+3 = 10
5+5 = 10
to solve it I made this function:
function getNumberOfWays($money, $coins) {
static $level = 0;
if (!$level) {
sort($coins);
}
if ($level && !$money) {
return 1;
} elseif (!$level && !$money) {
return 0;
}
if ($money === 1 && array_search(1, $coins) !== false) {
return 1;
} elseif ($money === 1 && array_search(1, $coins) === false) {
return 0;
}
$r = 0;
$tmpCoins = $coins;
foreach ($coins as $index => $coin) {
if (!$coin || $coin > $money) {
continue;
}
$tmpCoins[$index] = 0;
$tmpMoney = $money;
do {
$tmpMoney -= $coin;
if ($tmpMoney >= 0) {
$level++;
$r += getNumberOfWays($tmpMoney, $tmpCoins);
$level--;
} elseif (!$tmpMoney) {
$r++;
}
} while ($tmpMoney >= 0);
}
return $r;
}
This function works ok and returns the right value.
My question is if there is a better way for it.
Thanks
I have two sorted arrays:
$idProducts = [2,9,25,666,1001,1002,1005,2546...n]; //almost 55.000 values
$someIds = [1,9,11,12,99,111,855...n]; //almost 2.500 values
I try make a new array from intersection of idProducts and $someIds.
I applied 3 search algorithms: linear, binary, interpolar searching, but only linear and binary algorithm work properly:
foreach($someIds as $id){
if(interpolation_search($idProducts, $id) >=0){
$newArray[]=$id;
}
}
function interpolation_search($list, $x)
{
$l = 0;
$r = count($list) - 1;
while ($l <= $r) {
if ($list[$l] == $list[$r]) {
if ($list[$l] == $x) {
return $l;
} else {
// not found
return -1;
}
}
$k = ($x - $list[$l])/($list[$r] - $list[$l]);
// not found
if ($k < 0 || $k > 1) {
return -1;
}
$mid = round($l + $k*($r - $l));
if ($x < $list[$mid]) {
$r = $mid - 1;
} else if ($x > $list[$mid]) {
$l = $mid + 1;
} else {
// success!
return $mid;
}
// not found
return -1;
}
}
Anyway the best solution was:
$idProducts = array_flip($idProducts);
foreach ($someIds as $id){
if (isset($idProducts [$id])===true){
$newArray[]=$id;
}
}
But i want know why interpolation doesnt work properly in my case and in which case we can implement interpolation algorithm searching.
Thank you.
I'm trying to implement the ball clock (determine how many iterations an inputed number of balls in the initial queue would go before returning to its initial order) in php. I believe at this point my code is not recognizing when to stop. I'm using an SplQueue to represent the queue and keeping an int inside each ball that represents their order.
I believe the difficulty to be in traversing the $queue in the function hasTerminated(). The code runs, it just doesn't stop. An input of 30, for example (php script.php 30) should output 30 12 hour cycles to complete, but it does not terminate. I appreciate any and all ideas as to how to access the objects of the $queue and compare them without destroying the $queue. PHP is not a language I am very comfortable with.
<?php
class Ball {
static $counter = 0;
protected $index;
function __construct(){
$this->index = self::$counter;
self::$counter++;
}
function getIndex(){
return $this->index;
}
}
class Clock {
protected $queue;
protected $minuteTrack;
protected $fiveMinuteTrack;
protected $hourTrack;
protected $count;
protected $time = 0;
function __construct($num){
$this->queue = new \SplQueue;
$this->minuteTrack = new \SplStack;
$this->fiveMinuteTrack = new \SplStack;
$this->hourTrack = new \SplStack;
$this->count = $num;
for($i = 0; $i<$num; $i++){
$ball = new Ball();
$this->queue->enqueue($ball); //inefficient
}
}
function hasTerminated(){
$inOrder = true;
$prev = -1;
$q = clone $this->queue;
while($q->count() != 0){
$elt = $q->dequeue();
if($elt->getIndex() >= $prev){
$inOrder = false;
} else {
$prev = $elt->getIndex();
}
}
echo PHP_EOL;
if($inOrder){
echo 'It takes ' . $time . ' 12 hour cycles to return to the original ordering' . PHP_EOL;
exit(0);
} else {
echo 'Not finished yet' . PHP_EOL;
}
}
function run(){
while(true){
$ball = $this->queue->dequeue();
if(!$this->addBallToMinuteTrack($ball)){
if(!$this->addBallToFiveMinuteTrack($ball)){
if(!$this->addBallToHourTrack($ball)){
$this->queue->enqueue($ball);
}
}
}
}
}
function addBallToMinuteTrack($ball){
echo '1';
if($this->minuteTrack->count() < 4){
$this->minuteTrack->push($ball);
return true;
} else if( $this->minuteTrack->count() == 4){
for($i=0; $i<4; $i++){
$this->queue->enqueue($this->minuteTrack->pop()); //hate this
}
return false;
}
}
function addBallToFiveMinuteTrack($ball){
echo '5';
if($this->fiveMinuteTrack->count() < 11){
$this->fiveMinuteTrack->push($ball);
return true;
} else if( $this->fiveMinuteTrack->count() == 11){
for($i=0; $i<11; $i++){
$this->queue->enqueue($this->fiveMinuteTrack->pop());
}
return false;
}
}
function addBallToHourTrack($ball){
echo '60' . PHP_EOL;
if($this->hourTrack->count() < 11){
$this->hourTrack->push($ball);
return true;
} else if( $this->hourTrack->count() == 11){
for($i=0; $i<11; $i++){
$this->queue->enqueue($this->hourTrack->pop()); //hate this
}
$this->time = $this->time + 1; //In half day increments
$this->hasTerminated();
return false;
}
}
}
foreach (array_slice($argv, 1) as $arg) {
if(!is_numeric($arg)){
echo 'Arguments must be numeric' . PHP_EOL;
exit(1);
} else {
$clock = new Clock($arg);
$clock->run();
}
}
?>
I have this PHP code which is supposed to increase a URL shortener mask on each new entry.
My problem is that it dosen't append a new char when it hits the last one (z).
(I know incrementing is a safety issue since you can guess earlier entries, but this is not a problem in this instance)
If i add 00, it can figure out 01 and so on... but is there a simple fix to why it won't do it on its own?
(The param is the last entry)
<?php
class shortener
{
public function ShortURL($str = null)
{
if (!is_null($str))
{
for($i = (strlen($str) - 1);$i >= 0;$i--)
{
if($str[$i] != 'Z')
{
$str[$i] = $this->_increase($str[$i]);
#var_dump($str[$i]);
break;
}
else
{
$str[$i] = '0';
if($i == 0)
{
$str = '0'.$str;
}
}
}
return $str;
}
else {
return '0';
}
}
private function _increase($letter)
{
//Lowercase: 97 - 122
//Uppercase: 65 - 90
// 0 - 9 : 48 - 57
$ord = ord($letter);
if($ord == 122)
{
$ord = 65;
}
elseif ($ord == 57)
{
$ord = 97;
}
else
{
$ord++;
}
return chr($ord);
}
}
?>
Effectively, all you are doing is encoding a number into Base62. So if we take the string, decode it into base 10, increment it, and reencode it into Base62, it will be much easier to know what we are doing, and the length of the string will take care of itself.
class shortener
{
public function ShortURL($str = null)
{
if ($str==null) return 0;
$int_val = $this->toBase10($str);
$int_val++;
return $this->toBase62($int_val);
}
public function toBase62($num, $b=62) {
$base='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$r = $num % $b ;
$res = $base[$r];
$q = floor($num/$b);
while ($q) {
$r = $q % $b;
$q =floor($q/$b);
$res = $base[$r].$res;
}
return $res;
}
function toBase10( $num, $b=62) {
$base='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$limit = strlen($num);
$res=strpos($base,$num[0]);
for($i=1;$i<$limit;$i++) {
$res = $b * $res + strpos($base,$num[$i]);
}
return $res;
}
}
I made this code for calculating max winning & losing streaks on an array of values. But I can get my head around doing it in one foreach loop. Currently I'm using 2 loops as follow :
public function calculateStreaks()
{
$max_win_streak = 0;
$_win_streak = 0;
$max_loss_streak = 0;
$_loss_streak = 0;
foreach($this->all_trades_pnl as $value){
if($value >= 0) {
$_win_streak++;
if($_win_streak > $max_win_streak){
$max_win_streak = $_win_streak;
}
}
else {
$_win_streak = 0;
}
}
foreach($this->all_trades_pnl as $value){
if($value < 0) {
$_loss_streak++;
if($_loss_streak > $max_loss_streak) {
$max_loss_streak = $_loss_streak;
}
}
else {
$_loss_streak = 0;
}
}
return array('win_streak' => $max_win_streak, 'loss_streak' => $max_loss_streak);
}
It works but seems far from optimized, any ideas to code this better ?
Thanks a lot in advance,
Regards,
John
Since both your loop are equal i think you can mix them toghter and assign all variables at once as follow
public function calculateStreaks()
{
$max_win_streak = 0;
$_win_streak = 0;
$max_loss_streak = 0;
$_loss_streak = 0;
foreach($this->all_trades_pnl as $value){
if($value >= 0) {
$_win_streak++;
if($_win_streak > $max_win_streak){
$max_win_streak = $_win_streak;
}
$_loss_streak = 0;
}
else if($value < 0) {
$_loss_streak++;
if($_loss_streak > $max_loss_streak) {
$max_loss_streak = $_loss_streak;
}
$_win_streak = 0;
}
}
return array('win_streak' => $max_win_streak, 'loss_streak' => $max_loss_streak);
}