mancala-1.0.3/000755 000765 000024 00000000000 12165225446 014124 5ustar00sverrehustaff000000 000000 mancala-1.0.3/ChangeLog000644 000765 000024 00000001254 12165225133 015671 0ustar00sverrehustaff000000 000000 2013-07-04 Sverre H. Huseby * Release 1.0.3 * No longer ignoring return value from fgets. * No longer accepting negative max-ply arguments. 2012-06-18 Sverre H. Huseby * Release 1.0.2 * Mostly changes from the Debian maintainer Paul Wise again. See NEWS for details. Thank you, Paul! 2007-06-13 Sverre H. Huseby * Release 1.0.1 * Updated author addresses. * Added patch from Paul Wise , allowing users to specify the initial number of stones on the command line. Thanks, Paul! 1998-07-05 Sverre H. Huseby * Release 1.0.0 * Updated author address. mancala-1.0.3/INSTALL000644 000765 000024 00000002443 11767667163 015175 0ustar00sverrehustaff000000 000000 Building on a Un*x system ------------------------- You _will_ have to edit the Makefile to compile the programs. At least you should check the defines CC, XINCDIR, XLIBDIR and OPTIM. I've only built this using GNU make and GNU gcc. Please let me know if you have to make major changes to make this work using other tools. To make the X11 version, you need the XForms-library by T.C. Zhao and Mark Overmars. This nice software is available by anonymous ftp from ftp://laue.phys.uwm.edu/pub/xforms The API of XForms changed a bit between version 0.75 and 0.81. This version on Mancala is updated to compile with 0.81. Typing `make' will attempt to build both the text based version and the X11 version. Run `make mancala' or `make xmancala' to build only one of the programs. Building on MS-DOS ------------------ Only the text based program is available on MS-DOS. The file Makefile.bcc should let you compile mancala using Borland's bcc-compiler. Try `make -f Makefile.bcc'. If you don't have bcc, but you have another C compiler, compile and link mancala.c, minimax.c and textmain.c to mancala.exe. Note that I don't check to see that every new version compiles on MS-DOS. Let me know if it doesn't. If you should happen to make a Windows interface to this program, please let me know! mancala-1.0.3/Makefile000644 000765 000024 00000004307 12165225424 015564 0ustar00sverrehustaff000000 000000 # $Id: Makefile,v 1.11 2012-06-18 18:20:06 sverrehu Exp $ TARGETS = mancala xmancala DIST = mancala VERMAJ = 1 VERMIN = 0 VERPAT = 3 VERSION = $(VERMAJ).$(VERMIN).$(VERPAT) CC = gcc # Common directories and libraries INCDIR = -I. LIBDIR = -L. LIBS = # Directories and libraries for X, Xpm and XForms. # If you don't have Xpm, you'll need to link with a static version of XForms. XINCDIR = -I/usr/include/X11 -I/opt/local/include XLIBDIR = -L/usr/X11R6/lib -L/opt/local/lib XLIBS = -lforms OPTIM = -O3 -fomit-frame-pointer CCOPT = -Wall $(OPTIM) $(INCDIR) $(XINCDIR) -DVERSION=\"$(VERSION)\" LDOPT = -s $(LIBDIR) # Object files common to all programs. OBJS = minimax.o mancala.o # Object files used by xmancala XSRCS = xform.c rulestxt.c XOBJS = $(XSRCS:.c=.o) all: $(TARGETS) %.c %.h: %.fd fdesign -convert $< mancala: textmain.o $(OBJS) $(CC) $(CCOPT) -o $@ textmain.o $(OBJS) $(LDOPT) $(LIBS) xmancala: $(XOBJS) xmain.o $(OBJS) $(CC) $(CCOPT) -o $@ xmain.o $(XOBJS) $(OBJS) \ $(LDOPT) $(XLIBDIR) $(XLIBS) .c.o: $(CC) -o $@ -c $(CCOPT) $< clean: rm -f *.o core depend *~ xform.c xform.h veryclean: clean rm -f $(TARGETS) $(DIST)-$(VERSION).tar.gz chmod: chmod a+r * depend dep: xform.h $(CC) $(INCDIR) $(XINCDIR) -MM *.c >depend # To let the authors make a distribution. The rest of the Makefile # should be used by the authors only. LSMFILE = $(DIST)-$(VERSION).lsm DISTDIR = $(DIST)-$(VERSION) DISTFILE = $(DIST)-$(VERSION).tar.gz DISTFILES = README INSTALL RULES $(LSMFILE) ChangeLog NEWS \ Makefile Makefile.bcc \ mancala.c mancala.h minimax.c minimax.h \ textmain.c \ xform.fd xmain.c rulestxt.c mancala.6 mancala-text $(LSMFILE): FORCE VER=$(VERSION); \ DATE=`date "+%d%b%y"|tr '[a-z]' '[A-Z]'`; \ sed -e "s/VER/$$VER/g;s/DATE/$$DATE/g" $(DIST).lsm.in > $(LSMFILE) FORCE: # Warning: distclean removes the lsm-file, which can not be # regenerated without the lsm.in-file, which is not part of the package. distclean: veryclean rm -f $(LSMFILE) dist: $(LSMFILE) chmod mkdir $(DISTDIR) chmod a+rx $(DISTDIR) ln $(DISTFILES) $(DISTDIR) tar -cvzf $(DISTFILE) $(DISTDIR) chmod a+r $(DISTFILE) rm -rf $(DISTDIR) ifeq (depend,$(wildcard depend)) include depend endif mancala-1.0.3/Makefile.bcc000644 000765 000024 00000001042 06005410433 016273 0ustar00sverrehustaff000000 000000 PROG = mancala EXE = exe CC = bcc RM = del CMODEL = s AMODEL = SMALL OPTIM = -G -O2 -vi -a -k- -N- CPU = -3 CCOPT = -c -d -m$(CMODEL) $(OPTIM) $(CPU) LDOPT = -m$(CMODEL) $(DEBUG) LIBS = OBJS = minimax.obj textmain.obj .AUTODEPEND $(PROG).$(EXE): $(PROG).obj $(OBJS) $(CC) $(LDOPT) -e$(PROG) $(PROG).obj $(OBJS) $(LIBS) clean: $(RM) *.obj $(RM) *.bak veryclean: clean $(RM) $(PROG).$(EXE) .c.obj: $(CC) $(CCOPT) {$< } mancala-1.0.3/mancala-1.0.3.lsm000644 000765 000024 00000001505 12165225446 016673 0ustar00sverrehustaff000000 000000 Begin3 Title: Mancala Version: 1.0.3 Entered-date: 04JUL13 Description: Implementation of the simple board game called Mancala. Contains both a user unfriendly character based interface, and a nice, user friendly X11 interface. Be warned: If you just bother to read the rules, you may get hooked on this! Keywords: game, mancala, minimax Author: shh@thathost.com (Sverre H. Huseby) glennli@simula.no (Glenn Terje Lines) Primary-site: http://shh.thathost.com/pub-unix/ Alternate-site: sunsite.unc.edu /pub/Linux/games/strategy mancala-1.0.3.tar.gz Platforms: For X11 interface, Xlib and XForms are required. If you don't have XForms, get it! It's available from the following site: ftp://laue.phys.uwm.edu/pub/xforms Copying-policy: Artistic License http://sunsite.unc.edu/pub/Linux/LICENSES/artistic.license End mancala-1.0.3/mancala-text000644 000765 000024 00000000451 11767663560 016437 0ustar00sverrehustaff000000 000000 #!/bin/sh # mancala-text - short script to run mancala in xterm for debian # Andrew Gray , Sat, 15 Jul 2000 00:16:28 +0100 echo "Rules are in file /usr/share/doc/mancala/RULES under debian." echo "" /usr/games/mancala 0 4 echo "" printf "Finished - press ENTER " read RUBBISH mancala-1.0.3/mancala.6000644 000765 000024 00000005017 11767667557 015636 0ustar00sverrehustaff000000 000000 .\" Hey, EMACS: -*- nroff -*- .TH MANCALA 6 "13 March 2001" .\" Please adjust this date whenever revising the manpage. .\" .SH NAME mancala \- simple board game for human player and computer .SH SYNOPSIS .B mancala .I level\-top level\-bottom number\-of\-stones .br .B xmancala [\-stones N] .br .B mancala\-text .SH DESCRIPTION This manual page documents briefly the .B mancala and .B xmancala commands. This manual page was written for the Debian GNU/Linux distribution because the original program does not have a manual page. .PP .B mancala runs the game in the console, while .B xmancala is a fancy X\-interface. .B mancala\-text is a shell script to run mancala in a console from the Debian menu system. .PP When run as .B xmancala the program has no options, and should be self\-explanatory. Built in buttons and sliders control the difficulty level (default 4), and bring up the rules of the game. .PP The console version, .BR mancala , has no access to the rules, and the difficulty level can only be set on the command line (see below). When it is your turn to play, you enter the letter of the cell that you want to move the stones from, .IR A " to " F (case insensitive). The rules are contained in the file .IR /usr/share/doc/mancala/RULES . .SH OPTIONS When run as .BR mancala , the program normally takes two arguments, .IR level\-top " and " level\-bottom , one of which should be .I 0 to represent the human player (you), and the other should be in the range .IR 1 " to " 9 , to represent the skill level of the computer player. The first argument represents the top player, who starts. .PP Alternatively, you can give a single argument or two non\-zero arguments, to watch the computer playing itself, or two zero arguments, to play against another human player on the same console. .PP For the console version, you can add a third argument to specify the number of stones per hole. For xmancala you can specify the number of stones per hole using the \-stones N argument. .PP On Debian systems, the shell script .B mancala\-text is provided to facilitate running mancala in a console, from the Debian menu system. This script will run mancala with a difficulty level of 4, and will pause when the game is finished, to enable the player to view the final screen. .SH SEE ALSO .SH AUTHOR This manual page was written by Andrew Gray for the Debian GNU/Linux system (but may be used by others). The program was written by Sverre H. Huseby and Glenn T. Lines. mancala-1.0.3/mancala.c000644 000765 000024 00000032464 10634033640 015665 0ustar00sverrehustaff000000 000000 /* $Id: mancala.c,v 1.2 2007-06-13 18:29:52 sverrehu Exp $ */ /************************************************************************** * * FILE mancala.c * MODULE OF The board game Mancala. * * DESCRIPTION An implementation of the simple game called Mancala. * This is the backend. * * WRITTEN BY Sverre H. Huseby * **************************************************************************/ #include #include #include #include #include #include "minimax.h" #include "mancala.h" /************************************************************************** * * * P R I V A T E D A T A * * * **************************************************************************/ /* * Define this if you want a more timeconsuming evaluation function. * We're not sure it makes it any better -- it may even make it worse. */ #define FULL_EVAL /* * The board state stack, and the current stacklevel. */ static Board boardStack[STACK_LEVELS]; static int idx = 0; /* * The game logic function requests an array of pointers to possible * moves for a level of it's recursion. We need one array for each * possible recursion level, so we don't mess things up. possibleMoveTable * contains the real move variables, while possibleMove contains the * pointers into the variable table. */ static Move possibleMoveTable[STACK_LEVELS][MAX_HOLES]; static PMove possibleMove[STACK_LEVELS][MAX_HOLES]; /************************************************************************** * * * P R I V A T E F U N C T I O N S * * * **************************************************************************/ /*------------------------------------------------------------------------* | Callbacks for the game logic function | *------------------------------------------------------------------------*/ /*------------------------------------------------------------------------- * * NAME pushBoard * * FUNCTION Save the current board state on a stack. * * DESCRIPTION This callback is called at the start of each recursion * level. We use it to save the current board setup, and * to make sure requests for new moves (getMoves()) don't * mess up the array of moves currently being explored. */ static void pushBoard(void) { ++idx; memcpy(&boardStack[idx], &boardStack[idx - 1], sizeof(Board)); } /*------------------------------------------------------------------------- * * NAME popBoard * * FUNCTION Restore the current board stat from the stack. * * DESCRIPTION This callback is called at the end of each recursion * level. We restore the board state (remove the * `experimental' moves), and open for reuse of the array * of possible moves. */ static void popBoard(void) { --idx; } /*------------------------------------------------------------------------- * * NAME getMoves * * FUNCTION Make list of possible moves for the given player. * * INPUT player the player to make movelist for. * * OUTPUT numMoves * number of moves in the returned array. * * RETURNS An array of pointers to legal moves, or NULL if no * legal moves for this player. * * DESCRIPTION This callback is called for each recursion level to * get a list of possible following moves. A legal move is * the number of a non-empty hole for the player in question. * * The current stacklevel is used to find a `free' array * that we can fill with legal moves. We couldn't use the * same array all the time, since the array we returned in * the previous call is not fully examined (yeah, recursion * gives some interresting problems...). */ static PMove *getMoves(Player player, int *numMoves) { int q, n = 0; StoneCount *hole; PMove *m, *ret; m = ret = possibleMove[idx]; hole = boardStack[idx].hole[player]; for (q = 0; q < MAX_HOLES; q++) { if (*hole) m[n++]->hole = q; ++hole; } if ((*numMoves = n) == 0) ret = NULL; return ret; } /*------------------------------------------------------------------------- * * NAME undoMove * * FUNCTION Update the board to the state before the given move. * * INPUT player the player that did the move. * move pointer to the move to undo. * * DESCRIPTION This callback is called at the end of each recursion * level. Since we really change the board in popBoard(), * we don't need to do anything here. */ static void undoMove(Player player, PMove move) { /* memcpy(&boardStack[idx], &boardStack[idx - 1], sizeof(Board)); */ } /*------------------------------------------------------------------------- * * NAME evalBoard * * FUNCTION Evaluate the board after a player has moved. * * INPUT player the player to evaluate the board for. * * RETURNS A measure indicating how good the current board is for * the given player. Larger values are better. A positive * value indicates that the given player is in the lead, * while a negative value indicates the opposite. * * DESCRIPTION We do it simple; just see which player has captured * most stones. */ static Score evalBoard(Player player) { Board *b; #ifdef FULL_EVAL Player winner; /* * The next thing that happens after the evaluation, is that the * previous board is popped from the stack, so we can let the * evaluation function mess the board up like this: */ checkAndFixWin(&winner); #endif b = &boardStack[idx]; return b->mancala[player] - b->mancala[player ^ 1]; } /************************************************************************** * * * P U B L I C F U N C T I O N S * * * **************************************************************************/ /*------------------------------------------------------------------------- * * NAME initGame * * FUNCTION Set up variables for a new game. */ void initGame(int stones_pr_hole) { int q, w; Board *b; Player player; /* * The game logic function uses random numbers to choose between * moves that gives equal score, so we initiate the generator. */ srand(time(NULL)); /* * An important step: Set up the possible move -pointers that the * caller of getMoves() want. We make pointers into the table * containing the real values. */ for (q = 0; q < STACK_LEVELS; q++) for (w = 0; w < MAX_HOLES; w++) possibleMove[q][w] = &possibleMoveTable[q][w]; /* * Clear the game stack, and set up the board to initial state. * The `real' board is actually at position 0 of the stack array. */ idx = 0; b = &boardStack[idx]; for (player = 0; player < 2; player++) { b->mancala[player] = 0; for (w = 0; w < MAX_HOLES; w++) b->hole[player][w] = stones_pr_hole; } } /*------------------------------------------------------------------------- * * NAME getHole * * FUNCTION Get number of stones in a hole on the board. * * INPUT player the player to get number of stones for. * hole the hole number. 0 is the hole closest to * the mancala. * * RETURNS The number of stones in the given hole. */ StoneCount getHole(Player player, int hole) { return boardStack[0].hole[player][hole]; } /*------------------------------------------------------------------------- * * NAME getMancala * * FUNCTION Get number of stones in the mancala of a player. * * INPUT player the player to get number of stones for. * * RETURNS The number of stones in the given player's mancala. */ StoneCount getMancala(Player player) { return boardStack[0].mancala[player]; } /*------------------------------------------------------------------------- * * NAME legalMove * * FUNCTION Check if a human's move is legal. * * INPUT player the player that wants to move. * move pointer to the move to check. * * RETURNS 1 if legal move, 0 if illegal. * * DESCRIPTION This is used to check if a human player has chosen to * do a legal move. The computer player will only do moves * returned by getMoves(), and those are (or should) all be * legal. */ int legalMove(Player player, PMove move) { int q; PMove *legalmove; /* array of pointerts to legal moves */ int numMoves; /* number of moves in this array */ legalmove = getMoves(player, &numMoves); for (q = 0; q < numMoves; q++) { if ((*legalmove)->hole == move->hole) return 1; ++legalmove; } return 0; } /*------------------------------------------------------------------------- * * NAME checkAndFixWin * * FUNCTION Check if the game is ended, and in that case fix scores. * * OUTPUT winner the player that won. * * RETURNS 0 if game not finished, 1 if one winner, 2 if draw. * * DESCRIPTION The game is over if any of the players have no stones * left to move. In that case, all remaining stones for the * opposite player is moved to that player's mancala, and * the winner is the one with more stones. */ int checkAndFixWin(Player *winner) { int q, ret = 0, sum[2]; Board *b; StoneCount *hole; Player player; b = &boardStack[idx]; for (player = 0; player < 2; player++) { hole = b->hole[player]; sum[player] = 0; for (q = 0; q < MAX_HOLES; q++) sum[player] += hole[q]; } if (sum[0] == 0 || sum[1] == 0) { ret = 1; for (player = 0; player < 2; player++) { b->mancala[player] += sum[player]; hole = b->hole[player]; for (q = 0; q < MAX_HOLES; q++) hole[q] = 0; } if (b->mancala[0] > b->mancala[1]) *winner = 0; else if (b->mancala[0] < b->mancala[1]) *winner = 1; else ret = 2; } return ret; } /*------------------------------------------------------------------------- * * NAME getBestMove * * FUNCTION Calculate best move for player. * * INPUT player the player to evaluate. * maxPly max recursion depth. * * RETURNS Pointer to the best move to make, or NULL if no move * available. * * DESCRIPTION This is just a frontend to the real game logic function, * hiding the static functions found in this file. */ PMove getBestMove(Player player, int maxPly) { return miniMax(pushBoard, popBoard, getMoves, doMove, undoMove, evalBoard, player, maxPly); } /*------------------------------------------------------------------------* | The following function is a callback, but it's used for moving too. | *------------------------------------------------------------------------*/ /*------------------------------------------------------------------------- * * NAME doMove * * FUNCTION Update the board with the given move. * * INPUT player the player that does the move. * move pointer to the move to make. * * RETURNS The next player to move. For Mancala, this is the same * player once again if the last stone ends in the mancala. * * DESCRIPTION This function is used both as a callback for finding the * best moves, and as a `normal' function to update the * board when a move is chosen. */ Player doMove(Player player, PMove move) { int seeds, holeNum, oppositeHoleNum; StoneCount *hole, *oppositeHole; Board *b; Player nextPlayer, holeOwner; nextPlayer = player ^ 1; b = &boardStack[idx]; holeOwner = player; hole = b->hole[holeOwner]; holeNum = move->hole; seeds = hole[holeNum]; hole[holeNum] = 0; for (;;) { if (--holeNum < 0) { if (holeOwner == player) { ++b->mancala[player]; if (!--seeds) { nextPlayer = player; break; } } holeOwner ^= 1; hole = b->hole[holeOwner]; holeNum = MAX_HOLES - 1; } ++hole[holeNum]; if (!--seeds) { if (holeOwner == player) { oppositeHoleNum = MAX_HOLES - 1 - holeNum; oppositeHole = b->hole[player ^ 1]; if (hole[holeNum] == 1 && oppositeHole[oppositeHoleNum]) { b->mancala[player] += 1 + oppositeHole[oppositeHoleNum]; oppositeHole[oppositeHoleNum] = 0; hole[holeNum] = 0; } } break; } } return nextPlayer; } mancala-1.0.3/mancala.h000644 000765 000024 00000002266 10634033640 015667 0ustar00sverrehustaff000000 000000 /* $Id: mancala.h,v 1.2 2007-06-13 18:29:52 sverrehu Exp $ */ #ifndef MANCALA_H #define MANCALA_H /* * Mancala board setup. According to tradition, there are 6 small holes * and 4 stones pr hole initially. */ #define MAX_HOLES 6 #define STONES_PR_HOLE 4 /* * We use board stack position 0 as the `real' board, so we need a stack * that is one level deeper than the max recursion depth. */ #define MAX_DEPTH 20 #define STACK_LEVELS (MAX_DEPTH + 1) /* * A Move is identified by a hole number. */ typedef struct Move { int hole; } Move; /* * The Board is described by the number of stones in each of the small * holes, and in the mancalas (larger holes). In this setup, hole[x][0] * is the hole closest to the mancala[x] for player x. */ typedef char StoneCount; typedef struct Board { StoneCount hole[2][MAX_HOLES]; StoneCount mancala[2]; } Board; void initGame(int stones_pr_hole); StoneCount getHole(Player player, int hole); StoneCount getMancala(Player player); int legalMove(Player player, PMove move); int checkAndFixWin(Player *winner); PMove getBestMove(Player player, int maxPly); Player doMove(Player player, PMove move); #endif mancala-1.0.3/minimax.c000644 000765 000024 00000022564 12051677152 015742 0ustar00sverrehustaff000000 000000 /* $Id: minimax.c,v 1.1.1.1 1995-07-25 11:55:21 sverrehu Exp $ */ /************************************************************************** * * FILE minimax.c * * DESCRIPTION General routines for doing simple minimax game tree * search. The caller supplies helper functions for doing * everything that is not directly a part of the algorithm. * * WRITTEN BY Sverre H. Huseby & Glenn Terje Lines * **************************************************************************/ #include #include #include "minimax.h" /************************************************************************** * * * P R I V A T E D A T A * * * **************************************************************************/ /* * Define RANDOM_CHOICE to have the routine choose randomly among moves * giving equal scores. The main part of the program should use srand() * to initialize the random number generator. */ #define RANDOM_CHOICE /* * User supplied callback functions. */ static void (*pushBoard)(void); static void (*popBoard)(void); static PMove *(*getMoves)(Player, int *); static Player (*doMove)(Player, PMove); static void (*undoMove)(Player, PMove); static Score (*evalBoard)(Player); /************************************************************************** * * * P R I V A T E F U N C T I O N S * * * **************************************************************************/ /*------------------------------------------------------------------------- * * NAME recMiniMax * * FUNCTION Do recursive min/max. * * INPUT player the player to evaluate. * move the move to do for this player. * depth max recursion depth. * * RETURNS The score for this move. A positive value indicates * that the given player is in the lead after the move. * A negative value means the opposite player is in the * lead. * * DESCRIPTION This function is the core of the implementation. * */ static Score recMiniMax(Player player, PMove move, int depth) { int q; /* my favourite counter */ PMove *newmove; /* array of pointers to possible following moves */ int numMoves; /* number of moves in this array */ Player nextPlayer; /* the player to do the move follwing */ Score ret; /* the score we'll return in the end */ Score tmp; /* the score for a single move */ pushBoard(); nextPlayer = doMove(player, move); if (!depth) { /* * We have reached the max recursion depth, so we just evaluate * the board with no more checking of moves. */ ret = evalBoard(player); goto finish; } newmove = getMoves(nextPlayer, &numMoves); if (!numMoves) { /* * No more moves for the next player. Just evaluate the board. */ ret = evalBoard(player); goto finish; } /* * Check all following moves, and choose the one maximizing * the score for the player in question. */ ret = -INF_SCORE; for (q = 0; q < numMoves; q++) { tmp = recMiniMax(nextPlayer, *newmove, depth - 1); if (tmp > ret) ret = tmp; ++newmove; } if (player != nextPlayer) ret = -ret; finish: undoMove(player, move); popBoard(); return ret; } /************************************************************************** * * * P U B L I C F U N C T I O N S * * * **************************************************************************/ /*------------------------------------------------------------------------- * * NAME miniMax * * FUNCTION Get the best move for a given player. * * INPUT Several userdefined callback functions: * fncPushBoard * This is called at the start of each level * in the recursion. If neccessary it should save * the status of the board, so it can be restored * later. This is neccessary if fncUndoMove() is * unable to undo a move. * fncPopBoard * This is called at the end of each level in the * recursion. It can be used to restore the board. * fncGetMoves * Return an array of pointers to the possible * moves for the given player. Also return the * number of moves in this array through the pointer * to the integer. Return NULL if there are no * possible moves to do. * The functions in this file don't know what a * move is, but only what pointers to moves are. * Therefore, this is an array of pointers to moves, * not an array of moves. * It is important that this array lasts through all * sublevels of the recursion from where it is * requested. You will probably use fncPushBoard() * and fncPopBoard() to maintain a list of arrays * to choose from. * fncDoMove * This is also called at the start of each recursion * level. * Update the board with the given move. Also, return * the player that is to move next. For some games * this may be the player that just moved. * fncUndoMove * Undo the given move. The move to undo is always the * move most recently done. If undoing is hard, you can * leave it to fncPopBoard() instead, since they * are both called at the end of a recursion level. * fncUndoMove() is called before fncPopBoard(). * fncEvalBoard * function evaluating the current state of the * board for the player supplied. This function should * return a positive value if the player is in the * lead, or a negative value if the opposite player * is in the lead. A higher (absolute) value indicates * a better lead. * player the player to evaluate. * maxPly max recursion depth. * * RETURNS Pointer to the best move to make, or NULL if no move * available. For some games NULL means game over, for other * games, it just means that it's the other player's turn. * You should check the state of the game between each call * to this function. * * Note that the pointer returned points into the array * returned by fncGetMoves(), so you shouldn't overwrite * this array before using the move. * * DESCRIPTION This is the front end to the minimax algorithm. * It sets it all up, and uses recMiniMax() to check each * possible move. * */ PMove miniMax( void (*fncPushBoard)(void), void (*fncPopBoard)(void), PMove *(*fncGetMoves)(Player, int *), Player (*fncDoMove)(Player, PMove), void (*fncUndoMove)(Player, PMove), Score (*fncEvalBoard)(Player), Player player, int maxPly ) { int q; /* a counter variable */ PMove *move; /* array of pointerts to possible moves to make */ int numMoves; /* number of moves in this array */ PMove bestMove; /* the best move so far */ Score v; /* value for a single move */ Score bestValue; /* the value of the best move so far */ /* * Set up the callback functions for the recursive routine. */ pushBoard = fncPushBoard; popBoard = fncPopBoard; getMoves = fncGetMoves; doMove = fncDoMove; undoMove = fncUndoMove; evalBoard = fncEvalBoard; /* * Now loop through all possible moves for the given player, * and find the one with the highest score according to the * minimax algorithm. If there are no possible moves left, * we return NULL. */ bestMove = NULL; move = getMoves(player, &numMoves); bestValue = -INF_SCORE; for (q = 0; q < numMoves; q++) { v = recMiniMax(player, *move, maxPly - 1); #ifdef RANDOM_CHOICE /* * If this value equals the previous best value, we choose * randomly whether we should use this one instead. */ if (v > bestValue || (v == bestValue && (rand() & 1))) { #else if (v >= bestValue) { #endif bestValue = v; bestMove = *move; } ++move; } return bestMove; } mancala-1.0.3/minimax.h000644 000765 000024 00000001164 12051677152 015740 0ustar00sverrehustaff000000 000000 /* $Id: minimax.h,v 1.1.1.1 1995-07-25 11:55:21 sverrehu Exp $ */ #ifndef MINIMAX_H #define MINIMAX_H #include #ifdef __cplusplus extern "C" { #endif struct Move; typedef struct Move *PMove; typedef int Player; typedef int Score; #define INF_SCORE INT_MAX PMove miniMax( void (*fncPushBoard)(void), void (*fncPopBoard)(void), PMove *(*fncGetMoves)(Player, int *), Player (*fncDoMove)(Player, PMove), void (*fncUndoMove)(Player, PMove), Score (*fncEvalBoard)(Player), Player player, int maxPly ); #ifdef __cplusplus } #endif #endif mancala-1.0.3/NEWS000644 000765 000024 00000000602 12165225231 014611 0ustar00sverrehustaff000000 000000 mancala 1.0.3 ------------- Crash fixes. mancala 1.0.2 ------------- Major changes in this version (there really aren't any, so here comes the minor changes): * Updated to work with more recent version of XForms. * Added a man page. * Added NEWS (this file!) and ChangeLog. * Builds on OS X with MacPorts. Most changes were provided by the Debian maintainer Paul Wise. mancala-1.0.3/README000644 000765 000024 00000001511 10634031212 014763 0ustar00sverrehustaff000000 000000 This is an implementation of the simple, but fun board game known as Mancala. For rules of the game, see RULES. A simple character/terminal-based version of the game can be compiled on both Un*x- and MS-DOS-systems. A fancy windowing version requires the X Window System. See INSTALL for brief instructions on how to build the programs. This package is brought to you by `Fellowship of Beer-drinking Programmers', currently Sverre H. Huseby shh@thathost.com Glenn Terje Lines glennli@simula.no Feel free to do what you want with this software, but please drop any of us a mail if you use it (or if you don't use it, for some reason). If you make any changes to the code before passing it further, you must include a file identifying what changes you made. Please leave our names where you find'em. Fame and fortune, you know... mancala-1.0.3/RULES000644 000765 000024 00000002435 11767667163 014762 0ustar00sverrehustaff000000 000000 Note that there are lots of different rule sets for Mancala. Our implementation uses this one: Setup: The board consists of 2 rows of 6 bins and 2 mancalas. Each player is allocated the row of bins closest to him and the mancala to his right. Initially each bin contains 4 stones, and the mancalas are empty. Object: The object of the game is to place as many stones as possible in one's mancala before the game ends. This happens when a player, at his turn, has no legal moves, meaning that all his holes are empty. The other player then gets to put any remaining stones on his side into his mancala. Moving: At his turn, the player picks up all the stones in any of his non-empty bins and starts to sow them by placing one in each of the preceding bins, starting with the one on the right and continuing counterclockwise around the board, skipping the oponents mancala (but not his own). Extra move: If the last stone is sown in one's mancala, one gets to move again. Capturing: If the last stone is sown in an empty bin belonging to the player, he captures all the stones lying in the bin directly across this bin. These stones and the capturing stone are then placed in the mancala. If the bin directly across is empty, nothing special happens. mancala-1.0.3/rulestxt.c000644 000765 000024 00000004434 11767667557 016213 0ustar00sverrehustaff000000 000000 /* $Id: rulestxt.c,v 1.5 2012-06-18 18:11:52 sverrehu Exp $ */ /************************************************************************** * * FILE rulestxt.c * MODULE OF The board game Mancala * * DESCRIPTION Compiled-in instructions used by the X-version. * * WRITTEN BY Sverre H. Huseby * **************************************************************************/ /************************************************************************** * * * P U B L I C D A T A * * * **************************************************************************/ char *textRules = "Note that there are lots of different rule sets for Mancala. Our\n" "implementation uses this one:\n" "\n" "Setup: The board consists of 2 rows of 6 bins and 2 mancalas. Each\n" " player is allocated the row of bins closest to him and the mancala\n" " to his right. Initially each bin contains 4 stones, and the\n" " mancalas are empty.\n" "\n" "Object: The object of the game is to place as many stones as possible\n" " in one's mancala before the game ends. This happens when a player,\n" " at his turn, has no legal moves, meaning that all his holes are\n" " empty. The other player then gets to put any remaining stones on\n" " his side into his mancala.\n" "\n" "Moving: At his turn, the player picks up all the stones in any of his\n" " non-empty bins and starts to sow them by placing one in each of the\n" " preceding bins, starting with the one on the right and continuing\n" " counterclockwise around the board, skipping the oponents mancala\n" " (but not his own).\n" "\n" "Extra move: If the last stone is sown in one's mancala one gets to\n" " move again.\n" "\n" "Capturing: If the last stone is sown in an empty bin belonging to the\n" " player, he captures all the stones lying in the bin directly across\n" " this bin. These stones and the capturing stone are then placed in\n" " the mancala. If the bin directly across is empty, nothing special\n" " happens.\n" "\n" "@_\n" "\n" "Comments? Send mail to either of\n" " shh@thathost.com\n" " glennli@simula.no\n" "\n" ; mancala-1.0.3/textmain.c000644 000765 000024 00000010071 12165224743 016117 0ustar00sverrehustaff000000 000000 /* $Id: textmain.c,v 1.2 2007-06-13 18:29:52 sverrehu Exp $ */ /************************************************************************** * * FILE textmain.c * MODULE OF The board game Mancala * * DESCRIPTION Main function / frontend for using a textbased display. * * WRITTEN BY Sverre H. Huseby * **************************************************************************/ #include #include #include #include #include "minimax.h" #include "mancala.h" /************************************************************************** * * * P R I V A T E F U N C T I O N S * * * **************************************************************************/ /*------------------------------------------------------------------------- * * NAME showBoard * * FUNCTION Display the current board on screen. * * DESCRIPTION Very simple thing. */ static void showBoard(void) { int q; printf(" "); for (q = 0; q < MAX_HOLES; q++) printf((q < MAX_HOLES - 1) ? "%c:%2d | " : "%c:%2d\n", 'a' + MAX_HOLES - q - 1, getHole(1, q)); printf(" %2d ", getMancala(1)); for (q = 0; q < MAX_HOLES; q++) printf((q < MAX_HOLES - 1) ? "-----+-" : "----"); printf(" %2d\n", getMancala(0)); printf(" "); for (q = 0; q < MAX_HOLES; q++) printf((q < MAX_HOLES - 1) ? "%c:%2d | " : "%c:%2d\n", 'a' + q, getHole(0, MAX_HOLES - q - 1)); } /************************************************************************** * * * P U B L I C F U N C T I O N S * * * **************************************************************************/ int main(int argc, char *argv[]) { char s[80]; char playerName[2][20] = { "player at bottom", "player at top" }; int maxPly[2] = { MAX_DEPTH, MAX_DEPTH }, status; int stones_pr_hole = STONES_PR_HOLE; PMove move; Move move2; Player player, winner; if (argc == 2) maxPly[0] = maxPly[1] = atoi(argv[1]); else if (argc == 3) { maxPly[1] = atoi(argv[1]); maxPly[0] = atoi(argv[2]); } else if (argc == 4) { maxPly[1] = atoi(argv[1]); maxPly[0] = atoi(argv[2]); stones_pr_hole = atoi(argv[3]); } else { printf("usage: mancala max-ply\n" " mancala max-ply-top max-ply-bottom\n" " mancala max-ply-top max-ply-bottom number-of-stones\n" "\n" "max-ply of 0 indicates human user.\n" "Player at top starts.\n" "\n"); return 1; } if (stones_pr_hole < 1) { fprintf(stderr, "mancala: the number of stones per hole must be greater " "than zero\n"); return 1; } if (maxPly[0] < 0 || maxPly[1] < 0) { fprintf(stderr, "mancala: max-ply must be no less than zero\n"); return 1; } initGame(stones_pr_hole); player = 1; for (;;) { showBoard(); printf("\n"); if ((status = checkAndFixWin(&winner)) != 0) { printf("=== Game over; "); if (status == 1) printf("%s wins", playerName[winner]); else printf("we have a draw"); printf(" ===\n\n"); showBoard(); printf("\n"); return 0; } printf("Move for %s: ", playerName[player]); if (!maxPly[player]) { move = &move2; for (;;) { if (fgets(s, 80, stdin) == NULL) { printf("\nBye.\n"); return 0; } move->hole = 'A' + (MAX_HOLES - 1) - toupper(s[0]); if (legalMove(player, move)) break; printf("not a legal move. try again: "); } printf("\n"); } else { move = getBestMove(player, maxPly[player]); printf("%c\n\n", 'A' + (MAX_HOLES - 1) - move->hole); } player = doMove(player, move); } return 0; } mancala-1.0.3/xform.fd000644 000765 000024 00000037731 11767663564 015623 0ustar00sverrehustaff000000 000000 Magic: 13000 Internal Form Definition File (do not change) Number of forms: 2 Unit of measure: FL_COORD_PIXEL Border Width: 1 =============== FORM =============== Name: mancala Width: 530 Height: 220 Number of Objects: 45 -------------------- class: FL_BOX type: UP_BOX box: 0 0 530 220 boxtype: FL_UP_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_NORMAL_STYLE size: FL_DEFAULT_SIZE lcol: FL_BLACK label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: callback: argument: -------------------- class: FL_TEXT type: NORMAL_TEXT box: 370 75 150 15 boxtype: FL_FLAT_BOX colors: FL_COL1 FL_MCOL alignment: FL_ALIGN_CENTER|FL_ALIGN_INSIDE style: FL_NORMAL_STYLE size: FL_DEFAULT_SIZE lcol: FL_BLACK label: Glenn Terje Lines shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: callback: argument: -------------------- class: FL_FRAME type: ENGRAVED_FRAME box: 10 25 350 150 boxtype: FL_NO_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_NORMAL_STYLE size: FL_DEFAULT_SIZE lcol: FL_BLACK label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: callback: argument: -------------------- class: FL_BUTTON type: NORMAL_BUTTON box: 65 60 40 40 boxtype: FL_DOWN_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_BOLD_STYLE+FL_EMBOSSED_STYLE size: FL_HUGE_SIZE lcol: FL_SLATEBLUE label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: holeT0 callback: doHole argument: 100 -------------------- class: FL_BUTTON type: NORMAL_BUTTON box: 105 60 40 40 boxtype: FL_DOWN_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_BOLD_STYLE+FL_EMBOSSED_STYLE size: FL_HUGE_SIZE lcol: FL_SLATEBLUE label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: holeT1 callback: doHole argument: 101 -------------------- class: FL_BUTTON type: NORMAL_BUTTON box: 145 60 40 40 boxtype: FL_DOWN_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_BOLD_STYLE+FL_EMBOSSED_STYLE size: FL_HUGE_SIZE lcol: FL_SLATEBLUE label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: holeT2 callback: doHole argument: 102 -------------------- class: FL_BUTTON type: NORMAL_BUTTON box: 185 60 40 40 boxtype: FL_DOWN_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_BOLD_STYLE+FL_EMBOSSED_STYLE size: FL_HUGE_SIZE lcol: FL_SLATEBLUE label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: holeT3 callback: doHole argument: 103 -------------------- class: FL_BUTTON type: NORMAL_BUTTON box: 225 60 40 40 boxtype: FL_DOWN_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_BOLD_STYLE+FL_EMBOSSED_STYLE size: FL_HUGE_SIZE lcol: FL_SLATEBLUE label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: holeT4 callback: doHole argument: 104 -------------------- class: FL_BUTTON type: NORMAL_BUTTON box: 265 60 40 40 boxtype: FL_DOWN_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_BOLD_STYLE+FL_EMBOSSED_STYLE size: FL_HUGE_SIZE lcol: FL_SLATEBLUE label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: holeT5 callback: doHole argument: 105 -------------------- class: FL_BUTTON type: NORMAL_BUTTON box: 65 100 40 40 boxtype: FL_DOWN_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_BOLD_STYLE+FL_EMBOSSED_STYLE size: FL_HUGE_SIZE lcol: FL_SLATEBLUE label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: holeB5 callback: doHole argument: 5 -------------------- class: FL_BUTTON type: NORMAL_BUTTON box: 105 100 40 40 boxtype: FL_DOWN_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_BOLD_STYLE+FL_EMBOSSED_STYLE size: FL_HUGE_SIZE lcol: FL_SLATEBLUE label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: holeB4 callback: doHole argument: 4 -------------------- class: FL_BUTTON type: NORMAL_BUTTON box: 145 100 40 40 boxtype: FL_DOWN_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_BOLD_STYLE+FL_EMBOSSED_STYLE size: FL_HUGE_SIZE lcol: FL_SLATEBLUE label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: holeB3 callback: doHole argument: 3 -------------------- class: FL_BUTTON type: NORMAL_BUTTON box: 185 100 40 40 boxtype: FL_DOWN_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_BOLD_STYLE+FL_EMBOSSED_STYLE size: FL_HUGE_SIZE lcol: FL_SLATEBLUE label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: holeB2 callback: doHole argument: 2 -------------------- class: FL_BUTTON type: NORMAL_BUTTON box: 225 100 40 40 boxtype: FL_DOWN_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_BOLD_STYLE+FL_EMBOSSED_STYLE size: FL_HUGE_SIZE lcol: FL_SLATEBLUE label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: holeB1 callback: doHole argument: 1 -------------------- class: FL_BUTTON type: NORMAL_BUTTON box: 265 100 40 40 boxtype: FL_DOWN_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_BOLD_STYLE+FL_EMBOSSED_STYLE size: FL_HUGE_SIZE lcol: FL_SLATEBLUE label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: holeB0 callback: doHole argument: 0 -------------------- class: FL_TEXT type: NORMAL_TEXT box: 105 150 160 20 boxtype: FL_FLAT_BOX colors: FL_COL1 FL_MCOL alignment: FL_ALIGN_CENTER|FL_ALIGN_INSIDE style: FL_BOLD_STYLE+FL_ENGRAVED_STYLE size: FL_MEDIUM_SIZE lcol: FL_PALEGREEN label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: playerB callback: argument: -------------------- class: FL_TEXT type: NORMAL_TEXT box: 10 190 350 20 boxtype: FL_DOWN_BOX colors: FL_COL1 FL_MCOL alignment: FL_ALIGN_CENTER|FL_ALIGN_INSIDE style: FL_NORMAL_STYLE size: FL_NORMAL_SIZE lcol: FL_BLUE label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: textStatus callback: argument: -------------------- class: FL_BUTTON type: NORMAL_BUTTON box: 370 190 70 20 boxtype: FL_UP_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_NORMAL_STYLE size: FL_DEFAULT_SIZE lcol: FL_BLACK label: Rules shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: buttRules callback: doRules argument: 0 -------------------- class: FL_TEXT type: NORMAL_TEXT box: 370 10 150 35 boxtype: FL_FLAT_BOX colors: FL_COL1 FL_MCOL alignment: FL_ALIGN_CENTER|FL_ALIGN_INSIDE style: FL_BOLDITALIC_STYLE+FL_EMBOSSED_STYLE size: FL_HUGE_SIZE lcol: FL_DARKGOLD label: Mancala shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: callback: argument: -------------------- class: FL_TEXT type: NORMAL_TEXT box: 370 40 150 15 boxtype: FL_FLAT_BOX colors: FL_COL1 FL_MCOL alignment: FL_ALIGN_CENTER|FL_ALIGN_INSIDE style: FL_NORMAL_STYLE size: FL_DEFAULT_SIZE lcol: FL_BLACK label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: textVer callback: argument: -------------------- class: FL_TEXT type: NORMAL_TEXT box: 15 60 45 80 boxtype: FL_DOWN_BOX colors: FL_COL1 FL_MCOL alignment: FL_ALIGN_CENTER|FL_ALIGN_INSIDE style: FL_BOLD_STYLE+FL_EMBOSSED_STYLE size: FL_HUGE_SIZE lcol: FL_SLATEBLUE label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: mancalaT callback: argument: -------------------- class: FL_BOX type: FLAT_BOX box: 70 50 30 5 boxtype: FL_FLAT_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_NORMAL_STYLE size: FL_DEFAULT_SIZE lcol: FL_BLACK label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: lightT0 callback: argument: -------------------- class: FL_BOX type: FLAT_BOX box: 110 50 30 5 boxtype: FL_FLAT_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_NORMAL_STYLE size: FL_DEFAULT_SIZE lcol: FL_BLACK label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: lightT1 callback: argument: -------------------- class: FL_BOX type: FLAT_BOX box: 150 50 30 5 boxtype: FL_FLAT_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_NORMAL_STYLE size: FL_DEFAULT_SIZE lcol: FL_BLACK label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: lightT2 callback: argument: -------------------- class: FL_BOX type: FLAT_BOX box: 190 50 30 5 boxtype: FL_FLAT_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_NORMAL_STYLE size: FL_DEFAULT_SIZE lcol: FL_BLACK label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: lightT3 callback: argument: -------------------- class: FL_BOX type: FLAT_BOX box: 230 50 30 5 boxtype: FL_FLAT_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_NORMAL_STYLE size: FL_DEFAULT_SIZE lcol: FL_BLACK label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: lightT4 callback: argument: -------------------- class: FL_BOX type: FLAT_BOX box: 270 50 30 5 boxtype: FL_FLAT_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_NORMAL_STYLE size: FL_DEFAULT_SIZE lcol: FL_BLACK label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: lightT5 callback: argument: -------------------- class: FL_BOX type: FLAT_BOX box: 270 145 30 5 boxtype: FL_FLAT_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_NORMAL_STYLE size: FL_DEFAULT_SIZE lcol: FL_BLACK label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: lightB0 callback: argument: -------------------- class: FL_BOX type: FLAT_BOX box: 230 145 30 5 boxtype: FL_FLAT_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_NORMAL_STYLE size: FL_DEFAULT_SIZE lcol: FL_BLACK label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: lightB1 callback: argument: -------------------- class: FL_BOX type: FLAT_BOX box: 190 145 30 5 boxtype: FL_FLAT_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_NORMAL_STYLE size: FL_DEFAULT_SIZE lcol: FL_BLACK label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: lightB2 callback: argument: -------------------- class: FL_BOX type: FLAT_BOX box: 150 145 30 5 boxtype: FL_FLAT_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_NORMAL_STYLE size: FL_DEFAULT_SIZE lcol: FL_BLACK label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: lightB3 callback: argument: -------------------- class: FL_BOX type: FLAT_BOX box: 110 145 30 5 boxtype: FL_FLAT_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_NORMAL_STYLE size: FL_DEFAULT_SIZE lcol: FL_BLACK label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: lightB4 callback: argument: -------------------- class: FL_BOX type: FLAT_BOX box: 70 145 30 5 boxtype: FL_FLAT_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_NORMAL_STYLE size: FL_DEFAULT_SIZE lcol: FL_BLACK label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: lightB5 callback: argument: -------------------- class: FL_TEXT type: NORMAL_TEXT box: 105 30 160 20 boxtype: FL_FLAT_BOX colors: FL_COL1 FL_MCOL alignment: FL_ALIGN_CENTER|FL_ALIGN_INSIDE style: FL_BOLD_STYLE+FL_ENGRAVED_STYLE size: FL_MEDIUM_SIZE lcol: FL_PALEGREEN label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: playerT callback: argument: -------------------- class: FL_TEXT type: NORMAL_TEXT box: 310 60 45 80 boxtype: FL_DOWN_BOX colors: FL_COL1 FL_MCOL alignment: FL_ALIGN_CENTER|FL_ALIGN_INSIDE style: FL_BOLD_STYLE+FL_EMBOSSED_STYLE size: FL_HUGE_SIZE lcol: FL_SLATEBLUE label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: mancalaB callback: argument: -------------------- class: FL_VALSLIDER type: HOR_SLIDER box: 370 150 150 15 boxtype: FL_DOWN_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_BOTTOM style: FL_NORMAL_STYLE size: FL_TINY_SIZE lcol: FL_BLACK label: Difficulty Level shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: slidLevel callback: doLevel argument: 0 -------------------- class: FL_BUTTON type: NORMAL_BUTTON box: 370 100 150 20 boxtype: FL_UP_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_NORMAL_STYLE size: FL_DEFAULT_SIZE lcol: FL_BLACK label: New Game, Human 1st shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: buttNewGameB callback: doNewGame argument: 0 -------------------- class: FL_BUTTON type: NORMAL_BUTTON box: 370 125 150 20 boxtype: FL_UP_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_NORMAL_STYLE size: FL_DEFAULT_SIZE lcol: FL_BLACK label: New Game, Computer 1st shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: buttNewGameT callback: doNewGame argument: 1 -------------------- class: FL_TEXT type: NORMAL_TEXT box: 15 30 90 10 boxtype: FL_FLAT_BOX colors: FL_COL1 FL_MCOL alignment: FL_ALIGN_LEFT|FL_ALIGN_INSIDE style: FL_NORMAL_STYLE size: FL_TINY_SIZE lcol: FL_BLACK label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: startT callback: argument: -------------------- class: FL_TEXT type: NORMAL_TEXT box: 265 160 90 10 boxtype: FL_FLAT_BOX colors: FL_COL1 FL_MCOL alignment: FL_ALIGN_RIGHT|FL_ALIGN_INSIDE style: FL_NORMAL_STYLE size: FL_TINY_SIZE lcol: FL_BLACK label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: startB callback: argument: -------------------- class: FL_TEXT type: NORMAL_TEXT box: 275 150 80 10 boxtype: FL_FLAT_BOX colors: FL_COL1 FL_MCOL alignment: FL_ALIGN_RIGHT|FL_ALIGN_INSIDE style: FL_NORMAL_STYLE size: FL_TINY_SIZE lcol: FL_BLACK label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: winB callback: argument: -------------------- class: FL_TEXT type: NORMAL_TEXT box: 15 40 90 10 boxtype: FL_FLAT_BOX colors: FL_COL1 FL_MCOL alignment: FL_ALIGN_LEFT|FL_ALIGN_INSIDE style: FL_NORMAL_STYLE size: FL_TINY_SIZE lcol: FL_BLACK label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: winT callback: argument: -------------------- class: FL_BUTTON type: NORMAL_BUTTON box: 450 190 70 20 boxtype: FL_UP_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_NORMAL_STYLE size: FL_DEFAULT_SIZE lcol: FL_BLACK label: Quit shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: buttQuit callback: doQuit argument: 0 -------------------- class: FL_TEXT type: NORMAL_TEXT box: 370 60 150 15 boxtype: FL_FLAT_BOX colors: FL_COL1 FL_MCOL alignment: FL_ALIGN_CENTER|FL_ALIGN_INSIDE style: FL_NORMAL_STYLE size: FL_DEFAULT_SIZE lcol: FL_BLACK label: Sverre H. Huseby shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: callback: argument: -------------------- class: FL_BUTTON type: NORMAL_BUTTON box: 0 0 0 0 boxtype: FL_UP_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_NORMAL_STYLE size: FL_DEFAULT_SIZE lcol: FL_BLACK label: shortcut: resize: FL_RESIZE_NONE gravity: FL_NoGravity FL_NoGravity name: dummyButton callback: argument: =============== FORM =============== Name: rules Width: 505 Height: 300 Number of Objects: 3 -------------------- class: FL_BOX type: UP_BOX box: 0 0 505 300 boxtype: FL_UP_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_NORMAL_STYLE size: FL_DEFAULT_SIZE lcol: FL_BLACK label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: callback: argument: -------------------- class: FL_BUTTON type: NORMAL_BUTTON box: 10 270 485 20 boxtype: FL_UP_BOX colors: FL_COL1 FL_COL1 alignment: FL_ALIGN_CENTER style: FL_NORMAL_STYLE size: FL_DEFAULT_SIZE lcol: FL_BLACK label: Ok, I know the rules shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: buttRulesDone callback: doRulesDone argument: 0 -------------------- class: FL_BROWSER type: NORMAL_BROWSER box: 10 10 485 255 boxtype: FL_DOWN_BOX colors: FL_WHITE FL_YELLOW alignment: FL_ALIGN_BOTTOM style: FL_NORMAL_STYLE size: FL_DEFAULT_SIZE lcol: FL_BLACK label: shortcut: resize: FL_RESIZE_ALL gravity: FL_NoGravity FL_NoGravity name: textRules callback: doRules argument: 0 ============================== create_the_forms mancala-1.0.3/xmain.c000644 000765 000024 00000024735 11767667163 015434 0ustar00sverrehustaff000000 000000 /* $Id: xmain.c,v 1.7 2012-06-18 18:07:40 sverrehu Exp $ */ /************************************************************************** * * FILE xmain.c * MODULE OF The board game Mancala * * DESCRIPTION Main function / frontend for using an X/XForms-based * display. * * This code is _really_ (I mean: REALLY) dirty... * The main purpose of this is to make someone play the * game. For those interested in the gaming (as I am), * please take a look at the other sourcefiles instead. * I'm not at all proud of this file, it's my first * attempt on using the XForms library, and it was done * in a hurry. * * I don't like event driven programming... * * WRITTEN BY Sverre H. Huseby * **************************************************************************/ #include #include #include #include #include #include "minimax.h" #include "mancala.h" #include "xform.h" #define flushForms fl_check_only_forms extern char *textRules; /************************************************************************** * * * P R I V A T E D A T A * * * **************************************************************************/ static FD_mancala *frm; static FD_rules *frmRules; static FL_OBJECT *frmMancala[2]; static FL_OBJECT *frmHole[2][MAX_HOLES]; static FL_OBJECT *frmLight[2][MAX_HOLES]; static const char *playerName[2] = { "the human player", "the computer" }; static int maxPly[2] = { 0, 4 }; static int rulesDisplayed = 0; static int stones_pr_hole = STONES_PR_HOLE; /* * Player side and hole chosen by human player. */ static Player movePlayer; static int moveHole = -1; static Player player; static int status; /************************************************************************** * * * P R I V A T E F U N C T I O N S * * * **************************************************************************/ /*------------------------------------------------------------------------- * * NAME showBoard * * FUNCTION Display the current board on screen. * * DESCRIPTION Very simple thing. */ static void showBoard(void) { Player p; int q; char s[10]; fl_freeze_form(frm->mancala); for (p = 0; p < 2; p++) { sprintf(s, "%d", getMancala(p)); fl_set_object_label(frmMancala[p], s); for (q = 0; q < MAX_HOLES; q++) { sprintf(s, "%d", getHole(p, q)); fl_set_object_label(frmHole[p][q], s); } } fl_unfreeze_form(frm->mancala); flushForms(); } /*------------------------------------------------------------------------- * * NAME setMessage * * FUNCTION Set the message of the status line. * * INPUT format, ... * Arguments used as with printf(). */ static void setMessage(const char *format, ...) { va_list ap; char s[120]; va_start(ap, format); vsprintf(s, format, ap); va_end(ap); fl_set_object_label(frm->textStatus, s); flushForms(); } /* set hole to -1 to turn all off */ static void setLastMove(Player player, int hole) { static Player lastPlayer; static int lastHole = -1; #ifdef WORKS_ON_SGI_NOW static int colorSet = 0; #endif static FL_COLOR origCol1 = FL_COL1, origCol2 = FL_BOTTOM_BCOL; /* * For some reason, SGI fucks up with segmenation fault during * the next few lines. Harcoding it instead... */ #ifdef WORKS_ON_SGI_NOW if (!colorSet) { origCol1 = frmLight[player][hole]->col1; origCol2 = frmLight[player][hole]->col2; colorSet = 1; } #endif if (lastHole >= 0) fl_set_object_color(frmLight[lastPlayer][lastHole], origCol1, origCol2); if (hole >= 0) fl_set_object_color(frmLight[player][hole], FL_RED, FL_BOTTOM_BCOL); lastPlayer = player; lastHole = hole; flushForms(); } static void flashMove(Player player, int hole) { int q; setLastMove(0, -1); for (q = 5; q > 0; q--) { setLastMove(player, hole); fl_msleep(100); setLastMove(0, -1); fl_msleep(100); } } static Player computerPlayer(void) { if (maxPly[0]) return 0; if (maxPly[1]) return 1; return -1; } static void formInit(void) { char s[20]; frm = create_form_mancala(); sprintf(s, "version %s", VERSION); fl_set_object_label(frm->textVer, s); frmMancala[0] = frm->mancalaB; frmHole[0][0] = frm->holeB0; frmHole[0][1] = frm->holeB1; frmHole[0][2] = frm->holeB2; frmHole[0][3] = frm->holeB3; frmHole[0][4] = frm->holeB4; frmHole[0][5] = frm->holeB5; frmMancala[1] = frm->mancalaT; frmHole[1][0] = frm->holeT0; frmHole[1][1] = frm->holeT1; frmHole[1][2] = frm->holeT2; frmHole[1][3] = frm->holeT3; frmHole[1][4] = frm->holeT4; frmHole[1][5] = frm->holeT5; frmLight[0][0] = frm->lightB0; frmLight[0][1] = frm->lightB1; frmLight[0][2] = frm->lightB2; frmLight[0][3] = frm->lightB3; frmLight[0][4] = frm->lightB4; frmLight[0][5] = frm->lightB5; frmLight[1][0] = frm->lightT0; frmLight[1][1] = frm->lightT1; frmLight[1][2] = frm->lightT2; frmLight[1][3] = frm->lightT3; frmLight[1][4] = frm->lightT4; frmLight[1][5] = frm->lightT5; fl_set_slider_bounds(frm->slidLevel, 1.0, 9.0); fl_set_slider_step(frm->slidLevel, 1.0); fl_set_slider_precision(frm->slidLevel, 0); /* HERE: change if not always human vs. computer */ fl_set_slider_value(frm->slidLevel, maxPly[computerPlayer()]); frmRules = create_form_rules(); fl_set_browser_fontsize(frmRules->textRules, FL_NORMAL_SIZE); fl_set_browser_fontstyle(frmRules->textRules, FL_NORMAL_STYLE); fl_add_browser_line(frmRules->textRules, textRules); fl_show_form(frm->mancala, FL_PLACE_SIZE, FL_FULLBORDER, "Mancala"); } static void formFinish(void) { fl_hide_form(frm->mancala); if (rulesDisplayed) fl_hide_form(frmRules->rules); } static void ourInitGame(Player starter) { initGame(stones_pr_hole); player = starter; status = 0; fl_set_object_label(frm->playerB, playerName[0]); fl_set_object_label(frm->playerT, playerName[1]); fl_set_object_label(frm->startB, starter == 0 ? "Starter" : ""); fl_set_object_label(frm->startT, starter == 1 ? "Starter" : ""); fl_set_object_label(frm->winB, ""); fl_set_object_label(frm->winT, ""); } static void formHandler(void) { PMove move; Move move2; Player winner; ourInitGame(0); for (;;) { showBoard(); if (!status) { if ((status = checkAndFixWin(&winner)) != 0) { if (status == 1) { setMessage("Game over; %s wins", playerName[winner]); fl_set_object_label(winner == 0 ? frm->winB : frm->winT, "Winner"); } else setMessage("Game over; we have a draw"); } else { if (!maxPly[player]) { setMessage("It's %s's turn", playerName[player]); fl_do_forms(); move = &move2; if (moveHole >= 0) { if (movePlayer == player) { move->hole = moveHole; if (legalMove(player, move)) { flashMove(player, move->hole); player = doMove(player, move); } else { setMessage("Not a legal move. Try again!"); fl_msleep(1500); } } else { setMessage("Stick to your side, please!"); fl_msleep(1500); } moveHole = -1; } } else { setMessage("%s is thinking...", playerName[player]); move = getBestMove(player, maxPly[player]); setMessage(""); flashMove(player, move->hole); player = doMove(player, move); } } } else fl_do_forms(); } } /************************************************************************** * * * P U B L I C F U N C T I O N S * * * **************************************************************************/ /*------------------------------------------------------------------------* | Callbacks for the objects in the form | *------------------------------------------------------------------------*/ void doRules(FL_OBJECT *ob, long arg) { if (rulesDisplayed) return; fl_show_form(frmRules->rules, FL_PLACE_FREE, FL_FULLBORDER, "Mancala Rules"); fl_deactivate_object(frm->buttRules); rulesDisplayed = 1; } void doRulesDone(FL_OBJECT *ob, long arg) { if (!rulesDisplayed) return; fl_hide_form(frmRules->rules); fl_activate_object(frm->buttRules); rulesDisplayed = 0; } void doHole(FL_OBJECT *ob, long arg) { Player player; int hole; if (moveHole >= 0) { setMessage("Hold your horses! " "I haven't done your previous move yet!"); fl_msleep(1000); } else { player = arg / 100; hole = arg % 100; movePlayer = player; moveHole = hole; } fl_trigger_object(frm->dummyButton); } void doLevel(FL_OBJECT *ob, long arg) { double v; Player comp; if ((comp = computerPlayer()) < 0) return; v = fl_get_slider_value(ob); maxPly[comp] = (int) v; } void doNewGame(FL_OBJECT *ob, long arg) { ourInitGame(arg); fl_trigger_object(frm->dummyButton); } void doQuit(FL_OBJECT *ob, long arg) { formFinish(); exit(0); } /**************************************************************************/ #define stringify(a) #a #define stringize(a) stringify(a) int main(int argc, char *argv[]) { static FL_CMD_OPT cmdopt [] = { { "-stones", "*.stones", XrmoptionSepArg, 0 }}; static FL_resource res [] = { { "stones", "XMancala", FL_INT, &stones_pr_hole, stringize(STONES_PR_HOLE), 0 }}; fl_initialize(&argc, argv, "XMancala", cmdopt, sizeof(cmdopt) / sizeof(FL_CMD_OPT)); fl_get_app_resources(res, sizeof(res ) / sizeof(FL_resource)); if (stones_pr_hole < 1) { fprintf(stderr, "xmancala: the number of stones per hole must be " "greater than zero\n"); return 1; } formInit(); formHandler(); formFinish(); return 0; }