Task: 24 game

C: #include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <setjmp.h>
#include <time.h>
 
jmp_buf ctx;
const char *msg;
 
enum { OP_NONE = 0, OP_NUM, OP_ADD, OP_SUB, OP_MUL, OP_DIV };
 
typedef struct expr_t *expr, expr_t;
struct expr_t {
	int op, val, used;
	expr left, right;
};
 
#define N_DIGITS 4
expr_t digits[N_DIGITS];
 
void gen_digits()
{
	int i;
	for (i = 0; i < N_DIGITS; i++)
		digits[i].val = 1 + rand() % 9;
}
 
#define MAX_INPUT 64
char str[MAX_INPUT];
int pos;
 
#define POOL_SIZE 8
expr_t pool[POOL_SIZE];
int pool_ptr;
 
void reset()
{
	int i;
	msg = 0;
	pool_ptr = pos = 0;
	for (i = 0; i < POOL_SIZE; i++) {
		pool[i].op = OP_NONE;
		pool[i].left = pool[i].right = 0;
	}
	for (i = 0; i < N_DIGITS; i++)
		digits[i].used = 0;
}
 

void bail(const char *s)
{
	msg = s;
	longjmp(ctx, 1);
}
 
expr new_expr()
{
	if (pool_ptr < POOL_SIZE)
		return pool + pool_ptr++;
	return 0;
}
 

int next_tok()
{
	while (isspace(str[pos])) pos++;
	return str[pos];
}
 

int take()
{
	if (str[pos] != '\0') return ++pos;
	return 0;
}
 

 
expr get_fact();
expr get_term();
expr get_expr();
 
expr get_expr()
{
	int c;
	expr l, r, ret;
	if (!(ret = get_term())) bail("Expected term");
	while ((c = next_tok()) == '+' || c == '-') {
		if (!take()) bail("Unexpected end of input");
		if (!(r = get_term())) bail("Expected term");
 
		l = ret;
		ret = new_expr();
		ret->op = (c == '+') ? OP_ADD : OP_SUB;
		ret->left = l;
		ret->right = r;
	}
	return ret;
}
 
expr get_term()
{
	int c;
	expr l, r, ret;
	ret = get_fact();
	while((c = next_tok()) == '*' || c == '/') {
		if (!take()) bail("Unexpected end of input");
 
		r = get_fact();
		l = ret;
		ret = new_expr();
		ret->op = (c == '*') ? OP_MUL : OP_DIV;
		ret->left = l;
		ret->right = r;
	}
	return ret;
}
 
expr get_digit()
{
	int i, c = next_tok();
	expr ret;
	if (c >= '0' && c <= '9') {
		take();
		ret = new_expr();
		ret->op = OP_NUM;
		ret->val = c - '0';
		for (i = 0; i < N_DIGITS; i++)
			if (digits[i].val == ret->val && !digits[i].used) {
				digits[i].used = 1;
				return ret;
			}
		bail("Invalid digit");
	}
	return 0;
}
 
expr get_fact()
{
	int c;
	expr l = get_digit();
	if (l) return l;
	if ((c = next_tok()) == '(') {
		take();
		l = get_expr();
		if (next_tok() != ')') bail("Unbalanced parens");
		take();
		return l;
	}
	return 0;
}
 
expr parse()
{
	int i;
	expr ret = get_expr();
	if (next_tok() != '\0')
		bail("Trailing garbage");
	for (i = 0; i < N_DIGITS; i++)
		if (!digits[i].used)
			bail("Not all digits are used");
	return ret;
}
 
typedef struct frac_t frac_t, *frac;
struct frac_t { int denom, num; };
 
int gcd(int m, int n)
{
	int t;
	while (m) {
		t = m; m = n % m; n = t;
	}
	return n;
}
 

void eval_tree(expr e, frac res)
{
	frac_t l, r;
	int t;
	if (e->op == OP_NUM) {
		res->num = e->val;
		res->denom = 1;
		return;
	}
 
	eval_tree(e->left, &l);
	eval_tree(e->right, &r);
 
	switch(e->op) {
	case OP_ADD:
		res->num = l.num * r.denom + l.denom * r.num;
		res->denom = l.denom * r.denom;
		break;
	case OP_SUB:
		res->num = l.num * r.denom - l.denom * r.num;
		res->denom = l.denom * r.denom;
		break;
	case OP_MUL:
		res->num = l.num * r.num;
		res->denom = l.denom * r.denom;
		break;
	case OP_DIV:
		res->num = l.num * r.denom;
		res->denom = l.denom * r.num;
		break;
	}
	if ((t = gcd(res->denom, res->num))) {
		res->denom /= t;
		res->num /= t;
	}
}
 
void get_input()
{
	int i;
reinput:
	reset();
	printf("\nAvailable digits are:");
	for (i = 0; i < N_DIGITS; i++) 
		printf(" %d", digits[i].val);
	printf(". Type an expression and I'll check it for you, or make new numbers.\n"
		"Your choice? [Expr/n/q] ");
 
	while (1) {
		for (i = 0; i < MAX_INPUT; i++) str[i] = '\n';
		fgets(str, MAX_INPUT, stdin);
		if (*str == '\0') goto reinput;
		if (str[MAX_INPUT - 1] != '\n')
			bail("string too long");
 
		for (i = 0; i < MAX_INPUT; i++)
			if (str[i] == '\n') str[i] = '\0';
		if (str[0] == 'q') {
			printf("Bye\n");
			exit(0);
		}
		if (str[0] == 'n') {
			gen_digits();
			goto reinput;
		}
		return;
	}
}
 
