Logo Search packages:      
Sourcecode: yics version File versions  Download package

formula.c

/*
 * YICS: Connect a FICS interface to the Yahoo! Chess server.
 * Copyright (C) 2004  Chris Howie
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "types.h"
#include "formula.h"
#include "globals.h"
#include "vars.h"

static float groupValue(const char *group, int *i, FormulaToken *tokens, bool *failed, bool first);

#define IsNumber(x) (((x) >= '0') && ((x) <= '9'))
#define IsLetter(x) (((x) >= 'a') && ((x) <= 'z'))

enum {
      F_BLITZ = 0,
      F_HOST,
      F_INC,
      F_LIGHTNING,
      F_MYRATING,
      F_PRIVATE,
      F_RATED,
      F_RATING,
      F_RATINGDIFF,
      F_STANDARD,
      F_TIME,
      F_UNRATED,
      F_UNTIMED,
      F_USER
};

static const FormulaToken newFormulaTokens[] = {
      {"blitz",   0},
      {"host",    0},
      {"inc",           0},
      {"lightning",     0},
      {"myrating",      0},
      {"private", 0},
      {"rated",   0},
      {"rating",  0},
      {"ratingdiff",    0},
      {"standard",      0},
      {"time",    0},
      {"unrated", 0},
      {"untimed", 0},

      {NULL,            0},   /* f1 */
      {NULL,            0},   /* f2 */
      {NULL,            0},   /* f3 */
      {NULL,            0},   /* f4 */
      {NULL,            0},   /* f5 */
      {NULL,            0},   /* f6 */
      {NULL,            0},   /* f7 */
      {NULL,            0},   /* f8 */
      {NULL,            0},   /* f9 */

      {NULL,            0},
};

const char *fvars[] = {"f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", NULL};

static OperatorDef operators[] = {
      {"#", OP_NONE},
      {")", OP_ENDGROUP},

      /* Spaces to prevent blending into a token. */
      {"and ",OP_AND},
      {"or ",     OP_OR},

      /* Ordered by length to prevent "|" from matching before "||", etc. */
      {"||",      OP_OR},
      {"&&",      OP_AND},
      {"==",      OP_EQ},
      {"!=",      OP_NEQ},
      {"<>",      OP_NEQ},
      {">=",      OP_GE},
      {"=>",      OP_GE},
      {"<=",      OP_LE},
      {"=<",      OP_LE},
      {"|", OP_OR},
      {"&", OP_AND},
      {">", OP_GT},
      {"<", OP_LT},
      {"=", OP_EQ},
      {"+", OP_ADD},
      {"-", OP_SUB},
      {"*", OP_MULT},
      {"/", OP_DIV},
      {NULL,      OP_BAD}
};

static float ff_abs(float v) {
      return (v < 0) ? -v : v;
}

static float ff_int(float v) {
      return (float)((int)v);
}

static FunctionDef functions[] = {
      {"abs",     ff_abs},
      {"int",     ff_int},
      {NULL,      NULL}
};

static void fillTokens(FormulaToken *tokens, Table *table) {
      Player *opp = NULL;
      int rated;

      if (table->players[0] == pme)
            opp = table->players[1];
      else if (table->players[1] == pme)
            opp = table->players[0];

      tokens[F_TIME].value = (float)tabletime(table);
      tokens[F_INC].value = (float)tableinc(table);

      switch (gameType(table)) {
            case 'u':
                  tokens[F_UNTIMED].value = 1;
                  break;

            case 'l':
                  tokens[F_LIGHTNING].value = 1;
                  break;

            case 'b':
                  tokens[F_BLITZ].value = 1;
                  break;

            case 's':
                  tokens[F_STANDARD].value = 1;
                  break;
      }

      tokens[F_MYRATING].value = (pme->rating == PROVISIONAL) ? 1200.0f : pme->rating;
      tokens[F_RATING].value = (opp == NULL) ? 0.0f :
            (opp->rating == PROVISIONAL) ? 1200.0f : opp->rating;

      tokens[F_RATINGDIFF].value = tokens[F_RATING].value - tokens[F_MYRATING].value;

      tokens[F_PRIVATE].value = (table->protection == 2) ? 1.0f : 0.0f;

      rated = tablerated(table) ? 1 : 0;
      tokens[F_RATED].value = (float)rated;
      tokens[F_UNRATED].value = (float)(1 - rated);

      tokens[F_HOST].value = (table->host == pme) ? 1.0f : 0.0f;
}

static void skipSpace(const char *s, int *i) {
      while ((s[*i] != '\0') && (s[*i] == ' '))
            (*i)++;
}

static float getNumber(const char *s, int *i, bool *failed) {
      int j = *i;
      int ns;
      bool negate = false;

      *failed = false;

      while (s[j] == '-') {
            negate = (bool)!negate;
            j++;
      }

      ns = j;
      while (IsNumber(s[j]))
            j++;

      if (j == ns) {
            *failed = true;
            return 0;
      }

      if (s[j] == '.') {
            ns = ++j;

            while (IsNumber(s[j]))
                  j++;

            if (j == ns) {
                  *failed = true;
                  return 0;
            }
      }

      *i = j;
      return (float)atof(&s[ns]);
}

