Exit Full View

Games Cupboard / gamescupboard-server / src / main / kotlin / uk / co / nickthecoder / gamescupboard / server / games / Bananagrams.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.PeelCommand
import uk.co.nickthecoder.gamescupboard.server.commands.ShuffleCommand
import uk.co.nickthecoder.gamescupboard.server.commands.TakeCommand

private const val smallTileSize = 24
private const val margin = 10
private const val left = margin
private const val top = margin

/**
 * The point where the "bag" of tiles is located.
 */
private val bagPoint = SpecialPoint("bag", 101, playingAreaHeight - 21)

/**
 * Avatars are at the top of the screen.
 */
private val avatarPositions = HorizontalAvatarPositions(26)

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

/**
 * The main playing area. Tiles are always face up here.
 */
private val playArea = SpecialArea(
    "tileGrid",
    AreaType.PUBLIC,
    left,
    top,
    playingAreaWidth,
    playingAreaHeight - top - handArea.height,
    changeZOrder = ChangeZOrder.TOP,
    snap = RectangularSnapToGrid(smallTileSize, smallTileSize),
    warnUsingTint = false
)


private val letterTiles =
    Grid("pieces", "letterTiles.png", across = 7, down = 4, itemWidth = smallTileSize, itemHeight = smallTileSize)


/**
 * Tile frequency (taken from Wikipedia)
 *
 * * 2: J, K, Q, X, Z
 * * 3: B, C, F, H, M, P, V, W, Y
 * * 4: G
 * * 5: L
 * * 6: D, S, U
 * * 8: N
 * * 9: T, R
 * * 11: O
 * * 12: I
 * * 13: A
 * * 18: E
 */
private fun MutableList<GameObject>.banagramsTiles() {

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

    // Place all pieces on the "bag" SpecialPoint. These are unsorted!

    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) {
                add(
                    FlippableImageObject(
                        size,
                        bagPoint.x,
                        bagPoint.y,
                        grid = letterTiles, gx = x, gy = y,
                        altX = 0, altY = 0, isFaceUp = false
                    )
                )
            }
        }
    }
}

private val bananagramsRules = """
    _Bananagrams_
    
    See the [Wikipedia article for details|https://en.wikipedia.org/wiki/Bananagrams].
    
    All players are dealt anywhere from 11 to 21 tiles (depending on the number of players).
    
    All players simultaneously start building crossword-like structure of intersecting
    words. All words must join together (but not to other players' tiles).
    
    If a player is finding it hard to place any letter, they may place one tile back in the bunch,
    and call out "Dump", taking 3 tiles from the bunch.
    
    When a player has used up all their tiles, they call out "Peel", and the other players
    take a tile from the bunch.
    
    The game ends when a player calls "Peel", and there are insufficient tiles left in the bunch
    for all other players to take a tile.
    
    If a player is found to use a misspelled word, or their tiles are not all connected,
    the that player looses, and the remaining players continue to play.
    """.trimIndent()

private val regularBananagrams = GameVariation(
    "bananagrams",
    "Bananagrams",
    minPlayers = 2,
    maxPlayers = 6,

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

    playingObjects = mutableListOf<GameObject>().apply { banagramsTiles() },
    foregroundObjects = listOf(
        PieceAtPointCounter(0, bagPoint.x + 30, bagPoint.y, bagPoint.name)
    ),
    backgroundObjects = listOf(
        ImageObject(- 1, 400, 300, path = "backgrounds/bananagrams.jpg", draggable = false)
    ),

    avatarPositions = avatarPositions,
    commands = listOf(
        ShuffleCommand(bagPoint),
        DealCommand(bagPoint, handArea, 50),
        TakeCommand(bagPoint, handArea, 50),
        PeelCommand(bagPoint, handArea)
    ),
    commandPrototypes = listOf(
        CommandPrototype("Deal", "deal", parameters = listOf("21")),
        CommandPrototype("Take", "take"),
        CommandPrototype("Peel", "peel", isComplete = true),
        CommandPrototype("Dump", "take", parameters = listOf("3"), isComplete = true),
        CommandPrototype("Shuffle", "shuffle", isComplete = true)
    ),
    rules = bananagramsRules
)

val bananagrams = GameType(
    "bananagrams", "Bananagrams", "bananagrams.png",
    listOf(regularBananagrams)
)