Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion basic_kotlin_project/src/main/kotlin/Main.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
import model.Animal
import model.BouncyMap
import model.MoveDirection
import model.Vector2d

fun main(args: Array<String>) {
println("Hello World!")

// Try adding program arguments via Run/Debug configuration.
// Learn more about running applications: https://www.jetbrains.com/help/idea/running-applications.html.
println("Program arguments: ${args.joinToString()}")
val map = BouncyMap(5,5)
val animal1 = Animal(position = Vector2d(3, 3))
val animal2 = Animal(position = Vector2d(3, 3))
map.place(animal1)
map.place(animal2)
println(map.objectAt(Vector2d(3, 3)))
map.move(animal1,MoveDirection.FORWARD)
println(map.objectAt(Vector2d(3, 3)))
println(map.objectAt(Vector2d(3, 4)))
val map2 = ArrayList<Animal>(map.getElements)
println(map2[0].getPosition())
println(map2[1].getPosition())
// println("Program arguments: ${args.joinToString()}")
}
21 changes: 21 additions & 0 deletions basic_kotlin_project/src/main/kotlin/model/Animal.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package model

class Animal(private var orientation: MapDirection = MapDirection.NORTH, private var position: Vector2d = Vector2d(2,2)) {

override fun toString() = orientation.toString()

fun isAt(position: Vector2d) = this.position == position

fun getPosition() = this.position

fun setPosition(position: Vector2d) {
this.position = position
}

fun move(direction: MoveDirection) = when(direction) {
MoveDirection.RIGHT -> this.orientation = orientation.next()
MoveDirection.LEFT -> this.orientation = orientation.previous()
MoveDirection.FORWARD -> this.position += this.orientation.toUnitVector()
MoveDirection.BACKWARD -> this.position -= this.orientation.toUnitVector()
}
}
52 changes: 52 additions & 0 deletions basic_kotlin_project/src/main/kotlin/model/BouncyMap.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package model

class BouncyMap(private var width: Int, private var height: Int) : IWorldMap {
private val map = HashMap<Vector2d, Animal>()

override val id = this.hashCode()

override val getElements: ArrayList<Animal> get() = ArrayList(map.values)

override fun isOccupied(position: Vector2d) = (objectAt(position) != null)

fun canMoveTo(position: Vector2d) = (position follows Vector2d(0,0) && position precedes Vector2d(width,height))

override fun objectAt(position: Vector2d) = map[position]

override fun move(animal: Animal, direction: MoveDirection) {
val oldPosition = animal.getPosition()
val oldOrientation = animal.toString()
animal.move(direction)
if(canMoveTo(animal.getPosition())) {
if(animal.getPosition() != oldPosition) {
map.remove(oldPosition)
map[animal.getPosition()] = animal
} else if(oldOrientation != animal.toString()) {
map[animal.getPosition()] = animal
}
}
}

override fun place(animal: Animal) {
val position = animal.getPosition()
if(map.containsValue(animal)) {
val oldPosition = map.filterValues { it == animal }.keys.first()
map.remove(oldPosition)
map[position] = animal
} else if(canMoveTo(position)) {
if(objectAt(position) == null) {
map[position] = animal
} else if(map.values.size == width * height){
val randomPosition = map.randomPosition()
map.remove(randomPosition)
map[position] = animal
} else {
val freePosition = map.randomFreePosition(Vector2d(width,height))
if(freePosition != null) {
map[freePosition] = animal
animal.setPosition(freePosition)
}
}
}
}
}
43 changes: 43 additions & 0 deletions basic_kotlin_project/src/main/kotlin/model/IWorldMap.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package model

/**
* The interface responsible for interacting with the map of the world.
* Assumes that Vector2d and MoveDirection classes are defined.
*
* @author apohllo, idzik
*/
interface IWorldMap {
/**
* Place a animal on the map.
*
* @param animal The animal to place on the map.
* @return True if the animal was placed. The animal cannot be placed if the move is not valid.
*/
fun place(animal: Animal)

/**
* Moves an animal (if it is present on the map) according to specified direction.
* If the move is not possible, this method has no effect.
*/
fun move(animal: Animal, direction: MoveDirection)

/**
* Return true if given position on the map is occupied. Should not be
* confused with canMove since there might be empty positions where the animal
* cannot move.
*
* @param position Position to check.
* @return True if the position is occupied.
*/
fun isOccupied(position: Vector2d): Boolean

/**
* Return an animal at a given position.
*
* @param position The position of the animal.
* @return animal or null if the position is not occupied.
*/
fun objectAt(position: Vector2d): Animal?
val getElements: ArrayList<Animal>
val id: Int
}
26 changes: 26 additions & 0 deletions basic_kotlin_project/src/main/kotlin/model/MapDirection.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package model;

enum class MapDirection {
NORTH,
SOUTH,
WEST,
EAST;
override fun toString() = when(this) {
NORTH -> ("N")
SOUTH -> ("S")
WEST -> ("W")
EAST -> ("E")
}
fun next() = when(this) {
NORTH -> EAST
SOUTH -> WEST
WEST -> NORTH
EAST -> SOUTH
}
fun previous() = when(this) {
NORTH -> WEST
WEST -> SOUTH
SOUTH -> EAST
EAST -> NORTH
}
}
8 changes: 8 additions & 0 deletions basic_kotlin_project/src/main/kotlin/model/MoveDirection.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package model;