static float tokenValue(const char *s, int *i, FormulaToken *tokens, bool *failed) {
      float value;
      FormulaToken *ft;
      FunctionDef *fnct;
      int len;
      int pos;
      bool not = false;

      /* Check unary operators. */
      while (s[*i] == '!') {
            not = (bool)!not;
            (*i)++;
            skipSpace(s, i);
      }

      /* This is unary too, despite that it matches a closing paren. */
      if (s[*i] == '(') {
            (*i)++;
            return groupValue(s, i, tokens, failed, false);
      }

      /* Check numeric. */
      value = getNumber(s, i, failed);
      if (!*failed)
            return not ? !value : value;

      /* Check formula token. */
      for (ft = tokens; ft->token != NULL; ft++) {
            len = strlen(ft->token);
            if (!strncmp(&s[*i], ft->token, len)) {
                  pos = len + *i;
                  if (IsLetter(s[pos]) || IsNumber(s[pos]))
                        continue;

                  *i += len;
                  *failed = false;
                  return not ? !ft->value : ft->value;
            }
      }

      /* Check functions. */
      for (fnct = functions; fnct->name != NULL; fnct++) {
            len = strlen(fnct->name);
            if (!strncmp(&s[*i], fnct->name, len)) {
                  pos = len + *i;
                  skipSpace(s, &pos);
                  if (s[pos] != '(')
                        continue;

                  *i = pos + 1;
                  value = groupValue(s, i, tokens, failed, false);
                  if (*failed)
                        break;

                  *failed = false;
                  return fnct->func(value);
            }
      }

      *failed = true;
      return 0;
}

static Operator operator(const char *s, int *i) {
      OperatorDef *ops;
      int len;

      for (ops = operators; ops->token != NULL; ops++) {
            len = strlen(ops->token);
            if (!strncmp(&s[*i], ops->token, len)) {
                  *i += len;
                  return ops->op;
            }
      }

      return OP_BAD;
}

static float groupValue(const char *group, int *i, FormulaToken *tokens, bool *failed, bool first) {
      float value;
      float value2;
      Operator op;

      skipSpace(group, i);

      if ((group[*i] == '\0') || (group[*i] == '#')) {
            *failed = (bool)!first;
            return 0;
      }

      value = tokenValue(group, i, tokens, failed);
      if (*failed)
            return 0;

      for (;;) {
            skipSpace(group, i);

            if (group[*i] == '\0')
                  break;

            op = operator(group, i);
            if (op == OP_BAD) {
                  *failed = true;
                  return 0;
            } else if (op == OP_ENDGROUP) {
                  *failed = (bool)first;
                  return value;
            } else if (op == OP_NONE) {
                  *failed = (bool)!first;
                  return value;
            }

            skipSpace(group, i);

            value2 = tokenValue(group, i, tokens, failed);
            if (*failed)
                  return 0;

            switch (op) {
                  /* Boolean */
                  case OP_OR:
                        value = (float)(value || value2);
                        break;

                  case OP_AND:
                        value = (float)(value && value2);
                        break;

                  /* Comparison */
                  case OP_EQ:
                        value = (float)(value == value2);
                        break;

                  case OP_NEQ:
                        value = (float)(value != value2);
                        break;

                  case OP_GT:
                        value = (float)(value > value2);
                        break;

                  case OP_GE:
                        value = (float)(value >= value2);
                        break;

                  case OP_LT:
                        value = (float)(value < value2);
                        break;

                  case OP_LE:
                        value = (float)(value <= value2);
                        break;

                  /* Mathematical */
                  case OP_ADD:
                        value += value2;
                        break;

                  case OP_SUB:
                        value -= value2;
                        break;

                  case OP_MULT:
                        value *= value2;
                        break;

                  case OP_DIV:
                        if (value2 == 0)
                              value = 0;
                        else
                              value /= value2;
                        break;

                  /* These are handled other places. */
                  case OP_BAD:
                  case OP_NONE:
                  case OP_ENDGROUP:
                        break;
            }
      }

      *failed = (bool)!first;
      return value;
}

int checkFormula(Table *table) {
      String *formula;
      String *fvar;
      int fv, i;
      float value;
      bool failed;
      FormulaToken *tokens;

      formula = variables[VAR_FORMULA].string;
      if ((formula == NULL) || (formula->length == 0))
            return 1;

      tokens = malloc(sizeof(newFormulaTokens));
      if (tokens == NULL)
            return -2;

      memcpy(tokens, newFormulaTokens, sizeof(newFormulaTokens));
      fillTokens(tokens, table);

      for (fv = 0; fv < 10; fv++) {
            fvar = variables[VAR_F1 + fv].string;

            if ((fvar == NULL) || (fvar->length == 0))
                  value = 0;
            else {
                  i = 0;
                  value = groupValue(fvar->string, &i, tokens, &failed, true);

                  if (failed) {
                        free(tokens);
                        return -1;
                  }
            }

            tokens[F_USER + fv].token = fvars[fv];
            tokens[F_USER + fv].value = value;
      }

      i = 0;
      value = groupValue(formula->string, &i, tokens, &failed, true);

      free(tokens);

      if (failed)
            return -1;

      return ((int)value == 0) ? 0 : 1;
}

bool validateFormula(Variable *fvar, void *new) {
      FormulaToken *tokens;
      FormulaToken *ct;
      const char **fn = fvars;
      int mv, i;
      bool failed;

      tokens = malloc(sizeof(newFormulaTokens));
      if (tokens == NULL)
            return false;

      memcpy(tokens, newFormulaTokens, sizeof(newFormulaTokens));

      /* Give a dummy value to other f-vars. */
      mv = (fvar->number == 0) ? 9 : (fvar->number - 1);
      for (i = 0, ct = &tokens[F_USER]; i < mv; i++, ct++) {
            ct->token = *(fn++);
            ct->value = 0;
      }

      i = 0;
      groupValue((char *)new, &i, tokens, &failed, true);

      free(tokens);
      return (bool)!failed;
}

Generated by  Doxygen 1.6.0   Back to index