From c0d0d2de7455f45ef20486950a4f629916e039e3 Mon Sep 17 00:00:00 2001 From: Neale Pickett Date: Wed, 14 Jul 2010 15:21:20 -0600 Subject: [PATCH] Mostly-working forftanks implementation --- Makefile | 2 + cforf.c | 748 +++++++++++++++++++ cforf.h | 146 ++++ ctanks.c | 214 ++++-- ctanks.h | 19 +- dump.h | 14 + examples/easy/berzerker/author | 1 + examples/easy/berzerker/name | 1 + examples/easy/berzerker/program | 15 + examples/easy/rabbitwithgun/author | 1 + examples/easy/rabbitwithgun/name | 1 + examples/easy/rabbitwithgun/program | 26 + examples/easy/rabbitwithgun/sensor0 | 1 + examples/easy/rabbitwithgun/sensor1 | 1 + examples/medium/simpleton/author | 1 + examples/medium/simpleton/name | 1 + examples/medium/simpleton/program | 5 + examples/medium/simpleton/sensor0 | 1 + examples/medium/simpleton/sensor1 | 1 + examples/medium/sittingduckwithteeth/author | 1 + examples/medium/sittingduckwithteeth/name | 1 + examples/medium/sittingduckwithteeth/program | 3 + examples/medium/sittingduckwithteeth/sensor0 | 1 + examples/medium/sittingduckwithteeth/sensor1 | 1 + examples/medium/sittingduckwithteeth/sensor2 | 1 + examples/medium/sweeper/author | 1 + examples/medium/sweeper/name | 1 + examples/medium/sweeper/program | 10 + examples/medium/sweeper/sensor0 | 1 + examples/medium/sweeper/sensor1 | 1 + examples/medium/sweeper/sensor2 | 1 + examples/medium/sweeper/sensor3 | 1 + run-tanks.c | 524 +++++++++++++ tanks.js | 6 +- test-tanks.c | 16 +- trig.py | 12 - 36 files changed, 1671 insertions(+), 110 deletions(-) create mode 100644 cforf.c create mode 100644 cforf.h create mode 100644 dump.h create mode 100644 examples/easy/berzerker/author create mode 100644 examples/easy/berzerker/name create mode 100644 examples/easy/berzerker/program create mode 100644 examples/easy/rabbitwithgun/author create mode 100644 examples/easy/rabbitwithgun/name create mode 100644 examples/easy/rabbitwithgun/program create mode 100644 examples/easy/rabbitwithgun/sensor0 create mode 100644 examples/easy/rabbitwithgun/sensor1 create mode 100644 examples/medium/simpleton/author create mode 100644 examples/medium/simpleton/name create mode 100644 examples/medium/simpleton/program create mode 100644 examples/medium/simpleton/sensor0 create mode 100644 examples/medium/simpleton/sensor1 create mode 100644 examples/medium/sittingduckwithteeth/author create mode 100644 examples/medium/sittingduckwithteeth/name create mode 100644 examples/medium/sittingduckwithteeth/program create mode 100644 examples/medium/sittingduckwithteeth/sensor0 create mode 100644 examples/medium/sittingduckwithteeth/sensor1 create mode 100644 examples/medium/sittingduckwithteeth/sensor2 create mode 100644 examples/medium/sweeper/author create mode 100644 examples/medium/sweeper/name create mode 100644 examples/medium/sweeper/program create mode 100644 examples/medium/sweeper/sensor0 create mode 100644 examples/medium/sweeper/sensor1 create mode 100644 examples/medium/sweeper/sensor2 create mode 100644 examples/medium/sweeper/sensor3 create mode 100644 run-tanks.c delete mode 100755 trig.py diff --git a/Makefile b/Makefile index 287ebdc..e0359c2 100644 --- a/Makefile +++ b/Makefile @@ -7,5 +7,7 @@ test: test-tanks test-tanks: test-tanks.o ctanks.o +run-tanks: run-tanks.o ctanks.o cforf.o + clean: rm -f test-tanks *.o \ No newline at end of file diff --git a/cforf.c b/cforf.c new file mode 100644 index 0000000..34d329a --- /dev/null +++ b/cforf.c @@ -0,0 +1,748 @@ +/* forf: a crappy Forth implementation + * Copyright (C) 2010 Adam Glasgall + * + * 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 3 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, see . + */ + +/* Notes + * ------------------------------------------------------- + * + * This is intended to be implemented as a library. As such, it doesn't + * use the libc memory allocation functions. This may be a different + * programming style than you're used to. + * + * There are two data types: numbers and stacks. Because we can't + * allocate memory, stacks are implemented with begin and end markers + * and not new stack types. + */ + +#include +#include +#include + +#include "cforf.h" +#include "dump.h" + +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +char *forf_error_str[] = { + "None", + "Runtime", + "Parse", + "Underflow", + "Overflow", + "Type", + "No such procedure", + "Divide by zero", +}; + +/* + * + * Memory manipulation + * + */ +void +forf_memory_init(struct forf_memory *m, + long *values, + size_t size) +{ + m->mem = values; + m->size = size; +} + + +/* + * + * Stack manipulation + * + */ + +void +forf_stack_init(struct forf_stack *s, + struct forf_value *values, + size_t size) +{ + s->stack = values; + s->size = size; + s->top = 0; +} + +void +forf_stack_reset(struct forf_stack *s) +{ + s->top = 0; +} + +size_t +forf_stack_len(struct forf_stack *s) +{ + return s->top; +} + +int +forf_stack_push(struct forf_stack *s, struct forf_value *v) +{ + if (s->top == s->size) { + return 0; + } + s->stack[(s->top)++] = *v; + return 1; +} + +int +forf_stack_pop(struct forf_stack *s, struct forf_value *v) +{ + if (0 == s->top) { + return 0; + } + *v = s->stack[--(s->top)]; + return 1; +} + +void +forf_stack_copy(struct forf_stack *dst, struct forf_stack *src) +{ + int top = min(dst->size, src->top); + + dst->top = top; + memcpy(dst->stack, src->stack, sizeof(*dst->stack) * top); +} + + +void +forf_stack_reverse(struct forf_stack *s) +{ + struct forf_value val; + size_t pos; + + for (pos = 0; pos < (s->top)/2; pos += 1) { + size_t qos = s->top - pos - 1; + + val = s->stack[pos]; + s->stack[pos] = s->stack[qos]; + s->stack[qos] = val; + } +} + +long +forf_pop_num(struct forf_env *env) +{ + struct forf_value val; + + if (! forf_stack_pop(env->data, &val)) { + env->error = forf_error_underflow; + return 0; + } + if (forf_type_number != val.type) { + forf_stack_push(env->data, &val); + env->error = forf_error_type; + return 0; + } + return val.v.i; +} + +void +forf_push_num(struct forf_env *env, long i) +{ + struct forf_value val; + + val.type = forf_type_number; + val.v.i = i; + if (! forf_stack_push(env->data, &val)) { + env->error = forf_error_overflow; + } +} + + +/* Pop an entire stack + * + * DANGER WILL ROBINSON + * + * This returned stack points to values on the data stack. You must be + * finished with this stack before you push anything onto the data + * stack, otherwise your returned stack will be corrupted. + */ +struct forf_stack +forf_pop_stack(struct forf_env *env) +{ + struct forf_stack s = { 0, 0, NULL }; + struct forf_value val; + size_t depth = 1; + + if (! forf_stack_pop(env->data, &val)) { + env->error = forf_error_underflow; + return s; + } + if (forf_type_stack_end != val.type) { + forf_stack_push(env->data, &val); + env->error = forf_error_type; + return s; + } + /* Duplicate just the stack onto s. Begin with -1 to account for the + end of list marker. */ + s.size = -1; + while (depth) { + s.size += 1; + if (! forf_stack_pop(env->data, &val)) { + /* You should never underflow here, there should at least be a + stack begin marker. */ + env->error = forf_error_runtime; + s.size = 0; + return s; + } + switch (val.type) { + case forf_type_stack_end: + depth += 1; + break; + case forf_type_stack_begin: + depth -= 1; + break; + } + } + s.top = s.size; + s.stack = (env->data->stack) + (env->data->top + 1); + return s; +} + +/* Push an entire stack onto another stack. + */ +int +forf_push_stack(struct forf_stack *dst, struct forf_stack *src) +{ + struct forf_value val; + + while (forf_stack_pop(src, &val)) { + if (! forf_stack_push(dst, &val)) { + return 0; + } + } + return 1; +} + +/* Push an entire stack onto the command stack. + * + * This is meant to work with the return value from forf_pop_stack. + */ +int +forf_push_to_command_stack(struct forf_env *env, struct forf_stack *src) +{ + if (! forf_push_stack(env->command, src)) { + env->error = forf_error_overflow; + return 0; + } + return 1; +} + +/* Move one value from src to dst. Note that one value could mean a + * whole substack, in which case dst gets the stack in reverse! dst can + * also be NULL, in which case a value is just discarded. + * + * Because of the reversing thing, it's important to make sure that the + * data stack is either src or dst. This way, the data stack will + * always have "reversed" substacks, and everything else will have them + * in the right order. + */ +int +forf_stack_move_value(struct forf_env *env, + struct forf_stack *dst, + struct forf_stack *src) +{ + struct forf_value val; + size_t depth = 0; + + do { + /* Pop from src */ + if (! forf_stack_pop(env->command, &val)) { + env->error = forf_error_underflow; + return 0; + } + + /* Push to dst (or discard if dst is NULL) */ + if (dst) { + if (! forf_stack_push(env->data, &val)) { + env->error = forf_error_overflow; + return 0; + } + } + + /* Deal with it being a substack marker */ + switch (val.type) { + case forf_type_stack_begin: + depth += 1; + break; + case forf_type_stack_end: + depth -= 1; + break; + } + } while (depth > 0); + + return 1; + +} + + +/* + * + * Procedures + * + */ + +#define unproc(name, op) \ + static void \ + forf_proc_ ## name(struct forf_env *env) \ + { \ + long a = forf_pop_num(env); \ + \ + forf_push_num(env, op a); \ + } + +unproc(inv, ~) +unproc(not, !) + +#define binproc(name, op) \ + static void \ + forf_proc_ ## name(struct forf_env *env) \ + { \ + long a = forf_pop_num(env); \ + long b = forf_pop_num(env); \ + \ + forf_push_num(env, b op a); \ + } + +binproc(add, +) +binproc(sub, -) +binproc(mul, *) +binproc(and, &) +binproc(or, |) +binproc(xor, ^) +binproc(lshift, <<) +binproc(rshift, >>) +binproc(gt, >) +binproc(ge, >=) +binproc(lt, <) +binproc(le, <=) +binproc(eq, ==) +binproc(ne, !=) + +static void +forf_proc_div(struct forf_env *env) +{ + long a = forf_pop_num(env); + long b = forf_pop_num(env); + + if (0 == a) { + env->error = forf_error_divzero; + return; + } + forf_push_num(env, b / a); +} + +static void +forf_proc_mod(struct forf_env *env) +{ + long a = forf_pop_num(env); + long b = forf_pop_num(env); + + if (0 == a) { + env->error = forf_error_divzero; + return; + } + forf_push_num(env, b % a); +} + +static void +forf_proc_abs(struct forf_env *env) +{ + forf_push_num(env, abs(forf_pop_num(env))); +} + +static void +forf_proc_dup(struct forf_env *env) +{ + long a = forf_pop_num(env); + + forf_push_num(env, a); + forf_push_num(env, a); +} + +static void +forf_proc_pop(struct forf_env *env) +{ + forf_pop_num(env); +} + +static void +forf_proc_exch(struct forf_env *env) +{ + long a = forf_pop_num(env); + long b = forf_pop_num(env); + + forf_push_num(env, a); + forf_push_num(env, b); +} + +static void +forf_proc_if(struct forf_env *env) +{ + struct forf_stack ifclause = forf_pop_stack(env); + long cond = forf_pop_num(env); + + if (cond) { + forf_push_to_command_stack(env, &ifclause); + } +} + +static void +forf_proc_ifelse(struct forf_env *env) +{ + struct forf_stack elseclause = forf_pop_stack(env); + struct forf_stack ifclause = forf_pop_stack(env); + long cond = forf_pop_num(env); + + if (cond) { + forf_push_to_command_stack(env, &ifclause); + } else { + forf_push_to_command_stack(env, &elseclause); + } +} + +static void +forf_proc_memset(struct forf_env *env) +{ + long pos = forf_pop_num(env); + long a = forf_pop_num(env); + + if (pos >= env->memory->size) { + env->error = forf_error_overflow; + return; + } + + env->memory->mem[pos] = a; +} + +static void +forf_proc_memget(struct forf_env *env) +{ + long pos = forf_pop_num(env); + + if (pos >= env->memory->size) { + env->error = forf_error_overflow; + return; + } + + forf_push_num(env, env->memory->mem[pos]); +} + +/* + * + * Lexical environment + * + */ +struct forf_lexical_env forf_base_lexical_env[] = { + {"~", forf_proc_inv}, + {"!", forf_proc_not}, + {"+", forf_proc_add}, + {"-", forf_proc_sub}, + {"*", forf_proc_mul}, + {"/", forf_proc_div}, + {"%", forf_proc_mod}, + {"&", forf_proc_and}, + {"|", forf_proc_or}, + {"^", forf_proc_xor}, + {"<<", forf_proc_lshift}, + {">>", forf_proc_rshift}, + {">", forf_proc_gt}, + {">=", forf_proc_ge}, + {"<", forf_proc_lt}, + {"<=", forf_proc_le}, + {"=", forf_proc_eq}, + {"<>", forf_proc_ne}, + {"abs", forf_proc_abs}, + {"dup", forf_proc_dup}, + {"pop", forf_proc_pop}, + {"exch", forf_proc_exch}, + {"if", forf_proc_if}, + {"ifelse", forf_proc_ifelse}, + {"mset", forf_proc_memset}, + {"mget", forf_proc_memget}, + {NULL, NULL} +}; + +/** Extend a lexical environment */ +int +forf_extend_lexical_env(struct forf_lexical_env *dest, + struct forf_lexical_env *src, + size_t size) +{ + int base, i; + + for (base = 0; dest[base].name; base += 1); + for (i = 0; (base+i < size) && (src[i].name); i += 1) { + dest[base+i] = src[i]; + } + if (base + i == size) { + /* Not enough room */ + return 0; + } + dest[base+i].name = NULL; + dest[base+i].proc = NULL; + return 1; +} + + +/* + * + * Parsing + * + */ +static int +forf_push_token(struct forf_env *env, char *token, size_t tokenlen) +{ + long i; + char s[MAX_TOKEN_LEN + 1]; + char *endptr; + struct forf_value val; + + /* Zero-length token yields int:0 from strtol */ + + /* NUL-terminate it */ + memcpy(s, token, tokenlen); + s[tokenlen] = '\0'; + + /* Try to make in an integer */ + i = strtol(s, &endptr, 0); + if ('\0' == *endptr) { + /* Was an int */ + val.type = forf_type_number; + val.v.i = i; + } else { + /* If not an int, a procedure name */ + val.type = forf_type_proc; + for (i = 0; NULL != env->lenv[i].name; i += 1) { + if (0 == strcmp(s, env->lenv[i].name)) { + val.v.p = env->lenv[i].proc; + break; + } + } + if (NULL == env->lenv[i].name) { + env->error = forf_error_noproc; + return 0; + } + } + + if (! forf_stack_push(env->command, &val)) { + env->error = forf_error_overflow; + return 0; + } + + return 1; +} + +/* Parse an input stream onto the command stack */ +int +forf_parse_stream(struct forf_env *env, + forf_getch_func *getch, + void *datum) +{ + int running = 1; + long pos = 0; + char token[MAX_TOKEN_LEN]; + size_t tokenlen = 0; + struct forf_value val; + size_t stack_depth = 0; + int comment = 0; + +#define _tokenize() \ + do { \ + if (tokenlen) { \ + if (! forf_push_token(env, token, tokenlen)) return pos; \ + tokenlen = 0; \ + } \ + } while (0) + + while (running) { + int c; + + c = getch(datum); + pos += 1; + + /* Handle comments */ + if (comment) { + if (')' == c) { + comment = 0; + } + continue; + } + + switch (c) { + case EOF: + running = 0; + break; + case '(': + comment = 1; + break; + case ' ': + case '\f': + case '\n': + case '\r': + case '\t': + case '\v': + _tokenize(); + break; + case '{': + _tokenize(); + val.type = forf_type_stack_begin; + if (! forf_stack_push(env->command, &val)) { + env->error = forf_error_overflow; + return pos; + } + stack_depth += 1; + break; + case '}': + _tokenize(); + val.type = forf_type_stack_end; + if (! forf_stack_push(env->command, &val)) { + env->error = forf_error_overflow; + return pos; + } + stack_depth -= 1; + break; + default: + if (tokenlen < sizeof(token)) { + token[tokenlen++] = c; + } + break; + } + } + _tokenize(); + + if (0 != stack_depth) { + env->error = forf_error_parse; + return pos; + } + + // The first thing we read should be the first thing we do + forf_stack_reverse(env->command); + + return 0; +} + +struct forf_char_stream { + char *buf; + size_t len; + size_t pos; +}; + +static int +forf_string_getch(struct forf_char_stream *stream) +{ + if (stream->pos >= stream->len) { + return EOF; + } + return stream->buf[stream->pos++]; +} + +int +forf_parse_buffer(struct forf_env *env, + char *buf, + size_t len) +{ + struct forf_char_stream stream; + + stream.buf = buf; + stream.len = len; + stream.pos = 0; + + return forf_parse_stream(env, (forf_getch_func *)forf_string_getch, &stream); +} + +int +forf_parse_string(struct forf_env *env, + char *str) +{ + return forf_parse_buffer(env, str, strlen(str)); +} + +int +forf_parse_file(struct forf_env *env, + FILE *f) +{ + return forf_parse_stream(env, (forf_getch_func *)fgetc, f); +} + + +/* + * + * Forf environment + * + */ + +void +forf_env_init(struct forf_env *env, + struct forf_lexical_env *lenv, + struct forf_stack *data, + struct forf_stack *cmd, + struct forf_memory *mem, + void *udata) +{ + env->lenv = lenv; + env->data = data; + env->command = cmd; + env->memory = mem; + env->udata = udata; +} + + +int +forf_eval_once(struct forf_env *env) +{ + struct forf_value val; + + if (! forf_stack_pop(env->command, &val)) { + env->error = forf_error_underflow; + return 0; + } + switch (val.type) { + case forf_type_number: + case forf_type_stack_begin: + // Push back on command stack, then move it + forf_stack_push(env->command, &val); + if (! forf_stack_move_value(env, env->data, env->command)) return 0; + break; + case forf_type_proc: + (val.v.p)(env); + break; + default: + env->error = forf_error_runtime; + return 0; + } + return 1; +} + +int +forf_eval(struct forf_env *env) +{ + int ret; + + while (env->command->top) { + ret = forf_eval_once(env); + if ((! ret) || (env->error)) { + return 0; + } + } + return 1; +} diff --git a/cforf.h b/cforf.h new file mode 100644 index 0000000..c399f64 --- /dev/null +++ b/cforf.h @@ -0,0 +1,146 @@ +#ifndef __CFORF_H__ +#define __CFORF_H__ + +#include +#include + +#define MAX_TOKEN_LEN 20 +#define MAX_CMDSTACK 200 + +struct forf_env; + +enum forf_value_type { + forf_type_number, + forf_type_proc, + forf_type_stack_begin, + forf_type_stack_end, +}; + +enum forf_error_type { + forf_error_none, + forf_error_runtime, + forf_error_parse, + forf_error_underflow, + forf_error_overflow, + forf_error_type, + forf_error_noproc, + forf_error_divzero, +}; + +extern char *forf_error_str[]; + +typedef void (forf_proc)(struct forf_env *); + +struct forf_value { + enum forf_value_type type; + union { + forf_proc *p; + long i; + } v; +}; + +struct forf_stack { + size_t size; + size_t top; + struct forf_value *stack; +}; + +struct forf_memory { + size_t size; + long *mem; +}; + +struct forf_lexical_env { + char *name; + forf_proc *proc; +}; + +struct forf_env { + enum forf_error_type error; + struct forf_lexical_env *lenv; + struct forf_stack *data; + struct forf_stack *command; + struct forf_memory *memory; + void *udata; +}; + + +/* + * + * Main entry points + * + */ + +/** Initialize a memory structure, given an array of longs */ +void forf_memory_init(struct forf_memory *m, + long *values, + size_t size); + +/** Initialize a stack, given an array of values */ +void forf_stack_init(struct forf_stack *s, + struct forf_value *values, + size_t size); + +void forf_stack_reset(struct forf_stack *s); +void forf_stack_copy(struct forf_stack *dst, struct forf_stack *src); +int forf_stack_push(struct forf_stack *s, struct forf_value *v); +int forf_stack_pop(struct forf_stack *s, struct forf_value *v); + +/** Pop a number off the data stack */ +long forf_pop_num(struct forf_env *env); + +/** Push a number onto the data stack */ +void forf_push_num(struct forf_env *env, long i); + +/** Pop a whole stack */ +struct forf_stack forf_pop_stack(struct forf_env *env); + + +/** The base lexical environment */ +extern struct forf_lexical_env forf_base_lexical_env[]; + +/** Extend a lexical environment */ +int +forf_extend_lexical_env(struct forf_lexical_env *dest, + struct forf_lexical_env *src, + size_t size); + +/** Initialize a forf runtime environment. + * + * data, cmd, and mem should have already been initialized + */ +void forf_env_init(struct forf_env *env, + struct forf_lexical_env *lenv, + struct forf_stack *data, + struct forf_stack *cmd, + struct forf_memory *mem, + void *udata); + +/** The type of a getch function (used for parsing) */ +typedef int (forf_getch_func)(void *); + +/** Parse something by calling getch(datum) */ +int forf_parse_stream(struct forf_env *env, + forf_getch_func *getch, + void *datum); + +/** Parse a buffer */ +int forf_parse_buffer(struct forf_env *env, + char *buf, + size_t len); + +/** Parse a string */ +int forf_parse_string(struct forf_env *env, + char *str); + +/** Parse a FILE * */ +int forf_parse_file(struct forf_env *env, + FILE *f); + +/** Evaluate the topmost value on the command stack */ +int forf_eval_once(struct forf_env *env); + +/** Evaluate the entire command stack */ +int forf_eval(struct forf_env *env); + +#endif diff --git a/ctanks.c b/ctanks.c index 47de757..e3ed999 100644 --- a/ctanks.c +++ b/ctanks.c @@ -12,6 +12,7 @@ #define DUMP_s(v) DUMPf("%s = %s", #v, v) #define DUMP_c(v) DUMPf("%s = %c", #v, v) #define DUMP_f(v) DUMPf("%s = %f", #v, v) +#define DUMP_p(v) DUMPf("%s = %p", #v, v) #define DUMP_xy(v) DUMPf("%s = (%f, %f)", #v, v[0], v[1]); #define DUMP_angle(v) DUMPf("%s = %.3fπ", #v, (v/PI)); @@ -41,8 +42,8 @@ tank_fire(struct tank *tank) void tank_set_speed(struct tank *tank, float left, float right) { - tank->speed.desired[0] = left; - tank->speed.desired[1] = right; + tank->speed.desired[0] = min(max(left, -100), 100); + tank->speed.desired[1] = min(max(right, -100), 100); } float @@ -90,32 +91,69 @@ rotate_point(float angle, float point[2]) static void -tank_sensor_calc(struct tanks_game *game, - struct tank *this, - struct tank *that, - float dist2) +tanks_fire_cannon(struct tanks_game *game, + struct tank *this, + struct tank *that, + float vector[2], + float dist2) { - float tpos[2]; - int i; + float theta = this->angle + this->turret.current; + float rpos[2]; - /* Translate other tank's position to make us the origin */ - for (i = 0; i < 2; i += 1) { - tpos[i] = that->position[i] - this->position[i]; + /* If someone's a crater, this is easy */ + if (this->killer || that->killer) { + return; + } + + /* Did they collide? */ + if (dist2 < TANK_COLLISION_ADJ2) { + this->killer = that; + this->cause_death = "collision"; + + that->killer = this; + that->cause_death = "collision"; + + return; + } + + /* No need to check if it's not even firing */ + if (! this->turret.firing) { + return; + } + + /* Also no need to check if it's outside cannon range */ + if (dist2 > TANK_CANNON_ADJ2) { + return; } /* Did this shoot that? Rotate point by turret degrees, and if |y| < TANK_RADIUS, we have a hit. */ - if ((this->turret.firing) && (dist2 <= TANK_CANNON_RANGE)) { - int theta = this->angle + this->turret.current; - float rpos[2]; + rpos[0] = vector[0]; + rpos[1] = vector[1]; + rotate_point(-theta, rpos); + if (fabsf(rpos[1]) < TANK_RADIUS) { + that->killer = this; + that->cause_death = "shot"; + } +} - rpos[0] = tpos[0]; - rpos[1] = tpos[1]; - rotate_point(-theta, rpos); - if (fabsf(rpos[1]) < TANK_RADIUS) { - that->killer = this; - that->cause_death = "shot"; - } +static void +tanks_sensor_calc(struct tanks_game *game, + struct tank *this, + struct tank *that, + float vector[2], + float dist2) +{ + int i; + + /* If someone's a crater, this is easy */ + if (this->killer || that->killer) { + return; + } + + /* If they're not inside the max sensor, just skip it */ + if (dist2 > TANK_SENSOR_ADJ2) { + return; } /* Calculate sensors */ @@ -124,6 +162,11 @@ tank_sensor_calc(struct tanks_game *game, float rpos[2]; float m_r, m_s; + if (0 == this->sensors[i].range) { + /* Sensor doesn't exist */ + continue; + } + /* No need to re-check this sensor if it's already firing */ if (this->sensors[i].triggered) { continue; @@ -139,11 +182,10 @@ tank_sensor_calc(struct tanks_game *game, if (this->sensors[i].turret) { theta += this->turret.current; } - theta = fmodf(theta, 2*PI); - /* Rotate tpos by theta */ - rpos[0] = tpos[0]; - rpos[1] = tpos[1]; + /* Rotate their position by theta */ + rpos[0] = vector[0]; + rpos[1] = vector[1]; rotate_point(-theta, rpos); /* Sensor is symmetrical, we can consider only top quadrants */ @@ -153,14 +195,6 @@ tank_sensor_calc(struct tanks_game *game, m_s = 1 / tanf(this->sensors[i].width / 2); m_r = rpos[0] / rpos[1]; - if (1 == i) { - DUMP(); - DUMP_angle(this->angle); - DUMP_angle(theta); - DUMP_xy(tpos); - DUMP_xy(rpos); - } - /* If our inverse slope is less than theirs, they're inside the arc */ if (m_r >= m_s) { this->sensors[i].triggered = 1; @@ -177,52 +211,30 @@ tank_sensor_calc(struct tanks_game *game, } void -do_shit_with(struct tanks_game *game, - struct tank *this, - struct tank *that) +compute_vector(struct tanks_game *game, + float vector[2], + float *dist2, + struct tank *this, + struct tank *that) { - float vector[2]; - float dist2; /* distance squared */ - float tpos; /* Translated position */ int i; - /* Don't bother if that is dead */ - /* XXX: If three tanks occupy the same space at the same time, only - the first two will collide. */ - if ((this->killer) || (that->killer)) { - return; - } - /* Establish shortest vector from center of this to center of that, * taking wrapping into account */ for (i = 0; i < 2; i += 1) { float halfsize = game->size[i] / 2; - vector[i] = halfsize - fabsf(that->position[i] - this->position[i] - halfsize); + vector[i] = that->position[i] - this->position[i]; + if (vector[i] > halfsize) { + vector[i] = vector[i] - game->size[i]; + } + else if (vector[i] < -halfsize) { + vector[i] = game->size[i] + vector[i]; + } } /* Compute distance^2 for range comparisons */ - dist2 = sq(vector[0]) + sq(vector[1]); - - /* If they're not within sensor range, there's nothing to do. */ - if (dist2 > TANK_SENSOR_ADJ2) { - return; - } - - /* Did they collide? */ - if (dist2 < TANK_COLLISION_ADJ2) { - this->killer = that; - this->cause_death = "collision"; - - that->killer = this; - that->cause_death = "collision"; - - return; - } - - /* Figure out who's whomin' whom */ - tank_sensor_calc(game, this, that, dist2); - tank_sensor_calc(game, that, this, dist2); + *dist2 = sq(vector[0]) + sq(vector[1]); } void @@ -232,6 +244,7 @@ tanks_move_tank(struct tanks_game *game, int i; float movement; float angle; + int dir = 1; /* Rotate the turret */ { @@ -282,7 +295,6 @@ tanks_move_tank(struct tanks_game *game, float So, Si; float r; float theta; - int dir; /* The first thing Paul's code does is find "friction", which seems to be a penalty for having the treads go in opposite directions. @@ -322,8 +334,8 @@ tanks_move_tank(struct tanks_game *game, { float m[2]; - m[0] = cosf(tank->angle) * movement; - m[1] = sinf(tank->angle) * movement; + m[0] = cosf(tank->angle) * movement * dir; + m[1] = sinf(tank->angle) * movement * dir; for (i = 0; i < 2; i += 1) { tank->position[i] = fmodf(tank->position[i] + m[i] + game->size[i], @@ -335,33 +347,69 @@ tanks_move_tank(struct tanks_game *game, void tanks_run_turn(struct tanks_game *game, struct tank *tanks, int ntanks) { - int i, j; + int i, j; + float vector[2]; + float dist2; /* distance squared */ - /* Run programs */ - for (i = 0; i < ntanks; i += 1) { - if (! tanks[i].killer) { - tanks[i].run(&tanks[i], &tanks[i].udata); - } + /* It takes (at least) two to tank-o */ + if (ntanks < 2) { + return; } - /* Clear sensors */ + /* Charge cannons and reset sensors */ for (i = 0; i < ntanks; i += 1) { + if (tanks[i].turret.firing) { + tanks[i].turret.firing = 0; + tanks[i].turret.recharge = TANK_CANNON_RECHARGE; + } + if (tanks[i].killer) continue; + if (tanks[i].turret.recharge) { + tanks[i].turret.recharge -= 1; + } for (j = 0; j < TANK_MAX_SENSORS; j += 1) { tanks[i].sensors[j].triggered = 0; } } - /* Move */ + /* Move tanks */ for (i = 0; i < ntanks; i += 1) { - if (! tanks[i].killer) { - tanks_move_tank(game, &(tanks[i])); + if (tanks[i].killer) continue; + tanks_move_tank(game, &(tanks[i])); + } + + /* Probe sensors */ + for (i = 0; i < ntanks; i += 1) { + if (tanks[i].killer) continue; + for (j = i + 1; j < ntanks; j += 1) { + struct tank *this = &tanks[i]; + struct tank *that = &tanks[j]; + + compute_vector(game, vector, &dist2, this, that); + tanks_sensor_calc(game, this, that, vector, dist2); + vector[0] = -vector[0]; + vector[1] = -vector[1]; + tanks_sensor_calc(game, that, this, vector, dist2); } } - /* Sense and fire */ + /* Run programs */ for (i = 0; i < ntanks; i += 1) { + if (tanks[i].killer) continue; + tanks[i].run(&tanks[i], tanks[i].udata); + } + + /* Fire cannons and check for crashes */ + for (i = 0; i < ntanks; i += 1) { + if (tanks[i].killer) continue; for (j = i + 1; j < ntanks; j += 1) { - do_shit_with(game, &(tanks[i]), &(tanks[j])); + struct tank *this = &tanks[i]; + struct tank *that = &tanks[j]; + + compute_vector(game, vector, &dist2, this, that); + tanks_fire_cannon(game, this, that, vector, dist2); + vector[0] = -vector[0]; + vector[1] = -vector[1]; + tanks_fire_cannon(game, that, this, vector, dist2); } } } diff --git a/ctanks.h b/ctanks.h index dd9a2d9..243ce59 100644 --- a/ctanks.h +++ b/ctanks.h @@ -4,13 +4,14 @@ /* Some useful constants */ #define PI 3.14159265358979323846 -#define TANK_MAX_SENSORS 10 -#define TANK_RADIUS 7.5 -#define TANK_SENSOR_RANGE 100 -#define TANK_CANNON_RANGE (TANK_SENSOR_RANGE / 2) -#define TANK_MAX_ACCEL 35 -#define TANK_MAX_TURRET_ROT (PI/3) -#define TANK_TOP_SPEED 7 +#define TANK_MAX_SENSORS 10 +#define TANK_RADIUS 7.5 +#define TANK_SENSOR_RANGE 100 +#define TANK_CANNON_RECHARGE 20 /* Turns to recharge cannon */ +#define TANK_CANNON_RANGE (TANK_SENSOR_RANGE / 2) +#define TANK_MAX_ACCEL 35 +#define TANK_MAX_TURRET_ROT (PI/3) +#define TANK_TOP_SPEED 7 /* (tank radius + tank radius)^2 */ #define TANK_COLLISION_ADJ2 \ @@ -22,9 +23,12 @@ #define TANK_SENSOR_ADJ2 \ ((TANK_SENSOR_RANGE + TANK_RADIUS) * (TANK_SENSOR_RANGE + TANK_RADIUS)) +#define TANK_CANNON_ADJ2 \ + ((TANK_CANNON_RANGE + TANK_RADIUS) * (TANK_CANNON_RANGE + TANK_RADIUS)) #ifndef rad2deg #define rad2deg(r) ((int)(180*(r)/PI)) +#define deg2rad(r) ((r*PI)/180) #endif #ifndef max @@ -42,7 +46,6 @@ struct sensor { float angle; float width; int range; - int range_adj2; /* (range + TANK_RADIUS)^2 */ int turret; /* Mounted to turret? */ int triggered; }; diff --git a/dump.h b/dump.h new file mode 100644 index 0000000..d7f454c --- /dev/null +++ b/dump.h @@ -0,0 +1,14 @@ +#ifndef __DUMP_H__ + +/* Debugging help */ +#define DUMPf(fmt, args...) fprintf(stderr, "%s:%s:%d " fmt "\n", __FILE__, __FUNCTION__, __LINE__, ##args) +#define DUMP() DUMPf("") +#define DUMP_d(v) DUMPf("%s = %d", #v, v) +#define DUMP_x(v) DUMPf("%s = 0x%x", #v, v) +#define DUMP_s(v) DUMPf("%s = %s", #v, v) +#define DUMP_c(v) DUMPf("%s = %c", #v, v) +#define DUMP_f(v) DUMPf("%s = %f", #v, v) +#define DUMP_p(v) DUMPf("%s = %p", #v, v) +#define DUMP_xy(v) DUMPf("%s = (%f, %f)", #v, v[0], v[1]); + +#endif diff --git a/examples/easy/berzerker/author b/examples/easy/berzerker/author new file mode 100644 index 0000000..69ebd74 --- /dev/null +++ b/examples/easy/berzerker/author @@ -0,0 +1 @@ +Neale Pickett diff --git a/examples/easy/berzerker/name b/examples/easy/berzerker/name new file mode 100644 index 0000000..16db0a1 --- /dev/null +++ b/examples/easy/berzerker/name @@ -0,0 +1 @@ +berzerker diff --git a/examples/easy/berzerker/program b/examples/easy/berzerker/program new file mode 100644 index 0000000..f507fed --- /dev/null +++ b/examples/easy/berzerker/program @@ -0,0 +1,15 @@ +2 random +0 = +{ 50 100 set-speed! } +{ 100 50 set-speed! } +ifelse + +4 random +0 = +{ 360 random set-turret! } +if + +30 random +0 = +{ fire! } +if diff --git a/examples/easy/rabbitwithgun/author b/examples/easy/rabbitwithgun/author new file mode 100644 index 0000000..69ebd74 --- /dev/null +++ b/examples/easy/rabbitwithgun/author @@ -0,0 +1 @@ +Neale Pickett diff --git a/examples/easy/rabbitwithgun/name b/examples/easy/rabbitwithgun/name new file mode 100644 index 0000000..0876e09 --- /dev/null +++ b/examples/easy/rabbitwithgun/name @@ -0,0 +1 @@ +Rabbit With Gun diff --git a/examples/easy/rabbitwithgun/program b/examples/easy/rabbitwithgun/program new file mode 100644 index 0000000..83fd3ee --- /dev/null +++ b/examples/easy/rabbitwithgun/program @@ -0,0 +1,26 @@ +100 100 set-speed! + +( Always set turret to 180 degrees ) +180 set-turret! + +( Vary walk 1/8 of the time ) +8 random +0 = +{ + 2 random + 0 = + { 70 100 set-speed! } + { 100 70 set-speed! } + ifelse +} +if + +( If you see something, shoot it ) +0 sensor? +{ fire! } +if + +( Turn, if trouble lies ahead ) +1 sensor? +{ 0 100 set-speed! } +if diff --git a/examples/easy/rabbitwithgun/sensor0 b/examples/easy/rabbitwithgun/sensor0 new file mode 100644 index 0000000..98ddff9 --- /dev/null +++ b/examples/easy/rabbitwithgun/sensor0 @@ -0,0 +1 @@ +50 0 0 1 diff --git a/examples/easy/rabbitwithgun/sensor1 b/examples/easy/rabbitwithgun/sensor1 new file mode 100644 index 0000000..6c28b4f --- /dev/null +++ b/examples/easy/rabbitwithgun/sensor1 @@ -0,0 +1 @@ +70 0 50 0 diff --git a/examples/medium/simpleton/author b/examples/medium/simpleton/author new file mode 100644 index 0000000..5d7a8e0 --- /dev/null +++ b/examples/medium/simpleton/author @@ -0,0 +1 @@ +Paul Ferrell diff --git a/examples/medium/simpleton/name b/examples/medium/simpleton/name new file mode 100644 index 0000000..a5df3a7 --- /dev/null +++ b/examples/medium/simpleton/name @@ -0,0 +1 @@ +Simpleton diff --git a/examples/medium/simpleton/program b/examples/medium/simpleton/program new file mode 100644 index 0000000..caaa041 --- /dev/null +++ b/examples/medium/simpleton/program @@ -0,0 +1,5 @@ +90 100 set-speed! +0 set-turret! + +0 sensor? { fire! } if +1 sensor? { -100 100 set-speed! } if diff --git a/examples/medium/simpleton/sensor0 b/examples/medium/simpleton/sensor0 new file mode 100644 index 0000000..469111b --- /dev/null +++ b/examples/medium/simpleton/sensor0 @@ -0,0 +1 @@ +50 0 5 1 diff --git a/examples/medium/simpleton/sensor1 b/examples/medium/simpleton/sensor1 new file mode 100644 index 0000000..ab87ddb --- /dev/null +++ b/examples/medium/simpleton/sensor1 @@ -0,0 +1 @@ +30 0 50 diff --git a/examples/medium/sittingduckwithteeth/author b/examples/medium/sittingduckwithteeth/author new file mode 100644 index 0000000..902019d --- /dev/null +++ b/examples/medium/sittingduckwithteeth/author @@ -0,0 +1 @@ +Neale Pickett diff --git a/examples/medium/sittingduckwithteeth/name b/examples/medium/sittingduckwithteeth/name new file mode 100644 index 0000000..38607b2 --- /dev/null +++ b/examples/medium/sittingduckwithteeth/name @@ -0,0 +1 @@ +Sitting Duck with Teeth diff --git a/examples/medium/sittingduckwithteeth/program b/examples/medium/sittingduckwithteeth/program new file mode 100644 index 0000000..dc28c4f --- /dev/null +++ b/examples/medium/sittingduckwithteeth/program @@ -0,0 +1,3 @@ +0 sensor? { fire! } if +1 sensor? { get-turret 40 + set-turret! } if +2 sensor? { get-turret 40 - set-turret! } if diff --git a/examples/medium/sittingduckwithteeth/sensor0 b/examples/medium/sittingduckwithteeth/sensor0 new file mode 100644 index 0000000..b5725bf --- /dev/null +++ b/examples/medium/sittingduckwithteeth/sensor0 @@ -0,0 +1 @@ +50 0 10 1 diff --git a/examples/medium/sittingduckwithteeth/sensor1 b/examples/medium/sittingduckwithteeth/sensor1 new file mode 100644 index 0000000..d040b72 --- /dev/null +++ b/examples/medium/sittingduckwithteeth/sensor1 @@ -0,0 +1 @@ +100 90 150 1 diff --git a/examples/medium/sittingduckwithteeth/sensor2 b/examples/medium/sittingduckwithteeth/sensor2 new file mode 100644 index 0000000..e21488b --- /dev/null +++ b/examples/medium/sittingduckwithteeth/sensor2 @@ -0,0 +1 @@ +100 270 150 1 diff --git a/examples/medium/sweeper/author b/examples/medium/sweeper/author new file mode 100644 index 0000000..902019d --- /dev/null +++ b/examples/medium/sweeper/author @@ -0,0 +1 @@ +Neale Pickett diff --git a/examples/medium/sweeper/name b/examples/medium/sweeper/name new file mode 100644 index 0000000..a7a883d --- /dev/null +++ b/examples/medium/sweeper/name @@ -0,0 +1 @@ +Sweeper diff --git a/examples/medium/sweeper/program b/examples/medium/sweeper/program new file mode 100644 index 0000000..ea1811d --- /dev/null +++ b/examples/medium/sweeper/program @@ -0,0 +1,10 @@ +3 random +dup 0 = { 70 70 set-speed! } if +dup 1 = { 40 70 set-speed! } if + 2 = { 70 40 set-speed! } if +get-turret 20 - set-turret! + +3 sensor? { 0 0 set-speed! } if +1 sensor? { get-turret 60 + set-turret! } if +2 sensor? { get-turret 60 - set-turret! } if +0 sensor? { fire! } if diff --git a/examples/medium/sweeper/sensor0 b/examples/medium/sweeper/sensor0 new file mode 100644 index 0000000..469111b --- /dev/null +++ b/examples/medium/sweeper/sensor0 @@ -0,0 +1 @@ +50 0 5 1 diff --git a/examples/medium/sweeper/sensor1 b/examples/medium/sweeper/sensor1 new file mode 100644 index 0000000..d040b72 --- /dev/null +++ b/examples/medium/sweeper/sensor1 @@ -0,0 +1 @@ +100 90 150 1 diff --git a/examples/medium/sweeper/sensor2 b/examples/medium/sweeper/sensor2 new file mode 100644 index 0000000..e21488b --- /dev/null +++ b/examples/medium/sweeper/sensor2 @@ -0,0 +1 @@ +100 270 150 1 diff --git a/examples/medium/sweeper/sensor3 b/examples/medium/sweeper/sensor3 new file mode 100644 index 0000000..dcd5f9c --- /dev/null +++ b/examples/medium/sweeper/sensor3 @@ -0,0 +1 @@ +100 0 359 0 diff --git a/run-tanks.c b/run-tanks.c new file mode 100644 index 0000000..b0ac839 --- /dev/null +++ b/run-tanks.c @@ -0,0 +1,524 @@ +#include +#include +#include +#include +#include +#include +#include "ctanks.h" +#include "cforf.h" +#include "dump.h" + +#define MAX_TANKS 50 +#define ROUNDS 500 +#define SPACING 150 + +#define LENV_SIZE 100 + +#define DSTACK_SIZE 200 +#define CSTACK_SIZE 500 +#define MEMORY_SIZE 10 + +struct forftank { + struct forf_env env; + char color[7]; /* "ff0088" */ + char name[50]; + + struct forf_stack _prog; + struct forf_value _progvals[CSTACK_SIZE]; + struct forf_stack _cmd; + struct forf_value _cmdvals[CSTACK_SIZE]; + struct forf_stack _data; + struct forf_value _datavals[DSTACK_SIZE]; + struct forf_memory _mem; + long _memvals[MEMORY_SIZE]; +}; + + +#ifndef NODEBUG +void +forf_print_val(struct forf_value *val) +{ + switch (val->type) { + case forf_type_number: + printf("%d", val->v.i); + break; + case forf_type_proc: + printf("[proc %p]", val->v.p); + break; + case forf_type_stack_begin: + printf("{"); + break; + case forf_type_stack_end: + printf("}"); + break; + } +} + +void +forf_print_stack(struct forf_stack *s) +{ + size_t pos; + + for (pos = 0; pos < s->top; pos += 1) { + forf_print_val(&(s->stack[pos])); + printf(" "); + } +} + +void +forf_dump_stack(struct forf_stack *s) +{ + printf("Stack at %p: ", s); + forf_print_stack(s); + printf("\n"); +} +#endif + +/* + * + * Forf API + * + */ + +/** Has the turret recharged? */ +void +forf_tank_fire_ready(struct forf_env *env) +{ + struct tank *tank = (struct tank *)env->udata; + + forf_push_num(env, tank_fire_ready(tank)); +} + +/** Fire! */ +void +forf_tank_fire(struct forf_env *env) +{ + struct tank *tank = (struct tank *)env->udata; + + tank_fire(tank); +} + +/** Set desired speed */ +void +forf_tank_set_speed(struct forf_env *env) +{ + struct tank *tank = (struct tank *)env->udata; + long right = forf_pop_num(env); + long left = forf_pop_num(env); + + tank_set_speed(tank, left, right); +} + +/** Get the current turret angle */ +void +forf_tank_get_turret(struct forf_env *env) +{ + struct tank *tank = (struct tank *)env->udata; + float angle = tank_get_turret(tank); + + forf_push_num(env, rad2deg(angle)); +} + +/** Set the desired turret angle */ +void +forf_tank_set_turret(struct forf_env *env) +{ + struct tank *tank = (struct tank *)env->udata; + long angle = forf_pop_num(env); + + tank_set_turret(tank, deg2rad(angle)); +} + +/** Is a sensor active? */ +void +forf_tank_get_sensor(struct forf_env *env) +{ + struct tank *tank = (struct tank *)env->udata; + long sensor_num = forf_pop_num(env); + + forf_push_num(env, tank_get_sensor(tank, sensor_num)); +} + +/** Set the LED state */ +void +forf_tank_set_led(struct forf_env *env) +{ + struct tank *tank = (struct tank *)env->udata; + long active = forf_pop_num(env); + + tank_set_led(tank, active); +} + +/** Pick a random number */ +void +forf_proc_random(struct forf_env *env) +{ + long max = forf_pop_num(env); + + forf_push_num(env, rand() % max); +} + +/* Tanks lexical environment */ +struct forf_lexical_env tanks_lenv_addons[] = { + {"fire-ready?", forf_tank_fire_ready}, + {"fire!", forf_tank_fire}, + {"set-speed!", forf_tank_set_speed}, + {"get-turret", forf_tank_get_turret}, + {"set-turret!", forf_tank_set_turret}, + {"sensor?", forf_tank_get_sensor}, + {"set-led!", forf_tank_set_led}, + {"random", forf_proc_random}, + {NULL, NULL} +}; + +/* + * + * Filesystem stuff + * + */ + +int +ft_read_file(char *ptr, size_t size, char *dir, char *fn) +{ + char path[256]; + FILE *f = NULL; + int ret; + int success = 0; + + do { + snprintf(path, sizeof(path), "%s/%s", dir, fn); + f = fopen(path, "r"); + if (! f) break; + + ret = fread(ptr, 1, size - 1, f); + ptr[ret] = '\0'; + if (! ret) break; + + success = 1; + } while (0); + + if (f) fclose(f); + if (! success) { + return 0; + } + return 1; +} + +void +ft_bricked_tank(struct tank *tank, void *ignored) +{ + /* Do nothing, the tank is comatose */ +} + +void +ft_run_tank(struct tank *tank, struct forftank *ftank) +{ + int ret; + + /* Copy program into command stack */ + forf_stack_copy(&ftank->_cmd, &ftank->_prog); + forf_stack_reset(&ftank->_data); + ret = forf_eval(&ftank->env); + if (! ret) { + fprintf(stderr, "Error in %s: %s\n", + ftank->name, + forf_error_str[ftank->env.error]); + } +} + +int +ft_read_program(struct forftank *ftank, + struct tank *tank, + struct forf_lexical_env *lenv, + char *path) +{ + int ret; + char progpath[256]; + FILE *f; + + /* Open program */ + snprintf(progpath, sizeof(progpath), "%s/program", path); + f = fopen(progpath, "r"); + if (! f) return 0; + + /* Parse program */ + ret = forf_parse_file(&ftank->env, f); + fclose(f); + if (ret) { + fprintf(stderr, "Parse error in %s, character %d: %s\n", + progpath, + ret, + forf_error_str[ftank->env.error]); + return 0; + } + + /* Back up the program so we can run it over and over without + needing to re-parse */ + forf_stack_copy(&ftank->_prog, &ftank->_cmd); + + tank_init(tank, (tank_run_func *)ft_run_tank, ftank); + + return 1; +} + +void +ft_tank_init(struct forftank *ftank, + struct tank *tank, + struct forf_lexical_env *lenv) +{ + /* Set up forf environment */ + forf_stack_init(&ftank->_prog, ftank->_progvals, CSTACK_SIZE); + forf_stack_init(&ftank->_cmd, ftank->_cmdvals, CSTACK_SIZE); + forf_stack_init(&ftank->_data, ftank->_datavals, DSTACK_SIZE); + forf_memory_init(&ftank->_mem, ftank->_memvals, MEMORY_SIZE); + forf_env_init(&ftank->env, + lenv, + &ftank->_data, + &ftank->_cmd, + &ftank->_mem, + tank); +} + +void +ft_read_sensors(struct tank *tank, + char *path) +{ + int i; + + for (i = 0; i < TANK_MAX_SENSORS; i += 1) { + int ret; + char fn[10]; + char s[20]; + char *p = s; + long range; + long angle; + long width; + long turret; + + snprintf(fn, sizeof(fn), "sensor%d", i); + ret = ft_read_file(s, sizeof(s), path, fn); + if (! ret) { + s[0] = 0; + } + range = strtol(p, &p, 0); + angle = strtol(p, &p, 0); + width = strtol(p, &p, 0); + turret = strtol(p, &p, 0); + + tank->sensors[i].range = min(range, TANK_SENSOR_RANGE); + tank->sensors[i].angle = deg2rad(angle % 360); + tank->sensors[i].width = deg2rad(width % 360); + tank->sensors[i].turret = (0 != turret); + } +} + +int +ft_read_tank(struct forftank *ftank, + struct tank *tank, + struct forf_lexical_env *lenv, + char *path) +{ + int ret; + + /* What is your name? */ + ret = ft_read_file(ftank->name, sizeof(ftank->name), path, "name"); + if (! ret) { + strncpy(ftank->name, path, sizeof(ftank->name)); + } + + /* What is your quest? */ + ft_tank_init(ftank, tank, lenv); + ret = ft_read_program(ftank, tank, lenv, path); + if (ret) { + ft_read_sensors(tank, path); + } else { + /* Brick the tank */ + tank_init(tank, ft_bricked_tank, NULL); + } + + /* What is your favorite color? */ + ret = ft_read_file(ftank->color, sizeof(ftank->color), path, "color"); + if (! ret) { + strncpy(ftank->color, "808080", sizeof(ftank->color)); + } + + return 1; +} + +void +print_header(FILE *f, + struct tanks_game *game, + struct forftank *forftanks, + struct tank *tanks, + int ntanks) +{ + int i, j; + + fprintf(f, "[[%d, %d, %d],[\n", + (int)game->size[0], (int)game->size[1], TANK_CANNON_RANGE); + for (i = 0; i < ntanks; i += 1) { + fprintf(f, " [\"#%s\",[", forftanks[i].color); + for (j = 0; j < TANK_MAX_SENSORS; j += 1) { + struct sensor *s = &(tanks[i].sensors[j]); + + if (! s->range) { + continue; + } + fprintf(f, "[%d, %.2f, %.2f, %d],", + (int)(s->range), + s->angle, + s->width, + s->turret); + } + fprintf(f, "]],\n"); + } + fprintf(f, "],[\n"); +} + +void +print_footer(FILE *f) +{ + fprintf(f, "]]\n"); +} + +int +main(int argc, char *argv[]) +{ + struct tanks_game game; + struct forftank myftanks[MAX_TANKS]; + struct tank mytanks[MAX_TANKS]; + struct forf_lexical_env lenv[LENV_SIZE]; + int order[MAX_TANKS]; + int ntanks = 0; + int i; + int alive; + + lenv[0].name = NULL; + lenv[0].proc = NULL; + if ((! forf_extend_lexical_env(lenv, forf_base_lexical_env, LENV_SIZE)) || + (! forf_extend_lexical_env(lenv, tanks_lenv_addons, LENV_SIZE))) { + fprintf(stderr, "Unable to initialize lexical environment.\n"); + return 1; + } + + /* We only need slightly random numbers */ + { + char *s = getenv("SEED"); + int seed = atoi(s?s:""); + + if (! seed) { + seed = getpid(); + } + + srand(seed); + fprintf(stdout, "// SEED=%d\n", seed); + } + + /* Shuffle the order we read things in */ + for (i = 0; i < MAX_TANKS; i += 1) { + order[i] = i; + } + for (i = 0; i < argc - 1; i += 1) { + int j = rand() % (argc - 1); + int n = order[j]; + + order[j] = order[i]; + order[i] = n; + } + + /* Every argument is a tank directory */ + for (i = 0; i < argc - 1; i += 1) { + if (ft_read_tank(&myftanks[ntanks], + &mytanks[ntanks], + lenv, + argv[order[i] + 1])) { + ntanks += 1; + } + } + + if (0 == ntanks) { + fprintf(stderr, "No usable tanks!\n"); + return 1; + } + + /* Calculate the size of the game board */ + { + int x, y; + + for (x = 1; x * x < ntanks; x += 1); + y = ntanks / x; + if (ntanks % x) { + y += 1; + } + + game.size[0] = x * SPACING; + game.size[1] = y * SPACING; + } + + /* Position tanks */ + { + int x = 50; + int y = 50; + + for (i = 0; i < ntanks; i += 1) { + mytanks[i].position[0] = (float)x; + mytanks[i].position[1] = (float)y; + mytanks[i].angle = deg2rad(rand() % 360); + mytanks[i].turret.current = deg2rad(rand() % 360); + + x += SPACING; + if (x > game.size[0]) { + x %= (int)(game.size[0]); + y += SPACING; + } + } + } + + print_header(stdout, &game, myftanks, mytanks, ntanks); + + /* Run rounds */ + alive = ntanks; + for (i = 0; (alive > 1) && (i < ROUNDS); i += 1) { + int j; + + tanks_run_turn(&game, mytanks, ntanks); + fprintf(stdout, "[\n"); + alive = ntanks; + for (j = 0; j < ntanks; j += 1) { + struct tank *t = &(mytanks[j]); + + if (t->killer) { + alive -= 1; + fprintf(stdout, " 0,\n"); + } else { + int k; + int flags = 0; + int sensors = 0; + + for (k = 0; k < TANK_MAX_SENSORS; k += 1) { + if (t->sensors[k].triggered) { + sensors |= (1 << k); + } + } + if (t->turret.firing) { + flags |= 1; + } + if (t->led) { + flags |= 2; + } + fprintf(stdout, " [%d,%d,%.2f,%.2f,%d,%d],\n", + (int)(t->position[0]), + (int)(t->position[1]), + t->angle, + t->turret.current, + flags, + sensors); + } + } + fprintf(stdout, "],\n"); + } + + print_footer(stdout); + + return 0; +} diff --git a/tanks.js b/tanks.js index 78c29d7..2bd5c16 100644 --- a/tanks.js +++ b/tanks.js @@ -114,7 +114,7 @@ function Tank(ctx, width, height, color, sensors) { ctx.fillRect(-7, 4, 15, 5); ctx.rotate(this.turret); if (this.fire) { - ctx.fillStyle = ("rgba(68,204,68," + this.fire/5 +")"); + ctx.fillStyle = ("rgba(128,128,255," + this.fire/5 + ")"); ctx.fillRect(0, -1, 45, 2); this.fire -= 1; } else { @@ -210,8 +210,8 @@ function start(game) { } } - //loop_id = setInterval(update, 66); - loop_id = setInterval(update, 200); + loop_id = setInterval(update, 66); + //loop_id = setInterval(update, 400); if (fps) { setInterval(update_fps, 1000); } diff --git a/test-tanks.c b/test-tanks.c index 03c967d..d3996aa 100644 --- a/test-tanks.c +++ b/test-tanks.c @@ -7,8 +7,11 @@ void test_run(struct tank *tank, void *unused) { - tank_set_speed(tank, 61, 60); - tank_set_turret(tank, 0); + tank_set_speed(tank, -60, -61); + tank_set_turret(tank, tank->turret.desired + PI/15); + if (tank->sensors[0].triggered) { + tank_fire(tank); + } } void @@ -25,7 +28,7 @@ main(int argc, char *argv[]) int i; game.size[0] = 600; - game.size[1] = 600; + game.size[1] = 200; printf("[\n"); printf("[%d, %d, %d],\n", @@ -66,6 +69,11 @@ main(int argc, char *argv[]) mytanks[i].sensors[4].range = 100; mytanks[i].sensors[4].turret = 1; + mytanks[i].sensors[5].angle = 0; + mytanks[i].sensors[5].width = PI*1.99; + mytanks[i].sensors[5].range = 80; + mytanks[i].sensors[5].turret = 0; + for (j = 0; j < TANK_MAX_SENSORS; j += 1) { struct sensor *s = &(mytanks[i].sensors[j]); @@ -87,7 +95,7 @@ main(int argc, char *argv[]) printf("// Rounds\n"); printf("[\n"); - for (i = 0; i < 200; i += 1) { + for (i = 0; i < 100; i += 1) { int j; tanks_run_turn(&game, mytanks, NTANKS); diff --git a/trig.py b/trig.py deleted file mode 100755 index 270aece..0000000 --- a/trig.py +++ /dev/null @@ -1,12 +0,0 @@ -#! /usr/bin/python - -import math - -print "static float trig_cos_table[] = {" -for i in range(91): - r = (i * math.pi) / 180.0 - cos = math.cos(r) - print ("%0f," % cos), - if 5 == i % 6: - print -print "};"