home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / games / volume17 / malawi / part01 < prev    next >
Encoding:
Internet Message Format  |  1993-04-17  |  42.3 KB

  1. Path: uunet!news.tek.com!master!saab!billr
  2. From: billr@saab.CNA.TEK.COM (Bill Randle)
  3. Newsgroups: comp.sources.games
  4. Subject: v17i074:  malawi - 2 players board game, Part01/01
  5. Message-ID: <4926@master.CNA.TEK.COM>
  6. Date: 19 Apr 93 02:19:52 GMT
  7. Sender: news@master.CNA.TEK.COM
  8. Lines: 1445
  9. Approved: billr@saab.CNA.TEK.COM
  10. Xref: uunet comp.sources.games:1753
  11.  
  12. Submitted-by: Johannes Sixt <Johannes.Sixt@risc.uni-linz.ac.at>
  13. Posting-number: Volume 17, Issue 74
  14. Archive-name: malawi/Part01
  15. Environment: X11, Xaw
  16.  
  17.  
  18. [From the author...]
  19. [[This is malawi, version 0.1, a 2 players board game.
  20.   You will require X11R4 or higher to compile and link.
  21.   Check the man page for the rules of the game and how to play it.
  22.  
  23.   The initial version 0.0 of malawi was posted to alt.sources. The major
  24.   improvement of 0.1 is that the opponents can play on different displays.
  25.  
  26.   Have fun!
  27.  
  28.   -- Hannes
  29.  
  30.   PS: I do not know whether it is coincidence or not that the name of this
  31.   game is the same as that of an African republic, because I did not invent
  32.   the name for the game.]]
  33.  
  34. #! /bin/sh
  35. # This is a shell archive.  Remove anything before this line, then unpack
  36. # it by saving it into a file and typing "sh file".  To overwrite existing
  37. # files, type "sh file -c".  You can also feed this as standard input via
  38. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  39. # will see the following message at the end:
  40. #        "End of archive 1 (of 1)."
  41. # Contents:  README malawi.man HISTORY Imakefile MANIFEST Makefile.std
  42. #   Malawi.ad malawi.c patchlevel.h
  43. # Wrapped by jsixt@melmac on Tue Apr 13 09:33:58 1993
  44. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  45. if test -f 'README' -a "${1}" != "-c" ; then 
  46.   echo shar: Will not clobber existing file \"'README'\"
  47. else
  48. echo shar: Extracting \"'README'\" \(1122 characters\)
  49. sed "s/^X//" >'README' <<'END_OF_FILE'
  50. XThis is Malawi.
  51. X
  52. XMalawi is a 2 players board game modeled after PIATNIK game No. 6104 by
  53. XGerhard Kodys.
  54. X
  55. XThe sources are available from
  56. X
  57. X    ftp.risc.uni-linz.ac.at:/pub/x11/malawi-0.1.tar.Z
  58. X
  59. X
  60. XInstallation:
  61. X
  62. XIf you have imake:
  63. X
  64. X    csh> xmkmf
  65. X    csh> make
  66. X    csh> make install
  67. X    csh> make install.man
  68. X
  69. XIf you don't have imake, try
  70. X
  71. X    csh> make -f Makefile.std
  72. X
  73. XTo test the program
  74. X
  75. X    csh> (setenv XFILESEARCHPATH ./%N.ad; ./malawi)
  76. X
  77. Xbecause the application defaults are mandatory for malawi to display correctly.
  78. X
  79. XThe program runs on the following platforms:
  80. X
  81. X    Sequent, DYNIX/ptx 1.3.1 (SYSV 3.2), X11R5, gcc
  82. X    Apollo, Domain/OS 10.3.4 (BSD 4.3), X11R4, cc
  83. X    DEC 5000/240, Ultrix 4.2, X11R5, cc
  84. X    IBM RS6000, AIX 3.2.2, X11R5, cc
  85. X
  86. XSince the program is fairly operating system independent, it should actually
  87. Xcompile under every environment with X11R4 or higher.
  88. X
  89. X
  90. XBug reports:
  91. X
  92. XDirect bug reports, bug fixes, enhancements, ... to
  93. XJohannes Sixt (jsixt@risc.uni-linz.ac.at)
  94. X
  95. XSince my native language is not English any comments on and improvements of
  96. XEnglish prose anywhere in the source and, most importantly, in the man page
  97. Xare welcome.
  98. END_OF_FILE
  99. if test 1122 -ne `wc -c <'README'`; then
  100.     echo shar: \"'README'\" unpacked with wrong size!
  101. fi
  102. # end of 'README'
  103. fi
  104. if test -f 'malawi.man' -a "${1}" != "-c" ; then 
  105.   echo shar: Will not clobber existing file \"'malawi.man'\"
  106. else
  107. echo shar: Extracting \"'malawi.man'\" \(5154 characters\)
  108. sed "s/^X//" >'malawi.man' <<'END_OF_FILE'
  109. X.TH MALAWI 6 "Version 0.1"
  110. X.SH NAME
  111. Xmalawi - a computerized 2 players strategy board game
  112. X.SH SYNOPSIS
  113. X.B malawi
  114. X[-\fItoolkitoption\fP ...]
  115. X[-white \fIdisplay\fP]
  116. X[-black \fIdisplay\fP]
  117. X[-mto \fItime-out\fP]
  118. X.SH DESCRIPTION
  119. X.I malawi
  120. Xis a strategy board game for 2 players which has fairly simple rules.
  121. XIf played by somewhat experienced opponents a game can take one hour and
  122. Xmore.
  123. X.LP
  124. XThe board is a 6x6 raster.
  125. XEach player has a \fBbase line\fP on the board, 6 \fBsticks\fP, and 12
  126. X\fBeumels\fP.
  127. XInitially, the 6 sticks are positioned on the base line, each carrying 2
  128. Xeumels.
  129. XThe 2 players' base lines are opposite rows on the board.
  130. X.LP
  131. XThe goal of the game is to move a stick that carries at least one eumel onto
  132. Xthe opponent's base line such that the eumels carried by the stick can not be
  133. Xcaptured immediatly by the opponent. To achieve this goal the sticks can be
  134. Xmoved, eumels can be redistributed, and opponent's eumels can be captured.
  135. XThere are always 6 sticks per player in the game, but the number of eumels
  136. Xwill decrease (and never increase) as they get captured.
  137. X.LP
  138. XThe program watches the rules, so it is not possible for a player to do
  139. Xsomething against the rules.
  140. X.SH RULES
  141. XThe two opponents alternate their turns. White begins the game.
  142. XDuring his turn, a player must do one of three things:
  143. X.IP "Move a stick"
  144. XThe player moves one of his sticks. It can be moved only horizontally or
  145. Xvertically. It must be moved exactly as many fields as eumels are on the
  146. Xstick. (Hence, a stick without eumels or with 6 eumels cannot be moved.)
  147. XThe direction of the
  148. Xmovement must not be altered during the move. All fields on the path as well
  149. Xas the final field must be empty, i.e. no stick of either player may occupy
  150. Xsuch a field, not even a stick that carries no eumels.
  151. X.IP "Capture opponent's eumels"
  152. XThe player removes \fBall\fP eumels of one of the opponent's sticks. This stick
  153. Xmust occupy a field that could be reached, if it were empty, by a move of
  154. Xa stick of the player as described above. It is not allowed to "remove"
  155. Xzero eumels. The captured eumels are out of play and are never returned.
  156. X.IP "Distribute eumels"
  157. XThe player distributes \fBall\fP eumels of one of his sticks onto his other
  158. Xsticks. It is not allowed to "distribute" zero eumels. Each stick may receive
  159. Xat most one eumel. The stick which was emptied must not receive an eumel (it
  160. Xremains, therefore, empty). A stick can carry at most 6 eumels. If
  161. X6 eumels are to be distributed the superfluous eumel is removed from play
  162. Xand is lost. On the other hand, it is not allowed to remove eumels from play
  163. Xby not putting it on any stick if there are still sticks which can take an
  164. Xeumel (because they have not received one yet and they are not the stick 
  165. Xthat is distributed).
  166. X.LP
  167. XThe game ends in two cases:
  168. X.IP "No eumels"
  169. XIf a player loses all eumels he has lost the game.
  170. X.IP "Eumel survives on enemy base line"
  171. XIf a player's eumel survives one opponent's turn on the opponent's base line
  172. Xthe player wins the game; that is, if the player gets his turn and still has
  173. Xa stick on the opponent's base line and this stick carries at least one eumel.
  174. X.SH USING THE PROGRAM
  175. XWhen a player's turn begins he selects one of his sticks. Then he chooses
  176. Xwhat to do with the stick by pressing one of the buttons "Move Stick",
  177. X"Distribute Eumels", or "Capture Eumels". Then he either selects the
  178. Xdestination
  179. Xfield(s) and presses "Execute Turn" or he selects "Cancel Turn" in which case
  180. Xhe again can choose a field.
  181. X.LP
  182. XA message line displays errors and gives hints on what a player can do.
  183. X.LP
  184. XThe buttons "New Game" and "Quit Game" have obvious functions. However, if
  185. Xone player presses the button only a request is issued: The button is
  186. Xhighlighted on both players' displays, but the function
  187. Xis not executed until the other player also presses the button and thus
  188. Xconfirms the request. A request can be withdrawn by the player who issued it
  189. Xbefore the other player confirms it by again pressing the button.
  190. X.SH OPTIONS
  191. XSince
  192. X.I malawi
  193. Xis built on top of the X Toolkit and the Athena Widget Set,
  194. Xall standard toolkit options are accepted. Additionally,
  195. X.I malawi
  196. Xunderstands the following command line options and application resources:
  197. X.PP
  198. X.TP 8
  199. X.B \-white \fIdisplay\fP
  200. X(Resource \fBwhite\fP) This option specifies on which display the white
  201. Xside player is.
  202. X.TP 8
  203. X.B \-black \fIdisplay\fP
  204. X(Resource \fBblack\fP) This option specifies on which display the black
  205. Xside player is.
  206. X.TP 8
  207. X.B \-mto \fItime-out\fP
  208. X(Resource \fBmessageTimeOut\fP) This option specifies how long error messages
  209. Xare displayed in the message line. \fItime-out\fP is in milli-seconds.
  210. XThe default is 4000.
  211. X.SH BUGS
  212. XThere's no possibility for the players to communicate unless they sit in the
  213. Xsame room where they can yell each other.
  214. X.LP
  215. XThere is no possibility to take back moves once they are "executed".
  216. X.LP
  217. XIt is planned that the game can be played against the computer.
  218. X.LP
  219. X.SH IDEA
  220. XThe idea for the game is stolen from PIATNIK game No. 6104 by Gerhard Kodys.
  221. X.SH COPYRIGHT
  222. XThe computerized version of the game is written by
  223. XJohannes Sixt (jsixt@risc.uni-linz.ac.at).
  224. X.LP
  225. XCopyright (c) 1993 Johannes Sixt
  226. END_OF_FILE
  227. if test 5154 -ne `wc -c <'malawi.man'`; then
  228.     echo shar: \"'malawi.man'\" unpacked with wrong size!
  229. fi
  230. # end of 'malawi.man'
  231. fi
  232. if test -f 'HISTORY' -a "${1}" != "-c" ; then 
  233.   echo shar: Will not clobber existing file \"'HISTORY'\"
  234. else
  235. echo shar: Extracting \"'HISTORY'\" \(188 characters\)
  236. sed "s/^X//" >'HISTORY' <<'END_OF_FILE'
  237. XMalawi 0.1
  238. X
  239. X    Data structures have been rewritten such that future extensions, like
  240. X    computer as player are possible.
  241. X
  242. X    Multiple displays are supported.
  243. X
  244. X
  245. XMalawi 0.0
  246. X
  247. X    First implementation
  248. END_OF_FILE
  249. if test 188 -ne `wc -c <'HISTORY'`; then
  250.     echo shar: \"'HISTORY'\" unpacked with wrong size!
  251. fi
  252. # end of 'HISTORY'
  253. fi
  254. if test -f 'Imakefile' -a "${1}" != "-c" ; then 
  255.   echo shar: Will not clobber existing file \"'Imakefile'\"
  256. else
  257. echo shar: Extracting \"'Imakefile'\" \(102 characters\)
  258. sed "s/^X//" >'Imakefile' <<'END_OF_FILE'
  259. XLOCAL_LIBRARIES = XawClientLibs
  260. XMANSUFFIX = 6
  261. X
  262. XSimpleProgramTarget(malawi)
  263. XInstallAppDefaults(Malawi)
  264. END_OF_FILE
  265. if test 102 -ne `wc -c <'Imakefile'`; then
  266.     echo shar: \"'Imakefile'\" unpacked with wrong size!
  267. fi
  268. # end of 'Imakefile'
  269. fi
  270. if test -f 'MANIFEST' -a "${1}" != "-c" ; then 
  271.   echo shar: Will not clobber existing file \"'MANIFEST'\"
  272. else
  273. echo shar: Extracting \"'MANIFEST'\" \(569 characters\)
  274. sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
  275. X   File Name        Archive #    Description
  276. X-----------------------------------------------------------
  277. X README                     1    Read this first
  278. X malawi.man                 1    Unformatted man page
  279. X HISTORY                    1    The evolution of the computerized game
  280. X Imakefile                  1    
  281. X MANIFEST                   1    This shipping list
  282. X Makefile.std               1    Simple Makefile in case you don't have imake
  283. X Malawi.ad                  1    Application defaults
  284. X malawi.c                   1    Source of everything
  285. X patchlevel.h               1    Version information
  286. END_OF_FILE
  287. if test 569 -ne `wc -c <'MANIFEST'`; then
  288.     echo shar: \"'MANIFEST'\" unpacked with wrong size!
  289. fi
  290. # end of 'MANIFEST'
  291. fi
  292. if test -f 'Makefile.std' -a "${1}" != "-c" ; then 
  293.   echo shar: Will not clobber existing file \"'Makefile.std'\"
  294. else
  295. echo shar: Extracting \"'Makefile.std'\" \(115 characters\)
  296. sed "s/^X//" >'Makefile.std' <<'END_OF_FILE'
  297. XOBJS = malawi.o
  298. XLIBS = -lXaw -lXmu -lXt -lXext -lX11
  299. XCFLAGS = -O
  300. X
  301. Xmalawi: $(OBJS)
  302. X    $(CC) -o malawi $(OBJS) $(LIBS)
  303. END_OF_FILE
  304. if test 115 -ne `wc -c <'Makefile.std'`; then
  305.     echo shar: \"'Makefile.std'\" unpacked with wrong size!
  306. fi
  307. # end of 'Makefile.std'
  308. fi
  309. if test -f 'Malawi.ad' -a "${1}" != "-c" ; then 
  310.   echo shar: Will not clobber existing file \"'Malawi.ad'\"
  311. else
  312. echo shar: Extracting \"'Malawi.ad'\" \(340 characters\)
  313. sed "s/^X//" >'Malawi.ad' <<'END_OF_FILE'
  314. X*new.label:        New Game
  315. X*quit.label:        Quit Game
  316. X*quit.fromHoriz:    new
  317. X
  318. X*move.label:        Move Stick
  319. X*distribute.label:    Distribute Eumels
  320. X*capture.label:        Capture Eumels
  321. X*do.label:        Execute Turn
  322. X*cancel.label:        Cancel Turn
  323. X
  324. X*distribute.fromHoriz:    move
  325. X*capture.fromHoriz:    distribute
  326. X*do.fromVert:        move
  327. X*cancel.fromHoriz:    do
  328. X*cancel.fromVert:    move
  329. END_OF_FILE
  330. if test 340 -ne `wc -c <'Malawi.ad'`; then
  331.     echo shar: \"'Malawi.ad'\" unpacked with wrong size!
  332. fi
  333. # end of 'Malawi.ad'
  334. fi
  335. if test -f 'malawi.c' -a "${1}" != "-c" ; then 
  336.   echo shar: Will not clobber existing file \"'malawi.c'\"
  337. else
  338. echo shar: Extracting \"'malawi.c'\" \(29241 characters\)
  339. sed "s/^X//" >'malawi.c' <<'END_OF_FILE'
  340. X/*
  341. X * Copyright 1993 Johannes Sixt
  342. X *
  343. X * Permission to use, copy, modify and distribute this software and its
  344. X * documentation for any purpose other than its commercial exploitation
  345. X * is hereby granted without fee, provided that the above copyright
  346. X * notice appear in all copies and that both that copyright notice and
  347. X * this permission notice appear in supporting documentation. The author
  348. X * makes no representations about the suitability of this software for
  349. X * any purpose. It is provided "as is" without express or implied warranty.
  350. X *
  351. X * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
  352. X * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
  353. X * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  354. X * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
  355. X * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  356. X * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  357. X * PERFORMANCE OF THIS SOFTWARE.
  358. X *
  359. X * Author: Johannes Sixt <jsixt@risc.uni-linz.ac.at>
  360. X */
  361. X
  362. X#include <stdio.h>        /* also for sprintf */
  363. X
  364. X#include <X11/Intrinsic.h>
  365. X#include <X11/StringDefs.h>
  366. X#include <X11/Shell.h>
  367. X#include <X11/Xaw/Paned.h>
  368. X/* #include <X11/Xaw/Command.h> included by <X11/Xaw/Toggle.h> */
  369. X#include <X11/Xaw/Toggle.h>
  370. X#include <X11/Xaw/Form.h>
  371. X#include "patchlevel.h"
  372. X
  373. X#define BOARD_SIZE    6
  374. X#define INITIAL_EUMELS    2
  375. X#define MAX_EUMELS    BOARD_SIZE
  376. X#define FIELD_WIDTH    80
  377. X#define FIELD_HEIGHT    FIELD_WIDTH
  378. X#define WIN_WIDTH    (FIELD_WIDTH * BOARD_SIZE)
  379. X#define WIN_HEIGHT    (FIELD_HEIGHT * BOARD_SIZE)
  380. X#define MESS_TIME_OUT    4000
  381. X
  382. Xtypedef enum {
  383. X    BlackSide, WhiteSide, Empty
  384. X    } FieldType;
  385. X#define MAX_FIELD_TYPE    ((int) Empty)
  386. X
  387. Xtypedef struct {
  388. X    FieldType type;        /* empty, black, white */
  389. X    int count;        /* number of Eumels */
  390. X    int r, c;        /* row, column of this field */
  391. X} Field;
  392. X
  393. Xtypedef struct {
  394. X    FieldType side;        /* whose turn is it? Empty is not allowed */
  395. X    FieldType other_side;    /* whose turn is it NOT? For ease of program */
  396. X#if BOARD_SIZE < 4
  397. X#define MAX_POSS    4
  398. X#else
  399. X#define MAX_POSS    BOARD_SIZE
  400. X#endif
  401. X    Field *possibility[MAX_POSS];    /* possible destinations */
  402. X    int no_possibilities;    /* number of possible destinations */
  403. X    Field *source;        /* field involved in this turn */
  404. X    Field board[BOARD_SIZE][BOARD_SIZE];
  405. X    int remain[MAX_FIELD_TYPE];    /* remaining eumels */
  406. X} MoveData;
  407. X
  408. Xtypedef struct {
  409. X    Pixmap bitmap;        /* bitmap to draw Field in */
  410. X    Widget widget;        /* the toggle widget */
  411. X} DisplayField;
  412. X
  413. Xtypedef struct {
  414. X    Display *d;
  415. X    GC cleargc, gc;        /* Graphics Contexts for clearing and drawing */
  416. X    Widget top_level;    /* application shell of the display */
  417. X    Widget new_game, quit;
  418. X    Widget turnMove, turnDistr, turnCapture, turnDo, turnCancel;
  419. X    Widget message;
  420. X    XtIntervalId messTimeOut;
  421. X    char *rulesHint;
  422. X    DisplayField board[BOARD_SIZE][BOARD_SIZE];
  423. X} PerDisplay;
  424. X
  425. Xtypedef struct {
  426. X    MoveData *move;
  427. X    /* what is player going to do in this turn? */
  428. X#if NeedFunctionPrototypes
  429. X    Boolean (*executeTurn)(void);
  430. X#else
  431. X    Boolean (*executeTurn)();
  432. X#endif
  433. X    PerDisplay pd[MAX_FIELD_TYPE];    /* don't provide for Empty */
  434. X    FieldType requests_new, requests_quit;
  435. X} DisplayData;
  436. X
  437. Xstatic void CreateUserInterface();
  438. Xstatic void InitDrawing();
  439. Xstatic void DrawField();
  440. Xstatic void InitTurn();
  441. Xstatic void NewGame();
  442. Xstatic void InitGame();
  443. Xstatic void Quit();
  444. Xstatic void DoMove();
  445. Xstatic void DoDistribute();
  446. Xstatic void DoCapture();
  447. Xstatic void DoTurn();
  448. Xstatic void PrepareDoTurn();
  449. Xstatic void CancelTurn();
  450. Xstatic Boolean FindSingleSelected();
  451. Xstatic void FindPossibilities();
  452. Xstatic Boolean Move();
  453. Xstatic Boolean Distribute();
  454. Xstatic Boolean Capture();
  455. Xstatic Boolean IsSet();
  456. Xstatic Boolean GameEnds();
  457. X#define OtherSide(side) \
  458. X        ((FieldType)(((int) BlackSide + (int) WhiteSide) - (int) side))
  459. Xstatic void BeepMessage();
  460. Xstatic void SetRulesHint();
  461. Xstatic void RestoreMessage();
  462. X
  463. Xstatic XtAppContext app_context;
  464. X
  465. Xstatic MoveData move_data;        /* data of next move */
  466. Xstatic DisplayData dd = { &move_data };    /* data to display field */
  467. X
  468. Xstatic struct _app_res {
  469. X    String black_side;
  470. X    String white_side;
  471. X    int message_time_out;
  472. X} app_res;
  473. X
  474. X#define offset(field) XtOffsetOf(struct _app_res, field)
  475. Xstatic XtResource resources[] = {
  476. X    {"black", "Black", XtRString, sizeof(String),
  477. X        offset(black_side), XtRImmediate, NULL},
  478. X    {"white", "White", XtRString, sizeof(String),
  479. X        offset(white_side), XtRImmediate, NULL},
  480. X    {"messageTimeOut", "MessageTimeOut", XtRInt, sizeof(int),
  481. X        offset(message_time_out), XtRImmediate,
  482. X                        (XtPointer) MESS_TIME_OUT},
  483. X};
  484. X
  485. Xstatic XrmOptionDescRec options[] = {
  486. X    {"-black", ".black",        XrmoptionSepArg,    NULL},
  487. X    {"-white", ".white",        XrmoptionSepArg,    NULL},
  488. X    {"-mto", ".messageTimeOut",    XrmoptionSepArg,    NULL},
  489. X};
  490. X
  491. Xstatic String fallback_res[] = {
  492. X    "*quit.fromHoriz:    new",
  493. X    "*distribute.fromHoriz:    move",
  494. X    "*capture.fromHoriz:    distribute",
  495. X    "*do.fromHoriz:        capture",
  496. X    "*cancel.fromHoriz:    do",
  497. X    NULL
  498. X};
  499. X
  500. Xstatic char app_class[] = "Malawi";
  501. X
  502. Xvoid main(argc, argv)
  503. Xint argc;
  504. Xchar *argv[];
  505. X{
  506. X    Display *d;
  507. X    Widget top_level;
  508. X    Window w;
  509. X    int i, j;
  510. X    static char white_title[50], black_title[50];
  511. X    Boolean top_level_used;
  512. X
  513. X    (void) sprintf(white_title, "Malawi %s, White side", VERSION);
  514. X    (void) sprintf(black_title, "Malawi %s, Black side", VERSION);
  515. X
  516. X    XtToolkitInitialize();
  517. X    app_context = XtCreateApplicationContext();
  518. X    XtAppSetFallbackResources(app_context, fallback_res);
  519. X
  520. X    d = XtOpenDisplay(app_context, NULL, argv[0], app_class,
  521. X            options, XtNumber(options), &argc, argv);
  522. X    if (d == NULL) {
  523. X    fprintf(stderr, "Can't open display\n");
  524. X    exit(1);
  525. X    }
  526. X    if (argc > 1) {
  527. X    fprintf(stderr,
  528. X        "Usage: %s [-black display] [-white display] [-mto time-out]\n",
  529. X        argv[0]);
  530. X    exit(1);
  531. X    }
  532. X
  533. X    top_level = XtVaAppCreateShell(NULL, app_class,
  534. X            applicationShellWidgetClass, d, 
  535. X            NULL);
  536. X
  537. X    XtGetApplicationResources(top_level, (XtPointer) &app_res,
  538. X            resources, XtNumber(resources), NULL, 0);
  539. X
  540. X    top_level_used = False;
  541. X    /* find white side's display */
  542. X    if (app_res.white_side == NULL) {
  543. X    dd.pd[WhiteSide].d = d;
  544. X    dd.pd[WhiteSide].top_level = top_level;
  545. X    XtVaSetValues(top_level, XtNtitle, (XtArgVal) white_title, NULL);
  546. X    top_level_used = True;
  547. X    } else {
  548. X    dd.pd[WhiteSide].d = XtOpenDisplay(app_context, app_res.white_side,
  549. X                argv[0], app_class, NULL, 0, &argc, argv);
  550. X    if (dd.pd[WhiteSide].d == NULL) {
  551. X        fprintf(stderr, "Can't open display %s\n", app_res.white_side);
  552. X        exit(1);
  553. X    }
  554. X    dd.pd[WhiteSide].top_level = XtVaAppCreateShell(NULL, app_class,
  555. X            applicationShellWidgetClass, dd.pd[WhiteSide].d,
  556. X            XtNtitle,    (XtArgVal) white_title,
  557. X            NULL);
  558. X    }
  559. X    CreateUserInterface(WhiteSide);
  560. X
  561. X    /* find black side's display */
  562. X    if (!top_level_used && app_res.black_side == NULL) {
  563. X    dd.pd[BlackSide].d = d;
  564. X    dd.pd[BlackSide].top_level = top_level;
  565. X    XtVaSetValues(top_level, XtNtitle, (XtArgVal) black_title, NULL);
  566. X    top_level_used = True;
  567. X    } else {
  568. X    dd.pd[BlackSide].d = XtOpenDisplay(app_context, app_res.black_side,
  569. X                argv[0], app_class, NULL, 0, &argc, argv);
  570. X    if (dd.pd[BlackSide].d == NULL) {
  571. X        fprintf(stderr, "Can't open display %s\n", app_res.black_side);
  572. X        exit(1);
  573. X    }
  574. X    dd.pd[BlackSide].top_level = XtVaAppCreateShell(NULL, app_class,
  575. X            applicationShellWidgetClass, dd.pd[BlackSide].d,
  576. X            XtNtitle,    (XtArgVal) black_title,
  577. X            NULL);
  578. X    }
  579. X    CreateUserInterface(BlackSide);
  580. X
  581. X    XtRealizeWidget(dd.pd[WhiteSide].top_level);
  582. X    XtRealizeWidget(dd.pd[BlackSide].top_level);
  583. X
  584. X    /* draw board */
  585. X    w = XtWindow(dd.pd[WhiteSide].top_level);
  586. X    for (i = 0; i < BOARD_SIZE; i++) {
  587. X    for (j = 0; j < BOARD_SIZE; j++) {
  588. X        dd.pd[WhiteSide].board[i][j].bitmap =
  589. X        XCreatePixmap(dd.pd[WhiteSide].d, w,
  590. X                    FIELD_WIDTH, FIELD_HEIGHT, 1);
  591. X    }
  592. X    }
  593. X
  594. X    w = XtWindow(dd.pd[BlackSide].top_level);
  595. X    for (i = 0; i < BOARD_SIZE; i++) {
  596. X    for (j = 0; j < BOARD_SIZE; j++) {
  597. X        dd.pd[BlackSide].board[i][j].bitmap =
  598. X        XCreatePixmap(dd.pd[BlackSide].d, w,
  599. X                    FIELD_WIDTH, FIELD_HEIGHT, 1);
  600. X    }
  601. X    }
  602. X
  603. X    InitDrawing(WhiteSide);
  604. X    InitDrawing(BlackSide);
  605. X
  606. X    InitGame(NULL, NULL, NULL);
  607. X
  608. X    XtAppMainLoop(app_context);
  609. X    /*NOTREACHED*/
  610. X}
  611. X
  612. X
  613. X#define HIGHTHICK    2
  614. X
  615. Xstatic void CreateUserInterface(side)
  616. XFieldType side;
  617. X{
  618. X    Widget panel, game, form, turn;
  619. X    int i, j;
  620. X
  621. X    panel = XtVaCreateManagedWidget("panel", panedWidgetClass,
  622. X                dd.pd[side].top_level, NULL);
  623. X
  624. X    game = XtVaCreateManagedWidget("game", formWidgetClass, panel,
  625. X            NULL);
  626. X
  627. X    dd.pd[side].new_game = XtVaCreateManagedWidget("new",
  628. X            toggleWidgetClass, game,
  629. X            XtNstate,    (XtArgVal) False,
  630. X            NULL);
  631. X    XtAddCallback(dd.pd[side].new_game, XtNcallback, NewGame, (XtPointer) side);
  632. X
  633. X    dd.pd[side].quit = XtVaCreateManagedWidget("quit", toggleWidgetClass, game,
  634. X            XtNstate,    (XtArgVal) False,
  635. X            NULL);
  636. X    XtAddCallback(dd.pd[side].quit, XtNcallback, Quit, (XtPointer) side);
  637. X
  638. X    dd.pd[side].message = XtVaCreateManagedWidget("message",
  639. X            labelWidgetClass, panel,
  640. X            XtNlabel,    (XtArgVal) "",
  641. X            NULL);
  642. X
  643. X    dd.pd[side].messTimeOut = NULL;
  644. X
  645. X    turn = XtVaCreateManagedWidget("turn", formWidgetClass, panel,
  646. X            NULL);
  647. X    dd.pd[side].turnMove = XtVaCreateManagedWidget("move",
  648. X            commandWidgetClass, turn, NULL);
  649. X    dd.pd[side].turnDistr = XtVaCreateManagedWidget("distribute",
  650. X            commandWidgetClass, turn, NULL);
  651. X    dd.pd[side].turnCapture = XtVaCreateManagedWidget("capture",
  652. X            commandWidgetClass, turn, NULL);
  653. X    dd.pd[side].turnDo = XtVaCreateManagedWidget("do",
  654. X            commandWidgetClass, turn, NULL);
  655. X    dd.pd[side].turnCancel = XtVaCreateManagedWidget("cancel",
  656. X            commandWidgetClass, turn, NULL);
  657. X
  658. X    XtAddCallback(dd.pd[side].turnMove, XtNcallback, DoMove, NULL);
  659. X    XtAddCallback(dd.pd[side].turnDistr, XtNcallback, DoDistribute, NULL);
  660. X    XtAddCallback(dd.pd[side].turnCapture, XtNcallback, DoCapture, NULL);
  661. X    XtAddCallback(dd.pd[side].turnDo, XtNcallback, DoTurn, NULL);
  662. X    XtAddCallback(dd.pd[side].turnCancel, XtNcallback, CancelTurn, NULL);
  663. X
  664. X    form = XtVaCreateManagedWidget("form", formWidgetClass, panel,
  665. X            NULL);
  666. X
  667. X    /* create buttons for fields */
  668. X    for (i = 0; i < BOARD_SIZE; i++) {
  669. X    for (j = 0; j < BOARD_SIZE; j++) {
  670. X        dd.pd[side].board[i][j].widget =
  671. X        XtVaCreateManagedWidget("field", toggleWidgetClass, form,
  672. X            XtNlabel,    (XtArgVal) "",
  673. X            XtNwidth,    (XtArgVal) (FIELD_WIDTH+(2*HIGHTHICK)),
  674. X            XtNheight,    (XtArgVal) (FIELD_HEIGHT+(2*HIGHTHICK)),
  675. X            XtNinternalWidth,    (XtArgVal) 0,
  676. X            XtNinternalHeight,    (XtArgVal) 0,
  677. X            XtNhighlightThickness,    (XtArgVal) HIGHTHICK,
  678. X            XtNfromVert,    (XtArgVal) (i == 0 ?
  679. X                NULL : dd.pd[side].board[i-1][0].widget),
  680. X            XtNfromHoriz,    (XtArgVal) (j == 0 ?
  681. X                NULL : dd.pd[side].board[i][j-1].widget),
  682. X            NULL);
  683. X    }
  684. X    }
  685. X}
  686. X
  687. X
  688. X#define SIDE_MARKER_THICK    8
  689. X
  690. Xstatic XRectangle rect[MAX_EUMELS + 1];
  691. X
  692. Xstatic void InitDrawing(side)
  693. XFieldType side;
  694. X{
  695. X    XGCValues values;
  696. X    unsigned long mask;
  697. X    int i;
  698. X
  699. X    values.function = GXcopy;
  700. X    values.foreground = 1;
  701. X    values.background = 0;
  702. X    values.line_width = 0;
  703. X    values.line_style = LineSolid;
  704. X    values.fill_style = FillSolid;
  705. X    mask = GCFunction | GCForeground | GCBackground |
  706. X        GCLineWidth | GCLineStyle | GCFillStyle;
  707. X    dd.pd[side].gc = XCreateGC(dd.pd[side].d,
  708. X            dd.pd[side].board[0][0].bitmap, mask, &values);
  709. X
  710. X    values.foreground = 0;
  711. X    mask = GCFunction | GCForeground | GCBackground | GCFillStyle;
  712. X    dd.pd[side].cleargc = XCreateGC(dd.pd[side].d,
  713. X            dd.pd[side].board[0][0].bitmap, mask, &values);
  714. X
  715. X    /* initialize rectangles array */
  716. X    rect[0].x = SIDE_MARKER_THICK;
  717. X    rect[0].y = FIELD_HEIGHT - ((3 * SIDE_MARKER_THICK) / 2);
  718. X    rect[0].width = FIELD_WIDTH - 2 * SIDE_MARKER_THICK;
  719. X    rect[0].height = SIDE_MARKER_THICK;
  720. X    /* try hard to make the rects nicly aligned */
  721. X    for (i = 1; i <= MAX_EUMELS; i++) {
  722. X    rect[i].x = i * (FIELD_WIDTH / (MAX_EUMELS + 3));
  723. X    rect[i].y = FIELD_HEIGHT - ((3 * SIDE_MARKER_THICK) / 2) -
  724. X            i * ((FIELD_WIDTH - ((3 * SIDE_MARKER_THICK) / 2)) /
  725. X                (MAX_EUMELS + 3));
  726. X    rect[i].width = 2 * (FIELD_WIDTH / (MAX_EUMELS + 3));
  727. X    rect[i].height = ((FIELD_WIDTH - ((3 * SIDE_MARKER_THICK) / 2)) /
  728. X                (MAX_EUMELS + 3)) - 1;
  729. X    }
  730. X}
  731. X
  732. X
  733. Xstatic void DrawField(r, c)
  734. Xint r, c;
  735. X{
  736. X#if NeedFunctionPrototypes
  737. X    int (*fun)(Display *display, Drawable d, GC gc, XRectangle *rect, int c);
  738. X#else
  739. X    int (*fun)();
  740. X#endif
  741. X
  742. X    /* clear drawing area */
  743. X    XFillRectangle(dd.pd[WhiteSide].d, dd.pd[WhiteSide].board[r][c].bitmap,
  744. X            dd.pd[WhiteSide].cleargc,
  745. X            0, 0, FIELD_WIDTH, FIELD_HEIGHT);
  746. X    XFillRectangle(dd.pd[BlackSide].d, dd.pd[BlackSide].board[r][c].bitmap,
  747. X            dd.pd[BlackSide].cleargc,
  748. X            0, 0, FIELD_WIDTH, FIELD_HEIGHT);
  749. X    if (dd.move->board[r][c].type == Empty) {
  750. X    /* we're done */
  751. X    XtVaSetValues(dd.pd[WhiteSide].board[r][c].widget,
  752. X        XtNbitmap, (XtArgVal) dd.pd[WhiteSide].board[r][c].bitmap,
  753. X        NULL);
  754. X    XtVaSetValues(dd.pd[BlackSide].board[r][c].widget,
  755. X        XtNbitmap, (XtArgVal) dd.pd[BlackSide].board[r][c].bitmap,
  756. X        NULL);
  757. X    return;
  758. X    }
  759. X
  760. X    switch (dd.move->board[r][c].type) {
  761. X    case BlackSide:
  762. X    fun = XFillRectangles;
  763. X    break;
  764. X
  765. X    case WhiteSide:
  766. X    fun = XDrawRectangles;
  767. X    break;
  768. X
  769. X    default:
  770. X    BeepMessage(BlackSide, "Default case must not occur. Call maintainer.");
  771. X    BeepMessage(WhiteSide, "Default case must not occur. Call maintainer.");
  772. X    return;
  773. X    }
  774. X
  775. X    (*fun)(dd.pd[WhiteSide].d, dd.pd[WhiteSide].board[r][c].bitmap,
  776. X            dd.pd[WhiteSide].gc,
  777. X            rect, dd.move->board[r][c].count + 1);
  778. X    XtVaSetValues(dd.pd[WhiteSide].board[r][c].widget,
  779. X        XtNbitmap, (XtArgVal) dd.pd[WhiteSide].board[r][c].bitmap,
  780. X        NULL);
  781. X    (*fun)(dd.pd[BlackSide].d, dd.pd[BlackSide].board[r][c].bitmap,
  782. X            dd.pd[BlackSide].gc,
  783. X            rect, dd.move->board[r][c].count + 1);
  784. X    XtVaSetValues(dd.pd[BlackSide].board[r][c].widget,
  785. X        XtNbitmap, (XtArgVal) dd.pd[BlackSide].board[r][c].bitmap,
  786. X        NULL);
  787. X}
  788. X
  789. X
  790. X/*ARGSUSED*/
  791. Xstatic void NewGame(w, client_data, call_data)
  792. XWidget w;
  793. XXtPointer client_data, call_data;
  794. X{
  795. X    FieldType side = (FieldType) client_data;
  796. X    FieldType other_side = OtherSide(side);
  797. X    static Arg arg = { XtNstate };
  798. X
  799. X    arg.value = (XtArgVal) IsSet(w);
  800. X    /* set button on opponent's display */
  801. X    XtSetValues(dd.pd[other_side].new_game, &arg, 1);
  802. X
  803. X    if (arg.value) {    /* i.e. if (IsSet(w)) */
  804. X    BeepMessage(other_side, "Opponent requests new game");
  805. X    dd.requests_new = side;
  806. X    } else {
  807. X    if (dd.requests_new != side) {
  808. X        /* request confirmed */
  809. X        InitGame();
  810. X
  811. X        /* withdraw quit requests; arg.value is False */
  812. X        XtSetValues(dd.pd[WhiteSide].quit, &arg, 1);
  813. X        XtSetValues(dd.pd[BlackSide].quit, &arg, 1);
  814. X    }
  815. X    }
  816. X}
  817. X
  818. X
  819. Xstatic void InitGame()
  820. X{
  821. X    int i, j;
  822. X
  823. X    dd.move->remain[BlackSide] = dd.move->remain[WhiteSide] =
  824. X                BOARD_SIZE * INITIAL_EUMELS;
  825. X    dd.move->side = WhiteSide;
  826. X    dd.move->other_side = BlackSide;
  827. X
  828. X    for (j = 0; j < BOARD_SIZE; j++) {
  829. X    dd.move->board[0][j].type = BlackSide;
  830. X    dd.move->board[0][j].count = INITIAL_EUMELS;
  831. X    dd.move->board[0][j].r = 0;
  832. X    dd.move->board[0][j].c = j;
  833. X    dd.move->board[BOARD_SIZE - 1][j].type = WhiteSide;
  834. X    dd.move->board[BOARD_SIZE - 1][j].count = INITIAL_EUMELS;
  835. X    dd.move->board[BOARD_SIZE - 1][j].r = BOARD_SIZE - 1;
  836. X    dd.move->board[BOARD_SIZE - 1][j].c = j;
  837. X    }
  838. X    for (i = 1; i < BOARD_SIZE - 1; i++) {
  839. X    for (j = 0; j < BOARD_SIZE; j++) {
  840. X        dd.move->board[i][j].type = Empty;
  841. X        dd.move->board[i][j].count = 0;
  842. X        dd.move->board[i][j].r = i;
  843. X        dd.move->board[i][j].c = j;
  844. X    }
  845. X    }
  846. X
  847. X    CancelTurn(NULL, NULL, NULL);
  848. X
  849. X    for (i = 0; i < BOARD_SIZE; i++) {
  850. X    for (j = 0; j < BOARD_SIZE; j++) {
  851. X        DrawField(i, j);
  852. X    }
  853. X    }
  854. X}
  855. X
  856. X
  857. X/*ARGSUSED*/
  858. Xstatic void Quit(w, client_data, call_data)
  859. XWidget w;
  860. XXtPointer client_data, call_data;
  861. X{
  862. X    FieldType side = (FieldType) client_data;
  863. X    FieldType other_side = OtherSide(side);
  864. X
  865. X    if (IsSet(w)) {
  866. X    XtVaSetValues(dd.pd[other_side].quit,
  867. X            XtNstate, (XtArgVal) True, NULL);
  868. X    BeepMessage(other_side, "Opponent requests quitting");
  869. X    dd.requests_quit = side;
  870. X    } else {
  871. X    XtVaSetValues(dd.pd[other_side].quit,
  872. X            XtNstate, (XtArgVal) False, NULL);
  873. X    if (dd.requests_quit != side) {
  874. X        /* request confirmed */
  875. X        exit(0);
  876. X    }
  877. X    }
  878. X}
  879. X
  880. X
  881. X/*ARGSUSED*/
  882. Xstatic void DoMove(w, clientData, notused)
  883. XWidget w;
  884. XXtPointer clientData, notused;
  885. X{
  886. X    int i, j, k;
  887. X    int r, c;        /* position of source field */
  888. X    int move_possibilities;
  889. X    Field *source, *field;
  890. X    Widget wid;
  891. X
  892. X    /* prepare board for move */
  893. X    dd.executeTurn = Move;
  894. X    if (FindSingleSelected(&r, &c)) {
  895. X    source = &dd.move->board[r][c];
  896. X    if (source->count == 0) {
  897. X        BeepMessage(dd.move->side, "This field cannot move, no eumels!");
  898. X        return;
  899. X    }
  900. X    dd.move->source = source;
  901. X
  902. X    FindPossibilities(dd.move, r, c);
  903. X
  904. X    move_possibilities = dd.move->no_possibilities;
  905. X    for (k = dd.move->no_possibilities; k > 0;) {
  906. X        if (dd.move->possibility[--k]->type != Empty) {
  907. X        move_possibilities--;
  908. X        }
  909. X    }
  910. X    if (move_possibilities == 0) {
  911. X        BeepMessage(dd.move->side, "These eumels cannot move: blocked!");
  912. X        return;
  913. X    }
  914. X
  915. X    k = 0;
  916. X    for (i = 0; i < BOARD_SIZE; i++) {
  917. X        for (j = 0; j < BOARD_SIZE; j++) {
  918. X        field = &dd.move->board[i][j];
  919. X        wid = dd.pd[dd.move->side].board[i][j].widget;
  920. X        XtVaSetValues(wid, XtNstate, (XtArgVal) False, NULL);
  921. X        if (dd.move->possibility[k] == field) {
  922. X            /* the field may or may not be empty;
  923. X            FindPossibilities doesn't check for that */
  924. X            XtSetSensitive(wid, field->type == Empty);
  925. X            k++;
  926. X        } else {
  927. X            XtSetSensitive(wid, False);
  928. X        }
  929. X        }
  930. X    }
  931. X
  932. X    SetRulesHint(dd.move->side, "Select destination field");
  933. X    PrepareDoTurn();
  934. X    }
  935. X}
  936. X
  937. X
  938. X/*ARGSUSED*/
  939. Xstatic void DoDistribute(w, clientData, notused)
  940. XWidget w;
  941. XXtPointer clientData, notused;
  942. X{
  943. X    int i, j;
  944. X    int r, c;        /* position of source field */
  945. X    Field *source, *field;
  946. X    Widget wid;
  947. X
  948. X    /* prepare board for distribution */
  949. X    dd.executeTurn = Distribute;
  950. X    if (FindSingleSelected(&r, &c)) {
  951. X    source = &dd.move->board[r][c];
  952. X    if (source->count == 0) {
  953. X        BeepMessage(dd.move->side, "This field has no eumels!");
  954. X        return;
  955. X    }
  956. X    dd.move->source = source;
  957. X
  958. X    dd.move->no_possibilities = 0;
  959. X    for (i = 0; i < BOARD_SIZE; i++) {
  960. X        for (j = 0; j < BOARD_SIZE; j++) {
  961. X        field = &dd.move->board[i][j];
  962. X        wid = dd.pd[dd.move->side].board[i][j].widget;
  963. X        XtVaSetValues(wid, XtNstate, (XtArgVal) False, NULL);
  964. X        if (field != source &&
  965. X            field->type == dd.move->side &&
  966. X            field->count < BOARD_SIZE) {
  967. X            XtSetSensitive(wid, True);
  968. X            dd.move->possibility[dd.move->no_possibilities++] = field;
  969. X        } else {
  970. X            XtSetSensitive(wid, False);
  971. X        }
  972. X        }
  973. X    }    
  974. X
  975. X    SetRulesHint(dd.move->side,
  976. X            "Select sticks onto which to distribute eumels");
  977. X    PrepareDoTurn();
  978. X    }
  979. X}
  980. X
  981. X
  982. X/*ARGSUSED*/
  983. Xstatic void DoCapture(w, clientData, notused)
  984. XWidget w;
  985. XXtPointer clientData, notused;
  986. X{
  987. X    int i, j, k;
  988. X    int r, c;        /* position of source field */
  989. X    int capture_possibilities;
  990. X    Field *source, *field;
  991. X    Widget wid;
  992. X
  993. X    /* prepare board for capture */
  994. X    dd.executeTurn = Capture;
  995. X    if (FindSingleSelected(&r, &c)) {
  996. X    source = &dd.move->board[r][c];
  997. X    if (source->count == 0) {
  998. X        BeepMessage(dd.move->side, "This stick has no eumels!");
  999. X        return;
  1000. X    }
  1001. X    dd.move->source = source;
  1002. X
  1003. X    FindPossibilities(dd.move, r, c);
  1004. X
  1005. X    capture_possibilities = dd.move->no_possibilities;
  1006. X    for (k = dd.move->no_possibilities - 1; k >= 0; k--) {
  1007. X        if (dd.move->possibility[k]->count == 0 ||
  1008. X        dd.move->possibility[k]->type != dd.move->other_side) {
  1009. X        capture_possibilities--;
  1010. X        }
  1011. X    }
  1012. X    if (capture_possibilities == 0) {
  1013. X        BeepMessage(dd.move->side, "This stick cannot capture eumels!");
  1014. X        return;
  1015. X    }
  1016. X
  1017. X    k = 0;
  1018. X    for (i = 0; i < BOARD_SIZE; i++) {
  1019. X        for (j = 0; j < BOARD_SIZE; j++) {
  1020. X        field = &dd.move->board[i][j];
  1021. X        wid = dd.pd[dd.move->side].board[i][j].widget;
  1022. X        XtVaSetValues(wid, XtNstate, (XtArgVal) False, NULL);
  1023. X        if (dd.move->possibility[k] == field) {
  1024. X            /* the field may or may not be removable;
  1025. X            FindPossibilities doesn't check for that */
  1026. X            XtSetSensitive(wid, field->count != 0 &&
  1027. X                    field->type == dd.move->other_side);
  1028. X            k++;
  1029. X        } else {
  1030. X            XtSetSensitive(wid, False);
  1031. X        }
  1032. X        }
  1033. X    }
  1034. X
  1035. X    SetRulesHint(dd.move->side,
  1036. X            "Select the field from which to capture eumels");
  1037. X    PrepareDoTurn();
  1038. X    }
  1039. X}
  1040. X
  1041. X
  1042. Xstatic void PrepareDoTurn()
  1043. X{
  1044. X    PerDisplay *me = &dd.pd[dd.move->side];
  1045. X
  1046. X    XtSetSensitive(me->turnMove, False);
  1047. X    XtSetSensitive(me->turnDistr, False);
  1048. X    XtSetSensitive(me->turnCapture, False);
  1049. X    XtSetSensitive(me->turnDo, True);
  1050. X    XtSetSensitive(me->turnCancel, True);
  1051. X}
  1052. X
  1053. X
  1054. X/*ARGSUSED*/
  1055. Xstatic void DoTurn(w, client_data, unused)
  1056. XWidget w;
  1057. XXtPointer client_data, unused;
  1058. X{
  1059. X    PerDisplay *me, *other;
  1060. X    FieldType won_by;
  1061. X    static char buf[100];
  1062. X
  1063. X    if ((*dd.executeTurn)()) {
  1064. X    dd.move->other_side = dd.move->side;
  1065. X    dd.move->side = OtherSide(dd.move->side);
  1066. X
  1067. X    if (GameEnds(dd.move, &won_by)) {
  1068. X        /* game ends */
  1069. X        BeepMessage(WhiteSide, "GAME OVER");
  1070. X        BeepMessage(BlackSide, "GAME OVER");
  1071. X        (void) sprintf(buf, "Game won by %s side",
  1072. X            won_by == BlackSide ? "Black" : "White");
  1073. X        dd.pd[WhiteSide].rulesHint = buf;
  1074. X        dd.pd[BlackSide].rulesHint = buf;
  1075. X
  1076. X        me = &dd.pd[dd.move->side];
  1077. X        other = &dd.pd[dd.move->other_side];
  1078. X        XtSetSensitive(me->turnDo, False);
  1079. X        XtSetSensitive(me->turnCancel, False);
  1080. X        XtSetSensitive(other->turnDo, False);
  1081. X        XtSetSensitive(other->turnCancel, False);
  1082. X    } else {
  1083. X        CancelTurn(w, NULL, NULL);
  1084. X    }
  1085. X    }
  1086. X}
  1087. X
  1088. X
  1089. X/*ARGSUSED*/
  1090. Xstatic void CancelTurn(w, client_data, unused)
  1091. XWidget w;
  1092. XXtPointer client_data, unused;
  1093. X{
  1094. X    PerDisplay *me, *other;
  1095. X
  1096. X    me = &dd.pd[dd.move->side];
  1097. X    other = &dd.pd[dd.move->other_side];
  1098. X
  1099. X    XtSetSensitive(me->turnMove, True);
  1100. X    XtSetSensitive(me->turnDistr, True);
  1101. X    XtSetSensitive(me->turnCapture, True);
  1102. X    XtSetSensitive(me->turnDo, False);
  1103. X    XtSetSensitive(me->turnCancel, False);
  1104. X    XtSetSensitive(other->turnMove, False);
  1105. X    XtSetSensitive(other->turnDistr, False);
  1106. X    XtSetSensitive(other->turnCapture, False);
  1107. X    XtSetSensitive(other->turnDo, False);
  1108. X    XtSetSensitive(other->turnCancel, False);
  1109. X    InitTurn();
  1110. X}
  1111. X
  1112. X
  1113. X/* Initialize for new turn: Only the side's fields are sensitive. */
  1114. Xstatic void InitTurn()
  1115. X{
  1116. X    int i, j;
  1117. X    PerDisplay *me, *other;
  1118. X
  1119. X    me = &dd.pd[dd.move->side];
  1120. X    other = &dd.pd[dd.move->other_side];
  1121. X    for (i = 0; i < BOARD_SIZE; i++) {
  1122. X    for (j = 0; j < BOARD_SIZE; j++) {
  1123. X        XtSetSensitive(me->board[i][j].widget,
  1124. X            dd.move->board[i][j].type == dd.move->side);
  1125. X        XtVaSetValues(me->board[i][j].widget,
  1126. X            XtNstate, (XtArgVal) False, NULL);
  1127. X        XtSetSensitive(other->board[i][j].widget, False);
  1128. X        XtVaSetValues(other->board[i][j].widget,
  1129. X            XtNstate, (XtArgVal) False, NULL);
  1130. X    }
  1131. X    }
  1132. X
  1133. X    SetRulesHint(dd.move->side, "Select one stick");
  1134. X    SetRulesHint(dd.move->other_side, "Opponent's turn");
  1135. X}
  1136. X
  1137. X
  1138. Xstatic Boolean FindSingleSelected(r, c)
  1139. Xint *r, *c;
  1140. X{
  1141. X    int i, j;
  1142. X    Boolean found = False;
  1143. X    PerDisplay *me = &dd.pd[dd.move->side];
  1144. X
  1145. X    for (i = 0; i < BOARD_SIZE; i++) {
  1146. X    for (j = 0; j < BOARD_SIZE; j++) {
  1147. X        if (IsSet(me->board[i][j].widget)) {
  1148. X        if (found) {
  1149. X            /* more than one field selected */
  1150. X            BeepMessage(dd.move->side,
  1151. X                    "More than one field is selected!");
  1152. X            return False;
  1153. X        }
  1154. X        found = True;
  1155. X        *r = i;
  1156. X        *c = j;
  1157. X        }
  1158. X    }
  1159. X    }
  1160. X    if (!found) {
  1161. X    /* no field is selected */
  1162. X    BeepMessage(dd.move->side, "No field is selected!");
  1163. X    return False;
  1164. X    }
  1165. X    return True;
  1166. X}
  1167. X
  1168. X
  1169. X/**
  1170. XFind the fields which can be reached from a given field.
  1171. X*/
  1172. Xstatic void FindPossibilities(move, r, c)
  1173. XMoveData *move;
  1174. Xint r, c;        /* position of source field */
  1175. X{
  1176. X    int i;
  1177. X    int dpr, dpc;    /* positive row, column */
  1178. X    int dnr, dnc;    /* negative row, coulmn */
  1179. X    Boolean north_ok, east_ok, south_ok, west_ok;
  1180. X
  1181. X    north_ok = east_ok = south_ok = west_ok = True;
  1182. X    dpr = dnr = r, dpc = dnc = c;
  1183. X
  1184. X    i = move->board[r][c].count;
  1185. X    if (i == 0) {
  1186. X        move->no_possibilities = 0;
  1187. X        return;
  1188. X    }
  1189. X
  1190. X    /* check for occupied fields BETWEEN source and possible destinations */
  1191. X    for (i--; i; i--) {
  1192. X        ++dpr, ++dpc, --dnr, --dnc;
  1193. X        if (north_ok &&
  1194. X            (dnr < 0 || move->board[dnr][c].type != Empty))
  1195. X        north_ok = False;
  1196. X        if (west_ok &&
  1197. X            (dnc < 0 || move->board[r][dnc].type != Empty))
  1198. X        west_ok = False;
  1199. X        if (east_ok &&
  1200. X            (dpc>=BOARD_SIZE || move->board[r][dpc].type != Empty))
  1201. X        east_ok = False;
  1202. X        if (south_ok &&
  1203. X            (dpr>=BOARD_SIZE || move->board[dpr][c].type != Empty))
  1204. X        south_ok = False;
  1205. X    }
  1206. X    ++dpr, ++dpc, --dnr, --dnc;
  1207. X
  1208. X    /* check that possible destinations don't fall off board */
  1209. X    if (dnr < 0)
  1210. X        north_ok = False;
  1211. X    if (dnc < 0)
  1212. X        west_ok = False;
  1213. X    if (dpc >= BOARD_SIZE)
  1214. X        east_ok = False;
  1215. X    if (dpr >= BOARD_SIZE)
  1216. X        south_ok = False;
  1217. X
  1218. X
  1219. X    /* ensure that the possiblities are stored in "increasing" order if
  1220. X        board is scanned with column index changing fastest */
  1221. X    i = 0;
  1222. X    if (north_ok)
  1223. X        move->possibility[i++] = &move->board[dnr][c];
  1224. X    if (west_ok)
  1225. X        move->possibility[i++] = &move->board[r][dnc];
  1226. X    if (east_ok)
  1227. X        move->possibility[i++] = &move->board[r][dpc];
  1228. X    if (south_ok)
  1229. X        move->possibility[i++] = &move->board[dpr][c];
  1230. X    move->no_possibilities = i;
  1231. X
  1232. X    /* erase remaining array */
  1233. X    while (i < MAX_POSS)
  1234. X        move->possibility[i++] = NULL;
  1235. X}
  1236. X
  1237. X
  1238. Xstatic Boolean Move()
  1239. X{
  1240. X    int i;
  1241. X    Field *field = NULL, *f;
  1242. X    PerDisplay *me = &dd.pd[dd.move->side];
  1243. X
  1244. X    /* this should not occur */ if (dd.move->no_possibilities == 0) return True;
  1245. X
  1246. X    for (i = dd.move->no_possibilities; i > 0;) {
  1247. X    f = dd.move->possibility[--i];
  1248. X    if (IsSet(me->board[f->r][f->c].widget)) {
  1249. X        if (field != NULL) {
  1250. X        BeepMessage(dd.move->side, "More than one field is selected!");
  1251. X        return False;
  1252. X        }
  1253. X        field = f;
  1254. X    }
  1255. X    }
  1256. X    if (field == NULL) {
  1257. X    BeepMessage(dd.move->side, "No field is selected!");
  1258. X    return False;
  1259. X    }
  1260. X    field->type = dd.move->source->type;
  1261. X    field->count = dd.move->source->count;
  1262. X    dd.move->source->type = Empty;
  1263. X    dd.move->source->count = 0;
  1264. X    DrawField(dd.move->source->r, dd.move->source->c);
  1265. X    DrawField(field->r, field->c);
  1266. X    return True;
  1267. X}
  1268. X
  1269. X
  1270. Xstatic Boolean Distribute()
  1271. X{
  1272. X    int i;
  1273. X    int must_distribute;    /* number of fields that must be selected */
  1274. X    int selected = 0;
  1275. X    Field *field;
  1276. X    PerDisplay *me = &dd.pd[dd.move->side];
  1277. X
  1278. X    must_distribute = dd.move->source->count < dd.move->no_possibilities ?
  1279. X            dd.move->source->count : dd.move->no_possibilities;
  1280. X
  1281. X    for (i = dd.move->no_possibilities; i > 0;) {
  1282. X    field = dd.move->possibility[--i];
  1283. X    if (IsSet(me->board[field->r][field->c].widget)) {
  1284. X        selected++;
  1285. X    }
  1286. X    }
  1287. X
  1288. X    if (selected != must_distribute) {
  1289. X    char buf[100];
  1290. X    (void) sprintf(buf, "Exactly %d fields must be selected!",
  1291. X            must_distribute);
  1292. X    BeepMessage(dd.move->side, buf);
  1293. X    return False;
  1294. X    }
  1295. X
  1296. X    for (i = dd.move->no_possibilities; i > 0;) {
  1297. X    field = dd.move->possibility[--i];
  1298. X    if (IsSet(me->board[field->r][field->c].widget)) {
  1299. X        field->count++;
  1300. X        dd.move->source->count--;
  1301. X        DrawField(field->r, field->c);
  1302. X    }
  1303. X    }
  1304. X
  1305. X    /* remaining eumels are lost */
  1306. X    dd.move->remain[dd.move->side] -= dd.move->source->count;
  1307. X    dd.move->source->count = 0;
  1308. X    DrawField(dd.move->source->r, dd.move->source->c);
  1309. X
  1310. X    /* This should not lead to end of game. Correct me if I'm wrong. */
  1311. X
  1312. X    return True;
  1313. X}
  1314. X
  1315. X
  1316. Xstatic Boolean Capture()
  1317. X{
  1318. X    int i;
  1319. X    Field *field = NULL, *f;
  1320. X    PerDisplay *me = &dd.pd[dd.move->side];
  1321. X
  1322. X    /* this should not occur */ if (dd.move->no_possibilities == 0) return True;
  1323. X
  1324. X    for (i = dd.move->no_possibilities; i > 0;) {
  1325. X    f = dd.move->possibility[--i];
  1326. X    if (IsSet(me->board[f->r][f->c].widget)) {
  1327. X        if (field != NULL) {
  1328. X        BeepMessage(dd.move->side, "More than one field is selected!");
  1329. X        return False;
  1330. X        }
  1331. X        field = f;
  1332. X    }
  1333. X    }
  1334. X    if (field == NULL) {
  1335. X    BeepMessage(dd.move->side, "No field is selected!");
  1336. X    return False;
  1337. X    }
  1338. X    dd.move->remain[field->type] -= field->count;
  1339. X    field->count = 0;
  1340. X    DrawField(field->r, field->c);
  1341. X
  1342. X    return True;
  1343. X}
  1344. X
  1345. X
  1346. Xstatic Boolean IsSet(w)
  1347. XWidget w;
  1348. X{
  1349. X    Boolean state;
  1350. X
  1351. X    XtVaGetValues(w, XtNstate, (XtArgVal) &state, NULL);
  1352. X    return state;
  1353. X}
  1354. X
  1355. X
  1356. Xstatic Boolean GameEnds(move, winning_side)
  1357. XMoveData *move;
  1358. XFieldType *winning_side;
  1359. X{
  1360. X    int i, j;
  1361. X
  1362. X    if (move->remain[move->side] == 0) {
  1363. X    *winning_side = move->other_side;
  1364. X    return True;
  1365. X    }
  1366. X
  1367. X    i = move->side == BlackSide ? BOARD_SIZE - 1 : 0;
  1368. X    for (j = 0; j < BOARD_SIZE; j++) {
  1369. X    if (move->board[i][j].type == move->side &&
  1370. X        move->board[i][j].count != 0) {
  1371. X        *winning_side = move->side;
  1372. X        return True;
  1373. X    }
  1374. X    }
  1375. X    return False;
  1376. X}
  1377. X
  1378. X
  1379. Xstatic void BeepMessage(side, text)
  1380. XFieldType side;
  1381. Xchar *text;
  1382. X{
  1383. X    PerDisplay *me = &dd.pd[side];
  1384. X
  1385. X    XtVaSetValues(me->message, XtNlabel, (XtArgVal) text, NULL);
  1386. X    XBell(me->d, 0);
  1387. X    if (me->messTimeOut != NULL) {
  1388. X    XtRemoveTimeOut(me->messTimeOut);
  1389. X    }
  1390. X    me->messTimeOut =
  1391. X    XtAppAddTimeOut(app_context, app_res.message_time_out,
  1392. X            RestoreMessage, (XtPointer) side);
  1393. X}
  1394. X
  1395. X
  1396. Xstatic void SetRulesHint(side, text)
  1397. XFieldType side;
  1398. Xchar *text;
  1399. X{
  1400. X    PerDisplay *me = &dd.pd[side];
  1401. X
  1402. X    me->rulesHint = text;
  1403. X    XtVaSetValues(me->message, XtNlabel, (XtArgVal) text, NULL);
  1404. X    if (me->messTimeOut != NULL) {
  1405. X    XtRemoveTimeOut(me->messTimeOut);
  1406. X    }
  1407. X}
  1408. X
  1409. X
  1410. X/*ARGSUSED*/
  1411. Xstatic void RestoreMessage(client_data, id)
  1412. XXtPointer client_data;
  1413. XXtIntervalId *id;
  1414. X{
  1415. X    FieldType side = (FieldType) client_data;
  1416. X    PerDisplay *me = &dd.pd[side];
  1417. X
  1418. X    XtVaSetValues(me->message, XtNlabel, (XtArgVal) me->rulesHint, NULL);
  1419. X    me->messTimeOut = NULL;
  1420. X}
  1421. END_OF_FILE
  1422. if test 29241 -ne `wc -c <'malawi.c'`; then
  1423.     echo shar: \"'malawi.c'\" unpacked with wrong size!
  1424. fi
  1425. # end of 'malawi.c'
  1426. fi
  1427. if test -f 'patchlevel.h' -a "${1}" != "-c" ; then 
  1428.   echo shar: Will not clobber existing file \"'patchlevel.h'\"
  1429. else
  1430. echo shar: Extracting \"'patchlevel.h'\" \(23 characters\)
  1431. sed "s/^X//" >'patchlevel.h' <<'END_OF_FILE'
  1432. X#define VERSION        "0.1"
  1433. END_OF_FILE
  1434. if test 23 -ne `wc -c <'patchlevel.h'`; then
  1435.     echo shar: \"'patchlevel.h'\" unpacked with wrong size!
  1436. fi
  1437. # end of 'patchlevel.h'
  1438. fi
  1439. echo shar: End of archive 1 \(of 1\).
  1440. cp /dev/null ark1isdone
  1441. MISSING=""
  1442. for I in 1 ; do
  1443.     if test ! -f ark${I}isdone ; then
  1444.     MISSING="${MISSING} ${I}"
  1445.     fi
  1446. done
  1447. if test "${MISSING}" = "" ; then
  1448.     echo You have the archive.
  1449.     rm -f ark[1-9]isdone
  1450. else
  1451.     echo You still need to unpack the following archives:
  1452.     echo "        " ${MISSING}
  1453. fi
  1454. ##  End of shell archive.
  1455. exit 0
  1456.  
  1457.