mirror of https://git.sr.ht/~rabbits/parade
Implemented @save/@load
This commit is contained in:
parent
3826987861
commit
20b96e123e
32
NOTES
32
NOTES
|
@ -16,18 +16,24 @@ answer(¶de, "warp to the library");
|
|||
answer(¶de, "become the ghost");
|
||||
answer(¶de, "use the bat");
|
||||
|
||||
/* world export */
|
||||
Parser
|
||||
|
||||
answer(¶de, "create a machine");
|
||||
answer(¶de, "enter the machine");
|
||||
answer(¶de, "program create a thing");
|
||||
answer(¶de, "leave");
|
||||
answer(¶de, "create a place");
|
||||
answer(¶de, "enter the place");
|
||||
answer(¶de, "note this is a thing");
|
||||
answer(¶de, "leave");
|
||||
answer(¶de, "create a mixture");
|
||||
answer(¶de, "enter the mixture");
|
||||
answer(¶de, "note this is a both");
|
||||
answer(¶de, "program create a both");
|
||||
answer(¶de, "leave");
|
||||
answer(¶de, "save");
|
||||
|
||||
answer(¶de, "create box1");
|
||||
answer(¶de, "create box2");
|
||||
answer(¶de, "create box3");
|
||||
answer(¶de, "take box1");
|
||||
answer(¶de, "take box2");
|
||||
answer(¶de, "enter box3");
|
||||
answer(¶de, "note example note");
|
||||
answer(¶de, "note example program");
|
||||
answer(¶de, "warp to box2");
|
||||
answer(¶de, "note example note");
|
||||
answer(¶de, "note example program");
|
||||
answer(¶de, "save");
|
||||
Stress
|
||||
|
||||
answer(¶de, "create a machine00000000");
|
||||
answer(¶de, "create a machine000000000");
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
# Parade
|
||||
|
||||
This simple implementation incorporates the basic primitives.
|
||||
|
||||
```
|
||||
create a teapot
|
||||
become the teapot
|
||||
enter the ghost
|
||||
leave
|
||||
|
||||
take the ghost
|
||||
drop the ghost
|
||||
warp in the teapot
|
||||
transform into a bat
|
||||
|
||||
note the bat is dark.
|
||||
program create a teapot
|
||||
warp to the library
|
||||
become the ghost
|
||||
|
||||
use the bat ; should create a teapot
|
||||
```
|
||||
|
||||
## Save/Load
|
||||
|
||||
To save a world, use `@save filename.txt`, to load `@load filename.txt`, to quit `@quit`.
|
17
build.sh
17
build.sh
|
@ -1,15 +1,20 @@
|
|||
#!/bin/bash
|
||||
|
||||
clang-format -i main.c
|
||||
rm ./parade
|
||||
|
||||
clang-format -i parade.c
|
||||
|
||||
# Linux
|
||||
cc -std=c89 -DDEBUG -Wall -Wpedantic -Wshadow -Wextra -Werror=implicit-int -Werror=incompatible-pointer-types -Werror=int-conversion -Wvla -g -Og -fsanitize=address -fsanitize=undefined -o main main.c
|
||||
cc -std=c89 -DDEBUG -Wall -Wpedantic -Wshadow -Wextra -Werror=implicit-int -Werror=incompatible-pointer-types -Werror=int-conversion -Wvla -g -Og -fsanitize=address -fsanitize=undefined -o parade parade.c
|
||||
|
||||
# Plan9
|
||||
# pcc main.c -o main
|
||||
# pcc parade.c -o parade
|
||||
|
||||
# Load external world
|
||||
# curl --silent https://wiki.xxiivv.com/links/tw.txt | ./comm
|
||||
# curl --silent https://wiki.xxiivv.com/links/teapot.txt
|
||||
|
||||
./main export.txt
|
||||
rm ./main
|
||||
# file mode
|
||||
./parade
|
||||
|
||||
# spawn mode
|
||||
# ./parade teapot.txt
|
||||
|
|
|
@ -0,0 +1,539 @@
|
|||
#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(¶de);
|
||||
if(argc == 2)
|
||||
load(¶de, argv[1]);
|
||||
printf("A %s%c appeared in the %s%c.\n",
|
||||
guest->name, rune(guest),
|
||||
guest->parent->name, rune(guest->parent));
|
||||
while(listen(¶de))
|
||||
;
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue