123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696 |
- #ifndef SMALLCHESSLIB_H
- #define SMALLCHESSLIB_H
- /**
- @file smallchesslib.h
- Small and simple single header C99 public domain chess library and engine.
- author: Miloslav Ciz (drummyfish)
- license: CC0 1.0 (public domain)
- found at https://creativecommons.org/publicdomain/zero/1.0/
- + additional waiver of all IP
- version: 0.8d
- Default notation format for this library is a coordinate one, i.e.
-
- squarefrom squareto [promotedpiece]
- e.g.: e2e4 or A2A1q
- This work's goal is to never be encumbered by any exclusive intellectual
- property rights. The work is therefore provided under CC0 1.0 + additional
- WAIVER OF ALL INTELLECTUAL PROPERTY RIGHTS that waives the rest of
- intellectual property rights not already waived by CC0 1.0. The WAIVER OF ALL
- INTELLECTUAL PROPERTY RGHTS is as follows:
- Each contributor to this work agrees that they waive any exclusive rights,
- including but not limited to copyright, patents, trademark, trade dress,
- industrial design, plant varieties and trade secrets, to any and all ideas,
- concepts, processes, discoveries, improvements and inventions conceived,
- discovered, made, designed, researched or developed by the contributor either
- solely or jointly with others, which relate to this work or result from this
- work. Should any waiver of such right be judged legally invalid or
- ineffective under applicable law, the contributor hereby grants to each
- affected person a royalty-free, non transferable, non sublicensable, non
- exclusive, irrevocable and unconditional license to this right.
- */
- #include <stdint.h>
- #ifndef SCL_DEBUG_AI
- /** AI will print out a Newick-like tree of searched moves. */
- #define SCL_DEBUG_AI 0
- #endif
- /**
- Maximum number of moves a chess piece can have (a queen in the middle of the
- board).
- */
- #define SCL_CHESS_PIECE_MAX_MOVES 25
- #define SCL_BOARD_SQUARES 64
- typedef uint8_t (*SCL_RandomFunction)(void);
- #if SCL_COUNT_EVALUATED_POSITIONS
- uint32_t SCL_positionsEvaluated = 0; /**< If enabled by
- SCL_COUNT_EVALUATED_POSITIONS, this
- will increment with every
- dynamically evaluated position (e.g.
- when AI computes its move). */
- #endif
- #ifndef SCL_CALL_WDT_RESET
- #define SCL_CALL_WDT_RESET 0 /**< Option that should be enabled on some
- Arduinos. If 1, call to watchdog timer
- reset will be performed during dynamic
- evaluation (without it if AI takes long the
- program will reset). */
- #endif
- /**
- Returns a pseudorandom byte. This function has a period 256 and returns each
- possible byte value exactly once in the period.
- */
- uint8_t SCL_randomSimple(void);
- void SCL_randomSimpleSeed(uint8_t seed);
- /**
- Like SCL_randomSimple, but internally uses a 16 bit value, so the period is
- 65536.
- */
- uint8_t SCL_randomBetter(void);
- void SCL_randomBetterSeed(uint16_t seed);
- #ifndef SCL_EVALUATION_FUNCTION
- /**
- If defined, AI will always use the static evaluation function with this
- name. This helps avoid pointers to functions and can be faster but the
- function can't be changed at runtime.
- */
- #define SCL_EVALUATION_FUNCTION
- #undef SCL_EVALUATION_FUNCTION
- #endif
- #ifndef SCL_960_CASTLING
- /**
- If set, chess 960 (Fisher random) castling will be considered by the library
- rather than normal castling. 960 castling is slightly different (e.g.
- requires the inital rook positions to be stored in board state). The
- castling move is performed as "capturing own rook".
- */
- #define SCL_960_CASTLING 0
- #endif
- #ifndef SCL_ALPHA_BETA
- /**
- Turns alpha-beta pruning (AI optimization) on or off. This can gain
- performance and should normally be turned on. AI behavior should not
- change at all.
- */
- #define SCL_ALPHA_BETA 1
- #endif
- /**
- A set of game squares as a bit array, each bit representing one game square.
- Useful for representing e.g. possible moves. To easily iterate over the set
- use provided macros (SCL_SQUARE_SET_ITERATE, ...).
- */
- typedef uint8_t SCL_SquareSet[8];
- #define SCL_SQUARE_SET_EMPTY {0, 0, 0, 0, 0, 0, 0, 0}
- void SCL_squareSetClear(SCL_SquareSet squareSet);
- void SCL_squareSetAdd(SCL_SquareSet squareSet, uint8_t square);
- uint8_t SCL_squareSetContains(const SCL_SquareSet squareSet, uint8_t square);
- uint8_t SCL_squareSetSize(const SCL_SquareSet squareSet);
- uint8_t SCL_squareSetEmpty(const SCL_SquareSet squareSet);
- /**
- Returns a random square from a square set.
- */
- uint8_t SCL_squareSetGetRandom(const SCL_SquareSet squareSet,
- SCL_RandomFunction randFunc);
- #define SCL_SQUARE_SET_ITERATE_BEGIN(squareSet) \
- { uint8_t iteratedSquare = 0;\
- uint8_t iterationEnd = 0;\
- for (int8_t _i = 0; _i < 8 && !iterationEnd; ++_i) {\
- uint8_t _row = squareSet[_i];\
- if (_row == 0) { iteratedSquare += 8; continue; }\
- \
- for (uint8_t _j = 0; _j < 8 && !iterationEnd; ++_j) {\
- if (_row & 0x01) {
- /*
- Between SCL_SQUARE_SET_ITERATE_BEGIN and _END iteratedSquare variable
- represents the next square contained in the set. To break out of the
- iteration set iterationEnd to 1.
- */
- #define SCL_SQUARE_SET_ITERATE_END }\
- _row >>= 1;\
- iteratedSquare++;}\
- } /*for*/ }
- #define SCL_SQUARE_SET_ITERATE(squareSet,command)\
- SCL_SQUARE_SET_ITERATE_BEGIN(squareSet)\
- { command }\
- SCL_SQUARE_SET_ITERATE_END
-
- #define SCL_BOARD_STATE_SIZE 69
- /**
- Represents chess board state as a string in this format:
- - First 64 characters represent the chess board (A1, B1, ... H8), each field
- can be either a piece (PRNBKQprnbkq) or empty ('.'). I.e. the state looks
- like this:
- 0 (A1) RNBQKBNR
- PPPPPPPP
- ........
- ........
- ........
- ........
- pppppppp
- rnbqkbnr 63 (H8)
- - After this more bytes follow to represent global state, these are:
- - 64: bits holding en-passant and castling related information:
- - bits 0-3 (lsb): Column of the pawn that can, in current turn, be
- taken by en-passant (0xF means no pawn can be taken this way).
- - bit 4: Whether white is not prevented from short castling by previous
- king or rook movement.
- - bit 5: Same as 4, but for long castling.
- - bit 6: Same as 4, but for black.
- - bit 7: Same as 4, but for black and long castling.
- - 65: Number saying the number of ply (half-moves) that have already been
- played, also determining whose turn it currently is.
- - 66: Move counter used in the 50 move rule, says the number of ply since
- the last pawn move or capture.
- - 67: Extra byte, left for storing additional info in variants. For normal
- chess this byte should always be 0.
- - 68: The last byte is always 0 to properly terminate the string in case
- someone tries to print it.
- - The state is designed so as to be simple and also print-friendly, i.e. you
- can simply print it with line break after 8 characters to get a human
- readable representation of the board.
- NOTE: there is a much more compact representation which however trades some
- access speed which would affect the AI performance and isn't print friendly,
- so we don't use it. In it each square takes 4 bits, using 15 out of 16
- possible values (empty square and W and B pieces including 2 types of pawns,
- one "en-passant takeable"). Then only one extra byte needed is for castling
- info (4 bits) and ply count (4 bits).
- */
- typedef char SCL_Board[SCL_BOARD_STATE_SIZE];
- #define SCL_BOARD_ENPASSANT_CASTLE_BYTE 64
- #define SCL_BOARD_PLY_BYTE 65
- #define SCL_BOARD_MOVE_COUNT_BYTE 66
- #define SCL_BOARD_EXTRA_BYTE 67
- #if SCL_960_CASTLING
- #define _SCL_EXTRA_BYTE_VALUE (0 | (7 << 3)) // rooks on classic positions
- #else
- #define _SCL_EXTRA_BYTE_VALUE 0
- #endif
- #define SCL_BOARD_START_STATE \
- {82, 78, 66, 81, 75, 66, 78, 82,\
- 80, 80, 80, 80, 80, 80, 80, 80,\
- 46, 46, 46, 46, 46, 46, 46, 46,\
- 46, 46, 46, 46, 46, 46, 46, 46,\
- 46, 46, 46, 46, 46, 46, 46, 46,\
- 46, 46, 46, 46, 46, 46, 46, 46,\
- 112,112,112,112,112,112,112,112,\
- 114,110,98, 113,107,98, 110,114,\
- (char) 0xff,0,0,_SCL_EXTRA_BYTE_VALUE,0}
- #define SCL_FEN_START \
- "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
- #define SCL_FEN_HORDE \
- "ppp2ppp/pppppppp/pppppppp/pppppppp/3pp3/8/PPPPPPPP/RNBQKBNR w KQ - 0 1"
- #define SCL_FEN_UPSIDE_DOWN \
- "RNBKQBNR/PPPPPPPP/8/8/8/8/pppppppp/rnbkqbnr w - - 0 1"
- #define SCL_FEN_PEASANT_REVOLT \
- "1nn1k1n1/4p3/8/8/8/8/PPPPPPPP/4K3 w - - 0 1"
- #define SCL_FEN_ENDGAME \
- "4k3/pppppppp/8/8/8/8/PPPPPPPP/4K3 w - - 0 1"
- #define SCL_FEN_KNIGHTS \
- "N6n/1N4n1/2N2n2/3Nn3/k2nN2K/2n2N2/1n4N1/n6N w - - 0 1"
- /**
- Holds an info required to undo a single move.
- */
- typedef struct
- {
- uint8_t squareFrom; ///< start square
- uint8_t squareTo; ///< target square
- char enPassantCastle; ///< previous en passant/castle byte
- char moveCount; ///< previous values of the move counter byte
- uint8_t other; /**< lowest 7 bits: previous value of target square,
- highest bit: if 1 then the move was promotion or
- en passant */
- } SCL_MoveUndo;
- #define SCL_GAME_STATE_PLAYING 0x00
- #define SCL_GAME_STATE_WHITE_WIN 0x01
- #define SCL_GAME_STATE_BLACK_WIN 0x02
- #define SCL_GAME_STATE_DRAW 0x10 ///< further unspecified draw
- #define SCL_GAME_STATE_DRAW_STALEMATE 0x11 ///< draw by stalemate
- #define SCL_GAME_STATE_DRAW_REPETITION 0x12 ///< draw by repetition
- #define SCL_GAME_STATE_DRAW_50 0x13 ///< draw by 50 move rule
- #define SCL_GAME_STATE_DRAW_DEAD 0x14 ///< draw by dead position
- #define SCL_GAME_STATE_END 0xff ///< end without known result
- /**
- Converts square in common notation (e.g. 'c' 8) to square number. Only accepts
- lowercase column.
- */
- #define SCL_SQUARE(colChar,rowInt) (((rowInt) - 1) * 8 + ((colChar) - 'a'))
- #define SCL_S(c,r) SCL_SQUARE(c,r)
- void SCL_boardInit(SCL_Board board);
- void SCL_boardCopy(const SCL_Board boardFrom, SCL_Board boardTo);
- /**
- Initializes given chess 960 (Fisher random) position. If SCL_960_CASTLING
- is not set, castling will be disabled by this function.
- */
- void SCL_boardInit960(SCL_Board board, uint16_t positionNumber);
- void SCL_boardDisableCastling(SCL_Board board);
- uint32_t SCL_boardHash32(const SCL_Board board);
- #define SCL_PHASE_OPENING 0
- #define SCL_PHASE_MIDGAME 1
- #define SCL_PHASE_ENDGAME 2
- /**
- Estimates the game phase: opening, midgame or endgame.
- */
- uint8_t SCL_boardEstimatePhase(SCL_Board board);
- /**
- Sets the board position. The input string should be 64 characters long zero
- terminated C string representing the board as squares A1, A2, ..., H8 with
- each char being either a piece (RKBKQPrkbkqp) or an empty square ('.').
- */
- void SCL_boardSetPosition(SCL_Board board, const char *pieces,
- uint8_t castlingEnPassant, uint8_t moveCount, uint8_t ply);
- uint8_t SCL_boardsDiffer(SCL_Board b1, SCL_Board b2);
- /**
- Gets a random move on given board for the player whose move it is.
- */
- void SCL_boardRandomMove(SCL_Board board, SCL_RandomFunction randFunc,
- uint8_t *squareFrom, uint8_t *squareTo, char *resultProm);
- #define SCL_FEN_MAX_LENGTH 90
- /**
- Converts a position to FEN (Forsyth–Edwards Notation) string. The string has
- to have at least SCL_FEN_MAX_LENGTH bytes allocated to guarantee the
- function won't write to unallocated memory. The string will be terminated by
- 0 (this is included in SCL_FEN_MAX_LENGTH). The number of bytes written
- (including the terminating 0) is returned.
- */
- uint8_t SCL_boardToFEN(SCL_Board board, char *string);
- /**
- Loads a board from FEN (Forsyth–Edwards Notation) string. Returns 1 on
- success, 0 otherwise. XFEN isn't supported fully but a start position in
- chess960 can be loaded with this function.
- */
- uint8_t SCL_boardFromFEN(SCL_Board board, const char *string);
- /**
- Returns an approximate/heuristic board rating as a number, 0 meaning equal
- chances for both players, positive favoring white, negative favoring black.
- */
- typedef int16_t (*SCL_StaticEvaluationFunction)(SCL_Board);
- /*
- NOTE: int8_t as a return value was tried for evaluation function, which would
- be simpler, but it fails to capture important non-material position
- differences counted in fractions of pawn values, hence we have to use int16_t.
- */
- /**
- Basic static evaluation function. WARNING: this function supposes a standard
- chess game, for non-standard positions it may either not work well or even
- crash the program. You should use a different function for non-standard games.
- */
- int16_t SCL_boardEvaluateStatic(SCL_Board board);
- /**
- Dynamic evaluation function (search), i.e. unlike SCL_boardEvaluateStatic,
- this one performs a recursive search for deeper positions to get a more
- accurate score. Of course, this is much slower and hugely dependent on
- baseDepth (you mostly want to keep this under 5).
- */
- int16_t SCL_boardEvaluateDynamic(SCL_Board board, uint8_t baseDepth,
- uint8_t extensionExtraDepth, SCL_StaticEvaluationFunction evalFunction);
- #define SCL_EVALUATION_MAX_SCORE 32600 // don't increase this, we need a margin
- /**
- Checks if the board position is dead, i.e. mate is impossible (e.g. due to
- insufficient material), which by the rules results in a draw. WARNING: This
- function may fail to detect some dead positions as this is a non-trivial task.
- */
- uint8_t SCL_boardDead(SCL_Board board);
- /**
- Tests whether given player is in check.
- */
- uint8_t SCL_boardCheck(SCL_Board board, uint8_t white);
- /**
- Checks whether given move resets the move counter (used in the 50 move rule).
- */
- uint8_t SCL_boardMoveResetsCount(SCL_Board board,
- uint8_t squareFrom, uint8_t squareTo);
- uint8_t SCL_boardMate(SCL_Board board);
- /**
- Performs a move on a board WITHOUT checking if the move is legal. Returns an
- info with which the move can be undone.
- */
- SCL_MoveUndo SCL_boardMakeMove(SCL_Board board, uint8_t squareFrom, uint8_t squareTo,
- char promotePiece);
- void SCL_boardUndoMove(SCL_Board board, SCL_MoveUndo moveUndo);
- /**
- Checks if the game is over, i.e. the current player to move has no legal
- moves, the game is in dead position etc.
- */
- uint8_t SCL_boardGameOver(SCL_Board board);
- /**
- Checks if given move is legal.
- */
- uint8_t SCL_boardMoveIsLegal(SCL_Board board, uint8_t squareFrom,
- uint8_t squareTo);
- /**
- Checks if the player to move has at least one legal move.
- */
- uint8_t SCL_boardMovePossible(SCL_Board board);
- #define SCL_POSITION_NORMAL 0x00
- #define SCL_POSITION_CHECK 0x01
- #define SCL_POSITION_MATE 0x02
- #define SCL_POSITION_STALEMATE 0x03
- #define SCL_POSITION_DEAD 0x04
- uint8_t SCL_boardGetPosition(SCL_Board board);
- /**
- Returns 1 if the square is attacked by player of given color. This is used to
- examine checks, so for performance reasons the functions only checks whether
- or not the square is attacked (not the number of attackers).
- */
- uint8_t SCL_boardSquareAttacked(SCL_Board board, uint8_t square,
- uint8_t byWhite);
- /**
- Gets pseudo moves of a piece: all possible moves WITHOUT eliminating moves
- that lead to own check. To get only legal moves use SCL_boardGetMoves.
- */
- void SCL_boardGetPseudoMoves(
- SCL_Board board,
- uint8_t pieceSquare,
- uint8_t checkCastling,
- SCL_SquareSet result);
- /**
- Gets all legal moves of given piece.
- */
- void SCL_boardGetMoves(
- SCL_Board board,
- uint8_t pieceSquare,
- SCL_SquareSet result);
- static inline uint8_t SCL_boardWhitesTurn(SCL_Board board);
- static inline uint8_t SCL_pieceIsWhite(char piece);
- static inline uint8_t SCL_squareIsWhite(uint8_t square);
- char SCL_pieceToColor(uint8_t piece, uint8_t toWhite);
- /**
- Converts square coordinates to square number. Each coordinate must be a number
- <1,8>. Validity of the coordinates is NOT checked.
- */
- static inline uint8_t SCL_coordsToSquare(uint8_t row, uint8_t column);
- #ifndef SCL_VALUE_PAWN
- #define SCL_VALUE_PAWN 256
- #endif
- #ifndef SCL_VALUE_KNIGHT
- #define SCL_VALUE_KNIGHT 768
- #endif
- #ifndef SCL_VALUE_BISHOP
- #define SCL_VALUE_BISHOP 800
- #endif
- #ifndef SCL_VALUE_ROOK
- #define SCL_VALUE_ROOK 1280
- #endif
- #ifndef SCL_VALUE_QUEEN
- #define SCL_VALUE_QUEEN 2304
- #endif
- #ifndef SCL_VALUE_KING
- #define SCL_VALUE_KING 0
- #endif
- #define SCL_ENDGAME_MATERIAL_LIMIT \
- (2 * (SCL_VALUE_PAWN * 4 + SCL_VALUE_QUEEN + \
- SCL_VALUE_KING + SCL_VALUE_ROOK + SCL_VALUE_KNIGHT))
- #define SCL_START_MATERIAL \
- (16 * SCL_VALUE_PAWN + 4 * SCL_VALUE_ROOK + 4 * SCL_VALUE_KNIGHT + \
- 4 * SCL_VALUE_BISHOP + 2 * SCL_VALUE_QUEEN + 2 * SCL_VALUE_KING)
- #ifndef SCL_RECORD_MAX_LENGTH
- #define SCL_RECORD_MAX_LENGTH 256
- #endif
- #define SCL_RECORD_MAX_SIZE (SCL_RECORD_MAX_LENGTH * 2)
- /**
- Records a single chess game. The format is following:
- Each record item consists of 2 bytes which record a single move (ply):
- abxxxxxx cdyyyyyy
- xxxxxx Start square of the move, counted as A0, A1, ...
- yyyyyy End square of the move in the same format as the start square.
- ab 00 means this move isn't the last move of the game, other possible
- values are 01: white wins, 10: black wins, 11: draw or end for
- other reasons.
- cd In case of pawn promotion move this encodes the promoted piece as
- 00: queen, 01: rook, 10: bishop, 11: knight (pawn isn't allowed by
- chess rules).
- Every record should be ended by an ending move (ab != 00), empty record should
- have one move where xxxxxx == yyyyyy == 0 and ab == 11.
- */
- typedef uint8_t SCL_Record[SCL_RECORD_MAX_SIZE];
- #define SCL_RECORD_CONT 0x00
- #define SCL_RECORD_W_WIN 0x40
- #define SCL_RECORD_B_WIN 0x80
- #define SCL_RECORD_END 0xc0
- #define SCL_RECORD_PROM_Q 0x00
- #define SCL_RECORD_PROM_R 0x40
- #define SCL_RECORD_PROM_B 0x80
- #define SCL_RECORD_PROM_N 0xc0
- #define SCL_RECORD_ITEM(s0,s1,p,e) ((e) | (s0)),((p) | (s1))
- void SCL_recordInit(SCL_Record r);
- void SCL_recordCopy(SCL_Record recordFrom, SCL_Record recordTo);
- /**
- Represents a complete game of chess (or a variant with different staring
- position). This struct along with associated functions allows to easily
- implement a chess game that allows undoing moves, detecting draws, recording
- the moves etc. On platforms with extremely little RAM one can reduce
- SCL_RECORD_MAX_LENGTH to reduce the size of this struct (which will however
- possibly limit how many moves can be undone).
- */
- typedef struct
- {
- SCL_Board board;
- SCL_Record record; /**< Holds the game record. This record is here
- firstly because games are usually recorded and
- secondly this allows undoing moves up to the
- beginning of the game. This infinite undoing will
- only work as long as the record is able to hold
- the whole game; if the record is full, undoing is
- no longet possible. */
- uint16_t state;
- uint16_t ply; ///< ply count (board ply counter is only 8 bit)
- uint32_t prevMoves[14]; ///< stores last moves, for repetition detection
- const char *startState; /**< Optional pointer to the starting board state.
- If this is null, standard chess start position is
- assumed. This is needed for undoing moves with
- game record. */
- } SCL_Game;
- /**
- Initializes a new chess game. The startState parameter is optional and allows
- for setting up chess variants that differ by starting positions, setting this
- to 0 will assume traditional starting position. WARNING: if startState is
- provided, the pointed to board mustn't be deallocated afterwards, the string
- is not internally copied (for memory saving reasons).
- */
- void SCL_gameInit(SCL_Game *game, const SCL_Board startState);
- void SCL_gameMakeMove(SCL_Game *game, uint8_t squareFrom, uint8_t squareTo,
- char promoteTo);
- uint8_t SCL_gameUndoMove(SCL_Game *game);
- /**
- Gets a move which if played now would cause a draw by repetition. Returns 1
- if such move exists, 0 otherwise. The results parameters can be set to 0 in
- which case they will be ignored and only the existence of a draw move will be
- tested.
- */
- uint8_t SCL_gameGetRepetiotionMove(SCL_Game *game,
- uint8_t *squareFrom, uint8_t *squareTo);
- /**
- Leads a game record from PGN string. The function will probably not strictly
- adhere to the PGN input format, but should accept most sanely written PGN
- strings.
- */
- void SCL_recordFromPGN(SCL_Record r, const char *pgn);
- uint16_t SCL_recordLength(const SCL_Record r);
- /**
- Gets the move out of a game record, returns the end state of the move
- (SCL_RECORD_CONT, SCL_RECORD_END etc.)
- */
- uint8_t SCL_recordGetMove(const SCL_Record r, uint16_t index,
- uint8_t *squareFrom, uint8_t *squareTo, char *promotedPiece);
- /**
- Adds another move to the game record. Terminating the record is handled so
- that the last move is always marked with end flag, endState is here to only
- indicate possible game result (otherwise pass SCL_RECORD_CONT). Returns 1 if
- the item was added, otherwise 0 (replay was already of maximum size).
- */
- uint8_t SCL_recordAdd(SCL_Record r, uint8_t squareFrom,
- uint8_t squareTo, char promotePiece, uint8_t endState);
- /**
- Removes the last move from the record, returns 1 if the replay is non-empty
- after the removal, otherwise 0.
- */
- uint8_t SCL_recordRemoveLast(SCL_Record r);
- /**
- Applies given number of half-moves (ply) to a given board (the board is
- automatically initialized at the beginning).
- */
- void SCL_recordApply(const SCL_Record r, SCL_Board b, uint16_t moves);
- int16_t SCL_pieceValue(char piece);
- int16_t SCL_pieceValuePositive(char piece);
- #define SCL_PRINT_FORMAT_NONE 0
- #define SCL_PRINT_FORMAT_NORMAL 1
- #define SCL_PRINT_FORMAT_COMPACT 2
- #define SCL_PRINT_FORMAT_UTF8 3
- #define SCL_PRINT_FORMAT_COMPACT_UTF8 4
- /**
- Gets the best move for the currently moving player as computed by AI. The
- return value is the value of the move (with the same semantics as the value
- of an evaluation function). baseDepth is depth in plys to which all moves will
- be checked. If baseDepth 0 is passed, the function makes a random move and
- returns the evaluation of the board. extensionExtraDepth is extra depth for
- checking specific situations like exchanges and checks. endgameExtraDepth is
- extra depth which is added to baseDepth in the endgame. If the randomness
- function is 0, AI will always make the first best move it finds, if it is
- not 0 and randomness is 0, AI will randomly pick between the equally best
- moves, if it is not 0 and randomness is positive, AI will randomly choose
- between best moves with some bias (may not pick the best rated move).
- */
- int16_t SCL_getAIMove(
- SCL_Board board,
- uint8_t baseDepth,
- uint8_t extensionExtraDepth,
- uint8_t endgameExtraDepth,
- SCL_StaticEvaluationFunction evalFunc,
- SCL_RandomFunction randFunc,
- uint8_t randomness,
- uint8_t repetitionMoveFrom,
- uint8_t repetitionMoveTo,
- uint8_t *resultFrom,
- uint8_t *resultTo,
- char *resultProm);
- /**
- Function that prints out a single character. This is passed to printing
- functions.
- */
- typedef void (*SCL_PutCharFunction)(char);
- /**
- Prints given chessboard using given format and an abstract printing function.
- */
- void SCL_printBoard(
- SCL_Board board,
- SCL_PutCharFunction putCharFunc,
- SCL_SquareSet highlightSquares,
- uint8_t selectSquare,
- uint8_t format,
- uint8_t offset,
- uint8_t labels,
- uint8_t blackDown);
- void SCL_printBoardSimple(
- SCL_Board board,
- SCL_PutCharFunction putCharFunc,
- uint8_t selectSquare,
- uint8_t format);
- void SCL_printSquareUTF8(uint8_t square, SCL_PutCharFunction putCharFunc);
- void SCL_printPGN(SCL_Record r, SCL_PutCharFunction putCharFunc,
- SCL_Board initialState);
- /**
- Reads a move from string (the notation format is described at the top of this
- file). The function is safe as long as the string is 0 terminated. Returns 1
- on success or 0 on fail (invalid move string).
- */
- uint8_t SCL_stringToMove(const char *moveString, uint8_t *resultFrom,
- uint8_t *resultTo, char *resultPromotion);
- char *SCL_moveToString(SCL_Board board, uint8_t s0, uint8_t s1,
- char promotion, char *string);
- /**
- Function used in drawing, it is called to draw the next pixel. The first
- parameter is the pixel color, the second one if the sequential number of the
- pixel.
- */
- typedef void (*SCL_PutPixelFunction)(uint8_t, uint16_t);
- #define SCL_BOARD_PICTURE_WIDTH 64
- /**
- Draws a simple 1bit 64x64 pixels board using a provided abstract function for
- drawing pixels. The function renders from top left to bottom right, i.e. no
- frame buffer is required.
- */
- void SCL_drawBoard(
- SCL_Board board,
- SCL_PutPixelFunction putPixel,
- uint8_t selectedSquare,
- SCL_SquareSet highlightSquares,
- uint8_t blackDown);
- /**
- Converts square number to string representation (e.g. "d2"). This function
- will modify exactly the first two bytes of the provided string.
- */
- static inline char *SCL_squareToString(uint8_t square, char *string);
- /**
- Converts a string, such as "A1" or "b4", to square number. The string must
- start with a letter (lower or upper case) and be followed by a number <1,8>.
- Validity of the string is NOT checked.
- */
- uint8_t SCL_stringToSquare(const char *square);
- //=============================================================================
- // privates:
- #define SCL_UNUSED(v) (void)(v)
- uint8_t SCL_currentRandom8 = 0;
- uint16_t SCL_currentRandom16 = 0;
- void SCL_randomSimpleSeed(uint8_t seed)
- {
- SCL_currentRandom8 = seed;
- }
- uint8_t SCL_randomSimple(void)
- {
- SCL_currentRandom8 *= 13;
- SCL_currentRandom8 += 7;
- return SCL_currentRandom8;
- }
- uint8_t SCL_randomBetter(void)
- {
- SCL_currentRandom16 *= 13;
- SCL_currentRandom16 += 7;
- return (SCL_currentRandom16 % 256) ^ (SCL_currentRandom16 / 256);
- }
- void SCL_randomBetterSeed(uint16_t seed)
- {
- SCL_currentRandom16 = seed;
- }
- void SCL_squareSetClear(SCL_SquareSet squareSet)
- {
- for (uint8_t i = 0; i < 8; ++i)
- squareSet[i] = 0;
- }
- uint8_t SCL_stringToSquare(const char *square)
- {
- return (square[1] - '1') * 8 +
- (square[0] - ((square[0] >= 'A' && square[0] <= 'Z') ? 'A' : 'a'));
- }
- char *SCL_moveToString(SCL_Board board, uint8_t s0, uint8_t s1,
- char promotion, char *string)
- {
- char *result = string;
- SCL_squareToString(s0,string);
- string += 2;
- string = SCL_squareToString(s1,string);
- string += 2;
- char c = board[s0];
- if (c == 'p' || c == 'P')
- {
- uint8_t rank = s1 / 8;
- if (rank == 0 || rank == 7)
- {
- *string = promotion;
- string++;
- }
- }
- *string = 0;
- return result;
- }
- uint8_t SCL_boardWhitesTurn(SCL_Board board)
- {
- return (board[SCL_BOARD_PLY_BYTE] % 2) == 0;
- }
- uint8_t SCL_coordsToSquare(uint8_t row, uint8_t column)
- {
- return row * 8 + column;
- }
- uint8_t SCL_pieceIsWhite(char piece)
- {
- return piece < 'a';
- }
- char *SCL_squareToString(uint8_t square, char *string)
- {
- string[0] = 'a' + square % 8;
- string[1] = '1' + square / 8;
- return string;
- }
-
- uint8_t SCL_squareIsWhite(uint8_t square)
- {
- return (square % 2) != ((square / 8) % 2);
- }
- char SCL_pieceToColor(uint8_t piece, uint8_t toWhite)
- {
- return (SCL_pieceIsWhite(piece) == toWhite) ?
- piece : (piece + (toWhite ? -32 : 32));
- }
- /**
- Records the rook starting positions in the board state. This is required in
- chess 960 in order to be able to correctly perform castling (castling rights
- knowledge isn't enough as one rook might have moved to the other side and we
- wouldn't know which one can castle and which not).
- */
- void _SCL_board960RememberRookPositions(SCL_Board board)
- {
- uint8_t pos = 0;
- uint8_t rooks = 2;
- while (pos < 8 && rooks != 0)
- {
- if (board[pos] == 'R')
- {
- board[SCL_BOARD_EXTRA_BYTE] = rooks == 2 ? pos :
- (board[SCL_BOARD_EXTRA_BYTE] | (pos << 3));
- rooks--;
- }
- pos++;
- }
- }
- void SCL_boardInit(SCL_Board board)
- {
- /*
- We might use SCL_BOARD_START_STATE and copy it to the board, but that might
- waste RAM on Arduino, so we init the board by code.
- */
- char *b = board;
- *b = 'R'; b++; *b = 'N'; b++;
- *b = 'B'; b++; *b = 'Q'; b++;
- *b = 'K'; b++; *b = 'B'; b++;
- *b = 'N'; b++; *b = 'R'; b++;
- char *b2 = board + 48;
- for (uint8_t i = 0; i < 8; ++i, b++, b2++)
- {
- *b = 'P';
- *b2 = 'p';
- }
- for (uint8_t i = 0; i < 32; ++i, b++)
- *b = '.';
- b += 8;
- *b = 'r'; b++; *b = 'n'; b++;
- *b = 'b'; b++; *b = 'q'; b++;
- *b = 'k'; b++; *b = 'b'; b++;
- *b = 'n'; b++; *b = 'r'; b++;
- for (uint8_t i = 0; i < SCL_BOARD_STATE_SIZE - SCL_BOARD_SQUARES; ++i, ++b)
- *b = 0;
- board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] = (char) 0xff;
- #if SCL_960_CASTLING
- _SCL_board960RememberRookPositions(board);
- #endif
- }
- void _SCL_boardPlaceOnNthAvailable(SCL_Board board, uint8_t pos, char piece)
- {
- char *c = board;
- while (1)
- {
- if (*c == '.')
- {
- if (pos == 0)
- break;
- pos--;
- }
- c++;
- }
- *c = piece;
- }
- void SCL_boardInit960(SCL_Board board, uint16_t positionNumber)
- {
- SCL_Board b;
- SCL_boardInit(b);
- for (uint8_t i = 0; i < SCL_BOARD_STATE_SIZE; ++i)
- board[i] = ((i >= 8 && i < 56) || i >= 64) ? b[i] : '.';
- uint8_t helper = positionNumber % 16;
- board[(helper / 4) * 2] = 'B';
- board[1 + (helper % 4) * 2] = 'B';
- helper = positionNumber / 16;
- // maybe there's a simpler way :)
- _SCL_boardPlaceOnNthAvailable(board,helper % 6,'Q');
- _SCL_boardPlaceOnNthAvailable(board,0,helper <= 23 ? 'N' : 'R');
- _SCL_boardPlaceOnNthAvailable(board,0,
- (helper >= 7 && helper <= 23) ? 'R' :
- (helper > 41 ? 'K' : 'N' ));
- _SCL_boardPlaceOnNthAvailable(board,0,
- (helper <= 5 || helper >= 54) ? 'R' :
- (((helper >= 12 && helper <= 23) ||
- (helper >= 30 && helper <= 41)) ? 'K' : 'N'));
- _SCL_boardPlaceOnNthAvailable(board,0,
- (helper <= 11 || (helper <= 29 && helper >= 24)) ? 'K' :
- (
- (
- (helper >= 18 && helper <= 23) ||
- (helper >= 36 && helper <= 41) ||
- (helper >= 48 && helper <= 53)
- ) ? 'R' : 'N'
- )
- );
- uint8_t rooks = 0;
- for (uint8_t i = 0; i < 8; ++i)
- if (board[i] == 'R')
- rooks++;
- _SCL_boardPlaceOnNthAvailable(board,0,rooks == 2 ? 'N' : 'R');
-
- for (uint8_t i = 0; i < 8; ++i)
- board[56 + i] = SCL_pieceToColor(board[i],0);
- #if SCL_960_CASTLING
- _SCL_board960RememberRookPositions(board);
- #else
- SCL_boardDisableCastling(board);
- #endif
- }
- uint8_t SCL_boardsDiffer(SCL_Board b1, SCL_Board b2)
- {
- const char *p1 = b1, *p2 = b2;
- while (p1 < b1 + SCL_BOARD_STATE_SIZE)
- {
- if (*p1 != *p2)
- return 1;
- p1++;
- p2++;
- }
- return 0;
- }
- void SCL_recordInit(SCL_Record r)
- {
- r[0] = 0 | SCL_RECORD_END;
- r[1] = 0;
- }
- void SCL_recordFromPGN(SCL_Record r, const char *pgn)
- {
- SCL_Board board;
- SCL_boardInit(board);
- SCL_recordInit(r);
- uint8_t state = 0;
- uint8_t evenMove = 0;
- while (*pgn != 0)
- {
- switch (state)
- {
- case 0: // skipping tags and spaces, outside []
- if (*pgn == '1')
- state = 2;
- else if (*pgn == '[')
- state = 1;
- break;
- case 1: // skipping tags and spaces, inside []
- if (*pgn == ']')
- state = 0;
- break;
- case 2: // reading move number
- if (*pgn == '{')
- state = 3;
- else if ((*pgn >= 'a' && *pgn <= 'h') || (*pgn >= 'A' && *pgn <= 'Z'))
- {
- state = 4;
- pgn--;
- }
- break;
- case 3: // initial comment
- if (*pgn == '}')
- state = 2;
- break;
- case 4: // reading move
- {
- char piece = 'p';
- char promoteTo = 'q';
- uint8_t castle = 0;
- uint8_t promotion = 0;
- int8_t coords[4];
- uint8_t ranks = 0, files = 0;
- for (uint8_t i = 0; i < 4; ++i)
- coords[i] = -1;
- while (*pgn != ' ' && *pgn != '\n' &&
- *pgn != '\t' && *pgn != '{' && *pgn != 0)
- {
- if (*pgn == '=')
- promotion = 1;
- if (*pgn == 'O' || *pgn == '0')
- castle++;
- if (*pgn >= 'A' && *pgn <= 'Z')
- {
- if (promotion)
- promoteTo = *pgn;
- else
- piece = *pgn;
- }
- else if (*pgn >= 'a' && *pgn <= 'h')
- {
- coords[files * 2] = *pgn - 'a';
- files++;
- }
- else if (*pgn >= '1' && *pgn <= '8')
- {
- coords[1 + ranks * 2] = *pgn - '1';
- ranks++;
- }
- pgn++;
- }
- if (castle)
- {
- piece = 'K';
- coords[0] = 4;
- coords[1] = 0;
- coords[2] = castle < 3 ? 6 : 2;
- coords[3] = 0;
- if (evenMove)
- {
- coords[1] = 7;
- coords[3] = 7;
- }
- }
- piece = SCL_pieceToColor(piece,evenMove == 0);
- if (coords[2] < 0)
- {
- coords[2] = coords[0];
- coords[0] = -1;
- }
- if (coords[3] < 0)
- {
- coords[3] = coords[1];
- coords[1] = -1;
- }
-
- uint8_t squareTo = coords[3] * 8 + coords[2];
- if (coords[0] < 0 || coords[1] < 0)
- {
- // without complete starting coords we have to find the piece
- for (int i = 0; i < SCL_BOARD_SQUARES; ++i)
- if (board[i] == piece)
- {
- SCL_SquareSet s;
- SCL_squareSetClear(s);
- SCL_boardGetMoves(board,i,s);
- if (SCL_squareSetContains(s,squareTo) &&
- (coords[0] < 0 || coords[0] == i % 8) &&
- (coords[1] < 0 || coords[1] == i / 8))
- {
- coords[0] = i % 8;
- coords[1] = i / 8;
- break;
- }
- }
- }
- uint8_t squareFrom = coords[1] * 8 + coords[0];
- SCL_boardMakeMove(board,squareFrom,squareTo,promoteTo);
- // for some reason tcc bugs here, the above line sets squareFrom to 0 lol
- // can be fixed with doing "squareFrom = coords[1] * 8 + coords[0];" again
- SCL_recordAdd(r,squareFrom,squareTo,promoteTo,SCL_RECORD_CONT);
- while (*pgn == ' ' || *pgn == '\n' || *pgn == '\t' || *pgn == '{')
- {
- if (*pgn == '{')
- while (*pgn != '}')
- pgn++;
- pgn++;
- }
- if (*pgn == 0)
- return;
- pgn--;
- if (evenMove)
- state = 2;
- evenMove = !evenMove;
- break;
- }
- default: break;
- }
- pgn++;
- }
- }
- uint16_t SCL_recordLength(const SCL_Record r)
- {
- if ((r[0] & 0x3f) == (r[1] & 0x3f)) // empty record that's only terminator
- return 0;
- uint16_t result = 0;
- while ((r[result] & 0xc0) == 0)
- result += 2;
- return (result / 2) + 1;
- }
- uint8_t SCL_recordGetMove(const SCL_Record r, uint16_t index,
- uint8_t *squareFrom, uint8_t *squareTo, char *promotedPiece)
- {
- index *= 2;
-
- uint8_t b = r[index];
- *squareFrom = b & 0x3f;
- uint8_t result = b & 0xc0;
- index++;
- b = r[index];
-
- *squareTo = b & 0x3f;
- b &= 0xc0;
- switch (b)
- {
- case SCL_RECORD_PROM_Q: *promotedPiece = 'q'; break;
- case SCL_RECORD_PROM_R: *promotedPiece = 'r'; break;
- case SCL_RECORD_PROM_B: *promotedPiece = 'b'; break;
- case SCL_RECORD_PROM_N:
- default: *promotedPiece = 'n'; break;
- }
- return result;
- }
- uint8_t SCL_recordAdd(SCL_Record r, uint8_t squareFrom,
- uint8_t squareTo, char promotePiece, uint8_t endState)
- {
- uint16_t l = SCL_recordLength(r);
- if (l >= SCL_RECORD_MAX_LENGTH)
- return 0;
- l *= 2;
- if (l != 0)
- r[l - 2] &= 0x3f; // remove the end flag from previous item
- if (endState == SCL_RECORD_CONT)
- endState = SCL_RECORD_END;
- r[l] = squareFrom | endState;
- uint8_t p;
- switch (promotePiece)
- {
- case 'n': case 'N': p = SCL_RECORD_PROM_N; break;
- case 'b': case 'B': p = SCL_RECORD_PROM_B; break;
- case 'r': case 'R': p = SCL_RECORD_PROM_R; break;
- case 'q': case 'Q':
- default: p = SCL_RECORD_PROM_Q; break;
- }
- l++;
- r[l] = squareTo | p;
- return 1;
- }
- uint8_t SCL_recordRemoveLast(SCL_Record r)
- {
- uint16_t l = SCL_recordLength(r);
-
- if (l == 0)
- return 0;
- if (l == 1)
- SCL_recordInit(r);
- else
- {
- l = (l - 2) * 2;
- r[l] = (r[l] & 0x3f) | SCL_RECORD_END;
- }
-
- return 1;
- }
- void SCL_recordApply(const SCL_Record r, SCL_Board b, uint16_t moves)
- {
- SCL_boardInit(b);
- uint16_t l = SCL_recordLength(r);
- if (moves > l)
- moves = l;
- for (uint16_t i = 0; i < moves; ++i)
- {
- uint8_t s0, s1;
- char p;
- SCL_recordGetMove(r,i,&s0,&s1,&p);
- SCL_boardMakeMove(b,s0,s1,p);
- }
- }
- void SCL_boardUndoMove(SCL_Board board, SCL_MoveUndo moveUndo)
- {
- #if SCL_960_CASTLING
- char squareToNow = board[moveUndo.squareTo];
- #endif
- board[moveUndo.squareFrom] = board[moveUndo.squareTo];
- board[moveUndo.squareTo] = moveUndo.other & 0x7f;
- board[SCL_BOARD_PLY_BYTE]--;
- board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] = moveUndo.enPassantCastle;
- board[SCL_BOARD_MOVE_COUNT_BYTE] = moveUndo.moveCount;
- if (moveUndo.other & 0x80)
- {
- moveUndo.squareTo /= 8;
- if (moveUndo.squareTo == 0 || moveUndo.squareTo == 7)
- board[moveUndo.squareFrom] = SCL_pieceIsWhite(board[moveUndo.squareFrom])
- ? 'P' : 'p';
- // ^ was promotion
- else
- board[(moveUndo.squareFrom / 8) * 8 + (moveUndo.enPassantCastle & 0x0f)] =
- (board[moveUndo.squareFrom] == 'P') ? 'p' : 'P'; // was en passant
- }
- #if !SCL_960_CASTLING
- else if (board[moveUndo.squareFrom] == 'k' && // black castling
- moveUndo.squareFrom == 60)
- {
- if (moveUndo.squareTo == 58)
- {
- board[59] = '.';
- board[56] = 'r';
- }
- else if (moveUndo.squareTo == 62)
- {
- board[61] = '.';
- board[63] = 'r';
- }
- }
- else if (board[moveUndo.squareFrom] == 'K' && // white castling
- moveUndo.squareFrom == 4)
- {
- if (moveUndo.squareTo == 2)
- {
- board[3] = '.';
- board[0] = 'R';
- }
- else if (moveUndo.squareTo == 6)
- {
- board[5] = '.';
- board[7] = 'R';
- }
- }
- #else // 960 castling
- else if (((moveUndo.other & 0x7f) == 'r') && // black castling
- (squareToNow == '.' || !SCL_pieceIsWhite(squareToNow)))
- {
- board[moveUndo.squareTo < moveUndo.squareFrom ? 59 : 61] = '.';
- board[moveUndo.squareTo < moveUndo.squareFrom ? 58 : 62] = '.';
- board[moveUndo.squareFrom] = 'k';
- board[moveUndo.squareTo] = 'r';
- }
- else if (((moveUndo.other & 0x7f) == 'R') && // white castling
- (squareToNow == '.' || SCL_pieceIsWhite(squareToNow)))
- {
- board[moveUndo.squareTo < moveUndo.squareFrom ? 3 : 5] = '.';
- board[moveUndo.squareTo < moveUndo.squareFrom ? 2 : 6] = '.';
- board[moveUndo.squareFrom] = 'K';
- board[moveUndo.squareTo] = 'R';
- }
- #endif
- }
- /**
- Potentially disables castling rights according to whether something moved from
- or to a square with a rook.
- */
- void _SCL_handleRookActivity(SCL_Board board, uint8_t rookSquare)
- {
- #if !SCL_960_CASTLING
- switch (rookSquare)
- {
- case 0: board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x20; break;
- case 7: board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x10; break;
- case 56: board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x80; break;
- case 63: board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x40; break;
- default: break;
- }
- #else // 960 castling
- if (rookSquare == (board[SCL_BOARD_EXTRA_BYTE] & 0x07))
- board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x20;
- else if (rookSquare == (board[SCL_BOARD_EXTRA_BYTE] >> 3))
- board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x10;
- else if (rookSquare == 56 + (board[SCL_BOARD_EXTRA_BYTE] & 0x07))
- board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x80;
- else if (rookSquare == 56 + (board[SCL_BOARD_EXTRA_BYTE] >> 3))
- board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= (uint8_t) ~0x40;
- #endif
- }
- SCL_MoveUndo SCL_boardMakeMove(SCL_Board board, uint8_t squareFrom, uint8_t squareTo,
- char promotePiece)
- {
- char s = board[squareFrom];
- SCL_MoveUndo moveUndo;
- moveUndo.squareFrom = squareFrom;
- moveUndo.squareTo = squareTo;
- moveUndo.moveCount = board[SCL_BOARD_MOVE_COUNT_BYTE];
- moveUndo.enPassantCastle = board[SCL_BOARD_ENPASSANT_CASTLE_BYTE];
- moveUndo.other = board[squareTo];
- // reset the en-passant state
- board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] |= 0x0f;
- if (SCL_boardMoveResetsCount(board,squareFrom,squareTo))
- board[SCL_BOARD_MOVE_COUNT_BYTE] = 0;
- else
- board[SCL_BOARD_MOVE_COUNT_BYTE]++;
- #if SCL_960_CASTLING
- uint8_t castled = 0;
- #endif
- if ((s == 'k') || (s == 'K'))
- {
- #if !SCL_960_CASTLING
- if ((squareFrom == 4) || (squareFrom == 60)) // check castling
- {
- int8_t difference = squareTo - squareFrom;
- char rook = SCL_pieceToColor('r',SCL_pieceIsWhite(s));
- if (difference == 2) // short
- {
- board[squareTo - 1] = rook;
- board[squareTo + 1] = '.';
- }
- else if (difference == -2) // long
- {
- board[squareTo - 2] = '.';
- board[squareTo + 1] = rook;
- }
- }
- #else // 960 castling
- uint8_t isWhite = SCL_pieceIsWhite(s);
- char rook = SCL_pieceToColor('r',isWhite);
- if (board[squareTo] == rook)
- {
- castled = 1;
-
- board[squareFrom] = '.';
- board[squareTo] = '.';
- if (squareTo > squareFrom) // short
- {
- board[isWhite ? 6 : (56 + 6)] = s;
- board[isWhite ? 5 : (56 + 5)] = rook;
- }
- else // long
- {
- board[isWhite ? 2 : (56 + 2)] = s;
- board[isWhite ? 3 : (56 + 3)] = rook;
- }
- }
- #endif
- // after king move disable castling
- board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= ~(0x03 << ((s == 'K') ? 4 : 6));
- }
- else if ((s == 'p') || (s == 'P'))
- {
- uint8_t row = squareTo / 8;
- int8_t rowDiff = squareFrom / 8 - row;
- if (rowDiff == 2 || rowDiff == -2) // record en passant column
- {
- board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] =
- (board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & 0xf0) | (squareFrom % 8);
- }
- if (row == 0 || row == 7)
- {
- // promotion
- s = SCL_pieceToColor(promotePiece,SCL_pieceIsWhite(s));
- moveUndo.other |= 0x80;
- }
- else
- {
- // check en passant move
- int8_t columnDiff = (squareTo % 8) - (squareFrom % 8);
- if ((columnDiff != 0) && (board[squareTo] == '.'))
- {
- board[squareFrom + columnDiff] = '.';
- moveUndo.other |= 0x80;
- }
- }
- }
- else if ((s == 'r') || (s == 'R'))
- _SCL_handleRookActivity(board,squareFrom);
- char taken = board[squareTo];
- // taking a rook may also disable castling:
- if (taken == 'R' || taken == 'r')
- _SCL_handleRookActivity(board,squareTo);
- #if SCL_960_CASTLING
- if (!castled)
- #endif
- {
- board[squareTo] = s;
- board[squareFrom] = '.';
- }
- board[SCL_BOARD_PLY_BYTE]++; // increase ply count
- return moveUndo;
- }
- void SCL_boardSetPosition(SCL_Board board, const char *pieces,
- uint8_t castlingEnPassant, uint8_t moveCount, uint8_t ply)
- {
- for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, pieces++)
- if (*pieces != 0)
- board[i] = *pieces;
- else
- break;
- board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] = castlingEnPassant;
- board[SCL_BOARD_PLY_BYTE] = ply;
- board[SCL_BOARD_MOVE_COUNT_BYTE] = moveCount;
- board[SCL_BOARD_STATE_SIZE - 1] = 0;
- }
- void SCL_squareSetAdd(SCL_SquareSet squareSet, uint8_t square)
- {
- squareSet[square / 8] |= 0x01 << (square % 8);
- }
- uint8_t SCL_squareSetContains(const SCL_SquareSet squareSet, uint8_t square)
- {
- return squareSet[square / 8] & (0x01 << (square % 8));
- }
- uint8_t SCL_squareSetSize(const SCL_SquareSet squareSet)
- {
- uint8_t result = 0;
- for (uint8_t i = 0; i < 8; ++i)
- {
- uint8_t byte = squareSet[i];
- for (uint8_t j = 0; j < 8; ++j)
- {
- result += byte & 0x01;
- byte >>= 1;
- }
- }
- return result;
- }
- uint8_t SCL_squareSetEmpty(const SCL_SquareSet squareSet)
- {
- for (uint8_t i = 0; i < 8; ++i)
- if (squareSet[i] != 0)
- return 0;
- return 1;
- }
- uint8_t SCL_squareSetGetRandom(
- const SCL_SquareSet squareSet, SCL_RandomFunction randFunc)
- {
- uint8_t size = SCL_squareSetSize(squareSet);
- if (size == 0)
- return 0;
- uint8_t n = (randFunc() % size) + 1;
- uint8_t i = 0;
- while (i < SCL_BOARD_SQUARES)
- {
- if (SCL_squareSetContains(squareSet,i))
- {
- n--;
- if (n == 0)
- break;
- }
- ++i;
- }
- return i;
- }
- void SCL_boardCopy(const SCL_Board boardFrom, SCL_Board boardTo)
- {
- for (uint8_t i = 0; i < SCL_BOARD_STATE_SIZE; ++i)
- boardTo[i] = boardFrom[i];
- }
- uint8_t SCL_boardSquareAttacked(
- SCL_Board board,
- uint8_t square,
- uint8_t byWhite)
- {
- const char *currentSquare = board;
- /* We need to place a temporary piece on the tested square in order to test if
- the square is attacked (consider testing if attacked by a pawn). */
- char previous = board[square];
- board[square] = SCL_pieceToColor('r',!byWhite);
- for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++currentSquare)
- {
- char s = *currentSquare;
-
- if ((s == '.') || (SCL_pieceIsWhite(s) != byWhite))
- continue;
- SCL_SquareSet moves;
- SCL_boardGetPseudoMoves(board,i,0,moves);
- if (SCL_squareSetContains(moves,square))
- {
- board[square] = previous;
- return 1;
- }
- }
-
- board[square] = previous;
- return 0;
- }
- uint8_t SCL_boardCheck(SCL_Board board,uint8_t white)
- {
- const char *square = board;
- char kingChar = white ? 'K' : 'k';
- for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++square)
- if ((*square == kingChar &&
- SCL_boardSquareAttacked(board,i,!white)))
- return 1;
- return 0;
- }
- uint8_t SCL_boardGameOver(SCL_Board board)
- {
- uint8_t position = SCL_boardGetPosition(board);
- return (position == SCL_POSITION_MATE) ||
- (position == SCL_POSITION_STALEMATE) ||
- (position == SCL_POSITION_DEAD);
- }
- uint8_t SCL_boardMovePossible(SCL_Board board)
- {
- uint8_t white = SCL_boardWhitesTurn(board);
- for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i)
- {
- char s = board[i];
- if ((s != '.') && (SCL_pieceIsWhite(s) == white))
- {
- SCL_SquareSet moves;
- SCL_boardGetMoves(board,i,moves);
- if (SCL_squareSetSize(moves) != 0)
- return 1;
- }
- }
- return 0;
- }
- uint8_t SCL_boardMate(SCL_Board board)
- {
- return SCL_boardGetPosition(board) == SCL_POSITION_MATE;
- }
- void SCL_boardGetPseudoMoves(
- SCL_Board board,
- uint8_t pieceSquare,
- uint8_t checkCastling,
- SCL_SquareSet result)
- {
- char piece = board[pieceSquare];
- SCL_squareSetClear(result);
- uint8_t isWhite = SCL_pieceIsWhite(piece);
- int8_t horizontalPosition = pieceSquare % 8;
- int8_t pawnOffset = -8;
- switch (piece)
- {
- case 'P':
- pawnOffset = 8;
- case 'p':
- {
- uint8_t square = pieceSquare + pawnOffset;
- uint8_t verticalPosition = pieceSquare / 8;
- if (board[square] == '.') // forward move
- {
- SCL_squareSetAdd(result,square);
- if (verticalPosition == (1 + (piece == 'p') * 5)) // start position?
- {
- uint8_t square2 = square + pawnOffset;
- if (board[square2] == '.')
- SCL_squareSetAdd(result,square2);
- }
- }
- #define checkDiagonal(hor,add) \
- if (horizontalPosition != hor) \
- { \
- uint8_t square2 = square + add; \
- char c = board[square2]; \
- if (c != '.' && SCL_pieceIsWhite(c) != isWhite) \
- SCL_squareSetAdd(result,square2); \
- }
- // diagonal moves
- checkDiagonal(0,-1)
- checkDiagonal(7,1)
- uint8_t enPassantRow = 4;
- uint8_t enemyPawn = 'p';
- if (piece == 'p')
- {
- enPassantRow = 3;
- enemyPawn = 'P';
- }
- // en-passant moves
- if (verticalPosition == enPassantRow)
- {
- uint8_t enPassantColumn = board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & 0x0f;
- uint8_t column = pieceSquare % 8;
- for (int8_t offset = -1; offset < 2; offset += 2)
- if ((enPassantColumn == column + offset) &&
- (board[pieceSquare + offset] == enemyPawn))
- {
- SCL_squareSetAdd(result,pieceSquare + pawnOffset + offset);
- break;
- }
- }
- #undef checkDiagonal
- }
- break;
- case 'r': // rook
- case 'R':
- case 'b': // bishop
- case 'B':
- case 'q': // queen
- case 'Q':
- {
- const int8_t offsets[8] = {-8, 1, 8, -1, -7, 9, -9, 7};
- const int8_t columnDirs[8] = { 0, 1, 0, -1, 1, 1, -1,-1};
- uint8_t from = (piece == 'b' || piece == 'B') * 4;
- uint8_t to = 4 + (piece != 'r' && piece != 'R') * 4;
- for (uint8_t i = from; i < to; ++i)
- {
- int8_t offset = offsets[i];
- int8_t columnDir = columnDirs[i];
- int8_t square = pieceSquare;
- int8_t col = horizontalPosition;
- while (1)
- {
- square += offset;
- col += columnDir;
- if (square < 0 || square > 63 || col < 0 || col > 7)
- break;
- char squareC = board[square];
- if (squareC == '.')
- SCL_squareSetAdd(result,square);
- else
- {
- if (SCL_pieceIsWhite(squareC) != isWhite)
- SCL_squareSetAdd(result,square);
- break;
- }
- }
- }
- }
- break;
- case 'n': // knight
- case 'N':
- {
- const int8_t offsets[4] = {6, 10, 15, 17};
- const int8_t columnsMinus[4] = {2,-2,1,-1};
- const int8_t columnsPlus[4] = {-2,2,-1,1};
- const int8_t *off, *col;
- #define checkOffsets(op,comp,limit,dir)\
- off = offsets;\
- col = columns ## dir;\
- for (uint8_t i = 0; i < 4; ++i, ++off, ++col)\
- {\
- int8_t square = pieceSquare op (*off);\
- if (square comp limit) /* out of board? */\
- break;\
- int8_t horizontalCheck = horizontalPosition + (*col);\
- if (horizontalCheck < 0 || horizontalCheck >= 8)\
- continue;\
- char squareC = board[square];\
- if ((squareC == '.') || (SCL_pieceIsWhite(squareC) != isWhite))\
- SCL_squareSetAdd(result,square);\
- }
- checkOffsets(-,<,0,Minus)
- checkOffsets(+,>=,SCL_BOARD_SQUARES,Plus)
- #undef checkOffsets
- }
- break;
- case 'k': // king
- case 'K':
- {
- uint8_t verticalPosition = pieceSquare / 8;
- uint8_t
- u = verticalPosition != 0,
- d = verticalPosition != 7,
- l = horizontalPosition != 0,
- r = horizontalPosition != 7;
- uint8_t square2 = pieceSquare - 9;
- #define checkSquare(cond,add) \
- if (cond && ((board[square2] == '.') || \
- (SCL_pieceIsWhite(board[square2])) != isWhite))\
- SCL_squareSetAdd(result,square2);\
- square2 += add;
- checkSquare(l && u,1)
- checkSquare(u,1)
- checkSquare(r && u,6)
- checkSquare(l,2)
- checkSquare(r,6)
- checkSquare(l && d,1)
- checkSquare(d,1)
- checkSquare(r && d,0)
- #undef checkSquare
- // castling:
- if (checkCastling)
- {
- uint8_t bitShift = 4 + 2 * (!isWhite);
- if ((board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & (0x03 << bitShift)) &&
- !SCL_boardSquareAttacked(board,pieceSquare,!isWhite)) // no check?
- {
- #if !SCL_960_CASTLING
- // short castle:
- pieceSquare++;
-
- if ((board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & (0x01 << bitShift)) &&
- (board[pieceSquare] == '.') &&
- (board[pieceSquare + 1] == '.') &&
- (board[pieceSquare + 2] == SCL_pieceToColor('r',isWhite)) &&
- !SCL_boardSquareAttacked(board,pieceSquare,!isWhite))
- SCL_squareSetAdd(result,pieceSquare + 1);
- /* note: don't check the final square for check, it will potentially
- be removed later (can't end up in check) */
- // long castle:
- pieceSquare -= 2;
- if ((board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & (0x02 << bitShift)) &&
- (board[pieceSquare] == '.') &&
- (board[pieceSquare - 1] == '.') &&
- (board[pieceSquare - 2] == '.') &&
- (board[pieceSquare - 3] == SCL_pieceToColor('r',isWhite)) &&
- !SCL_boardSquareAttacked(board,pieceSquare,!isWhite))
- SCL_squareSetAdd(result,pieceSquare - 1);
- #else // 960 castling
- for (int i = 0; i < 2; ++i) // short and long
- if (board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & ((i + 1) << bitShift))
- {
- uint8_t
- rookPos = board[SCL_BOARD_EXTRA_BYTE] >> 3,
- targetPos = 5;
- if (i == 1)
- {
- rookPos = board[SCL_BOARD_EXTRA_BYTE] & 0x07,
- targetPos = 3;
- }
- if (!isWhite)
- {
- rookPos += 56;
- targetPos += 56;
- }
- uint8_t ok = board[rookPos] == SCL_pieceToColor('r',isWhite);
- if (!ok)
- continue;
- int8_t inc = 1 - 2 * (targetPos > rookPos);
- while (targetPos != rookPos) // check vacant squares for the rook
- {
- if (board[targetPos] != '.' &&
- targetPos != pieceSquare)
- {
- ok = 0;
- break;
- }
- targetPos += inc;
- }
- if (!ok)
- continue;
- targetPos = i == 0 ? 6 : 2;
- if (!isWhite)
- targetPos += 56;
- inc = 1 - 2 * (targetPos > pieceSquare);
- while (targetPos != pieceSquare) // check squares for the king
- {
- if ((board[targetPos] != '.' &&
- targetPos != rookPos) ||
- SCL_boardSquareAttacked(board,targetPos,!isWhite))
- {
- ok = 0;
- break;
- }
- targetPos += inc;
- }
- if (ok)
- SCL_squareSetAdd(result,rookPos);
- }
- #endif
- }
- }
- }
- break;
- default:
- break;
- }
- }
- void SCL_printSquareSet(SCL_SquareSet set, SCL_PutCharFunction putCharFunc)
- {
- uint8_t first = 1;
- putCharFunc('(');
- for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i)
- {
- if (!SCL_squareSetContains(set, i))
- continue;
- if (!first)
- putCharFunc(',');
- else
- first = 0;
- putCharFunc('A' + i % 8);
- putCharFunc('1' + i / 8);
- }
- putCharFunc(')');
- }
- void SCL_printSquareUTF8(uint8_t square, SCL_PutCharFunction putCharFunc)
- {
- uint32_t val = 0;
- switch (square)
- {
- case 'r': val = 0x9c99e200; break;
- case 'n': val = 0x9e99e200; break;
- case 'b': val = 0x9d99e200; break;
- case 'q': val = 0x9b99e200; break;
- case 'k': val = 0x9a99e200; break;
- case 'p': val = 0x9f99e200; break;
- case 'R': val = 0x9699e200; break;
- case 'N': val = 0x9899e200; break;
- case 'B': val = 0x9799e200; break;
- case 'Q': val = 0x9599e200; break;
- case 'K': val = 0x9499e200; break;
- case 'P': val = 0x9999e200; break;
- case '.': val = 0x9296e200; break;
- case ',': val = 0x9196e200; break;
- default: putCharFunc(square); return; break;
- }
- uint8_t count = 4;
- while ((val % 256 == 0) && (count > 0))
- {
- val /= 256;
- count--;
- }
- while (count > 0)
- {
- putCharFunc(val % 256);
- val /= 256;
- count--;
- }
- }
- void SCL_boardGetMoves(
- SCL_Board board,
- uint8_t pieceSquare,
- SCL_SquareSet result)
- {
- SCL_SquareSet allMoves;
- SCL_squareSetClear(allMoves);
- for (uint8_t i = 0; i < 8; ++i)
- result[i] = 0;
- SCL_boardGetPseudoMoves(board,pieceSquare,1,allMoves);
- // Now only keep moves that don't lead to one's check:
- SCL_SQUARE_SET_ITERATE_BEGIN(allMoves)
- SCL_MoveUndo undo = SCL_boardMakeMove(board,pieceSquare,iteratedSquare,'q');
- if (!SCL_boardCheck(board,!SCL_boardWhitesTurn(board)))
- SCL_squareSetAdd(result,iteratedSquare);
- SCL_boardUndoMove(board,undo);
- SCL_SQUARE_SET_ITERATE_END
- }
- uint8_t SCL_boardDead(SCL_Board board)
- {
- /*
- This byte represents material by bits:
- MSB _ _ _ _ _ _ _ _ LSB
- | | | | | \_ white knight
- | | | | \__ white bishop on white
- | | | \____ white bishop on black
- | | \________ black knight
- | \__________ black bishop on white
- \____________ black bishop on black
- */
- uint8_t material = 0;
- const char *p = board;
- for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i)
- {
- char c = *p;
- switch (c)
- {
- case 'n': material |= 0x01; break;
- case 'N': material |= 0x10; break;
- case 'b': material |= (0x02 << (!SCL_squareIsWhite(i))); break;
- case 'B': material |= (0x20 << (!SCL_squareIsWhite(i))); break;
- case 'p':
- case 'P':
- case 'r':
- case 'R':
- case 'q':
- case 'Q':
- return 0; // REMOVE later if more complex check are performed
- break;
- default: break;
- }
- p++;
- }
- // TODO: add other checks than only insufficient material
- // possible combinations of insufficient material:
- return
- (material == 0x00) || // king vs king
- (material == 0x01) || // king and knight vs king
- (material == 0x10) || // king and knight vs king
- (material == 0x02) || // king and bishop vs king
- (material == 0x20) || // king and bishop vs king
- (material == 0x04) || // king and bishop vs king
- (material == 0x40) || // king and bishop vs king
- (material == 0x22) || // king and bishop vs king and bishop (same color)
- (material == 0x44); // king and bishop vs king and bishop (same color)
- }
- uint8_t SCL_boardGetPosition(SCL_Board board)
- {
- uint8_t check = SCL_boardCheck(board,SCL_boardWhitesTurn(board));
- uint8_t moves = SCL_boardMovePossible(board);
- if (check)
- return moves ? SCL_POSITION_CHECK : SCL_POSITION_MATE;
- else if (!moves)
- return SCL_POSITION_STALEMATE;
- if (SCL_boardDead(board))
- return SCL_POSITION_DEAD;
- return SCL_POSITION_NORMAL;
- }
- uint8_t SCL_stringToMove(const char *moveString, uint8_t *resultFrom,
- uint8_t *resultTo, char *resultPromotion)
- {
- char c;
- uint8_t *dst = resultFrom;
- for (uint8_t i = 0; i < 2; ++i)
- {
- c = *moveString;
-
- *dst = (c >= 'a') ? (c - 'a') : (c - 'A');
- if (*dst > 7)
- return 0;
- moveString++;
- c = *moveString;
-
- *dst += 8 * (c - '1');
-
- if (*dst > 63)
- return 0;
-
- moveString++;
- dst = resultTo;
- }
- c = *moveString;
- if (c < 'A')
- c = c - 'A' + 'a';
- switch (c)
- {
- case 'N': case 'n': *resultPromotion = 'n'; break;
- case 'B': case 'b': *resultPromotion = 'b'; break;
- case 'R': case 'r': *resultPromotion = 'r'; break;
- case 'Q': case 'q':
- default: *resultPromotion = 'q'; break;
- }
-
- return 1;
- }
- void SCL_printBoard(
- SCL_Board board,
- SCL_PutCharFunction putCharFunc,
- SCL_SquareSet highlightSquares,
- uint8_t selectSquare,
- uint8_t format,
- uint8_t offset,
- uint8_t labels,
- uint8_t blackDown)
- {
- if (labels)
- {
- for (uint8_t i = 0; i < offset + 2; ++i)
- putCharFunc(' ');
- for (uint8_t i = 0; i < 8; ++i)
- {
- if ((format != SCL_PRINT_FORMAT_COMPACT) &&
- (format != SCL_PRINT_FORMAT_COMPACT_UTF8))
- putCharFunc(' ');
- putCharFunc(blackDown ? ('H' - i) : ('A' + i));
- }
- putCharFunc('\n');
- }
-
- int8_t i = 7;
- int8_t add = 1;
- if (!blackDown)
- {
- i = 56;
- add = -1;
- }
- for (int8_t row = 0; row < 8; ++row)
- {
- for (uint8_t j = 0; j < offset; ++j)
- putCharFunc(' ');
- if (labels)
- {
- putCharFunc(!blackDown ? ('8' - row) : ('1' + row));
- putCharFunc(' ');
- }
- const char *square = board + i;
- for (int8_t col = 0; col < 8; ++col)
- {
- switch (format)
- {
- case SCL_PRINT_FORMAT_COMPACT:
- putCharFunc(
- (*square == '.') ? (
- ((i != selectSquare) ?
- (!SCL_squareSetContains(highlightSquares,i) ? *square : '*')
- : '#')) : *square);
- break;
- case SCL_PRINT_FORMAT_UTF8:
- {
- char squareChar = SCL_squareIsWhite(i) ? '.' : ',';
- char pieceChar = (*square == '.') ? squareChar : *square;
- if (i == selectSquare)
- {
- putCharFunc('(');
- if (*square == '.')
- putCharFunc(')');
- else
- SCL_printSquareUTF8(pieceChar,putCharFunc);
- }
- else if (!SCL_squareSetContains(highlightSquares,i))
- {
- SCL_printSquareUTF8(squareChar,putCharFunc);
- SCL_printSquareUTF8(pieceChar,putCharFunc);
- }
- else
- {
- putCharFunc('[');
- if (*square == '.')
- putCharFunc(']');
- else
- SCL_printSquareUTF8(*square,putCharFunc);
- }
- break;
- }
- case SCL_PRINT_FORMAT_COMPACT_UTF8:
- SCL_printSquareUTF8(
- (*square == '.') ? (
- SCL_squareSetContains(highlightSquares,i) ? '*' :
- (i == selectSquare ? '#' : ((SCL_squareIsWhite(i) ? '.' : ',')))
- ) : *square,putCharFunc);
- break;
- case SCL_PRINT_FORMAT_NORMAL:
- default:
- {
- uint8_t c = *square;
- char squareColor = SCL_squareIsWhite(i) ? ' ' : ':';
- putCharFunc((i != selectSquare) ?
- (!SCL_squareSetContains(highlightSquares,i) ?
- squareColor : '#') : '@');
- putCharFunc(c == '.' ? squareColor : *square);
- break;
- }
- }
- i -= add;
- square -= add;
- }
-
- putCharFunc('\n');
- i += add * 16;
- } // for rows
- }
- int16_t SCL_pieceValuePositive(char piece)
- {
- switch (piece)
- {
- case 'p':
- case 'P': return SCL_VALUE_PAWN; break;
- case 'n':
- case 'N': return SCL_VALUE_KNIGHT; break;
- case 'b':
- case 'B': return SCL_VALUE_BISHOP; break;
- case 'r':
- case 'R': return SCL_VALUE_ROOK; break;
- case 'q':
- case 'Q': return SCL_VALUE_QUEEN; break;
- case 'k':
- case 'K': return SCL_VALUE_KING; break;
- default: break;
- }
- return 0;
- }
- int16_t SCL_pieceValue(char piece)
- {
- switch (piece)
- {
- case 'P': return SCL_VALUE_PAWN; break;
- case 'N': return SCL_VALUE_KNIGHT; break;
- case 'B': return SCL_VALUE_BISHOP; break;
- case 'R': return SCL_VALUE_ROOK; break;
- case 'Q': return SCL_VALUE_QUEEN; break;
- case 'K': return SCL_VALUE_KING; break;
- case 'p': return -1 * SCL_VALUE_PAWN; break;
- case 'n': return -1 * SCL_VALUE_KNIGHT; break;
- case 'b': return -1 * SCL_VALUE_BISHOP; break;
- case 'r': return -1 * SCL_VALUE_ROOK; break;
- case 'q': return -1 * SCL_VALUE_QUEEN; break;
- case 'k': return -1 * SCL_VALUE_KING; break;
- default: break;
- }
- return 0;
- }
- #define ATTACK_BONUS 3
- #define MOBILITY_BONUS 10
- #define CENTER_BONUS 7
- #define CHECK_BONUS 5
- #define KING_CASTLED_BONUS 30
- #define KING_BACK_BONUS 15
- #define KING_NOT_CENTER_BONUS 15
- #define PAWN_NON_DOUBLE_BONUS 3
- #define PAWN_PAIR_BONUS 3
- #define KING_CENTERNESS 10
- int16_t _SCL_rateKingEndgamePosition(uint8_t position)
- {
- int16_t result = 0;
- uint8_t rank = position / 8;
- position %= 8;
- if (position > 1 && position < 6)
- result += KING_CENTERNESS;
- if (rank > 1 && rank < 6)
- result += KING_CENTERNESS;
- return result;
- }
- int16_t SCL_boardEvaluateStatic(SCL_Board board)
- {
- uint8_t position = SCL_boardGetPosition(board);
-
- int16_t total = 0;
- switch (position)
- {
- case SCL_POSITION_MATE:
- return SCL_boardWhitesTurn(board) ?
- -1 * SCL_EVALUATION_MAX_SCORE : SCL_EVALUATION_MAX_SCORE;
- break;
- case SCL_POSITION_STALEMATE:
- case SCL_POSITION_DEAD:
- return 0;
- break;
- /*
- main points are assigned as follows:
- - points for material as a sum of all material on board
- - for playing side: if a piece attacks piece of greater value, a fraction
- of the value difference is gained (we suppose exchange), this is only
- gained once per every attacking piece (maximum gain is taken), we only
- take fraction so that actually taking the piece is favored
- - ATTACK_BONUS points for any attacked piece
- other points are assigned as follows (in total these shouldn't be more
- than the value of one pawn)
- - mobility: MOBILITY_BONUS points for each piece with at least 4 possible
- moves
- - center control: CENTER_BONUS points for a piece on a center square
- - CHECK_BONUS points for check
- - king:
- - safety (non endgame): KING_BACK_BONUS points for king on staring rank,
- additional KING_CASTLED_BONUS if the kind if on castled square or
- closer to the edge, additional KING_NOT_CENTER_BONUS for king not on
- its start neighbouring center square
- - center closeness (endgame): up to 2 * KING_CENTERNESS points for
- being closer to center
- - non-doubled pawns: PAWN_NON_DOUBLE_BONUS points for each pawn without
- same color pawn directly in front of it
- - pawn structure: PAWN_PAIR_BONUS points for each pawn guarding own pawn
- - advancing pawns: 1 point for each pawn's rank in its move
- direction
- */
- case SCL_POSITION_CHECK:
- total += SCL_boardWhitesTurn(board) ? -1 * CHECK_BONUS : CHECK_BONUS;
- case SCL_POSITION_NORMAL:
- default:
- {
- SCL_SquareSet moves;
- const char *p = board;
- int16_t positiveMaterial = 0;
- uint8_t endgame = 0;
- // first count material to see if this is endgame or not
- for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++p)
- {
- char s = *p;
- if (s != '.')
- {
- positiveMaterial += SCL_pieceValuePositive(s);
- total += SCL_pieceValue(s);
- }
- }
- endgame = positiveMaterial <= SCL_ENDGAME_MATERIAL_LIMIT;
- p = board;
- for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++p)
- {
- char s = *p;
- if (s != '.')
- {
- uint8_t white = SCL_pieceIsWhite(s);
- switch (s)
- {
- case 'k': // king safety
- if (endgame)
- total -= _SCL_rateKingEndgamePosition(i);
- else if (i >= 56)
- {
- total -= KING_BACK_BONUS;
- if (i != 59)
- {
- total -= KING_NOT_CENTER_BONUS;
- if (i >= 62 || i <= 58)
- total -= KING_CASTLED_BONUS;
- }
- }
- break;
- case 'K':
- if (endgame)
- total += _SCL_rateKingEndgamePosition(i);
- else if (i <= 7)
- {
- total += KING_BACK_BONUS;
- if (i != 3)
- {
- total += KING_NOT_CENTER_BONUS;
- if (i <= 2 || i >= 6)
- total += KING_CASTLED_BONUS;
- }
- }
- break;
- case 'P': // pawns
- case 'p':
- {
- int8_t rank = i / 8;
- if (rank != 0 && rank != 7)
- {
- if (s == 'P')
- {
- total += rank;
- char *tmp = board + i + 8;
- if (*tmp != 'P')
- total += PAWN_NON_DOUBLE_BONUS;
- if (i % 8 != 7)
- {
- tmp++;
- if (*tmp == 'P')
- total += PAWN_PAIR_BONUS;
- if (*(tmp - 16) == 'P')
- total += PAWN_PAIR_BONUS;
- }
- }
- else
- {
- total -= 7 - rank;
- char *tmp = board + i - 8;
- if (*tmp != 'p')
- total -= PAWN_NON_DOUBLE_BONUS;
- if (i % 8 != 7)
- {
- tmp += 17;
- if (*tmp == 'p')
- total -= PAWN_PAIR_BONUS;
- if (*(tmp - 16) == 'p')
- total -= PAWN_PAIR_BONUS;
- }
- }
- }
-
- break;
- }
- default: break;
- }
- if (i >= 27 && i <= 36 && (i >= 35 || i <= 28)) // center control
- total += white ? CENTER_BONUS : (-1 * CENTER_BONUS);
- // for performance we only take pseudo moves
- SCL_boardGetPseudoMoves(board,i,0,moves);
- if (SCL_squareSetSize(moves) >= 4) // mobility
- total += white ?
- MOBILITY_BONUS : (-1 * MOBILITY_BONUS);
- int16_t exchangeBonus = 0;
- SCL_SQUARE_SET_ITERATE_BEGIN(moves)
- if (board[iteratedSquare] != '.')
- {
- total += white ?
- ATTACK_BONUS : (- 1 * ATTACK_BONUS);
- if (SCL_boardWhitesTurn(board) == white)
- {
- int16_t valueDiff =
- SCL_pieceValuePositive(board[iteratedSquare]) -
- SCL_pieceValuePositive(s);
- valueDiff /= 4; // only take a fraction to favor taking
- if (valueDiff > exchangeBonus)
- exchangeBonus = valueDiff;
- }
- }
- SCL_SQUARE_SET_ITERATE_END
- if (exchangeBonus != 0)
- total += white ? exchangeBonus : -1 * exchangeBonus;
- }
- } // for each square
- return total;
- break;
- } // normal position
- } // switch
- return 0;
- }
- #undef ATTACK_BONUS
- #undef MOBILITY_BONUS
- #undef CENTER_BONUS
- #undef CHECK_BONUS
- #undef KING_CASTLED_BONUS
- #undef KING_BACK_BONUS
- #undef PAWN_NON_DOUBLE_BONUS
- #undef PAWN_PAIR_BONUS
- #undef KING_CENTERNESS
- SCL_StaticEvaluationFunction _SCL_staticEvaluationFunction;
- int16_t _SCL_currentEval;
- int8_t _SCL_depthHardLimit;
- /**
- Inner recursive function for SCL_boardEvaluateDynamic. It is passed a square
- (or -1) at which last capture happened, to implement capture extension.
- */
- int16_t _SCL_boardEvaluateDynamic(SCL_Board board, int8_t depth,
- int16_t alphaBeta, int8_t takenSquare)
- {
- #if SCL_COUNT_EVALUATED_POSITIONS
- SCL_positionsEvaluated++;
- #endif
- #if SCL_CALL_WDT_RESET
- wdt_reset();
- #endif
- uint8_t whitesTurn = SCL_boardWhitesTurn(board);
- int8_t valueMultiply = whitesTurn ? 1 : -1;
- int16_t bestMoveValue = -1 * SCL_EVALUATION_MAX_SCORE;
- uint8_t shouldCompute = depth > 0;
- uint8_t extended = 0;
- uint8_t positionType = SCL_boardGetPosition(board);
- if (!shouldCompute)
- {
- /* here we do two extensions (deeper search): taking on a same square
- (exchanges) and checks (good for mating and preventing mates): */
- extended =
- (depth > _SCL_depthHardLimit) &&
- (takenSquare >= 0 ||
- (SCL_boardGetPosition(board) == SCL_POSITION_CHECK));
- shouldCompute = extended;
- }
- #if SCL_DEBUG_AI
- char moveStr[8];
- uint8_t debugFirst = 1;
- #endif
- if (shouldCompute &&
- (positionType == SCL_POSITION_NORMAL || positionType == SCL_POSITION_CHECK))
- {
- #if SCL_DEBUG_AI
- putchar('(');
- #endif
- alphaBeta *= valueMultiply;
- uint8_t end = 0;
- const char *b = board;
- depth--;
- for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++b)
- {
- char s = *b;
- if (s != '.' && SCL_pieceIsWhite(s) == whitesTurn)
- {
- SCL_SquareSet moves;
- SCL_squareSetClear(moves);
- SCL_boardGetMoves(board,i,moves);
- if (!SCL_squareSetEmpty(moves))
- {
- SCL_SQUARE_SET_ITERATE_BEGIN(moves)
- int8_t captureExtension = -1;
-
- if (board[iteratedSquare] != '.' && // takes a piece
- (takenSquare == -1 || // extend on first taken sq.
- (extended && takenSquare != -1) || // ignore check extension
- (iteratedSquare == takenSquare))) // extend on same sq. taken
- captureExtension = iteratedSquare;
- SCL_MoveUndo undo = SCL_boardMakeMove(board,i,iteratedSquare,'q');
- uint8_t s0Dummy, s1Dummy;
- char pDummy;
- SCL_UNUSED(s0Dummy);
- SCL_UNUSED(s1Dummy);
- SCL_UNUSED(pDummy);
- #if SCL_DEBUG_AI
- if (debugFirst)
- debugFirst = 0;
- else
- putchar(',');
- if (extended)
- putchar('*');
- printf("%s ",SCL_moveToString(board,i,iteratedSquare,'q',moveStr));
- #endif
- int16_t value = _SCL_boardEvaluateDynamic(
- board,
- depth, // this is depth - 1, we decremented it
- #if SCL_ALPHA_BETA
- valueMultiply * bestMoveValue,
- #else
- 0,
- #endif
- captureExtension
- ) * valueMultiply;
- SCL_boardUndoMove(board,undo);
- if (value > bestMoveValue)
- {
- bestMoveValue = value;
- #if SCL_ALPHA_BETA
- // alpha-beta pruning:
- if (value > alphaBeta) // no, >= can't be here
- {
- end = 1;
- iterationEnd = 1;
- }
- #endif
- }
- SCL_SQUARE_SET_ITERATE_END
- } // !squre set empty?
- } // valid piece?
- if (end)
- break;
- } // for each square
- #if SCL_DEBUG_AI
- putchar(')');
- #endif
- }
- else // don't dive recursively, evaluate statically
- {
- bestMoveValue = valueMultiply *
- #ifndef SCL_EVALUATION_FUNCTION
- _SCL_staticEvaluationFunction(board);
- #else
- SCL_EVALUATION_FUNCTION(board);
- #endif
- /* For stalemate return the opposite value of the board, i.e. if the
- position is good for white, then stalemate is good for black and vice
- versa. */
- if (positionType == SCL_POSITION_STALEMATE)
- bestMoveValue *= -1;
- }
- /* Here we either improve (if the move worsens the situation) or devalve (if
- it improves the situation) the result: this needs to be done so that good
- moves far away are seen as worse compared to equally good moves achieved
- in fewer moves. Without this an AI in winning situation may just repeat
- random moves and draw by repetition even if it has mate in 1 (it sees all
- moves as leading to mate). */
- bestMoveValue += bestMoveValue > _SCL_currentEval * valueMultiply ? -1 : 1;
- #if SCL_DEBUG_AI
- printf("%d",bestMoveValue * valueMultiply);
- #endif
- return bestMoveValue * valueMultiply;
- }
- int16_t SCL_boardEvaluateDynamic(SCL_Board board, uint8_t baseDepth,
- uint8_t extensionExtraDepth, SCL_StaticEvaluationFunction evalFunction)
- {
- _SCL_staticEvaluationFunction = evalFunction;
- _SCL_currentEval = evalFunction(board);
- _SCL_depthHardLimit = 0;
- _SCL_depthHardLimit -= extensionExtraDepth;
- return _SCL_boardEvaluateDynamic(
- board,
- baseDepth,
- SCL_boardWhitesTurn(board) ?
- SCL_EVALUATION_MAX_SCORE : (-1 * SCL_EVALUATION_MAX_SCORE),-1);
- }
- void SCL_boardRandomMove(SCL_Board board, SCL_RandomFunction randFunc,
- uint8_t *squareFrom, uint8_t *squareTo, char *resultProm)
- {
- *resultProm = (randFunc() < 128) ?
- ((randFunc() < 128) ? 'r' : 'n') :
- ((randFunc() < 128) ? 'b' : 'q');
- SCL_SquareSet set;
- uint8_t white = SCL_boardWhitesTurn(board);
- const char *s = board;
- SCL_squareSetClear(set);
- // find squares with pieces that have legal moves
- for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++s)
- {
- char c = *s;
- if (c != '.' && SCL_pieceIsWhite(c) == white)
- {
- SCL_SquareSet moves;
- SCL_boardGetMoves(board,i,moves);
- if (SCL_squareSetSize(moves) != 0)
- SCL_squareSetAdd(set,i);
- }
- }
-
- *squareFrom = SCL_squareSetGetRandom(set,randFunc);
- SCL_boardGetMoves(board,*squareFrom,set);
- *squareTo = SCL_squareSetGetRandom(set,randFunc);
- }
- void SCL_printBoardSimple(
- SCL_Board board,
- SCL_PutCharFunction putCharFunc,
- uint8_t selectSquare,
- uint8_t format)
- {
- SCL_SquareSet s;
- SCL_squareSetClear(s);
- SCL_printBoard(board,putCharFunc,s,selectSquare,format,1,1,0);
- }
- int16_t SCL_getAIMove(
- SCL_Board board,
- uint8_t baseDepth,
- uint8_t extensionExtraDepth,
- uint8_t endgameExtraDepth,
- SCL_StaticEvaluationFunction evalFunc,
- SCL_RandomFunction randFunc,
- uint8_t randomness,
- uint8_t repetitionMoveFrom,
- uint8_t repetitionMoveTo,
- uint8_t *resultFrom,
- uint8_t *resultTo,
- char *resultProm)
- {
- #if SCL_DEBUG_AI
- puts("===== AI debug =====");
- putchar('(');
- unsigned char debugFirst = 1;
- char moveStr[8];
- #endif
- if (baseDepth == 0)
- {
- SCL_boardRandomMove(board,randFunc,resultFrom,resultTo,resultProm);
- #ifndef SCL_EVALUATION_FUNCTION
- return evalFunc(board);
- #else
- return SCL_EVALUATION_FUNCTION(board);
- #endif
- }
- if (SCL_boardEstimatePhase(board) == SCL_PHASE_ENDGAME)
- baseDepth += endgameExtraDepth;
- *resultFrom = 0;
- *resultTo = 0;
- *resultProm = 'q';
- int16_t bestScore =
- SCL_boardWhitesTurn(board) ?
- -1 * SCL_EVALUATION_MAX_SCORE - 1 : (SCL_EVALUATION_MAX_SCORE + 1);
- for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i)
- if (board[i] != '.' &&
- SCL_boardWhitesTurn(board) == SCL_pieceIsWhite(board[i]))
- {
- SCL_SquareSet moves;
- SCL_squareSetClear(moves);
- SCL_boardGetMoves(board,i,moves);
- SCL_SQUARE_SET_ITERATE_BEGIN(moves)
- int16_t score = 0;
- #if SCL_DEBUG_AI
- if (debugFirst)
- debugFirst = 0;
- else
- putchar(',');
- printf("%s ",SCL_moveToString(
- board,i,iteratedSquare,'q',moveStr));
- #endif
- if (i != repetitionMoveFrom || iteratedSquare != repetitionMoveTo)
- {
- SCL_MoveUndo undo = SCL_boardMakeMove(board,i,iteratedSquare,'q');
- score = SCL_boardEvaluateDynamic(board,baseDepth - 1,
- extensionExtraDepth,evalFunc);
- SCL_boardUndoMove(board,undo);
- }
- if (randFunc != 0 &&
- randomness > 1 &&
- score < 16000 &&
- score > -16000)
- {
- /*^ We limit randomizing by about half the max score for two reasons:
- to prevent over/under flows and secondly we don't want to alter
- the highest values for checkmate -- these are modified by tiny
- values depending on their depth so as to prevent endless loops in
- which most moves are winning, biasing such values would completely
- kill that algorithm */
- int16_t bias = randFunc();
- bias = (bias - 128) / 2;
- bias *= randomness - 1;
- score += bias;
- }
- uint8_t comparison =
- score == bestScore;
- if ((comparison != 1) &&
- (
- (SCL_boardWhitesTurn(board) && score > bestScore) ||
- (!SCL_boardWhitesTurn(board) && score < bestScore)
- ))
- comparison = 2;
- uint8_t replace = 0;
- if (randFunc == 0)
- replace = comparison == 2;
- else
- replace = (comparison == 2) ||
- ((comparison == 1) && (randFunc() < 160)); // not uniform distr. but simple
- if (replace)
- {
- *resultFrom = i;
- *resultTo = iteratedSquare;
- bestScore = score;
- }
- SCL_SQUARE_SET_ITERATE_END
- }
- #if SCL_DEBUG_AI
- printf(")%d %s\n",bestScore,SCL_moveToString(board,*resultFrom,*resultTo,'q',moveStr));
- puts("===== AI debug end ===== ");
- #endif
- return bestScore;
- }
- uint8_t SCL_boardToFEN(SCL_Board board, char *string)
- {
- uint8_t square = 56;
- uint8_t spaces = 0;
- uint8_t result = 0;
-
- #define put(c) { *string = (c); string++; result++; }
- while (1) // pieces
- {
- char s = board[square];
- if (s == '.')
- {
- spaces++;
- }
- else
- {
- if (spaces != 0)
- {
- put('0' + spaces)
- spaces = 0;
- }
- put(s)
- }
- square++;
- if (square % 8 == 0)
- {
- if (spaces != 0)
- {
- put('0' + spaces)
- spaces = 0;
- }
- if (square == 8)
- break;
-
- put('/');
- square -= 16;
- }
- }
- put(' ');
- put(SCL_boardWhitesTurn(board) ? 'w' : 'b');
- put(' ');
- uint8_t b = board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & 0xf0;
- if (b != 0) // castling
- {
- if (b & 0x10) put('K');
- if (b & 0x20) put('Q');
- if (b & 0x40) put('k');
- if (b & 0x80) put('q');
- }
- else
- put('-');
- put(' ');
- b = board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] & 0x0f;
- if (b < 8)
- {
- put('a' + b);
- put(SCL_boardWhitesTurn(board) ? '6' : '3');
- }
- else
- put('-');
- for (uint8_t i = 0; i < 2; ++i)
- {
- put(' ');
- uint8_t moves = i == 0 ?
- ((uint8_t) board[SCL_BOARD_MOVE_COUNT_BYTE]) :
- (((uint8_t) board[SCL_BOARD_PLY_BYTE]) / 2 + 1);
- uint8_t hundreds = moves / 100;
- uint8_t tens = (moves % 100) / 10;
- if (hundreds != 0)
- {
- put('0' + hundreds);
- put('0' + tens);
- }
- else if (tens != 0)
- put('0' + tens);
- put('0' + moves % 10);
-
- }
- *string = 0; // terminate the string
- return result + 1;
- #undef put
- }
- uint8_t SCL_boardFromFEN(SCL_Board board, const char *string)
- {
- uint8_t square = 56;
- while (1)
- {
- char c = *string;
- if (c == 0)
- return 0;
- if (c != '/' && c != ' ') // ignore line separators
- {
- if (c < '9') // empty square sequence
- {
- while (c > '0')
- {
- board[square] = '.';
- square++;
- c--;
- }
- }
- else // piece
- {
- board[square] = c;
- square++;
- }
- }
- else
- {
- if (square == 8)
- break;
- square -= 16;
- }
- string++;
- }
- #define nextChar string++; if (*string == 0) return 0;
- nextChar // space
- board[SCL_BOARD_PLY_BYTE] = *string == 'b';
- nextChar
- nextChar // space
- uint8_t castleEnPassant = 0x0;
- while (*string != ' ')
- {
- switch (*string)
- {
- case 'K': castleEnPassant |= 0x10; break;
- case 'Q': castleEnPassant |= 0x20; break;
- case 'k': castleEnPassant |= 0x40; break;
- case 'q': castleEnPassant |= 0x80; break;
- default: castleEnPassant |= 0xf0; break; // for partial XFEN compat.
- }
- nextChar
- }
- nextChar // space
- if (*string != '-')
- {
- castleEnPassant |= *string - 'a';
- nextChar
- }
- else
- castleEnPassant |= 0x0f;
- nextChar
- board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] = castleEnPassant;
- for (uint8_t i = 0; i < 2; ++i)
- {
- nextChar // space
- uint8_t ply = 0;
- while (1)
- {
- char c = *string;
- if (c < '0' || c > '9')
- break;
- ply = ply * 10 + (c - '0');
- string++;
- }
- if (i == 0 && *string == 0)
- return 0;
- if (i == 0)
- board[SCL_BOARD_MOVE_COUNT_BYTE] = ply;
- else
- board[SCL_BOARD_PLY_BYTE] += (ply - 1) * 2;
- }
- #if SCL_960_CASTLING
- _SCL_board960RememberRookPositions(board);
- #endif
- return 1;
- #undef nextChar
- }
- uint8_t SCL_boardEstimatePhase(SCL_Board board)
- {
- uint16_t totalMaterial = 0;
- uint8_t ply = board[SCL_BOARD_PLY_BYTE];
- for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i)
- {
- char s = *board;
- if (s != '.')
- {
- int16_t v = SCL_pieceValue(s);
- if (!SCL_pieceIsWhite(s))
- v *= -1;
- totalMaterial += v;
- }
- board++;
- }
- if (totalMaterial < SCL_ENDGAME_MATERIAL_LIMIT)
- return SCL_PHASE_ENDGAME;
- if (ply <= 10 && (totalMaterial >= SCL_START_MATERIAL - 3 * SCL_VALUE_PAWN))
- return SCL_PHASE_OPENING;
- return SCL_PHASE_MIDGAME;
- }
- #define SCL_IMAGE_COUNT 12
- static const uint8_t SCL_images[8 * SCL_IMAGE_COUNT] =
- {
- 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
- 0xff,0x81,0xff,0xff,0xff,0xff,0xff,0x81,0xff,0xff,0xff,0xff,
- 0xff,0x81,0xe7,0xf7,0xf7,0xaa,0xff,0xbd,0xe7,0xf7,0xf7,0xaa,
- 0xff,0xc3,0xc3,0xe3,0xc1,0x80,0xff,0x99,0xdb,0xeb,0xc9,0x94,
- 0xe7,0xc3,0x81,0xc1,0x94,0x80,0xe7,0xdb,0xbd,0xdd,0xbe,0xbe,
- 0xc3,0xc3,0x91,0xe3,0x80,0x80,0xdb,0x99,0x8d,0xeb,0xaa,0xbe,
- 0xc3,0x81,0xe1,0xc1,0xc1,0xc1,0xdb,0xbd,0xdd,0xe3,0xdd,0xdd,
- 0x81,0x81,0xc1,0x9c,0xc1,0xc1,0x81,0x81,0xc1,0x9c,0xc1,0xc1
- };
- void SCL_drawBoard(
- SCL_Board board,
- SCL_PutPixelFunction putPixel,
- uint8_t selectedSquare,
- SCL_SquareSet highlightSquares,
- uint8_t blackDown)
- {
- uint8_t row = 0;
- uint8_t col = 0;
- uint8_t x = 0;
- uint8_t y = 0;
- uint16_t n = 0;
- uint8_t s = 0;
- uint8_t pictureLine = 0;
- uint8_t loadLine = 1;
- while (row < 8)
- {
- if (loadLine)
- {
- s = blackDown ? (row * 8 + (7 - col)) : ((7 - row) * 8 + col);
- char piece = board[s];
- if (piece == '.')
- pictureLine = (y == 4) ? 0xef : 0xff;
- else
- {
- uint8_t offset = SCL_pieceIsWhite(piece) ? 6 : 0;
- piece = SCL_pieceToColor(piece,1);
- switch (piece)
- {
- case 'R': offset += 1; break;
- case 'N': offset += 2; break;
- case 'B': offset += 3; break;
- case 'K': offset += 4; break;
- case 'Q': offset += 5; break;
- default: break;
- }
-
- pictureLine = SCL_images[y * SCL_IMAGE_COUNT + offset];
- }
- if (SCL_squareSetContains(highlightSquares,s))
- pictureLine &= (y % 2) ? 0xaa : 0x55;
-
- if (s == selectedSquare)
- pictureLine &= (y == 0 || y == 7) ? 0x00 : ~0x81;
- loadLine = 0;
- }
- putPixel(pictureLine & 0x80,n);
- pictureLine <<= 1;
- n++;
- x++;
- if (x == 8)
- {
- col++;
- loadLine = 1;
- x = 0;
- }
- if (col == 8)
- {
- y++;
- col = 0;
- x = 0;
- }
- if (y == 8)
- {
- row++;
- y = 0;
- }
- }
- }
- uint32_t SCL_boardHash32(const SCL_Board board)
- {
- uint32_t result = (board[SCL_BOARD_PLY_BYTE] & 0x01) +
- (((uint32_t) ((uint8_t) board[SCL_BOARD_ENPASSANT_CASTLE_BYTE])) << 24) +
- board[SCL_BOARD_MOVE_COUNT_BYTE];
- const char *b = board;
- for (uint8_t i = 0; i < SCL_BOARD_SQUARES; ++i, ++b)
- {
- switch (*b)
- {
- #define C(p,n) case p: result ^= (i + 1) * n; break;
- // the below number are primes
- C('P',4003)
- C('R',84673)
- C('N',93911)
- C('B',999331)
- C('Q',909091)
- C('K',2796203)
- C('p',4793)
- C('r',19391)
- C('n',391939)
- C('b',108301)
- C('q',174763)
- C('k',2474431)
- #undef C
- default: break;
- }
- }
- // for extra spread of values we swap the low/high parts:
- result = (result >> 16) | (result << 16);
- return result;
- }
- void SCL_boardDisableCastling(SCL_Board board)
- {
- board[SCL_BOARD_ENPASSANT_CASTLE_BYTE] &= 0x0f;
- }
- uint8_t SCL_boardMoveResetsCount(SCL_Board board,
- uint8_t squareFrom, uint8_t squareTo)
- {
- return board[squareFrom] == 'P' || board[squareFrom] == 'p' ||
- board[squareTo] != '.';
- }
- void SCL_printPGN(SCL_Record r, SCL_PutCharFunction putCharFunc,
- SCL_Board initialState)
- {
- if (SCL_recordLength(r) == 0)
- return;
- uint16_t pos = 0;
- SCL_Board board;
- if (initialState != 0)
- for (uint8_t i = 0; i < SCL_BOARD_STATE_SIZE; ++i)
- board[i] = initialState[i];
- else
- SCL_boardInit(board);
- while (1)
- {
- uint8_t s0, s1;
- char p;
- uint8_t state = SCL_recordGetMove(r,pos,&s0,&s1,&p);
- pos++;
- if (pos % 2)
- {
- uint8_t move = pos / 2 + 1;
- if (move / 100 != 0)
- putCharFunc('0' + move / 100);
- if (move / 10 != 0 || move / 100 != 0)
- putCharFunc('0' + (move % 100) / 10);
- putCharFunc('0' + move % 10);
- putCharFunc('.');
- putCharFunc(' ');
- }
- #if !SCL_960_CASTLING
- if ((board[s0] == 'K' && s0 == 4 && (s1 == 2 || s1 == 6)) ||
- (board[s0] == 'k' && s0 == 60 && (s1 == 62 || s1 == 58)))
- #else
- if ((board[s0] == 'K' && board[s1] == 'R') ||
- (board[s0] == 'k' && board[s1] == 'r'))
- #endif
- {
- putCharFunc('O');
- putCharFunc('-');
- putCharFunc('O');
- #if !SCL_960_CASTLING
- if (s1 == 58 || s1 == 2)
- #else
- if ((s1 == (board[SCL_BOARD_EXTRA_BYTE] & 0x07)) ||
- (s1 == 56 + (board[SCL_BOARD_EXTRA_BYTE] & 0x07)))
- #endif
- {
- putCharFunc('-');
- putCharFunc('O');
- }
- }
- else
- {
- uint8_t pawn = board[s0] == 'P' || board[s0] == 'p';
- if (!pawn)
- {
- putCharFunc(SCL_pieceToColor(board[s0],1));
- // disambiguation:
- uint8_t specify = 0;
- for (int i = 0; i < SCL_BOARD_SQUARES; ++i)
- if (i != s0 && board[i] == board[s0])
- {
- SCL_SquareSet s;
-
- SCL_squareSetClear(s);
- SCL_boardGetMoves(board,i,s);
- if (SCL_squareSetContains(s,s1))
- specify |= (s0 % 8 != s1 % 8) ? 1 : 2;
- }
- if (specify & 0x01)
- putCharFunc('a' + s0 % 8);
- if (specify & 0x02)
- putCharFunc('1' + s0 / 8);
- }
- if (board[s1] != '.' ||
- (pawn && s0 % 8 != s1 % 8 && board[s1] == '.')) // capture?
- {
- if (pawn)
- putCharFunc('a' + s0 % 8);
-
- putCharFunc('x');
- }
-
- putCharFunc('a' + s1 % 8);
- putCharFunc('1' + s1 / 8);
- if (pawn && (s1 >= 56 || s1 <= 7)) // promotion?
- {
- putCharFunc('=');
- putCharFunc(SCL_pieceToColor(p,1));
- }
- }
- SCL_boardMakeMove(board,s0,s1,p);
- uint8_t position = SCL_boardGetPosition(board);
-
- if (position == SCL_POSITION_CHECK)
- putCharFunc('+');
- if (position == SCL_POSITION_MATE)
- {
- putCharFunc('#');
- break;
- }
- else if (state != SCL_RECORD_CONT)
- {
- putCharFunc('*');
- break;
- }
-
- putCharFunc(' ');
- }
- }
- void SCL_recordCopy(SCL_Record recordFrom, SCL_Record recordTo)
- {
- for (uint16_t i = 0; i < SCL_RECORD_MAX_SIZE; ++i)
- recordTo[i] = recordFrom[i];
- }
- void SCL_gameInit(SCL_Game *game, const SCL_Board startState)
- {
- game->startState = startState;
- if (startState != 0)
- SCL_boardCopy(startState,game->board);
- else
- SCL_boardInit(game->board);
- SCL_recordInit(game->record);
- for (uint8_t i = 0; i < 14; ++i)
- game->prevMoves[i] = 0;
-
- game->state = SCL_GAME_STATE_PLAYING;
- game->ply = 0;
- SCL_recordInit(game->record);
- }
- uint8_t SCL_gameGetRepetiotionMove(SCL_Game *game,
- uint8_t *squareFrom, uint8_t *squareTo)
- {
- if (squareFrom != 0 && squareTo != 0)
- {
- *squareFrom = 0;
- *squareTo = 0;
- }
- /* pos. 1st 2nd 3rd
- | | |
- v v v
- 01 23 45 67 89 AB CD EF
- move ab cd ba dc ab cd ba dc */
- if (game->ply >= 7 &&
- game->prevMoves[0] == game->prevMoves[5] &&
- game->prevMoves[0] == game->prevMoves[8] &&
- game->prevMoves[0] == game->prevMoves[13] &&
- game->prevMoves[1] == game->prevMoves[4] &&
- game->prevMoves[1] == game->prevMoves[9] &&
- game->prevMoves[1] == game->prevMoves[12] &&
- game->prevMoves[2] == game->prevMoves[7] &&
- game->prevMoves[2] == game->prevMoves[10] &&
- game->prevMoves[3] == game->prevMoves[6] &&
- game->prevMoves[3] == game->prevMoves[11]
- )
- {
- if (squareFrom != 0 && squareTo != 0)
- {
- *squareFrom = game->prevMoves[3];
- *squareTo = game->prevMoves[2];
- }
- return 1;
- }
- return 0;
- }
- void SCL_gameMakeMove(SCL_Game *game, uint8_t squareFrom, uint8_t squareTo,
- char promoteTo)
- {
- uint8_t repetitionS0, repetitionS1;
- SCL_gameGetRepetiotionMove(game,&repetitionS0,&repetitionS1);
- SCL_boardMakeMove(game->board,squareFrom,squareTo,promoteTo);
- SCL_recordAdd(game->record,squareFrom,squareTo,promoteTo,SCL_RECORD_CONT);
- // ^ TODO: SCL_RECORD_CONT
- game->ply++;
- for (uint8_t i = 0; i < 14 - 2; ++i)
- game->prevMoves[i] = game->prevMoves[i + 2];
- game->prevMoves[12] = squareFrom;
- game->prevMoves[13] = squareTo;
- if (squareFrom == repetitionS0 && squareTo == repetitionS1)
- game->state = SCL_GAME_STATE_DRAW_REPETITION;
- else if (game->board[SCL_BOARD_MOVE_COUNT_BYTE] >= 50)
- game->state = SCL_GAME_STATE_DRAW_50;
- else
- {
- uint8_t position = SCL_boardGetPosition(game->board);
- switch (position)
- {
- case SCL_POSITION_MATE:
- game->state = SCL_boardWhitesTurn(game->board) ?
- SCL_GAME_STATE_BLACK_WIN : SCL_GAME_STATE_WHITE_WIN;
- break;
- case SCL_POSITION_STALEMATE:
- game->state = SCL_GAME_STATE_DRAW_STALEMATE;
- break;
- case SCL_POSITION_DEAD:
- game->state = SCL_GAME_STATE_DRAW_DEAD;
- break;
- default: break;
- }
- }
- }
- uint8_t SCL_gameUndoMove(SCL_Game *game)
- {
- if (game->ply == 0)
- return 0;
- if ((game->ply - 1) > SCL_recordLength(game->record))
- return 0; // can't undo, lacking record
- SCL_Record r;
- SCL_recordCopy(game->record,r);
- uint16_t applyMoves = game->ply - 1;
- SCL_gameInit(game,game->startState);
- for (uint16_t i = 0; i < applyMoves; ++i)
- {
- uint8_t s0, s1;
- char p;
- SCL_recordGetMove(r,i,&s0,&s1,&p);
- SCL_gameMakeMove(game,s0,s1,p);
- }
-
- return 1;
- }
- uint8_t SCL_boardMoveIsLegal(SCL_Board board, uint8_t squareFrom,
- uint8_t squareTo)
- {
- if (squareFrom >= SCL_BOARD_SQUARES || squareTo >= SCL_BOARD_SQUARES)
- return 0;
- char piece = board[squareFrom];
- if ((piece == '.') ||
- (SCL_boardWhitesTurn(board) != SCL_pieceIsWhite(piece)))
- return 0;
- SCL_SquareSet moves;
- SCL_boardGetMoves(board,squareFrom,moves);
- return SCL_squareSetContains(moves,squareTo);
- }
- #endif // guard
|