Data commit
This commit is contained in:
parent
7387c8f97b
commit
cb5bb5e222
199093 changed files with 3378972 additions and 0 deletions
281
Task/24-game/C/24-game.c
Normal file
281
Task/24-game/C/24-game.c
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
#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;
|
||||
}
|
||||
|
||||
/* longish jumpish back to input cycle */
|
||||
void bail(const char *s)
|
||||
{
|
||||
msg = s;
|
||||
longjmp(ctx, 1);
|
||||
}
|
||||
|
||||
expr new_expr()
|
||||
{
|
||||
if (pool_ptr < POOL_SIZE)
|
||||
return pool + pool_ptr++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check next input char */
|
||||
int next_tok()
|
||||
{
|
||||
while (isspace(str[pos])) pos++;
|
||||
return str[pos];
|
||||
}
|
||||
|
||||
/* move input pointer forward */
|
||||
int take()
|
||||
{
|
||||
if (str[pos] != '\0') return ++pos;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* BNF(ish)
|
||||
expr = term { ("+")|("-") term }
|
||||
term = fact { ("*")|("/") expr }
|
||||
fact = number
|
||||
| '(' expr ')'
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* evaluate expression tree. result in fraction form */
|
||||
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 parse error, jump back here with err msg set */
|
||||
if (msg) {
|
||||
/* after error jump; announce, reset, redo */
|
||||
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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue