XXIIVV

Parade is a minimal implementation of Paradise.

In Paradise, you are but a force acting upon places, objects, words — vessels. Parade is a subset of the Paradise interactive fiction engine designed explicitly to explore the possible interactions available within the primitives.

I have always imagined that Paradise will be a kind of library.
—Jorge Luis Borges
#include <stdio.h>

#define NAMELEN 16
#define TEXTLEN 256
#define BUFLEN 512

typedef struct Vessel {
	char id;
	struct Vessel* owner;
	struct Vessel* parent;
	char name[NAMELEN];
	char note[TEXTLEN];
	char prog[TEXTLEN];
} Vessel;

typedef struct Parade {
	Vessel vessels[256];
	int len;
} Parade;

char* actions[12] = {
    "create", "become", "enter", "leave",
    "take", "drop", "warp", "transform",
    "note", "program", "use", ""};

Vessel* guest;

/* helpers */

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

int
cisp(char c)
{
	return c == ' ' || c == '\t' || c == '\n' || c == '\r';
}

unsigned char
chex(char c)
{
	if(c >= 'a' && c <= 'f')
		return 10 + c - 'a';
	if(c >= 'A' && c <= 'F')
		return 10 + c - 'A';
	return (c - '0') & 0xF;
}

int
slen(char* s)
{
	int n = 0;
	while(s[n] != '\0' && s[++n])
		;
	return n;
}

unsigned char
shex(char* s, int len)
{
	int i, n = 0;
	for(i = 0; i < len; ++i)
		n |= (chex(s[i]) << ((len - i - 1) * 4));
	return n;
}

int
cpos(char* s, char c)
{
	int i;
	for(i = 0; i < slen(s); i++)
		if(s[i] == c)
			return i;
	return -1;
}

char*
spor(char* s, int c)
{
	int i;
	for(i = slen(s); i >= 0; --i)
		if(s[i] == c)
			return s + i;
	return s - 1;
}

int
scmp(char* a, char* b)
{
	int i, l = slen(a);
	if(l != slen(b))
		return 0;
	for(i = 0; i < l; ++i)
		if(a[i] != b[i])
			return 0;
	return 1;
}

char*
strm(char* s)
{
	char* end;
	while(cisp(*s))
		s++;
	if(*s == 0)
		return s;
	end = s + slen(s) - 1;
	while(end > s && cisp(*end))
		end--;
	end[1] = '\0';
	return s;
}

int
afnd(char* src[], int len, char* val)
{
	int i;
	for(i = 0; i < len; i++)
		if(scmp(src[i], val))
			return i;
	return -1;
}

char*
sstr(char* src, char* dest, int from, int to)
{
	int i;
	char *a = (char*)src + from, *b = (char*)dest;
	for(i = 0; i < to; i++)
		b[i] = a[i];
	dest[to] = '\0';
	return dest;
}

/* Parade */

int
isvisible(Vessel* g, Vessel* v)
{
	if(g->parent != v->parent)
		return 0;
	if(g->parent == v)
		return 0;
	if(g == v)
		return 0;
	return 1;
}

int
isparadox(Vessel* v)
{
	return v->parent == v;
}

char
rune(Vessel* v)
{
	if(isparadox(v))
		return '^';
	if(slen(v->note) > 0)
		return '*';
	if(slen(v->prog) > 0)
		return '+';
	if(v->owner == guest)
		return '~';
	return 0;
}

Vessel*
addvessel(Parade* p, Vessel* v, char* name)
{
	Vessel* nv = &p->vessels[p->len];
	nv->id = p->len;
	nv->owner = v ? v : nv;
	nv->parent = v ? v->parent : nv;
	sstr(name, nv->name, 0, imin(slen(name), NAMELEN - 1));
	p->len++;
	return nv;
}

Vessel*
findvisible(Parade* p, Vessel* v, char* name)
{
	int i;
	char* n = spor(name, ' ') + 1;
	for(i = 0; i < p->len; ++i) {
		if(!isvisible(v, &p->vessels[i]))
			continue;
		if(scmp(p->vessels[i].name, n))
			return &p->vessels[i];
	}
	return NULL;
}

Vessel*
findinventory(Parade* p, Vessel* v, char* name)
{
	int i;
	char* n = spor(name, ' ') + 1;
	for(i = 0; i < p->len; ++i) {
		if(&p->vessels[i] == v)
			continue;
		if(p->vessels[i].parent != v)
			continue;
		if(scmp(p->vessels[i].name, n))
			return &p->vessels[i];
	}

	return NULL;
}

Vessel*
findany(Parade* p, char* name)
{
	int i;
	char* n = spor(name, ' ') + 1;
	for(i = 0; i < p->len; ++i)
		if(scmp(p->vessels[i].name, n))
			return &p->vessels[i];
	return NULL;
}

/* Actions */

void
createvessel(Parade* p, char* val)
{
	Vessel* v;
	if(findany(p, val) || slen(val) < 4 || p->len > 255)
		printf("You cannot create the %s.\n", val);
	else {
		v = addvessel(p, guest, spor(val, ' ') + 1);
		printf("You created the %s%c.\n", v->name, rune(v));
	}
}

void
becomevessel(Parade* p, char* val)
{
	Vessel* v = findany(p, val);
	if(!v)
		printf("You do not see the %s.\n", val);
	else {
		guest = v;
		printf("You became the %s%c.\n", v->name, rune(v));
	}
}

void
entervessel(Parade* p, char* val)
{
	Vessel* v = findvisible(p, guest, val);
	if(!v)
		printf("You do not see the %s.\n", val);
	else {
		guest->parent = v;
		printf("You entered the %s%c.\n", v->name, rune(v));
	}
}

void
leavevessel(void)
{
	Vessel* v = guest->parent;
	if(v == v->parent)
		printf("You cannot leave the %s%c.\n", v->name, rune(v));
	else {
		printf("You left the %s%c.\n", v->name, rune(v));
		guest->parent = v->parent;
	}
}

void
takevessel(Parade* p, char* val)
{
	Vessel* v = findvisible(p, guest, val);
	if(!v)
		printf("You do not see the %s.\n", val);
	else {
		v->parent = guest;
		printf("You took the %s%c.\n", v->name, rune(v));
	}
}

void
dropvessel(Parade* p, char* val)
{
	Vessel* v = findinventory(p, guest, val);
	if(!v)
		printf("You do not carry the %s.\n", val);
	else {
		v->parent = guest->parent->parent;
		printf("You dropped the %s%c.\n", v->name, rune(v));
	}
}

void
warpvessel(Parade* p, char* val)
{
	Vessel* v = findany(p, val);
	if(!v)
		printf("You cannot warp to the %s.\n", val);
	else {
		guest->parent = v;
		printf("You warped to the %s%c.\n", v->name, rune(v));
	}
}

void
transformvessel(Parade* p, char* val)
{
	char* name = spor(val, ' ') + 1;
	if(findany(p, name) || slen(name) < 3)
		printf("You cannot transform into the %s.\n", name);
	else {
		sstr(name, guest->name, 0, imin(slen(name), TEXTLEN - 1));
		printf("You transformed into the %s%c.\n", guest->name, rune(guest));
	}
}

void
notevessel(char* val)
{
	Vessel* v = guest->parent;
	if(slen(val) < 1)
		printf("You remove the note of the %s%c.\n", v->name, rune(v));
	else {
		sstr(val, v->note, 0, imin(slen(val), TEXTLEN - 1));
		printf("You added a note to the %s%c.\n", v->name, rune(v));
	}
}

void
programvessel(char* val)
{
	Vessel* v = guest->parent;
	if(slen(val) < 1)
		printf("You remove the program of the %s%c.\n", v->name, rune(v));
	else {
		sstr(val, v->prog, 0, imin(slen(val), TEXTLEN - 1));
		printf("You programmed the %s%c.\n", v->name, rune(v));
	}
}

void
lookvessel(Parade* p)
{
	int i;
	if(isparadox(guest))
		printf("You are the %s%c.\n", guest->name, rune(guest));
	else
		printf("You are the %s%c in the %s%c.\n",
		       guest->name, rune(guest),
		       guest->parent->name, rune(guest->parent));
	if(slen(guest->parent->note) > 2)
		printf("%s\n", guest->parent->note);
	for(i = 0; i < p->len; ++i)
		if(isvisible(guest, &p->vessels[i]))
			printf("- %s%c\n", p->vessels[i].name, rune(&p->vessels[i]));
}

/* Parade */

int usevessel(Parade* p, char* val);

void
act(Parade* p, char* cmd, char* val)
{
	switch(afnd(actions, 12, cmd)) {
	case 0x0:
		createvessel(p, val);
		break;
	case 0x1:
		becomevessel(p, val);
		break;
	case 0x2:
		entervessel(p, val);
		break;
	case 0x3:
		leavevessel();
		break;
	case 0x4:
		takevessel(p, val);
		break;
	case 0x5:
		dropvessel(p, val);
		break;
	case 0x6:
		warpvessel(p, val);
		break;
	case 0x7:
		transformvessel(p, val);
		break;
	case 0x8:
		notevessel(val);
		break;
	case 0x9:
		programvessel(val);
		break;
	case 0xA:
		usevessel(p, val);
		break;
	case 0xB:
		lookvessel(p);
		break;
	default:
		printf("Unknown action: %s.\n", cmd);
		break;
	}
}

Vessel*
spawn(Parade* p)
{
	addvessel(p, NULL, "library");
	addvessel(p, &p->vessels[0], "ghost");
	return &p->vessels[1];
}

int
parse(Parade* p, char* line, int id)
{
	int split = cpos(line, '|');
	int len = slen(line);
	Vessel* nv = &p->vessels[id];
	if(len < 22 || split < 0 || line[0] == ';')
		return 0;
	nv->id = id;
	nv->owner = &p->vessels[shex(line, 2)];
	nv->parent = &p->vessels[shex(line + 2, 2)];
	strm(sstr(line, nv->name, 5, NAMELEN));
	if(split > 23)
		sstr(line, nv->note, 21, split - 22);
	if(len - split > 3)
		sstr(line, nv->prog, split + 2, len - split - 3);
	return 1;
}

int
save(Parade* p, char* filename)
{
	int i;
	FILE* f = fopen(filename, "w");
	for(i = 0; i < p->len; ++i)
		fprintf(f, "%02x%02x %-15s %s | %s\n",
		        p->vessels[i].owner->id,
		        p->vessels[i].parent->id,
		        p->vessels[i].name,
		        p->vessels[i].note,
		        p->vessels[i].prog);
	fclose(f);
	return 1;
}

int
load(Parade* p, char* filename)
{
	char line[BUFLEN];
	FILE* f = fopen(filename, "r");
	if(f == NULL)
		return 1;
	p->len = 0;
	while(fgets(line, BUFLEN, f)) {
		if(parse(p, line, p->len))
			p->len++;
	}
	return 1;
}

int
answer(Parade* p, char* input)
{
	int split = cpos(input, ' ');
	char action[NAMELEN], value[TEXTLEN];
	if(cpos(input, '|') >= 0)
		return 1;
	if(split >= 0) {
		sstr(input, action, 0, split);
		sstr(input, value, split + 1,
		     imin(slen(input) - split, TEXTLEN - 1));
	} else if(slen(input) < 2) {
		action[0] = '\0';
		value[0] = '\0';
	} else if(slen(input) >= 0) {
		sstr(input, action, 0,
		     imin(slen(input), NAMELEN - 1));
		value[0] = '\0';
	}
	if(scmp(action, "@quit"))
		return 0;
	if(scmp(action, "@save") && slen(value) > 3)
		return save(p, spor(input, ' ') + 1);
	if(scmp(action, "@load") && slen(value) > 3)
		return load(p, spor(input, ' ') + 1);
	act(p, action, value);
	return 1;
}

int
usevessel(Parade* p, char* val)
{
	Vessel* v = findvisible(p, guest, val);
	if(!v)
		printf("You do not see %s.\n", val);
	else if(slen(v->prog) < 2)
		printf("You cannot use %s%c.\n", val, rune(v));
	else
		answer(p, v->prog);
	return 0;
}

int
listen(Parade* p)
{
	char input[TEXTLEN];
	printf("> ");
	if(fgets(input, TEXTLEN, stdin))
		return answer(p, strm(input));
	return 0;
}

int
main(int argc, char** argv)
{
	Parade parade;
	parade.len = 0;
	guest = spawn(&parade);
	if(argc == 2)
		load(&parade, argv[1]);
	printf("A %s%c appeared in the %s%c.\n",
	       guest->name, rune(guest),
	       guest->parent->name, rune(guest->parent));
	while(listen(&parade))
		;
	return 0;
}
parade.c 539 lines

incoming(2): paradise defunct