Skip to content

Commit

Permalink
Stockfish update
Browse files Browse the repository at this point in the history
  • Loading branch information
philippeZim committed Jan 21, 2025
1 parent 080be23 commit 4face40
Show file tree
Hide file tree
Showing 8 changed files with 167 additions and 18 deletions.
2 changes: 1 addition & 1 deletion .bsp/sbt.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"name":"sbt","version":"1.10.4","bspVersion":"2.1.0-M1","languages":["scala"],"argv":["C:\\Users\\jakob\\.jdks\\openjdk-23.0.1/bin/java","-Xms100m","-Xmx100m","-classpath","C:/Users/jakob/AppData/Roaming/JetBrains/IntelliJIdea2024.3/plugins/Scala/launcher/sbt-launch.jar","-Dsbt.script=C:\\Program%20Files%20(x86)\\sbt\\bin\\sbt.bat","xsbt.boot.Boot","-bsp"]}
{"name":"sbt","version":"1.10.4","bspVersion":"2.1.0-M1","languages":["scala"],"argv":["C:\\Users\\eronz\\.jdks\\openjdk-23.0.1/bin/java","-Xms100m","-Xmx100m","-classpath","C:/Users/eronz/AppData/Roaming/JetBrains/IdeaIC2024.3/plugins/Scala/launcher/sbt-launch.jar","-Dsbt.script=C:\\Program%20Files%20(x86)\\sbt\\bin\\sbt.bat","xsbt.boot.Boot","-bsp"]}
2 changes: 1 addition & 1 deletion src/main/resources/GameState.xml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<box><fen>rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1</fen><state>0</state></box>
<box><fen>r3k2r/pppq1ppp/2np1n2/2b1pb2/2B1PB2/2NP1N2/PPPQ1PPP/R4RK1 b kq - 6 8</fen><state>0</state></box>
2 changes: 1 addition & 1 deletion src/main/scala/Chess.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import scala.concurrent.Future

