So I am very, very new to scala. I am implementing Conways's Game of Life with a GUI. I can not figure out how to get my panel to update when the 2D array changes. Could someone please point me in the right direction? My entire code follows:
import swing._
import java.awt.{Color, Graphics2D, Dimension}
// initialize variables
// infinite plane variable
var infCurrent = scala.collection.mutable.ListBuffer[List[Int]]()
var infNext = scala.collection.mutable.ListBuffer[List[Int]]()
var infNext1 = scala.collection.mutable.ListBuffer[List[Int]]()
var infLifeTester = scala.collection.mutable.MutableList[List[Int]]()
// general variables
var lifeCount = 0
var yes = 1
// displayed variables
val xDim = 20
val yDim = 20
var currentState = Array.ofDim[Int](xDim, yDim)
var colorIndexList = scala.collection.mutable.ListBuffer[List[Int]]()
var colorData = Array.ofDim[Color](xDim, yDim)
// initial value
infCurrent = scala.collection.mutable.ListBuffer(List(6,6), List(6,7), List(6,8), List(5,8), List(4,7))
// this function tests if a CURRENTLY ALIVE CELL on the INF PLANE STAYS ALIVE
def infStayAlive(current: scala.collection.mutable.ListBuffer[List[Int]]): scala.collection.mutable.ListBuffer[List[Int]] = {
for (i <- current) {
var a = i(0)
var b = i(1)
if (current.contains(List(a - 1, b - 1))) lifeCount += 1
if (current.contains(List(a - 1, b))) lifeCount += 1
if (current.contains(List(a - 1, b + 1))) lifeCount += 1
if (current.contains(List(a, b - 1))) lifeCount += 1
if (current.contains(List(a, b + 1))) lifeCount += 1
if (current.contains(List(a + 1, b - 1))) lifeCount += 1
if (current.contains(List(a + 1, b))) lifeCount += 1
if (current.contains(List(a + 1, b + 1))) lifeCount += 1
if (lifeCount == 2) infNext = infNext :+ i
lifeCount = 0
}
return infNext
}
// this function gets ALL NEIGHBORS for what's on the INF PLANE
def infGetNeighbors(current: scala.collection.mutable.ListBuffer[List[Int]]): scala.collection.mutable.MutableList[List[Int]] = {
for (i <- current) {
var a = i(0)
var b = i(1)
infLifeTester = infLifeTester :+ List(a - 1, b - 1)
infLifeTester = infLifeTester :+ List(a - 1, b)
infLifeTester = infLifeTester :+ List(a - 1, b + 1)
infLifeTester = infLifeTester :+ List(a, b - 1)
infLifeTester = infLifeTester :+ List(a, b + 1)
infLifeTester = infLifeTester :+ List(a + 1, b - 1)
infLifeTester = infLifeTester :+ List(a + 1,b)
infLifeTester = infLifeTester :+ List(a + 1, b + 1)
}
infLifeTester = infLifeTester.distinct
return infLifeTester
}
// this function determines whether cells on the INF PLANE DIE or COME ALIVE
def infComeAlive(infLifeTester: scala.collection.mutable.MutableList[List[Int]]): scala.collection.mutable.ListBuffer[List[Int]] = {
for(i <- infLifeTester) {
var a = i(0)
var b = i(1)
if (infCurrent.contains(List(a - 1, b - 1))) lifeCount += 1
if (infCurrent.contains(List(a - 1, b))) lifeCount += 1
if (infCurrent.contains(List(a - 1, b + 1))) lifeCount += 1
if (infCurrent.contains(List(a, b - 1))) lifeCount += 1
if (infCurrent.contains(List(a, b + 1))) lifeCount += 1
if (infCurrent.contains(List(a + 1, b - 1))) lifeCount += 1
if (infCurrent.contains(List(a + 1, b))) lifeCount += 1
if (infCurrent.contains(List(a + 1, b + 1))) lifeCount += 1
if (lifeCount == 3) infNext1 = infNext1 :+ i
lifeCount = 0
}
infNext1 = infNext1.distinct
return infNext1
}
def printGrid(infCurrent: scala.collection.mutable.ListBuffer[List[Int]]): Array[Array[Int]] = {
for(i <- infCurrent) {
if(i(0) >= 0) {
if(i(0) < xDim) {
if(i(1) >= 0) {
if(i(1) < yDim) {
currentState(i(0))(i(1)) = 1
colorIndexList = colorIndexList :+ i
}
}
}
}
}
return currentState
}
def colorGrid(colorIndexList: scala.collection.mutable.ListBuffer[List[Int]]): Array[Array[Color]] = {
for (i <- colorIndexList) {
colorData(i(0))(i(1)) = Color.WHITE
}
return colorData
}
// define panel class
class DataPanel(data: Array[Array[Color]]) extends Panel {
override def paintComponent(g: Graphics2D) {
val dx = g.getClipBounds.width.toFloat / data.length
val dy = g.getClipBounds.height.toFloat / data.map(_.length).max
for {
x <- 0 until data.length
y <- 0 until data(x).length
x1 = (x * dx).toInt
y1 = (y * dy).toInt
x2 = ((x + 1) * dx).toInt
y2 = ((y + 1) * dy).toInt
} {
data(x)(y) match {
case c: Color => g.setColor(c)
case _ => g.setColor(Color.BLACK)
repaint
}
g.fillRect(x1, y1, x2 - x1, y2 - y1)
println("hi")
}
println("hey")
}
}
// make swing app
object Draw extends SimpleSwingApplication {
val data = colorData
def top = new MainFrame {
background = Color.RED
title = "Shombo's Game of Life"
val button = new Button {
text = "Stahhhp!!"
}
val life = new DataPanel(data) {
preferredSize = new Dimension(500, 500)
}
contents = new BoxPanel(Orientation.Vertical) {
contents += life
contents += button
}
}
}
Draw.top.visible = true
while(yes == 1) {
infLifeTester = infGetNeighbors(infCurrent)
infNext = infStayAlive(infCurrent)
infNext1 = infComeAlive(infLifeTester)
infNext = scala.collection.mutable.ListBuffer.concat(infNext, infNext1)
infCurrent = infNext
infNext = scala.collection.mutable.ListBuffer[List[Int]]()
infNext1 = scala.collection.mutable.ListBuffer[List[Int]]()
infLifeTester = scala.collection.mutable.MutableList[List[Int]]()
currentState = printGrid(infCurrent)
println(currentState.deep.mkString("\n"))
//println("\n")
colorData = colorGrid(colorIndexList)
Draw.top.contents.repaint()
currentState = Array.ofDim[Int](xDim, yDim)
colorIndexList = scala.collection.mutable.ListBuffer[List[Int]]()
colorData = Array.ofDim[Color](xDim, yDim)
yes = 1
}
You have an unclear situation of mixing
var
s an mutable elements. You should either combinevar
s with immutable state that is replaced, or use mutable state (e.g. the arrays) but update it in place and do not create new arrays.In your application, you create an instance of
DataPanel
which takes as argument the array stored incolorData
. But then in your iteration, you are not updating the contents ofcolorData
, but you actually creating a completely new array and replace the value of variablecolorData
:Therefore after the first iteration, your global state differs from what is stored in
DataPanel
.If you re-use the array in
colorData
, you need to changecolorGrid
to assign black color to the inactive cells. You are right now using anull
color as black, and aColor.WHITE
as white. It would make more sense to defineval colorData = Array.ofDim[Boolean](xDim, yDim)
. IncolorGrid
you would first set all elements tofalse
and then iterate throughcolorIndexList
.You should define your iteration as a function that is called in each step. Right now you have an infinite loop
while(yes == 1)
becauseyes
is never changed. Executing this will freeze your computer.Also note that calling
Draw.top
is not good this way, because you defined it as a function; this would create a new window each time you call. Always uselazy val top =
to prevent this problem.Your code is still a big mess, but at least you will have something to move on from. This is a minimum working example, containing only the changes from above:
Run:
Draw.main(Array())