1
- module MyModule where
2
-
3
- import Data.List
4
- import Data.Char
5
-
6
- -- Cordinates Of Shot:
7
-
8
- -- {
9
- -- "coord": ["D", "7"],
10
- -- "result": "HIT",
11
- -- "prev": {
12
- -- "coord": ["A", "10"],
13
- -- "result": null,
14
- -- "prev": null
15
- -- }
16
- -- }
17
-
18
- -- {"coord":["D","7"],"result":"HIT","prev":{"coord":["A","10"],"result":null,"prev":null}}
19
-
20
- -- data MyBool a = MyFalse | MyTrue a
21
- -- deriving Show
22
-
23
- data MoveMsg = DicMsg [(String , MoveMsg )]
24
- | CordList [MoveMsg ]
25
- | CordValue String
26
- | Exist (Maybe MoveMsg )
27
- deriving Show
28
-
29
-
30
- -- Some examples of parsing data
31
- firstMove :: MoveMsg
32
- firstMove = DicMsg [
33
- (" coord" , CordList [CordValue " A" , CordValue " 10" ]),
34
- (" result" , Exist Nothing ),
35
- (" prev" , Exist Nothing )
36
- ]
37
-
38
- secondMove :: MoveMsg
39
- secondMove = DicMsg [
40
- (" coord" , CordList [CordValue " D" , CordValue " 7" ]),
41
- (" result" , CordValue " HIT" ),
42
- (" prev" , firstMove)
43
- ]
1
+ module BattleShip where
44
2
3
+ import Parser
4
+ import GameData
5
+
6
+ printSimple :: String -> String
7
+ printSimple msg = msg
8
+
9
+ -- Resolves errors
10
+ resolveEithers :: Either String a -> a
11
+ resolveEithers (Right msg) = msg
12
+
13
+ getPlayersShots :: Either String MoveMsg -> [MoveMsg ]
14
+ getPlayersShots msg = playerMoves " coord" $ (resolveEithers msg)
15
+
16
+ resolveError :: Either String a -> String
17
+ resolveError (Left msg) = msg
18
+
19
+ -- Checks if we reached the end of data structure recursively
20
+ isTheEnd :: MoveMsg -> Bool
21
+ isTheEnd (DicMsg msg) = False
22
+ isTheEnd (CordValue " null" ) = True
23
+
24
+ ------- Checking if the game end symbol is the last one ------
25
+
26
+ checkEmptyCoord :: MoveMsg -> Bool
27
+ checkEmptyCoord (CordList [] ) = True
28
+ checkEmptyCoord (CordList _) = False
29
+
30
+ checkGameEndSymbol :: [MoveMsg ] -> Bool
31
+ checkGameEndSymbol [] = False
32
+ checkGameEndSymbol (head : tail ) | checkGameEndSymbol tail == True = True
33
+ | checkEmptyCoord head == True = True
34
+ | otherwise = False
35
+
36
+ checkIfFirstEndSymb :: [MoveMsg ] -> Bool
37
+ checkIfFirstEndSymb (head : tail ) | checkEmptyCoord head == True && checkGameEndSymbol tail == False = True
38
+ | checkEmptyCoord head == False && checkGameEndSymbol tail == False = True
39
+ | otherwise = False
40
+
41
+ -------------------------------------------------------------
42
+
43
+ getCoorValue :: MoveMsg -> String
44
+ getCoorValue (CordList [CordValue x, CordValue y]) = x ++ y
45
+ getCoorValue (CordList [] ) = " "
46
+
47
+ firstPlayerCoord :: ([MoveMsg ], [MoveMsg ]) -> [MoveMsg ]
48
+ firstPlayerCoord (msg, _) = msg
49
+
50
+ secondPlayerCoord :: ([MoveMsg ], [MoveMsg ]) -> [MoveMsg ]
51
+ secondPlayerCoord (_, msg) = msg
52
+
53
+ -- Based on key, returns value of MoveMsg data structure
54
+ getKeyValue :: String -> MoveMsg -> (String , MoveMsg )
55
+ getKeyValue (" coord" ) (DicMsg msg) = msg!! 0
56
+ getKeyValue (" result" ) (DicMsg msg) = msg!! 1
57
+ getKeyValue (" prev" ) (DicMsg msg) = msg!! 2
58
+
59
+
60
+ -- Can return either all the cordinates or shoot results of both players combined
61
+ -- Recursively iterrates through MoveMsg and returns player moves
62
+ playerMoves :: String -> MoveMsg -> [MoveMsg ]
63
+ playerMoves key msg = accCoordinates msg
64
+ where
65
+ accCoordinates :: MoveMsg -> [MoveMsg ]
66
+ accCoordinates msg =
67
+ let
68
+ (_, nextMsg) = getKeyValue " prev" msg
69
+ (_, result) = getKeyValue key msg
70
+ in
71
+ if isTheEnd nextMsg then result: []
72
+ else result: (accCoordinates nextMsg)
73
+
74
+ -- Returns shooting coordinates of both players separated
75
+ getShootingCoordinates :: [MoveMsg ] -> [MoveMsg ] -> [MoveMsg ] -> ([MoveMsg ], [MoveMsg ])
76
+ getShootingCoordinates [] accFirsPlayer accSecondPlayer = (accFirsPlayer, accSecondPlayer)
77
+ getShootingCoordinates (head : tail ) accFirsPlayer accSecondPlayer
78
+ | length tail `mod` 2 == 0 = (getShootingCoordinates tail (head : accFirsPlayer) accSecondPlayer)
79
+ | otherwise = (getShootingCoordinates tail accFirsPlayer (head : accSecondPlayer))
80
+
81
+
82
+ countPlayerCoordinates :: Either String ([MoveMsg ], [MoveMsg ]) -> Either String (Int , Int )
83
+ countPlayerCoordinates shootingCoords =
84
+ let
85
+ (firstPlayer, secondPlayer) | isRight(shootingCoords) == " True" = resolveEithers shootingCoords
86
+ in
87
+ if isRight(shootingCoords) == " True"
88
+ then Right (100 - (length firstPlayer), (100 - (length secondPlayer)))
89
+ else Left (resolveError shootingCoords)
90
+
91
+ -- Combines coordinate, so that later it could be easy to compare if before A 1 seperated => A1
92
+ combineCoordinate :: [MoveMsg ] -> [String ]
93
+ combineCoordinate [] = []
94
+ combineCoordinate (head : tail ) = (getCoorValue head ): (combineCoordinate tail )
95
+
96
+
97
+ -- Every time it comes back checks if True, if true, doesn't check anymore
98
+ -- Checks for multiple shots, if true wrong!
99
+ checkMultipleShots :: [MoveMsg ] -> Bool
100
+ checkMultipleShots [] = False
101
+ checkMultipleShots (head : tail ) | checkMultipleShots tail == True = True
102
+ | otherwise = ((getCoorValue head ) `elem` (combineCoordinate tail ))
103
+
104
+
105
+ checkGameLogic :: Either String ([MoveMsg ], [MoveMsg ]) -> Either String MoveMsg -> (Bool , Bool , Bool )
106
+ checkGameLogic shootingCoords parsedMessage =
107
+ let
108
+ -- Checking for duplicate shots for first player
109
+ firstPlayerDup = checkMultipleShots (firstPlayerCoord (resolveEithers shootingCoords))
110
+
111
+ -- Checking for duplicate shots for second player
112
+ secondPlayerDup = checkMultipleShots (secondPlayerCoord (resolveEithers shootingCoords))
113
+
114
+ -- checking for end symbol, must be at the end!
115
+ endSymbolCorrect = (checkGameEndSymbol (getPlayersShots parsedMessage) == True ) && (checkIfFirstEndSymb (getPlayersShots parsedMessage) == False )
116
+ in
117
+ (firstPlayerDup, secondPlayerDup, endSymbolCorrect)
118
+
119
+
120
+ resolveCheckGameLogic :: (Bool , Bool , Bool ) -> Either String String
121
+ resolveCheckGameLogic (True , _, _) = Left (" Game end symbol is in wrong position! Must be at the end" )
122
+ resolveCheckGameLogic (_, True , _) = Left (" First Player duplicate shots!" )
123
+ resolveCheckGameLogic (_, _, True ) = Left (" Second Player duplicate shots!" )
124
+ resolveCheckGameLogic (_, _, _) = Right (" All Logic Valid" )
125
+
126
+
127
+ -- Finds number of moves available for a players
128
+ available :: String -> Either String (Int , Int )
129
+ available msg =
130
+ let
131
+ parsedMessage = parseMessage msg
132
+ shootingCoords | isRight(parsedMessage) == " False" = Left (resolveError parsedMessage)
133
+ | otherwise = Right (getShootingCoordinates (getPlayersShots parsedMessage) [] [] )
134
+
135
+ (firstPlayerDup, secondPlayerDup, endSymbolCorrect) | isRight(shootingCoords) == " True" = checkGameLogic shootingCoords parsedMessage
136
+ | otherwise = (False , False , False )
137
+ logicError = resolveCheckGameLogic(endSymbolCorrect, firstPlayerDup, secondPlayerDup)
138
+ in
139
+ -- Checks for all the logical error in message
140
+ if isRight(logicError) == " False"
141
+ then Left (resolveError logicError)
142
+ -- Checks if JSON is correct format
143
+ else if isRight(shootingCoords) == " False"
144
+ then Left (resolveError shootingCoords)
145
+ -- If finds out that end symbol exist, end game symbol [], no available moves!
146
+ else if checkGameEndSymbol (getPlayersShots parsedMessage) == True
147
+ then Right (0 ,0 )
148
+ -- If everything is okey, main scenario, return available moves for both players
149
+ else countPlayerCoordinates shootingCoords
0 commit comments