💾 Archived View for runjimmyrunrunyoufuckerrun.com › src › games › draughts.c captured on 2021-12-17 at 13:26:06.

View Raw

More Information

-=-=-=-=-=-=-

#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <cursor.h>
enum{
	BLACK, WHITE, LSQ, BAIZE, DSQ, OUTLINE, HSQ, HUES,
	HL = 2, MAN = 4, KING = 8, ⛂ = 4, ⛀ = 5, ⛃ = 8, ⛁ = 9, ∅ = 16, ☠ = 49,
	⭦ = -5, ⭧ = -4, ⭩ = 4, ⭨ = 5, ∞ = 0x7fffffff,
	Enine = 8, SQ = 50, RAD = 20, MSGLEN = 128, MSGS = 5
};
ulong rgb[HUES] = {
	[BLACK] DRed, [WHITE] DWhite, [BAIZE] DDarkgreen, [OUTLINE] DBlack,
	[DSQ] DYellowgreen, [LSQ] DPaleyellow, [HSQ] DPalegreygreen
};
Cursor ⌛ = {{-1, -1},
	{0xff, 0x80, 0xff, 0x80, 0xff, 0x00, 0xfe, 0x00,
	 0xff, 0x00, 0xff, 0x80, 0xff, 0xc0, 0xef, 0xe0,
	 0xc7, 0xf0, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0xc0,
	 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, 0x03, 0xff},
	{0x00, 0x00, 0x7f, 0x00, 0x7e, 0x00, 0x7c, 0x00,
	 0x7e, 0x00, 0x7f, 0x00, 0x6f, 0x80, 0x47, 0xc0,
	 0x03, 0xe0, 0x01, 0xf0, 0x00, 0xe0, 0x00, 0x40,
	 0x00, 0x00, 0x01, 0xb6, 0x01, 0xb6, 0x00, 0x00}};
