Перекраска качелей Scala

Так что я очень, очень новичок в scala. Я реализую игру Conways's Game of Life с графическим интерфейсом. Я не могу понять, как заставить мою панель обновляться при изменении 2D-массива. Может ли кто-нибудь указать мне правильное направление? Весь мой код следует:

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

}

person user3080906    schedule 08.12.2013    source источник
comment
Используйте шаблон наблюдателя, показанный здесь в Java.   -  person trashgod    schedule 09.12.2013


Ответы (1)


У вас непонятная ситуация смешивания vars с изменяемыми элементами. Вы должны либо комбинировать vars с неизменяемым состоянием, которое заменяется, либо использовать изменяемое состояние (например, массивы), но обновлять его на месте и не создавать новые массивы.

В вашем приложении вы создаете экземпляр DataPanel, который принимает в качестве аргумента массив, хранящийся в colorData. Но тогда в вашей итерации вы не обновляете содержимое colorData, а фактически создаете совершенно новый массив и заменяете значение переменной colorData:

colorData = Array.ofDim[Color](xDim, yDim)

Поэтому после первой итерации ваше глобальное состояние отличается от того, что хранится в DataPanel.

Если вы повторно используете массив в colorData, вам нужно изменить colorGrid, чтобы назначить черный цвет неактивным ячейкам. Вы прямо сейчас используете цвет null как черный, а Color.WHITE как белый. Было бы разумнее определить val colorData = Array.ofDim[Boolean](xDim, yDim). В colorGrid вы должны сначала установить все элементы в false, а затем выполнить итерацию по colorIndexList.

Вы должны определить свою итерацию как функцию, которая вызывается на каждом шаге. Прямо сейчас у вас есть бесконечный цикл while(yes == 1), потому что yes никогда не изменяется. Выполнение этого заморозит ваш компьютер.

Также обратите внимание, что вызов Draw.top таким образом не годится, потому что вы определили его как функцию; это будет создавать новое окно каждый раз, когда вы звоните. Всегда используйте lazy val top =, чтобы предотвратить эту проблему.


Ваш код по-прежнему представляет собой большой беспорядок, но, по крайней мере, вам будет от чего двигаться дальше. Это минимальный рабочий пример, содержащий только изменения выше:

import scala.collection.mutable.ListBuffer

val colorData = Array.ofDim[Boolean](xDim, yDim)

def colorGrid(colorIndexList: ListBuffer[List[Int]]): Unit = {
  colorData.foreach { row => java.util.Arrays.fill(row, false) } // all 'black'
  for (i <- colorIndexList) {
    colorData(i(0))(i(1)) = true  // some 'white'
  }
}

class DataPanel(data: Array[Array[Boolean]]) extends Panel {

  override def paintComponent(g: Graphics2D): Unit = {
    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
    } {
      g.setColor(if (data(x)(y)) Color.black else Color.white) // !
      g.fillRect(x1, y1, x2 - x1, y2 - y1)
    }
  }
}

 

def step(): Unit = {
  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")

  colorGrid(colorIndexList)
  currentState = Array.ofDim[Int](xDim, yDim)
  colorIndexList = scala.collection.mutable.ListBuffer[List[Int]]()
}

 

object Draw extends SimpleSwingApplication {
    lazy val life = new DataPanel(colorData) {
      preferredSize = new Dimension(500, 500)
    }

    lazy val top = new MainFrame {
    background = Color.RED
    title = "Shombo's Game of Life"
    val button = Button("Step") {
      step()             // updates colorData
      life.repaint()     // refreshes view
    }
    contents = new BoxPanel(Orientation.Vertical) {
      contents += life
      contents += button
    }
  }
}

Пробег: Draw.main(Array())

person 0__    schedule 09.12.2013