Scala Swing пропускает перекраску фрейма - PullRequest
0 голосов
/ 31 мая 2019

В настоящее время я работаю над реализацией игры Othello в Scala, и пока она работает довольно хорошо.Хотя при реализации GUI (с использованием Scala Swing) я наткнулся на проблему, которую, похоже, не могу исправить.

При игре против компьютерного оппонента фрейм, кажется, перекрашивается, только когда бот готовделает ход.

В игру также можно играть через терминал, и при этом каждый раз корректно обновляется фрейм независимо от конфигурации игрока (игрок против игрока или игрок против компьютера).Кроме того, игра "игрок против игрока" с использованием графического интерфейса пользователя не представляет никаких проблем.

Возможно, это упущение от моего имени, но пока я не могу найти решение и очень признателен за любую помощь.

До сих пор я пробовал различные комбинации переаттестации и перекрашивания отдельных панелей, добавления и удаления слушателей, изменения моей реализации схемы реактора на ту, что была предоставлена ​​Scala Swing, добавление Thread.sleep, чтобы увидеть, может ли быть планированиеконфликт сортов.

import java.awt.Color

import othello.controller.Controller
import javax.swing.ImageIcon
import javax.swing.border.LineBorder

import scala.swing.event.MouseClicked
import scala.swing.{BorderPanel, BoxPanel, Dimension, FlowPanel, GridPanel, Label, Orientation}

class TablePanel(controller: Controller) extends FlowPanel {

  val sides = 32
  val sidesColor: Color = Color.lightGray
  val squareSize = 52

  def tableSize: Int = controller.board.size

  def edgeLength: Int = tableSize * squareSize

  def rows: BoxPanel = new BoxPanel(Orientation.Vertical) {
    background = sidesColor
    preferredSize = new Dimension(sides, edgeLength)
    contents += new Label {
      preferredSize = new Dimension(sides, sides)
    }
    contents += new GridPanel(tableSize, 1) {
      background = sidesColor
      for { i <- 1 to rows } contents += new Label(s"$i")
    }
  }

  def columns: GridPanel = new GridPanel(1, tableSize) {
    background = sidesColor
    preferredSize = new Dimension(edgeLength, sides)
    for { i <- 0 until columns } contents += new Label(s"${(i + 65).toChar}")
  }

  def table: GridPanel = new GridPanel(tableSize, tableSize) {
    background = new Color(10, 90, 10)
    for {
      col <- 0 until columns
      row <- 0 until rows
    } contents += square(col, row)
  }

  def square(row: Int, col: Int): Label = new Label {
    border = new LineBorder(new Color(30, 30, 30, 140), 1)
    preferredSize = new Dimension(squareSize, squareSize)
    icon = controller.board.valueOf(col, row) match {
      case -1 => new ImageIcon("resources/big_dot.png")
      case 0  => new ImageIcon("resources/empty.png")
      case 1  => new ImageIcon("resources/black_shadow.png")
      case 2  => new ImageIcon("resources/white_shadow.png")
    }
    listenTo(mouse.clicks)
    reactions += {
      case _: MouseClicked =>
        if (controller.options.contains((col, row))) controller.set(col, row)
        else if (controller.board.gameOver) controller.newGame()
        else controller.highlight()
    }
  }

  def redraw(): Unit = {
    contents.clear
    contents += new BorderPanel {
      add(rows, BorderPanel.Position.West)
      add(new BoxPanel(Orientation.Vertical) {
        contents += columns
        contents += table
      }, BorderPanel.Position.East)
    }
    repaint
  }
}
import scala.swing._
import othello.controller._
import othello.util.Observer

import scala.swing.event.Key

class SwingGui(controller: Controller) extends Frame with Observer {

  controller.add(this)

  lazy val tablePanel = new TablePanel(controller)

  lazy val mainFrame: MainFrame = new MainFrame {
    title = "Othello"
    menuBar = menus
    contents = tablePanel
    centerOnScreen
    // peer.setAlwaysOnTop(true)
    resizable = false
    visible = true
  }

  def menus: MenuBar = new MenuBar {
    contents += new Menu("File") {
      mnemonic = Key.F
      contents += new MenuItem(Action("New Game") {
        controller.newGame()
      })
      contents += new MenuItem(Action("Quit") {
        controller.exit()
      })
    }
    contents += new Menu("Edit") {
      mnemonic = Key.E
      contents += new MenuItem(Action("Undo") {
        controller.undo()
      })
      contents += new MenuItem(Action("Redo") {
        controller.redo()
      })
    }
    contents += new Menu("Options") {
      mnemonic = Key.O
      contents += new MenuItem(Action("Highlight possible moves") {
        controller.highlight()
      })
      contents += new MenuItem(Action("Reduce board size") {
        controller.resizeBoard("-")
      })
      contents += new MenuItem(Action("Increase board size") {
        controller.resizeBoard("+")
      })
      contents += new MenuItem(Action("Reset board size") {
        controller.resizeBoard(".")
      })
      contents += new Menu("Game mode") {
        contents += new MenuItem(Action("Player vs. Computer") {
          controller.setupPlayers("1")
        })
        contents += new MenuItem(Action("Player vs. Player") {
          controller.setupPlayers("2")
        })
      }
    }
  }

  def update: Boolean = {
    tablePanel.redraw()
    mainFrame.pack
    mainFrame.centerOnScreen
    mainFrame.repaint
    true
  }
}

Ожидаемое поведение - перекрашенный фрейм на каждом ходу.Фактическим результатом является перекрашивание фрейма только после того, как противник сделал ход.Это происходит только когда игрок играет против бота исключительно через нажатие интерфейса.

Ответы [ 2 ]

1 голос
/ 31 мая 2019

Как предположил Рич, решением был параллелизм.Создание алгоритма поиска типа Future решило проблему на данный момент.Возможно, это не самая чистая реализация, так как я впервые использую Future, но сейчас это выглядит так:

  def selectAndSet(): Future[_] = Future(if (!board.gameOver && player.isBot) {
    new MoveSelector(this).select() match {
      case Success(square) => set(square)
      case _ => omitPlayer()
    }
    selectAndSet()
  })(ExecutionContext.global)
1 голос
/ 31 мая 2019

Я не думаю, что проблема в коде, который вы показали, но я бы поспорил, что вы блокируете "поток рассылки событий" (поток пользовательского интерфейса) с помощью своего вычисления AI для компьютерного игрока.

В приложении Swing существует специальный поток, называемый «поток диспетчеризации событий», который отвечает за обработку сообщений от O / S, включая обработку сообщений перерисовки.Все обработчики событий пользовательского интерфейса будут вызываться в этом потоке.Если вы используете этот поток для выполнения каких-либо вычислений, которые занимают много времени (например, перемещение компьютера в такой игре), любые обновления пользовательского интерфейса будут заблокированы до тех пор, пока поток не станет свободным.

В этом руководстве содержится дополнительная информация: https://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html

Вам необходимо переместить ИИ в фоновый поток и освободить поток отправки событий, чтобы иметь дело с перерисовками.Это может быть сложно реализовать, если вы не знакомы с многопоточными программами!Удачи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...