Skip to content

Commit

Permalink
Improve move sorting.
Browse files Browse the repository at this point in the history
  • Loading branch information
dykstrom committed Feb 1, 2018
1 parent f6fb2a7 commit 83075ab
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 36 deletions.
21 changes: 12 additions & 9 deletions src/main/java/se/dykstrom/ronja/common/model/Move.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,27 @@
* 00-05 - to square
* 06-11 - from square
* 12-14 - moved piece
* 15-17 - captured piece
* 18-22 - promoted piece
* 21 - castle flag
* 22 - en passant flag
* 15 - castle flag
* 16 - en passant flag
* 17-19 - promoted piece
* 20-22 - captured piece
*
* Note that the bits must be stored in this order, because the move sorting depends
* on more promising moves like captures being greater than less promising moves.
*
* @author Johan Dykstrom
*/
public class Move {

private static final int PIECE_MASK = 0x07;
private static final int SQUARE_MASK = 0x3f;
private static final int CASTLE_MASK = 0x01 << 21;
private static final int ENPASSANT_MASK = 0x01 << 22;
private static final int CASTLE_MASK = 0x01 << 15;
private static final int ENPASSANT_MASK = 0x01 << 16;

private static final int FROM_OFFSET = 6;
private static final int MOVED_OFFSET = 12;
private static final int CAPTURED_OFFSET = 15;
private static final int PROMOTED_OFFSET = 18;
private static final int PROMOTED_OFFSET = 17;
private static final int CAPTURED_OFFSET = 20;

private static final int CAPTURED_PIECE_MASK = PIECE_MASK << CAPTURED_OFFSET;
private static final int PROMOTED_PIECE_MASK = PIECE_MASK << PROMOTED_OFFSET;
Expand Down Expand Up @@ -170,7 +173,7 @@ public static String toString(int move) {
builder.append(Square.idToName(getFrom(move)));
builder.append(Square.idToName(getTo(move)));
if (isPromotion(move)) {
builder.append(" -> ").append(Piece.toSymbol(getPromoted(move)));
builder.append("->").append(Piece.toSymbol(getPromoted(move)));
} else if (isCastling(move)) {
builder.append(", castling");
} else if (isEnPassant(move)) {
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/se/dykstrom/ronja/common/model/Position.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@

package se.dykstrom.ronja.common.model;

import static se.dykstrom.ronja.common.model.Piece.*;
import se.dykstrom.ronja.common.parser.IllegalMoveException;
import se.dykstrom.ronja.common.parser.MoveParser;
import se.dykstrom.ronja.engine.core.AttackGenerator;

import java.util.Arrays;
import java.util.List;
import java.util.logging.Logger;

import se.dykstrom.ronja.common.parser.IllegalMoveException;
import se.dykstrom.ronja.common.parser.MoveParser;
import se.dykstrom.ronja.engine.core.AttackGenerator;
import static se.dykstrom.ronja.common.model.Piece.*;

/**
* Represents a chess position, including the move number, the active color,
Expand Down Expand Up @@ -217,7 +217,7 @@ public Position withMove(int move) {
long to = Move.getTo(move);

int fromPiece = Move.getPiece(move);
int toPiece = getPiece(to);
int toPiece = getPiece(to); // TODO: Use Move.getCaptured(move)?

long white = this.white;
long black = this.black;
Expand Down
22 changes: 4 additions & 18 deletions src/main/java/se/dykstrom/ronja/engine/core/AlphaBetaFinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,14 @@ public int findBestMoveWithinTime(Position position, long maxTime) {
long remainingTime = maxTime;
long estimatedTime;
int bestMove = 0;
int maxDepth = 1;
int maxDepth = 0;

// Find all possible moves
int numberOfMoves = fullMoveGenerator.generateMoves(position, 0);

try {
do {
maxDepth++;
long startTimeForDepth = System.currentTimeMillis();
bestMove = findBestMove(position, maxDepth, numberOfMoves, remainingTime);
if (DEBUG) TLOG.fine("Best move at depth " + maxDepth + " is " + bestMove);
Expand All @@ -85,7 +86,6 @@ public int findBestMoveWithinTime(Position position, long maxTime) {
estimatedTime = TimeUtils.estimateTimeForNextDepth(searchTimes);
if (DEBUG) TLOG.fine("Estimated = time " + estimatedTime + ", remaining time = " + remainingTime);
sort(fullMoveGenerator, 0, numberOfMoves, bestMove);
maxDepth++;
} while (estimatedTime < remainingTime);
} catch (OutOfTimeException exception) {
if (DEBUG) TLOG.fine("Aborted search with max depth " + maxDepth + " because time is up");
Expand Down Expand Up @@ -253,29 +253,15 @@ private void sort(FullMoveGenerator moveGenerator, int depth, int numberOfMoves,
return -1;
} else if (y == bestMove) {
return 1;
} else {
return 0;
}
return Integer.compare(y, x);
});
}

/**
* Sorts the moves on the given depth.
*/
private void sort(FullMoveGenerator moveGenerator, int depth, int numberOfMoves) {
SortUtils.sort(moveGenerator.moves[depth], numberOfMoves, MOVE_COMPARATOR);
SortUtils.sort(moveGenerator.moves[depth], numberOfMoves);
}

private static final SortUtils.IntComparator MOVE_COMPARATOR = (x, y) -> {
boolean isCaptureX = Move.isCapture(x);
boolean isCaptureY = Move.isCapture(y);

if (isCaptureX && !isCaptureY) {
return -1;
}
if (!isCaptureX && isCaptureY) {
return 1;
}
return 0;
};
}
21 changes: 21 additions & 0 deletions src/main/java/se/dykstrom/ronja/engine/core/SortUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,25 @@ public static void sort(int[] array, int number, IntComparator comparator) {
array[dataIndex] = data;
}
}

/**
* Sorts the first part of the given array into descending order. The number of elements to
* actually sort is given by {@code number}, which must be less than or equal to the array size.
*
* @implNote The sorting algorithm used is insert sort. This may be changed in future versions.
*
* @param array The array to sort.
* @param number The number of elements to sort in the array.
*/
public static void sort(int[] array, int number) {
for (int index = 1; index < number; index++) {
int data = array[index];
int dataIndex = index;
while (dataIndex > 0 && data > array[dataIndex - 1]) {
array[dataIndex] = array[dataIndex - 1];
dataIndex--;
}
array[dataIndex] = data;
}
}
}
7 changes: 3 additions & 4 deletions src/test/java/se/dykstrom/ronja/common/model/MoveTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,13 @@

