From 4c3e1af0175ba828c7e253f457b174836dc1c416 Mon Sep 17 00:00:00 2001 From: Neale Pickett Date: Fri, 25 Jun 2010 16:15:09 -0600 Subject: [PATCH] First try with binary scaling --- Makefile | 6 ++ brad.c | 159 +++++++++++++++++++++++++++++++++++++++++++++++++++ brad.h | 50 ++++++++++++++++ brad.py | 25 ++++++++ ctanks.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++++ ctanks.h | 89 ++++++++++++++++++++++++++++ test-tanks.c | 25 ++++++++ 7 files changed, 508 insertions(+) create mode 100644 Makefile create mode 100644 brad.c create mode 100644 brad.h create mode 100755 brad.py create mode 100644 ctanks.c create mode 100644 ctanks.h create mode 100644 test-tanks.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..47f2beb --- /dev/null +++ b/Makefile @@ -0,0 +1,6 @@ +all: test + +test: test-tanks + ./test-tanks + +test-tanks: test-tanks.o ctanks.o diff --git a/brad.c b/brad.c new file mode 100644 index 0000000..e498476 --- /dev/null +++ b/brad.c @@ -0,0 +1,159 @@ +#include +#include "brad.h" + +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +/* Approximate vector length + * + * http://www.flipcode.com/archives/Fast_Approximate_Distance_Functions.shtml + * + * (1007/1024) * 256 == 251.75 + * (441/1024) * 256 == 110.25 + */ +bs_t +bs_approx_dist(bs_t dx, bs_t dy) +{ + bs_t x = abs(dx); + bs_t y = abs(dy); + + return (252 * max(x, y)) + (110 * min(x, y)); +} + + +static bs_t bs_cos_table[] = { + 256, 256, 256, 255, 255, 254, 253, 252, + 251, 250, 248, 247, 245, 243, 241, 239, + 237, 234, 231, 229, 226, 223, 220, 216, + 213, 209, 206, 202, 198, 194, 190, 185, + 181, 177, 172, 167, 162, 157, 152, 147, + 142, 137, 132, 126, 121, 115, 109, 104, + 98, 92, 86, 80, 74, 68, 62, 56, + 50, 44, 38, 31, 25, 19, 13, 6, + 0 +}; + +bs_t +bs_cos(brad_t angle) +{ + brad_t cos; + bs_t a; + + a = abs(angle) % 128; + if (a > 64) { + a = 128 - a; + } + cos = bs_cos_table[a]; + a = abs(angle) % 256; + if ((a > 64) && (a < 192)) { + cos = -cos; + } + return cos; +} + +bs_t +bs_sin(brad_t angle) +{ + return bs_cos(64 - angle); +} + +bs_t +bs_tan(brad_t angle) +{ + return bs_sin(angle) / bs_cos(angle); +} + + +static brad_t bs_asin_table[] = { + 0, 0, 1, 1, 1, 2, 2, 2, + 3, 3, 3, 4, 4, 4, 4, 5, + 5, 5, 6, 6, 6, 7, 7, 7, + 8, 8, 8, 9, 9, 9, 10, 10, + 10, 11, 11, 11, 11, 12, 12, 12, + 13, 13, 13, 14, 14, 14, 15, 15, + 15, 16, 16, 16, 17, 17, 17, 18, + 18, 18, 19, 19, 19, 20, 20, 20, + 21, 21, 21, 22, 22, 22, 23, 23, + 23, 24, 24, 24, 25, 25, 25, 26, + 26, 26, 27, 27, 27, 28, 28, 28, + 29, 29, 29, 30, 30, 30, 31, 31, + 31, 32, 32, 32, 33, 33, 33, 34, + 34, 34, 35, 35, 35, 36, 36, 37, + 37, 37, 38, 38, 38, 39, 39, 39, + 40, 40, 40, 41, 41, 42, 42, 42, + 43, 43, 43, 44, 44, 45, 45, 45, + 46, 46, 46, 47, 47, 48, 48, 48, + 49, 49, 49, 50, 50, 51, 51, 51, + 52, 52, 53, 53, 53, 54, 54, 55, + 55, 55, 56, 56, 57, 57, 57, 58, + 58, 59, 59, 60, 60, 60, 61, 61, + 62, 62, 63, 63, 64, 64, 64, 65, + 65, 66, 66, 67, 67, 68, 68, 69, + 69, 70, 70, 71, 71, 72, 72, 73, + 73, 74, 74, 75, 75, 76, 76, 77, + 77, 78, 78, 79, 80, 80, 81, 81, + 82, 82, 83, 84, 84, 85, 86, 86, + 87, 87, 88, 89, 90, 90, 91, 92, + 92, 93, 94, 95, 96, 96, 97, 98, + 99, 100, 101, 102, 103, 104, 105, 106, + 108, 109, 110, 112, 114, 116, 118, 121, + 128 +}; + +brad_t +bs_asin(bs_t n) +{ + bs_t a = abs(n); + brad_t ret; + + if (a > 256) { + return 0; + } + ret = bs_asin_table[a]; + if (n < 0) { + ret = -ret; + } + return ret; +} + +brad_t +bs_acos(bs_t n) +{ + return 128 - bs_asin(n); +} + +brad_t +bs_atan2(bs_t y, bs_t x) +{ + bs_t r = bs_approx_dist(x, y); + brad_t t; + + t = bs_acos(x / r); + if (y < 0) { + t = 256 - t; + } + return t; +} + + +#include +#include +#define PI 3.14159265358979323846 +int +main() +{ + int i, j; + float f; + int t; + + for (i = 0; i < 257; i += 1) { + for (j = 0; j < 257; j += 1) { + f = sqrt(i*i + j*j); + t = bs_approx_dist(i, j); + printf("%d %d %d\n", i, (int)round(f*256)/256, t/256); + } + } + return 0; +} diff --git a/brad.h b/brad.h new file mode 100644 index 0000000..50f9704 --- /dev/null +++ b/brad.h @@ -0,0 +1,50 @@ +#ifndef __BRAD_H__ +#define __BRAD_H__ + +/** Binary scaling library + * + * There are B8 binary scaled ints. That means the 8 lowest bits are + * the fractional part. To convert to a float you'd just divide by 256. + * + * The trig functions use brads (Binary Radians). There are 128 brads + * in 2pi radians. One brad is about 1.4 degrees. Using brads makes + * trig really fast on a binary computer, and reallier faster on a + * binary computer without an FPU. + * + * You must be careful not to overflow your bs_t. For instance, I wrote + * this library for a game with a 2^9-pixel-wide playfield. That's + * 2^17 B8, but I also needed to do distance calculations, which + * requires squaring things. 2^34 is obviously too big to represent in + * 32 bits. + * + * C's type system leaves a lot to be desired when it comes to making + * sure you're not mixing these things up with normal ints. Be careful! + */ + +/* Just to make it clear what scale these functions are dealing with */ +typedef struct { + int v; +} bs_t; +typedef int brad_t; + +/* If you change this, you must also change the lookup tables in + * brad.c. Don't change this. */ +#define BINARY_SCALE 8 +#define BS_DENOMINATOR 1 << BINARY_SCALE + +#define bs_to_int(n) ((n) >> BINARY_SCALE) +#define bs_of_int(i) ((i) << BINARY_SCALE) + +bs_t bs_cos(brad_t angle); +bs_t bs_sin(brad_t angle); +bs_t bs_tan(brad_t angle); + +brad_t bs_acos(bs_t angle); +brad_t bs_asin(bs_t angle); + +bs_t bs_mul(bs_t a, bs_t b); +bs_t bs_div(bs_t a, bs_t b); + +bs_t bs_approx_dist(bs_t dx, bs_t dy); + +#endif /* __BRAD_H__ */ diff --git a/brad.py b/brad.py new file mode 100755 index 0000000..8b1a999 --- /dev/null +++ b/brad.py @@ -0,0 +1,25 @@ +#! /usr/bin/python + +# Binary radians with B12 binary scaling (multiply floats by 256) +# pi radians = 128 brads + +import math + +brad2rad = math.pi/128 + +print "static bs_t bs_cos_table[] = {" +for i in range(129): + r = (i * math.pi) / 128.0 + cos = math.sin(r) + bcos = int(round(cos * 256)) + print ("%3d," % bcos), +print "}" + +print "static brad_t bs_asin_table[] = {" +for i in range(257): + f = i / 256.0 + acos = math.asin(f) + bacos = int(round(256 * acos / math.pi)) + print ("%3d," % bacos), +print "}" + diff --git a/ctanks.c b/ctanks.c new file mode 100644 index 0000000..3930072 --- /dev/null +++ b/ctanks.c @@ -0,0 +1,154 @@ +#include +#include +#include "brad.h" +#include "ctanks.h" + +void +tank_init(struct tank *tank, tank_run_func *run, void *udata) +{ + memset(tank, 0, sizeof(*tank)); + tank->run = run; + tank->udata = udata; +} + +int +tank_fire_ready(struct tank *tank) +{ + return (! tank->turret.recharge); +} + +void +tank_fire(struct tank *tank) +{ + tank->turret.firing = tank_fire_ready(tank); +} + +void +tank_set_speed(struct tank *tank, bs_t left, bs_t right) +{ + tank->speed.desired[0] = left; + tank->speed.desired[1] = right; +} + +brad_t +tank_get_turret(struct tank *tank) +{ + return tank->turret.current; +} + +void +tank_set_turret(struct tank *tank, brad_t angle) +{ + tank->turret.desired = angle; +} + +int +tank_get_sensor(struct tank *tank, int sensor_num) +{ + if ((sensor < 0) || (sensor > MAX_SENSORS)) { + return 0; + } + return tank->sensor[sensor_num].triggered; +} + +void +tank_set_led(struct tank *tank, int active) +{ + tank->led = active; +} + +/** Return distance^2 between tanks a and b. + * + * Comparing this against sensor_range^2 will tell you whether the tanks + * are within sensor range of one another. Similarly, comparing it + * against (2*tank_radius)^2 will tell you if they've collided. + * + */ +bs_t +tank_dist2(struct tanks_game *game, struct tank *a, struct tank *b) +{ + bs_t d[2]; + int i; + + for (i = 0; i < 2; i += 1) { + d[i] = abs(a.position[i] - b.position[i]); + d[i] = min(game.size[i] - dx[i], dx[i]); + } + return ((d[0] * d[0]) + (d[1] * d[1])); +} + +void +do_shit_with(struct tanks_game *game, + struct tank *this, + struct tank *that) +{ + bs_t vector[2]; + int dist2; /* Integer to avoid overflow! */ + bs_t xpos; /* Translated position */ + int i; + + /* Don't bother if one is dead */ + 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) { + bs_t halfsize = game->size[i] / 2; + + /* XXX: is there a more elegant way to do this? */ + vector[i] = that->position[i] - this->position[i]; + if (2*vector[i] > game->size[i]) { + vector[i] -= game->size[i]; + } else if (2*vector[i] < game->size[i]) { + vector[i] += game->size[i]; + } + } + /* Compute distance^2 for range comparisons */ + dist2 = ((bs_to_int(vector[0]) * bs_to_int(vector[0])) + + (bs_to_int(vector[1]) * bs_to_int(vector[1]))); + + /* If they're not within sensor range, there's nothing to do. */ + if (dist2 > TANK_SENSOR_ADJ2) { + return; + } + + /* Did they collide? Oh, goody! */ + if (dist2 < TANK_COLLISION_ADJ2) { + /* XXX: kill both tanks */ + return; + } + + /* Has anybody been shot? */ + + /* Translate other tank's position to make us the origin */ + for (i = 0; i < 2; i += 1) { + xpos[i] = that->position[i] - this->position[i]; + } + + /* Calculate sensors */ + for (i = 0; i < this->num_sensors; i += 1) { + brad_t theta; + + if (dist2 < this->sensor[i].range_adj2) { + continue; + } + + /* Translate other tank so that we're the origin */ + } +} + +void +tanks_run_turn(struct tanks_game *game, struct tank *tanks, int ntanks) +{ + int i, j; + + for (i = 0; i < ntanks; i += 1) { + for (j = i + 1; j < ntanks, j += 1) { + do_shit_with(game, &(tanks[i]), &(tanks[j])); + } + tanks_print_tank(game, &(tanks[i])); + tanks_move(game, &(tanks[i])); + } +} diff --git a/ctanks.h b/ctanks.h new file mode 100644 index 0000000..d62904c --- /dev/null +++ b/ctanks.h @@ -0,0 +1,89 @@ +#ifndef __CTANKS_H__ +#define __CTANKS_H__ + +#include "brad.h" + +/* Some useful constants */ +#define TANK_MAX_SENSORS 10 +#define TANK_RADIUS ((bs_t)1920) +#define TANK_SENSOR_RANGE ((int)100) + +/* (tank radius + tank radius)^2 */ +#define TANK_COLLISION_ADJ2 \ + bs_to_int((TANK_RADIUS + TANK_RADIUS) * (TANK_RADIUS + TANK_RADIUS)) + +/* (Sensor range + tank radius)^2 + * If the distance^2 to the center of a tank <= TANK_SENSOR_ADJ2, + * that tank is within sensor range. */ +#define TANK_SENSOR_ADJ2 ((int)11556) + +struct tanks_game { + bs_t size[2]; /* dimensions of playing field */ +}; + +struct tank; + +struct sensor { + brad_t angle; + brad_t width; + int range; + int range_adj2; /* (range + TANK_RADIUS)^2 */ + int turret; /* Mounted to turret? */ + int triggered; +}; + +typedef void tank_run_func(struct tank *, void *); + +struct tank { + bs_t position[2]; /* Current position on the board */ + brad_t angle; /* Current orientation */ + struct { + bs_t current[2]; /* Current tread speed */ + bs_t desired[2]; /* Desired tread speed */ + } speed; + struct { + brad_t current; /* Current turret angle */ + brad_t desired; /* Desired turret angle */ + int firing; /* True if firing this turn */ + int recharge; /* Turns until gun is recharged */ + } turret; + struct sensor sensor[MAX_SENSORS]; /* Sensor array */ + int num_sensors; /* Number of sensors */ + int led; /* State of the LED */ + struct tank *killer; /* Killer, or NULL if alive */ + char *cause_death; /* Cause of death */ + + tank_run_func *run; /* Function to run a tank */ + void *udata; /* Argument to pass to run */ +}; + +void tank_init(struct tank *tank, tank_run_func *run, void *udata); + +/* + * + * Tanks API for scripts + * + */ + +/** Has the turret recharged? */ +int tank_fire_ready(struct tank *tank); + +/** Fire! */ +void tank_fire(struct tank *tank); + +/** Set desired speed */ +void tank_set_speed(struct tank *tank, int left, int right); + +/** Get the current turret angle */ +int tank_get_turret(struct tank *tank); + +/** Set the desired turret angle */ +void tank_set_turret(struct tank *tank, int angle); + +/** Is a sensor active? */ +int tank_get_sensor(struct tank *tank, int sensor_num); + +/** Set the LED state */ +void tank_set_led(struct tank *tank, int active); + +#endif /* __CTANKS_H__ */ diff --git a/test-tanks.c b/test-tanks.c new file mode 100644 index 0000000..7babfd4 --- /dev/null +++ b/test-tanks.c @@ -0,0 +1,25 @@ +#include +#include +#include "ctanks.h" + +void +test_run(struct tank *tank, void *unused) +{ + tank_set_speed(tank, 30, 50); + tank_set_turret(tank, fmod(tank->turret.desired + 0.2, 2*PI)); +} + +int +main(int argc, char *argv[]) +{ + struct tank mytank; + int i; + + tank_init(&mytank, test_run, NULL); + printf("hi\n"); + for (i = 0; i < 4; i += 1) { + printf("%f\n", mytank.turret.desired); + mytank.run(&mytank, mytank.udata); + } + return 0; +}