summaryrefslogtreecommitdiff
path: root/progs/demo/X11/gobang/gobang.hs
blob: f4844dc10a36dc2e0d56c0e6f56976239feb618e (about) (plain)
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