Browsing qwx/city at commit HEAD: /sim.c

view raw
#include <u.h>
#include <libc.h>
#include <thread.h>
#include "dat.h"
#include "fns.h"

extern QLock drwlock;

int paused;
vlong clock;

static int tdiv;

static Tile **objs, **objhead;
static int maxobj;

enum{
	UpkeepΔt = 150,
};
static int stock[Gtot], rstock[Rtot];

int
max(int a, int b)
{
	return a > b ? a : b;
}

int
min(int a, int b)
{
	return a < b ? a : b;
}

static void
spawn(Tile *m, int n)
{
	Tile **o;

	if(objhead - objs >= maxobj)
		sysfatal("spawn: out of bounds");
	m->t = terrains + Tplain;
	m->b = buildings + n;
	for(o=objhead-1; o>=objs; o--)
		if((*o)->b - buildings <= n)
			break;
	/* push in front of lower priority object */
	if(++o < objhead)
		memmove(o+1, o, (objhead - o) * sizeof *o);
	*o = m;
	objhead++;
}

static void
starve(Tile *o)
{
	o->state = Svoid;
}

static void
upkeep(void)
{
	int n, r, g, upkeep[nelem(resources)];
	Tile *o, **ol;

	if(clock % UpkeepΔt != 0 || clock == 0)
		return;
	for(ol=objhead-1; ol>=objs; ol--){
		o = *ol;
		if(o->state <= Svoid)
			continue;
		if(o->b == nil)
			sysfatal("empty active tile");
		memcpy(upkeep, o->b->upkeep, sizeof upkeep);
		for(r=0; r<nelem(resources); r++)
			if(upkeep[r] > rstock[r]){
				starve(o);
				goto bail;
			}
		for(g=0; g<nelem(goods); g++){
			r = goods[g].resource;
			if(upkeep[r] <= 0)
				continue;
			n = min(stock[g], upkeep[r]);
			stock[g] -= n;
			rstock[r] -= n;
			upkeep[r] -= n;
		}
		for(r=0; r<nelem(upkeep); r++)
			if(upkeep[r] > 0)
				sysfatal("upkeep: goods/resources phase error");
	bail:;
	}
}

static int
trypickup(Tile *o)
{
	int g;

	if(o->pickupΔt == 0 || o->clock < o->pickupΔt)
		return 0;
	for(g=0; g<nelem(goods); g++)
		stock[g] += o->prodstock[g];
	o->pickupΔt = 0;
	return 1;
}

static int
trysupply(Tile *o)
{
	int g;
	for(g=0; g<nelem(goods); g++)
		if(o->b->prodcost[g] > stock[g])
			return 0;
	for(g=0; g<nelem(goods); g++)
		if(o->b->prodcost[g] > 0)
			stock[g] -= o->b->prodcost[g];
	o->supplyΔt = o->clock + o->distance;
	return 1;
}

static void
updateobj(void)
{
	int g;
	Tile *o, **ol;

	for(ol=objhead-1; ol>=objs; ol--){
		o = *ol;
		o->clock++;
		switch(o->state){
		case Swaitbuild:
			if(o->clock >= o->distance){
				o->clock = 0;
				o->state = Sbuild;
			}
			break;
		case Sbuild:
			if(o->clock >= o->b->buildtime){
				o->clock = 0;
				o->state = o->b->prodtime > 0 ? Sstarved : Svoid;
			}
			break;
		case Sstarved:
			trypickup(o);
			if(trysupply(o))
				o->state = Swaitsupply;
			break;
		case Swaitsupply:
			trypickup(o);
			if(o->clock >= o->supplyΔt){
				o->gotsupply = 0;
				o->supplyΔt = 0;
				o->prodΔt = o->clock + o->b->prodtime;
				o->state = Sproduce;
			}
			break;
		case Sproduce:
			if(!o->gotsupply){
				if(o->supplyΔt == 0){
					if(!trysupply(o))
						return;
					for(g=0; g<nelem(goods); g++)
						if(o->b->prodcost[g] > stock[g])
							break;
				}else if(o->clock >= o->supplyΔt){
					o->gotsupply = 1;
					o->supplyΔt = 0;
				}
			}
			trypickup(o);
			if(o->clock >= o->prodΔt){
				o->pickupΔt = o->clock + o->distance * 2;
				if(!o->gotsupply){
					if(o->supplyΔt == 0)
						o->state = Sstarved;
					else
						o->state = Swaitsupply;
				}else{
					o->prodΔt += o->b->prodtime;
					o->gotsupply = 0;
				}
			}
			break;
		default: break;
		}
	}
}

static void
step(void)
{
	if(paused)
		return;
	qlock(&drwlock);
	upkeep();
	updateobj();
	qunlock(&drwlock);
}

static void
simproc(void *)
{
	int Δtc;
	vlong t, t0, dt;

	tdiv = Te9 / SimHz;
	t0 = nsec();
	for(;;){
		step();
		clock++;
		t = nsec();
		Δtc = (t - t0) / tdiv;
		if(Δtc <= 0)
			Δtc = 1;
		t0 += Δtc * tdiv;
		dt = (t0 - t) / Te6;
		if(dt > 0)
			sleep(dt);
	}
}

void
startsim(void)
{
	if(proccreate(simproc, nil, 8192) < 0)
		sysfatal("init: %r");
}

static void
calcdists(int n)
{
	int x, y, x´, y´;
	Tile *o;

	x = n % mapwidth;
	y = n / mapheight;
	for(o=map, x´=0, y´=0; o<map+mapwidth*mapheight; o++, x´++){
		o->distance = mhdist(x, y, x´, y´);
		if(x´ == mapwidth){
			x´ = 0;
			y´++;
		}
	}
}

void
init(void)
{
	int i, n;

	srand(time(nil));
	initmap();
	maxobj = mapwidth * mapheight;
	objs = emalloc(maxobj * sizeof *objs);
	objhead = objs;
	n = nrand(maxobj);
	spawn(map + n, Btownhall);
	calcdists(n);
	for(i=0; i<nelem(initialstock); i++){
		stock[i] = initialstock[i];
		rstock[goods[i].resource] += stock[i];
	}
}