/* misc.c Copyright (C) 1994 Lambert Klasen & Detlef Steuer klasen@asterix.uni-muenster.de steuer@amadeus.statistik.uni-dortmund.de This file is free source code; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See COPYING for more details. */ #include #include #include #include #include #include #include #include #include #include "xgammon.h" #include "gammon.h" extern Cursor gammon_cursor; extern XColor fg_col, bg_col; /* gammon_cursor colors */ extern int from_pin; extern int width, stone_width, height; extern int done_yet, done_hit, current_hit[4]; extern unsigned long delaytime; extern FILE *protokol_file; int moves_set = 0; char protokol_file_name[50]; /* for some irix */ #ifdef irix #define usleep (a) sleep ((a/10)) #endif extern void set_game_kind (); /* decision.c */ extern float evaluate (); /* popup.c */ extern void Info (); /* drawing.c */ extern void DrawDice (); extern void DrawDoubler (); extern void RedrawAllStones (); extern void DrawEmptyBoard (); extern void DrawBoard (); extern void DrawDoubler (); struct _move_hist *move_hist = NULL; long int NaufM[16][7]; long int Binomial[7][7]; void switch_turn (void) { Display *dpy = Player[0].X11Set.dpy; if (turn==BLACK) { turn = WHITE; other = BLACK; direction = -1; start_pin = 25; end_pin = 0; } else { turn = BLACK; other = WHITE; direction = 1; start_pin = 0; end_pin = 25; } done_hit = 0; /* make the cursor look like the stones you move */ if (Player[0].type == HUMAN && Player[1].type == HUMAN) { if (!gammon_resource.other_display) { if (turn == BLACK) XRecolorCursor (dpy, Player[0].X11Set.cursor, &Player[0].X11Set.fg_col, &Player[0].X11Set.bg_col); else XRecolorCursor (dpy, Player[0].X11Set.cursor, &Player[0].X11Set.bg_col, &Player[0].X11Set.fg_col); } /* else nothing to recolor */ } } void RollDice (void) { roll[0] = (random ()%6 + 1) * direction; roll[1] = (random ()%6 + 1) * direction; if (roll[0] != roll[1]) { roll[2] = 0; /* sure and necessary */ roll[3] = 0; to_move = 2; pash = 0; } else { roll[2] = roll[0]; roll[3] = roll[1]; to_move = 4; pash = 1; } sprintf (add_text, "%s rolls %d and %d.\n", Player[turn-1].name, abs (roll[0]), abs (roll[1])); AppendDialogText (LOWER, add_text); } int end_of_game_test (int color) { int i, k; for (i=start_pin+direction*19; i!=start_pin+direction*25; i+=direction) { if (Pin[i].color == turn) return 0; } /* if we get here lookup if we have to count doubler two or three times */ if (Pin[OTHER_BAR].count) { /* a stone of the opponent on BAR */ doubler.value *= 3; Player[turn-1].backgammons ++; return BACKGAMMON; } for (i = end_pin-direction; i != end_pin-direction*7; i-=direction) { /* a stone in the opponents first quarter */ if (Pin[i].color == other) { doubler.value *= 3; Player[turn-1].backgammons ++; return BACKGAMMON; } } /* other has no stone off */ k = Pin[OTHER_BAR].count; for (i=1; i<25; i++) if (Pin[i].color == other) k += Pin[i].count; if (k == 15) { doubler.value *=2; Player[turn-1].gammons ++; return GAMMON; } return NORMAL; } int complete_blockade (void) { int i; for (i=start_pin+direction; i!=start_pin+direction*7; i+=direction) { if (Pin[i].count == 0 || Pin[i].color == turn || (Pin[i].color != turn && Pin[i].count < 2)) return 0; } return 1; } /* this function checkes, if you make a multible move, if you have to hit an opponents stone on the run */ int have_to_hit (int from, int to) { if (!pash) { if (Pin[from + roll[0]].color == other && Pin[from + roll[1]].color == other) { if (Pin[from + roll[0]].count == 1 && Pin[from + roll[1]].count == 1) { Info ("Please decide witch stone you want to hit"); return 1; } else if (Pin[from + roll[0]].count == 1) { RemoveStone (from + roll[0]); PutStone (other, OTHER_BAR); return 0; } else if (Pin[from + roll[1]].count == 1){ RemoveStone (from + roll[1]); PutStone (other, OTHER_BAR); return 0; } } else return 0; /* will not hit by default */ } else { int i; for (i=1; i<4; i++) { /* to_pin is checked by PlaceStone */ if ((from + i*roll[0]) == to) break; if (Pin[from + i*roll[0]].color == other && ! ((turn==BLACK) ? from + i*roll[0] >= end_pin : from + i*roll[0] <= end_pin)) { RemoveStone (from + i*roll[0]); PutStone (other, OTHER_BAR); } } } return 0; } void free_move_hist (void) { struct _move_hist *d, *h = move_hist; while (h) { d = h; h = h->next; free (d); } } void init_game (void) { int i; from_pin = 0; done_yet = 0; done_hit = 0; if (initialize == NORMAL_GAME || initialize == COMPUTER_TOURNAMENT) { for (i=0; i<29; i++) { Pin[i].count=0; Pin[i].color=0; } Pin[ 1].color = BLACK; Pin[ 1].count = 2; Pin[12].color = BLACK; Pin[12].count = 5; Pin[17].color = BLACK; Pin[17].count = 3; Pin[19].color = BLACK; Pin[19].count = 5; Pin[ 6].color = WHITE; Pin[ 6].count = 5; Pin[ 8].color = WHITE; Pin[ 8].count = 3; Pin[13].color = WHITE; Pin[13].count = 5; Pin[24].color = WHITE; Pin[24].count = 2; Pin[ (25+BLACK)].color = BLACK; /* init BARS */ Pin[ (25+WHITE)].color = WHITE; doubler.value = 1; doubler.owner = 0; tournament.game_number++; if (Player[0].type == HUMAN && Player[1].type == HUMAN) sprintf (add_text,"Starting a new game with %s, ", Player[0].name); else if (Player[0].type == COMPUTER && Player[1].type == COMPUTER) sprintf (add_text,"Starting a new game with xgammon (0)\n"); else if (Player[0].type == HUMAN) sprintf (add_text,"Starting a new game with %s\n", Player[0].name); else sprintf (add_text,"Starting a new game with %s\n", Player[1].name); AppendDialogText (LOWER, add_text); direction = 1; /* get beginner of game */ do { roll[0] = (random ()%6 + 1); roll[1] = (random ()%6 + 1); } while (roll[0] == roll[1]); roll[2] = 0; roll[3] = 0; to_move = 2; pash = 0; if (roll[1] > roll[0]) { /* white begins */ turn = BLACK; switch_turn (); /* switch_turn sets turn to white and all the other vars */ roll[0] *= direction; /* must a be */ roll[1] *= direction; roll[2] *= direction; roll[3] *= direction; Player[1].beginner_of_game = 1; Player[0].beginner_of_game = 0; } else if (roll[0] > roll[1]) { turn = WHITE; switch_turn (); Player[0].beginner_of_game = 1; Player[1].beginner_of_game = 0; } DrawEmptyBoard (); DrawBoard (); RedrawAllStones (); DrawDoubler (doubler.value, doubler.owner); sprintf (add_text, "%s rolled %d, %s rolled %d\n", Player[0].name, abs (roll[0]), Player[1].name, abs (roll[1])); AppendDialogText (LOWER, add_text); if (roll[0] < 0) sprintf (add_text, "%s makes the first move.\n", Player[1].name); else sprintf (add_text, "%s makes the first move.\n\n", Player[0].name); AppendDialogText (LOWER, add_text); DrawDice (turn); break_loop = 0; } /* end initialize */ if (move_hist) free_move_hist (); move_hist = NULL; } void AppendMoveString (MOVE *m) { struct _move_hist *new; char f[5], t[5]; int i; if (!m || !(m->from || m->to)) { sprintf (add_text, "%s can't move.\n\n", Player[turn-1].name); } else { sprintf (add_text, "%s moves ", Player[turn-1].name); for (i=0; ifrom != BAR) sprintf (f,"%d-", (m+i)->from); else strcpy (f,"bar-"); strcat (add_text, f); if ((m+i)->to != end_pin) sprintf (t,"%d ", (m+i)->to); else strcpy (t,"off "); strcat (add_text, t); } strcat (add_text, ".\n\n"); } AppendDialogText (LOWER, add_text); if (!m) { XSync (Player[0].X11Set.dpy, 0); if (gammon_resource.other_display) { XSync (Player[1].X11Set.dpy, 0); } usleep (delaytime); } moves_set ++; new = (struct _move_hist *) malloc (sizeof (struct _move_hist)); new->next = move_hist; move_hist = new; if (m) { for (i=0; ifrom[i] = (m+i)->from; if ((m+i)->to != end_pin) move_hist->from[i] = (m+i)->to; else move_hist->to[i] = FINISHED; move_hist->hit[i] = current_hit[i]; } for (i=to_move; i<4; i++) { move_hist->from[i] = 0; move_hist->to[i] = 0; move_hist->hit[i] = 0; } } else { for (i=0; i<4; i++) { move_hist->from[i] = 0; move_hist->to[i] = 0; move_hist->hit[i] = 0; } } } XPoint PinToPosition (int pin) { XPoint p; if (pin == BAR || pin == OTHER_BAR) p.x = width/2 - stone_width/2; else if (pin == FINISHED) p.x = width - stone_width; else if (pin < 7) p.x = width - (pin + 1) * stone_width; else if (pin < 13) p.x = width - (pin + 1) * stone_width - stone_width; else if (pin < 19) p.x = width - (26 - pin) * stone_width - stone_width; else if (pin < 25) p.x = width - (26 - pin) * stone_width; if (pin == FINISHED) p.y = Pin[FINISHED].count * height/2/15; else if (Pin[pin].count < 5) p.y = Pin[pin].count * stone_width; else if (Pin[pin].count < 9) p.y = (Pin[pin].count-5) * stone_width + stone_width/2; else if (Pin[pin].count < 13) p.y = (Pin[pin].count-9) * stone_width + stone_width/4; else p.y = (Pin[pin].count-13) * stone_width + stone_width*3/4; /* for upper half of board */ if (((pin == BAR || pin == OTHER_BAR) && Pin[pin].color == WHITE) || (pin == FINISHED && turn == BLACK) || (pin > 12 && pin != FINISHED)) p.y = height - stone_width - p.y; return p; } int EventToPin (int x, int y) { int p; p = (width - x) / stone_width; switch (p) { case 0: return end_pin; break; case 7: return BAR; break; case 14: return DOUBLER; break; } if ((y - height/2) < 0) { /* upper part of the board */ if (p > 6) return p-1; /* left of bar */ else return p; /* right of bar */ } else { /* lower part of the board */ p = 25 - p; if (p < 19) return p+1; /* left of bar */ else return p; /* right of bar */ } } void AddResult (int whom) { char *gammon_str; Player[whom-1].points += doubler.value; switch (end_of_game) { case NORMAL: gammon_str = ""; break; case GAMMON: gammon_str = "gammon"; break; case BACKGAMMON: gammon_str = "backgammon"; break; } sprintf (add_text, "%s wins the %d. game %s and gets %d points.\n", Player[whom-1].name, tournament.game_number, gammon_str, doubler.value); AppendDialogText (UPPER, add_text); sprintf (add_text, "%s wins the game %s and gets %d points.\n", Player[whom-1].name, gammon_str, doubler.value); AppendDialogText (LOWER, add_text); if (gammon_resource.moneygame) sprintf (add_text, "score in moneygame match: %s-%d %s-%d\n\n", Player[0].name, Player[0].points, Player[1].name, Player[1].points); else if (gammon_resource.winat) sprintf (add_text, "score in %d point match: %s-%d %s-%d\n\n", tournament.winning_point, Player[0].name, Player[0].points, Player[1].name, Player[1].points); AppendDialogText (LOWER, add_text); AppendDialogText (UPPER, add_text); if (gammon_resource.moneygame) return; else if (gammon_resource.winat && Player[whom-1].points >= tournament.winning_point) sprintf (add_text, "%s wins the %d point match %d-%d .\n\n", Player[whom-1].name, tournament.winning_point, Player[0].points, Player[1].points); AppendDialogText (LOWER, add_text); AppendDialogText (UPPER, add_text); AppendDialogText (UPPER, "end of tournament\n\n"); } long int fak (int n) { if (n <= 1) return (1); else return (n * fak (n - 1)); } long int binomial (int n, int m) { return (fak (n) / fak (m) / fak (n - m)); } long int naufm (int n, int m) { if (n < m) return (0); if (m == 1) return (1); return (naufm (n - 1, m) + naufm (n - 1, m - 1)); } void set_naufm () { int i, j; for (i = 1; i < 16; i++) { for (j = 1; j < 7; j++) { NaufM[i][j] = naufm (i, j); }} } void set_binom () { int i, j; for (i = 1; i < 7; i++) { for (j = 1; j < 7; j++) { Binomial[i][j] = binomial (i, j); }} } /* PositionNumber: this function calculates out of a given endgame position the corresponding position number in the endgame database, in a running game pin 6 is set as aim for stones not in the homeboard */ long int PositionNumber (int STARTPIN, int color) { int pin[7]; int sum = 0, s, i, stones; int start, nextpin, local_direction, local_endpin; long int number = 0L; if (color == turn) { local_direction = direction; local_endpin = end_pin; } else { local_direction = -1 * direction; local_endpin = start_pin; } pin[0] = 0; for (i = 1; i < 7; i++) { if (Pin[local_endpin - i * local_direction].color == color) pin[i] = Pin[local_endpin - i * local_direction].count; else pin[i] = 0; } for (i = 1; i < 7; i++) sum += pin[i]; /* put all stones on the 6. pin */ pin[6] += 15 - sum - Pin[25 + color].count; sum = 0; for (i = STARTPIN + 1; i < 7; i++) sum += pin[i]; if (sum == 0) return (0); i = STARTPIN + 1; while (pin[i] == 0 && i < 7) i++; nextpin = i - STARTPIN; number += nextpin; for (start = STARTPIN; start < STARTPIN + nextpin; start++) { for (stones = 1; stones < sum; stones++) { for (s = 1; s < 6 - start + 1; s++) { number += Binomial[6 - start][s] * NaufM[stones][s]; } } } number += PositionNumber (STARTPIN + nextpin, color); return (number); } MOVE *find_best_move () { MOVE *move_return = NULL, *pm = possible_moves; int i, hit[4]; float maxvalue = -10000.0, value; while (pm != list) { for (i = 0; i < to_move; i++) { /* do move */ if ((pm + i)->from + (pm + i)->to) { hit[i] = 0; Pin[ (pm + i)->from].count--; if (!Pin[ (pm + i)->from].count) Pin[ (pm + i)->from].color = 0; if ((pm + i)->to == end_pin) Pin[FINISHED].count++; else { if (Pin[ (pm + i)->to].color == other) { Pin[ (pm + i)->to].color = turn; hit[i] = 1; Pin[OTHER_BAR].count++; Pin[OTHER_BAR].color = other; } else { Pin[ (pm + i)->to].count++; Pin[ (pm + i)->to].color = turn; } } } } #ifdef DEBUG_EVALUATE { int k; MOVE *d = pm; printf ("\n"); for (k = 0; k < 4; k++) { printf ("%2d -> %2d ", d->from, d->to); d++; } printf ("\n"); } #endif value = evaluate (turn); if (value > maxvalue) { maxvalue = value; move_return = pm; } for (i = to_move - 1; i > -1; i--) { /* undo move */ if ((pm + i)->from + (pm + i)->to) { Pin[ (pm + i)->from].count++; Pin[ (pm + i)->from].color = turn; if (hit[i]) { Pin[ (pm + i)->to].color = other; Pin[OTHER_BAR].count--; if (!Pin[OTHER_BAR].count) Pin[OTHER_BAR].color = 0; } else { if ((pm + i)->to == end_pin) Pin[FINISHED].count--; else { Pin[ (pm + i)->to].count--; if (!Pin[ (pm + i)->to].count) Pin[ (pm + i)->to].color = 0; } } } } pm += 4; } #ifdef DEBUG_EVALUATE { int k; MOVE *d = move_return; for (k = 0; k < 4; k++) { printf ("%2d -> %2d ", d->from, d->to); d++; } printf ("\n this is the best move with vlaue %f\n",maxvalue); } #endif return (move_return); } void maildump (void) { FILE *maildumpfile; int i, j; maildumpfile = fopen ("xgammon.maildump", "w"); if (!maildumpfile) { Info ("Couldn't open dump file!\nSorry."); return; } fprintf (maildumpfile, "\n\n"); fprintf (maildumpfile, " 13 14 15 16 17 18 19 20 21 22 23 24\n +------------------------------------------+ X: - score: %d\n", Player[1].points); for (i=0; i<4; i++) { fprintf (maildumpfile, " |"); for (j=12; j>6; j--) { if (Pin[j].color == WHITE && Pin[j].count >= i+1) fprintf (maildumpfile, " X "); else if (Pin[j].color == BLACK && Pin[j].count >= i+1) fprintf (maildumpfile, " O "); else fprintf (maildumpfile, " "); } fprintf (maildumpfile, "| | "); for (j=6; j>0; j--) { if (Pin[j].color == WHITE && Pin[j].count >= i+1) fprintf (maildumpfile, " X "); else if (Pin[j].color == BLACK && Pin[j].count >= i+1) fprintf (maildumpfile, " O "); else fprintf (maildumpfile, " "); } fprintf (maildumpfile, "|\n"); } fprintf (maildumpfile, " |"); for (j=12; j>6; j--) { if (Pin[j].color == WHITE && Pin[j].count == 5) fprintf (maildumpfile, " X "); else if (Pin[j].color == WHITE && Pin[j].count > 5) fprintf (maildumpfile, "%2d ", Pin[j].count); else if (Pin[j].color == BLACK && Pin[j].count == 5) fprintf (maildumpfile, " O "); else if (Pin[j].color == BLACK && Pin[j].count > 5) fprintf (maildumpfile, "%2d ", Pin[j].count); else fprintf (maildumpfile, " "); } fprintf (maildumpfile, "| | "); for (j=6; j>0; j--) { if (Pin[j].color == WHITE && Pin[j].count == 5) fprintf (maildumpfile, " X "); else if (Pin[j].color == WHITE && Pin[j].count > 5) fprintf (maildumpfile, "%2d ", Pin[j].count); else if (Pin[j].color == BLACK && Pin[j].count == 5) fprintf (maildumpfile, " O "); else if (Pin[j].color == BLACK && Pin[j].count > 5) fprintf (maildumpfile, "%2d ", Pin[j].count); else fprintf (maildumpfile, " "); } fprintf (maildumpfile, "|\n"); fprintf (maildumpfile, " v| |BAR| | "); if (gammon_resource.moneygame) fprintf (maildumpfile, "money-game\n"); else fprintf (maildumpfile, "%d-point match\n", gammon_resource.winat); fprintf (maildumpfile, " |"); for (j=13; j<19; j++) { if (Pin[j].color == WHITE && Pin[j].count == 5) fprintf (maildumpfile, " X "); else if (Pin[j].color == WHITE && Pin[j].count > 5) fprintf (maildumpfile, "%2d ", Pin[j].count); else if (Pin[j].color == BLACK && Pin[j].count == 5) fprintf (maildumpfile, " O "); else if (Pin[j].color == BLACK && Pin[j].count > 5) fprintf (maildumpfile, "%2d ", Pin[j].count); else fprintf (maildumpfile, " "); } fprintf (maildumpfile, "| | "); for (j=19; j<25; j++) { if (Pin[j].color == WHITE && Pin[j].count == 5) fprintf (maildumpfile, " X "); else if (Pin[j].color == WHITE && Pin[j].count > 5) fprintf (maildumpfile, "%2d ", Pin[j].count); else if (Pin[j].color == BLACK && Pin[j].count == 5) fprintf (maildumpfile, " O "); else if (Pin[j].color == BLACK && Pin[j].count > 5) fprintf (maildumpfile, "%2d ", Pin[j].count); else fprintf (maildumpfile, " "); } fprintf (maildumpfile, "|\n"); for (i=4; i>0; i--) { fprintf (maildumpfile, " |"); for (j=13; j<19; j++) { if (Pin[j].color == WHITE && Pin[j].count >= i) fprintf (maildumpfile, " X "); else if (Pin[j].color == BLACK && Pin[j].count >= i) fprintf (maildumpfile, " O "); else fprintf (maildumpfile, " "); } fprintf (maildumpfile, "| | "); for (j=19; j<25; j++) { if (Pin[j].color == WHITE && Pin[j].count >= i) fprintf (maildumpfile, " X "); else if (Pin[j].color == BLACK && Pin[j].count >= i) fprintf (maildumpfile, " O "); else fprintf (maildumpfile, " "); } fprintf (maildumpfile, "|\n"); } fprintf (maildumpfile, " +------------------------------------------+ O: - score: %d\n", Player[0].points); fprintf (maildumpfile, " 12 11 10 9 8 7 6 5 4 3 2 1\n\n"); if (turn == BLACK) fprintf (maildumpfile, " BAR: O-%d X-%d", Pin[0].count, Pin[25].count); else fprintf (maildumpfile, " BAR: O-%d X-%d", Pin[25].count, Pin[0].count); fprintf (maildumpfile, " OFF: O-%d X-%d", Pin[26].count, Pin[27].count); fprintf (maildumpfile, " Cube: %d ", doubler.value); if (doubler.owner == BLACK) fprintf (maildumpfile, "(owned by Player O)\n"); else if (doubler.owner == WHITE) fprintf (maildumpfile, "(owned by Player X)\n"); else fprintf (maildumpfile, "(owned by Player none)\n"); fprintf (maildumpfile, "\n\n"); fclose (maildumpfile); Info (" position written to \n xgammon.maildump "); } void open_protokol (void) { if (gammon_resource.protokol) { if (protokol_file) return; /* already open */ sprintf (protokol_file_name,"xgammon.protokol.%d", getpid ()); protokol_file = fopen (protokol_file_name, "w"); } } char *UserName (void) { return getpwuid (getuid ())->pw_name; }