diff --git a/addon/tictac/tictac.php b/addon/tictac/tictac.php new file mode 100644 index 000000000..d44254411 --- /dev/null +++ b/addon/tictac/tictac.php @@ -0,0 +1,661 @@ +Three Dimensional Tic-Tac-Toe
'; +} + + +function tictac_module() { + return; +} + + + + + +function tictac_content(&$a) { + + $o = ''; + + if($_POST['move']) { + $handicap = $a->argv[1]; + $mefirst = $a->argv[2]; + $dimen = $a->argv[3]; + $yours = $a->argv[4]; + $mine = $a->argv[5]; + + $yours .= $_POST['move']; + } + elseif($a->argc > 1) { + $handicap = $a->argv[1]; + $dimen = 3; + } + else { + $dimen = 3; + } + + $o .= '

3D Tic-Tac-Toe


'; + + $t = new tictac($dimen,$handicap,$mefirst,$yours,$mine); + $o .= $t->play(); + + $o .= 'New game
'; + $o .= 'New game with handicap
'; +$o .= <<< EOT +

+Three dimensional tic-tac-toe is just like the traditional game except that it is played on multiple levels simultaneously. In this case there are three levels. You win by getting three in a row on any level, as well as up, down, and diagonally across the different levels. +

+

+The handicap game disables the center position on the middle level because the player claiming this square often has an unfair advantage. +

