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