Я новичок в Scala и функциональном программировании. Я создаю игру в крестики-нолики (День 1 из «Семи языков за семь недель» (книга)), и мне хотелось бы узнать, как сделать метод «проверь, выиграл» функционально.

Я хочу сделать часть 'checkrow' (первая часть) похожей на часть 'checkcolumn' (вторая часть), но то, что я пытаюсь, не работает.

Вот мой (рабочий) код:

def checkGame() {
    for (y <- board.indices) {
      // checks a row
      checkRow(y)
    }
    for (x <- board(0).indices) {
      // checks a column
      if(!board.exists(y => y(x) != 'x')){
        println("You have won mate! (column: " + x + ")")
      }
    }
  }

def checkRow(y: Integer) {
var isWon = true
for (x <- board(y).indices) {

  if (board(y)(x) != 'x') {
    isWon = false
  }
}
if (isWon) println("You have won mate! (row: " + y + ")")
}

Примечание: доска - это двумерный массив.

Что я получил до сих пор (не работает):

if(!board.exists(x => x(y) != 'x')){
        println("You have won mate! (row: " + x + ")")
}
1
Kevin 4 Апр 2017 в 20:24

2 ответа

Лучший ответ

Весь смысл наличия функций высшего порядка, таких как exists, состоит в том, чтобы избежать необходимости проходить через Array, используя индексы.

Вот как я это сделаю:

def wonRow(row: Array[Char]): Boolean = row.forall(c => c == 'x')

При этом используется метод forall, который проверяет, удовлетворяют ли ВСЕ элементы массива предикату (здесь все элементы должны быть 'x').

def wonSomeRow(board: Array[Array[Char]]: Boolean = board.exists(row => wonRow(row))

Здесь мы считаем, что какая-то строка выигрывает, если ЛЮБОЙ элемент массива (поэтому любая строка) удовлетворяет предикату (здесь, чтобы быть выигрышной строкой)

Для столбцов это несколько сложнее, поэтому проще всего сделать так, как вы начали:

def wonColumn(board: Array[Array[Char]], col: Int) = board.forall(row => row(i) == 'x')

def wonSomeColumn(board: Array[Array[Char]]) = (0 until board(0).size).exists(i => wonColumn(board, i))

Однако я настоятельно рекомендую заменить board(0).size на некоторую константу в верхней части кода, чтобы избежать ошибок. Действительно, это предполагает, что

  • 1) board имеет первую строку
  • 2) все строки в board имеют размер списка board(0).size

Конечно, эти два допущения хороши в Tic-Tac-Toe, но в функциональном программировании такие допущения во время компиляции следует скорее поместить в систему типов, чтобы их можно было проверить во время компиляции. Тем не менее, это было бы хорошим шагом для начала функционального программирования с такими вещами (просто знайте, что они существуют).

< Сильный > ИЗМЕНИТЬ

Я только что вспомнил, что есть метод transpose для массивов, так что вы можете просто сделать (для столбцов)

def wonSomeCol(board: Array[Array[Char]]) = wonSomeRow(board.transpose)
7
Cyrille Corpet 5 Апр 2017 в 08:17

Ну ... одним функциональным подходом может быть использование foldLeft. Здесь вы начинаете с двух Set из Int, каждый для winRows и winColumns со всеми строками и столбцами.

Затем вы складываете игровое поле, чтобы убрать rows и columns, которые не удовлетворяют условию победы.

def findVictoryRowsAndColumns(board: Array[Array[Char]], height: Int, width: Int): (Set[Int], Set[Int])  = {
  val winRowsInit = (1 to height).toSet
  val winColumnsInit = (1 to width).toSet

  val (winRows, winColumns) = board.zipWithIndex.foldLeft((winRowsInit, winColumnsInit))({
    case ((winRows1, winColumns1), (row, rowIndex)) => row.zipWithIndex.foldLeft(winRows1, winColumns1)({
      case ((winRows2, winColumns2), (cell, columnIndex)) => cell match {
        case 'x' => (winRows2, winColumns2)
        case _ => (winRows2 - rowIndex, winColumns2 - columnIndex)
      }
    })
  })

  (winRows, winColumns)
}

def checkGame(board: Array[Array[Char]], height: Int, width: Int): Unit = {
  val (winRows, winColumns) = findVictoryRowsAndColumns(board, height, width)

  winRows.foreach(i => println("You have won in Row : " + i))
  winColumns.foreach(i => println("You have won in Column : " + i))
}
1
sarveshseri 5 Апр 2017 в 08:42