~rabbits/orca-toy

dd53b62baccb78ce29ffcecce39290b930a1f2eb — neauoire 10 months ago 3665745
Moved logic to library
10 files changed, 858 insertions(+), 587 deletions(-)

M README.md
D abflm-expected.orca
D abflm-result.orca
D abflm.orca
M build.sh
A cli
A cli.c
A sim.c
A sim.h
M toy.c
M README.md => README.md +8 -0
@@ 37,6 37,14 @@ To display the list of operators inside of Orca, use `CmdOrCtrl+G`.
- `*` **bang**: Bangs neighboring operands.
- `#` **comment**: Halts a line.

## Syntax Highlight

- `1` locked
- `2` port(left)
- `3` operator
- `4` port(right)
- `5` port(output)

## TODOs

- Default values

D abflm-expected.orca => abflm-expected.orca +0 -10
@@ 1,10 0,0 @@
.A.aA..AaaAa1AA2AZ
.0..a..a..k..B..1.
.B.aB..BaaBa1BA2BZ
.0..a..a..0..9..X.
.F.aF..FaaFa1FAzFZ
.*........*.......
.L.aL..LaaLa1LAzLZ
..........a..1..Z.
.M.aM..MaaMa1MAzMZ
.0..0..0..s..A..1.

D abflm-result.orca => abflm-result.orca +0 -22
@@ 1,22 0,0 @@
.A.aA..AaaAa1AA2AZ
.0..a..a..k.......
.B.aB..BaaBa1BA2BZ
.0..a..a..0.......
.F.aF..FaaFa1FAzFZ
.*........*.......
.L.aL..LaaLa1LAzLZ
..........a.......
.M.aM..MaaMa1MAzMZ
.0..0..0..s.......

234234234234344444
050050050050000000
234234234234344444
050050050050000000
234234234234344444
050050050050000000
234234234234344444
050050050050000000
234234234234344444
050050050050000000


D abflm.orca => abflm.orca +0 -10
@@ 1,10 0,0 @@
.A.aA..AaaAa1AA2AZ
..................
.B.aB..BaaBa1BA2BZ
..................
.F.aF..FaaFa1FAzFZ
..................
.L.aL..LaaLa1LAzLZ
..................
.M.aM..MaaMa1MAzMZ
..................

M build.sh => build.sh +13 -15
@@ 1,20 1,18 @@
#!/bin/bash

clang-format -i sim.c
clang-format -i sim.h
clang-format -i cli.c
clang-format -i toy.c

rm -f ./toy

# GNU/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 toy toy.c

# GNU/Linux
cc toy.c -std=c89 -O2 -DNDEBUG -g0 -s -Wall -o toy
# cli
# rm -f ./cli
# 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 cli cli.c sim.c
# cc cli.c sim.c -std=c89 -O2 -DNDEBUG -g0 -s -Wall -o cli
# ./cli demo.orca

# Plan9
# pcc toy.c -o toy

# ./toy abflm.orca > abflm-result.orca
# diff abflm-result.orca abflm-expected.orca

# time ./toy ~/Git/orca-examples/benchmarks/logic.orca
./toy  demo.orca
# toy
rm -f ./toy
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 -L/usr/local/lib -lSDL2 toy.c sim.c -o toy
# cc toy.c sim.c -std=c89 -O2 -DNDEBUG -g0 -s -Wall -L/usr/local/lib -lSDL2 -o toy
./toy

A cli => cli +0 -0
A cli.c => cli.c +33 -0
@@ 0,0 1,33 @@
#include <stdio.h>
#include "sim.h"

int
error(char *name)
{
	printf("Error: %s\n", name);
	return 0;
}

int
main(int argc, char *argv[])
{
	FILE *f;
	int limit = 3;
	Grid g;
	g.w = 0;
	g.h = 0;
	g.f = 0;
	g.r = 1;
	if(argc < 2)
		return error("No input.");
	f = fopen(argv[1], "r");
	if(!f)
		return error("Missing input.");
	if(!disk(f, &g))
		return error("Invalid grid");
	while(g.f < limit) {
		run(&g);
		print(&g);
	}
	return 0;
}

A sim.c => sim.c +555 -0
@@ 0,0 1,555 @@
#include <stdio.h>
#include "sim.h"

int
ciuc(char c)
{
	return c >= 'A' && c <= 'Z';
}

int
cilc(char c)
{
	return c >= 'a' && c <= 'z';
}

int
cinu(char c)
{
	return c >= '0' && c <= '9';
}

int
cisp(char c)
{
	return !ciuc(c) && !cilc(c) && !cinu(c) && c != '.';
}

int
clca(int c)
{
	return ciuc(c) ? c + ('a' - 'A') : c;
}

int
cint(char c)
{
	if(c == '.')
		return 0;
	if(cinu(c))
		return c - '0';
	if(cilc(c))
		return c - 'a' + 10;
	if(ciuc(c))
		return c - 'A' + 10;
	return 0;
}

char
cchr(int v, int cap)
{
	v %= 36;
	v *= v < 0 ? -1 : 1;
	if(v >= 0 && v <= 9)
		return '0' + v;
	if(cap)
		return 'A' + (v - 10);
	return 'a' + (v - 10);
}

int
valid(Grid *g, int x, int y)
{
	return x >= 0 && x <= g->w && y >= 0 && y <= g->h;
}

