diff --git a/addon/tictac/tictac.php b/addon/tictac/tictac.php
new file mode 100644
index 0000000000..d44254411a
--- /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 .= "';
+ return $o;
+
+ }
+
+
+}
+
diff --git a/boot.php b/boot.php
index 322a4e3074..7f68689360 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" >' : '');
return $plink;
}}
diff --git a/include/nav.php b/include/nav.php
index 94a71d227a..228774d958 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 661676bfd8..55edd072e4 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'))