char sq[] = {
	☠,		☠,		☠,		☠,		☠,
		⛀,		⛀,		⛀,		⛀,
	⛀,		⛀,		⛀,		⛀,		☠,
		⛀,		⛀,		⛀,		⛀,
	∅,		∅,		∅,		∅,		☠,
		∅,		∅,		∅,		∅,
	⛂,		⛂,		⛂,		⛂,		☠,
		⛂,		⛂,		⛂,		⛂,
	⛂,		⛂,		⛂,		⛂,		☠,
		☠,		☠,		☠,		☠
};
char coordof[] = {
	0,		0,		0,		0,		0,
		28,		29,		30,		31,
	24,		25,		26,		27,		0,
		20,		21,		22,		23,
	16,		17,		18,		19,		0,
		12,		13,		14,		15,
	8,		9,		10,		11,		0,
		4,		5,		6,		7,
	0,		1,		2,		3,		0,
		0,		0,		0,		0
};
char indexof[] = {
	36, 37, 38, 39, 32, 33, 34, 35, 27, 28, 29, 30, 23, 24, 25, 26,
	18, 19, 20, 21, 14, 15, 16, 17, 9, 10, 11, 12, 5, 6, 7, 8, 0
};
int direction[] = {⭦, ⭧, ⭩, ⭨};
Rectangle square[32], side[2], msgs, moves;
Point centre[32];
Image *hue[HUES];
char p[2][64], buf[4096], msg[MSGS][MSGLEN], *ms, *m, *me, *u;
int t, flip, msgn, fd = -1;
int aijump(int, int, int, int);
int aislide(int, int, int, int);
void
drawboard(void){
	Point o;
	char *s, *e;
	int i, j, n;
	for(i = 0; i < 32; i++){
		n = indexof[i];
		draw(screen, square[i], hue[DSQ | sq[n] & HL], nil, ZP);
		if(sq[n] < ∅){
			fillellipse(screen, centre[i], RAD, RAD, hue[sq[n] & WHITE], ZP);
			ellipse(screen, centre[i], RAD, RAD, 0, hue[OUTLINE], ZP);
			if(sq[n] & KING)
				ellipse(screen, centre[i], RAD >> 1, RAD >> 1, 0, hue[OUTLINE], ZP);
		}
	}
	draw(screen, moves, hue[BAIZE], nil, ZP);
	o = screen->r.min;
	i = t - 10 * SQ / font->height & ~1;
	if(i < 0){
		o.y += i / -2 * font->height;
		i = 0;
	}
	for(j = 0, s = ms; j < i; j++) do ++s; while(s[-1] != ' ');
	while(s < me && o.y + font->height < moves.max.y){
		for(e = s + 4; e[-1] != ' '; e++);
		o = stringn(screen, o, hue[WHITE << (i < t)], ZP, font, s, e - s);
		s = e;
		if(i++ & WHITE)
			o = Pt(screen->r.min.x, o.y + font->height);
	}
}
void
drawmsgs(void){
	Point o;
	draw(screen, msgs, hue[WHITE], nil, ZP);
	for(o = msgs.min; o.y < msgs.max.y; o.y += font->height){
		string(screen, o, hue[OUTLINE], ZP, font, msg[msgn]);
		msgn = (msgn + 1) % MSGS;
	}
}
void
eresized(int i){
	Rectangle r;
	if(i && getwindow(display, Refnone) < 0) sysfatal("can't reattach to window");
	r.max.y = screen->r.min.y + 10 * SQ;
	if(fd >= 0) r.max.y += MSGS * font->height;
	if((!i || screen->r.max.y != r.max.y) && (i = open("/dev/wctl", OWRITE)) >= 0){
		fprint(i, "resize -dy %d", r.max.y - screen->r.min.y + 2 * Borderwidth);
		close(i);
	}
	r.min.x = screen->r.min.x + screen->r.max.x - 8 * SQ >> 1;
	r.min.y = screen->r.min.y + SQ;
	r.max.x = r.min.x + 8 * SQ;
	r.max.y = r.min.y + 8 * SQ;
	side[flip] = Rect(r.min.x, r.max.y, r.max.x, r.max.y + SQ);
	side[!flip] = Rect(r.min.x, screen->r.min.y, r.max.x, r.min.y);
	moves.min = screen->r.min;
	moves.max.x = r.min.x;
	moves.max.y = r.max.y + SQ;
	if(fd >= 0){
		msgs.min.x = moves.min.x;
		msgs.min.y = moves.max.y;
		msgs.max = screen->r.max;
	}
	if(flip){
		centre[0].x = r.max.x - SQ / 2;
		centre[0].y = r.min.y + SQ / 2;
		for(i = 1; i < 32; i++){
			if(i & 3) centre[i] = Pt(centre[i - 1].x - SQ * 2, centre[i - 1].y);
			else{
				centre[i].x = i & 4 ? r.max.x - SQ * 3 / 2 : r.max.x - SQ / 2;
				centre[i].y = centre[i - 1].y + SQ;
			}
		}
	}
	else{
		centre[0].x = r.min.x + SQ / 2;
		centre[0].y = r.max.y - SQ / 2;
		for(i = 1; i < 32; i++){
			if(i & 3) centre[i] = Pt(centre[i - 1].x + SQ * 2, centre[i - 1].y);
			else{
				centre[i].x = i & 4 ? r.min.x + SQ * 3 / 2 : r.min.x + SQ / 2;
				centre[i].y = centre[i - 1].y - SQ;
			}
		}
	}
	draw(screen, screen->r, hue[BAIZE], nil, ZP);
	draw(screen, r, hue[LSQ], nil, ZP);
	r = Rect(SQ/-2, SQ/-2, SQ/2, SQ/2);
	for(i = 0; i < 32; i++) square[i] = rectaddpt(r, centre[i]);
	drawboard();
	if(fd >= 0) drawmsgs();
	r.min.x = side[flip].min.x + side[flip].max.x - stringwidth(font, p[flip]) >> 1;
	r.min.y = side[flip].min.y + side[flip].max.y - font->height >> 1;
	string(screen, r.min, hue[LSQ], ZP, font, p[flip]);
	r.min.x = side[!flip].min.x + side[!flip].max.x - stringwidth(font, p[!flip]) >> 1;
	r.min.y = side[!flip].min.y + side[!flip].max.y - font->height >> 1;
	string(screen, r.min, hue[LSQ], ZP, font, p[!flip]);
}
void
clearhl(void){
	int i;
	for(i = 5; i < 40; i++) sq[i] &= ~HL;
}
int
canjump(int from, int dir){
	int enemy;
	enemy = ~sq[from] & WHITE;
	return (sq[from] & KING
	|| enemy == WHITE && (dir == ⭦ || dir == ⭧)
	|| enemy == BLACK && (dir == ⭩ || dir == ⭨))
	&& (sq[from + dir] & ☠) == enemy && sq[from + dir + dir] == ∅;
}
int
jumper(int i){
	return canjump(i, ⭦) || canjump(i, ⭧) || canjump(i, ⭩) || canjump(i, ⭨);
}
int
canslide(int from, int dir){
	int colour;
	colour = sq[from] & WHITE;
	return (sq[from] & KING
	|| colour == BLACK && (dir == ⭦ || dir == ⭧)
	|| colour == WHITE && (dir == ⭩ || dir == ⭨))
	&& sq[from + dir] == ∅;
}
void
checkgameover(void){
	int i, hl, canmove;
	clearhl();
	hl = strcmp(p[t & 1], u) || fd >= 0 && m < me ? 0 : HL;
	canmove = 0;
	for(i = 5; i < 40; i++)
		if(!(sq[i] & ☠ ^ t & WHITE) && jumper(i) && ++canmove) sq[i] |= hl;
	if(!canmove)
		for(i = 5; i < 40; i++)
			if(!(sq[i] & ☠ ^ t & WHITE) &&
			(canslide(i, ⭦) || canslide(i, ⭧) || canslide(i, ⭩) || canslide(i, ⭨)) && ++canmove)
				sq[i] |= hl;
	if(canmove || fd < 0) return;
	me = seprint(me, buf + sizeof(buf), "# %s vs %s: %s won\n", p[0], p[1], p[~t & 1]);
	write(fd, m, me - m);
	close(fd);
	write(1, buf, me - buf);
	exits(nil);
}
int
aisubjump(int d, int α, int β, int from, char *stash){
	int i, to, prisoner, val;
	for(i = to = 0; α < β && i < 4; i++)
		if(canjump(from, direction[i])){
			to = from + direction[i] * 2;
			prisoner = sq[from + direction[i]];
			sq[to] = sq[from];
			sq[from] = ∅;
			sq[from + direction[i]] = ∅;
			val = aisubjump(d, α, β, to, seprint(stash, stash + 4, "%c%d",
				prisoner & KING ? 'X' : 'x', coordof[to] + 1));
			if(val > α) α = val;
			sq[from] = sq[to];
			sq[to] = ∅;
			sq[from + direction[i]] = prisoner;
		}
	if(to) return α;
	if(sq[from] & MAN && (from > 35 || from < 9)){
		*stash++ = 'K';
		sq[from] ^= MAN | KING;
	}
	*stash++ = ' ';
	*stash = 0;
	t++;
	val = -aijump(d - 1, -β, -α, 0);
	t--;
	if(stash[-2] == 'K') sq[from] ^= MAN | KING;
	return val;
}
int
aijump(int d, int α, int β, int stashit){
	int i, val;
	char stash[40], *s;
	s = t & WHITE ? stash : seprint(stash, stash + 8, "%d.", t + 2 >> 1);
	*s = 0;
	for(i = 5; α < β && i < 40; i++)
		if(!(sq[i] & ☠ ^ t & WHITE) && jumper(i)){
			val = aisubjump(d, α, β, i, seprint(s, s + 3, "%d", coordof[i] + 1));
			if(val > α){
				α = val;
				if(stashit) strecpy(me, buf + sizeof(buf), stash);
			}
		}
	return *s ? α : aislide(d, α, β, stashit);
}
int
aislide(int d, int α, int β, int stashit){
	int i, from, to, promote, val;
	if(d < 1){	/* evaluate */
		val = 0;
		from = t & WHITE;
		/* material advantage; not too sophisticated */
		for(i = 5; i < 40; i++){
			if((sq[i] & ☠) == from){
				val += (sq[i] & (MAN | KING)) << 7;
			}
			else if((sq[i] & ☠) == !from){
				val -= (sq[i] & (MAN | KING)) << 7;
			}
		}
		return val | rand() & 0xf;
	}
	to = 0;
	for(from = 5; α < β && from < 40; from++)
		if(!(sq[from] & ☠ ^ t & WHITE))
			for(i = 0; α < β && i < 4; i++)
				if(canslide(from, direction[i])){
					to = from + direction[i];
					promote = sq[from] & MAN && (to > 35 || to < 9) ? MAN | KING : 0;
					sq[to] = sq[from] ^ promote;
					sq[from] = ∅;
					t++;
					val = -aijump(d - 1, -β, -α, 0);
					t--;
					sq[from] = sq[to] ^ promote;
					sq[to] = ∅;
					if(val > α){
						α = val;
						if(stashit){
							if(t & WHITE)
								seprint(me, buf + sizeof(buf), "%d-%d%s",
									coordof[from] + 1, coordof[to] + 1,
									promote ? "K " : " ");
							else
								seprint(me, buf + sizeof(buf), "%d.%d-%d%s",
									t + 2 >> 1,
									coordof[from] + 1, coordof[to] + 1,
									promote ? "K " : " ");
						}
					}
				}
	return to ? α : -∞;
}
int
getsq(char **sp){
	int i;
	i = **sp - '0';
	if(i < 1 || i > 9) return 0;
	if(sp[0][1] >= '0' && sp[0][1] <= '9'){
		i = i * 10 + sp[0][1] - '0';
		if(i < 1 || i > 32) return 0;
	}
	*sp += (i > 9) + 1;
	return indexof[i - 1];
}
char*
fwd(void){
	int seq[10], i;
	char *s;
	if(m >= me) return "at end of movetext";
	if(*m == '#'){
		write(1, buf, me - buf);
		exits(nil);
	}
	s = m;
	if(~t & WHITE){
		while(*s >= '0' && *s <= '9') s++;
		if(*s++ != '.') return "bad move number indication";
	}
	seq[0] = getsq(&s);
	if(!seq[0] || sq[seq[0]] & ☠ ^ t & WHITE) return "bad source square";
	if(*s == '-'){
		s++;
		seq[1] = getsq(&s);
		if(!seq[1] || sq[seq[1]] != ∅) return "bad destination square";
		if(*s == 'K'){
			s++;
			if(*s++ != ' ') return "no terminating space";
			sq[seq[0]] ^= MAN | KING;
		}
		else if(*s++ != ' ') return "no terminating space";
		sq[seq[1]] = sq[seq[0]];
		sq[seq[0]] = ∅;
		t++;
		m = s;
		return nil;
	}
	if(*s == 'x' || *s == 'X'){
		i = 0;
		do{
			s++;
			seq[++i] = getsq(&s);
			if(!seq[i] || sq[seq[i]] != ∅ || sq[seq[i - 1] + seq[i] >> 1] & ☠ ^ ~t & WHITE)
				return "bad jump";
		}while(*s == 'x' || *s == 'X');
		if(*s == 'K'){
			s++;
			if(*s++ != ' ') return "no terminating space";
			sq[seq[0]] ^= MAN | KING;
		}
		else if(*s++ != ' ') return "no terminating space";
		sq[seq[i]] = sq[seq[0]];
		sq[seq[0]] = ∅;
		while(i--) sq[seq[i] + seq[i + 1] >> 1] = ∅;
		t++;
		m = s;
		return nil;
	}
	return "bad move indicator";
}
void
bwd(void){
	int from, to, last, prisoner;
	char *s;
	do --m; while(m[-1] != ' ');
	s = m;
	if(t & WHITE) while(*s++ != '.');
	from = to = getsq(&s);
	if(*s == '-'){
		s++;
		to = getsq(&s);
	}
	else do{
		last = to;
		prisoner = (*s++ == 'x' ? MAN : KING) | t & WHITE;
		to = getsq(&s);
		sq[last + to >> 1] = prisoner;
	}while(*s == 'x' || *s == 'X');
	sq[from] = sq[to] ^ (*s == 'K' ? MAN | KING : 0);
	sq[to] = ∅;
	t--;
}
void
main(int argc, char **argv){
	Event e;
	char *s;
	int i, j;
	u = getuser();
	strcpy(p[BLACK], u);
	strcpy(p[WHITE], u);
	m = ms = me = seprint(buf, buf + sizeof(buf), "%s ", argv[0]);
	ARGBEGIN{
	default:
	 	sysfatal("usage: %s [-9 file | movetext]", argv0);
	case '9':
		m = s = EARGF(sysfatal("bad argument to -9"));
		if(strncmp(s, "draughts~", 9)) sysfatal("filename must start with draughts~");
		while(*s++ != '~');
		for(i = 0; i < 63 && s[i] != '~'; i++) p[0][i] = s[i];
		if(i == 63) sysfatal("bad filename format: player separator ~ not found");
		p[0][i] = 0;
		s += i + 1;
		for(i = 0; i < 63 && s[i]; i++) p[1][i] = s[i];
		if(i == 63) sysfatal("bad filename format: player separator ~ not found");
		p[1][i] = 0;
		if((fd = create(m, ORDWR, 0666)) < 0) sysfatal("create: %r");
		i = read(fd, buf, sizeof(buf));
		if(i == 0){
			if(write(fd, buf, me - buf) != me - buf) sysfatal("write: %r");
			m = me;
			break;
		}
		if(i < 1 || i >= sizeof(buf)) sysfatal("read %d bytes: not a 9p draughts file?", i);
		me = buf + i;
		for(m = buf; m < me && *m != ' '; m++);
		if(m == me) sysfatal("no space character found: not a 9p draughts file?");
		ms = ++m;
		break;
	}ARGEND
	if(argc){
		if(fd >= 0) sysfatal("usage: %s [-9 file | movetext]", argv0);
		for(i = 0; i < argc; i++) me = seprint(me, buf + sizeof(buf), "%s ", argv[i]);
	}
	while((s = fwd()) == nil);
	if(m != me) sysfatal("bad movetext: %s: %s", s, m);
	if(fd < 0) s = argv0;
	else{
		s = me;
		seprint(s, buf + sizeof(buf), "%s: %s vs %s", argv0, p[0], p[1]);
	}
	if(initdraw(nil, nil, s) < 0) sysfatal("initdraw: %r");
	for(i = 0; i < HUES; i++)
		if((hue[i] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, rgb[i])) == nil)
			sysfatal("allocimage: %r");
	einit(Emouse | Ekeyboard);
	if(fd >= 0){
		estart(Enine, fd, 256);
		if(strcmp(u, p[t & 1])) esetcursor(&⌛);
	}
	srand(truerand());
	checkgameover();
	if(!strcmp(u, p[1]) && strcmp(u, p[0])) flip = 1;
	eresized(0);
	for(;;){
		if(fd < 0 && m == me && !strncmp(p[t & 1], "ai level ", 9) && !p[t & 1][10]){
			flushimage(display, 1);
			if(ecankbd() || me + 33 >= buf + sizeof(buf)){
				while(ecankbd()) ekbd();	/* gulp */
				strcpy(p[t & 1] + 10, " interrupted");
				checkgameover();
				eresized(0);
				continue;
			}
			esetcursor(&⌛);
			*me = 0;
			aijump(p[t & 1][9] - '0' + 1, -∞, ∞, 1);
			sleep(400);
			esetcursor(nil);
			if(*me == 0){
				strcpy(p[t & 1] + 10, " resigned");
				eresized(0);
				continue;
			}
			while(*me) me++;
			fwd();
			checkgameover();
		}
		else switch(event(&e)){
		case Ekeyboard:
			switch(e.kbdc){
			case 127:
				s = seprint(me, buf + sizeof(buf), "# %s vs %s\n", p[0], p[1]);
				write(1, buf, s - buf);
				exits(nil);
			case 0xf011:
				if(m == ms) continue;
				bwd();
				checkgameover();
				break;
			case 0xf012:
				fwd();
				checkgameover();
				break;
			case 0xf00e:
				while(m > ms) bwd();
				checkgameover();
				break;
			case 0xf800:
				while(fwd() == nil);
				checkgameover();
				break;
			case 0x1b:
				flip ^= 1;
				eresized(0);
				continue;
			default:
				if(fd < 0) continue;
				i = me + MSGLEN > buf + sizeof(buf) ? buf + sizeof(buf) - me : MSGLEN;
				me[0] = e.kbdc;
				me[1] = 0;
				i = eenter("message", me, i, &e.mouse);
				if(i < 1 || me[i - 1] == ' ') continue;
				if(!strcmp(u, p[t & 1]) && (!strcmp(me, "draw!") || !strcmp(me, "resign!"))){
					s = seprint(me, buf + sizeof(buf), "# %s %s\n",
						u, *me == 'd' ? "declared a draw" : "resigned");
					write(fd, me, s - me);
					write(1, buf, s - buf);
					exits(nil);
				}
				if(write(fd, me, i) != i){
					snprint(msg[msgn], MSGLEN, "write: %r");
					msgn = (msgn + 1) % MSGS;
					drawmsgs();
				}
				continue;
			}
			break;
		case Emouse:
			if(e.mouse.buttons & 16){fwd(); checkgameover(); break;}
			if(e.mouse.buttons & 8 && m > ms){bwd(); checkgameover(); break;}
			if(!e.mouse.buttons) continue;
			do eread(Emouse, &e); while(e.mouse.buttons);
			if((ptinrect(e.mouse.xy, side[i = 0]) || ptinrect(e.mouse.xy, side[i = 1]))
			&& fd < 0){
				strcpy(p[i], "ai level ");
				eenter("ai level ? [0–9]", p[i] + 9, 2, &e.mouse);
				if(p[i][9] < '1' || p[i][9] > '9') strcpy(p[i], u);
				eresized(0);
				continue;
			}
			for(i = 0; i < 32 && !ptinrect(e.mouse.xy, square[i]); i++);
			j = indexof[i];
			if(!(sq[j] & HL)) continue;
			clearhl();
			me = t & WHITE
				? seprint(m, buf + sizeof(buf), "%d", i + 1)
				: seprint(m, buf + sizeof(buf), "%d.%d", t + 2 >> 1, i + 1);
			if(jumper(j))
				do{
					i = j;
					if(canjump(j, ⭦)) sq[j + ⭦ + ⭦] |= HL;
					if(canjump(j, ⭧)) sq[j + ⭧ + ⭧] |= HL;
					if(canjump(j, ⭩)) sq[j + ⭩ + ⭩] |= HL;
					if(canjump(j, ⭨)) sq[j + ⭨ + ⭨] |= HL;
					drawboard();
					do{
						do eread(Emouse, &e); while(!e.mouse.buttons);
						do eread(Emouse, &e); while(e.mouse.buttons);
						for(j = 0; j < 32 && !ptinrect(e.mouse.xy, square[j]); j++);
						j = indexof[j];
					}while(!(sq[j] & HL));
					*me++ = sq[i + j >> 1] & KING ? 'X' : 'x';
					me = seprint(me, buf + sizeof(buf), "%d", coordof[j] + 1);
					sq[i + j >> 1] = ∅;
					sq[j] = sq[i];
					sq[i] = ∅;
					clearhl();
				}while(jumper(j));
			else{
				if(canslide(j, ⭦)) sq[j + ⭦] |= HL;
				if(canslide(j, ⭧)) sq[j + ⭧] |= HL;
				if(canslide(j, ⭩)) sq[j + ⭩] |= HL;
				if(canslide(j, ⭨)) sq[j + ⭨] |= HL;
				drawboard();
				do{
					do eread(Emouse, &e); while(!e.mouse.buttons);
					do eread(Emouse, &e); while(e.mouse.buttons);
					for(i = 0; i < 32 && !ptinrect(e.mouse.xy, square[i]); i++);
					i = indexof[i];
				}while(!(sq[i] & HL));
				me = seprint(me, buf + sizeof(buf), "-%d", coordof[i] + 1);
				sq[i] = sq[j];
				sq[j] = ∅;
				j = i;
			}
			if(sq[j] & MAN && (j > 35 || j < 9)){
				sq[j] ^= MAN | KING;
				*me++ = 'K';
			}
			*me++ = ' ';
			t++;
			checkgameover();
			if(fd >= 0){
				if(write(fd, m, me - m) != me - m){
					write(1, buf, me - buf);
					sysfatal("write: %r");
				}
				esetcursor(&⌛);
				do eread(Enine, &e);
					while(e.n != me - m && strncmp((char*)e.data, m, e.n));
			}
			m = me;
			break;
		case Enine:
			if(e.n < 1) continue;
			if(e.data[e.n - 1] != ' ' && e.data[e.n - 1] != '\n'){
				i = e.n < MSGLEN ? e.n : MSGLEN - 1;
				msg[msgn][i] = 0;
				while(i--) msg[msgn][i] = e.data[i];
				msgn = (msgn + 1) % MSGS;
				drawmsgs();
				continue;
			}
			if(me + e.n >= buf + sizeof(buf)){
				strcpy(msg[msgn], "length of received move would overflow buffer!");
				msgn = (msgn + 1) % MSGS;
				drawmsgs();
				continue;
			}
			memmove(me, e.data, e.n);
			me += e.n;
			while(fwd() == nil);
			checkgameover();
			if(!strcmp(p[t & 1], u)) esetcursor(nil);
		}
		drawboard();
	}
}