After more than a little hassle, my colleagues and I have a Kotlin shell that we can pass around. Today, for fun, I’m going to try to write some tests and code.

The Friday Night Zoom Ensemble, set to meet tonight, Tuesday, are going to try working together on something. We’ve chosen the Kotlin language, and we think we’ll write a game, at least to start. The purpose? Fun, mostly, and to give us things to experience, think about, write about, make videos about. Fun, mostly.

With great effort, Hill has managed to produce a single IntelliJ IDEA project that we can all clone and, with just a few simple steps, run on all our machines. Well, three of our machines. Nt everyone has tried yet. But we have a PC, a Mac, and an M1 Mac, so it’s a good start. I may write about this but the simple fact is that with all the best of will, it seems to be a black art to create a repo that can be cloned and just brought up in IDEA with no need to probe around changing this and that.

I am new to IDEA and to Kotlin. I have some Java and C# in my past, but have been away from those worlds for a long time. So, in what follows, you can watch the fumblings of someone with a lot of experience, only some of which applies. You might wish to follow along for amusement, though I hope you’ll see some useful things about how at least one old duffer learns in a new space.

Worst case, you can find things not to do.

Short Term Plan

I decided this morning that what I’ll do is to implement a robot-world thing, not because that’s the game, but because I have just been working on Robot World, so the problem and Lua solution are as fresh in my mind as anything is.

I don’t plan to keep what I do … nor do I plan to throw it away. I’ll commit the work locally. I will probably not push it to our official repo. We’ll see.

I have never written a line of Kotlin code, to the best of my knowledge, and it has been years since I’ve written any Java. I’ve been reviewing a bit of Kotlin as we watch some of the things Hill is working on. and I’ve just barely started reading the various documents and tutorials. I suspect that I can’t write a valid line, but I might be able to get close.

And I’m expecting that IDEA is going to help me. It certainly seems to want to, even when I don’t really want any help.

Let’s get started. This will be messy.

Where Am I?

I’m faced with this huge hierarchy of stuff in this “starter” appl:

starter

And that’s just the source part.

I would like to start with the Knowledge class, a class whose instances hold the robot’s knowledge of the space around it. Knowledge is supposed to be a sparse dictionary kind of thing, indexed by X and Y coordinates, containing something. The Knowledge doesn’t care what it contains.

I want to start with a test that will drive out the very few methods of this class.

And I don’t even know how to create a new test. I know where I want to put it though: I want it to go in here:

test location

After deciding that I need to create a file, I realize that I want to create a class. Remove the file. Create the class. Copy ideas from existing test. Get this:

package org.geepawhill.starter

import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test

class KnowledgeTest {
    @Test
    fun hookup() {
        assertThat("ron").isEqualTo("jeffries")
    }
}

Test fails! Woot!

org.opentest4j.AssertionFailedError: 
expected: "jeffries"
 but was: "ron"
Expected :"jeffries"
Actual   :"ron"

An Hour of Fumbling Later

Well, I have a test file with some tests that run. It looks like this:

package org.geepawhill.starter

import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import java.awt.Point

class KnowledgeTest {
    @Test
    fun hookup() {
        assertThat("ron").isEqualTo("ron")
    }

    @Test
    fun createOne() {
        val knowledge = Knowledge()
        assertThat(knowledge.contents).isEqualTo("contents")
        assertThat(knowledge.doSomething()).isEqualTo("did it")
        // assertThat(knowledge).isInstanceOf(Knowledge)
    }

    fun addAndRetrieveItem() {
        val k = Knowledge()
        k.addItemAt(item="Hello", at=Point(10,10))
        assertThat(k.getItem(Point(10,10))).isEqualTo("Hello")
    }
}

class Knowledge {
    val map = mutableMapOf<Any,Any>()
    val contents = "contents"

    fun doSomething(): String {
        return "did it"
    }

    fun addItemAt(item: String, at: Point) {
        map.put(at,item)
    }

    fun getItem(at: Point) {
        map.get(at)
    }
}

class Fact(thing: Any) {
    val thing = thing
}

I’m not using the Fact yet. I couldn’t figure out how to check the Knowledge instance for being an instance of Knowledge. The commented line doesn’t work. I’ll find out later, I don’t really doubt that the thing coming back is a Knowledge.

Getting that to work took an hour, mostly spent searching the internet and reading examples. I had to discover things like mutableMap when what I had in mind was “dictionary”.

