Anonymous avatar Anonymous committed 36acda4

more gardening of LevelGen

Comments (0)

Files changed (1)

src/main/scala/com/mojang/ld22/level/levelgen/LevelGen.scala

 
 import java.awt.Image
 import java.awt.image.BufferedImage
-import java.util.Random
 import javax.swing.ImageIcon
 import javax.swing.JOptionPane
 import com.mojang.ld22.level.tile.Tile
+import annotation.tailrec
+import scala.util.Random
 
 object LevelGen {
 
   import util.Implicits._
 
+  private implicit val random = new Random // levelgen uses its own RNG instance to support explicit seeds later
+
+  @tailrec
   def createAndValidateTopMap(w: Int, h: Int): Array[Array[Byte]] = {
     val result = createTopMap(w, h)
     val count = new Array[Int](256)
       result
   }
 
+  @tailrec
   def createAndValidateUndergroundMap(w: Int, h: Int, depth: Int): Array[Array[Byte]] = {
     val result = createUndergroundMap(w, h, depth)
     val count = new Array[Int](256)
       result
   }
 
+  @tailrec
   def createAndValidateSkyMap(w: Int, h: Int): Array[Array[Byte]] = {
-    val result: Array[Array[Byte]] = createSkyMap(w, h)
-    val count: Array[Int] = new Array[Int](256)
+    val result = createSkyMap(w, h)
+    val count = new Array[Int](256)
 
     for (i <- 0 until w * h)
       count(result(0)(i) & 0xff) += 1
   }
 
   private def createTopMap(w: Int, h: Int): Array[Array[Byte]] = {
-    val mnoise1: LevelGen = new LevelGen(w, h, 16)
-    val mnoise2: LevelGen = new LevelGen(w, h, 16)
-    val mnoise3: LevelGen = new LevelGen(w, h, 16)
-    val noise1: LevelGen = new LevelGen(w, h, 32)
-    val noise2: LevelGen = new LevelGen(w, h, 32)
-    val map: Array[Byte] = new Array[Byte](w * h)
-    val data: Array[Byte] = new Array[Byte](w * h)
+    val mnoise1 = new LevelGen(w, h, 16)
+    val mnoise2 = new LevelGen(w, h, 16)
+    val mnoise3 = new LevelGen(w, h, 16)
+    val noise1 = new LevelGen(w, h, 32)
+    val noise2 = new LevelGen(w, h, 32)
+    val map = new Array[Byte](w * h)
+    val data = new Array[Byte](w * h)
 
     for (y <- 0 until h; x <- 0 until w) {
       var value = math.abs(noise1(x, y) - noise2(x, y)) * 3 - 2
       dist = dist * dist * dist * dist
       value = value + 1 - dist * 20
 
-      val i: Int = x + y * w
+      val i = x + y * w
       if (value < -0.5)
         map(i) = Tile.water.id
       else if (value > 0.5 && mval < -1.5)
     }
 
     (w * h / 2800) times {
-      val xs: Int = random.nextInt(w)
-      val ys: Int = random.nextInt(h)
+      val xs = random.nextInt(w)
+      val ys = random.nextInt(h)
 
       10 times {
-        val x: Int = xs + random.nextInt(21) - 10
-        val y: Int = ys + random.nextInt(21) - 10
+        val x = xs + random.nextInt(21) - 10
+        val y = ys + random.nextInt(21) - 10
 
 
         100 times {
-          val xo: Int = x + random.nextInt(5) - random.nextInt(5)
-          val yo: Int = y + random.nextInt(5) - random.nextInt(5)
+          val xo = x + random.nextInt(5) - random.nextInt(5)
+          val yo = y + random.nextInt(5) - random.nextInt(5)
 
           for (yy <- yo - 1 to yo + 1; xx <- xo - 1 to xo + 1) {
             if (xx >= 0 && yy >= 0 && xx < w && yy < h) {
     }
 
     (w * h / 400) times {
-      val x: Int = random.nextInt(w)
-      val y: Int = random.nextInt(h)
+      val x = random.nextInt(w)
+      val y = random.nextInt(h)
 
       200 times {
-        val xx: Int = x + random.nextInt(15) - random.nextInt(15)
-        val yy: Int = y + random.nextInt(15) - random.nextInt(15)
+        val xx = x + random.nextInt(15) - random.nextInt(15)
+        val yy = y + random.nextInt(15) - random.nextInt(15)
         if (xx >= 0 && yy >= 0 && xx < w && yy < h) {
           if (map(xx + yy * w) == Tile.grass.id) {
             map(xx + yy * w) = Tile.tree.id
     }
 
     (w * h / 400) times {
-      val x: Int = random.nextInt(w)
-      val y: Int = random.nextInt(h)
-      val col: Int = random.nextInt(4)
+      val x = random.nextInt(w)
+      val y = random.nextInt(h)
+      val col = random.nextInt(4)
 
       30 times {
-        val xx: Int = x + random.nextInt(5) - random.nextInt(5)
-        val yy: Int = y + random.nextInt(5) - random.nextInt(5)
+        val xx = x + random.nextInt(5) - random.nextInt(5)
+        val yy = y + random.nextInt(5) - random.nextInt(5)
         if (xx >= 0 && yy >= 0 && xx < w && yy < h) {
           if (map(xx + yy * w) == Tile.grass.id) {
             map(xx + yy * w) = Tile.flower.id
     }
 
     (w * h / 100) times {
-      val xx: Int = random.nextInt(w)
-      val yy: Int = random.nextInt(h)
+      val xx = random.nextInt(w)
+      val yy = random.nextInt(h)
       if (xx >= 0 && yy >= 0 && xx < w && yy < h) {
         if (map(xx + yy * w) == Tile.sand.id) {
           map(xx + yy * w) = Tile.cactus.id
       }
     }
 
-    var stairsMade: Int = 0
-    var attempts: Int = 0
+    var stairsMade = 0
+    var attempts = 0
     while ((attempts < w * h / 100) && (stairsMade < 4)) {
       attempts += 1
-      val x: Int = random.nextInt(w - 2) + 1
-      val y: Int = random.nextInt(h - 2) + 1
+      val x = random.nextInt(w - 2) + 1
+      val y = random.nextInt(h - 2) + 1
       var eligible: Boolean = true
 
       for (yy <- y - 1 to y + 1; xx <- x - 1 to x + 1)
   }
 
   private def createUndergroundMap(w: Int, h: Int, depth: Int): Array[Array[Byte]] = {
-    val mnoise1: LevelGen = new LevelGen(w, h, 16)
-    val mnoise2: LevelGen = new LevelGen(w, h, 16)
-    val mnoise3: LevelGen = new LevelGen(w, h, 16)
-    val nnoise1: LevelGen = new LevelGen(w, h, 16)
-    val nnoise2: LevelGen = new LevelGen(w, h, 16)
-    val nnoise3: LevelGen = new LevelGen(w, h, 16)
-    val wnoise1: LevelGen = new LevelGen(w, h, 16)
-    val wnoise2: LevelGen = new LevelGen(w, h, 16)
-    val wnoise3: LevelGen = new LevelGen(w, h, 16)
-    val noise1: LevelGen = new LevelGen(w, h, 32)
-    val noise2: LevelGen = new LevelGen(w, h, 32)
-    val map: Array[Byte] = new Array[Byte](w * h)
-    val data: Array[Byte] = new Array[Byte](w * h)
+    val mnoise1 = new LevelGen(w, h, 16)
+    val mnoise2 = new LevelGen(w, h, 16)
+    val mnoise3 = new LevelGen(w, h, 16)
+    val nnoise1 = new LevelGen(w, h, 16)
+    val nnoise2 = new LevelGen(w, h, 16)
+    val nnoise3 = new LevelGen(w, h, 16)
+    val wnoise1 = new LevelGen(w, h, 16)
+    val wnoise2 = new LevelGen(w, h, 16)
+    val wnoise3 = new LevelGen(w, h, 16)
+    val noise1 = new LevelGen(w, h, 32)
+    val noise2 = new LevelGen(w, h, 32)
+    val map = new Array[Byte](w * h)
+    val data = new Array[Byte](w * h)
 
-    {
-      var y: Int = 0
-      while (y < h) {
-        {
-          {
-            var x: Int = 0
-            while (x < w) {
-              {
-                val i: Int = x + y * w
-                var value: Double = math.abs(noise1(x, y) - noise2(x, y)) * 3 - 2
-                var mval: Double = math.abs(mnoise1(x, y) - mnoise2(x, y))
-                mval = math.abs(mval - mnoise3(x, y)) * 3 - 2
-                var nval: Double = math.abs(nnoise1(x, y) - nnoise2(x, y))
-                nval = math.abs(nval - nnoise3(x, y)) * 3 - 2
-                var wval: Double = math.abs(wnoise1(x, y) - wnoise2(x, y))
-                wval = math.abs(nval - wnoise3(x, y)) * 3 - 2
-                var xd: Double = x / (w - 1.0) * 2 - 1
-                var yd: Double = y / (h - 1.0) * 2 - 1
-                if (xd < 0) xd = -xd
-                if (yd < 0) yd = -yd
-                var dist: Double = if (xd >= yd) xd else yd
-                dist = dist * dist * dist * dist
-                dist = dist * dist * dist * dist
-                value = value + 1 - dist * 20
-                if (value > -2 && wval < -2.0 + (depth) / 2 * 3) {
-                  if (depth > 2) map(i) = Tile.lava.id
-                  else map(i) = Tile.water.id
-                }
-                else if (value > -2 && (mval < -1.7 || nval < -1.4)) {
-                  map(i) = Tile.dirt.id
-                }
-                else {
-                  map(i) = Tile.rock.id
-                }
-              }
-              x += 1
-            }
-          }
-        }
-        y += 1
+    for (y <- 0 until h; x <- 0 until w) {
+      val i = x + y * w
+      var value: Double = math.abs(noise1(x, y) - noise2(x, y)) * 3 - 2
+      var mval: Double = math.abs(mnoise1(x, y) - mnoise2(x, y))
+      mval = math.abs(mval - mnoise3(x, y)) * 3 - 2
+      var nval: Double = math.abs(nnoise1(x, y) - nnoise2(x, y))
+      nval = math.abs(nval - nnoise3(x, y)) * 3 - 2
+      var wval: Double = math.abs(wnoise1(x, y) - wnoise2(x, y))
+      wval = math.abs(nval - wnoise3(x, y)) * 3 - 2
+      var xd: Double = x / (w - 1.0) * 2 - 1
+      var yd: Double = y / (h - 1.0) * 2 - 1
+      if (xd < 0) xd = -xd
+      if (yd < 0) yd = -yd
+      var dist: Double = if (xd >= yd) xd else yd
+      dist = dist * dist * dist * dist
+      dist = dist * dist * dist * dist
+      value = value + 1 - dist * 20
+      if (value > -2 && wval < -2.0 + (depth) / 2 * 3) {
+        if (depth > 2) map(i) = Tile.lava.id
+        else map(i) = Tile.water.id
+      }
+      else if (value > -2 && (mval < -1.7 || nval < -1.4)) {
+        map(i) = Tile.dirt.id
+      }
+      else {
+        map(i) = Tile.rock.id
       }
     }
-    {
-      val r: Int = 2
 
-      {
-        var i: Int = 0
-        while (i < w * h / 400) {
-          {
-            val x: Int = random.nextInt(w)
-            val y: Int = random.nextInt(h)
-
-            {
-              var j: Int = 0
-              while (j < 30) {
-                {
-                  val xx: Int = x + random.nextInt(5) - random.nextInt(5)
-                  val yy: Int = y + random.nextInt(5) - random.nextInt(5)
-                  if (xx >= r && yy >= r && xx < w - r && yy < h - r) {
-                    if (map(xx + yy * w) == Tile.rock.id) {
-                      map(xx + yy * w) = ((Tile.ironOre.id & 0xff) + depth - 1).asInstanceOf[Byte]
-                    }
-                  }
-                }
-                j += 1
-              }
-            }
+    val r = 2
+    (w * h / 400) times {
+      val x = random.nextInt(w)
+      val y = random.nextInt(h)
+      30 times {
+        val xx = x + random.nextInt(5) - random.nextInt(5)
+        val yy = y + random.nextInt(5) - random.nextInt(5)
+        if (xx >= r && yy >= r && xx < w - r && yy < h - r) {
+          if (map(xx + yy * w) == Tile.rock.id) {
+            map(xx + yy * w) = ((Tile.ironOre.id & 0xff) + depth - 1).asInstanceOf[Byte]
           }
-          i += 1
         }
       }
     }
+
     if (depth < 3) {
-      var stairsMade: Int = 0
-      var attempts: Int = 0
+      var stairsMade = 0
+      var attempts = 0
       while ((attempts < w * h / 100) && (stairsMade < 4)) {
         attempts += 1
-        val x: Int = random.nextInt(w - 20) + 10
-        val y: Int = random.nextInt(h - 20) + 10
-        var eligible: Boolean = true
+        val x = random.nextInt(w - 20) + 10
+        val y = random.nextInt(h - 20) + 10
+        var eligible = true
 
         for (yy <- y - 1 to y + 1; xx <- x - 1 to x + 1)
           if (map(xx + yy * w) != Tile.rock.id) eligible = false
   }
 
   private def createSkyMap(w: Int, h: Int): Array[Array[Byte]] = {
-    val noise1: LevelGen = new LevelGen(w, h, 8)
-    val noise2: LevelGen = new LevelGen(w, h, 8)
-    val map: Array[Byte] = new Array[Byte](w * h)
-    val data: Array[Byte] = new Array[Byte](w * h)
+    val noise1 = new LevelGen(w, h, 8)
+    val noise2 = new LevelGen(w, h, 8)
+    val map = new Array[Byte](w * h)
+    val data = new Array[Byte](w * h)
 
     for (y <- 0 until h; x <- 0 until w) {
-      val i: Int = x + y * w
+      val i = x + y * w
       var value: Double = math.abs(noise1(x, y) - noise2(x, y)) * 3 - 2
       var xd: Double = x / (w - 1.0) * 2 - 1
       var yd: Double = y / (h - 1.0) * 2 - 1
     }
 
 
-    var attempts: Int = 0
+    var attempts = 0
     while ((attempts < w * h / 50)) {
       attempts += 1
-      val x: Int = random.nextInt(w - 2) + 1
-      val y: Int = random.nextInt(h - 2) + 1
+      val x = random.nextInt(w - 2) + 1
+      val y = random.nextInt(h - 2) + 1
       var eligible: Boolean = true
 
       for (yy <- y - 1 to y + 1; xx <- x - 1 to x + 1)
         map(x + y * w) = Tile.cloudCactus.id
       }
     }
-    var stairsMade: Int = 0
+
+    var stairsMade = 0
     attempts = 0
     while ((attempts < w * h) && (stairsMade < 2)) {
       attempts += 1
-      val x: Int = random.nextInt(w - 2) + 1
-      val y: Int = random.nextInt(h - 2) + 1
-      var eligible: Boolean = true
+      val x = random.nextInt(w - 2) + 1
+      val y = random.nextInt(h - 2) + 1
+      var eligible = true
 
       for (yy <- y - 1 to y + 1; xx <- x - 1 to x + 1)
         if (map(xx + yy * w) != Tile.cloud.id) eligible = false
     Array[Array[Byte]](map, data)
   }
 
+  @tailrec
   def main(args: Array[String]) {
-    //    var d= 0
-    while (true) {
-      val w: Int = 128
-      val h: Int = 128
+    var d = 0
+    val w = 128
+    val h = 128
 
-      val map: Array[Byte] = LevelGen.createAndValidateTopMap(w, h)(0)
-      //      val map: Array[Byte] = LevelGen.createAndValidateSkyMap(w, h)(0)
-      //      val map: Array[Byte] = LevelGen.createAndValidateUndergroundMap(w, h, (( {d += 1; d} ) % 3) + 1)(0)
-      val img: BufferedImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB)
-      val pixels: Array[Int] = new Array[Int](w * h)
+    val map: Array[Byte] = LevelGen.createAndValidateTopMap(w, h)(0)
+    // val map: Array[Byte] = LevelGen.createAndValidateSkyMap(w, h)(0)
+    // val map: Array[Byte] = LevelGen.createAndValidateUndergroundMap(w, h, (({ d += 1; d }) % 3) + 1)(0)
 
-      for (y <- 0 until h; x <- 0 until w) {
-        val i: Int = x + y * w
-        pixels(i) = 0x000000
-        if (map(i) == Tile.water.id) pixels(i) = 0x000080
-        if (map(i) == Tile.grass.id) pixels(i) = 0x208020
-        if (map(i) == Tile.rock.id) pixels(i) = 0xa0a0a0
-        if (map(i) == Tile.dirt.id) pixels(i) = 0x604040
-        if (map(i) == Tile.sand.id) pixels(i) = 0xa0a040
-        if (map(i) == Tile.tree.id) pixels(i) = 0x003000
-        if (map(i) == Tile.lava.id) pixels(i) = 0xff2020
-        if (map(i) == Tile.cloud.id) pixels(i) = 0xa0a0a0
-        if (map(i) == Tile.stairsDown.id) pixels(i) = 0xffffff
-        if (map(i) == Tile.stairsUp.id) pixels(i) = 0xffffff
-        if (map(i) == Tile.cloudCactus.id) pixels(i) = 0xff00ff
-        if (map(i) == Tile.flower.id) pixels(i) = 0xaaaa66
-        if (map(i) == Tile.cactus.id) pixels(i) = 0x66cc66
-      }
-      img.setRGB(0, 0, w, h, pixels, 0, w)
-      val ico: ImageIcon = new ImageIcon(img.getScaledInstance(w * 4, h * 4, Image.SCALE_AREA_AVERAGING))
-      val v: Int = JOptionPane.showConfirmDialog(null, Array[AnyRef](ico, "Another Map?"), "Map Generator", JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE, null)
-      if (v != JOptionPane.YES_OPTION) System.exit(0)
-    }
+    import Tile._
+    val colors = Map[Byte, Int](
+      water.id -> 0x000080,
+      grass.id -> 0x208020,
+      rock.id -> 0xa0a0a0,
+      dirt.id -> 0x604040,
+      sand.id -> 0xa0a040,
+      tree.id -> 0x003000,
+      lava.id -> 0xff2020,
+      cloud.id -> 0xa0a0a0,
+      stairsDown.id -> 0xffffff,
+      stairsUp.id -> 0xffffff,
+      cloudCactus.id -> 0xff00ff,
+      flower.id -> 0xaaaa66,
+      cactus.id -> 0x66cc66
+    ).withDefaultValue(0x00000000)
+
+    val pixels: Array[Int] = map.map(colors(_))
+    val img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB)
+    img.setRGB(0, 0, w, h, pixels, 0, w)
+    val ico = new ImageIcon(img.getScaledInstance(w * 4, h * 4, Image.SCALE_AREA_AVERAGING))
+    val result = JOptionPane.showConfirmDialog(
+      null, Array[AnyRef](ico, "Another Map?"), "Map Generator",
+      JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE, null
+    )
+    if (result != JOptionPane.YES_OPTION)
+      System.exit(0)
+    else
+      main(args)
   }
-
-  private final val random: Random = new Random
 }
 
 
-class LevelGen(val w: Int, val h: Int, featureSize: Int) {
-
-  import com.mojang.ld22.random
-
-  //  unnecessary imports above to make IDEA happy, whee
+class LevelGen(val w: Int, val h: Int, featureSize: Int)(implicit random: Random) {
 
   // These checks aren't in Notch's code, but I thought I'd make them explicit,
   // especially since we end up in infinite loops if they aren't powers of 2.
   require((w & (w - 1)) == 0, "Level width must be a power of 2 (w == %s)".format(w))
   require((h & (h - 1)) == 0, "Level height must be a power of 2 (h == %s)".format(h))
 
-  var values: Array[Double] = new Array[Double](w * h)
+  var values = new Array[Double](w * h)
 
   populate()
 
   def populate() {
-    // XXX HMM both x and y axis use w, which looks like a bug.
+    // XXX both x and y axis use w, which looks like a bug, but apparently isn't
     for (y <- 0 until w by featureSize; x <- 0 until w by featureSize)
       setSample(x, y, random.nextFloat * 2 - 1)
 
     var scale: Double = 1.0 / w
     var scaleMod: Double = 1
     do {
-      val halfStep: Int = stepSize / 2
+      val halfStep = stepSize / 2
 
       for (y <- 0 until w by stepSize; x <- 0 until w by stepSize) {
         val a = sample(x, y)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.