how to continue

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

Ras
Posts: 2487
Joined: Tue Aug 30, 2016 8:19 pm
Full name: Rasmus Althoff

Re: how to continue

Post by Ras »

tcusr wrote: Wed Sep 08, 2021 11:41 pmthat's admirable.
Wait until you see the opening book - more than 23k different moves in 13k different positions. All done and checked manually. :)
i still don't understand how i am supposed to choose the values for the bonuses though. is there a mathematical formula to find out these values?
It's my own guess how much I think it should be worth, from a chess point of view.
Rasmus Althoff
https://www.ct800.net
tcusr
Posts: 323
Joined: Tue Aug 31, 2021 10:32 pm
Full name: tcusr

Re: how to continue

Post by tcusr »

Ras wrote: Wed Sep 08, 2021 11:57 pm
tcusr wrote: Wed Sep 08, 2021 11:41 pmthat's admirable.
Wait until you see the opening book - more than 23k different moves in 13k different positions. All done and checked manually. :)
this is admirable AND insane
i still don't understand how i am supposed to choose the values for the bonuses though. is there a mathematical formula to find out these values?
It's my own guess how much I think it should be worth, from a chess point of view.
i respect that. i feel like i could give my engine my own style of playing instead of using AI generated values, since i myself am chess player
User avatar
algerbrex
Posts: 596
Joined: Sun May 30, 2021 5:03 am
Location: United States
Full name: Christian Dean

Re: how to continue

Post by algerbrex »

tcusr wrote: Wed Sep 08, 2021 11:25 pm
algerbrex wrote: Wed Sep 08, 2021 11:09 pm
tcusr wrote: Wed Sep 08, 2021 10:34 pm
tcusr wrote: Wed Sep 08, 2021 10:22 pm
Ras wrote: Wed Sep 08, 2021 9:10 pm
tcusr wrote: Wed Sep 08, 2021 2:03 pmthis seems correct but i have no way to detect stalemate in quiescence
You mostly don't need that. I only use a special stalemate verification in QS that if the side to move is down to the bare king and not in check. That's helpful for some endgames with K+Q:K+P. During normal game, I defer that to main search only, not QS.

My QS mate detection works quite simple. First, I don't transition from main search to QS if I'm in check (check extension). Within QS, I don't verify for in-check in the first QS level because there cannot be a check.

The next three QS levels have the in-check detection. Assuming that I'm in check, then if alpha is below -MATE+ply, I set alpha to -MATE+ply. If none of the check evasions (special movegen) gets out of check, alpha stays at -MATE+ply, hence returning a proper mate score with correct distance.

Conversely, if alpha is already above -MATE+ply, then there's still no move that would improve alpha because there's no legal move at all, hence I return the node's score as alpha, which is a fail-low. The kicker is that it doesn't matter how low it fails because that branch will be discarded anyway.
i don't think i understand.
lithander said that when in check he generates all the moves. if none of the move tried are legal it has to be checkmate.
but you have check extension at root, so you check if you are in check after the search. but since you have not tried all the moves you still don't know if it's checkmate, so you have to generate the rest and try them right?
lithander's approach seems simpler, i will still skip checking for stalemate because you said i don't need to
sorry i only saw now that you use a special move generator when in check. i understand now

