1
1
Fork 0

Regenerated project, added placeholders for exercises

This commit is contained in:
Svetlana Isakova 2020-02-05 19:33:00 +01:00
parent b757823579
commit abd63a217b
159 changed files with 2262 additions and 1725 deletions

View File

@ -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.

View File

@ -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
}

View File

@ -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()
}
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -0,0 +1,3 @@
package abstractClassesExercise1
// type your solution here

View File

@ -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

View File

@ -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#
###""")
}
}
}

View File

@ -0,0 +1,3 @@
package abstractClassesExercise2
// type your solution here

View File

@ -0,0 +1,6 @@
type: edu
files:
- name: src/Task.kt
visible: true
- name: test/Tests.kt
visible: false

View File

@ -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
"""
}
```

View File

@ -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)
}
}

View File

@ -0,0 +1,3 @@
package abstractClassesExercise3
// type your solution here

View File

@ -0,0 +1,6 @@
type: edu
files:
- name: src/Task.kt
visible: true
- name: test/Tests.kt
visible: false

View File

@ -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
"""
}
```

View File

@ -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)
}
}

View File

@ -1,3 +1,5 @@
content:
- Examples
- Exercise 1
- Exercise 2
- Exercise 3

View File

@ -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()
}*/

View File

@ -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
}
}*/

View File

@ -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

View File

@ -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
}
}

View File

@ -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

View File

@ -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.

View File

@ -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)
})
}
}

View File

@ -3,4 +3,3 @@ content:
- Exercise 1
- Exercise 2
- Exercise 3
- Exercise 4

View File

@ -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()
"""
}

View File

@ -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> {

View File

@ -5,7 +5,6 @@ class Numbered {
companion object {
private var count = 0
}
private val id = count++
override fun toString() = "#$id"
}

View File

@ -5,7 +5,6 @@ class WithObjectProperty {
companion object {
private var n: Int = 0 // Only one
}
fun increment(): Int {
n += 1
return n

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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.

View File

@ -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())
}

View File

@ -3,4 +3,3 @@ content:
- Exercise 1
- Exercise 2
- Exercise 3
- Exercise 4

View File

@ -0,0 +1,3 @@
package compositionExercise1
// type your solution here

View File

@ -0,0 +1,6 @@
type: edu
files:
- name: src/Task.kt
visible: true
- name: test/Tests.kt
visible: false

View File

@ -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
"""
}
```

View File

@ -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)
}
}

View File

@ -0,0 +1,3 @@
package compositionExercise2
// type your solution here

View File

@ -0,0 +1,6 @@
type: edu
files:
- name: src/Task.kt
visible: true
- name: test/Tests.kt
visible: false

View File

@ -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
"""
}
```

View File

@ -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)
}
}

View File

@ -0,0 +1,3 @@
package compositionExercise3
// type your solution here

View File

@ -0,0 +1,6 @@
type: edu
files:
- name: src/Task.kt
visible: true
- name: test/Tests.kt
visible: false

View File

@ -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
"""
}
```

View File

@ -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)
}
}

View File

@ -1,2 +1,5 @@
content:
- Examples
- Exercise 1
- Exercise 2
- Exercise 3

View File

@ -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)
}
}

View File

@ -2,7 +2,6 @@
package robotexplorer2
import robotmaze.*
/*
fun main() {
Stage(stringMaze).run(solution)
}*/
}

View File

@ -1,7 +0,0 @@
// RobotExplorer3/ExploreMaze3.kt
package robotexplorer3
import robotmaze.*
fun main() {
Stage(stringMaze).run(solution)
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -1,6 +0,0 @@
// RobotExplorer3/Room3.kt
package robotexplorer3
class Room(var player: Player = Empty()) {
val doors = Doors()
}

View File

@ -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"
}

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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

View File

@ -0,0 +1,3 @@
package inheritanceAndExtensionsExercise1
// type your solution here

View File

@ -0,0 +1,6 @@
type: edu
files:
- name: src/Task.kt
visible: true
- name: test/Tests.kt
visible: false

View File

@ -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
"""
}
```

View File

@ -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)
}
}

View File

@ -0,0 +1,3 @@
package inheritanceAndExtensionsExercise2
// type your solution here

View File

@ -0,0 +1,6 @@
type: edu
files:
- name: src/Task.kt
visible: true
- name: test/Tests.kt
visible: false

View File

@ -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
"""
}
```

View File

@ -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)
}
}

View File

@ -0,0 +1,3 @@
package inheritanceAndExtensionsExercise3
// type your solution here

View File

@ -0,0 +1,6 @@
type: edu
files:
- name: src/Task.kt
visible: true
- name: test/Tests.kt
visible: false

View File

@ -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()
"""
}
```

View File

@ -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)
}
}

View File

@ -1,2 +1,5 @@
content:
- Examples
- Exercise 1
- Exercise 2
- Exercise 3

View File

@ -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()
}

View File

@ -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
}

View File

@ -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()
}
}

View File

@ -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

View File

@ -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.

View File

@ -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"
)
}

View File

@ -3,4 +3,3 @@ content:
- Exercise 1
- Exercise 2
- Exercise 3
- Exercise 4

View File

@ -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`.

View File

@ -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!"`.

View File

@ -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() = '.'
}

View File

@ -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()
}

View File

@ -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

View File

@ -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`.

View File

@ -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())
}

View File

@ -3,4 +3,3 @@ content:
- Exercise 1
- Exercise 2
- Exercise 3
- Exercise 4

View File

@ -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)
}

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -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"
}

View File

@ -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

View File

@ -0,0 +1,3 @@
## OO Design, Revisited
Examples accompanying the atom.

View File

@ -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

View File

@ -1,5 +1,5 @@
// ObjectOrientedDesign/ExploreMaze.kt
package robotexplorer
// RobotExplorer1/ExploreMaze.kt
package robotexplorer1
import robotmaze.*
fun main() {

View File

@ -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"
}

View File

@ -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
}

View File

@ -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