enum class MoveDirection {
FORWARD,
BACKWARD,
RIGHT,
LEFT;
}
18 changes: 18 additions & 0 deletions basic_kotlin_project/src/main/kotlin/model/RandomExtensions.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package model

fun <Object> Map<Vector2d, Object>.randomPosition(): Vector2d? {
return keys.toList().randomOrNull()
}
fun <Object> Map<Vector2d, Object>.randomFreePosition(mapSize: Vector2d): Vector2d? {
val x = mapSize.x
val y = mapSize.y
val allPositions = mutableSetOf<Vector2d>()
for(i in 0..x) {
for(j in 0..y) {
allPositions.add(Vector2d(i,j))
}
}
val occupiedPositions = keys.toList().toSet()
if(allPositions == occupiedPositions) return null
return (allPositions - occupiedPositions).random()
}
23 changes: 23 additions & 0 deletions basic_kotlin_project/src/main/kotlin/model/Vector2d.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package model;

data class Vector2d(val x: Int, val y: Int) {
infix fun follows(other: Vector2d) = (x >= other.x && y >= other.y)
infix fun precedes(other: Vector2d) = (x <= other.x && y <= other.y)

fun upperRight(other: Vector2d) = (Vector2d(maxOf(x, other.x), maxOf(y, other.y)))
fun lowerLeft(other: Vector2d) = (Vector2d(minOf(x, other.x), minOf(y, other.y)))

operator fun plus(other: Vector2d) = (Vector2d(x + other.x, y + other.y))
operator fun minus(other: Vector2d) = (Vector2d(x - other.x, y - other.y))
fun opposite() = (Vector2d(y, x))

override fun toString() = ("($x,$y)")
}
fun MapDirection.toUnitVector() : Vector2d {
return when (this) {
MapDirection.NORTH -> Vector2d(0, 1)
MapDirection.WEST -> Vector2d(-1, 0)
MapDirection.SOUTH -> Vector2d(0, -1)
MapDirection.EAST -> Vector2d(1, 0)
}
}
84 changes: 84 additions & 0 deletions basic_kotlin_project/src/test/kotlin/model/AnimalTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package model

import org.junit.jupiter.api.Assertions.assertEquals
import kotlin.test.Test

class AnimalTest {
@Test
fun `toString should return the string representation of the animal's orientation`() {
val animal1 = Animal()
val animal2 = Animal(MapDirection.EAST)
val animal3 = Animal(position = Vector2d(2, 2))

assertEquals("N", animal1.toString())
assertEquals("E", animal2.toString())
assertEquals("N", animal3.toString())
}

@Test
fun `isAt should return true when the animal is at the specified position and false when not`() {
val animal1 = Animal(position = Vector2d(4, 4))
val animal2 = Animal()

assertEquals(true, animal1.isAt(Vector2d(4, 4)))
assertEquals(true, animal2.isAt(Vector2d(2, 2)))
assertEquals(false, animal1.isAt(Vector2d(3, 3)))
assertEquals(false, animal2.isAt(Vector2d(3, 3)))
}

@Test
fun `getPosition should return the current position of the animal`() {
val animal1 = Animal(position = Vector2d(7, 7))
val animal2 = Animal()

assertEquals(Vector2d(7, 7), animal1.getPosition())
assertEquals(Vector2d(2, 2), animal2.getPosition())
}

@Test
fun `move should update the animal's orientation and position based on the given direction`() {
val animal1 = Animal()
val animal2 = Animal(MapDirection.SOUTH, Vector2d(4, 4))

animal1.move(MoveDirection.RIGHT)
assertEquals(MapDirection.EAST.toString(), animal1.toString())

animal1.move(MoveDirection.LEFT)
assertEquals(MapDirection.NORTH.toString(), animal1.toString())

animal1.move(MoveDirection.FORWARD)
assertEquals(Vector2d(2, 3), animal1.getPosition())

animal1.move(MoveDirection.BACKWARD)
assertEquals(Vector2d(2, 2), animal1.getPosition())

animal1.move(MoveDirection.LEFT)
assertEquals(MapDirection.WEST.toString(), animal1.toString())

animal1.move(MoveDirection.FORWARD)
assertEquals(Vector2d(1, 2), animal1.getPosition())

animal1.move(MoveDirection.BACKWARD)
assertEquals(Vector2d(2, 2), animal1.getPosition())



animal2.move(MoveDirection.RIGHT)
assertEquals(MapDirection.WEST.toString(), animal2.toString())

animal2.move(MoveDirection.FORWARD)
assertEquals(Vector2d(3, 4), animal2.getPosition())

animal2.move(MoveDirection.BACKWARD)
assertEquals(Vector2d(4, 4), animal2.getPosition())

animal2.move(MoveDirection.LEFT)
assertEquals(MapDirection.SOUTH.toString(), animal2.toString())

animal2.move(MoveDirection.FORWARD)
assertEquals(Vector2d(4, 3), animal2.getPosition())

animal2.move(MoveDirection.BACKWARD)
assertEquals(Vector2d(4, 4), animal2.getPosition())
}
}
Loading