/* * File: SelfAvoidingRandomWalks.kt * Compilation: processing-ktc SelfAvoidingRandomWalks.kt * Execution: processing-kt SelfAvoidingRandomWalks n t * where n and t are positive integers * Purpose: Simulates t trials of taking a self-avoiding random walk * starting from the center of an n x n lattice until it escapes from * the lattice or gets stuck in a dead end. It then prints out the * percentage of trials that result in a dead end. * Date: 2019-09-27 * @author: San Skulrattanakulchai */ import processing.core.PApplet import kotlin.system.exitProcess import kotlin.random.* const val SHORT_PAUSE = 1000L var n = 0 // lattice size is n x n var trials = 0 // number of desired trials var numDeadEnds = 0 // number of trials that end in a dead end fun main(args: Array) { if (args.size != 2) { println(""" | |Usage: processing-kt SelfAvoidingRandomWalks n t | where n and t are positive integers | | This program simulates t trials of taking a self-avoiding random | walk starting from the center of an n x n lattice until it escapes | from the lattice or gets stuck in a dead end. It then prints out | the percentage of trials that result in a dead end. | """.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) } try { trials = args[1].toInt() // number of desired trials } catch (e: NumberFormatException) { println("'${args[1]}' is not an integer.") exitProcess(2) } if (n <= 0 || trials <= 0) { println("both '${args[0]}' and '${args[1]}' must be positive integers.") exitProcess(2) } PApplet.main("SelfAvoidingRandomWalksSketch") } class SelfAvoidingRandomWalksSketch : PApplet() { val MIN_COORD = 0 val MAX_COORD = n - 1 val range = MIN_COORD..MAX_COORD val MIN_COORD_F = 0F // MIN_COORD as a float val MAX_COORD_F = n - 1F // MAX_COORD as a float // this keeps track of visited intersections val visited = Array(n) { BooleanArray(n) } // (x, y) will be set to the center point of the lattice in setup() var x = 0 var y = 0 var completed = 0 // number of completed trials override fun settings() { size(680, 680) } override fun setup() { // initialize all lattice intersections to unvisited for (i in 0..MAX_COORD) for (j in 0..MAX_COORD) visited[i][j] = false // first clear the window screen background(250) // then draw the lattice in gray stroke(125) strokeWeight(1F) 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(20F) } 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) } private fun finishOneTrial() { if (++completed == trials) { println("%.2f%% dead ends.".format(100.0 * numDeadEnds / trials)) stop() } else { Thread.sleep(SHORT_PAUSE) setup() } } 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 numDeadEnds++ finishOneTrial() return } // 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) { finishOneTrial() 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) { finishOneTrial() 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) { finishOneTrial() 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) { finishOneTrial() return } else if (!visited[x][y-1]) { line(transformX(xf), transformY(yf), transformX(xf), transformY(yf-1F)) y-- return } } } while (true) } } }