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 "};"