smallchesslib.h 92 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696
  1. #ifndef SMALLCHESSLIB_H
  2. #define SMALLCHESSLIB_H
  3. /**
  4. @file smallchesslib.h
  5. Small and simple single header C99 public domain chess library and engine.
  6. author: Miloslav Ciz (drummyfish)
  7. license: CC0 1.0 (public domain)
  8. found at https://creativecommons.org/publicdomain/zero/1.0/
  9. + additional waiver of all IP
  10. version: 0.8d
  11. Default notation format for this library is a coordinate one, i.e.
  12. squarefrom squareto [promotedpiece]
  13. e.g.: e2e4 or A2A1q
  14. This work's goal is to never be encumbered by any exclusive intellectual
  15. property rights. The work is therefore provided under CC0 1.0 + additional
  16. WAIVER OF ALL INTELLECTUAL PROPERTY RIGHTS that waives the rest of
  17. intellectual property rights not already waived by CC0 1.0. The WAIVER OF ALL
  18. INTELLECTUAL PROPERTY RGHTS is as follows:
  19. Each contributor to this work agrees that they waive any exclusive rights,
  20. including but not limited to copyright, patents, trademark, trade dress,
  21. industrial design, plant varieties and trade secrets, to any and all ideas,
  22. concepts, processes, discoveries, improvements and inventions conceived,
  23. discovered, made, designed, researched or developed by the contributor either
  24. solely or jointly with others, which relate to this work or result from this
  25. work. Should any waiver of such right be judged legally invalid or
  26. ineffective under applicable law, the contributor hereby grants to each
  27. affected person a royalty-free, non transferable, non sublicensable, non
  28. exclusive, irrevocable and unconditional license to this right.
  29. */
  30. #include <stdint.h>
  31. #ifndef SCL_DEBUG_AI
  32. /** AI will print out a Newick-like tree of searched moves. */
  33. #define SCL_DEBUG_AI 0
  34. #endif
  35. /**
  36. Maximum number of moves a chess piece can have (a queen in the middle of the
  37. board).
  38. */
  39. #define SCL_CHESS_PIECE_MAX_MOVES 25
  40. #define SCL_BOARD_SQUARES 64
  41. typedef uint8_t (*SCL_RandomFunction)(void);
  42. #if SCL_COUNT_EVALUATED_POSITIONS
  43. uint32_t SCL_positionsEvaluated = 0; /**< If enabled by
  44. SCL_COUNT_EVALUATED_POSITIONS, this
  45. will increment with every
  46. dynamically evaluated position (e.g.
  47. when AI computes its move). */
  48. #endif
  49. #ifndef SCL_CALL_WDT_RESET
  50. #define SCL_CALL_WDT_RESET 0 /**< Option that should be enabled on some
  51. Arduinos. If 1, call to watchdog timer
  52. reset will be performed during dynamic
  53. evaluation (without it if AI takes long the
  54. program will reset). */
  55. #endif
  56. /**
  57. Returns a pseudorandom byte. This function has a period 256 and returns each
  58. possible byte value exactly once in the period.
  59. */
  60. uint8_t SCL_randomSimple(void);
  61. void SCL_randomSimpleSeed(uint8_t seed);
  62. /**
  63. Like SCL_randomSimple, but internally uses a 16 bit value, so the period is
  64. 65536.
  65. */
  66. uint8_t SCL_randomBetter(void);
  67. void SCL_randomBetterSeed(uint16_t seed);
  68. #ifndef SCL_EVALUATION_FUNCTION
  69. /**
  70. If defined, AI will always use the static evaluation function with this
  71. name. This helps avoid pointers to functions and can be faster but the
  72. function can't be changed at runtime.
  73. */
  74. #define SCL_EVALUATION_FUNCTION
  75. #undef SCL_EVALUATION_FUNCTION
  76. #endif
  77. #ifndef SCL_960_CASTLING
  78. /**
  79. If set, chess 960 (Fisher random) castling will be considered by the library
  80. rather than normal castling. 960 castling is slightly different (e.g.
  81. requires the inital rook positions to be stored in board state). The
  82. castling move is performed as "capturing own rook".
  83. */
  84. #define SCL_960_CASTLING 0
  85. #endif
  86. #ifndef SCL_ALPHA_BETA
  87. /**
  88. Turns alpha-beta pruning (AI optimization) on or off. This can gain
  89. performance and should normally be turned on. AI behavior should not
  90. change at all.
  91. */
  92. #define SCL_ALPHA_BETA 1
  93. #endif
  94. /**
  95. A set of game squares as a bit array, each bit representing one game square.
  96. Useful for representing e.g. possible moves. To easily iterate over the set
  97. use provided macros (SCL_SQUARE_SET_ITERATE, ...).
  98. */
  99. typedef uint8_t SCL_SquareSet[8];
  100. #define SCL_SQUARE_SET_EMPTY {0, 0, 0, 0, 0, 0, 0, 0}
  101. void SCL_squareSetClear(SCL_SquareSet squareSet);
  102. void SCL_squareSetAdd(SCL_SquareSet squareSet, uint8_t square);
  103. uint8_t SCL_squareSetContains(const SCL_SquareSet squareSet, uint8_t square);
  104. uint8_t SCL_squareSetSize(const SCL_SquareSet squareSet);
  105. uint8_t SCL_squareSetEmpty(const SCL_SquareSet squareSet);
  106. /**
  107. Returns a random square from a square set.
  108. */
  109. uint8_t SCL_squareSetGetRandom(const SCL_SquareSet squareSet,
  110. SCL_RandomFunction randFunc);
  111. #define SCL_SQUARE_SET_ITERATE_BEGIN(squareSet) \
  112. { uint8_t iteratedSquare = 0;\
  113. uint8_t iterationEnd = 0;\
  114. for (int8_t _i = 0; _i < 8 && !iterationEnd; ++_i) {\
  115. uint8_t _row = squareSet[_i];\
  116. if (_row == 0) { iteratedSquare += 8; continue; }\
  117. \
  118. for (uint8_t _j = 0; _j < 8 && !iterationEnd; ++_j) {\
  119. if (_row & 0x01) {
  120. /*
  121. Between SCL_SQUARE_SET_ITERATE_BEGIN and _END iteratedSquare variable
  122. represents the next square contained in the set. To break out of the
  123. iteration set iterationEnd to 1.
  124. */
  125. #define SCL_SQUARE_SET_ITERATE_END }\
  126. _row >>= 1;\
  127. iteratedSquare++;}\
  128. } /*for*/ }
  129. #define SCL_SQUARE_SET_ITERATE(squareSet,command)\
  130. SCL_SQUARE_SET_ITERATE_BEGIN(squareSet)\
  131. { command }\
  132. SCL_SQUARE_SET_ITERATE_END
  133. #define SCL_BOARD_STATE_SIZE 69
  134. /**
  135. Represents chess board state as a string in this format:
  136. - First 64 characters represent the chess board (A1, B1, ... H8), each field
  137. can be either a piece (PRNBKQprnbkq) or empty ('.'). I.e. the state looks
  138. like this:
  139. 0 (A1) RNBQKBNR
  140. PPPPPPPP
  141. ........
  142. ........
  143. ........
  144. ........
  145. pppppppp
  146. rnbqkbnr 63 (H8)
  147. - After this more bytes follow to represent global state, these are:
  148. - 64: bits holding en-passant and castling related information:
  149. - bits 0-3 (lsb): Column of the pawn that can, in current turn, be
  150. taken by en-passant (0xF means no pawn can be taken this way).
  151. - bit 4: Whether white is not prevented from short castling by previous
  152. king or rook movement.
  153. - bit 5: Same as 4, but for long castling.
  154. - bit 6: Same as 4, but for black.
  155. - bit 7: Same as 4, but for black and long castling.
  156. - 65: Number saying the number of ply (half-moves) that have already been
  157. played, also determining whose turn it currently is.
  158. - 66: Move counter used in the 50 move rule, says the number of ply since
  159. the last pawn move or capture.
  160. - 67: Extra byte, left for storing additional info in variants. For normal
  161. chess this byte should always be 0.
  162. - 68: The last byte is always 0 to properly terminate the string in case
  163. someone tries to print it.
  164. - The state is designed so as to be simple and also print-friendly, i.e. you
  165. can simply print it with line break after 8 characters to get a human
  166. readable representation of the board.
  167. NOTE: there is a much more compact representation which however trades some
  168. access speed which would affect the AI performance and isn't print friendly,
  169. so we don't use it. In it each square takes 4 bits, using 15 out of 16
  170. possible values (empty square and W and B pieces including 2 types of pawns,
  171. one "en-passant takeable"). Then only one extra byte needed is for castling
  172. info (4 bits) and ply count (4 bits).
  173. */
  174. typedef char SCL_Board[SCL_BOARD_STATE_SIZE];
  175. #define SCL_BOARD_ENPASSANT_CASTLE_BYTE 64
  176. #define SCL_BOARD_PLY_BYTE 65
  177. #define SCL_BOARD_MOVE_COUNT_BYTE 66
  178. #define SCL_BOARD_EXTRA_BYTE 67
  179. #if SCL_960_CASTLING
  180. #define _SCL_EXTRA_BYTE_VALUE (0 | (7 << 3)) // rooks on classic positions
  181. #else
  182. #define _SCL_EXTRA_BYTE_VALUE 0
  183. #endif
  184. #define SCL_BOARD_START_STATE \
  185. {82, 78, 66, 81, 75, 66, 78, 82,\
  186. 80, 80, 80, 80, 80, 80, 80, 80,\
  187. 46, 46, 46, 46, 46, 46, 46, 46,\
  188. 46, 46, 46, 46, 46, 46, 46, 46,\
  189. 46, 46, 46, 46, 46, 46, 46, 46,\
  190. 46, 46, 46, 46, 46, 46, 46, 46,\
  191. 112,112,112,112,112,112,112,112,\
  192. 114,110,98, 113,107,98, 110,114,\
  193. (char) 0xff,0,0,_SCL_EXTRA_BYTE_VALUE,0}
  194. #define SCL_FEN_START \
  195. "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
  196. #define SCL_FEN_HORDE \
  197. "ppp2ppp/pppppppp/pppppppp/pppppppp/3pp3/8/PPPPPPPP/RNBQKBNR w KQ - 0 1"
  198. #define SCL_FEN_UPSIDE_DOWN \
  199. "RNBKQBNR/PPPPPPPP/8/8/8/8/pppppppp/rnbkqbnr w - - 0 1"
  200. #define SCL_FEN_PEASANT_REVOLT \
  201. "1nn1k1n1/4p3/8/8/8/8/PPPPPPPP/4K3 w - - 0 1"
  202. #define SCL_FEN_ENDGAME \
  203. "4k3/pppppppp/8/8/8/8/PPPPPPPP/4K3 w - - 0 1"
  204. #define SCL_FEN_KNIGHTS \
  205. "N6n/1N4n1/2N2n2/3Nn3/k2nN2K/2n2N2/1n4N1/n6N w - - 0 1"
  206. /**
  207. Holds an info required to undo a single move.
  208. */
  209. typedef struct
  210. {
  211. uint8_t squareFrom; ///< start square
  212. uint8_t squareTo; ///< target square
  213. char enPassantCastle; ///< previous en passant/castle byte
  214. char moveCount; ///< previous values of the move counter byte
  215. uint8_t other; /**< lowest 7 bits: previous value of target square,
  216. highest bit: if 1 then the move was promotion or
  217. en passant */
  218. } SCL_MoveUndo;
  219. #define SCL_GAME_STATE_PLAYING 0x00
  220. #define SCL_GAME_STATE_WHITE_WIN 0x01
  221. #define SCL_GAME_STATE_BLACK_WIN 0x02
  222. #define SCL_GAME_STATE_DRAW 0x10 ///< further unspecified draw
  223. #define SCL_GAME_STATE_DRAW_STALEMATE 0x11 ///< draw by stalemate
  224. #define SCL_GAME_STATE_DRAW_REPETITION 0x12 ///< draw by repetition
  225. #define SCL_GAME_STATE_DRAW_50 0x13 ///< draw by 50 move rule
  226. #define SCL_GAME_STATE_DRAW_DEAD 0x14 ///< draw by dead position
  227. #define SCL_GAME_STATE_END 0xff ///< end without known result
  228. /**
  229. Converts square in common notation (e.g. 'c' 8) to square number. Only accepts
  230. lowercase column.
  231. */
  232. #define SCL_SQUARE(colChar,rowInt) (((rowInt) - 1) * 8 + ((colChar) - 'a'))
  233. #define SCL_S(c,r) SCL_SQUARE(c,r)
  234. void SCL_boardInit(SCL_Board board);
  235. void SCL_boardCopy(const SCL_Board boardFrom, SCL_Board boardTo);
  236. /**
  237. Initializes given chess 960 (Fisher random) position. If SCL_960_CASTLING
  238. is not set, castling will be disabled by this function.
  239. */
  240. void SCL_boardInit960(SCL_Board board, uint16_t positionNumber);
  241. void SCL_boardDisableCastling(SCL_Board board);
  242. uint32_t SCL_boardHash32(const SCL_Board board);
  243. #define SCL_PHASE_OPENING 0
  244. #define SCL_PHASE_MIDGAME 1
  245. #define SCL_PHASE_ENDGAME 2
  246. /**
  247. Estimates the game phase: opening, midgame or endgame.
  248. */
  249. uint8_t SCL_boardEstimatePhase(SCL_Board board);
  250. /**
  251. Sets the board position. The input string should be 64 characters long zero
  252. terminated C string representing the board as squares A1, A2, ..., H8 with
  253. each char being either a piece (RKBKQPrkbkqp) or an empty square ('.').
  254. */
  255. void SCL_boardSetPosition(SCL_Board board, const char *pieces,
  256. uint8_t castlingEnPassant, uint8_t moveCount, uint8_t ply);
  257. uint8_t SCL_boardsDiffer(SCL_Board b1, SCL_Board b2);
  258. /**
  259. Gets a random move on given board for the player whose move it is.
  260. */
  261. void SCL_boardRandomMove(SCL_Board board, SCL_RandomFunction randFunc,
  262. uint8_t *squareFrom, uint8_t *squareTo, char *resultProm);
  263. #define SCL_FEN_MAX_LENGTH 90
  264. /**
  265. Converts a position to FEN (Forsyth–Edwards Notation) string. The string has
  266. to have at least SCL_FEN_MAX_LENGTH bytes allocated to guarantee the
  267. function won't write to unallocated memory. The string will be terminated by
  268. 0 (this is included in SCL_FEN_MAX_LENGTH). The number of bytes written
  269. (including the terminating 0) is returned.
  270. */
  271. uint8_t SCL_boardToFEN(SCL_Board board, char *string);
  272. /**
  273. Loads a board from FEN (Forsyth–Edwards Notation) string. Returns 1 on
  274. success, 0 otherwise. XFEN isn't supported fully but a start position in
  275. chess960 can be loaded with this function.
  276. */
  277. uint8_t SCL_boardFromFEN(SCL_Board board, const char *string);
  278. /**
  279. Returns an approximate/heuristic board rating as a number, 0 meaning equal
  280. chances for both players, positive favoring white, negative favoring black.
  281. */
  282. typedef int16_t (*SCL_StaticEvaluationFunction)(SCL_Board);
  283. /*
  284. NOTE: int8_t as a return value was tried for evaluation function, which would
  285. be simpler, but it fails to capture important non-material position
  286. differences counted in fractions of pawn values, hence we have to use int16_t.
  287. */
  288. /**
  289. Basic static evaluation function. WARNING: this function supposes a standard
  290. chess game, for non-standard positions it may either not work well or even
  291. crash the program. You should use a different function for non-standard games.
  292. */
  293. int16_t SCL_boardEvaluateStatic(SCL_Board board);
  294. /**
  295. Dynamic evaluation function (search), i.e. unlike SCL_boardEvaluateStatic,
  296. this one performs a recursive search for deeper positions to get a more
  297. accurate score. Of course, this is much slower and hugely dependent on
  298. baseDepth (you mostly want to keep this under 5).
  299. */
  300. int16_t SCL_boardEvaluateDynamic(SCL_Board board, uint8_t baseDepth,
  301. uint8_t extensionExtraDepth, SCL_StaticEvaluationFunction evalFunction);
  302. #define SCL_EVALUATION_MAX_SCORE 32600 // don't increase this, we need a margin
  303. /**
  304. Checks if the board position is dead, i.e. mate is impossible (e.g. due to
  305. insufficient material), which by the rules results in a draw. WARNING: This
  306. function may fail to detect some dead positions as this is a non-trivial task.
  307. */
  308. uint8_t SCL_boardDead(SCL_Board board);
  309. /**
  310. Tests whether given player is in check.
  311. */
  312. uint8_t SCL_boardCheck(SCL_Board board, uint8_t white);
  313. /**
  314. Checks whether given move resets the move counter (used in the 50 move rule).
  315. */
  316. uint8_t SCL_boardMoveResetsCount(SCL_Board board,
  317. uint8_t squareFrom, uint8_t squareTo);
  318. uint8_t SCL_boardMate(SCL_Board board);
  319. /**
  320. Performs a move on a board WITHOUT checking if the move is legal. Returns an
  321. info with which the move can be undone.
  322. */
  323. SCL_MoveUndo SCL_boardMakeMove(SCL_Board board, uint8_t squareFrom, uint8_t squareTo,
  324. char promotePiece);
  325. void SCL_boardUndoMove(SCL_Board board, SCL_MoveUndo moveUndo);
  326. /**
  327. Checks if the game is over, i.e. the current player to move has no legal
  328. moves, the game is in dead position etc.
  329. */
  330. uint8_t SCL_boardGameOver(SCL_Board board);
  331. /**
  332. Checks if given move is legal.
  333. */
  334. uint8_t SCL_boardMoveIsLegal(SCL_Board board, uint8_t squareFrom,
  335. uint8_t squareTo);
  336. /**
  337. Checks if the player to move has at least one legal move.
  338. */
  339. uint8_t SCL_boardMovePossible(SCL_Board board);
  340. #define SCL_POSITION_NORMAL 0x00
  341. #define SCL_POSITION_CHECK 0x01
  342. #define SCL_POSITION_MATE 0x02
  343. #define SCL_POSITION_STALEMATE 0x03
  344. #define SCL_POSITION_DEAD 0x04
  345. uint8_t SCL_boardGetPosition(SCL_Board board);
  346. /**
  347. Returns 1 if the square is attacked by player of given color. This is used to
  348. examine checks, so for performance reasons the functions only checks whether
  349. or not the square is attacked (not the number of attackers).
  350. */
  351. uint8_t SCL_boardSquareAttacked(SCL_Board board, uint8_t square,
  352. uint8_t byWhite);
  353. /**
  354. Gets pseudo moves of a piece: all possible moves WITHOUT eliminating moves
  355. that lead to own check. To get only legal moves use SCL_boardGetMoves.
  356. */
  357. void SCL_boardGetPseudoMoves(
  358. SCL_Board board,
  359. uint8_t pieceSquare,
  360. uint8_t checkCastling,
  361. SCL_SquareSet result);
  362. /**
  363. Gets all legal moves of given piece.
  364. */
  365. void SCL_boardGetMoves(
  366. SCL_Board board,
  367. uint8_t pieceSquare,
  368. SCL_SquareSet result);
  369. static inline uint8_t SCL_boardWhitesTurn(SCL_Board board);
  370. static inline uint8_t SCL_pieceIsWhite(char piece);
  371. static inline uint8_t SCL_squareIsWhite(uint8_t square);
  372. char SCL_pieceToColor(uint8_t piece, uint8_t toWhite);
  373. /**
  374. Converts square coordinates to square number. Each coordinate must be a number
  375. <1,8>. Validity of the coordinates is NOT checked.
  376. */
  377. static inline uint8_t SCL_coordsToSquare(uint8_t row, uint8_t column);
  378. #ifndef SCL_VALUE_PAWN
  379. #define SCL_VALUE_PAWN 256
  380. #endif
  381. #ifndef SCL_VALUE_KNIGHT
  382. #define SCL_VALUE_KNIGHT 768
  383. #endif
  384. #ifndef SCL_VALUE_BISHOP
  385. #define SCL_VALUE_BISHOP 800
  386. #endif
  387. #ifndef SCL_VALUE_ROOK
  388. #define SCL_VALUE_ROOK 1280
  389. #endif
  390. #ifndef SCL_VALUE_QUEEN
  391. #define SCL_VALUE_QUEEN 2304
  392. #endif
  393. #ifndef SCL_VALUE_KING
  394. #define SCL_VALUE_KING 0
  395. #endif
  396. #define SCL_ENDGAME_MATERIAL_LIMIT \
  397. (2 * (SCL_VALUE_PAWN * 4 + SCL_VALUE_QUEEN + \
  398. SCL_VALUE_KING + SCL_VALUE_ROOK + SCL_VALUE_KNIGHT))
  399. #define SCL_START_MATERIAL \
  400. (16 * SCL_VALUE_PAWN + 4 * SCL_VALUE_ROOK + 4 * SCL_VALUE_KNIGHT + \
  401. 4 * SCL_VALUE_BISHOP + 2 * SCL_VALUE_QUEEN + 2 * SCL_VALUE_KING)
  402. #ifndef SCL_RECORD_MAX_LENGTH
  403. #define SCL_RECORD_MAX_LENGTH 256
  404. #endif
  405. #define SCL_RECORD_MAX_SIZE (SCL_RECORD_MAX_LENGTH * 2)
  406. /**
  407. Records a single chess game. The format is following:
  408. Each record item consists of 2 bytes which record a single move (ply):
  409. abxxxxxx cdyyyyyy
  410. xxxxxx Start square of the move, counted as A0, A1, ...
  411. yyyyyy End square of the move in the same format as the start square.
  412. ab 00 means this move isn't the last move of the game, other possible
  413. values are 01: white wins, 10: black wins, 11: draw or end for
  414. other reasons.
  415. cd In case of pawn promotion move this encodes the promoted piece as
  416. 00: queen, 01: rook, 10: bishop, 11: knight (pawn isn't allowed by
  417. chess rules).
  418. Every record should be ended by an ending move (ab != 00), empty record should
  419. have one move where xxxxxx == yyyyyy == 0 and ab == 11.
  420. */
  421. typedef uint8_t SCL_Record[SCL_RECORD_MAX_SIZE];
  422. #define SCL_RECORD_CONT 0x00
  423. #define SCL_RECORD_W_WIN 0x40
  424. #define SCL_RECORD_B_WIN 0x80
  425. #define SCL_RECORD_END 0xc0
  426. #define SCL_RECORD_PROM_Q 0x00
  427. #define SCL_RECORD_PROM_R 0x40
  428. #define SCL_RECORD_PROM_B 0x80
  429. #define SCL_RECORD_PROM_N 0xc0
  430. #define SCL_RECORD_ITEM(s0,s1,p,e) ((e) | (s0)),((p) | (s1))
  431. void SCL_recordInit(SCL_Record r);
  432. void SCL_recordCopy(SCL_Record recordFrom, SCL_Record recordTo);
  433. /**
  434. Represents a complete game of chess (or a variant with different staring
  435. position). This struct along with associated functions allows to easily
  436. implement a chess game that allows undoing moves, detecting draws, recording
  437. the moves etc. On platforms with extremely little RAM one can reduce
  438. SCL_RECORD_MAX_LENGTH to reduce the size of this struct (which will however
  439. possibly limit how many moves can be undone).
  440. */
  441. typedef struct
  442. {
  443. SCL_Board board;
  444. SCL_Record record; /**< Holds the game record. This record is here
  445. firstly because games are usually recorded and
  446. secondly this allows undoing moves up to the
  447. beginning of the game. This infinite undoing will
  448. only work as long as the record is able to hold
  449. the whole game; if the record is full, undoing is
  450. no longet possible. */
  451. uint16_t state;
  452. uint16_t ply; ///< ply count (board ply counter is only 8 bit)
  453. uint32_t prevMoves[14]; ///< stores last moves, for repetition detection
  454. const char *startState; /**< Optional pointer to the starting board state.
  455. If this is null, standard chess start position is
  456. assumed. This is needed for undoing moves with
  457. game record. */
  458. } SCL_Game;
  459. /**
  460. Initializes a new chess game. The startState parameter is optional and allows
  461. for setting up chess variants that differ by starting positions, setting this
  462. to 0 will assume traditional starting position. WARNING: if startState is
  463. provided, the pointed to board mustn't be deallocated afterwards, the string
  464. is not internally copied (for memory saving reasons).
  465. */
  466. void SCL_gameInit(SCL_Game *game, const SCL_Board startState);
  467. void SCL_gameMakeMove(SCL_Game *game, uint8_t squareFrom, uint8_t squareTo,
  468. char promoteTo);
  469. uint8_t SCL_gameUndoMove(SCL_Game *game);
  470. /**
  471. Gets a move which if played now would cause a draw by repetition. Returns 1
  472. if such move exists, 0 otherwise. The results parameters can be set to 0 in
  473. which case they will be ignored and only the existence of a draw move will be
  474. tested.
  475. */
  476. uint8_t SCL_gameGetRepetiotionMove(SCL_Game *game,
  477. uint8_t *squareFrom, uint8_t *squareTo);
  478. /**
  479. Leads a game record from PGN string. The function will probably not strictly
  480. adhere to the PGN input format, but should accept most sanely written PGN
  481. strings.
  482. */
  483. void SCL_recordFromPGN(SCL_Record r, const char *pgn);
  484. uint16_t SCL_recordLength(const SCL_Record r);
  485. /**
  486. Gets the move out of a game record, returns the end state of the move
  487. (SCL_RECORD_CONT, SCL_RECORD_END etc.)
  488. */
  489. uint8_t SCL_recordGetMove(const SCL_Record r, uint16_t index,
  490. uint8_t *squareFrom, uint8_t *squareTo, char *promotedPiece);
  491. /**
  492. Adds another move to the game record. Terminating the record is handled so
  493. that the last move is always marked with end flag, endState is here to only
  494. indicate possible game result (otherwise pass SCL_RECORD_CONT). Returns 1 if
  495. the item was added, otherwise 0 (replay was already of maximum size).
  496. */
  497. uint8_t SCL_recordAdd(SCL_Record r, uint8_t squareFrom,
  498. uint8_t squareTo, char promotePiece, uint8_t endState);
  499. /**
  500. Removes the last move from the record, returns 1 if the replay is non-empty
  501. after the removal, otherwise 0.
  502. */
  503. uint8_t SCL_recordRemoveLast(SCL_Record r);
  504. /**
  505. Applies given number of half-moves (ply) to a given board (the board is
  506. automatically initialized at the beginning).
  507. */
  508. void SCL_recordApply(const SCL_Record r, SCL_Board b, uint16_t moves);
  509. int16_t SCL_pieceValue(char piece);
  510. int16_t SCL_pieceValuePositive(char piece);
  511. #define SCL_PRINT_FORMAT_NONE 0
  512. #define SCL_PRINT_FORMAT_NORMAL 1
  513. #define SCL_PRINT_FORMAT_COMPACT 2
  514. #define SCL_PRINT_FORMAT_UTF8 3
  515. #define SCL_PRINT_FORMAT_COMPACT_UTF8 4
  516. /**
  517. Gets the best move for the currently moving player as computed by AI. The
  518. return value is the value of the move (with the same semantics as the value
  519. of an evaluation function). baseDepth is depth in plys to which all moves will
  520. be checked. If baseDepth 0 is passed, the function makes a random move and
  521. returns the evaluation of the board. extensionExtraDepth is extra depth for
  522. checking specific situations like exchanges and checks. endgameExtraDepth is
  523. extra depth which is added to baseDepth in the endgame. If the randomness
  524. function is 0, AI will always make the first best move it finds, if it is
  525. not 0 and randomness is 0, AI will randomly pick between the equally best
  526. moves, if it is not 0 and randomness is positive, AI will randomly choose
  527. between best moves with some bias (may not pick the best rated move).
  528. */
  529. int16_t SCL_getAIMove(
  530. SCL_Board board,
  531. uint8_t baseDepth,
  532. uint8_t extensionExtraDepth,
  533. uint8_t endgameExtraDepth,
  534. SCL_StaticEvaluationFunction evalFunc,
  535. SCL_RandomFunction randFunc,
  536. uint8_t randomness,
  537. uint8_t repetitionMoveFrom,
  538. uint8_t repetitionMoveTo,
  539. uint8_t *resultFrom,
  540. uint8_t *resultTo,
  541. char *resultProm);
  542. /**
  543. Function that prints out a single character. This is passed to printing
  544. functions.
  545. */
  546. typedef void (*SCL_PutCharFunction)(char);
  547. /**
  548. Prints given chessboard using given format and an abstract printing function.
  549. */
  550. void SCL_printBoard(
  551. SCL_Board board,
  552. SCL_PutCharFunction putCharFunc,
  553. SCL_SquareSet highlightSquares,
  554. uint8_t selectSquare,
  555. uint8_t format,
  556. uint8_t offset,
  557. uint8_t labels,
  558. uint8_t blackDown);
  559. void SCL_printBoardSimple(
  560. SCL_Board board,
  561. SCL_PutCharFunction putCharFunc,
  562. uint8_t selectSquare,
  563. uint8_t format);
  564. void SCL_printSquareUTF8(uint8_t square, SCL_PutCharFunction putCharFunc);
  565. void SCL_printPGN(SCL_Record r, SCL_PutCharFunction putCharFunc,
  566. SCL_Board initialState);
  567. /**
  568. Reads a move from string (the notation format is described at the top of this
  569. file). The function is safe as long as the string is 0 terminated. Returns 1
  570. on success or 0 on fail (invalid move string).
  571. */
  572. uint8_t SCL_stringToMove(const char *moveString, uint8_t *resultFrom,
  573. uint8_t *resultTo, char *resultPromotion);
  574. char *SCL_moveToString(SCL_Board board, uint8_t s0, uint8_t s1,
  575. char promotion, char *string);
  576. /**
  577. Function used in drawing, it is called to draw the next pixel. The first
  578. parameter is the pixel color, the second one if the sequential number of the
  579. pixel.
  580. */
  581. typedef void (*SCL_PutPixelFunction)(uint8_t, uint16_t);
  582. #define SCL_BOARD_PICTURE_WIDTH 64
  583. /**
  584. Draws a simple 1bit 64x64 pixels board using a provided abstract function for
  585. drawing pixels. The function renders from top left to bottom right, i.e. no
  586. frame buffer is required.
  587. */
  588. void SCL_drawBoard(
  589. SCL_Board board,
  590. SCL_PutPixelFunction putPixel,
  591. uint8_t selectedSquare,
  592. SCL_SquareSet highlightSquares,
  593. uint8_t blackDown);
  594. /**
  595. Converts square number to string representation (e.g. "d2"). This function
  596. will modify exactly the first two bytes of the provided string.
  597. */
  598. static inline char *SCL_squareToString(uint8_t square, char *string);
  599. /**
  600. Converts a string, such as "A1" or "b4", to square number. The string must
  601. start with a letter (lower or upper case) and be followed by a number <1,8>.
  602. Validity of the string is NOT checked.
  603. */
  604. uint8_t SCL_stringToSquare(const char *square);
  605. //=============================================================================
  606. // privates:
  607. #define SCL_UNUSED(v) (void)(v)
  608. uint8_t SCL_currentRandom8 = 0;
  609. uint16_t SCL_currentRandom16 = 0;
  610. void SCL_randomSimpleSeed(uint8_t seed)
  611. {
  612. SCL_currentRandom8 = seed;
  613. }
  614. uint8_t SCL_randomSimple(void)
  615. {
  616. SCL_currentRandom8 *= 13;
  617. SCL_currentRandom8 += 7;
  618. return SCL_currentRandom8;
  619. }
  620. uint8_t SCL_randomBetter(void)
  621. {
  622. SCL_currentRandom16 *= 13;
  623. SCL_currentRandom16 += 7;
  624. return (SCL_currentRandom16 % 256) ^ (SCL_currentRandom16 / 256);
  625. }
  626. void SCL_randomBetterSeed(uint16_t seed)
  627. {
  628. SCL_currentRandom16 = seed;
  629. }
  630. void SCL_squareSetClear(SCL_SquareSet squareSet)
  631. {
  632. for (uint8_t i = 0; i < 8; ++i)
  633. squareSet[i] = 0;
  634. }
  635. uint8_t SCL_stringToSquare(const char *square)
  636. {
  637. return (square[1] - '1') * 8 +
  638. (square[0] - ((square[0] >= 'A' && square[0] <= 'Z') ? 'A' : 'a'));
  639. }
  640. char *SCL_moveToString(SCL_Board board, uint8_t s0, uint8_t s1,
  641. char promotion, char *string)
  642. {
  643. char *result = string;
  644. SCL_squareToString(s0,string);
  645. string += 2;
  646. string = SCL_squareToString(s1,string);
  647. string += 2;
  648. char c = board[s0];
  649. if (c == 'p' || c == 'P')
  650. {
  651. uint8_t rank = s1 / 8;
  652. if (rank == 0 || rank == 7)
  653. {
  654. *string = promotion;
  655. string++;
  656. }
  657. }
  658. *string = 0;
  659. return result;
  660. }
  661. uint8_t SCL_boardWhitesTurn(SCL_Board board)
  662. {
  663. return (board[SCL_BOARD_PLY_BYTE] % 2) == 0;
  664. }
  665. uint8_t SCL_coordsToSquare(uint8_t row, uint8_t column)
  666. {
  667. return row * 8 + column;
  668. }
  669. uint8_t SCL_pieceIsWhite(char piece)
  670. {
  671. return piece < 'a';
  672. }
  673. char *SCL_squareToString(uint8_t square, char *string)
  674. {
  675. string[0] = 'a' + square % 8;
  676. string[1] = '1' + square / 8;
  677. return string;
  678. }
  679. uint8_t SCL_squareIsWhite(uint8_t square)
  680. {
  681. return (square % 2) != ((square / 8) % 2);
  682. }
  683. char SCL_pieceToColor(uint8_t piece, uint8_t toWhite)
  684. {
  685. return (SCL_pieceIsWhite(piece) == toWhite) ?
  686. piece : (piece + (toWhite ? -32 : 32));
  687. }
  688. /**
  689. Records the rook starting positions in the board state. This is required in
  690. chess 960 in order to be able to correctly perform castling (castling rights
  691. knowledge isn't enough as one rook might have moved to the other side and we
  692. wouldn't know which one can castle and which not).
  693. */
  694. void _SCL_board960RememberRookPositions(SCL_Board board)
  695. {
  696. uint8_t pos = 0;
  697. uint8_t rooks = 2;
  698. while (pos < 8 && rooks != 0)
  699. {
  700. if (board[pos] == 'R')
  701. {
  702. board[SCL_BOARD_EXTRA_BYTE] = rooks == 2 ? pos :
  703. (board[SCL_BOARD_EXTRA_BYTE] | (pos << 3));
  704. rooks--;
  705. }
  706. pos++;
  707. }
  708. }
  709. void SCL_boardInit(SCL_Board board)
  710. {
  711. /*
  712. We might use SCL_BOARD_START_STATE and copy it to the board, but that might
  713. waste RAM on Arduino, so we init the board by code.
  714. */
  715. char *b = board;
  716. *b = 'R'; b++; *b = 'N'; b++;
  717. *b = 'B'; b++; *b = 'Q'; b++;
  718. *b = 'K'; b++; *b = 'B'; b++;
  719. *b = 'N'; b++; *b = 'R'; b++;
  720. char *b2 = board + 48;
  721. for (uint8_t i = 0; i < 8; ++i, b++, b2++)
  722. {
  723. *b = 'P';
  724. *b2 = 'p';
  725. }
  726. for (uint8_t i = 0; i < 32; ++i, b++)
  727. *b = '.';
  728. b += 8;
  729. *b = 'r'; b++; *b = 'n'; b++;
  730. *b = 'b'; b++; *b = 'q'; b++;
  731. *b = 'k'; b++; *b = 'b'; b++;
  732. *b = 'n'; b++; *b = 'r'; b++;
  733. for (uint8_t i = 0; i < SCL_BOARD_STATE_SIZE - SCL_BOARD_SQUARES; ++i, ++b)
  734. *b = 0;
  735. board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] = (char) 0xff;
  736. #if SCL_960_CASTLING
  737. _SCL_board960RememberRookPositions(board);
  738. #endif
  739. }
  740. void _SCL_boardPlaceOnNthAvailable(SCL_Board board, uint8_t pos, char piece)
  741. {
  742. char *c = board;
  743. while (1)
  744. {
  745. if (*c == '.')
  746. {
  747. if (pos == 0)
  748. break;
  749. pos--;
  750. }
  751. c++;
  752. }
  753. *c = piece;
  754. }
  755. void SCL_boardInit960(SCL_Board board, uint16_t positionNumber)
  756. {
  757. SCL_Board b;
  758. SCL_boardInit(b);
  759. for (uint8_t i = 0; i < SCL_BOARD_STATE_SIZE; ++i)
  760. board[i] = ((i >= 8 && i < 56) || i >= 64) ? b[i] : '.';
  761. uint8_t helper = positionNumber % 16;
  762. board[(helper / 4) * 2] = 'B';
  763. board[1 + (helper % 4) * 2] = 'B';
  764. helper = positionNumber / 16;
  765. // maybe there's a simpler way :)
  766. _SCL_boardPlaceOnNthAvailable(board,helper % 6,'Q');
  767. _SCL_boardPlaceOnNthAvailable(board,0,helper <= 23 ? 'N' : 'R');
  768. _SCL_boardPlaceOnNthAvailable(board,0,
  769. (helper >= 7 && helper <= 23) ? 'R' :
  770. (helper > 41 ? 'K' : 'N' ));
  771. _SCL_boardPlaceOnNthAvailable(board,0,
  772. (helper <= 5 || helper >= 54) ? 'R' :
  773. (((helper >= 12 && helper <= 23) ||
  774. (helper >= 30 && helper <= 41)) ? 'K' : 'N'));
  775. _SCL_boardPlaceOnNthAvailable(board,0,
  776. (helper <= 11 || (helper <= 29 && helper >= 24)) ? 'K' :
  777. (
  778. (
  779. (helper >= 18 && helper <= 23) ||
  780. (helper >= 36 && helper <= 41) ||
  781. (helper >= 48 && helper <= 53)
  782. ) ? 'R' : 'N'
  783. )
  784. );
  785. uint8_t rooks = 0;
  786. for (uint8_t i = 0; i < 8; ++i)
  787. if (board[i] == 'R')
  788. rooks++;
  789. _SCL_boardPlaceOnNthAvailable(board,0,rooks == 2 ? 'N' : 'R');
  790. for (uint8_t i = 0; i < 8; ++i)
  791. board[56 + i] = SCL_pieceToColor(board[i],0);
  792. #if SCL_960_CASTLING
  793. _SCL_board960RememberRookPositions(board);
  794. #else
  795. SCL_boardDisableCastling(board);
  796. #endif
  797. }
  798. uint8_t SCL_boardsDiffer(SCL_Board b1, SCL_Board b2)
  799. {
  800. const char *p1 = b1, *p2 = b2;
  801. while (p1 < b1 + SCL_BOARD_STATE_SIZE)
  802. {
  803. if (*p1 != *p2)
  804. return 1;
  805. p1++;
  806. p2++;
  807. }
  808. return 0;
  809. }
  810. void SCL_recordInit(SCL_Record r)
  811. {
  812. r[0] = 0 | SCL_RECORD_END;
  813. r[1] = 0;
  814. }
  815. void SCL_recordFromPGN(SCL_Record r, const char *pgn)
  816. {
  817. SCL_Board board;
  818. SCL_boardInit(board);
  819. SCL_recordInit(r);
  820. uint8_t state = 0;
  821. uint8_t evenMove = 0;
  822. while (*pgn != 0)
  823. {
  824. switch (state)
  825. {
  826. case 0: // skipping tags and spaces, outside []
  827. if (*pgn == '1')
  828. state = 2;
  829. else if (*pgn == '[')
  830. state = 1;
  831. break;
  832. case 1: // skipping tags and spaces, inside []
  833. if (*pgn == ']')
  834. state = 0;
  835. break;
  836. case 2: // reading move number
  837. if (*pgn == '{')
  838. state = 3;
  839. else if ((*pgn >= 'a' && *pgn <= 'h') || (*pgn >= 'A' && *pgn <= 'Z'))
  840. {
  841. state = 4;
  842. pgn--;
  843. }
  844. break;
  845. case 3: // initial comment
  846. if (*pgn == '}')
  847. state = 2;
  848. break;
  849. case 4: // reading move
  850. {
  851. char piece = 'p';
  852. char promoteTo = 'q';
  853. uint8_t castle = 0;
  854. uint8_t promotion = 0;
  855. int8_t coords[4];
  856. uint8_t ranks = 0, files = 0;
  857. for (uint8_t i = 0; i < 4; ++i)
  858. coords[i] = -1;
  859. while (*pgn != ' ' && *pgn != '\n' &&
  860. *pgn != '\t' && *pgn != '{' && *pgn != 0)
  861. {
  862. if (*pgn == '=')
  863. promotion = 1;
  864. if (*pgn == 'O' || *pgn == '0')
  865. castle++;
  866. if (*pgn >= 'A' && *pgn <= 'Z')
  867. {
  868. if (promotion)
  869. promoteTo = *pgn;
  870. else
  871. piece = *pgn;
  872. }
  873. else if (*pgn >= 'a' && *pgn <= 'h')
  874. {
  875. coords[files * 2] = *pgn - 'a';
  876. files++;
  877. }
  878. else if (*pgn >= '1' && *pgn <= '8')
  879. {
  880. coords[1 + ranks * 2] = *pgn - '1';
  881. ranks++;
  882. }
  883. pgn++;
  884. }
  885. if (castle)
  886. {
  887. piece = 'K';
  888. coords[0] = 4;
  889. coords[1] = 0;
  890. coords[2] = castle < 3 ? 6 : 2;
  891. coords[3] = 0;
  892. if (evenMove)
  893. {
  894. coords[1] = 7;
  895. coords[3] = 7;
  896. }
  897. }
  898. piece = SCL_pieceToColor(piece,evenMove == 0);
  899. if (coords[2] < 0)
  900. {
  901. coords[2] = coords[0];
  902. coords[0] = -1;
  903. }
  904. if (coords[3] < 0)
  905. {
  906. coords[3] = coords[1];
  907. coords[1] = -1;
  908. }
  909. uint8_t squareTo = coords[3] * 8 + coords[2];
  910. if (coords[0] < 0 || coords[1] < 0)
  911. {
  912. // without complete starting coords we have to find the piece
  913. for (int i = 0; i < SCL_BOARD_SQUARES; ++i)
  914. if (board[i] == piece)
  915. {
  916. SCL_SquareSet s;
  917. SCL_squareSetClear(s);
  918. SCL_boardGetMoves(board,i,s);
  919. if (SCL_squareSetContains(s,squareTo) &&
  920. (coords[0] < 0 || coords[0] == i % 8) &&
  921. (coords[1] < 0 || coords[1] == i / 8))
  922. {
  923. coords[0] = i % 8;
  924. coords[1] = i / 8;
  925. break;
  926. }
  927. }
  928. }
  929. uint8_t squareFrom = coords[1] * 8 + coords[0];
  930. SCL_boardMakeMove(board,squareFrom,squareTo,promoteTo);
  931. // for some reason tcc bugs here, the above line sets squareFrom to 0 lol
  932. // can be fixed with doing "squareFrom = coords[1] * 8 + coords[0];" again
  933. SCL_recordAdd(r,squareFrom,squareTo,promoteTo,SCL_RECORD_CONT);
  934. while (*pgn == ' ' || *pgn == '\n' || *pgn == '\t' || *pgn == '{')
  935. {
  936. if (*pgn == '{')
  937. while (*pgn != '}')
  938. pgn++;
  939. pgn++;
  940. }
  941. if (*pgn == 0)
  942. return;
  943. pgn--;
  944. if (evenMove)
  945. state = 2;
  946. evenMove = !evenMove;
  947. break;
  948. }
  949. default: break;
  950. }
  951. pgn++;
  952. }
  953. }
  954. uint16_t SCL_recordLength(const SCL_Record r)
  955. {
  956. if ((r[0] & 0x3f) == (r[1] & 0x3f)) // empty record that's only terminator
  957. return 0;
  958. uint16_t result = 0;
  959. while ((r[result] & 0xc0) == 0)
  960. result += 2;
  961. return (result / 2) + 1;
  962. }
  963. uint8_t SCL_recordGetMove(const SCL_Record r, uint16_t index,
  964. uint8_t *squareFrom, uint8_t *squareTo, char *promotedPiece)
  965. {
  966. index *= 2;
  967. uint8_t b = r[index];
  968. *squareFrom = b & 0x3f;
  969. uint8_t result = b & 0xc0;
  970. index++;
  971. b = r[index];
  972. *squareTo = b & 0x3f;
  973. b &= 0xc0;
  974. switch (b)
  975. {
  976. case SCL_RECORD_PROM_Q: *promotedPiece = 'q'; break;
  977. case SCL_RECORD_PROM_R: *promotedPiece = 'r'; break;
  978. case SCL_RECORD_PROM_B: *promotedPiece = 'b'; break;
  979. case SCL_RECORD_PROM_N:
  980. default: *promotedPiece = 'n'; break;
  981. }
  982. return result;
  983. }
  984. uint8_t SCL_recordAdd(SCL_Record r, uint8_t squareFrom,
  985. uint8_t squareTo, char promotePiece, uint8_t endState)
  986. {
  987. uint16_t l = SCL_recordLength(r);
  988. if (l >= SCL_RECORD_MAX_LENGTH)
  989. return 0;
  990. l *= 2;
  991. if (l != 0)
  992. r[l - 2] &= 0x3f; // remove the end flag from previous item
  993. if (endState == SCL_RECORD_CONT)
  994. endState = SCL_RECORD_END;
  995. r[l] = squareFrom | endState;
  996. uint8_t p;
  997. switch (promotePiece)
  998. {
  999. case 'n': case 'N': p = SCL_RECORD_PROM_N; break;
  1000. case 'b': case 'B': p = SCL_RECORD_PROM_B; break;
  1001. case 'r': case 'R': p = SCL_RECORD_PROM_R; break;
  1002. case 'q': case 'Q':
  1003. default: p = SCL_RECORD_PROM_Q; break;
  1004. }
  1005. l++;
  1006. r[l] = squareTo | p;
  1007. return 1;
  1008. }
  1009. uint8_t SCL_recordRemoveLast(SCL_Record r)
  1010. {
  1011. uint16_t l = SCL_recordLength(r);
  1012. if (l == 0)
  1013. return 0;
  1014. if (l == 1)
  1015. SCL_recordInit(r);
  1016. else
  1017. {
  1018. l = (l - 2) * 2;
  1019. r[l] = (r[l] & 0x3f) | SCL_RECORD_END;
  1020. }
  1021. return 1;
  1022. }
  1023. void SCL_recordApply(const SCL_Record r, SCL_Board b, uint16_t moves)
  1024. {
  1025. SCL_boardInit(b);
  1026. uint16_t l = SCL_recordLength(r);
  1027. if (moves > l)
  1028. moves = l;
  1029. for (uint16_t i = 0; i < moves; ++i)
  1030. {
  1031. uint8_t s0, s1;
  1032. char p;
  1033. SCL_recordGetMove(r,i,&s0,&s1,&p);
  1034. SCL_boardMakeMove(b,s0,s1,p);
  1035. }
  1036. }
  1037. void SCL_boardUndoMove(SCL_Board board, SCL_MoveUndo moveUndo)
  1038. {
  1039. #if SCL_960_CASTLING
  1040. char squareToNow = board[moveUndo.squareTo];
  1041. #endif
  1042. board[moveUndo.squareFrom] = board[moveUndo.squareTo];
  1043. board[moveUndo.squareTo] = moveUndo.other & 0x7f;
  1044. board[SCL_BOARD_PLY_BYTE]--;
  1045. board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] = moveUndo.enPassantCastle;
  1046. board[SCL_BOARD_MOVE_COUNT_BYTE] = moveUndo.moveCount;
  1047. if (moveUndo.other & 0x80)
  1048. {
  1049. moveUndo.squareTo /= 8;
  1050. if (moveUndo.squareTo == 0 || moveUndo.squareTo == 7)
  1051. board[moveUndo.squareFrom] = SCL_pieceIsWhite(board[moveUndo.squareFrom])
  1052. ? 'P' : 'p';
  1053. // ^ was promotion
  1054. else
  1055. board[(moveUndo.squareFrom / 8) * 8 + (moveUndo.enPassantCastle & 0x0f)] =
  1056. (board[moveUndo.squareFrom] == 'P') ? 'p' : 'P'; // was en passant
  1057. }
  1058. #if !SCL_960_CASTLING
  1059. else if (board[moveUndo.squareFrom] == 'k' && // black castling
  1060. moveUndo.squareFrom == 60)
  1061. {
  1062. if (moveUndo.squareTo == 58)
  1063. {
  1064. board[59] = '.';
  1065. board[56] = 'r';
  1066. }
  1067. else if (moveUndo.squareTo == 62)
  1068. {
  1069. board[61] = '.';
  1070. board[63] = 'r';
  1071. }
  1072. }
  1073. else if (board[moveUndo.squareFrom] == 'K' && // white castling
  1074. moveUndo.squareFrom == 4)
  1075. {
  1076. if (moveUndo.squareTo == 2)
  1077. {
  1078. board[3] = '.';
  1079. board[0] = 'R';
  1080. }
  1081. else if (moveUndo.squareTo == 6)
  1082. {
  1083. board[5] = '.';
  1084. board[7] = 'R';
  1085. }
  1086. }
  1087. #else // 960 castling
  1088. else if (((moveUndo.other & 0x7f) == 'r') && // black castling
  1089. (squareToNow == '.' || !SCL_pieceIsWhite(squareToNow)))
  1090. {
  1091. board[moveUndo.squareTo < moveUndo.squareFrom ? 59 : 61] = '.';
  1092. board[moveUndo.squareTo < moveUndo.squareFrom ? 58 : 62] = '.';
  1093. board[moveUndo.squareFrom] = 'k';
  1094. board[moveUndo.squareTo] = 'r';
  1095. }
  1096. else if (((moveUndo.other & 0x7f) == 'R') && // white castling
  1097. (squareToNow == '.' || SCL_pieceIsWhite(squareToNow)))
  1098. {
  1099. board[moveUndo.squareTo < moveUndo.squareFrom ? 3 : 5] = '.';
  1100. board[moveUndo.squareTo < moveUndo.squareFrom ? 2 : 6] = '.';
  1101. board[moveUndo.squareFrom] = 'K';
  1102. board[moveUndo.squareTo] = 'R';
  1103. }
  1104. #endif
  1105. }
  1106. /**
  1107. Potentially disables castling rights according to whether something moved from
  1108. or to a square with a rook.
  1109. */
  1110. void _SCL_handleRookActivity(SCL_Board board, uint8_t rookSquare)
  1111. {
  1112. #if !SCL_960_CASTLING
  1113. switch (rookSquare)
  1114. {
  1115. case 0: board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x20; break;
  1116. case 7: board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x10; break;
  1117. case 56: board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x80; break;
  1118. case 63: board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x40; break;
  1119. default: break;
  1120. }
  1121. #else // 960 castling
  1122. if (rookSquare == (board[SCL_BOARD_EXTRA_BYTE] & 0x07))
  1123. board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x20;
  1124. else if (rookSquare == (board[SCL_BOARD_EXTRA_BYTE] >> 3))
  1125. board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x10;
  1126. else if (rookSquare == 56 + (board[SCL_BOARD_EXTRA_BYTE] & 0x07))
  1127. board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x80;
  1128. else if (rookSquare == 56 + (board[SCL_BOARD_EXTRA_BYTE] >> 3))
  1129. board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x40;
  1130. #endif
  1131. }
  1132. SCL_MoveUndo SCL_boardMakeMove(SCL_Board board, uint8_t squareFrom, uint8_t squareTo,
  1133. char promotePiece)
  1134. {
  1135. char s = board[squareFrom];
  1136. SCL_MoveUndo moveUndo;
  1137. moveUndo.squareFrom = squareFrom;
  1138. moveUndo.squareTo = squareTo;
  1139. moveUndo.moveCount = board[SCL_BOARD_MOVE_COUNT_BYTE];
  1140. moveUndo.enPassantCastle = board[SCL_BOARD_ENPASSANT_CASTLE_BYTE];
  1141. moveUndo.other = board[squareTo];
  1142. // reset the en-passant state
  1143. board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] |= 0x0f;
  1144. if (SCL_boardMoveResetsCount(board,squareFrom,squareTo))
  1145. board[SCL_BOARD_MOVE_COUNT_BYTE] = 0;
  1146. else
  1147. board[SCL_BOARD_MOVE_COUNT_BYTE]++;
  1148. #if SCL_960_CASTLING
  1149. uint8_t castled = 0;
  1150. #endif
  1151. if ((s == 'k') || (s == 'K'))
  1152. {
  1153. #if !SCL_960_CASTLING
  1154. if ((squareFrom == 4) || (squareFrom == 60)) // check castling
  1155. {
  1156. int8_t difference = squareTo - squareFrom;
  1157. char rook = SCL_pieceToColor('r',SCL_pieceIsWhite(s));
  1158. if (difference == 2) // short
  1159. {
  1160. board[squareTo - 1] = rook;
  1161. board[squareTo + 1] = '.';
  1162. }
  1163. else if (difference == -2) // long
  1164. {
  1165. board[squareTo - 2] = '.';
  1166. board[squareTo + 1] = rook;
  1167. }
  1168. }
  1169. #else // 960 castling
  1170. uint8_t isWhite = SCL_pieceIsWhite(s);
  1171. char rook = SCL_pieceToColor('r',isWhite);
  1172. if (board[squareTo] == rook)
  1173. {
  1174. castled = 1;
  1175. board[squareFrom] = '.';
  1176. board[squareTo] = '.';
  1177. if (squareTo > squareFrom) // short
  1178. {
  1179. board[isWhite ? 6 : (56 + 6)] = s;
  1180. board[isWhite ? 5 : (56 + 5)] = rook;
  1181. }
  1182. else // long
  1183. {
  1184. board[isWhite ? 2 : (56 + 2)] = s;
  1185. board[isWhite ? 3 : (56 + 3)] = rook;
  1186. }
  1187. }
  1188. #endif
  1189. // after king move disable castling
  1190. board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= ~(0x03 << ((s == 'K') ? 4 : 6));
  1191. }
  1192. else if ((s == 'p') || (s == 'P'))
  1193. {
  1194. uint8_t row = squareTo / 8;
  1195. int8_t rowDiff = squareFrom / 8 - row;
  1196. if (rowDiff == 2 || rowDiff == -2) // record en passant column
  1197. {
  1198. board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] =
  1199. (board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & 0xf0) | (squareFrom % 8);
  1200. }
  1201. if (row == 0 || row == 7)
  1202. {
  1203. // promotion
  1204. s = SCL_pieceToColor(promotePiece,SCL_pieceIsWhite(s));
  1205. moveUndo.other |= 0x80;
  1206. }
  1207. else
  1208. {
  1209. // check en passant move
  1210. int8_t columnDiff = (squareTo % 8) - (squareFrom % 8);
  1211. if ((columnDiff != 0) && (board[squareTo] == '.'))
  1212. {
  1213. board[squareFrom + columnDiff] = '.';
  1214. moveUndo.other |= 0x80;
  1215. }
  1216. }
  1217. }
  1218. else if ((s == 'r') || (s == 'R'))
  1219. _SCL_handleRookActivity(board,squareFrom);
  1220. char taken = board[squareTo];
  1221. // taking a rook may also disable castling:
  1222. if (taken == 'R' || taken == 'r')
  1223. _SCL_handleRookActivity(board,squareTo);
  1224. #if SCL_960_CASTLING
  1225. if (!castled)
  1226. #endif
  1227. {
  1228. board[squareTo] = s;
  1229. board[squareFrom] = '.';
  1230. }
  1231. board[SCL_BOARD_PLY_BYTE]++; // increase ply count
  1232. return moveUndo;
  1233. }
  1234. void SCL_boardSetPosition(SCL_Board board, const char *pieces,
  1235. uint8_t castlingEnPassant, uint8_t moveCount, uint8_t ply)
  1236. {
  1237. for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, pieces++)
  1238. if (*pieces != 0)
  1239. board[i] = *pieces;
  1240. else
  1241. break;
  1242. board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] = castlingEnPassant;
  1243. board[SCL_BOARD_PLY_BYTE] = ply;
  1244. board[SCL_BOARD_MOVE_COUNT_BYTE] = moveCount;
  1245. board[SCL_BOARD_STATE_SIZE - 1] = 0;
  1246. }
  1247. void SCL_squareSetAdd(SCL_SquareSet squareSet, uint8_t square)
  1248. {
  1249. squareSet[square / 8] |= 0x01 << (square % 8);
  1250. }
  1251. uint8_t SCL_squareSetContains(const SCL_SquareSet squareSet, uint8_t square)
  1252. {
  1253. return squareSet[square / 8] & (0x01 << (square % 8));
  1254. }
  1255. uint8_t SCL_squareSetSize(const SCL_SquareSet squareSet)
  1256. {
  1257. uint8_t result = 0;
  1258. for (uint8_t i = 0; i < 8; ++i)
  1259. {
  1260. uint8_t byte = squareSet[i];
  1261. for (uint8_t j = 0; j < 8; ++j)
  1262. {
  1263. result += byte & 0x01;
  1264. byte >>= 1;
  1265. }
  1266. }
  1267. return result;
  1268. }
  1269. uint8_t SCL_squareSetEmpty(const SCL_SquareSet squareSet)
  1270. {
  1271. for (uint8_t i = 0; i < 8; ++i)
  1272. if (squareSet[i] != 0)
  1273. return 0;
  1274. return 1;
  1275. }
  1276. uint8_t SCL_squareSetGetRandom(
  1277. const SCL_SquareSet squareSet, SCL_RandomFunction randFunc)
  1278. {
  1279. uint8_t size = SCL_squareSetSize(squareSet);
  1280. if (size == 0)
  1281. return 0;
  1282. uint8_t n = (randFunc() % size) + 1;
  1283. uint8_t i = 0;
  1284. while (i < SCL_BOARD_SQUARES)
  1285. {
  1286. if (SCL_squareSetContains(squareSet,i))
  1287. {
  1288. n--;
  1289. if (n == 0)
  1290. break;
  1291. }
  1292. ++i;
  1293. }
  1294. return i;
  1295. }
  1296. void SCL_boardCopy(const SCL_Board boardFrom, SCL_Board boardTo)
  1297. {
  1298. for (uint8_t i = 0; i < SCL_BOARD_STATE_SIZE; ++i)
  1299. boardTo[i] = boardFrom[i];
  1300. }
  1301. uint8_t SCL_boardSquareAttacked(
  1302. SCL_Board board,
  1303. uint8_t square,
  1304. uint8_t byWhite)
  1305. {
  1306. const char *currentSquare = board;
  1307. /* We need to place a temporary piece on the tested square in order to test if
  1308. the square is attacked (consider testing if attacked by a pawn). */
  1309. char previous = board[square];
  1310. board[square] = SCL_pieceToColor('r',!byWhite);
  1311. for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++currentSquare)
  1312. {
  1313. char s = *currentSquare;
  1314. if ((s == '.') || (SCL_pieceIsWhite(s) != byWhite))
  1315. continue;
  1316. SCL_SquareSet moves;
  1317. SCL_boardGetPseudoMoves(board,i,0,moves);
  1318. if (SCL_squareSetContains(moves,square))
  1319. {
  1320. board[square] = previous;
  1321. return 1;
  1322. }
  1323. }
  1324. board[square] = previous;
  1325. return 0;
  1326. }
  1327. uint8_t SCL_boardCheck(SCL_Board board,uint8_t white)
  1328. {
  1329. const char *square = board;
  1330. char kingChar = white ? 'K' : 'k';
  1331. for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++square)
  1332. if ((*square == kingChar &&
  1333. SCL_boardSquareAttacked(board,i,!white)))
  1334. return 1;
  1335. return 0;
  1336. }
  1337. uint8_t SCL_boardGameOver(SCL_Board board)
  1338. {
  1339. uint8_t position = SCL_boardGetPosition(board);
  1340. return (position == SCL_POSITION_MATE) ||
  1341. (position == SCL_POSITION_STALEMATE) ||
  1342. (position == SCL_POSITION_DEAD);
  1343. }
  1344. uint8_t SCL_boardMovePossible(SCL_Board board)
  1345. {
  1346. uint8_t white = SCL_boardWhitesTurn(board);
  1347. for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i)
  1348. {
  1349. char s = board[i];
  1350. if ((s != '.') && (SCL_pieceIsWhite(s) == white))
  1351. {
  1352. SCL_SquareSet moves;
  1353. SCL_boardGetMoves(board,i,moves);
  1354. if (SCL_squareSetSize(moves) != 0)
  1355. return 1;
  1356. }
  1357. }
  1358. return 0;
  1359. }
  1360. uint8_t SCL_boardMate(SCL_Board board)
  1361. {
  1362. return SCL_boardGetPosition(board) == SCL_POSITION_MATE;
  1363. }
  1364. void SCL_boardGetPseudoMoves(
  1365. SCL_Board board,
  1366. uint8_t pieceSquare,
  1367. uint8_t checkCastling,
  1368. SCL_SquareSet result)
  1369. {
  1370. char piece = board[pieceSquare];
  1371. SCL_squareSetClear(result);
  1372. uint8_t isWhite = SCL_pieceIsWhite(piece);
  1373. int8_t horizontalPosition = pieceSquare % 8;
  1374. int8_t pawnOffset = -8;
  1375. switch (piece)
  1376. {
  1377. case 'P':
  1378. pawnOffset = 8;
  1379. case 'p':
  1380. {
  1381. uint8_t square = pieceSquare + pawnOffset;
  1382. uint8_t verticalPosition = pieceSquare / 8;
  1383. if (board[square] == '.') // forward move
  1384. {
  1385. SCL_squareSetAdd(result,square);
  1386. if (verticalPosition == (1 + (piece == 'p') * 5)) // start position?
  1387. {
  1388. uint8_t square2 = square + pawnOffset;
  1389. if (board[square2] == '.')
  1390. SCL_squareSetAdd(result,square2);
  1391. }
  1392. }
  1393. #define checkDiagonal(hor,add) \
  1394. if (horizontalPosition != hor) \
  1395. { \
  1396. uint8_t square2 = square + add; \
  1397. char c = board[square2]; \
  1398. if (c != '.' && SCL_pieceIsWhite(c) != isWhite) \
  1399. SCL_squareSetAdd(result,square2); \
  1400. }
  1401. // diagonal moves
  1402. checkDiagonal(0,-1)
  1403. checkDiagonal(7,1)
  1404. uint8_t enPassantRow = 4;
  1405. uint8_t enemyPawn = 'p';
  1406. if (piece == 'p')
  1407. {
  1408. enPassantRow = 3;
  1409. enemyPawn = 'P';
  1410. }
  1411. // en-passant moves
  1412. if (verticalPosition == enPassantRow)
  1413. {
  1414. uint8_t enPassantColumn = board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & 0x0f;
  1415. uint8_t column = pieceSquare % 8;
  1416. for (int8_t offset = -1; offset < 2; offset += 2)
  1417. if ((enPassantColumn == column + offset) &&
  1418. (board[pieceSquare + offset] == enemyPawn))
  1419. {
  1420. SCL_squareSetAdd(result,pieceSquare + pawnOffset + offset);
  1421. break;
  1422. }
  1423. }
  1424. #undef checkDiagonal
  1425. }
  1426. break;
  1427. case 'r': // rook
  1428. case 'R':
  1429. case 'b': // bishop
  1430. case 'B':
  1431. case 'q': // queen
  1432. case 'Q':
  1433. {
  1434. const int8_t offsets[8] = {-8, 1, 8, -1, -7, 9, -9, 7};
  1435. const int8_t columnDirs[8] = { 0, 1, 0, -1, 1, 1, -1,-1};
  1436. uint8_t from = (piece == 'b' || piece == 'B') * 4;
  1437. uint8_t to = 4 + (piece != 'r' && piece != 'R') * 4;
  1438. for (uint8_t i = from; i < to; ++i)
  1439. {
  1440. int8_t offset = offsets[i];
  1441. int8_t columnDir = columnDirs[i];
  1442. int8_t square = pieceSquare;
  1443. int8_t col = horizontalPosition;
  1444. while (1)
  1445. {
  1446. square += offset;
  1447. col += columnDir;
  1448. if (square < 0 || square > 63 || col < 0 || col > 7)
  1449. break;
  1450. char squareC = board[square];
  1451. if (squareC == '.')
  1452. SCL_squareSetAdd(result,square);
  1453. else
  1454. {
  1455. if (SCL_pieceIsWhite(squareC) != isWhite)
  1456. SCL_squareSetAdd(result,square);
  1457. break;
  1458. }
  1459. }
  1460. }
  1461. }
  1462. break;
  1463. case 'n': // knight
  1464. case 'N':
  1465. {
  1466. const int8_t offsets[4] = {6, 10, 15, 17};
  1467. const int8_t columnsMinus[4] = {2,-2,1,-1};
  1468. const int8_t columnsPlus[4] = {-2,2,-1,1};
  1469. const int8_t *off, *col;
  1470. #define checkOffsets(op,comp,limit,dir)\
  1471. off = offsets;\
  1472. col = columns ## dir;\
  1473. for (uint8_t i = 0; i < 4; ++i, ++off, ++col)\
  1474. {\
  1475. int8_t square = pieceSquare op (*off);\
  1476. if (square comp limit) /* out of board? */\
  1477. break;\
  1478. int8_t horizontalCheck = horizontalPosition + (*col);\
  1479. if (horizontalCheck < 0 || horizontalCheck >= 8)\
  1480. continue;\
  1481. char squareC = board[square];\
  1482. if ((squareC == '.') || (SCL_pieceIsWhite(squareC) != isWhite))\
  1483. SCL_squareSetAdd(result,square);\
  1484. }
  1485. checkOffsets(-,<,0,Minus)
  1486. checkOffsets(+,>=,SCL_BOARD_SQUARES,Plus)
  1487. #undef checkOffsets
  1488. }
  1489. break;
  1490. case 'k': // king
  1491. case 'K':
  1492. {
  1493. uint8_t verticalPosition = pieceSquare / 8;
  1494. uint8_t
  1495. u = verticalPosition != 0,
  1496. d = verticalPosition != 7,
  1497. l = horizontalPosition != 0,
  1498. r = horizontalPosition != 7;
  1499. uint8_t square2 = pieceSquare - 9;
  1500. #define checkSquare(cond,add) \
  1501. if (cond && ((board[square2] == '.') || \
  1502. (SCL_pieceIsWhite(board[square2])) != isWhite))\
  1503. SCL_squareSetAdd(result,square2);\
  1504. square2 += add;
  1505. checkSquare(l && u,1)
  1506. checkSquare(u,1)
  1507. checkSquare(r && u,6)
  1508. checkSquare(l,2)
  1509. checkSquare(r,6)
  1510. checkSquare(l && d,1)
  1511. checkSquare(d,1)
  1512. checkSquare(r && d,0)
  1513. #undef checkSquare
  1514. // castling:
  1515. if (checkCastling)
  1516. {
  1517. uint8_t bitShift = 4 + 2 * (!isWhite);
  1518. if ((board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & (0x03 << bitShift)) &&
  1519. !SCL_boardSquareAttacked(board,pieceSquare,!isWhite)) // no check?
  1520. {
  1521. #if !SCL_960_CASTLING
  1522. // short castle:
  1523. pieceSquare++;
  1524. if ((board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & (0x01 << bitShift)) &&
  1525. (board[pieceSquare] == '.') &&
  1526. (board[pieceSquare + 1] == '.') &&
  1527. (board[pieceSquare + 2] == SCL_pieceToColor('r',isWhite)) &&
  1528. !SCL_boardSquareAttacked(board,pieceSquare,!isWhite))
  1529. SCL_squareSetAdd(result,pieceSquare + 1);
  1530. /* note: don't check the final square for check, it will potentially
  1531. be removed later (can't end up in check) */
  1532. // long castle:
  1533. pieceSquare -= 2;
  1534. if ((board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & (0x02 << bitShift)) &&
  1535. (board[pieceSquare] == '.') &&
  1536. (board[pieceSquare - 1] == '.') &&
  1537. (board[pieceSquare - 2] == '.') &&
  1538. (board[pieceSquare - 3] == SCL_pieceToColor('r',isWhite)) &&
  1539. !SCL_boardSquareAttacked(board,pieceSquare,!isWhite))
  1540. SCL_squareSetAdd(result,pieceSquare - 1);
  1541. #else // 960 castling
  1542. for (int i = 0; i < 2; ++i) // short and long
  1543. if (board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & ((i + 1) << bitShift))
  1544. {
  1545. uint8_t
  1546. rookPos = board[SCL_BOARD_EXTRA_BYTE] >> 3,
  1547. targetPos = 5;
  1548. if (i == 1)
  1549. {
  1550. rookPos = board[SCL_BOARD_EXTRA_BYTE] & 0x07,
  1551. targetPos = 3;
  1552. }
  1553. if (!isWhite)
  1554. {
  1555. rookPos += 56;
  1556. targetPos += 56;
  1557. }
  1558. uint8_t ok = board[rookPos] == SCL_pieceToColor('r',isWhite);
  1559. if (!ok)
  1560. continue;
  1561. int8_t inc = 1 - 2 * (targetPos > rookPos);
  1562. while (targetPos != rookPos) // check vacant squares for the rook
  1563. {
  1564. if (board[targetPos] != '.' &&
  1565. targetPos != pieceSquare)
  1566. {
  1567. ok = 0;
  1568. break;
  1569. }
  1570. targetPos += inc;
  1571. }
  1572. if (!ok)
  1573. continue;
  1574. targetPos = i == 0 ? 6 : 2;
  1575. if (!isWhite)
  1576. targetPos += 56;
  1577. inc = 1 - 2 * (targetPos > pieceSquare);
  1578. while (targetPos != pieceSquare) // check squares for the king
  1579. {
  1580. if ((board[targetPos] != '.' &&
  1581. targetPos != rookPos) ||
  1582. SCL_boardSquareAttacked(board,targetPos,!isWhite))
  1583. {
  1584. ok = 0;
  1585. break;
  1586. }
  1587. targetPos += inc;
  1588. }
  1589. if (ok)
  1590. SCL_squareSetAdd(result,rookPos);
  1591. }
  1592. #endif
  1593. }
  1594. }
  1595. }
  1596. break;
  1597. default:
  1598. break;
  1599. }
  1600. }
  1601. void SCL_printSquareSet(SCL_SquareSet set, SCL_PutCharFunction putCharFunc)
  1602. {
  1603. uint8_t first = 1;
  1604. putCharFunc('(');
  1605. for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i)
  1606. {
  1607. if (!SCL_squareSetContains(set, i))
  1608. continue;
  1609. if (!first)
  1610. putCharFunc(',');
  1611. else
  1612. first = 0;
  1613. putCharFunc('A' + i % 8);
  1614. putCharFunc('1' + i / 8);
  1615. }
  1616. putCharFunc(')');
  1617. }
  1618. void SCL_printSquareUTF8(uint8_t square, SCL_PutCharFunction putCharFunc)
  1619. {
  1620. uint32_t val = 0;
  1621. switch (square)
  1622. {
  1623. case 'r': val = 0x9c99e200; break;
  1624. case 'n': val = 0x9e99e200; break;
  1625. case 'b': val = 0x9d99e200; break;
  1626. case 'q': val = 0x9b99e200; break;
  1627. case 'k': val = 0x9a99e200; break;
  1628. case 'p': val = 0x9f99e200; break;
  1629. case 'R': val = 0x9699e200; break;
  1630. case 'N': val = 0x9899e200; break;
  1631. case 'B': val = 0x9799e200; break;
  1632. case 'Q': val = 0x9599e200; break;
  1633. case 'K': val = 0x9499e200; break;
  1634. case 'P': val = 0x9999e200; break;
  1635. case '.': val = 0x9296e200; break;
  1636. case ',': val = 0x9196e200; break;
  1637. default: putCharFunc(square); return; break;
  1638. }
  1639. uint8_t count = 4;
  1640. while ((val % 256 == 0) && (count > 0))
  1641. {
  1642. val /= 256;
  1643. count--;
  1644. }
  1645. while (count > 0)
  1646. {
  1647. putCharFunc(val % 256);
  1648. val /= 256;
  1649. count--;
  1650. }
  1651. }
  1652. void SCL_boardGetMoves(
  1653. SCL_Board board,
  1654. uint8_t pieceSquare,
  1655. SCL_SquareSet result)
  1656. {
  1657. SCL_SquareSet allMoves;
  1658. SCL_squareSetClear(allMoves);
  1659. for (uint8_t i = 0; i < 8; ++i)
  1660. result[i] = 0;
  1661. SCL_boardGetPseudoMoves(board,pieceSquare,1,allMoves);
  1662. // Now only keep moves that don't lead to one's check:
  1663. SCL_SQUARE_SET_ITERATE_BEGIN(allMoves)
  1664. SCL_MoveUndo undo = SCL_boardMakeMove(board,pieceSquare,iteratedSquare,'q');
  1665. if (!SCL_boardCheck(board,!SCL_boardWhitesTurn(board)))
  1666. SCL_squareSetAdd(result,iteratedSquare);
  1667. SCL_boardUndoMove(board,undo);
  1668. SCL_SQUARE_SET_ITERATE_END
  1669. }
  1670. uint8_t SCL_boardDead(SCL_Board board)
  1671. {
  1672. /*
  1673. This byte represents material by bits:
  1674. MSB _ _ _ _ _ _ _ _ LSB
  1675. | | | | | \_ white knight
  1676. | | | | \__ white bishop on white
  1677. | | | \____ white bishop on black
  1678. | | \________ black knight
  1679. | \__________ black bishop on white
  1680. \____________ black bishop on black
  1681. */
  1682. uint8_t material = 0;
  1683. const char *p = board;
  1684. for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i)
  1685. {
  1686. char c = *p;
  1687. switch (c)
  1688. {
  1689. case 'n': material |= 0x01; break;
  1690. case 'N': material |= 0x10; break;
  1691. case 'b': material |= (0x02 << (!SCL_squareIsWhite(i))); break;
  1692. case 'B': material |= (0x20 << (!SCL_squareIsWhite(i))); break;
  1693. case 'p':
  1694. case 'P':
  1695. case 'r':
  1696. case 'R':
  1697. case 'q':
  1698. case 'Q':
  1699. return 0; // REMOVE later if more complex check are performed
  1700. break;
  1701. default: break;
  1702. }
  1703. p++;
  1704. }
  1705. // TODO: add other checks than only insufficient material
  1706. // possible combinations of insufficient material:
  1707. return
  1708. (material == 0x00) || // king vs king
  1709. (material == 0x01) || // king and knight vs king
  1710. (material == 0x10) || // king and knight vs king
  1711. (material == 0x02) || // king and bishop vs king
  1712. (material == 0x20) || // king and bishop vs king
  1713. (material == 0x04) || // king and bishop vs king
  1714. (material == 0x40) || // king and bishop vs king
  1715. (material == 0x22) || // king and bishop vs king and bishop (same color)
  1716. (material == 0x44); // king and bishop vs king and bishop (same color)
  1717. }
  1718. uint8_t SCL_boardGetPosition(SCL_Board board)
  1719. {
  1720. uint8_t check = SCL_boardCheck(board,SCL_boardWhitesTurn(board));
  1721. uint8_t moves = SCL_boardMovePossible(board);
  1722. if (check)
  1723. return moves ? SCL_POSITION_CHECK : SCL_POSITION_MATE;
  1724. else if (!moves)
  1725. return SCL_POSITION_STALEMATE;
  1726. if (SCL_boardDead(board))
  1727. return SCL_POSITION_DEAD;
  1728. return SCL_POSITION_NORMAL;
  1729. }
  1730. uint8_t SCL_stringToMove(const char *moveString, uint8_t *resultFrom,
  1731. uint8_t *resultTo, char *resultPromotion)
  1732. {
  1733. char c;
  1734. uint8_t *dst = resultFrom;
  1735. for (uint8_t i = 0; i < 2; ++i)
  1736. {
  1737. c = *moveString;
  1738. *dst = (c >= 'a') ? (c - 'a') : (c - 'A');
  1739. if (*dst > 7)
  1740. return 0;
  1741. moveString++;
  1742. c = *moveString;
  1743. *dst += 8 * (c - '1');
  1744. if (*dst > 63)
  1745. return 0;
  1746. moveString++;
  1747. dst = resultTo;
  1748. }
  1749. c = *moveString;
  1750. if (c < 'A')
  1751. c = c - 'A' + 'a';
  1752. switch (c)
  1753. {
  1754. case 'N': case 'n': *resultPromotion = 'n'; break;
  1755. case 'B': case 'b': *resultPromotion = 'b'; break;
  1756. case 'R': case 'r': *resultPromotion = 'r'; break;
  1757. case 'Q': case 'q':
  1758. default: *resultPromotion = 'q'; break;
  1759. }
  1760. return 1;
  1761. }
  1762. void SCL_printBoard(
  1763. SCL_Board board,
  1764. SCL_PutCharFunction putCharFunc,
  1765. SCL_SquareSet highlightSquares,
  1766. uint8_t selectSquare,
  1767. uint8_t format,
  1768. uint8_t offset,
  1769. uint8_t labels,
  1770. uint8_t blackDown)
  1771. {
  1772. if (labels)
  1773. {
  1774. for (uint8_t i = 0; i < offset + 2; ++i)
  1775. putCharFunc(' ');
  1776. for (uint8_t i = 0; i < 8; ++i)
  1777. {
  1778. if ((format != SCL_PRINT_FORMAT_COMPACT) &&
  1779. (format != SCL_PRINT_FORMAT_COMPACT_UTF8))
  1780. putCharFunc(' ');
  1781. putCharFunc(blackDown ? ('H' - i) : ('A' + i));
  1782. }
  1783. putCharFunc('\n');
  1784. }
  1785. int8_t i = 7;
  1786. int8_t add = 1;
  1787. if (!blackDown)
  1788. {
  1789. i = 56;
  1790. add = -1;
  1791. }
  1792. for (int8_t row = 0; row < 8; ++row)
  1793. {
  1794. for (uint8_t j = 0; j < offset; ++j)
  1795. putCharFunc(' ');
  1796. if (labels)
  1797. {
  1798. putCharFunc(!blackDown ? ('8' - row) : ('1' + row));
  1799. putCharFunc(' ');
  1800. }
  1801. const char *square = board + i;
  1802. for (int8_t col = 0; col < 8; ++col)
  1803. {
  1804. switch (format)
  1805. {
  1806. case SCL_PRINT_FORMAT_COMPACT:
  1807. putCharFunc(
  1808. (*square == '.') ? (
  1809. ((i != selectSquare) ?
  1810. (!SCL_squareSetContains(highlightSquares,i) ? *square : '*')
  1811. : '#')) : *square);
  1812. break;
  1813. case SCL_PRINT_FORMAT_UTF8:
  1814. {
  1815. char squareChar = SCL_squareIsWhite(i) ? '.' : ',';
  1816. char pieceChar = (*square == '.') ? squareChar : *square;
  1817. if (i == selectSquare)
  1818. {
  1819. putCharFunc('(');
  1820. if (*square == '.')
  1821. putCharFunc(')');
  1822. else
  1823. SCL_printSquareUTF8(pieceChar,putCharFunc);
  1824. }
  1825. else if (!SCL_squareSetContains(highlightSquares,i))
  1826. {
  1827. SCL_printSquareUTF8(squareChar,putCharFunc);
  1828. SCL_printSquareUTF8(pieceChar,putCharFunc);
  1829. }
  1830. else
  1831. {
  1832. putCharFunc('[');
  1833. if (*square == '.')
  1834. putCharFunc(']');
  1835. else
  1836. SCL_printSquareUTF8(*square,putCharFunc);
  1837. }
  1838. break;
  1839. }
  1840. case SCL_PRINT_FORMAT_COMPACT_UTF8:
  1841. SCL_printSquareUTF8(
  1842. (*square == '.') ? (
  1843. SCL_squareSetContains(highlightSquares,i) ? '*' :
  1844. (i == selectSquare ? '#' : ((SCL_squareIsWhite(i) ? '.' : ',')))
  1845. ) : *square,putCharFunc);
  1846. break;
  1847. case SCL_PRINT_FORMAT_NORMAL:
  1848. default:
  1849. {
  1850. uint8_t c = *square;
  1851. char squareColor = SCL_squareIsWhite(i) ? ' ' : ':';
  1852. putCharFunc((i != selectSquare) ?
  1853. (!SCL_squareSetContains(highlightSquares,i) ?
  1854. squareColor : '#') : '@');
  1855. putCharFunc(c == '.' ? squareColor : *square);
  1856. break;
  1857. }
  1858. }
  1859. i -= add;
  1860. square -= add;
  1861. }
  1862. putCharFunc('\n');
  1863. i += add * 16;
  1864. } // for rows
  1865. }
  1866. int16_t SCL_pieceValuePositive(char piece)
  1867. {
  1868. switch (piece)
  1869. {
  1870. case 'p':
  1871. case 'P': return SCL_VALUE_PAWN; break;
  1872. case 'n':
  1873. case 'N': return SCL_VALUE_KNIGHT; break;
  1874. case 'b':
  1875. case 'B': return SCL_VALUE_BISHOP; break;
  1876. case 'r':
  1877. case 'R': return SCL_VALUE_ROOK; break;
  1878. case 'q':
  1879. case 'Q': return SCL_VALUE_QUEEN; break;
  1880. case 'k':
  1881. case 'K': return SCL_VALUE_KING; break;
  1882. default: break;
  1883. }
  1884. return 0;
  1885. }
  1886. int16_t SCL_pieceValue(char piece)
  1887. {
  1888. switch (piece)
  1889. {
  1890. case 'P': return SCL_VALUE_PAWN; break;
  1891. case 'N': return SCL_VALUE_KNIGHT; break;
  1892. case 'B': return SCL_VALUE_BISHOP; break;
  1893. case 'R': return SCL_VALUE_ROOK; break;
  1894. case 'Q': return SCL_VALUE_QUEEN; break;
  1895. case 'K': return SCL_VALUE_KING; break;
  1896. case 'p': return -1 * SCL_VALUE_PAWN; break;
  1897. case 'n': return -1 * SCL_VALUE_KNIGHT; break;
  1898. case 'b': return -1 * SCL_VALUE_BISHOP; break;
  1899. case 'r': return -1 * SCL_VALUE_ROOK; break;
  1900. case 'q': return -1 * SCL_VALUE_QUEEN; break;
  1901. case 'k': return -1 * SCL_VALUE_KING; break;
  1902. default: break;
  1903. }
  1904. return 0;
  1905. }
  1906. #define ATTACK_BONUS 3
  1907. #define MOBILITY_BONUS 10
  1908. #define CENTER_BONUS 7
  1909. #define CHECK_BONUS 5
  1910. #define KING_CASTLED_BONUS 30
  1911. #define KING_BACK_BONUS 15
  1912. #define KING_NOT_CENTER_BONUS 15
  1913. #define PAWN_NON_DOUBLE_BONUS 3
  1914. #define PAWN_PAIR_BONUS 3
  1915. #define KING_CENTERNESS 10
  1916. int16_t _SCL_rateKingEndgamePosition(uint8_t position)
  1917. {
  1918. int16_t result = 0;
  1919. uint8_t rank = position / 8;
  1920. position %= 8;
  1921. if (position > 1 && position < 6)
  1922. result += KING_CENTERNESS;
  1923. if (rank > 1 && rank < 6)
  1924. result += KING_CENTERNESS;
  1925. return result;
  1926. }
  1927. int16_t SCL_boardEvaluateStatic(SCL_Board board)
  1928. {
  1929. uint8_t position = SCL_boardGetPosition(board);
  1930. int16_t total = 0;
  1931. switch (position)
  1932. {
  1933. case SCL_POSITION_MATE:
  1934. return SCL_boardWhitesTurn(board) ?
  1935. -1 * SCL_EVALUATION_MAX_SCORE : SCL_EVALUATION_MAX_SCORE;
  1936. break;
  1937. case SCL_POSITION_STALEMATE:
  1938. case SCL_POSITION_DEAD:
  1939. return 0;
  1940. break;
  1941. /*
  1942. main points are assigned as follows:
  1943. - points for material as a sum of all material on board
  1944. - for playing side: if a piece attacks piece of greater value, a fraction
  1945. of the value difference is gained (we suppose exchange), this is only
  1946. gained once per every attacking piece (maximum gain is taken), we only
  1947. take fraction so that actually taking the piece is favored
  1948. - ATTACK_BONUS points for any attacked piece
  1949. other points are assigned as follows (in total these shouldn't be more
  1950. than the value of one pawn)
  1951. - mobility: MOBILITY_BONUS points for each piece with at least 4 possible
  1952. moves
  1953. - center control: CENTER_BONUS points for a piece on a center square
  1954. - CHECK_BONUS points for check
  1955. - king:
  1956. - safety (non endgame): KING_BACK_BONUS points for king on staring rank,
  1957. additional KING_CASTLED_BONUS if the kind if on castled square or
  1958. closer to the edge, additional KING_NOT_CENTER_BONUS for king not on
  1959. its start neighbouring center square
  1960. - center closeness (endgame): up to 2 * KING_CENTERNESS points for
  1961. being closer to center
  1962. - non-doubled pawns: PAWN_NON_DOUBLE_BONUS points for each pawn without
  1963. same color pawn directly in front of it
  1964. - pawn structure: PAWN_PAIR_BONUS points for each pawn guarding own pawn
  1965. - advancing pawns: 1 point for each pawn's rank in its move
  1966. direction
  1967. */
  1968. case SCL_POSITION_CHECK:
  1969. total += SCL_boardWhitesTurn(board) ? -1 * CHECK_BONUS : CHECK_BONUS;
  1970. case SCL_POSITION_NORMAL:
  1971. default:
  1972. {
  1973. SCL_SquareSet moves;
  1974. const char *p = board;
  1975. int16_t positiveMaterial = 0;
  1976. uint8_t endgame = 0;
  1977. // first count material to see if this is endgame or not
  1978. for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++p)
  1979. {
  1980. char s = *p;
  1981. if (s != '.')
  1982. {
  1983. positiveMaterial += SCL_pieceValuePositive(s);
  1984. total += SCL_pieceValue(s);
  1985. }
  1986. }
  1987. endgame = positiveMaterial <= SCL_ENDGAME_MATERIAL_LIMIT;
  1988. p = board;
  1989. for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++p)
  1990. {
  1991. char s = *p;
  1992. if (s != '.')
  1993. {
  1994. uint8_t white = SCL_pieceIsWhite(s);
  1995. switch (s)
  1996. {
  1997. case 'k': // king safety
  1998. if (endgame)
  1999. total -= _SCL_rateKingEndgamePosition(i);
  2000. else if (i >= 56)
  2001. {
  2002. total -= KING_BACK_BONUS;
  2003. if (i != 59)
  2004. {
  2005. total -= KING_NOT_CENTER_BONUS;
  2006. if (i >= 62 || i <= 58)
  2007. total -= KING_CASTLED_BONUS;
  2008. }
  2009. }
  2010. break;
  2011. case 'K':
  2012. if (endgame)
  2013. total += _SCL_rateKingEndgamePosition(i);
  2014. else if (i <= 7)
  2015. {
  2016. total += KING_BACK_BONUS;
  2017. if (i != 3)
  2018. {
  2019. total += KING_NOT_CENTER_BONUS;
  2020. if (i <= 2 || i >= 6)
  2021. total += KING_CASTLED_BONUS;
  2022. }
  2023. }
  2024. break;
  2025. case 'P': // pawns
  2026. case 'p':
  2027. {
  2028. int8_t rank = i / 8;
  2029. if (rank != 0 && rank != 7)
  2030. {
  2031. if (s == 'P')
  2032. {
  2033. total += rank;
  2034. char *tmp = board + i + 8;
  2035. if (*tmp != 'P')
  2036. total += PAWN_NON_DOUBLE_BONUS;
  2037. if (i % 8 != 7)
  2038. {
  2039. tmp++;
  2040. if (*tmp == 'P')
  2041. total += PAWN_PAIR_BONUS;
  2042. if (*(tmp - 16) == 'P')
  2043. total += PAWN_PAIR_BONUS;
  2044. }
  2045. }
  2046. else
  2047. {
  2048. total -= 7 - rank;
  2049. char *tmp = board + i - 8;
  2050. if (*tmp != 'p')
  2051. total -= PAWN_NON_DOUBLE_BONUS;
  2052. if (i % 8 != 7)
  2053. {
  2054. tmp += 17;
  2055. if (*tmp == 'p')
  2056. total -= PAWN_PAIR_BONUS;
  2057. if (*(tmp - 16) == 'p')
  2058. total -= PAWN_PAIR_BONUS;
  2059. }
  2060. }
  2061. }
  2062. break;
  2063. }
  2064. default: break;
  2065. }
  2066. if (i >= 27 && i <= 36 && (i >= 35 || i <= 28)) // center control
  2067. total += white ? CENTER_BONUS : (-1 * CENTER_BONUS);
  2068. // for performance we only take pseudo moves
  2069. SCL_boardGetPseudoMoves(board,i,0,moves);
  2070. if (SCL_squareSetSize(moves) >= 4) // mobility
  2071. total += white ?
  2072. MOBILITY_BONUS : (-1 * MOBILITY_BONUS);
  2073. int16_t exchangeBonus = 0;
  2074. SCL_SQUARE_SET_ITERATE_BEGIN(moves)
  2075. if (board[iteratedSquare] != '.')
  2076. {
  2077. total += white ?
  2078. ATTACK_BONUS : (- 1 * ATTACK_BONUS);
  2079. if (SCL_boardWhitesTurn(board) == white)
  2080. {
  2081. int16_t valueDiff =
  2082. SCL_pieceValuePositive(board[iteratedSquare]) -
  2083. SCL_pieceValuePositive(s);
  2084. valueDiff /= 4; // only take a fraction to favor taking
  2085. if (valueDiff > exchangeBonus)
  2086. exchangeBonus = valueDiff;
  2087. }
  2088. }
  2089. SCL_SQUARE_SET_ITERATE_END
  2090. if (exchangeBonus != 0)
  2091. total += white ? exchangeBonus : -1 * exchangeBonus;
  2092. }
  2093. } // for each square
  2094. return total;
  2095. break;
  2096. } // normal position
  2097. } // switch
  2098. return 0;
  2099. }
  2100. #undef ATTACK_BONUS
  2101. #undef MOBILITY_BONUS
  2102. #undef CENTER_BONUS
  2103. #undef CHECK_BONUS
  2104. #undef KING_CASTLED_BONUS
  2105. #undef KING_BACK_BONUS
  2106. #undef PAWN_NON_DOUBLE_BONUS
  2107. #undef PAWN_PAIR_BONUS
  2108. #undef KING_CENTERNESS
  2109. SCL_StaticEvaluationFunction _SCL_staticEvaluationFunction;
  2110. int16_t _SCL_currentEval;
  2111. int8_t _SCL_depthHardLimit;
  2112. /**
  2113. Inner recursive function for SCL_boardEvaluateDynamic. It is passed a square
  2114. (or -1) at which last capture happened, to implement capture extension.
  2115. */
  2116. int16_t _SCL_boardEvaluateDynamic(SCL_Board board, int8_t depth,
  2117. int16_t alphaBeta, int8_t takenSquare)
  2118. {
  2119. #if SCL_COUNT_EVALUATED_POSITIONS
  2120. SCL_positionsEvaluated++;
  2121. #endif
  2122. #if SCL_CALL_WDT_RESET
  2123. wdt_reset();
  2124. #endif
  2125. uint8_t whitesTurn = SCL_boardWhitesTurn(board);
  2126. int8_t valueMultiply = whitesTurn ? 1 : -1;
  2127. int16_t bestMoveValue = -1 * SCL_EVALUATION_MAX_SCORE;
  2128. uint8_t shouldCompute = depth > 0;
  2129. uint8_t extended = 0;
  2130. uint8_t positionType = SCL_boardGetPosition(board);
  2131. if (!shouldCompute)
  2132. {
  2133. /* here we do two extensions (deeper search): taking on a same square
  2134. (exchanges) and checks (good for mating and preventing mates): */
  2135. extended =
  2136. (depth > _SCL_depthHardLimit) &&
  2137. (takenSquare >= 0 ||
  2138. (SCL_boardGetPosition(board) == SCL_POSITION_CHECK));
  2139. shouldCompute = extended;
  2140. }
  2141. #if SCL_DEBUG_AI
  2142. char moveStr[8];
  2143. uint8_t debugFirst = 1;
  2144. #endif
  2145. if (shouldCompute &&
  2146. (positionType == SCL_POSITION_NORMAL || positionType == SCL_POSITION_CHECK))
  2147. {
  2148. #if SCL_DEBUG_AI
  2149. putchar('(');
  2150. #endif
  2151. alphaBeta *= valueMultiply;
  2152. uint8_t end = 0;
  2153. const char *b = board;
  2154. depth--;
  2155. for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++b)
  2156. {
  2157. char s = *b;
  2158. if (s != '.' && SCL_pieceIsWhite(s) == whitesTurn)
  2159. {
  2160. SCL_SquareSet moves;
  2161. SCL_squareSetClear(moves);
  2162. SCL_boardGetMoves(board,i,moves);
  2163. if (!SCL_squareSetEmpty(moves))
  2164. {
  2165. SCL_SQUARE_SET_ITERATE_BEGIN(moves)
  2166. int8_t captureExtension = -1;
  2167. if (board[iteratedSquare] != '.' && // takes a piece
  2168. (takenSquare == -1 || // extend on first taken sq.
  2169. (extended && takenSquare != -1) || // ignore check extension
  2170. (iteratedSquare == takenSquare))) // extend on same sq. taken
  2171. captureExtension = iteratedSquare;
  2172. SCL_MoveUndo undo = SCL_boardMakeMove(board,i,iteratedSquare,'q');
  2173. uint8_t s0Dummy, s1Dummy;
  2174. char pDummy;
  2175. SCL_UNUSED(s0Dummy);
  2176. SCL_UNUSED(s1Dummy);
  2177. SCL_UNUSED(pDummy);
  2178. #if SCL_DEBUG_AI
  2179. if (debugFirst)
  2180. debugFirst = 0;
  2181. else
  2182. putchar(',');
  2183. if (extended)
  2184. putchar('*');
  2185. printf("%s ",SCL_moveToString(board,i,iteratedSquare,'q',moveStr));
  2186. #endif
  2187. int16_t value = _SCL_boardEvaluateDynamic(
  2188. board,
  2189. depth, // this is depth - 1, we decremented it
  2190. #if SCL_ALPHA_BETA
  2191. valueMultiply * bestMoveValue,
  2192. #else
  2193. 0,
  2194. #endif
  2195. captureExtension
  2196. ) * valueMultiply;
  2197. SCL_boardUndoMove(board,undo);
  2198. if (value > bestMoveValue)
  2199. {
  2200. bestMoveValue = value;
  2201. #if SCL_ALPHA_BETA
  2202. // alpha-beta pruning:
  2203. if (value > alphaBeta) // no, >= can't be here
  2204. {
  2205. end = 1;
  2206. iterationEnd = 1;
  2207. }
  2208. #endif
  2209. }
  2210. SCL_SQUARE_SET_ITERATE_END
  2211. } // !squre set empty?
  2212. } // valid piece?
  2213. if (end)
  2214. break;
  2215. } // for each square
  2216. #if SCL_DEBUG_AI
  2217. putchar(')');
  2218. #endif
  2219. }
  2220. else // don't dive recursively, evaluate statically
  2221. {
  2222. bestMoveValue = valueMultiply *
  2223. #ifndef SCL_EVALUATION_FUNCTION
  2224. _SCL_staticEvaluationFunction(board);
  2225. #else
  2226. SCL_EVALUATION_FUNCTION(board);
  2227. #endif
  2228. /* For stalemate return the opposite value of the board, i.e. if the
  2229. position is good for white, then stalemate is good for black and vice
  2230. versa. */
  2231. if (positionType == SCL_POSITION_STALEMATE)
  2232. bestMoveValue *= -1;
  2233. }
  2234. /* Here we either improve (if the move worsens the situation) or devalve (if
  2235. it improves the situation) the result: this needs to be done so that good
  2236. moves far away are seen as worse compared to equally good moves achieved
  2237. in fewer moves. Without this an AI in winning situation may just repeat
  2238. random moves and draw by repetition even if it has mate in 1 (it sees all
  2239. moves as leading to mate). */
  2240. bestMoveValue += bestMoveValue > _SCL_currentEval * valueMultiply ? -1 : 1;
  2241. #if SCL_DEBUG_AI
  2242. printf("%d",bestMoveValue * valueMultiply);
  2243. #endif
  2244. return bestMoveValue * valueMultiply;
  2245. }
  2246. int16_t SCL_boardEvaluateDynamic(SCL_Board board, uint8_t baseDepth,
  2247. uint8_t extensionExtraDepth, SCL_StaticEvaluationFunction evalFunction)
  2248. {
  2249. _SCL_staticEvaluationFunction = evalFunction;
  2250. _SCL_currentEval = evalFunction(board);
  2251. _SCL_depthHardLimit = 0;
  2252. _SCL_depthHardLimit -= extensionExtraDepth;
  2253. return _SCL_boardEvaluateDynamic(
  2254. board,
  2255. baseDepth,
  2256. SCL_boardWhitesTurn(board) ?
  2257. SCL_EVALUATION_MAX_SCORE : (-1 * SCL_EVALUATION_MAX_SCORE),-1);
  2258. }
  2259. void SCL_boardRandomMove(SCL_Board board, SCL_RandomFunction randFunc,
  2260. uint8_t *squareFrom, uint8_t *squareTo, char *resultProm)
  2261. {
  2262. *resultProm = (randFunc() < 128) ?
  2263. ((randFunc() < 128) ? 'r' : 'n') :
  2264. ((randFunc() < 128) ? 'b' : 'q');
  2265. SCL_SquareSet set;
  2266. uint8_t white = SCL_boardWhitesTurn(board);
  2267. const char *s = board;
  2268. SCL_squareSetClear(set);
  2269. // find squares with pieces that have legal moves
  2270. for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++s)
  2271. {
  2272. char c = *s;
  2273. if (c != '.' && SCL_pieceIsWhite(c) == white)
  2274. {
  2275. SCL_SquareSet moves;
  2276. SCL_boardGetMoves(board,i,moves);
  2277. if (SCL_squareSetSize(moves) != 0)
  2278. SCL_squareSetAdd(set,i);
  2279. }
  2280. }
  2281. *squareFrom = SCL_squareSetGetRandom(set,randFunc);
  2282. SCL_boardGetMoves(board,*squareFrom,set);
  2283. *squareTo = SCL_squareSetGetRandom(set,randFunc);
  2284. }
  2285. void SCL_printBoardSimple(
  2286. SCL_Board board,
  2287. SCL_PutCharFunction putCharFunc,
  2288. uint8_t selectSquare,
  2289. uint8_t format)
  2290. {
  2291. SCL_SquareSet s;
  2292. SCL_squareSetClear(s);
  2293. SCL_printBoard(board,putCharFunc,s,selectSquare,format,1,1,0);
  2294. }
  2295. int16_t SCL_getAIMove(
  2296. SCL_Board board,
  2297. uint8_t baseDepth,
  2298. uint8_t extensionExtraDepth,
  2299. uint8_t endgameExtraDepth,
  2300. SCL_StaticEvaluationFunction evalFunc,
  2301. SCL_RandomFunction randFunc,
  2302. uint8_t randomness,
  2303. uint8_t repetitionMoveFrom,
  2304. uint8_t repetitionMoveTo,
  2305. uint8_t *resultFrom,
  2306. uint8_t *resultTo,
  2307. char *resultProm)
  2308. {
  2309. #if SCL_DEBUG_AI
  2310. puts("===== AI debug =====");
  2311. putchar('(');
  2312. unsigned char debugFirst = 1;
  2313. char moveStr[8];
  2314. #endif
  2315. if (baseDepth == 0)
  2316. {
  2317. SCL_boardRandomMove(board,randFunc,resultFrom,resultTo,resultProm);
  2318. #ifndef SCL_EVALUATION_FUNCTION
  2319. return evalFunc(board);
  2320. #else
  2321. return SCL_EVALUATION_FUNCTION(board);
  2322. #endif
  2323. }
  2324. if (SCL_boardEstimatePhase(board) == SCL_PHASE_ENDGAME)
  2325. baseDepth += endgameExtraDepth;
  2326. *resultFrom = 0;
  2327. *resultTo = 0;
  2328. *resultProm = 'q';
  2329. int16_t bestScore =
  2330. SCL_boardWhitesTurn(board) ?
  2331. -1 * SCL_EVALUATION_MAX_SCORE - 1 : (SCL_EVALUATION_MAX_SCORE + 1);
  2332. for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i)
  2333. if (board[i] != '.' &&
  2334. SCL_boardWhitesTurn(board) == SCL_pieceIsWhite(board[i]))
  2335. {
  2336. SCL_SquareSet moves;
  2337. SCL_squareSetClear(moves);
  2338. SCL_boardGetMoves(board,i,moves);
  2339. SCL_SQUARE_SET_ITERATE_BEGIN(moves)
  2340. int16_t score = 0;
  2341. #if SCL_DEBUG_AI
  2342. if (debugFirst)
  2343. debugFirst = 0;
  2344. else
  2345. putchar(',');
  2346. printf("%s ",SCL_moveToString(
  2347. board,i,iteratedSquare,'q',moveStr));
  2348. #endif
  2349. if (i != repetitionMoveFrom || iteratedSquare != repetitionMoveTo)
  2350. {
  2351. SCL_MoveUndo undo = SCL_boardMakeMove(board,i,iteratedSquare,'q');
  2352. score = SCL_boardEvaluateDynamic(board,baseDepth - 1,
  2353. extensionExtraDepth,evalFunc);
  2354. SCL_boardUndoMove(board,undo);
  2355. }
  2356. if (randFunc != 0 &&
  2357. randomness > 1 &&
  2358. score < 16000 &&
  2359. score > -16000)
  2360. {
  2361. /*^ We limit randomizing by about half the max score for two reasons:
  2362. to prevent over/under flows and secondly we don't want to alter
  2363. the highest values for checkmate -- these are modified by tiny
  2364. values depending on their depth so as to prevent endless loops in
  2365. which most moves are winning, biasing such values would completely
  2366. kill that algorithm */
  2367. int16_t bias = randFunc();
  2368. bias = (bias - 128) / 2;
  2369. bias *= randomness - 1;
  2370. score += bias;
  2371. }
  2372. uint8_t comparison =
  2373. score == bestScore;
  2374. if ((comparison != 1) &&
  2375. (
  2376. (SCL_boardWhitesTurn(board) && score > bestScore) ||
  2377. (!SCL_boardWhitesTurn(board) && score < bestScore)
  2378. ))
  2379. comparison = 2;
  2380. uint8_t replace = 0;
  2381. if (randFunc == 0)
  2382. replace = comparison == 2;
  2383. else
  2384. replace = (comparison == 2) ||
  2385. ((comparison == 1) && (randFunc() < 160)); // not uniform distr. but simple
  2386. if (replace)
  2387. {
  2388. *resultFrom = i;
  2389. *resultTo = iteratedSquare;
  2390. bestScore = score;
  2391. }
  2392. SCL_SQUARE_SET_ITERATE_END
  2393. }
  2394. #if SCL_DEBUG_AI
  2395. printf(")%d %s\n",bestScore,SCL_moveToString(board,*resultFrom,*resultTo,'q',moveStr));
  2396. puts("===== AI debug end ===== ");
  2397. #endif
  2398. return bestScore;
  2399. }
  2400. uint8_t SCL_boardToFEN(SCL_Board board, char *string)
  2401. {
  2402. uint8_t square = 56;
  2403. uint8_t spaces = 0;
  2404. uint8_t result = 0;
  2405. #define put(c) { *string = (c); string++; result++; }
  2406. while (1) // pieces
  2407. {
  2408. char s = board[square];
  2409. if (s == '.')
  2410. {
  2411. spaces++;
  2412. }
  2413. else
  2414. {
  2415. if (spaces != 0)
  2416. {
  2417. put('0' + spaces)
  2418. spaces = 0;
  2419. }
  2420. put(s)
  2421. }
  2422. square++;
  2423. if (square % 8 == 0)
  2424. {
  2425. if (spaces != 0)
  2426. {
  2427. put('0' + spaces)
  2428. spaces = 0;
  2429. }
  2430. if (square == 8)
  2431. break;
  2432. put('/');
  2433. square -= 16;
  2434. }
  2435. }
  2436. put(' ');
  2437. put(SCL_boardWhitesTurn(board) ? 'w' : 'b');
  2438. put(' ');
  2439. uint8_t b = board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & 0xf0;
  2440. if (b != 0) // castling
  2441. {
  2442. if (b & 0x10) put('K');
  2443. if (b & 0x20) put('Q');
  2444. if (b & 0x40) put('k');
  2445. if (b & 0x80) put('q');
  2446. }
  2447. else
  2448. put('-');
  2449. put(' ');
  2450. b = board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & 0x0f;
  2451. if (b < 8)
  2452. {
  2453. put('a' + b);
  2454. put(SCL_boardWhitesTurn(board) ? '6' : '3');
  2455. }
  2456. else
  2457. put('-');
  2458. for (uint8_t i = 0; i < 2; ++i)
  2459. {
  2460. put(' ');
  2461. uint8_t moves = i == 0 ?
  2462. ((uint8_t) board[SCL_BOARD_MOVE_COUNT_BYTE]) :
  2463. (((uint8_t) board[SCL_BOARD_PLY_BYTE]) / 2 + 1);
  2464. uint8_t hundreds = moves / 100;
  2465. uint8_t tens = (moves % 100) / 10;
  2466. if (hundreds != 0)
  2467. {
  2468. put('0' + hundreds);
  2469. put('0' + tens);
  2470. }
  2471. else if (tens != 0)
  2472. put('0' + tens);
  2473. put('0' + moves % 10);
  2474. }
  2475. *string = 0; // terminate the string
  2476. return result + 1;
  2477. #undef put
  2478. }
  2479. uint8_t SCL_boardFromFEN(SCL_Board board, const char *string)
  2480. {
  2481. uint8_t square = 56;
  2482. while (1)
  2483. {
  2484. char c = *string;
  2485. if (c == 0)
  2486. return 0;
  2487. if (c != '/' && c != ' ') // ignore line separators
  2488. {
  2489. if (c < '9') // empty square sequence
  2490. {
  2491. while (c > '0')
  2492. {
  2493. board[square] = '.';
  2494. square++;
  2495. c--;
  2496. }
  2497. }
  2498. else // piece
  2499. {
  2500. board[square] = c;
  2501. square++;
  2502. }
  2503. }
  2504. else
  2505. {
  2506. if (square == 8)
  2507. break;
  2508. square -= 16;
  2509. }
  2510. string++;
  2511. }
  2512. #define nextChar string++; if (*string == 0) return 0;
  2513. nextChar // space
  2514. board[SCL_BOARD_PLY_BYTE] = *string == 'b';
  2515. nextChar
  2516. nextChar // space
  2517. uint8_t castleEnPassant = 0x0;
  2518. while (*string != ' ')
  2519. {
  2520. switch (*string)
  2521. {
  2522. case 'K': castleEnPassant |= 0x10; break;
  2523. case 'Q': castleEnPassant |= 0x20; break;
  2524. case 'k': castleEnPassant |= 0x40; break;
  2525. case 'q': castleEnPassant |= 0x80; break;
  2526. default: castleEnPassant |= 0xf0; break; // for partial XFEN compat.
  2527. }
  2528. nextChar
  2529. }
  2530. nextChar // space
  2531. if (*string != '-')
  2532. {
  2533. castleEnPassant |= *string - 'a';
  2534. nextChar
  2535. }
  2536. else
  2537. castleEnPassant |= 0x0f;
  2538. nextChar
  2539. board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] = castleEnPassant;
  2540. for (uint8_t i = 0; i < 2; ++i)
  2541. {
  2542. nextChar // space
  2543. uint8_t ply = 0;
  2544. while (1)
  2545. {
  2546. char c = *string;
  2547. if (c < '0' || c > '9')
  2548. break;
  2549. ply = ply * 10 + (c - '0');
  2550. string++;
  2551. }
  2552. if (i == 0 && *string == 0)
  2553. return 0;
  2554. if (i == 0)
  2555. board[SCL_BOARD_MOVE_COUNT_BYTE] = ply;
  2556. else
  2557. board[SCL_BOARD_PLY_BYTE] += (ply - 1) * 2;
  2558. }
  2559. #if SCL_960_CASTLING
  2560. _SCL_board960RememberRookPositions(board);
  2561. #endif
  2562. return 1;
  2563. #undef nextChar
  2564. }
  2565. uint8_t SCL_boardEstimatePhase(SCL_Board board)
  2566. {
  2567. uint16_t totalMaterial = 0;
  2568. uint8_t ply = board[SCL_BOARD_PLY_BYTE];
  2569. for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i)
  2570. {
  2571. char s = *board;
  2572. if (s != '.')
  2573. {
  2574. int16_t v = SCL_pieceValue(s);
  2575. if (!SCL_pieceIsWhite(s))
  2576. v *= -1;
  2577. totalMaterial += v;
  2578. }
  2579. board++;
  2580. }
  2581. if (totalMaterial < SCL_ENDGAME_MATERIAL_LIMIT)
  2582. return SCL_PHASE_ENDGAME;
  2583. if (ply <= 10 && (totalMaterial >= SCL_START_MATERIAL - 3 * SCL_VALUE_PAWN))
  2584. return SCL_PHASE_OPENING;
  2585. return SCL_PHASE_MIDGAME;
  2586. }
  2587. #define SCL_IMAGE_COUNT 12
  2588. static const uint8_t SCL_images[8 * SCL_IMAGE_COUNT] =
  2589. {
  2590. 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
  2591. 0xff,0x81,0xff,0xff,0xff,0xff,0xff,0x81,0xff,0xff,0xff,0xff,
  2592. 0xff,0x81,0xe7,0xf7,0xf7,0xaa,0xff,0xbd,0xe7,0xf7,0xf7,0xaa,
  2593. 0xff,0xc3,0xc3,0xe3,0xc1,0x80,0xff,0x99,0xdb,0xeb,0xc9,0x94,
  2594. 0xe7,0xc3,0x81,0xc1,0x94,0x80,0xe7,0xdb,0xbd,0xdd,0xbe,0xbe,
  2595. 0xc3,0xc3,0x91,0xe3,0x80,0x80,0xdb,0x99,0x8d,0xeb,0xaa,0xbe,
  2596. 0xc3,0x81,0xe1,0xc1,0xc1,0xc1,0xdb,0xbd,0xdd,0xe3,0xdd,0xdd,
  2597. 0x81,0x81,0xc1,0x9c,0xc1,0xc1,0x81,0x81,0xc1,0x9c,0xc1,0xc1
  2598. };
  2599. void SCL_drawBoard(
  2600. SCL_Board board,
  2601. SCL_PutPixelFunction putPixel,
  2602. uint8_t selectedSquare,
  2603. SCL_SquareSet highlightSquares,
  2604. uint8_t blackDown)
  2605. {
  2606. uint8_t row = 0;
  2607. uint8_t col = 0;
  2608. uint8_t x = 0;
  2609. uint8_t y = 0;
  2610. uint16_t n = 0;
  2611. uint8_t s = 0;
  2612. uint8_t pictureLine = 0;
  2613. uint8_t loadLine = 1;
  2614. while (row < 8)
  2615. {
  2616. if (loadLine)
  2617. {
  2618. s = blackDown ? (row * 8 + (7 - col)) : ((7 - row) * 8 + col);
  2619. char piece = board[s];
  2620. if (piece == '.')
  2621. pictureLine = (y == 4) ? 0xef : 0xff;
  2622. else
  2623. {
  2624. uint8_t offset = SCL_pieceIsWhite(piece) ? 6 : 0;
  2625. piece = SCL_pieceToColor(piece,1);
  2626. switch (piece)
  2627. {
  2628. case 'R': offset += 1; break;
  2629. case 'N': offset += 2; break;
  2630. case 'B': offset += 3; break;
  2631. case 'K': offset += 4; break;
  2632. case 'Q': offset += 5; break;
  2633. default: break;
  2634. }
  2635. pictureLine = SCL_images[y * SCL_IMAGE_COUNT + offset];
  2636. }
  2637. if (SCL_squareSetContains(highlightSquares,s))
  2638. pictureLine &= (y % 2) ? 0xaa : 0x55;
  2639. if (s == selectedSquare)
  2640. pictureLine &= (y == 0 || y == 7) ? 0x00 : ~0x81;
  2641. loadLine = 0;
  2642. }
  2643. putPixel(pictureLine & 0x80,n);
  2644. pictureLine <<= 1;
  2645. n++;
  2646. x++;
  2647. if (x == 8)
  2648. {
  2649. col++;
  2650. loadLine = 1;
  2651. x = 0;
  2652. }
  2653. if (col == 8)
  2654. {
  2655. y++;
  2656. col = 0;
  2657. x = 0;
  2658. }
  2659. if (y == 8)
  2660. {
  2661. row++;
  2662. y = 0;
  2663. }
  2664. }
  2665. }
  2666. uint32_t SCL_boardHash32(const SCL_Board board)
  2667. {
  2668. uint32_t result = (board[SCL_BOARD_PLY_BYTE] & 0x01) +
  2669. (((uint32_t) ((uint8_t) board[SCL_BOARD_ENPASSANT_CASTLE_BYTE])) << 24) +
  2670. board[SCL_BOARD_MOVE_COUNT_BYTE];
  2671. const char *b = board;
  2672. for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++b)
  2673. {
  2674. switch (*b)
  2675. {
  2676. #define C(p,n) case p: result ^= (i + 1) * n; break;
  2677. // the below number are primes
  2678. C('P',4003)
  2679. C('R',84673)
  2680. C('N',93911)
  2681. C('B',999331)
  2682. C('Q',909091)
  2683. C('K',2796203)
  2684. C('p',4793)
  2685. C('r',19391)
  2686. C('n',391939)
  2687. C('b',108301)
  2688. C('q',174763)
  2689. C('k',2474431)
  2690. #undef C
  2691. default: break;
  2692. }
  2693. }
  2694. // for extra spread of values we swap the low/high parts:
  2695. result = (result >> 16) | (result << 16);
  2696. return result;
  2697. }
  2698. void SCL_boardDisableCastling(SCL_Board board)
  2699. {
  2700. board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= 0x0f;
  2701. }
  2702. uint8_t SCL_boardMoveResetsCount(SCL_Board board,
  2703. uint8_t squareFrom, uint8_t squareTo)
  2704. {
  2705. return board[squareFrom] == 'P' || board[squareFrom] == 'p' ||
  2706. board[squareTo] != '.';
  2707. }
  2708. void SCL_printPGN(SCL_Record r, SCL_PutCharFunction putCharFunc,
  2709. SCL_Board initialState)
  2710. {
  2711. if (SCL_recordLength(r) == 0)
  2712. return;
  2713. uint16_t pos = 0;
  2714. SCL_Board board;
  2715. if (initialState != 0)
  2716. for (uint8_t i = 0; i < SCL_BOARD_STATE_SIZE; ++i)
  2717. board[i] = initialState[i];
  2718. else
  2719. SCL_boardInit(board);
  2720. while (1)
  2721. {
  2722. uint8_t s0, s1;
  2723. char p;
  2724. uint8_t state = SCL_recordGetMove(r,pos,&s0,&s1,&p);
  2725. pos++;
  2726. if (pos % 2)
  2727. {
  2728. uint8_t move = pos / 2 + 1;
  2729. if (move / 100 != 0)
  2730. putCharFunc('0' + move / 100);
  2731. if (move / 10 != 0 || move / 100 != 0)
  2732. putCharFunc('0' + (move % 100) / 10);
  2733. putCharFunc('0' + move % 10);
  2734. putCharFunc('.');
  2735. putCharFunc(' ');
  2736. }
  2737. #if !SCL_960_CASTLING
  2738. if ((board[s0] == 'K' && s0 == 4 && (s1 == 2 || s1 == 6)) ||
  2739. (board[s0] == 'k' && s0 == 60 && (s1 == 62 || s1 == 58)))
  2740. #else
  2741. if ((board[s0] == 'K' && board[s1] == 'R') ||
  2742. (board[s0] == 'k' && board[s1] == 'r'))
  2743. #endif
  2744. {
  2745. putCharFunc('O');
  2746. putCharFunc('-');
  2747. putCharFunc('O');
  2748. #if !SCL_960_CASTLING
  2749. if (s1 == 58 || s1 == 2)
  2750. #else
  2751. if ((s1 == (board[SCL_BOARD_EXTRA_BYTE] & 0x07)) ||
  2752. (s1 == 56 + (board[SCL_BOARD_EXTRA_BYTE] & 0x07)))
  2753. #endif
  2754. {
  2755. putCharFunc('-');
  2756. putCharFunc('O');
  2757. }
  2758. }
  2759. else
  2760. {
  2761. uint8_t pawn = board[s0] == 'P' || board[s0] == 'p';
  2762. if (!pawn)
  2763. {
  2764. putCharFunc(SCL_pieceToColor(board[s0],1));
  2765. // disambiguation:
  2766. uint8_t specify = 0;
  2767. for (int i = 0; i < SCL_BOARD_SQUARES; ++i)
  2768. if (i != s0 && board[i] == board[s0])
  2769. {
  2770. SCL_SquareSet s;
  2771. SCL_squareSetClear(s);
  2772. SCL_boardGetMoves(board,i,s);
  2773. if (SCL_squareSetContains(s,s1))
  2774. specify |= (s0 % 8 != s1 % 8) ? 1 : 2;
  2775. }
  2776. if (specify & 0x01)
  2777. putCharFunc('a' + s0 % 8);
  2778. if (specify & 0x02)
  2779. putCharFunc('1' + s0 / 8);
  2780. }
  2781. if (board[s1] != '.' ||
  2782. (pawn && s0 % 8 != s1 % 8 && board[s1] == '.')) // capture?
  2783. {
  2784. if (pawn)
  2785. putCharFunc('a' + s0 % 8);
  2786. putCharFunc('x');
  2787. }
  2788. putCharFunc('a' + s1 % 8);
  2789. putCharFunc('1' + s1 / 8);
  2790. if (pawn && (s1 >= 56 || s1 <= 7)) // promotion?
  2791. {
  2792. putCharFunc('=');
  2793. putCharFunc(SCL_pieceToColor(p,1));
  2794. }
  2795. }
  2796. SCL_boardMakeMove(board,s0,s1,p);
  2797. uint8_t position = SCL_boardGetPosition(board);
  2798. if (position == SCL_POSITION_CHECK)
  2799. putCharFunc('+');
  2800. if (position == SCL_POSITION_MATE)
  2801. {
  2802. putCharFunc('#');
  2803. break;
  2804. }
  2805. else if (state != SCL_RECORD_CONT)
  2806. {
  2807. putCharFunc('*');
  2808. break;
  2809. }
  2810. putCharFunc(' ');
  2811. }
  2812. }
  2813. void SCL_recordCopy(SCL_Record recordFrom, SCL_Record recordTo)
  2814. {
  2815. for (uint16_t i = 0; i < SCL_RECORD_MAX_SIZE; ++i)
  2816. recordTo[i] = recordFrom[i];
  2817. }
  2818. void SCL_gameInit(SCL_Game *game, const SCL_Board startState)
  2819. {
  2820. game->startState = startState;
  2821. if (startState != 0)
  2822. SCL_boardCopy(startState,game->board);
  2823. else
  2824. SCL_boardInit(game->board);
  2825. SCL_recordInit(game->record);
  2826. for (uint8_t i = 0; i < 14; ++i)
  2827. game->prevMoves[i] = 0;
  2828. game->state = SCL_GAME_STATE_PLAYING;
  2829. game->ply = 0;
  2830. SCL_recordInit(game->record);
  2831. }
  2832. uint8_t SCL_gameGetRepetiotionMove(SCL_Game *game,
  2833. uint8_t *squareFrom, uint8_t *squareTo)
  2834. {
  2835. if (squareFrom != 0 && squareTo != 0)
  2836. {
  2837. *squareFrom = 0;
  2838. *squareTo = 0;
  2839. }
  2840. /* pos. 1st 2nd 3rd
  2841. | | |
  2842. v v v
  2843. 01 23 45 67 89 AB CD EF
  2844. move ab cd ba dc ab cd ba dc */
  2845. if (game->ply >= 7 &&
  2846. game->prevMoves[0] == game->prevMoves[5] &&
  2847. game->prevMoves[0] == game->prevMoves[8] &&
  2848. game->prevMoves[0] == game->prevMoves[13] &&
  2849. game->prevMoves[1] == game->prevMoves[4] &&
  2850. game->prevMoves[1] == game->prevMoves[9] &&
  2851. game->prevMoves[1] == game->prevMoves[12] &&
  2852. game->prevMoves[2] == game->prevMoves[7] &&
  2853. game->prevMoves[2] == game->prevMoves[10] &&
  2854. game->prevMoves[3] == game->prevMoves[6] &&
  2855. game->prevMoves[3] == game->prevMoves[11]
  2856. )
  2857. {
  2858. if (squareFrom != 0 && squareTo != 0)
  2859. {
  2860. *squareFrom = game->prevMoves[3];
  2861. *squareTo = game->prevMoves[2];
  2862. }
  2863. return 1;
  2864. }
  2865. return 0;
  2866. }
  2867. void SCL_gameMakeMove(SCL_Game *game, uint8_t squareFrom, uint8_t squareTo,
  2868. char promoteTo)
  2869. {
  2870. uint8_t repetitionS0, repetitionS1;
  2871. SCL_gameGetRepetiotionMove(game,&repetitionS0,&repetitionS1);
  2872. SCL_boardMakeMove(game->board,squareFrom,squareTo,promoteTo);
  2873. SCL_recordAdd(game->record,squareFrom,squareTo,promoteTo,SCL_RECORD_CONT);
  2874. // ^ TODO: SCL_RECORD_CONT
  2875. game->ply++;
  2876. for (uint8_t i = 0; i < 14 - 2; ++i)
  2877. game->prevMoves[i] = game->prevMoves[i + 2];
  2878. game->prevMoves[12] = squareFrom;
  2879. game->prevMoves[13] = squareTo;
  2880. if (squareFrom == repetitionS0 && squareTo == repetitionS1)
  2881. game->state = SCL_GAME_STATE_DRAW_REPETITION;
  2882. else if (game->board[SCL_BOARD_MOVE_COUNT_BYTE] >= 50)
  2883. game->state = SCL_GAME_STATE_DRAW_50;
  2884. else
  2885. {
  2886. uint8_t position = SCL_boardGetPosition(game->board);
  2887. switch (position)
  2888. {
  2889. case SCL_POSITION_MATE:
  2890. game->state = SCL_boardWhitesTurn(game->board) ?
  2891. SCL_GAME_STATE_BLACK_WIN : SCL_GAME_STATE_WHITE_WIN;
  2892. break;
  2893. case SCL_POSITION_STALEMATE:
  2894. game->state = SCL_GAME_STATE_DRAW_STALEMATE;
  2895. break;
  2896. case SCL_POSITION_DEAD:
  2897. game->state = SCL_GAME_STATE_DRAW_DEAD;
  2898. break;
  2899. default: break;
  2900. }
  2901. }
  2902. }
  2903. uint8_t SCL_gameUndoMove(SCL_Game *game)
  2904. {
  2905. if (game->ply == 0)
  2906. return 0;
  2907. if ((game->ply - 1) > SCL_recordLength(game->record))
  2908. return 0; // can't undo, lacking record
  2909. SCL_Record r;
  2910. SCL_recordCopy(game->record,r);
  2911. uint16_t applyMoves = game->ply - 1;
  2912. SCL_gameInit(game,game->startState);
  2913. for (uint16_t i = 0; i < applyMoves; ++i)
  2914. {
  2915. uint8_t s0, s1;
  2916. char p;
  2917. SCL_recordGetMove(r,i,&s0,&s1,&p);
  2918. SCL_gameMakeMove(game,s0,s1,p);
  2919. }
  2920. return 1;
  2921. }
  2922. uint8_t SCL_boardMoveIsLegal(SCL_Board board, uint8_t squareFrom,
  2923. uint8_t squareTo)
  2924. {
  2925. if (squareFrom >= SCL_BOARD_SQUARES || squareTo >= SCL_BOARD_SQUARES)
  2926. return 0;
  2927. char piece = board[squareFrom];
  2928. if ((piece == '.') ||
  2929. (SCL_boardWhitesTurn(board) != SCL_pieceIsWhite(piece)))
  2930. return 0;
  2931. SCL_SquareSet moves;
  2932. SCL_boardGetMoves(board,squareFrom,moves);
  2933. return SCL_squareSetContains(moves,squareTo);
  2934. }
  2935. #endif // guard