Table of Contents

Lab 6. Scala on steroids

5-Tic-Tac-Toe

Tic Tac Toe is usually played on a 3×3 board, marking positions by each player in rounds. Our game is slightly different (usually called 5-in-a-row):

Example of a winning position for X on a 5×5 board:

X...0
0X.0.
..X0.
...X.
.0..X

Example of a winning position for 0 on a 7×7 board:

.X...X.
...0...
...0...
.X.0..X
0..0..0
...0...
...X...

Encodings

trait Player {}
case object One extends Player {
  override def toString: String = "X"
}
case object Two extends Player {
  override def toString: String = "0"
}
case object Empty extends Player {
  override def toString: String = "."
}
type Line = List[Player]
type BoardList = List[Line]
 
case class Board(b: BoardList) {
  override def toString: String = ???
}

Tasks

The following functions have a given signature. However, it is up to the student to decide whether these will be methods of a class or just simple functions.

6.1.1. Write a function which converts a string into a Board. As a helper, you can use _.split( c ) where c is a separator string, and _.toList. The best solution is to use a combination of map calls with the above mentioned functions. A string is encoded exactly as in the examples shown above:

def makeBoard(s: String): Board = {
    def toPos(c: Char): Player =
      c match {
        case 'X' => One
        case '0' => Two
        case _ => Empty
      }
    ???
}

6.1.2. Write a function which checks if a position on the board is free. Recall that list indexing can be done using l(_). Positions are numbered from 0.

def isFree(x:Int, y:Int):Boolean = ???

6.1.3. Write a function which returns the opponent of a player:

def complement(p: Player): Player = ???

6.1.4. We want to write a function which converts a board to a string, following the same strategy. Complete the toString in the Board class. Hint: instead of foldRight, you can use reduce which works quite similarly, but without requiring an accumulator.

6.1.5. Write a function which returns the columns of a board:

def getColumns: Board = ???

6.1.6. Implement the following two functions for extracting the first and second diagonal, as lines, from a board. Hint: use for comprehensions.

def getFstDiag(): Line = ???
def getSndDiag(): Line = ???

6.1.7. Implement the following functions for extracting diagonals above/below the first/second diagonal, as lines. It's not really necessary to make sure that at least 5 positions are available, for now. Hint: if one function must be implemented with element-by-element iteration, the three other can be implemented using each-other, as single-line calls.

def getAboveFstDiag: List[Line] = ???
def getBelowFstDiag: List[Line] = ???
def getAboveSndDiag: List[Line] = ???
def getBelowSndDiag: List[Line] = ???

6.1.8. Write a function which checks if a player is the winner. Hint: functions l.forall(_) and l.exists(_) may be very helpful, together with patterns.

def winner(p: Player): Boolean = ???

6.1.9. Write a function which updates a position from the board, with a given player. The position need not be empty and you are not required to check this. Hint: re-use an inner aux-function together with take and drop.

def update(p: Player)(ln: Int, col: Int) : Board = ??? 

6.1.10. Write a function which generates all possible next-moves for any of the two players. A next-move consists in a new board, where the player-at-hand played his move.

def next(p: Player): List[Board] = ???

Testing

Use the following board configurations to test your solutions:

val t1 =
  """X0X0X0
    |0X0X0X
    |X0X0X0
    |.XX0..
    |X00...
    |X0X0X0""".stripMargin
 
val t2 =
  """......
    |......
    |......
    |.XX...
    |.0000.
    |......""".stripMargin
 
val t3 =
  """0X0X0.
    |000.X0
    |0.0X..
    |0..0..
    |0X..0X
    |...X..""".stripMargin