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.
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.
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.
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
24 /* Operators allowed, in order of precedence:
28 <, <=, =<, >, >=, =>, (<= and =< are equivalent, as are >= and =>)
29 =, ==, !=, <>, (two ways of saying both equals and not equals)
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.
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.
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'
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
57 set formula f1 # rated 5 min. games only
58 set formula etime >= 10 & f2 > -100 # long games, decent competition
60 or set formula f2 >= 0 | blitz
61 depending on your mood.
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.
71 static int CheckFormula(struct game *g, int clause, int *i, int op_type,
72 int *result, int eval);
75 static char *GetPlayersFormula (struct player *pWho, int clause)
77 if (clause==MAX_FORMULA) return (pWho->formula);
78 else return (pWho->formulaLines[clause]);
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.
86 static int MoveIndexPastString (char *string, int *i, char *text)
89 if (strncasecmp(text, string + *i, n)) return (0);
92 } /* end of function MoveIndexPastString. */
94 static int GetNumberInsideParens (struct game *g, int clause, int *i, int *token,
97 char *string = GetPlayersFormula(&player_globals.parray[g->black], clause);
100 while (string[*i] != '\0' && isspace(string[*i])) (*i)++;
101 if (!MoveIndexPastString(string, i, "(")) return (ERR_BADCHAR);
103 ret = CheckFormula(g, clause, i, OPTYPE_PAREN, token, eval);
106 if (MoveIndexPastString(string, i, ")")) return (ERR_NONE);
107 else return (ERR_PAREN);
110 static int Maxtime (struct game *g, int numMoves, int numPlayers)
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 */
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 */
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 */
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.
146 static int VarToToken (struct game *g, int clause, char *string, int *i, int *tok, int eval)
148 struct player *me = &player_globals.parray[g->black];
149 struct player *you = &player_globals.parray[g->white];
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"))
161 rating_sterr_delta(g->black, g->white, g->type,
162 time(0), RESULT_LOSS, tok, &dummy_sterr);
165 else if (MoveIndexPastString(string, i, "assessdraw"))
168 rating_sterr_delta(g->black, g->white, g->type,
169 time(0), RESULT_DRAW, tok, &dummy_sterr);
172 else if (MoveIndexPastString(string, i, "assesswin"))
175 rating_sterr_delta(g->black, g->white, g->type,
176 time(0), RESULT_WIN, tok, &dummy_sterr);
179 else if (MoveIndexPastString(string, i, "mymaxtime"))
181 if (GetNumberInsideParens(g, clause, i, tok, eval) != ERR_NONE)
183 *tok = Maxtime (g, *tok, 1);
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"))
197 if (GetNumberInsideParens(g, clause, i, tok, eval) != ERR_NONE)
199 *tok = Maxtime (g, *tok, 2);
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)
224 return (!CheckFormula (g, c, &index, OPTYPE_NONE, tok, eval));
228 } /* end of function VarToToken. */
230 /* ScanForOp checks for an operator at position *i in string[]. */
231 static int ScanForOp (char *string, int *i)
233 while (string[*i] != '\0' && isspace(string[*i])) (*i)++;
235 if (string[*i] == '\0') return (OP_NONE);
236 if (string[*i] == '#') return (OP_NONE);
237 if (string[*i] == ')') return (OP_RTPAREN);
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);
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);
263 } /* end of function ScanForOp. */
265 /* OpType returns the precedence of the operator op. */
266 static int OpType (int op)
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);
282 } /* end of function OpType. */
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.
293 static int EvalBinaryOp (int *left, int op, struct game *g, int clause, int *i)
296 if ((op==OP_OR) && (*left != 0)) /* Nothing to decide. */
299 return (CheckFormula (g, clause, i, OpType(op), &right, 0));
301 else if ((op==OP_AND) && (*left == 0)) /* Nothing to decide. */
302 return (CheckFormula (g, clause, i, OpType(op), &right, 0));
305 ret = CheckFormula (g, clause, i, OpType(op), &right, 1);
306 if (ret != ERR_NONE) return (ret);
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);
331 pprintf(g->black, "Dividing by %f instead or zero in formula.\n", FUDGE_FACTOR);
332 *left /= FUDGE_FACTOR;
336 } /* end of function EvalBinaryOp. */
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.
346 static int ScanForNumber (struct game *g, int clause, int *i, int op_type,
347 int *token, int eval)
349 char *string = GetPlayersFormula (&player_globals.parray[g->black], clause);
353 while ((string[*i] != '\0') && (isspace(string[*i]))) (*i)++;
354 switch (c = string[*i])
357 if (op_type != OPTYPE_NONE) return (ERR_EOF);
362 if (op_type != OPTYPE_PAREN) return (ERR_PAREN);
366 case '(': return GetNumberInsideParens(g,clause,i,token,eval);
370 if (c==string[*i]) return(ERR_UNARY);
371 ret = ScanForNumber(g,clause,i,OPTYPE_UNARY,token,eval);
374 if (c == '-') *token = -(*token);
375 else if (c == '!') *token = !(*token);
379 if (isdigit(string[*i]))
383 do *token = 10 * (*token) + string[(*i)++] - '0';
384 while (isdigit(string[*i]));
386 while (string[*i] != '\0' && isspace(string[*i])) (*i)++;
387 if (MoveIndexPastString (string, i, "minutes"))
391 else if (isalpha(string[*i]))
393 if (!VarToToken (g, clause, string, i, token, eval))
397 else return (ERR_NONESENSE);
399 } /* end of function ScanForNumber. */
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.
407 static int CheckFormula(struct game *g, int clause, int *i, int op_type,
408 int *result, int eval)
410 int token, ret, nextOp, lastPos;
411 char *string = GetPlayersFormula (&player_globals.parray[g->black], clause);
418 ret = ScanForNumber (g, clause, i, op_type, &token, eval);
422 nextOp = ScanForOp (string, i);
423 while (OpType(nextOp) > op_type) /* higher precedence. */
425 if (nextOp==OP_RTPAREN) return (ERR_PAREN);
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);
431 nextOp = ScanForOp (string, i);
433 if (nextOp == OP_BAD) return(ERR_BADOP);
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;
440 } /* end of function CheckFormula. */
442 /* which clauses are relevant for a player's formula. */
443 static int ChooseClauses(struct player *who, char *formula)
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]))
453 sscanf(&formula[i], "f%d", &which);
454 ret |= (1 << (which - 1));
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]);
463 } /* end of function ChooseClauses. */
465 static void ExplainFormula (struct game *g, struct textlist **clauses)
467 int i, which, dummy_index, value;
469 struct player *challenged = &player_globals.parray[g->black];
470 struct textlist **Cur = clauses;
472 which = ChooseClauses(challenged, challenged->formula);
474 for (i = 0; i < MAX_FORMULA; i++) {
475 if ((which & (1 << i)) == 0)
478 CheckFormula (g, i, &dummy_index, OPTYPE_NONE, &value, 1);
479 sprintf(txt, "%d", value);
480 SaveTextListEntry (Cur, txt, i);
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.
490 int GameMatchesFormula (int w, int b, int wTime, int wInc, int bTime,
491 int bInc, int rated, int gametype, struct textlist **clauseList)
496 g.white = w; g.black = b;
497 g.wInitTime = wTime; g.bInitTime = bTime;
498 g.wIncrement = wInc; g.bIncrement = bInc;
502 if (CheckFormula(&g, MAX_FORMULA, &index, OPTYPE_NONE, &ret, 1) != ERR_NONE)
504 if (ret == 0) ExplainFormula (&g, clauseList);
506 } /* end of function GameMatchesFormula. */
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.
512 int SetValidFormula (int p, int clause, char *string)
515 int index=0, ret, err=ERR_NONE;
516 char *Old=NULL, **Cur;
517 struct player *me = &player_globals.parray[p];
519 if (clause==MAX_FORMULA)
522 Cur = &me->formulaLines[clause];
526 if (string != NULL) {
527 string = eatwhite(string);
528 *Cur = (*string != '\0' ? strdup (string) : NULL);
533 if (Old != NULL) free(Old);
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);
542 err = CheckFormula (&g, clause, &index, OPTYPE_NONE, &ret, 0);
544 if (err != ERR_NONE) {
545 /* Bad formula; reset it. */
549 if (Old != NULL) free(Old);
551 return (err == ERR_NONE);
554 void ShowClauses (int p, int p1, textlist *clauses)
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]);
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]);