int
random(Grid *g)
{
	(void)g;
	return 0;
	/*
	g->r *= 1103515245;
	return ((g->r / 65536 * g->f) % 32768) ^ g->f;
	*/
}

/* IO */

char
get(Grid *g, int x, int y)
{
	if(valid(g, x, y))
		return g->data[x + (y * g->w)];
	return '.';
}

void
set(Grid *g, int x, int y, char c)
{
	if(valid(g, x, y))
		g->data[x + (y * g->w)] = c;
}

/* Locks */

void
lock(Grid *g, int x, int y)
{
	if(valid(g, x, y)) {
		g->lock[x + (y * g->w)] = 1;
		g->type[x + (y * g->w)] = 1;
	}
}

/* Variables */

void
save(Grid *g, char key, char val)
{
	g->vars[cint(key)] = val;
}

char
load(Grid *g, char key)
{
	return g->vars[cint(key)];
}

/* Syntax */

int
gettype(Grid *g, int x, int y)
{
	if(valid(g, x, y))
		return g->type[x + (y * g->w)];
	return 0;
}

void
settype(Grid *g, int x, int y, int t)
{
	if(valid(g, x, y))
		g->type[x + (y * g->w)] = t;
}

/* Port Setters */

void
setport(Grid *g, int x, int y, char c)
{
	lock(g, x, y);
	settype(g, x, y, 5);
	set(g, x, y, c);
}

int
getport(Grid *g, int x, int y, int l)
{
	if(l) {
		lock(g, x, y);
		settype(g, x, y, 4);
	} else
		settype(g, x, y, 2);
	return get(g, x, y);
}

int
bang(Grid *g, int x, int y)
{
	return get(g, x - 1, y) == '*' || get(g, x + 1, y) == '*' || get(g, x, y - 1) == '*' || get(g, x, y + 1) == '*';
}

/* Library */

void
opa(Grid *g, int x, int y, char c)
{
	char a = getport(g, x - 1, y, 0);
	char b = getport(g, x + 1, y, 1);
	setport(g, x, y + 1, cchr(cint(a) + cint(b), ciuc(b)));
	(void)c;
}

void
opb(Grid *g, int x, int y, char c)
{
	char a = getport(g, x - 1, y, 0);
	char b = getport(g, x + 1, y, 1);
	setport(g, x, y + 1, cchr(cint(a) - cint(b), ciuc(b)));
	(void)c;
}

void
opc(Grid *g, int x, int y, char c)
{
	char rate = getport(g, x - 1, y, 0);
	char mod = getport(g, x + 1, y, 1);
	setport(g, x, y + 1, cchr(g->f / rate % mod, ciuc(mod)));
	(void)c;
}

void
opd(Grid *g, int x, int y, char c)
{
	char rate = getport(g, x - 1, y, 0);
	char mod = getport(g, x + 1, y, 1);
	setport(g, x, y + 1, g->f % (rate * mod) == 0 ? '*' : '.');
	(void)c;
}

void
ope(Grid *g, int x, int y, char c)
{
	if(x == g->w || get(g, x + 1, y) != '.')
		set(g, x, y, '*');
	else {
		set(g, x, y, '.');
		setport(g, x + 1, y, c);
	}
}

void
opf(Grid *g, int x, int y, char c)
{
	setport(g, x, y + 1, getport(g, x - 1, y, 0) == getport(g, x + 1, y, 1) ? '*' : '.');
	(void)c;
}

void
opg(Grid *g, int x, int y, char c)
{
	int tx = cint(getport(g, x - 3, y, 0));
	int ty = cint(getport(g, x - 2, y, 0));
	int i, len = cint(getport(g, x - 1, y, 0));
	for(i = 0; i < len; ++i)
		setport(g, x + i + tx, y + 1 + ty, getport(g, x + 1 + i, y, 1));
	(void)c;
}

void
oph(Grid *g, int x, int y, char c)
{
	getport(g, x, y + 1, 1);
	(void)c;
}

void
opi(Grid *g, int x, int y, char c)
{
	char step = getport(g, x - 1, y, 0);
	char mod = getport(g, x + 1, y, 1);
	char val = getport(g, x, y + 1, 1);
	setport(g, x, y + 1, cchr((cint(val) + cint(step)) % (cint(mod) || 1), ciuc(mod)));
	(void)c;
}

void
opj(Grid *g, int x, int y, char c)
{
	int i;
	char link = getport(g, x, y - 1, 0);
	if(link != c) {
		for(i = 1; y + i < g->h; ++i)
			if(get(g, x, y + i) != c)
				break;
		setport(g, x, y + i, link);
	}
}

void
opk(Grid *g, int x, int y, char c)
{
	int i, len = cint(getport(g, x - 1, y, 0));
	for(i = 0; i < len; ++i) {
		char key = getport(g, x + 1 + i, y, 1);
		if(key == '.')
			continue;
		setport(g, x + 1 + i, y + 1, load(g, key));
	}
	(void)c;
}

void
opl(Grid *g, int x, int y, char c)
{
	char a = getport(g, x - 1, y, 0);
	char b = getport(g, x + 1, y, 1);
	setport(g, x, y + 1, cint(a) < cint(b) ? a : b);
	(void)c;
}

