fix: add thread synchronization to awaitingPromotion and complete coverage tests for all promotion pieces
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -48,7 +48,7 @@ class TerminalUI(engine: GameEngine) extends Observer:
|
||||
|
||||
case _: PromotionRequiredEvent =>
|
||||
println("Promote to: q=Queen, r=Rook, b=Bishop, n=Knight")
|
||||
awaitingPromotion = true
|
||||
synchronized { awaitingPromotion = true }
|
||||
|
||||
/** Start the terminal UI game loop. */
|
||||
def start(): Unit =
|
||||
@@ -63,24 +63,26 @@ class TerminalUI(engine: GameEngine) extends Observer:
|
||||
// Game loop
|
||||
while running do
|
||||
val input = Option(StdIn.readLine()).getOrElse("quit").trim
|
||||
if awaitingPromotion then
|
||||
input.toLowerCase match
|
||||
case "q" => awaitingPromotion = false; engine.completePromotion(PromotionPiece.Queen)
|
||||
case "r" => awaitingPromotion = false; engine.completePromotion(PromotionPiece.Rook)
|
||||
case "b" => awaitingPromotion = false; engine.completePromotion(PromotionPiece.Bishop)
|
||||
case "n" => awaitingPromotion = false; engine.completePromotion(PromotionPiece.Knight)
|
||||
case _ =>
|
||||
println("Invalid choice. Enter q, r, b, or n.")
|
||||
println("Promote to: q=Queen, r=Rook, b=Bishop, n=Knight")
|
||||
else
|
||||
input.toLowerCase match
|
||||
case "quit" | "q" =>
|
||||
running = false
|
||||
println("Game over. Goodbye!")
|
||||
case "" =>
|
||||
printPrompt(engine.turn)
|
||||
case _ =>
|
||||
engine.processUserInput(input)
|
||||
synchronized {
|
||||
if awaitingPromotion then
|
||||
input.toLowerCase match
|
||||
case "q" => awaitingPromotion = false; engine.completePromotion(PromotionPiece.Queen)
|
||||
case "r" => awaitingPromotion = false; engine.completePromotion(PromotionPiece.Rook)
|
||||
case "b" => awaitingPromotion = false; engine.completePromotion(PromotionPiece.Bishop)
|
||||
case "n" => awaitingPromotion = false; engine.completePromotion(PromotionPiece.Knight)
|
||||
case _ =>
|
||||
println("Invalid choice. Enter q, r, b, or n.")
|
||||
println("Promote to: q=Queen, r=Rook, b=Bishop, n=Knight")
|
||||
else
|
||||
input.toLowerCase match
|
||||
case "quit" | "q" =>
|
||||
running = false
|
||||
println("Game over. Goodbye!")
|
||||
case "" =>
|
||||
printPrompt(engine.turn)
|
||||
case _ =>
|
||||
engine.processUserInput(input)
|
||||
}
|
||||
|
||||
// Unsubscribe when done
|
||||
engine.unsubscribe(this)
|
||||
|
||||
@@ -264,4 +264,64 @@ class TerminalUITest extends AnyFunSuite with Matchers {
|
||||
capturedPiece should be(Some(PromotionPiece.Rook))
|
||||
out.toString should include("Invalid")
|
||||
}
|
||||
|
||||
test("TerminalUI routes Bishop promotion choice to engine.completePromotion") {
|
||||
import de.nowchess.api.move.PromotionPiece
|
||||
|
||||
var capturedPiece: Option[PromotionPiece] = None
|
||||
|
||||
val engine = new GameEngine() {
|
||||
override def processUserInput(rawInput: String): Unit =
|
||||
if rawInput.trim == "e7e8" then
|
||||
notifyObservers(PromotionRequiredEvent(
|
||||
Board(Map.empty), GameHistory.empty, Color.White,
|
||||
Square(File.E, Rank.R7), Square(File.E, Rank.R8)
|
||||
))
|
||||
override def completePromotion(piece: PromotionPiece): Unit =
|
||||
capturedPiece = Some(piece)
|
||||
notifyObservers(MoveExecutedEvent(Board(Map.empty), GameHistory.empty, Color.Black, "e7", "e8", None))
|
||||
}
|
||||
|
||||
val in = new ByteArrayInputStream("e7e8\nb\nquit\n".getBytes)
|
||||
val out = new ByteArrayOutputStream()
|
||||
val ui = new TerminalUI(engine)
|
||||
|
||||
Console.withIn(in) {
|
||||
Console.withOut(out) {
|
||||
ui.start()
|
||||
}
|
||||
}
|
||||
|
||||
capturedPiece should be(Some(PromotionPiece.Bishop))
|
||||
}
|
||||
|
||||
test("TerminalUI routes Knight promotion choice to engine.completePromotion") {
|
||||
import de.nowchess.api.move.PromotionPiece
|
||||
|
||||
var capturedPiece: Option[PromotionPiece] = None
|
||||
|
||||
val engine = new GameEngine() {
|
||||
override def processUserInput(rawInput: String): Unit =
|
||||
if rawInput.trim == "e7e8" then
|
||||
notifyObservers(PromotionRequiredEvent(
|
||||
Board(Map.empty), GameHistory.empty, Color.White,
|
||||
Square(File.E, Rank.R7), Square(File.E, Rank.R8)
|
||||
))
|
||||
override def completePromotion(piece: PromotionPiece): Unit =
|
||||
capturedPiece = Some(piece)
|
||||
notifyObservers(MoveExecutedEvent(Board(Map.empty), GameHistory.empty, Color.Black, "e7", "e8", None))
|
||||
}
|
||||
|
||||
val in = new ByteArrayInputStream("e7e8\nn\nquit\n".getBytes)
|
||||
val out = new ByteArrayOutputStream()
|
||||
val ui = new TerminalUI(engine)
|
||||
|
||||
Console.withIn(in) {
|
||||
Console.withOut(out) {
|
||||
ui.start()
|
||||
}
|
||||
}
|
||||
|
||||
capturedPiece should be(Some(PromotionPiece.Knight))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user