24 game/C

From Rosetta Code

First we need to create a RPN parser (RPN since it's easier!).

rpn.h

#ifndef RC_RPN_H
#define RC_RPN_H 1
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdarg.h>
#include <math.h>
#include <assert.h>

#define RPN_STACK_LIMIT 1024

double rpn_pop(void);
void rpn_push(double);
void rpn_reset();

bool rpn_is_numeric(char *);
double rpn_get_number(char *);

bool rpn_evaluate(const char *);

#endif

rpn.c

Uses: C Runtime (Components:{{#foreach: component$n$|{{{component$n$}}}Property "Uses Library" (as page type) with input value "Library/C Runtime/{{{component$n$}}}" contains invalid characters or is incomplete and therefore can cause unexpected results during a query or annotation process., }})
#include "rpn.h"

static double stack[RPN_STACK_LIMIT];
static int stack_ip = 0;

static char *delim = " \t\n";

static void rpn_log(char *msg, ...)
{
  va_list al;
  va_start(al, msg);
  fprintf(stderr, "\n*** RC_RPN says: ");
  vfprintf(stderr, msg, al);
  fprintf(stderr, "\n");
  va_end(al);
}


// OPERATORS
static void rpn_add()
{
  rpn_push( rpn_pop() + rpn_pop() );
}
static void rpn_sub()
{
  double a, b;
  a = rpn_pop(); b = rpn_pop();
  rpn_push( b - a );
}
static void rpn_mul()
{
  rpn_push( rpn_pop() * rpn_pop() );
}
static void rpn_div()
{
  double a, b;
  a = rpn_pop(); b = rpn_pop();
  rpn_push( b / a );  
}
static void rpn_neg()
{
  rpn_push( -rpn_pop() );
}
static void rpn_swap()
{
  double a, b;
  a = rpn_pop(); b = rpn_pop();
  rpn_push(a); rpn_push(b);
}
// END OPERATORS

static struct op_func {
  char *opname;
  void (*func)();
} oplist[] = {
  { "+", rpn_add }, { "-", rpn_sub }, { "*", rpn_mul }, { "/", rpn_div },
  { "n", rpn_neg }, { "s", rpn_swap }, { NULL, NULL }
};

static void rpn_call_op(char *op)
{
  int i;
  
  for(i=0; oplist[i].opname != NULL; i++)
  {
    if ( strcmp(op, oplist[i].opname) == 0 )
    {
      oplist[i].func();
      return;
    }
  }
  rpn_log("unknown operator ignored");
}


double rpn_pop()
{
  if ( stack_ip > 0 ) return stack[--stack_ip];
  rpn_log("stack underflow; computations won't be right");
  return 0.0;
}

void rpn_push(double v)
{
  if ( stack_ip < RPN_STACK_LIMIT )
  {
    stack[ stack_ip++ ] = v;
  } else {
    rpn_log("stack overflow");
  }
}

void rpn_reset()
{
  stack_ip = 0;
}

bool rpn_is_numeric(char *e)
{ // lazy check; it says true even for "1.23hello"...
  char *end;
  double d = strtod(e, &end);
  if ( end == e ) return false;
  return true;
}

// this is meaningful only if e is already checked to be a possible number
// it's rendundant but...
double rpn_get_number(char *e)
{
  if ( !rpn_is_numeric(e) )
  {
    rpn_log("wrong call; return value will be wrong");
  }
  return strtod(e, NULL);
}

bool rpn_evaluate(const char *expr)
{
  char *tok;
  
  char *buf = malloc( strlen(expr) + 1); assert( buf != NULL );
  strcpy(buf, expr);

  for(tok = strtok(buf, delim); tok != NULL; tok = strtok(NULL, delim) )
  {
    if ( rpn_is_numeric(tok) )
    {
      rpn_push( rpn_get_number(tok) );
    } else {
      rpn_call_op(tok);
    }
  }
  free(buf);
  return true; // should check if it's all ok...
}

24 game

Now we have something able to evaluate simple expression; so here it is the game.

Uses: C Runtime (Components:{{#foreach: component$n$|{{{component$n$}}}Property "Uses Library" (as page type) with input value "Library/C Runtime/{{{component$n$}}}" contains invalid characters or is incomplete and therefore can cause unexpected results during a query or annotation process., }})
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#include "rpn.h"

#define N_NUMBERS 4
#define MAX_EXPR_LEN 1024
#define EVAL_TO 24
#define TOLERANCE 0.00001

int ranged_rand(int min, int max)
{
  return min + (int)((double)(max - min) * (rand() / (RAND_MAX + 1.0)));
}

// to be ok (true) all numbers must appears and just once
bool check_expression(const char *expr, int *numbers)
{
  bool rv = true;
  char *tok;
  char *expr_copy = malloc(strlen(expr)+1);
  char nc[10];
  int i;
  const char *delim = " \t\n";

  strcpy(expr_copy, expr);
  for(i=0; i < 10; i++) nc[i] = 0;
  for(i=0; i < N_NUMBERS; i++)
  {
    nc[ numbers[i] ]++;
  }
  for (tok = strtok(expr_copy, delim); tok != NULL; tok = strtok(NULL, delim) )
  {
    if ( rpn_is_numeric(tok) )
    {
      if ( (strlen(tok) != 1) || (*tok < '1') || (*tok > '9') )
      {
	rv = false;
	printf("numbers must be integer and between 1 and 9\n");
	break;
      } else {
	int in = *tok - '0';
	if ( (nc[in] <= 0) ) { 
	  printf("you can't use more numbers than those given!\n");
	  rv = false; break;
	}
	nc[in]--;
      }
    }
  }
  free(expr_copy);
  if ( rv )
  {
    for(i=0; i < 10; i++) { 
      if ( nc[i] > 0 ) { 
	printf("you must use all numbers!\n");
	rv = false; break; 
      } 
    }
  }
  return rv;
}

int main()
{
  int n[N_NUMBERS], i;
  char expression[MAX_EXPR_LEN];
  double ee;
  bool playagain = true;

  while(playagain)
  {
    printf("The numbers are:\n");
    for(i=0; i < N_NUMBERS; i++)
    {
      n[i] = ranged_rand(1, 9);
      printf("n%d = %d\n", i, n[i]);
    }
    printf("\nNow enter the expression (in RPN) which evaluate to %d\n"
	   "Allowed operators: + - / * n"
           "[syntactic sugar for '0 Num -' which is not allowed because of 0]\n"
	   "You must use all numbers only once\n\n", EVAL_TO);
    for(;;)
    {
      printf("Enter the expression (must give %d): ", EVAL_TO);
      fgets(expression, MAX_EXPR_LEN, stdin);
      if ( !check_expression(expression, n) ) { continue;}
      if ( !rpn_evaluate(expression) ) continue;
      ee = rpn_pop();
      if ( fabs(ee - EVAL_TO) < TOLERANCE )
      {
	printf("\n\nYou got it! Well done!!\n\n");
	break;
      } else {
	printf("Your expression evaluate to %lf\n"
	       "Let's try again? (n say no) ", ee);
	fgets(expression, MAX_EXPR_LEN, stdin);
	if ( expression[0] == 'n' ) break;
      }
    }

    printf("\n\nPlay again? (n to say no) ");
    fgets(expression, MAX_EXPR_LEN, stdin);
    playagain = !(expression[0] == 'n');
  }

  return 0;
}