void
opm(Grid *g, int x, int y, char c)
{
	char a = getport(g, x - 1, y, 0);
	char b = getport(g, x + 1, y, 1);
	setport(g, x, y + 1, cchr(cint(a) * cint(b), ciuc(b)));
	(void)c;
}

void
opn(Grid *g, int x, int y, char c)
{
	if(y == 0 || get(g, x, y - 1) != '.')
		set(g, x, y, '*');
	else {
		set(g, x, y, '.');
		setport(g, x, y - 1, c);
	}
}

void
opo(Grid *g, int x, int y, char c)
{
	int tx = cint(getport(g, x - 2, y, 0));
	int ty = cint(getport(g, x - 1, y, 0));
	setport(g, x, y + 1, getport(g, x + 1 + tx, y + ty, 1));
	(void)c;
}

void
opp(Grid *g, int x, int y, char c)
{
	int key = cint(getport(g, x - 2, y, 0));
	int i, len = cint(getport(g, x - 1, y, 0));
	/* TODO */
	for(i = 0; i < len; ++i)
		lock(g, x + i, y + 1);
	setport(g, x + (key % len), y + 1, get(g, x + 1, y));
	(void)c;
}

void
opq(Grid *g, int x, int y, char c)
{
	int tx = cint(getport(g, x - 3, y, 0));
	int ty = cint(getport(g, x - 2, y, 0));
	int i, len = cint(getport(g, x - 1, y, 0));
	for(i = 0; i < len; ++i)
		setport(g, x + 1 - len + i, y + 1, getport(g, x + 1 + tx + i, y + ty, 1));
	(void)c;
}

void
opr(Grid *g, int x, int y, char c)
{
	int min = cint(getport(g, x - 1, y, 0));
	char max = getport(g, x + 1, y, 1);
	setport(g, x, y + 1, cchr((random(g) % ((cint(max) - min) || 1)) + min, ciuc(max)));
	(void)c;
}

void
ops(Grid *g, int x, int y, char c)
{
	if(y == g->h || get(g, x, y + 1) != '.')
		set(g, x, y, '*');
	else {
		set(g, x, y, '.');
		setport(g, x, y + 1, c);
	}
}

void
opt(Grid *g, int x, int y, char c)
{
	int key = cint(getport(g, x - 2, y, 0));
	int i, len = cint(getport(g, x - 1, y, 0));
	for(i = 0; i < len; ++i)
		lock(g, x + 1 + i, y);
	setport(g, x, y + 1, getport(g, x + 1 + (key % len), y, 1));
	(void)c;
}

void
opu(Grid *g, int x, int y, char c)
{
	int max = cint(getport(g, x - 1, y, 0));
	int step = cint(getport(g, x + 1, y, 1));
	int bucket = (step * (g->f + max - 1)) % max + step;
	setport(g, x, y + 1, bucket >= max ? '*' : '.');
	(void)c;
}

void
opv(Grid *g, int x, int y, char c)
{
	char w = getport(g, x - 1, y, 0);
	char r = getport(g, x + 1, y, 1);
	if(w != '.')
		save(g, w, r);
	else if(w == '.' && r != '.')
		setport(g, x, y + 1, load(g, r));
	(void)c;
}

void
opw(Grid *g, int x, int y, char c)
{
	if(x == 0 || get(g, x - 1, y) != '.')
		set(g, x, y, '*');
	else {
		set(g, x, y, '.');
		setport(g, x - 1, y, c);
	}
}

void
opx(Grid *g, int x, int y, char c)
{
	int tx = cint(getport(g, x - 2, y, 0));
	int ty = cint(getport(g, x - 1, y, 0));
	setport(g, x + tx, y + ty + 1, getport(g, x + 1, y, 1));
	(void)c;
}

void
opy(Grid *g, int x, int y, char c)
{
	int i;
	char link = getport(g, x - 1, y, 0);
	if(link != c) {
		for(i = 1; x + i < g->w; ++i)
			if(get(g, x + i, y) != c)
				break;
		setport(g, x + i, y, link);
	}
}

void
opz(Grid *g, int x, int y, char c)
{
	int rate = cint(getport(g, x - 1, y, 0));
	char target = getport(g, x + 1, y, 1);
	char val = cint(getport(g, x, y + 1, 1));
	int t = cint(target);
	int mod = val < t ? rate : val > t ? -rate : 0;
	setport(g, x, y + 1, cchr(val + mod, ciuc(target)));
	(void)c;
}

void
opcomment(Grid *g, int x, int y)
{
	int i;
	for(i = 1; x + i < g->w; ++i) {
		lock(g, x + i, y);
		if(get(g, x + i, y) == '#')
			break;
	}
}

void
opspecial(Grid *g, int x, int y)
{
	int i, b = bang(g, x, y);
	for(i = 0; x + i < g->w; ++i) {
		char c = getport(g, x + i, y, 1);
		if(c == '.')
			break;
		if(b)
			printf("%c", c);
	}
	if(b)
		printf("\n");
}

