2013-12-08 5 views
0

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

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 

} 
+0

использовать шаблон наблюдатель, иллюстрированный [здесь] (http://stackoverflow.com/a/3072979/230513) в Java. – trashgod

ответ

0

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

В вашем приложении вы создаете экземпляр 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 
    } 
    } 
} 

Run: Draw.main(Array())

Смежные вопросы