BUGFIX: check for 2nd 10 of hearts in wedding and poverty games
[e-DoKo.git] / include / functions.php
1 <?php
2 /* Copyright 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Arun Persaud <arun@nubati.net>
3  *
4  *   This file is part of e-DoKo.
5  *
6  *   e-DoKo is free software: you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation, either version 3 of the License, or
9  *   (at your option) any later version.
10  *
11  *   e-DoKo is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with e-DoKo.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 /* make sure that we are not called from outside the scripts,
22  * use a variable defined in config.php to check this
23  */
24 if(!isset($HOST))
25   exit;
26
27 function config_check()
28 {
29   global $EmailName,$EMAIL_REPLY,$ADMIN_NAME,$ADMIN_EMAIL,$DB_work;
30
31   /* check if some variables are set in the config file, else set defaults */
32   if(!isset($EmailName))
33     $EmailName='[DoKo] ';
34   if(isset($EMAIL_REPLY))
35     {
36       ini_set('sendmail_from',$EMAIL_REPLY);
37     }
38   if(!isset($ADMIN_NAME))
39     {
40       output_header();
41       echo '<h1>Setup not completed</h1>';
42       echo 'You need to set $ADMIN_NAME in config.php.';
43       output_footer();
44       exit();
45     }
46   if(!isset($ADMIN_EMAIL))
47     {
48       output_header();
49       echo '<h1>Setup not completed</h1>';
50       echo 'You need to set $ADMIN_EMAIL in config.php. '.
51         'If something goes wrong an email will be send to this address.';
52       output_footer();
53       exit();
54     }
55   if(!isset($DB_work))
56     {
57       output_header();
58       echo '<h1>Setup not completed</h1>';
59       echo 'You need to set $DB_work in config.php. '.
60         'If this is set to anything else than 0, the game will be suspended and one can work safely on the database. '.
61         'A message will be displayed that it will probably take about N minutes, with N being the number $DB_work is set to. '.
62         'The default should be 0 for the game to work.';
63       output_footer();
64       exit();
65     }
66   if($DB_work)
67     {
68       output_header();
69       echo '<div class="WIP">'.
70         _("Working on some aspect of e-DoKo... This will probably take max. $DB_work minutes. It could be over in a few seconds too though ;)").
71         '</div>';
72       output_footer();
73       exit();
74     }
75
76   return;
77 }
78
79 /* define possible status for email subsjects */
80 define("GAME_CANCELED",         0);
81 define("GAME_CANCELED_POVERTY", 1);
82 define("GAME_CANCELED_TIMEOUT", 2);
83 define("GAME_YOUR_TURN",        3);
84 define("GAME_READY",            4);
85 define("GAME_POVERTY",          5);
86 define("GAME_DPOVERTY",         6);
87 define("GAME_OVER",             7);
88 define("GAME_RECOVERY",         8);
89 define("GAME_REMINDER",         9);
90 define("GAME_NEW",             10);
91
92 /* define possible status for showing cards */
93
94 define("CARDS_EMPTY",       0); /* show player's hand*/
95 define("CARDS_SHOW",        1); /* show player's hand*/
96 define("CARDS_MYTURN",      2); /* show radiobuttons for cards that can be played*/
97 define("CARDS_EXCHANGE",    3); /* do we need to return cards to our partner in poverty?*/
98 define("CARDS_GAMEOVER_ME", 4); /* show all cards from everyone*/
99 define("CARDS_GAMEOVER",    5); /* show all cards from everyone (looking at someone else's game)*/
100
101  /*   cards_status: SHOW      show our hand
102  *                 MYTURN    show radiobutton for cards that can be played
103  *                 EXCHANGE  do we need to return cards to our partner in poverty?
104  *                 GAMEOVER  show all cards from everyone
105  */
106
107 function mymail($uid,$gameid=0,$type,$message)
108 {
109   global $EmailName,$WIKI,$PREF;
110
111   /* uid can be either a single uid or an array, convert everything to
112    *  an array, so that we can loop over it */
113   if(!is_array($uid))
114     {
115       $to_uid=$uid;
116       $uid = array();
117       $uid[]=$to_uid;
118     }
119
120   foreach($uid as $user)
121     {
122       /* do we send the email right away or save it in the database? */
123       $send_now = 1;
124
125       $name = DB_get_name('userid',$user);
126       $To   = DB_get_email('userid',$user);
127
128       /* check if user wants email right away or if we should save it in
129        * the database for later delivery
130        */
131
132       $uidPREF = DB_get_PREF($user);
133       if( $uidPREF['digest'] != 'digest-off' )
134         $send_now = 0;
135       /* use local language */
136       set_language($uidPREF['language']);
137
138       $header  = sprintf(_('Hello %s'),$name);
139       $header .= "\n\n";
140
141       /* add standard header and footer */
142       $subject = "$EmailName ";
143       if($gameid)
144         $game = DB_format_gameid($gameid);
145       else
146         $game = '';
147
148       switch($type)
149         {
150         case GAME_CANCELED:
151           $subject.=sprintf(_('Game %s canceled'),$game);
152           break;
153         case GAME_CANCELED_POVERTY:
154           $subject.=sprintf(_('Game %s canceled (poverty not resolved)'),$game);
155           break;
156         case GAME_CANCELED_TIMEOUT:
157           $subject.=sprintf(_('Game %s canceled (timed out)'),$game);
158           break;
159         case GAME_YOUR_TURN:
160           $subject.=sprintf(_('A card has been played in game %s'),$game);
161           break;
162         case GAME_READY:
163           $subject.=sprintf(_('Ready, set, go... (game %s)'),$game);
164           break;
165         case GAME_POVERTY:
166           $subject.=sprintf(_('Poverty (game %s)'),$game);
167           break;
168         case GAME_DPOVERTY:
169           $subject.=sprintf(_('Double poverty (game %s)'),$game);
170           break;
171         case GAME_OVER:
172           $subject.=sprintf(_('Game over (game %s)'),$game);
173           break;
174         case GAME_RECOVERY:
175           $subject.=_('Recovery');
176           break;
177         case GAME_REMINDER:
178           $subject.=sprintf(_("Reminder: game %s it's your turn"),$game);
179           break;
180         case GAME_NEW:
181           $subject.=sprintf(_('You are invited to a game of DoKo (game %s)'),$game);
182           break;
183         default:
184           $subject.=sprintf(_('Problem with email, contact admin (errorcode %d)'),$gameid);
185         }
186
187       /* standard goodbye */
188       $footer  = "\n"._('Have a nice day')."\n   "._('your E-Doko service department').
189         "\n\n".
190         "-- \n".
191         _('You can change your mail delivery mode in the preference menu.').
192         "\n".
193         _('web').': http://doko.nubati.net   '.
194         _('help, bugs, etc.').": $WIKI";
195
196       if($send_now)
197         sendmail($To,$subject,$header.$message.$footer);
198       else
199         {
200           /* store email in database */
201           DB_digest_insert_email($To,$message,$type,$gameid);
202         }
203     }
204
205   /* reset language to original user*/
206   set_language($PREF['language']);
207
208   return;
209 }
210
211 function sendmail($To,$Subject,$message)
212 {
213   /* this function sends the mail or outputs to the screen in case of debugging */
214   global $debug,$EMAIL_REPLY;
215
216   $header = "";
217
218   if(isset($EMAIL_REPLY))
219     $header .= "From: e-DoKo daemon <$EMAIL_REPLY>\r\n";
220
221   $header .= "Content-Type: text/plain; charset = \"UTF-8\";\r\n";
222   $header .= "Content-Transfer-Encoding: 8bit\r\n";
223   $header .= "\r\n";
224
225   if($debug)
226     {
227       /* display email on screen,
228        * change txt -> html
229        */
230       $message = str_replace("\n","<br />\n",$message);
231       $message = preg_replace("#(\w+://[^<>\s]+[\w/]*)#",
232                               "<a href=\"$1\">$1</a>", $message);
233
234       echo "<br />To: $To<br />";
235       echo $header."<br />";
236       echo "Subject: $Subject <br />$message<br />\n";
237     }
238   else
239     mail($To,$Subject,$message,$header);
240
241   return;
242 }
243
244 function myisset()
245 {
246   /* returns 1 if all names passed as args are defined by a GET or POST statement,
247    * else return 0
248    */
249
250   $ok   = 1;
251   $args = func_get_args();
252
253   foreach($args as $arg)
254     {
255       $ok = $ok * isset($_REQUEST[$arg]);
256       /*echo "$arg: ok = $ok <br />";
257        */
258     }
259   return $ok;
260 }
261
262 function myerror($message)
263 {
264   echo "<span class=\"error\">".htmlspecialchars($message)."</span>\n";
265   sendmail($ADMIN_EMAIL,$EmailName." Error in Code",$message);
266   return;
267 }
268
269 function pos_array($c,$arr)
270 {
271   $ret = 0;
272
273   $i   = 0;
274   foreach($arr as $a)
275     {
276       $i++;
277       if($a == $c)
278         {
279           $ret = $i;
280           break;
281         }
282     }
283   return $ret;
284 }
285
286 function is_trump($c)
287 {
288   global $CARDS;
289
290   if(in_array($c,$CARDS["trump"]))
291     return 1;
292   else
293     return 0;
294 }
295
296 function is_same_suite($c1,$c2)
297 {
298   global $CARDS;
299
300   if(in_array($c1,$CARDS["trump"]   ) && in_array($c2,$CARDS["trump"]   ) ) return 1;
301   if(in_array($c1,$CARDS["clubs"]   ) && in_array($c2,$CARDS["clubs"]   ) ) return 1;
302   if(in_array($c1,$CARDS["hearts"]  ) && in_array($c2,$CARDS["hearts"]  ) ) return 1;
303   if(in_array($c1,$CARDS["spades"]  ) && in_array($c2,$CARDS["spades"]  ) ) return 1;
304   if(in_array($c1,$CARDS["diamonds"]) && in_array($c2,$CARDS["diamonds"]) ) return 1;
305
306   return 0;
307 }
308
309 function compare_cards($a,$b,$game)
310 {
311   /* if "a" is higher than "b" return 1, else 0, "a" being the card first played */
312
313   global $CARDS;
314   global $RULES;
315   global $GAME;
316
317   /* first map all cards to the odd number,
318    * this insure that the first card wins the trick
319    * if they are the same card
320    */
321   if( $a/2 - (int)($a/2) != 0.5)
322     $a--;
323   if( $b/2 - (int)($b/2) != 0.5)
324     $b--;
325
326   /* check for schweinchen and ten of hearts*/
327   switch($game)
328     {
329     case "normal":
330     case "silent":
331     case "trump":
332     case "wedding":
333       if($RULES['schweinchen']=='both' && $GAME['schweinchen-who'])
334         {
335           if($a == 19 || $a == 20 )
336             return 1;
337           if($b == 19 || $b == 20 )
338             return 0;
339         }
340       else if($RULES['schweinchen']=='second' && $GAME['schweinchen-second'])
341         {
342           if($a == 19 || $a == 20 )
343             return 1;
344           if($b == 19 || $b == 20 )
345             return 0;
346         }
347       else if($RULES['schweinchen']=='secondaftercall' && $GAME['schweinchen-who'] && $GAME['schweinchen-second'] )
348         {
349           /* check if a call was made either by the player or his partner. If so activate Schweinchen rule. */
350           if(DB_get_call_by_hash($GAME['schweinchen-who']) || DB_get_partner_call_by_hash($GAME['schweinchen-who']) )
351             {
352               if($a == 19 || $a == 20 )
353                 return 1;
354               if($b == 19 || $b == 20 )
355                 return 0;
356             }
357           /* if not, do nothing and the foxes are just handeled as normal trump */
358         }
359         ;
360     case "poverty":
361     case "dpoverty":
362     case "heart":
363     case "spade":
364     case "club":
365       /* check for ten of hearts rule */
366       if($RULES["dullen"]=="secondwins")
367         if($a==1 && $b==1) /* both 10 of hearts */
368           return 0;        /* second one wins.*/
369     case "trumpless":
370     case "jack":
371     case "queen":
372       /* no special cases here */
373     }
374
375   /* normal case */
376   if(is_trump($a) && is_trump($b) && $a<=$b)
377     return 1;
378   else if(is_trump($a) && is_trump($b) )
379     return 0;
380   else
381     { /*$a is not a trump */
382       if(is_trump($b))
383         return 0;
384       else
385         { /* both no trump */
386
387           /* both clubs? */
388           $posA = pos_array($a,$CARDS["clubs"]);
389           $posB = pos_array($b,$CARDS["clubs"]);
390           if($posA && $posB)
391             if($posA <= $posB)
392               return 1;
393             else
394               return 0;
395
396           /* both spades? */
397           $posA = pos_array($a,$CARDS["spades"]);
398           $posB = pos_array($b,$CARDS["spades"]);
399           if($posA && $posB)
400             if($posA <= $posB)
401               return 1;
402             else
403               return 0;
404
405           /* both hearts? */
406           $posA = pos_array($a,$CARDS["hearts"]);
407           $posB = pos_array($b,$CARDS["hearts"]);
408           if($posA && $posB)
409             if($posA <= $posB)
410               return 1;
411             else
412               return 0;
413
414           /* both diamonds? */
415           $posA = pos_array($a,$CARDS["diamonds"]);
416           $posB = pos_array($b,$CARDS["diamonds"]);
417           if($posA && $posB)
418             if($posA <= $posB)
419               return 1;
420             else
421               return 0;
422
423           /* not the same suit and no trump: a wins */
424           return 1;
425         }
426     }
427 }
428
429 function get_winner($p,$mode)
430 {
431   /* get all 4 cards played in a trick, in the order they are played */
432   $tmp = $p[1];
433   $c1    = $tmp["card"];
434   $c1pos = $tmp["pos"];
435
436   $tmp = $p[2];
437   $c2    = $tmp["card"];
438   $c2pos = $tmp["pos"];
439
440   $tmp = $p[3];
441   $c3    = $tmp["card"];
442   $c3pos = $tmp["pos"];
443
444   $tmp = $p[4];
445   $c4    = $tmp["card"];
446   $c4pos = $tmp["pos"];
447
448   /* first card is better than all the rest */
449   if( compare_cards($c1,$c2,$mode) && compare_cards($c1,$c3,$mode) && compare_cards($c1,$c4,$mode) )
450     return $c1pos;
451
452   /* second card is better than first and better than the rest */
453   if( !compare_cards($c1,$c2,$mode) &&  compare_cards($c2,$c3,$mode) && compare_cards($c2,$c4,$mode) )
454     return $c2pos;
455
456   /* third card is better than first card and better than last */
457   if( !compare_cards($c1,$c3,$mode) &&  compare_cards($c3,$c4,$mode) )
458     /* if second card is better than first, third card needs to be even better */
459     if( !compare_cards($c1,$c2,$mode) && !compare_cards($c2,$c3,$mode) )
460       return $c3pos;
461     /* second is worse than first, e.g. not following suite */
462     else if (compare_cards($c1,$c2,$mode) )
463       return $c3pos;
464
465   /* non of the above */
466   return $c4pos;
467 }
468
469 function count_nines($cards)
470 {
471   $nines = 0;
472
473   foreach($cards as $c)
474     {
475       if($c == "25" || $c == "26") $nines++;
476       else if($c == "33" || $c == "34") $nines++;
477       else if($c == "41" || $c == "42") $nines++;
478       else if($c == "47" || $c == "48") $nines++;
479     }
480
481   return $nines;
482 }
483
484 function check_wedding($cards)
485 {
486
487   if( in_array("3",$cards) && in_array("4",$cards) )
488     return 1;
489
490   return 0;
491 }
492
493 function count_trump($cards,$status='pregame')
494 {
495   global $RULES;
496
497   $trump = 0;
498
499   /* count each trump, including the foxes, since this is used to determine poverty status */
500   foreach($cards as $c)
501     if( (int)($c) <27)
502       $trump++;
503
504   /* In case we really want to know the amount of trump, we can use the status variable.
505    * This is needed for example to figure out what icon to display on the table in case of
506    * trump given back in poverty */
507   if($status=='all') return $trump;
508
509   /* normally foxes don't count as trump, so we substract them here
510    * in case someone has schweinchen, one or two of them should count as trump
511    * though, so we need to add one trump for those cases */
512
513   /* subtract foxes */
514   if( in_array("19",$cards))
515     $trump--;
516   if( in_array("20",$cards) )
517     $trump--;
518
519   /* handle case where player has schweinchen */
520   if( in_array("19",$cards) && in_array("20",$cards) )
521     switch($RULES["schweinchen"])
522       {
523       case "both":
524         /* add two, in case the player has both foxes (schweinchen) */
525         $trump++;
526         $trump++;
527         break;
528       case "second":
529       case "secondaftercall":
530         /* add one, in case the player has both foxes (schweinchen) */
531         $trump++;
532         break;
533       case "none":
534         break;
535       }
536
537   return $trump;
538 }
539
540 function check_low_trump($cards)
541 {
542   global $RULES;
543
544   if($RULES['lowtrump']=='none')
545     return 0;
546
547   /* check if we have low trump */
548
549   $lowtrump=1;
550   foreach($cards as $card)
551     {
552       /* card a trump, but not a diamond? */
553       if( $card<19 )
554          $lowtrump=0;
555     }
556
557   /* handle case where player has schweinchen */
558   if( in_array("19",$cards) && in_array("20",$cards) )
559     switch($RULES["schweinchen"])
560       {
561       case "both":
562       case "second":
563       case "secondaftercall":
564         $lowtrump=0;
565         break;
566       case "none":
567         break;
568       }
569
570   return $lowtrump;
571 }
572
573 function  create_array_of_random_numbers($useridA,$useridB,$useridC,$useridD)
574 {
575   global $debug;
576
577   $r = array();
578
579   if($debug)
580     {
581       /* fix the hands; makes debugging easier; the following hands have lots of sicknesses,
582        * to make testing easier
583        */
584       $r[ 0]=1;     $r[12]=47;   $r[24]=13;       $r[36]=37;
585       $r[ 1]=2;     $r[13]=23;   $r[25]=14;       $r[37]=38;
586       $r[ 2]=3;     $r[14]=27;   $r[26]=15;       $r[38]=39;
587       $r[ 3]=4;     $r[15]=16;   $r[27]=28;       $r[39]=40;
588       $r[ 4]=5;     $r[16]=17;   $r[28]=29;       $r[40]=21;
589       $r[ 5]=18;    $r[17]=6;    $r[29]=30;       $r[41]=42;
590       $r[ 6]=41;    $r[18]=7;    $r[30]=31;       $r[42]=43;
591       $r[ 7]=22;    $r[19]=8;    $r[31]=32;       $r[43]=20;
592       $r[ 8]=45;    $r[20]=9;    $r[32]=33;       $r[44]=19;
593       $r[ 9]=46;    $r[21]=10;   $r[33]=44;       $r[45]=24;
594       $r[10]=35;    $r[22]=11;   $r[34]=48;       $r[46]=25;
595       $r[11]=36;    $r[23]=12;   $r[35]=34;       $r[47]=26;
596     }
597   else
598     {
599       /* check if we can find a game were non of the player was involved and return
600        * cards instead
601        */
602       $userstr = "'".implode("','",array($useridA,$useridB,$useridC,$useridD))."'";
603       $randomnumbers = DB_get_unused_randomnumbers($userstr);
604       $randomnumbers = explode(":",$randomnumbers);
605
606       if(sizeof($randomnumbers)==48)
607         return $randomnumbers;
608
609       /* need to create new numbers */
610       for($i=0;$i<48;$i++)
611         $r[$i]=$i+1;
612
613       /* shuffle using a better random generator than the standard one */
614       for ($i = 0; $i <48; $i++)
615         {
616           $j = @mt_rand(0, $i);
617           $tmp = $r[$i];
618           $r[$i] = $r[$j];
619           $r[$j] = $tmp;
620         }
621     };
622
623   return $r;
624 }
625
626 function display_cards($me,$myturn)
627 {
628   return;
629 }
630
631 function have_suit($cards,$c)
632 {
633   global $CARDS;
634   $suite = array();
635
636   if(in_array($c,$CARDS["trump"]))
637     $suite = $CARDS["trump"];
638   else if(in_array($c,$CARDS["clubs"]))
639     $suite = $CARDS["clubs"];
640   else if(in_array($c,$CARDS["spades"]))
641     $suite = $CARDS["spades"];
642   else if(in_array($c,$CARDS["hearts"]))
643     $suite = $CARDS["hearts"];
644   else if(in_array($c,$CARDS["diamonds"]))
645     $suite = $CARDS["diamonds"];
646
647   foreach($cards as $card)
648     {
649       if(in_array($card,$suite))
650         return 1;
651     }
652
653   return 0;
654 }
655
656 function same_type($card,$c)
657 {
658   global $CARDS;
659   $suite = "";
660
661   /* figure out what kind of card c is */
662   if(in_array($c,$CARDS["trump"]))
663     $suite = $CARDS["trump"];
664   else if(in_array($c,$CARDS["clubs"]))
665     $suite = $CARDS["clubs"];
666   else if(in_array($c,$CARDS["spades"]))
667     $suite = $CARDS["spades"];
668   else if(in_array($c,$CARDS["hearts"]))
669     $suite = $CARDS["hearts"];
670   else if(in_array($c,$CARDS["diamonds"]))
671     $suite = $CARDS["diamonds"];
672
673   /* card is the same suid return 1 */
674   if(in_array($card,$suite))
675     return 1;
676
677   return 0;
678 }
679
680 function set_gametype($gametype)
681 {
682   global $CARDS;
683   global $RULES;
684   global $GAME;
685
686   switch($gametype)
687     {
688     case "normal":
689     case "wedding":
690     case "poverty":
691     case "dpoverty":
692     case "trump":
693     case "silent":
694       $CARDS["trump"]    = array('1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16',
695                                  '17','18','19','20','21','22','23','24','25','26');
696       $CARDS["diamonds"] = array();
697       $CARDS["clubs"]    = array('27','28','29','30','31','32','33','34');
698       $CARDS["spades"]   = array('35','36','37','38','39','40','41','42');
699       $CARDS["hearts"]   = array('43','44','45','46','47','48');
700       $CARDS["foxes"]    = array('19','20');
701       if($RULES["dullen"]=='none')
702         {
703           $CARDS["trump"]    = array('3','4','5','6','7','8','9','10','11','12','13','14','15','16',
704                                      '17','18','19','20','21','22','23','24','25','26');
705           $CARDS["hearts"]   = array('43','44','1','2','45','46','47','48');
706         }
707       /* do we need to reorder for Schweinchen? need to search for it because of special case for dullen above*/
708       if($RULES['schweinchen']=='both'&& $GAME['schweinchen-who'])
709         {
710           /* find the fox and put them at the top of the stack */
711           foreach(array('19','20') as $fox)
712             {
713               /* search for fox */
714               $trump = $CARDS['trump'];
715               $key = array_keys($trump, $fox);
716
717               /* reorder */
718               $foxa = array();
719               $foxa[]=$trump[$key[0]];
720               unset($trump[$key[0]]);
721               $trump = array_merge($foxa,$trump);
722               $CARDS['trump'] = $trump;
723             }
724         }
725       else if( ($RULES['schweinchen']=='second' || $RULES['schweinchen']=='secondaftercall')
726                && $GAME['schweinchen-who'])
727         {
728           /* find the fox and put them at the top of the stack */
729           $trump = $CARDS['trump'];
730           $key = array_keys($trump, '19');
731
732           /* reorder */
733           $foxa = array();
734           $foxa[]=$trump[$key[0]];
735           unset($trump[$key[0]]);
736           $trump = array_merge($foxa,$trump);
737           $CARDS['trump'] = $trump;
738         }
739       break;
740     case "queen":
741       $CARDS["trump"]    = array('3','4','5','6','7','8','9','10');
742       $CARDS["clubs"]    = array('27','28','29','30','31','32','11','12','33','34');
743       $CARDS["spades"]   = array('35','36','37','38','39','40','13','14','41','42');
744       $CARDS["hearts"]   = array('43','44', '1', '2','45','46','15','16','47','48');
745       $CARDS["diamonds"] = array('19','20','21','22','23','24','17','18','25','26');
746       $CARDS["foxes"]    = array();
747       break;
748     case "jack":
749       $CARDS["trump"]    = array('11','12','13','14','15','16','17','18');
750       $CARDS["clubs"]    = array('27','28','29','30','31','32','3', '4','33','34');
751       $CARDS["spades"]   = array('35','36','37','38','39','40','5', '6','41','42');
752       $CARDS["hearts"]   = array('43','44', '1', '2','45','46','7', '8','47','48');
753       $CARDS["diamonds"] = array('19','20','21','22','23','24','9','10','25','26');
754       $CARDS["foxes"]    = array();
755       break;
756     case "trumpless":
757       $CARDS["trump"]    = array();
758       $CARDS["clubs"]    = array('27','28','29','30','31','32','3', '4','11','12','33','34');
759       $CARDS["spades"]   = array('35','36','37','38','39','40','5', '6','13','14','41','42');
760       $CARDS["hearts"]   = array('43','44', '1', '2','45','46','7', '8','15','16','47','48');
761       $CARDS["diamonds"] = array('19','20','21','22','23','24','9','10','17','18','25','26');
762       $CARDS["foxes"]    = array();
763       break;
764     case "club":
765       $CARDS["trump"]    = array('1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16',
766                                  '17','18','27','28','29','30','31','32','33','34');
767       $CARDS["clubs"]    = array();
768       $CARDS["spades"]   = array('35','36','37','38','39','40','41','42');
769       $CARDS["hearts"]   = array('43','44','45','46','47','48');
770       $CARDS["diamonds"] = array('19','20','21','22','23','24','25','26');
771       $CARDS["foxes"]    = array();
772       if($RULES["dullen"]=='none')
773         {
774           $CARDS["trump"]    = array('3','4','5','6','7','8','9','10','11','12','13','14','15','16',
775                                      '17','18','27','28','29','30','31','32','33','34');
776           $CARDS["hearts"]   = array('43','44','1','2','45','46','47','48');
777         }
778       break;
779     case "spade":
780       $CARDS["trump"]    = array('1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16',
781                                  '17','18','35','36','37','38','39','40','41','42');
782       $CARDS["clubs"]    = array('27','28','29','30','31','32','33','34');
783       $CARDS["spades"]   = array();
784       $CARDS["hearts"]   = array('43','44','45','46','47','48');
785       $CARDS["diamonds"] = array('19','20','21','22','23','24','25','26');
786       $CARDS["foxes"]    = array();
787       if($RULES["dullen"]=='none')
788         {
789           $CARDS["trump"]    = array('3','4','5','6','7','8','9','10','11','12','13','14','15','16',
790                                      '17','18','35','36','37','38','39','40','41','42');
791           $CARDS["hearts"]   = array('43','44','1','2','45','46','47','48');
792         }
793       break;
794     case "heart":
795       $CARDS["trump"]    = array('1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16',
796                                  '17','18','43','44','45','46','47','48');
797       $CARDS["clubs"]    = array('27','28','29','30','31','32','33','34');
798       $CARDS["spades"]   = array('35','36','37','38','39','40','41','42');
799       $CARDS["hearts"]   = array();
800       $CARDS["diamonds"] = array('19','20','21','22','23','24','25','26');
801       $CARDS["foxes"]    = array();
802       if($RULES["dullen"]=='none')
803         {
804           $CARDS["trump"]    = array('3','4','5','6','7','8','9','10','11','12','13','14','15','16',
805                             '17','18','43','44','1','2','45','46','47','48');
806         }
807       break;
808     }
809 }
810
811 function mysort($cards,$gametype)
812 {
813   global $PREF;
814   if(isset($PREF['sorting']))
815     if($PREF['sorting']=='high-low')
816       usort ( $cards, 'sort_comp_high_low' );
817     else
818       usort ( $cards, 'sort_comp_low_high' );
819   else
820     usort ( $cards, 'sort_comp_high_low' );
821   return $cards;
822 }
823
824 function sort_comp_high_low($a,$b)
825 {
826   global $CARDS;
827
828   $ALL = array();
829   $ALL = array_merge($CARDS['trump'],$CARDS['diamonds'],$CARDS['clubs'],
830                      $CARDS['hearts'],$CARDS['spades']);
831
832   return pos_array($a,$ALL)-pos_array($b,$ALL);
833 }
834
835 function sort_comp_low_high($a,$b)
836 {
837   global $CARDS;
838
839   $ALL = array();
840   $ALL = array_merge($CARDS['trump'],$CARDS['diamonds'],$CARDS['clubs'],
841                      $CARDS['hearts'],$CARDS['spades']);
842
843   return -pos_array($a,$ALL)+pos_array($b,$ALL);
844 }
845
846 function can_call($what,$hash)
847 {
848   /* figure out if a person can make a call:
849    $what in 0,30,60,90,120 = points of the call
850    $hash                   = the hash of the person who wants to make the call
851
852    return values:
853    0   can't make that call
854    1   can make the call
855    2   can make the call, but this is the last chance to do so...
856    */
857
858   global $RULES;
859
860   /* get some information
861    */
862   $gameid   = DB_get_gameid_by_hash($hash);
863   $gametype = DB_get_gametype_by_gameid($gameid);
864   $oldcall  = DB_get_call_by_hash($hash); /* did the person already made a call? */
865   $pcall    = DB_get_partner_call_by_hash($hash); /* did the partner already made a call */
866
867
868   /* you're call must be better than the one you or your partner already made
869    */
870   if( ($pcall!=NULL && ($what >= $pcall))
871       || ($oldcall!=NULL && ($what >=$oldcall)) )
872     {
873       return 0;
874     }
875
876   /* for some rules we need to know how many cards people have
877    */
878   $NRcards  = count(DB_get_hand($hash));
879
880   $NRallcards = 0;
881   for ($i=1;$i<5;$i++)
882     {
883       $user         = DB_get_hash_from_game_and_pos($gameid,$i);
884       $NRallcards  += count(DB_get_hand($user));
885     };
886
887   /* in case of a wedding, everything will be delayed by an offset
888    */
889   $offset = 0;
890   if($gametype=="wedding")
891     {
892       $offset = DB_get_sickness_by_gameid($gameid);
893       if ($offset <0) /* not resolved */
894         return 0;
895     };
896
897   /* now check if the call is allowed depending on the rule set
898    */
899   switch ($RULES["call"])
900     {
901     case "1st-own-card":
902       /* calls can be made before/while you play your card...
903        * first card = 120, second card = 90, etc.
904        */
905       if( 4-($what/30) == 12 - ($NRcards + $offset))
906         return 2;
907       if( 4-($what/30) > 12 - ($NRcards + $offset))
908         return 1;
909       break;
910     case "5th-card":
911       /* you can make the first call anytime during the first trick
912        */
913       if( 27+4*($what/30) == $NRallcards + $offset*4)
914         return 2;
915       if( 27+4*($what/30) < $NRallcards + $offset*4)
916         return 1;
917       break;
918     case "9-cards":
919       /* you can call 120 with 12 cards, 90 with 9 or more cards, 60 with 6 or more, etc.
920        * you can't skip a call though
921        */
922
923       /* figure out last call
924        */
925       if($oldcall!=NULL && $pcall!=NULL)
926         $mincall = ($oldcall>$pcall) ? $pcall : $oldcall;
927       else if($oldcall!=NULL)
928         $mincall = $oldcall;
929       else if ($pcall!=NULL)
930         $mincall = $pcall;
931       else
932         $mincall = -1;
933
934
935       if( 12 == ($NRcards + $offset))
936         {
937           return 2;
938         }
939       else if( 12 < ($NRcards + $offset))
940         {
941           return 1;
942         }
943       else if ( 9 == ($NRcards + $offset))
944         {
945           if( ($mincall>=0 && $mincall<=120 && $what<=90 ) )
946             return 2;
947         }
948       else if ( 9 < ($NRcards + $offset))
949         {
950           if( ($mincall>=0 && $mincall<=120 && $what<=90 ) )
951             return 1;
952         }
953       else if ( 6 == ($NRcards + $offset))
954         {
955           if( ($mincall>=0 && $mincall<=90 && $what<=60 ) )
956             return 2;
957         }
958       else if ( 6 < ($NRcards + $offset))
959         {
960           if( ($mincall>=0 && $mincall<=90 && $what<=60 ) )
961             return 1;
962         }
963       else if ( 3 == ($NRcards + $offset))
964         {
965           if( ($mincall>=0 && $mincall<=60 && $what<=30 ) )
966             return 2;
967         }
968       else if ( 3 < ($NRcards + $offset))
969         {
970           if( ($mincall>=0 && $mincall<=60 && $what<=30 ) )
971             return 1;
972         }
973       else if ( 0 == ($NRcards + $offset))
974         {
975           if( ($mincall>=0 && $mincall<=30 && $what==0 ) )
976             return 2;
977         }
978       else if ( 0 < ($NRcards + $offset))
979         {
980           if( ($mincall>=0 && $mincall<=30 && $what==0 ) )
981             return 1;
982         };
983       break;
984     }
985
986   return 0;
987 }
988
989 function display_table_begin ()
990 {
991   global $gameid;
992
993   $result = DB_query('SELECT  User.fullname as name,'.
994                      '        Hand.position as position,'.
995                      '        User.id,'.
996                      '        Hand.party as party,'.
997                      '        Hand.sickness as sickness,'.
998                      '        Hand.point_call,'.
999                      '        User.last_login,'.
1000                      '        Hand.hash,'.
1001                      '        User.timezone,'.
1002                      '        User.email'.
1003                      ' FROM Hand'.
1004                      ' LEFT JOIN User ON User.id=Hand.user_id'.
1005                      ' WHERE Hand.game_id='.DB_quote_smart($gameid).
1006                      ' ORDER BY position ASC');
1007
1008   $row0 = DB_fetch_array($result);
1009   $row1 = DB_fetch_array($result);
1010   $row2 = DB_fetch_array($result);
1011   $row3 = DB_fetch_array($result);
1012
1013   echo "<div class=\"table\">\n";
1014   display_single_user($row1);
1015   echo "\n<div class=\"middle\">\n";
1016   display_single_user($row0,1); /* mark starting player in case re/contra is not set yet */
1017   echo "  <img class=\"table\" src=\"pics/table.png\" alt=\"table\" />\n";
1018   display_single_user($row2);
1019
1020   return;
1021 }
1022
1023
1024 function display_table_end ()
1025 {
1026   global $gameid;
1027
1028   $result = DB_query('SELECT  User.fullname as name,'.
1029                      '        Hand.position as position,'.
1030                      '        User.id,'.
1031                      '        Hand.party as party,'.
1032                      '        Hand.sickness as sickness,'.
1033                      '        Hand.point_call,'.
1034                      '        User.last_login,'.
1035                      '        Hand.hash,'.
1036                      '        User.timezone,'.
1037                      '        User.email'.
1038                      ' FROM Hand'.
1039                      ' LEFT JOIN User ON User.id=Hand.user_id'.
1040                      ' WHERE Hand.game_id='.DB_quote_smart($gameid).
1041                      ' ORDER BY position ASC');
1042
1043   $row0 = DB_fetch_array($result);
1044   $row1 = DB_fetch_array($result);
1045   $row2 = DB_fetch_array($result);
1046   $row3 = DB_fetch_array($result);
1047
1048   echo "</div>\n";
1049   display_single_user($row3);
1050   echo "</div>\n";
1051
1052   return;
1053 }
1054
1055 function display_single_user($r,$start=0)
1056 {
1057   /* start=1, mark starting player, default=0, so the player on the left is not marked */
1058
1059   global $gameid, $debug,$INDEX,$defaulttimezone;
1060   global $RULES,$GAME, $gametype_raw;
1061
1062       $name  = $r[0];
1063       $pos   = $r[1];
1064       $user  = $r[2];
1065       $party = $r[3];
1066       $sickness  = $r[4];
1067       $call      = $r[5];
1068       $hash      = $r[7];
1069       $timezone  = $r[8];
1070       $email     = $r[9];
1071       $wins      = DB_get_number_of_tricks($gameid,$pos);
1072       date_default_timezone_set($defaulttimezone);
1073       $lastlogin = strtotime($r[6]);
1074       date_default_timezone_set($timezone);
1075       $timenow   = strtotime(date("Y-m-d H:i:s"));
1076       $gravatar = "$name<br />\n       <img class=\"gravatar\" title=\"$name\" src=\"http://www.gravatar.com/avatar/".
1077         md5(strtolower(trim($email)))."?d=identicon\" alt=\"$name's gravatar\" />";
1078
1079       echo "  <div class=\"table".($pos-1)."\">\n";
1080
1081       /* mark starting player */
1082       if($start && ! ($party=="re" || $party=="contra"))
1083         echo '   <span class="start">'._('Starting Player')."</span> <br />\n";
1084
1085       if($debug)
1086         echo "   <a href=\"".$INDEX."?action=game&amp;me=".$hash."\">";
1087       if($vacation = check_vacation($user))
1088         {
1089           $start   = $vacation[0];
1090           $stop    = substr($vacation[1],0,10);
1091           $comment = $vacation[2];
1092
1093           $title = _("begin:")." $start  "._("end:")." $stop $comment";
1094               echo "   <span class=\"vacation\" title=\"$title\">$gravatar "._("(on vacation until $stop)")."</span> \n";
1095         }
1096       else
1097         echo "   $gravatar \n";
1098       if($debug)
1099         echo"   </a>\n";
1100
1101       /* add hints for poverty, wedding, solo, etc */
1102       if( $gametype_raw != "solo")
1103         if( $RULES["schweinchen"]=="both" && $GAME["schweinchen-who"]==$hash )
1104           echo " Schweinchen. <br />";
1105
1106       if($gametype_raw=="poverty" && $party=="re")
1107         if($sickness=="poverty" || ($RULES['lowtrump']=='poverty' && $sickness=='lowtrump'))
1108           {
1109             $userhash = DB_get_hash_from_gameid_and_userid($gameid,$user);
1110             $cards    = DB_get_all_hand($userhash);
1111             $trumpNR  = count_trump($cards,'all');
1112             if($trumpNR)
1113               echo "   <img src=\"pics/button/poverty_trump_button.png\" class=\"button\" ".
1114                 "alt=\"poverty - trump back\" title=\"poverty - trump back\" />\n";
1115             else
1116               echo "   <img src=\"pics/button/poverty_notrump_button.png\" class=\"button\" ".
1117                 "alt=\"poverty - no trump back\" title=\"poverty - no trump back\" />\n";
1118           }
1119         else
1120           echo "   <img src=\"pics/button/poverty_partner_button.png\" class=\"button\" ".
1121             "alt=\"poverty partner\" title=\"poverty partner\" />\n";
1122
1123       if($gametype_raw=="dpoverty")
1124         if($party=="re")
1125           if($sickness=="poverty" || ($RULES['lowtrump']=='poverty' && $sickness=='lowtrump'))
1126             {
1127               $userhash = DB_get_hash_from_gameid_and_userid($gameid,$user);
1128               $cards    = DB_get_all_hand($userhash);
1129               $trumpNR  = count_trump($cards,'all');
1130               if($trumpNR)
1131                 echo "   <img src=\"pics/button/poverty_trump_button.png\" class=\"button\" ".
1132                   "alt=\"poverty < trump back\" title=\"poverty - trump back\" />\n";
1133               else
1134                 echo "   <img src=\"pics/button/poverty_notrump_button.png\" class=\"button\" ".
1135                   "alt=\"poverty <\" title=\"poverty - no trump back\" />\n";
1136             }
1137           else
1138             echo "   <img src=\"pics/button/poverty_partner_button.png\" class=\"button\" ".
1139               "alt=\"poverty >\" title=\"poverty partner\" />\n";
1140         else
1141           if($sickness=="poverty"  || ($RULES['lowtrump']=='poverty' && $sickness=='lowtrump'))
1142             {
1143               $userhash = DB_get_hash_from_gameid_and_userid($gameid,$user);
1144               $cards    = DB_get_all_hand($userhash);
1145               $trumpNR  = count_trump($cards,'all');
1146               if($trumpNR)
1147                 echo "   <img src=\"pics/button/poverty2_trump_button.png\" class=\"button\" ".
1148                   "alt=\"poverty2 < trump back\" title=\"poverty2 - trump back\"/>\n";
1149               else
1150                 echo "   <img src=\"pics/button/poverty2_notrump_button.png\" class=\"button\" ".
1151                   "alt=\"poverty2 <\" title=\"poverty2 - no trump back\" />\n";
1152             }
1153           else
1154             echo "   <img src=\"pics/button/poverty2_partner_button.png\" class=\"button\" ".
1155               "alt=\"poverty2 >\" title=\"poverty2 partner\" />\n";
1156
1157       if($gametype_raw=="wedding" && $party=="re")
1158         if($sickness=="wedding")
1159           echo "   <img src=\"pics/button/wedding_button.png\" class=\"button\" alt=\"wedding\" title=\"wedding\" />\n";
1160         else
1161           echo "   <img src=\"pics/button/wedding_partner_button.png\" class=\"button\" ".
1162             "alt=\"wedding partner\" title=\"wedding partner\" />\n";
1163
1164       if( $gametype_raw=='solo' && $party=="re")
1165         {
1166           $solotype = DB_get_solo_by_gameid($gameid);
1167           $GT = get_display_gametype($gameid);
1168
1169           if($solotype=='queen')
1170             echo "   <img src=\"pics/button/queensolo_button.png\" class=\"button\" alt=\"$GT\" title=\"Queen solo\" />\n";
1171           else if($solotype=='jack')
1172             echo "   <img src=\"pics/button/jacksolo_button.png\" class=\"button\" alt=\"$GT\" title=\"Jack solo\" />\n";
1173           else if($solotype=='club')
1174             echo "   <img src=\"pics/button/clubsolo_button.png\" class=\"button\" alt=\"$GT\" title=\"Club solo\" />\n";
1175           else if($solotype=='spade')
1176             echo "   <img src=\"pics/button/spadesolo_button.png\" class=\"button\" alt=\"$GT\" title=\"Spade solo\" />\n";
1177           else if($solotype=='heart')
1178             echo "   <img src=\"pics/button/heartsolo_button.png\" class=\"button\" alt=\"$GT\" title=\"Heart solo\" />\n";
1179           else if($solotype=='trumpless')
1180             echo "   <img src=\"pics/button/notrumpsolo_button.png\" class=\"button\" alt=\"$GT\" title=\"Trumpless solo\" />\n";
1181           else if($solotype=='trump')
1182             echo "   <img src=\"pics/button/trumpsolo_button.png\" class=\"button\" alt=\"$GT\" title=\"Trump solo\" />\n";
1183         }
1184
1185       /* add point calls */
1186       if($call!=NULL)
1187         {
1188           if($party=="re")
1189             echo "   <img src=\"pics/button/re_button.png\" class=\"button\" alt=\"re\" title=\"Re\" />\n";
1190           else
1191             echo "   <img src=\"pics/button/contra_button.png\" class=\"button\" alt=\"contra\" title=\"Contra\" />\n";
1192           switch($call)
1193             {
1194             case "0":
1195               echo "   <img src=\"pics/button/0_button.png\" class=\"button\" alt=\"0\" title=\"Call 0\" />\n";
1196               break;
1197             case "30":
1198               echo "   <img src=\"pics/button/30_button.png\" class=\"button\" alt=\"30\" title=\"Call 30\" />\n";
1199               break;
1200             case "60":
1201               echo "   <img src=\"pics/button/60_button.png\" class=\"button\" alt=\"60\" title=\"Call 60\" />\n";
1202               break;
1203             case "90":
1204               echo "   <img src=\"pics/button/90_button.png\" class=\"button\" alt=\"90\" title=\"Call 90\" />\n";
1205               break;
1206             }
1207         }
1208
1209       echo "   <img src=\"pics/button/time-info.png\" class=\"button\" alt=\"time info\" ".
1210         "title=\"local time: ".date("Y-m-d H:i:s",$timenow).  " ".
1211         "last login: ".date("Y-m-d H:i:s",$lastlogin)."\" />";
1212
1213       echo "   <br /><span class=\"numberoftricks\">";
1214       /* show how many tricks the person made */
1215       switch($wins)
1216         {
1217         case 0:
1218           echo _('#tricks 0'); break;
1219         case 1:
1220           echo _('#tricks 1'); break;
1221         case 2:
1222         case 3:
1223         case 4:
1224           echo _('#tricks few'); break;
1225         default:
1226           echo _('#tricks many'); break;
1227         }
1228       echo "</span>\n";
1229       echo "  </div>\n";
1230 }
1231
1232
1233 function display_user_menu($id, $skiphash=NULL)
1234 {
1235   global $WIKI,$INDEX;
1236
1237   if($skiphash)
1238     $result = DB_query("SELECT Hand.hash,Hand.game_id,Game.player from Hand".
1239                        " LEFT JOIN Game On Hand.game_id=Game.id".
1240                        " WHERE Hand.user_id=".DB_quote_smart($id).
1241                        " AND Hand.hash!=".DB_quote_smart($skiphash).
1242                        " AND ( Game.player='$id' OR ISNULL(Game.player) )".
1243                        " AND ( Game.status='pre' OR Game.status='play' )".
1244                        " ORDER BY Game.session" );
1245   else
1246     $result = DB_query('SELECT Hand.hash,Hand.game_id,Game.player from Hand'.
1247                        ' LEFT JOIN Game On Hand.game_id=Game.id'.
1248                        ' WHERE Hand.user_id='.DB_quote_smart($id).
1249                        ' AND ( Game.player='.DB_quote_smart($id).' OR ISNULL(Game.player) )'.
1250                        " AND ( Game.status='pre' OR Game.status='play' )".
1251                        ' ORDER BY Game.session' );
1252
1253   $i=0;
1254   while( $r = DB_fetch_array($result))
1255     {
1256       if($i==0)
1257         {
1258           echo "\n<div class=\"usermenu\">\n  ";
1259           echo _('It\'s your turn in these games').":\n";
1260         }
1261       else
1262         {
1263           echo ", ";
1264         }
1265
1266       $i++;
1267       echo "  <a href=\"".$INDEX."?action=game&amp;me=".$r[0].
1268         "\"> ".DB_format_gameid($r[1])." </a>\n";
1269       if($i>4)
1270         {
1271           echo ",  ...\n";
1272           break;
1273         }
1274     }
1275
1276   if($i)
1277     echo  "</div>\n\n";
1278   return;
1279 }
1280
1281 function generate_score_table($session)
1282 {
1283   /* returns an array with N entries
1284    * $score[$i]["gameid"]   = gameid
1285    * $score[$i]["players"] = array (id=>total points)
1286    * $score[$i]["points"]   = points for this game
1287    * $score[$i]["solo"]     = 1 or 0
1288    */
1289   $score = array();
1290   $i=0;
1291
1292   /* get all ids, scores and gametypes */
1293   $gameids = DB_get_gameids_of_finished_games_by_session($session);
1294
1295   if($gameids == NULL)
1296     return $score;
1297
1298   $player = array();
1299   $player_party = array();
1300
1301   /* get player id from the first game */
1302   $result = DB_query('SELECT user_id from Hand'.
1303                      ' WHERE Hand.game_id='.DB_quote_smart($gameids[0][0]));
1304   while( $r = DB_fetch_array($result))
1305     $player[$r[0]] = 0;
1306
1307   /* get party of players for each game in the session */
1308   foreach($player as $id=>$points)
1309     $player_party[$id]=DB_get_party_by_session_and_userid($session,$id);
1310
1311   /* get points and generate table */
1312   foreach($gameids as $gameid)
1313     {
1314       $re_score = $gameid[1];
1315       $gametype = $gameid[2];
1316       foreach($player as $id=>$points)
1317         {
1318           $party = $player_party[$id][$i][0];
1319           if($party == 're')
1320             if($gametype=='solo')
1321               $player[$id] += 3*$re_score;
1322             else
1323               $player[$id] += $re_score;
1324           else if ($party == 'contra')
1325             $player[$id] -= $re_score;
1326         }
1327       $score[$i]['gameid']  = $gameid[0] ;
1328       $score[$i]['players'] = $player;
1329       $score[$i]['points']  = abs($re_score);
1330       $score[$i]['solo']    = ($gametype=='solo');
1331
1332       $i++;
1333     }
1334
1335   return $score;
1336 }
1337
1338 function generate_global_score_table()
1339 {
1340   $return = array();
1341
1342   /* get all ids, scores and gametypes */
1343   $gameids = DB_get_gameids_of_finished_games_by_session(0);
1344
1345   if($gameids == NULL)
1346     return '';
1347
1348   $player = array();
1349   /* get player id, names... from the User table */
1350   $result = DB_query('SELECT User.id, User.fullname FROM User');
1351
1352   /* save information in an array */
1353   while( $r = DB_fetch_array($result))
1354     $player[$r[0]] = array('name'=> $r[1], 'points' => 0 , 'nr' => 0, 'active' => 0,
1355                            'response' => 0 , 'solo' => 0, 'soloavg' => 0);
1356
1357   /* get points and generate table */
1358   foreach($gameids as $gameid)
1359     {
1360       $re_score = $gameid[1];
1361       $gametype = $gameid[2];
1362
1363       /* get players involved in this game */
1364       $result = DB_query('SELECT user_id FROM Hand WHERE game_id='.DB_quote_smart($gameid[0]));
1365       while($r = DB_fetch_array($result))
1366         {
1367           $id = $r[0];
1368           $party = DB_get_party_by_gameid_and_userid($gameid[0],$id);
1369           if($party == 're')
1370             if($gametype=='solo')
1371               $player[$id]['points'] += 3*$re_score;
1372             else
1373               $player[$id]['points'] += $re_score;
1374           else if ($party == 'contra')
1375             $player[$id]['points'] -= $re_score;
1376           if($party)
1377             $player[$id]['nr']+=1;
1378         }
1379     }
1380
1381   /* add number of active games */
1382   $result = DB_query_array_all("SELECT user_id, COUNT(*) as c  " .
1383                                " FROM Hand".
1384                                " LEFT JOIN Game ON Game.id=game_id".
1385                                " WHERE Game.status IN ('pre','play')".
1386                                " GROUP BY user_id");
1387
1388   foreach($result as $res)
1389     {
1390       $player[$res[0]]['active'] = $res[1];
1391     }
1392
1393   /* response time of users*/
1394   $result = DB_query_array_all("SELECT user_id,".
1395                               "IFNULL(AVG(if(P1.sequence in (2,3,4),".
1396                               "-timestampdiff(MINUTE,mod_date,(select mod_date from Play P2 where P1.trick_id=P2.trick_id  and P2.sequence=P1.sequence-1)),NULL )),1e9) as a ".
1397                               "FROM Play P1 ".
1398                               "LEFT JOIN Hand_Card ON P1.hand_card_id=Hand_Card.id ".
1399                               "LEFT JOIN Hand ON Hand.id=Hand_Card.hand_id ".
1400                               "GROUP BY user_id ");
1401
1402   foreach($result as $res)
1403     {
1404       $player[$res[0]]['response'] = $res[1];
1405     }
1406
1407   /* most solos */
1408   $result = DB_query_array_all("SELECT user_id as uid,".
1409                                "       COUNT(*), ".
1410                                "       COUNT(*)/(SELECT COUNT(*) FROM Hand LEFT JOIN User ON User.id=Hand.user_id WHERE User.id=uid) as c ".
1411                                " FROM Game ".
1412                                " LEFT JOIN Hand ON Hand.position=startplayer AND Game.id=Hand.game_id ".
1413                                " WHERE type='solo' AND Game.status='gameover' ".
1414                                " GROUP BY user_id ");
1415
1416   foreach($result as $res)
1417     {
1418       $player[$res[0]]['solo'] = $res[1];
1419       $player[$res[0]]['soloavg'] = $res[2];
1420     }
1421
1422
1423   /* sort everything nicely */
1424
1425   function cmp($a,$b)
1426   {
1427     if($a['nr']==0) return 1;
1428     if($b['nr']==0) return 1;
1429
1430     $a=$a['points']/$a['nr'];
1431     $b=$b['points']/$b['nr'];
1432
1433     if ($a == $b)
1434       return 0;
1435     return ($a > $b) ? -1 : 1;
1436   }
1437   usort($player,'cmp');
1438
1439
1440   foreach($player as $pl)
1441     {
1442       /* limit to players with at least 10 games */
1443       if($pl['nr']>10)
1444         $return[] = array( $pl['name'], round($pl['points']/$pl['nr'],3), $pl['points'],$pl['nr'],$pl['active'],
1445                            $pl['response'],$pl['solo'],$pl['soloavg']);
1446     }
1447
1448   return $return;
1449 }
1450
1451 function format_score_table_ascii($score)
1452 {
1453   $output="";
1454   if(sizeof($score)==0)
1455     return "";
1456
1457   /* truncate table if we have too many games */
1458   $max = sizeof($score);
1459   if($max>6) $output.=" "._("(table truncated to last 6 games)")."\n";
1460
1461   /* output header */
1462   foreach($score[0]['players'] as $id=>$points)
1463     {
1464       $name = DB_get_name('userid',$id); /*TODO*/
1465       $output.= "  ".substr($name,0,2)."  |";
1466     }
1467   $output.="  P   |\n";
1468   $output.= "------+------+------+------+------+\n";
1469
1470   /* output score for each game */
1471   $i=0;
1472   foreach($score as $game)
1473     {
1474       $i++;
1475       if($i-1<$max-6) continue;
1476
1477       foreach($game['players'] as $id=>$points)
1478         $output.=str_pad($points,6," ",STR_PAD_LEFT)."|";
1479       $output.=str_pad($game['points'],4," ",STR_PAD_LEFT);
1480
1481       /* check for solo */
1482       if($game['solo'])
1483         $output.= " S|";
1484       else
1485         $output.= "  |";
1486
1487       $output.="\n";
1488     }
1489   return $output;
1490 }
1491
1492 function format_score_table_html($score,$userid)
1493 {
1494   global $INDEX;
1495
1496   if(sizeof($score)==0)
1497     return "";
1498
1499   $output = "<div class=\"scoretable\">\n<table class=\"score\">\n";
1500
1501   /* output header */
1502   $header = "";
1503   $header.= " <thead>\n  <tr>\n";
1504   $header.= "   <th> No </th>";
1505   foreach($score[0]['players'] as $id=>$points)
1506     {
1507       $name = DB_get_name('userid',$id); /*TODO*/
1508       $header.= "<th> ".substr($name,0,2)." </th>";
1509     }
1510   $header.="<th>P</th>\n  </tr>\n </thead>\n";
1511
1512   /* use the same as footer */
1513   $footer = "";
1514   $footer.= " <tfoot>\n  <tr>\n";
1515   $footer.= "   <td> No </td>";
1516   foreach($score[0]['players'] as $id=>$points)
1517     {
1518       $name = DB_get_name('userid',$id); /*TODO*/
1519       $footer.= "<td> ".substr($name,0,2)." </td>";
1520     }
1521   $footer.="<td>P</td>\n  </tr>\n </tfoot>\n";
1522
1523   /* body */
1524   $body = "";
1525   $body.= " <tbody>\n";
1526   $i=0;
1527   foreach($score as $game)
1528     {
1529       $i++;
1530       $body.="  <tr>";
1531       $userhash = DB_get_hash_from_gameid_and_userid($game['gameid'],$userid);
1532       /* create link to old games only if you are logged in and its your game*/
1533       if(isset($_SESSION['id']) && $_SESSION['id']==$userid)
1534         $body.="  <td> <a href=\"".$INDEX."?action=game&amp;me=".$userhash."\">$i</a></td>";
1535       else
1536         $body.="  <td>$i</td>";
1537
1538       foreach($game['players'] as $id=>$points)
1539         $body.="<td>".$points."</td>";
1540       $body.="<td>".$game['points'];
1541
1542       /* check for solo */
1543       if($game['solo'])
1544         $body.= " S";
1545       $body.="</td></tr>\n";
1546     }
1547
1548   $output.=$header;
1549   if($i>12)
1550     $output.=$footer;
1551   $output.=$body;
1552
1553   $output.=" </tbody>\n</table>\n</div>\n";
1554
1555   return $output;
1556 }
1557
1558 function createCache($content, $cacheFile)
1559 {
1560   $fp = fopen($cacheFile,"w");
1561   if($fp)
1562     {
1563       fwrite($fp,$content);
1564       fclose($fp);
1565     }
1566   else
1567     echo "WARNING: couldn't create cache file";
1568
1569   return;
1570 }
1571
1572 function getCache($cacheFile, $expireTime)
1573 {
1574   if( file_exists($cacheFile) &&
1575       filemtime($cacheFile )>( time() - $expireTime ) )
1576     {
1577       return file_get_contents($cacheFile);
1578     }
1579
1580   return false;
1581 }
1582
1583 function check_vacation($userid)
1584 {
1585   /* get start date */
1586   $result = DB_query_array("SELECT value FROM User_Prefs".
1587                            " WHERE user_id=".DB_quote_smart($userid)." AND pref_key='vacation start'" );
1588   if($result)
1589     $start = $result[0];
1590   else
1591     return NULL;
1592
1593   /* get end date */
1594   $result = DB_query_array("SELECT value FROM User_Prefs".
1595                            " WHERE user_id=".DB_quote_smart($userid)." AND pref_key='vacation stop'" );
1596   if($result)
1597     $stop = $result[0];
1598   else
1599     return NULL;
1600
1601   /* get comment */
1602   $result = DB_query_array("SELECT value FROM User_Prefs".
1603                            " WHERE user_id=".DB_quote_smart($userid)." AND pref_key='vacation comment'" );
1604   if($result)
1605     $comment = $result[0];
1606   else
1607     $comment = '';
1608
1609   /* check if user is on vacation. TODO: use user's timezone */
1610   if( (time() - strtotime($start) >0) &&
1611       (strtotime($stop) - time()  >0))
1612     return array ($start,$stop,$comment);
1613   else
1614     return NULL;
1615 }
1616
1617 function cancel_game($why,$gameid)
1618 {
1619   $gameid = DB_quote_smart($gameid);
1620
1621   /* update the game table */
1622   switch($why)
1623     {
1624     case 'timedout':
1625       DB_query("UPDATE Game SET status='cancel-timedout' WHERE id=$gameid");
1626       break;
1627     case 'nines':
1628       DB_query("UPDATE Game SET status='cancel-nines' WHERE id=$gameid");
1629       break;
1630     case 'trump':
1631       DB_query("UPDATE Game SET status='cancel-trump' WHERE id=$gameid");
1632       break;
1633     case 'noplay':
1634       DB_query("UPDATE Game SET status='cancel-noplay' WHERE id=$gameid");
1635       break;
1636     case 'lowtrump':
1637       DB_query("UPDATE Game SET status='cancel-lowtrump' WHERE id=$gameid");
1638       break;
1639     }
1640   /* set each player to gameover */
1641   $result = DB_query("SELECT id FROM Hand WHERE game_id=".DB_quote_smart($gameid));
1642   while($r = DB_fetch_array($result))
1643     {
1644       $id = $r[0];
1645       DB_query("UPDATE Hand SET status='gameover' WHERE id=".DB_quote_smart($id));
1646     }
1647
1648   return;
1649 }
1650
1651 function get_user_token($userid)
1652 {
1653
1654   $token = NULL;
1655
1656   $date = DB_get_user_creation_date($userid);
1657   $name = DB_get_name('userid',$userid);
1658
1659   if($date && $name)
1660     $token = md5("token".$name.$date);
1661
1662   return $token;
1663 }
1664
1665 function verify_password($email, $password)
1666 {
1667   /* verify password, if old password has length 32 assume it's an old md5, else use new password scheme */
1668   /* return 0 if verified, else return error code
1669    *        1 can't find email
1670    *        2 can't calculate correct hash
1671    *        3 misc error
1672    */
1673
1674   /* check user email by getting his id */
1675   $userid = DB_get_userid('email',$email);
1676   if(!$userid)
1677     return 1;
1678
1679   /* test for temporary passwords, only valid for one date (tested in the DB) */
1680   $tmppasswd = md5($password);
1681   if(DB_check_recovery_passwords($tmppasswd,$email))
1682     return 0;
1683
1684   /* get saved password */
1685   $existingpassword =  DB_get_passwd_by_userid($userid);
1686
1687   if(strlen($existingpassword)==32) /* old password type */
1688     {
1689       if ($existingpassword == md5($password))
1690         {
1691           /* update password to new crypt version */
1692           // create a password hash using the crypt function, need php 5.3 for this
1693           // create and random salt
1694           $salt = substr(str_replace('+', '.', base64_encode(sha1(microtime(true), true))), 0, 22);
1695           // hash incoming password using 12 rounds of blowfish
1696           $hash = crypt($password, '$2y$12$' . $salt);
1697           if(strlen($hash)>13)
1698             DB_query("UPDATE User SET password='$hash' where id='$userid'");
1699           else
1700             return 2;
1701
1702           return 0;
1703         }
1704     }
1705   else
1706     {
1707       if ($existingpassword == crypt($password, $existingpassword))
1708         return 0;
1709     };
1710
1711   return 3;
1712 }
1713
1714 /* language functions */
1715 function detectlanguage()
1716 {
1717         /* read out browser's prefered language, taken from php-manual*/
1718         $langcode = explode(";", $_SERVER['HTTP_ACCEPT_LANGUAGE']);
1719         $langcode = explode(",", $langcode['0']);
1720         return $langcode['0'];
1721 }
1722
1723 function set_language($l,$type='lang')
1724 {
1725    if($type=='uid')
1726      {
1727        $userPREF = DB_get_PREF($l);
1728        $l = $userPREF['language'];
1729      };
1730
1731     switch($l)
1732       {
1733       case 'de':
1734         putenv("LC_ALL=de_DE");
1735         setlocale(LC_ALL, "de_DE");
1736         break;
1737       default:
1738         putenv("LC_ALL=en_US");
1739         setlocale(LC_ALL, "en_US");
1740         break;
1741       }
1742
1743     // Specify location of translation tables
1744     bindtextdomain("edoko", "./locale");
1745     bind_textdomain_codeset("edoko", 'UTF-8');
1746     // Choose domain
1747     textdomain("edoko");
1748
1749     return;
1750 }
1751
1752 function get_display_gametype($gameid)
1753 {
1754   /* return a readable string that can be displayed to show the game type
1755    * this means hiding silent solo from the user
1756    */
1757
1758   $gametype = DB_get_gametype_by_gameid($gameid);
1759
1760   if ($gametype == 'normal')
1761     $GT = _('normal');
1762   else if($gametype=='solo')
1763     {
1764       $solotype = DB_get_solo_by_gameid($gameid);
1765
1766       switch($solotype)
1767         {
1768         case 'trumpless':
1769           $GT = _('trumpless solo');
1770           break;
1771         case 'jack':
1772           $GT = _('jack solo');
1773           break;
1774         case 'queen':
1775           $GT = _('queen solo');
1776           break;
1777         case 'trump':
1778           $GT = _('trump solo');
1779           break;
1780         case 'club':
1781           $GT = _('club solo');
1782           break;
1783         case 'spade':
1784           $GT = _('spade solo');
1785           break;
1786         case 'heart':
1787           $GT = _('heart solo');
1788           break;
1789         case 'silent':
1790           $GT = _('normal');   /* this is change compared to $gametype */
1791           break;
1792         }
1793     }
1794   else if ($gametype == 'wedding')
1795     $GT = _('wedding');
1796   else if ($gametype == 'poverty')
1797     $GT = _('poverty');
1798   else if ($gametype == 'dpoverty')
1799     $GT = _('double poverty');
1800
1801   return $GT;
1802 }
1803
1804 ?>