object Chess {
given ChessTrait = RealChessFacade()
val controller = ChessModule.provideDuoChessXML()
val controller = ChessModule.provideEngineChessXML()
val tui = new Tui(controller)
controller.notifyObservers

Expand Down
6 changes: 3 additions & 3 deletions src/main/scala/ChessModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -58,21 +58,21 @@ object ChessModule {

def provideEngineChessXML(): ControllerTrait = {
given ApiFileTrait = XMLApi()
given ChessTrait = DevourChessFacade()
given ChessTrait = RealChessFacade()

val fileApi = XMLApi()
val xmlContent: scala.xml.Node = XML.loadFile("src/main/resources/GameState.xml")
val wrapper: DataWrapper = DataWrapper(Some(xmlContent), None)
val arg1 = unpackToFen(wrapper, fileApi)
val arg2 = new ChessContext
val arg3 = ""
new EngineController(arg1, arg2, arg3, 15)
new EngineController(arg1, arg2, arg3, 10)
}

def provideEngineChessJSON(): ControllerTrait = {
given ApiFileTrait = JSONApi()

given ChessTrait = DevourChessFacade()
given ChessTrait = RealChessFacade()

val fileApi = JSONApi()
val filePath = "src/main/resources/GameState.json"
Expand Down
27 changes: 18 additions & 9 deletions src/main/scala/Model/ChessComponent/RealChess/ChessApiClient.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,34 @@ package Model.ChessComponent.RealChess
import requests.Response

object ChessApiClient {
val host = "https://d948-141-37-128-1.ngrok-free.app"
var host = "https://stockfish.online/api/s/v2.php"

def getBestMove(fen: String, depth: Int): String = {
val payload = ujson.Obj(
if (depth >= 16) {
throw new IllegalArgumentException("Depth must be less than 16")
}

val params = Map(
"fen" -> fen,
"depth" -> depth
"depth" -> depth.toString
)

val response: Response = requests.post(
url = s"$host/bestmove/",
data = payload.render(),
headers = Map("Content-Type" -> "application/json")
val response: Response = requests.get(
url = host,
params = params
)

if (response.statusCode == 200) {
val json = ujson.read(response.text())
json("best_move").str
if (json("success").bool) {
json("bestmove").str.split(" ")(1)
} else {
println("a")
throw new Exception(s"API request failed: ${json("data").str}")
}
} else {
println("b")
throw new Exception(s"Error: ${response.statusCode}, ${response.text()}")
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class EngineController (override var fen : String, var context : ChessContext, v
this.notifyObservers
}

def createOutput() : String = {output}
def createOutput() : String = output

def play(move : (Int, Int)) : Unit = {
val legalMoves = gameMode.getAllLegalMoves(fen);
Expand All @@ -38,8 +38,7 @@ class EngineController (override var fen : String, var context : ChessContext, v
if(state) {return}

val engineMoveString = gameMode.getBestMove(fen, depth)
print("test")
print(engineMoveString)

val engineMoveInt = gameMode.translateMoveStringToInt(fen, engineMoveString)

UndoInvoker.doStep(new SetCommand(gameMode.makeMove(fen, engineMoveInt), fen, this))
Expand Down
97 changes: 97 additions & 0 deletions src/test/scala/ControllerTests/EngineControllerSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package ControllerTests

import Model.ChessComponent.BasicChessComponent.StandartChess.{ChessBoard, Piece}
import cController.ControllerComponent.SoloChessController.EngineController
import Model.ChessComponent.ChessTrait
import Model.ChessComponent.RealChess.RealChessFacade
import cController.ControllerComponent.Extra.{ChessContext, Event, State}
import cController.ControllerComponent.RealChessController.Controller
import cController.ControllerComponent.StateComponent.{ApiFileTrait, DataWrapper}
import cController.ControllerComponent.StateComponent.xmlSolution.XMLApi
import org.scalatest.wordspec.AnyWordSpec
import org.scalatest.matchers.should.Matchers.*
import util.Observer

import scala.xml.XML

class EngineControllerSpec extends AnyWordSpec {
def unpackToFen(dataWrapped: DataWrapper, fileApi: ApiFileTrait): String = {
val data: (String, State) = fileApi.from(dataWrapped)
data._2 match {
case State.remisState => "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
case State.whiteWonState => "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
case State.blackWonState => "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
case _ => data._1
}
}

given ApiFileTrait = XMLApi()

given ChessTrait = RealChessFacade()

val fileApi = XMLApi()
val xmlContent: scala.xml.Node = XML.loadFile("src/main/resources/GameState.xml")
val wrapper: DataWrapper = DataWrapper(Some(xmlContent), None)
val arg1 = unpackToFen(wrapper, fileApi)
val arg2 = new ChessContext
val arg3 = ""
val ec = EngineController(arg1, arg2, arg3, 10)

"EngineControllerSpec" should {
"resetBoard Test" in {
val b = ec.resetBoard()
ec.fen should be("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
}

"createOutput Test" in {
ec.createOutput() should be(ec.output)
}

"play Test" in {
ec.resetBoard()
ec.play(ChessBoard.translateMoveStringToInt(ec.fen, "e2e4"))
ec.fen should be ("rnbqkbnr/ppp1pppp/8/3p4/4P3/8/PPPP1PPP/RNBQKBNR w KQkq d6 0 2")
}

"checkGameState Test" in {
ec.checkGameState(List()) should be (true)
}


"promote correctly" in {
ec.fen = "rPbqkbnr/1pppppp1/8/8/8/8/P1PPPPpP/RNBQKBNR w KQkq - 0 5"
ec.promotePawn("Q")
ec.fen should be ("rqbqkbnr/1pppppp1/8/8/8/8/P1PPPPpP/RNBQKBNR w KQkq - 0 5")

}

"do redo and undo correctly" in {
ec.fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
ec.play(ChessBoard.moveToIndex("e2","e4"))
val save = ec.fen
ec.undo()
ec.undo()
ec.fen should be ("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")

ec.redo()
ec.redo()
ec.fen should be(save)
}

"implement squareClicked correctly" in {
ec.fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
ec.squareClicked(7)
ec.activeSquare should be(-5)

ec.squareClicked(60)
ec.activeSquare should be(60)

}

"switch themes correctly" in {
ec.fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
ec.nextTheme()
ec.current_theme = 1
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package ModelTests.ChessComponentTests

import Model.ChessComponent.RealChess.ChessApiClient
import org.scalatest.wordspec.AnyWordSpec
import org.scalatest.matchers.should.Matchers.*
import org.scalatest.BeforeAndAfterEach

class ChessApiClientSpec extends AnyWordSpec with BeforeAndAfterEach {
"ChessApiClient" should {
"return a valid best move for a valid FEN and depth" in {
val fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
val depth = 5
val bestMove = ChessApiClient.getBestMove(fen, depth)
bestMove should fullyMatch regex "[a-h][1-8][a-h][1-8][nbrq]?"
}
"throw an exception for a depth greater than or equal to 16" in {
val fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
val depth = 16
assertThrows[IllegalArgumentException] {
ChessApiClient.getBestMove(fen, depth)
}
}
"throw an exception when the API request fails with success false" in {
val fen = "invalid fen which hopefully makes the real api return false"
val depth = 5
assertThrows[Exception] {
ChessApiClient.getBestMove(fen, depth)
}
}

"throw an exception when the API returns a non-200 status code" in {
val originalHost = ChessApiClient.host
ChessApiClient.host = "https://an.invalid.url.that.does.not.exist.stockfish.online/api/s/v2.php" // Replace with an invalid URL that causes a connection error
val fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
val depth = 5
val exception = intercept[Exception] {
ChessApiClient.getBestMove(fen, depth)
}
exception.getMessage should startWith("Unknown") // The exception message in this case will be connection specific
// Reset the host back to its original value to not affect other tests.
ChessApiClient.host = originalHost
}
}
}

0 comments on commit 4face40

Please sign in to comment.