i'm still worried about my engine speed though
it takes 8 seconds to reach depth 10 here 8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -. i suppose this is mostly an evaluation problem or maybe some pruning techniques (which i don't have) improve speed a lot here. i'm curious on how long it takes for algerbrex's engine to reach depth 10 (my evaluation function is basically his)
Hi Pier, here's the output I get from the position you gave:

Code: Select all

info depth 1 seldepth 1 score cp 89 time 2 nodes 33
info depth 2 seldepth 4 score cp 96 time 7 nodes 290
info depth 3 seldepth 4 score cp 82 time 7 nodes 728
info depth 4 seldepth 4 score cp 85 time 4 nodes 2663
info depth 5 seldepth 5 score cp 37 time 6 nodes 12187
info depth 6 seldepth 5 score cp 33 time 5 nodes 11549
info depth 7 seldepth 5 score cp 33 time 13 nodes 28609
info depth 8 seldepth 5 score cp 30 time 33 nodes 69833
info depth 9 seldepth 6 score cp 30 time 53 nodes 180832
info depth 10 seldepth 5 score cp 37 time 80 nodes 268286
...
Keep in mind however that I'm using several features which are making my engine faster. To name a few:
  • Killer moves
  • Transposition table (64 MB)
  • Null-move pruning
  • Static null-move pruning
See what kind of performance you get after you start adding some of those features. The time to depth 10 should start to drop pretty dramatically, especially after implementing a transposition table, since the position you gave is an endgame position with few pieces on the board and so there are a ton of transpositions that will keep occuring.
hi algerbrex, thank you for bothering trying it! i overestimated the role of the evaluation function i guess
i'll study your code for now on because my goal is to reach 2000, also your code seems really clean and easy to follow.

btw, do you see anything wrong with this implementation of your evaluation function? sometimes it suggests weak moves even at medium depths

Code: Select all

template<int C>
void eval_color(const Board& board, int *mg, int *eg, int& phase)
{
	for (int p = KING; p <= QUEEN; ++p) {
		uint64_t us = board.piece(p, C);

		while (us) {
			int s = poplsb(&us);

			if (C == BLACK)
				s ^= 56;

			*mg += mg_psqt[p][s];
			*eg += eg_psqt[p][s];
			phase -= phase_value[p];
		}
	}
}

template<int C>
int eval(const Board& board)
{
	constexpr int total_phase = 24;
	int phase = total_phase;
	int mg_scores[2] = {{0}, {0}};
	int eg_scores[2] = {{0}, {0}};

	eval_color<WHITE>(board, mg_scores + WHITE, eg_scores + WHITE, phase);
	eval_color<BLACK>(board, mg_scores + BLACK, eg_scores + BLACK, phase);

	const int mg = mg_scores[C] - mg_scores[C ^ 1];
	const int eg = eg_scores[C] - eg_scores[C ^ 1];

	phase = (phase * 256 + (total_phase / 2)) / total_phase;
	return ((mg * (256 - phase)) + (eg * phase)) / 256;
}
the value for the pieces is this: KING = 1, PAWN, KNIGHT, BISHOP, ROOK, QUEEN. yours is different and maybe when i modified it i forgot to change it somewhere
Yup, that looks right to me. As long as your indexes into mg_psqt, eg_psqt, and phase_values match up to your piece numbers, things should be fine.

And I'm glad you're finding Blunder useful. Part of the reason I tried to write clean code with Blunder is that I wanted to try to give back to the chess programming community and make it useful to people who were just starting off in chess programming!
tcusr
Posts: 323
Joined: Tue Aug 31, 2021 10:32 pm
Full name: tcusr

Re: how to continue

Post by tcusr »

algerbrex wrote: Thu Sep 09, 2021 1:03 am
tcusr wrote: Wed Sep 08, 2021 11:25 pm
algerbrex wrote: Wed Sep 08, 2021 11:09 pm
tcusr wrote: Wed Sep 08, 2021 10:34 pm
tcusr wrote: Wed Sep 08, 2021 10:22 pm
Ras wrote: Wed Sep 08, 2021 9:10 pm
tcusr wrote: Wed Sep 08, 2021 2:03 pmthis seems correct but i have no way to detect stalemate in quiescence
You mostly don't need that. I only use a special stalemate verification in QS that if the side to move is down to the bare king and not in check. That's helpful for some endgames with K+Q:K+P. During normal game, I defer that to main search only, not QS.

My QS mate detection works quite simple. First, I don't transition from main search to QS if I'm in check (check extension). Within QS, I don't verify for in-check in the first QS level because there cannot be a check.

The next three QS levels have the in-check detection. Assuming that I'm in check, then if alpha is below -MATE+ply, I set alpha to -MATE+ply. If none of the check evasions (special movegen) gets out of check, alpha stays at -MATE+ply, hence returning a proper mate score with correct distance.

Conversely, if alpha is already above -MATE+ply, then there's still no move that would improve alpha because there's no legal move at all, hence I return the node's score as alpha, which is a fail-low. The kicker is that it doesn't matter how low it fails because that branch will be discarded anyway.
i don't think i understand.
lithander said that when in check he generates all the moves. if none of the move tried are legal it has to be checkmate.
but you have check extension at root, so you check if you are in check after the search. but since you have not tried all the moves you still don't know if it's checkmate, so you have to generate the rest and try them right?
lithander's approach seems simpler, i will still skip checking for stalemate because you said i don't need to
sorry i only saw now that you use a special move generator when in check. i understand now

i'm still worried about my engine speed though
it takes 8 seconds to reach depth 10 here 8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -. i suppose this is mostly an evaluation problem or maybe some pruning techniques (which i don't have) improve speed a lot here. i'm curious on how long it takes for algerbrex's engine to reach depth 10 (my evaluation function is basically his)
Hi Pier, here's the output I get from the position you gave:

Code: Select all

info depth 1 seldepth 1 score cp 89 time 2 nodes 33
info depth 2 seldepth 4 score cp 96 time 7 nodes 290
info depth 3 seldepth 4 score cp 82 time 7 nodes 728
info depth 4 seldepth 4 score cp 85 time 4 nodes 2663
info depth 5 seldepth 5 score cp 37 time 6 nodes 12187
info depth 6 seldepth 5 score cp 33 time 5 nodes 11549
info depth 7 seldepth 5 score cp 33 time 13 nodes 28609
info depth 8 seldepth 5 score cp 30 time 33 nodes 69833
info depth 9 seldepth 6 score cp 30 time 53 nodes 180832
info depth 10 seldepth 5 score cp 37 time 80 nodes 268286
...
Keep in mind however that I'm using several features which are making my engine faster. To name a few:
  • Killer moves
  • Transposition table (64 MB)
  • Null-move pruning
  • Static null-move pruning
See what kind of performance you get after you start adding some of those features. The time to depth 10 should start to drop pretty dramatically, especially after implementing a transposition table, since the position you gave is an endgame position with few pieces on the board and so there are a ton of transpositions that will keep occuring.
hi algerbrex, thank you for bothering trying it! i overestimated the role of the evaluation function i guess
i'll study your code for now on because my goal is to reach 2000, also your code seems really clean and easy to follow.

btw, do you see anything wrong with this implementation of your evaluation function? sometimes it suggests weak moves even at medium depths

Code: Select all

template<int C>
void eval_color(const Board& board, int *mg, int *eg, int& phase)
{
	for (int p = KING; p <= QUEEN; ++p) {
		uint64_t us = board.piece(p, C);

		while (us) {
			int s = poplsb(&us);

			if (C == BLACK)
				s ^= 56;

			*mg += mg_psqt[p][s];
			*eg += eg_psqt[p][s];
			phase -= phase_value[p];
		}
	}
}

template<int C>
int eval(const Board& board)
{
	constexpr int total_phase = 24;
	int phase = total_phase;
	int mg_scores[2] = {{0}, {0}};
	int eg_scores[2] = {{0}, {0}};

	eval_color<WHITE>(board, mg_scores + WHITE, eg_scores + WHITE, phase);
	eval_color<BLACK>(board, mg_scores + BLACK, eg_scores + BLACK, phase);

	const int mg = mg_scores[C] - mg_scores[C ^ 1];
	const int eg = eg_scores[C] - eg_scores[C ^ 1];

	phase = (phase * 256 + (total_phase / 2)) / total_phase;
	return ((mg * (256 - phase)) + (eg * phase)) / 256;
}
the value for the pieces is this: KING = 1, PAWN, KNIGHT, BISHOP, ROOK, QUEEN. yours is different and maybe when i modified it i forgot to change it somewhere
Yup, that looks right to me. As long as your indexes into mg_psqt, eg_psqt, and phase_values match up to your piece numbers, things should be fine.

And I'm glad you're finding Blunder useful. Part of the reason I tried to write clean code with Blunder is that I wanted to try to give back to the chess programming community and make it useful to people who were just starting off in chess programming!
yes the mg_psqt and eg_psqt match my pieces numbers but it still recommends some weak moves. i decided to use pesto's code for evaluation and it plays a bit better, but it's a also slower. this concerns me.
since i started school i have to slow down the development of the engine. what are some features to speed up search which i can easily add? i will implement them as soon as i finish writing the code for the UCI protocol.
i think i might try using an iterative deepening loop (because it's also useful for fast time controls) and null-move pruning, but i don't know how much they will improve speed. with the ID loop maybe i will also be able to order moves in a better way like you guys do (pv and killer moves first, then captures ordered with MVVLVA and then quiet moves in random order)
a transposition table seems a hard thing to do now, i will implement it much later. i assume that a TT makes a big difference though
User avatar
algerbrex
Posts: 596
Joined: Sun May 30, 2021 5:03 am
Location: United States
Full name: Christian Dean

Re: how to continue

Post by algerbrex »

tcusr wrote: Thu Sep 09, 2021 1:55 pm
algerbrex wrote: Thu Sep 09, 2021 1:03 am
tcusr wrote: Wed Sep 08, 2021 11:25 pm
algerbrex wrote: Wed Sep 08, 2021 11:09 pm
tcusr wrote: Wed Sep 08, 2021 10:34 pm
tcusr wrote: Wed Sep 08, 2021 10:22 pm
Ras wrote: Wed Sep 08, 2021 9:10 pm
tcusr wrote: Wed Sep 08, 2021 2:03 pmthis seems correct but i have no way to detect stalemate in quiescence
You mostly don't need that. I only use a special stalemate verification in QS that if the side to move is down to the bare king and not in check. That's helpful for some endgames with K+Q:K+P. During normal game, I defer that to main search only, not QS.

My QS mate detection works quite simple. First, I don't transition from main search to QS if I'm in check (check extension). Within QS, I don't verify for in-check in the first QS level because there cannot be a check.

The next three QS levels have the in-check detection. Assuming that I'm in check, then if alpha is below -MATE+ply, I set alpha to -MATE+ply. If none of the check evasions (special movegen) gets out of check, alpha stays at -MATE+ply, hence returning a proper mate score with correct distance.

Conversely, if alpha is already above -MATE+ply, then there's still no move that would improve alpha because there's no legal move at all, hence I return the node's score as alpha, which is a fail-low. The kicker is that it doesn't matter how low it fails because that branch will be discarded anyway.
i don't think i understand.
lithander said that when in check he generates all the moves. if none of the move tried are legal it has to be checkmate.
but you have check extension at root, so you check if you are in check after the search. but since you have not tried all the moves you still don't know if it's checkmate, so you have to generate the rest and try them right?
lithander's approach seems simpler, i will still skip checking for stalemate because you said i don't need to
sorry i only saw now that you use a special move generator when in check. i understand now

i'm still worried about my engine speed though
it takes 8 seconds to reach depth 10 here 8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -. i suppose this is mostly an evaluation problem or maybe some pruning techniques (which i don't have) improve speed a lot here. i'm curious on how long it takes for algerbrex's engine to reach depth 10 (my evaluation function is basically his)
Hi Pier, here's the output I get from the position you gave:

Code: Select all

info depth 1 seldepth 1 score cp 89 time 2 nodes 33
info depth 2 seldepth 4 score cp 96 time 7 nodes 290
info depth 3 seldepth 4 score cp 82 time 7 nodes 728
info depth 4 seldepth 4 score cp 85 time 4 nodes 2663
info depth 5 seldepth 5 score cp 37 time 6 nodes 12187
info depth 6 seldepth 5 score cp 33 time 5 nodes 11549
info depth 7 seldepth 5 score cp 33 time 13 nodes 28609
info depth 8 seldepth 5 score cp 30 time 33 nodes 69833
info depth 9 seldepth 6 score cp 30 time 53 nodes 180832
info depth 10 seldepth 5 score cp 37 time 80 nodes 268286
...
Keep in mind however that I'm using several features which are making my engine faster. To name a few:
  • Killer moves
  • Transposition table (64 MB)
  • Null-move pruning
  • Static null-move pruning
See what kind of performance you get after you start adding some of those features. The time to depth 10 should start to drop pretty dramatically, especially after implementing a transposition table, since the position you gave is an endgame position with few pieces on the board and so there are a ton of transpositions that will keep occuring.
hi algerbrex, thank you for bothering trying it! i overestimated the role of the evaluation function i guess
i'll study your code for now on because my goal is to reach 2000, also your code seems really clean and easy to follow.

btw, do you see anything wrong with this implementation of your evaluation function? sometimes it suggests weak moves even at medium depths

Code: Select all

template<int C>
void eval_color(const Board& board, int *mg, int *eg, int& phase)
{
	for (int p = KING; p <= QUEEN; ++p) {
		uint64_t us = board.piece(p, C);

		while (us) {
			int s = poplsb(&us);

			if (C == BLACK)
				s ^= 56;

			*mg += mg_psqt[p][s];
			*eg += eg_psqt[p][s];
			phase -= phase_value[p];
		}
	}
}

template<int C>
int eval(const Board& board)
{
	constexpr int total_phase = 24;
	int phase = total_phase;
	int mg_scores[2] = {{0}, {0}};
	int eg_scores[2] = {{0}, {0}};

	eval_color<WHITE>(board, mg_scores + WHITE, eg_scores + WHITE, phase);
	eval_color<BLACK>(board, mg_scores + BLACK, eg_scores + BLACK, phase);

	const int mg = mg_scores[C] - mg_scores[C ^ 1];
	const int eg = eg_scores[C] - eg_scores[C ^ 1];

	phase = (phase * 256 + (total_phase / 2)) / total_phase;
	return ((mg * (256 - phase)) + (eg * phase)) / 256;
}
the value for the pieces is this: KING = 1, PAWN, KNIGHT, BISHOP, ROOK, QUEEN. yours is different and maybe when i modified it i forgot to change it somewhere
Yup, that looks right to me. As long as your indexes into mg_psqt, eg_psqt, and phase_values match up to your piece numbers, things should be fine.

And I'm glad you're finding Blunder useful. Part of the reason I tried to write clean code with Blunder is that I wanted to try to give back to the chess programming community and make it useful to people who were just starting off in chess programming!
yes the mg_psqt and eg_psqt match my pieces numbers but it still recommends some weak moves. i decided to use pesto's code for evaluation and it plays a bit better, but it's a also slower. this concerns me.
since i started school i have to slow down the development of the engine. what are some features to speed up search which i can easily add? i will implement them as soon as i finish writing the code for the UCI protocol.
i think i might try using an iterative deepening loop (because it's also useful for fast time controls) and null-move pruning, but i don't know how much they will improve speed. with the ID loop maybe i will also be able to order moves in a better way like you guys do (pv and killer moves first, then captures ordered with MVVLVA and then quiet moves in random order)
a transposition table seems a hard thing to do now, i will implement it much later. i assume that a TT makes a big difference though
Well keep in mind that, in general, what makes a chess engine strong, is a good evaluation and search. Right now, PeSTO or my evaluation should give you a pretty strong evaluation, but your engine probably won't make some of the same moves mines do or PeSTO does because our searches are more complex and so our engines are able to find stronger moves.

And yeah, I started school again recently too, so you'll just have to find a balance. Right now, the features I think that would give you some nice improvements would be killer moves and null-move pruning. Get those working well first, along with an ID loop.

And you're right, the transposition table can be a little difficult to set up and debug, but again it's well worth it because it gives you very nice gains from the move ordering and hash hits.
User avatar
algerbrex
Posts: 596
Joined: Sun May 30, 2021 5:03 am
Location: United States
Full name: Christian Dean

Re: how to continue

Post by algerbrex »

lithander wrote: Wed Sep 08, 2021 10:36 pm
algerbrex wrote: Wed Sep 08, 2021 1:54 pm Have you considered yet trying to tune any more tables for something like passed pawns or what not? It sounds like those results would be interesting at least.
Adding more evaluation terms like passed pawns would probably help improve the eval. No doubt about it. But that's some very specific concept whereas the current evaluation is based on machine learning. I kinda like to keep that self-learning approach where I only gave the engine only the means to encode information about what's a good position but didn't hardcode that knowledge.

There are promising extension possible in that direction, too. For example I was thinking about a version where I use different PSTs based on the king location. It probably makes a difference whether you castled left or right but currently the PSTs have to encoding knowledge that has to work equally well (or bad) for both cases.

But I'm already at the very limits of what I consider a "minimal" engine and so this is something I may only try in Minimal Chess' successor if I decide to do one. (I'm still on the fence)
Please do continue with a successor :D I'd love to see what ideas you'd come up with for PSQTs, and I'd think you'd enjoy working with bitboards if you decide to take a different approach with your next engine.
User avatar
lithander
Posts: 881
Joined: Sun Dec 27, 2020 2:40 am
Location: Bremen, Germany
Full name: Thomas Jahn

Re: how to continue

Post by lithander »

algerbrex wrote: Thu Sep 09, 2021 2:16 pm Please do continue with a successor :D I'd love to see what ideas you'd come up with for PSQTs, and I'd think you'd enjoy working with bitboards if you decide to take a different approach with your next engine.
Yeah, you are right. Bitboards is definitely an intriguing concept and providing a lot of new learning opportunities for me. And you can start small and humble with a move generator make it correct first, then make it faster in perft. I had fun with that the first time ;)
Minimal Chess (simple, open source, C#) - Youtube & Github
Leorik (competitive, in active development, C#) - Github & Lichess
tcusr
Posts: 323
Joined: Tue Aug 31, 2021 10:32 pm
Full name: tcusr

Re: how to continue

Post by tcusr »

i have an idea but i don't know where to ask it so i'll post here.
i want to rewrite my engine and dangi12012's posts gave me an idea.
what if i get rid of my move list and make/unmake functions by running a functor on each move right after i generate it?
i would the remove the move-type branch (castling, promotion, normal, en passant) and my whole makeMove function (i still need unmakeMove to restore captured pieces and other things).
to generate captures i use magic bitboards while for quiet moves i have a branchless function which shifts a bitboard to directions known at compile time (color and piece type are passed are template arguments).
the only thing holding be back is when i have to implement alphabeta and i have to order my moves. i would be force to save them somewhere (at least for MVVLVA ordering, the other ordering techniques (afaik) do not depend on the move generator).
any suggestions?
User avatar
j.t.
Posts: 239
Joined: Wed Jun 16, 2021 2:08 am
Location: Berlin
Full name: Jost Triller

Re: how to continue

Post by j.t. »

tcusr wrote: Fri Sep 24, 2021 7:48 pm i have an idea but i don't know where to ask it so i'll post here.
i want to rewrite my engine and dangi12012's posts gave me an idea.
what if i get rid of my move list and make/unmake functions by running a functor on each move right after i generate it?
i would the remove the move-type branch (castling, promotion, normal, en passant) and my whole makeMove function (i still need unmakeMove to restore captured pieces and other things).
to generate captures i use magic bitboards while for quiet moves i have a branchless function which shifts a bitboard to directions known at compile time (color and piece type are passed are template arguments).
the only thing holding be back is when i have to implement alphabeta and i have to order my moves. i would be force to save them somewhere (at least for MVVLVA ordering, the other ordering techniques (afaik) do not depend on the move generator).
any suggestions?
As I already mentioned in the other thread, a generic bitboard move generator is fast enough to take less than 10% of the whole search time. Even if you'd manage to completely remove that, you will probably only see an Elo improvement of like 10 points. I would suggest using some bitboard movegen (kindergarten, magic, rotated or something like that), that makes the move generator pleasant to work with.
tcusr
Posts: 323
Joined: Tue Aug 31, 2021 10:32 pm
Full name: tcusr

Re: how to continue

Post by tcusr »

j.t. wrote: Fri Sep 24, 2021 10:31 pm
tcusr wrote: Fri Sep 24, 2021 7:48 pm i have an idea but i don't know where to ask it so i'll post here.
i want to rewrite my engine and dangi12012's posts gave me an idea.
what if i get rid of my move list and make/unmake functions by running a functor on each move right after i generate it?
i would the remove the move-type branch (castling, promotion, normal, en passant) and my whole makeMove function (i still need unmakeMove to restore captured pieces and other things).
to generate captures i use magic bitboards while for quiet moves i have a branchless function which shifts a bitboard to directions known at compile time (color and piece type are passed are template arguments).
the only thing holding be back is when i have to implement alphabeta and i have to order my moves. i would be force to save them somewhere (at least for MVVLVA ordering, the other ordering techniques (afaik) do not depend on the move generator).
any suggestions?
As I already mentioned in the other thread, a generic bitboard move generator is fast enough to take less than 10% of the whole search time. Even if you'd manage to completely remove that, you will probably only see an Elo improvement of like 10 points. I would suggest using some bitboard movegen (kindergarten, magic, rotated or something like that), that makes the move generator pleasant to work with.
i want to do this because it would actually simplify my code. i do not need a makeMove/unmakeMove functions (i realized i can write a custom functor for special moves like enpassant or castling), i also do not need a way to represent/store moves.
i kinda want to try this but move ordering bothers me.

EDIT:
by removing makeMove and unamake i mean i simply apply the update function on each move with the functor.