package uk.co.nickthecoder.gamescupboard.server.games
import uk.co.nickthecoder.gamescupboard.common.*
import uk.co.nickthecoder.gamescupboard.server.*
private const val snapX = 49
private const val snapY = 49
private const val left = 400 - snapX * 4
private const val top = 300 - snapY * 4
private const val bottom = top + 8 * snapY
private val boardArea = SpecialArea(
"chessBoard", AreaType.PUBLIC,
left,
top,
8 * snapX,
8 * snapY,
changeZOrder = ChangeZOrder.BOTTOM,
snap = RectangularSnapToGrid(snapX, snapY)
)
private val chessPieces = Grid("chessPieces", "chessPieces.png", 8, 2, 46, 50)
private fun chessPieces(startIndex: Int = 0): List<GameObject> {
val result = mutableListOf<GameObject>()
var id = startIndex
// Pawns
for (tx in 0..7) {
val x = left + snapX / 2 + tx * snapX
val pieceGX = listOf(2, 4, 3, 1, 0, 3, 4, 2)[tx]
// Back pawns
result.add(GridImageObject(id++, x = x, y = top + snapY + snapY / 2, grid = chessPieces, gx = 5, gy = 1))
// White pawns
result.add(GridImageObject(id++, x = x, y = bottom - snapY - snapY / 2, grid = chessPieces, gx = 5, gy = 0))
// Black pieces
result.add(GridImageObject(id++, x = x, y = top + snapY / 2, grid = chessPieces, gx = pieceGX, gy = 1))
// White pieces
result.add(GridImageObject(id++, x = x, y = bottom - snapY / 2, grid = chessPieces, gx = pieceGX, gy = 0))
}
return result
}
private val regularChess = GameVariation(
"chess",
"Chess",
minPlayers = 2, // For hand and brain, the "brain" should join as spectators (enable spec chat!)
maxPlayers = 2,
grids = listOf(chessPieces),
backgroundObjects = listOf(
ImageObject(-1, 400, 300, path = "backgrounds/khaki.jpg", draggable = false),
ImageObject(-2, 400, 300, path = "chessBoard.jpg", draggable = false),
),
playingObjects = chessPieces(0),
specialAreas = listOf(boardArea),
specialPoints = listOf(),
avatarPositions = VerticalAvatarPositions(70, 3),
mirrorX = playingAreaWidth/2,
mirrorY = playingAreaHeight/2,
playerColors = listOf(GameVariation.chessBlack, GameVariation.chessWhite)
)
private val seirawanChess = GameVariation(
"seirawan",
"Seirawan Chess",
minPlayers = 2, // For hand and brain, the "brain" should join as spectators (enable spec chat!)
maxPlayers = 2,
grids = regularChess.grids,
backgroundObjects = regularChess.backgroundObjects,
playingObjects = regularChess.playingObjects + listOf(
GridImageObject(regularChess.playingObjects.size, 350, 30, grid = chessPieces, gx = 6, gy = 1),
GridImageObject(regularChess.playingObjects.size + 1, 450, 30, grid = chessPieces, gx = 7, gy = 1),
GridImageObject(regularChess.playingObjects.size + 2, 350, 570, grid = chessPieces, gx = 6, gy = 0),
GridImageObject(regularChess.playingObjects.size + 3, 450, 570, grid = chessPieces, gx = 7, gy = 0)
),
specialAreas = regularChess.specialAreas,
specialPoints = listOf(),
avatarPositions = regularChess.avatarPositions,
mirrorX = regularChess.mirrorX,
mirrorY = regularChess.mirrorY,
playerColors = regularChess.playerColors,
rules = """
The normal rules of chess apply. However, when you first move each piece
(`Rook`, `Knight`, `Bishop`, `Queen` or `King`), you may additionally place the `Hawk` or `Elephant`
in the newly vacated square.
If you move all of your pieces without placing either (or both) of the extra pieces,
you lose that right.
_The Elephant_
Moves as a `Rook` or a `Knight`.
Also known as a `Chancellor` or `Empress`.
_The Hawk_
Moves as a `Bishop` or `Knight`.
Also known as an `Archbishop` or `Princess`.
Read about [Seirawan Chess on Wikipedia|https://en.wikipedia.org/wiki/Capablanca_chess#Seirawan_chess].
_Similar Variations_
`Seirawan Chess` is similar to [Capablanca Chess|https://en.wikipedia.org/wiki/Capablanca_chess]`.
Capablanca Chess is played on a 10x8 board,
with no pieces in hand. The `Archbishop` and `Chancellor` are situated next to the rooks.
`Birds Chess` is the same a `Capablanca`, but with the `Archbishop` and `Chancellor` placed next to
the `King` and `Queen`.
""".trimIndent()
)
private val duckChess = GameVariation(
"duckChess",
"Duck Chess",
minPlayers = 2, // For hand and brain, the "brain" should join as spectators (enable spec chat!)
maxPlayers = 2,
grids = regularChess.grids,
backgroundObjects = regularChess.backgroundObjects,
playingObjects = regularChess.playingObjects + listOf(
ImageObject(regularChess.playingObjects.size, 100, 300, path = "chessDuck.png")
),
specialAreas = regularChess.specialAreas,
specialPoints = regularChess.specialPoints,
avatarPositions = regularChess.avatarPositions,
mirrorX = regularChess.mirrorX,
mirrorY = regularChess.mirrorY,
playerColors = regularChess.playerColors,
rules = """
A move consists of two parts. First move a `regular` chess piece, then move the `duck`.
It is illegal to move the duck before moving a `regular` piece. It's a `fowl` move!
The `duck` can be placed on any unoccupied square (but it _must_ be moved).
Regular chess pieces cannot move through the `duck`, or land on the same square as the `duck`.
As with regular pieces, the Knight can jump over (or around) the `duck`.
The rules of `Check` and `Checkmate` do not apply. You can move your king into check.
You can castle through check.
To win the game, take your opponent's King.
In the event of `stalemate`, the player unable to move is the victor.
_Quacktics_
In the early game, position the `duck` to prevent your opponent developing.
e.g. block the `Pawn` that opens up a `Bishop`.
Use the `duck` to block your opponent's `King`.
If your `King` has only 1 escape square, beware of `smothered duck mate`.
Place the `duck` where you do _not_ want it on your next turn. Your opponent is forced to move it elsewhere.
Long range pieces (`Queen`, `Rook`, `Bishop`) do not defend well, because the duck can be placed between them
and the piece they are defending.
Therefore the `Knight` may be stronger than the `Bishop`.
Build up multiple threats; the `duck` can't defend again all of them!
Watch Eric Rosen's journey from `novice` to `grand duck wrangler` on
[YouTube|https://www.youtube.com/watch?v=BqvYsPAufB8&list=PLdT3OotRiHJnOXq9TcNJh_xfOvBXncNcP]
""".trimIndent()
)
private val draughtsPieces = Grid("draughtsPieces", "draughtsPieces.png", 4, 1, 46, 46)
private val redPoint = SpecialPoint("redCounters", (boardArea.x + boardArea.width + playingAreaWidth) / 2, 200)
private val bluePoint = SpecialPoint("redCounters", (boardArea.x + boardArea.width + playingAreaWidth) / 2, 400)
private fun draughtsPieces(startIndex: Int = 0): List<GameObject> {
val result = mutableListOf<GameObject>()
var id = startIndex
for (y in 0..2) {
val start = if (y == 1) 0 else 1
val end = if (y == 1) 6 else 7
for (x in (start..end).step(2)) {
result.add(
// Red pieces
FlippableImageObject(
id++,
boardArea.x + x * snapX + snapX / 2,
boardArea.y + y * snapY + snapY / 2,
grid = draughtsPieces, gx = 0, gy = 0, altX = 1, isFaceUp = true
)
)
result.add(
// Blue pieces
FlippableImageObject(
id++,
boardArea.x + boardArea.width - x * snapX - snapX / 2,
boardArea.y + boardArea.height - y * snapY - snapY / 2,
grid = draughtsPieces, gx = 2, gy = 0, altX = 3, isFaceUp = true
)
)
}
// Spare Red pieces
for (i in 0..1) {
result.add(
FlippableImageObject(
id++,
redPoint.x,
redPoint.y,
grid = draughtsPieces, gx = 0, gy = 0, altX = 1, isFaceUp = true
).apply { regen = true }
)
}
// Spare Blue pieces
for (i in 0..1) {
result.add(
FlippableImageObject(
id++,
bluePoint.x,
bluePoint.y,
grid = draughtsPieces, gx = 2, gy = 0, altX = 3, isFaceUp = true
).apply { regen = true }
)
}
}
return result
}
private val draughts = GameVariation(
"draughts",
"Draughts",
minPlayers = 2, // For hand and brain, the "brain" should join as spectators (enable spec chat!)
maxPlayers = 2,
grids = listOf(draughtsPieces),
backgroundObjects = regularChess.backgroundObjects + listOf(
TextObject(-3, 400, 560, text = "Double-Click a piece to create a King")
),
playingObjects = draughtsPieces(1),
specialAreas = regularChess.specialAreas,
specialPoints = listOf(redPoint, bluePoint),
avatarPositions = regularChess.avatarPositions,
mirrorX = regularChess.mirrorX,
mirrorY = regularChess.mirrorY
)
val chess = GameType(
"chessAndDraughts", "Chess & Draughts", "chessBoard.jpg",
listOf(regularChess, duckChess, seirawanChess, draughts)
)