int main()
{
	frac_t f;
	srand(time(0));
 
	gen_digits();
	while(1) {
		get_input();
		setjmp(ctx); 
		if (msg) {
			
			printf("%s at '%.*s'\n", msg, pos, str);
			continue;
		}
 
		eval_tree(parse(), &f);
 
		if (f.denom == 0) bail("Divide by zero");
		if (f.denom == 1 && f.num == 24)
			printf("You got 24.  Very good.\n");
		else {
			if (f.denom == 1)
				printf("Eval to: %d, ", f.num);
			else
				printf("Eval to: %d/%d, ", f.num, f.denom);
			printf("no good.  Try again.\n");
		}
	}
	return 0;
}








VB: Sub Rosetta_24game()

Dim Digit(4) As Integer, i As Integer, iDigitCount As Integer
Dim stUserExpression As String
Dim stFailMessage As String, stFailDigits As String
Dim bValidExpression As Boolean, bValidDigits As Boolean, bValidChars As Boolean
Dim vResult As Variant, vTryAgain As Variant, vSameDigits As Variant


GenerateNewDigits:
    For i = 1 To 4
        Digit(i) = [randbetween(1,9)]
    Next i


GetUserExpression:
    bValidExpression = True
    stFailMessage = ""
    stFailDigits = ""
    stUserExpression = InputBox("Enter a mathematical expression which results in 24, using the following digits: " & _
        Digit(1) & ", " & Digit(2) & ", " & Digit(3) & " and " & Digit(4), "Rosetta Code | 24 Game")


    bValidDigits = True
    stFailDigits = ""
    For i = 1 To 4
        If InStr(stUserExpression, Digit(i)) = 0 Then
            bValidDigits = False
            stFailDigits = stFailDigits & " " & Digit(i)
        End If
    Next i
    If bValidDigits = False Then
        bValidExpression = False
        stFailMessage = "Your expression excluded the following required digits: " & stFailDigits & vbCr & vbCr
    End If


    bValidDigits = True
    stFailDigits = ""
    For i = 1 To Len(stUserExpression)
        If InStr("0123456789+-*/()", Mid(stUserExpression, i, 1)) = 0 Then
            bValidDigits = False
            stFailDigits = stFailDigits & " " & Mid(stUserExpression, i, 1)
        End If
    Next i
    If bValidDigits = False Then
        bValidExpression = False
        stFailMessage = stFailMessage & "Your expression contained invalid characters:" & stFailDigits & vbCr & vbCr
    End If


    bValidDigits = True
    stFailDigits = ""
    iDigitCount = 0
    For i = 1 To Len(stUserExpression)
        If Not InStr("0123456789", Mid(stUserExpression, i, 1)) = 0 Then
            iDigitCount = iDigitCount + 1
            If IsError(Application.Match(--(Mid(stUserExpression, i, 1)), Digit, False)) Then
                bValidDigits = False
                stFailDigits = stFailDigits & " " & Mid(stUserExpression, i, 1)
            End If
        End If
    Next i
    If iDigitCount > 4 Then
        bValidExpression = False
        stFailMessage = stFailMessage & "Your expression contained more than 4 digits" & vbCr & vbCr
    End If
        If iDigitCount < 4 Then
        bValidExpression = False
        stFailMessage = stFailMessage & "Your expression contained less than 4 digits" & vbCr & vbCr
    End If
    If bValidDigits = False Then
        bValidExpression = False
        stFailMessage = stFailMessage & "Your expression contained invalid digits:" & stFailDigits & vbCr & vbCr
    End If


    bValidDigits = True
    stFailDigits = ""
    For i = 11 To 99
        If Not InStr(stUserExpression, i) = 0 Then
            bValidDigits = False
            stFailDigits = stFailDigits & " " & i
        End If
    Next i
    If bValidDigits = False Then
        bValidExpression = False
        stFailMessage = stFailMessage & "Your expression contained invalid numbers:" & stFailDigits & vbCr & vbCr
    End If


    On Error GoTo EvalFail
    vResult = Evaluate(stUserExpression)
    If Not vResult = 24 Then
        bValidExpression = False
        stFailMessage = stFailMessage & "Your expression did not result in 24. It returned: " & vResult
    End If


    If bValidExpression = False Then
        vTryAgain = MsgBox(stFailMessage & vbCr & vbCr & "Would you like to try again?", vbCritical + vbRetryCancel, "Rosetta Code | 24 Game | FAILED")
            If vTryAgain = vbRetry Then
                vSameDigits = MsgBox("Do you want to use the same numbers?", vbQuestion + vbYesNo, "Rosetta Code | 24 Game | RETRY")
                If vSameDigits = vbYes Then
                    GoTo GetUserExpression
                Else
                    GoTo GenerateNewDigits
                End If
            End If
    Else
        vTryAgain = MsgBox("You entered: " & stUserExpression & vbCr & vbCr & "which resulted in: " & vResult, _
            vbInformation + vbRetryCancel, "Rosetta Code | 24 Game | SUCCESS")
        If vTryAgain = vbRetry Then
            GoTo GenerateNewDigits
        End If
    End If
    Exit Sub
EvalFail:
    bValidExpression = False
    vResult = Err.Description
    Resume
End Sub