package se.dykstrom.ronja.common.model;

import org.junit.Test;
import se.dykstrom.ronja.test.AbstractTestCase;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static se.dykstrom.ronja.common.model.Piece.*;

import org.junit.Test;

import se.dykstrom.ronja.test.AbstractTestCase;

/**
* This class is for testing class {@code Move} using JUnit.
*
Expand Down
26 changes: 26 additions & 0 deletions src/test/java/se/dykstrom/ronja/engine/core/SortUtilsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,19 @@ public void shouldSortArray() {
assertArrayEquals(expected, array);
}

@Test
public void shouldSortArrayDescending() {
// Given
int[] array = {2, 1, 5, 3, 2};
int[] expected = {5, 3, 2, 2, 1};

// When
SortUtils.sort(array, array.length);

// Then
assertArrayEquals(expected, array);
}

@Test
public void shouldLongArray() {
// Given
Expand Down Expand Up @@ -132,6 +145,19 @@ public void shouldSortPartOfArray() {
assertArrayEquals(expected, array);
}

@Test
public void shouldSortPartOfArrayDescending() {
// Given
int[] array = {2, 1, 5, 3, 2};
int[] expected = {5, 2, 1, 3, 2};

// When
SortUtils.sort(array, 3);

// Then
assertArrayEquals(expected, array);
}

@Test
public void shouldSortArrayUsingMoveComparator() {
// Given
Expand Down

0 comments on commit 83075ab

Please sign in to comment.