/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * This is GNU Go, a Go program. Contact gnugo@gnu.org, or see * * http://www.gnu.org/software/gnugo/ for more information. * * * * Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, * * 2008 and 2009 by the Free Software Foundation. * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation - version 3 or * * (at your option) any later version. * * * * 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 the * * GNU General Public License in file COPYING for more details. * * * * You should have received a copy of the GNU General Public * * License along with this program; if not, write to the Free * * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02111, USA. * \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include #include #include #include "gg_utils.h" #include "random.h" #ifdef HAVE_GLIB_H #include #endif /* Avoid compiler warnings with unused parameters */ #define UNUSED(x) (void)x /* Define TERMINFO or ANSI_COLOR to enable coloring of pieces. * This is normally done in config.h. */ /* enabling color */ /* linux console : * 0=black * 1=red * 2=green * 3=yellow/brown * 4=blue * 5=magenta * 6=cyan * 7=white */ #ifdef TERMINFO #ifdef _AIX #define _TPARM_COMPAT #endif #if HAVE_CURSES_H #include #elif HAVE_NCURSES_CURSES_H #include #else #endif #if HAVE_TERM_H #include #elif HAVE_NCURSES_TERM_H #include #else #endif /* terminfo attributes */ static char *setaf; /* terminfo string to set color */ static char *op; /* terminfo string to reset colors */ static int init = 0; #endif /* TERMINFO */ /* for gg_cputime */ #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_SYS_TIMES_H #include #elif defined(WIN32) #include #endif void gg_init_color() { #ifdef TERMINFO /* compiler is set to make string literals const char * * But system header files dont prototype things correctly. * These are equivalent to a non-const string literals */ static char setaf_literal[] = "setaf"; static char op_literal[] = "op"; static char empty_literal[] = ""; if (init) return; init = 1; setupterm(NULL, 2, NULL); setaf = tigetstr(setaf_literal); if (!setaf) setaf = empty_literal; op = tigetstr(op_literal); if (!op) op = empty_literal; #endif /* TERMINFO */ } #ifdef WIN32 #ifdef VC #include verifyW32(BOOL b) { if (!b) { _ASSERTE(0 && "Win32 Error"); fprintf(stderr, "Win32 Err: %ld\n", GetLastError()); } } #else /* mingw32 lacks crtdbg.h and _ASSERTE */ verifyW32(BOOL b) { if (!b) { fprintf(stderr, "Win32 Err: %ld\n", GetLastError()); } } #endif #endif void write_color_char_no_space(int c, int x) { #ifdef TERMINFO fprintf(stderr, "%s%c", tparm(setaf, c, 0, 0, 0, 0, 0, 0, 0, 0), x); fputs(tparm(op, 0, 0, 0, 0, 0, 0, 0, 0, 0), stderr); #elif defined(ANSI_COLOR) fprintf(stderr, "\033[%dm%c\033[0m", 30+c, x); #elif defined(WIN32) static HANDLE hStdErr = 0; DWORD iCharsWritten; BOOL succeed32; CONSOLE_SCREEN_BUFFER_INFO bufInfo; if (!hStdErr) { hStdErr = GetStdHandle(STD_ERROR_HANDLE); if (hStdErr == INVALID_HANDLE_VALUE) { fprintf(stderr, "Unable to open stderr.\n"); } } /* Red & Blue are switched from what MS-Windows wants: * FOREGROUND_BLUE 0x0001 // text color contains blue. * FOREGROUND_GREEN 0x0002 // text color contains green. * FOREGROUND_RED 0x0004 // text color contains red * This magic switches the bits back: */ c = (c & 1) * 4 + (c & 2) + (c & 4) / 4; c += FOREGROUND_INTENSITY; succeed32 = GetConsoleScreenBufferInfo(hStdErr, &bufInfo); if (!succeed32) { /* Probably redirecting output, just give plain text. */ fprintf(stderr, "%c", x); return; } verifyW32(SetConsoleTextAttribute(hStdErr, (WORD) c)); verifyW32(WriteConsole(hStdErr, &x, 1, &iCharsWritten, 0)); verifyW32(SetConsoleTextAttribute(hStdErr, bufInfo.wAttributes)); #else fprintf(stderr, "%c", x); #endif } void write_color_string(int c, const char *str) { while (*str) write_color_char_no_space(c, *str++); } void write_color_char(int c, int x) { fprintf(stderr, " "); write_color_char_no_space(c, x); } /* * A wrapper around vsnprintf. */ void gg_vsnprintf(char *dest, unsigned long len, const char *fmt, va_list args) { #ifdef HAVE_VSNPRINTF vsnprintf(dest, len, fmt, args); #elif HAVE_G_VSNPRINTF g_vsnprintf(dest, len, fmt, args); #elif HAVE__VSNPRINTF _vsnprintf(dest, len, fmt, args); #else UNUSED(len); vsprintf(dest, fmt, args); #endif } void gg_snprintf(char *dest, unsigned long len, const char *fmt, ...) { va_list args; va_start(args, fmt); gg_vsnprintf(dest, len, fmt, args); va_end(args); } /* Get the time of day, calling gettimeofday from sys/time.h * if available, otherwise substituting a workaround for portability. */ double gg_gettimeofday(void) { struct timeval tv; #ifdef HAVE_GETTIMEOFDAY gettimeofday(&tv, NULL); #else tv.tv_sec = time(NULL); tv.tv_usec = 0; #endif return tv.tv_sec + 1.e-6 * tv.tv_usec; } const char * gg_version(void) { return VERSION; } /* return cputime used in secs */ double gg_cputime(void) { #if HAVE_SYS_TIMES_H && HAVE_TIMES && HAVE_UNISTD_H struct tms t; times(&t); return (t.tms_utime + t.tms_stime + t.tms_cutime + t.tms_cstime) / ((double) sysconf(_SC_CLK_TCK)); #elif defined(WIN32) FILETIME creationTime, exitTime, kernelTime, userTime; ULARGE_INTEGER uKernelTime, uUserTime, uElapsedTime; GetProcessTimes(GetCurrentProcess(), &creationTime, &exitTime, &kernelTime, &userTime); uKernelTime.LowPart = kernelTime.dwLowDateTime; uKernelTime.HighPart = kernelTime.dwHighDateTime; uUserTime.LowPart = userTime.dwLowDateTime; uUserTime.HighPart = userTime.dwHighDateTime; uElapsedTime.QuadPart = uKernelTime.QuadPart + uUserTime.QuadPart; /*_ASSERTE(0 && "Debug Times");*/ /* convert from multiples of 100nanosecs to seconds: */ return (signed __int64)(uElapsedTime.QuadPart) * 1.e-7; #else static int warned = 0; if (!warned) { fprintf(stderr, "CPU timing unavailable - returning wall time."); warned = 1; } /* return wall clock seconds */ return gg_gettimeofday(); #endif } /* Before we sort floating point values (or just compare them) we * may need to normalize them. This may sound cryptic but is * required to avoid an obscure platform dependency. * * The underlying problem is that most fractional decimal numbers * can't be represented exactly in a floating point number with base * two. The error may be small but it is there. When such numbers * are added or subtracted, the errors accumulate and even if the * result (counting exactly) should be a number which can be * represented exactly, this cannot be assumed to be the case. * * To give an example of this, the computation 0.3 + 0.05 - 0.35 may * sum to 0, a small negative value, or a small positive value. * Moreover, which case we encounter depends on the number of * mantissa bits in the floating point type used and the exact * details of the floating point arithmetic on the platform. * * In the context of sorting, assume that two values both should be * 0.35, but one has been computed as 0.3 + 0.05 and the other * directly assigned 0.35. Then it depends on the platform whether * they compare as equal or one of them is larger than the other. * * This code normalizes the values to avoid this problem. It is * assumed that all values encountered are integer multiples of a. */ float gg_normalize_float(float x, float a) { return a * ((int) (0.5 + x / a)); } int gg_normalize_float2int(float x, float a) { return ((int) (0.5 + x / a)); } /* A sorting algorithm, call-compatible with the libc qsort() function. * * The reason to prefer this to standard qsort() is that quicksort is * an unstable sorting algorithm, i.e. the relative ordering of * elements with the same comparison value may change. Exactly how the * ordering changes depends on implementation specific details like * the strategy for choosing the pivot element. Thus a list with * "equal" values may be sorted differently between platforms, which * potentially can lead to significant differences in the move * generation. * * This is an implementation of the combsort algorithm. * * Testing shows that it is faster than the GNU libc qsort() function * on small data sets and within a factor of two slower for large * random data sets. Its performance does not degenerate for common * special cases (i.e. sorted or reversed data) but it seems to be * susceptible to O(N^2) behavior for repetitive data with specific * cycle lengths. * * Like qsort() this algorithm is unstable, but since the same * implementation (this one) is used on all platforms, the reordering * of equal elements will be consistent. */ void gg_sort(void *base, size_t nel, size_t width, int (*cmp)(const void *, const void *)) { int gap = nel; int swap_made; char *end = (char *) base + width * (nel - 1); do { char *a, *b; swap_made = 0; gap = (10 * gap + 3) / 13; for (a = base, b = a + gap * width; b <= end; a += width, b += width) { if (cmp((void *) a, (void *) b) > 0) { char *c = a; char *d = b; size_t size = width; while (size-- > 0) { char tmp = *c; *c++ = *d; *d++ = tmp; } swap_made = 1; } } } while (gap > 1 || swap_made); } /* Linearly interpolate f(x) from the data given in interpolation_data. */ float gg_interpolate(struct interpolation_data *f, float x) { int i; float ratio; float diff; if (x < f->range_lowerbound) return f->values[0]; else if (x > f->range_upperbound) return f->values[f->sections]; else { ratio = ((float) f->sections) * (x - f->range_lowerbound) / (f->range_upperbound - f->range_lowerbound); i = (int) ratio; diff = ratio - ((float) i); if (0) fprintf(stderr, "Floating point Ratio: %f, integer: %d, diff %f", ratio, i, diff); return ((1 - diff) * f->values[i] + diff * f->values[i+1]); } } /* This is the simplest function that returns appr. a when a is small, * and approximately b when a is large. */ float soft_cap(float a, float b) { return ((a * b) / (a + b)); } /* Reorientation of point (i, j) into (*ri, *rj) */ void rotate(int i, int j, int *ri, int *rj, int bs, int rot) { int bs1; assert(bs > 0); assert(ri != NULL && rj != NULL); assert(rot >= 0 && rot < 8); /* PASS case */ if (i == -1 && j == -1) { *ri = i; *rj = j; return; } assert(i >= 0 && i < bs); assert(j >= 0 && j < bs); bs1 = bs - 1; if (rot == 0) { /* identity map */ *ri = i; *rj = j; } else if (rot == 1) { /* rotation over 90 degrees */ *ri = bs1 - j; *rj = i; } else if (rot == 2) { /* rotation over 180 degrees */ *ri = bs1 - i; *rj = bs1 - j; } else if (rot == 3) { /* rotation over 270 degrees */ *ri = j; *rj = bs1 - i; } else if (rot == 4) { /* flip along diagonal */ *ri = j; *rj = i; } else if (rot == 5) { /* flip */ *ri = bs1 - i; *rj = j; } else if (rot == 6) { /* flip along diagonal */ *ri = bs1 - j; *rj = bs1 - i; } else if (rot == 7) { /* flip */ *ri = i; *rj = bs1 - j; } } /* inverse reorientation of reorientation rot */ void inv_rotate(int i, int j, int *ri, int *rj, int bs, int rot) { /* every reorientation is it's own inverse except rotations over 90 and 270 degrees */ if (rot == 1) rotate(i, j, ri, rj, bs, 3); else if (rot == 3) rotate(i, j, ri, rj, bs, 1); else rotate(i, j, ri, rj, bs, rot); } /* Intermediate layer to random.c. gg_srand() should only be called via the * functions below. */ /* Private variable remembering the random seed. */ static unsigned int random_seed; unsigned int get_random_seed() { return random_seed; } void set_random_seed(unsigned int seed) { random_seed = seed; gg_srand(seed); } /* Update the random seed. This should be called at the start of each * new game. * We reset the random seed before obtaining a new one, to make the * next random seed depend deterministically on the old one. */ void update_random_seed(void) { gg_srand(random_seed); random_seed = gg_rand(); /* Since random seed 0 has a special interpretation when given as * command line argument with the -r option, we make sure to avoid * it. */ if (random_seed == 0) random_seed = 1; gg_srand(random_seed); } /* Restart the pseudo-random sequence with the initialization given * by the random seed. Should be called at each move. */ void reuse_random_seed() { gg_srand(random_seed); } /* * Local Variables: * tab-width: 8 * c-basic-offset: 2 * End: */