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
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
|
module Gobang where
import Xlib
import Utilities
import Redraw
import Weights
getXInfo :: String -> IO XInfo
getXInfo host =
xOpenDisplay host `thenIO` \ display ->
let (screen:_) = xDisplayRoots display
fg_pixel = xScreenBlackPixel screen
bg_pixel = xScreenWhitePixel screen
root = xScreenRoot screen
in
xCreateWindow root
(XRect 0 0 900 600)
[XWinBackground bg_pixel,
XWinEventMask (XEventMask [XButtonPress,
XKeyPress,
XExposure])]
`thenIO` \ window ->
xSetWmName window "Gobang" `thenIO` \() ->
xMapWindow window `thenIO` \() ->
xOpenFont display "10x20" `thenIO` \ playerfont ->
xOpenFont display "6x13" `thenIO` \ genericfont ->
xCreateGcontext (XDrawWindow window)
[XGCBackground bg_pixel,
XGCForeground fg_pixel] `thenIO` \ gcontext ->
xCreateGcontext (XDrawWindow window)
[XGCBackground fg_pixel,
XGCForeground bg_pixel,
XGCFont genericfont] `thenIO` \ gcontext2 ->
xCreateGcontext (XDrawWindow window)
[XGCBackground bg_pixel,
XGCForeground fg_pixel,
XGCFont playerfont] `thenIO` \ gcontextp ->
returnIO (XInfo display window gcontext gcontext2 gcontextp)
demo = main
main = getEnv "DISPLAY" exit $ \ host ->
xHandleError (\(XError msg) -> appendChan stdout msg exit done) $
gobang host
gobang :: String -> IO ()
gobang host =
getXInfo host `thenIO` \ xinfo ->
xMArrayCreate [1..361] `thenIO` \ board ->
xMArrayCreate [1..361] `thenIO` \ weight1 ->
xMArrayCreate [1..361] `thenIO` \ weight2 ->
xMArrayCreate [1..722] `thenIO` \ steps ->
xMArrayCreate [""] `thenIO` \ player1 ->
xMArrayCreate [""] `thenIO` \ player2 ->
xMArrayCreate [1..4] `thenIO` \ time ->
xMArrayCreate [1] `thenIO` \ numbersteps ->
xMArrayCreate [""] `thenIO` \ promptString ->
xMArrayCreate [1] `thenIO` \ next_player ->
let state = GameState player1 player2 board steps weight1 weight2 time
numbersteps promptString next_player
in
initGame xinfo state `thenIO` \ _ ->
promptPlayers xinfo state `thenIO` \ _ ->
playGame xinfo state
promptPlayers xinfo state =
let (XInfo display window gcontext gcontext2 gcontextp) = xinfo
(GameState player1 player2 board steps weight1 weight2 time
numbersteps promptString next_player) = state
in
promptFor "player 1:" xinfo state `thenIO` \ player1_name ->
xDrawGlyphs (XDrawWindow window) gcontextp (XPoint 710 65) player1_name
`thenIO` \ _ ->
xMArrayUpdate player1 0 player1_name `thenIO` \ _ ->
promptFor "player 2:" xinfo state `thenIO` \ player2_name ->
xDrawGlyphs (XDrawWindow window) gcontextp (XPoint 710 205) player2_name
`thenIO` \ _ ->
xMArrayUpdate player2 0 player2_name `thenIO` \ _ ->
clearCmd xinfo state
initGame :: XInfo -> GameState -> IO ()
initGame xinfo
state@(GameState player1 player2 board steps weight1 weight2 time
numbersteps promptString next_player) =
getTime `thenIO` \ curtime ->
initArray time 0 2 0 `thenIO` \() ->
initArray time 2 4 curtime `thenIO` \() ->
initArray numbersteps 0 1 0 `thenIO` \() ->
initArray board 0 361 0 `thenIO` \() ->
initArray weight1 0 361 0 `thenIO` \() ->
initArray weight2 0 361 0 `thenIO` \ () ->
initArray next_player 0 1 1 `thenIO` \ () ->
clearCmd xinfo state `thenIO` \ () ->
redraw xinfo state
handleButton :: XPoint -> XInfo -> GameState -> GameCont -> IO ()
handleButton (XPoint x y)
xinfo
state@(GameState player1 player2 board steps weight1 weight2 time
numbersteps promptString next_player)
cont
| buttonPress 700 330 x y = initArray player1 0 1 "" `thenIO` \ _ ->
initArray player2 0 1 "" `thenIO` \ _ ->
initGame xinfo state `thenIO` \ _ ->
promptPlayers xinfo state `thenIO` \ _ ->
playGame xinfo state
| buttonPress 700 360 x y = initGame xinfo state `thenIO` \ _ ->
playGame xinfo state
| buttonPress 700 390 x y = undoGame xinfo state cont
| buttonPress 700 420 x y = loadGame xinfo state cont
| buttonPress 700 450 x y = saveGame xinfo state `thenIO` \ () ->
cont xinfo state
| buttonPress 700 480 x y = quitGame xinfo state cont
| ishelp x y = helpGame xinfo state `thenIO` \ () ->
cont xinfo state
| otherwise = cont xinfo state
when :: Bool -> IO () -> IO ()
when cond action = if cond then action else returnIO ()
undoGame xinfo@(XInfo display window gcontext gcontext2 gcontextp)
state@(GameState player1 player2 board steps weight1 weight2 time
numbersteps promptString next_player)
cont =
xMArrayLookup next_player 0 `thenIO` \ next_p ->
xMArrayLookup player1 0 `thenIO` \ name1 ->
xMArrayLookup player2 0 `thenIO` \ name2 ->
let undoStep n =
xMArrayLookup steps (2*n) `thenIO` \ x ->
xMArrayLookup steps (2*n+1) `thenIO` \ y ->
xMArrayUpdate board ((x-1)*19 + y-1) 0 `thenIO` \ _ ->
(if (name1 == "computer" || name2 == "computer")
then draw_unit board weight1 weight2 x y
else returnIO ()) `thenIO` \ _ ->
xDrawRectangle (XDrawWindow window) gcontext2
(XRect (x*30-15) (y*30-15) 30 30) True
`thenIO` \() ->
-- drawBoard xinfo `thenIO` \ _ ->
-- drawPieces 1 1 board xinfo `thenIO` \ _ ->
let x30 = x * 30
y30 = y * 30
c = XPoint x30 y30
w = XPoint (x30-15) y30
e = XPoint (x30+15) y30
no = XPoint x30 (y30-15)
s = XPoint x30 (y30+15)
m = XArc (x30-3) (y30-3) 6 6 (-1.0) 6.283
in
when (x > 1) (xDrawLine (XDrawWindow window) gcontext w c)
`thenIO` \ _ ->
when (x < 19) (xDrawLine (XDrawWindow window) gcontext c e)
`thenIO` \ _ ->
when (y > 1) (xDrawLine (XDrawWindow window) gcontext no c)
`thenIO` \ _ ->
when (y < 19) (xDrawLine (XDrawWindow window) gcontext c s)
`thenIO` \ _ ->
when ((x `elem` [4,10,16]) && (y `elem` [4,10,16]))
(xDrawArc (XDrawWindow window) gcontext m True)
`thenIO` \ _ ->
xDisplayForceOutput display `thenIO` \ _ ->
xMArrayUpdate numbersteps 0 n `thenIO` \ _ ->
xMArrayLookup next_player 0 `thenIO` \ next_p ->
xMArrayUpdate next_player 0 (if next_p == 1 then 2 else 1)
cur_name = if next_p == 1 then name1 else name2
last_name = if next_p == 1 then name2 else name1
in
xMArrayLookup numbersteps 0 `thenIO` \ n ->
if n==0 then drawCmd "No more steps to undo!" xinfo state `thenIO` \ _ ->
cont xinfo state
else
if cur_name == "computer" then cont xinfo state
else
(undoStep (n-1) `thenIO` \_ ->
if (last_name == "computer" && n /= 1) then undoStep (n-2)
else
returnIO ()) `thenIO` \ _ ->
playGame xinfo state
promptFile xinfo state cont =
promptFor "File name:" xinfo state `thenIO` \ name ->
readFile name
(\ _ -> drawCmd ("Can't read file:" ++ name) xinfo state
`thenIO` \ _ ->
cont XNull)
(\ content -> cont (XSome content))
loadGame xinfo state cont =
promptFile xinfo state $ \ file ->
case file of
XNull -> cont xinfo state
XSome file_content ->
readGameState file_content `thenIO` \ new_state ->
let (GameState _ _ _ _ _ _ time _ _ _) = new_state
in
getTime `thenIO` \ curtime ->
initArray time 2 4 curtime `thenIO` \() ->
redraw xinfo new_state `thenIO` \ _ ->
playGame xinfo new_state
saveGame :: XInfo -> GameState -> IO ()
saveGame xinfo state =
promptFor "File name:" xinfo state `thenIO` \ name ->
showGameState state `thenIO` \ str ->
writeFile name str
(\ _ -> drawCmd ("Can't write file: " ++ name) xinfo state)
done
quitGame :: XInfo -> GameState -> GameCont -> IO ()
quitGame xinfo state cont =
let (XInfo display window gcontext gcontext2 gcontextp) = xinfo
in
promptFor "Are you sure? (y/n)" xinfo state `thenIO` \ reps ->
if (reps == "y" || reps == "Y") then xCloseDisplay display
else clearCmd xinfo state `thenIO` \ _ ->
cont xinfo state
playGame :: XInfo -> GameState -> IO ()
playGame xinfo state =
let
(XInfo display window gcontext gcontext2 gcontextp) = xinfo
(GameState player1 player2 board steps weight1 weight2 time
numbersteps promptString next_player) = state
in
xMArrayLookup numbersteps 0 `thenIO` \ x ->
(\cont -> if x == 361
then drawCmd "It's a tie!" xinfo state `thenIO` \ _ ->
let loop xinfo state = waitButton xinfo state (\ _ -> loop)
in loop xinfo state
else cont) $
xMArrayLookup next_player 0 `thenIO` \ next_player_num ->
getTime `thenIO` \ curtime ->
xMArrayLookup time 0 `thenIO` \ lstm0 ->
xMArrayLookup time 1 `thenIO` \ lstm1 ->
xMArrayLookup time 2 `thenIO` \ lstm2 ->
xMArrayLookup time 3 `thenIO` \ lstm3 ->
drawCmd ("Waiting for player # " ++ (show next_player_num)) xinfo state
`thenIO` \() ->
if (next_player_num == 1)
then xDrawGlyph (XDrawWindow window) gcontextp (XPoint 850 70)
'<' `thenIO` \(trash) ->
xDrawRectangle (XDrawWindow window) gcontext2
(XRect 840 180 40 40) True `thenIO` \() ->
xMArrayUpdate time 2 curtime `thenIO` \() ->
xMArrayUpdate time 1 (lstm1+curtime-lstm3) `thenIO` \() ->
showtime 705 270 (lstm1+curtime-lstm3) xinfo `thenIO` \() ->
xMArrayLookup player1 0 `thenIO` \ x ->
if (x == "computer")
then computerplay xinfo state
else humanplay xinfo state
else xDrawGlyph (XDrawWindow window) gcontextp (XPoint 850 210)
'<' `thenIO` \(trash) ->
xDrawRectangle (XDrawWindow window) gcontext2
(XRect 840 40 40 40) True `thenIO` \() ->
xMArrayUpdate time 3 curtime `thenIO` \() ->
xMArrayUpdate time 0 (lstm0+curtime-lstm2) `thenIO` \() ->
showtime 705 130 (lstm0+curtime-lstm3) xinfo `thenIO` \() ->
xMArrayLookup player2 0 `thenIO` \ x ->
if (x == "computer")
then computerplay xinfo state
else humanplay xinfo state
waitButton xinfo@(XInfo display _ _ _ _) state cont =
let
loop xinfo state =
xGetEvent display `thenIO` \ event ->
case (xEventType event) of
XExposureEvent -> may_redraw (xEventCount event == 0) xinfo state
`thenIO` \ _ ->
loop xinfo state
XButtonPressEvent ->
let pos = xEventPos event
in
handleButton pos xinfo state (cont pos)
_ -> xBell display 0 `thenIO` \ _ ->
loop xinfo state
in
loop xinfo state
updateboard :: XInfo -> GameState -> Int -> Int -> IO ()
updateboard xinfo state x y =
let (GameState player1 player2 board steps weight1 weight2 time
numbersteps promptString next_player) = state
(XInfo display window gcontext gcontext2 gcontextp) = xinfo
in
xMArrayLookup next_player 0 `thenIO` \ next_player_num ->
xMArrayUpdate next_player 0 (if next_player_num == 1 then 2 else 1)
`thenIO` \ _ ->
xMArrayLookup numbersteps 0 `thenIO` \ z ->
xMArrayUpdate numbersteps 0 (z+1) `thenIO` \() ->
xMArrayUpdate steps (2*z) x `thenIO` \() ->
xMArrayUpdate steps (2*z+1) y `thenIO` \() ->
xMArrayLookup player1 0 `thenIO` \ name1 ->
xMArrayLookup player2 0 `thenIO` \ name2 ->
xMArrayUpdate board (19*(x-1)+y-1) next_player_num
`thenIO` \() ->
human_unit board x y `thenIO` \ win ->
if win
then drawCmd ("Player " ++ (show next_player_num) ++ " has won!")
xinfo state `thenIO` \ _ ->
let loop xinfo state = waitButton xinfo state (\ _ -> loop)
in loop xinfo state
else if (name1 == "computer" || name2 == "computer")
then draw_unit board weight1 weight2 x y `thenIO` \() ->
xMArrayUpdate weight1 (19*(x-1)+y-1) (-1) `thenIO` \() ->
xMArrayUpdate weight2 (19*(x-1)+y-1) (-1) `thenIO` \() ->
playGame xinfo state
else playGame xinfo state
choice :: XPoint -> XInfo -> GameState -> IO ()
choice (XPoint x y) xinfo@(XInfo display _ _ _ _) state =
let (GameState player1 player2 board steps weight1 weight2 time
numbersteps promptString next_player) = state
in
case (getposition x y) of
XNull -> humanplay xinfo state
XSome (x, y) ->
xMArrayLookup board (19*(x-1)+y-1) `thenIO` \ z ->
if (z>0)
then xBell display 0 `thenIO` \ _ ->
drawCmd "Wrong point, please re-enter" xinfo state `thenIO` \() ->
humanplay xinfo state
else xMArrayLookup next_player 0 `thenIO` \ next_player_num ->
drawPiece x y xinfo (next_player_num == 1) `thenIO` \() ->
updateboard xinfo state x y
humanplay :: XInfo -> GameState -> IO ()
humanplay xinfo state = waitButton xinfo state choice
computerplay :: XInfo -> GameState -> IO ()
computerplay xinfo@(XInfo display window gcontext gcontext2 gcontextp)
state =
let process_events xinfo state cont =
xEventListen display `thenIO` \ n_event ->
if n_event == 0 then cont xinfo state
else xGetEvent display `thenIO` \ event ->
case (xEventType event) of
XButtonPressEvent ->
handleButton (xEventPos event) xinfo state cont
XExposureEvent ->
may_redraw (xEventCount event == 0)
xinfo state
`thenIO` \ _ ->
process_events xinfo state cont
XKeyPressEvent ->
process_events xinfo state cont
in
process_events xinfo state $
\ xinfo@(XInfo display window gcontext gcontext2 gcontextp)
state@(GameState _ _ _ _ weight1 weight2 _ numbersteps _ next_player) ->
robot numbersteps weight1 weight2 `thenIO` \pt ->
let (XPoint x y) = pt
in
xMArrayLookup next_player 0 `thenIO` \ next_player_num ->
drawPiece x y xinfo (next_player_num == 1) `thenIO` \() ->
updateboard xinfo state x y
|