feat: NCS-37 Add initial API structure and DTOs for NowChess application
This commit is contained in:
Generated
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ScalaProjectSettings">
|
||||||
|
<option name="scala3DisclaimerShown" value="true" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package de.nowchess.chess.config
|
package de.nowchess.chess.config
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.Version
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
import com.fasterxml.jackson.module.scala.DefaultScalaModule
|
import com.fasterxml.jackson.module.scala.DefaultScalaModule
|
||||||
import io.quarkus.jackson.ObjectMapperCustomizer
|
import io.quarkus.jackson.ObjectMapperCustomizer
|
||||||
@@ -9,9 +8,4 @@ import jakarta.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class JacksonConfig extends ObjectMapperCustomizer:
|
class JacksonConfig extends ObjectMapperCustomizer:
|
||||||
def customize(mapper: ObjectMapper): Unit =
|
def customize(mapper: ObjectMapper): Unit =
|
||||||
mapper.registerModule(new DefaultScalaModule() {
|
mapper.registerModule(DefaultScalaModule)
|
||||||
override def version(): Version =
|
|
||||||
// scalafix:off DisableSyntax.null
|
|
||||||
new Version(2, 21, 1, null, "com.fasterxml.jackson.module", "jackson-module-scala")
|
|
||||||
// scalafix:on DisableSyntax.null
|
|
||||||
})
|
|
||||||
|
|||||||
@@ -9,5 +9,6 @@ final case class GameEntry(
|
|||||||
engine: GameEngine,
|
engine: GameEngine,
|
||||||
white: PlayerInfo,
|
white: PlayerInfo,
|
||||||
black: PlayerInfo,
|
black: PlayerInfo,
|
||||||
|
drawOfferedBy: Option[Color] = None,
|
||||||
resigned: Boolean = false,
|
resigned: Boolean = false,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -20,19 +20,10 @@ import jakarta.ws.rs.*
|
|||||||
import jakarta.ws.rs.core.{MediaType, Response}
|
import jakarta.ws.rs.core.{MediaType, Response}
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
import scala.compiletime.uninitialized
|
|
||||||
|
|
||||||
@Path("/api/board/game")
|
@Path("/api/board/game")
|
||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
class GameResource:
|
class GameResource(@Inject val registry: GameRegistry, @Inject val objectMapper: ObjectMapper):
|
||||||
|
|
||||||
// scalafix:off DisableSyntax.var
|
|
||||||
@Inject
|
|
||||||
var registry: GameRegistry = uninitialized
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
var objectMapper: ObjectMapper = uninitialized
|
|
||||||
// scalafix:on DisableSyntax.var
|
|
||||||
|
|
||||||
private val DefaultWhite = PlayerInfo(PlayerId("p1"), "Player 1")
|
private val DefaultWhite = PlayerInfo(PlayerId("p1"), "Player 1")
|
||||||
private val DefaultBlack = PlayerInfo(PlayerId("p2"), "Player 2")
|
private val DefaultBlack = PlayerInfo(PlayerId("p2"), "Player 2")
|
||||||
@@ -40,7 +31,7 @@ class GameResource:
|
|||||||
// ── mapping ──────────────────────────────────────────────────────────────
|
// ── mapping ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
private def statusOf(entry: GameEntry): String =
|
private def statusOf(entry: GameEntry): String =
|
||||||
if entry.engine.pendingDrawOfferBy.isDefined then "drawOffered"
|
if entry.drawOfferedBy.isDefined then "drawOffered"
|
||||||
else
|
else
|
||||||
val ctx = entry.engine.context
|
val ctx = entry.engine.context
|
||||||
ctx.result match
|
ctx.result match
|
||||||
@@ -113,7 +104,7 @@ class GameResource:
|
|||||||
val error = new AtomicReference[Option[String]](None)
|
val error = new AtomicReference[Option[String]](None)
|
||||||
val obs = new Observer:
|
val obs = new Observer:
|
||||||
def onGameEvent(e: GameEvent): Unit = e match
|
def onGameEvent(e: GameEvent): Unit = e match
|
||||||
case InvalidMoveEvent(_, reason) => error.set(Some(reason.toString))
|
case InvalidMoveEvent(_, reason) => error.set(Some(reason))
|
||||||
case _ => ()
|
case _ => ()
|
||||||
engine.subscribe(obs)
|
engine.subscribe(obs)
|
||||||
engine.processUserInput(uci)
|
engine.processUserInput(uci)
|
||||||
@@ -142,7 +133,6 @@ class GameResource:
|
|||||||
val black = playerInfoFrom(req.black, DefaultBlack)
|
val black = playerInfoFrom(req.black, DefaultBlack)
|
||||||
val entry = newEntry(GameContext.initial, white, black)
|
val entry = newEntry(GameContext.initial, white, black)
|
||||||
registry.store(entry)
|
registry.store(entry)
|
||||||
println(s"Created game ${entry.gameId}")
|
|
||||||
created(toGameFullDto(entry))
|
created(toGameFullDto(entry))
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@@ -245,16 +235,26 @@ class GameResource:
|
|||||||
assertGameNotOver(entry)
|
assertGameNotOver(entry)
|
||||||
action match
|
action match
|
||||||
case "offer" =>
|
case "offer" =>
|
||||||
entry.engine.offerDraw(entry.engine.context.turn)
|
registry.update(entry.copy(drawOfferedBy = Some(entry.engine.context.turn)))
|
||||||
ok(OkResponseDto())
|
ok(OkResponseDto())
|
||||||
case "accept" =>
|
case "accept" =>
|
||||||
entry.engine.acceptDraw(entry.engine.context.turn)
|
entry.drawOfferedBy match
|
||||||
ok(OkResponseDto())
|
case None =>
|
||||||
|
throw BadRequestException("NO_DRAW_OFFER", "No draw offer to accept")
|
||||||
|
case Some(offerer) if offerer == entry.engine.context.turn =>
|
||||||
|
throw BadRequestException("CANNOT_ACCEPT_OWN_OFFER", "Cannot accept your own draw offer")
|
||||||
|
case _ =>
|
||||||
|
entry.engine.applyDraw(DrawReason.Agreement)
|
||||||
|
registry.update(entry.copy(drawOfferedBy = None))
|
||||||
|
ok(OkResponseDto())
|
||||||
case "decline" =>
|
case "decline" =>
|
||||||
entry.engine.declineDraw(entry.engine.context.turn)
|
if entry.drawOfferedBy.isEmpty then throw BadRequestException("NO_DRAW_OFFER", "No draw offer to decline")
|
||||||
|
registry.update(entry.copy(drawOfferedBy = None))
|
||||||
ok(OkResponseDto())
|
ok(OkResponseDto())
|
||||||
case "claim" =>
|
case "claim" =>
|
||||||
entry.engine.claimDraw()
|
if entry.engine.context.halfMoveClock < 100 then
|
||||||
|
throw BadRequestException("CLAIM_NOT_AVAILABLE", "Fifty-move rule draw is not available")
|
||||||
|
entry.engine.applyDraw(DrawReason.FiftyMoveRule)
|
||||||
ok(OkResponseDto())
|
ok(OkResponseDto())
|
||||||
case _ =>
|
case _ =>
|
||||||
throw BadRequestException("INVALID_ACTION", s"Unknown draw action: $action", Some("action"))
|
throw BadRequestException("INVALID_ACTION", s"Unknown draw action: $action", Some("action"))
|
||||||
|
|||||||
Reference in New Issue
Block a user