HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Dungeon generation in Scala

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
scalagenerationdungeon

Problem

I've recently begun learning Scala, and while I've run into its concepts before (immutability, tuples, first-class functions) I'm not sure whether I'm using the language how it's supposed to be used. My learning project is going to be a simple little dungeon crawler, and while you can see the code on GitHub, I'm copying a simplified version here below.

Example output looks like this, where ?s are potential rooms and #s are actual rooms:

?     
 ??  #  ?  
?## ### #  
  ### ####?
  ?? ## #  
      ##   
      #    
    ?##?   
     ??


And here's how it's implemented:

```
class LevelBuilder(random: Random) {

/** Mapping of positions to the rooms at them. */
var rooms: mutable.Map[(Int, Int), Room] = mutable.Map()

/** List of areas where rooms could be generated. */
var possibilities: Seq[(Int, Int)] = List()

/**
* Adds a new room to the map at the given position, and adds the available
* positions around it to the possibilities list.
*/
def addRoom(position: (Int, Int), room: Room): LevelBuilder = {
rooms += position -> room

// First, remove the room from the list of possibilities.
possibilities = possibilities.filter(pos => pos != position).++(around(position))

// Next, add the positions around that one to the list...
possibilities = possibilities ++ around(position)

// ...but then remove all of the ones around those, so no two paths will ever link up.
possibilities = possibilities.filter(pos => roomPossibility(around(pos).count(p => rooms.contains(p))) && !rooms.contains(pos))

this
}

/**
* Determine whether a room should be build based on its number of neighbours.
*/
private def roomPossibility(neighboursCount: Int): Boolean = {
neighboursCount <= 1
}

/** Adds the given number of rooms to the level. */
def addRooms(number: Int): LevelBuilder = {
for (i <- 0 to number) {
var index = random.nextInt(possibilities.length) // Pick a random room from the

Solution

Looking quickly over your code here are a couple of the changes I would make:

First, I would add a class to your application (probably within the levelbuilder.scala file) that encapsulates the (Int, Int) tuple you are using throughout this code snippet. E.g.

case class Pos(x: Int, y: Int) {
      def neighborhood: List[Pos] = {
        Pos(x + 1, y) :: Pos(x - 1, y) :: Pos(x, y - 1) :: Pos(x, y + 1) :: Nil
      }
    }


When building this class I also took the chance to rewrite the method called around(pos: (Int, Int)) from the LevelBuilder class to a more concise method which I've called neighborhood just to differentiate it.

Also, if you haven't had a chance to read-up on case classes in Scala, now would be a good time to do so :) They provide a lot of functionality with just a few lines of code.

Now you may declare the LevelBuilder fields rooms and possibilities like so:

val rooms = scala.collection.mutable.HashMap[Pos, Room]()
    var possibilities = List[Pos]()


One place that these changes drastically add to readability in your code is in the debug() method:

def debug(): Unit = {
      val possibilePos = rooms.keys ++ possibilities

      val minY = possiblePos.minBy(_.y).y
      val maxY = possiblePos.maxBy(_.y).y
      val minX = possiblePos.maxBy(_.x).x
      val maxX = possiblePos.maxBy(_.x).x

      // print map 
    }


Notice also that I changed miny to minY etc.

Code Snippets

case class Pos(x: Int, y: Int) {
      def neighborhood: List[Pos] = {
        Pos(x + 1, y) :: Pos(x - 1, y) :: Pos(x, y - 1) :: Pos(x, y + 1) :: Nil
      }
    }
val rooms = scala.collection.mutable.HashMap[Pos, Room]()
    var possibilities = List[Pos]()
def debug(): Unit = {
      val possibilePos = rooms.keys ++ possibilities

      val minY = possiblePos.minBy(_.y).y
      val maxY = possiblePos.maxBy(_.y).y
      val minX = possiblePos.maxBy(_.x).x
      val maxX = possiblePos.maxBy(_.x).x

      // print map 
    }

Context

StackExchange Code Review Q#59684, answer score: 4

Revisions (0)

No revisions yet.