/* * File: SelfAvoidingRandomWalk.kt * Compilation: processing-ktc SelfAvoidingRandomWalk.kt * Execution: processing-kt SelfAvoidingRandomWalk n * where n is a positive integer * Purpose: Simulate a self-avoiding random walk from the center of * an n x n lattice until it escapes or gets stuck in a dead end. * Date: 2019-09-27 * @author: San Skulrattanakulchai */ import processing.core.PApplet import kotlin.system.exitProcess import kotlin.random.* var n = 0 // lattice size is n x n fun main(args: Array) { if (args.size != 1) { println(""" | |Usage: processing-kt SelfAvoidingRandomWalk n | where n is a positive integer | """.trimMargin()) kotlin.system.exitProcess(1) } try { n = args[0].toInt() // lattice size is n x n } catch (e: NumberFormatException) { println("'${args[0]}' is not an integer.") exitProcess(2) } if (n <= 0) { println("'${args[0]}' must be a a positive integer") exitProcess(2) } PApplet.main("SelfAvoidingRandomWalkSketch") } class SelfAvoidingRandomWalkSketch : PApplet() { val MIN_COORD = 0 val MAX_COORD = n - 1 val MIN_COORD_F = 0F // MIN_COORD as a float val MAX_COORD_F = n - 1F // MAX_COORD as a float val range = MIN_COORD..MAX_COORD // (x, y) will be initialized to the center point of the lattice in setup() var x = 0 var y = 0 // this keeps track of visited intersections val visited = Array(n) { BooleanArray(n) } override fun settings() { size(680, 680) } override fun setup() { // draw the lattice in gray stroke(125) var d = MIN_COORD_F while (d <= MAX_COORD_F) { line(transformX(d), transformY(MIN_COORD_F), transformX(d), transformY(MAX_COORD_F)) line(transformX(MIN_COORD_F), transformY(d), transformX(MAX_COORD_F), transformY(d)) d += 1F } // set the starting point (x, y) to the center point of the lattice x = (MAX_COORD + 1) / 2 /* x-coord of current position */ y = x /* y-coord of current position */ // get ready to draw the random walks stroke(0F, 0F, 255F) // blue strokeWeight(2F) frameRate(10F) // just one trial, so can afford to run slowly } private fun transformX(x: Float): Float { // set a reasonable x-scale, given the lattice size and window size return map(x, MIN_COORD_F, MAX_COORD_F, 0F, width-1F) } private fun transformY(y: Float): Float { // set a reasonable y-scale, given the lattice size and window size return map(y, MIN_COORD_F, MAX_COORD_F, height-1F, 0F) } override fun draw() { // take a random step if (x in range && y in range) { if (x-1 in range && visited[x-1][y] && x+1 in range && visited[x+1][y] && y-1 in range && visited[x][y-1] && y+1 in range && visited[x][y+1]) { // at dead end println("It walks into a dead end.") stop() } // mark (x, y) as visited visited[x][y] = true val xf = x + 0F // x as a Float val yf = y + 0F // y as a Float // take a random step to an unvisited neighbor do { val r = Random.nextInt(4) when (r) { 0 -> if (x+1 !in range) { println("It escapes.") stop() return } else if (!visited[x+1][y]) { line(transformX(xf), transformY(yf), transformX(xf+1F), transformY(yf)) x++ return } 1 -> if (x-1 !in range) { println("It escapes.") stop() return } else if (!visited[x-1][y]) { line(transformX(xf), transformY(yf), transformX(xf-1F), transformY(yf)) x-- return } 2 -> if (y+1 !in range) { println("It escapes.") stop() return } else if (!visited[x][y+1]) { line(transformX(xf), transformY(yf), transformX(xf), transformY(yf+1F)) y++ return } 3 -> if (y-1 !in range) { println("It escapes.") stop() return } else if (!visited[x][y-1]) { line(transformX(xf), transformY(yf), transformX(xf), transformY(yf-1F)) y-- return } } } while (true) } } }