Plan 9 from Bell Labs’s /usr/web/sources/contrib/axel/rushhour/src/trafficfs.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include <u.h>
#include <libc.h>
#include <ctype.h>
#include <bio.h>
#include <draw.h>
#include <auth.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>

#include "trafficfs.h"

/*
 * we generate a three-level directory structure:
 *  	root/
 * 		trafficfs		(our own root)
 * 			ctl			(toc of generated files; generate files)
 * 			toc			(table of contents of nr files)
 * 			21/			(group of levels with same minimal solution move count)
 * 			0		(first level in this group)
 *				1		(second level in this group)
 *				...
 *			22/
 * 			0		(first level in this group)
 *				1		(second level in this group)
 *				...
 *			...
 * 			foo			(generated file)
 * 			bar			(generated file)
 */

typedef struct Aux Aux;
struct Aux {
	int doff;
	int gnr;
	int gidx;
	int lnr;
};

enum {
	Qroot = 0,
	Qdir,
	Qctl,
	Qtoc,
	Qgen,
	Qgrp,
	Qlvl,
};

#define PATH(qid,grp,lvl) (((lvl)<<16)|(((grp)&0xFF)<<8)|((qid)&0xFF))
#define QDIR(qid) ((int)((qid)&0xFF))
#define GRP(qid) (((qid)>>8) &0xFF)
#define LVL(qid) ((qid)>>16)

int debug;
ulong starttime;

/* representation of levels */
Directory *directory;

/* stuff used to on-the-fly generate the combined level files */
int *choicelist;		/* array from which we choose problems */
int maxchoicelist;	/* max nr of entries in choicelist (without subtracting seen levels) */
Seen *dirseen;		/* keeps track of levels that user has seen */
Generated *generated;	/* on-the-fly generated files of levels */
int ngenerated;		/* number of items in generated */


void printlevel(char *s, Level *l);
void chooseproblems(Generated *g);

void*
emalloc(ulong sz)
{
	void *v;

	v = malloc(sz);
	if(v == nil) {
		abort();
		sysfatal("malloc %lud fails\n", sz);
	}
	setmalloctag(v, getcallerpc(&sz));
	memset(v, 0, sz);
	return v;
}

void*
erealloc(void *p, ulong sz)
{
	void *v;

	v = realloc(p, sz);
	if(v == nil)
		sysfatal("realloc %lud fails\n", sz);
	setrealloctag(v, getcallerpc(&p));
	return v;
}

static Point
lin2grouplvl(int n)
{
	Group *gp;
	Level *lp;

	lp = directory->level + n;

	for (gp=directory->group; gp<directory->group+directory->ngroup; gp++) {
		if (lp >= gp->base && lp < gp->beyond)
			return Pt(gp->moves, lp-gp->base);
	}
	return Pt(0,0);
}

int
index(int n)
{
	if (n < directory->base)
		return -1;
	if (n > directory->beyond)
		return -1;
	return n - directory->base;
}

int
isseen(uchar *seen, int n)
{
	int i, r;

	i = n / 8;
	r = n % 8;
	return (seen[i] & 1<<r) == (1<<r);
}

void
setseen(uchar *seen, int n)
{
	int i, r;

	i = n / 8;
	r = n % 8;
	seen[i] |= 1<<r;
}

void
unsetseen(uchar *seen, int n)
{
	int i, r;

	i = n / 8;
	r = n % 8;
	seen[i] &= ~(1<<r);
if(debug) fprint(2, "unsetseen %d i=%d r=%d\n", n, i, r);
}

void
dirsetseen(Seen *s, int n)
{
	Point p;
	int i;

	p = lin2grouplvl(n);
	i = index(p.x);
if (debug)  fprint(2, "setseenlin %d -> %d (%d), %d\n", n, p.x, i, p.y);

	if (!isseen(s->seen, n)) {
		setseen(s->seen, n);
		s->group[i].nseen++;
	}
}

void
resetseen(Seen *s, int base, int beyond)
{
	int baselvl, beyondlvl, count, nextlvl, i;
	
	baselvl = directory->group[base - directory->base].base - directory->level;
	beyondlvl = directory->group[beyond - directory->base - 1].beyond - directory->level;
	if (debug) fprint(2, "resetseen base %d beyond %d baselvl %d beyondlvl %d\n", base, beyond, baselvl, beyondlvl);

	/* first bits in byte containing also bits for previous level */
	for(i=baselvl; i<beyondlvl && i%8 != 0; i++) {
		unsetseen(s->seen, i);
		if (debug) fprint(2, "resetseen %d beyondlvl=%d\n", i, beyondlvl);
	}
	nextlvl = i;
	count = beyondlvl - nextlvl;

	/* complete  bytes containing only bits for this level */
	if (count/8 > 0) {
		if (debug) fprint(2, "resetseen off %d count %d\n", nextlvl/8, count/8);
		memset(s->seen+(nextlvl/8), 0, count/8);
		nextlvl += (count/8)*8;
	}

	/* last  bits in byte containing also bits for next level */
	for(i=nextlvl; i<beyondlvl; i++) {
		unsetseen(s->seen, i);
		if (debug) fprint(2, "resetseen %d nextlvl=%d beyondlvl=%d\n", i, nextlvl, beyondlvl);
	}
		
	for (i=base - directory->base; i < beyond - directory->base; i++)
		s->group[i].nseen = 0;
}



static void
fsattach(Req *r)
{
	r->fid->qid = (Qid){Qroot, 0, QTDIR};
	r->ofcall.qid = r->fid->qid;
	r->fid->aux = emalloc(sizeof(Aux));
	respond(r, nil);
}

static char*
fsclone(Fid *old, Fid *new)
{
	Aux *na;

	na = emalloc(sizeof(Aux));
	*na = *((Aux*)old->aux);
	new->aux = na;
	return nil;
}

static char*
fswalk1(Fid *fid, char *name, Qid *qid)
{
	int i, n;
	char *p;
	Aux *a;

	a = fid->aux;
	switch(QDIR(fid->qid.path)) {
	case Qroot:
		if(strcmp(name, "..") == 0) {
			*qid = (Qid){Qroot, 0, QTDIR};
			return nil;
		}
		if(strcmp(name, "trafficfs") == 0) {
			*qid = (Qid){Qdir, 0, QTDIR};
			return nil;
		}
		return "file not found";

	case Qdir:
		if(strcmp(name, "..") == 0) {
			*qid = (Qid){Qroot, 0, QTDIR};
			return nil;
		}
		if(strcmp(name, "toc") == 0) {
			*qid = (Qid){Qtoc, 0, QTFILE};
			return nil;
		}
		if(strcmp(name, "ctl") == 0) {
			*qid = (Qid){Qctl, 0, QTFILE};
			return nil;
		}
		/* group directories */
		n = strtoul(name, &p, 0);
		if('0'<=name[0] && name[0]<='9' && *p=='\0'){
			for(i=0; i<directory->ngroup; i++)
				if(directory->group[i].moves == n) {
					*qid = (Qid){PATH(Qgrp,i,0), 0, QTDIR};
					a->gnr = n;
					a->gidx = i;
					return nil;
				}
		}
		/* generated files */
		for (i=0; i < ngenerated; i++) {
			if(strcmp(generated[i].name, name) ==0) {
				*qid = (Qid){PATH(Qgen,i,0), 0, QTFILE};
				return nil;
			}
		}
		return "file not found";

	case Qgrp:
		if(strcmp(name, "..") == 0) {
			*qid = (Qid){Qdir, 0, QTDIR};
			return nil;
		}
		/* level files */
		n = strtoul(name, &p, 0);
		if('0'<=name[0] && name[0]<='9' && *p=='\0'){
			if (n >= 0 && n < directory->group[a->gidx].count) {
				*qid = (Qid){PATH(Qlvl,a->gidx,n), 0, QTFILE};
				a->lnr = n;
				return nil;
			}
		}
		return "file not found";
	default:	/* bug: lib9p could handle this */
		return "walk in non-directory";
	}
}

static void
fsopen(Req *r)
{
	int omode;

	omode = r->ifcall.mode;
	if(omode == OREAD)
		respond(r, nil);
	else if ((omode&~OTRUNC) == OWRITE && QDIR(r->fid->qid.path) == Qctl)
		respond(r, nil);
	else
		respond(r, "permission denied");
}


int
fillstat(ulong qid, Dir *d)
{
	char buf[32];

	memset(d, 0, sizeof(Dir));
	d->uid = estrdup9p("rushhour");
	d->gid = estrdup9p("rushhour");
	d->muid = estrdup9p("");
	d->qid = (Qid){qid, 0, 0};
	d->atime = d->mtime = starttime;

	switch(QDIR(qid)){
	case Qroot:
		d->name = estrdup9p("/");
		d->qid.type = QTDIR;
		d->mode = DMDIR|0555;
		break;

	case Qdir:
		d->name = estrdup9p("trafficfs");
		d->qid.type = QTDIR;
		d->mode = DMDIR|0555;
		break;

	case Qctl:
		d->name = estrdup9p("ctl");
		d->qid.type = QTFILE;
		d->mode = 0666;
		break;

	case Qtoc:
		d->name = estrdup9p("toc");
		d->qid.type = QTFILE;
		d->mode = 0444;
		break;

	case Qgen:
		d->name = estrdup9p(generated[GRP(qid)].name);
		d->qid.type = QTFILE;
		d->mode = 0444;
		d->mtime = generated[GRP(qid)].mtime;
		d->length = generated[GRP(qid)].ndata;
		break;

	case Qgrp:
		sprint(buf, "%d", directory->group[GRP(qid)].moves);
		d->name = estrdup9p(buf);
		d->qid.type = QTDIR;
		d->mode = DMDIR|0555;
		break;

	case Qlvl:
		sprint(buf, "%uld", LVL(qid));
		d->name = estrdup9p(buf);
		d->qid.type = QTFILE;
		d->mode = 0444;
		break;

	default:
		/* should not happen */
		return 0;
		break;
	}
	return 1;
}

static void
fsstat(Req *r)
{
	fillstat((ulong)r->fid->qid.path, &r->d);
	respond(r, nil);
}

static int
genroot(int n, Dir *dir, void *aux)
{
	USED(aux);
	if (n==0)
		return fillstat(PATH(Qdir,0, 0), dir);
	else
		return -1;
}

static int
gendir(int n, Dir *dir, void *aux)
{
	USED(aux);
	if (n==0)
		return fillstat(PATH(Qctl,0, 0), dir);
	else if (n == 1)
		return fillstat(PATH(Qtoc,0, 0), dir);
	else if (n < ngenerated+1+1)
		return fillstat(PATH(Qgen,n-1-1, 0), dir);
	else  if (n < 1+1+ngenerated+directory->ngroup)
		return fillstat(PATH(Qgrp,n-1-1-ngenerated, 0), dir);
	else
		return -1;
}

static int
gengrp(int n, Dir *dir, void *aux)
{
	Aux *a;
	
	a = aux;
	if (n < directory->group[a->gidx].count)
		return fillstat(PATH(Qlvl,a->gidx,n), dir);
	else
		return -1;
}

