Here's something to start with

Discussion of anything and everything relating to chess playing software and machines.

Moderators: hgm, Rebel, chrisw

kranium
Posts: 2129
Joined: Thu May 29, 2008 10:43 am

Here's something to start with

Post by kranium »

In May of this year, some compelling information was released...here in this thread:
http://64.68.157.89/forum/viewtopic.php ... elka+rybka

please read carefully post # 9 on the 2nd page. The engineer who produced this data had never seen the Fruit 2.1 source code, and was working to prove that Stelka was a clone of Rybka. He was a Rybka customer, and an active forum poster, and even communicated with Vas on occasion.

In order to accomplish this he examined rybka 1.0 beta executable with a microscope. He used a disaasember and produced the assembly language from the binary's machine language.

the section of code he chose to post is a particulary critical part of the program, where search variables are initialized, the time management routine is run, and search is launched. He converted everything he saw to ANSI C. (Bob will argue with me on that). the process took more than 4 hours and only a very skilled engineer with lots of assembly and C experience is capable of accomplishing this.

the result was a copy of the rybka void start_go (char string []) function in C for the world to see. He did the same with Strelka, compared them, and posted the results. These two code fragments are near identical (and equivilent).

now, please take a close look at the the rybka 1.0 beta C function start_go() that was presented, and compare it line for line with Fruit 2.1 parse_go function (found in protocol.cpp). there are many many similarities, many exact. Even F. Bluemers and I remarked at the time, posted in the thread, that we recognized the code immediately.

as an alternative to this 'manual' comparison process, i spent quite a bit of time today preparing an excel spreadsheet which places each source side by side, with numbered lines for ease of comparison.

the document is quite wide, i'm not sure it will post here in any kind of neat and readable manner, but i have produced it because that's what was requested.

lastly, the accusations did not begin without a shread of evidence as some have alleged. suspicions by many programmers have abounded for years, the Strelka controversy produced the Strelka source code which Vas immediately claimed as Rybka.

this made a lot of people think... then I posted this document outlining the many many (often exact) similarites between fruit and strelka:
http://64.68.157.89/forum/viewtopic.php ... elka+fruit

it was at that time that some started to speak up. i just ask everyone to read and consider these documents carefully, and make the comparison...with an open mind.

and please give Zach, and others, some time to produce the web site and further info...and give time to Vas to answer the questions that have been posed.

Norm
kranium
Posts: 2129
Joined: Thu May 29, 2008 10:43 am

Re: Here's something to start with

Post by kranium »

one typo, correction, void start_go (char string []) clearly takes one parameter...the addition of void was inadvertent.
kranium
Posts: 2129
Joined: Thu May 29, 2008 10:43 am

Re: Here's something to start with

Post by kranium »

