home *** CD-ROM | disk | FTP | other *** search
- /* check.c 10/29/91
- *
- * Copyright 1991 Perry R. Ross
- *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation without fee is hereby granted, subject to the restrictions
- * detailed in the README file, which is included here by reference.
- * Any other use requires written permission from the author. This software
- * is distributed "as is" without any warranty, including any implied
- * warranties of merchantability or fitness for a particular purpose.
- * The author shall not be liable for any damages resulting from the
- * use of this software. By using this software, the user agrees
- * to these terms.
- */
-
- #include "ldb.h"
-
- /*======================================================================
- * This file contains the functions that check for unused moves.
- *======================================================================
- */
-
-
- PRIVATE int maxused, hiused, nlegal;
-
- struct legal *rmlegal();
-
- /*----------------------------------------------------------------------
- * legalmoves -- calculate all legal moves for a game
- *
- * This function is called after the dice have been rolled to search
- * all possible combinations of moves to see which ones are legal.
- * While this function is working, it keeps a list of legal moves,
- * but this list is deleted before returning. For the purposes
- * of checking a move, it suffices to only keep the number of usable
- * dice and the highest numbered usable dice. Any move that uses as
- * many dice as can be used, and uses the highest usable dice, is
- * legal. These values are stored in the maxused and hiused fields
- * of the game structure.
- *
- * legalmoves performs an exhaustive search of all possible move
- * combinations, storing the combinations that are not rejected
- * by apply() in an instance of struct legal, which is linked into
- * the doubly-linked list headed by lhead. Note that, at this point,
- * this list may contain many combinations that are not legal because
- * they do not use all possible dice, or they use a smaller dice when
- * a larger one is usable. These illegal combinations are trimmed from
- * the list by trimunused() and trimlowused(). Finally, duplicate combinations
- * are removed from the list by trimequal(). Duplicate moves are those that
- * use the same rolls to move from the same points, but possibly in a
- * different order, for example (3/8 1/6) and (1/6 3/8). This allows
- * detection of the case where there is only one legal move, in which
- * case this move is automatically applied (if rc.automove is set).
- * If the list is empty, there are no legal moves. Otherwise, the
- * highest roll used in the list is stored in g->hiused, and the largest
- * number of rolls used in the list is stored in g->maxused. This information
- * is used by check().
- *----------------------------------------------------------------------
- */
-
- legalmoves(g)
- struct game *g;
- {
- struct game tmp;
- int i;
- struct legal *l;
-
- tmp = *g; /* we don't want to change actual game */
- for (i = 0; i < 4; tmp.mvs[i++].pt = -1); /* mark all unused */
- maxused = -1; /* init to not all used */
- hiused = 0; /* init to 0 */
- nlegal = 0; /* init to no legal moves*/
- lhead = NULL;
- ltail = NULL;
- if (tmp.mvs[0].roll == tmp.mvs[1].roll)
- scanmvs(&tmp,0,3); /* there is only one ordering */
- else {
- scanmvs(&tmp,0,1); /* scan for one ordering */
- i = tmp.mvs[0].roll; /* reverse rolls */
- tmp.mvs[0].roll = tmp.mvs[1].roll;
- tmp.mvs[1].roll = i;
- for (i = 0; i < 4; tmp.mvs[i++].pt = -1); /* mark all unused */
- scanmvs(&tmp,0,1); /* scan for other ordering */
- }
- trimunused(); /* zap combinations that leave usable rolls unused */
- trimlowused(); /* zap combinations that use the wrong die */
- trimequal(); /* zap duplicates */
- g->maxused = maxused; /* store maxused and hiused into the game structure */
- g->hiused = hiused;
- if (nlegal == 0) { /* check for no legal moves */
- if (g->dispmsg != NULL)
- free(g->dispmsg);
- g->dispmsg = save("You don't have any legal moves.");
- }
- else if (nlegal == 1) { /* check for only one legal move */
- if (g->dispmsg != NULL)
- free(g->dispmsg);
- g->dispmsg = save("You only have one legal move.");
- if (*rc.automove == 'y') { /* you want the move applied? */
- if ( (lhead->nmove==0) && (lhead->mvs[0].roll!=g->mvs[0].roll))
- g->mvs[1] = g->mvs[0];
- for (i = 0; i <= lhead->nmove; i++) {
- g->mvs[i] = lhead->mvs[i];
- apply(g,WHO_ME,i,0,NULL);
- }
- }
- }
-
- /* we have no use for the legal moves list, so free it */
- /* maybe in the future we will have a use for it */
-
- for (l = lhead; l != NULL; l = l->next)
- free(l);
- lhead = NULL;
- ltail = NULL;
- }
-
-
- /*----------------------------------------------------------------------
- * scanmvs -- search for all possible combinations of a move
- *
- * This function takes a single die and tries to use it on every point
- * of the board. For every point it is successful, it adds the
- * combination to the legal moves list and performs a recursive descent
- * searching for all legal combinations of the remaining dice.
- *----------------------------------------------------------------------
- */
- scanmvs(g,mn,max)
- struct game *g;
- int mn, max;
- {
- int i;
- board sv;
-
- copyboard(g->board,sv); /* save the board */
- for (i = 0; i <= 24; i++) {
- if (i == 0)
- g->mvs[mn].pt = BARPT(g->mydir); /* use correct barpt */
- else
- g->mvs[mn].pt = i;
- if (apply(g,WHO_ME,mn,0,NULL) < 0) /* can't move from this pt */
- continue;
- addlegal(mn,g->mvs[mn].roll,g->mvs[mn].pt); /* add to list */
- if (mn < max)
- scanmvs(g,mn+1,max); /* try all remaining comb's */
- copyboard(sv,g->board); /* restore board */
- }
- }
-
-
- /*----------------------------------------------------------------------
- * addlegal -- add a move combination to the list
- *
- * This function adds an instance of struct legal to the legal moves list.
- * The arguments to addlegal only specify the usage of one particular
- * die, by specifying its "move number". For example, for a roll of
- * 5 2, the 5 is move number 0 and the 2 is move number 1. The usage
- * of all lower numbered rolls is copied from the previous entry in the
- * moves list (since that entry was added by the higher level scanmvs
- * before the current scanmvs was called), and all higher-numbered rolls
- * are marked unused.
- *----------------------------------------------------------------------
- */
-
- addlegal(mn,r,pt)
- int mn, r, pt;
- {
- int i;
- struct legal *n;
-
- if ( (n = (struct legal *) calloc(sizeof(struct legal),1)) == NULL)
- fatal("Out of memory!");
- clearmvs(n->mvs);
- if (ltail != NULL)
- for (i = 0; i < mn; i++) /* copy prefix from prev move */
- n->mvs[i] = ltail->mvs[i];
- n->mvs[mn].roll = r; /* copy in this move */
- n->mvs[mn].pt = pt;
- n->next = NULL; /* this is end of list */
- if (lhead == NULL) { /* link into list */
- n->prev = NULL;
- lhead = n;
- ltail = n;
- }
- else {
- n->prev = ltail;
- ltail->next = n;
- ltail = n;
- }
- n->himove = 0;
- for (i = 0; i <= mn; i++) /* search for highest used move */
- if (n->mvs[i].roll > n->himove)
- n->himove = n->mvs[i].roll;
- if (mn > maxused) /* keep track of whether it is possible */
- maxused = mn; /* to use all of the rolls */
- nlegal++;
- n->nmove = mn; /* store number of moves used by this entry */
- }
-
-
- /*----------------------------------------------------------------------
- * trimunused -- remove moves that do not use all possible rolls
- *
- * This function scans the move list and deletes combinations that
- * leave usable rolls unused.
- *----------------------------------------------------------------------
- */
-
- trimunused()
- {
- struct legal *l;
-
- l = lhead;
- while (l != NULL) {
- if (l->nmove < maxused)
- l = rmlegal(l);
- else
- l = l->next;
- }
- }
-
-
- /*----------------------------------------------------------------------
- * trimlowused -- remove moves that do not use the largest possible roll
- *
- * This function scans the move list and deletes combinations that
- * do not use the highest usable roll.
- *----------------------------------------------------------------------
- */
-
- trimlowused()
- {
- struct legal *l;
-
- l = lhead;
- while (l != NULL) {
- if (l->himove < hiused)
- l = rmlegal(l);
- else
- l = l->next;
- }
- }
-
-
- /*----------------------------------------------------------------------
- * trimequal -- remove duplicate moves
- *
- * This function scans the move list and deletes combinations that
- * are duplicates.
- *----------------------------------------------------------------------
- */
-
- trimequal()
- {
- struct legal *l, *p;
- struct mv m1[4], m2[4];
- int i, n;
-
- for (l = lhead; l != NULL; l = l->next) {
- extractmvs(l,m1);
- n = l->nmove;
- p = l->next;
- while (p != NULL) {
- if (p->nmove != n) {
- p = p->next;
- continue;
- }
- extractmvs(p,m2);
- for (i = 0; i <= n; i++)
- if ((m1[i].roll != m2[i].roll)||(m1[i].pt != m2[i].pt))
- break;
- if (i <= n)
- p = p->next;
- else
- p = rmlegal(p);
- }
- }
- }
-
-
- /*----------------------------------------------------------------------
- * rmlegal -- remove a struct legal from the move list
- *
- * This function unlinks an entry from the move list and free's it.
- *----------------------------------------------------------------------
- */
-
- struct legal *rmlegal(l)
- struct legal *l;
- {
- struct legal *t;
-
- t = l;
- if (l == lhead) {
- lhead = l->next;
- l = lhead;
- }
- else {
- if ( (l->prev->next = l->next) != NULL)
- l->next->prev = l->prev;
- l = l->next;
- }
- free(t);
- nlegal--;
- return(l);
- }
-
-
- /*----------------------------------------------------------------------
- * extractmvs -- extract a struct legal into a struct mv
- *
- * This function copies the information from a move list entry into
- * an instance of struct mv, and then sorts the rolls in the struct mv
- * by increasing dice value (and by increasing point number for equal
- * rolls). Sorting the rolls makes it easier for trimequal to check
- * for duplicate entries, it has no value as far as the game is concerned.
- *----------------------------------------------------------------------
- */
-
- extractmvs(l,m)
- struct legal *l;
- struct mv *m;
- {
- int i, n, s;
- struct mv tmp;
-
- clearmvs(m);
- for (i = 0; i <= l->nmove; i++) /* extract the moves */
- m[i] = l->mvs[i];
- n = l->nmove;
- do { /* sort by increasing roll then increasing point */
- s = 0;
- for (i = 0; i < n; i++) { /* long live bubblesort */
- if (m[i].roll < m[i+1].roll)
- continue;
- else if ( (m[i].roll == m[i+1].roll) && (m[i].pt < m[i+1].pt) )
- continue;
- tmp = m[i];
- m[i] = m[i+1];
- m[i+1] = tmp;
- s = 1;
- }
- n--;
- } while (s);
- }
-
-
- /*----------------------------------------------------------------------
- * checkused -- check that a move uses the correct rolls
- *
- * This function is called just before a move is sent to the
- * opponent. It uses the values stored in g->maxused and g->hiused
- * to make sure that the move uses all usable rolls, and that it
- * uses the highest usable roll. As a special case, it considers
- * to be legal any move where all pieces are borne off. This
- * takes care of the special case where there is one piece
- * left to bear off, and two rolls, one of which is too small
- * to bear off. Normally, ldb would insist that the smaller
- * roll be used, then the larger one, so that both rolls
- * would be used. If one roll is large enough to bear the last
- * man off, though, there is no need to force the other roll to be used.
- *----------------------------------------------------------------------
- */
-
- checkused(g)
- struct game *g;
- {
- int h, i;
-
- if (g->board[OFFPT(g->mydir)].qty == 15) /* special case, if all pcs */
- return(0); /* are off, then all rolls have been used */
- h = 0;
- for (i = 0; i <= g->maxused; i++) {
- if (g->mvs[i].pt < 0) {
- FeMessage("You left a roll unused.");
- return(1);
- }
- if (h < g->mvs[i].roll)
- h = g->mvs[i].roll;
- }
- if (g->hiused > h) {
- FeMessage("You can use the higher roll.");
- return(1);
- }
- return(0);
- }
-