From 01c98981a223a4534f0fc1e7fceefd522a3b74db Mon Sep 17 00:00:00 2001 From: H.G. Muller Date: Thu, 14 Jan 2010 23:12:20 +0100 Subject: [PATCH] Checked in the Fairy-Max 4.8 M package --- Makefile | 42 +++ README | 30 +++ changelog | 61 +++++ copyright | 25 ++ data/fmax.ini | 393 ++++++++++++++++++++++++++++ data/qmax.ini | 34 +++ fairymax.6 | 236 +++++++++++++++++ fairymax.c | 813 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ fmax.6.pod | 119 +++++++++ maxqi.c | 791 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 2544 insertions(+), 0 deletions(-) create mode 100644 Makefile create mode 100644 README create mode 100644 changelog create mode 100644 copyright create mode 100644 data/fmax.ini create mode 100644 data/qmax.ini create mode 100644 fairymax.6 create mode 100644 fairymax.c create mode 100644 fmax.6.pod create mode 100644 maxqi.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b389dc9 --- /dev/null +++ b/Makefile @@ -0,0 +1,42 @@ +srcdir = . + +CC=gcc +CFLAGS += -O2 + + +ALL= fairymax shamax maxqi + +all: ${ALL} + +fairymax: fairymax.c + $(CC) $(CFLAGS) -DINI_FILE=\"/usr/share/games/fairymax/fmax.ini\" fairymax.c -o fairymax + +shamax: fairymax.c + $(CC) $(CFLAGS) -DINI_FILE=\"/usr/share/games/fairymax/fmax.ini\" -DSHATRANJ fairymax.c -o shamax + +maxqi: maxqi.c + $(CC) $(CFLAGS) -DINI_FILE=\"/usr/share/games/fairymax/qmax.ini\" maxqi.c -o maxqi + +install: ${ALL} ${srcdir}/data/* + cp -u ${srcdir}/fairymax /usr/games + cp -u ${srcdir}/shamax /usr/games + cp -u ${srcdir}/maxqi /usr/games + install -d -m0755 /usr/share/games/fairymax + cp -u ${srcdir}/data/* /usr/share/games/fairymax + install -d -m0755 /usr/share/man/man6 + cp -u ${srcdir}/fairymax.6.gz /usr/share/man/man6 + +clean: + rm -f ${ALL} + +dist-clean: + rm -f ${ALL} *~ data/*~ + +uninstall: + rm -f /usr/share/games/fairymax/* + rmdir /usr/share/games/fairymax + rm -f /usr/share/man/man6/fairymax.6.gz + rm -f /usr/games/fairymax + rm -f /usr/games/shamax + rm -f /usr/games/maxqi + diff --git a/README b/README new file mode 100644 index 0000000..db5e7c9 --- /dev/null +++ b/README @@ -0,0 +1,30 @@ +This package contains the sources of the XBoard-compatible Chess and +Chess-variant engine Fairy-Max, and its dedicaed derivatives ShaMax +(for Shatranj) and MaxQi (for XiangQi = Chinese Chess). A Makefile +is provided to compile and install them. Normally this would require only +the command (given from the main directory of the package): + +sudo make install + +This will install the executables in /usr/games, (where XBoard expects +them), and their data files in /usr/share/games/fairymax. + +Should you want to compile by hand, you could use the following +commands: + +gcc -O2 fairymax.c -o fairymax + +gcc -O2 fairymax.c -DSHATRANJ -o shamax + +gcc -O2 maxqi.c -o maxqi + +In this case you will get versions that expect their fmax.ini or qmax.ini +files in the current direcory. To change the default path of the ini files, +you can include the argument + +-DINI_FILE=\"pathname\" + +to the gcc compilation command line. + +H.G.Muller + diff --git a/changelog b/changelog new file mode 100644 index 0000000..3c4f32a --- /dev/null +++ b/changelog @@ -0,0 +1,61 @@ +/***************************************************************************/ +/* micro-Max version 4.8 (~1950 characters) features: */ +/* - recursive negamax search */ +/* - all-capture quiescence search with MVV/LVA priority */ +/* - (internal) iterative deepening */ +/* - best-move-first 'sorting' */ +/* - a hash table storing score and best move */ +/* - futility pruning */ +/* - king safety through magnetic frozen king */ +/* - null-move pruning */ +/* - Late-move reductions */ +/* - full FIDE rules (expt minor promotion) and move-legality checking */ +/* - keep hash + rep-draw detect */ +/* - end-game Pawn-push bonus, new piece values, gradual promotion */ +/***************************************************************************/ +/* The Fairy-Max version reads the piece description from a file fmax.ini */ +/* The format supports many fairy pieces, including hoppers. */ +/* f) now supports 15 piece types, by requisitioning WHITE bit */ +/* g) supports larger board width. */ +/* h) castling bug ('in-check by non-captures') corrected */ +/* i) rep-draw bug ('side-to-move') corrected */ +/* k) allow user underpromotions, recognize & ignore 'variant' command */ +/* l) edit bug corrected (i & j file clear) */ +/* m) piece values no longer quantized, game-stage counting bug corrected */ +/* n) edit-menu K-side castling bug corrected. */ +/* o) retrieve the requested variant from the .ini file */ +/* p) clear hash table on variant switch */ +/* q) reduced piece-material count for better Pawn push */ +/* r) hash-table bug corrected (X still ORed with flags) */ +/* s) Bug that prevented initialization center points corrected */ +/* t) castling bug after edit fixed */ +/* u) converted to protocol 2; ping implemented */ +/* v) white e.p. rights hash bug fixed; */ +/* w) piece indicators programable, multi-path support */ +/* x) e.p. changed to support Berolina Pawns */ +/* y) capture value of 6-7th-rank Pawn reduced in Shatranj */ +/* z) bug in promotion input corrected */ +/* A) stalemate-detection bug in printResult fixed */ +/* B) Invalidate hash on game-level promotion (might be under-promotion!) */ +/* C) King move evaluation based on negative piece value in stead of nr */ +/* D) WB memory command added, undo fixed */ +/* E) 15th piece read in */ +/* F) accepts ini fileargument */ +/* G) bug in calculation ASCII promotion character fixed */ +/* H) unified normal and shatranj source */ +/* J) rewrite under-promotion code, fixes persistent bug there */ +/* K) o[] and oo[] made int to make fairymax work on big-endian machines */ +/* L) added Resign option feature (using new WB protocol) */ +/* M) char -> signed char for better portability */ +/***************************************************************************/ + +4/6/2009 Unified source of ShaMax and Fairy-Max into single fmax.c file, and + created this ChangeLog from the until then self-documenting source. + +6/2/2009 Promotion code rewitten to not refer to default piece characters. + +9/3/2009 Fixed big-endian bug in reading inifile (char with int format), + and added some option features to make Fairy-Max useful as test + engine for GUIs that want to implement the WB protocol extensions. + +9/16/2009 made signedness of char in AI explicit diff --git a/copyright b/copyright new file mode 100644 index 0000000..1eeedf6 --- /dev/null +++ b/copyright @@ -0,0 +1,25 @@ +This package was debianized by H.G.Muller on +Fri Jun 5 20:42:56 CEST 2009. + +Upstream Author(s): + + H.G. Muller + +Copyright: + + Copyright (C) 2009 H.G. Muller + +License: + + Fairy-Max 4.8 is free software, and you have permission do + with it whatever you want, whether it is commercial or not. + Note, however, that Fairy-Max can easily be configured through + its fmax.ini file to play Chess variants that are legally pro- + tected by patents, and to do so would also require permission + of the holders of such patents. No guarantees are given that + Fairy-Max does anything in particular, or that it would not + wreck the hardware it runs on, and running it is entirely for + your own risk. + +The Debian packaging is (C) H.G.Muller. + diff --git a/data/fmax.ini b/data/fmax.ini new file mode 100644 index 0000000..64bd2ac --- /dev/null +++ b/data/fmax.ini @@ -0,0 +1,393 @@ +version 4.8(w) +8x8 +6 4 5 7 3 5 4 6 +6 4 5 7 3 5 4 6 +p:74 -16,24 -16,6 -15,5 -17,5 +p:74 16,24 16,6 15,5 17,5 +k:-1 1,34 -1,34 1,7 16,7 15,7 17,7 -1,7 -16,7 -15,7 -17,7 +n:259 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 +b:296 15,3 17,3 -15,3 -17,3 +R:444 1,3 16,3 -1,3 -16,3 +Q:851 1,3 16,3 15,3 17,3 -1,3 -16,3 -15,3 -17,3 + + +Some Common piece definitions: + +The board steps are encoded as follows: + + ^ toward 8th rank + | + -52 -51 -50 -49 -48 -47 -46 -45 -44 + -36 -35 -34 -33 -32 -31 -30 -29 -28 + -20 -19 -18 -17 -16 -15 -14 -13 -12 + <-- -4 -3 -2 -1 start 1 2 3 4 --> towards h file + to 12 13 14 15 16 17 18 19 20 +a-file 28 29 30 31 32 33 34 35 36 + 44 45 46 47 48 49 50 51 52 + | + v towards first rank + +SIMPLE LEAPERS +Ferz: +f:150 15,7 17,7 -15,7 -17,7 +Wazir: +w:125 1,7 16,7 -1,7 -16,7 +Knight: +n:325 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 +Dabbabah: +d:100 2,7 32,7 -2,7 -32,7 +Elephant: +e:80 30,7 34,7 -30,7 -34,7 +Camel: +C:225 13,7 47,7 49,7 19,7 -13,7 -47,7 -49,7 -19,7 +Zebra: +Z:175 29,7 46,7 50,7 35,7 -29,7 -46,7 -50,7 -35,7 +Unicorn: +u:-1 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 + +COMPOUND LEAPERS +King: +k:-1 1,34 -1,34 1,7 16,7 15,7 17,7 -1,7 -16,7 -15,7 -17,7 1,34 -1,34 +King (Shatranj, no castling): +k:-1 1,7 16,7 15,7 17,7 -1,7 -16,7 -15,7 -17,7 1,34 -1,34 +King (Capablanca castling): +k:-1 2,3034 -2,1034 1,7 16,7 15,7 17,7 -1,7 -16,7 -15,7 -17,7 +Commoner: +m:260 1,7 16,7 15,7 17,7 -1,7 -16,7 -15,7 -17,7 +Bison: +1000 13,7 47,7 49,7 19,7 -13,7 -47,7 -49,7 -19,7 29,7 46,7 50,7 35,7 -29,7 -46,7 -50,7 -35,7 +Wildebeest: +g:800 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 13,7 47,7 49,7 19,7 -13,7 -47,7 -49,7 -19,7 +Carpenter: +c:450 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 2,7 32,7 -2,7 -32,7 +Kangaroo: +o:450 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 30,7 34,7 -30,7 -34,7 +High Priestess: +h:625 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 30,7 34,7 -30,7 -34,7 15,7 17,7 -15,7 -17,7 +Minister +c:625 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 2,7 32,7 -2,7 -32,7 1,7 16,7 -1,7 -16,7 +Crab: +c:150 31,7 33,7 -14,7 -18,7 +Barc: +b:150 14,7 18,7 -31,7 -33,7 + +SEPARATE CAPTURES / NON-CAPTURES +White Pawn: +p:100 -16,6 -15,5 -17,5 -16,24 +White Pawn (Shatranj, no double move): +p:100 -16,6 -15,5 -17,5 +White Berolina Pawn +p:74 -15,24 -17,24 -16,5 -15,6 -17,6 +Black Pawn: +p:100 16,6 15,5 17,5 16,24 +Pegasus (moves as Queen, captures as Rook): +S:500 1,2 16,2 15,2 17,2 -1,2 -16,2 -15,2 -17,2 14,5 31,5 33,5 18,5 -14,5 -31,5 -33,5 -18,5 +Keen (moves as King, captures as Queen): +k:750 1,6 16,6 15,6 17,6 -1,6 -16,6 -15,6 -17,6 1,1 16,1 15,1 17,1 -1,1 -16,1 -15,1 -17,1 +Quing (moves as Queen, captures as King): +q:600 1,5 16,5 15,5 17,5 -1,5 -16,5 -15,5 -17,5 1,2 16,2 15,2 17,2 -1,2 -16,2 -15,2 -17,2 + +SLIDERS +Bishop: +b:350 15,3 17,3 -15,3 -17,3 +Rook: +R:500 1,3 16,3 -1,3 -16,3 +NightRider: +H:560 14,3 31,3 33,3 18,3 -14,3 -31,3 -33,3 -18,3 +Queen: +Q:950 1,3 16,3 15,3 17,3 -1,3 -16,3 -15,3 -17,3 + +LEAPER / SLIDER COMPOUNDS +Archbishop: +A:875 15,3 17,3 -15,3 -17,3 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 +Caliph: +C:875 15,3 17,3 -15,3 -17,3 13,7 47,7 49,7 19,7 -13,7 -47,7 -49,7 -19,7 +Marshall: +C:900 1,3 16,3 -1,3 -16,3 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 +Canvasser: +C:900 1,3 16,3 -1,3 -16,3 13,7 47,7 49,7 19,7 -13,7 -47,7 -49,7 -19,7 +Amazon: +A:1150 1,3 16,3 15,3 17,3 -1,3 -16,3 -15,3 -17,3 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 +Crooked Bishop: +B:900 15,1E003 17,1E003 -15,1E003 -17,1E003 15,FFFE0003 -17,FFFE0003 -15,FFFE0003 17,FFFE0003 + +HOPPERS +GrassHopper: +G:200 1,F8 16,F8 15,F8 17,F8 -1,F8 -16,F8 -15,F8 -17,F8 +Cannon: +C:400 1,BA 16,BA -1,BA -16,BA + +LAME LEAPERS +Horse: +N:5 16,1070 16,1F070 1,10070 1,FFFF0070 -16,1070 -16,1F070 -1,10070 -1,FFFF0070 +Xiangqi Elephant: +E:1 15,70 17,70 -15,70 -17,70 + + + +Syntax of a variant description: + +Each variant starts with the label "Game:" followed by the variant name. +After that follow upto 18 lines with the description. + +Line 1: Board size (files x ranks). Ranks must currently still be 8. +Line 2: initial setup of white pieces on back rank +Line 3: initial setup of black pieces on back rank +Line 4-10: Description of pieces present in the opening position +Line 11-18: Description 8 additional piece types + +The rest of the lines is ignored, but some pre-cooked descriptions are +provided for easy copying into the first positions. + + + +In case you want to build your own pieces, this is how it works: + +The piece-description lines have the following syntax: +1) a piece-indicator character (lower case if piece should be centralized) +2) a colon +3) the internal value to be used for the piece (in centiPawn; Royal = -1) +4) for each direction it moves in, a ray descriptor consisting of: + a) the initial step vector (on a 16x8 board, so 16 = straight ahead) + b) a comma + c) the move-mode descriptor, most easily given in hexadecimal, as the + individual bits specify the various options + +The individual bits in the move-mode descriptor have the following meaning: +In the last hexadecimal digit: + 1 capture allowed (of enemy piece; own pieces always block a move) + 2 con-capture allowed (i.e. we can move here if the square is empty) + 4 leaper, i.e. move terminates after one step (as opposed to slider) + 8 hop over non-empty square (normally occupied squares terminate a move) +Bits set in the forelast digits TOGGLE the corresponding bits in the last +digit. For hoppers when they hop over something, for the other pieces after +every step (so for normal pieces, better not set those bits!). + +The digit before that can only be 0 or 1; a 1 indicates the board should +be treated as a cylinder, pieces crossing the right edge re-entering the +board at the left, and vice versa. + +The higher-order bits toggle corresponding bits in the step vector, +to allow zig-zag paths. Better not set those either, if a straight +path is desired. + +Useful bit combinations for the last digit are: + 3 normal slider + 7 normal leaper + 6 leaper that only moves (e.g. Pawn straight ahead) + 5 leaper that only captures (e.g. Pawn diagonal) + 1 slider that only captures + 2 slider that only moves + 0 pass through (for testing emptiness by Xiangqi Horse and Elephant) + 4 reserved for skip-step of Pawn double move and castling + 8 skip to hopper platform (1st part of Grasshopper move) + A non-capture before hop (1st part of Cannon move) + C must hop immediately + +For example, if the initial step vector equals 1, and the descriptor +is 11003, the piece is an alternator (as no hop bit is set), and alternates +the mode from 3 to 3 (as the toggle digit is 0), i.e. all steps are moves +that can both capture and non-capture, and only captures terminate the ray. +The step vector is toggled by 11, though, and thus alternates between 1 +and 10 (hex) = 16. So the piece zig-zags over the board, right, forward, right, +forward, etc. Had the move attributes been 11032, captures would only +be allowed on the odd steps (after moving right), while the even steps could +only be non-captures (both the 1 and 2 bit are toggled). With 11030 the +odd steps can only be skipped (if empty), and the even steps can both capture +and non-capture, meaning the piece moves like a Bishop that can be blocked +by a piece just next to the diagonal. + +If the first step is a slider, a second step is made (if the square was +empty), but if the mode toggles to leaper, the move stops there (e.g. Horse). + +Hoppers MUST change into non-hoppers on hopping, i.e. the 8 bit of BOTH +lower digits must be set. Otherwise results will be undefined. + +Note that the first two piece-describing lines MUST be for the white and +black Pawn, respectively, or promotions will have undefined effects. Also +note that uMax does do primitive evaluation of Pawn structure, which might +become counter-productive if the Pawn move is changed. + +Castling is done with the Rook replacement (the piece that starts in the +corner, whatever its type). If you don't want that, remove the castling +moves from the King desription. If the castling initiator does not start +in a central file, the results are currently undefined. + + +For the truly lazy, a few complete game descriptions can be found below: + +// FIDE Chess (a.k.a. Mad Queen variant) +Game: normal +8x8 +6 4 5 7 3 5 4 6 +6 4 5 7 3 5 4 6 +p:74 -16,24 -16,6 -15,5 -17,5 +p:74 16,24 16,6 15,5 17,5 +k:-1 1,34 -1,34 1,7 16,7 15,7 17,7 -1,7 -16,7 -15,7 -17,7 +n:259 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 +b:296 15,3 17,3 -15,3 -17,3 +R:444 1,3 16,3 -1,3 -16,3 +Q:851 1,3 16,3 15,3 17,3 -1,3 -16,3 -15,3 -17,3 +f:481 13,FFFFF207 29,F207 46,F207 47,10207 49,10207 50,11207 35,11207 19,1207 -13,1207 -29,FFFF1207 -46,FFFF1207 -47,FFFF0207 -49,FFFF0207 -50,FFFEF207 -35,FFFEF207 -19,FFFFF207 + +// Arabic precursor of modern Chess +Game: shatranj +8x8 +6 4 5 3 7 5 4 6 +6 4 5 3 7 5 4 6 +p:100 -16,6 -15,5 -17,5 +p:100 16,6 15,5 17,5 +k:-1 1,7 16,7 15,7 17,7 -1,7 -16,7 -15,7 -17,7 +n:450 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 +b:110 30,7 34,7 -30,7 -34,7 +R:630 1,3 16,3 -1,3 -16,3 +q:180 15,7 17,7 -15,7 -17,7 +e:110 30,7 34,7 -30,7 -34,7 +f:180 15,7 17,7 -15,7 -17,7 + +// Medieval intermediate between Shatranj and FIDE Chess +Game: courier +12x8 +6 4 8 5 10 3 7 9 5 8 4 6 +6 4 8 5 10 3 7 9 5 8 4 6 +p:65 -16,6 -15,5 -17,5 +p:65 16,6 15,5 17,5 +k:-1 1,7 16,7 15,7 17,7 -1,7 -16,7 -15,7 -17,7 +n:300 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 +b:366 15,3 17,3 -15,3 -17,3 +R:550 1,3 16,3 -1,3 -16,3 +f:120 15,7 17,7 -15,7 -17,7 +e:70 30,7 34,7 -30,7 -34,7 +w:100 1,7 16,7 -1,7 -16,7 +m:280 1,7 16,7 15,7 17,7 -1,7 -16,7 -15,7 -17,7 + +// The King moves as a Knight, and vice versa +Game: knightmate +8x8 +6 4 5 7 3 5 4 6 +6 4 5 7 3 5 4 6 +p:74 -16,24 -16,6 -15,5 -17,5 +p:74 16,24 16,6 15,5 17,5 +u:-1 1,34 -1,34 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 +m:222 1,7 16,7 15,7 17,7 -1,7 -16,7 -15,7 -17,7 +b:296 15,3 17,3 -15,3 -17,3 +R:444 1,3 16,3 -1,3 -16,3 +Q:851 1,3 16,3 15,3 17,3 -1,3 -16,3 -15,3 -17,3 + +// Modern variant with two new pieces (Archbishop and Chancellor) on 10x8 board +Game: capablanca +10x8 +6 4 8 5 7 3 5 9 4 6 +6 4 8 5 7 3 5 9 4 6 +p:100 -16,24 -16,6 -15,5 -17,5 +p:100 16,24 16,6 15,5 17,5 +k:-1 1,3034 -1,1034 1,7 16,7 15,7 17,7 -1,7 -16,7 -15,7 -17,7 +n:310 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 +b:350 15,3 17,3 -15,3 -17,3 +R:475 1,3 16,3 -1,3 -16,3 +Q:950 1,3 16,3 15,3 17,3 -1,3 -16,3 -15,3 -17,3 +A:825 15,3 17,3 -15,3 -17,3 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 +C:875 1,3 16,3 -1,3 -16,3 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 +e:1000 15,7 17,7 -15,7 -17,7 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 -1,7 1,7 -16,7 16,7 -1,34 1,34 -16,34 16,34 + +// Gothic Chess is protected through U.S. patent #6,481,716 by Ed Trice. +// Spreading it without license might be a criminal offense! +Game: gothic +10x8 +6 4 5 7 9 3 8 5 4 6 +6 4 5 7 9 3 8 5 4 6 +p:100 -16,24 -16,6 -15,5 -17,5 +p:100 16,24 16,6 15,5 17,5 +k:-1 1,3034 -1,1034 1,7 16,7 15,7 17,7 -1,7 -16,7 -15,7 -17,7 +n:310 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 +b:350 15,3 17,3 -15,3 -17,3 +R:475 1,3 16,3 -1,3 -16,3 +Q:950 1,3 16,3 15,3 17,3 -1,3 -16,3 -15,3 -17,3 +A:825 15,3 17,3 -15,3 -17,3 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 +C:875 1,3 16,3 -1,3 -16,3 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 + +// Cylinder Chess (WinBoard / XBoard legality testing should be off toplay this!) +Game: cylinder +8x8 +6 4 5 7 3 5 4 6 +6 4 5 7 3 5 4 6 +p:100 -16,24 -16,6 -15,105 -17,105 +p:100 16,24 16,6 15,105 17,105 +k:-1 1,34 -1,34 1,107 16,7 15,107 17,107 -1,107 -16,7 -15,107 -17,107 +n:350 14,107 31,107 33,107 18,107 -14,107 -31,107 -33,107 -18,107 +b:450 15,103 17,103 -15,103 -17,103 +R:525 1,103 16,3 -1,103 -16,3 +Q:1150 1,103 16,3 15,103 17,103 -1,103 -16,3 -15,103 -17,103 + +// Berolina Chess. In WinBoard 4.3.15 you can play this with legality testing switched off +Game: berolina +8x8 +6 4 5 7 3 5 4 6 +6 4 5 7 3 5 4 6 +p:74 -15,24 -17,24 -16,5 -15,6 -17,6 +p:74 15,24 17,24 16,5 15,6 17,6 +k:-1 1,34 -1,34 1,7 16,7 15,7 17,7 -1,7 -16,7 -15,7 -17,7 +n:259 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 +b:296 15,3 17,3 -15,3 -17,3 +R:444 1,3 16,3 -1,3 -16,3 +Q:851 1,3 16,3 15,3 17,3 -1,3 -16,3 -15,3 -17,3 + +// Modern variant with four new pieces in randomly chosen setup on 8x8 board +Game: super +8x8 +6 4 5 7 3 5 4 6 +6 4 5 7 3 5 4 6 +p:100 -16,24 -16,6 -15,5 -17,5 +p:100 16,24 16,6 15,5 17,5 +k:-1 1,34 -1,34 1,7 16,7 15,7 17,7 -1,7 -16,7 -15,7 -17,7 +n:350 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 +b:360 15,3 17,3 -15,3 -17,3 +R:575 1,3 16,3 -1,3 -16,3 +Q:900 1,3 16,3 15,3 17,3 -1,3 -16,3 -15,3 -17,3 +S:825 15,3 17,3 -15,3 -17,3 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 +E:850 1,3 16,3 -1,3 -16,3 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 +V:775 1,7 16,7 15,7 17,7 -1,7 -16,7 -15,7 -17,7 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 +A:1200 1,3 16,3 -1,3 -16,3 15,3 17,3 -15,3 -17,3 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 +o:700 2,7 30,7 32,7 34,7 -2,7 -30,7 -32,7 -34,7 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 +g:640 1,7 16,7 15,7 17,7 -1,7 -16,7 -15,7 -17,7 2,7 30,7 32,7 34,7 -2,7 -30,7 -32,7 -34,7 +m:-1 1,34 -1,34 1,7 16,7 15,7 17,7 -1,7 -16,7 -15,7 -17,7 16,70 -16,70 +d:625 1,7 16,7 -1,7 -16,7 15,3 17,3 -15,3 -17,3 + +Game: fairy +8x8 +10 9 8 7 3 5 4 6 +10 9 8 7 3 5 4 6 +p:100 -16,24 -16,6 -15,5 -17,5 +p:100 16,24 16,6 15,5 17,5 +k:-1 1,34 -1,34 1,7 16,7 15,7 17,7 -1,7 -16,7 -15,7 -17,7 +n:325 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 +b:350 15,3 17,3 -15,3 -17,3 +R:500 1,3 16,3 -1,3 -16,3 +Q:950 1,3 16,3 15,3 17,3 -1,3 -16,3 -15,3 -17,3 +e:60 30,7 34,7 -30,7 -34,7 +H:560 14,3 31,3 33,3 18,3 -14,3 -31,3 -33,3 -18,3 +O:320 1,BA 16,BA -1,BA -16,BA +A:875 15,3 17,3 -15,3 -17,3 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 +C:900 1,3 16,3 -1,3 -16,3 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 +v:850 1,7 16,7 15,7 17,7 -1,7 -16,7 -15,7 -17,7 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 +Z:1260 1,3 16,3 -1,3 -16,3 15,3 17,3 -15,3 -17,3 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 +m:275 1,7 16,7 15,7 17,7 -1,7 -16,7 -15,7 -17,7 + +// Great Shatranj: modern variant with range-two leapers replacing sliders, on 10x8 board +// Must be played with legality testing off in XBoard 4.4.0. +Game: great +10x8 +6 4 5 8 3 10 9 5 4 6 +6 4 5 8 3 10 9 5 4 6 +p:100 -16,6 -15,5 -17,5 +p:100 16,6 15,5 17,5 +k:-1 1,7 16,7 15,7 17,7 -1,7 -16,7 -15,7 -17,7 +n:290 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 +e:270 15,7 17,7 -15,7 -17,7 30,7 34,7 -30,7 -34,7 +w:300 1,7 16,7 -1,7 -16,7 2,7 -2,7 32,7 -32,7 +s:280 1,7 16,7 15,7 17,7 -1,7 -16,7 -15,7 -17,7 +g:640 1,7 16,7 -1,7 -16,7 2,7 -2,7 32,7 -32,7 15,7 17,7 -15,7 -17,7 30,7 34,7 -30,7 -34,7 +h:640 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 15,7 17,7 -15,7 -17,7 30,7 34,7 -30,7 -34,7 +m:640 14,7 31,7 33,7 18,7 -14,7 -31,7 -33,7 -18,7 1,7 16,7 -1,7 -16,7 2,7 -2,7 32,7 -32,7 + +// End of game file diff --git a/data/qmax.ini b/data/qmax.ini new file mode 100644 index 0000000..6e215db --- /dev/null +++ b/data/qmax.ini @@ -0,0 +1,34 @@ +version 4.8(w) +10x9 +11 9 4 8 3 8 4 9 11 +11 9 5 8 3 8 5 9 11 +p:100 -1,7 +p:100 1,7 +k:-1 1,C07 16,C07 -1,C07 -16,C07 +e:150 15,470 17,470 -15,470 -17,470 +e:150 15,870 17,870 -15,870 -17,870 +q:190 -1,7 16,7 -16,7 +q:190 1,7 16,7 -16,7 +A:200 15,C07 17,C07 -15,C07 -17,C07 +h:450 16,1070 16,1F070 1,10070 1,FFFF0070 -16,1070 -16,1F070 -1,10070 -1,FFFF0070 +C:460 1,BA 16,BA -1,BA -16,BA +R:900 1,3 16,3 -1,3 -16,3 + + +// Chinese Chess +Game: xiangqi +10x9 +11 9 4 8 3 8 4 9 11 +11 9 5 8 3 8 5 9 11 +p:100 -1,7 +p:100 1,7 +k:-1 1,C07 16,C07 -1,C07 -16,C07 +e:150 15,470 17,470 -15,470 -17,470 +e:150 15,870 17,870 -15,870 -17,870 +q:190 -1,7 16,7 -16,7 +q:190 1,7 16,7 -16,7 +A:200 15,C07 17,C07 -15,C07 -17,C07 +h:450 16,1070 16,1F070 1,10070 1,FFFF0070 -16,1070 -16,1F070 -1,10070 -1,FFFF0070 +C:460 1,BA 16,BA -1,BA -16,BA +R:900 1,3 16,3 -1,3 -16,3 +// End of game file diff --git a/fairymax.6 b/fairymax.6 new file mode 100644 index 0000000..a84b44b --- /dev/null +++ b/fairymax.6 @@ -0,0 +1,236 @@ +.\" Automatically generated by Pod::Man v1.37, Pod::Parser v1.32 +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sh \" Subsection heading +.br +.if t .Sp +.ne 5 +.PP +\fB\\$1\fR +.PP +.. +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +'br\} +.\" +.\" If the F register is turned on, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.if \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. nr % 0 +. rr F +.\} +.\" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.hy 0 +.if n .na +.\" +.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). +.\" Fear. Run. Save yourself. No user-serviceable parts. +. \" fudge factors for nroff and troff +.if n \{\ +. ds #H 0 +. ds #V .8m +. ds #F .3m +. ds #[ \f1 +. ds #] \fP +.\} +.if t \{\ +. ds #H ((1u-(\\\\n(.fu%2u))*.13m) +. ds #V .6m +. ds #F 0 +. ds #[ \& +. ds #] \& +.\} +. \" simple accents for nroff and troff +.if n \{\ +. ds ' \& +. ds ` \& +. ds ^ \& +. ds , \& +. ds ~ ~ +. ds / +.\} +.if t \{\ +. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" +. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' +. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' +. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' +. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' +. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' +.\} +. \" troff and (daisy-wheel) nroff accents +.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' +.ds 8 \h'\*(#H'\(*b\h'-\*(#H' +.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] +.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' +.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' +.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] +.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] +.ds ae a\h'-(\w'a'u*4/10)'e +.ds Ae A\h'-(\w'A'u*4/10)'E +. \" corrections for vroff +.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' +.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' +. \" for low resolution devices (crt and lpr) +.if \n(.H>23 .if \n(.V>19 \ +\{\ +. ds : e +. ds 8 ss +. ds o a +. ds d- d\h'-1'\(ga +. ds D- D\h'-1'\(hy +. ds th \o'bp' +. ds Th \o'LP' +. ds ae ae +. ds Ae AE +.\} +.rm #[ #] #H #V #F C +.\" ======================================================================== +.\" +.IX Title "FMAX.6 1" +.TH FMAX.6 1 "2009-06-05" "perl v5.8.8" "User Contributed Perl Documentation" +.SH "NAME" +fairymax \- xboard\-compatible chess and chess\-variant engine 'Fairy\-Max' +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +\&\fBfairymax\fR [hashSize] [iniFile] +.PP +\&\fBshamax\fR [hashSize] [iniFile] +.PP +\&\fBmaxqi\fR [hashSize] [iniFile] +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +\&\fBfairymax\fR is a program that plays chess and chess variants. +It uses the xboard/winboard chess-engine protocol to communicate. +Apart from 'regular' chess (also known as the Mad-Queen variant), +it can play Capablanca chess, gothic chess, knightmate, cylinder chess, +berolina chess, superchess and courier chess. +Fairy-Max can be easily configured by the user to play other variants as well, +by modifying the ini file. +This ini file describes the rules of movement +of the participating pieces and the initial board setup. +.PP +Fairy-Max can also play shatranj, +but in this case is not aware of the shatranj rule that a bare king loses. +So it might play sub-optimally in the late end\-game. +A version of Fairy-Max adapted to implement the baring rule is +available under the name \fBshamax\fR. +.PP +Similarly, a version of Fairy-Max adapted to play Xiang Qi (Chinese Chess) +is included in the fmax package as well. +.PP +\&\fBfairymax\fR is a derivative of the world's (once) smallest chess program +(source\-code wise), micro\-Max. +The latter measures less that 2000 characters, (about 100 code lines), +and has a computer rating of around 2050 on the \s-1CCRL\s0 rating list. +Although this is about 1000 rating points behind the world champion, +micro-Max still makes a quite tough opponent even for club players, +although it is not unbeatable. +.PP +The main difference between micro-Max and Fairy-Max is that the latter loads +its move-generator tables, which specify how the various pieces move, +from an external file, so it can be easily adapted to incorporate un-orthodox pieces. +For ease of use of the artificial\-intelligence, Fairy-Max is equipped with +I/O routines that allow it to run with the xboard graphical user interface. +.PP +See \fIxboard\fR\|(6) for instructions about how to use \fBfairymax\fR through xboard. To +start up quickly, you just need the command: \fBxboard \-fcp fairymax\fR. +However, XBoard might not support symbols for every unorthodox piece in board sizes +different from \fBbulky\fR, \fBmiddling\fR and \fBpetite\fR. +It might thius be adviasable to specify a board size as well, e.g. +\&\fBxboard \-fcp shamax \-boardSize middling \-variant shatranj\fR +to get correct display of the elephant and general pieces in shatranj. +Note that to be able to play the chess variants, +you will need xboard 4.3.14 or later. +.PP +The fmax.ini file from which Fairy-Max by default takes the piece and game definitions +is a self-documenting text file, +which contains instructions for how to define new pieces and chess variants. +In addition it contains an extensive list of pre-defined pieces, +incuding many not occurring in any of the pre-defined variants, +which the user can draw on to define his own variants. +.PP +Amongst the move types supported by Fairy-Max are normal leaper and slider moves, +(e.g. knight and rook), +divergent moves (i.e. capture and non-capture moves can be different) +hoppers (which jump over other pieces, such as the Chinese cannon or the grasshopper), +lame leapers (the move of which can be blocked on squares they cannot move to, +such as the Chinese horse and elephant), +and any combination thereof, +in every possible direction. +The board width is configurable upto a width of 14 squares, +and cylindrical boards (where left and right edge connect) are supported as well. +.SH "OPTIONS" +.IX Header "OPTIONS" +.IP "\fBhashSize\fR" 8 +.IX Item "hashSize" +If the first argument to fairymax is numeric, +it is taken as an indicator for the amount of memory Fairy-Max is allowed to use +for its internal hash table. +The default value for this argument, 22, would result in a memory usage of 48MB. +Each next-higher number doubles the memory usage, each next-lower halves it. +Running with less than 6MB (i.e. argument 19) is not recommended. +When fairymax is running under xboard 4.3.15 the hash-table size can be set +through the xboard menus, +making this argument superfluous. +.IP "\fBiniFile\fR" 8 +.IX Item "iniFile" +A second or non-numeric first argument is taken as a filename. +Fairy-max will use the mentioned file in stead of its default fmax.ini file +to define the movement of pieces and initial setup of the variants. +This makes it easier to define your own variants. +.SH "SEE ALSO" +.IX Header "SEE ALSO" +\&\fIxboard\fR\|(6) +.PP +http://www.chessvariants.org/index/msdisplay.php?itemid=MSfairy\-max +.PP +http://home.hccnet.nl/h.g.muller/max\-src2.html +.PP +http://www.open\-aurec.com/wbforum/viewtopic.php?t=49439 +.SH "AUTHOR" +.IX Header "AUTHOR" +\&\fBFairy-Max\fR was written by H.G.Muller . +.PP +This manual page was generated with \fIpod2man\fR\|(1). diff --git a/fairymax.c b/fairymax.c new file mode 100644 index 0000000..8b7b45a --- /dev/null +++ b/fairymax.c @@ -0,0 +1,813 @@ +/***************************************************************************/ +/* fairy-Max, */ +/* Version of the sub-2KB (source) micro-Max Chess program, fused to a */ +/* generic WinBoard interface, loading its move-generator tables from file */ +/***************************************************************************/ + + /*****************************************************************/ + /* LICENCE NOTIFICATION */ + /* Fairy-Max 4.8 is free software, and you have my permission do */ + /* with it whatever you want, whether it is commercial or not. */ + /* Note, however, that Fairy-Max can easily be configured through*/ + /* its fmax.ini file to play Chess variants that are legally pro-*/ + /* tected by patents, and that to do so would also require per- */ + /* mission of the holders of such patents. No guarantees are */ + /* given that Fairy-Max does anything in particular, or that it */ + /* would not wreck the hardware it runs on, and running it is */ + /* entirely for your own risk. H.G,Muller, author of Fairy-Max */ + /*****************************************************************/ + +#define MULTIPATH +#define VERSION "4.8M" + +#include +#include +#include +#include +#include + +#ifndef INI_FILE +#define INI_FILE "fmax.ini" +#endif + +#ifdef WIN32 +# include +#else +# include + int GetTickCount() // with thanks to Tord + { struct timeval t; + gettimeofday(&t, NULL); + return t.tv_sec*1000 + t.tv_usec/1000; + } +#endif + +int StartKey; + +#define EMPTY -1 +#define WHITE 0 +#define BLACK 16 + +#define STATE 128 + +/* The following macros indicate the differences between Fairy-Max and its */ +/* dedicated Shatranj derivative ShaMax so that these can now be compiled */ +/* from the same unified source file. */ +/* Compile with gcc option -DSHATRANJ to build ShaMax. */ +#ifdef SHATRANJ +# define FAC 175 +# define EG 13 +# define NAME "ShaMax" +# define SHAMAX(x) x +# define FMAX(x) +#else +# define FAC 128 +# define EG 10 +# define NAME "Fairy-Max" +# define SHAMAX(x) +# define FMAX(x) x +#endif + +/* make unique integer from engine move representation */ +#define PACK_MOVE 256*K + L; + +/* convert intger argument back to engine move representation */ +#define UNPACK_MOVE(A) K = (A)>>8 & 255; L = (A) & 255; + +/* Global variables visible to engine. Normally they */ +/* would be replaced by the names under which these */ +/* are known to your engine, so that they can be */ +/* manipulated directly by the interface. */ + +int Side; +int Move; +int PromPiece; +int Result; +int TimeLeft; +int MovesLeft; +int MaxDepth; +int Post; +int Fifty; +int UnderProm; +int GameNr; +int Resign; +int Threshold = 800; +int Score; +char piecename[32], piecetype[32]; +char *inifile = INI_FILE; + +int Ticks, tlim, Setup, SetupQ; + +int GameHistory[1024]; +char HistoryBoards[1024][STATE], setupPosition[131]; +int GamePtr, HistPtr; + +#define W while +#define K(A,B) *(int*)(T+A+S*(B&31)) +#define J(A) K(y+A,b[y])-K(x+A,u)-K(H+A,t) + +int U=(1<<23)-1; +struct _ {int K,V;char X,Y,D,F;} *A; /* hash table, 16M+8 entries*/ + +int M=136,S=128,I=8e3,Q,O,K,N,j,R,J,Z,LL, /* M=0x88 */ +BW,BH,sh, +w[16]={0,2,2,-1,7,8,12,23,7,5}, /* relative piece values */ +o[256], +oo[32], /* initial piece setup */ +of[256], +od[16]; /* 1st dir. in o[] per piece*/ + +signed char L,pl[17], +b[513], /* board: 16x8+dummy, + PST */ +T[4104], /* hash translation table */ +centr[32], +n[]=".*XKNBRQEWFMACHG?x+knbrqewfmachg"; /* piece symbols on printout*/ + +pboard() +{int i; + i=-1;W(++i<128)printf(" %c",(i&15)==BW&&(i+=15-BW)?10:n[b[i]&31]); +} + + +D(k,q,l,e,E,z,n) /* recursive minimax search, k=moving side, n=depth*/ +int k,q,l,e,E,z,n; /* (q,l)=window, e=current eval. score, E=e.p. sqr.*/ +{ /* e=score, z=prev.dest; J,Z=hashkeys; return score*/ + int j,r,m,v,d,h,i,F,G,P,V,f=J,g=Z,C,s,flag,FF; + signed char t,p,u,x,y,X,Y,H,B; + struct _*a=A+(J+(k+S)*E&U-1); /* lookup pos. in hash table*/ + q-=qD;m=a->V;X=a->F;Y=a->Y; /* resume at stored depth */ + if(a->K-Z|z&S | /* miss: other pos. or empty*/ + !(m<=q|X&8&&m>=l|X&S)) /* or window incompatible */ + d=Y=0; /* start iter. from scratch */ + X=a->X; /* start at best-move hint */ + W(d++2&&l+I?D(16-k,-l,1-l,-e,2*S,2*S,d-3):I; /* search null move */ + m=-P beta unconsidered:static eval */ + SHAMAX( if(pl[k]<=1&pl[16-k]>1)m=I-1; ) /* bare king loses */ + N++; /* node count (for timing) */ + do{u=b[x]; /* scan board looking for */ + if(u&&(u&16)==k) /* own piece (inefficient!)*/ + {r=p=u&15; /* p = piece type (set r>0) */ + j=od[p]; /* first step vector f.piece*/ + W(r=o[++j]) /* loop over directions o[] */ + {A: /* resume normal after best */ + flag=h?3:of[j]; /* move modes (for fairies) */ + y=x;F=FF=G=S; /* (x,y)=move, (F,G)=castl.R*/ + do{ /* y traverses ray, or: */ + H=y=h?Y^h:y+r; /* sneak in prev. best move */ + if(flag&1<<8)H=y=(y&15)>13?y+BW:(y&15)>=BW?y-BW:y; /* cylinder board */ + if(y&S|(y&15)>=BW)break; /* board edge hit */ +#ifdef MULTIPATH + if(flag&1<<9) /* if multipath move */ + {t=flag>>12; /* get dir. stepped twice */ + if(b[x+t]){if(b[y-2*t]|b[y-t])break;}else + if(b[x+2*t]&&b[y-t])break; /* test if empty path exists*/ + } +#endif + m=E<16|(E^112)<16&&flag&1&y-E<2&E-y<2?I:m; /* bad castling */ + if(p<3&y==E)H=z&127; /* shift capt.sqr. H if e.p.*/ + t=b[H]; + if(flag&1+!t) /* mode (capt/nonc) allowed?*/ + {if(t&&(t&16)==k)break; /* capture own */ + i=w[t&15]+((t&192)>>sh); /* value of capt. piece t */ + if(i<0)m=I,d=98; /* K capture */ + if(m>=l&d>1)goto C; /* abort on fail high */ + v=d-1?e:i-p; /*** MVV/LVA scoring if d=1**/ + if(d-!t>1) /*** all captures if d=2 ***/ + {v=centr[p]?b[x+257]-b[y+257]:0; /* center positional pts. */ + b[G]=b[H]=b[x]=0;b[y]=u|32; /* do move, set non-virgin */ + if(!(G&S))b[FF]=k+6,v+=50; /* castling: put R & score */ + v-=w[p]>0|R>2); /* end-game Pawn-push bonus */ + b[y]+=V=y+r+1&S?647-p:2*(u&y+16&32); /* upgrade P or convert to Q*/ + V>>=sh; /* for Shatranj promo to F */ + i+=V; /* promotion / passer bonus */ + } if(z&S && GamePtr<6) v+=(rand()>>10&31)-16; + J+=J(0);Z+=J(4)+G-S; + SHAMAX( pl[k]-=!!t; ) /* count pieces per side */ + v+=e+i;V=m>q?m:q; /*** new eval & alpha ****/ + C=d-1-(d>5&p>2&!t&!h); /* nw depth, reduce non-cpt.*/ + C=R2|v>V?-D(16-k,-l,-V,-v,/*** futility, recursive eval. of reply */ + F,y&255,C):v; + W(s>q&++CD=99;a->V=0; /* lock game in hash as draw*/ + R-=i/FAC; /*** total captd material ***/ + Fifty = t|p<3?0:Fifty+1; + return l;} /* & not in check, signal */ + v=m; /* (prevent fail-lows on */ + } /* K-capt. replies) */ + J=f;Z=g; + SHAMAX( pl[k]+=!!t; ) + b[G]=k+6;b[FF]=b[y]=0;b[x]=u;b[H]=t; /* undo move,G can be dummy */ + } /* if non-castling */ + if(v>m) /* new best, update max,best*/ + m=v,X=x,Y=y|S&F; /* mark non-double with S */ + if(h){h=0;goto A;} /* redo after doing old best*/ + } + s=t; + if(flag&15^4|u&32|| /* no double or moved before*/ + p>2&& /* no P & no lateral K move,*/ + (b[G=r<0?x&~15:BW-1|x&112]-k-6 /* no virgin R in corner G, */ + ||b[G^1]|b[G^2]|b[FF=r<0?G+3:G-2]) /* no 2 empty sq. next to R */ + )t+=flag&4; /* fake capt. for nonsliding*/ + else F=y; /* enable e.p. */ + if(s&&flag&8)t=0,flag^=flag>>4&15; /* hoppers go to next phase */ + if(!(flag&S)) /* zig-zag piece? */ + r^=flag>>12,flag^=flag>>4&15; /* alternate vector & mode */ + }W(!t); /* if not capt. continue ray*/ + }} + if((++x&15)>=BW)x=x+16&112; /* next sqr. of board, wrap */ + }W(x-B); +C:FMAX( m=m+I|P==I?m:(X=Y=0); ) /* if stalemate, draw-score */ + if(a->D<99) /* protect game history */ + a->K=Z,a->V=m,a->D=d,a->X=X, /* always store in hash tab */ + a->F=8*(m>q)|S*(mY=Y; /* move, type (bound/exact),*/ + if(z&S&&Post){ + printf("%2d ",d-2); + printf("%6d ",m); + printf("%8d %10d %c%c%c%c\n",(GetTickCount()-Ticks)/10,N, + 'a'+(X&15),'8'-(X>>4),'a'+(Y&15),'8'-(Y>>4&7)),fflush(stdout); + }} /* encoded in X S,8 bits */ +if(z&4*S)K=X,L=Y&~S; + return m+=m 1) /* third repeat */ + { + printf("1/2-1/2 {Draw by repetition}\n"); + return 1; + } + differs: ; + } + K=I; + cnt = D(s,-I,I,Q,O,LL|4*S,3); + if(cnt>-I+1 && K==0 && L==0) { + printf("1/2-1/2 {Stalemate}\n"); + return 2; + } + if(cnt==-I+1) { + if (s == WHITE) + printf("0-1 {Black mates}\n"); + else + printf("1-0 {White mates}\n"); + return 3; + } + if(Fifty >=100) { + printf("1/2-1/2 {Draw by fifty move rule}\n"); + return 4; + } +#ifdef SHATRANJ + if(pl[s]==1 && pl[16-s]==1) { + printf("1/2-1/2 {Insufficient mating material}\n"); + return 4; + } + if(pl[s]<=1 && pl[16-s]>1) { + if (s == BLACK) + printf("0-1 {Bare King}\n"); + else + printf("1-0 {Bare King}\n"); + return 5; + } +#endif + return 0; +} + + +InitEngine() +{ + int i, j; + + N=32*S+7;W(N-->S+3)T[N]=rand()>>9; + srand(GetTickCount()); +} + +InitGame() +{ + int i,j; + + for(i=0;i<16*BH;i++)b[i]=0; + K=BW;W(K--) + {b[K]=oo[K+16]+16;b[K+112]=oo[K];b[K+16]=18;b[K+96]=1; /* initial board setup*/ + L=8;W(L--)b[16*L+K+257]=(K-BW/2)*(K-BW/2)+(L-3.5)*(L-3.5); /* center-pts table */ + } /*(in unused half b[])*/ + Side = WHITE; Q=0; O=S; + Fifty = 0; R = 0; + for(i=0; i12 || BH!=8) + { printf("telluser unsupported board size %dx%d\n",BW,BH); exit(0); } + + for(i=0; i='a'; + piecetype[c&31]=i; piecename[i]=c&31; + } + j++; o[j]=0; + /* printf("# c='%c' i=%d od[i]=%d j=%d (%3d,%8x)\n",c?c:' ',i,od[i],j,o[j-1],of[j-1]); /**/ + c=0; if(i>15 || j>255) break; + } + + fclose(f); + sh = w[7] < 250 ? 3 : 0; +} + +int main(int argc, char **argv) +{ + int Computer, MaxTime, MaxMoves, TimeInc, sec, i, j; + char line[256], command[256], c, cc; + int m, nr; + FILE *f; + + if(argc>1 && sscanf(argv[1], "%d", &m)==1) + { U = (1<1) inifile = argv[1]; + + signal(SIGINT, SIG_IGN); + printf("tellics say " NAME " " VERSION "\n"); + printf("tellics say by H.G. Muller\n"); + printf("tellics say Gothic Chess is protected by U.S. patent #6,481,716 by Ed Trice.\n"); + printf("tellics say Falcon Chess is protected by U.S. patent #5,690,334 by George W. Duke\n"); + InitEngine(); + LoadGame(NULL); + InitGame(); + Computer = EMPTY; + MaxTime = 10000; /* 10 sec */ + MaxDepth = 30; /* maximum depth of your search */ + + for (;;) { + fflush(stdout); + if (Side == Computer) { + /* think up & do move, measure time used */ + /* it is the responsibility of the engine */ + /* to control its search time based on */ + /* MovesLeft, TimeLeft, MaxMoves, TimeInc */ + /* Next 'MovesLeft' moves have to be done */ + /* within TimeLeft+(MovesLeft-1)*TimeInc */ + /* If MovesLeft<0 all remaining moves of */ + /* the game have to be done in this time. */ + /* If MaxMoves=1 any leftover time is lost*/ + Ticks = GetTickCount(); + m = MovesLeft<=0 ? 40 : MovesLeft; + tlim = (0.6-0.06*(BW-8))*(TimeLeft+(m-1)*TimeInc)/(m+7); + if(tlim>TimeLeft/15) tlim = TimeLeft/15; + PromPiece = 0; /* Always promote to Queen ourselves */ + N=0;K=I; + if (D(Side,-I,I,Q,O,LL|S,3)==I) { + Side ^= BLACK^WHITE; + if(UnderProm>=0 && UnderProm != L) + { printf("tellics I hate under-promotions!\n"); + printf("resign { underpromotion } \n"); + Computer = EMPTY; + continue; + } else UnderProm = -1; + printf("move "); + printf("%c%c%c%c",'a'+(K&15),'0'+BH-(K>>4), + 'a'+(L&15),'0'+BH-(L>>4)); + printf("\n"); + m = GetTickCount() - Ticks; + + /* time-control accounting */ + TimeLeft -= m; + TimeLeft += TimeInc; + if(--MovesLeft == 0) { + MovesLeft = MaxMoves; + if(MaxMoves == 1) + TimeLeft = MaxTime; + else TimeLeft += MaxTime; + } + + GameHistory[GamePtr++] = PACK_MOVE; + CopyBoard(HistPtr=HistPtr+1&1023); + if(Resign && Score <= -Threshold) { + printf("resign\n"); Computer=EMPTY; + } else if(PrintResult(Side)) + Computer = EMPTY; + } else { + if(!PrintResult(Side)) + printf("resign { refuses own move }\n"); + Computer = EMPTY; + } + continue; + } + if (!fgets(line, 256, stdin)) + return; + if (line[0] == '\n') + continue; + sscanf(line, "%s", command); + if (!strcmp(command, "xboard")) + continue; + if (!strcmp(command, "protover")) { + printf("feature myname=\"" NAME " " VERSION "\"\n"); + printf("feature memory=1\n"); + printf("feature setboard=0 ping=1 done=0\n"); + printf("feature variants=\""); + PrintVariants(); + printf("\"\n"); + PrintOptions(); + continue; + } + if (!strcmp(command, "ping")) { int nr=0; + sscanf(line, "ping %d", &nr); + printf("pong %d\n", nr); + continue; + } + if (!strcmp(command, "p")) { + pboard(); + continue; + } + if (!strcmp(command, "memory")) { + int mem, mask; + sscanf(line+6, "%d", &mem); mem = (mem*1024*1024)/12; // max nr of hash entries + mask = 0x7FFFFFFF; while(mask > mem) mask >>= 1; + if(mask != U) { + free(A); U = mask; + A = (struct _ *) calloc(U+1, sizeof(struct _)); + } + continue; + } + if (!strcmp(command, "new")) { + /* start new game */ + LoadGame("normal"); + InitGame(); + GamePtr = Setup = 0; + GameNr++; + HistPtr = 0; + Computer = BLACK; + TimeLeft = MaxTime; + MovesLeft = MaxMoves; + for(nr=0; nr<1024; nr++) + for(m=0; mK = 0; + continue; + } + if (!strcmp(command, "go")) { + /* set computer to play current side to move */ + Computer = Side; + MovesLeft = -(GamePtr+(Side==WHITE)>>1); + while(MaxMoves>0 && MovesLeft<=0) + MovesLeft += MaxMoves; + continue; + } + if (!strcmp(command, "hint")) { + Ticks = GetTickCount(); tlim = 1000; + D(Side,-I,I,Q,O,LL|4*S,6); + if (K==0 && L==0) + continue; + printf("Hint: "); + printf("%c%c%c%c",'a'+(K&7),'8'-(K>>4), + 'a'+(L&7),'8'-(L>>4)); + printf("\n"); + continue; + } + if (!strcmp(command, "undo") && (nr=1) || + !strcmp(command, "remove") && (nr=2) ) { + /* 'take back' moves by replaying game */ + /* from history until desired ply */ + if (GamePtr - nr < 0) + continue; + GamePtr -= nr; + HistPtr -= nr; /* erase history boards */ + while(nr-- > 0) + for(m=0; m= 'A' && m <= 'Z' && piecetype[m&31] + && line[1] >= 'a' && line[1] <= 'a'+BW-1 + && line[2] >= '1' && line[2] <= '0'+BH) { + m = line[1]-16*line[2]+799; + switch(p = piecetype[line[0]&31]) + { + case 1: + case 2: + if(color==WHITE) + b[m]=(m&0x70)==0x60?1:33, + Q+=w[1]; + else b[m]=(m&0x70)==0x10?18:50, + Q+=w[2]; + break; + case 3: // can castle, normally King + b[m]=3+color+32; + if(m==BW>>1 && color==BLACK || + m==0x70+(BW>>1) && color==WHITE) + b[m] -= 32; + break; + case 6: // can castle, normally Rook + b[m]=6+color+32; + if((m==0x00 || m==BW-1 ) && color==BLACK || + (m==0x70 || m==0x6F+BW) && color==WHITE) + b[m] -= 32; + Q+=w[6]; R+=w[6]/FAC; + break; + default: + b[m]=p+color; + Q+=w[p]; R+=w[p]/FAC; + case 0: // undefined piece, ignore + break; + } + pl[BLACK+WHITE-color]++; + continue; + } + } + if(Side != color) Q = -Q; + GamePtr = HistPtr = 0; Setup = 1; SetupQ = Q; // start anew + for(i=0; i<128; i++) setupPosition[i] = b[i]; // remember position + setupPosition[128] = Side; + setupPosition[129] = pl[WHITE]; + setupPosition[130] = pl[BLACK]; + continue; + } + /* command not recognized, assume input move */ + m = line[0]<'a' | line[0]>='a'+BW | line[1]<'1' | line[1]>='1'+BH | + line[2]<'a' | line[2]>='a'+BW | line[3]<'1' | line[3]>='1'+BH; + if(line[4] == '\n') line[4] = piecename[7]; + PromPiece = 7 - piecetype[line[4]&31]; + if(PromPiece == 7) PromPiece = 0; + {char *c=line; K=c[0]-16*c[1]+799;L=c[2]-16*c[3]+799; } + if (m) + /* doesn't have move syntax */ + printf("Error (unknown command): %s\n", command); + else if(D(Side,-I,I,Q,O,LL|S,3)!=I) { + /* did have move syntax, but illegal move */ + printf("Illegal move:%s\n", line); + } else { /* legal move, perform it */ + GameHistory[GamePtr++] = PACK_MOVE; + Side ^= BLACK^WHITE; + CopyBoard(HistPtr=HistPtr+1&1023); + if(PrintResult(Side)) Computer = EMPTY; + } + } +} diff --git a/fmax.6.pod b/fmax.6.pod new file mode 100644 index 0000000..5319169 --- /dev/null +++ b/fmax.6.pod @@ -0,0 +1,119 @@ +=head1 NAME + +fairymax - xboard-compatible chess and chess-variant engine 'Fairy-Max' + + +=head1 SYNOPSIS + +B [hashSize] [iniFile] + +B [hashSize] [iniFile] + +B [hashSize] [iniFile] + + +=head1 DESCRIPTION + +B is a program that plays chess and chess variants. +It uses the xboard/winboard chess-engine protocol to communicate. +Apart from 'regular' chess (also known as the Mad-Queen variant), +it can play Capablanca chess, gothic chess, knightmate, cylinder chess, +berolina chess, superchess and courier chess. +Fairy-Max can be easily configured by the user to play other variants as well, +by modifying the ini file. +This ini file describes the rules of movement +of the participating pieces and the initial board setup. + +Fairy-Max can also play shatranj, +but in this case is not aware of the shatranj rule that a bare king loses. +So it might play sub-optimally in the late end-game. +A version of Fairy-Max adapted to implement the baring rule is +available under the name B. + +Similarly, a version of Fairy-Max adapted to play Xiang Qi (Chinese Chess) +is included in the fmax package as well. + +B is a derivative of the world's (once) smallest chess program +(source-code wise), micro-Max. +The latter measures less that 2000 characters, (about 100 code lines), +and has a computer rating of around 2050 on the CCRL rating list. +Although this is about 1000 rating points behind the world champion, +micro-Max still makes a quite tough opponent even for club players, +although it is not unbeatable. + +The main difference between micro-Max and Fairy-Max is that the latter loads +its move-generator tables, which specify how the various pieces move, +from an external file, so it can be easily adapted to incorporate un-orthodox pieces. +For ease of use of the artificial-intelligence, Fairy-Max is equipped with +I/O routines that allow it to run with the xboard graphical user interface. + +See xboard(6) for instructions about how to use B through xboard. To +start up quickly, you just need the command: B. +However, XBoard might not support symbols for every unorthodox piece in board sizes +different from B, B and B. +It might thius be adviasable to specify a board size as well, e.g. +B +to get correct display of the elephant and general pieces in shatranj. +Note that to be able to play the chess variants, +you will need xboard 4.3.14 or later. + +The fmax.ini file from which Fairy-Max by default takes the piece and game definitions +is a self-documenting text file, +which contains instructions for how to define new pieces and chess variants. +In addition it contains an extensive list of pre-defined pieces, +incuding many not occurring in any of the pre-defined variants, +which the user can draw on to define his own variants. + +Amongst the move types supported by Fairy-Max are normal leaper and slider moves, +(e.g. knight and rook), +divergent moves (i.e. capture and non-capture moves can be different) +hoppers (which jump over other pieces, such as the Chinese cannon or the grasshopper), +lame leapers (the move of which can be blocked on squares they cannot move to, +such as the Chinese horse and elephant), +and any combination thereof, +in every possible direction. +The board width is configurable upto a width of 14 squares, +and cylindrical boards (where left and right edge connect) are supported as well. + +=head1 OPTIONS + +=over 8 + +=item B + +If the first argument to fairymax is numeric, +it is taken as an indicator for the amount of memory Fairy-Max is allowed to use +for its internal hash table. +The default value for this argument, 22, would result in a memory usage of 48MB. +Each next-higher number doubles the memory usage, each next-lower halves it. +Running with less than 6MB (i.e. argument 19) is not recommended. +When fairymax is running under xboard 4.3.15 the hash-table size can be set +through the xboard menus, +making this argument superfluous. + +=item B + +A second or non-numeric first argument is taken as a filename. +Fairy-max will use the mentioned file in stead of its default fmax.ini file +to define the movement of pieces and initial setup of the variants. +This makes it easier to define your own variants. + +=back + + +=head1 SEE ALSO + +xboard(6) + +http://www.chessvariants.org/index/msdisplay.php?itemid=MSfairy-max + +http://home.hccnet.nl/h.g.muller/max-src2.html + +http://www.open-aurec.com/wbforum/viewtopic.php?t=49439 + + +=head1 AUTHOR + +B was written by H.G.Muller . + +This manual page was generated with pod2man(1). diff --git a/maxqi.c b/maxqi.c new file mode 100644 index 0000000..9b15595 --- /dev/null +++ b/maxqi.c @@ -0,0 +1,791 @@ +/***************************************************************************/ +/* MaxQi, */ +/* Version of the sub-2KB (source) micro-Max Chess program, fused to a */ +/* generic WinBoard interface, loading its move-generator tables from file */ +/* Adapted to play Xiang Qi, which required rather specialized changes */ +/***************************************************************************/ +/* micro-Max version 4.8 (~1950 characters) features: */ +/* - recursive negamax search */ +/* - all-capture quiescence search with MVV/LVA priority */ +/* - (internal) iterative deepening */ +/* - best-move-first 'sorting' */ +/* - a hash table storing score and best move */ +/* - futility pruning */ +/* - king safety through magnetic frozen king */ +/* - null-move pruning */ +/* - Late-move reductions */ +/* - full FIDE rules (expt minor promotion) and move-legality checking */ +/* - keep hash + rep-draw detect */ +/* - end-game Pawn-push bonus, new piece values, gradual promotion */ +/***************************************************************************/ +/* This version reads the piece description from a file fmax.ini */ +/* The format supports many fairy pieces, including hoppers. */ +/* f) now supports 15 piece types, by requisitioning WHITE bit */ +/* g) supports larger board width. */ +/* h) castling bug ('in-check by non-captures') corrected */ +/* i) rep-draw bug ('side-to-move') corrected */ +/* k) allow user underpromotions, recognize & ignore 'variant' command */ +/* l) edit bug corrected (i & j file clear) */ +/* m) piece values no longer quantized, game-stage counting bug corrected */ +/* n) edit-menu K-side castling bug corrected. */ +/* o) retrieve the requested variant from the .ini file */ +/* p) clear hash table on variant switch */ +/* q) reduced piece-material count for better Pawn push */ +/* r) hash-table bug corrected (X still ORed with flags) */ +/* s) Bug that prevented initialization center points corrected */ +/* t) castling bug after edit fixed */ +/* u) converted to protocol 2; ping implemented */ +/* v) white e.p. rights hash bug fixed; */ +/* w) piece indicators programable, multi-path support */ +/* x) e.p. changed to support Berolina Pawns */ +/* y) capture vlue of 6-7th-rank Pawn reduced in Shatranj */ +/* z) bug in promotion input corrected */ +/* A) stalemate-detection bug in printResult fixed */ +/* B) Invalidate hash on game-level promotion (might be under-promotion!) */ +/* C) King move evaluation based on negative piece value in stead of nr */ +/* D) WB memory command added, undo fixed */ +/* E) 15th piece read in */ +/* F) accepts ini fileargument */ +/* Xiang Qi adaptations: */ +/* orient board sideways for easy implementation of King-facing rule */ +/* allow board to have 9 rows (= files) */ +/* add array for specifying board zones */ +/* add zone limiter for each piece */ +/* change promotion code to act when crossing river */ +/* remove stalemate code */ +/* G) o[] and oo[] made int, to work on big-endian machines */ +/***************************************************************************/ + + /*****************************************************************/ + /* LICENCE NOTIFICATION */ + /* Fairy-Max 4.8 is free software, and you have my permission do */ + /* with it whatever you want, whether it is commercial or not. */ + /* Note, however, that Fairy-Max can easily be configured through*/ + /* its fmax.ini file to play Chess variants that are legally pro-*/ + /* tected by patents, and that to do so would also require per- */ + /* mission of the holders of such patents. No guarantees are */ + /* given that Fairy-Max does anything in particular, or that it */ + /* would not wreck the hardware it runs on, and running it is */ + /* entirely for your own risk. H.G,Muller, author of Fairy-Max */ + /*****************************************************************/ + + +#define MULTIPATH + +/* fused to generic Winboard driver */ + +#include +#include +#include +#include +#include + +#ifndef WIN32 + +#include +int GetTickCount() // with thanks to Tord +{ struct timeval t; + gettimeofday(&t, NULL); + return t.tv_sec*1000 + t.tv_usec/1000; +} +#ifndef QINI_FILE +#define QINI_FILE "qmax.ini" +#endif + +#else + +#include +#define QINI_FILE "qmax.ini" + +#endif + +int StartKey; + +#define EMPTY -1 +#define WHITE 0 +#define BLACK 16 + +#define STATE 128 + +#define FAC 128 + +/* make unique integer from engine move representation */ +#define PACK_MOVE 256*K + L; + +/* convert intger argument back to engine move representation */ +#define UNPACK_MOVE(A) K = (A)>>8 & 255; L = (A) & 255; + +/* Global variables visible to engine. Normally they */ +/* would be replaced by the names under which these */ +/* are known to your engine, so that they can be */ +/* manipulated directly by the interface. */ + +int Side; +int Move; +int PromPiece; +int Result; +int TimeLeft; +int MovesLeft; +int MaxDepth; +int Post; +int Fifty; +int UnderProm; +int GameNr; +char piecename[32], piecetype[32], defaultchar[]=".PPKNBRQEWFMACHG"; +char *inifile = QINI_FILE; + +int Ticks, tlim, Setup, SetupQ; + +int GameHistory[1024]; +char HistoryBoards[1024][STATE], setupPosition[129]; +int GamePtr, HistPtr; + +#define W while +#define K(A,B) *(int*)(T+A+((B&31)<<8)) +#define J(A) K(y+A,b[y])-K(x+A,u)-K(y+A,t) + +int U=(1<<23)-1; +struct _ {int K,V;char X,Y,D,F;} *A; /* hash table, 16M+8 entries*/ + +int M=136,S=128,I=8e3,Q,O,K,N,j,R,J,Z,LL,L, /* M=0x88 */ +BW,BH,sh, +w[16]={0,2,2,-1,7,8,12,23,7,5}, /* relative piece values */ +o[256], +oo[32], /* initial piece setup */ +of[256], +od[16]; /* 1st dir. in o[] per piece*/ + +signed char +b[513], /* board: 16x8+dummy, + PST */ +T[8200], /* hash translation table */ +centr[32], +n[]=".P*KEEQQAHCR????x+pkeeqqahcr????"; /* piece symbols on printout*/ + +char zn[] = { /* zones of xiangqi board */ +1,1,1,1,1,2,2,2,2,2, 0,0,0,0,0,0, +1,1,1,1,1,2,2,2,2,2, 0,0,0,0,0,0, +1,1,1,1,1,2,2,2,2,2, 0,0,0,0,0,0, +0,0,0,1,1,2,2,0,0,0, 0,0,0,0,0,0, +0,0,0,1,1,2,2,0,0,0, 0,0,0,0,0,0, +0,0,0,1,1,2,2,0,0,0, 0,0,0,0,0,0, +1,1,1,1,1,2,2,2,2,2, 0,0,0,0,0,0, +1,1,1,1,1,2,2,2,2,2, 0,0,0,0,0,0, +1,1,1,1,1,2,2,2,2,2, 0,0,0,0,0,0 +}; + +pboard() +{int i; + i=-1;W(++i<144)printf(" %c",(i&15)==BW&&(i+=15-BW)?10:n[b[i]&31]); +} + +D(k,q,l,e,z,n) /* recursive minimax search, k=moving side, n=depth*/ +int k,q,l,e,z,n; /* (q,l)=window, e=current eval. score, E=e.p. sqr.*/ +{ /* e=score, z=prev.dest; J,Z=hashkeys; return score*/ + int j,r,m,v,d,h,i,P,V,f=J,g=Z,C,s,flag,F; + unsigned char t,p,u,x,y,X,Y,B,lu; + struct _*a=A+(J+k&U-1); /* lookup pos. in hash table*/ + q-=qD;m=a->V;F=a->F; /* resume at stored depth */ + X=a->X;Y=a->Y; /* start at best-move hint */ +if(z&S&&a->K==Z)printf("# root hit %d %d %x\n",a->D,a->V,a->F); + if(a->K-Z|z&S | /* miss: other pos. or empty*/ + !(m<=q|F&8&&m>=l|F&S)) /* or window incompatible */ + d=X=0,Y=-1; /* start iter. from scratch */ + W(d++2&&l+I?D(16-k,-l,1-l,-e,2*S,d-3):I; /* search null move */ + m=-P beta unconsidered:static eval */ + N++; /* node count (for timing) */ + do{u=b[x]; /* scan board looking for */ + if(u)m=lu|u&15^3?m:(d=98,I),lu=u&15^3; /* Kings facing each other */ + if(u&&(u&16)==k) /* own piece (inefficient!)*/ + {r=p=u&15; /* p = piece type (set r>0) */ + j=od[p]; /* first step vector f.piece*/ + W(r=o[++j]) /* loop over directions o[] */ + {A: /* resume normal after best */ + flag=h?3:of[j]; /* move modes (for fairies) */ + y=x; /* (x,y)=move */ + do{ /* y traverses ray, or: */ + y=h?Y:y+r; /* sneak in prev. best move */ + if(y>=16*BH|(y&15)>=BW)break; /* board edge hit */ + t=b[y]; /* captured piece */ + if(flag&1+!t) /* mode (capt/nonc) allowed?*/ + {if(t&&(t&16)==k||flag>>10&zn[y])break; /* capture own or bad zone */ + i=w[t&15]; /* value of capt. piece t */ + if(i<0)m=I,d=98; /* K capture */ + if(m>=l&d>1)goto C; /* abort on fail high */ + v=d-1?e:i-p; /*** MVV/LVA scoring if d=1**/ + if(d-!t>1) /*** all captures if d=2 ***/ + {v=centr[p]?b[x+257]-b[y+257]:0; /* center positional pts. */ + b[x]=0;b[y]=u; /* do move */ + v-=w[p]>0|R<10?0:20; /*** freeze K in mid-game ***/ + if(p<3) /* pawns: */ + {v+=2; /* end-game Pawn-push bonus */ + if(zn[x]-zn[y])b[y]+=5, /* upgrade Pawn and */ + i+=w[p+5]-w[p]; /* promotion bonus */ + } + if(z&S && GamePtr<6) v+=(rand()>>10&31)-16; // randomize in root + J+=J(0);Z+=J(4); + v+=e+i;V=m>q?m:q; /*** new eval & alpha ****/ + C=d-1-(d>5&p>2&!t&!h); /* nw depth, reduce non-cpt.*/ + C=R<10|P-I|d<3||t&&p-3?C:d; /* extend 1 ply if in-check */ + do + s=C>2|v>V?-D(16-k,-l,-V,-v,/*** futility, recursive eval. of reply */ + 0,C):v; + W(s>q&++CD=99;a->V=500; /* lock game in hash as loss*/ + R-=i/FAC; /*** total captd material ***/ + Fifty = t|p<3?0:Fifty+1; + return l;} /* & not in check, signal */ + v=m; /* (prevent fail-lows on */ + } /* K-capt. replies) */ + J=f;Z=g; + b[y]=t;b[x]=u; /* undo move */ + } /* if non-castling */ + if(v>m) /* new best, update max,best*/ + m=v,X=x,Y=y; /* no marking! */ + if(h){h=0;goto A;} /* redo after doing old best*/ + } + s=t; + t+=flag&4; /* fake capt. for nonsliding*/ + if(s&&flag&8)t=0,flag^=flag>>4&15; /* hoppers go to next phase */ + if(!(flag&S)) /* zig-zag piece? */ + r^=flag>>12,flag^=flag>>4&15; /* alternate vector & mode */ + }W(!t); /* if not capt. continue ray*/ + }} + if((++x&15)>=BW)x=x+16&240,lu=1; /* next sqr. of board, wrap */ + if(x>=16*BH)x=0; + }W(x-B); +C:if(a->D<99) /* protect game history */ + a->K=Z,a->V=m,a->D=d,a->X=X, /* always store in hash tab */ + a->F=8*(m>q)|S*(mY=Y; /* move, type (bound/exact),*/ +if(z&S&&Post){ + printf("%2d ",d-2); + printf("%6d ",m); + printf("%8d %10d %c%c%c%c\n",(GetTickCount()-Ticks)/10,N, + 'i'-(X>>4&15),'9'-(X&15),'i'-(Y>>4&15),'9'-(Y&15)),fflush(stdout);} + } /* encoded in X S,8 bits */ +if(z&4*S)K=X,L=Y&~S; + return m+=m 1) /* third repeat */ + { + printf("1/2-1/2 {Draw by repetition}\n"); + return 1; + } + differs: ; + } + K=I; + cnt = D(s,-I,I,Q,4*S,3); + if(cnt==0 && K==0 && L==0) { + printf("1/2-1/2 {Stalemate}\n"); + return 2; + } + if(cnt==-I+1) { + if (s == WHITE) + printf("0-1 {Black mates}\n"); + else + printf("1-0 {White mates}\n"); + return 3; + } + if(Fifty >=100) { + printf("1/2-1/2 {Draw by fifty move rule}\n"); + return 4; + } + return 0; +} + + +InitEngine() +{ + int i, j; + + N=8100;W(N-->256)T[N]=rand()>>9; + srand(GetTickCount()); +} + +InitGame() +{ + int i,j; + + for(i=0;i<16*BH;i++)b[i]=0; /* clear board */ + b[23]=b[119]=10;b[BW+8]=b[BW+104]=26; /* place Cannons */ + K=BH;W(K--) + {b[16*K]=oo[K+16]+16;b[16*K+BW-1]=oo[K];if(!(K&1))b[16*K+BW-7]=18,b[16*K+6]=1; /* initial board setup*/ + L=BW;W(L--)b[L+16*K+257]=(K-(BW-1)/2.)*(K-(BW-1)/2.)+(L-(BH-1)/2.)*(L-(BH-1)/2.); /* center-pts table */ + } /*(in unused half b[])*/ + Side = WHITE; Q=0; O=S; + Fifty = 0; R = 0; + for(i=0; i12 || BH>9) + { printf("telluser unsupported board size %dx%d\n",BW,BH); exit(0); } + + for(i=0; i='a'; + piecetype[c&31]=i; piecename[i]=c&31; + } + j++; o[j]=0; + /* printf("tell c='%c' i=%d od[i]=%d j=%d (%3d,%8x)\n",c,i,od[i],j,o[j-1],of[j-1]); /**/ + c=0; if(i>15 || j>255) break; + } + + fclose(f); + sh = w[7] < 250 ? 3 : 0; +} + +int main(int argc, char **argv) +{ + int Computer, MaxTime, MaxMoves, TimeInc, sec, i, j; + char line[256], command[256], c, cc; + int m, nr; + FILE *f; + + if(argc>1 && sscanf(argv[1], "%d", &m)==1) + { U = (1<1) inifile = argv[1]; + + signal(SIGINT, SIG_IGN); + printf("tellics say MaxQi 4.8 (G)\n"); + printf("tellics say by H.G. Muller\n"); + InitEngine(); + LoadGame(NULL); + InitGame(); + Computer = EMPTY; + MaxTime = 10000; /* 10 sec */ + MaxDepth = 30; /* maximum depth of your search */ + + for (;;) { + fflush(stdout); + if (Side == Computer) { + /* think up & do move, measure time used */ + /* it is the responsibility of the engine */ + /* to control its search time based on */ + /* MovesLeft, TimeLeft, MaxMoves, TimeInc */ + /* Next 'MovesLeft' moves have to be done */ + /* within TimeLeft+(MovesLeft-1)*TimeInc */ + /* If MovesLeft<0 all remaining moves of */ + /* the game have to be done in this time. */ + /* If MaxMoves=1 any leftover time is lost*/ + Ticks = GetTickCount(); + m = MovesLeft<=0 ? 40 : MovesLeft; + tlim = (0.6-0.06*(BW-8))*(TimeLeft+(m-1)*TimeInc)/(m+7); + if(tlim>TimeLeft/15) tlim = TimeLeft/15; + PromPiece = 0; /* Always promote to Queen ourselves */ + N=0;K=I; + if (D(Side,-I,I,Q,S,3)==I) { + Side ^= BLACK^WHITE; + if(UnderProm>=0 && UnderProm != L) + { printf("tellics I hate under-promotions!\n"); + printf("resign { underpromotion } \n"); + Computer = EMPTY; + continue; + } else UnderProm = -1; + printf("move "); + printf("%c%c%c%c",'i'-(K>>4),'9'-(K&15), + 'i'-(L>>4&15),'9'-(L&15)); + printf("\n"); + m = GetTickCount() - Ticks; + + /* time-control accounting */ + TimeLeft -= m; + TimeLeft += TimeInc; + if(--MovesLeft == 0) { + MovesLeft = MaxMoves; + if(MaxMoves == 1) + TimeLeft = MaxTime; + else TimeLeft += MaxTime; + } + + GameHistory[GamePtr++] = PACK_MOVE; + CopyBoard(HistPtr=HistPtr+1&1023); + if(PrintResult(Side)) + Computer = EMPTY; + } else { + if(!PrintResult(Side)) + printf("resign { refuses own move }\n"); + Computer = EMPTY; + } + continue; + } + if (!fgets(line, 256, stdin)) + return; + if (line[0] == '\n') + continue; + sscanf(line, "%s", command); + if (!strcmp(command, "xboard")) + continue; + if (!strcmp(command, "protover")) { + printf("feature myname=\"MaxQi 4.8G\"\n"); + printf("feature memory=1\n"); + printf("feature smp=1\n"); + printf("feature setboard=0 ping=1 done=0\n"); + printf("feature variants=\""); + PrintVariants(); + printf("\" done=1\n"); + continue; + } + if (!strcmp(command, "ping")) { int nr=0; + sscanf(line, "ping %d", &nr); + printf("pong %d\n", nr); + continue; + } + if (!strcmp(command, "p")) { + pboard(); + continue; + } + if (!strcmp(command, "memory")) { + int mem, mask; + sscanf(line+6, "%d", &mem); mem = (mem*1024*1024)/12; // max nr of hash entries + mask = 0x7FFFFFFF; while(mask > mem) mask >>= 1; + if(mask != U) { + free(A); U = mask; + A = (struct _ *) calloc(U+1, sizeof(struct _)); + } + continue; + } + if (!strcmp(command, "new")) { + /* start new game */ + LoadGame("xiangqi"); + InitGame(); + GamePtr = Setup = 0; + GameNr++; + HistPtr = 0; + Computer = BLACK; + TimeLeft = MaxTime; + MovesLeft = MaxMoves; + for(nr=0; nr<1024; nr++) + for(m=0; m>1); + while(MaxMoves>0 && MovesLeft<=0) + MovesLeft += MaxMoves; + continue; + } + if (!strcmp(command, "hint")) { + Ticks = GetTickCount(); tlim = 1000; + D(Side,-I,I,Q,4*S,6); + if (K==0 && L==0) + continue; + printf("Hint: "); + printf("%c%c%c%c",'a'+(K&7),'8'-(K>>4), + 'a'+(L&7),'8'-(L>>4)); + printf("\n"); + continue; + } + if (!strcmp(command, "undo") && (nr=1) || + !strcmp(command, "remove") && (nr=2) ) { + /* 'take back' moves by replaying game */ + /* from history until desired ply */ + if (GamePtr - nr < 0) + continue; + GamePtr -= nr; + HistPtr -= nr; /* erase history boards */ + while(nr-- > 0) + for(m=0; m= 'A' && m <= 'Z' + && line[1] >= 'a' && line[1] <= 'a'+BH-1 + && line[2] >= '0' && line[2] <= '0'+BW-1) { + m = 16*('i'-line[1])+'9'-line[2]; + p = 4; /* Elephant code */ + switch(line[0]) + { + case 'K': + b[m]=3+color; + break; + case 'R': p++; + case 'C': p++; + case 'H': p++; + case 'A': p+=4; + b[m]=p+color; + Q+=w[p]; R+=w[p]/FAC; + break; + case 'P': + if((color==WHITE) == ((m&15)<5)) + p = 6; else p = 1; + case 'E': p+=(color==BLACK); + b[m]=p+color; + Q+=w[p]; R+=w[p]/FAC; + break; + } + continue; + } + } + if(Side != color) Q = -Q; + GamePtr = HistPtr = 0; Setup = 1; SetupQ = Q; // start anew + for(i=0; i<128; i++) setupPosition[i] = b[i]; // remember position + setupPosition[128] = Side; + continue; + } + /* command not recognized, assume input move */ + m = line[0]<'a' | line[0]>='a'+BH | line[1]<'0' | line[1]>='0'+BW | + line[2]<'a' | line[2]>='a'+BH | line[3]<'0' | line[3]>='0'+BW | + line[4] != '\n'; + {char *c=line; K=16*('i'-c[0])+'9'-c[1]; + L=16*('i'-c[2])+'9'-c[3]; } + if (m) + /* doesn't have move syntax */ + printf("Error (unknown command): %s\n", command); + else if(D(Side,-I,I,Q,S,3)!=I) { + /* did have move syntax, but illegal move */ + printf("Illegal move:%s\n", line); + } else { /* legal move, perform it */ + GameHistory[GamePtr++] = PACK_MOVE; + Side ^= BLACK^WHITE; + CopyBoard(HistPtr=HistPtr+1&1023); + if(PrintResult(Side)) Computer = EMPTY; + } + } +} + + + -- 1.7.0.4