24 game/C
< 24 game
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
#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.
#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;
}