0 Fruit 2.1 Rybka 1.0 beta 0
1 static void parse_go( char string [] ) void start_go( char string [] ) 1
2 { { 2
3 3
4 const char* ptr; const char* ptr; 4
5 bool infinite, ponder; bool infinite, ponder; 5
6 6
7 double binc, btime, movetime, winc, wtime; int binc, btime, movetime, winc, wtime; 7
8 int depth, mate, movestogo; int movestogo; 8
9 9
10 // the following 4 variables are declared in Rybka, line 142 10
11 double time, inc; 11
12 double time_max, alloc; 12
13 // the preceding 4 variables are declared in Rybka, line 142 13
14 TimeDouble = 0.0; 14
15 // these vars are declared in search.h search_input_t struct 15
16 double time_limit_1; time_limit_1 = -1; 16
17 double time_limit_2; time_limit_2 = -1; 17
18 int depth_limit; depthLimit = 0x7FFFFFFF; 18
19 19
20 20
21 // some of the following vars are initiated outside of parse_go in Fruit's 21
22 search.cpp search_clear function. 22
23 ignoreClockFlag = 0; 23
24 24
25 stopSearch = 0; 25
26 26
27 depth = -1; nodeTickLow = 1024; 27
28 mate = -1; 28
29 nodes = -1; bestMove = 0; 29
30 moveScore = 0; 30
31 depthScore = 0; 31
32 32
33 bad_1 = 0; 33
34 bad_2 = 0; 34
35 change = 0; 35
36 easy = 0; 36
37 clackFlag = 0; 37
38 38
39 nodeTickHigh = 1; 39
40 40
41 41
42 42
43 43
44 44
45 infinite = false; infinite = 0; 45
46 ponder = false; ponder = 0; 46
47 movestogo = -1; movestogo = 25; 47
48 winc = -1.0; winc = 0; 48
49 wtime = -1.0; wtime = 0; 49
50 binc = -1.0; binc = 0; 50
51 btime = -1.0; btime = 0; 51
52 movetime = -1.0; movetime = 0; 52
53 53
54 ptr = strtok(string, " "); ptr = strtok(string, " "); 54
55 55
56 for ( ptr = strtok(NULL, " "); ptr != NULL; ptr = strtok(NULL, " ") ) for ( ptr = strtok(NULL, " "); ptr != NULL; ptr = strtok(NULL, " ") ) 56
57 { { 57
58 if (false) 58
59 { 59
60 } 60
61 else if (string_equal(ptr, "binc")) if (!strcmp(ptr, "binc")) 61
62 { { 62
63 ptr = strtok(NULL, " "); ptr = strtok(NULL, " "); 63
64 if (ptr == NULL) my_fatal("parse_go(): missing argument\n"); //fruit does error checking here 64
65 binc = double(atoi(ptr)) / 1000.0; binc = atoi(ptr); 65
66 } } 66
67 else if (string_equal(ptr, "btime")) else if (!strcmp(ptr, "btime")) 67
68 { { 68
69 ptr = strtok(NULL, " "); ptr = strtok(NULL, " "); 69
70 if (ptr == NULL) my_fatal("parse_go(): missing argument\n"); //fruit does error checking here 70
71 btime = double(atoi(ptr)) / 1000.0; btime = atoi(ptr); 71
72 } } 72
73 else if (string_equal(ptr, "depth")) else if (!strcmp(ptr, "depth")) 73
74 { { 74
75 ptr = strtok(NULL, " "); ptr = strtok(NULL, " "); 75
76 if (ptr == NULL) my_fatal("parse_go(): missing argument\n"); //fruit does error checking here 76
77 //unlike fruit, here 2 is added to search depth 77
78 depth = atoi(ptr); if ((atoi(ptr) + 2) < -1) depthLimit = -1; 78
79 } } 79
80 else 80
81 { 81
82 //unlike fruit, here 2 is added to search depth 82
83 depthLimit = atoi(ptr) + 2; 83
84 } 84
85 85
86 else if (string_equal(ptr, "infinite")) else if (!strcmp(ptr, "infinite")) 86
87 { { 87
88 infinite = true; infinite = 1; 88
89 } } 89
90 else if (string_equal(ptr, "mate")) 90
91 { 91
92 ptr = strtok(NULL, " "); 92
93 if (ptr == NULL) my_fatal("parse_go(): missing argument\n"); 93
94 mate = atoi(ptr); 94
95 } 95
96 else if (string_equal(ptr, "movestogo")) else if (!strcmp(ptr, "movestogo")) 96
97 { { 97
98 ptr = strtok(NULL, " "); ptr = strtok(NULL, " "); 98
99 if (ptr == NULL) my_fatal("parse_go(): missing argument\n"); //fruit does error checking here 99
100 movestogo = atoi(ptr); movestogo = atoi(ptr); 100
101 } 101
102 // the following block of code can be found in Fruit at line 169 102
103 if (movestogo > 30) 103
104 movestogo = 30; 104
105 105
106 if (movestogo < 1) 106
107 movestogo = 1; 107
108 // the preceeding block of code can be found in Fruit at line 169 108
109 } 109
110 110
111 else if (string_equal(ptr, "movetime")) else if (!strcmp(ptr, "movetime")) 111
112 { { 112
113 ptr = strtok(NULL, " "); ptr = strtok(NULL, " "); 113
114 if (ptr == NULL) my_fatal("parse_go(): missing argument\n"); //fruit does error checking here 114
115 movetime = double(atoi(ptr)) / 1000.0; movetime = atoi(ptr); 115
116 } } 116
117 else if (string_equal(ptr, "nodes")) 117
118 { 118
119 ptr = strtok(NULL, " "); 119
120 if (ptr == NULL) my_fatal("parse_go(): missing argument\n"); 120
121 nodes = my_atoll(ptr); 121
122 } 122
123 else if (string_equal(ptr, "ponder")) else if (!strcmp(ptr, "ponder")) 123
124 { { 124
125 ponder = true; ponder = 1; 125
126 } } 126
127 else if (string_equal(ptr, "searchmoves")) 127
128 { 128
129 } 129
130 else if (string_equal(ptr, "winc")) else if (!strcmp(ptr, "winc")) 130
131 { { 131
132 ptr = strtok(NULL, " "); ptr = strtok(NULL, " "); 132
133 if (ptr == NULL) my_fatal("parse_go(): missing argument\n"); //fruit does error checking here 133
134 winc = double(atoi(ptr)) / 1000.0; winc = atoi(ptr); 134
135 } } 135
136 else if (string_equal(ptr, "wtime")) else if (!strcmp(ptr, "wtime")) 136
137 { { 137
138 ptr = strtok(NULL, " "); ptr = strtok(NULL, " "); 138
139 if (ptr == NULL) my_fatal("parse_go(): missing argument\n"); //fruit does error checking here 139
140 wtime = double(atoi(ptr)) / 1000.0; wtime = atoi(ptr); 140
141 } } 141
142 } } 142
143 // the following 4 variable declarations are found in Fruit, line 10 143
144 search_clear(); int time, inc, time_max, alloc; 144
145 // the preceding 4 variable declarations are found in Fruit, line 10 145
146 if (depth >= 0) 146
147 { 147
148 SearchInput->depth_is_limited = true; 148
149 SearchInput->depth_limit = depth; 149
150 } 150
151 else if (mate >= 0) 151
152 { 152
153 SearchInput->depth_is_limited = true; 153
154 SearchInput->depth_limit = mate * 2 - 1; 154
155 } 155
156 156
157 if (COLOUR_IS_WHITE(SearchInput->board->turn)) if ((Board->turn) == 0) 157
158 { { 158
159 time = wtime; time = wtime; 159
160 inc = winc; inc = winc; 160
161 } } 161
162 else else 162
163 { { 163
164 time = btime; time = btime; 164
165 inc = binc; inc = binc; 165
166 } } 166
167 167
168 //the following code is found in Rybka, line 103 168
169 if (movestogo <= 0 || movestogo > 30) 169
170 movestogo = 30; 170
171 //the preceding code is found in Rybka, line 103 171
172 172
173 if (inc < 0.0) inc = 0.0; 173
174 174
175 175
176 if (movetime >= 0.0) if (movetime >= 0.0) 176
177 { { 177
178 SearchInput->time_is_limited = true; 178
179 SearchInput->time_limit_1 = movetime * 5.0; time_limit_1 = 5 * movetime; 179
180 SearchInput->time_limit_2 = movetime; time_limit_2 = 1000 * movetime; 180
181 } } 181
182 else if (time >= 0.0) else if (time > 0) 182
183 { { 183
184 time_max = time * 0.95 - 1.0; time_max = time - 5000; 184
185 185
186 if (time_max < 0.0) time_max = 0.0; 186
187 187
188 SearchInput->time_is_limited = true; 188
189 189
190 alloc = (time_max + inc * double(movestogo - 1)) / double(movestogo); alloc = (time_max + inc * (movestogo - 1)) / movestogo; 190
191 alloc *= (option_get_bool("Ponder") ? PonderRatio : NormalRatio); //ponder is not a UCI option in Rykba 1.0 191
192 192
193 if (alloc > time_max) alloc = time_max; if (alloc >= time_max) alloc = time_max; 193
194 SearchInput->time_limit_1 = alloc; time_limit_1 = alloc; 194
195 195
196 alloc = (time_max + inc * double(movestogo - 1)) * 0.5; alloc = (time_max + inc * (movestogo - 1)) / 2; 196
197 197
198 if (alloc < SearchInput->time_limit_1) alloc = SearchInput->time_limit_1; if (alloc < time_limit_1) alloc = time_limit_1; 198
199 199
200 if (alloc > time_max) alloc = time_max; if (alloc > time_max) alloc = time_max; 200
201 SearchInput->time_limit_2 = alloc; time_limit_2 = alloc; 201
202 } } 202
203 203
204 if (infinite || ponder) SearchInput->infinite = true; if (infinite || ponder) ignoreClockFlag = 1; 204
205 205
206 Searching = true; Searching = 1; 206
207 Infinite = infinite || ponder; Infinite = 0; if (infinite || ponder) Infinite = 1; 207
208 Delay = false; Delay = 0; 208
209 209
210 search(); start_search(); 210
211 search_update_current(); 211
212 212
213 Searching = false; Searching = 0; 213
214 Delay = Infinite; Delay = Infinite; 214
215 215
216 if (!Delay) if (!Delay) 216
217 send_best_move(); Send_Best_Move(); 217
218 } } 218
219 219
220 220


