Remote agent/Simulation/C: Difference between revisions

From Rosetta Code
Content added Content deleted
m (Use {{collection}}, not {{implementation}}.)
imported>Katsumi
No edit summary
 
(5 intermediate revisions by 4 users not shown)
Line 1: Line 1:
{{collection|Remote agent/Simulation}}
<noinclude>{{collection|Remote agent/Simulation}}</noinclude>
Server/client programs share a lot of code, so they are all lumped together here. To compile, dump all three files in the same dir, and compile server.c and client.c separately. The server binds to port 11111. Both server and client display the current map, though only the server has the whole world, the client only knows cells the agent has been to.
Server/client programs share a lot of code, so they are all lumped together here. To compile, dump all three files in the same dir, and compile server.c and client.c separately. The server binds to port 11111. Both server and client display the current map, though only the server has the whole world, the client only knows cells the agent has been to.


server.c:
;<nowiki>server.c:</nowiki>
<syntaxhighlight lang="c" line>
<lang c>#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdlib.h>
#include <unistd.h>
#include <unistd.h>
Line 131: Line 132:


return 1;
return 1;
}</lang>
}</syntaxhighlight>


client.c:
;<nowiki>client.c:</nowiki>
<syntaxhighlight lang="c" line>
<lang c>#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdlib.h>
#include <unistd.h>
#include <unistd.h>
Line 145: Line 147:
#include <err.h>
#include <err.h>
#include "common.c"
#include "common.c"

typedef struct task_node_t {
typedef struct task_node_t {
int x, y;
int x, y;
struct task_node_t *prev, *sibling, *chain;
struct task_node_t *prev, *sibling, *chain;
} task_node_t, *task;
} task_node_t, *task;

task pool = 0, used = 0;
task pool = 0, used = 0;
task find_place_to_go(byte **f);
task find_place_to_go(byte **f);
Line 165: Line 167:
return used;
return used;
}
}

void remove_tasks()
void remove_tasks()
{
{
Line 176: Line 178:
}
}
}
}

int w, h;
int w, h;
int do_client_stuff(int);
int do_client_stuff(int);

