]> git.mxchange.org Git - friendica.git/blob - addon/tictac/tictac.php
Merge branch 'omigeot-master'
[friendica.git] / addon / tictac / tictac.php
1 <?php
2
3
4 function tictac_install() {
5         register_hook('app_menu', 'addon/tictac/tictac.php', 'tictac_app_menu');
6 }
7
8 function tictac_uninstall() {
9         unregister_hook('app_menu', 'addon/tictac/tictac.php', 'tictac_app_menu');
10
11 }
12
13 function tictac_app_menu($a,&$b) {
14         $b['app_menu'] .= '<div class="app-title"><a href="tictac">' . t('Three Dimensional Tic-Tac-Toe') . '</a></div>'; 
15 }
16
17
18 function tictac_module() {
19         return;
20 }
21
22
23
24
25
26 function tictac_content(&$a) {
27
28         $o = '';
29
30   if($_POST['move']) {
31     $handicap = $a->argv[1];
32     $mefirst = $a->argv[2];
33     $dimen = $a->argv[3];
34     $yours = $a->argv[4];
35     $mine  = $a->argv[5];
36     
37     $yours .= $_POST['move'];
38   }
39   elseif($a->argc > 1) {
40     $handicap = $a->argv[1];
41     $dimen = 3;
42   }
43   else {
44    $dimen = 3;
45   }
46
47   $o .=  '<h3>' . t('3D Tic-Tac-Toe') . '</h3><br />';
48
49   $t = new tictac($dimen,$handicap,$mefirst,$yours,$mine);
50   $o .= $t->play();
51
52   $o .=  '<a href="tictac">' . t('New game') . '</a><br />';
53   $o .=  '<a href="tictac/1">' . t('New game with handicap') . '</a><br />';
54   $o .=  '<p>' . t('Three dimensional tic-tac-toe is just like the traditional game except that it is played on multiple levels simultaneously. ');
55   $o .= t('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.');
56   $o .= '</p><p>'; 
57   $o .= t('The handicap game disables the center position on the middle level because the player claiming this square often has an unfair advantage.');
58   $o .= '</p>';
59
60   return $o;
61
62 }
63
64 class tictac {
65   private $dimen;
66   private $first_move = true;
67   private $handicap = 0;
68   private $yours;
69   private $mine;
70   private $winning_play;  
71   private $you;
72   private $me;
73   private $debug = 1;
74   private $crosses = array('011','101','110','112','121','211');
75
76 /*
77     '001','010','011','012','021',
78     '101','110','111','112','121',
79     '201','210','211','212','221');
80 */
81
82   private $corners = array(
83     '000','002','020','022',
84     '200','202','220','222');
85
86   private $planes = array(
87     array('000','001','002','010','011','012','020','021','022'), // horiz 1
88     array('100','101','102','110','111','112','120','121','122'), // 2
89     array('200','201','202','210','211','212','220','221','222'), // 3
90     array('000','010','020','100','110','120','200','210','220'), // vert left
91     array('000','001','002','100','101','102','200','201','202'), // vert top
92     array('002','012','022','102','112','122','202','212','222'), // vert right
93     array('020','021','022','120','121','122','220','221','222'), // vert bot
94     array('010','011','012','110','111','112','210','211','212'), // left vertx
95     array('001','011','021','101','111','221','201','211','221'), // top vertx
96     array('000','001','002','110','111','112','220','221','222'), // diag top
97     array('020','021','022','110','111','112','200','201','202'), // diag bot
98     array('000','010','020','101','111','121','202','212','222'), // diag left
99     array('002','012','022','101','111','121','200','210','220'), // diag right
100     array('002','011','020','102','111','120','202','211','220'), // diag x
101     array('000','011','022','100','111','122','200','211','222')  // diag x
102     
103   );
104
105
106   private $winner = array(
107      array('000','001','002'),         // board 0 winners  - left corner across
108      array('000','010','020'),         // down
109      array('000','011','022'),         // diag
110      array('001','011','021'),         // middle-top down
111      array('010','011','012'),         // middle-left across
112      array('002','011','020'),         // right-top diag
113      array('002','012','022'),         // right-top down
114      array('020','021','022'),        // bottom-left across
115      array('100','101','102'),      // board 1 winners
116      array('100','110','120'),
117      array('100','111','122'),
118      array('101','111','121'),
119      array('110','111','112'),
120      array('102','111','120'),
121      array('102','112','122'),
122      array('120','121','122'),
123      array('200','201','202'),    // board 2 winners
124      array('200','210','220'),
125      array('200','211','222'),
126      array('201','211','221'),
127      array('210','211','212'),
128      array('202','211','220'),
129      array('202','212','222'),
130      array('220','221','222'),
131      array('000','100','200'),      // top-left corner 3d
132      array('000','101','202'),
133      array('000','110','220'),
134      array('000','111','222'),
135      array('001','101','201'),      // top-middle 3d
136      array('001','111','221'),
137      array('002','102','202'),      // top-right corner 3d
138      array('002','101','200'),
139      array('002','112','222'),
140      array('002','111','220'),
141      array('010','110','210'),      // left-middle 3d
142      array('010','111','212'),
143      array('011','111','211'),      // middle-middle 3d
144      array('012','112','212'),      // right-middle 3d
145      array('012','111','210'),
146      array('020','120','220'),      // bottom-left corner 3d
147      array('020','110','200'),
148      array('020','121','222'),
149      array('020','111','202'),
150      array('021','121','221'),      // bottom-middle 3d
151      array('021','111','201'),
152      array('022','122','222'),      // bottom-right corner 3d
153      array('022','121','220'),
154      array('022','112','202'),
155      array('022','111','200')
156
157   );
158
159   function __construct($dimen,$handicap,$mefirst,$yours,$mine) {
160     $this->dimen = 3;
161     $this->handicap = (($handicap) ? 1 : 0);
162     $this->mefirst = (($mefirst) ? 1 : 0);
163     $this->yours = str_replace('XXX','',$yours);
164     $this->mine  = $mine;
165     $this->you = $this->parse_moves('you');
166     $this->me  = $this->parse_moves('me');
167
168     if(strlen($yours))
169       $this->first_move = false;
170   }
171
172   function play() {
173
174      if($this->first_move) {
175        if(rand(0,1) == 1) {
176          $o .=  '<div class="error-message">' . t('You go first...') . '</div><br />';
177          $this->mefirst = 0;
178          $o .= $this->draw_board();
179          return $o;
180        }
181        $o .=  '<div class="error-message">' . t('I\'m going first this time...') . ' </div><br />';
182        $this->mefirst = 1;
183
184      }
185
186      if($this->check_youwin()) {
187        $o .=  '<div class="error-message">' . t('You won!') . '</div><br />';
188        $o .= $this->draw_board();
189        return $o;
190      }
191
192      if($this->fullboard())
193        $o .=  '<div class="error-message">' . t('"Cat" game!') . '</div><br />';
194
195      $move = $this->winning_move();
196      if(strlen($move)) {
197        $this->mine .= $move;
198        $this->me = $this->parse_moves('me');
199      }
200      else {
201        $move = $this->defensive_move();
202        if(strlen($move)) {
203          $this->mine .= $move;
204          $this->me = $this->parse_moves('me');
205        }
206        else {  
207          $move = $this->offensive_move();
208          if(strlen($move)) {
209            $this->mine .= $move;
210            $this->me = $this->parse_moves('me');
211          }
212        }
213      }
214
215      if($this->check_iwon())
216        $o .=  '<div class="error-message">' . t('I won!') . '</div><br />';
217      if($this->fullboard())
218        $o .=  '<div class="error-message">' . t('"Cat" game!') . '</div><br />';
219      $o .= $this->draw_board();
220         return $o;
221   }
222
223   function parse_moves($player) {
224     if($player == 'me')
225       $str = $this->mine;
226     if($player == 'you')
227       $str = $this->yours;
228     $ret = array();
229       while(strlen($str)) {
230          $ret[] = substr($str,0,3);
231          $str = substr($str,3);
232       }
233     return $ret;
234   }
235
236
237   function check_youwin() {
238     for($x = 0; $x < count($this->winner); $x ++) {
239       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)) {
240         $this->winning_play = $this->winner[$x];
241         return true;
242       }
243     }
244     return false;
245   }
246   function check_iwon() {
247     for($x = 0; $x < count($this->winner); $x ++) {
248       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)) {
249         $this->winning_play = $this->winner[$x];
250         return true;
251       }
252     }
253     return false;
254   }
255   function defensive_move() {
256
257     for($x = 0; $x < count($this->winner); $x ++) {
258       if(($this->handicap) && in_array('111',$this->winner[$x]))
259         continue;
260       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)))
261         return($this->winner[$x][2]);
262       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)))
263         return($this->winner[$x][1]);
264       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)))
265         return($this->winner[$x][0]);
266      }
267      return '';
268   }
269
270 function winning_move() {
271
272     for($x = 0; $x < count($this->winner); $x ++) {
273       if(($this->handicap) && in_array('111',$this->winner[$x]))
274         continue;
275       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)))
276         return($this->winner[$x][2]);
277       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)))
278         return($this->winner[$x][1]);
279       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)))
280         return($this->winner[$x][0]);
281      }
282
283 }
284
285   function offensive_move() {
286
287     shuffle($this->planes);
288     shuffle($this->winner);
289     shuffle($this->corners);
290     shuffle($this->crosses);
291
292     if(! count($this->me)) {
293       if($this->handicap) {
294         $p = $this->uncontested_plane();
295         foreach($this->corners as $c)
296           if((in_array($c,$p)) 
297             && (! $this->is_yours($c)) && (! $this->is_mine($c)))
298               return($c);
299       }
300       else {
301         if((! $this->marked_yours(1,1,1)) && (! $this->marked_mine(1,1,1)))
302           return '111';
303         $p = $this->uncontested_plane();
304         foreach($this->crosses as $c)
305           if((in_array($c,$p))
306             && (! $this->is_yours($c)) && (! $this->is_mine($c)))
307             return($c);
308       }
309     }
310
311     if($this->handicap) {
312       if(count($this->me) >= 1) {
313         if(count($this->get_corners($this->me)) == 1) {
314           if(in_array($this->me[0],$this->corners)) {
315             $p = $this->my_best_plane();
316             foreach($this->winner as $w) {
317               if((in_array($w[0],$this->you)) 
318               || (in_array($w[1],$this->you))
319               || (in_array($w[2],$this->you)))
320                 continue;        
321               if(in_array($w[0],$this->corners) 
322                 && in_array($w[2],$this->corners)
323                 && in_array($w[0],$p) && in_array($w[2],$p)) {
324                   if($this->me[0] == $w[0])
325                     return($w[2]);
326                   elseif($this->me[0] == $w[2])
327                     return($w[0]);
328               }
329             }
330           }
331         }
332         else {
333           $r = $this->get_corners($this->me);
334           if(count($r) > 1) {
335             $w1 = array(); $w2 = array();
336             foreach($this->winner as $w) {
337               if(in_array('111',$w))
338                 continue;
339               if(($r[0] == $w[0]) || ($r[0] == $w[2]))
340                 $w1[] = $w;
341               if(($r[1] == $w[0]) || ($r[1] == $w[2]))
342                 $w2[] = $w;
343             }
344             if(count($w1) && count($w2)) {
345               foreach($w1 as $a) {
346                 foreach($w2 as $b) {
347                   if((in_array($a[0],$this->you)) 
348                   || (in_array($a[1],$this->you))
349                   || (in_array($a[2],$this->you))
350                   || (in_array($b[0],$this->you))
351                   || (in_array($b[1],$this->you))
352                   || (in_array($b[2],$this->you)))
353                     continue; 
354                   if(($a[0] == $b[0]) && ! $this->is_mine($a[0])) {
355                     return $a[0];
356                   }
357                   elseif(($a[2] == $b[2]) && ! $this->is_mine($a[2])) {
358                     return $a[2];
359                   }
360                 }
361               }
362             }
363           }
364         }
365       }
366     }
367
368  //&& (count($this->me) == 1) && (count($this->you) == 1)
369  //     && in_array($this->you[0],$this->corners)
370  //     && $this->is_neighbor($this->me[0],$this->you[0])) {
371
372       // Yuck. You foiled my plan. Since you obviously aren't playing to win, 
373       // I'll try again. You may keep me busy for a few rounds, but I'm 
374       // gonna' get you eventually.
375
376 //      $p = $this->uncontested_plane();
377  //     foreach($this->crosses as $c)
378    //     if(in_array($c,$p))
379      //     return($c);
380
381 //    }
382
383
384     // find all the winners containing my points.
385     $mywinners = array();
386     foreach($this->winner as $w)
387       foreach($this->me as $m)
388         if((in_array($m,$w)) && (! in_array($w,$mywinners)))
389           $mywinners[] = $w;
390
391     // find all the rules where my points are in the center.
392       $trythese = array();
393       if(count($mywinners)) {
394         foreach($mywinners as $w) {
395           foreach($this->me as $m) {
396             if(($m == $w[1]) && ($this->uncontested_winner($w))
397               && (! in_array($w,$trythese)))
398             $trythese[] = $w;
399           }
400         }
401       }
402
403       $myplanes = array();
404       for($p = 0; $p < count($this->planes); $p ++) {
405         if($this->handicap && in_array('111',$this->planes[$p]))
406           continue;
407         foreach($this->me as $m)
408           if((in_array($m,$this->planes[$p])) 
409             && (! in_array($this->planes[$p],$myplanes)))
410               $myplanes[] = $this->planes[$p];
411       }
412       shuffle($myplanes);
413
414     // find all winners which share an endpoint, and which are uncontested
415       $candidates = array();
416       if(count($trythese) && count($myplanes)) {
417         foreach($trythese as $t) {
418           foreach($this->winner as $w) {
419             if(! $this->uncontested_winner($w))
420               continue;
421             if((in_array($t[0],$w)) || (in_array($t[2],$w))) {
422               foreach($myplanes as $p)
423                 if(in_array($w[0],$p) && in_array($w[1],$p) && in_array($w[2],$p) && ($w[1] != $this->me[0]))
424                   if(! in_array($w,$candidates))
425                     $candidates[] = $w;
426             }
427           }
428         }
429       }
430
431       // Find out if we are about to force a win.
432       // Looking for two winning vectors with a common endpoint
433       // and where we own the middle of both - we are now going to 
434       // grab the endpoint. The game isn't yet over but we've already won.
435
436       if(count($candidates)) {
437         foreach($candidates as $c) {
438           if(in_array($c[1],$this->me)) {
439             // return endpoint
440             foreach($trythese as $t)
441               if($t[0] == $c[0])
442                 return($t[0]);
443               elseif($t[2] == $c[2])
444                 return($t[2]);
445           }
446        }
447
448        // find opponents planes
449       $yourplanes = array();
450       for($p = 0; $p < count($this->planes); $p ++) {
451         if($this->handicap && in_array('111',$this->planes[$p]))
452           continue;
453         if(in_array($this->you[0],$this->planes[$p]))
454           $yourplanes[] = $this->planes[$p];
455       }
456
457       shuffle($this->winner);
458       foreach($candidates as $c) {
459
460          // We now have a list of winning strategy vectors for our second point
461          // Pick one that will force you into defensive mode.
462          // Pick a point close to you so we don't risk giving you two
463          // in a row when you block us. That would force *us* into 
464          // defensive mode.
465          // We want:        or:         not:
466          //           X|O|     X| |       X| |
467          //            |O|     O|O|        |O|
468          //            | |      | |        |O|
469
470          if(count($this->you) == 1) {
471            foreach($this->winner as $w) {
472              if(in_array($this->me[0], $w) && in_array($c[1],$w) 
473                && $this->uncontested_winner($w) 
474                && $this->is_neighbor($this->you[0],$c[1])) {
475                  return($c[1]);
476              }  
477            }
478          }
479        }         
480
481        // You're somewhere else entirely or have made more than one move 
482        // - any strategy vector which puts you on the defense will have to do
483
484        foreach($candidates as $c) {
485          foreach($this->winner as $w) {
486            if(in_array($this->me[0], $w) && in_array($c[1],$w) 
487              && $this->uncontested_winner($w)) {
488                    return($c[1]);
489            }  
490          }
491        }
492      }
493
494     // worst case scenario, no strategy we can play, 
495     // just find an empty space and take it
496
497     for($x = 0; $x < $this->dimen; $x ++)
498       for($y = 0; $y < $this->dimen; $y ++)
499         for($z = 0; $z < $this->dimen; $z ++)
500           if((! $this->marked_yours($x,$y,$z)) 
501             && (! $this->marked_mine($x,$y,$z))) {
502             if($this->handicap && $x == 1 && $y == 1 && $z == 1)
503               continue;
504             return(sprintf("%d%d%d",$x,$y,$z));
505           }
506        
507   return '';
508   }
509
510   function marked_yours($x,$y,$z) {
511    $str = sprintf("%d%d%d",$x,$y,$z);
512    if(in_array($str,$this->you))
513      return true;
514    return false;
515   }
516
517   function marked_mine($x,$y,$z) {
518    $str = sprintf("%d%d%d",$x,$y,$z);
519    if(in_array($str,$this->me))
520      return true;
521    return false;
522   }
523
524   function is_yours($str) {
525    if(in_array($str,$this->you))
526      return true;
527    return false;
528   }
529
530   function is_mine($str) {
531    if(in_array($str,$this->me))
532      return true;
533    return false;
534   }
535
536   function get_corners($a) {
537     $total = array();
538     if(count($a))
539       foreach($a as $b)
540         if(in_array($b,$this->corners))
541           $total[] = $b;
542     return $total;
543   }
544
545   function uncontested_winner($w) {
546     if($this->handicap && in_array('111',$w))
547       return false;
548     $contested = false;
549     if(count($this->you)) {
550       foreach($this->you as $you)
551         if(in_array($you,$w))
552           $contested = true;
553     }
554     return (($contested) ? false : true);
555   }
556
557
558   function is_neighbor($p1,$p2) {
559    list($x1,$y1,$z1) = sscanf($p1, "%1d%1d%1d");
560    list($x2,$y2,$z2) = sscanf($p2, "%1d%1d%1d");
561
562    if((($x1 == $x2) || ($x1 == $x2+1) || ($x1 == $x2-1)) &&
563       (($y1 == $y2) || ($y1 == $y2+1) || ($y1 == $y2-1)) &&
564       (($z1 == $z2) || ($z1 == $z2+1) || ($z1 == $z2-1)))
565      return true;
566    return false;
567
568   }
569
570   function my_best_plane() {
571
572     $second_choice = array();
573     shuffle($this->planes);
574     for($p = 0; $p < count($this->planes); $p ++ ) {
575       $contested = 0;
576       if($this->handicap && in_array('111',$this->planes[$p]))
577         continue;
578       if(! in_array($this->me[0],$this->planes[$p]))
579         continue;
580       foreach($this->you as $m) {
581         if(in_array($m,$this->planes[$p]))
582           $contested ++;   
583       }
584       if(! $contested)
585         return($this->planes[$p]);
586       if($contested == 1)
587         $second_choice = $this->planes[$p];
588     }
589     return $second_choice;
590   }
591
592
593
594
595
596
597
598   function uncontested_plane() {
599     $freeplane = true;
600     shuffle($this->planes);
601     $pl = $this->planes;
602
603     for($p = 0; $p < count($pl); $p ++ ) {
604         if($this->handicap && in_array('111',$pl[$p]))
605           continue;
606        foreach($this->you as $m) {
607          if(in_array($m,$pl[$p]))   
608            $freeplane = false;         
609        }
610        if(! $freeplane) {
611          $freeplane = true;
612          continue;
613        }
614        if($freeplane)
615          return($pl[$p]);
616     }
617     return array();
618   }
619
620   function fullboard() {
621    return false;
622   }
623
624   function draw_board() {
625     if(! strlen($this->yours))
626       $this->yours = 'XXX';
627     $o .=  "<form action=\"tictac/{$this->handicap}/{$this->mefirst}/{$this->dimen}/{$this->yours}/{$this->mine}\" method=\"post\" />";
628     for($x = 0; $x < $this->dimen; $x ++) {
629       $o .=  '<table>';
630       for($y = 0; $y < $this->dimen; $y ++) {
631         $o .=  '<tr>';
632         for($z = 0; $z < $this->dimen; $z ++) {
633           $s = sprintf("%d%d%d",$x,$y,$z);
634           $winner = ((is_array($this->winning_play) && in_array($s,$this->winning_play)) ? " color: #FF0000; " : "");
635           $bordertop = (($y != 0) ? " border-top: 2px solid #000;" : "");
636           $borderleft = (($z != 0) ? " border-left: 2px solid #000;" : "");
637           if($this->handicap && $x == 1 && $y == 1 && $z == 1)
638             $o .=  "<td style=\"width: 25px; height: 25px; $bordertop $borderleft\" align=\"center\">&nbsp;</td>";                  
639           elseif($this->marked_yours($x,$y,$z))
640             $o .=  "<td style=\"width: 25px; height: 25px; $bordertop $borderleft $winner\" align=\"center\">X</td>";
641           elseif($this->marked_mine($x,$y,$z))
642             $o .=  "<td style=\"width: 25px; height: 25px; $bordertop $borderleft $winner\" align=\"center\">O</td>";
643           else {
644             $val = sprintf("%d%d%d",$x,$y,$z);
645             $o .=  "<td style=\"width: 25px; height: 25px; $bordertop $borderleft\" align=\"center\"><input type=\"checkbox\" name=\"move\" value=\"$val\" onclick=\"this.form.submit();\" /></td>";
646           }
647         }
648         $o .=  '</tr>';
649       }
650       $o .=  '</table><br />';
651     }
652     $o .=  '</form>';
653         return $o;
654
655   }
656
657
658 }
659