static void
fsread(Req *r)
{
	int j, m, cum;
	Fid *fid;
	vlong offset;
	char s[1024];
	Aux *a;
	Generated *g;
	Level *lp;

	fid = r->fid;
	offset = r->ifcall.offset;
	a = fid->aux;

	switch(QDIR(fid->qid.path)) {
	case Qroot:
		dirread9p(r, genroot, a);
		respond(r, nil);
		break;

	case Qdir:
		dirread9p(r, gendir, a);
		respond(r, nil);
		break;

	case Qgrp:
		dirread9p(r, gengrp, a);
		respond(r, nil);
		break;

	case Qlvl:
		/* XXX make sure that s is big enough */
		sprint(s, "; moves %d level %d\n", a->gnr, a->lnr);
		sprint(s+strlen(s), ":%d/%d\n", a->gnr, a->lnr);
		lp = directory->group[a->gidx].base + a->lnr;
		printlevel(s, lp);
		readstr(r, s);
		respond(r, nil);
		break;

	case Qctl:
		/* XXX make sure that s is big enough */
		sprint(s,"");
		for(g=&generated[0]; g < &generated[ngenerated]; g++)
			sprint(s+strlen(s), "%s %d of %d (%d-%d) in [%d,%d) (requested %d in [%d,%d)) \n", g->name, g->count, g->avail, g->total, g->seen, g->min, g->max, g->req.count, g->req.min, g->req.max);
		s[strlen(s)] = '\0';
		readstr(r, s);
		respond(r, nil);
		break;

	case Qtoc:
		m = directory->ngroup;
		sprint(s,"");
		cum=0;
		for(j=0; j<m; j++) {
			cum += directory->group[j].count;
			sprint(s+strlen(s), "%d %d %d %d\n", directory->group[j].moves, directory->group[j].count, cum, dirseen->group[j].nseen);
		}
		s[strlen(s)] = '\0';
		readstr(r, s);
		respond(r, nil);
		break;

	case Qgen:
		g = &generated[GRP(fid->qid.path)];
		readbuf(r, g->data, g->ndata);
		respond(r, nil);
		break;
		
	default:
		/* should not happen */
		break;
	}
}

static int
parsectl(Req *r, char *data)
{
	int ok, i, j, n, w;
	char *lines[25];
	char *words[25];
	int min, max, count;
	char *name, *p;
	Generated *gp;
	GrpSeen *sgp;

	ok = 1;
	n = gettokens(data, lines, 25, "\r\n");
	for (j=0; j<n; j++) {
		w = gettokens(lines[j], words, 25, "\t ");
		if (w == 0)
			continue;
		if (strcmp(words[0], "gen") == 0 && w == 5) {
			name = words[1]; /* To Fix: check name does not contain '/' */
			if (strchr(name, '/') != nil) {
				respond(r, "name should not contain /");
				return 0;
			}
			for (p=name; *p != '\0' && isdigit(*p); p++)
				;
			if (p == '\0') {
				respond(r, "name should not contain only digits");
				return 0;
			}
			min = atoi(words[2]);
			max = atoi(words[3]);
			count = atoi(words[4]);
			if (min < 0 || max < 0 || count < 0){
				respond(r, "gen numbers should be positive");
				return 0;
			}
			for(gp=generated; gp<generated+ngenerated; gp++){
				if (strcmp(gp->name, name) == 0)
					break;
			}
			if (gp == generated+ngenerated) {
				generated = erealloc(generated, sizeof(Generated)*(ngenerated+1));
				ngenerated += 1;
				gp = generated+ngenerated-1;
				gp->name = estrdup9p(name);
				gp->data = nil;
				gp->ndata = 0;
			}
			gp->req.min = min;
			gp->req.max = max;
			gp->req.count = count;
			chooseproblems(gp);
		} else if (strcmp(words[0], "reset") == 0 && w == 2) {
			name = words[1];
			for(gp=generated; gp<generated+ngenerated; gp++){
				if (strcmp(gp->name, name) == 0) {
					resetseen(dirseen, gp->min, gp->max);
					break;
				}
			}
			if (gp == generated+ngenerated) {
				respond(r, "no such name");
				return 0;
			}
		} else if (strcmp(words[0], "reset") == 0 && w == 1) {
			memset(dirseen->seen, 0, (directory->nlevel/8)+1);
			for(sgp=dirseen->group; sgp<dirseen->group+dirseen->ngroup; sgp++)
				sgp->nseen = 0;

		} else {
			ok = 0;
			for(i=0; i<w; i++)
				fprint(2, "error:w[%d]=(%s) (strlen=%ld) %x\n", i, words[i], strlen(words[i]), *(words[i]));
		}
	}
	if (!ok)
		respond(r, "parse error");
	return ok;
}

static void
fswrite(Req *r)
{
	Fid *fid;
	long count;
	vlong offset;
	static char *data = nil;
	static int ndata = 0;

	fid = r->fid;
	count = r->ifcall.count;
	offset = r->ifcall.offset;

	switch(QDIR(fid->qid.path)) {
	case Qctl:
		/* if (offset == 0) { */
			if (ndata < count+1){
				data = erealloc(data, count+1);
				ndata = count+1;
			}
			memcpy(data, r->ifcall.data, count);
			data[count] = '\0';
			if (!parsectl(r, data))
				return;
	/*	} */
		r->ofcall.count = count;
		respond(r, nil);
		break;
	default:
		respond(r, "permission denied");
		break;
	}
}	