int main(int c, char *v[])
int main(int c, char *v[])
{
{
Line 185: Line 187:
struct sockaddr_in peer;
struct sockaddr_in peer;
struct hostent *server;
struct hostent *server;

if (c < 3) err(1, "Usage: %s host port\n", v[0]);
if (c < 3) err(1, "Usage: %s host port\n", v[0]);

port = atoi(v[2]);
port = atoi(v[2]);
if (port <= 0) err(1, "bad port: %d\n", port);
if (port <= 0) err(1, "bad port: %d\n", port);

server = gethostbyname(v[1]);
server = gethostbyname(v[1]);
if (!server) err(1, "Unknown server %s", v[1]);
if (!server) err(1, "Unknown server %s", v[1]);

if ((sfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
if ((sfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
err(1, "Can't open socket");
err(1, "Can't open socket");

memset(&peer, 0, sizeof(peer));
memset(&peer, 0, sizeof(peer));
peer.sin_family = AF_INET;
peer.sin_family = AF_INET;
memcpy(server->h_addr, &peer.sin_addr.s_addr, server->h_length);
memcpy(&peer.sin_addr.s_addr, server->h_addr, server->h_length);
peer.sin_port = htons(port);
peer.sin_port = htons(port);

if (connect(sfd, (struct sockaddr *)&peer, sizeof(peer)) < 0)
if (connect(sfd, (struct sockaddr *)&peer, sizeof(peer)) < 0)
err(1, "Can't connect to %s port %d", v[1], port);
err(1, "Can't connect to %s port %d", v[1], port);

puts("\033[H\033[J");
puts("\033[H\033[J");
do_client_stuff(sfd);
do_client_stuff(sfd);
close(sfd);
close(sfd);

return 0;
return 0;
}
}

byte ** init_map()
byte ** init_map()
{
{
Line 220: Line 222:
return f;
return f;
}
}

byte **f;
byte **f;
int try_forward(int);
int try_forward(int);
void say_and_listen(int, int, int*);
void say_and_listen(int, int, int*);

int do_client_stuff(int fd)
int do_client_stuff(int fd)
{
{
Line 230: Line 232:
int response[MAX_CMD];
int response[MAX_CMD];
task tgt;
task tgt;

if (c != 'A') {
if (c != 'A') {
fprintf(stderr, "Bad handshake: %c\n", c);
fprintf(stderr, "Bad handshake: %c\n", c);
Line 238: Line 240:
f = init_map();
f = init_map();
c = 0;
c = 0;

while (c != -1) {
while (c != -1) {
x = agent.x, y = agent.y;
x = agent.x, y = agent.y;
show_field(f, w, h);
show_field(f, w, h);

if (agent.ball == -1 && cell_ball(f, x, y) >= 0
if (agent.ball == -1 && cell_ball(f, x, y) >= 0
&& cell_ball(f, x, y) != cell_color(f, x, y))
&& cell_ball(f, x, y) != cell_color(f, x, y))
Line 252: Line 254:
continue;
continue;
}
}

if (agent.ball == cell_color(f, x, y) && cell_ball(f, x, y) == -1) {
if (agent.ball == cell_color(f, x, y) && cell_ball(f, x, y) == -1) {
say_and_listen(fd, c_drop, response);
say_and_listen(fd, c_drop, response);
Line 261: Line 263:
continue;
continue;
}
}

goto_place(fd, f, tgt = find_place_to_go(f));
goto_place(fd, f, tgt = find_place_to_go(f));
continue;
continue;
Line 267: Line 269:
return 0;
return 0;
}
}

void expand_map()
void expand_map()
{
{
Line 273: Line 275:
int dx, dy;
int dx, dy;
byte **nf;
byte **nf;

switch(d) {
switch(d) {
case 0: case 2: w2++; break;
case 0: case 2: w2++; break;
default: h2++; break;
default: h2++; break;
}
}

nf = byte_array(w2, h2);
nf = byte_array(w2, h2);
FOR(i, h2) FOR(j, w2) nf[i][j] = unknown;
FOR(i, h2) FOR(j, w2) nf[i][j] = unknown;

dx = agent.x == 0;
dx = agent.x == 0;
dy = agent.y == 0;
dy = agent.y == 0;

FOR(i, h) FOR(j, w) nf[i + dy][j + dx] = f[i][j];
FOR(i, h) FOR(j, w) nf[i + dy][j + dx] = f[i][j];

if (!agent.x) agent.x = 1;
if (!agent.x) agent.x = 1;
if (!agent.y) agent.y = 1;
if (!agent.y) agent.y = 1;

w = w2, h = h2;
w = w2, h = h2;
printf("expand: %d %d\n", w, h);
printf("expand: %d %d\n", w, h);
Line 295: Line 297:
f = nf;
f = nf;
}
}

int try_forward(int fd)
int try_forward(int fd)
{
{
Line 327: Line 329:
return 1;
return 1;
}
}

void say_and_listen(int fd, int cmd, int *resp)
void say_and_listen(int fd, int cmd, int *resp)
{
{
Line 340: Line 342:
}
}
}
}

int is_interesting(byte **f, task t)
int is_interesting(byte **f, task t)
{
{
Line 346: Line 348:
if ((f[y][x] & unknown))
if ((f[y][x] & unknown))
return y != agent.y || x != agent.x;
return y != agent.y || x != agent.x;

c = cell_color(f, x, y);
c = cell_color(f, x, y);
b = cell_ball(f, x, y);
b = cell_ball(f, x, y);
a = agent.ball;
a = agent.ball;

if (a >= 0)
if (a >= 0)
return a == c && b == -1;
return a == c && b == -1;
else if (b >= 0 && b != c)
else if (b >= 0 && b != c)
return 1;
return 1;

return 0;
return 0;
}
}

task find_place_to_go(byte **f)
task find_place_to_go(byte **f)
{
{
Line 368: Line 370:
current->x = agent.x, current->y = agent.y;
current->x = agent.x, current->y = agent.y;
f[current->y][current->x] |= marked;
f[current->y][current->x] |= marked;

if (is_interesting(f, current)) return current;
if (is_interesting(f, current)) return current;

while (current) {
while (current) {
while (current) {
while (current) {
Line 390: Line 392:
next = 0;
next = 0;
}
}
puts("nowhere to go");
abort();
abort();
}
}

void goto_place(int fd, byte **f, task t)
void goto_place(int fd, byte **f, task t)
{
{
Line 399: Line 402:
usleep(50000);
usleep(50000);
t->prev = 0;
t->prev = 0;

if (agent.x == t->x && agent.y == t->y) return;
if (agent.x == t->x && agent.y == t->y) return;
while ( t->x != agent.x + dirs[agent.facing][0] ||
while ( t->x != agent.x + dirs[agent.facing][0] ||
Line 405: Line 408:
turn(c_cw), say(c_cw), hear();
turn(c_cw), say(c_cw), hear();
try_forward(fd);
try_forward(fd);
}
}</lang>
</syntaxhighlight>


and share source "common.c":
;<nowiki>common.c:</nowiki>
<syntaxhighlight lang="c" line>
<lang c>struct agent_t{ int x, y, facing, ball; } agent = { 0, 0, 0, -1 };
struct agent_t{ int x, y, facing, ball; } agent = { 0, 0, 0, -1 };
enum {
enum {
c_fwd, c_cw, c_ccw, c_get, c_drop, c_over, c_stop,
c_fwd, c_cw, c_ccw, c_get, c_drop, c_over, c_stop,
Line 419: Line 424:
};
};


char *cmds = "^><@!+.RGYBrgyb|SAsa";
const char *cmds = "^><@!+.RGYBrgyb|SAsa";
inline int cmd_to_char(int c) { return cmds[c]; }
inline int cmd_to_char(int c) { return cmds[c]; }
inline int char_to_cmd(int c) {
inline int char_to_cmd(int c) {
Line 550: Line 555:
}
}


char *colors[] = { "31", "32", "33", "34" };
const char *colors[] = { "31", "32", "33", "34" };
char ag[] = { "<V>A" };
const char *ag = "<V>A";


inline int cell_color(byte **f, int x, int y)
inline int cell_color(byte **f, int x, int y)
Line 611: Line 616:
if (dir == c_cw)
if (dir == c_cw)
if (++agent.facing > 3) agent.facing = 0;
if (++agent.facing > 3) agent.facing = 0;
}
}</lang>
</syntaxhighlight>

Latest revision as of 11:22, 20 August 2023

Remote agent/Simulation/C is part of Remote agent/Simulation. You may find other members of Remote agent/Simulation at Category:Remote agent/Simulation.

Server/client programs share a lot of code, so they are all lumped together here. To compile, dump all three files in the same dir, and compile server.c and client.c separately. The server binds to port 11111. Both server and client display the current map, though only the server has the whole world, the client only knows cells the agent has been to.

server.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <err.h>
#include "common.c"

#define W 30
#define H 30

int do_server_stuff(int);

int main()
{
	int one = 1, client_fd, port = 11111;
	struct sockaddr_in svr_addr, cli_addr;
	socklen_t sin_len = sizeof(cli_addr);
 
	int sock = socket(AF_INET, SOCK_STREAM, 0);
	if (sock < 0)
		err(1, "can't open socket");
 
	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
 
	svr_addr.sin_family = AF_INET;
	svr_addr.sin_addr.s_addr = INADDR_ANY;
	svr_addr.sin_port = htons(port);
 
	if (bind(sock, (struct sockaddr *) &svr_addr, sizeof(svr_addr)) == -1) {
		close(sock);
		err(1, "Can't bind");
	}
 
	listen(sock, 5);
	while (1) {
		client_fd = accept(sock, (struct sockaddr *) &cli_addr, &sin_len);
 
		if (client_fd == -1) {
			perror("Can't accept");
			continue;
		}
 		puts("\033[H\033[J");
 		do_server_stuff(client_fd);
		close(client_fd);
	}
}

int work_done(byte **f)
{
	int x, y, b;
	FOR(y, H) FOR(x, W) {
		b = cell_ball(f, x, y);
		if (b < 0) continue;
		if (b != cell_color(f, x, y)) return 0;
	}
	return 1;
}

byte **f = 0;
int do_server_stuff(int fd)
{
	int c, x, y, b;
	agent.ball = -1;

	say_l('A');

	if (f) free(f);
	f = make_field(W, H);
	c = hear_l();
	if (c != 'A') {
		fprintf(stderr, "Bad hand shake: %c\n", c);
		return 0;
	}

	while (1) {
		show_field(f, W, H);
		c = hear();
		x = agent.x;
		y = agent.y;

		if (c == -1) break;
		switch(c) {
		case c_fwd:
			if (!agent_forward(f)) {
				say(a_bump);
				say(c_stop);
				continue;
			}
			say(s_red + cell_color(f, agent.x, agent.y));

			c = cell_ball(f, agent.x, agent.y);
			if (c >= 0) say(b_red + c);

			say(c_stop);
			continue;
		case c_cw:
			turn(c_cw), say(c_stop); continue;
		case c_ccw:
			turn(c_ccw), say(c_stop);continue;
		case c_get:
			b = cell_ball(f, agent.x, agent.y);
			if (b == -1) say(s_empty);
			if (agent.ball >= 0) b = -1, say(a_full);
			if (b >= 0) {
				agent.ball = b;
				f[y][x] &= ~ball;
			}
			say(c_stop);
			continue;
		case c_drop:
			if ((b = agent.ball) == -1) say(a_empty);
			if (cell_ball(f, x, y) >= 0) b = -1, say(s_full);
			if (b >= 0) {
				agent.ball = -1;
				f[y][x] &= ~12;
				f[y][x] |= ball | (b * 4);
			}
			if (work_done(f)) say(c_over);
			say(c_stop);
			continue;
		}
	}

	return 1;
}
client.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <err.h>
#include "common.c"
 
typedef struct task_node_t {
	int x, y;
	struct task_node_t *prev, *sibling, *chain;
} task_node_t, *task;
 
task pool = 0, used = 0;
task find_place_to_go(byte **f);
int is_interesting(byte **f, task t);
void goto_place(int fd, byte **f, task t);
task new_task_node()
{
	task t;
	if (!pool) pool = calloc(1, sizeof(task_node_t));
	t = pool->chain;
	pool->chain = used;
	used = pool;
	pool = t;
	return used;
}
 
void remove_tasks()
{
	task t;
	while (used) {
		t = used;
		used = used->chain;
		t->chain = pool;
		pool = t;
	}
}
 
int w, h;
int do_client_stuff(int);
 
int main(int c, char *v[])
{
	int sfd, port;
	struct sockaddr_in peer;
	struct hostent *server;
 
	if (c < 3) err(1, "Usage: %s host port\n", v[0]);
 
	port = atoi(v[2]);
	if (port <= 0) err(1, "bad port: %d\n", port);
 
	server = gethostbyname(v[1]);
	if (!server) err(1, "Unknown server %s", v[1]);
 
	if ((sfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
		err(1, "Can't open socket");
 
	memset(&peer, 0, sizeof(peer));
	peer.sin_family = AF_INET;
	memcpy(&peer.sin_addr.s_addr, server->h_addr, server->h_length);
	peer.sin_port = htons(port);
 
	if (connect(sfd, (struct sockaddr *)&peer, sizeof(peer)) < 0)
		err(1, "Can't connect to %s port %d", v[1], port);
 
	puts("\033[H\033[J");
	do_client_stuff(sfd);
	close(sfd);
 
	return 0;
}
 
byte ** init_map()
{
	int i, j;
	byte **f = byte_array(w = 3, h = 3);
	FOR (i, h) FOR(j, w) f[i][j] = unknown;
	agent.x = agent.y = 1;
	return f;
}
 
byte **f;
int try_forward(int);
void say_and_listen(int, int, int*);
 
int do_client_stuff(int fd)
{
	int c = hear_l(), x, y;
	int response[MAX_CMD];
	task tgt;
 
	if (c != 'A') {
		fprintf(stderr, "Bad handshake: %c\n", c);
		return 0;
	}
	say_l('A');
	f = init_map();
	c = 0;
 
	while (c != -1) {
		x = agent.x, y = agent.y;
		show_field(f, w, h);
 
		if (agent.ball == -1 && cell_ball(f, x, y) >= 0
				&& cell_ball(f, x, y) != cell_color(f, x, y))
		{
			say_and_listen(fd, c_get, response);
			if (response[a_full] || response[s_empty]) continue;
			agent.ball = cell_ball(f, x, y);
			f[y][x] &= ~ball;
			continue;
		}
 
		if (agent.ball == cell_color(f, x, y) && cell_ball(f, x, y) == -1) {
			say_and_listen(fd, c_drop, response);
			if (response[s_full] || response[a_empty]) continue;
			f[y][x] |= ball | (agent.ball << 2);
			agent.ball = -1;
			if (response[c_over]) return 1;
			continue;
		}
 
		goto_place(fd, f, tgt = find_place_to_go(f));
		continue;
	}
	return 0;
}
 
void expand_map()
{
	int w2 = w, h2 = h, i, j, d = agent.facing;
	int dx, dy;
	byte **nf;
 
	switch(d) {
	case 0: case 2: w2++; break;
	default: h2++; break;
	}
 
	nf = byte_array(w2, h2);
	FOR(i, h2) FOR(j, w2) nf[i][j] = unknown;
 
	dx = agent.x == 0;
	dy = agent.y == 0;
 
	FOR(i, h) FOR(j, w) nf[i + dy][j + dx] = f[i][j];
 
	if (!agent.x) agent.x = 1;
	if (!agent.y) agent.y = 1;
 
	w = w2, h = h2;
	printf("expand: %d %d\n", w, h);
	free(f);
	f = nf;	
}
 
int try_forward(int fd)
{
	int c, x, y, response[MAX_CMD] = {0};
	say(c_fwd);
	while (1) {
		c = hear();
		if (c == -1) return 0;
		if (c == c_stop) break;
		response[c] = 1;
	}
	x = agent.x + dirs[agent.facing][0];
	y = agent.y + dirs[agent.facing][1];
	if (response[a_bump]) {
		f[y][x] = wall;
		return 1;
	}
	agent.x = x, agent.y = y;
	for (c = s_red; c <= s_blue; c++) {
		if (!response[c]) continue;
		f[y][x] = (c - s_red);
		break;
	}
	for (c = b_red; c <= b_blue; c++) {
		if (!response[c]) continue;
		f[y][x] |= ball | ((c - b_red) * 4);
		break;
	}
	if (y == h - 1 || y == 0 || x == w - 1 || x == 0)
		expand_map();
	return 1;
}
 
void say_and_listen(int fd, int cmd, int *resp)
{
	int c;
	memset(resp, 0, sizeof(int) * MAX_CMD);
	say(cmd);
	while (1) {
		c = hear();
		if (c == -1) exit(1);
		resp[c] = 1;
		if (c == c_stop) return;
	}
}
 
int is_interesting(byte **f, task t)
{
	int x = t->x, y = t->y, a, b, c;
	if ((f[y][x] & unknown))
		return y != agent.y || x != agent.x;
 
	c = cell_color(f, x, y);
	b = cell_ball(f, x, y);
	a = agent.ball;
 
	if (a >= 0)
		return a == c && b == -1;
	else if (b >= 0 && b != c)
		return 1;
 
	return 0;
}
 
task find_place_to_go(byte **f)
{
	int d, x, y;
	task current = 0, next = 0, t;
	remove_tasks();
	FOR(y, h) FOR(x, w) f[y][x] &= ~marked;
	current = new_task_node();
	current->x = agent.x, current->y = agent.y;
	f[current->y][current->x] |= marked;
 
	if (is_interesting(f, current)) return current;
 
	while (current) {
		while (current) {
			FOR(d, 4) {
				x = current->x + dirs[d][0];
				y = current->y + dirs[d][1];
				if (x < 0 || x >= w || y < 0 || y >= h) continue;
				if (f[y][x] & (wall | marked)) continue;
				f[y][x] |= marked;
				t = new_task_node();
				t->x = x, t->y = y, t->prev = current;
				t->sibling = next;
				next = t;
				if (is_interesting(f, t)) return t;
			}
			current = current->sibling;
		}
		current = next;
		next = 0;
	}
	puts("nowhere to go");
	abort();
}
 
void goto_place(int fd, byte **f, task t)
{
	show_field(f, w, h);
	if (t->prev) goto_place(fd, f, t->prev);
	usleep(50000);
	t->prev = 0;
 
	if (agent.x == t->x && agent.y == t->y) return;
	while (	t->x != agent.x + dirs[agent.facing][0] ||
		t->y != agent.y + dirs[agent.facing][1])
			turn(c_cw), say(c_cw), hear();
	try_forward(fd);
}
common.c:
struct agent_t{ int x, y, facing, ball; } agent = { 0, 0, 0, -1 };
enum {
	c_fwd, c_cw, c_ccw, c_get, c_drop, c_over, c_stop,
	s_red, s_green, s_yellow, s_blue,
	b_red, b_green, b_yellow, b_blue,
	a_bump,
	s_full, a_full,
	s_empty, a_empty,
	MAX_CMD
};

const char *cmds = "^><@!+.RGYBrgyb|SAsa";
inline int cmd_to_char(int c) { return cmds[c]; }
inline int char_to_cmd(int c) {
	int i;
	for (i = 0; cmds[i] && cmds[i] != c; i++);
	return cmds[i] ? i : -1;
}

#define say_l(x) send_cmd(fd, x)
#define hear_l() read_cmd(fd)
#define say(x) send_cmd(fd, cmd_to_char(x))
#define hear() char_to_cmd(read_cmd(fd))

int irand(int n)
{
	int r, rand_max = RAND_MAX - RAND_MAX % n;
	while ((r = rand()) >= rand_max);
	return r / (rand_max / n);
}

int wrand(int *x, int l)
{
	int i, sum = 0;
	for (i = 0; i < l; i++) sum += x[i];
	sum = irand(sum);
	for (i = 0; i < l; sum -= x[i++])
		if (sum < x[i]) break;
	return i;
}

int read_fd(int fd, char *buf, int len)
{
	int l;
	l = read(fd, buf, len);
	if (l < 0) return 0;
	buf[l] = 0;
	return l;
}

int write_fd(int fd, char *buf, int len)
{
	int l = write(fd, buf, len);
	if (l != len) return 0;
	return 1;
}

int read_cmd(int fd)
{
	char buf[2];
	if (read_fd(fd, buf, 1) <= 0) return -1;
	return buf[0];
}

int send_cmd(int fd, char cmd)
{
	char c = cmd;
	return write_fd(fd, &c, 1);
}

typedef unsigned char byte;
enum { red, green, yellow, blue, ball = 16, wall = 32, unknown = 64, marked = 128 };

#define FOR(a, x) for (a = 0; a < x; a++)
byte **byte_array(int w, int h)
{
	int i;
	byte **f = calloc(1, sizeof(byte *) * h + w * h);
	f[0] = (byte*)(f + h);
	FOR(i, h - 1) f[i + 1] = f[i] + w;
	return f;
}

void shuffle(int *x, int len)
{
	int i, j, t;
	for (i = len - 1; i > 0; i--) {
		j = irand(i + 1);
		t = x[i], x[i] = x[j], x[j] = t;
	}
}

int dirs[4][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};
int randomize(byte **f, int w, int h, int x, int y, int rem)
{
	int d[4] = {0, 1, 2, 3}, i;
	if (x <= 0 || x >= w - 1 || y <= 0 || y >= h - 1) return rem;
	if (f[y][x] != wall) return rem;
	
	f[y][x] = 0;
	--rem;

	shuffle(d, 4);
	for (i = 0; i < 4 && rem; i++) {
		rem = randomize(f, w, h,
			x + dirs[d[i]][0], y + dirs[d[i]][1], rem);
	}
	return rem;
}

byte **make_field(int w, int h)
{
	int i, j, c, x, cells[4] = {0};
	byte **f = byte_array(w, h);
	FOR(i, w) FOR(j, h) f[i][j] = wall;
	randomize(f, w, h, irand(w - 2) + 1, irand(h - 2) + 1,
		(h - 2) * (w - 2) * 1 / 2);

	FOR(i, w) FOR(j, h) {
		if (f[i][j] == wall) continue;
		cells[ f[i][j] = irand(4) ] ++;
	}

	x = 0;
	FOR(i, w) FOR(j, h) {
		if (f[i][j] == wall) continue;
		if (irand(4)) {
		/* 1 in 4 chance to place a ball, else try place agent */
			if (!irand(++x))
				agent.x = j, agent.y = i;
			continue;
		}
		c = wrand(cells, 4);
		cells[c] --;
		f[i][j] |= ball | c * 4;
	}

	agent.facing = irand(3);

	return f;
}

const char *colors[] = { "31", "32", "33", "34" };
const char *ag = "<V>A";

inline int cell_color(byte **f, int x, int y)
{
	return f[y][x] & 3;
}

inline int cell_ball(byte **f, int x, int y)
{
	if (!(f[y][x] & ball) || (f[y][x] == unknown)) return -1;
	return (f[y][x] >> 2) & 3;
}

void show_field(byte **f, int w, int h)
{
	int i, j;

	printf("\033[H");
	FOR(i, h) {
		FOR(j, w) {
			if (f[i][j] == wall) {
				printf("## ");
				continue;
			}
			if (f[i][j] == unknown) {
				printf("-- ");
				continue;
			}

			printf("\033[7;%sm", colors[cell_color(f, j, i)]);
			putchar(j == agent.x && i == agent.y ? ag[agent.facing] : ' ');

			if (cell_ball(f, j, i) >= 0)
				printf("\033[%smO\033[m ", colors[cell_ball(f, j, i)]);
			else
				printf(" \033[m ");
 		}
		putchar('\n');
	}
	printf("agent carry: %d\n", agent.ball);
	fflush(stdout);
}

int agent_forward(byte **f)
{
	int x, y;
	x = agent.x + dirs[agent.facing][0];
	y = agent.y + dirs[agent.facing][1];
	if (f[y][x] == wall) return 0;
	agent.x = x;
	agent.y = y;
	return 1;
}

void turn(int dir)
{
	if (dir == c_ccw)
		if (--agent.facing < 0) agent.facing = 3;
	if (dir == c_cw)
		if (++agent.facing > 3) agent.facing = 0;
}