Scala code to print a Sudoku board and any possible unsolved values
First some minor points :


You create a tuple with new Tuple3(x, y, z), more ideomatic would be (x, y, z).
You were having troubles with using a var and immutable.List, it is perfectly ok to use mutable.ListBuffer inside your function. You just need to turn it into a List before returning, but since you are just printing out possibleValues I would just use ListBuffer.


Then my major point: I would split up printBoard in multiple functions :


reprBoard : a function which returns a textual representation (a String) of a Board.
possibleValuesOpenCells : a function which returns all the possible values of the empty cells of a Board.


You could then use these two functions in printBoard to print out the Board and the possible numbers for every open cell.

Below you find a possible implementation of reprBoard which is a little more functional (I have simplified Board to a matrix of numbers, but it should be simple to adjust this code to use your actual Board) :

// an open cell = 0
type Board = Vector[Vector[Int]]

def reprBoard(board: Board): String = {
  // I have made it independent of the size of the board
  // so you could pass a 9x9, a 4x4, ... sudoku board
  val rowLength = board.headOption.map(_.size).getOrElse(0)
  val size = Math.sqrt(rowLength.toDouble).toInt
  val rowSeparator = 
    intersperse(size, "+ ", Vector.fill(size * size)("- ")).mkString.trim
  val colSeparator = "| "

  // it depends of the implementation of Board if you can map over the rows 
  // and columns, but you should be able to do something similar
  val rows = board.map { row =&gt;
    val cells = row.map ( cell =&gt;
      if (cell != 0) s"$cell " else "x "
    )
    intersperse(size, colSeparator, cells).mkString.trim
  }
  intersperse(size, rowSeparator, rows).mkString("\n")
}

// a help function which adds an extra element every `n` elements and at
// the start and the end
// eg. intersperse(1, 0, Vector(1, 2))  -&gt;  Vector(0, 1, 0, 2, 0)
def intersperse[A](n: Int, value: A, vector: Vector[A]) = 
  vector.grouped(n).foldLeft(Vector.empty[A])((acc, elem) =&gt; 
    acc ++ Vector(value) ++ elem
  ) ++ Vector(value)


Which you could use as :

import scala.util.Random

// generate a random row with two open cells for testing
def randomRow = 
  Random.shuffle(Random.shuffle(Vector.range(1, 10)).drop(2) ++ Vector(0, 0))

val board = Vector.fill(9)(randomRow)

reprBoard(board)
// + - - - + - - - + - - - +
// | 6 3 9 | x 8 1 | 5 7 x |
// | x 6 7 | x 2 5 | 4 8 3 |
// | 3 9 8 | x 2 7 | x 6 1 |
// + - - - + - - - + - - - +
// | 9 1 5 | 2 3 7 | 8 x x |
// | x 1 3 | x 7 8 | 2 6 5 |
// | 8 1 4 | 9 5 7 | x 6 x |
// + - - - + - - - + - - - +
// | 3 7 4 | x 6 1 | x 2 9 |
// | 4 6 3 | 7 x 8 | 2 5 x |
// | 7 2 5 | 8 9 4 | x x 6 |
// + - - - + - - - + - - - +


In possibleValuesOpenCells you could :


use your nested for loop if the cells themselves don't have their coordinates
or use a nested map like I did in reprBoard (when you don't need the i and j to create the tuple, you could also look if zipWithIndex can solve this)


printBoard then could look like :

def printBoard(board: Board, message: String = "Printing a board", 
               displayPossibleValues: Boolean = true) = {
  println(message)
  println(reprBoard(board))
  println("Remaining values:")
  possibleValuesOpenCells(board).foreach(println)
}


You need to loop the board twice this way, but I think this is a (very) minor price to pay in comparision with the improved clarity.