Regenerated project, added placeholders for exercises
This commit is contained in:
parent
b757823579
commit
abd63a217b
|
@ -1,5 +1,5 @@
|
|||
## Helper Functions (#2)
|
||||
|
||||
Write a function `oddWithout1` that creates an infinite sequence of odd numbers
|
||||
that do not contain the digit `1`. In `main`, display to the console the sum of
|
||||
that do not contain the digit `1`. In `main`, display to the console the sum of
|
||||
the first 20 elements of this sequence.
|
|
@ -1,43 +0,0 @@
|
|||
package abstractClassesExercise1
|
||||
|
||||
interface GameElement {
|
||||
val symbol: Char
|
||||
val sharesCell: Boolean
|
||||
fun play(maze: Maze)
|
||||
}
|
||||
|
||||
abstract class StaticElement(
|
||||
override val sharesCell: Boolean
|
||||
) : GameElement {
|
||||
override fun play(maze: Maze) {
|
||||
// Default implementation: do nothing
|
||||
}
|
||||
}
|
||||
|
||||
class Wall : StaticElement(sharesCell = false) {
|
||||
override val symbol get() = '#'
|
||||
}
|
||||
|
||||
class Food : StaticElement(sharesCell = true) {
|
||||
override val symbol get() = '.'
|
||||
}
|
||||
|
||||
abstract class MobileElement : GameElement {
|
||||
override val sharesCell: Boolean
|
||||
get() = true
|
||||
|
||||
abstract fun move(move: Move, maze: Maze): Cell?
|
||||
|
||||
override fun toString() = symbol.toString()
|
||||
}
|
||||
|
||||
enum class Move {
|
||||
UP, RIGHT, DOWN, LEFT, WAIT
|
||||
}
|
||||
|
||||
fun createGameElement(char: Char?): GameElement? = when (char) {
|
||||
'#' -> Wall()
|
||||
'.' -> Food()
|
||||
'R' -> Robot()
|
||||
else -> null
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
package abstractClassesExercise1
|
||||
|
||||
data class Cell(val x: Int, val y: Int)
|
||||
|
||||
interface Maze {
|
||||
val width: Int
|
||||
val height: Int
|
||||
fun all(): Set<GameElement>
|
||||
fun allIn(cell: Cell): Set<GameElement>
|
||||
fun cell(element: GameElement): Cell?
|
||||
fun add(element: GameElement, cell: Cell)
|
||||
fun remove(element: GameElement)
|
||||
}
|
||||
|
||||
class MazeImpl(
|
||||
representation: String
|
||||
) : Maze {
|
||||
override val width: Int
|
||||
override val height: Int
|
||||
|
||||
private val elementMap: List<List<MutableSet<GameElement>>>
|
||||
private val cellMap = mutableMapOf<GameElement, Cell>()
|
||||
|
||||
init {
|
||||
val lines = representation.lines()
|
||||
height = lines.size
|
||||
width = lines.maxBy { it.length }?.length ?: 0
|
||||
elementMap = List(height) {
|
||||
List(width) { mutableSetOf<GameElement>() }
|
||||
}
|
||||
for (y in 0 until height) {
|
||||
for (x in 0 until width) {
|
||||
val ch = lines.getOrNull(y)?.getOrNull(x)
|
||||
val element = createGameElement(ch)
|
||||
if (element != null) {
|
||||
add(element, Cell(x, y))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun elements(cell: Cell): MutableSet<GameElement> {
|
||||
return elementMap[cell.y][cell.x]
|
||||
}
|
||||
|
||||
override fun all(): Set<GameElement> {
|
||||
return cellMap.keys.toSet()
|
||||
}
|
||||
|
||||
override fun allIn(cell: Cell): Set<GameElement> {
|
||||
return elements(cell)
|
||||
}
|
||||
|
||||
override fun cell(element: GameElement): Cell? {
|
||||
return cellMap[element]
|
||||
}
|
||||
|
||||
override fun add(element: GameElement, cell: Cell) {
|
||||
elements(cell) += element
|
||||
cellMap[element] = cell
|
||||
}
|
||||
|
||||
override fun remove(element: GameElement) {
|
||||
val cell = cell(element) ?: return
|
||||
elements(cell) -= element
|
||||
cellMap.remove(element)
|
||||
}
|
||||
|
||||
override fun toString() =
|
||||
elementMap.joinToString("\n") { row ->
|
||||
row.joinToString("") { elements ->
|
||||
"${elements.lastOrNull()?.symbol ?: ' '}"
|
||||
}.trimEnd()
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
package abstractClassesExercise1
|
||||
|
||||
fun Maze.isPassable(cell: Cell): Boolean {
|
||||
if (cell.x !in (0 until width) || cell.y !in (0 until height)) {
|
||||
return false
|
||||
}
|
||||
val cellOccupants = allIn(cell)
|
||||
return cellOccupants.all { it.sharesCell }
|
||||
}
|
||||
|
||||
fun Cell.applyMove(move: Move): Cell =
|
||||
when (move) {
|
||||
Move.WAIT -> Cell(x, y)
|
||||
Move.UP -> Cell(x, y - 1)
|
||||
Move.DOWN -> Cell(x, y + 1)
|
||||
Move.RIGHT -> Cell(x + 1, y)
|
||||
Move.LEFT -> Cell(x - 1, y)
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
package abstractClassesExercise1
|
||||
|
||||
import atomictest.eq
|
||||
|
||||
class Robot : MobileElement() {
|
||||
|
||||
private var eatenFoodItems: Int = 0
|
||||
|
||||
override val symbol: Char
|
||||
get() = 'R'
|
||||
|
||||
override fun play(maze: Maze) {
|
||||
val cell = maze.cell(this) ?: return
|
||||
val cellElements = maze.allIn(cell)
|
||||
cellElements
|
||||
.filterIsInstance<Food>()
|
||||
.forEach { food ->
|
||||
eatenFoodItems++
|
||||
maze.remove(food)
|
||||
}
|
||||
}
|
||||
|
||||
override fun move(move: Move, maze: Maze): Cell? {
|
||||
val currentCell = maze.cell(this) ?: return null
|
||||
val newCell = currentCell.applyMove(move)
|
||||
if (!maze.isPassable(newCell)) return null
|
||||
return newCell
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val mapRepresentation = """
|
||||
#####
|
||||
# #
|
||||
# ##
|
||||
""".trimIndent()
|
||||
val maze = MazeImpl(mapRepresentation)
|
||||
maze.toString() eq mapRepresentation
|
||||
|
||||
val robot = Robot()
|
||||
|
||||
fun updateRobotCell(cell: Cell?) {
|
||||
if (cell != null) {
|
||||
maze.remove(robot)
|
||||
maze.add(robot, cell)
|
||||
}
|
||||
}
|
||||
|
||||
updateRobotCell(Cell(x = 2, y = 1))
|
||||
maze.toString() eq """
|
||||
#####
|
||||
# R #
|
||||
# ##
|
||||
""".trimIndent()
|
||||
|
||||
|
||||
updateRobotCell(robot.move(Move.DOWN, maze))
|
||||
maze.toString() eq """
|
||||
#####
|
||||
# #
|
||||
# R##
|
||||
""".trimIndent()
|
||||
|
||||
// The right move is impossible:
|
||||
val cell = robot.move(Move.RIGHT, maze)
|
||||
cell eq null
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package abstractClassesExercise1
|
||||
|
||||
// type your solution here
|
|
@ -1,18 +1,6 @@
|
|||
type: edu
|
||||
files:
|
||||
- name: src/GameElements.kt
|
||||
visible: true
|
||||
- name: src/Maze.kt
|
||||
- name: src/Task.kt
|
||||
visible: true
|
||||
- name: test/Tests.kt
|
||||
visible: false
|
||||
- name: src/MazeEx.kt
|
||||
visible: true
|
||||
- name: src/Robot.kt
|
||||
visible: true
|
||||
placeholders:
|
||||
- offset: 483
|
||||
length: 160
|
||||
placeholder_text: TODO()
|
||||
feedback_link: |
|
||||
https://docs.google.com/forms/d/e/1FAIpQLSdkaliSwYkjiV21bZl0yP-In2g5p17sAQCfaGjyHx_QYMWTiQ/viewform?usp=pp_url&entry.189755027=Object-Oriented+Programming+%2F+Abstract+Classes+%2F+Exercise1
|
||||
|
|
|
@ -1,128 +1,11 @@
|
|||
package abstractClassesExercise1
|
||||
|
||||
import abstractClassesExercise1.Move.*
|
||||
import org.junit.Assert
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runners.MethodSorters
|
||||
import util.TIMEOUT
|
||||
|
||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
class TestAbstractClassesExercise1 {
|
||||
private fun checkMove(
|
||||
move: Move,
|
||||
initial: String,
|
||||
expected: String
|
||||
) {
|
||||
val maze = MazeImpl(initial.trimIndent())
|
||||
val robot = maze.all().filterIsInstance<Robot>().single()
|
||||
val cell = robot.move(move, maze)
|
||||
if (cell != null) {
|
||||
maze.remove(robot)
|
||||
maze.add(robot, cell)
|
||||
@Test fun testSolution() {
|
||||
//TODO: implement your test here
|
||||
Assert.assertTrue("Tests not implemented for the task", false)
|
||||
}
|
||||
Assert.assertEquals(
|
||||
"Wrong result for\n$initial",
|
||||
expected.trimIndent(),
|
||||
maze.toString()
|
||||
)
|
||||
}
|
||||
|
||||
private fun checkImpossibleMove(
|
||||
move: Move,
|
||||
initial: String
|
||||
) {
|
||||
val maze = MazeImpl(initial.trimIndent())
|
||||
val robot = maze.all().filterIsInstance<Robot>().single()
|
||||
val cell = robot.move(move, maze)
|
||||
|
||||
Assert.assertNull(
|
||||
"The $move move should be impossible for \n$initial",
|
||||
cell
|
||||
)
|
||||
}
|
||||
|
||||
@Test(timeout = TIMEOUT)
|
||||
fun testRight() {
|
||||
checkMove(
|
||||
RIGHT,
|
||||
initial = """
|
||||
#####
|
||||
# #
|
||||
# R #
|
||||
# #
|
||||
#####""",
|
||||
expected = """
|
||||
#####
|
||||
# #
|
||||
# R#
|
||||
# #
|
||||
#####""")
|
||||
}
|
||||
|
||||
@Test(timeout = TIMEOUT)
|
||||
fun testLeft() {
|
||||
checkMove(
|
||||
LEFT,
|
||||
initial = """
|
||||
#####
|
||||
# #
|
||||
# R #
|
||||
# #
|
||||
#####""",
|
||||
expected = """
|
||||
#####
|
||||
# #
|
||||
#R #
|
||||
# #
|
||||
#####""")
|
||||
}
|
||||
|
||||
@Test(timeout = TIMEOUT)
|
||||
fun testUp() {
|
||||
checkMove(
|
||||
UP,
|
||||
initial = """
|
||||
#####
|
||||
# #
|
||||
# R #
|
||||
# #
|
||||
#####""",
|
||||
expected = """
|
||||
#####
|
||||
# R #
|
||||
# #
|
||||
# #
|
||||
#####""")
|
||||
}
|
||||
|
||||
@Test(timeout = TIMEOUT)
|
||||
fun testDown() {
|
||||
checkMove(
|
||||
DOWN,
|
||||
initial = """
|
||||
#####
|
||||
# #
|
||||
# R #
|
||||
# #
|
||||
#####""",
|
||||
expected = """
|
||||
#####
|
||||
# #
|
||||
# #
|
||||
# R #
|
||||
#####""")
|
||||
}
|
||||
|
||||
@Test(timeout = TIMEOUT)
|
||||
fun testImpossibleMoves() {
|
||||
listOf(RIGHT, LEFT, UP, DOWN).forEach {
|
||||
checkImpossibleMove(
|
||||
it,
|
||||
initial = """
|
||||
###
|
||||
#R#
|
||||
###""")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package abstractClassesExercise2
|
||||
|
||||
// type your solution here
|
|
@ -0,0 +1,6 @@
|
|||
type: edu
|
||||
files:
|
||||
- name: src/Task.kt
|
||||
visible: true
|
||||
- name: test/Tests.kt
|
||||
visible: false
|
|
@ -0,0 +1,67 @@
|
|||
## Abstract Classes (#2)
|
||||
|
||||
Make an `enum` called `Note` with values `A`-`G`. Create an `abstract` class
|
||||
`Instrument` that takes a `val` class argument `name: String` which holds the
|
||||
name of the class. Add an `abstract` member function `play(n: Note)`.
|
||||
|
||||
Now inherit `Wind`, `Percussion` and `Stringed` from `Instrument`, overriding
|
||||
`play()` in each case to use `atomictest.Trace` to show the class name, then
|
||||
`blow` for `Wind`, `strike` for `Percussion`, and `pluck` for `Stringed`, and
|
||||
finally the `Note` being played.
|
||||
|
||||
Add a function `tune()` that takes an `Instrument` parameter and plays that
|
||||
`Instrument` using `Note.C`.
|
||||
|
||||
In `main()`, create an `orchestra` which is a `List<Instrument>` containing an
|
||||
instance of each type of `Instrument`. Call `tune()` for each `Instrument` in
|
||||
the `orchestra`, and validate the results using `Trace`.
|
||||
|
||||
```kotlin
|
||||
// Abstract/AbsExercise2.kt
|
||||
package abstractclasses2
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
enum class Note {
|
||||
A, B, C, D, E, F, G
|
||||
}
|
||||
|
||||
abstract class Instrument(val name: String) {
|
||||
abstract fun play(n: Note)
|
||||
}
|
||||
|
||||
open class Wind : Instrument("Wind") {
|
||||
override fun play(n: Note) {
|
||||
trace("$name blow $n")
|
||||
}
|
||||
}
|
||||
|
||||
class Percussion : Instrument("Percussion") {
|
||||
override fun play(n: Note) {
|
||||
trace("$name strike $n")
|
||||
}
|
||||
}
|
||||
|
||||
class Stringed : Instrument("Stringed") {
|
||||
override fun play(n: Note) {
|
||||
trace("$name pluck $n")
|
||||
}
|
||||
}
|
||||
|
||||
fun tune(i: Instrument) = i.play(Note.C)
|
||||
|
||||
fun main() {
|
||||
val orchestra = listOf(
|
||||
Wind(),
|
||||
Percussion(),
|
||||
Stringed()
|
||||
)
|
||||
orchestra.forEach { tune(it) }
|
||||
trace eq """
|
||||
Wind blow C
|
||||
Percussion strike C
|
||||
Stringed pluck C
|
||||
"""
|
||||
}
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
package abstractClassesExercise2
|
||||
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
||||
class TestAbstractClassesExercise2 {
|
||||
@Test fun testSolution() {
|
||||
//TODO: implement your test here
|
||||
Assert.assertTrue("Tests not implemented for the task", false)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package abstractClassesExercise3
|
||||
|
||||
// type your solution here
|
|
@ -0,0 +1,6 @@
|
|||
type: edu
|
||||
files:
|
||||
- name: src/Task.kt
|
||||
visible: true
|
||||
- name: test/Tests.kt
|
||||
visible: false
|
|
@ -0,0 +1,75 @@
|
|||
## Abstract Classes (#3)
|
||||
|
||||
Add classes to the previous example: `Bowed` inherits from `Stringed` and uses
|
||||
`bow` instead of `plucked` in the output, while `Brass` and `Reed` inherit
|
||||
from `Wind`. `Brass` and `Reed` do not override `play()`. Make the necessary
|
||||
modifications to allow each class to accept a `String` argument containing the
|
||||
class name.
|
||||
|
||||
```kotlin
|
||||
// Abstract/AbsExercise3.kt
|
||||
package abstractclasses3
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
enum class Note {
|
||||
A, B, C, D, E, F, G
|
||||
}
|
||||
|
||||
abstract class Instrument(val name: String) {
|
||||
abstract fun play(n: Note)
|
||||
}
|
||||
|
||||
open class Wind(name: String? = null) :
|
||||
Instrument(name ?: "Wind") {
|
||||
override fun play(n: Note) {
|
||||
trace("$name blow $n")
|
||||
}
|
||||
}
|
||||
|
||||
open class Percussion(name: String? = null) :
|
||||
Instrument(name ?: "Percussion") {
|
||||
override fun play(n: Note) {
|
||||
trace("$name strike $n")
|
||||
}
|
||||
}
|
||||
|
||||
open class Stringed(name: String? = null) :
|
||||
Instrument(name ?: "Stringed") {
|
||||
override fun play(n: Note) {
|
||||
trace("$name pluck $n")
|
||||
}
|
||||
}
|
||||
|
||||
class Bowed : Stringed("Bowed") {
|
||||
override fun play(n: Note) {
|
||||
trace("$name bow $n")
|
||||
}
|
||||
}
|
||||
|
||||
class Brass : Wind("Brass")
|
||||
class Reed : Wind("Reed")
|
||||
|
||||
fun tune(i: Instrument) = i.play(Note.C)
|
||||
|
||||
fun main() {
|
||||
val orchestra = listOf(
|
||||
Wind(),
|
||||
Percussion(),
|
||||
Stringed(),
|
||||
Bowed(),
|
||||
Brass(),
|
||||
Reed()
|
||||
)
|
||||
orchestra.forEach { tune(it) }
|
||||
trace eq """
|
||||
Wind blow C
|
||||
Percussion strike C
|
||||
Stringed pluck C
|
||||
Bowed bow C
|
||||
Brass blow C
|
||||
Reed blow C
|
||||
"""
|
||||
}
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
package abstractClassesExercise3
|
||||
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
||||
class TestAbstractClassesExercise3 {
|
||||
@Test fun testSolution() {
|
||||
//TODO: implement your test here
|
||||
Assert.assertTrue("Tests not implemented for the task", false)
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
content:
|
||||
- Examples
|
||||
- Exercise 1
|
||||
- Exercise 2
|
||||
- Exercise 3
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
// RobotExplorer2/Players2.kt
|
||||
package robotexplorer2
|
||||
|
||||
/*
|
||||
open class Player(val symbol: Char) {
|
||||
override fun toString() = symbol.toString()
|
||||
}
|
||||
|
||||
class Wall : Player('#')
|
||||
class Food : Player('.')
|
||||
class Empty : Player('_')
|
||||
class Edge : Player('/')
|
||||
class EndGame : Player('!')
|
||||
|
||||
class Robot(var room: Room) : Player('R') {
|
||||
fun move(urge: Urge) {
|
||||
room = room.doors.open(urge).enter(this)
|
||||
}
|
||||
}
|
||||
|
||||
class Teleport(
|
||||
val target: Char
|
||||
) : Player('T') {
|
||||
var originRoom = Room()
|
||||
var targetRoom = Room()
|
||||
override fun toString() = target.toString()
|
||||
}*/
|
|
@ -1,17 +0,0 @@
|
|||
// RobotExplorer2/Room2.kt
|
||||
package robotexplorer2
|
||||
|
||||
/*
|
||||
class Room(var player: Player = Empty()) {
|
||||
val doors = Doors()
|
||||
fun enter(robot: Robot): Room {
|
||||
when (val p = player) {
|
||||
is Wall, is Edge -> return robot.room
|
||||
is Teleport -> return p.targetRoom
|
||||
is EndGame -> return Room(EndGame())
|
||||
is Food -> player = Empty() // Eat food
|
||||
is Empty -> {}
|
||||
}
|
||||
return this // Enter new room
|
||||
}
|
||||
}*/
|
|
@ -8,14 +8,6 @@ files:
|
|||
visible: true
|
||||
- name: src/NoArgConstructor.kt
|
||||
visible: true
|
||||
- name: src/Players2.kt
|
||||
visible: true
|
||||
- name: src/ExploreMaze2.kt
|
||||
visible: true
|
||||
- name: src/Stage2.kt
|
||||
visible: true
|
||||
- name: src/Room2.kt
|
||||
visible: true
|
||||
- name: src/BCIExercise1.kt
|
||||
visible: true
|
||||
- name: src/BCIExercise2.kt
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
package baseClassInitializationExercise4
|
||||
|
||||
import atomictest.eq
|
||||
|
||||
interface Maze
|
||||
|
||||
interface GameElement {
|
||||
val symbol: Char
|
||||
val sharesCell: Boolean
|
||||
|
||||
fun play(maze: Maze)
|
||||
}
|
||||
|
||||
open class StaticElement(
|
||||
override val symbol: Char,
|
||||
override val sharesCell: Boolean
|
||||
) : GameElement {
|
||||
override fun play(maze: Maze) {
|
||||
// Default implementation: do nothing
|
||||
}
|
||||
}
|
||||
|
||||
class Wall : StaticElement('#', sharesCell = false)
|
||||
|
||||
class Food : StaticElement('.', sharesCell = true)
|
||||
|
||||
fun main() {
|
||||
val wall = Wall()
|
||||
wall.symbol eq '#'
|
||||
wall.sharesCell eq false
|
||||
|
||||
val food = Food()
|
||||
food.symbol eq '.'
|
||||
food.sharesCell eq true
|
||||
|
||||
// Wall and Food should extend StaticElement
|
||||
val elements: List<Any> = listOf(wall, food)
|
||||
elements.forEach {
|
||||
(it is StaticElement) eq true
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
type: edu
|
||||
files:
|
||||
- name: src/GameElements.kt
|
||||
visible: true
|
||||
placeholders:
|
||||
- offset: 380
|
||||
length: 38
|
||||
placeholder_text: |-
|
||||
GameElement {
|
||||
|
||||
override val symbol = '#'
|
||||
|
||||
override val sharesCell = false
|
||||
|
||||
override fun play(maze: Maze) {
|
||||
// Default implementation: do nothing
|
||||
}
|
||||
}
|
||||
- offset: 433
|
||||
length: 37
|
||||
placeholder_text: |-
|
||||
GameElement {
|
||||
|
||||
override val symbol = '.'
|
||||
|
||||
override val sharesCell = true
|
||||
|
||||
override fun play(maze: Maze) {
|
||||
// Default implementation: do nothing
|
||||
}
|
||||
}
|
||||
- name: test/Tests.kt
|
||||
visible: false
|
||||
feedback_link: |
|
||||
https://docs.google.com/forms/d/e/1FAIpQLSdkaliSwYkjiV21bZl0yP-In2g5p17sAQCfaGjyHx_QYMWTiQ/viewform?usp=pp_url&entry.189755027=Object-Oriented+Programming+%2F+Base+Class+Initialization+%2F+Exercise1
|
|
@ -1,14 +0,0 @@
|
|||
## Base Class Initialization (#1)
|
||||
|
||||
Change the implementations of `Wall` and `Food` classes, so that they extended
|
||||
the `StaticElement` class instead of implementing `GameElement` interface
|
||||
directly. Pass the corresponding values for `symbol` and `sharesCell`
|
||||
properties as function arguments. Note that you no longer need to provide
|
||||
implementation of `play()` function: the trivial implementation is
|
||||
inherited from the base class.
|
||||
|
||||
The `sharesCell` property specifies whether a mobile element like `Robot` or
|
||||
`Monster` can step at the same cell. For instance, a robot can be on the same
|
||||
cell with food or bomb (in this case food is eaten or bomb is exploded), but it
|
||||
can't share the cell with the wall. The wall occupies the whole cell and
|
||||
doesn't allow any movement to this cell.
|
|
@ -1,26 +0,0 @@
|
|||
package baseClassInitializationExercise4
|
||||
|
||||
import org.junit.Assert
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runners.MethodSorters
|
||||
import util.TIMEOUT
|
||||
|
||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
class TestBaseClassInitializationExercise4 {
|
||||
@Test(timeout = TIMEOUT)
|
||||
fun test1Sample() {
|
||||
val wall = Wall()
|
||||
Assert.assertEquals("Wrong value for wall.symbol", '#', wall.symbol)
|
||||
Assert.assertEquals("Wrong value for wall.sharesCell", false, wall.sharesCell)
|
||||
|
||||
val food = Food()
|
||||
Assert.assertEquals("Wrong value for food.symbol", '.', food.symbol)
|
||||
Assert.assertEquals("Wrong value for food.sharesCell", true, food.sharesCell)
|
||||
|
||||
val elements: List<Any> = listOf(wall, food)
|
||||
Assert.assertTrue("Wall and Food should extend StaticElement", elements.all {
|
||||
(it is StaticElement)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -3,4 +3,3 @@ content:
|
|||
- Exercise 1
|
||||
- Exercise 2
|
||||
- Exercise 3
|
||||
- Exercise 4
|
||||
|
|
|
@ -1,9 +1,42 @@
|
|||
// ClassDelegation/ClassDelegEx1.kt
|
||||
package classdelegationex1
|
||||
import usefullibrary.*
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
class MyClass {
|
||||
fun g() = trace("g()")
|
||||
fun h() = trace("h()")
|
||||
}
|
||||
|
||||
fun useMyClass(mc: MyClass) {
|
||||
mc.g()
|
||||
mc.h()
|
||||
}
|
||||
|
||||
class MyClassAdaptedForLib(
|
||||
val field: MyClass
|
||||
) : LibType {
|
||||
override fun f1() = field.h()
|
||||
override fun f2() = field.g()
|
||||
}
|
||||
|
||||
fun adapt(mc: MyClass) =
|
||||
MyClassAdaptedForLib(mc)
|
||||
|
||||
fun main() {
|
||||
trace eq ""
|
||||
val library = UsefulLibrary()
|
||||
val mc = adapt(MyClass())
|
||||
library.utility1(mc)
|
||||
library.utility2(mc)
|
||||
useMyClass(mc.field)
|
||||
trace eq """
|
||||
h()
|
||||
g()
|
||||
g()
|
||||
h()
|
||||
g()
|
||||
h()
|
||||
"""
|
||||
}
|
|
@ -5,7 +5,6 @@ import atomictest.eq
|
|||
class Numbered2
|
||||
private constructor(private val id: Int) {
|
||||
override fun toString(): String = "#$id"
|
||||
|
||||
companion object Factory {
|
||||
private var n = 0
|
||||
fun create(size: Int): List<Numbered2> {
|
||||
|
|
|
@ -5,7 +5,6 @@ class Numbered {
|
|||
companion object {
|
||||
private var count = 0
|
||||
}
|
||||
|
||||
private val id = count++
|
||||
override fun toString() = "#$id"
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ class WithObjectProperty {
|
|||
companion object {
|
||||
private var n: Int = 0 // Only one
|
||||
}
|
||||
|
||||
fun increment(): Int {
|
||||
n += 1
|
||||
return n
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
package complexConstructorsExercise4
|
||||
|
||||
interface GameElement {
|
||||
val symbol: Char
|
||||
}
|
||||
|
||||
class Robot : GameElement {
|
||||
override val symbol get() = 'R'
|
||||
}
|
||||
|
||||
class Wall : GameElement {
|
||||
override val symbol get() = '#'
|
||||
}
|
||||
|
||||
class Food : GameElement {
|
||||
override val symbol get() = '.'
|
||||
}
|
||||
|
||||
fun createGameElement(char: Char?): GameElement? = when (char) {
|
||||
'#' -> Wall()
|
||||
'.' -> Food()
|
||||
'R' -> Robot()
|
||||
else -> null
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
package complexConstructorsExercise4
|
||||
|
||||
import atomictest.eq
|
||||
|
||||
data class Cell(val x: Int, val y: Int)
|
||||
|
||||
interface Maze {
|
||||
val width: Int
|
||||
val height: Int
|
||||
fun all(): Set<GameElement>
|
||||
fun allIn(cell: Cell): Set<GameElement>
|
||||
fun cell(element: GameElement): Cell?
|
||||
fun add(element: GameElement, cell: Cell)
|
||||
fun remove(element: GameElement)
|
||||
}
|
||||
|
||||
class MazeImpl(
|
||||
representation: String
|
||||
) : Maze {
|
||||
override val width: Int
|
||||
override val height: Int
|
||||
|
||||
private val elementMap: List<List<MutableSet<GameElement>>>
|
||||
private val cellMap = mutableMapOf<GameElement, Cell>()
|
||||
|
||||
init {
|
||||
val lines = representation.lines()
|
||||
height = lines.size
|
||||
width = lines.maxBy { it.length }?.length ?: 0
|
||||
elementMap = List(height) {
|
||||
List(width) { mutableSetOf<GameElement>() }
|
||||
}
|
||||
for (y in 0 until height) {
|
||||
for (x in 0 until width) {
|
||||
val ch = lines.getOrNull(y)?.getOrNull(x)
|
||||
val element = createGameElement(ch)
|
||||
if (element != null) {
|
||||
add(element, Cell(x, y))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun elements(cell: Cell): MutableSet<GameElement> {
|
||||
return elementMap[cell.y][cell.x]
|
||||
}
|
||||
|
||||
override fun all(): Set<GameElement> {
|
||||
return cellMap.keys.toSet()
|
||||
}
|
||||
|
||||
override fun allIn(cell: Cell): Set<GameElement> {
|
||||
return elements(cell)
|
||||
}
|
||||
|
||||
override fun cell(element: GameElement): Cell? {
|
||||
return cellMap[element]
|
||||
}
|
||||
|
||||
override fun add(element: GameElement, cell: Cell) {
|
||||
elements(cell) += element
|
||||
cellMap[element] = cell
|
||||
}
|
||||
|
||||
override fun remove(element: GameElement) {
|
||||
val cell = cell(element) ?: return
|
||||
elements(cell) -= element
|
||||
cellMap.remove(element)
|
||||
}
|
||||
|
||||
override fun toString() =
|
||||
elementMap.joinToString("\n") { row ->
|
||||
row.joinToString("") { elements ->
|
||||
"${elements.lastOrNull()?.symbol ?: ' '}"
|
||||
}.trimEnd()
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val mazeRepresentation = """
|
||||
###
|
||||
#
|
||||
#R #
|
||||
|
||||
####
|
||||
""".trimIndent()
|
||||
val matrix = MazeImpl(mazeRepresentation)
|
||||
// trim whitespaces at the end of each line
|
||||
// to have equal representation
|
||||
matrix
|
||||
.toString().lines()
|
||||
.joinToString("\n") { it.trimEnd() } eq
|
||||
mazeRepresentation
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
type: edu
|
||||
files:
|
||||
- name: src/GameElements.kt
|
||||
visible: true
|
||||
- name: test/Tests.kt
|
||||
visible: false
|
||||
- name: src/Maze.kt
|
||||
visible: true
|
||||
placeholders:
|
||||
- offset: 658
|
||||
length: 38
|
||||
placeholder_text: 0 // TODO
|
||||
- offset: 789
|
||||
length: 244
|
||||
placeholder_text: // TODO
|
||||
feedback_link: |
|
||||
https://docs.google.com/forms/d/e/1FAIpQLSdkaliSwYkjiV21bZl0yP-In2g5p17sAQCfaGjyHx_QYMWTiQ/viewform?usp=pp_url&entry.189755027=Object-Oriented+Programming+%2F+Complex+Constructors+%2F+Exercise1
|
|
@ -1,13 +0,0 @@
|
|||
## Complex Constructors (#1)
|
||||
|
||||
Modify the primary constructor of the `MazeImpl` class to
|
||||
initialize a maze contents using only the maze representation in a string.
|
||||
Calculate `width` and `height` properties based on the maze representation.
|
||||
Use the auxiliary function `createGameElement()` (defined in `GameElements.kt`)
|
||||
to create an element by using a given character.
|
||||
|
||||
For a start, you can reuse the implementation given in the atom text.
|
||||
Note, however, that only one element is stored in a cell there, and in a real
|
||||
game several elements can be stored in one cell. Also, the maze representation
|
||||
with empty cells at the end of lines isn't supported in the implementation
|
||||
in the book. Fix this problem.
|
|
@ -1,61 +0,0 @@
|
|||
package complexConstructorsExercise4
|
||||
|
||||
import org.junit.Assert
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runners.MethodSorters
|
||||
import util.TIMEOUT
|
||||
|
||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
class TestComplexConstructorsExercise4 {
|
||||
@Test(timeout = TIMEOUT)
|
||||
fun test1Sample() {
|
||||
val mazeRepresentation = """
|
||||
###
|
||||
#
|
||||
#R #
|
||||
|
||||
####
|
||||
""".trimIndent()
|
||||
|
||||
val matrix = MazeImpl(mazeRepresentation)
|
||||
Assert.assertEquals("Wrong result for the sample:", mazeRepresentation,
|
||||
matrix.toString().lines().joinToString("\n") { it.trimEnd() })
|
||||
}
|
||||
|
||||
private fun checkMaze(width: Int, height: Int, maze: String) {
|
||||
val matrix = MazeImpl(maze)
|
||||
Assert.assertEquals("Wrong result for the maze:", maze,
|
||||
matrix.toString().lines().joinToString("\n") { it.trimEnd() })
|
||||
}
|
||||
|
||||
@Test(timeout = TIMEOUT)
|
||||
fun test2() = checkMaze(4, 3, """
|
||||
|
||||
#
|
||||
|
||||
""".trimIndent())
|
||||
|
||||
@Test(timeout = TIMEOUT)
|
||||
fun test3() = checkMaze(4, 3, """
|
||||
####
|
||||
#R.
|
||||
##
|
||||
""".trimIndent())
|
||||
|
||||
@Test(timeout = TIMEOUT)
|
||||
fun test4() = checkMaze(4, 3, """
|
||||
. ##
|
||||
|
||||
# R
|
||||
""".trimIndent())
|
||||
|
||||
@Test(timeout = TIMEOUT)
|
||||
fun test5() = checkMaze(5, 5, """
|
||||
.
|
||||
R
|
||||
|
||||
##
|
||||
.#
|
||||
""".trimIndent())
|
||||
}
|
|
@ -3,4 +3,3 @@ content:
|
|||
- Exercise 1
|
||||
- Exercise 2
|
||||
- Exercise 3
|
||||
- Exercise 4
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
package compositionExercise1
|
||||
|
||||
// type your solution here
|
|
@ -0,0 +1,6 @@
|
|||
type: edu
|
||||
files:
|
||||
- name: src/Task.kt
|
||||
visible: true
|
||||
- name: test/Tests.kt
|
||||
visible: false
|
|
@ -0,0 +1,60 @@
|
|||
## Composition (#1)
|
||||
|
||||
In `Car.kt`, add a `service()` member function to `Engine` and add a call to
|
||||
this function in `main()`.
|
||||
|
||||
```kotlin
|
||||
// Composition/CompositionExercise1.kt
|
||||
package compositionex1
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
class Engine {
|
||||
fun start() = trace("Engine start")
|
||||
fun stop() = trace("Engine stop")
|
||||
fun service() = trace("Engine service")
|
||||
}
|
||||
|
||||
class Wheel {
|
||||
fun inflate(psi: Int) =
|
||||
trace("Wheel inflate($psi)")
|
||||
}
|
||||
|
||||
class Window(val side: String) {
|
||||
fun rollUp() =
|
||||
trace("$side Window roll up")
|
||||
fun rollDown() =
|
||||
trace("$side Window roll down")
|
||||
}
|
||||
|
||||
class Door(val side: String) {
|
||||
val window = Window(side)
|
||||
fun open() = trace("$side Door open")
|
||||
fun close() = trace("$side Door close")
|
||||
}
|
||||
|
||||
class Car {
|
||||
val engine = Engine()
|
||||
val wheel = List(4) { Wheel() }
|
||||
// Two door:
|
||||
val leftDoor = Door("left")
|
||||
val rightDoor = Door("right")
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val car = Car()
|
||||
car.leftDoor.open()
|
||||
car.rightDoor.window.rollUp()
|
||||
car.wheel[0].inflate(72)
|
||||
car.engine.start()
|
||||
car.engine.service()
|
||||
trace eq """
|
||||
left Door open
|
||||
right Window roll up
|
||||
Wheel inflate(72)
|
||||
Engine start
|
||||
Engine service
|
||||
"""
|
||||
}
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
package compositionExercise1
|
||||
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
||||
class TestCompositionExercise1 {
|
||||
@Test fun testSolution() {
|
||||
//TODO: implement your test here
|
||||
Assert.assertTrue("Tests not implemented for the task", false)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package compositionExercise2
|
||||
|
||||
// type your solution here
|
|
@ -0,0 +1,6 @@
|
|||
type: edu
|
||||
files:
|
||||
- name: src/Task.kt
|
||||
visible: true
|
||||
- name: test/Tests.kt
|
||||
visible: false
|
|
@ -0,0 +1,70 @@
|
|||
## Composition (#2)
|
||||
|
||||
Modify `House3.kt` to replace all the `interface`s with classes, and add a
|
||||
`name` property to each. Using the same form, add `Bedroom` and `Bathroom`
|
||||
classes, and a `List<Bathroom>` and `List<Bedroom>` to `House`. Create a
|
||||
`House` named "Our House", with `Kitchen`s called "Main" and "Guest". Add
|
||||
`Bathroom`s called "Master", "Shared", and "Guest". Add `Bedroom`s called
|
||||
"Master", "Son", "Daughter", and "Guest". Use `atomictest.Trace` to capture all
|
||||
the class names and `name` properties as the `House` is constructed. Also
|
||||
notice the order of construction.
|
||||
|
||||
```kotlin
|
||||
// Composition/CompositionExercise2.kt
|
||||
package compositionex2
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
open class Building(val name: String) {
|
||||
init { trace("Building: $name") }
|
||||
}
|
||||
|
||||
class Kitchen(val name: String) {
|
||||
init { trace("Kitchen: $name") }
|
||||
}
|
||||
|
||||
class Bedroom(val name: String) {
|
||||
init { trace("Bedroom: $name") }
|
||||
}
|
||||
|
||||
class Bathroom(val name: String) {
|
||||
init { trace("Bathroom: $name") }
|
||||
}
|
||||
|
||||
class House(name: String) : Building(name) {
|
||||
val kitchens = listOf(
|
||||
Kitchen("Main"),
|
||||
Kitchen("Guest")
|
||||
)
|
||||
val bathrooms = listOf(
|
||||
Bathroom("Master"),
|
||||
Bathroom("Shared"),
|
||||
Bathroom("Guest")
|
||||
)
|
||||
val bedrooms = listOf(
|
||||
Bedroom("Master"),
|
||||
Bedroom("Son"),
|
||||
Bedroom("Daughter"),
|
||||
Bedroom("Guest")
|
||||
)
|
||||
init { trace("House: $name") }
|
||||
}
|
||||
|
||||
fun main() {
|
||||
House("Our House")
|
||||
trace eq """
|
||||
Building: Our House
|
||||
Kitchen: Main
|
||||
Kitchen: Guest
|
||||
Bathroom: Master
|
||||
Bathroom: Shared
|
||||
Bathroom: Guest
|
||||
Bedroom: Master
|
||||
Bedroom: Son
|
||||
Bedroom: Daughter
|
||||
Bedroom: Guest
|
||||
House: Our House
|
||||
"""
|
||||
}
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
package compositionExercise2
|
||||
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
||||
class TestCompositionExercise2 {
|
||||
@Test fun testSolution() {
|
||||
//TODO: implement your test here
|
||||
Assert.assertTrue("Tests not implemented for the task", false)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package compositionExercise3
|
||||
|
||||
// type your solution here
|
|
@ -0,0 +1,6 @@
|
|||
type: edu
|
||||
files:
|
||||
- name: src/Task.kt
|
||||
visible: true
|
||||
- name: test/Tests.kt
|
||||
visible: false
|
|
@ -0,0 +1,102 @@
|
|||
## Composition (#3)
|
||||
|
||||
The starter code provides:
|
||||
|
||||
```kotlin
|
||||
interface DataBase {
|
||||
fun write(key: String, value: String)
|
||||
fun read(key: String): String
|
||||
}
|
||||
```
|
||||
|
||||
The starter code also provides `class Holder(val db: DataBase)`. `Holder` uses
|
||||
composition by containing a `DataBase`. `Holder` contains a `test()` function
|
||||
which is used in `main()` to test three different `DataBase` classes, which
|
||||
you write:
|
||||
|
||||
- `NonRelational`, implemented with a `mutableListOf<Pair<String, String>>()`
|
||||
- `InMemory`, implemented with a `mutableMapOf<String, String>()`
|
||||
- `Mock`, implemented with two `String` `var`s.
|
||||
|
||||
Write these classes so they pass the tests given in `main()`.
|
||||
|
||||
```kotlin
|
||||
// Composition/CompositionExercise3.kt
|
||||
package compositionex3
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
interface DataBase {
|
||||
fun write(key: String, value: String)
|
||||
fun read(key: String): String
|
||||
}
|
||||
|
||||
class NonRelational : DataBase {
|
||||
private val db =
|
||||
mutableListOf<Pair<String, String>>()
|
||||
override fun write(
|
||||
key: String, value: String
|
||||
) {
|
||||
db.add(Pair(key, value))
|
||||
}
|
||||
override fun read(key: String) =
|
||||
db.first { it.first == key }.second
|
||||
}
|
||||
|
||||
class InMemory : DataBase {
|
||||
private val db =
|
||||
mutableMapOf<String, String>()
|
||||
override fun write(
|
||||
key: String, value: String
|
||||
) {
|
||||
db[key] = value
|
||||
}
|
||||
override fun read(key: String) =
|
||||
db[key] ?: ""
|
||||
}
|
||||
|
||||
class Mock : DataBase {
|
||||
private var k = ""
|
||||
private var v = ""
|
||||
override fun write(
|
||||
key: String, value: String
|
||||
) { k = key; v = value }
|
||||
override fun read(key: String) = v
|
||||
}
|
||||
|
||||
class Holder(val db: DataBase) {
|
||||
fun store(k: String, v: String) =
|
||||
db.write(k, v)
|
||||
fun fetch(k: String) = db.read(k)
|
||||
private val data = """
|
||||
color: purple
|
||||
dog: husky
|
||||
art: deco
|
||||
""".trimIndent().lines()
|
||||
.map { it.split(": ") }
|
||||
fun test() {
|
||||
for(line in data) {
|
||||
store(line[0], line[1])
|
||||
trace(fetch(line[0]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
Holder(NonRelational()).test()
|
||||
Holder(InMemory()).test()
|
||||
Holder(Mock()).test()
|
||||
trace eq """
|
||||
purple
|
||||
husky
|
||||
deco
|
||||
purple
|
||||
husky
|
||||
deco
|
||||
purple
|
||||
husky
|
||||
deco
|
||||
"""
|
||||
}
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
package compositionExercise3
|
||||
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
||||
class TestCompositionExercise3 {
|
||||
@Test fun testSolution() {
|
||||
//TODO: implement your test here
|
||||
Assert.assertTrue("Tests not implemented for the task", false)
|
||||
}
|
||||
}
|
|
@ -1,2 +1,5 @@
|
|||
content:
|
||||
- Examples
|
||||
- Exercise 1
|
||||
- Exercise 2
|
||||
- Exercise 3
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
// RobotExplorer3/Doors3.kt
|
||||
package robotexplorer3
|
||||
import robotexplorer3.Player.*
|
||||
import robotexplorer3.Urge.*
|
||||
|
||||
val edge = Room(Edge())
|
||||
|
||||
class Doors {
|
||||
private var north: Room = edge
|
||||
private var south: Room = edge
|
||||
private var east: Room = edge
|
||||
private var west: Room = edge
|
||||
fun open(urge: Urge): Room =
|
||||
when (urge) {
|
||||
North -> north
|
||||
South -> south
|
||||
East -> east
|
||||
West -> west
|
||||
}
|
||||
fun connect(
|
||||
row: Int, col: Int,
|
||||
rooms: Map<Pair<Int, Int>, Room>
|
||||
) {
|
||||
fun link(toRow: Int, toCol: Int) =
|
||||
rooms.getOrDefault(
|
||||
Pair(toRow, toCol), edge)
|
||||
north = link(row - 1, col)
|
||||
south = link(row + 1, col)
|
||||
east = link(row, col + 1)
|
||||
west = link(row, col - 1)
|
||||
}
|
||||
}
|
|
@ -2,7 +2,6 @@
|
|||
package robotexplorer2
|
||||
import robotmaze.*
|
||||
|
||||
/*
|
||||
fun main() {
|
||||
Stage(stringMaze).run(solution)
|
||||
}*/
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
// RobotExplorer3/ExploreMaze3.kt
|
||||
package robotexplorer3
|
||||
import robotmaze.*
|
||||
|
||||
fun main() {
|
||||
Stage(stringMaze).run(solution)
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
// RobotExplorer2/Players2.kt
|
||||
package robotexplorer2
|
||||
import robotexplorer1.Urge
|
||||
|
||||
interface Player {
|
||||
val symbol: Char
|
||||
val room: Room
|
||||
fun id() = symbol.toString()
|
||||
fun interact(robot: Robot): Room
|
||||
}
|
||||
|
||||
class Void() : Player {
|
||||
override val symbol = '~'
|
||||
override val room: Room
|
||||
get() = throw IllegalAccessException()
|
||||
override fun interact(robot: Robot) =
|
||||
robot.room // Stay in old room
|
||||
}
|
||||
|
||||
class Wall(override val room: Room) : Player {
|
||||
override val symbol = '#'
|
||||
override fun interact(robot: Robot) =
|
||||
robot.room // Stay in old room
|
||||
}
|
||||
|
||||
class Food(override val room: Room) : Player {
|
||||
override val symbol = '.'
|
||||
override fun interact(robot: Robot): Room {
|
||||
robot.energy++
|
||||
room.player = Empty(room)
|
||||
return room // Move into new room
|
||||
}
|
||||
}
|
||||
|
||||
class Empty(
|
||||
override val room: Room
|
||||
) : Player {
|
||||
override val symbol = '_'
|
||||
// Move into new room:
|
||||
override fun interact(robot: Robot) = room
|
||||
}
|
||||
|
||||
class EndGame(
|
||||
override val room: Room
|
||||
) : Player {
|
||||
override val symbol = '!'
|
||||
override fun interact(robot: Robot) =
|
||||
Room(EndGame(room))
|
||||
}
|
||||
|
||||
class Robot(
|
||||
override var room: Room
|
||||
) : Player {
|
||||
override val symbol = 'R'
|
||||
var energy = 0
|
||||
// Shouldn't happen:
|
||||
override fun interact(robot: Robot) =
|
||||
throw IllegalAccessException()
|
||||
fun move(urge: Urge) {
|
||||
val nextRoom = room.doors.open(urge)
|
||||
room = nextRoom.player.interact(this)
|
||||
}
|
||||
}
|
||||
|
||||
class Teleport(
|
||||
val target: Char, override val room: Room
|
||||
) : Player {
|
||||
override val symbol = 'T'
|
||||
var targetRoom = Room()
|
||||
override fun id() = target.toString()
|
||||
override fun interact(robot: Robot) =
|
||||
targetRoom
|
||||
}
|
||||
|
||||
class Bomb(override val room: Room) : Player {
|
||||
override val symbol = '*'
|
||||
override fun interact(robot: Robot): Room {
|
||||
robot.energy = 0 // Bomb erases energy
|
||||
room.player = Empty(room)
|
||||
return room
|
||||
}
|
||||
}
|
||||
|
||||
fun factory(ch: Char): Room {
|
||||
val room = Room()
|
||||
when (ch) {
|
||||
'R' -> {} // Handled in Stage
|
||||
'#' -> room.player = Wall(room)
|
||||
'.' -> room.player = Food(room)
|
||||
'_' -> room.player = Empty(room)
|
||||
'!' -> room.player = EndGame(room)
|
||||
'*' -> room.player = Bomb(room)
|
||||
in 'a'..'z' ->
|
||||
room.player = Teleport(ch, room)
|
||||
else -> throw IllegalStateException("$ch")
|
||||
}
|
||||
return room
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
// RobotExplorer3/Players3.kt
|
||||
package robotexplorer3
|
||||
|
||||
abstract class Player(val symbol: Char) {
|
||||
override fun toString() = symbol.toString()
|
||||
abstract fun
|
||||
interact(robot: Robot, room: Room): Room
|
||||
}
|
||||
|
||||
class Wall : Player('#') {
|
||||
override fun
|
||||
interact(robot: Robot, room: Room) =
|
||||
robot.room
|
||||
}
|
||||
|
||||
class Food : Player('.') {
|
||||
override fun
|
||||
interact(robot: Robot, room: Room): Room {
|
||||
room.player = Empty()
|
||||
return room
|
||||
}
|
||||
}
|
||||
|
||||
class Empty : Player('_') {
|
||||
override fun
|
||||
interact(robot: Robot, room: Room) =
|
||||
room
|
||||
}
|
||||
|
||||
class Edge : Player('/') {
|
||||
override fun
|
||||
interact(robot: Robot, room: Room) =
|
||||
robot.room
|
||||
}
|
||||
|
||||
class EndGame : Player('!') {
|
||||
override fun
|
||||
interact(robot: Robot, room: Room) =
|
||||
Room(EndGame())
|
||||
}
|
||||
|
||||
|
||||
class Robot(var room: Room) : Player('R') {
|
||||
override fun // Shouldn't happen
|
||||
interact(robot: Robot, room: Room) =
|
||||
throw IllegalAccessException()
|
||||
fun move(urge: Urge) {
|
||||
val nextRoom: Room = room.doors.open(urge)
|
||||
room = nextRoom.player
|
||||
.interact(this, nextRoom)
|
||||
}
|
||||
}
|
||||
|
||||
class Teleport(
|
||||
val target: Char
|
||||
) : Player('T') {
|
||||
var originRoom = Room()
|
||||
var targetRoom = Room()
|
||||
override fun toString() = target.toString()
|
||||
override fun
|
||||
interact(robot: Robot, room: Room) =
|
||||
targetRoom
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
// RobotExplorer2/Room2.kt
|
||||
package robotexplorer2
|
||||
import robotexplorer1.Urge
|
||||
import robotexplorer1.Urge.*
|
||||
|
||||
class Room(var player: Player = Void()) {
|
||||
val doors = Doors()
|
||||
}
|
||||
|
||||
private val edge = Room()
|
||||
|
||||
class Doors {
|
||||
private val doors = mutableMapOf(
|
||||
North to edge,
|
||||
South to edge,
|
||||
East to edge,
|
||||
West to edge
|
||||
)
|
||||
fun open(urge: Urge): Room =
|
||||
doors.getOrDefault(urge, edge)
|
||||
fun connect(
|
||||
row: Int, col: Int,
|
||||
rooms: Map<Pair<Int, Int>, Room>
|
||||
) {
|
||||
fun link(toRow: Int, toCol: Int) =
|
||||
rooms.getOrDefault(
|
||||
Pair(toRow, toCol), edge)
|
||||
doors[North] = link(row - 1, col)
|
||||
doors[South] = link(row + 1, col)
|
||||
doors[East] = link(row, col + 1)
|
||||
doors[West] = link(row, col - 1)
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
// RobotExplorer3/Room3.kt
|
||||
package robotexplorer3
|
||||
|
||||
class Room(var player: Player = Empty()) {
|
||||
val doors = Doors()
|
||||
}
|
|
@ -1,36 +1,26 @@
|
|||
// RobotExplorer2/Stage2.kt
|
||||
package robotexplorer2
|
||||
import robotexplorer1.urge
|
||||
|
||||
/*
|
||||
class Stage(val maze: String) {
|
||||
val robot = Robot(Room())
|
||||
val rooms =
|
||||
mutableMapOf<Pair<Int, Int>, Room>()
|
||||
private val view = View(this)
|
||||
val lines = maze.split("\n")
|
||||
private fun factory(ch: Char): Room {
|
||||
val room = Room()
|
||||
when (ch) {
|
||||
'R' -> robot.room = room
|
||||
'#' -> room.player = Wall()
|
||||
'.' -> room.player = Food()
|
||||
'_' -> room.player = Empty()
|
||||
'/' -> room.player = Edge()
|
||||
'!' -> room.player = EndGame()
|
||||
else -> {
|
||||
val teleport = Teleport(ch)
|
||||
teleport.originRoom = room
|
||||
room.player = teleport
|
||||
}
|
||||
}
|
||||
return room
|
||||
private inner class Adapt : Adapter {
|
||||
override fun height() =
|
||||
maze.lines().size + 3
|
||||
override fun textView() = mazeView()
|
||||
}
|
||||
// Construct it with the 'Builder' pattern:
|
||||
init {
|
||||
private val view = View(Adapt())
|
||||
val lines = maze.split("\n")
|
||||
init { // The 'Builder' pattern:
|
||||
// Step 1: Create rooms with players:
|
||||
lines.withIndex().forEach { (row, line) ->
|
||||
line.withIndex().forEach { (col, ch) ->
|
||||
rooms[Pair(row, col)] = factory(ch)
|
||||
val room = factory(ch)
|
||||
rooms[Pair(row, col)] = room
|
||||
if(ch == robot.symbol)
|
||||
robot.room = room
|
||||
}
|
||||
}
|
||||
// Step 2: Connect the doors
|
||||
|
@ -48,8 +38,8 @@ class Stage(val maze: String) {
|
|||
it.target
|
||||
}.zipWithNext()
|
||||
for((a, b) in teleportPairs) {
|
||||
a.targetRoom = b.originRoom
|
||||
b.targetRoom = a.originRoom
|
||||
a.targetRoom = b.room
|
||||
b.targetRoom = a.room
|
||||
}
|
||||
}
|
||||
fun run(solution: String) {
|
||||
|
@ -61,4 +51,20 @@ class Stage(val maze: String) {
|
|||
view.show()
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
fun Stage.mazeView(): String {
|
||||
var result = ""
|
||||
var currentRow = 0
|
||||
rooms.forEach { (pair, room) ->
|
||||
val row = pair.first
|
||||
if (row != currentRow) {
|
||||
result += "\n"
|
||||
currentRow = row
|
||||
}
|
||||
result += if (room == robot.room)
|
||||
robot.id() else room.player.id()
|
||||
}
|
||||
return result +
|
||||
"\n\nEnergy: ${robot.energy}\n"
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
// RobotExplorer3/Urge3.kt
|
||||
package robotexplorer3
|
||||
import robotexplorer3.Urge.*
|
||||
|
||||
enum class Urge { North, South, East, West }
|
||||
|
||||
fun urge(urgeChar: Char): Urge =
|
||||
when (urgeChar) {
|
||||
'n' -> North
|
||||
's' -> South
|
||||
'e' -> East
|
||||
'w' -> West
|
||||
else -> West
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
// RobotExplorer2/View2.kt
|
||||
package robotexplorer2
|
||||
|
||||
interface Adapter {
|
||||
fun height(): Int
|
||||
fun textView(): String
|
||||
}
|
||||
|
||||
class View(val adapter: Adapter) {
|
||||
// Start an ANSI terminal control string:
|
||||
private val ansiTerm = "\u001B["
|
||||
fun clear() =
|
||||
print("${ansiTerm}${adapter.height()}T")
|
||||
fun show() {
|
||||
print("${ansiTerm}0;0H") // Cursor home
|
||||
println(adapter.textView())
|
||||
Thread.sleep(300L) // Pause
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
// RobotExplorer3/View3.kt
|
||||
package robotexplorer3
|
||||
|
||||
class View(private val stage: Stage) {
|
||||
// Start an ANSI terminal control string:
|
||||
private val ansiTerm = "\u001B["
|
||||
fun clear() {
|
||||
val size = stage.maze.lines().size + 3
|
||||
print("${ansiTerm}${size}T")
|
||||
}
|
||||
private fun mazeView(): String {
|
||||
var result = ""
|
||||
var currentRow = 0
|
||||
stage.rooms.forEach { (pair, room) ->
|
||||
val row = pair.first
|
||||
if (row != currentRow) {
|
||||
result += "\n"
|
||||
currentRow = row
|
||||
}
|
||||
result += if (room == stage.robot.room)
|
||||
"${stage.robot}" else "${room.player}"
|
||||
}
|
||||
return result + "\n\n\n"
|
||||
}
|
||||
fun show() {
|
||||
print("${ansiTerm}0;0H") // Cursor home
|
||||
println(mazeView())
|
||||
Thread.sleep(300L) // Pause
|
||||
}
|
||||
}
|
|
@ -1,18 +1,14 @@
|
|||
type: theory
|
||||
files:
|
||||
- name: src/Players3.kt
|
||||
- name: src/Players2.kt
|
||||
visible: true
|
||||
- name: src/Room3.kt
|
||||
- name: src/ExploreMaze2.kt
|
||||
visible: true
|
||||
- name: src/Stage3.kt
|
||||
- name: src/Room2.kt
|
||||
visible: true
|
||||
- name: src/ExploreMaze3.kt
|
||||
- name: src/Stage2.kt
|
||||
visible: true
|
||||
- name: src/Urge3.kt
|
||||
visible: true
|
||||
- name: src/Doors3.kt
|
||||
visible: true
|
||||
- name: src/View3.kt
|
||||
- name: src/View2.kt
|
||||
visible: true
|
||||
- name: src/ExtensibilityEx1.kt
|
||||
visible: true
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
package inheritanceAndExtensionsExercise1
|
||||
|
||||
// type your solution here
|
|
@ -0,0 +1,6 @@
|
|||
type: edu
|
||||
files:
|
||||
- name: src/Task.kt
|
||||
visible: true
|
||||
- name: test/Tests.kt
|
||||
visible: false
|
|
@ -0,0 +1,94 @@
|
|||
## Inheritance & Extensions (#1)
|
||||
|
||||
Following `AdjustTemp.kt`, add two extension functions `openWindow()` and
|
||||
`fan()` as ways of cooling. Add a class `DeltaTemperature2` where the extension
|
||||
functions are instead member functions, and an overloaded `adjust()` function
|
||||
which takes a `DeltaTemperature2`. Which approach seems better, or are they
|
||||
about the same?
|
||||
|
||||
```kotlin
|
||||
// InheritanceExtensions/InhExtensionsEx1.kt
|
||||
package inheritanceextensionsex1
|
||||
|
||||
import atomictest.*
|
||||
|
||||
val trace = Trace()
|
||||
|
||||
class DeltaTemperature(
|
||||
val current: Double,
|
||||
val target: Double
|
||||
)
|
||||
|
||||
fun DeltaTemperature.heat() {
|
||||
if (current < target)
|
||||
trace("heating to $target ")
|
||||
}
|
||||
|
||||
fun DeltaTemperature.cool() {
|
||||
if (current > target)
|
||||
trace("cooling to $target ")
|
||||
}
|
||||
|
||||
fun DeltaTemperature.openWindow() {
|
||||
if (current > target)
|
||||
trace("cooling to $target")
|
||||
}
|
||||
|
||||
fun DeltaTemperature.fan() {
|
||||
if (current > target)
|
||||
trace("cooling to $target ")
|
||||
}
|
||||
|
||||
class DeltaTemperature2(
|
||||
val current: Double,
|
||||
val target: Double
|
||||
) {
|
||||
fun heat() {
|
||||
if (current < target)
|
||||
trace("heating to $target ")
|
||||
}
|
||||
fun cool() {
|
||||
if (current > target)
|
||||
trace("cooling to $target ")
|
||||
}
|
||||
fun openWindow() {
|
||||
if (current > target)
|
||||
trace("cooling to $target")
|
||||
}
|
||||
fun fan() {
|
||||
if (current > target)
|
||||
trace("cooling to $target ")
|
||||
}
|
||||
}
|
||||
|
||||
fun adjust(deltaT: DeltaTemperature) {
|
||||
deltaT.heat()
|
||||
deltaT.cool()
|
||||
deltaT.openWindow()
|
||||
deltaT.fan()
|
||||
}
|
||||
|
||||
fun adjust(deltaT: DeltaTemperature2) {
|
||||
deltaT.heat()
|
||||
deltaT.cool()
|
||||
deltaT.openWindow()
|
||||
deltaT.fan()
|
||||
}
|
||||
|
||||
fun main() {
|
||||
adjust(DeltaTemperature(60.0, 70.0))
|
||||
adjust(DeltaTemperature(80.0, 60.0))
|
||||
adjust(DeltaTemperature2(60.0, 70.0))
|
||||
adjust(DeltaTemperature2(80.0, 60.0))
|
||||
trace eq """
|
||||
heating to 70.0
|
||||
cooling to 60.0
|
||||
cooling to 60.0
|
||||
cooling to 60.0
|
||||
heating to 70.0
|
||||
cooling to 60.0
|
||||
cooling to 60.0
|
||||
cooling to 60.0
|
||||
"""
|
||||
}
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
package inheritanceAndExtensionsExercise1
|
||||
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
||||
class TestInheritanceAndExtensionsExercise1 {
|
||||
@Test fun testSolution() {
|
||||
//TODO: implement your test here
|
||||
Assert.assertTrue("Tests not implemented for the task", false)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package inheritanceAndExtensionsExercise2
|
||||
|
||||
// type your solution here
|
|
@ -0,0 +1,6 @@
|
|||
type: edu
|
||||
files:
|
||||
- name: src/Task.kt
|
||||
visible: true
|
||||
- name: test/Tests.kt
|
||||
visible: false
|
|
@ -0,0 +1,81 @@
|
|||
## Inheritance & Extensions (#2)
|
||||
|
||||
Refactor `BatteryPet2.kt` to improve the design:
|
||||
|
||||
- In `Pet`, add an open function `settle()` which calls `trace("")`
|
||||
- In `Pet`, add an open function `feed()` which calls `energy.replenish()`
|
||||
- In `Dog`, override `settle()` to call `trace("Sitting...")
|
||||
- You no longer need `Dog.sit()`
|
||||
- Change `playWithDog(dog: Dog)` to `playWithPet(pet: Pet)`
|
||||
- Add a `CatFood` type of `Energy`, and define the associated `Cat` class
|
||||
- In `main()`, test all types of `Pet`
|
||||
|
||||
```kotlin
|
||||
// InheritanceExtensions/InhExtensionsEx2.kt
|
||||
package inheritanceextensionsex2
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
interface Energy {
|
||||
fun replenish() = trace("Fill Bowl")
|
||||
}
|
||||
|
||||
open class Pet(open val energy: Energy) {
|
||||
open fun speak() = trace("")
|
||||
open fun settle() = trace("")
|
||||
open fun feed() = energy.replenish()
|
||||
}
|
||||
|
||||
class DogFood : Energy
|
||||
|
||||
open class Dog : Pet(DogFood()) {
|
||||
override fun speak() = trace("Bark!")
|
||||
override fun settle() = trace("Sitting...")
|
||||
}
|
||||
|
||||
class CatFood : Energy
|
||||
|
||||
open class Cat : Pet(CatFood()) {
|
||||
override fun speak() = trace("Meow!")
|
||||
override fun settle() =
|
||||
trace("In my basket...")
|
||||
}
|
||||
|
||||
class Batteries : Energy {
|
||||
override fun replenish() =
|
||||
trace("Change batteries")
|
||||
}
|
||||
|
||||
class ToyDog: Dog() {
|
||||
override val energy = Batteries()
|
||||
}
|
||||
|
||||
fun play(pet: Pet) = pet.speak()
|
||||
|
||||
fun playWithPet(pet: Pet) {
|
||||
play(pet)
|
||||
pet.settle()
|
||||
pet.energy.replenish()
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val dog1 = Dog()
|
||||
val dog2 = ToyDog()
|
||||
val cat = Cat()
|
||||
playWithPet(dog1)
|
||||
playWithPet(dog2)
|
||||
playWithPet(cat)
|
||||
trace eq """
|
||||
Bark!
|
||||
Sitting...
|
||||
Fill Bowl
|
||||
Bark!
|
||||
Sitting...
|
||||
Change batteries
|
||||
Meow!
|
||||
In my basket...
|
||||
Fill Bowl
|
||||
"""
|
||||
}
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
package inheritanceAndExtensionsExercise2
|
||||
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
||||
class TestInheritanceAndExtensionsExercise2 {
|
||||
@Test fun testSolution() {
|
||||
//TODO: implement your test here
|
||||
Assert.assertTrue("Tests not implemented for the task", false)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package inheritanceAndExtensionsExercise3
|
||||
|
||||
// type your solution here
|
|
@ -0,0 +1,6 @@
|
|||
type: edu
|
||||
files:
|
||||
- name: src/Task.kt
|
||||
visible: true
|
||||
- name: test/Tests.kt
|
||||
visible: false
|
|
@ -0,0 +1,52 @@
|
|||
## Inheritance & Extensions (#3)
|
||||
|
||||
Starting with `Adapter.kt` and `ComposeAdapter.kt`, create a function which
|
||||
dynamically adapts an object, accepting a `MyClass` and returning a
|
||||
`MyClassAdaptedForLib`. Is it possible to use the inheritance approach in
|
||||
`Adapter.kt`, or are you forced to use composition? (We'll revisit this issue
|
||||
in [Class Delegation]).
|
||||
|
||||
```kotlin
|
||||
// InheritanceExtensions/InhExtensionsEx3.kt
|
||||
package inheritanceextensionsex3
|
||||
import usefullibrary.*
|
||||
import atomictest.*
|
||||
|
||||
private val trace = Trace()
|
||||
|
||||
class MyClass {
|
||||
fun g() = trace("g()")
|
||||
fun h() = trace("h()")
|
||||
}
|
||||
|
||||
fun useMyClass(mc: MyClass) {
|
||||
mc.g()
|
||||
mc.h()
|
||||
}
|
||||
|
||||
class MyClassAdaptedForLib(
|
||||
val field: MyClass
|
||||
) : LibType {
|
||||
override fun f1() = field.h()
|
||||
override fun f2() = field.g()
|
||||
}
|
||||
|
||||
fun adapt(mc: MyClass) =
|
||||
MyClassAdaptedForLib(mc)
|
||||
|
||||
fun main() {
|
||||
val library = UsefulLibrary()
|
||||
val mc = adapt(MyClass())
|
||||
library.utility1(mc)
|
||||
library.utility2(mc)
|
||||
useMyClass(mc.field)
|
||||
trace eq """
|
||||
h()
|
||||
g()
|
||||
g()
|
||||
h()
|
||||
g()
|
||||
h()
|
||||
"""
|
||||
}
|
||||
```
|
|
@ -0,0 +1,11 @@
|
|||
package inheritanceAndExtensionsExercise3
|
||||
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
||||
class TestInheritanceAndExtensionsExercise3 {
|
||||
@Test fun testSolution() {
|
||||
//TODO: implement your test here
|
||||
Assert.assertTrue("Tests not implemented for the task", false)
|
||||
}
|
||||
}
|
|
@ -1,2 +1,5 @@
|
|||
content:
|
||||
- Examples
|
||||
- Exercise 1
|
||||
- Exercise 2
|
||||
- Exercise 3
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
package inheritanceExercise4
|
||||
|
||||
import atomictest.eq
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sqrt
|
||||
|
||||
class Bomb(
|
||||
private val diameter: Int
|
||||
) : StaticElement() {
|
||||
override val symbol get() = '0' + diameter
|
||||
|
||||
override fun play(maze: Maze) {
|
||||
val bombCell = maze.cell(this) ?: return
|
||||
maze.all().forEach { element ->
|
||||
val cell = maze.cell(element)
|
||||
if (cell != null &&
|
||||
isCloseToBomb(cell, bombCell)) {
|
||||
maze.remove(element)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isCloseToBomb(cell: Cell, bombCell: Cell) =
|
||||
2 * distance(cell, bombCell) < diameter + 0.0000001
|
||||
|
||||
private fun distance(from: Cell, to: Cell): Double {
|
||||
fun sqr(i: Int) = i.toDouble().pow(2)
|
||||
return sqrt(sqr(from.x - to.x) + sqr(from.y - to.y))
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val representation = """
|
||||
###########
|
||||
#.........#
|
||||
#.........#
|
||||
#.........#
|
||||
#....4....#
|
||||
#.........#
|
||||
#.........#
|
||||
#.........#
|
||||
###########
|
||||
""".trimIndent()
|
||||
val maze = MazeImpl(representation)
|
||||
maze.toString() eq representation
|
||||
|
||||
val bombCell = Cell(x = 5, y = 4)
|
||||
// Adding a robot to the 'bomb' cell should trigger the bomb
|
||||
val robot = Robot()
|
||||
maze.add(robot, bombCell)
|
||||
|
||||
val bomb = maze.allIn(bombCell)
|
||||
.filterIsInstance<Bomb>().single()
|
||||
bomb.play(maze)
|
||||
|
||||
maze.toString() eq """
|
||||
###########
|
||||
#.........#
|
||||
#.... ....#
|
||||
#... ...#
|
||||
#.. ..#
|
||||
#... ...#
|
||||
#.... ....#
|
||||
#.........#
|
||||
###########
|
||||
""".trimIndent()
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
package inheritanceExercise4
|
||||
|
||||
interface GameElement {
|
||||
val symbol: Char
|
||||
fun play(maze: Maze)
|
||||
}
|
||||
|
||||
open class StaticElement : GameElement {
|
||||
override fun play(maze: Maze) {
|
||||
// Default implementation: do nothing
|
||||
}
|
||||
|
||||
override val symbol: Char
|
||||
get() = ' '
|
||||
}
|
||||
|
||||
class Wall : StaticElement() {
|
||||
override val symbol get() = '#'
|
||||
}
|
||||
|
||||
class Food : StaticElement() {
|
||||
override val symbol get() = '.'
|
||||
}
|
||||
|
||||
class Robot : GameElement {
|
||||
private var eatenFoodItems: Int = 0
|
||||
|
||||
override val symbol: Char
|
||||
get() = 'R'
|
||||
|
||||
override fun play(maze: Maze) {
|
||||
val cell = maze.cell(this) ?: return
|
||||
val cellElements = maze.allIn(cell)
|
||||
cellElements
|
||||
.filterIsInstance<Food>()
|
||||
.forEach { food ->
|
||||
eatenFoodItems++
|
||||
maze.remove(food)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createGameElement(char: Char?): GameElement? = when (char) {
|
||||
'#' -> Wall()
|
||||
'.' -> Food()
|
||||
'R' -> Robot()
|
||||
in '0'..'9' -> Bomb(char!! - '0')
|
||||
else -> null
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
package inheritanceExercise4
|
||||
|
||||
data class Cell(val x: Int, val y: Int)
|
||||
|
||||
interface Maze {
|
||||
val width: Int
|
||||
val height: Int
|
||||
fun all(): Set<GameElement>
|
||||
fun allIn(cell: Cell): Set<GameElement>
|
||||
fun cell(element: GameElement): Cell?
|
||||
fun add(element: GameElement, cell: Cell)
|
||||
fun remove(element: GameElement)
|
||||
}
|
||||
|
||||
class MazeImpl(
|
||||
representation: String
|
||||
) : Maze {
|
||||
override val width: Int
|
||||
override val height: Int
|
||||
|
||||
private val elementMap: List<List<MutableSet<GameElement>>>
|
||||
private val cellMap = mutableMapOf<GameElement, Cell>()
|
||||
|
||||
init {
|
||||
val lines = representation.lines()
|
||||
height = lines.size
|
||||
width = lines.maxBy { it.length }?.length ?: 0
|
||||
elementMap = List(height) {
|
||||
List(width) { mutableSetOf<GameElement>() }
|
||||
}
|
||||
for (y in 0 until height) {
|
||||
for (x in 0 until width) {
|
||||
val ch = lines.getOrNull(y)?.getOrNull(x)
|
||||
val element = createGameElement(ch)
|
||||
if (element != null) {
|
||||
add(element, Cell(x, y))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun elements(cell: Cell): MutableSet<GameElement> {
|
||||
return elementMap[cell.y][cell.x]
|
||||
}
|
||||
|
||||
override fun all(): Set<GameElement> {
|
||||
return cellMap.keys.toSet()
|
||||
}
|
||||
|
||||
override fun allIn(cell: Cell): Set<GameElement> {
|
||||
return elements(cell)
|
||||
}
|
||||
|
||||
override fun cell(element: GameElement): Cell? {
|
||||
return cellMap[element]
|
||||
}
|
||||
|
||||
override fun add(element: GameElement, cell: Cell) {
|
||||
elements(cell) += element
|
||||
cellMap[element] = cell
|
||||
}
|
||||
|
||||
override fun remove(element: GameElement) {
|
||||
val cell = cell(element) ?: return
|
||||
elements(cell) -= element
|
||||
cellMap.remove(element)
|
||||
}
|
||||
|
||||
override fun toString() =
|
||||
elementMap.joinToString("\n") { row ->
|
||||
row.joinToString("") { elements ->
|
||||
"${elements.lastOrNull()?.symbol ?: ' '}"
|
||||
}.trimEnd()
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
type: edu
|
||||
files:
|
||||
- name: src/Bomb.kt
|
||||
visible: true
|
||||
placeholders:
|
||||
- offset: 245
|
||||
length: 222
|
||||
placeholder_text: TODO()
|
||||
- name: src/GameElements.kt
|
||||
visible: true
|
||||
- name: src/Maze.kt
|
||||
visible: true
|
||||
- name: test/Tests.kt
|
||||
visible: false
|
||||
feedback_link: |
|
||||
https://docs.google.com/forms/d/e/1FAIpQLSdkaliSwYkjiV21bZl0yP-In2g5p17sAQCfaGjyHx_QYMWTiQ/viewform?usp=pp_url&entry.189755027=Object-Oriented+Programming+%2F+Inheritance+%2F+Exercise1
|
|
@ -1,8 +0,0 @@
|
|||
## Inheritance (#1)
|
||||
|
||||
Implement a more powerful version of the bomb: the one with a diameter of
|
||||
the explosion. All the cells that are close enough to the bomb must be destroyed.
|
||||
The bomb should explode when any mobile element steps into the same cell.
|
||||
|
||||
Use the provided auxiliary function `isCloseToBomb()` to check whether
|
||||
an element is close enough to the bomb to be destroyed.
|
|
@ -1,178 +0,0 @@
|
|||
package inheritanceExercise4
|
||||
|
||||
import org.junit.Assert
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runners.MethodSorters
|
||||
import util.TIMEOUT
|
||||
|
||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
class TestInheritanceExercise4 {
|
||||
private fun checkMaze(
|
||||
bombCell: Cell,
|
||||
mazeRepresentation: String,
|
||||
afterDestroy: String,
|
||||
prefixMessage: String? = null
|
||||
) {
|
||||
val maze = MazeImpl(mazeRepresentation)
|
||||
val robot = Robot()
|
||||
maze.add(robot, bombCell)
|
||||
val bomb = maze.allIn(bombCell)
|
||||
.filterIsInstance<Bomb>().single()
|
||||
bomb.play(maze)
|
||||
|
||||
Assert.assertEquals("${prefixMessage ?: ""} " +
|
||||
"Wrong result after exploding a bomb for the following maze:\n$mazeRepresentation",
|
||||
afterDestroy,
|
||||
maze.toString())
|
||||
}
|
||||
|
||||
@Test(timeout = TIMEOUT)
|
||||
fun test1Sample() = checkMaze(Cell(5, 4),
|
||||
"""
|
||||
###########
|
||||
#.........#
|
||||
#.........#
|
||||
#.........#
|
||||
#....4....#
|
||||
#.........#
|
||||
#.........#
|
||||
#.........#
|
||||
###########
|
||||
""".trimIndent(),
|
||||
"""
|
||||
###########
|
||||
#.........#
|
||||
#.... ....#
|
||||
#... ...#
|
||||
#.. ..#
|
||||
#... ...#
|
||||
#.... ....#
|
||||
#.........#
|
||||
###########
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
@Test(timeout = TIMEOUT)
|
||||
fun test2() = checkMaze(Cell(5, 4),
|
||||
"""
|
||||
###########
|
||||
#.........#
|
||||
#.........#
|
||||
#.........#
|
||||
#....1....#
|
||||
#.........#
|
||||
#.........#
|
||||
#.........#
|
||||
###########
|
||||
""".trimIndent(),
|
||||
"""
|
||||
###########
|
||||
#.........#
|
||||
#.........#
|
||||
#.........#
|
||||
#.... ....#
|
||||
#.........#
|
||||
#.........#
|
||||
#.........#
|
||||
###########
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
@Test(timeout = TIMEOUT)
|
||||
fun test3() = checkMaze(Cell(5, 4),
|
||||
"""
|
||||
###########
|
||||
#.........#
|
||||
#.........#
|
||||
#.........#
|
||||
#....2....#
|
||||
#.........#
|
||||
#.........#
|
||||
#.........#
|
||||
###########
|
||||
""".trimIndent(),
|
||||
"""
|
||||
###########
|
||||
#.........#
|
||||
#.........#
|
||||
#.... ....#
|
||||
#... ...#
|
||||
#.... ....#
|
||||
#.........#
|
||||
#.........#
|
||||
###########
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
@Test(timeout = TIMEOUT)
|
||||
fun test4() = checkMaze(Cell(5, 4),
|
||||
"""
|
||||
###########
|
||||
#.........#
|
||||
#.........#
|
||||
#.........#
|
||||
#....3....#
|
||||
#.........#
|
||||
#.........#
|
||||
#.........#
|
||||
###########
|
||||
""".trimIndent(),
|
||||
"""
|
||||
###########
|
||||
#.........#
|
||||
#.........#
|
||||
#... ...#
|
||||
#... ...#
|
||||
#... ...#
|
||||
#.........#
|
||||
#.........#
|
||||
###########
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
@Test(timeout = TIMEOUT)
|
||||
fun test5() = checkMaze(Cell(5, 4),
|
||||
"""
|
||||
###########
|
||||
#.........#
|
||||
#.........#
|
||||
#.........#
|
||||
#....5....#
|
||||
#.........#
|
||||
#.........#
|
||||
#.........#
|
||||
###########
|
||||
""".trimIndent(),
|
||||
"""
|
||||
###########
|
||||
#.........#
|
||||
#... ...#
|
||||
#.. ..#
|
||||
#.. ..#
|
||||
#.. ..#
|
||||
#... ...#
|
||||
#.........#
|
||||
###########
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
@Test(timeout = TIMEOUT)
|
||||
fun test6() = checkMaze(Cell(2, 2),
|
||||
"""
|
||||
#####
|
||||
#####
|
||||
# 3##
|
||||
#####
|
||||
#####
|
||||
""".trimIndent(),
|
||||
"""
|
||||
#####
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
#####
|
||||
""".trimIndent(),
|
||||
"Walls should also be exploded"
|
||||
)
|
||||
}
|
|
@ -3,4 +3,3 @@ content:
|
|||
- Exercise 1
|
||||
- Exercise 2
|
||||
- Exercise 3
|
||||
- Exercise 4
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
|
||||
Define a `Pet` interface with a `speak()` function that returns a `String`.
|
||||
Implement three different `Pet`s: `Dog` which produces `"Bark!"`, `Cat` which
|
||||
produces `"Meow!"` and `Hamster` which produces `"Squeak!"`. Create a `List<Pet>`
|
||||
containing all three types of `Pet`. Iterate through the `List` and call
|
||||
`speak()` for each `Pet`.
|
||||
produces `"Meow!"` and `Hamster` which produces `"Squeak!"`. Create a
|
||||
`List<Pet>` containing all three types of `Pet`. Iterate through the `List` and
|
||||
call `speak()` for each `Pet`.
|
|
@ -1,6 +1,6 @@
|
|||
## Interfaces (#3)
|
||||
|
||||
Modify the previous exercise so that each `Pet` contains a `String`
|
||||
`description` property, which produces `dog`, `cat`, and `hamster`,
|
||||
appropriately. Use this property in `speak()` for each class. For example,
|
||||
`Dog.speak()` produces `"dog Bark!"`.
|
||||
Modify the previous exercise so each `Pet` contains a `String` `description`
|
||||
property, which produces `dog`, `cat`, and `hamster`, appropriately. Use this
|
||||
property in `speak()` for each class. For example, `Dog.speak()` produces `"dog
|
||||
Bark!"`.
|
|
@ -1,17 +0,0 @@
|
|||
package interfacesExercise4
|
||||
|
||||
interface GameElement {
|
||||
val symbol: Char
|
||||
}
|
||||
|
||||
class Robot : GameElement {
|
||||
override val symbol get() = 'R'
|
||||
}
|
||||
|
||||
class Wall : GameElement {
|
||||
override val symbol get() = '#'
|
||||
}
|
||||
|
||||
class Food : GameElement {
|
||||
override val symbol get() = '.'
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
package interfacesExercise4
|
||||
|
||||
import atomictest.eq
|
||||
|
||||
data class Cell(val x: Int, val y: Int)
|
||||
|
||||
interface Maze {
|
||||
val width: Int
|
||||
val height: Int
|
||||
fun all(): Set<GameElement>
|
||||
fun allIn(cell: Cell): Set<GameElement>
|
||||
fun cell(element: GameElement): Cell?
|
||||
fun add(element: GameElement, cell: Cell)
|
||||
fun remove(element: GameElement)
|
||||
}
|
||||
|
||||
class MazeImpl(
|
||||
override val width: Int,
|
||||
override val height: Int
|
||||
) : Maze {
|
||||
private val elementMap = List(height) {
|
||||
List(width) { mutableSetOf<GameElement>() }
|
||||
}
|
||||
private val cellMap = mutableMapOf<GameElement, Cell>()
|
||||
|
||||
private fun elementsIn(cell: Cell): MutableSet<GameElement> {
|
||||
return elementMap[cell.y][cell.x]
|
||||
}
|
||||
|
||||
override fun all(): Set<GameElement> {
|
||||
return cellMap.keys.toSet()
|
||||
}
|
||||
|
||||
override fun allIn(cell: Cell): Set<GameElement> {
|
||||
return elementsIn(cell)
|
||||
}
|
||||
|
||||
override fun cell(element: GameElement): Cell? {
|
||||
return cellMap[element]
|
||||
}
|
||||
|
||||
override fun add(element: GameElement, cell: Cell) {
|
||||
elementsIn(cell) += element
|
||||
cellMap[element] = cell
|
||||
}
|
||||
|
||||
override fun remove(element: GameElement) {
|
||||
val cell = cell(element) ?: return
|
||||
elementsIn(cell) -= element
|
||||
cellMap.remove(element)
|
||||
}
|
||||
|
||||
override fun toString() =
|
||||
elementMap.joinToString("\n") { row ->
|
||||
row.joinToString("") { elements ->
|
||||
// As we can't display several elements on the one cell
|
||||
// as one character, for simplicity
|
||||
// we display only the last element
|
||||
"${elements.lastOrNull()?.symbol ?: ' '}"
|
||||
}.trimEnd()
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val maze = MazeImpl(width = 4, height = 5)
|
||||
maze.add(Robot(), Cell(x = 1, y = 2))
|
||||
|
||||
val food = Food()
|
||||
maze.add(food, Cell(x = 2, y = 3))
|
||||
for (y in 0..4) {
|
||||
maze.add(Wall(), Cell(0, y))
|
||||
maze.add(Wall(), Cell(3, y))
|
||||
}
|
||||
for (x in 1..2) {
|
||||
maze.add(Wall(), Cell(x, 0))
|
||||
maze.add(Wall(), Cell(x, 4))
|
||||
}
|
||||
maze.toString() eq
|
||||
"""
|
||||
####
|
||||
# #
|
||||
#R #
|
||||
# .#
|
||||
####
|
||||
""".trimIndent()
|
||||
maze.allIn(Cell(1, 2)).singleOrNull()?.symbol eq 'R'
|
||||
maze.allIn(Cell(2, 3)).singleOrNull()?.symbol eq '.'
|
||||
|
||||
maze.remove(food)
|
||||
maze.allIn(Cell(2, 3)) eq emptySet()
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
type: edu
|
||||
files:
|
||||
- name: src/GameMatrix.kt
|
||||
visible: true
|
||||
placeholders:
|
||||
- offset: 986
|
||||
length: 55
|
||||
placeholder_text: TODO()
|
||||
- offset: 1097
|
||||
length: 94
|
||||
placeholder_text: TODO()
|
||||
- name: src/GameElements.kt
|
||||
visible: true
|
||||
- name: test/Tests.kt
|
||||
visible: false
|
||||
feedback_link: |
|
||||
https://docs.google.com/forms/d/e/1FAIpQLSdkaliSwYkjiV21bZl0yP-In2g5p17sAQCfaGjyHx_QYMWTiQ/viewform?usp=pp_url&entry.189755027=Object-Oriented+Programming+%2F+Interfaces+%2F+Exercise1
|
|
@ -1,12 +0,0 @@
|
|||
## Interfaces (#1)
|
||||
|
||||
The `MazeImpl` class implements the `Maze` interface. It stores game elements
|
||||
in a "table" of the size `height * width`, which is implemented as a `List` of
|
||||
`List`s, where the outer `List` is of `height` size and each inner `List` is of
|
||||
`width` size. Each cell holds a set of `GameElement`s. `MazeImpl` also keeps a
|
||||
record of the `Cell`s of all the elements by storing a `cellMap` `Map`
|
||||
which has `GameElement` keys and `Cell` values.
|
||||
|
||||
Your task is to implement the `add(GameElement, Cell)` and
|
||||
`remove(GameElement, Cell)` functions that add and remove `GameElement`s
|
||||
at a given `Cell`.
|
|
@ -1,92 +0,0 @@
|
|||
package interfacesExercise4
|
||||
|
||||
import org.junit.Assert
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
import org.junit.runners.MethodSorters
|
||||
import util.TIMEOUT
|
||||
|
||||
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||
class TestInterfacesExercise4 {
|
||||
@Test(timeout = TIMEOUT)
|
||||
fun test1Sample() {
|
||||
val matrix = MazeImpl(width = 4, height = 5)
|
||||
matrix.add(Robot(), Cell(x = 1, y = 2))
|
||||
matrix.add(Food(), Cell(x = 2, y = 3))
|
||||
for (i in 0..4) {
|
||||
matrix.add(Wall(), Cell(0, i))
|
||||
matrix.add(Wall(), Cell(3, i))
|
||||
}
|
||||
for (i in 1..2) {
|
||||
matrix.add(Wall(), Cell(i, 0))
|
||||
matrix.add(Wall(), Cell(i, 4))
|
||||
}
|
||||
Assert.assertEquals("Wrong result for the sample:",
|
||||
"""
|
||||
####
|
||||
# #
|
||||
#R #
|
||||
# .#
|
||||
####
|
||||
""".trimIndent(),
|
||||
matrix.toString())
|
||||
Assert.assertEquals("Wrong result for the sample. In Cell(1, 2) should be Robot",
|
||||
'R', matrix.allIn(Cell(1, 2)).singleOrNull()?.symbol)
|
||||
}
|
||||
|
||||
private fun createGameElement(char: Char): GameElement? = when (char) {
|
||||
'#' -> Wall()
|
||||
'.' -> Food()
|
||||
'R' -> Robot()
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun checkMaze(maze: String) {
|
||||
val lines = maze.lines().filter { it.isNotEmpty() }
|
||||
val height = lines.size
|
||||
val lengths = lines.map { it.length }
|
||||
val width = lengths.first()
|
||||
|
||||
val matrix = MazeImpl(width, height)
|
||||
for (y in 0 until height) {
|
||||
for (x in 0 until width) {
|
||||
val ch = lines[y][x]
|
||||
val element = createGameElement(ch)
|
||||
if (element != null) {
|
||||
matrix.add(element, Cell(x, y))
|
||||
}
|
||||
}
|
||||
}
|
||||
Assert.assertEquals("Wrong result for the maze:",
|
||||
maze,
|
||||
matrix.toString())
|
||||
}
|
||||
|
||||
@Test(timeout = TIMEOUT)
|
||||
fun test2() = checkMaze("""
|
||||
####
|
||||
####
|
||||
####
|
||||
""".trimIndent())
|
||||
|
||||
@Test(timeout = TIMEOUT)
|
||||
fun test3() = checkMaze("""
|
||||
####
|
||||
#R.#
|
||||
####
|
||||
""".trimIndent())
|
||||
|
||||
@Test(timeout = TIMEOUT)
|
||||
fun test4() = checkMaze("""
|
||||
. ##
|
||||
#
|
||||
# R
|
||||
""".trimIndent())
|
||||
|
||||
@Test(timeout = TIMEOUT)
|
||||
fun test5() = checkMaze("""
|
||||
....
|
||||
....
|
||||
...R
|
||||
""".trimIndent())
|
||||
}
|
|
@ -3,4 +3,3 @@ content:
|
|||
- Exercise 1
|
||||
- Exercise 2
|
||||
- Exercise 3
|
||||
- Exercise 4
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
// RobotExplorer3/ExploreMaze3.kt
|
||||
package robotexplorer3
|
||||
import robotmaze.*
|
||||
|
||||
fun testFactory(maze: String) {
|
||||
println(Player.prototypes.map {
|
||||
it::class.simpleName
|
||||
})
|
||||
val lines = maze.split("\n")
|
||||
lines.withIndex().forEach { (row, line) ->
|
||||
line.withIndex().forEach { (col, ch) ->
|
||||
println(Player.factory(ch))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
// testFactory(stringMaze)
|
||||
Stage(stringMaze).run(solution)
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
// RobotExplorer3/Players3.kt
|
||||
package robotexplorer3
|
||||
import robotexplorer1.Urge
|
||||
import kotlin.reflect.full.createInstance
|
||||
|
||||
sealed class Player {
|
||||
abstract val symbol: Char
|
||||
abstract val room: Room
|
||||
open fun id() = symbol.toString()
|
||||
override fun toString() =
|
||||
"${this::class.simpleName} ${id()}"
|
||||
open class Result(
|
||||
val success: Boolean,
|
||||
val room: Room
|
||||
)
|
||||
class Success(room: Room):
|
||||
Result(true, room)
|
||||
object Fail: Result(false, Room())
|
||||
abstract fun makePlayer(room: Room): Player
|
||||
open fun create(ch: Char): Result {
|
||||
if (ch == symbol) {
|
||||
val room = Room()
|
||||
room.player = makePlayer(room)
|
||||
return Success(room)
|
||||
}
|
||||
return Fail
|
||||
}
|
||||
companion object {
|
||||
val prototypes: List<Player> =
|
||||
Player::class.sealedSubclasses.map {
|
||||
it.createInstance()
|
||||
}
|
||||
fun factory(ch: Char): Room =
|
||||
prototypes.map { it.create(ch) }
|
||||
.first { it.success }.room
|
||||
}
|
||||
abstract fun interact(robot: Robot): Room
|
||||
}
|
||||
|
||||
class Void() : Player() {
|
||||
override val symbol = '~'
|
||||
override val room: Room
|
||||
get() = throw IllegalAccessException()
|
||||
override fun makePlayer(room: Room) =
|
||||
Void()
|
||||
override fun interact(robot: Robot) =
|
||||
robot.room // Stay in old room
|
||||
}
|
||||
|
||||
class Wall(
|
||||
override val room: Room = Room()
|
||||
) : Player() {
|
||||
override val symbol = '#'
|
||||
override fun makePlayer(room: Room) =
|
||||
Wall(room)
|
||||
override fun interact(robot: Robot) =
|
||||
robot.room // Stay in old room
|
||||
}
|
||||
|
||||
class Food(
|
||||
override val room: Room = Room()
|
||||
) : Player() {
|
||||
override val symbol = '.'
|
||||
override fun makePlayer(room: Room) =
|
||||
Food(room)
|
||||
override fun interact(robot: Robot): Room {
|
||||
robot.energy++ // Consume food
|
||||
room.player = Empty(room) // Remove food
|
||||
return room // Move into new room
|
||||
}
|
||||
}
|
||||
|
||||
class Empty(
|
||||
override val room: Room = Room()
|
||||
) : Player() {
|
||||
override val symbol = '_'
|
||||
override fun makePlayer(room: Room) =
|
||||
Empty(room)
|
||||
// Move into new room:
|
||||
override fun interact(robot: Robot) = room
|
||||
}
|
||||
|
||||
class EndGame(
|
||||
override val room: Room = Room()
|
||||
) : Player() {
|
||||
override val symbol = '!'
|
||||
override fun makePlayer(room: Room) =
|
||||
EndGame(room)
|
||||
override fun interact(robot: Robot) =
|
||||
Room(EndGame(room))
|
||||
}
|
||||
|
||||
class Robot(
|
||||
override var room: Room = Room()
|
||||
) : Player() {
|
||||
override val symbol = 'R'
|
||||
var energy = 0
|
||||
override fun makePlayer(room: Room) =
|
||||
Robot(room)
|
||||
override fun create(ch: Char) =
|
||||
if (ch == symbol)
|
||||
Success(Room())
|
||||
else Fail
|
||||
// Shouldn't happen:
|
||||
override fun interact(robot: Robot) =
|
||||
throw IllegalAccessException()
|
||||
fun move(urge: Urge) {
|
||||
val nextRoom = room.doors.open(urge)
|
||||
room = nextRoom.player.interact(this)
|
||||
}
|
||||
}
|
||||
|
||||
class Teleport(
|
||||
val target: Char = 'Z',
|
||||
override val room: Room = Room()
|
||||
) : Player() {
|
||||
override val symbol = 'T'
|
||||
var targetRoom = Room()
|
||||
override fun id() = target.toString()
|
||||
override fun toString() =
|
||||
"${this::class.simpleName} " +
|
||||
"$symbol: $target $targetRoom"
|
||||
override fun makePlayer(room: Room) =
|
||||
throw IllegalStateException()
|
||||
override fun create(ch: Char): Result {
|
||||
if (ch in 'a'..'z') {
|
||||
val room = Room()
|
||||
room.player = Teleport(ch, room)
|
||||
return Success(room)
|
||||
}
|
||||
return Fail
|
||||
}
|
||||
override fun interact(robot: Robot) =
|
||||
targetRoom
|
||||
}
|
||||
|
||||
class Bomb(
|
||||
override val room: Room = Room()
|
||||
) : Player() {
|
||||
override val symbol = '*'
|
||||
override fun makePlayer(room: Room) =
|
||||
Bomb(room)
|
||||
override fun interact(robot: Robot): Room {
|
||||
robot.energy = 0 // Bomb erases energy
|
||||
room.player = Empty(room)
|
||||
return room
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
// RobotExplorer3/Room3.kt
|
||||
package robotexplorer3
|
||||
import robotexplorer1.Urge
|
||||
import robotexplorer1.Urge.*
|
||||
|
||||
class Room(var player: Player = Void()) {
|
||||
val doors = Doors()
|
||||
override fun toString() = "Room($player)"
|
||||
companion object {
|
||||
val edge = Room()
|
||||
}
|
||||
}
|
||||
|
||||
class Doors {
|
||||
private val doors = mutableMapOf(
|
||||
North to Room.edge,
|
||||
South to Room.edge,
|
||||
East to Room.edge,
|
||||
West to Room.edge
|
||||
)
|
||||
fun open(urge: Urge): Room =
|
||||
doors.getOrDefault(urge, Room.edge)
|
||||
fun connect(
|
||||
row: Int, col: Int,
|
||||
rooms: Map<Pair<Int, Int>, Room>
|
||||
) {
|
||||
fun link(toRow: Int, toCol: Int) =
|
||||
rooms.getOrDefault(
|
||||
Pair(toRow, toCol), Room.edge)
|
||||
doors[North] = link(row - 1, col)
|
||||
doors[South] = link(row + 1, col)
|
||||
doors[East] = link(row, col + 1)
|
||||
doors[West] = link(row, col - 1)
|
||||
}
|
||||
}
|
|
@ -1,35 +1,28 @@
|
|||
// RobotExplorer3/Stage3.kt
|
||||
package robotexplorer3
|
||||
import robotexplorer1.urge
|
||||
import robotexplorer2.Adapter
|
||||
import robotexplorer2.View
|
||||
|
||||
class Stage(val maze: String) {
|
||||
val robot = Robot(Room())
|
||||
val rooms =
|
||||
mutableMapOf<Pair<Int, Int>, Room>()
|
||||
private val view = View(this)
|
||||
val lines = maze.split("\n")
|
||||
private fun factory(ch: Char): Room {
|
||||
val room = Room()
|
||||
when (ch) {
|
||||
'R' -> robot.room = room
|
||||
'#' -> room.player = Wall()
|
||||
'.' -> room.player = Food()
|
||||
'_' -> room.player = Empty()
|
||||
'/' -> room.player = Edge()
|
||||
'!' -> room.player = EndGame()
|
||||
else -> {
|
||||
val teleport = Teleport(ch)
|
||||
teleport.originRoom = room
|
||||
room.player = teleport
|
||||
}
|
||||
}
|
||||
return room
|
||||
private inner class Adapt : Adapter {
|
||||
override fun height() =
|
||||
maze.lines().size + 3
|
||||
override fun textView() = mazeView()
|
||||
}
|
||||
// Construct it with the 'Builder' pattern:
|
||||
init {
|
||||
private val view = View(Adapt())
|
||||
val lines = maze.split("\n")
|
||||
init { // The 'Builder' pattern:
|
||||
// Step 1: Create rooms with players:
|
||||
lines.withIndex().forEach { (row, line) ->
|
||||
line.withIndex().forEach { (col, ch) ->
|
||||
rooms[Pair(row, col)] = factory(ch)
|
||||
val room = Player.factory(ch)
|
||||
rooms[Pair(row, col)] = room
|
||||
if(ch == robot.symbol)
|
||||
robot.room = room
|
||||
}
|
||||
}
|
||||
// Step 2: Connect the doors
|
||||
|
@ -47,8 +40,8 @@ class Stage(val maze: String) {
|
|||
it.target
|
||||
}.zipWithNext()
|
||||
for((a, b) in teleportPairs) {
|
||||
a.targetRoom = b.originRoom
|
||||
b.targetRoom = a.originRoom
|
||||
a.targetRoom = b.room
|
||||
b.targetRoom = a.room
|
||||
}
|
||||
}
|
||||
fun run(solution: String) {
|
||||
|
@ -60,4 +53,20 @@ class Stage(val maze: String) {
|
|||
view.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Stage.mazeView(): String {
|
||||
var result = ""
|
||||
var currentRow = 0
|
||||
rooms.forEach { (pair, room) ->
|
||||
val row = pair.first
|
||||
if (row != currentRow) {
|
||||
result += "\n"
|
||||
currentRow = row
|
||||
}
|
||||
result += if (room == robot.room)
|
||||
robot.id() else room.player.id()
|
||||
}
|
||||
return result +
|
||||
"\n\nEnergy: ${robot.energy}\n"
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
type: theory
|
||||
files:
|
||||
- name: src/Players3.kt
|
||||
visible: true
|
||||
- name: src/ExploreMaze3.kt
|
||||
visible: true
|
||||
- name: src/Room3.kt
|
||||
visible: true
|
||||
- name: src/Stage3.kt
|
||||
visible: true
|
|
@ -0,0 +1,3 @@
|
|||
## OO Design, Revisited
|
||||
|
||||
Examples accompanying the atom.
|
|
@ -1,9 +1,8 @@
|
|||
// ObjectOrientedDesign/Doors.kt
|
||||
package robotexplorer
|
||||
import robotexplorer.Player.*
|
||||
import robotexplorer.Urge.*
|
||||
// RobotExplorer1/Doors.kt
|
||||
package robotexplorer1
|
||||
import robotexplorer1.Urge.*
|
||||
|
||||
val edge = Room(Edge)
|
||||
val edge = Room()
|
||||
|
||||
class Doors {
|
||||
private var north: Room = edge
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// ObjectOrientedDesign/ExploreMaze.kt
|
||||
package robotexplorer
|
||||
// RobotExplorer1/ExploreMaze.kt
|
||||
package robotexplorer1
|
||||
import robotmaze.*
|
||||
|
||||
fun main() {
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
// RobotExplorer1/MazeView.kt
|
||||
package robotexplorer1
|
||||
|
||||
fun Stage.mazeView(): String {
|
||||
var result = ""
|
||||
var currentRow = 0
|
||||
rooms.forEach { (pair, room) ->
|
||||
val row = pair.first
|
||||
if (row != currentRow) {
|
||||
result += "\n"
|
||||
currentRow = row
|
||||
}
|
||||
result += if (room == robot.room)
|
||||
"$robot" else "${room.player}"
|
||||
}
|
||||
return result +
|
||||
"\n\nEnergy: ${robot.energy}\n"
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
// ObjectOrientedDesign/Players.kt
|
||||
package robotexplorer
|
||||
// RobotExplorer1/Players.kt
|
||||
package robotexplorer1
|
||||
|
||||
class Robot(var room: Room) {
|
||||
val symbol = 'R'
|
||||
var energy = 0
|
||||
fun move(urge: Urge) {
|
||||
// Get a reference to the Room you've
|
||||
// been urged to go to, and see what
|
||||
|
@ -9,14 +11,14 @@ class Robot(var room: Room) {
|
|||
// Point robot to returned Room:
|
||||
room = room.doors.open(urge).enter(this)
|
||||
}
|
||||
override fun toString() = "R"
|
||||
override fun toString() = symbol.toString()
|
||||
}
|
||||
|
||||
enum class Player(val symbol: Char) {
|
||||
Wall('#'),
|
||||
Food('.'),
|
||||
Empty('_'),
|
||||
Edge('/'),
|
||||
Void('~'),
|
||||
EndGame('!');
|
||||
override fun toString() = symbol.toString()
|
||||
}
|
||||
|
@ -25,4 +27,18 @@ class Teleport(val target: Char) {
|
|||
var originRoom = Room()
|
||||
var targetRoom = Room()
|
||||
override fun toString() = target.toString()
|
||||
}
|
||||
|
||||
fun factory(ch: Char): Room {
|
||||
val room = Room()
|
||||
Player.values().forEach {
|
||||
if (ch == it.symbol) {
|
||||
room.player = it
|
||||
return room
|
||||
}
|
||||
}
|
||||
val teleport = Teleport(ch)
|
||||
room.player = teleport
|
||||
teleport.originRoom = room
|
||||
return room
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// ObjectOrientedDesign/RobotMaze.kt
|
||||
// RobotExplorer1/RobotMaze.kt
|
||||
package robotmaze
|
||||
|
||||
val stringMaze = """
|
||||
|
@ -16,4 +16,22 @@ eeeeeeeeee
|
|||
wwwwwwww
|
||||
eeennnwwwwwsseeeeeen
|
||||
ww
|
||||
""".trim()
|
||||
""".trim()
|
||||
|
||||
//val stringMaze = """
|
||||
//a_...#...__#_......._c
|
||||
//R_...#...__#_..#.d..__
|
||||
//######################
|
||||
//a_......._b#c......._b
|
||||
//######################
|
||||
//!_e_.....__#_d_....._e
|
||||
//""".trim()
|
||||
//
|
||||
//val solution = """
|
||||
//eeeenwwww
|
||||
//eeeeeeeeee
|
||||
//wwwwwwwww
|
||||
//wwwwwwwwseneese
|
||||
//eeeeeeee
|
||||
//ww
|
||||
//""".trim()
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue