Next steps for my engine?

Discussion of chess software programming and technical issues.

Moderator: Ras

Fguy64
Posts: 814
Joined: Sat May 09, 2009 4:51 pm
Location: Toronto

Re: Next steps for my engine?

Post by Fguy64 »

Thanks Aaron, that confirms what I suspected.

ok, here is my new makeMove method. Which works more or less as follows: I trust it is familiar enough that comments are not required.

1. This method first makes a call to possibleMoves(), which is a non-recursive method that returns a list of all possible legal moves.

2. alpha and beta are initialized

3. we loop through the possible moves list, eveluating each move recursively by passing the position after the move to the search function.

4. At each iteration, alpha is updated if necessary to reflect the "best Move so far".

5. Notice I stop searching altogether if a mating line is found. The ply -= 2; line is placed to ensure that the engine "finds" the same forcing line, or one equally as fast, at the next turn, which I suppose is my concession to a lack of a hash table at this point.

OK a few comments...

Q1: it is not clear to me if I should be doing anything with beta in this method. I don't think so.

Q2: I believe earlier "confusion" about my stop condition on the search originates here. Perhaps since my initial "possibleMoves" is non recursive, perhaps it would be more in line with convention if I passed ply-1 at this stage, which would indeed result in a stop condition of ply=0 instead of ply=1 in my search function.

Q3: Since my initial ply is non-recursive, I believe it is correct to pass -beta, -alpha at this initial call to search. In my actual search method siginature, these values will be accepted as alpha, beta.

OK, once I get this method nailed down, I move on to my recursive search method.

Sound good to you?

regards.
Fred.

Code: Select all

public Move makeMove( String pStr ) {
	
	if( diagnostics ) output.setText("");
	think = true;
	
	CMove[] list1 = possibleMoves(pStr);
		
	if( list1.length == 0 ) {
		if( inCheck( pStr ) ) return new Move( "Checkmate", false );
		else return new Move("Stalemate", false );
	}
		
	int alpha = -10000;
	int beta = 10000;
		
	for( CMove cm : list1 ) {
		
		cm.eval = search( cm.pStr, ply, -beta, -alpha );
		if( cm1.eval > alpha ) alpha = cm1.eval;
		
		// if a mating line is found, stop searching
		if( cm.eval == 10000 ) {
			ply -=2;
			return new Move( pStr.charAt(cm1.i), cm1, "forced mate");
		}
	}
	
	// code here to select the move with the maximum eval
	
	return bestMove;
}
User avatar
xsadar
Posts: 147
Joined: Wed Jun 06, 2007 10:01 am
Location: United States
Full name: Mike Leany

Re: Next steps for my engine?

Post by xsadar »

Fguy64 wrote:Q1: it is not clear to me if I should be doing anything with beta in this method. I don't think so.
No, beta cannot change in this function, so you don't need to do anything with it.
Q2: I believe earlier "confusion" about my stop condition on the search originates here. Perhaps since my initial "possibleMoves" is non recursive, perhaps it would be more in line with convention if I passed ply-1 at this stage, which would indeed result in a stop condition of ply=0 instead of ply=1 in my search function.
Yes, passing (ply - 1) would be better. I would also rename it to 'depth' personally, since that's a bit more conventional. Ply usually refers to a ply number that counts up as you get further from the root, while depth usually refers to the remaining depth -- the number of plies you have left to search.
Q3: Since my initial ply is non-recursive, I believe it is correct to pass -beta, -alpha at this initial call to search. In my actual search method siginature, these values will be accepted as alpha, beta.
Correct.

My only other comment would be that the name makeMove is generally used for a function that makes a single move and has nothing to do with your search. The first time I looked at your code I assumed that's what this method did, and didn't even look at it. If I were you, I would name it rootSearch or findBestMove or something like that.
Fguy64
Posts: 814
Joined: Sat May 09, 2009 4:51 pm
Location: Toronto

Re: Next steps for my engine?

Post by Fguy64 »

Thanks Mike, So I'm on the right track. Onwards and upwards.
Fguy64
Posts: 814
Joined: Sat May 09, 2009 4:51 pm
Location: Toronto

Re: Next steps for my engine?

Post by Fguy64 »

OK, this is proceeding a little faster than I expected, touch wood. Here is my search function this time a proper alpha-beta I believe. I went back and compared my changes to the suggestions made by Sven Schule on the previous page, and I think I've covered the bases.

I'm still not exactly clear on what is happening here, I guess that will come with time. I don't really follow what Moreland was doing by returning beta if val >= beta. Anyways, I ended up using the beuwolf page as a reference. If my algorithm is ok, I'll move on with an expectation that I will understand soon enough what I have done.

http://www.frayn.net/beowulf/theory.html

I think the code is pretty much self explanatory, but a few comments...

