Fix bug in setting up w3 and w4 games
[capablanca.git] / lasker-2.2.3 / src / formula.c
1 /*
2    This program is free software; you can redistribute it and/or modify
3    it under the terms of the GNU General Public License as published by
4    the Free Software Foundation; either version 2 of the License, or
5    (at your option) any later version.
6    
7    This program is distributed in the hope that it will be useful,
8    but WITHOUT ANY WARRANTY; without even the implied warranty of
9    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10    GNU General Public License for more details.
11    
12    You should have received a copy of the GNU General Public License
13    along with this program; if not, write to the Free Software
14    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
15 */
16
17 /* Formula program for FICS.  Written by Dave Herscovici
18                                          (dhersco@math.nps.navy.mil)
19   Edited by DAV to include wild,non-std and untimed
20   lightning flags
21
22 */
23
24 /*   Operators allowed, in order of precedence:
25       ! (not), - (unary),
26       *, /,
27       +, -,
28       <, <=, =<, >, >=, =>, (<= and =< are equivalent, as are >= and =>)
29       =, ==, !=, <>, (two ways of saying both equals and not equals)
30       &, &&, (both "and")
31       |, ||, (both "or").
32
33    Parentheses () are also allowed, as is a pound sign '#' for comments.
34    The program will divide by a fudge factor of .001 instead of 0.
35
36    Variables allowed:
37       time, inc, rating, myrating, rated, blitz, standard, lightning, registered,
38       assesswin, assessdraw, assessloss, timeseal,
39       ratingdiff = rating - myrating, private, wild, untimed, nonstandard,
40       maxtime(n) = maximum time n moves will take (in seconds),
41       mymaxtime(n) = same, but just count my time.
42       f1, f2, f3, f4, f5, f6, f7, f8, f9.
43
44    The code for translating blitz and standard variables may have to be
45    redone.  F1 through f9 are user-defined formula variables.  They can
46    be used to avoid having to retype your entire formula when you want
47    to change one part of it, or to compensate for the lack of a 'mood'
48    variable.
49
50    For example:
51       set f1 rated & time=5 & inc=0   # rated 5 minute games
52       set f2 rating - myrating
53       set f3 # this line is a random comment.
54       set f4 f2>400   # I want a REAL fight
55
56    Then you can type:
57       set formula f1   # rated 5 min. games only
58       set formula etime >= 10 & f2 > -100 # long games, decent competition
59       set formula f1 & !f4
60    or set formula f2 >= 0 | blitz
61    depending on your mood.
62
63    Further modifications could account for starting positions, time
64    odds games, provisional or established opponents, etc.  Maybe f0
65    should be reserved for your formula upon logging in; i.e. if f0
66    is set, your formula is automatically set to f0 when you log in.
67 */
68
69 #include "includes.h"
70
71 static int CheckFormula(struct game *g, int clause, int *i, int op_type,
72                         int *result, int eval);
73
74
75 static char *GetPlayersFormula (struct player *pWho, int clause)
76 {
77   if (clause==MAX_FORMULA) return (pWho->formula);
78   else return (pWho->formulaLines[clause]);
79 }
80
81 /* In MoveIndexPastString, *i is treated as the index into string[];
82    this function returns 1 if the character string beginning with
83    string[*i] match *text, and 0 otherwise.  In the former case, *i
84    is incremented to move the index past *text.
85 */
86 static int MoveIndexPastString (char *string, int *i, char *text)
87 {
88   int n = strlen(text);
89   if (strncasecmp(text, string + *i, n)) return (0);
90   *i += n;
91   return (n);
92 }    /* end of function MoveIndexPastString. */
93
94 static int GetNumberInsideParens (struct game *g, int clause, int *i, int *token,
95                            int eval)
96 {
97   char *string = GetPlayersFormula(&player_globals.parray[g->black], clause);
98   int ret;
99
100   while (string[*i] != '\0' && isspace(string[*i])) (*i)++;
101   if (!MoveIndexPastString(string, i, "(")) return (ERR_BADCHAR);
102
103   ret = CheckFormula(g, clause, i, OPTYPE_PAREN, token, eval);
104   if (ret != ERR_NONE)
105     return (ret);
106   if (MoveIndexPastString(string, i, ")")) return (ERR_NONE);
107   else return (ERR_PAREN);
108 }
109
110 static int Maxtime (struct game *g, int numMoves, int numPlayers)
111 {
112   int max;
113
114   if ((g->bInitTime == g->wInitTime) && (g->bIncrement == g->wIncrement)) {
115     max = numPlayers * (60 * g->wInitTime + numMoves * g->wIncrement);
116     if ((g->type != TYPE_UNTIMED) && (g->wInitTime == 0))
117       max +=10 * numPlayers; /* 0 x start with ten secs */
118
119   } else if (numPlayers == 2) {
120     max = 60 * (g->wInitTime + g->bInitTime)
121             + numMoves * (g->wIncrement + g->bIncrement);
122     if ((g->type != TYPE_UNTIMED) && (g->wInitTime == 0))
123       max +=10; /* 0 x start with ten secs */
124     if ((g->type != TYPE_UNTIMED) && (g->bInitTime == 0))
125       max +=10; /* 0 x start with ten secs */
126
127   } else {
128     max = 60 * g->bInitTime + numMoves * g->bIncrement;
129     if ((g->type != TYPE_UNTIMED) && (g->bInitTime == 0))
130       max +=10; /* 0 x start with ten secs */
131   } 
132
133   return max;
134 }
135
136
137 /* The black player in game *g has been challenged.  S/he has a list
138    of formulas, and we're checking the one given by the index clause
139    (MAX_FORMULA denotes the actual formula; if clause is smaller,
140    we're looking at one of the user-defined formulas).  We're at
141    position *i in the formula.  If we find a legitimate variable there
142    and eval is 1, VarToToken puts its value in *tok; if eval is 0,
143    just move past the variable.  Returns 1 or 0, depending on whether
144    a legitimate variable was found.
145 */
146 static int VarToToken (struct game *g, int clause, char *string, int *i, int *tok, int eval)
147 {
148   struct player *me = &player_globals.parray[g->black];
149   struct player *you = &player_globals.parray[g->white];
150   int index=0, c;
151   double dummy_sterr;
152
153   /* We list possible variables with the longest names first. */
154   if (MoveIndexPastString(string, i, "registered"))
155     *tok=BoolCheckPFlag(g->white, PFLAG_REG);
156   else if (MoveIndexPastString(string, i, "ratingdiff"))
157     *tok = GetRating(you, g->type) - GetRating(me, g->type);
158   else if (MoveIndexPastString(string, i, "assessloss"))
159   {
160     if (g->rated)
161       rating_sterr_delta(g->black, g->white, g->type,
162                          time(0), RESULT_LOSS, tok, &dummy_sterr);
163     else *tok = 0;
164   }
165   else if (MoveIndexPastString(string, i, "assessdraw"))
166   {
167     if (g->rated)
168       rating_sterr_delta(g->black, g->white, g->type,
169                          time(0), RESULT_DRAW, tok, &dummy_sterr);
170     else *tok = 0;
171   }
172   else if (MoveIndexPastString(string, i, "assesswin"))
173   {
174     if (g->rated)
175       rating_sterr_delta(g->black, g->white, g->type,
176                          time(0), RESULT_WIN, tok, &dummy_sterr);
177     else *tok = 0;
178   }
179   else if (MoveIndexPastString(string, i, "mymaxtime"))
180   {
181     if (GetNumberInsideParens(g, clause, i, tok, eval) != ERR_NONE)
182       return (0);
183     *tok = Maxtime (g, *tok, 1);
184   }
185   else if (MoveIndexPastString(string, i, "timeseal"))
186     *tok = net_globals.con[you->socket]->timeseal;
187   else if (MoveIndexPastString(string, i, "myrating"))
188     *tok = GetRating(me, g->type);
189   else if (MoveIndexPastString(string, i, "computer"))
190     *tok = in_list(-1, L_COMPUTER, you->name);
191   else if (MoveIndexPastString(string, i, "standard"))
192     *tok = (g->type == TYPE_STAND);
193   else if (MoveIndexPastString(string, i, "private"))
194     *tok = BoolCheckPFlag(g->white, PFLAG_PRIVATE);
195   else if (MoveIndexPastString(string, i, "maxtime"))
196   {
197     if (GetNumberInsideParens(g, clause, i, tok, eval) != ERR_NONE) 
198       return (0);
199     *tok = Maxtime (g, *tok, 2);
200   }
201   else if (MoveIndexPastString(string, i, "abuser"))
202     *tok = in_list(-1, L_ABUSER, you->name);
203   else if (MoveIndexPastString(string, i, "rating"))
204     *tok = GetRating(you, g->type);
205   else if (MoveIndexPastString(string, i, "rated")) *tok=g->rated;
206   else if (MoveIndexPastString(string, i, "nonstandard"))
207     *tok = (g->type == TYPE_NONSTANDARD);
208   else if (MoveIndexPastString(string, i, "lightning"))
209     *tok = (g->type == TYPE_LIGHT);
210   else if (MoveIndexPastString(string, i, "bughouse"))
211     *tok = (g->type == TYPE_BUGHOUSE);
212   else if (MoveIndexPastString(string, i, "untimed"))
213     *tok = (g->type == TYPE_UNTIMED);
214   else if (MoveIndexPastString(string, i, "blitz"))
215     *tok = (g->type == TYPE_BLITZ);
216   else if (MoveIndexPastString(string, i, "wild"))
217     *tok = (g->type == TYPE_WILD);
218   else if (MoveIndexPastString(string, i, "time")) *tok=g->wInitTime;
219   else if (MoveIndexPastString(string, i, "inc")) *tok=g->wIncrement;
220   else if (tolower(string[*i])=='f' && isdigit(string[*i+1]) &&
221            (c = (string[*i+1]-'1')) >= 0 && clause > c)
222   {
223     *i += 2;
224     return (!CheckFormula (g, c, &index, OPTYPE_NONE, tok, eval));
225   }
226   else return (0);
227   return (1);
228 }    /* end of function VarToToken. */
229
230 /* ScanForOp checks for an operator at position *i in string[]. */
231 static int ScanForOp (char *string, int *i)
232 {
233   while (string[*i] != '\0' && isspace(string[*i])) (*i)++;
234
235   if (string[*i] == '\0') return (OP_NONE);
236   if (string[*i] == '#') return (OP_NONE);
237   if (string[*i] == ')') return (OP_RTPAREN);
238
239   /* Two char operators and longer first. */
240   if (MoveIndexPastString(string, i, "and")) return (OP_AND);
241   if (MoveIndexPastString(string, i, "or")) return (OP_OR);
242   if (MoveIndexPastString(string, i, "||")) return (OP_OR);
243   if (MoveIndexPastString(string, i, "&&")) return (OP_AND);
244   if (MoveIndexPastString(string, i, "==")) return (OP_EQ);
245   if (MoveIndexPastString(string, i, "!=")) return (OP_NEQ);
246   if (MoveIndexPastString(string, i, "<>")) return (OP_NEQ);
247   if (MoveIndexPastString(string, i, ">=")) return (OP_GE);
248   if (MoveIndexPastString(string, i, "=>")) return (OP_GE);
249   if (MoveIndexPastString(string, i, "<=")) return (OP_LE);
250   if (MoveIndexPastString(string, i, "=<")) return (OP_LE);
251
252   /* One char operators now. */
253   if (MoveIndexPastString(string, i, "|")) return (OP_OR);
254   if (MoveIndexPastString(string, i, "&")) return (OP_AND);
255   if (MoveIndexPastString(string, i, ">")) return (OP_GT);
256   if (MoveIndexPastString(string, i, "<")) return (OP_LT);
257   if (MoveIndexPastString(string, i, "=")) return (OP_EQ);
258   if (MoveIndexPastString(string, i, "+")) return (OP_PLUS);
259   if (MoveIndexPastString(string, i, "-")) return (OP_MINUS);
260   if (MoveIndexPastString(string, i, "*")) return (OP_MULT);
261   if (MoveIndexPastString(string, i, "/")) return (OP_DIV);
262   return (OP_BAD);
263 }    /* end of function ScanForOp. */
264
265 /* OpType returns the precedence of the operator op. */
266 static int OpType (int op)
267 {
268   switch (op)
269   {
270     case OP_BAD:  return (OPTYPE_BAD);
271     case OP_NONE:  return (OPTYPE_NONE);
272     case OP_RTPAREN:  return (OPTYPE_RPAREN);
273     case OP_OR:  return (OPTYPE_OR);
274     case OP_AND:  return (OPTYPE_AND);
275     case OP_EQ: case OP_NEQ:  return (OPTYPE_COMPEQ);
276     case OP_GT: case OP_GE:  case OP_LT: case OP_LE:  return (OPTYPE_COMPGL);
277     case OP_PLUS: case OP_MINUS:  return (OPTYPE_ADD);
278     case OP_MULT: case OP_DIV:  return (OPTYPE_MULT);
279     case OP_NEGATE: case OP_NOT:  return (OPTYPE_UNARY);
280     default: return (OPTYPE_BAD);
281   }
282 }    /* end of function OpType. */
283
284 /* In EvalBinaryOp, *left is the left operand; and op is the
285    current operator.  *g and clause specify which formula string to
286    look at (we're checking player_globals.parray[g->black].formulaLines[clause]),
287    and *i tells us where we are in the string.  We look for a right
288    operand from position *i in the string, and place the expression
289    (*left op right) in *left.  For example, if *left=6, op =
290    OP_MULT, and we pull off right = 4, we replace *left with
291    6*4=24.  Returns 0 if no error; otherwise indicates the error.
292 */
293 static int EvalBinaryOp (int *left, int op, struct game *g, int clause, int *i)
294 {
295   int right, ret;
296   if ((op==OP_OR) && (*left != 0))            /* Nothing to decide. */
297   {
298     *left = 1;
299     return (CheckFormula (g, clause, i, OpType(op), &right, 0));
300   }
301   else if ((op==OP_AND) && (*left == 0))     /* Nothing to decide. */
302     return (CheckFormula (g, clause, i, OpType(op), &right, 0));
303
304   else {
305     ret = CheckFormula (g, clause, i, OpType(op), &right, 1);
306     if (ret != ERR_NONE) return (ret);
307   }
308   switch (op)
309   {
310     default:  case OP_BAD:  return (ERR_BADOP);
311     case OP_NONE:  case OP_RTPAREN:  return (ERR_NONE);
312     case OP_OR:  *left = (*left || right);  return (ERR_NONE);
313     case OP_AND:  *left = (*left && right);  return (ERR_NONE);
314     case OP_EQ:  *left = (*left == right);  return (ERR_NONE);
315     case OP_NEQ:  *left = (*left != right);  return (ERR_NONE);
316     case OP_GT:  *left = (*left > right);  return (ERR_NONE);
317     case OP_GE:  *left = (*left >= right);  return (ERR_NONE);
318     case OP_LT:  *left = (*left < right);  return (ERR_NONE);
319     case OP_LE:  *left = (*left <= right);  return (ERR_NONE);
320     case OP_PLUS:  *left += right;  return (ERR_NONE);
321     case OP_MINUS:  *left -= right;  return (ERR_NONE);
322     case OP_MULT:  *left *= right;  return (ERR_NONE);
323     case OP_DIV:
324       if (right != 0)
325       {
326         *left /= right;
327         return (ERR_NONE);
328       }
329       else
330       {
331         pprintf(g->black, "Dividing by %f instead or zero in formula.\n", FUDGE_FACTOR);
332         *left /= FUDGE_FACTOR;
333         return (ERR_NONE);
334       }
335   }
336 }    /* end of function EvalBinaryOp. */
337
338 /* If eval is 1, ScanForNumber evaluates the number at position *i
339    in the formula given by *g and clause, and place this value in
340    *token.  op_type is the precedence of the operator preceding the
341    *i'th position.  If we come to an operator of higher precedence,
342    we must keep going before we can leave this function.  If eval
343    is 0, just move past the number we would evaluate.  Returns 0 if
344    no error; otherwise return code indicates the error.
345 */
346 static int ScanForNumber (struct game *g, int clause, int *i, int op_type,
347                    int *token, int eval)
348 {
349   char *string = GetPlayersFormula (&player_globals.parray[g->black], clause);
350   char c;
351   int ret;
352
353   while ((string[*i] != '\0') && (isspace(string[*i]))) (*i)++;
354   switch (c = string[*i])
355   {
356     case '\0':  case '#':
357       if (op_type != OPTYPE_NONE) return (ERR_EOF);
358       else *token=1;
359       return (ERR_NONE);
360
361     case ')':
362       if (op_type != OPTYPE_PAREN) return (ERR_PAREN);
363       else *token=1;
364       return(ERR_NONE);
365
366     case '(':  return GetNumberInsideParens(g,clause,i,token,eval);
367
368     case '-':  case '!':
369       ++(*i);
370       if (c==string[*i]) return(ERR_UNARY);
371       ret = ScanForNumber(g,clause,i,OPTYPE_UNARY,token,eval);
372       if (ret != ERR_NONE)
373         return (ret);
374       if (c == '-') *token = -(*token);
375       else if (c == '!') *token = !(*token);
376       return (ERR_NONE);
377
378     default:
379       if (isdigit(string[*i]))
380       {
381         *token = 0;
382
383         do *token = 10 * (*token) + string[(*i)++] - '0';
384         while (isdigit(string[*i]));
385
386         while (string[*i] != '\0' && isspace(string[*i])) (*i)++;
387         if (MoveIndexPastString (string, i, "minutes"))
388           *token *= 60;
389         return (ERR_NONE);
390       }
391       else if (isalpha(string[*i]))
392       {
393         if (!VarToToken (g, clause, string, i, token, eval))
394           return (ERR_BADVAR);
395         return(ERR_NONE);
396       }    
397       else return (ERR_NONESENSE);
398   }
399 }    /* end of function ScanForNumber. */
400
401 /* If eval is 1, CheckFormula looks for the next token in the
402    formula given by *g, clause, and *i; usually this is the right
403    side of an expression whose operator has precedence OpType; if
404    eval is 0, just go to the end of an expression.  Return 0 if no
405    error; otherwise, return error type.
406 */
407 static int CheckFormula(struct game *g, int clause, int *i, int op_type,
408                         int *result, int eval)
409 {
410   int token, ret, nextOp, lastPos;
411   char *string = GetPlayersFormula (&player_globals.parray[g->black], clause);
412
413   if (string == NULL)
414   {
415     *result = 1;
416     return (ERR_NONE);
417   }
418   ret = ScanForNumber (g, clause, i, op_type, &token, eval);
419   if (ret != ERR_NONE)
420     return (ret);
421   lastPos = *i;
422   nextOp = ScanForOp (string, i);
423   while (OpType(nextOp) > op_type)    /* higher precedence. */
424   {
425     if (nextOp==OP_RTPAREN) return (ERR_PAREN);
426     if (!eval)
427       ret = CheckFormula(g, clause, i, OpType(nextOp), &token, 0);
428     else ret = EvalBinaryOp (&token, nextOp, g, clause, i);
429     if (ret != ERR_NONE) return (ret);
430     lastPos = *i;
431     nextOp = ScanForOp (string, i);
432   }
433   if (nextOp == OP_BAD) return(ERR_BADOP);
434   *result = token;
435
436   /* move back unless we're at the end or at a right paren, in which
437      case we never went forward. */
438   if (nextOp != OP_NONE && nextOp != OP_RTPAREN) *i=lastPos;
439   return (ERR_NONE);
440 }    /* end of function CheckFormula. */
441
442 /* which clauses are relevant for a player's formula. */
443 static int ChooseClauses(struct player *who, char *formula)
444 {
445   int i, which, ret=0;
446
447   if (formula == NULL)
448     return ret;
449   for (i=0; formula[i] != '\0' && formula[i] != '#'; i++) {
450     if (formula[i] != 'f' || (i > 0 && isalnum(formula[i-1]))
451         || !isdigit(formula[i+1]))
452       continue;
453     sscanf(&formula[i], "f%d", &which);
454     ret |= (1 << (which - 1));
455   }
456   /* now scan clauses found as part of the formula. */
457   for (i = MAX_FORMULA - 1; i >= 0; i--) {
458     if (ret & (1 << i)) {
459       ret |= ChooseClauses(who, who->formulaLines[i]);
460     }
461   }
462   return ret;
463 }    /* end of function ChooseClauses. */
464
465 static void ExplainFormula (struct game *g, struct textlist **clauses)
466 {
467   int i, which, dummy_index, value;
468   char txt[20];
469   struct player *challenged = &player_globals.parray[g->black];
470   struct textlist **Cur = clauses;
471
472   which = ChooseClauses(challenged, challenged->formula);
473
474   for (i = 0; i < MAX_FORMULA; i++) {
475     if ((which & (1 << i)) == 0)
476       continue;
477     dummy_index = 0;
478     CheckFormula (g, i, &dummy_index, OPTYPE_NONE, &value, 1);
479     sprintf(txt, "%d", value);
480     SaveTextListEntry (Cur, txt, i);
481     Cur = &(*Cur)->next;
482   }
483 }
484
485
486 /* GameMatchesFormula converts parameters to a game structure
487    and passes a pointer to this game to CheckFormula for
488    evaluation.  It returns the evaluated formula.
489 */
490 int GameMatchesFormula (int w, int b, int wTime, int wInc, int bTime,
491            int bInc, int rated, int gametype, struct textlist **clauseList)
492 {
493   struct game g;
494   int index=0, ret;
495
496   g.white = w;  g.black = b;
497   g.wInitTime = wTime;  g.bInitTime = bTime;
498   g.wIncrement = wInc;  g.bIncrement = bInc;
499   g.rated = rated;
500   g.type = gametype;
501
502   if (CheckFormula(&g, MAX_FORMULA, &index, OPTYPE_NONE, &ret, 1) != ERR_NONE)
503     return (0);
504   if (ret == 0) ExplainFormula (&g, clauseList);
505   return (ret);
506 }    /* end of function GameMatchesFormula. */
507
508 /* SetValidFormula sets a clause of player p and creates a game structure
509    to check whether that new formula is legitimate.  If so, return 1;
510    otherwise, reset the formula and return 0.
511 */
512 int SetValidFormula (int p, int clause, char *string)
513 {
514   struct game g;
515   int index=0, ret, err=ERR_NONE;
516   char *Old=NULL, **Cur;
517   struct player *me = &player_globals.parray[p];
518
519   if (clause==MAX_FORMULA)
520     Cur = &me->formula;
521   else
522     Cur = &me->formulaLines[clause];
523
524   Old = *Cur;
525
526   if (string != NULL) {
527       string = eatwhite(string);
528       *Cur = (*string != '\0'  ?  strdup (string)  :  NULL);
529   }
530   else *Cur = NULL;
531
532   if (*Cur==NULL) {
533       if (Old != NULL) free(Old);
534       return 1;
535   }
536
537   g.white = g.black = p;
538   g.wInitTime = g.bInitTime = me->d_time;
539   g.wIncrement = g.bIncrement = me->d_inc;
540   g.rated = BoolCheckFlag(me->Flags, PFLAG_RATED);
541   g.type = TYPE_BLITZ;
542   err = CheckFormula (&g, clause, &index, OPTYPE_NONE, &ret, 0);
543
544   if (err != ERR_NONE) {
545       /* Bad formula; reset it. */
546        free(*Cur);
547       *Cur = Old;
548   } else {
549       if (Old != NULL) free(Old);
550   }
551   return (err == ERR_NONE);
552 }
553
554 void ShowClauses (int p, int p1, textlist *clauses)
555 {
556   textlist *Cur;
557
558   if (clauses != NULL)
559     pprintf(p, "\n");
560   for (Cur = clauses; Cur != NULL; Cur = Cur->next) {
561 /* changed this next line to pass "" not NULL to pprintf. 2.24.96 fb
562     pprintf(p, "f%d=%s: %s\n", Cur->index + 1, Cur->text,
563                player_globals.parray[p1].formulaLines[Cur->index]);
564 */
565     pprintf(p, "f%d=%s: %s\n", Cur->index + 1, Cur->text,
566                (player_globals.parray[p1].formulaLines[Cur->index] == NULL) ? "" :
567                player_globals.parray[p1].formulaLines[Cur->index]);
568   }
569 }