static void
fsdestroyfid(Fid *fid)
{
	Aux *a;

	a = fid->aux;
	if(a == nil)
		return;
	free(a);
}

Srv fs = {
.attach=	fsattach,
.destroyfid=	fsdestroyfid,
.clone=	fsclone,
.walk1=	fswalk1,
.open=	fsopen,
.read=	fsread,
.write=	fswrite,
.stat=	fsstat,
};


int
levelscount(int min, int max)
{
	int base, beyond, res;
	
	if (min == max)
		return 0;
	if (min < directory->base)
		min = directory->base;
	if (min > directory->beyond)
		min = directory->beyond;
	if (max < directory->base)
		max = directory->base;
	if (max > directory->beyond)
		max = directory->beyond;
	if (min == max)
		return 0;
	base =  min - directory->base;
	beyond =  max - directory->base;
	if (beyond < 1)
		return 0;
	res = directory->group[beyond-1].beyond - directory->group[base].base;
// fprint(2, "levelscount min %d max %d base %d beyond %d res %d\n", min, max, base, beyond, res);
	return res;
}

int
buildchoicelist(int cum, int min, int max, Generated *g, Seen*s)
{
	int i, j, lvl, off;
	int base, beyond;

	maxchoicelist = cum;
	free(choicelist);
	choicelist = emalloc(sizeof(int)*maxchoicelist);
	base =  min - directory->base;
	beyond =  max - directory->base;
	USED(beyond);
	off = directory->group[base].base - directory->level;
	lvl = off;
	j = 0;
	for(i=0; i<cum; i++) {
		if (!isseen(s->seen, lvl)) {
			choicelist[j] = lvl;
			j++;
		} else
			if (debug) fprint(2, "buildchoicelist(%d, %d, %d) seen %d\n", cum, min, max, lvl);
		lvl++;
	}
	g->avail = j;
	g->total = lvl - off;
	g->seen = g->total - g->avail ;

	return j;
}

int
intcmp(void *pa, void *pb)
{
	int *a, *b;

	a = pa;
	b = pb;
	return (*a - *b);
}

/*
 * we generate g->count random numbers for
 * the levels in [g->min, g->max)
 * we may generated duplicate random numbers.
 * we just throw out duplicates, but thus may
 * end up with < g->count numbers
 */
void
chooseproblems(Generated *g)
{
	int i, *levels, n, cum, prev, v, r, k;
	Point p;
	char *lbuf, *lp;
	uchar *seen;
	Level *lvlp;

	g->mtime =time(0);

	g->min = g->req.min;
	g->max = g->req.max;

	if (g->min < directory->base)
		g->min = directory->base;
	if (g->max > directory->beyond)
		g->max = directory->beyond;
	if (g->min > g->max)
		g->min = g->max;
	
	cum = levelscount(g->min, g->max);
	g->avail = buildchoicelist(cum, g->min, g->max, g, dirseen);

	if (g->avail > g->req.count)
		n = g->req.count;
	else
		n = g->avail;
	levels = emalloc(sizeof(int) * n);
	lbuf = emalloc(sizeof(char)*n*150); /* be sure 150 is enough */

if (debug) fprint(2, "levelscount %d %d -> %d ; n = %d\n", g->min, g->max, g->avail, n);

	if (g->avail > g->req.count) {
		seen = emalloc((g->avail/8)+1);
		for(i=0; i<n; i++) {
			r = nrand(g->avail);
			v = choicelist[r];
			k = 0;
			while (isseen(seen, r) && k < g->avail){
				r = nrand(g->avail);
				v = choicelist[r];
				k++;
			}
// if (k > 0)  fprint(2, "rand i=%d r=%d v=%d k=%d\n", i, r, v,k);
if (debug) fprint(2, "rand i=%d r=%d v=%d k=%d\n", i, r, v,k);
			setseen(seen, r);
			levels[i] = v;
			dirsetseen(dirseen, v);
		}
		free(seen);
		qsort(levels, n, sizeof(int), intcmp);
	} else {
		for(i=0; i<n; i++) {
			v = choicelist[i];
			levels[i] = v;
			dirsetseen(dirseen, v);
		}
	}
	lp = lbuf;
	prev = -1;
	g->count = 0;
	for(i=0; i<n; i++) {
		if (prev == levels[i])
			continue; /* throw out duplicate */
		prev = levels[i];
		g->count ++;
		p = lin2grouplvl(levels[i]);
		sprint(lp, "; moves %d level %d\n", p.x, p.y);
		sprint(lp+strlen(lp), ":%d/%d\n", p.x, p.y);
		lvlp = directory->level + levels[i];
		printlevel(lp, lvlp);
		lp += strlen(lp);
	}
	free(levels);
	free(g->data);
	g->data = lbuf;
	g->ndata = lp - lbuf;	
}

