/* $Id: decision.c,v 1.3 1995/08/14 12:31:52 gammon Exp gammon $ */ /* decision.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 /* for the BOARD */ #include "gammon.h" extern MOVE *find_best_move (); extern long int PositionNumber (int STARTPIN, int color); int is_run (); float calculate_winning_percentage (int kind); float calculate_expected_time (); float evaluate (); void simple_pipcount (float *pc); void adjust_pipcount (float *pc); void sub_lossage_when_hit (float *pc); void Analyse_Doublet (int pin, int *set); void Analyse_No_Doublet (int pin, int *set); int Bar[3] = { 0, 0, 25 }; int Direction[3] = { 0, 1, -1 }; int Startpin[3] = { 0, 1, 24 }; int number_of_homepins[3] = { 0, 0, 0 }; int Endpin[3] = { 0, 24, 1 }; float Probtable[25]; float point_values[25] = { /* Wertetabelle fuer die 24 pins*/ 0.0, /* 0 is dummy */ 0.0, 1.0, 3.0, 8.0, 10.0, 10.0, 7.0, 6.0, 5.0, 4.0, 4.0, 6.0, /* 1 - 12 */ 6.0, 4.0, 4.0, 5.0, 6.0, 7.0, 10.0, 10.0, 8.0, 3.0, 1.0, 0.0 /* 13 - 25 */ }; /* Diese Tabelle enthaelt die exakten erwarteten Wartezeiten -1 bis ein Stein wieder eingewuerfelt ist */ float expected_waitingtime[7] = { 0.0, 1.0 / 35.0, 1.0 / 8.0, 1.0 / 3.0, 4.0 / 5.0, 25.0 / 11.0, 0.0 }; float prime_length_factor[8] = { 0.0, 1.0, 1.0, 1.2, 1.4, 1.7, 2.0, 2.0 }; void adjust_pipcount (float *pc) { int i; /* bestimmung der bestzten felder im Homefeld */ number_of_homepins[BLACK] = number_of_homepins[WHITE] = 0; for (i = 1; i < 7; i++) { if (Pin[ i].color == WHITE && Pin[ i].count>1) number_of_homepins[WHITE]++; if (Pin[25-i].color == BLACK && Pin[25-i].count>1) number_of_homepins[BLACK]++; } /* Verfeinerter pip_count (Steine auf bar als 25+x) Es wird die vereinfachung gemacht, das es n-mal solange dauert n Steine wieder hineinzuwuerfeln wie einen Stein hineinzuwuerfeln */ if (Pin[0].count) { if (number_of_homepins[WHITE] < 6) pc[BLACK] -= Pin[0].count * 8.1667 * expected_waitingtime[number_of_homepins[WHITE]]; else { for (i=6; i<26; i++) { if (Pin[i].color == WHITE) pc[WHITE] += Pin[i].count * (i - 5); } pc[BLACK] -= Pin[0].count * 8.1667 * expected_waitingtime[5]; } } if (Pin[25].count) { if (number_of_homepins[BLACK] < 6) pc[WHITE] -= Pin[25].count * 8.1667 * expected_waitingtime[number_of_homepins[BLACK]]; else { for (i=0; i<20; i++) { if (Pin[i].color == BLACK) pc[BLACK] += Pin[i].count * (20 - i); } pc[WHITE] -= Pin[25].count * 8.1667 * expected_waitingtime[5]; } } #ifdef DEBUG_EVALUATE printf("Verfeinerter Pipcount %f %f \n", pc[BLACK], pc[WHITE]); #endif } void add_prime_values(float *pc) { int i; for (i = 1; i < 25; i++) { if (Pin[i].color == BLACK && Pin[i].count > 1) { float zwischensumme = point_values[i]; int prime_length = 1; while (((i + 1) < 25 && Pin[i + 1].color == BLACK && Pin[i + 1].count > 1)) { i++; prime_length++; zwischensumme += point_values[i]; } zwischensumme *= prime_length_factor[prime_length]; pc[BLACK] += zwischensumme; } } for (i = 1; i < 25; i++) { if (Pin[i].color == WHITE && Pin[i].count > 1) { float zwischensumme = point_values[i]; int prime_length = 1; while (((i + 1) < 25 && Pin[i + 1].color == WHITE && Pin[i + 1].count > 1)) { i++; prime_length++; zwischensumme += point_values[i]; } zwischensumme *= prime_length_factor[prime_length]; pc[WHITE] += zwischensumme; } } } /* Diese Funktion soll eine Abschaetzung der Gewinnwahrscheinlichkeit fuer turn liefern. Im Endspiel wird diese exakt berechnet, sofern die Datenbank vorhanden ist */ float evaluate(int turn) { float pipcount[3] = {0.0, 0.0, 0.0}; int Runninggame = 1; float value = 0.0; Runninggame = is_run(); if (!Runninggame) { /* zunaechst ein grober pipcount*/ simple_pipcount(pipcount); #ifdef DEBUG_EVALUATE printf("Einfacher Pipcount %f %f \n", pipcount[BLACK], pipcount[WHITE]); #endif pipcount[other] += 8.1666 /* Anpassung an turn */; adjust_pipcount (pipcount) /* anpassen an BAR Besetzung*/; add_prime_values (pipcount) /* addieren der point_values */; /*sub_lossage_when_hit (pipcount); */ /* eventuelles geschlagenwerden */ #ifdef DEBUG_EVALUATE printf("Pipcount mit point_values %f %f \n", pipcount[BLACK], pipcount[WHITE]); #endif } else { /* Runninggame */ value = calculate_winning_percentage(ANSWER); /* die naechsten Zeilen garantieren, dass nur ins runninggame gewechselt wird wenn echt siegchance bestehen */ if (value >= 0.99) { value = calculate_expected_time(); return (1000.0 - value); } if (value >= 0.4 && value < 0.99) return ( 980.0 + value); if (value > 0.01) return (-899.0 + value); value = calculate_expected_time(); /* im Falle _sehr_ geringer Chancen wird als zweitbewertung der Erwartungswert herangezogen (hauptsaechlich Optik) */ return (-900.0 - value); } #ifdef DEBUG_EVALUATE printf("Wert der Position: %f \n", pipcount[turn] - pipcount[other]); #endif return (pipcount[turn] - pipcount[other]); } void sub_lossage_when_hit(float *pc) { int diceset[4]; int w1, w2, i, j; float lossage; for (i=0; i<25; i++) Probtable[i] = 0.0; for (i=0; i< 4; i++) diceset[i] = 0; /* first the non-doublets */ for (w1= 1; w1<6; w1++) { for (w2=w1+1; w2<7; w2++) { diceset[0] = w1; diceset[1] = w2; diceset[2] = w1; diceset[3] = 0; if (Pin[Bar[other]].count) { /* The enemies bar ist occupied */ if (Pin[Bar[other]].count > 1) diceset[2] = 0; Analyse_No_Doublet (Bar[other], diceset); if (Pin[Bar[other]].count> 1) diceset[0] = 0; else { int tp1, tp2, ways = 0, wp = 0; tp1 = Bar[other] + diceset[0] * Direction[other]; tp2 = Bar[other] + diceset[1] * Direction[other]; if (Pin[tp1].count < 2 || Pin[tp1].color == other) { ways++; wp = tp2; } if (Pin[tp2].count < 2 || Pin[tp2].color == other) { ways++; wp = tp1; } if (ways == 0) diceset[0] = 0; if (ways == 1) diceset[0] = wp; diceset[1] = 0; if (ways == 2) diceset[2] = 0; } } /* Bar analyse zuende */ for (i=Startpin[other]; i!=Endpin[other]; i+=Direction[other]) { if (Pin[i].color == other) Analyse_No_Doublet(i, diceset); } } } /* the doublets */ for (w1 = 1; w1 < 7; w1++) { diceset[0] = w1; diceset[1] = w1; diceset[2] = w1; diceset[3] = w1; if (Pin[Bar[other]].count) { /* The enemies bar ist occupied */ int tp; if (Pin[Bar[other]].count > 1) diceset[3] = 0; if (Pin[Bar[other]].count > 2) diceset[2] = 0; if (Pin[Bar[other]].count > 3) diceset[1] = 0; Analyse_Doublet(Bar[other], diceset); tp = Bar[other] + diceset[0] * Direction[other]; if (Pin[tp].color == turn && Pin[tp].count > 1) diceset[0] = 0; else { if (Pin[Bar[other]].count > 1) diceset[2] = 0; if (Pin[Bar[other]].count > 2) diceset[1] = 0; if (Pin[Bar[other]].count > 3) diceset[0] = 0; } } /* Bar analyse zuende */ for (i = Startpin[other]; i != Endpin[other]; i += Direction[other]) { if (Pin[i].color == other) Analyse_Doublet(i, diceset); } } /* Probability table is completly setup */ for (i = Startpin[turn]; i != Bar[other]; i += Direction[turn]) { if (Pin[i].count == 1 && Pin[i].color == turn && Probtable[i]) { lossage = 0.0; lossage += (i - Bar[turn]) * Direction[turn]; if (number_of_homepins[other] < 6) lossage += 8.16667 * expected_waitingtime[number_of_homepins[other]]; else { if (Pin[Bar[turn]].count) lossage += 8.16667 * expected_waitingtime[5]; else { for (j = Bar[other]; j != Bar[other] + 20 * Direction[other]; j += Direction[other]) { if (Pin[j].color == other) { lossage += Pin[j].count * (Bar[other] + 20 * Direction[other] - j) * Direction[other]; } } lossage += 8.1667 * expected_waitingtime[5]; } } #ifdef DEBUG_PROB printf("Blot auf %d, Wkeit = %f, Wert = %f \n", i, Probtable[i], lossage); #endif pc[turn] -= lossage * Probtable[i]; } } } void Analyse_Doublet(int pin, int *set) { int j; j = pin + set[0] * Direction[other]; if (j > 0 && j < 25) { Probtable[j] += 1.0 / 36.0; if (set[1] && (Pin[j].count < 2 || Pin[j].color == other)) { j += set[1] * Direction[other]; if (j > 0 && j < 25) { Probtable[j] += 1.0 / 36.0; if (set[2] && (Pin[j].count < 2 || Pin[j].color == other)) { j += set[2] * Direction[other]; if (j > 0 && j < 25) { Probtable[j] += 1.0 / 36.0; if (set[3] && (Pin[j].count < 2 || Pin[j].color == other)) { j += set[3] * Direction[other]; if (j > 0 && j < 25) Probtable[j] += 1.0 / 36.0; } } } } } } } void Analyse_No_Doublet(int pin, int *set) { int j, alset = 0; j = pin + set[0] * Direction[other]; if (j > 0 && j < 25) { Probtable[j] += 1.0 / 18.0; if (set[2] && (Pin[j].count < 2 || Pin[j].color == other)) { j = j + set[1] * Direction[other]; if (j > 0 && j < 25) { Probtable[j] += 1.0 / 18.0; alset = 1; } } j = pin + set[1] * Direction[other]; if (j > 0 && j < 25) { Probtable[j] += 1.0 / 18.0; if (set[2] && (Pin[j].count < 2 || Pin[j].color == other) && !alset) { j = j + set[0] * Direction[other]; if (j > 0 && j < 25) Probtable[j] += 1.0 / 18.0; } } } } int is_run() { int i, smallest_white = 0, smallest_black = 26; for (i = 0; i < 26; i++) { if (Pin[i].color == BLACK) { smallest_black = i; break; } } for (i = 25; i > -1; i--) { if (Pin[i].color == WHITE) { smallest_white = i; break; } } if (smallest_black < smallest_white) return (0); else return (1); } /* simple_pipcount: returns the simple pipcount for color not concerning turn, no special treatment of bar pc must be array[3] */ void simple_pipcount(float *pc) { int i, count[3] = { 0, 0, 0 }; for (i = 0; i < 26; i++) { if (Pin[i].color == BLACK) count[BLACK] -= Pin[i].count * (25 - i); if (Pin[i].color == WHITE) count[WHITE] -= Pin[i].count * i; } pc[BLACK] = (float) count[BLACK]; pc[WHITE] = (float) count[WHITE]; } int do_double (int kind) { float pipcount[3] = { 0.0, 0.0, 0.0 }; int Runninggame = 1; float chance; Runninggame = is_run(); if (!endgame_database) Runninggame = 0; /* Falls keine Datenbank existiert spielt er zumindest */ if (!Runninggame) { /* zunaechst ein grober pipcount*/ simple_pipcount(pipcount); /* Anpassung nach BAR*/ adjust_pipcount(pipcount); /* Anpassung entsprechend turn */ if (kind == ANSWER) pipcount[turn] += 8.1666; else pipcount[other] += 8.1666; if (pipcount[BLACK] > 0.0) pipcount[BLACK] = -0.5; if (pipcount[WHITE] > 0.0) pipcount[WHITE] = -0.5; if (kind == ANSWER) { chance = pipcount[turn] / pipcount[other]; #ifdef DEBUG_EVALUATE printf("Doubler: chance = %f kind = %d turn =%d \n", chance, kind, turn); #endif if (chance > 0.75) return 1; else return 0; } else { chance = pipcount[other] / pipcount[turn]; #ifdef DEBUG_EVALUATE printf("Doubler: chance = %f kind = %d turn =%d \n", chance, kind, turn); #endif if (chance >= 1.16) return 1; else return 0; } } else { chance = calculate_winning_percentage(kind); #ifdef DEBUG_EVALUATE printf("Doubler: chance = %f kind = %d turn =%d \n", chance, kind, turn); #endif if (kind == ANSWER) { if (chance >= 0.25) return 1; else return 0; } else { if (chance >= 0.65) return 1; else return 0; } } } float calc_endgame_offset(int color) { int i; float offset = 0.0; if (color == BLACK) { for (i = 1; i < 19; i++) if (Pin[i].color == BLACK) { offset += (19 - i) * Pin[i].count; /* Dies ist der reine Offset */ if (i < 7) offset += 4.0833333 * Pin[i].count; /* Extra punkte fuer crossovers */ if (i < 13) offset += 4.0833333 * Pin[i].count; offset += 4.0833333 * Pin[i].count; } } else { for (i = 24; i > 6; i--) if (Pin[i].color == WHITE) { offset += (i - 6) * Pin[i].count; if (i > 18) offset += 4.0833333 * Pin[i].count; if (i > 12) offset += 4.0833333 * Pin[i].count; offset += 4.0833333 * Pin[i].count; } } if (color == turn) offset += (19 + 16.3333333) * Pin[BAR].count; else offset += (19 + 16.3333333) * Pin[OTHER_BAR].count; #ifdef DEBUG_EVALUATE printf(" Offset = %f fuer Farbe %d \n", offset, color); #endif return (offset); } float calculate_expected_time() { float distribution[31]; unsigned short readarray[31]; float offset, e_value = 0.0; int nummer, i; nummer = PositionNumber(0, turn); if (nummer == 0L) return (0.0); fseek(endgame_database, (nummer - 1) * 60, 0); fread( readarray, 2, 30, endgame_database); for (i=0;i<30;i++) distribution[i]= ((float) readarray[i])/65535.0; offset = calc_endgame_offset(turn) / 8.16667; for (i = 0; i < 30; i++) e_value += ((float) i + 1.0 + offset) * distribution[i]; #ifdef DEBUG_EVALUATE printf(" EWert = %f \n", e_value); #endif return (e_value); } float calculate_winning_percentage(int kind) { float t_array[19], other_array[19], temp[19],sum; float turn_offset, other_offset, chance = 0, rest_prob; unsigned short readarray[19]; int t_off, o_off; int nummer, i, j; /* zunaechst fuer den turn einlesen */ nummer = PositionNumber (0, turn); if (nummer == 0L) return (1.0); fseek (endgame_database, (nummer - 1) * 36, 0); fread (readarray, 2, 18, endgame_database); sum=0.0; for (i=0; i<18; i++) { t_array[i] = ((float) readarray[i])/65535.0; sum+=t_array[i]; } for (i=0; i<=18;i++) t_array[i]/=sum; /* dann fuer den gegner */ nummer = PositionNumber(0, other); fseek (endgame_database, (nummer - 1) * 36, 0); fread (readarray, 2, 18, endgame_database); sum=0.0; for (i=0; i<18; i++) { other_array[i] = ((float) readarray[i])/65535.0; sum+=other_array[i]; } for (i=0; i<=18;i++) other_array[i]/=sum; t_array[18] = 0.0; other_array[18] = 0.0; /* Abstand zum Endgame mit einbeziehen */ turn_offset = calc_endgame_offset (turn) / 8.16667; other_offset = calc_endgame_offset (other) / 8.16667; /* Danach werden die Verteilungsfunktionen entsprechend dem offset transformiert */ if (turn_offset) { for (i = 0; i < 19; i++) temp[i] = 0; for (i = 0; i < 18; i++) { temp[i ] += (ceil(turn_offset) - turn_offset) * t_array[i]; temp[i+1] += (turn_offset - floor(turn_offset)) * t_array[i]; } for (i = 0; i < 19; i++) t_array[i] = temp[i]; } if (other_offset) { for (i = 0; i < 19; i++) temp[i] = 0; for (i = 0; i < 18; i++) { temp[i ] += (ceil(other_offset) - other_offset) * other_array[i]; temp[i+1] += (other_offset - floor(other_offset)) * other_array[i]; } for (i = 0; i < 19; i++) other_array[i] = temp[i]; } t_off = (int) floor(turn_offset); o_off = (int) floor(other_offset); if (kind == ANSWER) { /* diese frage soll herausfinden ob der andere am zug ist oder nicht Tut sie das? */ if ((turn == Player[0].color && Player[0].type == HUMAN) || (turn == Player[1].color && Player[1].type == HUMAN)) { for (i = 0; i < 19; i++) { rest_prob = 0.0; j = 0; while (j+o_off < i+t_off && j<31) j++; while (j < 19) { rest_prob += other_array[j]; j++; } chance += t_array[i] * rest_prob; } chance = 1 - chance; } else { for (i = 0; i < 19; i++) { rest_prob = 0.0; j = 0; while (j + t_off < i + o_off && j < 19) j++; while (j < 19) { rest_prob += t_array[j]; j++; } chance += rest_prob * other_array[i]; } chance = 1 - chance; } } else { for (i = 0; i < 19; i++) { rest_prob = 0.0; j = 0; while (j + o_off < i + t_off && j < 19) j++; while (j < 19) { rest_prob += other_array[j]; j++; } chance += t_array[i] * rest_prob; } } #ifdef DEBUG_EVALUATE printf(" Gewinnwahrscheinlichkeit fuer den Compi %f \n", chance); #endif return (chance); }