void
operate(Grid *g, int x, int y, char c)
{
	switch(clca(c)) {
	case 'a': opa(g, x, y, c); break;
	case 'b': opb(g, x, y, c); break;
	case 'c': opc(g, x, y, c); break;
	case 'd': opd(g, x, y, c); break;
	case 'e': ope(g, x, y, c); break;
	case 'f': opf(g, x, y, c); break;
	case 'g': opg(g, x, y, c); break;
	case 'h': oph(g, x, y, c); break;
	case 'i': opi(g, x, y, c); break;
	case 'k': opk(g, x, y, c); break;
	case 'j': opj(g, x, y, c); break;
	case 'l': opl(g, x, y, c); break;
	case 'm': opm(g, x, y, c); break;
	case 'n': opn(g, x, y, c); break;
	case 'o': opo(g, x, y, c); break;
	case 'p': opp(g, x, y, c); break;
	case 'q': opq(g, x, y, c); break;
	case 'r': opr(g, x, y, c); break;
	case 's': ops(g, x, y, c); break;
	case 't': opt(g, x, y, c); break;
	case 'u': opu(g, x, y, c); break;
	case 'v': opv(g, x, y, c); break;
	case 'w': opw(g, x, y, c); break;
	case 'x': opx(g, x, y, c); break;
	case 'y': opy(g, x, y, c); break;
	case 'z': opz(g, x, y, c); break;
	case '*': set(g, x, y, '.'); break;
	case '#': opcomment(g, x, y); break;
	default: opspecial(g, x, y);
	}
	settype(g, x, y, 3);
}

/* General */

void
print(Grid *g)
{
	int x, y;
	for(y = 0; y < g->h; ++y)
		for(x = 0; x < g->w; ++x) {
			putchar(get(g, x, y));
			if(x == g->w - 1)
				putchar('\n');
		}
	putchar('\n');
	for(y = 0; y < g->h; ++y)
		for(x = 0; x < g->w; ++x) {
			printf("%d", gettype(g, x, y));
			if(x == g->w - 1)
				putchar('\n');
		}
	putchar('\n');
}

int
run(Grid *g)
{
	int i, x, y;
	for(i = 0; i < g->l; ++i) {
		g->lock[i] = 0;
		g->type[i] = 0;
	}
	for(i = 0; i < g->l; ++i) {
		char c = g->data[i];
		x = i % g->w;
		y = i / g->w;
		if(c == '.' || g->lock[i])
			continue;
		if(cilc(c) && !bang(g, x, y))
			continue;
		operate(g, x, y, c);
	}
	g->f++;
	return 1;
}

int
disk(FILE *f, Grid *g)
{
	char c;
	g->l = 0;
	while((c = fgetc(f)) != EOF && g->l < MAXSZ) {
		if(c == '\n') {
			if(g->w == 0)
				g->w = g->l;
			g->h = g->l / g->w;
		} else {
			g->type[g->l] = 0;
			g->data[g->l++] = c;
		}
	}
	return g->w > 2 && g->h > 2;
}

A sim.h => sim.h +17 -0
@@ 0,0 1,17 @@
#pragma once

#include <stdio.h>

#define MAXSZ 128 * 128

typedef struct Grid {
	int w, h, l, f, r;
	int lock[MAXSZ];
	int type[MAXSZ];
	char vars[36];
	char data[MAXSZ];
} Grid;

void print(Grid *g);
int run(Grid *g);
int disk(FILE *f, Grid *g);

M toy.c => toy.c +232 -530
@@ 1,603 1,305 @@
#include <SDL2/SDL.h>
#include <stdio.h>
#include "sim.h"

#define HOR 32
#define VER 16
#define PAD 8
#define ZOOM 1
#define color1 0x000000
#define color2 0x72DEC2
#define color3 0x888888
#define color4 0xFFFFFF
#define color0 0x222222

#define PLIMIT 256
#define SZ (HOR * VER * 16)

typedef struct {
	int x, y;
} Point2d;

#define MAXSZ 128 * 128

typedef struct Grid {
	int w, h, l, f, r;
	int lock[MAXSZ];
	int type[MAXSZ];
	char vars[36];
	char data[MAXSZ];
} Grid;
typedef struct {
	int x, y, w, h;
} Rect2d;

/* Port types 
1 - locked
2 - port(left)
3 - operator
4 - port(right)
5 - port(output)
*/
unsigned char chrbuf[SZ];
int colors[] = {color1, color2, color3, color4, color0};
int WIDTH = 8 * HOR + PAD * 2;
int HEIGHT = 8 * VER + PAD * 2;
int FPS = 30;
int GUIDES = 1;
int COLOR = 3;
SDL_Window *gWindow = NULL;
SDL_Renderer *gRenderer = NULL;
SDL_Texture *gTexture = NULL;
uint32_t *pixels;

int
ciuc(char c)
{
	return c >= 'A' && c <= 'Z';
}
Rect2d selection;

int
cilc(char c)
Point2d
Pt2d(int x, int y)
{
	return c >= 'a' && c <= 'z';
	Point2d p;
	p.x = x;
	p.y = y;
	return p;
}

int
cinu(char c)
{
	return c >= '0' && c <= '9';
}
/* chr */

int
cisp(char c)
rowchr(int x, int y)
{
	return !ciuc(c) && !cilc(c) && !cinu(c) && c != '.';
	return (y % 8) + ((x / 8 + y / 8 * HOR) * 16);
}

int
clca(int c)
getchr(int x, int y)
{
	return ciuc(c) ? c + ('a' - 'A') : c;
}

int
cint(char c)
{
	if(c == '.')
	int ch1, ch2;
	int r = rowchr(x, y);
	int px = x % 8;
	if(r < 0 || r > SZ - 8)
		return 0;
	if(cinu(c))
		return c - '0';
	if(cilc(c))
		return c - 'a' + 10;
	if(ciuc(c))
		return c - 'A' + 10;
	return 0;
}

