首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >无国籍2048游戏

无国籍2048游戏
EN

Code Review用户
提问于 2015-10-29 15:39:40
回答 1查看 422关注 0票数 5

我创建了一个小Kotlin克隆的2048游戏,是可以在控制台上玩。我的目标是可读性和无状态,没有别的原因,只有实践。考虑到这是一个代码kata,其中我不想改变原来的网格,而是为每个操作返回一个新的排列。

为此,我非常感谢对我的代码的风格、简单性和可读性的评论和评论。性能不是问题。

代码语言:javascript
复制
import java.io.BufferedReader
import java.io.InputStreamReader

const val positiveGameOverMessage = "So sorry, but you won the game."
const val negativeGameOverMessage = "So sorry, but you lost the game."

fun main(args: Array<String>) {
    val grid = arrayOf(
            arrayOf(0, 0, 0, 0),
            arrayOf(0, 0, 0, 0),
            arrayOf(0, 0, 0, 0),
            arrayOf(0, 0, 0, 0)
    )

    val gameOverMessage = run2048(grid)
    println(gameOverMessage)
}

fun run2048(grid: Array<Array<Int>>): String {
    if (isGridSolved(grid))    return positiveGameOverMessage
    else if (isGridFull(grid)) return negativeGameOverMessage

    val populatedGrid = spawnNumber(grid)
    display(populatedGrid)

    return run2048(manipulateGrid(populatedGrid, waitForValidInput()))
}

fun isGridSolved(grid: Array<Array<Int>>): Boolean = grid.any { row -> row.contains(2048) }
fun isGridFull(grid: Array<Array<Int>>): Boolean   = grid.all { row -> !row.contains(0) }

fun spawnNumber(grid: Array<Array<Int>>):Array<Array<Int>> {
    val coordinates = locateSpawnCoordinates(grid)
    val number = generateNumber()

    return updateGrid(grid, coordinates, number)
}

fun locateSpawnCoordinates(grid: Array<Array<Int>>): Pair<Int, Int> {
    val emptyCells = arrayListOf<Pair<Int, Int>>()
    grid.forEachIndexed { x, row ->
        row.forEachIndexed { y, cell ->
            if (cell == 0) emptyCells.add(Pair(x, y))
        }
    }

    return emptyCells[(Math.random() * (emptyCells.size()-1)).toInt()]
}
fun generateNumber(): Int = if (Math.random() > 0.10) 2 else 4

fun updateGrid(grid: Array<Array<Int>>, at: Pair<Int, Int>, value: Int): Array<Array<Int>> {
    val updatedGrid = grid.copyOf()
    updatedGrid[at.first][at.second] = value
    return updatedGrid
}

fun waitForValidInput():String {
    val input = waitForInput()
    return if (isValidInput(input)) input else waitForValidInput()
}
fun isValidInput(input: String): Boolean = arrayOf("a", "s", "d", "w").contains(input)

fun waitForInput(): String {
    val br = BufferedReader(InputStreamReader(System.`in`));
    println("Direction?  ")
    return br.readLine()
}

fun manipulateGrid(grid: Array<Array<Int>>, input: String): Array<Array<Int>> = when (input) {
    "a" -> shiftCellsLeft(grid)
    "s" -> shiftCellsDown(grid)
    "d" -> shiftCellsRight(grid)
    "w" -> shiftCellsUp(grid)
    else -> throw IllegalArgumentException("Expected one of [a, s, d, w]")
}

fun shiftCellsLeft(grid: Array<Array<Int>>): Array<Array<Int>> =
    grid.map { row -> mergeAndOrganizeCells(row) }.toTypedArray()

fun shiftCellsRight(grid: Array<Array<Int>>): Array<Array<Int>> =
    grid.map { row -> mergeAndOrganizeCells(row.reversed().toTypedArray()).reversed().toTypedArray() }.toTypedArray()

fun shiftCellsUp(grid: Array<Array<Int>>): Array<Array<Int>> {
    val rows: Array<Array<Int>> = arrayOf(
            arrayOf(grid[0][0], grid[1][0], grid[2][0], grid[3][0]),
            arrayOf(grid[0][1], grid[1][1], grid[2][1], grid[3][1]),
            arrayOf(grid[0][2], grid[1][2], grid[2][2], grid[3][2]),
            arrayOf(grid[0][3], grid[1][3], grid[2][3], grid[3][3])
    )

    val updatedGrid = grid.copyOf()

    rows.map { row ->
        mergeAndOrganizeCells(row)
    }.forEachIndexed { rowIdx, row ->
        updatedGrid[0][rowIdx] = row[0]
        updatedGrid[1][rowIdx] = row[1]
        updatedGrid[2][rowIdx] = row[2]
        updatedGrid[3][rowIdx] = row[3]
    }

    return updatedGrid
}