shit, I know it's not great, the two spreadsheet columns crunch together...
but it's readable....

fruit 2.1 is followed directly by rybka 1.0 beta
the ';' is the delimiter between fruit/rybka
the spreadsheet has line numbers on each side...one at the beginneing of the line, one at the end.


sorry is a bit messy, it's the best i could do in a short time...
Alexander Schmidt
Posts: 1204
Joined: Thu May 10, 2007 2:49 pm

Re: Here's something to start with

Post by Alexander Schmidt »

TY Norman, this is completely convincing. I have not much programming experience (mainly vba) but this are too many similaries for a coincidence.

Unfortunately people will not read it and not believe it...
kranium
Posts: 2129
Joined: Thu May 29, 2008 10:43 am

Re: Here's something to start with

Post by kranium »

Please note that:

Fruits ASSERTs have been removed
comments have been removed

there are differences between the two...mainly:
where Fruit has TRUE, Rybka has 1
where Fruit has FALSE, Rybka has 0

where Fruit has double data types, Rybka uses integer
search->info struct has been replaced with a direct reference to the independant variable.

truthfully many of the differences reflect the implementation of backend C bitboard routines in place of object-oriented C++ class references

Fruit often does error checking that is absent in Rybka,
if (ptr == NULL) my_fatal("parse_go(): missing argument\n");

