There are a couple of issues with phase calculation, although I'm not sure they have an impact on Elo.
First one is when you set up a position in which there is more material than in the initial position (for example, because a promotion took place very early in the game, before there was any capture). In this case, phase would go negative if it wasn't for the check at the end of set_phase(), so that phase is always 0. This seems all OK, but the problem is that when you make a move that captures the promoted piece, phase will be updated with the value of the captured piece (a queen probably). The new value of phase will not match the one that it would have if you directly set up the position after the capture, so evaluation will differ.
I admit that such positions are rare anyway.
There is also the problem of how to update phase in make_move. Currently, it is only updated when there is a piece in the destination square. This doesn't take into account en passant captures or promotions. Both of these cases should be taken care of. The first one will obviously have no effect, since piece_phase[PAWN] == 0, but it would be good practice to deal with it in case that value changes in the future. The second case may have a measurable impact on strength, although I'm not really sure.
Also, keep in mind that subtracting the promotion piece from phase may make it go negative, like if you set up the start position and in the search, you follow a line that reaches a promotion before making any capture (which is the first case I mentioned).
An alternative could be to redefine phase as 0 when there are only 2 kings (ending) and the corresponding value in the opening, which would be the result of adding all the pieces. This would be the inverse of how it is done now, and there would be no problem with negative values, as there would be none.
There are a couple of issues with phase calculation, although I'm not sure they have an impact on Elo.
First one is when you set up a position in which there is more material than in the initial position (for example, because a promotion took place very early in the game, before there was any capture). In this case, phase would go negative if it wasn't for the check at the end of set_phase(), so that phase is always 0. This seems all OK, but the problem is that when you make a move that captures the promoted piece, phase will be updated with the value of the captured piece (a queen probably). The new value of phase will not match the one that it would have if you directly set up the position after the capture, so evaluation will differ.
I admit that such positions are rare anyway.
There is also the problem of how to update phase in make_move. Currently, it is only updated when there is a piece in the destination square. This doesn't take into account en passant captures or promotions. Both of these cases should be taken care of. The first one will obviously have no effect, since piece_phase[PAWN] == 0, but it would be good practice to deal with it in case that value changes in the future. The second case may have a measurable impact on strength, although I'm not really sure.
Also, keep in mind that subtracting the promotion piece from phase may make it go negative, like if you set up the start position and in the search, you follow a line that reaches a promotion before making any capture (which is the first case I mentioned).
An alternative could be to redefine phase as 0 when there are only 2 kings (ending) and the corresponding value in the opening, which would be the result of adding all the pieces. This would be the inverse of how it is done now, and there would be no problem with negative values, as there would be none.