OK, that sounds like a brilliant idea and a sensible application. Corona, yes ... a difficult time, even for me. OK, you've got me convinced, I'll have a look at that bloody Discord after all. Next week with more peace and quiet. I hope the chess programmers don't discover me, otherwise everyone will want to have a bone to pick with me ... because of the "move-average" trauma, you know. I think Conor is one of those people who doesn't like me and has it in for me.
Must do that in secret mission!
Hope Conor will not read it.
About Obsidian
Moderators: hgm, Rebel, chrisw
-
- Posts: 6831
- Joined: Wed Nov 18, 2009 7:16 pm
- Location: Gutweiler, Germany
- Full name: Frank Quisinsky
-
- Posts: 855
- Joined: Thu Aug 11, 2022 11:30 pm
- Full name: Esmeralda Pinto
Re: About Obsidian
Many thanks for this great work.mvanthoor wrote: ↑Fri Jan 26, 2024 2:43 pm... This was one of the big reasons for writing Rustic, so I would be able to also write https://rustic-chess.org/. ...Gabor Szots wrote: ↑Fri Jan 26, 2024 1:57 pm It's been my opinion for a long time now that if a very strong engine pops up, developed for some time then abandoned, then it is a clone (sorry, a derivative) with nothing original in it. The author simply abandons it because he has implemented everything there was for the taking and has no ideas of his own.
-
- Posts: 5
- Joined: Fri Nov 17, 2023 2:50 am
- Location: USA
- Full name: Jasper Sinclair
Re: About Obsidian
It's certainly seems possible to me that the author has simply:Gabor Szots wrote: ↑Fri Jan 26, 2024 1:57 pmI very much agree with all this. I would have written the same if I could find the appropriate words.mvanthoor wrote: ↑Fri Jan 26, 2024 1:14 pmThis engine posted by Gabor has the same main sources:
But the link to the engine programming discord doesn't work.Gabor Szots wrote: ↑Thu Jan 25, 2024 4:05 pm New engine Molybdenum
rn5f107s2, Germany
https://github.com/rn5f107s2/Molybdenum ... s/tag/v2.0
Even so, I can't believe that there are people who go: "Let's write a chess engine!" Then: "Let's hang around in the Discord and Engine Programming Channels for a few weeks!" Then, 200 commits and a fart later, another 3500 engine drops.
That just feels wrong to me. Either I'm exceedingly stupid because I can't do that, or many others are just (re)writing code without exactly understanding what it does and why. My challenge would then be: if I gave you a text editor, a compiler of your language of choice and your own knowledge and notes (so NOT the source code of your previous engine), could you do it _again_? If not, then... well... you either didn't understand, or you didn't document and forgot.
There are SO many engines that just list a bunch of features and pop out new versions every other day without even listing a changelog. Those are useless to learn from, even if open source. The same goes for "I've been writing a chess engine for the last few weeks and *pop!* here's your new 3500 Elo engine! Enjoy, bye!" And the creator was never seen again. (And may not even have been seen before.)
I ignore all of them. The only new open source engines I follow are the ones for which I can see the beginnings, that document their progress and what they are doing, and why.
It's been my opinion for a long time now that if a very strong engine pops up, developed for some time then abandoned, then it is a clone (sorry, a derivative) with nothing original in it. The author simply abandons it because he has implemented everything there was for the taking and has no ideas of his own.
lost interest
a family matter intervenes
...or any one of a million other possible life issues that routinely occur, does in fact occur.
But to immediately categorizing it it as a 'clone' because of that?
wow...
-
- Posts: 5
- Joined: Fri Nov 17, 2023 2:50 am
- Location: USA
- Full name: Jasper Sinclair
Re: About Obsidian
Dimitri has done no wrong? If the Obsidian author wishes to prevent future posts like this one...Ras wrote: ↑Fri Jan 26, 2024 1:34 pmNeed to compare what with what? The source code? The move output? What do you even mean? Obviously, SF16 is using the network, as seen in my posting.DmitriyFrosty wrote: ↑Fri Jan 26, 2024 1:24 pmYou need compare it to Stockfish with pure nnue evaluation (when he uses one network)
Also, since you started the thread: give evidence. Both Obsidian and SF16 are on Github. Give specific links to specific files with specific line numbers.
I recommend he include an original solution for things like operational macros, instead of what's included in his current source code.
See lines 200-230
https://github.com/gab8192/Obsidian/blo ... an/types.h
-
- Posts: 58
- Joined: Mon Mar 27, 2023 8:29 pm
- Full name: Dmitry Frosty
Re: About Obsidian
With source code. At the start, look at search.cpp files of both engines.Ras wrote: ↑Fri Jan 26, 2024 1:34 pmNeed to compare what with what? The source code? The move output? What do you even mean? Obviously, SF16 is using the network, as seen in my posting.DmitriyFrosty wrote: ↑Fri Jan 26, 2024 1:24 pmYou need compare it to Stockfish with pure nnue evaluation (when he uses one network)
Also, since you started the thread: give evidence. Both Obsidian and SF16 are on Github. Give specific links to specific files with specific line numbers.
-
- Posts: 2582
- Joined: Tue Aug 30, 2016 8:19 pm
- Full name: Rasmus Althoff
Re: About Obsidian
Not sure how much you know about chess engines, but convenience macros are just about the least important work in that, plus that they're attributed. This is in no way indicating an SF clone. Look at the actually important parts that make a chess engine, such as the search algo and eval.jasper.sinclair wrote: ↑Sun Jan 28, 2024 11:12 pmI recommend he include an original solution for things like operational macros
Specific line numbers. Not some vague hints.DmitriyFrosty wrote: ↑Mon Jan 29, 2024 8:17 amWith source code. At the start, look at search.cpp files of both engines.
Rasmus Althoff
https://www.ct800.net
https://www.ct800.net
-
- Posts: 58
- Joined: Mon Mar 27, 2023 8:29 pm
- Full name: Dmitry Frosty
Re: About Obsidian
Obsidian. types.hRas wrote: ↑Mon Jan 29, 2024 8:22 amNot sure how much you know about chess engines, but convenience macros are just about the least important work in that, plus that they're attributed. This is in no way indicating an SF clone. Look at the actually important parts that make a chess engine, such as the search algo and eval.jasper.sinclair wrote: ↑Sun Jan 28, 2024 11:12 pmI recommend he include an original solution for things like operational macros
Specific line numbers. Not some vague hints.DmitriyFrosty wrote: ↑Mon Jan 29, 2024 8:17 amWith source code. At the start, look at search.cpp files of both engines.
#include <algorithm>
#include <chrono>
#include <cstdint>
#include <cstring>
#include <iostream>
#include <nmmintrin.h>
#include <thread>
const std::string engineVersion = "dev-10.14";
using Key = uint64_t;
using Bitboard = uint64_t;
using TbResult = uint32_t;
using Score = int;
const std::string piecesChar = " PNBRQK pnbrqk";
constexpr int MAX_PLY = 246;
constexpr int MAX_MOVES = 224; // 32*7
#define BitCount(x) _mm_popcnt_u64(x)
inline void sleep(int millis) {
std::this_thread::sleep_for(std::chrono::milliseconds(millis));
}
inline int64_t timeMillis() {
auto sinceEpoch = std::chrono::steady_clock::now().time_since_epoch();
return std::chrono::duration_cast<std::chrono::milliseconds>(sinceEpoch).count();
}
constexpr Score
SCORE_DRAW = 0,
SCORE_MATE = 32000,
SCORE_INFINITE = 32001,
SCORE_NONE = 32002,
SCORE_MATE_IN_MAX_PLY = SCORE_MATE - MAX_PLY,
SCORE_TB_WIN = SCORE_MATE_IN_MAX_PLY - 1, // don't mix with mate scores
SCORE_TB_WIN_IN_MAX_PLY = SCORE_TB_WIN - MAX_PLY,
SCORE_TB_LOSS_IN_MAX_PLY = -SCORE_TB_WIN_IN_MAX_PLY;
enum Move {
MOVE_NONE = 0
};
enum MoveType {
MT_NORMAL, MT_CASTLING, MT_EN_PASSANT, MT_PROMOTION,
};
enum Square : int {
SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1,
SQ_A2, SQ_B2, SQ_C2, SQ_D2, SQ_E2, SQ_F2, SQ_G2, SQ_H2,
SQ_A3, SQ_B3, SQ_C3, SQ_D3, SQ_E3, SQ_F3, SQ_G3, SQ_H3,
SQ_A4, SQ_B4, SQ_C4, SQ_D4, SQ_E4, SQ_F4, SQ_G4, SQ_H4,
SQ_A5, SQ_B5, SQ_C5, SQ_D5, SQ_E5, SQ_F5, SQ_G5, SQ_H5,
SQ_A6, SQ_B6, SQ_C6, SQ_D6, SQ_E6, SQ_F6, SQ_G6, SQ_H6,
SQ_A7, SQ_B7, SQ_C7, SQ_D7, SQ_E7, SQ_F7, SQ_G7, SQ_H7,
SQ_A8, SQ_B8, SQ_C8, SQ_D8, SQ_E8, SQ_F8, SQ_G8, SQ_H8,
SQ_NONE,
SQUARE_NB = 64
};
enum Direction : int {
NORTH = 8,
EAST = 1,
SOUTH = -NORTH,
WEST = -EAST,
NORTH_EAST = NORTH + EAST,
SOUTH_EAST = SOUTH + EAST,
SOUTH_WEST = SOUTH + WEST,
NORTH_WEST = NORTH + WEST
};
enum Rank : int {
RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8,
RANK_NB = 8
};
enum File : int {
FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H,
FILE_NB = 8
};
enum CastlingRights {
NO_CASTLING,
WHITE_OO,
WHITE_OOO = WHITE_OO << 1,
BLACK_OO = WHITE_OO << 2,
BLACK_OOO = WHITE_OO << 3,
SHORT_CASTLING = WHITE_OO | BLACK_OO,
LONG_CASTLING = WHITE_OOO | BLACK_OOO,
WHITE_CASTLING = WHITE_OO | WHITE_OOO,
BLACK_CASTLING = BLACK_OO | BLACK_OOO,
ALL_CASTLING = WHITE_CASTLING | BLACK_CASTLING
};
struct CastlingData {
Square kingSrc, kingDest, rookSrc, rookDest;
};
constexpr CastlingData CASTLING_DATA[9] = {
{},
{SQ_E1, SQ_G1, SQ_H1, SQ_F1}, // WHITE_OO
{SQ_E1, SQ_C1, SQ_A1, SQ_D1}, // WHITE_OOO
{},
{SQ_E8, SQ_G8, SQ_H8, SQ_F8}, // BLACK_OO
{}, {}, {},
{SQ_E8, SQ_C8, SQ_A8, SQ_D8} // BLACK_OOO
};
enum Rank : int {
RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8,
RANK_NB = 8
};
enum File : int {
FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H,
FILE_NB = 8
};
// These defines are copied from stockfish types.h
#define ENABLE_BASE_OPERATORS_ON(T) \
inline T operator+(T d1, int d2) { return T(int(d1) + d2); } \
inline T operator-(T d1, int d2) { return T(int(d1) - d2); } \
inline T operator-(T d) { return T(-int(d)); } \
inline T& operator+=(T& d1, int d2) { return d1 = d1 + d2; } \
inline T& operator-=(T& d1, int d2) { return d1 = d1 - d2; }
#define ENABLE_LOGIC_OPERATORS_ON(T) \
inline T operator~(T d1) { return T(~ int(d1)); } \
inline T operator&(T d1, int d2) { return T(int(d1) & d2); } \
inline T& operator&=(T& d1, int d2) { return d1 = d1 & d2; } \
inline T operator|(T d1, int d2) { return T(int(d1) | d2); } \
inline T& operator|=(T& d1, int d2) { return d1 = d1 | d2; }
#define ENABLE_INCR_OPERATORS_ON(T) \
inline T& operator++(T& d) { return d = T(int(d) + 1); } \
inline T& operator--(T& d) { return d = T(int(d) - 1); }
ENABLE_BASE_OPERATORS_ON(File)
ENABLE_BASE_OPERATORS_ON(Rank)
ENABLE_BASE_OPERATORS_ON(Square)
ENABLE_INCR_OPERATORS_ON(Color)
ENABLE_INCR_OPERATORS_ON(File)
ENABLE_INCR_OPERATORS_ON(Rank)
ENABLE_INCR_OPERATORS_ON(Square)
ENABLE_LOGIC_OPERATORS_ON(CastlingRights)
enum Color : int {
WHITE, BLACK, COLOR_NB = 2
};
enum PieceType : int {
NO_PIECE_TYPE, PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING, ALL_PIECES,
PIECE_TYPE_NB = 8
};
Stockfish types.h
CastlingRights {
NO_CASTLING,
WHITE_OO,
WHITE_OOO = WHITE_OO << 1,
BLACK_OO = WHITE_OO << 2,
BLACK_OOO = WHITE_OO << 3,
KING_SIDE = WHITE_OO | BLACK_OO,
QUEEN_SIDE = WHITE_OOO | BLACK_OOO,
WHITE_CASTLING = WHITE_OO | WHITE_OOO,
BLACK_CASTLING = BLACK_OO | BLACK_OOO,
ANY_CASTLING = WHITE_CASTLING | BLACK_CASTLING,
Value VALUE_ZERO = 0;
Value VALUE_DRAW = 0;
Value VALUE_NONE = 32002;
Value VALUE_INFINITE = 32001;
Value VALUE_MATE = 32000;
Value VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY;
Value VALUE_MATED_IN_MAX_PLY = -VALUE_MATE_IN_MAX_PLY;
Value VALUE_TB = VALUE_MATE_IN_MAX_PLY - 1;
Value VALUE_TB_WIN_IN_MAX_PLY = VALUE_TB - MAX_PLY;
Value VALUE_TB_LOSS_IN_MAX_PLY = -VALUE_TB_WIN_IN_MAX_PLY;
enum Square : int {
SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1,
SQ_A2, SQ_B2, SQ_C2, SQ_D2, SQ_E2, SQ_F2, SQ_G2, SQ_H2,
SQ_A3, SQ_B3, SQ_C3, SQ_D3, SQ_E3, SQ_F3, SQ_G3, SQ_H3,
SQ_A4, SQ_B4, SQ_C4, SQ_D4, SQ_E4, SQ_F4, SQ_G4, SQ_H4,
SQ_A5, SQ_B5, SQ_C5, SQ_D5, SQ_E5, SQ_F5, SQ_G5, SQ_H5,
SQ_A6, SQ_B6, SQ_C6, SQ_D6, SQ_E6, SQ_F6, SQ_G6, SQ_H6,
SQ_A7, SQ_B7, SQ_C7, SQ_D7, SQ_E7, SQ_F7, SQ_G7, SQ_H7,
SQ_A8, SQ_B8, SQ_C8, SQ_D8, SQ_E8, SQ_F8, SQ_G8, SQ_H8,
SQ_NONE,
SQUARE_ZERO = 0,
SQUARE_NB = 64
};
enum Direction : int {
NORTH = 8,
EAST = 1,
SOUTH = -NORTH,
WEST = -EAST,
NORTH_EAST = NORTH + EAST,
SOUTH_EAST = SOUTH + EAST,
SOUTH_WEST = SOUTH + WEST,
NORTH_WEST = NORTH + WEST
};
enum File : int {
FILE_A,
FILE_B,
FILE_C,
FILE_D,
FILE_E,
FILE_F,
FILE_G,
FILE_H,
FILE_NB
};
enum Rank : int {
RANK_1,
RANK_2,
RANK_3,
RANK_4,
RANK_5,
RANK_6,
RANK_7,
RANK_8,
RANK_NB
};
enum Piece {
NO_PIECE,
W_PAWN = PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
B_PAWN = PAWN + 8, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING,
PIECE_NB = 16
};
And its only types.h.
-
- Posts: 58
- Joined: Mon Mar 27, 2023 8:29 pm
- Full name: Dmitry Frosty
Re: About Obsidian
Obsidian evaluate.cpp
namespace Eval {
// Scale down as 50 move rule approaches
score = score * (200 - pos.halfMoveClock) / 200;
// Make sure the evaluation does not mix with guaranteed win/loss scores
score = std::clamp(score, SCORE_TB_LOSS_IN_MAX_PLY + 1, SCORE_TB_WIN_IN_MAX_PLY - 1);
return score;
}
Stockfish evaluate.cpp
// Damp down the evaluation linearly when shuffling
v = v * (200 - shuffling) / 214;
// Guarantee evaluation does not hit the tablebase range
v = std::clamp(int(v), VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1);
namespace Eval {
// Scale down as 50 move rule approaches
score = score * (200 - pos.halfMoveClock) / 200;
// Make sure the evaluation does not mix with guaranteed win/loss scores
score = std::clamp(score, SCORE_TB_LOSS_IN_MAX_PLY + 1, SCORE_TB_WIN_IN_MAX_PLY - 1);
return score;
}
Stockfish evaluate.cpp
// Damp down the evaluation linearly when shuffling
v = v * (200 - shuffling) / 214;
// Guarantee evaluation does not hit the tablebase range
v = std::clamp(int(v), VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1);
-
- Posts: 2582
- Joined: Tue Aug 30, 2016 8:19 pm
- Full name: Rasmus Althoff
Re: About Obsidian
Yeah, some convenience macros, Irrelevant for originality of the engine and also attributed.
The downscaling is different, and downscaling in itself is common practice. Clamping to TB data is also common practice if using TBs.DmitriyFrosty wrote: ↑Mon Jan 29, 2024 8:54 am Obsidian evaluate.cppStockfish evaluate.cppCode: Select all
namespace Eval { // Scale down as 50 move rule approaches score = score * (200 - pos.halfMoveClock) / 200; // Make sure the evaluation does not mix with guaranteed win/loss scores score = std::clamp(score, SCORE_TB_LOSS_IN_MAX_PLY + 1, SCORE_TB_WIN_IN_MAX_PLY - 1); return score; }
Code: Select all
// Damp down the evaluation linearly when shuffling v = v * (200 - shuffling) / 214; // Guarantee evaluation does not hit the tablebase range v = std::clamp(int(v), VALUE_TB_LOSS_IN_MAX_PLY + 1, VALUE_TB_WIN_IN_MAX_PLY - 1);
How about you look at the actual search, which is the most important part of an engine?
Rasmus Althoff
https://www.ct800.net
https://www.ct800.net
-
- Posts: 58
- Joined: Mon Mar 27, 2023 8:29 pm
- Full name: Dmitry Frosty
Re: About Obsidian
Obsidian timeman.cpp
void calcOptimumTime(Search::Settings& settings, Color us, clock_t* optimumTime, clock_t* maximumTime)
int overhead = Options["Move Overhead"];
int mtg = settings.movestogo ? std::min(settings.movestogo, 50) : 50;
double optScale
clock_t timeLeft = std::max(clock_t(1),
settings.time[us] + settings.inc[us] * (mtg - 1) - overhead * (2 + mtg));
optScale = std::min(0.95 / mtg,
0.88 * settings.time[us] / double(timeLeft));
*optimumTime = clock_t(optScale * timeLeft);
*maximumTime = settings.time[us] * 0.8 - overhead;
Stockfish timeman.cpp
void TimeManagement::init(Search::LimitsType& limits, Color us, int ply)
TimePoint moveOverhead = TimePoint(Options["Move Overhead"]);
int mtg = limits.movestogo ? std::min(limits.movestogo, 50) : 50;
double optScale
TimePoint timeLeft = std::max(TimePoint(1),
limits.time[us] + limits.inc[us] * (mtg - 1) - moveOverhead * (2 + mtg));
optScale = std::min((0.88 + ply / 116.4) / mtg,
0.88 * limits.time[us] / double(timeLeft));
optimumTime = TimePoint(optScale * timeLeft);
maximumTime = TimePoint(std::min(0.8 * limits.time[us] - moveOverhead, maxScale * optimumTime));
void calcOptimumTime(Search::Settings& settings, Color us, clock_t* optimumTime, clock_t* maximumTime)
int overhead = Options["Move Overhead"];
int mtg = settings.movestogo ? std::min(settings.movestogo, 50) : 50;
double optScale
clock_t timeLeft = std::max(clock_t(1),
settings.time[us] + settings.inc[us] * (mtg - 1) - overhead * (2 + mtg));
optScale = std::min(0.95 / mtg,
0.88 * settings.time[us] / double(timeLeft));
*optimumTime = clock_t(optScale * timeLeft);
*maximumTime = settings.time[us] * 0.8 - overhead;
Stockfish timeman.cpp
void TimeManagement::init(Search::LimitsType& limits, Color us, int ply)
TimePoint moveOverhead = TimePoint(Options["Move Overhead"]);
int mtg = limits.movestogo ? std::min(limits.movestogo, 50) : 50;
double optScale
TimePoint timeLeft = std::max(TimePoint(1),
limits.time[us] + limits.inc[us] * (mtg - 1) - moveOverhead * (2 + mtg));
optScale = std::min((0.88 + ply / 116.4) / mtg,
0.88 * limits.time[us] / double(timeLeft));
optimumTime = TimePoint(optScale * timeLeft);
maximumTime = TimePoint(std::min(0.8 * limits.time[us] - moveOverhead, maxScale * optimumTime));