Updated README: Equal sign not required with `--mode` flag.
[sgk-go] / interface / gmp.c
CommitLineData
7eeb782e
AT
1/*
2 * src/gmp.h
3 * Copyright (C) 1995-1997 William Shubert.
4 *
5 * You may use this code in any way you wish as long as you retain the
6 * above copyright notice.
7 *
8 * This is based on David Fotland's Go Modem Protocol Code and the
9 * "protocol.Z" info file from Bruce Wilcox. It has been pretty much
10 * completely rewritten now, though.
11 */
12
13/* Modification by Daniel Bump for GNU Go: I made all GMP messages
14 contingent on gmp_debug. Otherwise this is identical to Bill Shubert's
15 version distributed with GoDummy.
16*/
17
18/* Modified by Paul Pogonyshev on 10.07.2003.
19 * Added support for Simplified GTP.
20 */
21
22#ifdef HAVE_CONFIG_H
23#include <config.h>
24#endif
25
26#define _GMP_C_ 1
27
28#include <assert.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32
33#if TIME_WITH_SYS_TIME
34# include <sys/time.h>
35# include <time.h>
36#else
37# if HAVE_SYS_TIME_H
38# include <sys/time.h>
39# else
40# include <time.h>
41# endif
42#endif
43
44#ifdef HAVE_SYS_TYPES_H
45#include <sys/types.h>
46#endif
47
48#ifdef HAVE_UNISTD_H
49#include <unistd.h>
50#endif
51
52#ifdef __MINGW32__
53#include <windows.h>
54#include <winsock.h>
55#include <io.h>
56#endif
57
58#ifdef HAVE_WINSOCK_IO_H
59#include <winsock.h>
60#include <io.h>
61#endif
62
63/**********************************************************************
64 * Constants
65 **********************************************************************/
66
67#define GMP_TIMEOUTRETRIES 60
68#define GMP_RETRYSECS 1
69#define SGMP_TIMEOUTRETRIES 9
70#define SGMP_RETRYSECS 20
71#define GMP_MAXSENDSQUEUED 16
72
73
74/**********************************************************************
75 * Data types
76 **********************************************************************/
77
78typedef enum {
79 cmd_ack, cmd_deny, cmd_reset, cmd_query, cmd_respond, cmd_move,
80 cmd_undo
81} Command;
82
83
84typedef enum {
85 query_game, query_bufSize, query_protocol, query_stones,
86 query_bTime, query_wTime, query_charSet, query_rules, query_handicap,
87 query_boardSize, query_timeLimit, query_color, query_who,
88 query_max
89} Query;
90
91
92typedef struct Gmp_struct {
93 int inFile, outFile;
94 int boardSize, sizeVerified;
95 int handicap, handicapVerified;
96 float komi;
97 int komiVerified;
98 int chineseRules, rulesVerified;
99 int iAmWhite, colorVerified;
100 Query lastQuerySent;
101
102 int recvSoFar, sendsQueued;
103 int sendFailures, noResponseSecs;
104 int waitingHighAck;
105 time_t lastSendTime;
106 int myLastSeq, hisLastSeq;
107 unsigned char recvData[4];
108 unsigned char sendData[4];
109 struct {
110 int cmd, val;
111 } sendsPending[GMP_MAXSENDSQUEUED];
112
113 int earlyMovePresent;
114 int earlyMoveX, earlyMoveY;
115
116 int simplified;
117} Gmp;
118
119
120/**********************************************************************
121 * Globals
122 **********************************************************************/
123
124int gmp_debug = 0;
125static const char *commandNames[] = {
126 "ACK", "DENY", "RESET", "QUERY", "RESPOND", "MOVE", "UNDO"};
127static const char *queryNames[] = {
128 "GAME", "BUFFER SIZE", "PROTOCOL", "STONES",
129 "BLACK TIME", "WHITE TIME", "CHAR SET", "RULES", "HANDICAP",
130 "BOARD SIZE", "TIME LIMIT", "COLOR", "WHO"};
131
132
133/**********************************************************************
134 * Forward Declarations
135 **********************************************************************/
136
137/* Get the forward declaration of externally visible functions. */
138#include "gmp.h"
139
140static unsigned char checksum(unsigned char p[4]);
141static GmpResult gotQueryResponse(Gmp *ge, int val, const char **err);
142static void putCommand(Gmp *ge, Command cmd, int val);
143static GmpResult respond(Gmp *ge, Query query);
144static void askQuery(Gmp *ge);
145static int heartbeat(Gmp *ge);
146static GmpResult getPacket(Gmp *ge, int *out1, int *out2,
147 const char **error);
148static GmpResult parsePacket(Gmp *ge, int *out1, int *out2,
149 const char **error);
150static GmpResult processCommand(Gmp *ge, Command command, int val,
151 int *out1, int *out2, const char **error);
152static void processQ(Gmp *ge);
153
154
155/**********************************************************************
156 * Functions
157 **********************************************************************/
158
159#define gmp_verified(ge) \
160 ((ge)->sizeVerified && (ge)->colorVerified && \
161 (ge)->handicapVerified && (ge)->rulesVerified)
162
163
164Gmp *gmp_create(int inFile, int outFile) {
165 Gmp *ge;
166
167 ge = malloc(sizeof(Gmp));
168 ge->inFile = inFile;
169 ge->outFile = outFile;
170
171 ge->boardSize = -1;
172 ge->sizeVerified = 0;
173
174 ge->handicap = -1;
175 ge->handicapVerified = 0;
176
177 ge->komi = 0.0;
178
179 ge->chineseRules = -1;
180 ge->rulesVerified = 0;
181
182 ge->iAmWhite = -1;
183 ge->colorVerified = 0;
184
185 ge->lastQuerySent = 0;
186
187 ge->recvSoFar = 0;
188 ge->sendsQueued = 0;
189 ge->sendFailures = 0;
190 ge->noResponseSecs = 0;
191 ge->waitingHighAck = 0;
192 ge->lastSendTime = 0;
193 ge->myLastSeq = 0;
194 ge->hisLastSeq = 0;
195
196 ge->earlyMovePresent = 0;
197
198 return(ge);
199}
200
201
202void gmp_destroy(Gmp *ge) {
203 free(ge);
204}
205
206
207GmpResult gmp_check(Gmp *ge, int gsleep, int *out1, int *out2,
208 const char **error) {
209 fd_set readReady;
210 struct timeval noTime;
211 int intDummy;
212 const char *charPtrDummy;
213 GmpResult result;
214
215 if (out1 == NULL)
216 out1 = &intDummy;
217 if (out2 == NULL)
218 out2 = &intDummy;
219 if (error == NULL)
220 error = &charPtrDummy;
221 if (gmp_verified(ge) && ge->earlyMovePresent) {
222 *out1 = ge->earlyMoveX;
223 *out2 = ge->earlyMoveY;
224 ge->earlyMovePresent = 0;
225 if (gmp_debug) {
226 fprintf(stderr, "GMP: Returning early move.\n");
227 }
228 return(gmp_move);
229 }
230 *out1 = 0;
231 *out2 = 0;
232 *error = NULL;
233 do {
234 if (time(NULL) != ge->lastSendTime) {
235 if (!heartbeat(ge)) {
236 *error = "GMP Timeout";
237 return(gmp_err);
238 }
239 }
240 FD_ZERO(&readReady);
241 FD_SET(ge->inFile, &readReady);
242 noTime.tv_usec = 0;
243 if (gsleep)
244 noTime.tv_sec = 1;
245 else
246 noTime.tv_sec = 0;
247 select(ge->inFile + 1, &readReady, NULL, NULL, &noTime);
248 if (!gsleep && !FD_ISSET(ge->inFile, &readReady))
249 return(gmp_nothing);
250 result = getPacket(ge, out1, out2, error);
251 } while (result == gmp_nothing);
252 return(result);
253}
254
255
256static GmpResult getPacket(Gmp *ge, int *out1, int *out2,
257 const char **error) {
258 unsigned char charsIn[4], c;
259 int count = 0, cNum;
260 static char errOut[200];
261
262 count = read(ge->inFile, charsIn, 4 - ge->recvSoFar);
263 if (count <= 0) {
264 sprintf(errOut, "System error.");
265 *error = errOut;
266 return(gmp_err);
267 }
268
269 for (cNum = 0; cNum < count; ++cNum) {
270 c = charsIn[cNum];
271 if (!ge->recvSoFar) {
272 /* idle, looking for start of packet */
273 if ((c & 0xfc) == 0) { /* start of packet */
274 ge->recvData[0] = c;
275 ge->recvSoFar = 1;
276 } else {
277 if (gmp_debug) {
278 fprintf(stderr, "GMP: Received invalid packet.\n");
279 }
280 }
281 } else {
282 /* We're in the packet. */
283 if ((c & 0x80) == 0) { /* error */
284 if (gmp_debug) {
285 fprintf(stderr, "GMP: Received invalid packet.\n");
286 }
287 ge->recvSoFar = 0;
288 if ((c & 0xfc) == 0) {
289 ge->recvData[ge->recvSoFar++] = c;
290 }
291 } else {
292 /* A valid character for in a packet. */
293 ge->recvData[ge->recvSoFar++] = c;
294 if (ge->recvSoFar == 4) { /* check for extra bytes */
295 assert(cNum + 1 == count);
296 ge->recvSoFar = 0;
297 if (checksum(ge->recvData) == ge->recvData[1])
298 return(parsePacket(ge, out1, out2, error));
299 }
300 }
301 }
302 }
303 return(gmp_nothing);
304}
305
306
307static unsigned char checksum(unsigned char p[4]) {
308 unsigned char sum;
309 sum = p[0] + p[2] + p[3];
310 sum |= 0x80; /* set sign bit */
311 return(sum);
312}
313
314
315static GmpResult parsePacket(Gmp *ge, int *out1, int *out2,
316 const char **error) {
317 int seq, ack, val;
318 Command command;
319 GmpResult result;
320
321 seq = ge->recvData[0] & 1;
322 ack = (ge->recvData[0] & 2) >> 1;
323 if (ge->recvData[2] & 0x08) { /* Not-understood command. */
324 if (gmp_debug) {
325 fprintf(stderr, "GMP: Unknown command byte 0x%x received.\n",
326 (unsigned int) ge->recvData[2]);
327 }
328 return(gmp_nothing);
329 }
330 command = (ge->recvData[2] >> 4) & 7;
331 val = ((ge->recvData[2] & 7) << 7) | (ge->recvData[3] & 0x7f);
332 if (gmp_debug) {
333 if (command == cmd_query) {
334 if (val >= query_max) {
335 if (gmp_debug)
336 fprintf(stderr, "GMP: Read in command: %s unkown value %d\n",
337 commandNames[command], val);
338 } else {
339 if (gmp_debug)
340 fprintf(stderr, "GMP: Read in command: %s %s\n",
341 commandNames[command], queryNames[val]);
342 }
343 } else {
344 if (gmp_debug)
345 fprintf(stderr, "GMP: Read in command: %s\n",
346 commandNames[command]);
347 }
348 }
349 if (!ge->waitingHighAck) {
350 if ((command == cmd_ack) || /* An ack. We don't need an ack now. */
351 (ack != ge->myLastSeq)) { /* He missed my last message. */
352 if (gmp_debug)
353 fprintf(stderr, "GMP: Unexpected ACK.\n");
354 return(gmp_nothing);
355 } else if (seq == ge->hisLastSeq) { /* Seen this one before. */
356 if (gmp_debug)
357 fprintf(stderr, "GMP: Received repeated message.\n");
358 putCommand(ge, cmd_ack, ~0);
359 } else {
360 ge->hisLastSeq = seq;
361 ge->sendFailures = 0;
362 ge->noResponseSecs = 0;
363 return(processCommand(ge, command, val, out1, out2, error));
364 }
365 } else {
366 /* Waiting for OK. */
367 if (command == cmd_ack) {
368 if ((ack != ge->myLastSeq) || (seq != ge->hisLastSeq)) {
369 /* Sequence error. */
370 fprintf(stderr, "Sequence error.\n");
371 return(gmp_nothing);
372 }
373 ge->sendFailures = 0;
374 ge->noResponseSecs = 0;
375 ge->waitingHighAck = 0;
376 if (!gmp_verified(ge)) {
377 askQuery(ge);
378 }
379 processQ(ge);
380 } else if ((command == cmd_reset) && (ge->iAmWhite == -1)) {
381 if (gmp_debug)
382 fprintf(stderr, "gmp/his last seq = %d\n", seq);
383 ge->hisLastSeq = seq;
384 ge->waitingHighAck = 0;
385 return(processCommand(ge, command, val, out1, out2, error));
386 } else if (seq == ge->hisLastSeq) {
387 /* His command is old. */
388 } else if (ack == ge->myLastSeq) {
389 ge->sendFailures = 0;
390 ge->noResponseSecs = 0;
391 ge->waitingHighAck = 0;
392 ge->hisLastSeq = seq;
393 result = processCommand(ge, command, val, out1, out2, error);
394 processQ(ge);
395 return(result);
396 } else {
397 /* Conflict with opponent. */
398 if (gmp_debug)
399 fprintf(stderr, "Sending conflict.\n");
400 ge->myLastSeq = 1 - ge->myLastSeq;
401 ge->waitingHighAck = 0;
402 processQ(ge);
403 }
404 }
405 return(gmp_nothing);
406}
407
408
409static GmpResult processCommand(Gmp *ge, Command command, int val,
410 int *out1, int *out2, const char **error) {
411 int s, x, y;
412
413 switch(command) {
414 case cmd_deny:
415 putCommand(ge, cmd_ack, ~0);
416 break;
417 case cmd_query:
418 return(respond(ge, val));
419 break;
420 case cmd_reset: /* New game. */
421 if (gmp_debug)
422 fprintf(stderr, "GMP: Resetted. New game.\n");
423 askQuery(ge);
424 return(gmp_reset);
425 break;
426 case cmd_undo: /* Take back moves. */
427 putCommand(ge, cmd_ack, ~0);
428 *out1 = val;
429 return(gmp_undo);
430 break;
431 case cmd_move:
432 s = val & 0x1ff;
433 if (s == 0) {
434 x = -1;
435 y = 0;
436 } else if (s == 0x1ff) {
437 x = -2;
438 y = 0;
439 } else {
440 --s;
441 x = (s % ge->boardSize);
442 y = ge->boardSize - 1 - (s / ge->boardSize);
443 }
444 putCommand(ge, cmd_ack, ~0);
445 if (x == -1)
446 return(gmp_pass);
447 else {
448 if (gmp_verified(ge)) {
449 *out1 = x;
450 *out2 = y;
451 return(gmp_move);
452 } else {
453 assert(ge->earlyMovePresent == 0);
454 ge->earlyMovePresent = 1;
455 ge->earlyMoveX = x;
456 ge->earlyMoveY = y;
457 askQuery(ge);
458 }
459 }
460 break;
461 case cmd_respond:
462 return(gotQueryResponse(ge, val, error));
463 break;
464 default: /* Don't understand command. */
465 putCommand(ge, cmd_deny, 0);
466 break;
467 }
468 return(gmp_nothing);
469}
470
471
472static void putCommand(Gmp *ge, Command cmd, int val) {
473 if (ge->waitingHighAck &&
474 (cmd != cmd_ack) && (cmd != cmd_respond) && (cmd != cmd_deny)) {
475 if (ge->sendsQueued < 1024) {
476 ge->sendsPending[ge->sendsQueued].cmd = cmd;
477 ge->sendsPending[ge->sendsQueued].val = val;
478 ++ge->sendsQueued;
479 } else {
480 if (gmp_debug)
481 fprintf(stderr, "GMP: Send buffer full. Catastrophic error.");
482 exit(EXIT_FAILURE);
483 }
484 return;
485 }
486 if ((cmd == cmd_ack) && (ge->sendsQueued)) {
487 ge->waitingHighAck = 0;
488 processQ(ge);
489 return;
490 }
491 if (cmd != cmd_ack)
492 ge->myLastSeq ^= 1;
493 ge->sendData[0] = ge->myLastSeq | (ge->hisLastSeq << 1);
494 ge->sendData[2] = 0x80 | (cmd << 4) | ((val >> 7) & 7);
495 ge->sendData[3] = 0x80 | val;
496 ge->sendData[1] = checksum(ge->sendData);
497 ge->lastSendTime = time(NULL);
498 if (gmp_debug) {
499 if (cmd == cmd_query)
500 fprintf(stderr, "GMP: Sending command: %s %s\n",
501 commandNames[cmd], queryNames[val]);
502 else
503 fprintf(stderr, "GMP: Sending command: %s\n", commandNames[cmd]);
504 }
505 write(ge->outFile, ge->sendData, 4);
506 ge->waitingHighAck = (cmd != cmd_ack);
507 return;
508}
509
510
511static GmpResult respond(Gmp *ge, Query query) {
512 int response;
513 int wasVerified;
514
515 wasVerified = gmp_verified(ge);
516 if (query & 0x200) {
517 /* Do you support this extended query? */
518 response = 0; /* No. */
519 } else {
520 ge->waitingHighAck = 1;
521 switch(query) {
522 case query_game:
523 response = 1; /* GO */
524 break;
525 case query_rules:
526 if (ge->chineseRules == -1) {
527 response = 0;
528 } else {
529 ge->rulesVerified = 1;
530 if (ge->chineseRules == 1)
531 response = 2;
532 else
533 response = 1;
534 }
535 break;
536 case query_handicap:
537 if (ge->handicap == -1)
538 response = 0;
539 else {
540 ge->handicapVerified = 1;
541 response = ge->handicap;
542 if (response == 0)
543 response = 1;
544 }
545 break;
546 case query_boardSize:
547 if (ge->boardSize == -1) {
548 response = 0;
549 } else {
550 response = ge->boardSize;
551 ge->sizeVerified = 1;
552 }
553 break;
554 case query_color:
555 if (ge->iAmWhite == -1) {
556 response = 0;
557 } else {
558 ge->colorVerified = 1;
559 if (ge->iAmWhite)
560 response = 1;
561 else
562 response = 2;
563 }
564 break;
565 default:
566 response = 0;
567 break;
568 }
569 }
570 putCommand(ge, cmd_respond, response);
571 if (!wasVerified && gmp_verified(ge)) {
572 if (gmp_debug)
573 fprintf(stderr, "GMP: New game ready.\n");
574 return(gmp_newGame);
575 } else {
576 return(gmp_nothing);
577 }
578}
579
580
581static void askQuery(Gmp *ge) {
582 if (!ge->simplified) {
583 if (!ge->rulesVerified) {
584 ge->lastQuerySent = query_rules;
585 } else if (!ge->sizeVerified) {
586 ge->lastQuerySent = query_boardSize;
587 } else if (!ge->handicapVerified) {
588 ge->lastQuerySent = query_handicap;
589 /* } else if (!ge->komiVerified) {
590 ge->lastQuerySent = query_komi; query komi is not define in GMP !? */
591 } else {
592 assert(!ge->colorVerified);
593 ge->lastQuerySent = query_color;
594 }
595 }
596 else {
597 if (!ge->colorVerified)
598 ge->lastQuerySent = query_color;
599 else if (!ge->handicapVerified)
600 ge->lastQuerySent = query_handicap;
601 }
602
603 putCommand(ge, cmd_query, ge->lastQuerySent);
604}
605
606
607static GmpResult gotQueryResponse(Gmp *ge, int val, const char **err) {
608 static const char *ruleNames[] = {"Japanese", "Chinese"};
609 static const char *colorNames[] = {"Black", "White"};
610 static char errOut[200];
611
612 switch(ge->lastQuerySent) {
613 case query_handicap:
614 if (val <= 1)
615 --val;
616 if (ge->handicap == -1) {
617 if (val == -1) {
618 sprintf(errOut, "Neither player knows what the handicap should be.");
619 *err = errOut;
620 return(gmp_err);
621 } else {
622 ge->handicap = val;
623 ge->handicapVerified = 1;
624 }
625 } else {
626 ge->handicapVerified = 1;
627 if ((val != -1) && (val != ge->handicap)) {
628 sprintf(errOut, "Handicaps do not agree; I want %d, he wants %d.",
629 ge->handicap, val);
630 *err = errOut;
631 return(gmp_err);
632 }
633 }
634 break;
635 case query_boardSize:
636 if (ge->boardSize == -1) {
637 if (val == 0) {
638 sprintf(errOut, "Neither player knows what the board size should be.");
639 *err = errOut;
640 return(gmp_err);
641 } else {
642 ge->boardSize = val;
643 ge->sizeVerified = 1;
644 }
645 } else {
646 ge->sizeVerified = 1;
647 if ((val != 0) && (val != ge->boardSize)) {
648 sprintf(errOut, "Board sizes do not agree; I want %d, he wants %d.",
649 ge->boardSize, val);
650 *err = errOut;
651 return(gmp_err);
652 }
653 }
654 break;
655 case query_rules:
656 if (ge->chineseRules == -1) {
657 if (val == 0) {
658 sprintf(errOut, "Neither player knows what rule set to use.");
659 *err = errOut;
660 return(gmp_err);
661 } else {
662 ge->chineseRules = val - 1;
663 ge->rulesVerified = 1;
664 }
665 } else {
666 ge->rulesVerified = 1;
667 if (val != 0) {
668 if (ge->chineseRules != (val == 2)) {
669 sprintf(errOut, "Rule sets do not agree; I want %s, he wants %s.",
670 ruleNames[ge->chineseRules], ruleNames[val == 2]);
671 *err = errOut;
672 return(gmp_err);
673 }
674 }
675 }
676 break;
677 case query_color:
678 if (ge->iAmWhite == -1) {
679 if (val == 0) {
680 sprintf(errOut, "Neither player knows who is which color.");
681 *err = errOut;
682 return(gmp_err);
683 } else {
684 ge->iAmWhite = !(val == 1);
685 ge->colorVerified = 1;
686 }
687 } else {
688 ge->colorVerified = 1;
689 if (val != 0) {
690 if (ge->iAmWhite == (val == 1)) {
691 sprintf(errOut, "Colors do not agree; we both want to be %s.",
692 colorNames[ge->iAmWhite]);
693 *err = errOut;
694 return(gmp_err);
695 }
696 }
697 }
698 break;
699 default:
700 break;
701 }
702 if (!gmp_verified(ge)) {
703 askQuery(ge);
704 return(gmp_nothing);
705 } else {
706 putCommand(ge, cmd_ack, ~0);
707 if (gmp_debug)
708 fprintf(stderr, "GMP: New game ready.\n");
709 return(gmp_newGame);
710 }
711}
712
713
714static int heartbeat(Gmp *ge) {
715 Command cmd;
716
717 if (ge->waitingHighAck) {
718 if (++ge->noResponseSecs
719 > (ge->simplified ? SGMP_RETRYSECS : GMP_RETRYSECS)) {
720 if (++ge->sendFailures
721 > (ge->simplified ? SGMP_TIMEOUTRETRIES : GMP_TIMEOUTRETRIES)) {
722 return(0);
723 } else {
724 if (gmp_debug) {
725 cmd = (ge->sendData[2] >> 4) & 7;
726 if (cmd == cmd_query) {
727 if (gmp_debug)
728 fprintf(stderr, "GMP: Sending command: %s %s (retry)\n",
729 commandNames[cmd],
730 queryNames[ge->sendData[3] & 0x7f]);
731 }
732 else
733 if (gmp_debug)
734 fprintf(stderr, "GMP: Sending command: %s (retry)\n",
735 commandNames[cmd]);
736 }
737 write(ge->outFile, ge->sendData, 4);
738 }
739 }
740 }
741 return(1);
742}
743
744
745void gmp_startGame(Gmp *ge, int size, int handicap, float komi,
746 int chineseRules, int iAmWhite, int simplified) {
747 assert((size == -1) || ((size > 1) && (size <= 22)));
748 assert((handicap >= -1) && (handicap <= 27));
749 assert((chineseRules >= -1) && (chineseRules <= 1));
750 assert((iAmWhite >= -1) && (iAmWhite <= 1));
751
752 ge->boardSize = size;
753 ge->sizeVerified = simplified;
754
755 ge->handicap = handicap;
756 ge->handicapVerified = 0;
757
758 ge->komi = komi;
759
760 ge->chineseRules = chineseRules;
761 ge->rulesVerified = simplified;
762
763 ge->iAmWhite = iAmWhite;
764 ge->colorVerified = 0;
765
766 ge->earlyMovePresent = 0;
767
768 ge->simplified = simplified;
769
770 if (iAmWhite != 1) {
771 putCommand(ge, cmd_reset, 0);
772 }
773}
774
775
776void gmp_sendPass(Gmp *ge) {
777 int arg;
778
779 if (ge->iAmWhite)
780 arg = 0x200;
781 else
782 arg = 0;
783 putCommand(ge, cmd_move, arg);
784}
785
786
787void gmp_sendMove(Gmp *ge, int x, int y) {
788 int val;
789
790 val = x + ge->boardSize * (ge->boardSize - 1 - y) + 1;
791 if (ge->iAmWhite)
792 val |= 0x200;
793 putCommand(ge, cmd_move, val);
794}
795
796
797void gmp_sendUndo(Gmp *ge, int numUndos) {
798 putCommand(ge, cmd_undo, numUndos);
799}
800
801
802const char *gmp_resultString(GmpResult result) {
803 static const char *names[] = {
804 "Nothing", "Move", "Pass", "Reset", "New game", "Undo", "Error"};
805
806 assert(result <= gmp_err);
807 return(names[result]);
808}
809
810
811int gmp_size(Gmp *ge) {
812 return(ge->boardSize);
813}
814
815
816int gmp_handicap(Gmp *ge) {
817 return(ge->handicap);
818}
819
820
821float gmp_komi(Gmp *ge) {
822 return(ge->komi);
823}
824
825
826int gmp_chineseRules(Gmp *ge) {
827 return(ge->chineseRules);
828}
829
830
831int gmp_iAmWhite(Gmp *ge) {
832 return(ge->iAmWhite);
833}
834
835
836static void processQ(Gmp *ge) {
837 int i;
838
839 if (!ge->waitingHighAck && ge->sendsQueued) {
840 putCommand(ge, ge->sendsPending[0].cmd, ge->sendsPending[0].val);
841 --ge->sendsQueued;
842 for (i = 0; i < ge->sendsQueued; ++i) {
843 ge->sendsPending[i] = ge->sendsPending[i + 1];
844 }
845 }
846}
847
848/*
849 * Local Variables:
850 * tab-width: 8
851 * c-basic-offset: 2
852 * End:
853 */