Edit this page Backlinks This page is read only. You can view the source, but not change it. Ask your administrator if you think this is wrong. ===== Homework 3: 5-in-a-row ===== In this homework, you will implement functionality which will allow you to design a completely functional 5-in-a-row game. ==== About 5-in-a-row ==== The game: * an be played on a square board of any size larger or equal to 5. * A player wins if it has marked a line, column or diagonal of 5 consecutive positions in a row. Example of a winning position for ''X'' on a 5x5 board: <code> X...0 0X.0. ..X0. ...X. .0..X </code> Example of a winning position for ''0'' on a 7x7 board: <code> .X...X. ...0... ...0... .X.0..X 0..0..0 ...0... ...X... </code> ==== Coding conventions ==== * In your project template, ''X'' is encoded as the **first** player (''One''), and ''0'', as ''Two''. * A ''Board'' is encoded as a List of Lists of **positions** (i.e. a matrix), where a position can be ''One'', ''Two'' or ''Empty''. We make no distinction in the code between a position and a player. This makes the code slightly easier to write, however ''Empty'' cannot be seen as a valid player. ==== Coding tasks ==== **1.** Boards will be represented using the class ''Board'' (see the project template), which contains a board matrix, as well as the player whose turn it is to play. In order to create a board, write a function which converts a string into a ''Board''. Implement the apply method in the companion object ''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: * there are no whitespaces - empty positions are marked by the character '.' * lines are delimited by '\n' (the last line does not have a trailing '\n'). <code scala> def apply(s: String): Board = { def toPos(c: Char): Player = c match { case 'X' => One case '0' => Two case _ => Empty } ??? </code> **2.** Implement the member function ''isFree'' which checks if a position ''(x,y)'' on the board is free. Recall that list indexing can be done using ''l(_)''. Positions are numbered from 0. <code scala> def isFree(x:Int, y:Int):Boolean = ??? </code> **3.** Write a function which returns the //opponent// of a player. The function is defined in the trait ''Player'', which is allowed in Scala: <code scala> def complement: Player = ??? </code> **4.** Implement the ''toString'' functions which converts a board to a string, following the same strategy. **Important:** this function will be used throughout the tests. Make sure the string doesn't end with '\n'. Hint: instead of ''foldRight'', you can use ''reduce'' which works quite similarly, but without requiring an accumulator. <code scala> override def toString: String = ??? </code> **5.** Write a function which returns the //columns// of a board: <code scala> def getColumns: Board = ??? </code> **6.** Implement the following two functions for extracting the first and second diagonal, as lines, from a board. Hint: use for comprehensions. <code scala> def getFstDiag: Line = ??? def getSndDiag: Line = ??? </code> **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. <code scala> def getAboveFstDiag: List[Line] = ??? def getBelowFstDiag: List[Line] = ??? def getAboveSndDiag: List[Line] = ??? def getBelowSndDiag: List[Line] = ??? </code> **8.** Write a function which checks if the current player is the winner. Hint: functions ''l.forall(_)'' and ''l.exists(_)'' may be very helpful, together with patterns. <code scala> def winner: Boolean = ??? </code> **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''. <code scala> def update(ln: Int, col: Int) : Board = ??? </code> **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. The order in which you generate next-moves is not important. <code scala> def next: List[Board] = ??? </code> **11.** Implement a function ''sequences'', that returns a map of the form: ''(5,a), (4,b), (3,c), (2,d)'' where: * ''a'' is the number sequences of length 5 that the player has established (on lines, columns or diagonals). * ''b'' is the number of sequences of length 4 **which can be filled-out in order to achieve a 5-sequence**. For example: ''XX XX'' is such a sequence for player ''One'', however ''XX0XX'' is not, since this cannot be filled-out in order to achieve a 5-sequence. * ''c'' (resp. ''d'') is the number of sequences of length 3 (resp. 2) which can be filled-out in order to achieve a 5-sequence. * Proper testing is **essential** for this function, as the AI will take decisions based on it. It will be harder to spot problems with ''sequences'' in a later stage of the project. * You can replace the implementation of ''winner'' with a call to this function, to make the code more compact and avoid partial re-implementations. <code scala> def sequences: Map[Int,Int] = ??? </code> ===== Submission rules ===== * Please follow the [[fp2023:submission-guidelines| Submission guidelines]] which are the same for all homework. * To solve your homework, download the {{:fp2023:h3-5-in-a-row.zip|Project template}}, import it in IntellIJ, and you are all set. Do not rename the project manually, as this may cause problems with IntellIJ.