fun shiftCellsDown(grid: Array<Array<Int>>): Array<Array<Int>> {
    val rows: Array<Array<Int>> = arrayOf(
            arrayOf(grid[3][0], grid[2][0], grid[1][0], grid[0][0]),
            arrayOf(grid[3][1], grid[2][1], grid[1][1], grid[0][1]),
            arrayOf(grid[3][2], grid[2][2], grid[1][2], grid[0][2]),
            arrayOf(grid[3][3], grid[2][3], grid[1][3], grid[0][3])
    )

    val updatedGrid = grid.copyOf()

    rows.map { row ->
        mergeAndOrganizeCells(row)
    }.forEachIndexed { rowIdx, row ->
        updatedGrid[3][rowIdx] = row[0]
        updatedGrid[2][rowIdx] = row[1]
        updatedGrid[1][rowIdx] = row[2]
        updatedGrid[0][rowIdx] = row[3]
    }

    return updatedGrid
}

fun mergeAndOrganizeCells(row: Array<Int>): Array<Int> = organize(merge(row.copyOf()))

fun merge(row: Array<Int>, idxToMatch: Int = 0, idxToCompare: Int = 1): Array<Int> {
    if (idxToMatch >= row.size)
        return row
    if (idxToCompare >= row.size)
        return merge(row, idxToMatch + 1, idxToMatch + 2)
    if (row[idxToMatch] == 0)
        return merge(row, idxToMatch + 1, idxToMatch + 2)

    if (row[idxToMatch] == row[idxToCompare]) {
        row[idxToMatch] *= 2
        row[idxToCompare] = 0

        return merge(row, idxToMatch + 1, idxToMatch + 2)
    } else {
        return if (row[idxToCompare] != 0) merge(row, idxToMatch + 1, idxToMatch + 2)
               else merge(row, idxToMatch, idxToCompare + 1)
    }
}

fun organize(row: Array<Int>, idxToMatch: Int = 0, idxToCompare: Int = 1): Array<Int> {
    if (idxToMatch >= row.size)
        return row
    if (idxToCompare >= row.size)
        return organize(row, idxToMatch + 1, idxToMatch + 2)
    if (row[idxToMatch] != 0)
        return organize(row, idxToMatch + 1, idxToMatch + 2)

    if (row[idxToCompare] != 0) {
        row[idxToMatch] = row[idxToCompare]
        row[idxToCompare] = 0

        return organize(row, idxToMatch + 1, idxToMatch + 2)
    } else {
        return organize(row, idxToMatch, idxToCompare + 1)
    }
}

fun display(grid: Array<Array<Int>>) {
    val prettyPrintableGrid = grid.map { row ->
        row.map { cell ->
            if (cell == 0) "    " else java.lang.String.format("%4d", cell)
        }
    }

    println("New Grid:")
    prettyPrintableGrid.forEach { row ->
        println("+----+----+----+----+")
        row.forEach { print("|$it") }
        println("|")
    }
    println("+----+----+----+----+")
}

警告:游戏目前只检查网格是否已满,而不是是否没有可玩的移动。这是游戏的一个改进,但我还没有实施,没有好的理由。

EN

回答 1

Code Review用户

回答已采纳

发布于 2016-02-09 17:17:27

首先,如果返回类型是一行并与=一起返回,则不需要指定返回类型。

我要更改的一件事是函数isGridFull()isGridSolved()shiftCells()

当您为网格创建扩展方法/属性时,我认为它更易读:

代码语言:javascript
复制
fun Array<Array<Int>>.isGridSolved() = this.any { row -> row.contains(2048) } 
fun Array<Array<Int>>.isGridFull() = this.all { row -> !row.contains(0) }

然后像这样使用它:

代码语言:javascript
复制
if (grid.isGridSolved())    return positiveGameOverMessage
else if (grid.isGridFull()) return negativeGameOverMessage

一些与Kotlin无关的事情:

我不会使用数组的ints作为你的网格。最好创建一个包含网格的类,并提供像isFull()isSolved()shiftCells()这样的方法。

在类似这样的小型项目中,这不会有多大问题,但后来你的“网格”和一组ints数组之间并没有真正的区别,你可以把它混在一起。

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/109133

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档