int AttachGalley(OBJECT hd, OBJECT *inners, OBJECT *suspend_pt) { OBJECT hd_index; /* the index of hd in the enclosing galley */ OBJECT hd_inners; /* inner galleys of hd, if unsized */ OBJECT dest; /* the target @Galley hd empties into */ OBJECT dest_index; /* the index of dest */ OBJECT target; /* the target indefinite containing dest */ OBJECT target_index; /* the index of target */ OBJECT target_galley; /* the body of target, made into a galley */ OBJECT tg_inners; /* inner galleys of target_galley */ BOOLEAN need_precedes = FALSE;/* true if destination lies before galley */ OBJECT recs; /* list of recursive definite objects */ OBJECT link, y = nilobj; /* for scanning through the components of hd */ CONSTRAINT c; /* temporary variable holding a constraint */ OBJECT env, n1, tmp, zlink, z, sym; /* placeholders and temporaries */ BOOLEAN was_sized; /* true if sized(hd) initially */ int dim; /* the galley direction */ FULL_LENGTH perp_back, perp_fwd; OBJECT why, junk; debug2(DGA, D, "[ AttachGalley(Galley %s into %s)", SymName(actual(hd)), SymName(whereto(hd))); ifdebug(DGA, DD, DebugGalley(hd, nilobj, 4)); assert( Up(hd) != hd, "AttachGalley: no index!" ); Parent(hd_index, Up(hd)); assert( type(hd_index) == UNATTACHED, "AttachGalley: not UNATTACHED!" ); hd_inners = tg_inners = nilobj; was_sized = sized(hd); dim = gall_dir(hd); for(;;) { /*************************************************************************/ /* */ /* Search for a destination for hd. If hd is unsized, search for */ /* inner galleys preceding it first of all, then for receptive objects */ /* following it, possibly in inner galleys. If no luck, exit. */ /* If hd is sized, search only for receptive objects in the current */ /* galley below the current spot, and fail if cannot find any. */ /* */ /*************************************************************************/ sym = whereto(hd); if( sized(hd) ) { /* sized galley case: search on from current spot */ target_index = SearchGalley(Up(hd_index), sym, TRUE, FALSE, TRUE, TRUE); if( target_index == nilobj ) { /* search failed to find any new target, so kill the galley */ for( link = Down(hd); link != hd; link = NextDown(link) ) { Child(y, link); if( type(y) == SPLIT ) Child(y, DownDim(y, dim)); if( is_definite(type(y)) ) break; } if( link != hd ) Error(19, 1, "galley %s deleted from here (no target)", WARN, &fpos(y), SymName(actual(hd))); if( hd_inners != nilobj ) DisposeObject(hd_inners), hd_inners=nilobj; if( tg_inners != nilobj ) DisposeObject(tg_inners), tg_inners=nilobj; KillGalley(hd, FALSE); *inners = nilobj; debug0(DGA, D, "] AttachGalley returning ATTACH_KILLED"); return ATTACH_KILLED; } else if( actual(actual(target_index)) == InputSym ) { /* search found input object, so suspend on that */ DeleteNode(hd_index); Link(target_index, hd); *inners = nilobj; debug0(DGA, D, "] AttachGalley returning ATTACH_INPUT"); return ATTACH_INPUT; } } else /* unsized galley, either backwards or normal */ { if( foll_or_prec(hd) == GALL_PREC ) { target_index= SearchGalley(Up(hd_index), sym, FALSE, TRUE,TRUE,FALSE); need_precedes = FALSE; } else { target_index = SearchGalley(Up(hd_index), sym, FALSE,TRUE,FALSE,FALSE); need_precedes = (target_index != nilobj); if( target_index == nilobj ) target_index = SearchGalley(Up(hd_index), sym, TRUE,TRUE,TRUE,FALSE); } /* if no luck, exit without error */ if( target_index == nilobj ) { *inners = nilobj; debug0(DGA, D, "] AttachGalley returning ATTACH_NOTARGET"); return ATTACH_NOTARGET; } } assert( type(target_index) == RECEPTIVE, "AttachGalley: target_index!" ); target = actual(target_index); assert( type(target) == CLOSURE, "AttachGalley: target!" ); /* set target_galley to the expanded value of target */ debug1(DYY, D, "[ EnterErrorBlock(FALSE) (expanding target %s)", SymName(actual(target))); EnterErrorBlock(FALSE); New(target_galley, HEAD); force_gall(target_galley) = FALSE; enclose_obj(target_galley) = limiter(target_galley) = nilobj; ClearHeaders(target_galley); opt_components(target_galley) = opt_constraints(target_galley) = nilobj; gall_dir(target_galley) = external_hor(target) ? COLM : ROWM; FposCopy(fpos(target_galley), fpos(target)); actual(target_galley) = actual(target); whereto(target_galley) = ready_galls(target_galley) = nilobj; foll_or_prec(target_galley) = GALL_FOLL; must_expand(target_galley) = FALSE; sized(target_galley) = FALSE; /* get perpendicular constraint (none if horizontal galley) */ if( dim == ROWM ) { Constrained(target, &c, 1-dim, &junk); if( !constrained(c) ) Error(19, 2, "receptive symbol %s has unconstrained width", FATAL, &fpos(target), SymName(actual(target))); debug2(DSC, DD, "Constrained( %s, 1-dim ) = %s", EchoObject(target), EchoConstraint(&c)); if( !FitsConstraint(0, 0, c) ) { debug0(DGA, D, " reject: target_galley horizontal constraint is -1"); y = nilobj; goto REJECT; } } else /* actually unused */ SetConstraint(c, MAX_FULL_LENGTH, MAX_FULL_LENGTH, MAX_FULL_LENGTH); debug1(DGA, DDD, " expanding %s", EchoObject(target)); tmp = CopyObject(target, no_fpos); Link(target_galley, tmp); env = DetachEnv(tmp); debug4(DGM, D, " external_ver(%s) = %s, external_hor(%s) = %s", SymName(actual(target)), bool(external_ver(target)), SymName(actual(target)), bool(external_hor(target))); SizeGalley(target_galley, env, external_ver(target) || external_hor(target), threaded(target), non_blocking(target_index), trigger_externs(target_index), &save_style(target), &c, whereto(hd), &dest_index, &recs, &tg_inners, enclose_obj(hd) != nilobj ? CopyObject(enclose_obj(hd), no_fpos):nilobj); debug1(DGA, DD, " SizeGalley tg_inners: %s", DebugInnersNames(tg_inners)); if( recs != nilobj ) ExpandRecursives(recs); dest = actual(dest_index); if( underline(dest) == UNDER_UNDEF ) underline(dest) = UNDER_OFF; /* verify that hd satisfies any horizontal constraint on dest */ if( dim == ROWM ) { debug1(DGA, DDD, " checking hor fit of hd in %s",SymName(actual(dest))); Constrained(dest, &c, 1-dim, &junk); debug3(DSC, DD, "Constrained( %s, %s ) = %s", EchoObject(dest), dimen(1-dim), EchoConstraint(&c)); assert( constrained(c), "AttachGalley: dest unconstrained!" ); if( !FitsConstraint(0, 0, c) ) { debug0(DGA, D, " reject: hd horizontal constraint is -1"); y = nilobj; goto REJECT; } } /* manifest and size the galley if not done yet */ if( !sized(hd) ) { debug2(DYY, D, "[ EnterErrorBlock(TRUE) (sizing galley %s into %s)", SymName(actual(hd)), SymName(whereto(hd))); EnterErrorBlock(TRUE); n1 = nilobj; Child(y, Down(hd)); env = DetachEnv(y); /*** threaded() only defined in ROWM case SizeGalley(hd, env, TRUE, threaded(dest), non_blocking(target_index), TRUE, &save_style(dest), &c, nilobj, &n1, &recs, &hd_inners); *** */ SizeGalley(hd, env, TRUE, dim == ROWM ? threaded(dest) : FALSE, non_blocking(target_index), TRUE, &save_style(dest), &c, nilobj, &n1, &recs, &hd_inners, nilobj); debug1(DGA,DD," SizeGalley hd_inners: %s", DebugInnersNames(hd_inners)); if( recs != nilobj ) ExpandRecursives(recs); if( need_precedes ) /* need an ordering constraint */ { OBJECT index1, index2; New(index1, PRECEDES); New(index2, FOLLOWS); blocked(index2) = FALSE; tmp = MakeWord(WORD, STR_EMPTY, no_fpos); Link(index1, tmp); Link(index2, tmp); Link(Up(hd_index), index1); Link(Down(hd), index2); debug0(DGA, D, " inserting PRECEDES and FOLLOWS"); } LeaveErrorBlock(TRUE); debug0(DYY, D, "] LeaveErrorBlock(TRUE) (finished sizing galley)"); } if( dim == ROWM ) { if( !FitsConstraint(back(hd, 1-dim), fwd(hd, 1-dim), c) ) { debug3(DGA, D, " reject: hd %s,%s does not fit target_galley %s", EchoLength(back(hd, 1-dim)), EchoLength(fwd(hd, 1-dim)), EchoConstraint(&c)); Error(19, 3, "too little horizontal space for galley %s at %s", WARN, &fpos(hd), SymName(actual(hd)), SymName(actual(dest))); goto REJECT; } } /* check status of first component of hd */ debug0(DGA, DDD, " now ready to attach; hd ="); ifdebug(DGA, DDD, DebugObject(hd)); for( link = Down(hd); link != hd; link = NextDown(link) ) { Child(y, link); debug1(DGA, DDD, " examining %s", EchoIndex(y)); if( type(y) == SPLIT ) Child(y, DownDim(y, dim)); switch( type(y) ) { case EXPAND_IND: case SCALE_IND: case COVER_IND: case GALL_PREC: case GALL_FOLL: case GALL_FOLL_OR_PREC: case GALL_TARG: case CROSS_PREC: case CROSS_FOLL: case CROSS_FOLL_OR_PREC: case CROSS_TARG: case PAGE_LABEL_IND: break; case PRECEDES: case UNATTACHED: if( was_sized ) { /* SizeGalley was not called, so hd_inners was not set by it */ if( hd_inners == nilobj ) New(hd_inners, ACAT); Link(hd_inners, y); } break; case RECEPTIVE: goto SUSPEND; case RECEIVING: goto SUSPEND; case FOLLOWS: Child(tmp, Down(y)); if( Up(tmp) == LastUp(tmp) ) { link = pred(link, CHILD); debug0(DGA, DD, " disposing FOLLOWS"); DisposeChild(NextDown(link)); break; } Parent(tmp, Up(tmp)); assert(type(tmp) == PRECEDES, "Attach: PRECEDES!"); switch( CheckComponentOrder(tmp, target_index) ) { case CLEAR: DeleteNode(tmp); link = pred(link, CHILD); DisposeChild(NextDown(link)); break; case PROMOTE: break; case BLOCK: debug0(DGA, DD, "CheckContraint: BLOCK"); goto SUSPEND; case CLOSE: debug0(DGA, D, " reject: CheckContraint"); goto REJECT; } break; case GAP_OBJ: underline(y) = underline(dest); if( !join(gap(y)) ) seen_nojoin(hd) = TRUE; break; case BEGIN_HEADER: case END_HEADER: case SET_HEADER: case CLEAR_HEADER: /* do nothing until actually promoted out of here */ underline(y) = underline(dest); break; case CLOSURE: case CROSS: case FORCE_CROSS: case NULL_CLOS: case PAGE_LABEL: underline(y) = underline(dest); break; case WORD: case QWORD: case ONE_COL: case ONE_ROW: case WIDE: case HIGH: case HSHIFT: case VSHIFT: case HMIRROR: case VMIRROR: case HSCALE: case VSCALE: case HCOVER: case VCOVER: case HCONTRACT: case VCONTRACT: case HLIMITED: case VLIMITED: case HEXPAND: case VEXPAND: case START_HVSPAN: case START_HSPAN: case START_VSPAN: case HSPAN: case VSPAN: case ROTATE: case BACKGROUND: case SCALE: case KERN_SHRINK: case INCGRAPHIC: case SINCGRAPHIC: case PLAIN_GRAPHIC: case GRAPHIC: case LINK_SOURCE: case LINK_DEST: case LINK_DEST_NULL: case LINK_URL: case ACAT: case HCAT: case VCAT: case ROW_THR: case COL_THR: underline(y) = underline(dest); if( dim == ROWM ) { /* make sure y is not joined to a target below (vertical only) */ for( zlink = NextDown(link); zlink != hd; zlink = NextDown(zlink) ) { Child(z, zlink); switch( type(z) ) { case RECEPTIVE: if( non_blocking(z) ) { zlink = PrevDown(zlink); DeleteNode(z); } else { y = z; goto SUSPEND; } break; case RECEIVING: if( non_blocking(z) ) { zlink = PrevDown(zlink); while( Down(z) != z ) { Child(tmp, Down(y)); if( opt_components(tmp) != nilobj ) { DisposeObject(opt_components(tmp)); opt_components(tmp) = nilobj; debug3(DOG, D, "AttachGalley(%s) de-optimizing %s %s", SymName(actual(hd)), SymName(actual(tmp)), "(join)"); } DetachGalley(tmp); KillGalley(tmp, FALSE); } DeleteNode(z); } else { y = z; goto SUSPEND; } break; case GAP_OBJ: if( !join(gap(z)) ) zlink = PrevDown(hd); break; default: break; } } /* if HCAT, try vertical hyphenation (vertical galleys only) */ if( type(y) == HCAT ) VerticalHyphenate(y); } /* check availability of parallel space for the first component */ why = nilobj; Constrained(dest, &c, dim, &why); debug3(DGF, DD, " dest parallel Constrained(%s, %s) = %s", EchoObject(dest), dimen(dim), EchoConstraint(&c)); if( !FitsConstraint(back(y, dim), fwd(y, dim), c) ) { BOOLEAN scaled; /* if forcing galley doesn't fit, try scaling first component */ scaled = FALSE; if( force_gall(hd) && size(y, dim) > 0 ) { int scale_factor; scale_factor = ScaleToConstraint(back(y,dim), fwd(y,dim), &c); if( scale_factor > 0.5 * SF ) { char num1[20], num2[20]; sprintf(num1, "%.1fc", (float) size(y, dim) / CM); sprintf(num2, "%.1fc", (float) bfc(c) / CM); if( dim == ROWM ) Error(19, 4, "%s object too high for %s space; %s inserted", WARN, &fpos(y), num1, num2, KW_SCALE); else Error(19, 5, "%s object too wide for %s space; %s inserted", WARN, &fpos(y), num1, num2, KW_SCALE); y = InterposeScale(y, scale_factor, dim); scaled = TRUE; } } /* otherwise we must reject, and warn the user */ if( !scaled ) { char num1[20], num2[20]; debug3(DGA, D, " reject: vsize %s,%s in %s; y=", EchoLength(back(y, dim)), EchoLength(fwd(y, dim)), EchoConstraint(&c)); ifdebug(DGA, D, DebugObject(y)); if( size(y, dim) > 0 ) { sprintf(num1, "%.1fc", (float) size(y, dim) / CM); sprintf(num2, "%.1fc", (float) bfc(c) / CM); if( dim == ROWM ) Error(19, 12, "%s object too high for %s space; will try elsewhere", WARN, &fpos(y), num1, num2); else Error(19, 13, "%s object too wide for %s space; will try elsewhere", WARN, &fpos(y), num1, num2); } goto REJECT; } } /* check availability of perpendicular space for first component */ if( dim == ROWM ) { perp_back = back(hd, 1-dim); perp_fwd = fwd(hd, 1-dim); } else { perp_back = back(y, 1-dim); perp_fwd = fwd(y, 1-dim); } Constrained(dest, &c, 1-dim, &junk); debug3(DGF, DD, " dest perpendicular Constrained(%s, %s) = %s", EchoObject(dest), dimen(1-dim), EchoConstraint(&c)); if( !FitsConstraint(perp_back, perp_fwd, c) ) { BOOLEAN scaled; /* if forcing galley doesn't fit, try scaling first component */ scaled = FALSE; if( force_gall(hd) && perp_back + perp_fwd > 0 ) { int scale_factor; scale_factor = ScaleToConstraint(perp_back, perp_fwd, &c); if( scale_factor > 0.5 * SF ) { char num1[20], num2[20]; sprintf(num1, "%.1fc", (float) (perp_back + perp_fwd) / CM); sprintf(num2, "%.1fc", (float) bfc(c) / CM); if( 1-dim == ROWM ) Error(19, 6, "%s object too high for %s space; %s inserted", WARN, &fpos(y), num1, num2, KW_SCALE); else Error(19, 7, "%s object too wide for %s space; %s inserted", WARN, &fpos(y), num1, num2, KW_SCALE); y = InterposeScale(y, scale_factor, 1-dim); scaled = TRUE; } } /* otherwise we must reject, and warn the user */ if( !scaled ) { debug3(DGA, D, " reject: vsize %s,%s in %s; y=", EchoLength(perp_back), EchoLength(perp_fwd), EchoConstraint(&c)); ifdebug(DGA, D, DebugObject(y)); goto REJECT; } } /* dest seems OK, so perform its size adjustments */ debug0(DSA, D, "calling AdjustSize from AttachGalley (a)"); AdjustSize(dest, back(y, dim), fwd(y, dim), dim); debug0(DSA, D, "calling AdjustSize from AttachGalley (b)"); AdjustSize(dest, perp_back, perp_fwd, 1-dim); /* now check parallel space for target_galley in target */ Constrained(target, &c, dim, &why); debug3(DGF, DD, " target parallel Constrained(%s, %s) = %s", EchoObject(target), dimen(dim), EchoConstraint(&c)); Child(z, LastDown(target_galley)); /* works in all cases? */ assert( !is_index(type(z)), "AttachGalley: is_index(z)!" ); assert( back(z, dim)>=0 && fwd(z, dim)>=0, "AttachGalley: z size!" ); if( !FitsConstraint(back(z, dim), fwd(z, dim), c) ) { BOOLEAN scaled; debug2(DGA, D, " why = %d %s", (int) why, EchoObject(why)); debug2(DGA, D, " limiter = %d %s", (int) limiter(hd), EchoObject(limiter(hd))); /* if forcing galley doesn't fit, try scaling z */ scaled = FALSE; if( force_gall(hd) && size(z, dim) > 0 && limiter(hd) != why ) { int scale_factor; scale_factor = ScaleToConstraint(back(z,dim), fwd(z,dim), &c); if( scale_factor > 0.5 * SF ) { char num1[20], num2[20]; sprintf(num1, "%.1fc", (float) size(z, dim) / CM); sprintf(num2, "%.1fc", (float) bfc(c) / CM); if( dim == ROWM ) Error(19, 8, "%s object too high for %s space; %s inserted", WARN, &fpos(y), num1, num2, KW_SCALE); else Error(19, 9, "%s object too wide for %s space; %s inserted", WARN, &fpos(y), num1, num2, KW_SCALE); z = InterposeWideOrHigh(z, dim); z = InterposeScale(z, scale_factor, dim); scaled = TRUE; } } if( !scaled ) { char num1[20], num2[20]; limiter(hd) = why; debug3(DGA, D, " set limiter(%s) = %d %s", SymName(actual(hd)), (int) limiter(hd), EchoObject(limiter(hd))); debug3(DGA, D, " reject: size was %s,%s in %s; y =", EchoLength(back(z, dim)), EchoLength(fwd(z, dim)), EchoConstraint(&c)); ifdebug(DGA, D, DebugObject(y)); if( size(z, dim) > 0 ) { sprintf(num1, "%.1fc", (float) size(z, dim) / CM); sprintf(num2, "%.1fc", (float) bfc(c) / CM); if( dim == ROWM ) Error(19, 14, "%s object too high for %s space; will try elsewhere", WARN, &fpos(y), num1, num2); else Error(19, 15, "%s object too wide for %s space; will try elsewhere", WARN, &fpos(y), num1, num2); } goto REJECT; } } limiter(hd) = why; debug3(DGA, D, " set limiter(%s) = %d %s", SymName(actual(hd)), (int) limiter(hd), EchoObject(limiter(hd))); /* now check perpendicular space for target_galley in target */ Constrained(target, &c, 1-dim, &junk); debug3(DGF, DD, " target perpendicular Constrained(%s, %s) = %s", EchoObject(target), dimen(1-dim), EchoConstraint(&c)); Child(z, LastDown(target_galley)); /* works in all cases? */ assert( !is_index(type(z)), "AttachGalley: is_index(z)!" ); assert( back(z, 1-dim)>=0 && fwd(z, 1-dim)>=0, "AttachGalley: z size (perpendicular)!" ); if( !FitsConstraint(back(z, 1-dim), fwd(z, 1-dim), c) ) { BOOLEAN scaled; /* if forcing galley doesn't fit, try scaling z */ scaled = FALSE; if( force_gall(hd) && size(z, 1-dim) > 0 ) { int scale_factor; scale_factor = ScaleToConstraint(back(z,1-dim), fwd(z,1-dim), &c); if( scale_factor > 0.5 * SF ) { char num1[20], num2[20]; sprintf(num1, "%.1fc", (float) size(z, 1-dim) / CM); sprintf(num2, "%.1fc", (float) bfc(c) / CM); if( 1-dim == ROWM ) Error(19, 10, "%s object too high for %s space; %s inserted", WARN, &fpos(y), num1, num2, KW_SCALE); else Error(19, 11, "%s object too wide for %s space; %s inserted", WARN, &fpos(y), num1, num2, KW_SCALE); z = InterposeWideOrHigh(z, 1-dim); z = InterposeScale(z, scale_factor, 1-dim); scaled = TRUE; } } if( !scaled ) { debug3(DGA, D, " reject: size was %s,%s in %s; y =", EchoLength(back(z, 1-dim)), EchoLength(fwd(z, 1-dim)), EchoConstraint(&c)); ifdebug(DGA, D, DebugObject(y)); goto REJECT; } } /* target seems OK, so adjust sizes and accept */ if( external_hor(target) ) { /* don't adjust any sizes, none to adjust */ debug0(DSA, D, "not calling AdjustSize from AttachGalley (c)"); } else if( external_ver(target) ) { /* adjust perp size only, to galley size */ debug0(DSA, D, "calling AdjustSize from AttachGalley (d)"); AdjustSize(target, back(target_galley, 1-dim), fwd(target_galley, 1-dim), 1-dim); } else { /* adjust both directions, using z (last component) */ Child(z, LastDown(target_galley)); debug0(DSA, D, "AttachGalley AdjustSize using z ="); ifdebug(DSA, D, DebugObject(z)); debug0(DSA, D, "calling AdjustSize from AttachGalley (e)"); AdjustSize(target, back(z, dim), fwd(z, dim), dim); debug0(DSA, D, "calling AdjustSize from AttachGalley (f)"); AdjustSize(target, back(z, 1-dim), fwd(z, 1-dim), 1-dim); } goto ACCEPT; default: assert1(FALSE, "AttachGalley:", Image(type(y))); break; } /* end switch */ } /* end for */ /* null galley: promote whole galley without expanding the target */ debug0(DGA, D, " null galley"); if( tg_inners != nilobj ) DisposeObject(tg_inners), tg_inners = nilobj; DisposeObject(target_galley); LeaveErrorBlock(FALSE); debug0(DYY, D, "] LeaveErrorBlock(FALSE) (null galley)"); /* kill off any null objects within the galley, then transfer it */ /* don't use Promote() since it does extra unwanted things here */ for( link = Down(hd); link != hd; link = NextDown(link) ) { Child(y, link); switch( type(y) ) { case GAP_OBJ: case CLOSURE: case CROSS: case FORCE_CROSS: case NULL_CLOS: case PAGE_LABEL: link = PrevDown(link); debug1(DGA, D, " null galley, disposing %s", Image(type(y))); DisposeChild(NextDown(link)); break; default: break; } } TransferLinks(NextDown(hd), hd, Up(target_index)); /* attach hd temporarily to target_index */ MoveLink(Up(hd), target_index, PARENT); assert( type(hd_index) == UNATTACHED, "AttachGalley: type(hd_index)!" ); DeleteNode(hd_index); /* return; only hd_inners needs to be flushed now */ *inners = hd_inners; debug0(DGA, D, "] AttachGalley returning ATTACH_NULL"); return ATTACH_NULL; REJECT: /* reject first component */ /* debug1(DGA, D, " reject %s", EchoObject(y)); */ debug0(DGA, D, " reject first component"); LeaveErrorBlock(TRUE); debug0(DYY, D, "] LeaveErrorBlock(TRUE) (REJECT)"); if( tg_inners != nilobj ) DisposeObject(tg_inners), tg_inners = nilobj; DisposeObject(target_galley); if( foll_or_prec(hd) == GALL_PREC && !sized(hd) ) { /* move to just before the failed target */ MoveLink(Up(hd_index), Up(target_index), PARENT); } else { /* move to just after the failed target */ MoveLink(Up(hd_index), NextDown(Up(target_index)), PARENT); } continue; SUSPEND: /* suspend at first component */ debug1(DGA, D, " suspend %s", EchoIndex(y)); blocked(y) = TRUE; LeaveErrorBlock(FALSE); debug0(DYY, D, "] LeaveErrorBlock(FALSE) (SUSPEND)"); if( tg_inners != nilobj ) DisposeObject(tg_inners), tg_inners = nilobj; DisposeObject(target_galley); MoveLink(Up(hd_index), Up(target_index), PARENT); if( was_sized ) { /* nothing new to flush if suspending and already sized */ if( hd_inners != nilobj ) DisposeObject(hd_inners), hd_inners=nilobj; *inners = nilobj; } else { /* flush newly discovered inners if not sized before */ *inners = hd_inners; } debug0(DGA, D, "] AttachGalley returning ATTACH_SUSPEND"); *suspend_pt = y; return ATTACH_SUSPEND; ACCEPT: /* accept first component; now committed to the attach */ debug3(DGA, D, " accept %s %s %s", Image(type(y)), EchoObject(y), EchoFilePos(&fpos(y))); LeaveErrorBlock(TRUE); debug0(DYY, D, "] LeaveErrorBlock(TRUE) (ACCEPT)"); /* attach hd to dest */ MoveLink(Up(hd), dest_index, PARENT); assert( type(hd_index) == UNATTACHED, "AttachGalley: type(hd_index)!" ); DeleteNode(hd_index); /* move first component of hd into dest */ /* nb Interpose must be done after all AdjustSize calls */ if( dim == ROWM && !external_ver(dest) ) Interpose(dest, VCAT, hd, y); else if( dim == COLM && !external_hor(dest) ) { Interpose(dest, ACAT, y, y); Parent(junk, Up(dest)); assert( type(junk) == ACAT, "AttachGalley: type(junk) != ACAT!" ); StyleCopy(save_style(junk), save_style(dest)); adjust_cat(junk) = padjust(save_style(junk)); } debug1(DGS, D, "calling Promote(hd, %s) from AttachGalley/ACCEPT", link == hd ? "hd" : "NextDown(link)"); Promote(hd, link == hd ? hd : NextDown(link), dest_index, TRUE); /* move target_galley into target */ /* nb Interpose must be done after all AdjustSize calls */ if( !(external_ver(target) || external_hor(target)) ) { Child(z, LastDown(target_galley)); Interpose(target, VCAT, z, z); } debug0(DGS, D, "calling Promote(target_galley) from AttachGalley/ACCEPT"); Promote(target_galley, target_galley, target_index, TRUE); DeleteNode(target_galley); assert(Down(target_index)==target_index, "AttachGalley: target_ind"); if( blocked(target_index) ) blocked(dest_index) = TRUE; DeleteNode(target_index); /* return; both tg_inners and hd_inners need to be flushed now; */ /* if was_sized, hd_inners contains the inners of the first component; */ /* otherwise it contains the inners of all components, from SizeGalley */ if( tg_inners == nilobj ) *inners = hd_inners; else if( hd_inners == nilobj ) *inners = tg_inners; else { TransferLinks(Down(hd_inners), hd_inners, tg_inners); DeleteNode(hd_inners); *inners = tg_inners; } debug0(DGA, D, "] AttachGalley returning ATTACH_ACCEPT"); ifdebug(DGA, D, if( dim == COLM && !external_hor(dest) ) { OBJECT z; Parent(z, Up(dest)); debug2(DGA, D, " COLM dest_encl on exit = %s %s", Image(type(z)), EchoObject(z)); } ) return ATTACH_ACCEPT; } /* end for */
*/ HashKey = tree->save_hash_key[ply]; PawnHashKey = tree->save_pawn_hash_key[ply]; /* ************************************************************ * * * Now do the things that are common to all pieces, such * * as updating the bitboards and hash signature. * * * ************************************************************ */ piece = Piece(move); from = From(move); to = To(move); captured = Captured(move); promote = Promote(move); bit_move = SetMask(from) | SetMask(to); ClearSet(bit_move, Pieces(side, piece)); ClearSet(bit_move, Occupied(side)); PcOnSq(to) = 0; PcOnSq(from) = pieces[side][piece]; /* ************************************************************ * * * Now do the piece-specific things by jumping to the * * appropriate routine (this only has to deal with pawns * * and king moves that are castling moves. * * * ************************************************************ */ switch (piece) {
/* ******************************************************************************** * * * Search() is the recursive routine used to implement the alpha/beta * * negamax search (similar to minimax but simpler to code.) Search() is * * called whenever there is "depth" remaining so that all moves are subject * * to searching, or when the side to move is in check, to make sure that this * * side isn't mated. Search() recursively calls itself until depth is ex- * * hausted, at which time it calls Quiesce() instead. * * * ******************************************************************************** */ int Search(int alpha, int beta, int wtm, int depth, int ply, int do_null) { register int first_move=1; register BITBOARD save_hash_key; register int initial_alpha, value; register int extensions; /* ---------------------------------------------------------- | | | check to see if we have searched enough nodes that it | | is time to peek at how much time has been used, or if | | is time to check for operator keyboard input. this is | | usually enough nodes to force a time/input check about | | once per second, except when the target time per move | | is very small, in which case we try to check the time | | at least 10 times during the search. | | | ---------------------------------------------------------- */ if (ply >= MAXPLY-2) return(beta); nodes_searched++; if (--next_time_check <= 0) { next_time_check=nodes_between_time_checks; if (CheckInput()) Interrupt(ply); time_abort+=TimeCheck(0); if (time_abort) { abort_search=1; return(0); } } /* ---------------------------------------------------------- | | | check for draw by repetition. | | | ---------------------------------------------------------- */ if (RepetitionCheck(ply,wtm)) { value=(wtm==root_wtm) ? DrawScore() : -DrawScore(); if (value < beta) SavePV(ply,value,0); #if !defined(FAST) if(ply <= trace_level) printf("draw by repetition detected, ply=%d.\n",ply); #endif return(value); } /* ---------------------------------------------------------- | | | now call LookUp() to see if this position has been | | searched before. if so, we may get a real score, | | produce a cutoff, or get nothing more than a good move | | to try first. there are four cases to handle: | | | | 1. LookUp() returned "EXACT_SCORE" if this score is | | greater than beta, return beta. otherwise, return the | | score. In either case, no further searching is needed | | from this position. note that lookup verified that | | the table position has sufficient "draft" to meet the | | requirements of the current search depth remaining. | | | | 2. LookUp() returned "LOWER_BOUND" which means that | | when this position was searched previously, every move | | was "refuted" by one of its descendents. as a result, | | when the search was completed, we returned alpha at | | that point. we simply return alpha here as well. | | | | 3. LookUp() returned "UPPER_BOUND" which means that | | when we encountered this position before, we searched | | one branch (probably) which promptly refuted the move | | at the previous ply. | | | | 4. LookUp() returned "AVOID_NULL_MOVE" which means | | the hashed score/bound was no good, but it indicated | | that trying a null-move in this position will be a | | waste of time. | | | ---------------------------------------------------------- */ switch (LookUp(ply,depth,wtm,&alpha,beta)) { case EXACT_SCORE: if(alpha >= beta) return(beta); else { SavePV(ply,alpha,1); return(alpha); } case LOWER_BOUND: return(alpha); case UPPER_BOUND: return(beta); case AVOID_NULL_MOVE: do_null=0; } /* ---------------------------------------------------------- | | | now it's time to try a probe into the endgame table- | | base files. this is done if (a) the previous move was | | a capture or promotion, unless we are at very shallow | | plies (<4) in the search; (b) there are less than 5 | | pieces left (currently all interesting 4 piece endings | | are available.) | | | ---------------------------------------------------------- */ #if defined(TABLEBASES) if (TotalPieces < 5) do { register int wpawn, bpawn; int tb_value; if (TotalWhitePawns && TotalBlackPawns) { wpawn=FirstOne(WhitePawns); bpawn=FirstOne(BlackPawns); if (FileDistance(wpawn,bpawn) == 1) { if(((Rank(wpawn)==1) && (Rank(bpawn)>2)) || ((Rank(bpawn)==6) && (Rank(wpawn)<5)) || EnPassant(ply)) break; } } tb_probes++; if (EGTBScore(ply, wtm, &tb_value)) { tb_probes_successful++; alpha=tb_value; if (abs(alpha) > MATE-100) alpha+=(alpha > 0) ? -(ply-1) : +(ply-1); else if (alpha == 0) alpha=(wtm==root_wtm) ? DrawScore() : -DrawScore(); if(alpha >= beta) return(beta); else { SavePV(ply,alpha,2); return(alpha); } } } while(0); # endif /* ---------------------------------------------------------- | | | initialize. | | | ---------------------------------------------------------- */ in_check[ply+1]=0; extended_reason[ply+1]=no_extension; initial_alpha=alpha; last[ply]=last[ply-1]; killer_count1[ply+1]=0; killer_count2[ply+1]=0; /* ---------------------------------------------------------- | | | first, we try a null move to see if we can get a quick | | cutoff with only a little work. this operates as | | follows. instead of making a legal move, the side on | | move 'passes' and does nothing. the resulting position | | is searched to a shallower depth than normal (usually | | one ply less but settable by the operator) this should | | result in a cutoff or at least should set the lower | | bound better since anything should be better than not | | doing anything. | | | | this is skipped for any of the following reasons: | | | | 1. the side on move is in check. the null move | | results in an illegal position. | | 2. no more than one null move can appear in succession | | or else the search will degenerate into nothing. | | 3. the side on move has little material left making | | zugzwang positions more likely. | | | ---------------------------------------------------------- */ # if defined(NULL_MOVE_DEPTH) if (do_null && !in_check[ply] && ((wtm) ? TotalWhitePieces : TotalBlackPieces)>2) { current_move[ply]=0; current_phase[ply]=NULL_MOVE; #if !defined(FAST) if (ply <= trace_level) SearchTrace(ply,depth,wtm,alpha,beta,"Search",0); #endif position[ply+1]=position[ply]; Rule50Moves(ply+1)++; save_hash_key=HashKey; if (EnPassant(ply)) { HashEP(EnPassant(ply+1),HashKey); EnPassant(ply+1)=0; } if ((depth-NULL_MOVE_DEPTH-INCREMENT_PLY) >= INCREMENT_PLY) value=-Search(-beta,-alpha,ChangeSide(wtm),depth-NULL_MOVE_DEPTH-INCREMENT_PLY,ply+1,NO_NULL); else value=-Quiesce(-beta,-alpha,ChangeSide(wtm),ply+1); HashKey=save_hash_key; if (abort_search) return(0); if (value >= beta) { StoreRefutation(ply,depth,wtm,beta); return(beta); } } # endif /* ---------------------------------------------------------- | | | if there is no best move from the hash table, and this | | is a PV node, then we need a good move to search | | first. while killers and history moves are good, they | | are not "good enough". the simplest action is to try | | a shallow search (depth-2) to get a move. note that | | when we call Search() with depth-2, it, too, will | | not have a hash move, and will therefore recursively | | continue this process, hence the name "internal | | iterative deepening." | | | ---------------------------------------------------------- */ next_status[ply].phase=FIRST_PHASE; if (hash_move[ply]==0 && (depth > 2*INCREMENT_PLY) && (((ply & 1) && alpha == root_alpha && beta == root_beta) || (!(ply & 1) && alpha == -root_beta && beta == -root_alpha))) { current_move[ply]=0; value=Search(alpha,beta,wtm,depth-2*INCREMENT_PLY,ply,DO_NULL); if (abort_search) return(0); if (value <= alpha) { value=Search(-MATE,beta,wtm,depth-2*INCREMENT_PLY,ply,DO_NULL); if (abort_search) return(0); } else if (value < beta) { if ((int) pv[ply-1].path_length >= ply) hash_move[ply]=pv[ply-1].path[ply]; } else hash_move[ply]=current_move[ply]; last[ply]=last[ply-1]; next_status[ply].phase=FIRST_PHASE; } /* ---------------------------------------------------------- | | | now iterate through the move list and search the | | resulting positions. note that Search() culls any | | move that is not legal by using Check(). the special | | case is that we must find one legal move to search to | | confirm that it's not a mate or draw. | | | ---------------------------------------------------------- */ while ((current_phase[ply]=(in_check[ply]) ? NextEvasion(ply,wtm) : NextMove(depth,ply,wtm))) { extended_reason[ply]&=check_extension; #if !defined(FAST) if (ply <= trace_level) SearchTrace(ply,depth,wtm,alpha,beta,"Search",current_phase[ply]); #endif /* ---------------------------------------------------------- | | | if two successive moves are capture / re-capture so | | that the material score is restored, extend the search | | by one ply on the re-capture since it is pretty much | | forced and easy to analyze. | | | ---------------------------------------------------------- */ extensions=-INCREMENT_PLY; if (Captured(current_move[ply]) && Captured(current_move[ply-1]) && To(current_move[ply-1]) == To(current_move[ply]) && (p_values[Captured(current_move[ply-1])+7] == p_values[Captured(current_move[ply])+7] || Promote(current_move[ply-1])) && !(extended_reason[ply-1]&recapture_extension)) { extended_reason[ply]|=recapture_extension; recapture_extensions_done++; extensions+=RECAPTURE; } /* ---------------------------------------------------------- | | | if we push a passed pawn, we need to look deeper to | | see if it is a legitimate threat. | | | ---------------------------------------------------------- */ if (Piece(current_move[ply])==pawn && !FutileAhead(wtm) && ((wtm && To(current_move[ply])>H5 && TotalBlackPieces<16 && !And(mask_pawn_passed_w[To(current_move[ply])],BlackPawns)) || (!wtm && To(current_move[ply])<A4 && TotalWhitePieces<16 && !And(mask_pawn_passed_b[To(current_move[ply])],WhitePawns)) || push_extensions[To(current_move[ply])]) && Swap(From(current_move[ply]),To(current_move[ply]),wtm) >= 0) { extended_reason[ply]|=passed_pawn_extension; passed_pawn_extensions_done++; extensions+=PASSED_PAWN_PUSH; } /* ---------------------------------------------------------- | | | now make the move and search the resulting position. | | if we are in check, the current move must be legal | | since NextEvasion ensures this, otherwise we have to | | make sure the side-on-move is not in check after the | | move to weed out illegal moves and save time. | | | ---------------------------------------------------------- */ MakeMove(ply,current_move[ply],wtm); if (in_check[ply] || !Check(wtm)) { /* ---------------------------------------------------------- | | | if the move to be made checks the opponent, then we | | need to remember that he's in check and also extend | | the depth by one ply for him to get out. | | | ---------------------------------------------------------- */ if (Check(ChangeSide(wtm))) { in_check[ply+1]=1; extended_reason[ply+1]=check_extension; check_extensions_done++; extensions+=IN_CHECK; } else { in_check[ply+1]=0; extended_reason[ply+1]=no_extension; } /* ---------------------------------------------------------- | | | now we toss in the "razoring" trick, which simply says | | if we are doing fairly badly, we can reduce the depth | | an additional ply, if there was nothing at the current | | ply that caused an extension. | | | ---------------------------------------------------------- */ if (depth < 3*INCREMENT_PLY && !in_check[ply] && extensions == -INCREMENT_PLY) { register int val=(wtm) ? Material : -Material; if (val+1500 < alpha) extensions-=INCREMENT_PLY; } /* ---------------------------------------------------------- | | | if there's only one legal move, extend the search one | | additional ply since this node is very easy to search. | | | ---------------------------------------------------------- */ if (first_move) { if (last[ply]-last[ply-1] == 1) { extended_reason[ply]|=one_reply_extension; one_reply_extensions_done++; extensions+=ONE_REPLY_TO_CHECK; } if (depth+MaxExtensions(extensions) >= INCREMENT_PLY) value=-Search(-beta,-alpha,ChangeSide(wtm),depth+MaxExtensions(extensions),ply+1,DO_NULL); else { value=-Quiesce(-beta,-alpha,ChangeSide(wtm),ply+1); } if (abort_search) { UnMakeMove(ply,current_move[ply],wtm); return(0); } first_move=0; } else { if (depth+MaxExtensions(extensions) >= INCREMENT_PLY) value=-Search(-alpha-1,-alpha,ChangeSide(wtm),depth+MaxExtensions(extensions),ply+1,DO_NULL); else { value=-Quiesce(-alpha-1,-alpha,ChangeSide(wtm),ply+1); } if (abort_search) { UnMakeMove(ply,current_move[ply],wtm); return(0); } if (value>alpha && value<beta) { if (depth+MaxExtensions(extensions) >= INCREMENT_PLY) value=-Search(-beta,-alpha,ChangeSide(wtm),depth+MaxExtensions(extensions),ply+1,DO_NULL); else value=-Quiesce(-beta,-alpha,ChangeSide(wtm),ply+1); if (abort_search) { UnMakeMove(ply,current_move[ply],wtm); return(0); } } } if (value > alpha) { if(value >= beta) { HistoryRefutation(ply,depth,wtm); UnMakeMove(ply,current_move[ply],wtm); StoreRefutation(ply,depth,wtm,beta); return(beta); } alpha=value; } } UnMakeMove(ply,current_move[ply],wtm); } /* ---------------------------------------------------------- | | | all moves have been searched. if none were legal, | | return either MATE or DRAW depending on whether the | | side to move is in check or not. | | | ---------------------------------------------------------- */ if (first_move == 1) { value=(Check(wtm)) ? -(MATE-ply) : ((wtm==root_wtm) ? DrawScore() : -DrawScore()); if(value > beta) value=beta; else if (value < alpha) value=alpha; if (value >=alpha && value <beta) { SavePV(ply,value,0); #if !defined(FAST) if (ply <= trace_level) printf("Search() no moves! ply=%d\n",ply); #endif } return(value); } else { if (alpha != initial_alpha) { memcpy(&pv[ply-1].path[ply],&pv[ply].path[ply],(pv[ply].path_length-ply+1)*4); memcpy(&pv[ply-1].path_hashed,&pv[ply].path_hashed,3); pv[ply-1].path[ply-1]=current_move[ply-1]; HistoryBest(ply,depth,wtm); } StoreBest(ply,depth,wtm,alpha,initial_alpha); /* ---------------------------------------------------------- | | | if the 50-move rule is drawing close, then adjust the | | score to reflect the impending draw. | | | ---------------------------------------------------------- */ if (Rule50Moves(ply) > 99) { value=(wtm==root_wtm) ? DrawScore() : -DrawScore(); if (value < beta) SavePV(ply,value,0); #if !defined(FAST) if(ply <= trace_level) printf("draw by 50-move rule detected, ply=%d.\n",ply); #endif return(value); } return(alpha); } }
static CAT_INLINE u8 *Resize(u8 *ptr, u32 new_trailing_bytes) { if (!ptr) return Acquire(new_trailing_bytes); return Resize(Promote(ptr), new_trailing_bytes); }
void UnMakeMove(int ply, int move, int wtm) { register int piece, from, to, captured, promote; /* ---------------------------------------------------------- | | | first, take care of the hash key if there's a possible | | enpassant pawn capture. | | | ---------------------------------------------------------- */ HashKey=save_hash_key[ply]; PawnHashKey=save_pawn_hash_key[ply]; /* ---------------------------------------------------------- | | | now do the piece-specific things by calling the | | appropriate routine. | | | ---------------------------------------------------------- */ piece=Piece(move); from=From(move); to=To(move); captured=Captured(move); promote=Promote(move); UnMakePieceMove: SetRL90(from,OccupiedRL90); SetRL45(from,OccupiedRL45); SetRR45(from,OccupiedRR45); ClearRL90(to,OccupiedRL90); ClearRL45(to,OccupiedRL45); ClearRR45(to,OccupiedRR45); bit_move=Or(set_mask[from],set_mask[to]); PieceOnSquare(to)=0; switch (piece) { /* ******************************************************************************** * * * unmake pawn moves. * * * ******************************************************************************** */ case pawn: if (wtm) { ClearSet(bit_move,WhitePawns); ClearSet(bit_move,WhitePieces); PieceOnSquare(from)=pawn; if (captured == 1) { if(EnPassant(ply) == to) { TotalPieces++; SetRL90(to-8,OccupiedRL90); SetRL45(to-8,OccupiedRL45); SetRR45(to-8,OccupiedRR45); Set(to-8,BlackPawns); Set(to-8,BlackPieces); PieceOnSquare(to-8)=-pawn; Material-=PAWN_VALUE; TotalBlackPawns++; captured=0; } } /* -------------------------------------------------------------------- | | | if this is a pawn promotion, remove the pawn from the counts | | then update the correct piece board to reflect the piece just | | created. | | | -------------------------------------------------------------------- */ if (promote) { TotalWhitePawns++; Material+=PAWN_VALUE; Clear(to,WhitePawns); Clear(to,WhitePieces); switch (promote) { case knight: Clear(to,WhiteKnights); TotalWhitePieces-=knight_v; Material-=KNIGHT_VALUE; break; case bishop: Clear(to,WhiteBishops); Clear(to,BishopsQueens); TotalWhitePieces-=bishop_v; Material-=BISHOP_VALUE; break; case rook: Clear(to,WhiteRooks); Clear(to,RooksQueens); TotalWhitePieces-=rook_v; Material-=ROOK_VALUE; break; case queen: Clear(to,WhiteQueens); Clear(to,BishopsQueens); Clear(to,RooksQueens); TotalWhitePieces-=queen_v; Material-=QUEEN_VALUE; break; } } } else { ClearSet(bit_move,BlackPawns); ClearSet(bit_move,BlackPieces); PieceOnSquare(from)=-pawn; if (captured == 1) { if(EnPassant(ply) == to) { TotalPieces++; SetRL90(to+8,OccupiedRL90); SetRL45(to+8,OccupiedRL45); SetRR45(to+8,OccupiedRR45); Set(to+8,WhitePawns); Set(to+8,WhitePieces); PieceOnSquare(to+8)=pawn; Material+=PAWN_VALUE; TotalWhitePawns++; captured=0; } } /* -------------------------------------------------------------------- | | | if this is a pawn promotion, remove the pawn from the counts | | then update the correct piece board to reflect the piece just | | created. | | | -------------------------------------------------------------------- */ if (promote) { TotalBlackPawns++; Material-=PAWN_VALUE; Clear(to,BlackPawns); Clear(to,BlackPieces); switch (promote) { case knight: Clear(to,BlackKnights); TotalBlackPieces-=knight_v; Material+=KNIGHT_VALUE; break; case bishop: Clear(to,BlackBishops); Clear(to,BishopsQueens); TotalBlackPieces-=bishop_v; Material+=BISHOP_VALUE; break; case rook: Clear(to,BlackRooks); Clear(to,RooksQueens); TotalBlackPieces-=rook_v; Material+=ROOK_VALUE; break; case queen: Clear(to,BlackQueens); Clear(to,BishopsQueens); Clear(to,RooksQueens); TotalBlackPieces-=queen_v; Material+=QUEEN_VALUE; break; } } } break; /* ******************************************************************************** * * * unmake knight moves. * * * ******************************************************************************** */ case knight: if (wtm) { ClearSet(bit_move,WhiteKnights); ClearSet(bit_move,WhitePieces); PieceOnSquare(from)=knight; } else { ClearSet(bit_move,BlackKnights); ClearSet(bit_move,BlackPieces); PieceOnSquare(from)=-knight; } break; /* ******************************************************************************** * * * unmake bishop moves. * * * ******************************************************************************** */ case bishop: ClearSet(bit_move,BishopsQueens); if (wtm) { ClearSet(bit_move,WhiteBishops); ClearSet(bit_move,WhitePieces); PieceOnSquare(from)=bishop; } else { ClearSet(bit_move,BlackBishops); ClearSet(bit_move,BlackPieces); PieceOnSquare(from)=-bishop; } break; /* ******************************************************************************** * * * unmake rook moves. * * * ******************************************************************************** */ case rook: ClearSet(bit_move,RooksQueens); if (wtm) { ClearSet(bit_move,WhiteRooks); ClearSet(bit_move,WhitePieces); PieceOnSquare(from)=rook; } else { ClearSet(bit_move,BlackRooks); ClearSet(bit_move,BlackPieces); PieceOnSquare(from)=-rook; } break; /* ******************************************************************************** * * * unmake queen moves. * * * ******************************************************************************** */ case queen: ClearSet(bit_move,BishopsQueens); ClearSet(bit_move,RooksQueens); if (wtm) { ClearSet(bit_move,WhiteQueens); ClearSet(bit_move,WhitePieces); PieceOnSquare(from)=queen; } else { ClearSet(bit_move,BlackQueens); ClearSet(bit_move,BlackPieces); PieceOnSquare(from)=-queen; } break; /* ******************************************************************************** * * * unmake king moves. * * * ******************************************************************************** */ case king: if (wtm) { ClearSet(bit_move,WhitePieces); PieceOnSquare(from)=king; WhiteKingSQ=from; if (abs(to-from) == 2) { if (to == 6) { from=H1; to=F1; piece=rook; goto UnMakePieceMove; } else { from=A1; to=D1; piece=rook; goto UnMakePieceMove; } } } else { ClearSet(bit_move,BlackPieces); PieceOnSquare(from)=-king; BlackKingSQ=from; if (abs(to-from) == 2) { if (to == 62) { from=H8; to=F8; piece=rook; goto UnMakePieceMove; } else { from=A8; to=D8; piece=rook; goto UnMakePieceMove; } } } break; } /* ******************************************************************************** * * * now it is time to restore a piece that was captured. * * * ******************************************************************************** */ if(captured) { TotalPieces++; SetRL90(to,OccupiedRL90); SetRL45(to,OccupiedRL45); SetRR45(to,OccupiedRR45); switch (captured) { /* ---------------------------------------------------------- | | | restore a captured pawn. | | | ---------------------------------------------------------- */ case pawn: if (wtm) { Set(to,BlackPawns); Set(to,BlackPieces); PieceOnSquare(to)=-pawn; Material-=PAWN_VALUE; TotalBlackPawns++; } else { Set(to,WhitePawns); Set(to,WhitePieces); PieceOnSquare(to)=pawn; Material+=PAWN_VALUE; TotalWhitePawns++; } break; /* ---------------------------------------------------------- | | | restore a captured knight. | | | ---------------------------------------------------------- */ case knight: if (wtm) { Set(to,BlackKnights); Set(to,BlackPieces); PieceOnSquare(to)=-knight; TotalBlackPieces+=knight_v; Material-=KNIGHT_VALUE; } else { Set(to,WhiteKnights); Set(to,WhitePieces); PieceOnSquare(to)=knight; TotalWhitePieces+=knight_v; Material+=KNIGHT_VALUE; } break; /* ---------------------------------------------------------- | | | restore a captured bishop. | | | ---------------------------------------------------------- */ case bishop: Set(to,BishopsQueens); if (wtm) { Set(to,BlackBishops); Set(to,BlackPieces); PieceOnSquare(to)=-bishop; TotalBlackPieces+=bishop_v; Material-=BISHOP_VALUE; } else { Set(to,WhiteBishops); Set(to,WhitePieces); PieceOnSquare(to)=bishop; TotalWhitePieces+=bishop_v; Material+=BISHOP_VALUE; } break; /* ---------------------------------------------------------- | | | restore a captured rook. | | | ---------------------------------------------------------- */ case rook: Set(to,RooksQueens); if (wtm) { Set(to,BlackRooks); Set(to,BlackPieces); PieceOnSquare(to)=-rook; TotalBlackPieces+=rook_v; Material-=ROOK_VALUE; } else { Set(to,WhiteRooks); Set(to,WhitePieces); PieceOnSquare(to)=rook; TotalWhitePieces+=rook_v; Material+=ROOK_VALUE; } break; /* ---------------------------------------------------------- | | | restore a captured queen. | | | ---------------------------------------------------------- */ case queen: Set(to,BishopsQueens); Set(to,RooksQueens); if (wtm) { Set(to,BlackQueens); Set(to,BlackPieces); PieceOnSquare(to)=-queen; TotalBlackPieces+=queen_v; Material-=QUEEN_VALUE; } else { Set(to,WhiteQueens); Set(to,WhitePieces); PieceOnSquare(to)=queen; TotalWhitePieces+=queen_v; Material+=QUEEN_VALUE; } break; /* ---------------------------------------------------------- | | | restore a captured king. [this is an error condition] | | | ---------------------------------------------------------- */ case king: printf("captured a king\n"); printf("piece=%d,from=%d,to=%d,captured=%d\n", piece,from,to,captured); printf("ply=%d\n",ply); if (log_file) DisplayChessBoard(log_file,search); } } #if defined(DEBUG) ValidatePosition(ply,move,"UnMakeMove(2)"); #endif return; }
/* ******************************************************************************** * * * Quiesce() is the recursive routine used to implement the alpha/beta * * negamax search (similar to minimax but simpler to code.) Quiesce() is * * called whenever there is no "depth" remaining so that only capture moves * * are searched deeper. * * * ******************************************************************************** */ int Quiesce(int alpha, int beta, int wtm, int ply) { register int initial_alpha, value, delta; register int *next_move; register int *goodmv, *movep, moves=0, *sortv, temp; /* ---------------------------------------------------------- | | | initialize. | | | ---------------------------------------------------------- */ if (ply >= MAXPLY-2) return(beta); nodes_searched++; next_time_check--; last[ply]=last[ply-1]; initial_alpha=alpha; /* ---------------------------------------------------------- | | | now call Evaluate() to produce the "stand-pat" score | | that will be returned if no capture is acceptable. | | if this score is > alpha, then we also have to save | | the "path" to this node as it is the PV that leads | | to this score. | | | ---------------------------------------------------------- */ value=Evaluate(ply,wtm,alpha,beta); if (value > alpha) { if (value >= beta) return(value); alpha=value; pv[ply].path_length=ply-1; pv[ply].path_hashed=0; pv[ply].path_iteration_depth=iteration_depth; } /* ---------------------------------------------------------- | | | generate captures and sort them based on (a) the value | | of the captured piece - the value of the capturing | | piece if this is > 0; or, (b) the value returned by | | Swap(). if the value of the captured piece won't | | bring the material score back up to near alpha, that | | capture is discarded as "futile." | | | ---------------------------------------------------------- */ last[ply]=GenerateCaptures(ply, wtm, last[ply-1]); delta=alpha-500-(wtm?Material:-Material); goodmv=last[ply-1]; sortv=sort_value; for (movep=last[ply-1];movep<last[ply];movep++) if (p_values[Captured(*movep)+7]+p_values[Promote(*movep)+7] >= delta) { if (Captured(*movep) == king) return(beta); if (p_values[Piece(*movep)+7] < p_values[Captured(*movep)+7] || (p_values[Piece(*movep)+7] <= p_values[Captured(*movep)+7] && delta<=0)) { *goodmv++=*movep; *sortv++=p_values[Captured(*movep)+7]; moves++; } else { temp=Swap(From(*movep),To(*movep),wtm); if (temp >= 0) { *sortv++=temp; *goodmv++=*movep; moves++; } } } /* ---------------------------------------------------------- | | | don't disdain the lowly bubble sort here. the list of | | captures is always short, and experiments with other | | algorithms are always slightly slower. | | | ---------------------------------------------------------- */ if (moves > 1) { register int done; register int *end=last[ply-1]+moves-1; do { done=1; sortv=sort_value; movep=last[ply-1]; for (;movep<end;movep++,sortv++) if (*sortv < *(sortv+1)) { temp=*sortv; *sortv=*(sortv+1); *(sortv+1)=temp; temp=*movep; *movep=*(movep+1); *(movep+1)=temp; done=0; } } while(!done); } next_move=last[ply-1]; /* ---------------------------------------------------------- | | | now iterate through the move list and search the | | resulting positions. | | | ---------------------------------------------------------- */ while (moves--) { current_move[ply]=*(next_move++); #if !defined(FAST) if (ply <= trace_level) SearchTrace(ply,0,wtm,alpha,beta,"quiesce",CAPTURE_MOVES); #endif MakeMove(ply,current_move[ply],wtm); value=-Quiesce(-beta,-alpha,ChangeSide(wtm),ply+1); UnMakeMove(ply,current_move[ply],wtm); if (value > alpha) { if(value >= beta) return(value); alpha=value; } } /* ---------------------------------------------------------- | | | all moves have been searched. return the search | | result that was found. if the result is not the | | original alpha score, then we need to return the PV | | that is associated with this score. | | | ---------------------------------------------------------- */ if (alpha != initial_alpha) { memcpy(&pv[ply-1].path[ply],&pv[ply].path[ply], (pv[ply].path_length-ply+1)*sizeof(int)); memcpy(&pv[ply-1].path_hashed,&pv[ply].path_hashed,3); pv[ply-1].path[ply-1]=current_move[ply-1]; } return(alpha); }
/* ******************************************************************************* * * * Interrupt() is used to read in a move when the operator types something * * while a search is in progress (during pondering as one example.) This * * routine reads in a command (move) and then makes two attempts to use this * * input: (1) call Option() to see if the command can be executed; (2) try * * InputMove() to see if this input is a legal move; If so, and we are * * pondering see if it matches the move we are pondering. * * * ******************************************************************************* */ void Interrupt(int ply) { int temp, i, left = 0, readstat, result, time_used; int save_move_number; TREE *const tree = block[0]; /* ************************************************************ * * * If trying to find a move to ponder, and the operator * * types a command, exit a.s.a.p. * * * ************************************************************ */ if (puzzling) abort_search = 1; /* ************************************************************ * * * First check to see if this is a command by calling * * Option(). Option() will return a 0 if it didn't * * recognize the command; otherwise it returns a 1 if * * the command was executed, or a 2 if we need to abort * * the search to execute the command. * * * ************************************************************ */ else do { readstat = Read(0, buffer); if (readstat <= 0) break; nargs = ReadParse(buffer, args, " ;"); if (nargs == 0) { Print(128, "ok.\n"); break; } if (strcmp(args[0], ".")) { save_move_number = move_number; if (!wtm) move_number--; if (root_wtm) Print(128, "Black(%d): %s\n", move_number, buffer); else Print(128, "White(%d): %s\n", move_number, buffer); move_number = save_move_number; } /* ************************************************************ * * * "." command displays status of current search. * * * ************************************************************ */ if (!strcmp(args[0], ".")) { if (xboard) { end_time = ReadClock(); time_used = (end_time - start_time); printf("stat01: %d ", time_used); printf(BMF " ", tree->nodes_searched); printf("%d ", iteration_depth); for (i = 0; i < n_root_moves; i++) if (!(root_moves[i].status & 128)) left++; printf("%d %d\n", left, n_root_moves); fflush(stdout); break; } else { end_time = ReadClock(); time_used = (end_time - start_time); printf("time:%s ", DisplayTime(time_used)); printf("nodes:" BMF "\n", tree->nodes_searched); DisplayTreeState(block[0], 1, 0, ply); } } /* ************************************************************ * * * "mn" command is used to set the move number to a * * specific value... * * * ************************************************************ */ else if (!strcmp("mn", args[0])) { if (nargs == 2) { move_number = atoi(args[1]); Print(128, "move number set to %d\n", move_number); } } /* ************************************************************ * * * "?" command says "move now!" * * * ************************************************************ */ else if (!strcmp(args[0], "?")) { if (thinking) { time_abort = 1; abort_search = 1; } } /* ************************************************************ * * * Next see if Option() recognizes this as a command. * * * ************************************************************ */ else { save_move_number = move_number; if (!analyze_mode && !wtm) move_number--; result = Option(tree); move_number = save_move_number; if (result >= 2) { if (thinking && result != 3) Print(128, "command not legal now.\n"); else { abort_search = 1; input_status = 2; break; } } else if ((result != 1) && analyze_mode) { abort_search = 1; input_status = 2; break; } /* ************************************************************ * * * Now, check to see if the operator typed a move. If * * so, and it matched the predicted move, switch from * * pondering to thinking to start the timer. If this * * is a move, but not the predicted move, abort the * * search, and start over with the right move. * * * ************************************************************ */ else if (!result) { if (pondering) { nargs = ReadParse(buffer, args, " ;"); temp = InputMove(tree, args[0], 0, Flip(root_wtm), 1, 1); if (temp) { if ((From(temp) == From(ponder_move)) && (To(temp) == To(ponder_move)) && (Piece(temp) == Piece(ponder_move)) && (Captured(temp) == Captured(ponder_move)) && (Promote(temp) == Promote(ponder_move))) { predicted++; input_status = 1; pondering = 0; thinking = 1; opponent_end_time = ReadClock(); program_start_time = ReadClock(); Print(128, "predicted move made.\n"); } else { input_status = 2; abort_search = 1; break; } } else if (!strcmp(args[0], "go") || !strcmp(args[0], "move") || !strcmp(args[0], "SP")) { abort_search = 1; break; } else Print(4095, "Illegal move: %s\n", args[0]); } else Print(4095, "unrecognized/illegal command: %s\n", args[0]); } } } while (1); if (log_file) fflush(log_file); }
static AstExpression PromoteArgument(AstExpression arg) { Type ty = Promote(arg->ty); return Cast(ty, arg); }