2014-07-16 2 views
0

저는 초보자이지만 자바와 C++로 경험했습니다. 이제는 Conway의 평행 게임을 구현하기 위해 akka 배우 모델을 사용하고 싶습니다. 내 생각은 50 * 50 격자를 만들고, 각 셀은 배우이고, 배우에게 메시지를 전달하여 업데이트합니다. 액터를 만드는 방법은 다음과 같습니다.스케이프 akka 배우 모델을 사용하여 인생 게임에서 이웃 배우를 호출합니다.

class World(row: Int, col: Int, step: Int) extends Actor { 



val random = new Random() //generate the alive or dead cell for the game 
    val cellgrid = new World(row, col, step) 
    def receive = { 

case Start => { 

    for(gridrow <- 0 until row) { 
    for(gridcol <- 0 until col) { 
     context.actorOf(Props(new Grid(random.nextBoolean, gridrow, gridcol, row, col, step))) 
    } 
    } 

    for (gridrow <- 0 until row) { 
    for (gridcol <- 0 until col) { 
     sender ! Compare() 
    } 
    } 

} 

case Done(gridrow, gridcol, alive) => { 
    for(gridrow <- 0 until row) { 
    for(gridcol <- 0 until col) { 
     if(alive) print("* ") 
     else print(" ") 
    } 
    print("\n") 
    } 
    sender ! Stop 
} 

case Stop => {context.stop(self); println("Stopped the actor system!")} 
case _ => context.stop(self) 


} 
} 

하지만 문제가 발생합니다. 그리드 클래스가 너무 많아서 이웃 배우를 호출하는 데 문제가 있습니다.

class Grid(var life: Boolean, gridrow: Int, gridcol: Int, row: Int, col: Int, step: Int) extends Actor { 



val alive = life 
    var numNeighbors = 0 
    var currentStep = 0 
    val desiredSteps = step 
    val count = Array(0,0) 
    val countsuround = Array(0,0) 

    for (neighbourX <- gridrow - 1 to gridrow + 1) { 
    if (neighbourX >= 0 && neighbourX < col) { 
     for (neighbourY <- gridcol - 1 to gridcol + 1) { 
     if (neighbourY >= 0 && neighbourY < row && (neighbourX != row && neighbourY != col)) 
      numNeighbors = numNeighbors + 1 
     } 
    } 
    } 

    def receive = { 

case Compare() => { 
    for (neighbourX <- gridrow - 1 to gridrow + 1) { 
    if (neighbourX >= 0 && neighbourX < col) { 
     for (neighbourY <- gridcol - 1 to gridcol + 1) { 
     if (neighbourY >= 0 && neighbourY < row && (neighbourX != row && neighbourY != col)) 
      sender ! Record(life, currentStep) //Here I need to pass messages to my neighbors 
     } 
    } 
    } 

} 

case Record(alive,step) => { 
    if(alive == true){ 
    count(step%2) += 1 
    } 
    countsuround(step%2) += 1 
    self ! Check() 
} 

case Check() => { 
    if(countsuround(currentStep%2) == numNeighbors) { 

    if(count(currentStep%2) ==3 ||life == true && count(currentStep%2) == 2) 
     life = true 
    else 
     life = false 

    count(currentStep%2) =0; countsuround(currentStep%2) = count(currentStep%2) 
    currentStep += 1 

    if(desiredSteps <= currentStep + 1) 
     sender ! Stop 
    else { 
     sender ! Done(gridrow, gridcol, alive) 
     //context.stop(self) 
    } 
    } 
} 

    } 

} 

그 말에, 내 이웃에게 메시지 기록을 보낼 필요가, 수신 기능의 비교 사례에서 봐 주시기 바랍니다,하지만 적절한를 찾을 수 없습니다 : 여기 그리드 클래스입니다 나는 이웃에 대한 어떤 인덱스도 가지고 있지 않다. (이웃 X, 이웃 Y). 레코드 (life, currentStep)). 제발 도와주세요. 몇 주 동안 여기 붙어 있습니다. 감사!!!

+0

나는 코드가 작동하는지 아직 확실하지 않다, 그래서 당신은 뭔가 문제가 있다고 생각하면, 내가 아니라 병렬 방식으로,이 작업을 수행, 코드를 시도했습니다 나에게 – HONTAKYU

답변

1

다음은 일부 의견이있는 완전한 작동 예제입니다.

import akka.actor._ 
import scala.concurrent.duration._ 


object GameOfLife extends App { 
    val system = ActorSystem("game-of-life") 

    implicit val ec = system.dispatcher // implicit ExecutionContext for scheduler 

    val Width = 20 
    val Height = 20 

    // create view so we can send results to 
    val view = system.actorOf(Props(classOf[View], Width, Height), "view") 