+EOT; +return $o; + +} + +class tictac { + private $dimen; + private $first_move = true; + private $handicap = 0; + private $yours; + private $mine; + private $winning_play; + private $you; + private $me; + private $debug = 1; + private $crosses = array('011','101','110','112','121','211'); + +/* + '001','010','011','012','021', + '101','110','111','112','121', + '201','210','211','212','221'); +*/ + + private $corners = array( + '000','002','020','022', + '200','202','220','222'); + + private $planes = array( + array('000','001','002','010','011','012','020','021','022'), // horiz 1 + array('100','101','102','110','111','112','120','121','122'), // 2 + array('200','201','202','210','211','212','220','221','222'), // 3 + array('000','010','020','100','110','120','200','210','220'), // vert left + array('000','001','002','100','101','102','200','201','202'), // vert top + array('002','012','022','102','112','122','202','212','222'), // vert right + array('020','021','022','120','121','122','220','221','222'), // vert bot + array('010','011','012','110','111','112','210','211','212'), // left vertx + array('001','011','021','101','111','221','201','211','221'), // top vertx + array('000','001','002','110','111','112','220','221','222'), // diag top + array('020','021','022','110','111','112','200','201','202'), // diag bot + array('000','010','020','101','111','121','202','212','222'), // diag left + array('002','012','022','101','111','121','200','210','220'), // diag right + array('002','011','020','102','111','120','202','211','220'), // diag x + array('000','011','022','100','111','122','200','211','222') // diag x + + ); + + + private $winner = array( + array('000','001','002'), // board 0 winners - left corner across + array('000','010','020'), // down + array('000','011','022'), // diag + array('001','011','021'), // middle-top down + array('010','011','012'), // middle-left across + array('002','011','020'), // right-top diag + array('002','012','022'), // right-top down + array('020','021','022'), // bottom-left across + array('100','101','102'), // board 1 winners + array('100','110','120'), + array('100','111','122'), + array('101','111','121'), + array('110','111','112'), + array('102','111','120'), + array('102','112','122'), + array('120','121','122'), + array('200','201','202'), // board 2 winners + array('200','210','220'), + array('200','211','222'), + array('201','211','221'), + array('210','211','212'), + array('202','211','220'), + array('202','212','222'), + array('220','221','222'), + array('000','100','200'), // top-left corner 3d + array('000','101','202'), + array('000','110','220'), + array('000','111','222'), + array('001','101','201'), // top-middle 3d + array('001','111','221'), + array('002','102','202'), // top-right corner 3d + array('002','101','200'), + array('002','112','222'), + array('002','111','220'), + array('010','110','210'), // left-middle 3d + array('010','111','212'), + array('011','111','211'), // middle-middle 3d + array('012','112','212'), // right-middle 3d + array('012','111','210'), + array('020','120','220'), // bottom-left corner 3d + array('020','110','200'), + array('020','121','222'), + array('020','111','202'), + array('021','121','221'), // bottom-middle 3d + array('021','111','201'), + array('022','122','222'), // bottom-right corner 3d + array('022','121','220'), + array('022','112','202'), + array('022','111','200') + + ); + + function __construct($dimen,$handicap,$mefirst,$yours,$mine) { + $this->dimen = 3; + $this->handicap = (($handicap) ? 1 : 0); + $this->mefirst = (($mefirst) ? 1 : 0); + $this->yours = str_replace('XXX','',$yours); + $this->mine = $mine; + $this->you = $this->parse_moves('you'); + $this->me = $this->parse_moves('me'); + + if(strlen($yours)) + $this->first_move = false; + } + + function play() { + + if($this->first_move) { + if(rand(0,1) == 1) { + $o .= '
You go first...

'; + $this->mefirst = 0; + $o .= $this->draw_board(); + return $o; + } + $o .= '
I\'m going first this time...

'; + $this->mefirst = 1; + + } + + if($this->check_youwin()) { + $o .= '
You won!

'; + $o .= $this->draw_board(); + return $o; + } + + if($this->fullboard()) + $o .= 'Cat game!'; + + $move = $this->winning_move(); + if(strlen($move)) { + $this->mine .= $move; + $this->me = $this->parse_moves('me'); + } + else { + $move = $this->defensive_move(); + if(strlen($move)) { + $this->mine .= $move; + $this->me = $this->parse_moves('me'); + } + else { + $move = $this->offensive_move(); + if(strlen($move)) { + $this->mine .= $move; + $this->me = $this->parse_moves('me'); + } + } + } + + if($this->check_iwon()) + $o .= '
I won!

'; + if($this->fullboard()) + $o .= 'Cat game!'; + $o .= $this->draw_board(); + return $o; + } + + function parse_moves($player) { + if($player == 'me') + $str = $this->mine; + if($player == 'you') + $str = $this->yours; + $ret = array(); + while(strlen($str)) { + $ret[] = substr($str,0,3); + $str = substr($str,3); + } + return $ret; + } + + + function check_youwin() { + for($x = 0; $x < count($this->winner); $x ++) { + if(in_array($this->winner[$x][0],$this->you) && in_array($this->winner[$x][1],$this->you) && in_array($this->winner[$x][2],$this->you)) { + $this->winning_play = $this->winner[$x]; + return true; + } + } + return false; + } + function check_iwon() { + for($x = 0; $x < count($this->winner); $x ++) { + if(in_array($this->winner[$x][0],$this->me) && in_array($this->winner[$x][1],$this->me) && in_array($this->winner[$x][2],$this->me)) { + $this->winning_play = $this->winner[$x]; + return true; + } + } + return false; + } + function defensive_move() { + + for($x = 0; $x < count($this->winner); $x ++) { + if(($this->handicap) && in_array('111',$this->winner[$x])) + continue; + if(in_array($this->winner[$x][0],$this->you) && in_array($this->winner[$x][1],$this->you) && (! in_array($this->winner[$x][2],$this->me))) + return($this->winner[$x][2]); + if(in_array($this->winner[$x][0],$this->you) && in_array($this->winner[$x][2],$this->you) && (! in_array($this->winner[$x][1],$this->me))) + return($this->winner[$x][1]); + if(in_array($this->winner[$x][1],$this->you) && in_array($this->winner[$x][2],$this->you) && (! in_array($this->winner[$x][0],$this->me))) + return($this->winner[$x][0]); + } + return ''; + } + +function winning_move() { + + for($x = 0; $x < count($this->winner); $x ++) { + if(($this->handicap) && in_array('111',$this->winner[$x])) + continue; + if(in_array($this->winner[$x][0],$this->me) && in_array($this->winner[$x][1],$this->me) && (! in_array($this->winner[$x][2],$this->you))) + return($this->winner[$x][2]); + if(in_array($this->winner[$x][0],$this->me) && in_array($this->winner[$x][2],$this->me) && (! in_array($this->winner[$x][1],$this->you))) + return($this->winner[$x][1]); + if(in_array($this->winner[$x][1],$this->me) && in_array($this->winner[$x][2],$this->me) && (! in_array($this->winner[$x][0],$this->you))) + return($this->winner[$x][0]); + } + +} + + function offensive_move() { + + shuffle($this->planes); + shuffle($this->winner); + shuffle($this->corners); + shuffle($this->crosses); + + if(! count($this->me)) { + if($this->handicap) { + $p = $this->uncontested_plane(); + foreach($this->corners as $c) + if((in_array($c,$p)) + && (! $this->is_yours($c)) && (! $this->is_mine($c))) + return($c); + } + else { + if((! $this->marked_yours(1,1,1)) && (! $this->marked_mine(1,1,1))) + return '111'; + $p = $this->uncontested_plane(); + foreach($this->crosses as $c) + if((in_array($c,$p)) + && (! $this->is_yours($c)) && (! $this->is_mine($c))) + return($c); + } + } + + if($this->handicap) { + if(count($this->me) >= 1) { + if(count($this->get_corners($this->me)) == 1) { + if(in_array($this->me[0],$this->corners)) { + $p = $this->my_best_plane(); + foreach($this->winner as $w) { + if((in_array($w[0],$this->you)) + || (in_array($w[1],$this->you)) + || (in_array($w[2],$this->you))) + continue; + if(in_array($w[0],$this->corners) + && in_array($w[2],$this->corners) + && in_array($w[0],$p) && in_array($w[2],$p)) { + if($this->me[0] == $w[0]) + return($w[2]); + elseif($this->me[0] == $w[2]) + return($w[0]); + } + } + } + } + else { + $r = $this->get_corners($this->me); + if(count($r) > 1) { + $w1 = array(); $w2 = array(); + foreach($this->winner as $w) { + if(in_array('111',$w)) + continue; + if(($r[0] == $w[0]) || ($r[0] == $w[2])) + $w1[] = $w; + if(($r[1] == $w[0]) || ($r[1] == $w[2])) + $w2[] = $w; + } + if(count($w1) && count($w2)) { + foreach($w1 as $a) { + foreach($w2 as $b) { + if((in_array($a[0],$this->you)) + || (in_array($a[1],$this->you)) + || (in_array($a[2],$this->you)) + || (in_array($b[0],$this->you)) + || (in_array($b[1],$this->you)) + || (in_array($b[2],$this->you))) + continue; + if(($a[0] == $b[0]) && ! $this->is_mine($a[0])) { + return $a[0]; + } + elseif(($a[2] == $b[2]) && ! $this->is_mine($a[2])) { + return $a[2]; + } + } + } + } + } + } + } + } + + //&& (count($this->me) == 1) && (count($this->you) == 1) + // && in_array($this->you[0],$this->corners) + // && $this->is_neighbor($this->me[0],$this->you[0])) { + + // Yuck. You foiled my plan. Since you obviously aren't playing to win, + // I'll try again. You may keep me busy for a few rounds, but I'm + // gonna' get you eventually. + +// $p = $this->uncontested_plane(); + // foreach($this->crosses as $c) + // if(in_array($c,$p)) + // return($c); + +// } + + + // find all the winners containing my points. + $mywinners = array(); + foreach($this->winner as $w) + foreach($this->me as $m) + if((in_array($m,$w)) && (! in_array($w,$mywinners))) + $mywinners[] = $w; + + // find all the rules where my points are in the center. + $trythese = array(); + if(count($mywinners)) { + foreach($mywinners as $w) { + foreach($this->me as $m) { + if(($m == $w[1]) && ($this->uncontested_winner($w)) + && (! in_array($w,$trythese))) + $trythese[] = $w; + } + } + } + + $myplanes = array(); + for($p = 0; $p < count($this->planes); $p ++) { + if($this->handicap && in_array('111',$this->planes[$p])) + continue; + foreach($this->me as $m) + if((in_array($m,$this->planes[$p])) + && (! in_array($this->planes[$p],$myplanes))) + $myplanes[] = $this->planes[$p]; + } + shuffle($myplanes); + + // find all winners which share an endpoint, and which are uncontested + $candidates = array(); + if(count($trythese) && count($myplanes)) { + foreach($trythese as $t) { + foreach($this->winner as $w) { + if(! $this->uncontested_winner($w)) + continue; + if((in_array($t[0],$w)) || (in_array($t[2],$w))) { + foreach($myplanes as $p) + if(in_array($w[0],$p) && in_array($w[1],$p) && in_array($w[2],$p) && ($w[1] != $this->me[0])) + if(! in_array($w,$candidates)) + $candidates[] = $w; + } + } + } + } + + // Find out if we are about to force a win. + // Looking for two winning vectors with a common endpoint + // and where we own the middle of both - we are now going to + // grab the endpoint. The game isn't yet over but we've already won. + + if(count($candidates)) { + foreach($candidates as $c) { + if(in_array($c[1],$this->me)) { + // return endpoint + foreach($trythese as $t) + if($t[0] == $c[0]) + return($t[0]); + elseif($t[2] == $c[2]) + return($t[2]); + } + } + + // find opponents planes + $yourplanes = array(); + for($p = 0; $p < count($this->planes); $p ++) { + if($this->handicap && in_array('111',$this->planes[$p])) + continue; + if(in_array($this->you[0],$this->planes[$p])) + $yourplanes[] = $this->planes[$p]; + } + + shuffle($this->winner); + foreach($candidates as $c) { + + // We now have a list of winning strategy vectors for our second point + // Pick one that will force you into defensive mode. + // Pick a point close to you so we don't risk giving you two + // in a row when you block us. That would force *us* into + // defensive mode. + // We want: or: not: + // X|O| X| | X| | + // |0| O|O| |O| + // | | | | |O| + + if(count($this->you) == 1) { + foreach($this->winner as $w) { + if(in_array($this->me[0], $w) && in_array($c[1],$w) + && $this->uncontested_winner($w) + && $this->is_neighbor($this->you[0],$c[1])) { + return($c[1]); + } + } + } + } + + // You're somewhere else entirely or have made more than one move + // - any strategy vector which puts you on the defense will have to do + + foreach($candidates as $c) { + foreach($this->winner as $w) { + if(in_array($this->me[0], $w) && in_array($c[1],$w) + && $this->uncontested_winner($w)) { + return($c[1]); + } + } + } + } + + // worst case scenario, no strategy we can play, + // just find an empty space and take it + + for($x = 0; $x < $this->dimen; $x ++) + for($y = 0; $y < $this->dimen; $y ++) + for($z = 0; $z < $this->dimen; $z ++) + if((! $this->marked_yours($x,$y,$z)) + && (! $this->marked_mine($x,$y,$z))) { + if($this->handicap && $x == 1 && $y == 1 && $z == 1) + continue; + return(sprintf("%d%d%d",$x,$y,$z)); + } + + return ''; + } + + function marked_yours($x,$y,$z) { + $str = sprintf("%d%d%d",$x,$y,$z); + if(in_array($str,$this->you)) + return true; + return false; + } + + function marked_mine($x,$y,$z) { + $str = sprintf("%d%d%d",$x,$y,$z); + if(in_array($str,$this->me)) + return true; + return false; + } + + function is_yours($str) { + if(in_array($str,$this->you)) + return true; + return false; + } + + function is_mine($str) { + if(in_array($str,$this->me)) + return true; + return false; + } + + function get_corners($a) { + $total = array(); + if(count($a)) + foreach($a as $b) + if(in_array($b,$this->corners)) + $total[] = $b; + return $total; + } + + function uncontested_winner($w) { + if($this->handicap && in_array('111',$w)) + return false; + $contested = false; + if(count($this->you)) { + foreach($this->you as $you) + if(in_array($you,$w)) + $contested = true; + } + return (($contested) ? false : true); + } + + + function is_neighbor($p1,$p2) { + list($x1,$y1,$z1) = sscanf($p1, "%1d%1d%1d"); + list($x2,$y2,$z2) = sscanf($p2, "%1d%1d%1d"); + + if((($x1 == $x2) || ($x1 == $x2+1) || ($x1 == $x2-1)) && + (($y1 == $y2) || ($y1 == $y2+1) || ($y1 == $y2-1)) && + (($z1 == $z2) || ($z1 == $z2+1) || ($z1 == $z2-1))) + return true; + return false; + + } + + function my_best_plane() { + + $second_choice = array(); + shuffle($this->planes); + for($p = 0; $p < count($this->planes); $p ++ ) { + $contested = 0; + if($this->handicap && in_array('111',$this->planes[$p])) + continue; + if(! in_array($this->me[0],$this->planes[$p])) + continue; + foreach($this->you as $m) { + if(in_array($m,$this->planes[$p])) + $contested ++; + } + if(! $contested) + return($this->planes[$p]); + if($contested == 1) + $second_choice = $this->planes[$p]; + } + return $second_choice; + } + + + + + + + + function uncontested_plane() { + $freeplane = true; + shuffle($this->planes); + $pl = $this->planes; + + for($p = 0; $p < count($pl); $p ++ ) { + if($this->handicap && in_array('111',$pl[$p])) + continue; + foreach($this->you as $m) { + if(in_array($m,$pl[$p])) + $freeplane = false; + } + if(! $freeplane) { + $freeplane = true; + continue; + } + if($freeplane) + return($pl[$p]); + } + return array(); + } + + function fullboard() { + return false; + } + + function draw_board() { + if(! strlen($this->yours)) + $this->yours = 'XXX'; + $o .= "
handicap}/{$this->mefirst}/{$this->dimen}/{$this->yours}/{$this->mine}\" method=\"post\" />"; + for($x = 0; $x < $this->dimen; $x ++) { + $o .= ''; + for($y = 0; $y < $this->dimen; $y ++) { + $o .= ''; + for($z = 0; $z < $this->dimen; $z ++) { + $s = sprintf("%d%d%d",$x,$y,$z); + $winner = ((is_array($this->winning_play) && in_array($s,$this->winning_play)) ? " color: #FF0000; " : ""); + $bordertop = (($y != 0) ? " border-top: 2px solid #000;" : ""); + $borderleft = (($z != 0) ? " border-left: 2px solid #000;" : ""); + if($this->handicap && $x == 1 && $y == 1 && $z == 1) + $o .= ""; + elseif($this->marked_yours($x,$y,$z)) + $o .= ""; + elseif($this->marked_mine($x,$y,$z)) + $o .= ""; + else { + $val = sprintf("%d%d%d",$x,$y,$z); + $o .= ""; + } + } + $o .= ''; + } + $o .= '
 XO

'; + } + $o .= '
'; + return $o; + + } + + +} + diff --git a/boot.php b/boot.php index 322a4e307..7f6868936 100644 --- a/boot.php +++ b/boot.php @@ -196,6 +196,7 @@ class App { public $timezone; public $interactive = true; public $plugins; + public $apps; private $scheme; private $hostname; @@ -2453,7 +2454,7 @@ if(! function_exists('get_plink')) { function get_plink($item) { $a = get_app(); $plink = (((x($item,'plink')) && (! $item['private'])) ? '' : ''); + . $item['plink'] . '" title="' . t('link to source') . '" target="external-link" >' . t('link to source') . '' : ''); return $plink; }} diff --git a/include/nav.php b/include/nav.php index 94a71d227..228774d95 100644 --- a/include/nav.php +++ b/include/nav.php @@ -56,6 +56,10 @@ $a->page['nav'] .= '' . t('Register') . "\r\n"; + if(strlen($a->apps)) { + $a->page['nav'] .= '' . t('Apps') . "\r\n"; + } + $a->page['nav'] .= '' . t('Search') . "\r\n"; $a->page['nav'] .= '' . t('Directory') . "\r\n"; diff --git a/index.php b/index.php index 661676bfd..55edd072e 100644 --- a/index.php +++ b/index.php @@ -113,6 +113,13 @@ else check_config($a); +$arr = array('app_menu' => $a->apps); + +call_hooks('app_menu', $arr); + +$a->apps = $arr['app_menu']; + + /** * * We have already parsed the server path into $->argc and $a->argv @@ -132,10 +139,9 @@ else * further processing. */ - if(strlen($a->module)) { if(is_array($a->plugins) && in_array($a->module,$a->plugins) && file_exists("addon/{$a->module}/{$a->module}.php")) { - include("addon/{$a->module}/{$a->module}.php"); + include_once("addon/{$a->module}/{$a->module}.php"); if(function_exists($a->module . '_module')) $a->module_loaded = true; } @@ -155,6 +161,8 @@ if(strlen($a->module)) { } } + + /* initialise content region */ if(! x($a->page,'content'))