shithub: battleship

ref: 3d638eb8e9f753d7766cba6efb17320597b3394b
dir: /andy.c/

View raw version
#include <u.h>
#include <libc.h>
#include <thread.h>
#include <draw.h>
#include <mouse.h>
#include <keyboard.h>
#include <geometry.h>
#include "dat.h"
#include "fns.h"

/* Nexus-9 technology from The Rosen Association */

static char *nametab[] = {
	"hannibal",
	"luba",
	"roy",
	"irmgard",
	"buster",
	"rachael",
	"phil",
	"pris",
	"polokov",
	"zhora",
	"kowalski",
	"luv",
	"sapper",
	"freysa",
	"mariette",
};
Point2 nwes[] = {
	{0,-1,0},
{-1,0,0},	{1,0,0},
	{0,1,0},
};

static char *
getaname(void)
{
	return nametab[getrand(nelem(nametab))];
}

static Point2
getnextfreecell(Map *m)
{
	Point2 p;
	int i, j;

	for(i = 0; i < MAPW; i++)
		for(j = 0; j < MAPH; j++)
			if(gettile(m, p = Pt2(i,j,1)) == Twater)
				return p;
	/*
	 * XXX getting here would mean that we shot every single cell and
	 * the game's still going, so something went wrong.
	 */
	abort();
	return Pt2(0,0,0);
}

static void
turnaround(Andy *a)
{
	if(--a->passes > 0){
		a->passdir = mulpt2(a->passdir, -1);
		a->lastshot = a->firsthit;
	}else
		a->disengage(a);
}

static int
between(double n, double min, double max)
{
	return n >= min && n < max;
}

static int
lineXline(Point2 min0, Point2 max0, Point2 min1, Point2 max1)
{
	double a₁, b₁;
	double a₂, b₂;
	double det;

	a₁ = max0.y - min0.y;
	b₁ = min0.x - max0.x;

	a₂ = max1.y - min1.y;
	b₂ = min1.x - max1.x;

	det = a₁*b₂ - a₂*b₁;
	if(det == 0){
		/* do they overlap? */
		if((min0.x == min1.x && (between(min0.y, min1.y, max1.y) || between(max0.y, min1.y, max1.y))) ||
			(min0.y == min1.y && (between(min0.x, min1.x, max1.x) || between(max0.x, min1.x, max1.x))))
			return 1;
		return 0;
	}
	return 1;
}

static void
andy_layout(Andy *a, Msg *m)
{
	Point2 cells[NSHIPS], sv[NSHIPS];
	char buf[NSHIPS*(1+3+1)+1];
	int i, j, o[NSHIPS], n;

	for(i = 0; i < NSHIPS; i++){
Retry:
		cells[i] = Pt2(getrand(MAPW-shiplen(i)), getrand(MAPH-shiplen(i)), 1);
		o[i] = getrand(1)? OH: OV;
		sv[i] = o[i] == OH? Vec2(1,0): Vec2(0,1);
		for(j = 0; j < i; j++)
			if(lineXline(cells[i], addpt2(cells[i], mulpt2(sv[i], shiplen(i))),
					cells[j], addpt2(cells[j], mulpt2(sv[j], shiplen(j)))))
				goto Retry;
	}

	n = 0;
	for(i = 0; i < nelem(cells); i++){
		assert(sizeof buf - n > 1+3+1);
		if(i != 0)
			buf[n++] = ',';
		n += cell2coords(buf+n, sizeof buf - n, cells[i]);
		buf[n++] = o[i] == OH? 'h': 'v';
	}
	buf[n] = 0;

	m->body = smprint("layout %s", buf);
	sendp(a->ego->battle->data, m);
}

static void
andy_shoot(Andy *a, Msg *m)
{
	Point2 cell;
	char buf[3+1];
	int tries;

	tries = 0;

Retry:
	switch(a->state){
	case ASearching:
		do
			cell = ++tries > 100?
				getnextfreecell(a): Pt2(getrand(MAPW), getrand(MAPH), 1);
		while(gettile(a, cell) != Twater);
		break;
	case ACalibrating:
		do
			cell = addpt2(a->firsthit, nwes[--a->ntries&3]);
		while((gettile(a, cell) != Twater || isoob(cell)) && a->ntries > 1);
		if(gettile(a, cell) != Twater || isoob(cell)){
			a->disengage(a);
			goto Retry;
		}
		break;
	case ABombing:
		cell = addpt2(a->lastshot, a->passdir);
		if(gettile(a, cell) != Twater || isoob(cell)){
			turnaround(a);
			goto Retry;
		}
		break;
	}
	cell2coords(buf, sizeof buf, cell);
	m->body = smprint("shoot %s", buf);
	sendp(a->ego->battle->data, m);
	a->lastshot = cell;
}

static void
andy_engage(Andy *a)
{
	a->firsthit = a->lastshot;
	a->state = ACalibrating;
	a->ntries = nelem(nwes);
	a->passes = 2;
}

static void
andy_disengage(Andy *a)
{
	a->state = ASearching;
}

static void
andy_registerhit(Andy *a)
{
	settile(a, a->lastshot, Thit);
	if(a->state == ASearching)
		a->engage(a);
	else if(a->state == ACalibrating){
		a->passdir = subpt2(a->lastshot, a->firsthit);
		a->state = ABombing;
	}
}

static void
andy_registermiss(Andy *a)
{
	settile(a, a->lastshot, Tmiss);
	if(a->state == ACalibrating && a->ntries < 1)
		a->disengage(a);
	else if(a->state == ABombing)
		turnaround(a);
}

Andy *
newandy(Player *p)
{
	Andy *a;

	a = emalloc(sizeof *a);
	memset(a->map, Twater, MAPW*MAPH);
	a->ego = p;
	snprint(p->name, sizeof p->name, "%s", getaname());
	a->state = ASearching;
	a->layout = andy_layout;
	a->shoot = andy_shoot;
	a->engage = andy_engage;
	a->disengage = andy_disengage;
	a->registerhit = andy_registerhit;
	a->registermiss = andy_registermiss;
	return a;
}

void
freeandy(Andy *a)
{
	free(a);
}