1. getCandidates() returns a list of moves based strictly on the geometry of the position. checkMove() deals with issues of castling rights, en-passant, and making sure the king is not exposed to check. checkMove() returns an updated position string which includes the usual fen variables.

2. As far as I can see, alpha will only change if the node being evaluated is better than any previous node at that depth in which case alpha and best will be equal.

3. think is a boolean variable controlled by the GUI, when set to false instructing the engine to stop thinking and return the best move so far. I have not made any attempt to conform to winboard or UCI standards.

Code: Select all

private int search( String pStr, int depth, int alpha, int beta ) {
	
	if( !think || (depth == 0) ) return qSearch( pStr );
	
	int best = -10000;
	int val;
	int i,j;

	boolean white = pStr.charAt(64);
	int[][] candidates = Utils.getCandidates( pStr );
		
	for( int m=0; m<candidates.length; m++ ) {

		i = candidates[m][0];
		j = candidates[m][1];
			
		p1Str = Utils.checkMove( pStr, i, j );
		if( p1Str.equals("false") ) continue;

		promoFlag = ( ((j/8 == 0) && (p1Str.charAt(j)=='P')) ||
					((j/8 == 7) && (p1Str.charAt(j)=='p')) );
						
		if( promoFlag ) {	// if the move is a promotion, four separate moves.
			
			p1Str = p1Str.substring(0, j) +( white ? "Q" : "q" ) +p1Str.substring(j+1);
			val = search( p1Str, depth-1, -beta, -alpha );
			if( val > best ) best = eval;
			
			p1Str = p1Str.substring(0, j) +( white ? "R" : "r" ) +p1Str.substring(j+1);
			val = search( p1Str, depth-1, -beta, -alpha );
			if( val > best ) best = eval;
				
			p1Str = p1Str.substring(0, j) +( white ? "B" : "b" ) +p1Str.substring(j+1);
			val = search( p1Str, depth-1, -beta, -alpha );
			if( val > best ) best = eval;
				
			p1Str = p1Str.substring(0, j) +( white ? "N" : "n" ) +p1Str.substring(j+1);
			val = search( p1Str, depth-1, -beta, -alpha );
			if( val > best ) best = eval;
		}
		else {
			val = search( p1Str, depth-1, -beta, -alpha );
			if( val > best ) best = eval;
		}
			
		if( best > alpha ) alpha = best;
		if( alpha >= beta ) return alpha;
	}
		
	return best;
}
User avatar
xsadar
Posts: 147
Joined: Wed Jun 06, 2007 10:01 am
Location: United States
Full name: Mike Leany

Re: Next steps for my engine?

Post by xsadar »

Fguy64 wrote: I'm still not exactly clear on what is happening here, I guess that will come with time. I don't really follow what Moreland was doing by returning beta if val >= beta. Anyways, I ended up using the beuwolf page as a reference. If my algorithm is ok, I'll move on with an expectation that I will understand soon enough what I have done.
Ah, yes. There are two different versions of alpha-beta. The one Bruce Moreland gives is called fail hard. The one you're using is called fail soft. Both are correct, and both have their advantages and disadvantages. See: http://chessprogramming.wikispaces.com/Fail-Soft
For fail hard, you never return a score greater than beta or less than alpha.
For fail soft, you just always return the score of the best move found, even if it's greater than beta or less than alpha.

The only issue I see with your code, is when you're doing promotions you don't increase alpha until after you've tried all promotions. It's a minor issue, but it would probably be better to change each occurrence of this:

Code: Select all

if( val > best ) best = eval; 
to this:

Code: Select all

if( val > best ) {
  best = eval;
  if( best > alpha ) alpha = best;
}
Then remove the other 'if( best > alpha )' line.

Other than that your code looks correct as far as I can tell. If there's anything you still don't understand, feel free to ask.
User avatar
xsadar
Posts: 147
Joined: Wed Jun 06, 2007 10:01 am
Location: United States
Full name: Mike Leany

Re: Next steps for my engine?

Post by xsadar »

Can't edit my post anymore, but you probably want this instead of what I gave in my previous post:

Code: Select all

if (val >= beta) return val;
if( val > best ) {
  best = val;
  if( best > alpha ) alpha = best;
}
Then remove the 'if(alpha >= beta)' line from your code too.

Otherwise you have to search 3 extra moves before pruning when a queen promotion gives you a cutoff (which it quite often will).
Fguy64
Posts: 814
Joined: Sat May 09, 2009 4:51 pm
Location: Toronto

Re: Next steps for my engine?

Post by Fguy64 »

xsadar wrote:Can't edit my post anymore, but you probably want this instead of what I gave in my previous post:

Code: Select all

if (val >= beta) return val;
if( val > best ) {
  best = val;
  if( best > alpha ) alpha = best;
}
Then remove the 'if(alpha >= beta)' line from your code too.

