features.asm
GNU Backgammon's 25 contact features of a position, in integer arithmetic. Download · All the source
; ------------------------------------------------------------- features; gnubg's 25 contact features of side X against Y (from X's side), each; as a count 0 to 7 (its value in eighths of its range, which is 1 or 2):; computed in integers, the divisions by repeated subtraction as no count; exceeds 7.; half: HL -> X, DE -> Y, IX -> 25 counts.half: push de ld de,SIDEX ld bc,25 ldir pop hl ld de,SIDEY ld bc,25 ldir ld hl,SIDEX ld de,BX call mkblk ld hl,SIDEY ld de,BY call mkblk ; 0-2: Y's checkers off (gnubg's quirk), in threes: 0, 2, 5, 7 ld hl,SIDEY call total neg add a,15 ld c,a ld b,3 push ix.off: ld a,c bit 7,a jr z,.o1 xor a.o1: cp 3 jr c,.o2 ld a,3.o2: ld hl,MENOFF call index ld (ix+0),a inc ix ld a,c sub 3 ld c,a djnz .off pop ix ; Y's rearmost checker, in X's indices ld hl,SIDEY call highest ld (HYI),a ld b,a ld a,23 sub b ld (NOB),a ; 3, 23: pips beyond it (from it) and before it ld hl,0 ld (T1),hl ld (T2),hl ld c,0.bc: ld hl,SIDEX ld a,c call index or a jr z,.bn ld b,a ld a,(NOB) sub c jr z,.bn ld hl,T2 jp p,.free neg inc a ; i + 1 - nob ld hl,T1 jr .acc.free: ld a,c inc a.acc: call addn.bn: inc c ld a,c cp 25 jr nz,.bc ld hl,(T1) ld de,167 call r4 ld (ix+3),a ld hl,(T2) ld de,100 call r4 ld (ix+23),a ; 19: timing ld a,(NOB) bit 7,a jr nz,.m11 cp 11 jr nc,.mok.m11: ld a,11.mok: ld (TM),a ld hl,0 ld (T3),hl ld a,(SIDEX+24) ld (T4),a or a jr z,.t0 ld b,a ld a,24 ld hl,T3 call addn.t0: ld c,6.tl: ld hl,SIDEX ld a,c call index ld b,a ld a,(TM) cp c ld a,b jr nc,.all cp 1 jr z,.all sub 2 jr nc,.all xor a.all: or a jr z,.tn ld b,a ld hl,T4 add a,(hl) ld (hl),a ld a,c ld hl,T3 call addn.tn: inc c ld a,c cp 24 jr nz,.tl ld c,5.th: ld hl,SIDEX ld a,c call index cp 2 jr z,.hn jr c,.fill sub 2 ld b,a ld hl,T4 add a,(hl) ld (hl),a ld a,c ld hl,T3 call addn jr .hn.fill: neg add a,2 ld b,a ld a,(T4) sub b jr c,.hn ld (T4),a ld a,c neg ld hl,T3 call addn.hn: dec c jp p,.th ld hl,(T3) ld de,100 call r4 ld (ix+19),a ; 16: the second moment of the checkers above their average point ld hl,SIDEX call total ld (T5),a ld hl,0 ld (T1),hl ld c,1.ms: ld hl,SIDEX ld a,c call index or a jr z,.mn ld b,a ld a,c ld hl,T1 call addn.mn: inc c ld a,c cp 25 jr nz,.ms ld a,(T5) ld e,a ld d,0 ld b,d or a jr z,.avg ld hl,(T1) add hl,de dec hl.dv: or a sbc hl,de jr c,.avg inc b jr .dv.avg: ld hl,0 ld (T2),hl ; the sum of squares ld (T7),hl ; the square xor a ld (T5),a ; checkers above ld c,b ld e,1.mo: inc c ld a,c cp 25 jr nc,.md ld hl,(T7) ld d,0 add hl,de ld (T7),hl inc e inc e push de ld hl,SIDEX ld a,c call index or a jr z,.mz ld b,a ld hl,T5 add a,(hl) ld (hl),a ld a,b ld de,(T7) call mul ld de,(T2) add hl,de ld (T2),hl.mz: pop de jr .mo.md: ; count k while sum > (100k - 1) x checkers, k up to 7 ld a,(T5) ld e,a ld d,0 ld a,100 call mul push hl or a sbc hl,de ex de,hl pop bc ld hl,(T2) xor a.mk: push hl scf sbc hl,de pop hl jr c,.me ex de,hl add hl,bc ex de,hl inc a cp 7 jr nz,.mk.me: ld (ix+16),a ; 4: the rearmost checker; 5, 6: the rearmost and forward anchors ld hl,SIDEX call highest bit 7,a jr z,.bk xor a.bk: ld l,a ld h,0 ld de,3 call ratio ld (ix+4),a ld hl,BX+23 ld b,24.ba: ld a,(hl) or a jr nz,.bf dec hl djnz .ba.bf: ld a,b dec a ld (TA),a ld l,a rla sbc a,a ld h,a ld de,3 call ratio ld (ix+5),a ld a,(TA) bit 7,a jr nz,.low cp 18 jr c,.low ld hl,BX+18 ld b,18.fl: ld a,(hl) or a jr nz,.ff inc hl inc b jr .fl.low: ld hl,BX+17 ld b,17.fh: ld a,(hl) or a jr nz,.ff dec hl dec b ld a,b cp 11 jr nz,.fh ld a,7 jr .fw.ff: ld a,24 sub b add a,a ld l,a ld h,0 ld de,3 call ratio.fw: ld (ix+6),a ; 7-9: shots call shots ; 10, 24: Y's rearmost checker escaping (past X's first point held) ld a,(HYI) cpl add a,25+low BX ld c,a push bc xor a call esc call r8_36 ld (ix+10),a pop bc ld a,1 call esc call r8_36 ld (ix+24),a ; 11-14: containment, the fewest escapes from Y's points 15 to 24 ld a,(NOB) ld b,a ld a,9 sub b jp p,.pa xor a.pa: ld (TP),a ; the escapes, kept for each half while X's points held are the same push ix pop hl ld a,l cp low FEAT ld hl,ECACHE jr z,.c0 ld hl,ECACHE+34.c0: push hl ld de,BX ld b,24.cc: ld a,(de) cp (hl) jr nz,.miss inc de inc hl djnz .cc jr .hit.miss: pop de push de ld hl,BX ld bc,24 ldir ld c,9+low BX.ce: push bc push de xor a call esc pop de ld (de),a inc de pop bc dec c ld a,c cp low BX-1 jr nz,.ce.hit: pop hl ld de,24 add hl,de ld a,36 ld (MINA),a ld (MINC),a ld c,0.cl: ld b,(hl) inc hl push hl ld a,c ld hl,TP cp (hl) jr nc,.na ld hl,MINA call minb.na: ld a,c cp 9 jr nc,.nc ld hl,MINC call minb.nc: pop hl inc c ld a,c cp 10 jr nz,.cl ld a,(MINA) call cont ld (ix+11),b ld (ix+12),a ld a,(MINC) call cont ld (ix+13),b ld (ix+14),a ; 15: mobility, X's checkers from point 7 up by the rolls escaping Y ld hl,0 ld (T1),hl ld c,6.mb: ld a,c ld (TI),a ld hl,SIDEX call index or a jr z,.mbn ld (TX),a ld a,24+low BY sub c ld c,a xor a call esc ld e,a ld d,0 ld a,(TX) call mul ex de,hl ld a,(TI) sub 5 call mul ld de,(T1) add hl,de ld (T1),hl.mbn: ld a,(TI) inc a ld c,a cp 25 jr nz,.mb ld hl,(T1) ld de,900 call ratio ld (ix+15),a ; 17: entering from the bar xor a ld hl,0 ld d,high BY ld c,a ld a,(SIDEX+24) or a jr z,.e0.e1: call yclosed jr z,.e1o ld a,c inc a add a,a add a,a call addhla ld b,c.e2: inc b ld a,b cp 6 jr nc,.en push bc ld c,b call yclosed pop bc jr z,.e2o ld a,c add a,b add a,2 add a,a call addhla jr .e2.e2o: ld a,(SIDEX+24) cp 2 jr c,.e2 ld a,c inc a add a,a call addhla jr .e2.e1o: ld a,(SIDEX+24) cp 2 jr c,.en ld b,c.e3: inc b ld a,b cp 6 jr nc,.en push bc ld c,b call yclosed pop bc jr z,.e3 ld a,b inc a add a,a call addhla jr .e3.en: inc c ld a,c cp 6 jr nz,.e1 add hl,hl add hl,hl add hl,hl ld de,294 call ratio.e0: ld (ix+17),a ; 18: Y's home points held ld hl,BY ld b,6 xor a.e2c: add a,(hl) inc hl djnz .e2c ld hl,ENTER2 call index ld (ix+18),a ; 20: backbone, X's highest point held (not the ace) against the ; others below it, by distance ld hl,BX+23 ld b,23.bb: ld a,(hl) or a jr nz,.bbf dec hl djnz .bb xor a jr .bbo.bbf: ld c,0 ld e,c ld a,b ld (TP),a.bbl: dec hl dec b jr z,.bbe ld a,(hl) or a jr z,.bbl inc c push hl ld a,(TP) sub b ld hl,AC call index add a,e ld e,a pop hl jr .bbl.bbe: ld a,c or a jr z,.bbo push de ld e,c ld d,0 ld a,11 call mul pop bc push hl ld b,0 or a sbc hl,bc add hl,hl add hl,hl add hl,hl pop de call ratio.bbo: ld (ix+20),a ; 21, 22: backgames: checkers back with two anchors or more, or one ld hl,BX+18 ld b,6 xor a.ga: add a,(hl) inc hl djnz .ga ld c,a ld hl,SIDEX+18 ld b,7 xor a.gb: add a,(hl) inc hl djnz .gb ld b,a xor a ld (ix+21),a ld (ix+22),a or c ret z dec a jr z,.g1 ld a,b sub 3 jr nc,.gc xor a.gc: cp 8 jr c,.gs ld a,7.gs: ld (ix+21),a ret.g1: ld a,b srl a cp 8 jr c,.g1s ld a,7.g1s: ld (ix+22),a ret; mkblk: DE[i] = 1 where HL[i] >= 2, for i = 0 to 23.mkblk: ld b,24.l: ld a,(hl) inc hl cp 2 ccf sbc a,a and 1 ld (de),a inc de djnz .l ret; addn: the word at HL += A x B (A signed, B >= 1).addn: ld e,a rla sbc a,a ld d,a push hl ld a,(hl) inc hl ld h,(hl) ld l,a.l: add hl,de djnz .l ex de,hl pop hl ld (hl),e inc hl ld (hl),d retaddhla: add a,l ld l,a ret nc inc h ret; r4: A = ratio(4 HL, DE).r4: add hl,hl add hl,hl jp ratio; r8_36: A = ratio(8 A, 36) = ratio(2 A, 9).r8_36: ld l,a ld h,0 add hl,hl ld de,9 jp ratio; minb: (HL) = min((HL), B).minb: ld a,(hl) cp b ret c ld (hl),b ret; cont: A = the fewest escapes -> B = ratio(8a, 36), A = ratio(a^2, 162); for a = 36 - A.cont: neg add a,36 push af call r8_36 ld b,a pop af push bc ld e,a ld d,0 call mul ld de,162 call ratio pop bc ret; yclosed: NZ if Y holds its point C (0 to 5). HL kept.yclosed: ld a,low BY add a,c ld e,a ld a,(de) or a ret; esc: C = the low byte of &BX[s] or &BY[s], A = 1 for gnubg's Escapes1; -> A = the rolls escaping a checker whose points at distances 1, 2, ...; are s, s + 1, ...: the open sums' rolls, less those whose two die points; are both held.esc: ld h,high BX ld e,0 or a jr z,.go ld l,c ld b,12.f: ld a,(hl) or a jr nz,.low inc l djnz .f xor a ret.low: ld a,l sub c ld e,a ; only sums beyond the first point held.go: ld d,0 ld b,11.s: ld a,b cp e jr z,.pairs add a,c ld l,a ld a,(hl) or a jr nz,.sn ; the sum's rolls: 1 to 6 up to 6, then 5 down to 1 ld a,b cp 7 jr c,.t neg add a,12.t: add a,d ld d,a.sn: djnz .s.pairs: ld b,0.pa: ld a,b call blk jr z,.pan ld a,b add a,a inc a call esub ld a,b ld (ESCA),a push bc.pb: dec b jp m,.pbe ld a,b call blk jr z,.pb ld a,(ESCA) add a,b inc a call esub call esub jr .pb.pbe: pop bc.pan: inc b ld a,b cp 6 jr nz,.pa ld a,d retblk: add a,c ld l,a ld a,(hl) or a retesub: cp e ret c ret z push af call blk jr nz,.x dec d.x: pop af ret; ---------------------------------------------------------------- shots; gnubg's I_PIPLOSS, I_P1 and I_P2: X hitting Y's blots, over the 21; rolls, the pips they would lose and the rolls hitting one or two.shots: ld hl,0 ld (SNP),hl ld (SN1),hl ; blots to hit: Y's, up to its point 22 (24 with three home ; points of X's made), by X's index ld hl,SIDEX ld b,6 ld c,0.nb: ld a,(hl) or a jr z,.nb1 inc c.nb1: inc hl djnz .nb ld a,c cp 3 ld a,23 jr nc,.st ld a,21.st: ld (SSTART),a ld hl,TB ld c,0 ld d,0.tb: ld a,23 sub c ld e,a ld a,(SSTART) cp e ld a,0 jr c,.tbw push hl ld hl,SIDEY ld a,e call index pop hl dec a ld a,0 jr nz,.tbw inc a ld d,a.tbw: ld (hl),a inc l inc c ld a,c cp 24 jr nz,.tb ld a,d or a jp z,shout ; and as a list, highest first ld de,BLIST ld hl,TB+23 ld b,24.bl: ld a,(hl) or a jr z,.bn ld a,b dec a ld (de),a inc de.bn: dec hl djnz .bl ld a,$ff ld (de),a ; hitters: X's points but for home points of exactly two; with two ; or more on the bar, only the bar ld a,(SIDEX+24) cp 2 sbc a,a ld (BARM),a ld hl,EL ld de,SIDEX ld c,0 ld b,0.el: ld a,(de) or a jr z,.elw ld a,(BARM) or a jr nz,.el0 ld a,c cp 24 ld a,0 jr nz,.elw.el0: ld a,(de) cp 2 jr nz,.el1 ld a,c cp 6 ld a,0 jr c,.elw.el1: ld a,1 ld b,a.elw: ld (hl),a inc l inc de inc c ld a,c cp 25 jr nz,.el ld a,b or a jp z,shout ld hl,YB ld de,BY+23 ld b,24.yb: ld a,(de) ld (hl),a inc l dec de djnz .yb ; direct shots, per die: the highest hitter, and how many xor a ld (OFFN),a ld b,1.dk: push bc call find pop bc ld h,high DK ld l,b set 7,l set 6,l ld (hl),a set 3,l ld (hl),e inc b ld a,b cp 7 jr nz,.dk ld a,(SIDEX+24) or a ld hl,nob_d ld de,nob_p jr z,.go ld hl,bar1_d ld de,bar1_p.go: push de call eachdie pop hl call eachpair jp shout; eachdie: the handler at HL for each double (RD the die), tallied once.eachdie: ld (PHAND),hl ld b,1.l: push bc ld a,b ld (RD),a call clr ld hl,.r push hl ld hl,(PHAND) jp (hl).r: ld c,1 call tally pop bc inc b ld a,b cp 7 jr nz,.l ret; eachpair: the handler at HL for each non-double (RD, RD1 the dice, the; offsets set by `dpair`), tallied twice.eachpair: ld (PHAND),hl ld hl,PAIRS ld b,15.l: push bc push hl call dpair call clr ld hl,.r push hl ld hl,(PHAND) jp (hl).r: ld c,2 call tally pop hl inc hl inc hl pop bc djnz .l ret; No checker on the bar: a double's direct shot, then by 2, 3, 4 times.nob_d: ld a,(RD) ld b,a call getdk cp $ff jr z,.x sub b inc a ld (RNP),a ld a,e ld (RNCH),a.x: call doffs ld hl,ind jp dsteps; A non-double's direct shots (a second checker if another point, or two; there), then the indirect one.nob_p: ld a,(RD) ld b,a call getdk ld (RK0),a ld c,0 ld d,c cp $ff jr z,.n0 sub b inc a ld d,a inc c.n0: ld a,(RD1) ld b,a call getdk cp $ff jr z,.n1 ld e,a ld a,(RK0) cp e jr nz,.inc ld a,e ld hl,BX call index or a jr z,.nop.inc: inc c.nop: ld a,e sub b inc a cp d jr c,.n1 ld d,a.n1: ld a,c ld (RNCH),a ld a,d ld (RNP),a call pairdist jp ind; The pair of dice at HL: RD, RD1, and as the offsets, either open.dpair: ld a,(hl) ld (RD),a ld (OFFS),a inc hl ld a,(hl) ld (RD1),a ld (OFFS+1),a ld a,2 ld (OFFN),a ld a,1 ld (OFFANY),a retpairdist: ld a,(RD) ld b,a ld a,(RD1) add a,b ld b,a ret; doffs: the offsets d, 2d, 3d of die RD, all to be open.doffs: ld a,(RD) ld b,a ld (OFFS),a add a,b ld (OFFS+1),a add a,b ld (OFFS+2),a xor a ld (OFFANY),a ret; dsteps: a double's shots by 2, 3 and 4 times its die, each by the; routine at HL.dsteps: ld (DHAND),hl ld c,1.s: ld a,c ld (OFFN),a push bc ld a,(RD) ld b,a xor a.m: add a,b dec c jp p,.m ld b,a ld hl,.r push hl ld hl,(DHAND) jp (hl).r: pop bc inc c ld a,c cp 4 jr nz,.s ret; getdk: B = a die -> A = its highest direct hitter ($ff: none), E = how; many (1, 2). Keeps B, C, D.getdk: ld h,high DK ld a,b or $c8 ld l,a ld e,(hl) res 3,l ld a,(hl) ret; find: B = a distance -> A = the highest hitter that far from a blot,; the points between passing the offsets' test, or $ff; E = how many; (up to 2).find: ld e,0 ld d,$ff ld hl,BLIST.l: ld a,(hl) inc hl cp $ff jr z,.done ld c,a push hl add a,b add a,low EL ld l,a ld h,high EL ld a,(hl) or a jr z,.n call open jr z,.n inc e ld a,d inc a jr nz,.two ld a,c add a,b ld d,a.n: pop hl jr .l.two: pop hl.done: ld a,d ret; open: C = a blot's index: NZ if the points at the offsets from it are; open, all of them or (OFFANY) any. Keeps B, C, D, E.open: ld a,(OFFN) or a jr z,.yes push bc ld b,a ld hl,OFFS.o: ld a,(hl) add a,c add a,low YB push hl ld l,a ld h,high YB ld a,(hl) pop hl or a ld a,(OFFANY) jr z,.op or a jr z,.no jr .nx.op: or a jr nz,.yes2.nx: inc hl djnz .o ld a,(OFFANY) or a jr z,.yes2.no: pop bc xor a ret.yes2: pop bc.yes: or 1 ret; ind: B = a distance: the indirect shot that far.ind: push bc call find pop bc cp $ff ret z ld c,a call one ld a,c sub b inc a call maxnp ld a,c cp 24 ret nc ld a,23 sub c ld c,a; blotoffs: RNCH += 1 if Y has a blot at C plus an offset.blotoffs: ld a,(OFFN) ld b,a ld de,OFFS.b: ld a,(de) add a,c ld hl,SIDEY call index dec a jr z,.hit inc de djnz .b ret.hit: ld hl,RNCH inc (hl) ret; one: RNCH = max(RNCH, 1).one: ld hl,RNCH ld a,(hl) or a ret nz inc (hl) retmaxnp: ld hl,RNP cp (hl) ret c ld (hl),a ret; tally: C = the roll's weight: its pips, and whether it hits one or two.tally: ld b,c.t: ld a,(RNP) ld hl,(SNP) call addhla ld (SNP),hl ld hl,SN1 ld a,(RNCH) or a jr z,.n inc (hl) dec a jr z,.n inc hl inc (hl).n: djnz .t ret; A checker on the bar: it hits entering, or (only one on the bar) enters; and another hits.bar1_d: ld a,(RD) ld b,a add a,a ld c,a call process call barm call doffs ld hl,fbar jp dstepsbar1_p: ld a,(RD) ld b,a ld a,(RD1) ld c,a call process ld a,(RD1) ld b,a ld a,(RD) ld c,a call process call barm call pairdist jp fbar; barm: with more than one on the bar, no indirect shots: out of the; caller.barm: ld a,(BARM) or a ret nz pop hl ret; clr: a roll's tallies cleared; A = RD.clr: xor a ld (RNCH),a ld (RNP),a ld (RNEED),a ld a,(RD) ret; process: B = a die, C = the die the bar checker enters with first:; direct shots with B, from the bar or (once) from the board.process: ld hl,BLIST.k: ld a,(hl) inc hl cp $ff ret z add a,b ld e,a ; the hitter's point push hl add a,low EL ld l,a ld h,high EL ld a,(hl) or a jr z,.n ld a,e cp 24 jr z,.c ld a,(RNEED) or a jr nz,.x ld a,c dec a ld hl,SIDEY call index cp 2 jr nc,.x ld a,1 ld (RNEED),a.c: ld hl,RNCH inc (hl) ld a,e sub b inc a call maxnp.n: pop hl jr .k.x: pop hl ret; fbar: B = a distance: the bar checker's indirect shot that far.fbar: push bc call find pop bc cp 24 ret nz call one ld a,25 sub b call maxnp ld c,1 jp blotoffsshout: ld hl,(SNP) ld de,432 call r4 ld (ix+7),a ld a,(SN1) call r8_36 ld (ix+8),a ld a,(SN2) call r8_36 ld (ix+9),a ret