I am currently developing a chess engine in C++, and I am in the process of debugging my move generator. For this purpose, I wrote a simple perft() function:
int32_t Engine::perft(GameState game_state, int32_t depth)
{
int32_t last_move_nodes = 0;
int32_t all_nodes = 0;
Timer timer;
timer.start();
int32_t output_depth = depth;
if (depth == 0)
{
return 1;
}
std::vector<Move> legal_moves = generator.generate_legal_moves(game_state);
for (Move move : legal_moves)
{
game_state.make_move(move);
last_move_nodes = perft_no_print(game_state, depth - 1);
all_nodes += last_move_nodes;
std::cout << index_to_square_name(move.get_from_index()) << index_to_square_name(move.get_to_index()) << ": " << last_move_nodes << "\n";
game_state.unmake_move(move);
}
std::cout << "\nDepth: " << output_depth << "\nTotal nodes: " << all_nodes << "\nTotal time: " << timer.get_milliseconds() << "ms/" << timer.get_milliseconds()/1000.0f << "s\n\n";
return all_nodes;
}
int32_t Engine::perft_no_print(GameState game_state, int32_t depth)
{
int32_t nodes = 0;
if (depth == 0)
{
return 1;
}
std::vector<Move> legal_moves = generator.generate_legal_moves(game_state);
for (Move move : legal_moves)
{
game_state.make_move(move);
nodes += perft_no_print(game_state, depth - 1);
game_state.unmake_move(move);
}
return nodes;
}
It's results for the initial chess position (FEN: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1) for depths 1 and 2 match the results of stockfish's perft command, so I assume they are correct:
h2h3: 1
h2h4: 1
g2g3: 1
g2g4: 1
f2f3: 1
f2f4: 1
e2e3: 1
e2e4: 1
d2d3: 1
d2d4: 1
c2c3: 1
c2c4: 1
b2b3: 1
b2b4: 1
a2a3: 1
a2a4: 1
g1h3: 1
g1f3: 1
b1c3: 1
b1a3: 1
Depth: 1
Total nodes: 20
Total time: 1ms/0.001s
h2h3: 20
h2h4: 20
g2g3: 20
g2g4: 20
f2f3: 20
f2f4: 20
e2e3: 20
e2e4: 20
d2d3: 20
d2d4: 20
c2c3: 20
c2c4: 20
b2b3: 20
b2b4: 20
a2a3: 20
a2a4: 20
g1h3: 20
g1f3: 20
b1c3: 20
b1a3: 20
Depth: 2
Total nodes: 400
Total time: 1ms/0.001s
The results stop matching at depth 3, though:
Stockfish:
go perft 3
a2a3: 380
b2b3: 420
c2c3: 420
d2d3: 539
e2e3: 599
f2f3: 380
g2g3: 420
h2h3: 380
a2a4: 420
b2b4: 421
c2c4: 441
d2d4: 560
e2e4: 600
f2f4: 401
g2g4: 421
h2h4: 420
b1a3: 400
b1c3: 440
g1f3: 440
g1h3: 400
Nodes searched: 8902
My engine:
h2h3: 361
h2h4: 380
g2g3: 340
g2g4: 397
f2f3: 360
f2f4: 436
e2e3: 380
e2e4: 437
d2d3: 380
d2d4: 437
c2c3: 399
c2c4: 326
b2b3: 300
b2b4: 320
a2a3: 280
a2a4: 299
g1h3: 281
g1f3: 280
b1c3: 357
b1a3: 320
Depth: 3
Total nodes: 7070
Total time: 10ms/0.01s
I figured that my move generator was just buggy, and tried to track down the bugs by making a move the engine gives incorrect values for on the board and then calling perft() with depth = 2 on it to find out which moves are missing. But for all moves I tried this with, the engine suddenly starts to output the correct results I expected to get earlier!
Here is an example for the move a2a3:
When calling perft() on the initial position in stockfish, it calculates 380 subnodes for a2a3 at depth 3.
When calling perft() on the initial position in my engine, it calculates 280 subnodes for a2a3 at depth 3.
When calling perft() on the position you get after making the move a2a3 in the initial position in my engine, it calculates the correct number of total nodes at depth 2, 380:
h7h5: 19
h7h6: 19
g7g5: 19
g7g6: 19
f7f5: 19
f7f6: 19
e7e5: 19
e7e6: 19
d7d5: 19
d7d6: 19
c7c5: 19
c7c6: 19
b7b5: 19
b7b6: 19
a7a5: 19
a7a6: 19
g8h6: 19
g8f6: 19
b8c6: 19
b8a6: 19
Depth: 2
Total nodes: 380
Total time: 1ms/0.001s
If you have any idea what the problem could be here, please help me out. Thank you!
EDIT:
I discovered some interesting new facts that might help to solve the problem, but I don't know what to do with them:
For some reason, using std::sort() like this in perft():
std::sort(legal_moves.begin(), legal_moves.end(), [](auto first, auto second){ return first.get_from_index() % 8 > second.get_from_index() % 8; });
to sort the vector of legal moves causes the found number of total nodes for the initial position (for depth 3) to change from the wrong 7070 to the (also wrong) 7331.
When printing the game state after calling game_state.make_move() in perft(), it seems to have had no effect on the position bitboards (the other properties change like they are supposed to). This is very strange, because isolated, the make_move() method works just fine.
I'm unsure if you were able to pin down the issue but from the limited information available in the question, the best I can assume (and something I faced myself earlier) is that there is a problem in your unmake_move() function when it comes to captures since
Your perft fails only at level 3 - this is when the first legal capture is possible, move 1 and 2 can have no legal captures.
Your perft works fine when it's at depth 1 in the position after a2a3 rather than when it's searching at depth 3 from the start
This probably means that your unmake_move() fails at a depth greater than 1 where you need to restore some of the board's state that cannot be derived from just the move parameter you are passing in (e.g. enpassant, castling rights etc. before you made the move).
This is how you would like to debug your move generator using perft.
Given startpos as p1, generate perft(3) for your engine and sf. (you did that)
Now check any move that have different nodes, you pick a2a3. (you did that)
Given startpos + a2a3 as p2, generate perft(2) for your engine and sf. (you partially did this)
Now check any move that have different nodes in step 3. Let's say move x.
Given startpos + a2a3 + x as p3, generate perft(1) for your engine and sf.
Since that is only perft(1) by this time you will be able to figure out the wrong move or the missing move from your generator. Setup that last position or p3 on the board and see the wrong/missing moves from your engine compared to sf perft(1) result.
I try to draw a normal XY plot using a TChart (TeeChart) component in Embarcadero RAD Studio. When I add new points that have evenly spaced x values, e. g.
x: 1 2 3 4 5
y: 10 20 5 8 100
everything is drawn OK.
But when I add points that are unevenly spaced on the x axis, e. g.
x: 1 2 100 120 150
y: 10 20 5 8 100
the chart is drawn in such a way that the points still have the same distance between each other on the x axis. That is the distance between points 1-2 is the same as between 2-100. Is it possible to draw a proportional XY plot?
This is my sample code:
Series1->Add(10, 1);
Series1->Add(20, 2);
Series1->Add(5, 100);
Series1->Add(8, 120);
Series1->Add(100, 150);
The style of Series1 is Line.
Instead of calling Add, you need to call AddXY to add XY points.
I have been using a combination of different variables and data structures for this, I am not sure which is best in practice. I don't care much how long it takes to run these calculations, I just care about the end result.
I have a combination of values. V - W - X - Y : Z. V,W,X, and Y are used to calculate Z. V, W, X, and Y each have 256 different possibilities. So I believe I am working with a combination.
V - W - X - Y : Z
So long as all my values are the same, the order doesn't matter, I still get the same result.
0 - 52 - 115 - 249 : 0.059784
52 - 249 - 0 - 114 : 0.059784
249 - 52 - 114 - 0 : 0.059784
0 - 52 - 115 - 250 : 0.057423
0 - 250 - 115 - 52 : 0.057423
250 - 0 - 52 - 115 : 0.057423
0 - 52 - 115 - 251 : 0.055006
115 - 251 - 52 - 0 : 0.055006
251 - 0 - 52 - 115 : 0.055006
I need my end result to be a list of these values that are unique to Z. I don't really care which combination gets saved to achieve said unique value for Z. I just need to retain the value of Z which is a float (though in the end result I can store it as a string I suppose), and the values of V, W, X, Y.
So if I start with this:
250 - 0 - 52 - 115 : 0.057423
0 - 52 - 115 - 249 : 0.059784
0 - 52 - 115 - 250 : 0.057423
52 - 249 - 0 - 114 : 0.059784
0 - 52 - 115 - 251 : 0.055006
0 - 250 - 115 - 52 : 0.057423
251 - 0 - 52 - 115 : 0.055006
249 - 52 - 114 - 0 : 0.059784
115 - 251 - 52 - 0 : 0.055006
I would end with something similar to this:
250 - 0 - 52 - 115 : 0.057423
0 - 52 - 115 - 249 : 0.059784
115 - 251 - 52 - 0 : 0.055006
Any help/advice would be appreciated. Many thanks!
EDIT: Adding more to this to help decipher some issues.
So the idea of the entire project I am working on is to calculate every possible resistance for a quad digital potentiometer when you wire each of the four potentiometers in parallel. I may calculate series later, but for right now parallel is what I am after.
The potentiometer itself has an A terminal, B terminal, and Wiper terminal. We are looking for the resistance between the A terminal and the wiper terminal.
This is a 1k ohm 8 bit potentiometer. That means we have 256 steps of resolution to work with. To calculate the resistance for a certain step, we figure out:
trim = (((( (ohmRatingOfDigiPot)*(numberofPossibleSteps - stepToCalculate) )/numberofPOssileSteps)+wiperResistance)/1000);
So, our equation turns out to be
trim = (((( (1000)*(256-index) )/256)+50)/1000)
Where index is the step (out of 256 total steps) we are on.
So I figure out the possible values of each of these steps and I pass them into an array. One for each of the four potentiometers in the quad potentiometer.
for(int i = 0; i < 256; i++)
{
trim = (((( (1000)*(256-index) )/256)+50)/1000);
index++;
potOneSteps[i] = trim;
potTwoSteps[i] = trim;
potThreeSteps[i] = trim;
potFourSteps[i] = trim;
}
Yes, I know there is probably a much better way to do that. Like I said, it has been so long since I've used C++. This worked and I knew I had other things to worry about so it ended up staying. Ha.
So now I need to figure out every possible value for these four potentiometers in every different combination available. 4 potentiometers with 256 possible value for each? Bring on the for loops!
To calculate the resistance of resistors in parallel, we use:
Total Resistance = ( 1 / ( (1/R1) + (1/R2) + (1/R3) + ... + (1/Rn))
For our four arrays I figured this:
for (int A1W1 = 0; A1W1 < 256; A1W1++)
{
for (int A2W2 = 0; A2W2 < 256; A2W2++)
{
for (int A3W3 = 0; A3W3 < 256; A3W3++)
{
for (int A4W4 = 0; A4W4 < 256; A4W4++)
{
rTrim = (1/((1/potOneSteps[A1W1]) + (1/potTwoSteps[A2W2]) + (1/potThreeSteps[A3W3]) + (1/potFourSteps[A4W4])));
}
}
}
}
Very long. Very processor and memory intensive. Very poor. I know. I know.
This was the only way I could think to go through all of the values to do the calculations. Not sure if there is a better way, open to any and all suggestions.
So in my original post, V - W - Y - X correspond with the index where Z was calculated. And Z is the the actual resistance that we calculated using the values at said indices. Since all of the arrays are the same we get repeat values like I spoke about in the original post. So I only need to know the unique values for Z/rTrim and the step(s) at which it was found in order to have all of the possible values and be able to set the digital pots to said values.
I am open to any and all variables/data structures/etc for this. Nothing really has to be set to a certain type so I can deal with doubles, floats, ints, arrays, vectors, sets, linked lists (oh please no!), etc.
I hope that makes sense. Kind of long winded but I figured it would be beneficial to have all of the information available for what I am trying to go for. Many thanks!!
EDIT2:
I may have solved this problem with PHP and mySQL, my "native" languages. If you guys still want to lend a hand, I am all ears and willing to learn best practices, but don't feel obligated. Much appreciated for the help you did provide!
Edit :
As I suspected, your problem is not to remove duplicates from an already computed array of values. From what I have understood about your edit, you can simply compute the necessary values without duplicates in the first place.
For instance, imagine two loops in the range 0 to 3 like so :
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
someVectorOfPair.push_back(std::make_pair(i, j));
}
}
Here, you will get all the possible ordered pairs of values from 0 to 3. Now, you are looking for all the possible unordered pairs, so you consider (0, 1) and (1, 0) duplicates. This is the most straightforward way to do it :
for (int i = 0; i < 4; ++i) {
for (int j = i; j < 4; ++j) { // Only one character changed
someVectorOfPair.push_back(std::make_pair(i, j));
}
}
If you start j with the current value of i, you will not compute pairs (x, y) where y < x. However, you will get every other combinations, and it seems that it solves your problem.
Oh, by the way, (((( (1000)*(256-index) )/256)+50)/1000) can be written like so : (256.-i)/256. + 0.05. I did not understand everything in your calculations, but if i can go backward, you can simplify further : i/256. + 0.05. The point is here to do floating-point calculation if i is an int. You can also cast with static_cast<double> (or float).
Original :
How do you store your data ? How much data do you have ? How do you calculate Z ?
Without the answers to these questions, I can only present a very simple (but not efficient) way to reach your goal : copying your data into another "vector of vector" with duplicate checking. This is roughly how it is done (not tested) :
for (itD : data) {
itR = std::find_if(result.begin(), result.end(), [&itD](result::iterator it) { it.Z == itD.Z });
if (itR == result.end()) {
result.push_back(*itD);
}
}
Then again, this is not really efficient. You can also use a set (ordered or not) to store the result : this way, insertion automatically checks for duplicates (since a set have unique keys).
First, let's get the combinatorics right:
Given n items and d possibilities for each item, where n=4and d=256 here, we have:
Number of combinations if order is important: d ^ n and 256^4 is simply 2^32 for 4 x 8 = 32 bit
If order is not important, the formula is (n+d-1)! / (d! * (n-1!)) ... 2.862.209 ~ 3M distinct combinations in this case (a lot better that the 4G of the ordered combinations)
However, given the problem of n resistors (in parallel or serial), not only is the order not important, but even the combinations themselves are only important if they yield different sums.
The number of different sums (as opposed to their probabilities) is trivially (n * d) - (n - 1) (With the lowest sum 0 := 0 + 0 + 0 + 0 and the highest 1020 := 255 + ... + 255 for 1021 different sums in this case.
So, what you should do, expanding on the answer of #PaulMc, is to define operator< in terms of the sum of the 4 values, not their possibly unstable float result.
However, given your for loops from the question, you'd still insert into the set 4G(!) (256^4) times to get your 1021 slots filled.
So, if you already know that the order doesn't matter, fill the set in an efficient manner, and it doesn't matter if you get all combinations, you just need to capture all different sums:
Full example here / the loop:
using Combination = std::array<int, n>;
...
std::set<Combination> all;
Combination c{}; // must value initialize a std::array to zero-init its int values
for (int pos = 0; pos != n; ++pos) {
for (int i = 0; i != d; ++i) {
c[pos] = i; // c[0] from 0..d (remains at d), then c[1] from 0..d, etc.
all.insert(c);
}
}
You can do the following:
1) define a class that holds the 4 values and the floating point value.
2) Define an operator < for this class that compares the floating point value.
3) Define a std::set of this class and populate it.
Here is an example:
#include <set>
#include <vector>
#include <array>
#include <algorithm>
#include <iterator>
#include <ostream>
#include <iostream>
typedef std::vector<int> IntVector;
struct MyValueClass
{
IntVector m_vals;
double m_finalVal;
bool operator < (const MyValueClass& c2) const
{ return (m_finalVal < c2.m_finalVal); }
MyValueClass(const IntVector& v, double val) : m_vals(v), m_finalVal(val) {}
};
typedef std::set<MyValueClass> ValueClassSet;
using namespace std;
int main()
{
vector<MyValueClass> mc;
// Sample data
mc.push_back(MyValueClass(IntVector{ 250, 0, 52, 115 }, 0.057423));
mc.push_back(MyValueClass(IntVector{ 0, 52, 115, 249 }, 0.059784));
mc.push_back(MyValueClass(IntVector{ 0, 52, 115, 250 }, 0.057423));
mc.push_back(MyValueClass(IntVector{ 52, 249, 0, 114 }, 0.059784));
mc.push_back(MyValueClass(IntVector{ 0, 52, 115, 251 }, 0.055006));
mc.push_back(MyValueClass(IntVector{ 0, 250, 115, 52 }, 0.057423));
mc.push_back(MyValueClass(IntVector{ 251, 0, 52, 115 }, 0.055006));
mc.push_back(MyValueClass(IntVector{ 249, 52, 114, 0 }, 0.059784));
mc.push_back(MyValueClass(IntVector{ 115, 251, 52, 0 }, 0.055006));
// populate set with sample data from vector
ValueClassSet ms;
ValueClassSet::iterator it = ms.begin();
copy(mc.begin(), mc.end(), inserter(ms, it));
// output results
ValueClassSet::iterator it2 = ms.begin();
while (it2 != ms.end())
{
copy(it2->m_vals.begin(), it2->m_vals.end(), ostream_iterator<int>(cout, " "));
cout << " : " << it2->m_finalVal << "\n";
++it2;
}
}
Output:
0 52 115 251 : 0.055006
250 0 52 115 : 0.057423
0 52 115 249 : 0.059784
So basically, we populate a vector of your information, and then we populate the set with the vector. Since a std::set only stores unique values, only the unique Z items will be stored. The operator < that we set up for the MyValueClass type is what std::set will use to determine if an item is already in the set.
Note: I used C++11 initialization syntax to populate the vector. If you're using a compiler that is pre C++11, populate the vector the "old-fashioned way".
I have a list of simple coordinates (longitude, latitude pairs) like
110 30
-120 0
130 -30
0 30
and try to expand it to this:
110 30 110\272E 30\272N 110 30 LON0
-120 0 120\272W 0\272 -120 0 LON0
130 -30 130\272E 30\272S 130 -30 LON0
0 30 0\272 30\272N 0 30 LON0
Examining the first line:
110 30 110\272E 30\272N 110 30 LON0
110 30 The first two values just stay the same
110\272E the third value is basically the first value with an added (octal \272) degree symbol and an E for positive values or a W for negative values
30\272N similar to the third value, this is the latitude with an added degree symbol and a N for positive and a S for negative values.
110 30 is just a repetition of the first two values
LON0 is a fixed string for later replacement.
Things tried so far:
I played around with sed, but was unable to achieve anything remotely useful. I wasn't able to manipulate the matched values depending on them being negative or positive.
Any help is greatly appreciated.
All the best,
Chris
EDIT: #jaypal suggested to add different possible cases that can occur. Original was only one case with minor deviations in value.
EDIT2: Had to adjust the example data due to me not updating all values in the sample data. My apologies.
Can you use awk? It will be very easy:
$ cat file
110 30
-120 0
130 -30
0 30
awk '
function abs(x) {
x = x > 0 ? x : x * -1
return x
}
{
print abs($1),abs($2), ($1>0?abs($1)"\272E":$1==0?$1"\272":abs($1)"\272W"), ($2>0?abs($2)"\272N":$2==0?$2"\272":abs($2)"\272S"), abs($1), abs($2), "LON0"
}' file
110 30 110ºE 30ºN 110 30 LON0
120 0 120ºW 0º 120 0 LON0
130 30 130ºE 30ºS 130 30 LON0
0 30 0º 30ºN 0 30 LON0
If you want to print \272 instead of º just add another backslash to prevent it from interpolating. So modify the above script and use \\272 where ever you see \272.
We print the fields as you desire in your output and the following two syntax:
($1>0?$1"\272E":$1"\272W")
($2>0?$2"\272N":$2"\272S")
are ternary operators that checks for the positivity of the values. If first is positive use E else W. If second is positive use N else use S.
Update:
awk '
function abs(x) {
x = x > 0 ? x : x * -1
return x
}
{
print $1,$2,($1>0?$1"\\272E":$1==0?$1"\\272":abs($1)"\\272W"),($2>0?$2"\\272N":$2==0?$2"\\272":abs($2)"\\272S"),$1,$2, "LON0"
}' file
110 30 110\272E 30\272N 110 30 LON0
-120 0 120\272W 0\272 -120 0 LON0
130 -30 130\272E 30\272S 130 -30 LON0
0 30 0\272 30\272N 0 30 LON0
My application obtains x,y,z coordinates like this:
x: -0.020941
y: -0.241276
z: 0.956
--------------
x: 0.0782352
y: -0.159108
z: 0.923
--------------
x: 0.0665857
y: -0.140757
z: 0.885
--------------
x: 0.0485952
y: -0.0859762
z: 0.785
--------------
x: 0.04494
y: -0.0477933
z: 0.749
--------------
x: -0.183467
y: 0.0505905
z: 0.64
--------------
x: -0.0519514
y: -0.0137343
z: 0.627
--------------
x: -0.0630648
y: -0.0206495
z: 0.586
--------------
x: -0.0774924
y: -0.0189667
z: 0.569
--------------
x: 0.0100971
y: -0.0100971
z: 0.558
--------------
x: 0.00456857
y: -0.0126905
z: 0.533
--------------
x: 0.000491429
y: -0.00835429
z: 0.516
--------------
x: -0.0227838
y: -0.01018
z: 0.509
--------------
x: -0.0222133
y: -0.00589333
z: 0.476
--------------
x: -0.10161
y: -0.00850476
z: 0.47
--------------
x: -0.0775429
y: 0.0162095
z: 0.46
--------------
x: -0.0897705
y: 0.0219057
z: 0.451
--------------
What I'm doing is every 3 seconds I'm taking the smallest z value from this stream and plotting it's position within a 2d array (using it's x and y position)
my question is how do I plot this result as I don't know what the maximum or minimum score could be and they're all double type, not int for the index. Is there a 'nice' way of plotting this? I'd also like to be able to manipulate this data based on it's array index. e.g. find where data is clustering, etc.
It seems that your issue is a result of having your heart set on using an array. Using a vector would allow you to add grid points dynamically as well as averaging (and performing other calculations) whenever you wanted.
That being said, if you really want to use an array then like you have noticed you cannot use doubles and/or negative values as indices. You could work out a system by adding an offset and multiplying your x,y values to get rounded integers and a center not at (0,0) but maybe at (2,2) or something instead but this would be really unwieldy and confusing.