Seen*
seeninit(Directory *d)
{
	Seen* s;

	s = emalloc(sizeof(Seen));
	s->ngroup = d->ngroup;
	s->group = emalloc(sizeof(GrpSeen)*s->ngroup);
	s->seen = emalloc((d->nlevel/8)+1);
	return s;
}

int
readint(Biobuf *bp)
{
	int i, n;
	uchar b;

	n = 0;
	for (i=0; i < 4; i++) {
		Bread(bp, &b, 1);
		n+= (b << (i*8));
	}
	return n;
}

Directory*
readlevels(Biobuf *bp)
{
	Directory *d;
	Group *gp;
	Level *lp;
	int lvl, *off, *op, noff;
	char ignored[10];

	d = emalloc(sizeof(Directory));
	d->base = readint(bp) - 1; /* should be 21-1 */
	d->beyond = readint(bp); /* should be 52 */
	d->ngroup = d->beyond - d->base;
	d->group = emalloc(sizeof(Group)*d->ngroup);
	noff = d->ngroup + 1; /* +1 for offset to end of file  */
	off = emalloc(sizeof(int)*noff);
	lvl = d->base;
	op = off;
	for (gp=d->group; gp<d->group+d->ngroup; gp++) {
		gp->moves = lvl++;
		*(op++) = readint(bp);
	}
	*op = readint(bp); /* one more time for offset to end of file */
	d->nlevel = (off[d->ngroup] - off[0])/8; /* 8 bytes per level */
	d->level  = emalloc(sizeof(Level)*d->nlevel);
	op = off;
	lp = d->level;
	for (gp=d->group; gp<d->group+d->ngroup; gp++) {
		if (debug) fprint(2, "Group %d\n", gp->moves);
		gp->base = lp;
		gp->count = (*(op+1) - *op)/8;
		gp->beyond = lp + gp->count;
		for (; lp<gp->beyond; lp++) {
			Bread(bp, lp->row, 3);
			Bread(bp, ignored, 1);
			Bread(bp, lp->col, 3);
			Bread(bp, ignored, 1);
		}
		op++;
	}
	free(off);
	return d;
}

void
setboard(char **board, Point p, int l, int orient, int v)
{
	int i;
	Point dir[] = {
		[OCol] Pt(1,0),
		[ORow] Pt(0,1),
	};

	if (orient == OCol)
		p = Pt(p.y,p.x);
	for (i=0; i<l; i++) {
		board[1+p.x][1+p.y] = v;
		p = addpt(p, dir[orient]);
	}
}
void
decstrip(char **board, int i, int b, int orient, char *car, char *truck)
{
	switch(b){
	case 0:
		break;
	case 1:
	case 2:
	case 3:
	case 4:
	case 5:
		setboard(board, Pt(i, b-1), 2, orient, *car);
		(*car)++;
		break;
	case 6:
	case 7:
	case 8:
	case 9:
		setboard(board, Pt(i, b-6), 3, orient, *truck);
		(*truck)++;
		break;
	case 10:
	case 11:
	case 12:
		setboard(board, Pt(i, 0), 2, orient, *car);
		(*car)++;
		setboard(board, Pt(i, b-8), 2, orient, *car);
		(*car)++;
		break;
	case 13:
	case 14:
		setboard(board, Pt(i, 1), 2, orient, *car);
		(*car)++;
		setboard(board, Pt(i, b-10), 2, orient, *car);
		(*car)++;
		break;
	case 15:
		setboard(board, Pt(i, 2), 2, orient, *car);
		(*car)++;
		setboard(board, Pt(i, 4), 2, orient, *car);
		(*car)++;
		break;
	}
}

