Source

vectorscope / spacerocks.c

/** \file
 * "Space Rocks" game.
 *
 * Might be similar to another game you've played.
 */
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include "sin_table.h"

#define rand() lrand48()

typedef struct
{
	int16_t x;
	int16_t y;
	int16_t vx;
	int16_t vy;
} point_t;

typedef struct
{
	point_t p;
	int16_t ax;
	int16_t ay;
	uint16_t fuel;
	uint8_t shots;
	uint8_t angle;
	uint8_t dead;
} ship_t;

typedef struct
{
	point_t p;
	uint8_t age;
} bullet_t;

typedef struct
{
	point_t p;
	uint8_t size;
} rock_t;


#define NUM_ROCKS	32
#define NUM_BULLETS	4

typedef struct
{
	ship_t s;
	bullet_t b[NUM_BULLETS];
	rock_t r[NUM_ROCKS];
} game_t;


static void
point_update(
	point_t * const p
)
{
	p->x += p->vx;
	p->y += p->vy;
}


static int
collide(
	const point_t * const p,
	const point_t * const q,
	const uint8_t radius
)
{
	int16_t dx = p->x - q->x;
	int16_t dy = p->y - q->y;

	if (-radius < dx && dx < radius
	&&  -radius < dy && dy < radius)
		return 1;

	// Not in the bounding box.
	return 0;
}


static void
ship_update_angle(
	ship_t * const s,
	int8_t rot
)
{
	if (rot == 0)
		return;

	s->angle += rot;
	s->ax = sin_lookup(s->angle);
	s->ay = cos_lookup(s->angle);
}


static void
ship_update_thrust(
	ship_t * const s,
	uint8_t thrust
)
{
	if (thrust == 0 || s->fuel == 0)
		return;

	if (s->fuel < thrust)
		thrust = s->fuel;

	s->fuel -= thrust;
	s->p.vx += (s->ax * thrust) / 128;
	s->p.vy += (s->ay * thrust) / 128;
}


static void
ship_fire(
	ship_t * const s,
	bullet_t * const b
)
{
	if (s->shots == 0)
		return;

#define BULLET_RANGE 255
#define BULLET_VEL 16

	b->age = BULLET_RANGE;
	b->p.x = s->p.x;
	b->p.y = s->p.y;
	b->p.vx = s->ax * BULLET_VEL + s->p.vx; // in the direction of the ship
	b->p.vy = s->ay * BULLET_VEL + s->p.vy; // in the direction of the ship

	s->shots--;
}


static void
ship_update(
	ship_t * const s,
	int8_t rot,
	uint8_t thrust
)
{
	ship_update_angle(s, rot);
	ship_update_thrust(s, thrust);

	// Update our position before we fire the gun
	point_update(&s->p);
}


static void
rocks_update(
	ship_t * const s,
	bullet_t * const bullets,
	rock_t * const rocks
)
{
	for (uint8_t i = 0 ; i < NUM_ROCKS ; i++)
	{
		rock_t * const r = &rocks[i];
		if (r->size == 0)
			break;

		point_update(&r->p);
		uint8_t rock_dead = 0;

		// check for bullet collision
		for (uint8_t j = 0 ; j < NUM_BULLETS ; j++)
		{
			bullet_t * const b = &bullets[i];
			if (b->age == 0)
				continue;
			if (collide(&r->p, &b->p, r->size))
			{
				r->size = 0;
				rock_dead = 1;
				// \todo split into mulitple rocks
				break;
			}
		}

		// check for ship collision if this rock wasn't destroyed
		if (!rock_dead && collide(&r->p, &s->p, r->size))
			s->dead = 1;
	}
}


static void
bullets_update(
	ship_t * const s,
	bullet_t * const bullets,
	uint8_t fire
)
{
	for (uint8_t i = 0 ; i < NUM_BULLETS ; i++)
	{
		bullet_t * const b = &bullets[i];
		if (b->age != 0)
			point_update(&b->p);
		else
		if (fire)
		{
			// We can try to fire this one
			ship_fire(s, b);
			fire = 0;
		}
	}
}



static void
ship_init(
	ship_t * const s
)
{
	s->p.x = 0;
	s->p.y = 0;
	s->p.vx = 0;
	s->p.vy = 0;
	s->angle = 0;
	s->ax = sin_lookup(s->angle);
	s->ay = cos_lookup(s->angle);
}


static void
bullets_init(
	bullet_t * const bullets
)
{
	for (uint8_t i = 0 ; i < NUM_BULLETS ; i++)
	{
		bullet_t * const b = &bullets[i];
		b->age = 0;
	}
}


static void
rocks_init(
	rock_t * const rocks,
	uint8_t num
)
{
	for (uint8_t i = 0 ; i < NUM_ROCKS ; i++)
	{
		rock_t * const r = &rocks[i];
		if (i > num)
		{
			// empty slot
			r->size = 0;
			continue;
		}

		r->size = 64;

#define MIN_RADIUS 1024

		// Make sure that there is space around the center
		int16_t x = rand();
		int16_t y = rand();
		if (0 <= x)
			x += MIN_RADIUS;
		else
			x -= MIN_RADIUS;
		if (0 <= y)
			y += MIN_RADIUS;
		else
			y -= MIN_RADIUS;
			
		r->p.x = x;
		r->p.y = y;
		r->p.vx = rand() % 64;
		r->p.vy = rand() % 64;
	}
}


static void
game_init(
	game_t * const g
)
{
	ship_init(&g->s);
	bullets_init(g->b);
	rocks_init(g->r, 8);
}


static void
game_update(
	game_t * const g,
	int8_t rot,
	uint8_t thrust,
	uint8_t fire
)
{
	ship_update(&g->s, rot, thrust);
	bullets_update(&g->s, g->b, fire);
	rocks_update(&g->s, g->b, g->r);
	if (&g->s.dead)
		game_init(g);
}


int main(void)
{
	game_t g;

	game_init(&g);

	while (1)
	{
		printf("---\nS: %+6d,%+6d %+6d,%+6d\n", g.s.p.x, g.s.p.y, g.s.p.vx, g.s.p.vy);

		for (uint8_t i = 0 ; i < NUM_ROCKS ; i++)
		{
			const rock_t * const r = &g.r[i];
			if (r->size == 0)
				continue;
			printf("%d: %+6d,%+6d %+6d,%+6d\n", i, g.s.p.x, g.s.p.y, g.s.p.vx, g.s.p.vy);
		}

		game_update(&g, 0, 0, 0);
	}
}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.