mirror of https://github.com/dirtbags/tanks.git
Mostly-working forftanks implementation
This commit is contained in:
parent
74b9e8e455
commit
c0d0d2de74
2
Makefile
2
Makefile
|
@ -7,5 +7,7 @@ test: test-tanks
|
||||||
|
|
||||||
test-tanks: test-tanks.o ctanks.o
|
test-tanks: test-tanks.o ctanks.o
|
||||||
|
|
||||||
|
run-tanks: run-tanks.o ctanks.o cforf.o
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f test-tanks *.o
|
rm -f test-tanks *.o
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* 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 <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
#ifndef __CFORF_H__
|
||||||
|
#define __CFORF_H__
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
#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
|
192
ctanks.c
192
ctanks.c
|
@ -12,6 +12,7 @@
|
||||||
#define DUMP_s(v) DUMPf("%s = %s", #v, v)
|
#define DUMP_s(v) DUMPf("%s = %s", #v, v)
|
||||||
#define DUMP_c(v) DUMPf("%s = %c", #v, v)
|
#define DUMP_c(v) DUMPf("%s = %c", #v, v)
|
||||||
#define DUMP_f(v) DUMPf("%s = %f", #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_xy(v) DUMPf("%s = (%f, %f)", #v, v[0], v[1]);
|
||||||
#define DUMP_angle(v) DUMPf("%s = %.3fπ", #v, (v/PI));
|
#define DUMP_angle(v) DUMPf("%s = %.3fπ", #v, (v/PI));
|
||||||
|
|
||||||
|
@ -41,8 +42,8 @@ tank_fire(struct tank *tank)
|
||||||
void
|
void
|
||||||
tank_set_speed(struct tank *tank, float left, float right)
|
tank_set_speed(struct tank *tank, float left, float right)
|
||||||
{
|
{
|
||||||
tank->speed.desired[0] = left;
|
tank->speed.desired[0] = min(max(left, -100), 100);
|
||||||
tank->speed.desired[1] = right;
|
tank->speed.desired[1] = min(max(right, -100), 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
float
|
float
|
||||||
|
@ -90,27 +91,45 @@ rotate_point(float angle, float point[2])
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
tank_sensor_calc(struct tanks_game *game,
|
tanks_fire_cannon(struct tanks_game *game,
|
||||||
struct tank *this,
|
struct tank *this,
|
||||||
struct tank *that,
|
struct tank *that,
|
||||||
|
float vector[2],
|
||||||
float dist2)
|
float dist2)
|
||||||
{
|
{
|
||||||
float tpos[2];
|
float theta = this->angle + this->turret.current;
|
||||||
int i;
|
float rpos[2];
|
||||||
|
|
||||||
/* Translate other tank's position to make us the origin */
|
/* If someone's a crater, this is easy */
|
||||||
for (i = 0; i < 2; i += 1) {
|
if (this->killer || that->killer) {
|
||||||
tpos[i] = that->position[i] - this->position[i];
|
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| <
|
/* Did this shoot that? Rotate point by turret degrees, and if |y| <
|
||||||
TANK_RADIUS, we have a hit. */
|
TANK_RADIUS, we have a hit. */
|
||||||
if ((this->turret.firing) && (dist2 <= TANK_CANNON_RANGE)) {
|
rpos[0] = vector[0];
|
||||||
int theta = this->angle + this->turret.current;
|
rpos[1] = vector[1];
|
||||||
float rpos[2];
|
|
||||||
|
|
||||||
rpos[0] = tpos[0];
|
|
||||||
rpos[1] = tpos[1];
|
|
||||||
rotate_point(-theta, rpos);
|
rotate_point(-theta, rpos);
|
||||||
if (fabsf(rpos[1]) < TANK_RADIUS) {
|
if (fabsf(rpos[1]) < TANK_RADIUS) {
|
||||||
that->killer = this;
|
that->killer = this;
|
||||||
|
@ -118,12 +137,36 @@ tank_sensor_calc(struct tanks_game *game,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 */
|
/* Calculate sensors */
|
||||||
for (i = 0; i < TANK_MAX_SENSORS; i += 1) {
|
for (i = 0; i < TANK_MAX_SENSORS; i += 1) {
|
||||||
float theta;
|
float theta;
|
||||||
float rpos[2];
|
float rpos[2];
|
||||||
float m_r, m_s;
|
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 */
|
/* No need to re-check this sensor if it's already firing */
|
||||||
if (this->sensors[i].triggered) {
|
if (this->sensors[i].triggered) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -139,11 +182,10 @@ tank_sensor_calc(struct tanks_game *game,
|
||||||
if (this->sensors[i].turret) {
|
if (this->sensors[i].turret) {
|
||||||
theta += this->turret.current;
|
theta += this->turret.current;
|
||||||
}
|
}
|
||||||
theta = fmodf(theta, 2*PI);
|
|
||||||
|
|
||||||
/* Rotate tpos by theta */
|
/* Rotate their position by theta */
|
||||||
rpos[0] = tpos[0];
|
rpos[0] = vector[0];
|
||||||
rpos[1] = tpos[1];
|
rpos[1] = vector[1];
|
||||||
rotate_point(-theta, rpos);
|
rotate_point(-theta, rpos);
|
||||||
|
|
||||||
/* Sensor is symmetrical, we can consider only top quadrants */
|
/* 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_s = 1 / tanf(this->sensors[i].width / 2);
|
||||||
m_r = rpos[0] / rpos[1];
|
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 our inverse slope is less than theirs, they're inside the arc */
|
||||||
if (m_r >= m_s) {
|
if (m_r >= m_s) {
|
||||||
this->sensors[i].triggered = 1;
|
this->sensors[i].triggered = 1;
|
||||||
|
@ -177,52 +211,30 @@ tank_sensor_calc(struct tanks_game *game,
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
do_shit_with(struct tanks_game *game,
|
compute_vector(struct tanks_game *game,
|
||||||
|
float vector[2],
|
||||||
|
float *dist2,
|
||||||
struct tank *this,
|
struct tank *this,
|
||||||
struct tank *that)
|
struct tank *that)
|
||||||
{
|
{
|
||||||
float vector[2];
|
|
||||||
float dist2; /* distance squared */
|
|
||||||
float tpos; /* Translated position */
|
|
||||||
int i;
|
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,
|
/* Establish shortest vector from center of this to center of that,
|
||||||
* taking wrapping into account */
|
* taking wrapping into account */
|
||||||
for (i = 0; i < 2; i += 1) {
|
for (i = 0; i < 2; i += 1) {
|
||||||
float halfsize = game->size[i] / 2;
|
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 */
|
/* Compute distance^2 for range comparisons */
|
||||||
dist2 = sq(vector[0]) + sq(vector[1]);
|
*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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -232,6 +244,7 @@ tanks_move_tank(struct tanks_game *game,
|
||||||
int i;
|
int i;
|
||||||
float movement;
|
float movement;
|
||||||
float angle;
|
float angle;
|
||||||
|
int dir = 1;
|
||||||
|
|
||||||
/* Rotate the turret */
|
/* Rotate the turret */
|
||||||
{
|
{
|
||||||
|
@ -282,7 +295,6 @@ tanks_move_tank(struct tanks_game *game,
|
||||||
float So, Si;
|
float So, Si;
|
||||||
float r;
|
float r;
|
||||||
float theta;
|
float theta;
|
||||||
int dir;
|
|
||||||
|
|
||||||
/* The first thing Paul's code does is find "friction", which seems
|
/* The first thing Paul's code does is find "friction", which seems
|
||||||
to be a penalty for having the treads go in opposite directions.
|
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];
|
float m[2];
|
||||||
|
|
||||||
m[0] = cosf(tank->angle) * movement;
|
m[0] = cosf(tank->angle) * movement * dir;
|
||||||
m[1] = sinf(tank->angle) * movement;
|
m[1] = sinf(tank->angle) * movement * dir;
|
||||||
|
|
||||||
for (i = 0; i < 2; i += 1) {
|
for (i = 0; i < 2; i += 1) {
|
||||||
tank->position[i] = fmodf(tank->position[i] + m[i] + game->size[i],
|
tank->position[i] = fmodf(tank->position[i] + m[i] + game->size[i],
|
||||||
|
@ -336,32 +348,68 @@ void
|
||||||
tanks_run_turn(struct tanks_game *game, struct tank *tanks, int ntanks)
|
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 */
|
/* It takes (at least) two to tank-o */
|
||||||
for (i = 0; i < ntanks; i += 1) {
|
if (ntanks < 2) {
|
||||||
if (! tanks[i].killer) {
|
return;
|
||||||
tanks[i].run(&tanks[i], &tanks[i].udata);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Clear sensors */
|
/* Charge cannons and reset sensors */
|
||||||
for (i = 0; i < ntanks; i += 1) {
|
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) {
|
for (j = 0; j < TANK_MAX_SENSORS; j += 1) {
|
||||||
tanks[i].sensors[j].triggered = 0;
|
tanks[i].sensors[j].triggered = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Move */
|
/* Move tanks */
|
||||||
for (i = 0; i < ntanks; i += 1) {
|
for (i = 0; i < ntanks; i += 1) {
|
||||||
if (! tanks[i].killer) {
|
if (tanks[i].killer) continue;
|
||||||
tanks_move_tank(game, &(tanks[i]));
|
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) {
|
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) {
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
5
ctanks.h
5
ctanks.h
|
@ -7,6 +7,7 @@
|
||||||
#define TANK_MAX_SENSORS 10
|
#define TANK_MAX_SENSORS 10
|
||||||
#define TANK_RADIUS 7.5
|
#define TANK_RADIUS 7.5
|
||||||
#define TANK_SENSOR_RANGE 100
|
#define TANK_SENSOR_RANGE 100
|
||||||
|
#define TANK_CANNON_RECHARGE 20 /* Turns to recharge cannon */
|
||||||
#define TANK_CANNON_RANGE (TANK_SENSOR_RANGE / 2)
|
#define TANK_CANNON_RANGE (TANK_SENSOR_RANGE / 2)
|
||||||
#define TANK_MAX_ACCEL 35
|
#define TANK_MAX_ACCEL 35
|
||||||
#define TANK_MAX_TURRET_ROT (PI/3)
|
#define TANK_MAX_TURRET_ROT (PI/3)
|
||||||
|
@ -22,9 +23,12 @@
|
||||||
#define TANK_SENSOR_ADJ2 \
|
#define TANK_SENSOR_ADJ2 \
|
||||||
((TANK_SENSOR_RANGE + TANK_RADIUS) * (TANK_SENSOR_RANGE + TANK_RADIUS))
|
((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
|
#ifndef rad2deg
|
||||||
#define rad2deg(r) ((int)(180*(r)/PI))
|
#define rad2deg(r) ((int)(180*(r)/PI))
|
||||||
|
#define deg2rad(r) ((r*PI)/180)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef max
|
#ifndef max
|
||||||
|
@ -42,7 +46,6 @@ struct sensor {
|
||||||
float angle;
|
float angle;
|
||||||
float width;
|
float width;
|
||||||
int range;
|
int range;
|
||||||
int range_adj2; /* (range + TANK_RADIUS)^2 */
|
|
||||||
int turret; /* Mounted to turret? */
|
int turret; /* Mounted to turret? */
|
||||||
int triggered;
|
int triggered;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
|
@ -0,0 +1 @@
|
||||||
|
Neale Pickett <neale@woozle.org>
|
|
@ -0,0 +1 @@
|
||||||
|
berzerker
|
|
@ -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
|
|
@ -0,0 +1 @@
|
||||||
|
Neale Pickett <neale@woozle.org>
|
|
@ -0,0 +1 @@
|
||||||
|
Rabbit With Gun
|
|
@ -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
|
|
@ -0,0 +1 @@
|
||||||
|
50 0 0 1
|
|
@ -0,0 +1 @@
|
||||||
|
70 0 50 0
|
|
@ -0,0 +1 @@
|
||||||
|
Paul Ferrell <pflarr@lanl.gov>
|
|
@ -0,0 +1 @@
|
||||||
|
Simpleton
|
|
@ -0,0 +1,5 @@
|
||||||
|
90 100 set-speed!
|
||||||
|
0 set-turret!
|
||||||
|
|
||||||
|
0 sensor? { fire! } if
|
||||||
|
1 sensor? { -100 100 set-speed! } if
|
|
@ -0,0 +1 @@
|
||||||
|
50 0 5 1
|
|
@ -0,0 +1 @@
|
||||||
|
30 0 50
|
|
@ -0,0 +1 @@
|
||||||
|
Neale Pickett <neale@lanl.gov>
|
|
@ -0,0 +1 @@
|
||||||
|
Sitting Duck with Teeth
|
|
@ -0,0 +1,3 @@
|
||||||
|
0 sensor? { fire! } if
|
||||||
|
1 sensor? { get-turret 40 + set-turret! } if
|
||||||
|
2 sensor? { get-turret 40 - set-turret! } if
|
|
@ -0,0 +1 @@
|
||||||
|
50 0 10 1
|
|
@ -0,0 +1 @@
|
||||||
|
100 90 150 1
|
|
@ -0,0 +1 @@
|
||||||
|
100 270 150 1
|
|
@ -0,0 +1 @@
|
||||||
|
Neale Pickett <neale@lanl.gov>
|
|
@ -0,0 +1 @@
|
||||||
|
Sweeper
|
|
@ -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
|
|
@ -0,0 +1 @@
|
||||||
|
50 0 5 1
|
|
@ -0,0 +1 @@
|
||||||
|
100 90 150 1
|
|
@ -0,0 +1 @@
|
||||||
|
100 270 150 1
|
|
@ -0,0 +1 @@
|
||||||
|
100 0 359 0
|
|
@ -0,0 +1,524 @@
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <math.h>
|
||||||
|
#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;
|
||||||
|
}
|
6
tanks.js
6
tanks.js
|
@ -114,7 +114,7 @@ function Tank(ctx, width, height, color, sensors) {
|
||||||
ctx.fillRect(-7, 4, 15, 5);
|
ctx.fillRect(-7, 4, 15, 5);
|
||||||
ctx.rotate(this.turret);
|
ctx.rotate(this.turret);
|
||||||
if (this.fire) {
|
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);
|
ctx.fillRect(0, -1, 45, 2);
|
||||||
this.fire -= 1;
|
this.fire -= 1;
|
||||||
} else {
|
} else {
|
||||||
|
@ -210,8 +210,8 @@ function start(game) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//loop_id = setInterval(update, 66);
|
loop_id = setInterval(update, 66);
|
||||||
loop_id = setInterval(update, 200);
|
//loop_id = setInterval(update, 400);
|
||||||
if (fps) {
|
if (fps) {
|
||||||
setInterval(update_fps, 1000);
|
setInterval(update_fps, 1000);
|
||||||
}
|
}
|
||||||
|
|
16
test-tanks.c
16
test-tanks.c
|
@ -7,8 +7,11 @@
|
||||||
void
|
void
|
||||||
test_run(struct tank *tank, void *unused)
|
test_run(struct tank *tank, void *unused)
|
||||||
{
|
{
|
||||||
tank_set_speed(tank, 61, 60);
|
tank_set_speed(tank, -60, -61);
|
||||||
tank_set_turret(tank, 0);
|
tank_set_turret(tank, tank->turret.desired + PI/15);
|
||||||
|
if (tank->sensors[0].triggered) {
|
||||||
|
tank_fire(tank);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -25,7 +28,7 @@ main(int argc, char *argv[])
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
game.size[0] = 600;
|
game.size[0] = 600;
|
||||||
game.size[1] = 600;
|
game.size[1] = 200;
|
||||||
|
|
||||||
printf("[\n");
|
printf("[\n");
|
||||||
printf("[%d, %d, %d],\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].range = 100;
|
||||||
mytanks[i].sensors[4].turret = 1;
|
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) {
|
for (j = 0; j < TANK_MAX_SENSORS; j += 1) {
|
||||||
struct sensor *s = &(mytanks[i].sensors[j]);
|
struct sensor *s = &(mytanks[i].sensors[j]);
|
||||||
|
|
||||||
|
@ -87,7 +95,7 @@ main(int argc, char *argv[])
|
||||||
printf("// Rounds\n");
|
printf("// Rounds\n");
|
||||||
printf("[\n");
|
printf("[\n");
|
||||||
|
|
||||||
for (i = 0; i < 200; i += 1) {
|
for (i = 0; i < 100; i += 1) {
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
tanks_run_turn(&game, mytanks, NTANKS);
|
tanks_run_turn(&game, mytanks, NTANKS);
|
||||||
|
|
Loading…
Reference in New Issue