Exit Full View

Games Cupboard / gamescupboard-server / src / main / kotlin / uk / co / nickthecoder / gamescupboard / server / games / Scabble.kt

package uk.co.nickthecoder.gamescupboard.server.games

import uk.co.nickthecoder.gamescupboard.common.*
import uk.co.nickthecoder.gamescupboard.server.*
import uk.co.nickthecoder.gamescupboard.server.commands.DealCommand
import uk.co.nickthecoder.gamescupboard.server.commands.ShuffleCommand
import uk.co.nickthecoder.gamescupboard.server.commands.TakeCommand

private const val pieceSize = 34
private const val left = 12
private const val top = 10

/**
 * Avatars are at the top of the screen.
 */
private val avatarPositions = SpacedVerticalAvatarPositions(playingAreaWidth - 130, 155, 50)

/**
 * The area containing each player's hand of tiles.
 * Each player only sees their own tiles (PRIVATE).
 */
private val handArea = SpecialArea("hand", AreaType.PRIVATE, 0, playingAreaHeight - 70, playingAreaWidth - 300, 70)

/**
 * The main playing area. Tiles are always face up here.
 */
private val playArea = SpecialArea(
    "playArea",
    AreaType.PUBLIC,
    left,
    top,
    pieceSize * 15,
    pieceSize * 15,
    changeZOrder = ChangeZOrder.TOP,
    snap = RectangularSnapToGrid(pieceSize, pieceSize),
    warnUsingTint = true
)

/**
 * The point where the "bag" of tiles is located.
 */
private val bagPoint = SpecialPoint("bag", playingAreaWidth - 200, handArea.y + handArea.height / 2)

private val tiles =
    Grid("tiles", "scrabbleTiles.png", across = 7, down = 4, itemWidth = pieceSize, itemHeight = pieceSize)

/**
 * Creates 108 tiles. 6 copies of each type of tile. There are 6 different shapes, each in 6 different colors.
 */
/**
 * Tile frequency (taken from Wikipedia)
 *
 * * 1: J, K, Q, X, Z
 * * 2: B, C, M, P, F, H, V, W, Y, <blank>
 * * 3: G
 * * 4: L, S, U, D
 * * 6: N, R, T
 * * 8: O
 * * 9: A, I
 * * 12: E
 */
private fun MutableList<GameObject>.scrabbleTiles() {

    val frequency = mutableMapOf<Char, Int>()
    frequency.putAll(listOf('J', 'K', 'Q', 'X', 'Z').map { it to 1 })
    frequency.putAll(listOf('[', 'B', 'C', 'M', 'P', 'F', 'H', 'V', 'W', 'Y').map { it to 2 })
    frequency.putAll(listOf('G').map { it to 3 })
    frequency.putAll(listOf('L', 'S', 'U', 'D').map { it to 4 })
    frequency.putAll(listOf('N', 'R', 'T').map { it to 6 })
    frequency.putAll(listOf('O').map { it to 8 })
    frequency.putAll(listOf('A', 'I').map { it to 9 })
    frequency.putAll(listOf('E').map { it to 12 })

    // Place all pieces on the "bag" SpecialPoint. These are unsorted!
    var total = 0
    for (y in 0..3) {
        for (x in 0..6) {
            val letter = 'A' - 1 + x + y * 7
            val count = frequency[letter] ?: 0

            for (c in 0 until count) {
                total ++
                add(
                    FlippableImageObject(
                        size,
                        bagPoint.x,
                        bagPoint.y,
                        grid = tiles, gx = x, gy = y,
                        altX = 0, altY = 0, isFaceUp = false
                    )
                )
            }
        }
    }
}


private val rummyCrossRules = """
    _Scrabble_
    
    See the [Wikipedia article for details|https://en.wikipedia.org/wiki/Scrabble].
    
    Each player is dealt 7 tiles.
    
    Players takes turns placing tiles on the board in a single horizontal or vertical line.
    All tiles in the line(s) must be valid (English) words.
    
    The player takes additional tiles from the bag, to make the total back up to 7.
    
    A player may choose to swap 1 or more of their tiles instead a placing tiles on the board.
    No points are awarded, and the game continues to the next player.
    
    The game ends when the bag is empty, and a player has no more tiles left.
    This player is awarded a finishing bonus : the total points of all tiles
    owned by the other players.
    The other players deduct the total of the tiles' points.
    
    The winner is the player with the highest score.
    
    """.trimIndent()

private val regularScrabble = GameVariation(
    "scrabble",
    "Scrabble",
    minPlayers = 2,
    maxPlayers = 6,

    grids = listOf(tiles),
    specialAreas = listOf(playArea, handArea),
    specialPoints = listOf(bagPoint),

    playingObjects = mutableListOf<GameObject>().apply { scrabbleTiles() },
    backgroundObjects = listOf(
        ImageObject(- 1, 400, 300, path = "backgrounds/scrabble.png", draggable = false)
    ),
    foregroundObjects = listOf(
        PieceAtPointCounter(0, bagPoint.x, bagPoint.y - 40, bagPoint.name)
    ),

    avatarPositions = avatarPositions,
    commands = listOf(
        ShuffleCommand(bagPoint),
        DealCommand(bagPoint, handArea, pieceSize + pieceSize / 2),
        TakeCommand(bagPoint, handArea, pieceSize + pieceSize / 2)
    ),
    commandPrototypes = listOf(
        CommandPrototype("Deal", "deal", parameters = listOf("7"), isComplete = true),
        CommandPrototype("Take", "take"),
        CommandPrototype("Shuffle", "shuffle", isComplete = true)
    ),
    rules = rummyCrossRules
)


val scrabble = GameType(
    "scrabble", "Scrabble", "scrabble.png",
    listOf(regularScrabble)
)