Another difference:
Fruit calls string_equal to accept input, whereas Rybka uses the C function:
int strcmp( const char *string1, const char *string2 )

but these functions are essentially equivilent...

string_equal is simply a Fabien re-write with ASSERTs included:

string_equal( const char s1 [], const char s2 [] )
{
ASSERT(s1 != NULL);
ASSERT(s2 != NULL);
return strcmp(s1, s2) == 0;
}

which is something he did often...see Fruit 2.1 util.cpp

one click...a global search and replace (for ex: replace true with 1, would recifiy some of the larger differences).
Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: Here's something to start with

Post by Sven »

Norman,

on the left side you show Fruit 2.1 code. What is at the right side? "start_go" is the name of a Strelka 2.0 function. Are you showing Strelka 2.0, or one of many possible Rybka C sources after decompiling Rybka 1.0 beta and choosing a mixture of Fruit and Strelka identifiers?
10 // the following 4 variables are declared in Rybka, line 142 10
11 double time, inc; 11
12 double time_max, alloc; 12
Another question: since you don't have the Rybka source, how can you know about the location and order of variable declarations in Rybka? Declarations of local variables often do not produce assembler code, you just see the variables when they are used.

I do not yet understand the process of your comparison.

Besides that, I hope everyone agrees that we are looking at code that is more of a trivial kind in the context of a chess engine. The only interesting part here is the time management code, and there I see some differences in implementation.

