Added obligatory assignments
This commit is contained in:
90
src/assignment/week5/games/game2048/Game2048.kt
Normal file
90
src/assignment/week5/games/game2048/Game2048.kt
Normal file
@ -0,0 +1,90 @@
|
||||
package games.game2048
|
||||
|
||||
import board.Cell
|
||||
import board.Direction
|
||||
import board.GameBoard
|
||||
import board.createGameBoard
|
||||
import games.game.Game
|
||||
|
||||
/*
|
||||
* Your task is to implement the game 2048 https://en.wikipedia.org/wiki/2048_(video_game).
|
||||
* Implement the utility methods below.
|
||||
*
|
||||
* After implementing it you can try to play the game running 'PlayGame2048'.
|
||||
*/
|
||||
fun newGame2048(initializer: Game2048Initializer<Int> = RandomGame2048Initializer): Game =
|
||||
Game2048(initializer)
|
||||
|
||||
class Game2048(private val initializer: Game2048Initializer<Int>) : Game {
|
||||
private val board = createGameBoard<Int?>(4)
|
||||
|
||||
override fun initialize() {
|
||||
repeat(2) {
|
||||
board.addNewValue(initializer)
|
||||
}
|
||||
}
|
||||
|
||||
override fun canMove() = board.any { it == null }
|
||||
|
||||
override fun hasWon() = board.any { it == 2048 }
|
||||
|
||||
override fun processMove(direction: Direction) {
|
||||
if (board.moveValues(direction)) {
|
||||
board.addNewValue(initializer)
|
||||
}
|
||||
}
|
||||
|
||||
override fun get(i: Int, j: Int): Int? = board.run { get(getCell(i, j)) }
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a new value produced by 'initializer' to a specified cell in a board.
|
||||
*/
|
||||
fun GameBoard<Int?>.addNewValue(initializer: Game2048Initializer<Int>) {
|
||||
val (cell, value) = initializer.nextValue(this) ?: return
|
||||
this[cell] = value
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the values stored in a board,
|
||||
* so that the values were "moved" in a specified rowOrColumn only.
|
||||
* Use the helper function 'moveAndMergeEqual' (in Game2048Helper.kt).
|
||||
* The values should be moved to the beginning of the row (or column),
|
||||
* in the same manner as in the function 'moveAndMergeEqual'.
|
||||
* Return 'true' if the values were moved and 'false' otherwise.
|
||||
*/
|
||||
fun GameBoard<Int?>.moveValuesInRowOrColumn(rowOrColumn: List<Cell>): Boolean {
|
||||
val values = rowOrColumn.map { this[it] }
|
||||
val merged = values.moveAndMergeEqual { it * 2 }
|
||||
|
||||
for ((index, cell) in rowOrColumn.withIndex()) {
|
||||
this[cell] = merged.getOrNull(index)
|
||||
}
|
||||
|
||||
return merged.mapIndexed { index, value -> value != values[index] }.any { it }
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the values stored in a board,
|
||||
* so that the values were "moved" to the specified direction
|
||||
* following the rules of the 2048 game .
|
||||
* Use the 'moveValuesInRowOrColumn' function above.
|
||||
* Return 'true' if the values were moved and 'false' otherwise.
|
||||
*/
|
||||
fun GameBoard<Int?>.moveValues(direction: Direction): Boolean = when (direction) {
|
||||
Direction.UP -> 1.rangeTo(width)
|
||||
.map { j -> moveValuesInRowOrColumn(getColumn(1..width, j)) }
|
||||
.any { it }
|
||||
|
||||
Direction.RIGHT -> 1.rangeTo(width)
|
||||
.map { i -> moveValuesInRowOrColumn(getRow(i, width downTo 1)) }
|
||||
.any { it }
|
||||
|
||||
Direction.DOWN -> 1.rangeTo(width)
|
||||
.map { j -> moveValuesInRowOrColumn(getColumn(width downTo 1, j)) }
|
||||
.any { it }
|
||||
|
||||
Direction.LEFT -> 1.rangeTo(width)
|
||||
.map { i -> moveValuesInRowOrColumn(getRow(i, 1..width)) }
|
||||
.any { it }
|
||||
}
|
41
src/assignment/week5/games/game2048/Game2048Helper.kt
Normal file
41
src/assignment/week5/games/game2048/Game2048Helper.kt
Normal file
@ -0,0 +1,41 @@
|
||||
package games.game2048
|
||||
|
||||
/*
|
||||
* This function moves all the non-null elements to the beginning of the list
|
||||
* (by removing nulls) and merges equal elements.
|
||||
* The parameter 'merge' specifies the way how to merge equal elements:
|
||||
* it returns a new element that should be present in the resulting list
|
||||
* instead of two merged elements.
|
||||
*
|
||||
* If the function 'merge("a")' returns "aa",
|
||||
* then the function 'moveAndMergeEqual' transforms the input in the following way:
|
||||
* a, a, b -> aa, b
|
||||
* a, null -> a
|
||||
* b, null, a, a -> b, aa
|
||||
* a, a, null, a -> aa, a
|
||||
* a, null, a, a -> aa, a
|
||||
*
|
||||
* You can find more examples in 'TestGame2048Helper'.
|
||||
*/
|
||||
fun <T : Any> List<T?>.moveAndMergeEqual(merge: (T) -> T): List<T> {
|
||||
val mutableList = mutableListOf<T>()
|
||||
val oldList = filterNotNull()
|
||||
|
||||
var index = 0
|
||||
while (true) {
|
||||
if (index >= oldList.size) break
|
||||
|
||||
val first = oldList[index]
|
||||
val second = oldList.getOrNull(index + 1)
|
||||
|
||||
if (first == second) {
|
||||
index += 2
|
||||
mutableList.add(merge(first))
|
||||
} else {
|
||||
index += 1
|
||||
mutableList.add(first)
|
||||
}
|
||||
}
|
||||
return mutableList
|
||||
}
|
||||
|
29
src/assignment/week5/games/game2048/Game2048Initializer.kt
Normal file
29
src/assignment/week5/games/game2048/Game2048Initializer.kt
Normal file
@ -0,0 +1,29 @@
|
||||
package games.game2048
|
||||
|
||||
import board.Cell
|
||||
import board.GameBoard
|
||||
import kotlin.random.Random
|
||||
|
||||
interface Game2048Initializer<T> {
|
||||
/*
|
||||
* Specifies the cell and the value that should be added to this cell.
|
||||
*/
|
||||
fun nextValue(board: GameBoard<T?>): Pair<Cell, T>?
|
||||
}
|
||||
|
||||
object RandomGame2048Initializer : Game2048Initializer<Int> {
|
||||
private fun generateRandomStartValue(): Int =
|
||||
if (Random.nextInt(10) == 9) 4 else 2
|
||||
|
||||
/*
|
||||
* Generate a random value and a random cell among free cells
|
||||
* that given value should be added to.
|
||||
* The value should be 2 for 90% cases, and 4 for the rest of the cases.
|
||||
* Use the 'generateRandomStartValue' function above.
|
||||
* If the board is full return null.
|
||||
*/
|
||||
override fun nextValue(board: GameBoard<Int?>): Pair<Cell, Int>? = board
|
||||
.filter { it == null }
|
||||
.randomOrNull()
|
||||
?.to(generateRandomStartValue())
|
||||
}
|
Reference in New Issue
Block a user