1

I am new to Scala and functional programming. I'm creating a tic-tac-toe game (Day 1 of 'Seven Languages in Seven Weeks' (book)) and I'd like to know how to do 'check if won' method in a functional way.

I want to make the 'checkrow' part (first part) like the 'checkcolumn' (second part) part, but what I'm trying is not working.

Here my (working) code:

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 + ")")
}

Note: board is a 2 dimensional array.

What I got so far (doesn't work):

if(!board.exists(x => x(y) != 'x')){
        println("You have won mate! (row: " + x + ")")
}
1
  • 1
    You're not doing it "in a functional way" if you're using for lopps and variable mutations. Commented Apr 6, 2017 at 9:14

2 Answers 2

7

The whole point of having higher-order functions such as exists is to avoid having to traverse your Array using indices.

Here is how I'd do it:

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

This uses the forall method, that checks if ALL the elements of the array satisfy the predicate (here, all the elements must be 'x').

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

Here, we consider that some row makes a win if ANY element of the array (so any row) satisfies the predicate (here, to be a winning row)

For columns, this is somehow more intricate, so the easiest way is to do as you started:

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))

However, I would strongly suggest that you replace board(0).size by some constant at the top of your code, to avoid getting some error. Indeed, this assumes that

  • 1) board has a first row
  • 2) all row in board have at list size board(0).size

Of course, these two assumptions are ok in a Tic-Tac-Toe, but in functional programming, such compile-time assumptions should rather be put in the type system, to be verified at compile time. However, this would make quite a step to start functional programming with this kind of things (just know that they exist).

EDIT

I just remembered there is a method transpose on arrays, so you can just do (for columns)

def wonSomeCol(board: Array[Array[Char]]) = wonSomeRow(board.transpose)
Sign up to request clarification or add additional context in comments.

Comments

1

Well... one functional approach can be the use of foldLeft. Here you start with two Sets of Int, each for winRows and winColumns with all rows and columns.

Then, you fold over the gameboard to eliminate the rows and columns which do not satisfy the victory condition.

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))
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.