So to sum up, I think that we do not see a GPL violation here even if ideas were copied to some extent.

Analysis of the search of Fruit 2.1 and Rybka 1.0 beta seems to be much more interesting stuff for me.

Sven
bob
Posts: 20943
Joined: Mon Feb 27, 2006 7:30 pm
Location: Birmingham, AL

Re: Here's something to start with

Post by bob »

Sven Schüle wrote:Norman,

on the left side you show Fruit 2.1 code. What is at the right side? "start_go" is the name of a Strelka 2.0 function. Are you showing Strelka 2.0, or one of many possible Rybka C sources after decompiling Rybka 1.0 beta and choosing a mixture of Fruit and Strelka identifiers?
10 // the following 4 variables are declared in Rybka, line 142 10
11 double time, inc; 11
12 double time_max, alloc; 12
Another question: since you don't have the Rybka source, how can you know about the location and order of variable declarations in Rybka? Declarations of local variables often do not produce assembler code, you just see the variables when they are used.

I do not yet understand the process of your comparison.

Besides that, I hope everyone agrees that we are looking at code that is more of a trivial kind in the context of a chess engine. The only interesting part here is the time management code, and there I see some differences in implementation.

So to sum up, I think that we do not see a GPL violation here even if ideas were copied to some extent.

Analysis of the search of Fruit 2.1 and Rybka 1.0 beta seems to be much more interesting stuff for me.

Sven
As a general rule, there are two types of data. That which is declared as "static" (which included global data), and that which is local to a procedure.

static/global variables are enumerated in memory, roughly as they are declared. Some compilers will order them such that all the quadword (8byte) values are first, followed by doublewords, then words and bytes. Some just lay them out in memory as they are declared.

local variables are allocated on the stack. And the same general rule is followed, except now you generally will see lots of [ebp + xxx] type references rather than actual variable names. With some effort, you can work on understanding the assembly language, and begin to decode what each variable must represent and then assign useful names to make reading easier.

If you look at the order of the variables as they either appear in memory or on the stack, you can almost infer the order they were declared.
User avatar
tiger
Posts: 819
Joined: Sat Mar 11, 2006 3:15 am
Location: Guadeloupe (french caribbean island)

Readable side-by-side listings here

Post by tiger »

I have managed to format your source code comparison but it cannot be posted as code directly in a message, so you can just click on this link to see the side-by-side listings:

http://pagesperso-orange.fr/ct_chess/Fr ... rt_go.html



// Christophe
Tony

Re: Here's something to start with

Post by Tony »

kranium wrote:Please note that:

Fruits ASSERTs have been removed
comments have been removed

there are differences between the two...mainly:
where Fruit has TRUE, Rybka has 1
where Fruit has FALSE, Rybka has 0
Actually, that is only in the Fruit source.
I'm pretty sure if you compile it and then disassemble it, Fruit also has 1 and 0

:)

EDIT you assumed Rybka has type integer, but since the values are only 0 and 1, you could have used boolean, ( or enum for that matter)

Tony
Last edited by Tony on Wed Aug 27, 2008 11:13 am, edited 1 time in total.
User avatar
tiger
Posts: 819
Joined: Sat Mar 11, 2006 3:15 am
Location: Guadeloupe (french caribbean island)

Re: Here's something to start with

Post by tiger »

Alexander Schmidt wrote:TY Norman, this is completely convincing. I have not much programming experience (mainly vba) but this are too many similaries for a coincidence.

Unfortunately people will not read it and not believe it...


Let's try a more readable format:

http://pagesperso-orange.fr/ct_chess/Fr ... rt_go.html



// Christophe