- cerr << "\n==========================="
- << "\nTotal time (ms) : " << elapsed
- << "\nNodes searched : " << nodes
- << "\nNodes/second : " << 1000 * nodes / elapsed << endl;
+ // The coefficients of a third-order polynomial fit is based on the fishtest data
+ // for two parameters that need to transform eval to the argument of a logistic
+ // function.
+ constexpr double as[] = { 0.38036525, -2.82015070, 23.17882135, 307.36768407};
+ constexpr double bs[] = { -2.29434733, 13.27689788, -14.26828904, 63.45318330 };
+
+ // Enforce that NormalizeToPawnValue corresponds to a 50% win rate at ply 64
+ static_assert(UCI::NormalizeToPawnValue == int(as[0] + as[1] + as[2] + as[3]));
+
+ double a = (((as[0] * m + as[1]) * m + as[2]) * m) + as[3];
+ double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3];
+
+ // Transform the eval to centipawns with limited range
+ double x = std::clamp(double(v), -4000.0, 4000.0);
+
+ // Return the win rate in per mille units, rounded to the nearest integer
+ return int(0.5 + 1000 / (1 + std::exp((a - x) / b)));