char
cchr(int v, int cap)
{
	v %= 36;
	v *= v < 0 ? -1 : 1;
	if(v >= 0 && v <= 9)
		return '0' + v;
	if(cap)
		return 'A' + (v - 10);
	return 'a' + (v - 10);
}

int
valid(Grid *g, int x, int y)
{
	return x >= 0 && x <= g->w && y >= 0 && y <= g->h;
}

int
random(Grid *g)
{
	(void)g;
	return 0;
	/*
	g->r *= 1103515245;
	return ((g->r / 65536 * g->f) % 32768) ^ g->f;
	*/
}

/* IO */

char
get(Grid *g, int x, int y)
{
	if(valid(g, x, y))
		return g->data[x + (y * g->w)];
	return '.';
}

void
set(Grid *g, int x, int y, char c)
{
	if(valid(g, x, y))
		g->data[x + (y * g->w)] = c;
}

/* Locks */

void
lock(Grid *g, int x, int y)
{
	if(valid(g, x, y)) {
		g->lock[x + (y * g->w)] = 1;
		g->type[x + (y * g->w)] = 1;
	ch1 = (chrbuf[r] >> (7 - px)) & 1;
	ch2 = (chrbuf[r + 8] >> (7 - px)) & 1;
	return ch1 && !ch2 ? 1 : !ch1 && ch2 ? 2 : ch1 && ch2 ? 3 : 0;
}

void
putchr(int x, int y, int color)
{
	int r = rowchr(x, y);
	int px = x % 8;
	if(x < 0 || y < 0 || x > 8 * HOR || y > 8 * VER || r > SZ - 8)
		return;
	if(color == 0) {
		chrbuf[r] &= ~(1UL << (7 - px));
		chrbuf[r + 8] &= ~(1UL << (7 - px));
	} else if(color == 2) {
		chrbuf[r] |= 1UL << (7 - px);
		chrbuf[r + 8] &= ~(1UL << (7 - px));
	} else if(color == 1) {
		chrbuf[r] &= ~(1UL << (7 - px));
		chrbuf[r + 8] |= 1UL << (7 - px);
	} else if(color == 3) {
		chrbuf[r] |= 1UL << (7 - px);
		chrbuf[r + 8] |= 1UL << (7 - px);
	}
}

/* Variables */

void
save(Grid *g, char key, char val)
newchr(void)
{
	g->vars[cint(key)] = val;
}

char
load(Grid *g, char key)
{
	return g->vars[cint(key)];
	int i;
	for(i = 0; i < SZ; ++i)
		chrbuf[i] = 0x00;
}

/* Syntax */
/* misc */

int
gettype(Grid *g, int x, int y)
guide(int x, int y)
{
	if(valid(g, x, y))
		return g->type[x + (y * g->w)];
	if(!GUIDES)
		return 0;
	if(x % 32 == 0 && y % 32 == 0)
		return 3;
	else if(x % 8 == 0 && y % 8 == 0)
		return 4;
	return 0;
}

void
settype(Grid *g, int x, int y, int t)
{
	if(valid(g, x, y))
		g->type[x + (y * g->w)] = t;
}

/* Port Setters */

void
setport(Grid *g, int x, int y, char c)
{
	lock(g, x, y);
	settype(g, x, y, 5);
	set(g, x, y, c);
}

int
getport(Grid *g, int x, int y, int l)
{
	if(l) {
		lock(g, x, y);
		settype(g, x, y, 4);
	} else
		settype(g, x, y, 2);
	return get(g, x, y);
}

int
bang(Grid *g, int x, int y)
{
	return get(g, x - 1, y) == '*' || get(g, x + 1, y) == '*' || get(g, x, y - 1) == '*' || get(g, x, y + 1) == '*';
}

/* Library */

void
opa(Grid *g, int x, int y, char c)
{
	char a = getport(g, x - 1, y, 0);
	char b = getport(g, x + 1, y, 1);
	setport(g, x, y + 1, cchr(cint(a) + cint(b), ciuc(b)));
	(void)c;
}

void
opb(Grid *g, int x, int y, char c)
{
	char a = getport(g, x - 1, y, 0);
	char b = getport(g, x + 1, y, 1);
	setport(g, x, y + 1, cchr(cint(a) - cint(b), ciuc(b)));
	(void)c;
}

void
opc(Grid *g, int x, int y, char c)
draw(uint32_t *dst)
{
	char rate = getport(g, x - 1, y, 0);
	char mod = getport(g, x + 1, y, 1);
	setport(g, x, y + 1, cchr(g->f / rate % mod, ciuc(mod)));
	(void)c;
	int b, i, j, id = 0;
	for(b = 0; b < SZ; b += 16)
		for(i = 0; i < 8; i++)
			for(j = 7; j >= 0; j--) {
				int ch1 = chrbuf[b + i];
				int ch2 = chrbuf[b + i + 8];
				int color = ((ch1 >> j) & 0x1) + (((ch2 >> j) & 0x1) << 1);
				int ti = id / 64;
				int tx = ti % HOR;
				int ty = ti / HOR;
				int odd = (ti + (ti / HOR + 2)) % 2 == 0;
				int px = (ti / (HOR * VER)) * (8 * HOR) + tx * 8 + (id % 8);
				int py = ((ti / HOR) * 8) + ((id % 64) / 8);
				if(tx == selection.x && ty == selection.y)
					dst[(py + PAD) * WIDTH + (px + PAD)] = colors[1];
				else
					dst[(py + PAD) * WIDTH + (px + PAD)] = colors[GUIDES && odd && color == 0 ? 4 : color];
				id++;
			}
	SDL_UpdateTexture(gTexture, NULL, dst, WIDTH * sizeof(uint32_t));
	SDL_RenderClear(gRenderer);
	SDL_RenderCopy(gRenderer, gTexture, NULL, NULL);
	SDL_RenderPresent(gRenderer);
}

void
opd(Grid *g, int x, int y, char c)
select(int x, int y, int w, int h)
{
	char rate = getport(g, x - 1, y, 0);
	char mod = getport(g, x + 1, y, 1);
	setport(g, x, y + 1, g->f % (rate * mod) == 0 ? '*' : '.');
	(void)c;
	selection.x = x;
	selection.y = y;
	selection.w = w;
	selection.h = h;
	draw(pixels);
}

void
ope(Grid *g, int x, int y, char c)
move(int x, int y)
{
	if(x == g->w || get(g, x + 1, y) != '.')
		set(g, x, y, '*');
	else {
		set(g, x, y, '.');
		setport(g, x + 1, y, c);
	int reqdraw = 0;
	if((x < 0 && selection.x > 0) || (x > 0 && selection.x < HOR - 1)) {
		selection.x += x;
		reqdraw = 1;
	}
}

void
opf(Grid *g, int x, int y, char c)
{
	setport(g, x, y + 1, getport(g, x - 1, y, 0) == getport(g, x + 1, y, 1) ? '*' : '.');
	(void)c;
}

void
opg(Grid *g, int x, int y, char c)
{
	int tx = cint(getport(g, x - 3, y, 0));
	int ty = cint(getport(g, x - 2, y, 0));
	int i, len = cint(getport(g, x - 1, y, 0));
	for(i = 0; i < len; ++i)
		setport(g, x + i + tx, y + 1 + ty, getport(g, x + 1 + i, y, 1));
	(void)c;
}

void
oph(Grid *g, int x, int y, char c)
{
	getport(g, x, y + 1, 1);
	(void)c;
}

void
opi(Grid *g, int x, int y, char c)
{
	char step = getport(g, x - 1, y, 0);
	char mod = getport(g, x + 1, y, 1);
	char val = getport(g, x, y + 1, 1);
	setport(g, x, y + 1, cchr((cint(val) + cint(step)) % (cint(mod) || 1), ciuc(mod)));
	(void)c;
}

void
opj(Grid *g, int x, int y, char c)
{
	int i;
	char link = getport(g, x, y - 1, 0);
	if(link != c) {
		for(i = 1; y + i < g->h; ++i)
			if(get(g, x, y + i) != c)
				break;
		setport(g, x, y + i, link);
	if((y < 0 && selection.y > 0) || (y > 0 && selection.y < VER - 1)) {
		selection.y += y;
		reqdraw = 1;
	}
	if(reqdraw)
		draw(pixels);
}

void
opk(Grid *g, int x, int y, char c)
{
	int i, len = cint(getport(g, x - 1, y, 0));
	for(i = 0; i < len; ++i) {
		char key = getport(g, x + 1 + i, y, 1);
		if(key == '.')
			continue;
		setport(g, x + 1 + i, y + 1, load(g, key));
	}
	(void)c;
}

void
opl(Grid *g, int x, int y, char c)
{
	char a = getport(g, x - 1, y, 0);
	char b = getport(g, x + 1, y, 1);
	setport(g, x, y + 1, cint(a) < cint(b) ? a : b);
	(void)c;
}

void
opm(Grid *g, int x, int y, char c)
{
	char a = getport(g, x - 1, y, 0);
	char b = getport(g, x + 1, y, 1);
	setport(g, x, y + 1, cchr(cint(a) * cint(b), ciuc(b)));
	(void)c;
}

void
opn(Grid *g, int x, int y, char c)
{
	if(y == 0 || get(g, x, y - 1) != '.')
		set(g, x, y, '*');
	else {
		set(g, x, y, '.');
		setport(g, x, y - 1, c);
	}
}

void
opo(Grid *g, int x, int y, char c)
{
	int tx = cint(getport(g, x - 2, y, 0));
	int ty = cint(getport(g, x - 1, y, 0));
	setport(g, x, y + 1, getport(g, x + 1 + tx, y + ty, 1));
	(void)c;
}

void
opp(Grid *g, int x, int y, char c)
{
	int key = cint(getport(g, x - 2, y, 0));
	int i, len = cint(getport(g, x - 1, y, 0));
	/* TODO */
	for(i = 0; i < len; ++i)
		lock(g, x + i, y + 1);
	setport(g, x + (key % len), y + 1, get(g, x + 1, y));
	(void)c;
}

void
opq(Grid *g, int x, int y, char c)
{
	int tx = cint(getport(g, x - 3, y, 0));
	int ty = cint(getport(g, x - 2, y, 0));
	int i, len = cint(getport(g, x - 1, y, 0));
	for(i = 0; i < len; ++i)
		setport(g, x + 1 - len + i, y + 1, getport(g, x + 1 + tx + i, y + ty, 1));
	(void)c;
}

void
opr(Grid *g, int x, int y, char c)
{
	int min = cint(getport(g, x - 1, y, 0));
	char max = getport(g, x + 1, y, 1);
	setport(g, x, y + 1, cchr((random(g) % ((cint(max) - min) || 1)) + min, ciuc(max)));
	(void)c;
}

void
ops(Grid *g, int x, int y, char c)
{
	if(y == g->h || get(g, x, y + 1) != '.')
		set(g, x, y, '*');
	else {
		set(g, x, y, '.');
		setport(g, x, y + 1, c);
	}
}

void
opt(Grid *g, int x, int y, char c)
{
	int key = cint(getport(g, x - 2, y, 0));
	int i, len = cint(getport(g, x - 1, y, 0));
	for(i = 0; i < len; ++i)
		lock(g, x + 1 + i, y);
	setport(g, x, y + 1, getport(g, x + 1 + (key % len), y, 1));
	(void)c;
}

void
opu(Grid *g, int x, int y, char c)
{
	int max = cint(getport(g, x - 1, y, 0));
	int step = cint(getport(g, x + 1, y, 1));
	int bucket = (step * (g->f + max - 1)) % max + step;
	setport(g, x, y + 1, bucket >= max ? '*' : '.');
	(void)c;
}

void
opv(Grid *g, int x, int y, char c)
{
	char w = getport(g, x - 1, y, 0);
	char r = getport(g, x + 1, y, 1);
	if(w != '.')
		save(g, w, r);
	else if(w == '.' && r != '.')
		setport(g, x, y + 1, load(g, r));
	(void)c;
}

void
opw(Grid *g, int x, int y, char c)
{
	if(x == 0 || get(g, x - 1, y) != '.')
		set(g, x, y, '*');
	else {
		set(g, x, y, '.');
		setport(g, x - 1, y, c);
	}
}

void
opx(Grid *g, int x, int y, char c)
{
	int tx = cint(getport(g, x - 2, y, 0));
	int ty = cint(getport(g, x - 1, y, 0));
	setport(g, x + tx, y + ty + 1, getport(g, x + 1, y, 1));
	(void)c;
}

void
opy(Grid *g, int x, int y, char c)
int
error(char *msg, const char *err)
{
	int i;
	char link = getport(g, x - 1, y, 0);
	if(link != c) {
		for(i = 1; x + i < g->w; ++i)
			if(get(g, x + i, y) != c)
				break;
		setport(g, x + i, y, link);
	}
	printf("Error %s: %s\n", msg, err);
	return 1;
}

void
opz(Grid *g, int x, int y, char c)
quit(void)
{
	int rate = cint(getport(g, x - 1, y, 0));
	char target = getport(g, x + 1, y, 1);
	char val = cint(getport(g, x, y + 1, 1));
	int t = cint(target);
	int mod = val < t ? rate : val > t ? -rate : 0;
	setport(g, x, y + 1, cchr(val + mod, ciuc(target)));
	(void)c;
	free(pixels);
	SDL_DestroyTexture(gTexture);
	gTexture = NULL;
	SDL_DestroyRenderer(gRenderer);
	gRenderer = NULL;
	SDL_DestroyWindow(gWindow);
	gWindow = NULL;
	SDL_Quit();
	exit(0);
}

void
opcomment(Grid *g, int x, int y)
render(void)
{
	int i;
	for(i = 1; x + i < g->w; ++i) {
		lock(g, x + i, y);
		if(get(g, x + i, y) == '#')
			break;
	}
	newchr();
	draw(pixels);
}

void
opspecial(Grid *g, int x, int y)
domouse(SDL_Event *event)
{
	int i, b = bang(g, x, y);
	for(i = 0; x + i < g->w; ++i) {
		char c = getport(g, x + i, y, 1);
		if(c == '.')
			break;
		if(b)
			printf("%c", c);
	Point2d touch = Pt2d(
		(event->motion.x - (PAD * ZOOM)) / ZOOM,
		(event->motion.y - (PAD * ZOOM)) / ZOOM);
	switch(event->type) {
	case SDL_MOUSEBUTTONUP:
		printf("mouse-up\n");
		break;
	case SDL_MOUSEBUTTONDOWN:
		printf("mouse-down\n");
		break;
	case SDL_MOUSEMOTION:
		break;
	}
	if(b)
		printf("\n");
}

void
operate(Grid *g, int x, int y, char c)
{
	switch(clca(c)) {
	case 'a': opa(g, x, y, c); break;
	case 'b': opb(g, x, y, c); break;
	case 'c': opc(g, x, y, c); break;
	case 'd': opd(g, x, y, c); break;
	case 'e': ope(g, x, y, c); break;
	case 'f': opf(g, x, y, c); break;
	case 'g': opg(g, x, y, c); break;
	case 'h': oph(g, x, y, c); break;
	case 'i': opi(g, x, y, c); break;
	case 'k': opk(g, x, y, c); break;
	case 'j': opj(g, x, y, c); break;
	case 'l': opl(g, x, y, c); break;
	case 'm': opm(g, x, y, c); break;
	case 'n': opn(g, x, y, c); break;
	case 'o': opo(g, x, y, c); break;
	case 'p': opp(g, x, y, c); break;
	case 'q': opq(g, x, y, c); break;
	case 'r': opr(g, x, y, c); break;
	case 's': ops(g, x, y, c); break;
	case 't': opt(g, x, y, c); break;
	case 'u': opu(g, x, y, c); break;
	case 'v': opv(g, x, y, c); break;
	case 'w': opw(g, x, y, c); break;
	case 'x': opx(g, x, y, c); break;
	case 'y': opy(g, x, y, c); break;
	case 'z': opz(g, x, y, c); break;
	case '*': set(g, x, y, '.'); break;
	case '#': opcomment(g, x, y); break;
	default: opspecial(g, x, y);
dokey(SDL_Event *event)
{
	switch(event->key.keysym.sym) {
	case SDLK_UP:
		move(0, -1);
		printf("up\n");
		break;
	case SDLK_DOWN:
		move(0, 1);
		printf("down\n");
		break;
	case SDLK_LEFT:
		move(-1, 0);
		printf("left\n");
		break;
	case SDLK_RIGHT:
		move(1, 0);
		printf("right\n");
		break;
	}
	settype(g, x, y, 3);
}

/* General */

void
print(Grid *g)
{
	int x, y;
	for(y = 0; y < g->h; ++y)
		for(x = 0; x < g->w; ++x) {
			putchar(get(g, x, y));
			if(x == g->w - 1)
				putchar('\n');
		}
	putchar('\n');
	for(y = 0; y < g->h; ++y)
		for(x = 0; x < g->w; ++x) {
			printf("%d", gettype(g, x, y));
			if(x == g->w - 1)
				putchar('\n');
		}
	putchar('\n');
	/* update(); */
}

int
run(Grid *g)
{
	int i, x, y;
	for(i = 0; i < g->l; ++i) {
		g->lock[i] = 0;
		g->type[i] = 0;
	}
	for(i = 0; i < g->l; ++i) {
		char c = g->data[i];
		x = i % g->w;
		y = i / g->w;
		if(c == '.' || g->lock[i])
			continue;
		if(cilc(c) && !bang(g, x, y))
			continue;
		operate(g, x, y, c);
	}
	g->f++;
init(void)
{
	int i, j;
	if(SDL_Init(SDL_INIT_VIDEO) < 0)
		return error("Init", SDL_GetError());
	gWindow = SDL_CreateWindow("Toy",
		SDL_WINDOWPOS_UNDEFINED,
		SDL_WINDOWPOS_UNDEFINED,
		WIDTH * ZOOM,
		HEIGHT * ZOOM,
		SDL_WINDOW_SHOWN);
	if(gWindow == NULL)
		return error("Window", SDL_GetError());
	gRenderer = SDL_CreateRenderer(gWindow, -1, 0);
	if(gRenderer == NULL)
		return error("Renderer", SDL_GetError());
	gTexture = SDL_CreateTexture(gRenderer,
		SDL_PIXELFORMAT_ARGB8888,
		SDL_TEXTUREACCESS_STATIC,
		WIDTH,
		HEIGHT);
	if(gTexture == NULL)
		return error("Texture", SDL_GetError());
	pixels = (uint32_t *)malloc(WIDTH * HEIGHT * sizeof(uint32_t));
	if(pixels == NULL)
		return error("Pixels", "Failed to allocate memory");
	for(i = 0; i < HEIGHT; i++)
		for(j = 0; j < WIDTH; j++)
			pixels[i * WIDTH + j] = color1;
	return 1;
}

int
disk(FILE *f, Grid *g)
{
	char c;
	g->l = 0;
	while((c = fgetc(f)) != EOF && g->l < MAXSZ) {
		if(c == '\n') {
			if(g->w == 0)
				g->w = g->l;
			g->h = g->l / g->w;
		} else {
			g->type[g->l] = 0;
			g->data[g->l++] = c;
		}
	}
	return g->w > 2 && g->h > 2;
}

int
error(char *name)
{
	printf("Error: %s\n", name);
	return 0;
}

int
main(int argc, char *argv[])
{
	FILE *f;
	int limit = 3;
	Grid g;
	g.w = 0;
	g.h = 0;
	g.f = 0;
	g.r = 1;
	if(argc < 2)
		return error("No input.");
	f = fopen(argv[1], "r");
	if(!f)
		return error("Missing input.");
	if(!disk(f, &g))
		return error("Invalid grid");
	while(g.f < limit) {
		run(&g);
		print(&g);
	int ticknext = 0;

	if(!init())
		return error("Init", "Failure");

	newchr();
	select(0, 0, 0, 0);
	draw(pixels);

	while(1) {
		int tick = SDL_GetTicks();
		SDL_Event event;
		if(tick < ticknext)
			SDL_Delay(ticknext - tick);
		ticknext = tick + (1000 / FPS);
		while(SDL_PollEvent(&event) != 0) {
			if(event.type == SDL_QUIT)
				quit();
			else if(event.type == SDL_MOUSEBUTTONUP ||
					event.type == SDL_MOUSEBUTTONDOWN ||
					event.type == SDL_MOUSEMOTION) {
				domouse(&event);
			} else if(event.type == SDL_KEYDOWN)
				dokey(&event);
			else if(event.type == SDL_WINDOWEVENT)
				if(event.window.event == SDL_WINDOWEVENT_EXPOSED)
					draw(pixels);
		}
	}
	quit();
	return 0;
}