    // create map of cells, key is coordinate, a tuple (Int, Int) 
    val cells = (for { i <- 0 until Width; j <- 0 until Height } yield { 
     val cellRef = system.actorOf(Props[Cell], s"cell_$i-$j") // correct usage of Props, see docs for details 
     ((i,j), cellRef) 
    }).toMap 

    // we need some helpers to work with grid 

    val neighbours = (x:Int, y:Int) => Neighbours(
     for (i <- x - 1 to x + 1; j <- y - 1 to y + 1; if ((i,j) != (x,y))) yield { 
      cells(((i + Width) % Width, (j + Height) % Height)) 
     } 
    ) 

    for { i <- 0 until Width; j <- 0 until Height } { // notice that this loop doesn't have yield, so it is foreach loop 
     cells((i,j)) ! neighbours(i,j) // send cell its' neighbours 
    } 

    // now we need to synchronously update all cells, 
    // this is the main reason why suggested model (one actor - one cell) is probably not the most adequate 

    for { i <- 0 until Width; j <- 0 until Height } { 
     cells((i,j)) ! SetState(alive = util.Random.nextBoolean, x = i, y = j) 
    } 

    // for simplicity I assume that update will take less then update time 
    val refreshTime = 100.millis 

    system.scheduler.schedule(1.second, refreshTime) { 
     view ! Run 
     for { i <- 0 until Width; j <- 0 until Height } { 
     cells((i,j)) ! Run 
     } 
    } 


} 


class View(w:Int, h:Int) extends Actor { 

    var actorStates: Map[(Int,Int), Boolean] = Map() 

    def receive:Receive = { 
    case Run => actorStates = Map.empty 
    case UpdateView(alive, x, y) => 
     actorStates = actorStates + (((x,y), alive)) 
     if (actorStates.size == w * h) { 
     for { j <- 0 until h } { 
      for(i <- 0 until w) { 
      if(actorStates((i,j))) { 
       print("x ") 
      } else { 
       print(". ") 
      } 
      } 
      println() 
     } 
     } 
    } 
} 

class Cell extends Actor { 

    var neighbours:Seq[ActorRef] = Seq() 
    var neighbourStates: Map[ActorRef, Boolean] = Map() // Map.empty[Map[ActorRef, Boolean]] is better 
    var alive:Boolean = false 
    var previousState:Boolean = false 
    var x:Int = 0 
    var y:Int = 0 

    def receive : Receive = { 
     case Run => 
     neighbourStates = Map.empty 
     previousState = alive 
     neighbours.foreach(_ ! QueryState) 
     case SetState(alive,x,y) => 
     this.alive = alive 
     this.x = x 
     this.y = y 
     case Neighbours(xs) => 
     neighbours = xs 

     case QueryState => 
     sender ! NeighbourState(alive = previousState) 
     case NeighbourState(alive) => 
     neighbourStates = neighbourStates + ((sender, alive)) 
     // this is tricky when all senders has send you their states it doesn't mean that you can mutate your own, 
     // they could still may request your internal state, will use hackish previousState 
     if (neighbourStates.size == 8) { // total of 8 neighbours sent their states, we are complete with update 
      val aliveMembers = neighbourStates.values.filter(identity).size 
      aliveMembers match { 
      case n if n < 2 => this.alive = false 
      case 3 => this.alive = true 
      case n if n > 3 => this.alive = false; 
      case _ => // 2, state doesn't change 
      } 

      context.actorSelection("/user/view") ! UpdateView(this.alive, x, y) 
     } 
    } 
} 


case class SetState(alive:Boolean, x:Int, y:Int) 
case class Neighbours(xs:Seq[ActorRef]) 
case object QueryState 
case object Run 
case class NeighbourState(alive:Boolean) 
case class UpdateView (alive:Boolean, x:Int, y:Int) 
+0

을 알려주세요. 내 생각은 액터를 만들어서 평행하게 움직이는 것입니다. 한 가지 방법은 내가 생각하는 병렬 컬렉션을 사용하는 것이지만 컬렉션을 사용하지 않는 액터를 만드는 다른 방법이 있습니까? 예를 들어, 재귀 적으로 액터를 만들지 만, 그렇다면이를 구현하고 좌표없이 이웃을 얻는 방법? :) – HONTAKYU

+0

어떻게 평행하지 않는 것을 의미합니까? 액터 간의 메시지 통신에 의해 수행되는 모든 계산과 99.999 % 병렬입니다.) 물론 모든 액터가 다음 업데이트를 수행하기 전에 상태를 변경하기를 기다려야합니다. 이 문제를 피하려면 불변의 셀 배열을 사용할 수 있습니다. 그런 다음 다음 업데이트는 이전의 상태를 변경하지 않으므로 병렬 배열로이를 수행 할 수 있습니다. 그렇지만 많은 배우가 필요하지 않습니다. 실제로 배우가 전혀 필요하지 않습니다. – vitalii

관련 문제