-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathShapes.hs
More file actions
224 lines (170 loc) · 5.84 KB
/
Copy pathShapes.hs
File metadata and controls
224 lines (170 loc) · 5.84 KB
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
-- | Types and functions for shapes. The list of all tetris pieces.
module Shapes where
import Data.List(transpose)
import Data.Maybe(isNothing)
import Test.QuickCheck
-- * Shapes
type Square = Maybe Colour
data Colour = Black | Red | Green | Yellow | Blue | Purple | Cyan | Grey
deriving (Eq,Bounded,Enum,Show)
-- | A geometric shape is represented as a list of lists of squares. Each square
-- can be empty or filled with a block of a specific colour.
data Shape = S [Row] deriving (Eq)
type Row = [Square]
rows :: Shape -> [Row]
rows (S rs) = rs
-- * Showing shapes
showShape :: Shape -> String
showShape s = unlines [showRow r | r <- rows s]
where
showRow :: Row -> String
showRow r = [showSquare s | s <- r]
showSquare Nothing = '.'
showSquare (Just Black) = '#' -- can change to '█' on linux/mac
showSquare (Just Grey) = 'g' -- can change to '▓'
showSquare (Just c) = head (show c)
instance Show Shape where
show = showShape
showList ss r = unlines (map show ss)++r
-- * The shapes used in the Tetris game
-- | All 7 tetrominoes (all combinations of connected 4 blocks),
-- see <https://en.wikipedia.org/wiki/Tetromino>
allShapes :: [Shape]
allShapes = [S (makeSquares s) | s <- shapes]
where
makeSquares = map (map colour)
colour c = lookup c [('I',Red),('J',Grey),('T',Blue),('O',Yellow),
('Z',Cyan),('L',Green),('S',Purple)]
shapes =
[["I",
"I",
"I",
"I"],
[" J",
" J",
"JJ"],
[" T",
"TT",
" T"],
["OO",
"OO"],
[" Z",
"ZZ",
"Z "],
["LL",
" L",
" L"],
["S ",
"SS",
" S"]]
-- * Some simple functions
-- ** A01
emptyShape :: (Int,Int) -> Shape
emptyShape (n,m) = S (replicate m emptyRow')
where emptyRow' = emptyRow n (Nothing)
emptyRow :: Int -> Square -> Row
emptyRow n square = replicate n square
-- ** A02
-- | The size (width and height) of a shape
shapeSize :: Shape -> (Int,Int)
shapeSize x = (shapeCol x, shapeRow x)
shapeRow :: Shape -> Int
shapeRow a = length(rows a)
shapeCol :: Shape -> Int
shapeCol a = length $ head (rows a)
-- ** A03
-- | Count how many non-empty squares a shape contains
blockCount :: Shape -> Int
blockCount a = length $ filter (/= Nothing) (concat (rows a))
{-OBS!!!!!!!!!! EXPLAIN!!!!!!!!!!
blockCount :: Shape -> Int
blockCount a = area b - (counterRow (rows a))
where b = shapeSize a
counterRow :: [Row] -> Int
counterRow [] = 0
counterRow (x:xs) = counterCol x + counterRow xs
counterCol :: Row -> Int
counterCol (x:xs) | xs == [] = 0
| x == Nothing = 1 + counterCol xs
| x /= Nothing = 0 + counterCol xs
area :: (Int,Int) -> Int
area (x,y) = x*y
-}
-- * The Shape invariant
-- ** A04
-- | Shape invariant (shapes have at least one row, at least one column,
-- and are rectangular)
prop_Shape :: Shape -> Bool
prop_Shape a = (shapeRow a >= 1) && (shapeCol a >= 1) && shapeCheck a
shapeCheck :: Shape -> Bool
shapeCheck a = (shapeCol a) * shapeRow a == length (concat (rows a))
-- * Test data generators
-- ** A05
-- | A random generator for colours
rColour :: Gen Colour
rColour = elements [Black, Red, Green, Yellow, Blue, Purple, Cyan, Grey]
instance Arbitrary Colour where
arbitrary = rColour
-- ** A06
-- | A random generator for shapes
rShape :: Gen Shape
rShape = elements allShapes
instance Arbitrary Shape where
arbitrary = rShape
-- * Transforming shapes
-- ** A07
-- | Rotate a shape 90 degrees
rotateShape :: Shape -> Shape
rotateShape a = S (transpose(rows a))
rotateShape' :: Shape -> Shape
rotateShape' a = S (transpose(reverse(rows a)))
-- ** A08
-- | shiftShape adds empty squares above and to the left of the shape
shiftShape :: (Int,Int) -> Shape -> Shape
shiftShape (x,y) shape = addCol x (addRow y shape)
addCol :: Int -> Shape -> Shape
addCol col shape = rotateShape (addRow col (rotateShape shape))
addRow :: Int -> Shape -> Shape
addRow row shape = S $ rows(empty) ++ rows shape
where empty = emptyShape (shapeCol shape,row)
{-
shiftShape :: (Int,Int) -> Shape -> Shape
shiftShape (x,y) a = S (addRow y (addCol x (rows a)))
-- Ta addRow först då addCol lägger till i varje lista
-- och den nya tillagda listan kommer sakna den
addCol :: Int -> [Row] -> [Row]
addCol x [] = undefined
addCol x (y:ys)= replicate x Nothing ++ y : addCol x ys
addRow :: Int -> [Row] -> [Row]
addRow x (y:ys) = replicate (addCol (S y)) [Nothing] ++ y ++ addRow ys
-}
-- ** A09
-- | padShape adds empty sqaure below and to the right of the shape
padShape :: (Int,Int) -> Shape -> Shape
padShape (col,row) shape | col <0 || row <0 = error "Error in padShape, negative values"
| otherwise = addRight col (addBottom row shape)
addRight :: Int -> Shape -> Shape
addRight col shape = rotateShape (addBottom col (rotateShape shape))
addBottom :: Int -> Shape -> Shape
addBottom row shape = S (rows shape ++ rows(emptyShape (shapeCol(shape),row)))
-- ** A10
-- | pad a shape to a given size
padShapeTo :: (Int,Int) -> Shape -> Shape
padShapeTo (x,y) shape = padShape ((x-a),(y-b)) shape
where (a,b) = shapeSize shape
{-
-- * Comparing and combining shapes
-- ** B01
-- | Test if two shapes overlap
overlaps :: Shape -> Shape -> Bool
s1 `overlaps` s2 = error "A11 overlaps undefined"
-- ** B02
-- | zipShapeWith, like 'zipWith' for lists
zipShapeWith :: (Square->Square->Square) -> Shape -> Shape -> Shape
zipShapeWith = error "A12 zipShapeWith undefined"
-- ** B03
-- | Combine two shapes. The two shapes should not overlap.
-- The resulting shape will be big enough to fit both shapes.
combine :: Shape -> Shape -> Shape
s1 `combine` s2 = error "A13 zipShapeWith undefined"
-}