(HASKELL LANGUAGE) PLEASE TAKE YOUR TIME OMOK GAME, NOT A TIC-TAC-TOE GAME Your program should follow the Model-View-Control (MVC) pattern. Your Haskell program must consist of two modules: one for the game model (M) and one for the console-based user interface (VC). The second module will require knowledge of basic I/O operations in Haskell (refer to Haskell tutorials such as https://www.haskell.org/tutorial/io.html). Only standard Prelude functions should be used in the program, and no I/O functions should be included in the first module. Note that in Haskell, an I/O function may call non-I/O functions, but a non-I/O function may not call I/O functions. Part I (65 points; 101 lines of source code): Develop a Haskell module named "Board" to model the Omok board and two players. The module should provide the following functions. 1. (8 points) Creating a board and accessing its elements mkBoard n Return an empty nxn board, where n is a positive number. A 1-based pair of indices (x,y) will be used to access a specific place of the board, where x and y are column and row indices, respectively. However, it's up to you how to represent an omok board concretely. mkPlayer Create and return the first player. It's up to you how to represent an omok player concretely. E.g., mkPlayer = 1. The idea is to call this function whenever you need to refer to the first play, rather than using a magic number like 1. mkOpponent Create and return the second player, i.e., the opponent. E.g., mkOpponent = 2 size bd Return the size of a board bd, n for an nxn board. row y bd Return a row y of a board bd, where y is a 1-based index. It returns a list of size n, where n is the size of bd. column x bd Return a column x of a board bd, where x is a 1-based index. It returns a list of size n, where n is the size of bd. 2. (12 points) Checking places and placing stones mark x y bd p Mark a place (x,y) in a board bd by a player p, where x and y are 1-based column and row indices. The specified place is assumed to be empty (see below). isEmpty x y bd Is a place (x,y) of a board bd unmarked or a stone not placed? The x and y are 1-based column and row indices. isMarked x y bd Does a place (x,y) of a board bd have a stone placed? The x and y are 1-based column and row indices. isMarkedBy x y bd p Does a place (x,y) of a board bd have a stone placed by a player p? The x and y are 1-based column and row indices. marker x y board Return the player of the stone placed on a place (x,y) of a board bd. The x and y are 1-based column and row indices. isFull bd Are all places of board bd marked, i.e., there is no empty place? 3. (35 points) Determining the game outcome isWonBy bd p Is the game played on a board bd won by a player p? That is, does the board bd has a winning row for the player p? isDraw bd Is the game played on a board bd ended in a draw? isGameOver bd Is the game played on a board bd over? 4. (10 points) Converting to a string for printing boardToStr playerToChar bd Return a string representation of the board "bd". This is a higher-order function, and "playerToChar" is a function that transforms a player into a character representation, such as 'O' and 'X' (refer to Part II for more information). A sample return value that is properly formatted is demonstrated below. It is created using the "playerToChar" function, which assigns 'O' to the first player, 'X' to the second player, and '.' to all others. " x 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5\n y ------------------------------\n 1| . . . . . . . . . . . . . . .\n 2| . . . . . . . . . . . . . . .\n 3| . . . . . . . . . . . . . . .\n 4| . . . . . . . . . . . . . . .\n 5| . . . . . . . . . . . . . . .\n 6| . . . . . . . . O . . . . . .\n 7| . . . . . . . X X . . . . . .\n 8| . . . . . . X O O . . . . . .\n 9| . . . . . . . . . . . . . . .\n 0| . . . . . . . . . . . . . . .\n 1| . . . . . . . . . . . . . . .\n 2| . . . . . . . . . . . . . . .\n 3| . . . . . . . . . . . . . . .\n 4| . . . . . . . . . . . . . . .\n 5| . . . . . . . . . . . . . . ." Part II (35 points; 112 lines): Create a Haskell module named "Main" that implements a console-based user interface for playing the omok game. To complete this part, you may need to import the "System.IO" and "System.Random" modules. Your "Main" module should include the following I/O functions.
1. (15 points) Reading user inputs and printing outputs. playerToChar p Return a character representation of the player "p". This function returns a character value. It can be utilized to display the current status of the board (refer to the "boardToStr" function in Part I). readXY bd p Read a pair of 1-based indices (x, y) for player "p", indicating an unmarked spot on the board "bd". This function retrieves inputs from the standard input (stdin) and returns an IO value in the format of IO(Int,Int) or IO(Integer,Integer). The following IO functions may be useful. putStr, putStrLn - print a string to the standard out getLine - read a line from the standard in reads:: [(Integer, String)] - parse an Integer from a string For example, the following IO function reads lines from the standard input (stdin) until a positive integer is read. getX = do putStrLn "Enter a positive value?" line <- getLine let parsed = reads line :: [(Integer, String)] in if length parsed == 0 then getX' else let (x, _) = head parsed in if x > 0 then return x else getX' where getX' = do putStrLn "Invalid input!" getX 2. (20 points) Playing game main Main function to play a game of omok between two human players. It returns an IO() value. The board has a size of 15x15, and inputs from the players are obtained through the standard input (refer to the "readXY" function below). The current state of the board and the outcome of the game are displayed on the standard output. Part III (20 bonus points): You have the opportunity to earn extra points by implementing the following features. However, these bonus points will only be awarded if you have successfully completed all the functions required for the regular points. 1. (10 points) Allow players to play against a computer by creating a computer move strategy. A straightforward way to add this feature is to define a strategy function, such as "getRandomXY" or "getSmartXY", and use it instead of the "readXY" function for the opponent. Utilize the "System.Random" module to generate a random value. For example, you can use the expression x <- randomRIO(1,15) to generate a random number between 1 and 15 (inclusive).