-- Function to print the game board displayBoard :: Game -> IO displayBoard game @Game{ .... ‘displayCell’ function determines how to display each cell based on whether it is flipped
Trang 1VIETNAM GENERAL CONFEDERATION OF LABOR
TON DUC THANG UNIVERSITY
FACULTY OF INFORMATION TECHNOLOGY
FINAL PROJECT FUNCTIONAL PROGRAMMING
Instructor: ĐỖ NHƯ TÀI
Student: Nguyễn Lâm Dương Tùng 522K0007–
LÊ GIA BẢO – 522K0003
Class: 22K50201
HO CHI MINH CITY, 2023
Trang 2VIETNAM GENERAL CONFEDERATION OF LABOR
TON DUC THANG UNIVERSITY
FACULTY OF INFORMATION TECHNOLOGY
FINAL PROJECT FUNCTIONAL PROGRAMMING
Instructor: ĐỖ NHƯ TÀI
Student: Nguyễn Lâm Dương Tùng 522K0007–
LÊ GIA BẢO – 522K0003
Class: 22K50201
HO CHI MINH CITY, 2023
Trang 3I would like to extend my heartfelt gratitude to my teacher, Do Nhu Tai, for his invaluable support and guidance in completing this assignment Without his assistance, this project could not have been executed with precision and excellence I seize this moment to express my sincere appreciation for his dedication and mentorship, and i hope for his continued support in my future endeavors.
With genuine gratitude.
Duong Tung.
Trang 4COMPLETED PROJECT
AT TON DUC THANG UNVERSITY
"I hereby affirm that this is our own work and was conducted under the guidance of Dr DO NHU TAI The research contents and results in this thesis are truthful and have not been previously published in any form The data in the tables used for analysis, comments, and evaluations were collected by the author from various sources, as clearly indicated in the references section Additionally, this thesis includes some comments, evaluations, and data from other authors and organizations, all of which are cited and referenced
If any form of academic dishonesty is discovered, I take full responsibility for the content of my thesis Ton Duc Thang University is not responsible for any copyright infringement or violations caused by me during the implementation process (if any)."
TP Hồ Chí Minh, ngày 23 tháng 11 năm 2023
Tác giả (ký tên và ghi rõ họ tên)
Nguyễn Lâm Dương Tùng
Lê Gia Bảo
Trang 5PHẦN XÁC NHẬN VÀ ĐÁNH GIÁ CỦA GIẢNG VIÊN
Phần xác nhận của GV hướng dẫn
_
Tp Hồ Chí Minh, ngày tháng năm (kí và ghi họ tên)
Phần đánh giá của GV chấm bài
_
Tp Hồ Chí Minh, ngày tháng năm (kí và ghi họ tên)
Trang 6TABLE OF THE CONTENT
ACKNOWLEDGEMENT 2
1 Overview 5
1 Importing 5
2 Code Structure 6
3 Updating the board (‘update board’ and related functions) 9
4 Game Start and Initialization (‘startNewGame’ and ‘createBoard’) 9
5 Updating board after hiding (‘updateBoardAfterHide’) 10
6 Checking two flipped are matched 10
7 Entering name and showing player scores (‘handleGameEnd’) 10
8 Handling players move (‘handleMove’ and related functions) 11
9 Finalizing a Turn (‘finalizeTurn’): 11
10. Processing a Player Move (‘processMove’): 12
11. Consoling screen (‘clearScreen’) 12
12. Shuffling a list (‘shuffle’) 12
13 Spliting list into chunks and checking if the game is over 13
14 Handling the flow of the game (‘playGame’ function) 13
15. Main Menu Function (‘mainMenu’) 15
16 Displaying guide 16
17 ‘initialGame’ Data Structure 17
Trang 71 Overview
The Memory Game implementation is a Haskell console-based application designed to test and enhance memory skills Players are presented with a grid of cards, face down, and their objective is to flip pairs of matching cards The game includes features such as a scoring system, user interface, and the ability
to start new games with varying difficulty levels
1 Importing
{-# LANGUAGE RecordWildCards #-}
import System.Random
import Control.Monad ( when )
import Control.Concurrent ( threadDelay )
import Data.Char ( chr , ord )
The {-# LANGUAGE RecordWildCards #-} pragma enables the use of record wildcards, allowing us to reference fields of a record without explicitly listing them
Importing necessary modules, including ‘System.Random’ for random number generation, ‘Control.Monad’ for monadic functions,
‘Control.Concurrent’ for managing concurrency, and ‘Data.Char’ for character-related functions
2 Code Structure
The code is organized into functional components, making use of Haskell's record syntax for defining data types Key data types include ‘Game’ and
‘Scores’, representing the game state and player scores, respectively The code
leverages Haskell's strong type system to ensure clarity and maintainability
Trang 8type Coord ( Int , Int )
type FlippedCells [( = Coord , Cell )]
data Game Game =
- { boardWidth :: Int
- , boardHeight :: Int
- , numValues :: Int
- , turns :: Int
- , flipped :: FlippedCells
- , matchedPairs :: FlippedCells
- , board :: Board
- , scores :: [ Scores ]
- } deriving ( Read , Show )
data Scores Scores
- { playerName :: String
- , playerturns :: Int
- } deriving ( Read , Show )
Define type aliases for Cell, Board, Coord, and FlippedCells
Define the ‘Game’ and ‘Scores’ data types using records These represent the game state and player scores, respectively
Function to parse user input
parseInput :: String -> Maybe Coord
parseInput input =
- case input of
- - [c1, c2] c1 | `elem` [ 'A' 'Z' ], c2 `elem` [ 'A' 'Z' ] -> Just (ord c1 ord - 'A' , ord c2 ord - 'A' ) + 1
- - _ -> Nothing
The ‘parseInput’ function takes a ‘String’ representing user input and attempts
to parse it into a ‘Coord’ value
parseInput :: String -> Maybe Coord
The function signature indicates that parseInput is a function that takes a
‘String’ as input and returns a ‘Maybe Coord’ The ‘Maybe’ type is used to represent the possibility of failure in parsing
Trang 9case input of
The function uses a ‘case’ expression to pattern match on the ‘input’
string
- [c1, c2] c1 | `elem` [ 'A' 'Z' ], c2 `elem` [ 'A' 'Z' ] -> Just (ord c1 ord - 'A' , ord c2 ord - 'A' ) + 1
- - _ -> Nothing
This line checks if the ‘input’ is a list of exactly two characters ‘[c1, c2]’
and whether both characters ‘c1’ and ‘c2’ are uppercase letters
‘(['A' 'Z'])’.
If this condition is met, the expression after the arrow ‘->’ is executed
The Just constructor is used to wrap a tuple ‘(ord c1 - ord 'A' + 1, ord c2
- ord 'A' + 1)’ This tuple represents the coordinates in the form of ‘(row, column)’ The ‘ord’ function is used to obtain the ASCII value of a
character, and by subtracting the ASCII value of 'A' and adding 1, it converts the uppercase letter into a corresponding integer (1 for 'A', 2 for 'B', and so on)
So, for example, if ‘input’ is "AB", the resulting Coord would be ‘Just (1, 2)’
Function to print the game board
displayBoard :: Game -> IO ()
displayBoard game @Game{ } = do
- clearScreen
- putStrLn $ "<Memory Game>"
- putStrLn $ "Your current turn is: " ++ show turns
- putStrLn ""
- putStrLn $ " " ++ concat [ " " ++ [c] c | <- take boardWidth
[ 'A' 'Z' ]]
- mapM_ (putStrLn displayRow flipped matchedPairs) (zip [ 1 ] (createBoard game))
‘displayBoard’ function prints the current state of the game board It
uses ‘clearScreen’ to clear the console, then displays information such as the game title, current turn, and a grid representing the board
Function to draw a single row of the game board
displayRow :: FlippedCells -> FlippedCells -> (Int , [ Cell ]) -> String displayRow flippedCells matchedPairs (i, row) =
Trang 10zip [ 1 ] row]
displayCell :: Coord -> Cell -> FlippedCells -> FlippedCells -> String displayCell coord (i, j) c flippedCells matchedPairs @
- coord | `elem` map fst flippedCells || coord `elem` map fst
matchedPairs c = : " "
- otherwise | = "* "
‘displayRow’ function takes a row of the board and constructs a string
representation, including row labels and cell values It calls ‘displayCell’
for each cell in the row
‘displayCell’ function determines how to display each cell based on
whether it is flipped or part of a matched pair
functions)
Function to update the board with matched pairs
updateBoard :: Board -> FlippedCells -> Board
updateBoard board matchedPairs foldr (updateRow matchedPairs) board = matchedPairs
Function to update a row with a matched pair
updateRow :: FlippedCells -> (Coord , Cell ) -> Board -> Board
updateRow matchedPairs ((row, col), val) board =
- take (row - 1 ) board ++ [updateCell col val (board !! (row - 1 ))] ++ drop row board
Function to update a cell in a row
updateCell :: Int -> Cell -> [Cell] -> [Cell]
updateCell col val row take (col = - 1 ) row ++ [val] ++ drop col row
‘updateBoard’ updates the game board by applying ‘updateRow’ to each matched pair
‘updateRow’ updates a specific row in the board with a matched pair.
‘updateCell’ updates a specific cell in a row.
and ‘createBoard’ )
Trang 11 ‘startNewGame’ initializes a new game by resetting flipped cells and
turns, then displays the initial game board
Function to start a new game
startNewGame :: Game -> IO ()
startNewGame game = do
- let newGame game { = flipped [] , turns }
- displayBoard newGame
- playGame newGame
Function to initialize the game board
createBoard :: Game -> Board
createBoard game =
- let values take (numValues game = `div` ) (cycle [ 2 'A' 'Z' ])
- - - shuffledValues shuffle (values = ++ values) (mkStdGen 42 )
- in chunksOf (boardWidth game) shuffledValues
‘createBoard’ generates a new game board with shuffled values.
(‘updateBoardAfterHide’)
Function to update the board after hiding unmatched cells
updateBoardAfterHide :: Game -> Game
updateBoardAfterHide game =
- let boardAfterHide updateBoard (createBoard game) (flipped game) =
- in game { flipped = [] , board boardAfterHide } =
‘updateBoardAfterHide’ updates the board after hiding unmatched
cells
6 Checking two flipped are matched
Function to check if two flipped cells are matched
isMatch :: Game -> Bool
isMatch game =
- case flipped game of
Trang 12 ‘isMatch’ checks if the currently flipped cells form a matched pair.
7 Entering name and showing player scores
(‘handleGameEnd’)
handleGameEnd :: Game -> IO ()
handleGameEnd game = do
- putStrLn "Enter your name: "
- playerName <- getLine
- let playerScores Scores playerName (turns game) =
- putStrLn $ "Player Score: " ++ show (playerturns playerScores)
- let updatedScores playerScores scores game - = : Append the new score to the existing list
- mainMenu updatedScores
‘handleGameEnd’ prompts the user to enter their name, records their
score, and proceeds to the main menu
functions)
The ‘handleMove’ function processes user input, validates it, and updates the game state
It calls ‘finalizeTurn’ to complete a turn and check for matching pairs Function to handle a player move
handleMove :: Maybe Coord -> Game -> Game
handleMove (Just coord) game
- length (flipped game) | == finalizeTurn (processMove coord game)
- otherwise game { | = flipped (coord, getValueAtPosition coord (createBoard game)) flipped game } :
handleMove Nothing game game =
9 Finalizing a Turn (‘finalizeTurn’):
The ‘finalizeTurn’ function is responsible for determining the outcome
of a turn
If the flipped cells form a matching pair, it updates the game state accordingly
Trang 13finalizeTurn game =
- case flipped game of
- - [pair1 (coord1, val1), pair2 (coord2, val2)] @ @ ->
- - - if val1 == val2
- - - - then game { matchedPairs pair1 pair2 matchedPairs game, : : turns turns game = + 1 , flipped = [] }
- - - - else game { turns turns game = + 1 }
- - _ -> game
10.Processing a Player Move (‘processMove’):
The ‘processMove’ function updates the game state based on a player's move
It checks whether the selected cell has already been flipped and updates the ‘flipped’ list accordingly.
Function to process a player move
processMove :: Coord -> Game -> Game
processMove coord game =
- case lookup coord (flipped game) of
- - Just _ -> game
- - Nothing ->
- - - let cellValue getValueAtPosition coord (createBoard game) =
- - - flipped' = if length (flipped game) == then [(coord, cellValue)] else (coord, cellValue) flipped game :
- - - in game { flipped flipped' } =
getValueAtPosition :: Coord -> Board -> Cell
getValueAtPosition (row, col) board (board = !! (row - 1 )) !! (col ) - 1
Function to clear the console screen
clearScreen :: IO ()
clearScreen putStrLn = " \ESC [2J"
‘clearScreen’ makes the result that when you finished all the cells, it will
be turned back to the ‘handleGameEnd’ for you to enter your name and show your player scores
12.Shuffling a list ( ‘shu ffle’ )
Function to shuffle a list
shuffle :: [ a ] -> StdGen -> [ a
shuffle [] _ = []
Trang 14- - - (left, right) splitAt index xs =
- in head right shuffle (left : ++ tail right) newGen
The `shuffle` function randomly permutes a given list of elements using a provided random generator If the input list is empty, it returns an empty list Otherwise, it selects a random element, moves it to the end, and recursively shuffles the remaining elements until the entire list is shuffled The result is a new list with elements in a random order
13.Spliting list into chunks and checking if the game
is over
Function to split a list into chunks
chunksOf :: Int -> [ ] a -> [[ a ]]
chunksOf _ [] = []
chunksOf n xs take n xs chunksOf n (drop n xs) = :
Purpose: This function takes an integer ‘n’ and a list ‘xs’ and divides
‘xs’ into chunks of size ‘n’
Use Case: It's commonly used to partition a list, such as dividing a list of
values into rows for a game board
Function to check if the game is over
isGameCompleted :: Game -> Bool
isGameCompleted game length (matchedPairs game) = == numValues game
Purpose: This function checks if the game is completed by comparing
the length of matched pairs with the total number of values in the game
Use Case: It's used to determine if all pairs have been matched, indicating
that the game is over
14.Handling the flow of the game (‘playGame’
function)
playGame :: Game -> IO ()
playGame game
- isGameCompleted game handleGameEnd game | =