// A Set simulation
$GAMES = 10;
$F = 4; // Features: Shape, Color, Number, Pattern
$V = 3; // Variations: Oval/Diamond/Squiggle, Red/Green/Purple
if (array_key_exists('games', $_POST)) {
$GAMES = $_POST['games'];
}
/* We can't actually deal with anything other than the hardcoded numbers
if (array_key_exists('features', $_POST)) {
$F = $_POST['features'];
}
if (array_key_exists('variations', $_POST)) {
$V = $_POST['variations'];
}
*/
$PRINT_HANDS = 2;
if (array_key_exists('print_hands', $_POST)) {
$PRINT_HANDS = $_POST['print_hands'];
}
$deck = array();
$board = array();
$outcomes = array();
$finalOutput = "";
$debugOutput = "";
function shuffleDeck() {
global $deck;
global $V;
global $F;
$deck = array();
// Just going to hardcode this, because I don't know how to
// nest for loops based on the features.
for ($i = 0; $i < $V; $i++) {
for ($j = 0; $j < $V; $j++) {
for ($k = 0; $k < $V; $k++) {
for ($l = 0; $l < $V; $l++) {
$deck[] = ($i << 6) + ($j << 4) + ($k << 2) + $l;
}
}
}
}
srand((float)microtime()*1000000);
shuffle($deck);
} // shuffleDeck()
// maybe this can be more efficient if we randomize the deck first,
// and then just deal the top card?
function dealCard() {
global $deck;
//$card = array_splice($deck, array_rand($deck), 1);
//return $card[0];
return array_pop($deck);
}
function printCard($card) {
$returnString = "";
$shape = "";
switch ($card >> 6) {
case 0: $shape = "S"; break;
case 1: $shape = "O"; break;
case 2: $shape = "X"; break;
default: $shape = "Z";
}
switch (($card >> 4) & 0x3) {
case 0: $returnString .= ""; break;
case 1: $returnString .= ""; break;
case 2: $returnString .= ""; break;
default: $returnString .= ""; break;
}
switch (($card >> 2) & 0x3) {
case 0: $returnString .= ""; break;
case 1: $returnString .= ""; break;
case 2: $returnString .= ""; break;
default: $returnString .= ""; break;
}
switch ($card & 0x3) {
case 0: $returnString .= $shape; break;
case 1: $returnString .= $shape . $shape; break;
case 2: $returnString .= $shape . $shape . $shape; break;
default: $returnString .= $shape . $shape . $shape . $shape; break;
}
$returnString .= "";
return $returnString;
}
function miniCheck ($a, $b, $c) {
return ($a == $b && $b == $c) || (($a ^ $b ^ $c) == 0x3);
}
//$doOnce=true;
function setCheck($a, $b, $c) {
return miniCheck($a >> 6, $b >> 6, $c >> 6) &&
miniCheck(($a >> 4) & 0x3, ($b >> 4) & 0x3, ($c >> 4) & 0x3) &&
miniCheck(($a >> 2) & 0x3, ($b >> 2) & 0x3, ($c >> 2) & 0x3) &&
miniCheck($a & 0x3, $b & 0x3, $c & 0x3);
/*
global $doOnce;
$tempBool= miniCheck($a >> 6, $b >> 6, $c >> 6) &&
miniCheck(($a >> 4) & 0x3, ($b >> 4) & 0x3, ($c >> 4) & 0x3) &&
miniCheck(($a >> 2) & 0x3, ($b >> 2) & 0x3, ($c >> 2) & 0x3) &&
miniCheck($a & 0x3, $b & 0x3, $c & 0x3);
if ($tempBool && $doOnce) {
echo "(".($a >> 6).", ".($b >> 6).", ". ($c >> 6).") ".
"(".(($a >> 4) & 0x3).", ".(($b >> 4) & 0x3).", ".(($c >> 4) & 0x3).") ".
"(".(($a >> 2) & 0x3).", ".(($b >> 2) & 0x3).", ".(($c >> 2) & 0x3).") ".
"(".($a & 0x3).", ".($b & 0x3).", ".($c & 0x3).") \n";
$doOnce=false;
}
return $tempBool;
*/
} // setCheck()
$maxBoard = 0;
function boardCheck($newCard) {
global $board;
global $debugOutput;
global $maxBoard;
$setFound = false;
for ($i = count($board) - 1; $i > 0; $i--) {
for ($j = $i - 1; $j >= 0; $j--) {
if (setCheck($newCard, $board[$i], $board[$j])) {
$setFound = true;
break 2;
}
}
}
if ($setFound) {
$debugOutput .= "Set: $board[$i]:".printCard($board[$i])." ".
$board[$j].":".printCard($board[$j])." ".
$newCard.":".printCard($newCard)." \n";
// remove the matching cards. Since we can't remove both cards simultaneously,
// and $i is always larger, we must remove $i first, which shouldn't affect the key $j
array_splice($board, $i, 1);
array_splice($board, $j, 1);
// don't add the new card
}
// add the new card
else {
$board[] = $newCard;
if (count($board) > $maxBoard) {
$maxBoard++;
}
}
return $setFound;
}
?>
A Set simulation with trials
for ($g = 0; $g < $GAMES; $g++) {
shuffleDeck();
$board = array();
// go through all the cards in the deck,
// then see how many are left on the board
for ($c = count($deck); $c > 0; $c--) {
/*
$tempCard = dealCard();
echo $c.":".printCard($tempCard)." \n";
boardCheck($tempCard);
*/
boardCheck(dealCard());
}
//echo " ".$debugOutput." \nMax Board: $maxBoard \n";
$debugOutput = "";
$outcomes[count($board)]++;
if ($PRINT_HANDS && $g == 0) {
$finalOutput .= "The features are Letter (S/X/O), ".
"Size (Small/Medium/Large) \n".
"Color (Red/Green/Purple), ".
"and Number (1/22/333) \n";
}
if ($g < $PRINT_HANDS) {
$finalOutput .= "Board ".($g+1).": ";
for ($i = 0; $i < count($board); $i++) {
$finalOutput .= printCard($board[$i]);
$finalOutput .= " ";
}
$finalOutput .= " \n";
}
} // for each game
?>
Leftover Board Cards
Frequency
for ($i = 0; $i < 17; $i++) { ?>
} ?>
Highest Board Size:
This simulation is currently hardcoded to use exactly 4 features and exactly 3 variations (per standard Set rules.) Perhaps in the future I will expand it to deal with a wider range of features and variations.