├── README.md ├── .gitignore ├── week5 ├── src │ ├── main │ │ └── kotlin │ │ │ ├── games │ │ │ ├── game │ │ │ │ └── Game.kt │ │ │ ├── gameOfFifteen │ │ │ │ ├── GameOfFifteenInitializer.kt │ │ │ │ ├── GameOfFifteenHelper.kt │ │ │ │ └── GameOfFifteen.kt │ │ │ ├── ui │ │ │ │ ├── PlayGameOfFifteen.kt │ │ │ │ ├── PlayGame2048.kt │ │ │ │ └── PlayGame.kt │ │ │ └── game2048 │ │ │ │ ├── Game2048Initializer.kt │ │ │ │ ├── Game2048Helper.kt │ │ │ │ └── Game2048.kt │ │ │ └── board │ │ │ ├── Board.kt │ │ │ └── BoardImpl.kt │ └── test │ │ └── kotlin │ │ └── games │ │ ├── game2048 │ │ ├── TestGame2048Helper.kt │ │ └── TestGame2048.kt │ │ ├── gameOfFifteen │ │ ├── TestGameOfFifteenHelper.kt │ │ └── TestGameOfFifteen.kt │ │ └── board │ │ ├── TestGameBoard.kt │ │ └── TestSquareBoard.kt └── pom.xml ├── week2 ├── src │ ├── main │ │ └── kotlin │ │ │ └── mastermind │ │ │ ├── evaluateGuess.kt │ │ │ └── playMastermind.kt │ └── test │ │ └── kotlin │ │ └── mastermind │ │ └── MastermindTests.kt └── pom.xml ├── week3 ├── src │ ├── main │ │ └── kotlin │ │ │ ├── taxipark │ │ │ ├── TaxiPark.kt │ │ │ └── TaxiParkTask.kt │ │ │ └── nicestring │ │ │ └── NiceString.kt │ └── test │ │ └── kotlin │ │ ├── nicestring │ │ └── TestNiceStrings.kt │ │ └── taxipark │ │ ├── TaxiParkTestUtil.kt │ │ └── TestTaxiPark.kt └── pom.xml ├── week4 ├── pom.xml └── src │ ├── main │ └── kotlin │ │ ├── board │ │ ├── Board.kt │ │ └── BoardImpl.kt │ │ └── rationals │ │ └── Rational.kt │ └── test │ └── kotlin │ └── board │ ├── TestGameBoard.kt │ └── TestSquareBoard.kt └── pom.xml /README.md: -------------------------------------------------------------------------------- 1 | Programming assignments given in Kotlin for Java Developers course by JetBrains at https://www.coursera.org 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | arget 2 | *.iml 3 | *.ipr 4 | *.iws 5 | .idea 6 | .DS_Store 7 | .classpath 8 | .project 9 | .settings 10 | .checkstyle 11 | -------------------------------------------------------------------------------- /week5/src/main/kotlin/games/game/Game.kt: -------------------------------------------------------------------------------- 1 | package games.game 2 | 3 | import board.Direction 4 | 5 | interface Game { 6 | fun initialize() 7 | fun canMove(): Boolean 8 | fun hasWon(): Boolean 9 | fun processMove(direction: Direction) 10 | operator fun get(i: Int, j: Int): Int? 11 | } 12 | -------------------------------------------------------------------------------- /week2/src/main/kotlin/mastermind/evaluateGuess.kt: -------------------------------------------------------------------------------- 1 | package mastermind 2 | 3 | data class Evaluation(val positions: Int, val letters: Int) 4 | 5 | fun evaluateGuess(secret: String, guess: String): Evaluation { 6 | val positions = secret.zip(guess).count { p -> p.first == p.second } 7 | val commonLetters = "ABCDEF".sumBy { ch -> Math.min(secret.count { it == ch }, guess.count { it == ch }) } 8 | return Evaluation(positions, commonLetters - positions) 9 | } 10 | -------------------------------------------------------------------------------- /week3/src/main/kotlin/taxipark/TaxiPark.kt: -------------------------------------------------------------------------------- 1 | package taxipark 2 | 3 | data class TaxiPark( 4 | val allDrivers: Set, 5 | val allPassengers: Set, 6 | val trips: List 7 | ) 8 | 9 | data class Driver(val name: String) 10 | data class Passenger(val name: String) 11 | 12 | data class Trip( 13 | val driver: Driver, 14 | val passengers: Set, 15 | val duration: Int, 16 | val distance: Double, 17 | val discount: Double? = null 18 | ) { 19 | val cost: Double 20 | get() = (1 - (discount ?: 0.0)) * (duration + distance) 21 | } 22 | -------------------------------------------------------------------------------- /week5/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | com.github.dkoval 8 | kotlin-for-java-developers 9 | 0.1.0-SNAPSHOT 10 | ../pom.xml 11 | 12 | 13 | kotlin-for-java-developers-week5 14 | 15 | -------------------------------------------------------------------------------- /week2/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | kotlin-for-java-developers 8 | com.github.dkoval 9 | 0.1.0-SNAPSHOT 10 | ../pom.xml 11 | 12 | 13 | kotlin-for-java-developers-week2 14 | 15 | 16 | -------------------------------------------------------------------------------- /week4/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | com.github.dkoval 8 | kotlin-for-java-developers 9 | 0.1.0-SNAPSHOT 10 | ../pom.xml 11 | 12 | 13 | kotlin-for-java-developers-week4 14 | 15 | 16 | -------------------------------------------------------------------------------- /week3/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.github.dkoval 9 | kotlin-for-java-developers 10 | 0.1.0-SNAPSHOT 11 | ../pom.xml 12 | 13 | 14 | kotlin-for-java-developers-week3 15 | 16 | -------------------------------------------------------------------------------- /week5/src/main/kotlin/games/gameOfFifteen/GameOfFifteenInitializer.kt: -------------------------------------------------------------------------------- 1 | package games.gameOfFifteen 2 | 3 | import java.util.* 4 | 5 | interface GameOfFifteenInitializer { 6 | /* 7 | * Even permutation of numbers 1..15 8 | * used to initialized first 15 cells on a board 9 | * (the last cell is empty) 10 | */ 11 | val initialPermutation: List 12 | } 13 | 14 | class RandomGameInitializer : GameOfFifteenInitializer { 15 | override val initialPermutation by lazy { 16 | val random = Random() 17 | var permutation: List 18 | do { 19 | permutation = (1..15).shuffled(random) 20 | } while (!isEven(permutation)) 21 | permutation 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /week3/src/test/kotlin/nicestring/TestNiceStrings.kt: -------------------------------------------------------------------------------- 1 | package nicestring 2 | 3 | import org.junit.Assert 4 | import org.junit.Test 5 | 6 | class TestNiceStrings { 7 | 8 | private fun testNiceString(string: String, expected: Boolean) { 9 | Assert.assertEquals("Wrong result for \"$string\".isNice()", expected, string.isNice()) 10 | } 11 | 12 | @Test 13 | fun testExample1() = testNiceString("bac", false) 14 | 15 | @Test 16 | fun testExample2() = testNiceString("aza", false) 17 | 18 | @Test 19 | fun testExample3() = testNiceString("abaca", false) 20 | 21 | @Test 22 | fun testExample4() = testNiceString("baaa", true) 23 | 24 | @Test 25 | fun testExample5() = testNiceString("aaab", true) 26 | } -------------------------------------------------------------------------------- /week5/src/main/kotlin/games/ui/PlayGameOfFifteen.kt: -------------------------------------------------------------------------------- 1 | package games.ui 2 | 3 | import games.gameOfFifteen.newGameOfFifteen 4 | import java.awt.Color 5 | 6 | object GameOfFifteenSettings : GameSettings("Game of fifteen", Color(0x909090)) { 7 | private val emptyColor = Color(0x787878) 8 | private val firstColor = Color(0xC8C8C8) 9 | private val secondColor = Color(0xCCCCFF) 10 | private val foregroundColor = Color(0x545AA7) 11 | 12 | override fun getBackgroundColor(value: Int) = when { 13 | value == 0 -> emptyColor 14 | ((value - 1) / 4 + value % 4) % 2 == 0 -> firstColor 15 | else -> secondColor 16 | } 17 | 18 | override fun getForegroundColor(value: Int) = foregroundColor 19 | } 20 | 21 | fun main(args: Array) { 22 | playGame(newGameOfFifteen(), GameOfFifteenSettings) 23 | } -------------------------------------------------------------------------------- /week5/src/main/kotlin/games/gameOfFifteen/GameOfFifteenHelper.kt: -------------------------------------------------------------------------------- 1 | package games.gameOfFifteen 2 | 3 | /* 4 | * This function should return the parity of the permutation. 5 | * true - the permutation is even 6 | * false - the permutation is odd 7 | * https://en.wikipedia.org/wiki/Parity_of_a_permutation 8 | 9 | * If the game of fifteen is started with the wrong parity, you can't get the correct result 10 | * (numbers sorted in the right order, empty cell at last). 11 | * Thus the initial permutation should be correct. 12 | */ 13 | fun isEven(permutation: List): Boolean { 14 | var count = 0 15 | for (i in 0 until permutation.size - 1) { 16 | for (j in (i + 1) until permutation.size) { 17 | if (permutation[i] > permutation[j]) count++ 18 | } 19 | } 20 | return (count % 2) == 0 21 | } 22 | -------------------------------------------------------------------------------- /week5/src/main/kotlin/games/ui/PlayGame2048.kt: -------------------------------------------------------------------------------- 1 | package games.ui 2 | 3 | import games.game2048.newGame2048 4 | import java.awt.Color 5 | 6 | object Game2048Settings : GameSettings("Game 2048", Color(0xbbada0)) { 7 | private val emptyColor = Color(0xcdc1b4) 8 | private val colors: Map = run { 9 | val colors = listOf( 10 | 0xeee4da, 0xede0c8, 0xf2b179, 0xf59563, 0xf67c5f, 0xf65e3b, 11 | 0xedcf72, 0xedcc61, 0xedc850, 0xedc53f, 0xedc22e) 12 | 13 | val values: List = (1..11).map { Math.pow(2.0, it.toDouble()).toInt() } 14 | values.zip(colors.map { Color(it) }).toMap() 15 | } 16 | 17 | override fun getBackgroundColor(value: Int) = colors[value] ?: emptyColor 18 | override fun getForegroundColor(value: Int) = if (value < 16) Color(0x776e65) else Color(0xf9f6f2) 19 | } 20 | 21 | 22 | fun main(args: Array) { 23 | playGame(newGame2048(), Game2048Settings) 24 | } -------------------------------------------------------------------------------- /week5/src/test/kotlin/games/game2048/TestGame2048Helper.kt: -------------------------------------------------------------------------------- 1 | package games.game2048 2 | 3 | import org.junit.Assert 4 | import org.junit.Test 5 | 6 | class TestGame2048Helper { 7 | @Test 8 | fun testSample1() = testMerge(listOf("a", "a", "b"), listOf("aa", "b")) 9 | 10 | @Test 11 | fun testSample2() = testMerge(listOf("a", null), listOf("a")) 12 | 13 | @Test 14 | fun testSample3() = testMerge(listOf("b", null, "a", "a"), listOf("b", "aa")) 15 | 16 | @Test 17 | fun testSample4() = testMerge(listOf("a", "a", null, "a"), listOf("aa", "a")) 18 | 19 | @Test 20 | fun testSample5() = testMerge(listOf("a", null, "a", "a"), listOf("aa", "a")) 21 | 22 | private fun testMerge(input: List, expected: List) { 23 | val result = input.moveAndMergeEqual { it.repeat(2) } 24 | Assert.assertEquals("Wrong result for $input.moveAndMergeEqual()", 25 | expected, result) 26 | } 27 | } -------------------------------------------------------------------------------- /week3/src/main/kotlin/nicestring/NiceString.kt: -------------------------------------------------------------------------------- 1 | package nicestring 2 | 3 | fun String.isNice(): Boolean { 4 | val predicates = listOf( 5 | ::doesNotContainPredefinedSubstrings, 6 | ::containsAtLeastThreeVowels, 7 | ::containsDoubleLetter 8 | ) 9 | return predicates.count { run(it) } >= 2 10 | } 11 | 12 | private fun String.doesNotContainPredefinedSubstrings(): Boolean { 13 | val containsPredefinedStrings = this.contains("bu", true) 14 | || this.contains("ba", true) 15 | || this.contains("be", true) 16 | return !containsPredefinedStrings 17 | } 18 | 19 | private fun String.containsAtLeastThreeVowels(): Boolean { 20 | return this.count { it == 'a' || it == 'e' || it == 'i' || it == 'o' || it == 'u' } >= 3 21 | } 22 | 23 | private fun String.containsDoubleLetter(): Boolean { 24 | for (i in 0 until this.length - 1) { 25 | if (this[i] == this[i + 1]) return true 26 | } 27 | return false 28 | } 29 | -------------------------------------------------------------------------------- /week5/src/main/kotlin/games/game2048/Game2048Initializer.kt: -------------------------------------------------------------------------------- 1 | package games.game2048 2 | 3 | import board.Cell 4 | import board.GameBoard 5 | import java.util.* 6 | 7 | interface Game2048Initializer { 8 | fun nextValue(board: GameBoard): Pair? 9 | } 10 | 11 | object RandomGame2048Initializer : Game2048Initializer { 12 | private val random = Random() 13 | private fun generateRandomStartValue(): Int = 14 | if (random.nextInt(10) == 9) 4 else 2 15 | 16 | /* 17 | * Generate a random value and a random cell (among free cells) 18 | * that given value should be added to. 19 | * The value should be 2 for 90% cases, and 4 for the rest of the cases. 20 | * Use the 'generateRandomStartValue' function above. 21 | * If the board is full return null. 22 | */ 23 | override fun nextValue(board: GameBoard): Pair? { 24 | return board.find { it -> it == null }?.let { cell -> Pair(cell, generateRandomStartValue()) } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /week5/src/test/kotlin/games/gameOfFifteen/TestGameOfFifteenHelper.kt: -------------------------------------------------------------------------------- 1 | package games.gameOfFifteen 2 | 3 | import org.junit.Assert 4 | import org.junit.Test 5 | 6 | class TestGameOfFifteenHelper { 7 | private fun testPermutation(permutation: List, isEven: Boolean) { 8 | Assert.assertEquals("This permutation should be ${if (isEven) "even" else "odd"}: $permutation", isEven, 9 | isEven(permutation)) 10 | } 11 | 12 | @Test 13 | fun testExample0() = testPermutation((1..15).toList(), isEven = true) 14 | 15 | @Test 16 | fun testExample1() = testPermutation(listOf(1, 2, 3, 4), isEven = true) 17 | 18 | @Test 19 | fun testExample2() = testPermutation(listOf(2, 1, 4, 3), isEven = true) 20 | 21 | @Test 22 | fun testExample3() = testPermutation(listOf(4, 3, 2, 1), isEven = true) 23 | 24 | @Test 25 | fun testExample5() = testPermutation(listOf(1, 2, 4, 3), isEven = false) 26 | 27 | @Test 28 | fun testExample6() = testPermutation(listOf(1, 4, 3, 2), isEven = false) 29 | } -------------------------------------------------------------------------------- /week2/src/test/kotlin/mastermind/MastermindTests.kt: -------------------------------------------------------------------------------- 1 | package mastermind 2 | 3 | import org.junit.Assert 4 | import org.junit.Test 5 | 6 | class MastermindTests { 7 | 8 | private fun testEvaluation(secret: String, guess: String, positions: Int, letters: Int) { 9 | val expected = Evaluation(positions, letters) 10 | val evaluation = evaluateGuess(secret, guess) 11 | Assert.assertEquals("Wrong evaluation for secret=$secret, guess=$guess", 12 | expected, evaluation) 13 | } 14 | 15 | // simple 16 | @Test 17 | fun testEqual() = testEvaluation("ABCD", "ABCD", 4, 0) 18 | 19 | @Test 20 | fun testOnlyLetters() = testEvaluation("DCBA", "ABCD", 0, 4) 21 | 22 | @Test 23 | fun testSwap() = testEvaluation("ABCD", "ABDC", 2, 2) 24 | 25 | @Test 26 | fun testPositions() = testEvaluation("ABCD", "ABCF", 3, 0) 27 | 28 | @Test 29 | fun testLetters() = testEvaluation("DAEF", "FECA", 0, 3) 30 | 31 | 32 | // repeated letters 33 | @Test 34 | fun testSample() = testEvaluation("AABC", "ADFE", 1, 0) 35 | } -------------------------------------------------------------------------------- /week4/src/main/kotlin/board/Board.kt: -------------------------------------------------------------------------------- 1 | package board 2 | 3 | interface Cell { 4 | val i: Int 5 | val j: Int 6 | } 7 | 8 | enum class Direction { 9 | UP, DOWN, RIGHT, LEFT; 10 | 11 | fun reversed() = when (this) { 12 | UP -> DOWN 13 | DOWN -> UP 14 | RIGHT -> LEFT 15 | LEFT -> RIGHT 16 | } 17 | } 18 | 19 | interface SquareBoard { 20 | val width: Int 21 | 22 | fun getCellOrNull(i: Int, j: Int): Cell? 23 | fun getCell(i: Int, j: Int): Cell 24 | 25 | fun getAllCells(): Collection 26 | 27 | fun getRow(i: Int, jRange: IntProgression): List 28 | fun getColumn(iRange: IntProgression, j: Int): List 29 | 30 | fun Cell.getNeighbour(direction: Direction): Cell? 31 | } 32 | 33 | interface GameBoard : SquareBoard { 34 | 35 | operator fun get(cell: Cell): T? 36 | operator fun set(cell: Cell, value: T?) 37 | 38 | fun filter(predicate: (T?) -> Boolean): Collection 39 | fun find(predicate: (T?) -> Boolean): Cell? 40 | fun any(predicate: (T?) -> Boolean): Boolean 41 | fun all(predicate: (T?) -> Boolean): Boolean 42 | } -------------------------------------------------------------------------------- /week5/src/main/kotlin/board/Board.kt: -------------------------------------------------------------------------------- 1 | package board 2 | 3 | interface Cell { 4 | val i: Int 5 | val j: Int 6 | } 7 | 8 | enum class Direction { 9 | UP, DOWN, RIGHT, LEFT; 10 | 11 | fun reversed() = when (this) { 12 | UP -> DOWN 13 | DOWN -> UP 14 | RIGHT -> LEFT 15 | LEFT -> RIGHT 16 | } 17 | } 18 | 19 | interface SquareBoard { 20 | val width: Int 21 | 22 | fun getCellOrNull(i: Int, j: Int): Cell? 23 | fun getCell(i: Int, j: Int): Cell 24 | 25 | fun getAllCells(): Collection 26 | 27 | fun getRow(i: Int, jRange: IntProgression): List 28 | fun getColumn(iRange: IntProgression, j: Int): List 29 | 30 | fun Cell.getNeighbour(direction: Direction): Cell? 31 | } 32 | 33 | interface GameBoard : SquareBoard { 34 | 35 | operator fun get(cell: Cell): T? 36 | operator fun set(cell: Cell, value: T?) 37 | 38 | fun filter(predicate: (T?) -> Boolean): Collection 39 | fun find(predicate: (T?) -> Boolean): Cell? 40 | fun any(predicate: (T?) -> Boolean): Boolean 41 | fun all(predicate: (T?) -> Boolean): Boolean 42 | } -------------------------------------------------------------------------------- /week5/src/main/kotlin/games/game2048/Game2048Helper.kt: -------------------------------------------------------------------------------- 1 | package games.game2048 2 | 3 | /* 4 | * This function moves all the non-null elements to the beginning of the list (by removing nulls) and merges equal elements. 5 | * The parameter 'double' specifies the way how to merge equal elements: 6 | * it returns a new element that should be present in the result list instead of two merged elements. 7 | * 8 | * If the function double("a") returns "aa", 9 | * then the function moveAndMergeEqual transforms the input in the following way: 10 | * a, a, b -> aa, b 11 | * a, null -> a 12 | * b, null, a, a -> b, aa 13 | * a, a, null, a -> aa, a 14 | * a, null, a, a -> aa, a 15 | */ 16 | fun List.moveAndMergeEqual(double: (T) -> T): List = asSequence() 17 | .filterNotNull() 18 | .fold(mutableListOf()) { acc, current -> 19 | val next = if (acc.isNotEmpty() && acc.last() == current) { 20 | acc.removeAt(acc.size - 1) 21 | double(current) 22 | } else { 23 | current 24 | } 25 | acc += next 26 | acc 27 | } 28 | -------------------------------------------------------------------------------- /week4/src/test/kotlin/board/TestGameBoard.kt: -------------------------------------------------------------------------------- 1 | package board 2 | 3 | import org.junit.Assert 4 | import org.junit.Test 5 | 6 | class TestGameBoard { 7 | operator fun GameBoard.get(i: Int, j: Int) = get(getCell(i, j)) 8 | operator fun GameBoard.set(i: Int, j: Int, value: T) = set(getCell(i, j), value) 9 | 10 | @Test 11 | fun testGetAndSetElement() { 12 | val gameBoard = createGameBoard(2) 13 | gameBoard[1, 1] = 'a' 14 | Assert.assertEquals('a', gameBoard[1, 1]) 15 | } 16 | 17 | @Test 18 | fun testFilter() { 19 | val gameBoard = createGameBoard(2) 20 | gameBoard[1, 1] = 'a' 21 | gameBoard[1, 2] = 'b' 22 | val cells = gameBoard.filter { it == 'a' } 23 | Assert.assertEquals(1, cells.size) 24 | val cell = cells.first() 25 | Assert.assertEquals(1, cell.i) 26 | Assert.assertEquals(1, cell.j) 27 | } 28 | 29 | @Test 30 | fun testAll() { 31 | val gameBoard = createGameBoard(2) 32 | gameBoard[1, 1] = 'a' 33 | gameBoard[1, 2] = 'a' 34 | Assert.assertFalse(gameBoard.all { it == 'a' }) 35 | gameBoard[2, 1] = 'a' 36 | gameBoard[2, 2] = 'a' 37 | Assert.assertTrue(gameBoard.all { it == 'a' }) 38 | } 39 | 40 | @Test 41 | fun testAny() { 42 | val gameBoard = createGameBoard(2) 43 | gameBoard[1, 1] = 'a' 44 | gameBoard[1, 2] = 'b' 45 | Assert.assertTrue(gameBoard.any { it in 'a'..'b' }) 46 | Assert.assertTrue(gameBoard.any { it == null }) 47 | } 48 | } -------------------------------------------------------------------------------- /week5/src/test/kotlin/games/board/TestGameBoard.kt: -------------------------------------------------------------------------------- 1 | package games.board 2 | 3 | import board.GameBoard 4 | import board.createGameBoard 5 | import org.junit.Assert 6 | import org.junit.Test 7 | 8 | class TestGameBoard { 9 | operator fun GameBoard.get(i: Int, j: Int) = get(getCell(i, j)) 10 | operator fun GameBoard.set(i: Int, j: Int, value: T) = set(getCell(i, j), value) 11 | 12 | @Test 13 | fun testGetAndSetElement() { 14 | val gameBoard = createGameBoard(2) 15 | gameBoard[1, 1] = 'a' 16 | Assert.assertEquals('a', gameBoard[1, 1]) 17 | } 18 | 19 | @Test 20 | fun testFilter() { 21 | val gameBoard = createGameBoard(2) 22 | gameBoard[1, 1] = 'a' 23 | gameBoard[1, 2] = 'b' 24 | val cells = gameBoard.filter { it == 'a' } 25 | Assert.assertEquals(1, cells.size) 26 | val cell = cells.first() 27 | Assert.assertEquals(1, cell.i) 28 | Assert.assertEquals(1, cell.j) 29 | } 30 | 31 | @Test 32 | fun testAll() { 33 | val gameBoard = createGameBoard(2) 34 | gameBoard[1, 1] = 'a' 35 | gameBoard[1, 2] = 'a' 36 | Assert.assertFalse(gameBoard.all { it == 'a' }) 37 | gameBoard[2, 1] = 'a' 38 | gameBoard[2, 2] = 'a' 39 | Assert.assertTrue(gameBoard.all { it == 'a' }) 40 | } 41 | 42 | @Test 43 | fun testAny() { 44 | val gameBoard = createGameBoard(2) 45 | gameBoard[1, 1] = 'a' 46 | gameBoard[1, 2] = 'b' 47 | Assert.assertTrue(gameBoard.any { it in 'a'..'b' }) 48 | Assert.assertTrue(gameBoard.any { it == null }) 49 | } 50 | } -------------------------------------------------------------------------------- /week3/src/test/kotlin/taxipark/TaxiParkTestUtil.kt: -------------------------------------------------------------------------------- 1 | package taxipark 2 | 3 | fun driver(i: Int) = Driver("D-$i") 4 | fun passenger(i: Int) = Passenger("P-$i") 5 | 6 | fun drivers(indices: List) = indices.map(::driver).toSet() 7 | fun drivers(range: IntRange) = drivers(range.toList()) 8 | fun drivers(vararg indices: Int) = drivers(indices.toList()) 9 | 10 | fun passengers(indices: List) = indices.map(::passenger).toSet() 11 | fun passengers(range: IntRange) = passengers(range.toList()) 12 | fun passengers(vararg indices: Int) = passengers(indices.toList()) 13 | 14 | fun taxiPark(driverIndexes: IntRange, passengerIndexes: IntRange, vararg trips: Trip) = 15 | TaxiPark(drivers(driverIndexes), passengers(passengerIndexes), trips.toList()) 16 | 17 | fun trip(driverIndex: Int, passengerIndexes: List, duration: Int = 10, distance: Double = 3.0, discount: Double? = null) = 18 | Trip(driver(driverIndex), passengers(passengerIndexes), duration, distance, discount) 19 | 20 | fun trip(driverIndex: Int, passenger: Int, duration: Int = 10, distance: Double = 3.0, discount: Double? = null) = 21 | Trip(driver(driverIndex), passengers(passenger), duration, distance, discount) 22 | 23 | fun TaxiPark.display() = buildString { 24 | appendln() 25 | appendln("Taxi park:") 26 | appendln("Drivers: ${allDrivers.joinToString { it.name }}") 27 | appendln("Passengers: ${allPassengers.joinToString { it.name }}") 28 | appendln("Trips: ${trips.joinToString { it.display() }}") 29 | } 30 | 31 | fun Trip.display(): String { 32 | val discountText = discount?.let { ", $it" } ?: "" 33 | return "(${driver.name}, ${passengers.joinToString(prefix = "[", postfix = "]") { it.name }}, $duration, $distance$discountText)" 34 | } 35 | -------------------------------------------------------------------------------- /week2/src/main/kotlin/mastermind/playMastermind.kt: -------------------------------------------------------------------------------- 1 | package mastermind 2 | 3 | import java.util.* 4 | 5 | val ALPHABET = 'A'..'F' 6 | val CODE_LENGTH = 4 7 | 8 | fun main(args: Array) { 9 | playBullsAndCows() 10 | } 11 | 12 | fun playBullsAndCows( 13 | secret: String = generateSecret() 14 | ) { 15 | val scanner = Scanner(System.`in`) 16 | var evaluation: Evaluation 17 | 18 | do { 19 | print("Your guess: ") 20 | var guess = scanner.next() 21 | while (hasErrorsInInput(guess)) { 22 | println("Incorrect input: $guess. " + 23 | "It should consist of ${CODE_LENGTH} digits. " + 24 | "Try again.") 25 | guess = scanner.next() 26 | } 27 | evaluation = evaluateGuess(secret, guess) 28 | if (evaluation.isComplete()) { 29 | println("You are correct!") 30 | } else { 31 | println("Positions: ${evaluation.positions}; letters: ${evaluation.letters}.") 32 | } 33 | 34 | } while (!evaluation.isComplete()) 35 | } 36 | 37 | fun Evaluation.isComplete(): Boolean = positions == CODE_LENGTH 38 | 39 | fun hasErrorsInInput(guess: String): Boolean { 40 | val possibleLetters = ALPHABET.toSet() 41 | return guess.length != CODE_LENGTH || guess.any { it !in possibleLetters } 42 | } 43 | 44 | fun generateSecret(differentLetters: Boolean = false): String { 45 | val chars = ALPHABET.toMutableList() 46 | val random = Random() 47 | return buildString { 48 | for (i in 1..CODE_LENGTH) { 49 | val letter = chars[random.nextInt(chars.size)] 50 | append(letter) 51 | if (differentLetters) { 52 | chars.remove(letter) 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /week5/src/main/kotlin/games/gameOfFifteen/GameOfFifteen.kt: -------------------------------------------------------------------------------- 1 | package games.gameOfFifteen 2 | 3 | import board.Direction 4 | import board.GameBoard 5 | import board.createGameBoard 6 | import games.game.Game 7 | 8 | /* 9 | * Implement the Game of Fifteen (https://en.wikipedia.org/wiki/15_puzzle). 10 | * When you finish, you can play the game by executing 'PlayGameOfFifteen' 11 | * (or choosing the corresponding run configuration). 12 | */ 13 | fun newGameOfFifteen(initializer: GameOfFifteenInitializer = RandomGameInitializer()): Game = 14 | GameOfFifteen(initializer) 15 | 16 | class GameOfFifteen(private val initializer: GameOfFifteenInitializer) : Game { 17 | private val board = createGameBoard(4) 18 | 19 | override fun initialize() { 20 | board.initialize(initializer) 21 | } 22 | 23 | override fun canMove(): Boolean = true 24 | 25 | override fun hasWon(): Boolean { 26 | var seqNum = 1 27 | return board.all { it -> it == null || it == seqNum++ } 28 | } 29 | 30 | override fun processMove(direction: Direction) { 31 | with(board) { 32 | find { it == null }?.also { cell -> 33 | cell.getNeighbour(direction.reversed())?.also { neighbour -> 34 | this[cell] = this[neighbour] 35 | this[neighbour] = null 36 | } 37 | } 38 | } 39 | } 40 | 41 | override fun get(i: Int, j: Int): Int? = board.run { 42 | val cell = getCell(i, j) 43 | this[cell] 44 | } 45 | } 46 | 47 | fun GameBoard.initialize(initializer: GameOfFifteenInitializer) { 48 | initializer.initialPermutation.forEachIndexed { index, value -> 49 | val i = index / width + 1 50 | val j = index % width + 1 51 | val cell = getCell(i, j) 52 | this[cell] = value 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /week4/src/test/kotlin/board/TestSquareBoard.kt: -------------------------------------------------------------------------------- 1 | package board 2 | 3 | import org.junit.Assert 4 | import org.junit.Test 5 | 6 | class TestSquareBoard { 7 | 8 | private fun Cell?.asString() = if (this != null) "($i, $j)" else "" 9 | 10 | private fun Collection.asString() = joinToString(prefix = "[", postfix = "]") { it.asString() } 11 | 12 | @Test 13 | fun testAllCells() { 14 | val board = createSquareBoard(2) 15 | val cells = board.getAllCells().sortedWith(compareBy { it.i }.thenBy { it.j }) 16 | Assert.assertEquals("[(1, 1), (1, 2), (2, 1), (2, 2)]", cells.asString()) 17 | } 18 | 19 | @Test 20 | fun testCell() { 21 | val board = createSquareBoard(2) 22 | val cell = board.getCellOrNull(1, 2) 23 | Assert.assertEquals(1, cell?.i) 24 | Assert.assertEquals(2, cell?.j) 25 | } 26 | 27 | @Test 28 | fun testNoCell() { 29 | val board = createSquareBoard(2) 30 | val cell = board.getCellOrNull(3, 3) 31 | Assert.assertEquals(null, cell) 32 | } 33 | 34 | @Test 35 | fun testRow() { 36 | val board = createSquareBoard(2) 37 | val row = board.getRow(1, 1..2) 38 | Assert.assertEquals("[(1, 1), (1, 2)]", row.asString()) 39 | } 40 | 41 | @Test 42 | fun testRowReversed() { 43 | val board = createSquareBoard(2) 44 | val row = board.getRow(1, 2 downTo 1) 45 | Assert.assertEquals("[(1, 2), (1, 1)]", row.asString()) 46 | } 47 | 48 | @Test 49 | fun testRowWrongRange() { 50 | val board = createSquareBoard(2) 51 | val row = board.getRow(1, 1..10) 52 | Assert.assertEquals("[(1, 1), (1, 2)]", row.asString()) 53 | } 54 | 55 | @Test 56 | fun testNeighbour() { 57 | val board = createSquareBoard(2) 58 | with(board) { 59 | val cell = getCellOrNull(1, 1) 60 | Assert.assertNotNull(cell) 61 | Assert.assertEquals(null, cell!!.getNeighbour(Direction.UP)) 62 | Assert.assertEquals(null, cell.getNeighbour(Direction.LEFT)) 63 | Assert.assertEquals("(2, 1)", cell.getNeighbour(Direction.DOWN).asString()) 64 | Assert.assertEquals("(1, 2)", cell.getNeighbour(Direction.RIGHT).asString()) 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /week5/src/test/kotlin/games/board/TestSquareBoard.kt: -------------------------------------------------------------------------------- 1 | package games.board 2 | 3 | import board.Cell 4 | import board.createSquareBoard 5 | import board.Direction.* 6 | import org.junit.Assert 7 | import org.junit.Test 8 | 9 | class TestSquareBoard { 10 | 11 | private fun Cell?.asString() = if (this != null) "($i, $j)" else "" 12 | 13 | private fun Collection.asString() = joinToString(prefix = "[", postfix = "]") { it.asString() } 14 | 15 | @Test 16 | fun testAllCells() { 17 | val board = createSquareBoard(2) 18 | val cells = board.getAllCells().sortedWith(compareBy { it.i }.thenBy { it.j }) 19 | Assert.assertEquals("[(1, 1), (1, 2), (2, 1), (2, 2)]", cells.asString()) 20 | } 21 | 22 | @Test 23 | fun testCell() { 24 | val board = createSquareBoard(2) 25 | val cell = board.getCellOrNull(1, 2) 26 | Assert.assertEquals(1, cell?.i) 27 | Assert.assertEquals(2, cell?.j) 28 | } 29 | 30 | @Test 31 | fun testNoCell() { 32 | val board = createSquareBoard(2) 33 | val cell = board.getCellOrNull(3, 3) 34 | Assert.assertEquals(null, cell) 35 | } 36 | 37 | @Test 38 | fun testRow() { 39 | val board = createSquareBoard(2) 40 | val row = board.getRow(1, 1..2) 41 | Assert.assertEquals("[(1, 1), (1, 2)]", row.asString()) 42 | } 43 | 44 | @Test 45 | fun testRowReversed() { 46 | val board = createSquareBoard(2) 47 | val row = board.getRow(1, 2 downTo 1) 48 | Assert.assertEquals("[(1, 2), (1, 1)]", row.asString()) 49 | } 50 | 51 | @Test 52 | fun testRowWrongRange() { 53 | val board = createSquareBoard(2) 54 | val row = board.getRow(1, 1..2) 55 | Assert.assertEquals("[(1, 1), (1, 2)]", row.asString()) 56 | } 57 | 58 | @Test 59 | fun testNeighbour() { 60 | val board = createSquareBoard(2) 61 | with(board) { 62 | val cell = getCellOrNull(1, 1) 63 | Assert.assertNotNull(cell) 64 | Assert.assertEquals(null, cell!!.getNeighbour(UP)) 65 | Assert.assertEquals(null, cell.getNeighbour(LEFT)) 66 | Assert.assertEquals("(2, 1)", cell.getNeighbour(DOWN).asString()) 67 | Assert.assertEquals("(1, 2)", cell.getNeighbour(RIGHT).asString()) 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.github.dkoval 7 | kotlin-for-java-developers 8 | 0.1.0-SNAPSHOT 9 | pom 10 | 11 | 12 | UTF-8 13 | 1.3.10 14 | official 15 | 4.12 16 | 17 | 18 | 19 | week2 20 | week3 21 | week4 22 | week5 23 | 24 | 25 | 26 | 27 | org.jetbrains.kotlin 28 | kotlin-stdlib 29 | ${kotlin.version} 30 | 31 | 32 | org.jetbrains.kotlin 33 | kotlin-test-junit 34 | ${kotlin.version} 35 | test 36 | 37 | 38 | junit 39 | junit 40 | ${junit.version} 41 | test 42 | 43 | 44 | 45 | 46 | ${project.basedir}/src/main/kotlin 47 | ${project.basedir}/src/test/kotlin 48 | 49 | 50 | org.jetbrains.kotlin 51 | kotlin-maven-plugin 52 | ${kotlin.version} 53 | 54 | 55 | compile 56 | 57 | compile 58 | 59 | 60 | 61 | test-compile 62 | 63 | test-compile 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /week3/src/main/kotlin/taxipark/TaxiParkTask.kt: -------------------------------------------------------------------------------- 1 | package taxipark 2 | 3 | /* 4 | * Task #1. Find all the drivers who performed no trips. 5 | */ 6 | fun TaxiPark.findFakeDrivers(): Set = 7 | allDrivers.subtract( 8 | trips.map { it.driver }.toSet() 9 | ) 10 | 11 | /* 12 | * Task #2. Find all the clients who completed at least the given number of trips. 13 | */ 14 | fun TaxiPark.findFaithfulPassengers(minTrips: Int): Set = 15 | if (minTrips == 0) allPassengers 16 | else trips.flatMap { trip -> trip.passengers.map { passenger -> passenger to trip } } 17 | .groupBy { it.first } 18 | .filter { (_, trips) -> trips.size >= minTrips } 19 | .keys 20 | 21 | /* 22 | * Task #3. Find all the passengers, who were taken by a given driver more than once. 23 | */ 24 | fun TaxiPark.findFrequentPassengers(driver: Driver): Set = 25 | trips.filter { trip -> trip.driver == driver } 26 | .flatMap { trip -> trip.passengers.map { passenger -> passenger to trip.driver } } 27 | .groupBy { it.first } 28 | .filter { (_, drivers) -> drivers.size > 1 } 29 | .keys 30 | 31 | /* 32 | * Task #4. Find the passengers who had a discount for majority of their trips. 33 | */ 34 | fun TaxiPark.findSmartPassengers(): Set = 35 | trips.flatMap { trip -> trip.passengers.map { passenger -> passenger to trip } } 36 | .groupBy { it.first } 37 | .mapValues { (_, list) -> list.map { it.second } } 38 | .mapValues { (_, list) -> list.partition { trip -> (trip.discount ?: 0.0) > 0.0 } } 39 | .filter { (_, pair) -> pair.first.size > pair.second.size } 40 | .keys 41 | 42 | /* 43 | * Task #5. Find the most frequent trip duration among minute periods 0..9, 10..19, 20..29, and so on. 44 | * Return any period if many are the most frequent, return `null` if there're no trips. 45 | */ 46 | fun TaxiPark.findTheMostFrequentTripDurationPeriod(): IntRange? { 47 | val bucket = trips.map { it.duration } 48 | .groupBy { it / 10 } 49 | .maxBy { (_, durations) -> durations.size } 50 | ?.key 51 | 52 | return bucket?.let { 53 | val start = it * 10 54 | val end = start + 9 55 | IntRange(start, end) 56 | } 57 | } 58 | 59 | /* 60 | * Task #6. 61 | * Check whether 20% of the drivers contribute 80% of the income. 62 | */ 63 | fun TaxiPark.checkParetoPrinciple(): Boolean = 64 | if (trips.isEmpty()) false 65 | else { 66 | val descSortedIncomeByDriver = trips.groupBy { it.driver } 67 | .mapValues { (_, trips) -> trips.sumByDouble { it.cost } } 68 | .entries 69 | .sortedByDescending { it.value } 70 | 71 | val pareto20Threshold = allDrivers.size / 5 // 20% of the drivers 72 | val pareto80Threshold = trips.sumByDouble { it.cost } * 0.8 // 80% of the income 73 | descSortedIncomeByDriver.take(pareto20Threshold).sumByDouble { it.value } >= pareto80Threshold 74 | } 75 | -------------------------------------------------------------------------------- /week5/src/test/kotlin/games/gameOfFifteen/TestGameOfFifteen.kt: -------------------------------------------------------------------------------- 1 | package games.gameOfFifteen 2 | 3 | import board.Direction 4 | import board.Direction.* 5 | import games.game.Game 6 | import org.junit.Assert 7 | import org.junit.Test 8 | 9 | class TestGameOfFifteen { 10 | private fun Game.asString() = 11 | (1..4).joinToString("\n") { i -> 12 | (1..4).joinToString(" ") { j -> 13 | get(i, j)?.let { "%2d".format(it) } ?: " -" 14 | } 15 | } 16 | 17 | class TestGameInitializer( 18 | override val initialPermutation: List 19 | ) : GameOfFifteenInitializer 20 | 21 | private fun testGame(initialPermutation: List, moves: List) { 22 | val game = newGameOfFifteen(TestGameInitializer(initialPermutation)) 23 | game.initialize() 24 | 25 | for ((index, move) in moves.withIndex()) { 26 | if (move.direction == null) continue 27 | // checking the state after each move 28 | Assert.assertTrue("The move for game of fifteen should be always possible", game.canMove()) 29 | game.processMove(move.direction) 30 | val prev = moves[index - 1].board 31 | Assert.assertEquals("Wrong result after pressing ${move.direction} " + 32 | "for\n$prev\n", 33 | move.board, game.asString()) 34 | } 35 | } 36 | 37 | data class Move( 38 | val direction: Direction?, 39 | val initialBoard: String 40 | ) { 41 | val board: String = initialBoard.trimMargin() 42 | } 43 | 44 | @Test 45 | fun testMoves() { 46 | testGame(listOf(3, 6, 13, 15, 7, 5, 8, 4, 14, 11, 12, 1, 10, 9, 2), 47 | listOf( 48 | Move(null, """ 49 | | 3 6 13 15 50 | | 7 5 8 4 51 | |14 11 12 1 52 | |10 9 2 -"""), 53 | Move(RIGHT, """ 54 | | 3 6 13 15 55 | | 7 5 8 4 56 | |14 11 12 1 57 | |10 9 - 2"""), 58 | Move(DOWN, """ 59 | | 3 6 13 15 60 | | 7 5 8 4 61 | |14 11 - 1 62 | |10 9 12 2"""), 63 | Move(LEFT, """ 64 | | 3 6 13 15 65 | | 7 5 8 4 66 | |14 11 1 - 67 | |10 9 12 2"""), 68 | Move(UP, """ 69 | | 3 6 13 15 70 | | 7 5 8 4 71 | |14 11 1 2 72 | |10 9 12 -"""), 73 | Move(RIGHT, """ 74 | | 3 6 13 15 75 | | 7 5 8 4 76 | |14 11 1 2 77 | |10 9 - 12""") 78 | )) 79 | } 80 | 81 | @Test 82 | fun testWinning() { 83 | val game = newGameOfFifteen(TestGameInitializer( 84 | (1..15).toList())) 85 | game.initialize() 86 | Assert.assertTrue("The player should win when the numbers are in order", 87 | game.hasWon()) 88 | } 89 | } -------------------------------------------------------------------------------- /week3/src/test/kotlin/taxipark/TestTaxiPark.kt: -------------------------------------------------------------------------------- 1 | package taxipark 2 | 3 | import org.junit.Assert 4 | import org.junit.Test 5 | 6 | class TestTaxiPark { 7 | @Test 8 | fun testFakeDrivers() { 9 | val tp = taxiPark(1..3, 1..2, trip(1, 1), trip(1, 2)) 10 | Assert.assertEquals("Wrong result for 'findFakeDrivers()'." + tp.display(), 11 | drivers(2, 3).toSet(), tp.findFakeDrivers()) 12 | } 13 | 14 | @Test 15 | fun testFaithfulPassengers() { 16 | val tp = taxiPark(1..3, 1..5, trip(1, 1), trip(2, 1), trip(1, 4), trip(3, 4), trip(1, 5), trip(2, 5), trip(2, 2)) 17 | Assert.assertEquals("Wrong result for 'findFaithfulPassengers()'. MinTrips: 2." + tp.display(), 18 | passengers(1, 4, 5), tp.findFaithfulPassengers(2)) 19 | } 20 | 21 | @Test 22 | fun testFrequentPassengers() { 23 | val tp = taxiPark(1..2, 1..4, trip(1, 1), trip(1, 1), trip(1, listOf(1, 3)), trip(1, 3), trip(1, 2), trip(2, 2)) 24 | Assert.assertEquals("Wrong result for 'findFrequentPassengers()'. Driver: ${driver(1).name}." + tp.display(), 25 | passengers(1, 3), tp.findFrequentPassengers(driver(1))) 26 | } 27 | 28 | @Test 29 | fun testSmartPassengers() { 30 | val tp = taxiPark(1..2, 1..2, trip(1, 1, discount = 0.1), trip(2, 2)) 31 | Assert.assertEquals("Wrong result for 'findSmartPassengers()'." + tp.display(), 32 | passengers(1), tp.findSmartPassengers()) 33 | } 34 | 35 | @Test 36 | fun testTheMostFrequentTripDuration() { 37 | val tp = taxiPark(1..3, 1..5, trip(1, 1, duration = 10), trip(3, 4, duration = 30), 38 | trip(1, 2, duration = 20), trip(2, 3, duration = 35)) 39 | Assert.assertEquals("Wrong result for 'findTheMostFrequentTripDurationPeriod()'.", 40 | 30..39, tp.findTheMostFrequentTripDurationPeriod()) 41 | } 42 | 43 | @Test 44 | fun testParetoPrincipleSucceeds() { 45 | val tp = taxiPark(1..5, 1..4, 46 | trip(1, 1, 20, 20.0), 47 | trip(1, 2, 20, 20.0), 48 | trip(1, 3, 20, 20.0), 49 | trip(1, 4, 20, 20.0), 50 | trip(2, 1, 20, 20.0)) 51 | Assert.assertEquals( 52 | "Wrong result for 'checkParetoPrinciple()'." + tp.display(), 53 | true, tp.checkParetoPrinciple()) 54 | } 55 | 56 | @Test 57 | fun testParetoPrincipleFails() { 58 | val tp = taxiPark(1..5, 1..4, 59 | trip(1, 1, 20, 20.0), 60 | trip(1, 2, 20, 20.0), 61 | trip(1, 3, 20, 20.0), 62 | trip(2, 4, 20, 20.0), 63 | trip(3, 1, 20, 20.0)) 64 | Assert.assertEquals( 65 | "Wrong result for 'checkParetoPrinciple()'." + tp.display(), 66 | false, tp.checkParetoPrinciple()) 67 | } 68 | 69 | @Test 70 | fun testParetoPrincipleNoTrips() { 71 | val tp = taxiPark(1..5, 1..4) 72 | Assert.assertEquals( 73 | "Wrong result for 'checkParetoPrinciple()'." + tp.display(), 74 | false, tp.checkParetoPrinciple()) 75 | } 76 | } -------------------------------------------------------------------------------- /week5/src/main/kotlin/games/game2048/Game2048.kt: -------------------------------------------------------------------------------- 1 | package games.game2048 2 | 3 | import board.Cell 4 | import board.Direction 5 | import board.GameBoard 6 | import board.createGameBoard 7 | import games.game.Game 8 | 9 | /* 10 | * Your task is to implement the game 2048 https://en.wikipedia.org/wiki/2048_(video_game) 11 | * Implement the utility methods below. 12 | * 13 | * After implementing it you can try to play the game executing 'PlayGame2048' 14 | * (or choosing the corresponding run configuration). 15 | */ 16 | fun newGame2048(initializer: Game2048Initializer = RandomGame2048Initializer): Game = 17 | Game2048(initializer) 18 | 19 | class Game2048(private val initializer: Game2048Initializer) : Game { 20 | private val board = createGameBoard(4) 21 | 22 | override fun initialize() { 23 | repeat(2) { 24 | board.addNewValue(initializer) 25 | } 26 | } 27 | 28 | override fun canMove() = board.any { it == null } 29 | 30 | override fun hasWon() = board.any { it == 2048 } 31 | 32 | override fun processMove(direction: Direction) { 33 | if (board.moveValues(direction)) { 34 | board.addNewValue(initializer) 35 | } 36 | } 37 | 38 | override fun get(i: Int, j: Int): Int? = board.run { get(getCell(i, j)) } 39 | } 40 | 41 | /* 42 | * Add a new value produced by 'initializer' to a specified cell in a board. 43 | */ 44 | fun GameBoard.addNewValue(initializer: Game2048Initializer) { 45 | initializer.nextValue(this)?.also { (cell, value) -> this[cell] = value } 46 | } 47 | 48 | /* 49 | * Move values in a specified rowOrColumn only. 50 | * Use the helper function 'moveAndMergeEqual' (in Game2048Helper.kt). 51 | * The values should be moved to the beginning of the row (or column), in the same manner as in the function 'moveAndMergeEqual'. 52 | * Return 'true' if the values were moved and 'false' otherwise. 53 | */ 54 | fun GameBoard.moveValuesInRowOrColumn(rowOrColumn: List): Boolean { 55 | val movedAndMerged = rowOrColumn 56 | .map { cell -> this[cell] } 57 | .moveAndMergeEqual { value -> value * 2 } 58 | 59 | val moved = movedAndMerged.isNotEmpty() && (movedAndMerged.size != rowOrColumn.size) 60 | if (moved) { 61 | rowOrColumn.forEach { cell -> this[cell] = null } 62 | movedAndMerged.forEachIndexed { index, value -> 63 | val cell = rowOrColumn[index] 64 | this[cell] = value 65 | } 66 | } 67 | return moved 68 | } 69 | 70 | /* 71 | * Move values by the rules of the 2048 game to the specified direction. 72 | * Use the 'moveValuesInRowOrColumn' function above. 73 | * Return 'true' if the values were moved and 'false' otherwise. 74 | */ 75 | fun GameBoard.moveValues(direction: Direction): Boolean { 76 | fun moveAt(rowOrColumnAt: (Int) -> List): Boolean { 77 | return 1.rangeTo(width).fold(false) { moved, index -> 78 | val rowOrColumn = rowOrColumnAt(index) 79 | moveValuesInRowOrColumn(rowOrColumn) || moved 80 | } 81 | } 82 | return when (direction) { 83 | Direction.LEFT -> moveAt { i -> getRow(i, 1.rangeTo(width)) } 84 | Direction.RIGHT -> moveAt { i -> getRow(i, width.downTo(1)) } 85 | Direction.UP -> moveAt { j -> getColumn(1.rangeTo(width), j) } 86 | Direction.DOWN -> moveAt { j -> getColumn(width.downTo(1), j) } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /week5/src/main/kotlin/board/BoardImpl.kt: -------------------------------------------------------------------------------- 1 | package board 2 | 3 | data class CellImpl(override val i: Int, override val j: Int, var value: T? = null) : Cell 4 | 5 | open class SquareBoardImpl(final override val width: Int) : SquareBoard { 6 | protected val cells: List> 7 | 8 | init { 9 | if (width <= 0) throw IllegalArgumentException("Width must be a positive number, was: $width") 10 | cells = IntRange(1, width).flatMap { i -> IntRange(1, width).map { j -> CellImpl(i, j) } } 11 | } 12 | 13 | override fun getCellOrNull(i: Int, j: Int): Cell? { 14 | return if (i <= width && i <= width) cells[to1DIndex(i, j)] else null 15 | } 16 | 17 | override fun getCell(i: Int, j: Int): Cell { 18 | return getCellOrNull(i, j) ?: throw IllegalArgumentException( 19 | "Cell ($i, $j) is out of $width x $width board boundaries") 20 | } 21 | 22 | protected fun to1DIndex(row: Int, col: Int): Int { 23 | require(row >= 1) { "row must be 1-based index, was: $row" } 24 | require(col >= 1) { "col must be 1-based index, was: $col" } 25 | return (row - 1) * width + (col - 1) 26 | } 27 | 28 | override fun getAllCells(): Collection { 29 | return cells 30 | } 31 | 32 | override fun getRow(i: Int, jRange: IntProgression): List { 33 | return getRange(i, jRange) { row, col -> to1DIndex(row, col) } 34 | } 35 | 36 | override fun getColumn(iRange: IntProgression, j: Int): List { 37 | return getRange(j, iRange) { col, row -> to1DIndex(row, col) } 38 | } 39 | 40 | private fun getRange(fixedCoord: Int, range: IntProgression, indexer: (Int, Int) -> Int): List { 41 | val (start, end) = restrictToBoardBoundaries(range) 42 | return IntProgression.fromClosedRange(start, end, range.step).map { fluentCoord -> 43 | val index = indexer(fixedCoord, fluentCoord) 44 | cells[index] 45 | } 46 | } 47 | 48 | private fun restrictToBoardBoundaries(range: IntProgression): Pair { 49 | return if (range.step > 0) 50 | Pair(maxOf(range.first, 1), minOf(range.last, width)) 51 | else 52 | Pair(minOf(range.first, width), maxOf(range.last, 1)) 53 | } 54 | 55 | override fun Cell.getNeighbour(direction: Direction): Cell? { 56 | return when (direction) { 57 | Direction.UP -> if (this.i > 1) cells[to1DIndex(this.i - 1, this.j)] else null 58 | Direction.DOWN -> if (this.i < width) cells[to1DIndex(this.i + 1, this.j)] else null 59 | Direction.LEFT -> if (this.j > 1) cells[to1DIndex(this.i, this.j - 1)] else null 60 | Direction.RIGHT -> if (this.j < width) cells[to1DIndex(this.i, this.j + 1)] else null 61 | } 62 | } 63 | } 64 | 65 | class GameBoardImpl(width: Int) : SquareBoardImpl(width), GameBoard { 66 | 67 | override fun get(cell: Cell): T? { 68 | return cells[to1DIndex(cell.i, cell.j)].value 69 | } 70 | 71 | override fun set(cell: Cell, value: T?) { 72 | cells[to1DIndex(cell.i, cell.j)].value = value 73 | } 74 | 75 | override fun filter(predicate: (T?) -> Boolean): Collection { 76 | return cells.filter(cellMatcher(predicate)) 77 | } 78 | 79 | override fun find(predicate: (T?) -> Boolean): Cell? { 80 | return cells.find(cellMatcher(predicate)) 81 | } 82 | 83 | override fun any(predicate: (T?) -> Boolean): Boolean { 84 | return cells.any(cellMatcher(predicate)) 85 | } 86 | 87 | override fun all(predicate: (T?) -> Boolean): Boolean { 88 | return cells.all(cellMatcher(predicate)) 89 | } 90 | 91 | private fun cellMatcher(predicate: (T?) -> Boolean): (CellImpl) -> Boolean { 92 | return { cell -> predicate(cell.value) } 93 | } 94 | } 95 | 96 | fun createSquareBoard(width: Int): SquareBoard = SquareBoardImpl(width) 97 | fun createGameBoard(width: Int): GameBoard = GameBoardImpl(width) 98 | -------------------------------------------------------------------------------- /week4/src/main/kotlin/board/BoardImpl.kt: -------------------------------------------------------------------------------- 1 | package board 2 | 3 | data class DefaultCell(override val i: Int, override val j: Int, var value: T? = null) : Cell 4 | 5 | open class DefaultSquareBoard(final override val width: Int) : SquareBoard { 6 | protected val cells: List> 7 | 8 | init { 9 | if (width <= 0) throw IllegalArgumentException("Width must be a positive number, was: $width") 10 | cells = IntRange(1, width).flatMap { i -> IntRange(1, width).map { j -> DefaultCell(i, j) } } 11 | } 12 | 13 | override fun getCellOrNull(i: Int, j: Int): Cell? { 14 | return if (i <= width && i <= width) cells[to1DIndex(i, j)] else null 15 | } 16 | 17 | override fun getCell(i: Int, j: Int): Cell { 18 | return getCellOrNull(i, j) ?: throw IllegalArgumentException( 19 | "Cell ($i, $j) is out of $width x $width board boundaries") 20 | } 21 | 22 | protected fun to1DIndex(row: Int, col: Int): Int { 23 | require(row >= 1) { "row must be 1-based index, was: $row" } 24 | require(col >= 1) { "col must be 1-based index, was: $col" } 25 | return (row - 1) * width + (col - 1) 26 | } 27 | 28 | override fun getAllCells(): Collection { 29 | return cells 30 | } 31 | 32 | override fun getRow(i: Int, jRange: IntProgression): List { 33 | return getRange(i, jRange) { row, col -> to1DIndex(row, col) } 34 | } 35 | 36 | override fun getColumn(iRange: IntProgression, j: Int): List { 37 | return getRange(j, iRange) { col, row -> to1DIndex(row, col) } 38 | } 39 | 40 | private fun getRange(fixedCoord: Int, range: IntProgression, indexer: (Int, Int) -> Int): List { 41 | val (start, end) = restrictToBoardBoundaries(range) 42 | return IntProgression.fromClosedRange(start, end, range.step).map { fluentCoord -> 43 | val index = indexer(fixedCoord, fluentCoord) 44 | cells[index] 45 | } 46 | } 47 | 48 | private fun restrictToBoardBoundaries(range: IntProgression): Pair { 49 | return if (range.step > 0) 50 | Pair(maxOf(range.first, 1), minOf(range.last, width)) 51 | else 52 | Pair(minOf(range.first, width), maxOf(range.last, 1)) 53 | } 54 | 55 | override fun Cell.getNeighbour(direction: Direction): Cell? { 56 | return when (direction) { 57 | Direction.UP -> if (this.i > 1) cells[to1DIndex(this.i - 1, this.j)] else null 58 | Direction.DOWN -> if (this.i < width) cells[to1DIndex(this.i + 1, this.j)] else null 59 | Direction.LEFT -> if (this.j > 1) cells[to1DIndex(this.i, this.j - 1)] else null 60 | Direction.RIGHT -> if (this.j < width) cells[to1DIndex(this.i, this.j + 1)] else null 61 | } 62 | } 63 | } 64 | 65 | class DefaultGameBoard(width: Int) : DefaultSquareBoard(width), GameBoard { 66 | 67 | override fun get(cell: Cell): T? { 68 | return cells[to1DIndex(cell.i, cell.j)].value 69 | } 70 | 71 | override fun set(cell: Cell, value: T?) { 72 | cells[to1DIndex(cell.i, cell.j)].value = value 73 | } 74 | 75 | override fun filter(predicate: (T?) -> Boolean): Collection { 76 | return cells.filter(cellMatcher(predicate)) 77 | } 78 | 79 | override fun find(predicate: (T?) -> Boolean): Cell? { 80 | return cells.find(cellMatcher(predicate)) 81 | } 82 | 83 | override fun any(predicate: (T?) -> Boolean): Boolean { 84 | return cells.any(cellMatcher(predicate)) 85 | } 86 | 87 | override fun all(predicate: (T?) -> Boolean): Boolean { 88 | return cells.all(cellMatcher(predicate)) 89 | } 90 | 91 | private fun cellMatcher(predicate: (T?) -> Boolean): (DefaultCell) -> Boolean { 92 | return { cell -> predicate(cell.value) } 93 | } 94 | } 95 | 96 | fun createSquareBoard(width: Int): SquareBoard = DefaultSquareBoard(width) 97 | fun createGameBoard(width: Int): GameBoard = DefaultGameBoard(width) -------------------------------------------------------------------------------- /week5/src/main/kotlin/games/ui/PlayGame.kt: -------------------------------------------------------------------------------- 1 | // drawing based on https://github.com/bulenkov/2048 2 | package games.ui 3 | 4 | import board.Direction 5 | import games.game.Game 6 | import java.awt.* 7 | import java.awt.event.KeyAdapter 8 | import java.awt.event.KeyEvent 9 | import javax.swing.JFrame 10 | import javax.swing.JPanel 11 | import javax.swing.WindowConstants 12 | 13 | class PlayGame(val game: Game, val settings: GameSettings) : JPanel() { 14 | init { 15 | isFocusable = true 16 | addKeyListener(object : KeyAdapter() { 17 | override fun keyPressed(e: KeyEvent) { 18 | if (game.hasWon() == false && game.canMove()) { 19 | val direction = when (e.keyCode) { 20 | KeyEvent.VK_LEFT -> Direction.LEFT 21 | KeyEvent.VK_RIGHT -> Direction.RIGHT 22 | KeyEvent.VK_DOWN -> Direction.DOWN 23 | KeyEvent.VK_UP -> Direction.UP 24 | else -> null 25 | } 26 | if (direction != null) { 27 | game.processMove(direction) 28 | } 29 | } 30 | repaint() 31 | } 32 | }) 33 | game.initialize() 34 | } 35 | 36 | override fun paint(g: Graphics) { 37 | super.paint(g) 38 | g.color = settings.backgroundColor 39 | g.fillRect(0, 0, this.size.width, this.size.height) 40 | for (y in 1..4) { 41 | for (x in 1..4) { 42 | drawTile(g as Graphics2D, game[y, x] ?: 0, x - 1, y - 1) 43 | } 44 | } 45 | } 46 | 47 | private fun offsetCoors(arg: Int): Int { 48 | return arg * (TILES_MARGIN + TILE_SIZE) + TILES_MARGIN 49 | } 50 | 51 | private fun drawTile(g: Graphics2D, value: Int, x: Int, y: Int) { 52 | g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON) 53 | g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE) 54 | 55 | val xOffset = offsetCoors(x) 56 | val yOffset = offsetCoors(y) 57 | g.color = settings.getBackgroundColor(value) 58 | g.fillRoundRect(xOffset, yOffset, TILE_SIZE, TILE_SIZE, 14, 14) 59 | g.color = settings.getForegroundColor(value) 60 | val size = if (value < 100) 36 else if (value < 1000) 32 else 24 61 | val font = Font(FONT_NAME, Font.BOLD, size) 62 | g.font = font 63 | 64 | val s = value.toString() 65 | val fm = getFontMetrics(font) 66 | 67 | val w = fm.stringWidth(s) 68 | val h = -fm.getLineMetrics(s, g).baselineOffsets[2].toInt() 69 | 70 | if (value != 0) 71 | g.drawString(s, xOffset + (TILE_SIZE - w) / 2, yOffset + TILE_SIZE - (TILE_SIZE - h) / 2 - 2) 72 | 73 | if (game.hasWon() || game.canMove() == false) { 74 | g.color = Color(255, 255, 255, 30) 75 | g.fillRect(0, 0, width, height) 76 | g.color = Color(78, 139, 202) 77 | g.font = Font(FONT_NAME, Font.BOLD, 48) 78 | if (game.hasWon()) { 79 | g.drawString("You won!", 68, 150) 80 | } 81 | if (!game.canMove()) { 82 | g.drawString("Game over!", 45, 160) 83 | } 84 | } 85 | g.font = Font(FONT_NAME, Font.PLAIN, 18) 86 | } 87 | } 88 | 89 | private val FONT_NAME = "Arial" 90 | private val TILE_SIZE = 64 91 | private val TILES_MARGIN = 16 92 | 93 | abstract class GameSettings(val name: String, val backgroundColor: Color) { 94 | abstract fun getBackgroundColor(value: Int): Color 95 | abstract fun getForegroundColor(value: Int): Color 96 | } 97 | 98 | fun playGame(game: Game, settings: GameSettings) { 99 | with(JFrame()) { 100 | title = settings.name 101 | defaultCloseOperation = WindowConstants.EXIT_ON_CLOSE 102 | setSize(340, 400) 103 | isResizable = false 104 | 105 | add(PlayGame(game, settings)) 106 | 107 | setLocationRelativeTo(null) 108 | isVisible = true 109 | } 110 | } -------------------------------------------------------------------------------- /week5/src/test/kotlin/games/game2048/TestGame2048.kt: -------------------------------------------------------------------------------- 1 | package games.game2048 2 | 3 | import board.Cell 4 | import board.Direction 5 | import board.Direction.* 6 | import board.GameBoard 7 | import games.game.Game 8 | import org.junit.Assert 9 | import org.junit.Test 10 | 11 | class TestGame2048 { 12 | private fun Game.asString() = 13 | (1..4).joinToString("\n") { i -> 14 | (1..4).joinToString(" ") { j -> 15 | "${get(i, j) ?: "-"}" 16 | } 17 | } 18 | 19 | class TestGame2048Initializer(moves: List) : Game2048Initializer { 20 | val iterator = moves.iterator() 21 | override fun nextValue(board: GameBoard): Pair { 22 | val move = iterator.next() 23 | return board.getCell(move.position.first, move.position.second) to move.value 24 | } 25 | } 26 | 27 | private fun testGame(moves: List) { 28 | val game = newGame2048(TestGame2048Initializer(moves)) 29 | game.initialize() 30 | run { 31 | // checking the state after initialization 32 | val first = moves[0] 33 | val second = moves[1] 34 | Assert.assertEquals("Wrong result after board initialization " + 35 | "by '${first.value}' at ${first.cell} and " + 36 | "'${second.value}' at ${second.cell}", 37 | second.board, game.asString()) 38 | } 39 | 40 | for ((index, move) in moves.withIndex()) { 41 | if (move.direction == null) continue 42 | // checking the state after each move 43 | game.processMove(move.direction) 44 | val prev = moves[index - 1].board 45 | Assert.assertEquals("Wrong result after moving ${move.direction} " + 46 | "and then adding '${move.value}' to ${move.cell} " + 47 | "for\n$prev\n", 48 | move.board, game.asString()) 49 | } 50 | } 51 | 52 | data class Move( 53 | val position: Pair, 54 | val value: Int, 55 | val direction: Direction?, 56 | val initialBoard: String 57 | ) { 58 | val cell: String 59 | get() = "Cell(${position.first}, ${position.second})" 60 | 61 | val board: String = initialBoard.trimMargin() 62 | } 63 | 64 | @Test 65 | fun testMoves() { 66 | testGame(listOf( 67 | Move(Pair(1, 1), 2, null, """ 68 | |2 - - - 69 | |- - - - 70 | |- - - - 71 | |- - - -"""), 72 | Move(Pair(1, 4), 2, null, """ 73 | |2 - - 2 74 | |- - - - 75 | |- - - - 76 | |- - - -"""), 77 | Move(Pair(3, 2), 4, RIGHT, """ 78 | |- - - 4 79 | |- - - - 80 | |- 4 - - 81 | |- - - -"""), 82 | Move(Pair(4, 2), 2, UP, """ 83 | |- 4 - 4 84 | |- - - - 85 | |- - - - 86 | |- 2 - -"""), 87 | Move(Pair(2, 2), 2, LEFT, """ 88 | |8 - - - 89 | |- 2 - - 90 | |- - - - 91 | |2 - - -"""), 92 | Move(Pair(4, 4), 2, DOWN, """ 93 | |- - - - 94 | |- - - - 95 | |8 - - - 96 | |2 2 - 2"""), 97 | Move(Pair(3, 3), 2, RIGHT, """ 98 | |- - - - 99 | |- - - - 100 | |- - 2 8 101 | |- - 2 4"""), 102 | Move(Pair(1, 2), 4, DOWN, """ 103 | |- 4 - - 104 | |- - - - 105 | |- - - 8 106 | |- - 4 4"""), 107 | Move(Pair(3, 1), 2, RIGHT, """ 108 | |- - - 4 109 | |- - - - 110 | |2 - - 8 111 | |- - - 8"""), 112 | Move(Pair(3, 3), 2, DOWN, """ 113 | |- - - - 114 | |- - - - 115 | |- - 2 4 116 | |2 - - 16"""), 117 | Move(Pair(2, 3), 2, DOWN, """ 118 | |- - - - 119 | |- - 2 - 120 | |- - - 4 121 | |2 - 2 16"""), 122 | Move(Pair(1, 4), 2, RIGHT, """ 123 | |- - - 2 124 | |- - - 2 125 | |- - - 4 126 | |- - 4 16"""), 127 | Move(Pair(3, 2), 2, LEFT, """ 128 | |2 - - - 129 | |2 - - - 130 | |4 2 - - 131 | |4 16 - -"""), 132 | Move(Pair(1, 3), 2, DOWN, """ 133 | |- - 2 - 134 | |- - - - 135 | |4 2 - - 136 | |8 16 - -""") 137 | )) 138 | } 139 | } -------------------------------------------------------------------------------- /week4/src/main/kotlin/rationals/Rational.kt: -------------------------------------------------------------------------------- 1 | package rationals 2 | 3 | import java.lang.IllegalArgumentException 4 | import java.math.BigInteger 5 | import java.util.* 6 | 7 | class Rational(numerator: BigInteger, denominator: BigInteger) : Comparable { 8 | companion object { 9 | val ZERO = Rational(BigInteger.ZERO, BigInteger.ONE) 10 | } 11 | 12 | private val numerator: BigInteger 13 | private val denominator: BigInteger 14 | 15 | constructor(number: Int) : this(number.toBigInteger(), BigInteger.ONE) 16 | constructor(number: Long) : this(number.toBigInteger(), BigInteger.ONE) 17 | constructor(number: BigInteger) : this(number, BigInteger.ONE) 18 | 19 | init { 20 | if (denominator == BigInteger.ZERO) { 21 | throw IllegalArgumentException("Denominator is zero") 22 | } 23 | val (num, den) = normalize(numerator, denominator) 24 | this.numerator = num 25 | this.denominator = den 26 | } 27 | 28 | private fun normalize(numerator: BigInteger, denominator: BigInteger): Pair { 29 | val gcd = gcd(numerator, denominator) 30 | var num = numerator / gcd 31 | var den = denominator / gcd 32 | if (den < BigInteger.ZERO) { 33 | num = -num 34 | den = -den 35 | } 36 | return Pair(num, den) 37 | } 38 | 39 | private fun gcd(m: BigInteger, n: BigInteger): BigInteger { 40 | val a = m.abs() 41 | val b = n.abs() 42 | return if (b == BigInteger.ZERO) a else gcd(b, a % b) 43 | } 44 | 45 | private fun lcm(m: BigInteger, n: BigInteger): BigInteger { 46 | val a = m.abs() 47 | val b = n.abs() 48 | return a * (b / gcd(a, b)) 49 | } 50 | 51 | operator fun unaryMinus(): Rational = Rational(-numerator, denominator) 52 | 53 | operator fun plus(b: Rational): Rational { 54 | val a = this 55 | 56 | // special cases 57 | if (a == ZERO) return b 58 | if (b == ZERO) return a 59 | 60 | // Find gcd of numerators and denominators 61 | val f = gcd(a.numerator, b.numerator) 62 | val g = gcd(a.denominator, b.denominator) 63 | 64 | // add cross-product terms for numerator 65 | val num = f * ((a.numerator / f) * (b.denominator / g) + (b.numerator / f) * (a.denominator / g)) 66 | val den = lcm(a.denominator, b.denominator) 67 | return Rational(num, den) 68 | } 69 | 70 | private fun negate(): Rational = Rational(-numerator, denominator) 71 | 72 | operator fun minus(b: Rational): Rational { 73 | val a = this 74 | return a.plus(b.negate()) 75 | } 76 | 77 | operator fun times(b: Rational): Rational { 78 | val a = this 79 | // reduce p1/q2 and p2/q1, then multiply 80 | val c = Rational(a.numerator, b.denominator) 81 | val d = Rational(b.numerator, a.denominator) 82 | return Rational(c.numerator * d.numerator, c.denominator * d.denominator) 83 | } 84 | 85 | private fun reciprocal(): Rational = Rational(denominator, numerator) 86 | 87 | operator fun div(b: Rational): Rational { 88 | val a = this 89 | return a.times(b.reciprocal()) 90 | } 91 | 92 | override fun toString(): String = 93 | if (denominator == BigInteger.ONE) "$numerator" else "$numerator/$denominator" 94 | 95 | override fun hashCode(): Int { 96 | return Objects.hash(numerator, denominator) 97 | } 98 | 99 | override fun equals(other: Any?): Boolean { 100 | if (this === other) return true 101 | if (other == null || javaClass != other.javaClass) return false 102 | return compareTo(other as Rational) == 0 103 | } 104 | 105 | override fun compareTo(other: Rational): Int { 106 | val lhs = this.numerator * other.denominator 107 | val rhs = this.denominator * other.numerator 108 | return lhs.compareTo(rhs) 109 | } 110 | } 111 | 112 | fun String.toRational(): Rational { 113 | val args = this.split('/', limit = 2) 114 | val num = args[0].toBigInteger() 115 | return if (args.size == 1) 116 | Rational(num) 117 | else { 118 | val den = args[1].toBigInteger() 119 | Rational(num, den) 120 | } 121 | } 122 | 123 | infix fun Int.divBy(number: Int): Rational = Rational(this) / Rational(number) 124 | infix fun Long.divBy(number: Long): Rational = Rational(this) / Rational(number) 125 | infix fun BigInteger.divBy(number: BigInteger): Rational = Rational(this) / Rational(number) 126 | 127 | fun main() { 128 | val r1 = 1 divBy 2 129 | val r2 = 2000000000L divBy 4000000000L 130 | println(r1 == r2) 131 | 132 | println((2 divBy 1).toString() == "2") 133 | 134 | println((-2 divBy 4).toString() == "-1/2") 135 | println("117/1098".toRational().toString() == "13/122") 136 | 137 | println("1/2".toRational() - "1/3".toRational() == "1/6".toRational()) 138 | println("1/2".toRational() + "1/3".toRational() == "5/6".toRational()) 139 | 140 | println(-(1 divBy 2) == (-1 divBy 2)) 141 | 142 | println((1 divBy 2) * (1 divBy 3) == "1/6".toRational()) 143 | println((1 divBy 2) / (1 divBy 4) == "2".toRational()) 144 | 145 | println((1 divBy 2) < (2 divBy 3)) 146 | println((1 divBy 2) in (1 divBy 3)..(2 divBy 3)) 147 | 148 | println( 149 | "912016490186296920119201192141970416029".toBigInteger() divBy 150 | "1824032980372593840238402384283940832058".toBigInteger() == 1 divBy 2 151 | ) 152 | } --------------------------------------------------------------------------------