Here is a commented disassembly from the Rybka 1.0 Beta 64-bit version. The code is the iterative deepening at the end of root search.
Code: Select all
0x4095a5: mov $0x1,%esi # %esi will be equal to 1 throughout
0x4095aa: mov %esi,%ebx
0x4095b0: cmp $0x5,%ebx # compare depth to 5
0x4095b3: jb 0x4095c4 # if at least 5
0x4095b5: lea -0x2(%rbx),%edx then subtract 2 before...
0x4095b8: lea 0x25af79(%rip),%rcx # 0x664538 ["info depth" string]
0x4095bf: callq 0x40d0b0 # ...printing the "info depth" string
0x4095c4: mov %ebx,%ecx # copy depth to %ecx reg for func call
0x4095c6: movb $0x0,0x262e81(%rip) # 0x66c44e set "change" to false
0x4095cd: movb $0x0,0x262e78(%rip) # 0x66c44c set "bad_1" to false
0x4095d4: callq 0x40ba70 # call search_full_root(ecx) [ecx=depth]
0x4095d9: callq 0x4070c0 # some sort of update function
0x4095de: mov 0x26706f(%rip),%r11d # 0x670654, get score
0x4095e5: cmp $0xffff8300,%r11d # fiddle around
0x4095ec: jle 0x4095fe # ...
0x4095ee: cmp $0x7d00,%r11d # with mate scores
0x4095f5: movzbl 0x262e54(%rip),%edx # 0x66c450 load "flag"
0x4095fc: jl 0x409601 # if mate score,
0x4095fe: mov %sil,%dl # set "flag" to true (esi is always 1)
0x409601: cmp %esi,%ebx # compare depth (%ebx) to 1
0x409603: jne 0x409631 # if depth == 1
0x409605: cmpl $0x0,0x267058(%rip) # 0x670664 think this is RML[1]
0x40960c: je 0x40962f # if only one legal move, skip next
0x40960e: movzbl 0x262e3a(%rip),%ecx # 0x66c44f "easy"
0x409615: mov 0x267449(%rip),%eax # 0x670a64 (value of move 1)
0x40961b: add $0x96,%eax # EasyThreshold of 150 [as in Fruit]
0x409620: cmp %eax,0x26743a(%rip) # 0x670a60 (value of move 0)
0x409626: cmovae %esi,%ecx # if move values differ by enough
0x409629: mov %cl,0x262e20(%rip) # 0x66c44f set "easy" as true
0x40962f: cmp %esi,%ebx # if depth > 1
0x409631: jbe 0x40964d* [0x409647]
0x409633: movzbl 0x262e12(%rip),%eax # 0x66c44c load old bad_1
0x40963a: movb $0x0,0x262e0b(%rip) # 0x66c44c bad_1 = false
0x409641: mov %al,0x262e06(%rip) # 0x66c44d bad_2 = (previous) bad_1
0x40964d* movzbl %dl,%eax # %dl: 1@4095fe (mate), "flag"@4095f5
0x409650* mov %r11d,0x262df1(%rip) # 0x66c448 last_value = score
0x409647: cmp 0x262cdb(%rip),%ebx # 0x66c328 see if depth>=depth_limit
0x409657: cmovae %esi,%eax # if depth >= depth_limit
0x40965a: mov %al,0x262df0(%rip) # 0x66c450 then "flag" is true
0x409666: mov 0x262cb3(%rip),%r8d # 0x66c320 load SearchInput->time_limit_1
0x40966d: movzbl 0x262dd8(%rip),%r9d # 0x66c44d load bad_2
0x409660* callq *0x139ca(%rip) # 0x41d030 GetTickCount -> %eax
0x409675: mov %eax,%r11d
0x40967c* sub 0x262db5(%rip),%r11d # 0x66c438 (subtract StartTime)
0x409678: lea (%r8,%r8,1),%ecx # compute 3 * time_limit_1
0x409683: mov $0xaaaaaaab,%eax # then mult by 2/3
0x409688: mul %ecx # (result goes in edx with mul here)
0x40968a: shr %edx # and div by 2 ... hmm = time_limit_1 ?
0x40968c: cmp %edx,%r11d # compare to time taken
0x40968f: jb 0x4096a6 # if small, ignore next
0x409691: movzbl 0x262db8(%rip),%ecx # 0x66c450 "flag"
0x409698: test %r9b,%r9b # if "bad_2" is false
0x40969b: cmove %esi,%ecx # ecx = 1 (esi is always 1)
0x40969e: mov %cl,0x262dac(%rip) # 0x66c450 store ecx in "flag"
0x4096a4: jmp 0x4096ac
0x4096a6: mov 0x262da4(%rip),%cl # 0x66c450 (reload "flag")
0x4096ac: mov $0xaaaaaaab,%eax
0x4096b1: mul %r8d # mult time_limit_1 by 2/3
0x4096b4: shr $0x2,%edx # and div by 4
0x4096b7: cmp %edx,%r11d # compare to time taken
0x4096ba: jb 0x4096d1 # if small, ignore next
0x4096bc: cmpb $0x0,0x262d8c(%rip) # 0x66c44f see if "easy"
0x4096c3: movzbl %cl,%eax # if not "easy", then eax is "flag"
0x4096c6: cmovne %esi,%eax # if it is "easy", then eax is true
0x4096c9: mov %al,%cl
0x4096cb: mov %al,0x262d7f(%rip) # 0x66c450 store eax in "flag"
0x4096d1: shr %r8d # time_limit_1 divided by 2
0x4096d4: cmp %r8d,%r11d # compare to time taken
0x4096d7: jb 0x4096f3 # if small, ignore next
0x4096d9: test %r9b,%r9b # if "bad_2" is true
0x4096dc: jne 0x4096f3 # then ignore next
0x4096de: cmp %r9b,0x262d69(%rip) # 0x66c44e "change", see if false
0x4096e5: movzbl %cl,%eax # if not, then eax is "flag"
0x4096e8: cmove %esi,%eax # if "change" is false, eax is true
0x4096eb: mov %al,%cl
0x4096ed: mov %al,0x262d5d(%rip) # 0x66c450 store eax in "flag"
0x4096f3: cmpb $0x0,0x262d36(%rip) # 0x66c430 see if "stop" is true
0x4096fa: jne 0x409714 # if so, then exit this function
0x4096fc: test %cl,%cl # see if "flag" is true
0x4096fe: je 0x409709 # if so
0x409700: cmpb $0x0,0x262c25(%rip) # 0x66c32c and SearchInput->infinite
0x409707: je 0x409714 # is false, then exit this function
0x409709: add %esi,%ebx # increment depth (%esi is 1)
0x40970b: cmp $0x48,%ebx # if depth < 72
0x40970e: jb 0x4095b0 # then loop
The asterisks here denote instructions that I have re-ordered, typically when the ASM code starts laying the groundwork for the next high-level operation prior to the completion of the previous.
The corresponding code in Fruit 2.1 is at the end of
search in
search.cpp. Here it is with ASSERTs removed, slightly reformatted, and minor comments added:
Code: Select all
for (depth = 1; depth < DepthMax; depth++) // DepthMax is 64
{ if (DispDepthStart) send("info depth %d",depth); // DispDepthStart is true
SearchRoot->bad_1 = false;
SearchRoot->change = false;
board_copy(SearchCurrent->board,SearchInput->board);
if (UseShortSearch && depth <= ShortSearchDepth) // UseShortSearch is true
search_full_root(SearchRoot->list,SearchCurrent->board,depth,SearchShort);
else
search_full_root(SearchRoot->list,SearchCurrent->board,depth,SearchNormal);
search_update_current();
if (DispDepthEnd) send("[...]"); // a complicated construct, omitted here
if (depth >= 1) SearchInfo->can_stop = true;
if (depth == 1 && LIST_SIZE(SearchRoot->list) >= 2
&& LIST_VALUE(SearchRoot->list,0) >=
LIST_VALUE(SearchRoot->list,1) + EasyThreshold) // this is 150
SearchRoot->easy = true;
if (UseBad && depth > 1) // UseBad is true
{ SearchRoot->bad_2 = SearchRoot->bad_1;
SearchRoot->bad_1 = false; }
SearchRoot->last_value = SearchBest->value;
if (SearchInput->depth_is_limited && depth >= SearchInput->depth_limit)
SearchRoot->flag = true;
if (SearchInput->time_is_limited
&& SearchCurrent->time >= SearchInput->time_limit_1
&& !SearchRoot->bad_2)
SearchRoot->flag = true;
if (UseEasy && SearchInput->time_is_limited // UseEasy is true
&& SearchCurrent->time >= SearchInput->time_limit_1 * EasyRatio // 0.20
&& SearchRoot->easy)
SearchRoot->flag = true;
if (UseEarly && SearchInput->time_is_limited // UseEarly is true
&& SearchCurrent->time >= SearchInput->time_limit_1 * EarlyRatio // 0.60
&& !SearchRoot->bad_2 && !SearchRoot->change)
SearchRoot->flag = true;
if (SearchInfo->can_stop
&& (SearchInfo->stop || (SearchRoot->flag && !SearchInput->infinite)))
break;
}
Here is a translation of the Rybka 1.0 Beta code into a higher-level language, using the Fruit 2.1 code as a template.
Code: Select all
for (depth = 1; depth < 72; depth++)
{ if (depth >= 5) printf("info depth %d\n",depth-2);
change = false;
bad_1 = false; // order is switched from Fruit -- might be the compiler
search_full_root(depth); // yields "score" in a global var
some_sort_of_update_function();
if (score <= -32000 || score >= 32000) // mate scores
flag = true;
if (depth == 1 && RootMoveList[1].move != MOVE_NONE &&
RootMoveList[0].value >= RootMoveList[1].value + 150) // 409601-40962f
easy = true;
if (depth > 1) // 409631-409641
{bad_2 = bad_1;
bad_1 = false;}
last_value = score;
if (depth >= depth_limit) // 409647
flag = true;
TimeUsed = GetTickCount() - StartTime;
if ((3*time_limit_1)/3 <= TimeUsed && !bad_2) // 409691, has mult/div by 3
flag = true;
if ((time_limit_1)/6 <= TimeUsed && easy) // 20% in Fruit
flag = true;
if ((time_limit_1)/2 <= TimeUsed && !bad_2 && !change) // 60% in Fruit
flag = true;
if (stop || (flag && !SearchInput->infinite))
break;
}
There are indeed some differences (e.g. some numbers are modified, mate scores are included), but for comparison, I don't think I can get the Fruit 1.0 code to look quite so much like Fruit 2.1. One noteworthy Fruit/Rybka commonality is the use of
bad_1,
bad_2,
change,
easy, and
flag, which are not in Fruit 1.0.. Also, as noted in RYBKA_FRUIT, the 6 variables are stored in the same way in Rybka 1.0 Beta as with Fruit 2.1, again a strong sign of the "origins" of the former (a similar variable-ordering congruence can be noted with the previous code snippet).
Code: Select all
struct search_root_t {
int last_value; // 0x66c448
bool bad_1; // 0x66c44c
bool bad_2; // 0x66c44d
bool change; // 0x66c44e
bool easy; // 0x66c44f
bool flag; // 0x66c450
}
Here is the Fruit 1.0 code:
Code: Select all
for (depth = 1; depth < DepthMax; depth++) {
if (DispDepth) send("info depth %d",depth);
board_copy(SearchCurrent->board,SearchInput->board);
if (SearchType == SEARCH_PERFT) {
SearchCurrent->node_nb = 0;
SearchCurrent->leaf_nb = 0;
my_timer_reset(SearchCurrent->timer);
my_timer_start(SearchCurrent->timer);
search_perft(SearchCurrent->board,depth);
} else if (SearchType == SEARCH_MATE) {
search_mate_root(SearchRoot->list,SearchCurrent->board,depth);
} else if (SearchType == SEARCH_MAT) {
if (UseShortSearch && depth <= ShortSearchDepth) {
search_mat_root(SearchRoot->list,SearchCurrent->board,depth,SearchShort);
} else {
search_mat_root(SearchRoot->list,SearchCurrent->board,depth,SearchNormal);
}
} else if (SearchType == SEARCH_FULL) {
if (UseShortSearch && depth <= ShortSearchDepth) {
search_full_root(SearchRoot->list,SearchCurrent->board,depth,SearchShort);
} else {
search_full_root(SearchRoot->list,SearchCurrent->board,depth,SearchNormal);
}
}
search_update_current();
if (DispDepth) {
send("info depth %d nodes " S64_FORMAT " time %.0f",depth,SearchCurrent->node_nb,SearchCurrent->time*1000.0);
}
SearchInfo->can_stop = true;
if ((SearchInput->depth_is_limited && depth >= SearchInput->depth_limit)
|| (SearchInfo->can_stop && SearchInput->time_is_limited && SearchCurrent->time >= SearchInput->time_limit * TimeRatio)) {
break;
}
}
Except for this last piece of Fruit 1.0 code, this all appears in Appendix A of RYBKA_FRUIT, and in a more readily readable format therein I would say.
This code is quite significant for probative purposes, and also has some substantial nature, in that it (e.g.) determines on what basis to start another search iteration.