Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Added brute force search

  • Loading branch information...
commit 4163290bfc4ffc4d016bd465a6a5d276fc08f1d1 1 parent 4279954
@rglenister authored
View
4 chess/src/main/scala/chess/Position.scala
@@ -79,8 +79,8 @@ trait Position {
* @param color is the color of the king.
* @return the index of the square containing the king.
*/
- def getKingSquare(color: PieceColor.Value): Int = pieceToSquaresMap(Piece(King, color)).head
-
+ def getKingSquare(color: PieceColor.Value): Int = squareToPieceMap.find(_._2 == Piece(King, color)).get._1
+
/**
* @return the count of half moves since the last irreversible move was made in accordance with the 50 move rule.
*/
View
3  chess/src/main/scala/chess/codec/FEN.scala
@@ -89,6 +89,8 @@ class FENSerializer(val position: Position) {
*/
object FENParser {
+ private val FenRegEx = """([pnbrqkPNBRQK12345678/]+) ([wb]) (K?Q?k?q?-?) ([a-e][1-8]|-) (\d+) (\d+)""".r
+
/**
* Creates a position from the given FEN.
*
@@ -96,7 +98,6 @@ object FENParser {
* @return the position representing the given fen.
*/
def parse(fen: String): GamePosition = {
- val FenRegEx = """([pnbrqkPNBRQK12345678/]+) ([wb]) (K?Q?k?q?-?) ([a-e][1-8]|-) (\d+) (\d+)""".r
val FenRegEx(fenBoard, fenSideToMove, fenCastlingAvailability, fenEnPassantSquare, fenHalfmoveClock, fenFullMoveNumber) = fen
val sideToMove = PieceColor(fenSideToMove.head).get
GamePosition(
View
62 chess/src/main/scala/chess/search/Search.scala
@@ -0,0 +1,62 @@
+package chess.search
+
+import chess.Position
+import chess.Move
+import chess.BasicMove
+import chess.GamePosition
+import chess.GameStatus._
+import chess.PieceColor._
+
+
+/**
+ * Search results.
+ *
+ * @param score is the score given.
+ * @param bestLine contains the move sequence that resulted in the score.
+ */
+case class SearchResults(score: Int, bestLine: List[Move]) extends Ordered[SearchResults] {
+ def compare(other: SearchResults) = {
+ score.compare(other.score)
+ }
+
+ def unary_- = SearchResults(-score, bestLine)
+}
+
+/**
+ * Brute force full width search.
+ */
+object Search {
+
+ /** The maximum possible score */
+ val MaxScore = 10000
+
+ /**
+ * Scores the given position using a full width search.
+ *
+ * @param position is the position to score.
+ * @param maxDepth is the maximum number of half moves to look ahead.
+ * @return the search results.
+ */
+ def search(position: Position, maxDepth: Int): SearchResults = {
+ -doSearch(position, List(), 0, maxDepth)
+ }
+
+ private def doSearch(position: Position, moves: List[Move], depth: Int, maxDepth: Int): SearchResults = {
+ val score = scorePosition(position, depth)
+ if (score != 0 || depth == maxDepth || position.gameStatus != InProgress) {
+ SearchResults(score, moves.reverse)
+ } else {
+ -(position.moveList.map { move =>
+ doSearch(GamePosition(position, move).get, move :: moves, depth + 1, maxDepth)
+ } max)
+ }
+ }
+
+ private def scorePosition(position: Position, depth: Int): Int = {
+ if (position.gameStatus == Checkmate) {
+ MaxScore - depth
+ } else {
+ 0
+ } * (if (position.sideToMove == White) 1 else -1)
+ }
+}
View
4 chess/src/test/scala/chess/AllTests.java
@@ -9,6 +9,7 @@
import chess.format.AlgebraicMoveFormatterSpec;
import chess.format.LongAlgebraicMoveFormatterSpec;
import chess.format.ICCFNumericMoveFormatterSpec;
+import chess.search.SearchSpec;
@RunWith(Suite.class)
@@ -27,7 +28,8 @@
PieceSquareNotifierSpec.class,
PositionSpec.class,
SquareAttackFinderSpec.class,
- StaticMoveGeneratorSpec.class
+ StaticMoveGeneratorSpec.class,
+ SearchSpec.class
})
public class AllTests {
View
69 chess/src/test/scala/chess/search/SearchTest.scala
@@ -0,0 +1,69 @@
+package chess.search
+
+
+import org.scalatest.FlatSpec
+import org.scalatest.matchers.ShouldMatchers
+import org.junit.runner.RunWith
+import org.scalatest.junit.JUnitRunner
+import chess.Board.{algebraicToSquareIndex => aToI}
+import chess.BoardSide._
+import chess.CastlingRights
+import chess.GamePosition
+import chess.Game
+import chess.Move
+import chess.BasicMove
+import chess.PromotionMove
+import chess.Piece
+import chess.PieceType._
+import chess.PieceColor._
+import chess.Board
+import chess.codec.FENParser
+
+
+
+@RunWith(classOf[JUnitRunner])
+class SearchSpec extends FlatSpec with ShouldMatchers {
+
+ "The Search" should "solve a mate in one" in {
+ val gamePosition = FENParser.parse("2n5/8/6R1/1k1KQb2/2N1N3/8/R2B4/r1n2B2 w - - 0 1")
+ val maxDepth = 1
+ val searchResults = Search.search(gamePosition, maxDepth) should equal (SearchResults(9999, List(BasicMove(35, 27, false))))
+ }
+
+ "The Search" should "solve a mate in two (1)" in {
+ val gamePosition = FENParser.parse("8/8/5KPk/8/B5P1/8/R3p3/8 w - - 0 1")
+ val maxDepth = 3
+ val searchResults = Search.search(gamePosition, maxDepth) should equal (
+ SearchResults(9997, List(BasicMove(24, 3, false), PromotionMove(12, 4, false, Knight), BasicMove(8, 15, false))))
+ }
+
+ "The Search" should "solve a mate in two (2)" in {
+ val gamePosition = FENParser.parse("8/1pnbbprp/1Nkp1p1p/5P2/2rP1R2/N3B2P/4K3/8 w - - 0 1")
+ val maxDepth = 3
+ val searchResults = Search.search(gamePosition, maxDepth) should equal (
+ SearchResults(9997,List(BasicMove(27,35,false), BasicMove(50,35,true), BasicMove(29,26,true))))
+ }
+
+ "The Search" should "solve a mate in two (3)" in {
+ val gamePosition = FENParser.parse("2B5/Kb1N4/6pn/2Q5/5pk1/1q3N2/4pPp1/n5r1 w - - 0 1")
+ val maxDepth = 3
+ val searchResults = Search.search(gamePosition, maxDepth) should equal (
+ SearchResults(9997,List(BasicMove(34,39,false), BasicMove(46,39,true), BasicMove(51,36,false))))
+ }
+
+ "The Search" should "solve a mate in two (4)" in {
+ val gamePosition = FENParser.parse("2N1K3/br2n3/q1pQ4/4pp2/1n2k3/4P1R1/Bp1R1p2/5r2 w - - 0 1")
+ val maxDepth = 3
+ val searchResults = Search.search(gamePosition, maxDepth) should equal (
+ SearchResults(9997,List(BasicMove(43,25,true), BasicMove(49,25,true), BasicMove(58,43,false))))
+ }
+
+ "The Search" should "solve a mate in two (5)" in {
+ val gamePosition = FENParser.parse("8/KNkp4/2p5/8/2Q5/8/4B3/8 w - - 0 1")
+ val maxDepth = 3
+ val searchResults = Search.search(gamePosition, maxDepth) should equal (
+ SearchResults(9997,List(BasicMove(12,30,false), BasicMove(42,34,false), BasicMove(26,34,true))))
+ }
+
+
+}
Please sign in to comment.
Something went wrong with that request. Please try again.