void
printlevel(char *s, Level *l)
{
	/*
	 * unfortunately, the following gives
	 * brd[rownr, colnr]
	 * which is opposite to convention (of brd[x][y])
	 */
	char *brd[] = {
		"########",
		"#......#",	
		"#......#",	
		"#......@",	
		"#......#",	
		"#......#",	
		"#......#",	
		"########",
	};
	char *board[8]; /* same as nr of rows of brd */
	char car = 'a';
	char truck = 'o';
	char us = 'x';
	int i;
	int bl[6];
	
	/*
	 * brd is only initialized once, not for for every call;
	 * if we change brd in-place we will be confronted
	 * with these changes in next call of this function
	 */
	for(i=0; i<8; i++)
		board[i] = estrdup9p(brd[i]);

	bl[0] = l->row[0] & 0x0F;
	bl[1] = (l->row[0] >> 4) & 0x0F;
	bl[2] = l->row[1] & 0x0F;
	bl[3] = (l->row[1] >> 4) & 0x0F;
	bl[4] = l->row[2] & 0x0F;
	bl[5] = (l->row[2] >> 4) & 0x0F;
	for (i=0; i< 6; i++)
		if (i == 2)
			decstrip(board, i, bl[i], ORow, &us, &truck);
		else
			decstrip(board, i, bl[i], ORow, &car, &truck);


	bl[0] = l->col[0] & 0x0F;
	bl[1] = (l->col[0] >> 4) & 0x0F;
	bl[2] = l->col[1] & 0x0F;
	bl[3] = (l->col[1] >> 4) & 0x0F;
	bl[4] = l->col[2] & 0x0F;
	bl[5] = (l->col[2] >> 4) & 0x0F;
	for (i=0; i< 6; i++)
		decstrip(board, i, bl[i], OCol, &car, &truck);

	for (i=0; i<8; i++)
		sprint(s+strlen(s), "%s\n", board[i]);
	sprint(s+strlen(s), "\n");
	for(i=0; i<8; i++)
		free(board[i]);
}

void
usage(void)
{
	fprint(2, "Usage: %s [-m mntpnt] [-s srv] [levelfile]\n", argv0);
	exits("usage");
}

void
main(int argc, char**argv)
{
	Biobuf *bp;
	char *mtpt, *srvpost, *srvname, *srvfile, *levelfile;

	mtpt = "/mnt";
	srvpost = nil;
	srvname = nil;

	levelfile = "/sys/games/lib/rushhour/levels/ttraffic.levels";
	debug = 0;
	ARGBEGIN{
	case 'd':
		debug++;
		break;
	case 'D':
		chatty9p++;
		break;
	case 'm':
		mtpt = EARGF(usage());
		break;
	case 's':
		srvpost = EARGF(usage());
		break;
	default:
		usage();
	}ARGEND
	
	if (mtpt != nil && *mtpt == 0)
		mtpt = nil;

	if (argc > 1)
		usage();
	if (argc == 1)
		levelfile = argv[0];

	starttime = time(0);
	srand(time(0));

	bp = Bopen(levelfile, OREAD);
	if (bp == nil)
		sysfatal("cannot open levelfile: %r");
	directory = readlevels(bp);
	Bterm(bp);

	dirseen = seeninit(directory);

	ngenerated = 0;
	generated = nil;

	if(srvpost){
		srvname = smprint("trafficfs.%s", srvpost);
		srvfile = smprint("/srv/%s", srvname);
		remove(srvfile);
		free(srvfile);
	}
	postmountsrv(&fs, srvname, mtpt, MBEFORE);
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.