Otherwise you have to search 3 extra moves before pruning when a queen promotion gives you a cutoff (which it quite often will).
understood. thanks.

just one error in my code boolean white = pStr.charAt(64); would cause a type mismatch, but the compiler would have picked that up. Anyways, the only reason the method needs color is so the position string can be properly updated for promotion.

One of the things I was pleased to discover, pretty much by accident, with my first algorithm is the was it finds checkmate without looking for it specifically. All this by virtue of the fact in a mating position there are no moves to disturb the initial best = -10000; once that eval makes its way back to rootSearch we know we have a mating line so we can stop searching. So the method needs a line of code to check for check when there are no legal moves

anyways, one idea of having a more conventional algorithm is that I am hoping it will be easier to understand and incorporate the ideas on quiescense. So that will be the next step once I get these methods compiled and tested.

Thanks again.
Fguy64
Posts: 814
Joined: Sat May 09, 2009 4:51 pm
Location: Toronto

Re: Next steps for my engine?

Post by Fguy64 »

xsadar wrote:Can't edit my post anymore, but you probably want this instead of what I gave in my previous post:

Code: Select all

if (val >= beta) return val;
if( val > best ) {
  best = val;
  if( best > alpha ) alpha = best;
}
Then remove the 'if(alpha >= beta)' line from your code too.

Otherwise you have to search 3 extra moves before pruning when a queen promotion gives you a cutoff (which it quite often will).
hmm, I thought I understood, now I'm not so sure. First of all it looks like we have a mixture of fail hard and fail soft here, where fail hard is required to induce an immediate cutoff after the promo. I guess that's ok. But if a Queen promo is going to produce a cutoff, then wouldn't just updating alpha right after the promo induce cutoffs in the next three promos anyway? Although I suppose it wouldn't happen quite so soon, I would expect the gain from introducing this fail-hard prune (for want of a better description) would be minimal. See what I mean?

also, I sense a problem here where a hard cutoff might cause us to miss a forced mate by underpromotion?
plattyaj

Re: Next steps for my engine?

Post by plattyaj »

Fguy64 wrote:hmm, I thought I understood, now I'm not so sure. First of all it looks like we have a mixture of fail hard and fail soft here, where fail hard is required to induce an immediate cutoff after the promo. I guess that's ok. But if a Queen promo is going to produce a cutoff, then wouldn't just updating alpha right after the promo induce cutoffs in the next three promos anyway? Although I suppose it wouldn't happen quite so soon, I would expect the gain from introducing this fail-hard prune (for want of a better description) would be minimal. See what I mean?

also, I sense a problem here where a hard cutoff might cause us to miss a forced mate by underpromotion?
I think it might be easier to fold the promotion into the move generation code so you return the promotion as four moves. Then you don't have to special case it.

As for the mate:

If either the mate score or the Queen promotion score is >= beta, it doesn't matter since we have a cutoff anyway. If it's < beta then the one with the higher score will update alpha. And hopefully your mate score is set high enough that it must be greater than anything other than a better mate score.

Andy.
Fguy64
Posts: 814
Joined: Sat May 09, 2009 4:51 pm
Location: Toronto

Re: Next steps for my engine?

Post by Fguy64 »

plattyaj wrote:
Fguy64 wrote:hmm, I thought I understood, now I'm not so sure. First of all it looks like we have a mixture of fail hard and fail soft here, where fail hard is required to induce an immediate cutoff after the promo. I guess that's ok. But if a Queen promo is going to produce a cutoff, then wouldn't just updating alpha right after the promo induce cutoffs in the next three promos anyway? Although I suppose it wouldn't happen quite so soon, I would expect the gain from introducing this fail-hard prune (for want of a better description) would be minimal. See what I mean?

also, I sense a problem here where a hard cutoff might cause us to miss a forced mate by underpromotion?
I think it might be easier to fold the promotion into the move generation code so you return the promotion as four moves. Then you don't have to special case it.

As for the mate:

If either the mate score or the Queen promotion score is >= beta, it doesn't matter since we have a cutoff anyway. If it's < beta then the one with the higher score will update alpha. And hopefully your mate score is set high enough that it must be greater than anything other than a better mate score.

Andy.
hmm yes I see what you mean about where to handle the promo code. At the moment I do it this way because when I was debugging my move legality code, I found it useful to use the same code to enforce legality on a human, and of course a human reserves the right to promote to the wrong piece. But Once I am certain I have no bugs in my move generation, I should probably separate the two. Like I said, I haven't tried to follow a winboard or UCI standard.

As for your second point. I think I understand, either kind of cutoff would cause the same issue of missing a desireable underpromotion. Which is why I have already decided to proceed with the way I originally had it, for now, which is don't update alpha until after all four promotion nodes are searched. I can always change it later.

As for mate score, a couple of posts ago I described how mate is currently handled. As long as that works, I'll stick with it and look for improvements like you have suggested later.

regards.