-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathApocTools.hs
287 lines (241 loc) · 10.9 KB
/
ApocTools.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
{- |
Module : ApocTools
Description : Required definitions for the CPSC 449 W2016 Haskell Apocalypse assignment.
Copyright : Copyright 2016, Rob Kremer (rkremer@ucalgary.ca), University of Calgary.
License : Permission to use, copy, modify, distribute and sell this software
and its documentation for any purpose is hereby granted without fee, provided
that the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation. The University of Calgary makes no representations about the
suitability of this software for any purpose. It is provided "as is" without
express or implied warranty.
Maintainer : rkremer@ucalgary.ca
Stability : experimental
Portability : ghc 7.10.2 - 7.10.3
This module is used for CPSC 449 for the Apocalypse assignment.
You MUST use this file as part of your assignment to deal with boards,
cells, etc. This may be tested by linking your assignment against a modified
version of this file.
Do not modify this file.
-}
-- The following pragmas are only necessary for the Read class instance of 'GameBoard'
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
module ApocTools (
-- * Cell (A "square" on the board)
Cell(WK,WP,BK,BP,E),
cell2Char,
char2Cell,
putCell,
-- * The board itself
Board,
initBoard,
putBoard,
board2Str,
getFromBoard,
-- * Players and pieces
Player(Black,White),
Piece(BlackKnight,BlackPawn,WhiteKnight,WhitePawn),
pieceOf,
playerOf,
-- * Move descriptions
Played(Played,Passed,Goofed,{-GoofedFromOther,GoofedFromInvalid,GoofedFromEmpty,-}Init,UpgradedPawn2Knight,PlacedPawn,BadPlacedPawn,NullPlacedPawn,None),
PlayType(Normal,PawnPlacement),
-- * The game state
GameState(GameState,blackPlay,blackPen,whitePlay,whitePen,theBoard),
-- * The interface for a strategy
Chooser
) where
import Data.Char (isSpace)
---Cells-----------------------------------------------------------
---Cells are the state of a cell: contains White and Black Pawns and Knights or is Empty
{- | The possible contents of a cell: 'WK', 'BK', 'WP', 'BP', and Empty ('E').
We do NOT include "deriving (Show)" here because we use the "instance Show ..."
so we can customize it's display from (say) "Cell E" to "_" according to the
'cell2Char' function.
-}
data Cell = WK -- ^ White knight
| WP -- ^ White pawn
| BK -- ^ Black knight
| BP -- ^ Black pawn
| E -- ^ Empty
deriving (Eq) --deriving (Show)
-- | Customized print form of Cell
instance {-# OVERLAPS #-}
Show (Cell) where
show c = [cell2Char c]
-- | Converts a 'Cell' to a displayable Char
cell2Char :: Cell -> Char
cell2Char WK = 'X'
cell2Char WP = '/'
cell2Char BK = '#'
cell2Char BP = '+'
cell2Char E = '_'
-- | Converts a 'Char' to a 'Cell'
char2Cell :: Char -> Cell
char2Cell 'X' = WK
char2Cell '/' = WP
char2Cell '#' = BK
char2Cell '+' = BP
char2Cell '_' = E
{- | IO function to print a 'Cell' in a 'Board', which is printed as '|' and the char
representation of the 'Cell'.
-}
putCell :: Cell -> IO()
putCell c = do putChar '|'
putChar (cell2Char c)
---Boards--------------------------------------------------------
---A board is just a 2d (8x8) list of Cells
-- | The representation of the 'Board' (which is 5x5).
type Board = [[Cell]]
-- | Customize the read function for 'Board' to coorespond with the show function.
instance {-# OVERLAPS #-}
Read Board where
readsPrec _ r = [(result, remainder)]
where
allLines = lines (dropWhile (isSpace) r)
lss = take 6 allLines
ls = tail lss
rows = map (filter (/='|')) ls
result = map (map char2Cell) rows
remainder = unlines $ drop 6 allLines
-- | Customized Show for 'Board' so it's more human-readable
instance {-# OVERLAPS #-} Show Board where show b = board2Str b
-- | The intial state of the board
initBoard :: GameState
initBoard = GameState Init 0 Init 0
[ [WK, WP, WP, WP, WK],
[WP, E , E , E , WP],
[E , E , E , E , E ],
[BP, E , E , E , BP],
[BK, BP, BP, BP, BK] ]
-- | Print out a row in a 'Board' in the for "|?|?|?|?|?|".
putRow :: [Cell] -> IO()
putRow r = do mapM_ putCell r
putStr "|\n"
-- | return a row in a 'Board' in the for "|?|?|?|?|?|".
row2Str :: [Cell] -> String
row2Str [] = []
row2Str (x:xs) = "|" ++ show x ++ row2Str xs
{- | IO function to print out a 'Board' in the form of:
@
_ _ _ _ _
|?|?|?|?|?|
|?|?|?|?|?|
|?|?|?|?|?|
|?|?|?|?|?|
|?|?|?|?|?|
@
Where the question marks are replaced with the appropriate 'Cell' character (see
'cell2Char').
-}
putBoard :: [[Cell]] -> IO()
putBoard a = do
putStr " _ _ _ _ _\n"
mapM_ putRow a
putStr ""
-- | Return a string representation of a 'Board' in the same form as 'putBoard', above.
board2Str :: [[Cell]] -> String
board2Str b = " _ _ _ _ _\n" ++ board2Str' b
-- | Helper function for 'board2Str'.
board2Str' :: [[Cell]] -> String
board2Str' [] = []
board2Str' (x:xs) = row2Str x ++ "|\n" ++ board2Str' xs
-- | Return the 'Cell' at a point from a 'Board'.
getFromBoard :: [[a]] -> (Int,Int) -> a
getFromBoard xs pt = xs !! snd pt !! fst pt
---Game state-------------------------------------------------------
-- | Represents a Player (Black or White).
data Player = Black | White deriving (Eq, Show, Read)
-- | Represents a Piece, which is slightly different from 'Cell' as a player can't be empty.
data Piece = BlackKnight | BlackPawn | WhiteKnight | WhitePawn deriving (Eq, Show, Read)
-- | Given a 'Cell', return the corresponding 'Piece'.
pieceOf :: Cell -> Piece
pieceOf BK = BlackKnight
pieceOf BP = BlackPawn
pieceOf WK = WhiteKnight
pieceOf WP = WhitePawn
-- | Given a 'Piece', return the corresponding 'Player'.
playerOf :: Piece -> Player
playerOf BlackKnight = Black
playerOf BlackPawn = Black
playerOf WhiteKnight = White
playerOf WhitePawn = White
-- | Represents the type of move played in a 'GameState'.
data Played = Played ((Int, Int), (Int, Int)) -- ^ A "normal" move.
| Passed -- ^ A (legal) pass.
| Goofed ((Int, Int), (Int, Int)) -- ^ An illegal move, penalty applied.
-- | GoofedFromEmpty ((Int, Int), (Int, Int)) -- ^ An illegal move, penalty applied.
-- | GoofedFromOther ((Int, Int), (Int, Int)) -- ^ An illegal move, penalty applied.
-- | GoofedFromInvalid ((Int, Int), (Int, Int)) -- ^ An illegal move, penalty applied.
| Init -- ^ No one has moved yet.
| UpgradedPawn2Knight (Int,Int) -- ^ A pawn reached the other side when <2 knights.
| PlacedPawn ((Int, Int), (Int, Int)) -- ^ A pawn that's been placed in any empty space after having reached the far end of the board.
| BadPlacedPawn ((Int, Int), (Int, Int)) -- ^ A strategy has attempted to do a pawn placement, but chose an invalid location.
| NullPlacedPawn -- ^ A strategy has attempted to do a pawn placement, but returned Nothing
| None -- ^ the legitimate 'pass' when the other player does a PlacedPawn
deriving (Eq, Show, Read)
{- | Represents the current state of the game. Contains:
* what each Player Played
* their penalties
* the state of the board.
-}
data GameState = GameState { blackPlay :: Played -- ^ The black player's play type
, blackPen :: Int -- ^ The black player's penalty
, whitePlay :: Played -- ^ The white player's play type
, whitePen :: Int -- ^ The white player's penalty
, theBoard :: Board -- ^ The actual board.
} deriving (Eq)
-- | Customize the print form of 'GameState'.
instance Show (GameState) where
show g = ">>>\n"
++ "(" ++ show (blackPlay g) ++ ", " ++ show (blackPen g) ++ ")\n"
++ "(" ++ show (whitePlay g) ++ ", " ++ show (whitePen g) ++ ")\n"
++ show (theBoard g)
-- | Customize the read function for 'Board' to coorespond with the show function.
instance Read GameState where
readsPrec _ r =
case readsPrec 0 (dropWhile (isSpace) (scanPastFlag r)) :: [((Played,Int),String)] of
(((bPlay,bPen),rest1):_) ->
case readsPrec 0 (dropWhile (isSpace) (tail rest1)) :: [((Played,Int),String)] of
(((wPlay,wPen),rest3):_) ->
case readsPrec 0 (tail rest3) :: [(Board,String)] of
((board,rest5):_) ->
[(GameState bPlay bPen wPlay wPen board, rest5)]
e3 -> []
e2 -> []
e1 -> []
scanPastFlag :: String -> String
scanPastFlag [] = ""
scanPastFlag ('>':'>':'>':'\n':cs) = cs
scanPastFlag (c:cs) = scanPastFlag cs
-- | The text version of a state for testing purposes.
testState = "some garbage\n\
\>>>\n\
\(PlacedPawn ((0,0),(3,2)), 1)\n\
\(None, 0)\n\
\ _ _ _ _ _\n\
\|_|_|_|_|X|\n\
\|_|_|_|_|/|\n\
\|_|_|_|+|_|\n\
\|+|_|_|_|+|\n\
\|#|_|+|+|#|"
---Strategies-------------------------------------------------------
{- | This type is used by 'Chooser' to tell the 'Chooser' strategy to generate either a
'Normal' move (source and destination) or a 'PawnPlacement' move (just a destination).
-}
data PlayType = Normal -- ^ The 'Chooser' should return a list of 2 (x,y) coordinates.
| PawnPlacement -- ^ The 'Chooser' should return a singleton list of (x,y) coordinates.
deriving Eq
{- | This is the type for all player functions. A player strategy function takes
1. a 'GameState' on which to base it's decision
2. 'PlayType', which may be 'Normal' to indicate that it must return both a source
and destination coordinate in the form [(a,b),(c,d)] where the letters must be
integers; or it may be 'PawnPlacement' to indicate that it must return just a
singlton list containing an empty cell in the 'Board'.
3. 'Player' which indicates that the strategy must work from the perspective of
'Black' or 'White'.
The return value can will be Just [(Int,Int)] with either one or two elements (see
point 2 above), or it may return Nothing to indicate a "pass".
-}
type Chooser = GameState -> PlayType -> Player -> IO (Maybe [(Int,Int)])