// Restricting user input setsystem maxmove 1; ban commands allmoves; allow moves 1 captures 1; alias b g B G s r S R y n Y N f e F E t f T F n p N P; // Maps legal directions to bits, going around the clockface from ne at 1 to n at 128 // Each hippogonal direction gets mapped to the or-sum of two radial directions set many ne 1 e 2 se 4 s 8 sw 16 w 32 nw 64 n 128 nee 3 see 6 sse 12 ssw 24 sww 48 nww 96 nnw 192 nne 129; // Sets legal directions from each square // Using calcset to define one direction at a time lets me reuse already tested code from the ZRF for this game calcset or 128 a1 b1 d1 e1 f1 h1 i1 a2 b2 d2 e2 f2 h2 i2 b3 c3 g3 h3 a4 c4 d4 e4 f4 g4 i4 d5 e5 f5 b6 d6 e6 f6 a7 c7 g7 i7 b8 c8 d8 e8 f8 g8 h8 a9 d9 e9 f9 i9; calcset or 8 a10 b10 d10 e10 f10 h10 i10 a9 b9 d9 e9 f9 h9 i9 b8 c8 g8 h8 a7 c7 d7 e7 f7 g7 i7 d6 e6 f6 d5 e5 f5 h5 a4 c4 g4 i4 b3 c3 d3 e3 f3 g3 h3 a2 d2 e2 f2 i2; calcset or 2 a1 c1 d1 e1 g1 i1 a2 b2 d2 e2 b3 d3 e3 g3 h3 b4 c4 d4 e4 f4 g4 h4 a5 b5 d5 e5 f5 a6 e6 b7 c7 d7 e7 f7 g7 h7 b8 d8 e8 g8 h8 a9 b9 d9 e9 a10 c10 d10 e10 g10; calcset or 32 i1 g1 f1 e1 c1 a1 i2 h2 f2 e2 h3 f3 e3 c3 b3 h4 g4 f4 e4 d4 b4 c4 i5 e5 d6 i6 h6 f6 e6 h7 g7 f7 e7 d7 c7 b7 h8 f8 e8 c8 b8 i9 h9 f9 e9 i10 g10 f10 e10 c10; calcset or 1 b1 c1 d1 g1 h1 e2 g2 a3 a5 c5 e5 g5 a6 c6 e6 g6 a8 d8 e9 g9 h9; calcset or 64 b1 c1 f1 g1 h1 c2 e2 i3 c5 e5 g5 i5 c6 e6 g6 i6 f8 i8 b9 c9 e9; calcset or 16 b2 c2 e2 f3 i3 c5 e5 g5 i5 c6 e6 g6 i6 i8 c9 e9 b10 c10 f10 g10 h10; calcset or 4 e2 g2 h2 a3 d3 a5 c5 e5 g5 a6 c6 e6 g6 a8 i8 e9 g9 b10 d10 c10 g10 h10; // This function returns the radial directions a piece may take its first step in toward its destination. // The radial directions are identified as bits as defined above. // One radial direction is returned for any legal radial move, // one or two for any legal hippogonal move, // or none for any move in an illegal or undefined direction. def legaldir bitand var #0 var direction #0 #1; // This function checks the legality of a Yahoo move from #0 to #1. // It is given the third argument to avoid the need to calculate it 3 times. // It should be called only by the Y and y functions. def Yahoo and checktwostep #0 #1 1 1 1 0 bitand 85 #2 or and checktwostep #0 #1 1 0 1 1 bitand 170 #2 and #2; // These functions check the legality of a move for each type of piece. // Function names match internal piece notation for each piece, which is that for Chinese Chess. def N fn Yahoo #0 #1 fn legaldir #0 #1; def P checktwostep #0 #1 1 1 1 1 or checktwostep #0 #1 0 1 0 1 and > rank #1 4 and > rank #0 4 and cond empty #0 capture (not empty #1) or checkleap #0 #1 1 1 or checkleap #0 #1 0 1 and fn legaldir #0 #1; def R checkride #0 #1 1 1 or checkride #0 #1 0 1 and fn legaldir #0 #1; def E checkleap #0 #1 1 1 or checkleap #0 #1 0 1 and > rank #1 4 and > rank #0 4 and cond empty #0 (not capture) (empty #1) or checktwostep #0 #1 1 1 1 1 or checktwostep #0 #1 0 1 0 1 and fn legaldir #0 #1; def C cond cond empty #0 (not capture) (empty #1) (checkride #0 #1 1 1 or checkride #0 #1 0 1) (checkhop #0 #1 1 1 or checkhop #0 #1 0 1) and fn legaldir #0 #1; def G checkride #0 #1 1 0 and == space #1 g or checkleap #0 #1 1 1 or checkleap #0 #1 0 1 and fn legaldir #0 #1; def F checkleap #0 #1 1 1 or checkleap #0 #1 0 1 and fn legaldir #0 #1; def n fn Yahoo #0 #1 fn legaldir #0 #1; def p checktwostep #0 #1 1 1 1 1 or checktwostep #0 #1 0 1 0 1 and < rank #1 5 and < rank #0 5 and cond empty #0 capture (not empty #1) or checkleap #0 #1 1 1 or checkleap #0 #1 0 1 and fn legaldir #0 #1; def r checkride #0 #1 1 1 or checkride #0 #1 0 1 and fn legaldir #0 #1; def e checkleap #0 #1 1 1 or checkleap #0 #1 0 1 and < rank #1 5 and < rank #0 5 and cond empty #0 (not capture) (empty #1) or checktwostep #0 #1 1 1 1 1 or checktwostep #0 #1 0 1 0 1 and fn legaldir #0 #1; def c cond cond empty #0 (not capture) (empty #1) (checkride #0 #1 1 1 or checkride #0 #1 0 1) (checkhop #0 #1 1 1 or checkhop #0 #1 0 1) and fn legaldir #0 #1; def g checkride #0 #1 1 0 and == space #1 G or checkleap #0 #1 1 1 or checkleap #0 #1 0 1 and fn legaldir #0 #1; def f checkleap #0 #1 1 1 or checkleap #0 #1 0 1 and fn legaldir #0 #1; // This subroutine checks whether a Brain is in check. sub checked brain: local piece from; if isupper space #brain: def enemies onlylower; else: def enemies onlyupper; endif; for (from piece) fn enemies: if fn #piece #from #brain: return true; endif; next; return false; endsub; // Checks whether a player has a legal move. // To check for checkmate, check for check + stalemate with separate subroutines. sub stalemated brain: local piece from to pos; store; if isupper space #brain: def friends onlyupper; def nofriends noupper; def friend isupper #0; else: def friends onlylower; def nofriends nolower; def friend islower #0; endif; // Can the Brain move? store; for pos fn GL #brain: if fn space #brain #brain #pos and not fn friend space #pos: move #brain #pos; set incheck sub checked #pos; restore; if not #incheck: setlegal #brain #pos; endif; endif; next; // Can another piece legally move? restore; for (from piece) fn friends: if == #from #brain: continue; endif; for to fn join toupper #piece L #from: if fn #piece #from #to and not fn friend space #to: move #from #to; set incheck sub checked #brain; restore; if not #incheck: setlegal #from #to; endif; endif; next; next; // All done. Return whether any legal moves were found. return cond count system legalmoves false true; endsub; // With hoppers in a game, it is not enough, when checking for stalemate, to check whether a piece can move one space. // Since that one space move could make it a screen, all possible moves should be checked. // These functions help narrow down the possible moves before more detailed checking is done. def PL merge merge leaps #0 1 0 leaps #0 1 1 merge leaps #0 2 0 leaps #0 2 2; def EL merge merge leaps #0 2 0 leaps #0 2 2 merge leaps #0 1 0 leaps #0 1 1; def RL merge rays #0 1 0 rays #0 1 1; def CL merge rays #0 1 0 rays #0 1 1; def GL merge merge leaps #0 1 0 leaps #0 1 1 (#b #B); def FL merge leaps #0 1 0 leaps #0 1 1; def NL leaps #0 1 2;