I’ve left the classes in the test. I think that IDEA would like to help me move them into separate files and at some future time I will do that. For now, I’ll keep them with the corresponding tests.

You’ll notice that I’m using Point class, a Java thing. I’m supposing that will be OK, at least for now.

World

I think I’m ready to start creating a World class that uses a Knowledge to store information about what’s in the world. My tentative design is to have an “enum” representing the various things that the map can contain. In my Lua program I used strings.

My World will have a few methods. I’ll start with addObstacle and lookAt, which will each refer to a single Point. At least that’s my plan here at the beginning.

class WorldTest {
    @Test
    fun hookup() {
        assertThat(1).isEqualTo(2)
    }
}

Test fails as intended. Hooked up. Yay

A few (well probably 20) minutes later, counting a break, I have this:

package org.geepawhill.starter

import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import java.awt.Point

enum class WorldThing { OBSTACLE, PIT, SEEN }

class World {
    fun addObstacle(i: Int, i1: Int) {

    }

    fun lookAt(i: Int, i1: Int): Any {
        return WorldThing.OBSTACLE
    }
}

class WorldTest {
    @Test
    fun hookup() {
        assertThat(2).isEqualTo(2)
    }

    @Test
    fun hasAnObstacle() {
        val world = World()
        world.addObstacle(12,12)
        val o = world.lookAt(12,12)
        assertThat(o).isEqualTo(WorldThing.OBSTACLE)
    }
}

Green so far. Of course the add and look are stubbed. We should be able to fix that easily.

Well, For Small Values of Easily

I found that there must be inadequate testing on Knowledge. I’ve wound up with what follows: notice that a bunch of types have been set to Any? which is Kotlin for “could be anything including null”. As a duck type no static type checking kind of guy, those are my favorite kinds of things. I’ll have to learn to work differently in Kotlin, I suppose.

Anyway now I have this:

package org.geepawhill.starter

import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import java.awt.Point

enum class WorldThing { OBSTACLE, PIT, SEEN }

class World {
    val knowledge:Knowledge = Knowledge()
    fun addObstacle(x: Int, y: Int) {
        knowledge.addItemAt(WorldThing.OBSTACLE, Point(x,y))
    }

    fun lookAt(x: Int, y: Int): Any? {
        return knowledge.getItem(Point(x,y))
    }
}

class WorldTest {
    @Test
    fun hookup() {
        assertThat(2).isEqualTo(2)
    }

    @Test
    fun hasAnObstacle() {
        val world = World()
        world.addObstacle(12,12)
        val o = world.lookAt(12,12)
        assertThat(o).isEqualTo(WorldThing.OBSTACLE)
    }
}

And some changes to the Knowledge as well:

package org.geepawhill.starter

import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test
import java.awt.Point

class KnowledgeTest {
    @Test
    fun hookup() {
        assertThat("ron").isEqualTo("ron")
    }

    @Test
    fun createOne() {
        val knowledge = Knowledge()
        assertThat(knowledge.contents).isEqualTo("contents")
        assertThat(knowledge.doSomething()).isEqualTo("did it")
        // assertThat(knowledge).isInstanceOf(Knowledge)
    }

    fun addAndRetrieveItem() {
        val k = Knowledge()
        k.addItemAt(item="Hello", at=Point(10,10))
        assertThat(k.getItem(Point(10,10))).isEqualTo("Hello")
    }
}

class Knowledge {
    val map = mutableMapOf<Any,Any>()
    val contents = "contents"

    fun doSomething(): String {
        return "did it"
    }

    fun addItemAt(item: Any, at: Point) {
        map.put(at,item)
    }

    fun getItem(at: Point): Any? {
        return map.get(at)
    }
}

class Fact(thing: Any) {
    val thing = thing
}

I think there are some methods in maps and elsewhere that will help me get rid of the Any stuff, when I want to.

The tests are green. I’m going to commit this stuff and sum up.

Summary

Well, that was very sloppy, as one would expect for someone with essentially zero experience with IDEA or Kotlin. I made some use of IDEA’s helpers: it’s happy to build a class shell or method shell for you, and it has a good chance of getting the basic syntax right.

Sloppy, yes, but the end result is two new test classes, with embedded classes Robot and Knowledge in the same files for now, covering the rudimentary behavior or saving an obstacle in the world and getting it back.

For first time out on my own, I’ll take it.

I look forward to your comments, critiques, questions, laughter, whatever.