gen.asm
The legal moves from a position, each position once. Download · All the source
; ------------------------------------------------------------------ gen; The legal plays from a board, each position once, in a fixed order: a; depth-first search over single steps, from the bar and the highest point; down (a double's steps never from above the last). It first measures how; many dice can be played, then calls the handler for each play of that; many (with only one die, the larger if it can be), with the position in; W, its steps in STEPS and their dice in GDIE. Two plays of a non-double; can give one position; those are told apart by a hash of their changes.; step: a checker from point B by die E on W. Carry if illegal; else; played, A = 1 if it hit. Keeps B, D, E.step: ld h,high W ld l,b ld a,(hl) or a scf ret z ; no checker there ld a,b sub e jr c,.off ld c,a ; the point it lands on ld a,OPPOFF+23 sub c ld l,a ; the same point, the opponent's ld a,(hl) cp 2 ccf ret c ; held dec a jr nz,.move ld (hl),a ; a blot: to the bar ld l,OPPOFF+24 inc (hl) ld a,1 jr .moved.move: xor a.moved: ld l,b dec (hl) ld l,c inc (hl) or a ret.off: ; bearing off: all home, and by the exact number or from the top ld l,24.hi: ld a,(hl) or a jr nz,.top dec l jr .hi.top: ld a,l cp 6 ccf ret c ld a,b inc a cp e jr z,.bear ld a,l cp b scf ret nz.bear: ld l,b dec (hl) xor a ret; unstep: takes back the step from B by E, which hit if A = 1.unstep: ld h,high W ld l,b inc (hl) ld c,a ld a,b sub e ret c ld l,a dec (hl) dec c ret nz ld a,OPPOFF+23 sub l ld l,a ld (hl),1 ld l,OPPOFF+24 dec (hl) ret; dfs: C = steps taken, B = the highest point to step from.dfs: ld a,(GMAX) cp c jr nc,.seen ld a,c ld (GMAX),a.seen: ld a,(GTARGET) cp c jr nz,.more ld a,(GEMIT) or a jr z,gabort ld hl,(LEAFFN) jp (hl).more: ld h,high GDIE ld a,low GDIE add a,c ld l,a ld e,(hl) ; the die ld d,0 ; the lowest point to step from ld a,(W+24) or a jr z,.loop ld d,24 ; the bar first.loop: ld h,high W ld l,b ld a,(hl) or a jr z,.next ; no checker there ld a,(GPRUNE) and c jr z,.try call prune jr z,.next.try: push bc call step pop bc jr c,.next push af ld h,high STEPS ld l,c sla l ld (hl),b inc l ld (hl),a ld hl,(FSCORE) push hl call fdelta ld hl,(KEY) push hl ld hl,(KEY+2) push hl ld a,(GKEYED) or a call nz,kstep push bc push de inc c ld a,(GDOUBLE) or a jr nz,.same ld b,24.same: call dfs pop de pop bc pop hl ld (KEY+2),hl pop hl ld (KEY),hl pop hl ld (FSCORE),hl pop af push bc call unstep pop bc.next: ld a,b cp d ret z dec b jr .loop; The target reached while measuring: out of the search at once.gabort: ld sp,(GSP) ld a,(GTARGET) ld (GMAX),a ld hl,(GROOT) ld de,W ld bc,64 ldir ret; rundfs: A = the steps to reach.rundfs: ld (GTARGET),a xor a ld (GMAX),a ld (GSP),sp ld bc,24*256 jp dfs; gen: HL -> the board, DHI >= DLO the dice, USERFN the handler. Plays; counted in GCOUNT.gen: ld (GROOT),hl ld de,W ld bc,64 ldir ld hl,0 ld (GCOUNT),hl xor a ld (GKEYED),a ld (GPRUNE),a ; a new number for the hash table's entries (clearing it on wrapping) ld hl,GENID inc (hl) jr nz,.id inc (hl) ld hl,HSTAMP ld de,HSTAMP+1 ld bc,255 ld (hl),b ldir.id: ld a,(DHI) ld b,a ld a,(DLO) cp b jr nz,.nd ld hl,GDIE ld (hl),a inc l ld (hl),a inc l ld (hl),a inc l ld (hl),a ld (GDOUBLE),a ld a,4 call measure or a ret z ld hl,leaf jr emit.nd: xor a ld (GDOUBLE),a call order_a call measure2 ld (GMAXA),a cp 2 jr z,.two call order_b call measure2 cp 2 jr z,.two ld b,a ld a,(GMAXA) or a jr nz,.a1 or b ret z jr .one ; the smaller die only.a1: call order_a.one: ld a,1 ld hl,leaf jr emit.two: ld a,1 ld (GKEYED),a call order_a ld a,2 ld hl,leafk call emit ld a,(W+24) or a jr nz,.bar inc a ld (GPRUNE),a.bar: call order_b ld a,2 ld hl,leafkemit: ld (LEAFFN),hl ld hl,0 ld (KEY),hl ld (KEY+2),hl ld hl,GEMIT ld (hl),1 jp rundfsmeasure2: ld a,2measure: ld hl,GEMIT ld (hl),0 call rundfs ld a,(GMAX) retorder_a: ld a,(DHI) ld (GDIE),a ld a,(DLO) ld (GDIE+1),a retorder_b: ld a,(DLO) ld (GDIE),a ld a,(DHI) ld (GDIE+1),a ret; A play of a non-double: dropped if its hash (the sum over its steps of; the values of the points it adds a checker to, less those it takes one; from, kept in KEY as the search goes) is one seen already.leafk: ; into a table of 256 by the hash's first byte, the next free ; entry on; an entry is in use if stamped with this search's number ld a,(KEY) ld l,a.p: ld h,high HSTAMP ld a,(GENID) cp (hl) jr nz,.new ld de,KEY ld b,4.c: inc h ld a,(de) cp (hl) jr nz,.nx inc de djnz .c ret ; seen.nx: inc l jr .p.new: ld (hl),a ld de,KEY ld b,4.w: inc h ld a,(de) ld (hl),a inc de djnz .wleaf: ld hl,(GCOUNT) inc hl ld (GCOUNT),hl ld hl,(USERFN) jp (hl); prune: in the second order (low die first), from no bar, the second; step from B by E: Z if the first order has made the same play, as it; has unless a checker is borne off, or B is the point the first step; reached and the root had no checker there.prune: ld a,b sub e jr c,.no ld a,(STEPS) ld hl,GDIE sub (hl) jr c,.no cp b jr nz,.yes ld hl,(GROOT) call index or a jr z,.no.yes: xor a ret.no: or 1 ret; kstep: KEY updated for the step from B by E (C the steps before it).; Keeps BC, DE.kstep: push bc push de ld h,high STEPS ld l,c sla l inc l ld c,(hl) ld a,b push bc push de call ksub pop de pop bc ld a,b sub e jr nc,.on ld a,25 ; borne off.on: push af push bc call kadd pop bc pop af dec c jr nz,.x cpl add a,26+24 ; the opponent's point hit: 26 + 23 - to call ksub ld a,26+24 ; and their bar call kadd.x: pop de pop bc ret; KEY += RTAB[A], KEY -= RTAB[A].kadd: call rptr ld de,KEY ld b,4 or a.l: ld a,(de) adc a,(hl) ld (de),a inc hl inc de djnz .l retksub: call rptr ld de,KEY ld b,4 or a.l: ld a,(de) sbc a,(hl) ld (de),a inc hl inc de djnz .l retrptr: ld l,a ld h,0 add hl,hl add hl,hl ld de,RTAB add hl,de ret; fdelta: after a step from B by E (A = 1 if it hit), FSCORE += the change; to the filter's score of W turned (the other side on roll): the checker; taken off its point, put on another or borne off, and a checker hit to; the bar. Adding a checker to a point of n adds its weight for one; checker (n = 0), for two less that for one (1), for three (2), or for; each beyond three (3 or more). Keeps BC, DE.fdelta: push bc push de ld (FHIT),a ld a,e ld (FDIE),a ld h,high W ld l,b ld c,(hl) ld a,b ld d,101 call frow ld a,c call dplus ld hl,(FSCORE) or a sbc hl,de ld (FSCORE),hl ld a,b ld hl,FDIE sub (hl) jr c,.off ld c,a ld h,high W ld l,a ld a,(hl) dec a push af ld a,c ld d,101 call frow pop af call dplus call .add ld a,(FHIT) or a jr z,.done ld a,23 sub c ld d,0 call frow xor a call dplus ld hl,(FSCORE) or a sbc hl,de ld (FSCORE),hl ld a,(W+OPPOFF+24) dec a push af ld a,24 ld d,0 call frow pop af call dplus call .add.done: pop de pop bc ret.off: ld a,(FILT_W+201) xor $80 call sext call .add jr .done.add: ld hl,(FSCORE) add hl,de ld (FSCORE),hl ret; frow: A = a point, D = 0 or 101 (the side on roll, or the other) -> HL; -> the filter's weights for it. Keeps C.frow: add a,a add a,a add a,d ld l,a ld h,0 ld de,FILT_W add hl,de ret; dplus: HL -> a point's filter weights (plus 128), A = its checkers -> DE; = the change adding one.dplus: or a jr z,.one dec a jr z,.two inc hl inc hl dec a jr z,.one inc hl.one: ld a,(hl) xor $80 jr sext.two: ld e,(hl) inc hl ld a,(hl) sub e ld e,a sbc a,a ld d,a ret; sext: DE = A sign-extended.sext: ld e,a rla sbc a,a ld d,a ret