<?php
/*
Written by Laurent as a submission to codewalker php contest #4
I can be reached at <removed>
goal is to write a script that would make best score / execute faster / displays each plays nicely (in this order of
importance) at klondike solitaire game
See http://codewalkers.com/php-contest.php
usage :
card.php -> will use default deck and default graphic mode
card.php?stock=deck -> will use "deck.txt" (note the added extension) and default graphic mode
card.php?stock=deck&graphic=on -> will use deck.txt and graphic mode
card.php?stock=deck&graphic=off -> will use deck.txt and text mode
timer is started just before starting the algo and right before displaying the result
temporary storage of each try's results and display is made through php's output_buffering (ob_xxx functions)
*/
define('PATH_TO_IMAGES','./'); define('DEFAULT_STOCK_FILE','deck.txt'); define('DEFAULT_GRAPHIC','true');
ob_start();
//error_reporting(E_ALL);
if (isset($stock)) $stockfilename=$stock.'.txt';
else $stockfilename=DEFAULT_STOCK_FILE;
if (isset($graphic) && ($graphic=='on')) $graphic=true;
else if (isset($graphic) && ($graphic=='off')) $graphic=false;
else $graphic=DEFAULT_GRAPHIC;
srand((double)microtime()*1000000);
function getmicrotime(){
list($usec, $sec) = explode(" ",microtime());
return ((float)$usec + (float)$sec);
}
$order = array('1'=>0,'2'=>1,'3'=>2,'4'=>3,'5'=>4,'6'=>5,'7'=>6,'8'=>7,'9'=>8,'10'=>9,'J'=>10,'Q'=>11,'K'=>12); $reverse_order = array_keys($order);
function Message($string) {
echo('<center><table border=1 width="100%"><tr><td align="center" bgcolor="#C0C0C0">'.$string.'</td></tr></table></center>'."n");
}
class card {
var $kind; //HSDC
var $color; //RB
var $pos; //0..12
var $symbol;
var $name;
var $visible;
var $moveable;
function card($c) {
global $order;
$this->name=$c;
$val = substr($c,0,strlen($c)-1);
$this->symbol=$val;
$this->pos=$order[$val];
$this->kind = substr($c,-1);
if ($this->kind=='H' || $this->kind=='D') $this->color='R'; else $this->color='B';
$this->visible=false;
}
function set_visible($yes) {
$this->visible=$yes;
}
function is_visible() {return $this->visible;}
function name() {return $this->name;}
//Colored name
function c_name() {
return ( ($this->color=='R'?'<font color="red">':'').
$this->name.
($this->color=='R'?'</font>':'')
);
}
function color() {return $this->color;}
function val() {return $this->pos;}
function kind() {return $this->kind;}
function symbol() {return $this->symbol;}
}
//Class responsible for the stock/talon management class stock{
var $stock = array(); //Main stock of cards
var $talon = array(); //returned cards
var $turn = 0; //Will count how many times we played 3 cards
//Initialize the class by loading the deck from file
function stock($filename='deck1.txt') {
if (file_exists($filename)) {
$this->stock = file($filename);
foreach($this->stock as $id=>$val) if (trim($val) != '') $this->stock[$id]=new card(strtoupper(trim($val)));
} else return false;
$this->turn=0; //Initialize turn counter
//shuffle ($this->stock); uncomment this line if you want to play with random (shuffled) stock
}
//Play $skip cards and return the next card from the stock
//(that is every one out of $skip or the last one if less than $skip remaining)
//Return the card if stock is not empty, 'empty string' if there are no more cards in the stock
function GetCard($skip='3') {
if (count($this->stock)==0) return ''; //No more card left in stock
if (count($this->stock) < $skip) $skip = count($this->stock);
for($i=0;$i<$skip;$i++) {
$card = array_shift($this->stock);
array_unshift($this->talon,$card);
}
$this->turn++;
return $card;
}
//Uses the current available card, that is remove it from talon
function UseCard() {
return array_shift($this->talon);
}
//will return the current available card from the talon or 'empty string' if talon is empty
function CurrentCard() {
if (count($this->talon) == 0) return '';
return reset($this->talon);
}
//Reset the stock to the remaining cards after it has been emptied
//Return number of cards in stock (0 probably means we won)
function DrawOver() {
$this->stock = array_reverse($this->talon); //turn talon over to stock
$this->talon=array(); //Empties talon
return count($this->stock); //return number of cards in stock
}
} //End class stock
//Class responsible for the management of a column in the tableau class column {
var $cards = array(); //Arrays of cards in this column
//Initialize the column to an empty one
function column () {
$this->cards=array();
}
//Append a card (or an array of cards) down to a colum.
function append ($c) {
//$c can be a single card or an array of cards
if (is_array($c)) foreach($c as $card) array_push($this->cards, $card);
else array_push($this->cards, $c);
}
//Remove cards from a column, start at position $pos in the column
function remove($pos) {
$cards = array_splice($this->cards,$pos);
//Just a test to make sure rules are correctly followed. Could be removed to increase speed performance
if ($cards[0]->is_visible()) return $cards;
echo("serious problem here, trying to move a column starting with a non visible card<br>");
return array();
}
// Return position of card $c if it is found in the column, -1 otherwise
// "Found" means exists AND is visible
function find($c) {
foreach($this->cards as $pos=>$card) {
if ($card->is_visible() && $card->name()==$c->name()) return $pos;
}
return -1;
}
//Will turn topmost card of the column face up
//Return number of cards returned (0 or 1 actually).
function faceup() {
if (count($this->cards)==0) return 0; //nothing left to face up
$card = array_pop($this->cards);
if (!$card->is_visible()) {
$card->set_visible(true);
array_push($this->cards,$card);
return 1;
} else {
array_push($this->cards,$card);
return 0;
}
}
//Return the top most card of a column
function topmost() {
if (count($this->cards)==0) return '';
$card = end($this->cards);
//Again, just a test to make sure rules are correctly followed. Could be removed to increase speed performance
if ($card->is_visible()) return $card; else return '';
}
//return moveable cards in a column (that is, all the visible ones)
function moveable() {
if (count($this->cards)==0) return array(); //If column is empty, return no cards
//Let's find out the first visible card of the column, which should be the head of the moveable cards
foreach($this->cards as $pos=>$card) {
//No reason to move a column headed by a king
if ($pos==0 && $card->is_visible() && substr($card->name(),0,1)=='K') return array();
if ($card->is_visible()) { //We found it, let's fetch the moveable cards
return array_slice($this->cards,$pos);
}
}
//No card is visible in this column, this should never happen (if there are cards of course) !!!
return array();
}
//Return number of cards in this column
function card_count() { return count($this->cards);}
function get_card($pos) {
if (count($this->cards)>$pos) return $this->cards[$pos]; else return '';
}
//Draw a nice table to display our column vertically. Each card occupy a row
function draw($graphic=false) {
reset($this->cards);
echo('<table border=1>');
if ($graphic) {
$count=count($this->cards);
foreach($this->cards as $card) {
echo('<tr><td>');
if ($card->is_visible()) {
if ($count>1) $src='c_'.$card->name().'.jpg'; else $src=$card->name().'.jpg';
echo('<img src="'.PATH_TO_IMAGES.$src.'">');
} else {
echo('<img src="'.PATH_TO_IMAGES.'hidden.jpg">');
}
echo('</td></tr>');
$count--;
}
} else {
foreach($this->cards as $card) {
echo('<tr><td>'); //Start row
if ($card->is_visible()) {
echo($card->c_name());
} else echo("X");
echo('</td></tr>'); //End row
}
}
echo('</table>'."n");
}
} //End class column
//Class responsible to manage the main tableau class tableau {
var $columns = array(); //Those are the columns of the tableau
var $nb_column; //Number of column in our tableau (could have been hardcoded though)
//Initialize tableau by playing cards over columns
function tableau(&$stock,$nb_column=7) {
$this->nb_column = $nb_column; //Define number of columns in our tableau
for($i=0;$i<$this->nb_column;$i++) { //Create our column
$this->columns[] = new column();
}
$i=0;
$j=0;
while ($j<$this->nb_column) { //Play cards from stock to the tableau to initialize it
for($i=$j;$i<$this->nb_column;$i++) {
$card = $stock->GetCard(1); //We play one card at a time
$stock->UseCard();
if ($i==$j) $card->set_visible(true); //Set each last card visible
$this->columns[$i]->append($card); //Append it down the column
}
$j++;
}
}
//This function will return an array of cards which are the ones that are susceptible to be stacked onto fondation
//ie, each latest card from each column
function get_topmost() {
$cards = array();
foreach($this->columns as $column) { //We simply retrieve the topmost card for each columns
$cards[] = $column->topmost();
}
return $cards;
}
//Will return an array of row of cards that are moveable from a column to another
//ie, all the cards in each row that are visible
function get_moveable() {
$moveable=array();
foreach($this->columns as $column) { //We simply retrieve the moveable cards for each columns
$moveable[] = $column->moveable();
}
return $moveable;
}
function empty_column_count() {
$count=0;
foreach($this->columns as $column) if ($column->card_count()==0) $count++;
return $count;
}
//Will check if a given card could be placed somewhere in the tableau
//Will return an array containing the columns where the given card could be placed
function is_placeable($card) {
$valid = array();
//Let's check each column to see if card could be placed there
foreach($this->columns as $id=>$column) {
$topmost = $column->topmost(); //Retrieve column's topmost card
if (($topmost=='') && ($card->symbol=='K')) //Only Kings can fit in an empty column
$valid[]=$id;
else {
//Let's check if card can fit over current topmost card : column not empty && color!= && next value
if ($topmost!='' && ($topmost->color()!=$card->color()) && ($topmost->val()==$card->val()+1)) {
$valid[]= $id;
}
}
}
return $valid;
}
//Will browse the tableau to find out if given card is visible or not
//This function is mainly used to see if a card is worth stacked or not
function is_visible($card) {
foreach($this->columns as $id=>$column) {
if (($pos=$column->find($card))!=-1) return true; //Card is found in this column
}
//Card was not found, return false
return false;
}
//Will check if all kings are heading a column or if there is a room (empty column) for them
//Return true if Number of empty rows + number of kings heading a row >= 4
//Mainly used to check if it's worth to move a card from a column to another one
function all_kings() {
$kings=0;
foreach($this->columns as $id=>$column) {
$firstcard = $this->columns[$id]->get_card(0); //Fetching first card of a row (bottom most)
if ($firstcard=='' || ($firstcard->symbol()=='K' && $firstcard->is_visible())) $kings++; //Count it if it's empty or headed by a king
}
return ($kings>=4); //
}
//Will remove a card (or a row of cards) from a given column
//if column is not supplied, a search will be made through each column to locate the given card
//Return the card(s) if they are found, empty array otherwise
function remove($card,$column=-1) {
//Column was specified
if ($column != -1) {
if (($pos = $this->columns[$column]->find($card)) !=-1) { //if card is found in specified column
$cards = $this->columns[$column]->remove($pos); //remove it
return $cards; //Return removed card
}
return array(); //Card was not found in specified column, return empty array
}
//No column supplied, so search for card
foreach($this->columns as $id=>$column) {
if (($pos=$column->find($card))!=-1) { //Card is found in this column
$cards = $this->columns[$id]->remove($pos); //Remove it
return $cards; //Return removed card
}
}
return array(); //Card was not found in the tableau, return empty array
}
//Will append an array of cards over specified column
function append($cards,$column) {
$this->columns[$column]->append($cards);
}
//Turn given column's last card face up if needed
//If no column is given, turn all columns last card face up if needed
//Return number of cards turned
function turn_faceup($column=-1) {
if ($column!=-1) {
return $this->columns[$column]->faceup();
}
$count=0;
foreach($this->columns as $id=>$column) {
$count += $this->columns[$id]->faceup();
}
return $count;
}
//Draw a nice table to display our tableau. Each column occupy a column
function draw($graphic=false) {
echo("<table><tr valign=top>n");
foreach($this->columns as $column) {echo('<td>');$column->draw($graphic);echo('</td>');}
echo("</tr></table>n");
}
} //End class tableau
//Class responsible to manage the fondation class fondation {
var $stack = array(); //Our stack of cards
function fondation () {
$this->stack['S']=array(); //spades
$this->stack['H']=array(); //Hearts
$this->stack['D']=array(); //Diamonds
$this->stack['C']=array(); //Clubs
}
//Check if a given card is stackable or not
function is_stackable($card) {
if ($card->val()=='0' && count($this->stack[$card->kind()])==0) return true;
else if (count($this->stack[$card->kind()])==0) return false;
$last_card = end($this->stack[$card->kind()]);
if ($card->val()==$last_card->val()+1) {return true;}
return false;
}
//Check if a given card is stacked
function is_stacked($card) {
global $order;
//split_card_name($card,$val,$kind,$color);
//$pos = $order[$val];
if (isset($this->stack[$card->kind()][$card->val()])) return true; else return false;
}
//Take a card and stack it. NO check is done !!
function stack($card) {
$this->stack[$card->kind()][] = $card;
}
//Draw a nice output to diplay the fondation
function draw($graphic) {
foreach($this->stack as $id=>$cards) {
if (!$graphic) echo("$id -> ");
foreach($cards as $card) {
if ($graphic) echo('<img src="'.PATH_TO_IMAGES.$card->name.'.jpg">');
else echo($card->symbol()." ");
}
echo("<br>n");
}
}
//Check if the fondation is full (then we know we won)
function is_full() {
foreach($this->stack as $id=>$cards) {
if (count($cards)<13) return false; //if a stock holds less than 13 cards, it's not full
}
return true;
}
} //End class fondation
//Draw the current situation (tableau + fondation) function draw_situation() {
global $tableau, $fondation;
global $graphic,$score;
echo("<table border=1>");
echo("<tr><td colspan=2 align="center">score : $score</td></tr>");
echo("<tr><td>");
$tableau->draw($graphic);
echo("</td><td>");
$fondation->draw($graphic);
echo("</td></tr></table>n");
}
//This function will decide which column is the *best* one to move a card on when there are more than one possible choice (2 actually)
//Best column number will be returned function best_column($columns,$cards)
{
global $tableau;
if (count($columns) <= 1) return $columns[0]; //no choice, just return with only available position
if ($cards[0]->symbol()=='K') return $columns[0]; //No need to bother for a king
//we'll never have more than 2 possible column for a card, so let's isolate them
$col1=$tableau->columns[$columns[0]];
$col2=$tableau->columns[$columns[1]];
//If number of cards in col1 < 2, let's first check if col 2 isn't better, otherwise check col1
if ($col1->card_count() < 2) { //not enough cards on $col1 to decide if col1 is the best place to move the card
//so, we'll check $col2 to see if current card's kind is the same as (last-1) card
if ($col2->card_count() >= 2) {
$match_card=$col2->get_card($col2->card_count()-2);
if ($match_card->kind() == $cards[0]->kind()) return $columns[1]; else return $columns[0];
} else return $columns[0]; //both column have only 1 card, so there is no 'best' choice
} else { //$col1 holds enough cards to decide if it's the 'best' choice
$match_card=$col1->get_card($col1->card_count()-2);
if ($match_card->kind() == $cards[0]->kind()) return $columns[0]; else return $columns[1];
}
//We should never reach here, but just in case, let's return a column
return $columns[0];
}
function best_move($moveable_cards,$column) {
global $tableau;
global $random_used;
$nb_empty=$tableau->empty_column_count();
$proposed_cards=$moveable_cards[$column];
$top_card=$proposed_cards[0];
for($i=$column+1;$i<count($moveable_cards);$i++) {
$cards=$moveable_cards[$i];
if (count($cards)==0) continue;
if (($cards[0]->val()==$top_card->val()) && ($cards[0]->color()==$top_card->color())) {
//This card could be moved in place of the other one. What shall we do ?
//if the proposed will free a column and we don't really need one, better move the other card
if ($top_card == $tableau->columns[$column]->get_card(0) && ($nb_empty>0 || $tableau->all_kings())) return false;
//otherwise, move the proposed card if it's closer to the top of the column than the other one
//if($tableau->columns[$column]->find($top_card) < $tableau->columns[$i]->find($cards[0]))return false;
$random_used=true;
return (rand(0,10) > 5);
}
}
return true;
}
//Check if a card is worth moved
//The only case a card is absolutely not worth moved is when :
//- There are no more hidden cards in the card's column, and
//- All 4 kings are either heading a column or have a free column to be moved on, and
//- The second card with the same value / same color is already visible (on tableau or stacked) or there is a place for it
//If all 3 conditions matches, the card won't be moved
function safe_keep($cards,$column) {
global $tableau;
global $reverse_order;
if ($tableau->columns[$column]->find($cards[0]) != 0) return false;
if ($cards[0]->color() == 'R') {
if ($cards[0]->kind()=='H') $same=new card($reverse_order[$cards[0]->val()].'D');
else $same = new card($reverse_order[$cards[0]->val()].'H');
} else {
if ($cards[0]->kind=='S') $same=new card($reverse_order[$cards[0]->val()].'C');
else $same = new card($reverse_order[$cards[0]->val()].'S');
}
$places = $tableau->is_placeable($same);
if ($tableau->is_visible($same) && count($places)>1) return false;
if (!$tableau->all_kings()) return false;
//Card is not worth moved if we reach here
return false;
}
//Check if a card is safe to stack function safe_stack($card,$column) {
global $fondation,$tableau;
global $order;
global $reverse_order;
//If moving this card means freeing a column for a king, then no hesitation, stack it !
if(($column!=-1) && !$tableau->all_kings() && ($tableau->columns[$column]->get_card(0)==$card)) return true;
if (($card->val()==0) || ($card->val()==1)) return true; //aces and 2 are always ok
if ($card->color=='R') {
$C1 = new card ($reverse_order[$card->val()-1].'C');
$C1stacked = $fondation->is_stacked($C1);
$C1visible = $tableau->is_visible($C1);
$S1 = new card($reverse_order[$card->val()-1].'S');
$S1stacked = $fondation->is_stacked($S1);
$S1visible = $tableau->is_visible($S1);
$C1present = $C1stacked || $C1visible;
$S1present = $S1stacked || $S1visible;
if ($C1present && $S1present) return true;
$C2 = new card ($reverse_order[$card->val()-2].'C');
$C2stacked = $fondation->is_stacked($C2);
$S2 = new card($reverse_order[$card->val()-2].'S');
$S2stacked = $fondation->is_stacked($S2);
if ($S1present && $C2stacked) return true;
if ($C1present && $S2stacked) return true;
if ($C2stacked && $S2stacked) return true;
} else {
$H1=new card($reverse_order[$card->val()-1].'H');
$H1stacked = $fondation->is_stacked($H1);
$H1visible = $tableau->is_visible($H1);
$D1=new card($reverse_order[$card->val()-1].'D');
$D1stacked = $fondation->is_stacked($D1);
$D1visible = $tableau->is_visible($D1);
$H1present = $H1stacked || $H1visible;
$D1present = $D1stacked || $D1visible;
if ($H1present && $D1present) return true;
$H2=new card ($reverse_order[$card->val()-2].'H');
$H2stacked = $fondation->is_stacked($H2);
$D2=new card ($reverse_order[$card->val()-2].'D');
$D2stacked = $fondation->is_stacked($D2);
if ($H1present && $D2stacked) return true;
if ($D1present && $H2stacked) return true;
if ($D2stacked && $H2stacked) return true;
}
return false;
}
//This function is part of the main algo. It's used to check (and eventually move) the moveable cards from the
//tableau to the tableau.
//Main goal is to uncover hidden cards and/or free cells for kings
//Attempt is made to move a card to the best place if more than one possible choice is given
//Whenever a card is moved, hidden card will be turned face up, score adapted and situation draw to browser
function move()
{
global $tableau; //We need to access tableau
global $score; //We'll update score
$moved=false; //The function returns this
$moveable_cards = $tableau->get_moveable(); //Get moveable cards from tableau
//uasort($moveable_cards,'sort_cards'); //sort moveable cards by higher value first so they will be moved first
//Now we will check each rows of moveable cards to find out if they are moveable somewhere else
foreach($moveable_cards as $column=>$cards) {
if (count($cards) == 0) continue; //No reason to move an empty column, so just skip it
//Get possible new places for the given card. By rules a card can have up to maximum 2 possible positions
$valid_columns = $tableau->is_placeable($cards[0]);
if (count($valid_columns)==0) continue; //There is no way to move this row of card so skip to next one
//Cards are moveable, let's check if it's safe to keep them nevertheless
if (safe_keep($cards,$column)) continue; //Yes, so skip to next one
//Let's now check if there is no a better move in this turn
if (!best_move($moveable_cards,$column)) continue; //Card not moved because it's not the best move
//Finally, we can try to figure out a best move for this row of cards
$n_column = best_column($valid_columns,$cards);
//And proceed to the actual move
Message("Moving cards (".$cards[0]->c_name().") from column $column to column $n_column in tableau");
$cards = $tableau->remove($cards[0],$column);
$tableau->append($cards,$n_column);
$moved=true;
$score+=($tableau->turn_faceup($column)*5); //Turn face up and increment score if applicable
draw_situation(); //let user know what happened
break; //Once a card has been moved, chance will be given to talon, so we break here
}
return $moved;
}
//This function is part of the main algo. It's used to check (and eventually stack) all stackable cards from the
//Tableau to the fondation.
//Main goal is to stack cards in a safe way if $always is false or to score if $always is true
//Whenever a card is stacked, hidden card will be turned face up, score adapted and situation draw to browser
function stack($always)
{
global $tableau,$fondation; //We need to access tableau and fondation
global $score; //We'll update score
$stacked = false; //The function returns this
$cards = $tableau->get_topmost(); //Get stackable cards from tableau
//Now we'll check each stackable card to find out if they are stackable or not
foreach($cards as $column=>$card) {
if ($card=='') continue; //No cards in this column, continue to next one
if (!$fondation->is_stackable($card)) continue; //It's not possible to stack this card, so continue to next one
if (!$always && !safe_stack($card,$column)) continue; //We want to play safe and it's not a safe stack, continue to next one
//Finally, if all went fine, we can now proceed to stack the card
$cards = $tableau->remove($card,$column); //first remove it
if (count($cards)!=1) {
echo("We got a serious problem here with ".count($cards)." cards<br>");
} else {
Message("Moving card ".$card->c_name()." from tableau to fondation"); //Then stack it
$fondation->stack($cards[0]);
$score+=10;
$stacked = true;
$score+=($tableau->turn_faceup($column)*5); //Turn face up and increment score if applicable
draw_situation(); //let user know what happened
//break;
}
}
return $stacked;
}
//This function is part of the main algo. It's used to check (and eventually move/stack) cards from the
//talon to either the tableau or the fondation. Priority is given to tableau (to score more).
//stackable cards are evaluated to be safe/unsafe and, if unsafe, will be stacked to score max if $always is true
//Moveable cards are evaluated against moveable cards in tableau and priority is given to cards from tableau if it's not safe
//to keep them function playfromtalon(&$card,$always,$skip_random)
{
global $stock,$tableau,$fondation; //We need access to stock, tableau and fondation
global $score; //We'll update score
$stock_used=false; //The function return this
do {
$played=false;
$card = $stock->CurrentCard(); //Get current card from talon
if ($card=='') { //If no more cards in talon, play 3 cards from stock
$card = $stock->GetCard(3);
if ($card!='') Message('Playing 3 cards from stock ->'.$card->c_name());
}
if ($card!='') { //If we have a card to play with, go on (We may have reach the end of the stock)
$valid_columns = $tableau->is_placeable($card);
if (count($valid_columns)>0) { //Check if card is placeable to tableau
//Let's check if the card we want to play from talon is not available from the tableau
$moveable_cards=$tableau->get_moveable();
foreach($moveable_cards as $column=>$moveable_card) {
//If it's the case, *maybe* we are better to play card from tableau
if (count($moveable_card)==0) continue; //No card to move in this column
if (($card->val() == $moveable_card[0]->val()) && ($card->color()==$moveable_card[0]->color())) {
if (!safe_keep($moveable_card,$column)) break 2;
}
}
if ($skip_random && $stock_used) { //We will randomly skip this card if we played at least one before
if ($card->symbol() != 'K' && $card->symbol()!='1') { //We won't skip a king or an ace !!
if (rand(0,10)>6) {
$stock->GetCard(3);
break;
}
}
}
$n_column = best_column($valid_columns,array($card));
$stock->UseCard(); //Yes, take card from stock
$card->set_visible(true);
$tableau->append(array($card),$n_column); //And move it into tableau
$score+=5;
$stock_used=true;
Message("moving card ".$card->c_name()." from stock to tableau, column ".$n_column);
$played=true;
} else if ($fondation->is_stackable($card)) { //Check if card from stock is stackable
if (!$always && !safe_stack($card,-1)) break; //Not a safe stack -> don't stack
$stock->UseCard(); //Yes, take card from stock
$card->set_visible(true);
$fondation->stack($card); //And stack it on fondation
$score+=10;
$stock_used=true;
$played=true;
Message("moving card ".$card->c_name()." from stock to fondation");
} else $played=false;
if ($played) draw_situation();
}
} while ($played && $card!='');
return $stock_used;
}
function sort_cards($a,$b)
{
global $order;
if (count($a)==0 || count($b)==0) return (count($a)==0);
if ($a[0]->val()==$b[0]->val()) return 0;
return ($a[0]->val() > $b[0]->val()) ? -1 : 1;
}
$try = 0; //current tries counter $random_used=false; //Will be set if random moves have been used in any try
$time_start = getmicrotime(); //Everything is set up, now we can start the timer
do { //main loop for tries
ob_start(); //Each try will be stored in its own output_buffer
$stock = new stock($stockfilename);
Message("Initializing Tableau");
$tableau = new tableau($stock);
$fondation = new fondation();
$score=0; //score is 0
$loop=0; //we can use up to 3 passes through the stock
$stock_used=false; //
draw_situation();
do { //Do a pass through the stock
do { //Keep playing to stack and to tableau till no move are available without a new card from stock
playfromtalon($card,$loop>=1,$try>2);
//Let's check if any cards is stackable on the fondation
do {
$stacked = stack($loop>=($try%3));
//$stacked = stack(true);
if ($stacked) playfromtalon($card,$loop>=1,$try>2);
} while ($stacked && ($loop<$try%3));
//Let's also check if any cards are moveable in the tableau
$moved = move();
if ($moved) playfromtalon($card,$loop>=1,$try>2);
} while ($stacked || $moved); //loop until nothing is moveable nor stackable
do { //Play a new card from stock
$card = $stock->GetCard(3);
if ($card!='') Message("Playing 3 cards from stock ".$card->c_name());
if ($card!='') $stock_used=playfromtalon($card,$loop>=1,$try>2);
} while (!$stock_used && $card!=''); //And loop till at least one card has been used (or stock is over)
if ($card=='') { //Stock is over :
$loop++; //Let's start a new pass through the stock
$remains = $stock->DrawOver();
if ($loop<3) Message("<font color="green">drawing stock over ($remains card remain) / pass n� ".($loop+1)."</font>");
}
} while($loop<3); //No more than 3 passes allowed
//Stock is over or maximum passes is reached, let's move and stack until we can't do anything.
do {
//Let's check if any cards is stackable on the fondation
$stacked = stack(true);
//Let's also check if any cards are moveable in the tableau
$moved = move();
} while ($stacked || $moved);
//Game Over
Message("<font color="red" size=+1>Game Over ! Score = $score in ".$stock->turn." turns</font>");
//Store this try's score
$scores[$try]=$score;
//And store the output_buffer
$display[$try] = ob_get_contents();
ob_end_clean(); //Start with a fresh output_buffer
//Uncomment next line if you want to know the score for each tries
//echo("$try : $score, "); if ($random_used) echo("<b>Random Used</b>"); echo("<br>");
//Now check if it's worth running more tries (ie, is the fondation full)
if ($fondation->is_full()) break;
$try++;
} while ($try<6 || ($random_used && ($try<12))); //If random move went to work, let's make a few more tries (2 times more)
//all tries processed, time to stop the timer $time_end = getmicrotime(); $time = $time_end - $time_start;
//Let's find out which tries gave the best score $maxscore =0;
foreach($scores as $try=>$score)
if ($score>$maxscore) {$maxscore=$score;$bestry = $try;}
//And display that try echo($display[$bestry]);
echo("<br><b>".$time." second</b>"); ?>
|
|