]> git.lizzy.rs Git - plan9front.git/blob - sys/src/games/xs.c
fix typo
[plan9front.git] / sys / src / games / xs.c
1 #include "xs.h"
2
3 /*
4  * engine for 4s, 5s, etc
5  */
6
7 Cursor whitearrow = {
8         {0, 0},
9         {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC, 
10          0xFF, 0xF0, 0xFF, 0xF0, 0xFF, 0xF8, 0xFF, 0xFC, 
11          0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC, 
12          0xF3, 0xF8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, },
13         {0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x06, 0xC0, 0x1C, 
14          0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x38, 0xC0, 0x1C, 
15          0xC0, 0x0E, 0xC0, 0x07, 0xCE, 0x0E, 0xDF, 0x1C, 
16          0xD3, 0xB8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, }
17 };
18
19 enum
20 {
21         CNone   = 0,
22         CBounds = 1,
23         CPiece  = 2,
24         NX      = 10,
25         NY      = 20,
26 };
27
28 enum{
29         TIMER,
30         MOUSE,
31         RESHAPE,
32         KBD,
33         SUSPEND,
34         NALT
35 };
36
37 char            board[NY][NX];
38 Rectangle       rboard;
39 Point           pscore;
40 Point           scoresz;
41 int             pcsz = 32;
42 Point           pos;
43 Image   *bb, *bbmask, *bb2, *bb2mask;
44 Image   *whitemask;
45 Rectangle       br, br2;
46 long            points;
47 int             dt;
48 int             DY;
49 int             DMOUSE;
50 int             lastmx;
51 Mouse   mouse;
52 int             newscreen;
53 Channel *timerc;
54 Channel *suspc;
55 Channel *mousec;
56 Channel *kbdc;
57 Mousectl        *mousectl;
58 Keyboardctl     *kbdctl;
59 int             suspended;
60
61 void            redraw(int);
62
63 int     tsleep;
64
65 Piece *piece;
66
67 #define NCOL    10
68
69 uchar txbits[NCOL][32]={
70         {0xDD,0xDD,0xFF,0xFF,0x77,0x77,0xFF,0xFF,
71          0xDD,0xDD,0xFF,0xFF,0x77,0x77,0xFF,0xFF,
72          0xDD,0xDD,0xFF,0xFF,0x77,0x77,0xFF,0xFF,
73          0xDD,0xDD,0xFF,0xFF,0x77,0x77,0xFF,0xFF},
74         {0xDD,0xDD,0x77,0x77,0xDD,0xDD,0x77,0x77,
75          0xDD,0xDD,0x77,0x77,0xDD,0xDD,0x77,0x77,
76          0xDD,0xDD,0x77,0x77,0xDD,0xDD,0x77,0x77,
77          0xDD,0xDD,0x77,0x77,0xDD,0xDD,0x77,0x77},
78         {0xAA,0xAA,0x55,0x55,0xAA,0xAA,0x55,0x55,
79          0xAA,0xAA,0x55,0x55,0xAA,0xAA,0x55,0x55,
80          0xAA,0xAA,0x55,0x55,0xAA,0xAA,0x55,0x55,
81          0xAA,0xAA,0x55,0x55,0xAA,0xAA,0x55,0x55},
82         {0xAA,0xAA,0x55,0x55,0xAA,0xAA,0x55,0x55,
83          0xAA,0xAA,0x55,0x55,0xAA,0xAA,0x55,0x55,
84          0xAA,0xAA,0x55,0x55,0xAA,0xAA,0x55,0x55,
85          0xAA,0xAA,0x55,0x55,0xAA,0xAA,0x55,0x55},
86         {0x22,0x22,0x88,0x88,0x22,0x22,0x88,0x88,
87          0x22,0x22,0x88,0x88,0x22,0x22,0x88,0x88,
88          0x22,0x22,0x88,0x88,0x22,0x22,0x88,0x88,
89          0x22,0x22,0x88,0x88,0x22,0x22,0x88,0x88},
90         {0x22,0x22,0x00,0x00,0x88,0x88,0x00,0x00,
91          0x22,0x22,0x00,0x00,0x88,0x88,0x00,0x00,
92          0x22,0x22,0x00,0x00,0x88,0x88,0x00,0x00,
93          0x22,0x22,0x00,0x00,0x88,0x88,0x00,0x00},
94         {0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,
95          0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,
96          0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,
97          0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00},
98         {0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,
99          0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,
100          0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,
101          0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00},
102         {0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,
103          0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,
104          0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,
105          0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC},
106         {0xCC,0xCC,0xCC,0xCC,0x33,0x33,0x33,0x33,
107          0xCC,0xCC,0xCC,0xCC,0x33,0x33,0x33,0x33,
108          0xCC,0xCC,0xCC,0xCC,0x33,0x33,0x33,0x33,
109          0xCC,0xCC,0xCC,0xCC,0x33,0x33,0x33,0x33},
110 };
111
112 int txpix[NCOL] = {
113         DYellow,        /* yellow */
114         DCyan,  /* cyan */
115         DGreen, /* lime green */
116         DGreyblue,      /* slate */
117         DRed,   /* red */
118         DGreygreen,     /* olive green */
119         DBlue,  /* blue */
120         0xFF55AAFF,     /* pink */
121         0xFFAAFFFF,     /* lavender */
122         0xBB005DFF,     /* maroon */
123 };
124
125 Image *tx[NCOL];
126
127 int
128 movemouse(void)
129 {
130         mouse.xy = Pt(rboard.min.x + Dx(rboard)/2, rboard.min.y +Dy(rboard)/2);
131         moveto(mousectl, mouse.xy);
132         return mouse.xy.x;
133 }
134
135 int
136 warp(Point p, int x)
137 {
138         if (!suspended && piece != nil) {
139                 x = pos.x + piece->sz.x*pcsz/2;
140                 if (p.y < rboard.min.y)
141                         p.y = rboard.min.y;
142                 if (p.y >= rboard.max.y)
143                         p.y = rboard.max.y - 1;
144                 moveto(mousectl, Pt(x, p.y));
145         }
146         return x;
147 }
148
149 Piece *
150 rotr(Piece *p)
151 {
152         if(p->rot == 3)
153                 return p-3;
154         return p+1;
155 }
156
157 Piece *
158 rotl(Piece *p)
159 {
160         if(p->rot == 0)
161                 return p+3;
162         return p-1;
163 }
164
165 int
166 collide(Point pt, Piece *p)
167 {
168         int i;
169         int c = CNone;
170
171         pt.x = (pt.x - rboard.min.x) / pcsz;
172         pt.y = (pt.y - rboard.min.y) / pcsz;
173         for(i=0; i<N; i++){
174                 pt.x += p->d[i].x;
175                 pt.y += p->d[i].y;
176                 if(pt.x<0 || pt.x>=NX || pt.y<0 || pt.y>=NY)
177                         c |= CBounds;
178                 if(board[pt.y][pt.x])
179                         c |= CPiece;
180         }
181         return c;
182 }
183
184 int
185 collider(Point pt, Point pmax)
186 {
187         int i, j, pi, pj, n, m;
188
189         pi = (pt.x - rboard.min.x) / pcsz;
190         pj = (pt.y - rboard.min.y) / pcsz;
191         n = pmax.x / pcsz;
192         m = pmax.y / pcsz + 1;
193         for(i = pi; i < pi+n && i < NX; i++)
194                 for(j = pj; j < pj+m && j < NY; j++)
195                         if(board[j][i])
196                                 return 1;
197         return 0;
198 }
199
200 void
201 setpiece(Piece *p){
202         int i;
203         Rectangle r, r2;
204         Point op, delta;
205
206         draw(bb, bb->r, display->white, nil, ZP);
207         draw(bbmask, bbmask->r, display->transparent, nil, ZP);
208         br = Rect(0, 0, 0, 0);
209         br2 = br;
210         piece = p;
211         if(p == 0)
212                 return;
213         r.min = bb->r.min;
214         for(i=0; i<N; i++){
215                 r.min.x += p->d[i].x*pcsz;
216                 r.min.y += p->d[i].y*pcsz;
217                 r.max.x = r.min.x + pcsz;
218                 r.max.y = r.min.y + pcsz;
219                 if(i == 0){
220                         draw(bb, r, display->black, nil, ZP);
221                         draw(bb, insetrect(r, 1), tx[piece->tx], nil, ZP);
222                         draw(bbmask, r, display->opaque, nil, ZP);
223                         op = r.min;
224                 }else{
225                         draw(bb, r, bb, nil, op);
226                         draw(bbmask, r, bbmask, nil, op);
227                 }
228                 if(br.max.x < r.max.x)
229                         br.max.x = r.max.x;
230                 if(br.max.y < r.max.y)
231                         br.max.y = r.max.y;
232         }
233         br.max = subpt(br.max, bb->r.min);
234         delta = Pt(0,DY);
235         br2.max = addpt(br.max, delta);
236         r = rectaddpt(br, bb2->r.min);
237         r2 = rectaddpt(br2, bb2->r.min);
238         draw(bb2, r2, display->white, nil, ZP);
239         draw(bb2, rectaddpt(r,delta), bb, nil, bb->r.min);
240         draw(bb2mask, r2, display->transparent, nil, ZP);
241         draw(bb2mask, r, display->opaque, bbmask, bb->r.min);
242         draw(bb2mask, rectaddpt(r,delta), display->opaque, bbmask, bb->r.min);
243 }
244
245 void
246 drawpiece(void){
247         draw(screen, rectaddpt(br, pos), bb, bbmask, bb->r.min);
248         if (suspended)
249                 draw(screen, rectaddpt(br, pos), display->white, whitemask, ZP);
250 }
251
252 void
253 undrawpiece(void)
254 {
255         Image *mask = nil;
256         if(collider(pos, br.max))
257                 mask = bbmask;
258         draw(screen, rectaddpt(br, pos), display->white, mask, bb->r.min);
259 }
260
261 void
262 rest(void)
263 {
264         int i;
265         Point pt;
266
267         pt = divpt(subpt(pos, rboard.min), pcsz);
268         for(i=0; i<N; i++){
269                 pt.x += piece->d[i].x;
270                 pt.y += piece->d[i].y;
271                 board[pt.y][pt.x] = piece->tx+16;
272         }
273 }
274
275 int
276 canfit(Piece *p)
277 {
278         static int dx[]={0, -1, 1, -2, 2, -3, 3, 4, -4};
279         int i, j;
280         Point z;
281
282         j = N + 1;
283         if(j >= 4){
284                 j = p->sz.x;
285                 if(j<p->sz.y)
286                         j = p->sz.y;
287                 j = 2*j-1;
288         }
289         for(i=0; i<j; i++){
290                 z.x = pos.x + dx[i]*pcsz;
291                 z.y = pos.y;
292                 if(!collide(z, p)){
293                         z.y = pos.y + pcsz-1;
294                         if(!collide(z, p)){
295                                 undrawpiece();
296                                 pos.x = z.x;
297                                 return 1;
298                         }
299                 }
300         }
301         return 0;
302 }
303
304 void
305 score(int p)
306 {
307         char buf[128];
308
309         points += p;
310         snprint(buf, sizeof(buf), "%.6ld", points);
311         draw(screen, Rpt(pscore, addpt(pscore, scoresz)), display->white, nil, ZP);
312         string(screen, pscore, display->black, ZP, font, buf);
313 }
314
315 void
316 drawsq(Image *b, Point p, int ptx){
317         Rectangle r;
318
319         r.min = p;
320         r.max.x = r.min.x+pcsz;
321         r.max.y = r.min.y+pcsz;
322         draw(b, r, display->black, nil, ZP);
323         draw(b, insetrect(r, 1), tx[ptx], nil, ZP);
324 }
325
326 void
327 drawboard(void)
328 {
329         int i, j;
330
331         border(screen, insetrect(rboard, -2), 2, display->black, ZP);
332         draw(screen, Rect(rboard.min.x, rboard.min.y-2, rboard.max.x, rboard.min.y),
333                 display->white, nil, ZP);
334         for(i=0; i<NY; i++)
335                 for(j=0; j<NX; j++)
336                         if(board[i][j])
337                                 drawsq(screen, Pt(rboard.min.x+j*pcsz, rboard.min.y+i*pcsz), board[i][j]-16);
338         score(0);
339         if (suspended)
340                 draw(screen, screen->r, display->white, whitemask, ZP);
341 }
342
343 void
344 choosepiece(void)
345 {
346         int i;
347
348         do{
349                 i = nrand(NP);
350                 setpiece(&pieces[i]);
351                 pos = rboard.min;
352                 pos.x += nrand(NX)*pcsz;
353         }while(collide(Pt(pos.x, pos.y+pcsz-DY), piece));
354         drawpiece();
355         flushimage(display, 1);
356 }
357
358 int
359 movepiece(void)
360 {
361         Image *mask = nil;
362
363         if(collide(Pt(pos.x, pos.y+pcsz), piece))
364                 return 0;
365         if(collider(pos, br2.max))
366                 mask = bb2mask;
367         draw(screen, rectaddpt(br2, pos), bb2, mask, bb2->r.min);
368         pos.y += DY;
369         flushimage(display, 1);
370         return 1;
371 }
372
373 void
374 suspend(int s)
375 {
376         suspended = s;
377         if (suspended)
378                 setcursor(mousectl, &whitearrow);
379         else
380                 setcursor(mousectl, nil);
381         if (!suspended)
382                 drawpiece();
383         drawboard();
384         flushimage(display, 1);
385 }
386
387 void
388 pause(int t)
389 {
390         int s;
391         Alt alts[NALT+1];
392
393         alts[TIMER].c = timerc;
394         alts[TIMER].v = nil;
395         alts[TIMER].op = CHANRCV;
396         alts[SUSPEND].c = suspc;
397         alts[SUSPEND].v = &s;
398         alts[SUSPEND].op = CHANRCV;
399         alts[RESHAPE].c = mousectl->resizec;
400         alts[RESHAPE].v = nil;
401         alts[RESHAPE].op = CHANRCV;
402         // avoid hanging up those writing ong mousec and kbdc
403         // so just accept it all and keep mouse up-to-date
404         alts[MOUSE].c = mousec;
405         alts[MOUSE].v = &mouse;
406         alts[MOUSE].op = CHANRCV;
407         alts[KBD].c = kbdc;
408         alts[KBD].v = nil;
409         alts[KBD].op = CHANRCV;
410         alts[NALT].op = CHANEND;
411
412         flushimage(display, 1);
413         for(;;)
414                 switch(alt(alts)){
415                 case SUSPEND:
416                         if (!suspended && s) {
417                                 suspend(1);
418                         } else if (suspended && !s) {
419                                 suspend(0);
420                                 lastmx = warp(mouse.xy, lastmx);
421                         }
422                         break;
423                 case TIMER:
424                         if(suspended)
425                                 break;
426                         if((t -= tsleep) < 0)
427                                 return;
428                         break;
429                 case RESHAPE:
430                         redraw(1);
431                         break;          
432                 }
433 }
434
435 int
436 horiz(void)
437 {
438         int lev[MAXN];
439         int i, j, h;
440         Rectangle r;
441
442         h = 0;
443         for(i=0; i<NY; i++){
444                 for(j=0; board[i][j]; j++)
445                         if(j == NX-1){
446                                 lev[h++] = i;
447                                 break;
448                         }
449         }
450         if(h == 0)
451                 return 0;
452         r = rboard;
453         newscreen = 0;
454         for(j=0; j<h; j++){
455                 r.min.y = rboard.min.y + lev[j]*pcsz;
456                 r.max.y = r.min.y + pcsz;
457                 draw(screen, r, display->white, whitemask, ZP);
458                 flushimage(display, 1);
459         }
460         for(i=0; i<3; i++){
461                 pause(250);
462                 if(newscreen){
463                         drawboard();
464                         break;
465                 }
466                 for(j=0; j<h; j++){
467                         r.min.y = rboard.min.y + lev[j]*pcsz;
468                         r.max.y = r.min.y + pcsz;
469                         draw(screen, r, display->white, whitemask, ZP);
470                 }
471                 flushimage(display, 1);
472         }
473         r = rboard;
474         for(j=0; j<h; j++){
475                 i = NY - lev[j] - 1;
476                 score(250+10*i*i);
477                 r.min.y = rboard.min.y;
478                 r.max.y = rboard.min.y+lev[j]*pcsz;
479                 draw(screen, rectaddpt(r, Pt(0,pcsz)), screen, nil, r.min);
480                 r.max.y = rboard.min.y+pcsz;
481                 draw(screen, r, display->white, nil, ZP);
482                 memcpy(&board[1][0], &board[0][0], NX*lev[j]);
483                 memset(&board[0][0], 0, NX);
484         }
485         flushimage(display, 1);
486         return 1;
487 }
488
489 void
490 mright(void)
491 {
492         if(!collide(Pt(pos.x+pcsz, pos.y), piece))
493         if(!collide(Pt(pos.x+pcsz, pos.y+pcsz-DY), piece)){
494                 undrawpiece();
495                 pos.x += pcsz;
496                 drawpiece();
497                 flushimage(display, 1);
498         }
499 }
500
501 void
502 mleft(void)
503 {
504         if(!collide(Pt(pos.x-pcsz, pos.y), piece))
505         if(!collide(Pt(pos.x-pcsz, pos.y+pcsz-DY), piece)){
506                 undrawpiece();
507                 pos.x -= pcsz;
508                 drawpiece();
509                 flushimage(display, 1);
510         }
511 }
512
513 void
514 rright(void)
515 {
516         if(canfit(rotr(piece))){
517                 setpiece(rotr(piece));
518                 drawpiece();
519                 flushimage(display, 1);
520         }
521 }
522
523 void
524 rleft(void)
525 {
526         if(canfit(rotl(piece))){
527                 setpiece(rotl(piece));
528                 drawpiece();
529                 flushimage(display, 1);
530         }
531 }
532
533 int fusst = 0;
534 int
535 drop(int f)
536 {
537         if(f){
538                 score(5L*(rboard.max.y-pos.y)/pcsz);
539                 do; while(movepiece());
540         }
541         fusst = 0;
542         rest();
543         if(pos.y==rboard.min.y && !horiz())
544                 return 1;
545         horiz();
546         setpiece(0);
547         pause(1500);
548         choosepiece();
549         lastmx = warp(mouse.xy, lastmx);
550         return 0;
551 }
552
553 int
554 play(void)
555 {
556         int i;
557         Mouse om;
558         int s;
559         Rune r;
560         Alt alts[NALT+1];
561
562         alts[TIMER].c = timerc;
563         alts[TIMER].v = nil;
564         alts[TIMER].op = CHANRCV;
565         alts[MOUSE].c = mousec;
566         alts[MOUSE].v = &mouse;
567         alts[MOUSE].op = CHANRCV;
568         alts[SUSPEND].c = suspc;
569         alts[SUSPEND].v = &s;
570         alts[SUSPEND].op = CHANRCV;
571         alts[RESHAPE].c = mousectl->resizec;
572         alts[RESHAPE].v = nil;
573         alts[RESHAPE].op = CHANRCV;
574         alts[KBD].c = kbdc;
575         alts[KBD].v = &r;
576         alts[KBD].op = CHANRCV;
577         alts[NALT].op = CHANEND;
578
579         dt = 64;
580         lastmx = -1;
581         lastmx = movemouse();
582         choosepiece();
583         lastmx = warp(mouse.xy, lastmx);
584         for(;;)
585         switch(alt(alts)){
586         case MOUSE:
587                 if(suspended) {
588                         om = mouse;
589                         break;
590                 }
591                 if(lastmx < 0)
592                         lastmx = mouse.xy.x;
593                 if(mouse.xy.x > lastmx+DMOUSE){
594                         mright();
595                         lastmx = mouse.xy.x;
596                 }
597                 if(mouse.xy.x < lastmx-DMOUSE){
598                         mleft();
599                         lastmx = mouse.xy.x;
600                 }
601                 if(mouse.buttons&1 && !(om.buttons&1))
602                         rleft();
603                 if(mouse.buttons&2 && !(om.buttons&2))
604                         if(drop(1))
605                                 return 1;
606                 if(mouse.buttons&4 && !(om.buttons&4))
607                         rright();
608                 om = mouse;
609                 break;
610         case SUSPEND:
611                 if (!suspended && s)
612                         suspend(1);
613                 else
614                 if (suspended && !s) {
615                         suspend(0);
616                         lastmx = warp(mouse.xy, lastmx);
617                 }
618                 break;
619         case RESHAPE:
620                 redraw(1);
621                 break;          
622         case KBD:
623                 if(suspended)
624                         break;
625                 switch(r){
626                 case 'f':
627                 case ';':
628                         mright();
629                         break;
630                 case 'a':
631                 case 'j':
632                         mleft();
633                         break;
634                 case 'd':
635                 case 'l':
636                         rright();
637                         break;
638                 case 's':
639                 case 'k':
640                         rleft();
641                         break;
642                 case ' ':
643                         if(drop(1))
644                                 return 1;
645                         break;
646                 }
647                 break;
648         case TIMER:
649                 if(suspended)
650                         break;
651                 dt -= tsleep;
652                 if(dt < 0){
653                         i = 1;
654                         dt = 16 * (points+nrand(10000)-5000) / 10000;
655                         if(dt >= 32){
656                                 i += (dt-32)/16;
657                                 dt = 32;
658                         }
659                         dt = 52-dt;
660                         while(i-- > 0)
661                                 if(movepiece()==0 && ++fusst==40){
662                                         if(drop(0))
663                                                 return 1;
664                                         break;
665                                 }
666                 }
667                 break;
668         }
669 }
670
671 void
672 setparms(void)
673 {
674         char buf[32];
675         int fd, n;
676
677         tsleep = 50;
678         fd = open("/dev/hz", OREAD);
679         if(fd < 0)
680                 return;
681         n = read(fd, buf, sizeof buf - 1);
682         close(fd);
683         if(n < 0)
684                 return;
685         buf[n] = '\0';
686         tsleep = strtoul(buf, 0, 10);
687         tsleep = (1000 + tsleep - 1) / tsleep;
688 }
689
690 void
691 timerproc(void *v)
692 {
693         Channel *c;
694         void **arg;
695
696         arg = v;
697         c = (Channel*)arg;
698
699         for(;;){
700                 sleep(tsleep);
701                 send(c, nil);
702         }
703 }
704
705 void
706 suspproc(void *)
707 {
708         Mouse mouse;
709         Rune r;
710         int s;
711         Alt alts[NALT+1];
712
713         alts[TIMER].op = CHANNOP;
714         alts[MOUSE].c = mousectl->c;
715         alts[MOUSE].v = &mouse;
716         alts[MOUSE].op = CHANRCV;
717         alts[SUSPEND].op = CHANNOP;
718         alts[RESHAPE].op = CHANNOP;
719         alts[KBD].c = kbdctl->c;
720         alts[KBD].v = &r;
721         alts[KBD].op = CHANRCV;
722         alts[NALT].op = CHANEND;
723
724         s = 0;
725         for(;;)
726                 switch(alt(alts)){
727                 case MOUSE:
728                         send(mousec, &mouse);
729                         break;
730                 case KBD:
731                         switch(r){
732                         case 'q':
733                         case 'Q':
734                         case 0x04:
735                         case 0x7F:
736                                 threadexitsall(nil);
737                         default:
738                                 if(s) {
739                                         s = 0;
740                                         send(suspc, &s);
741                                 } else
742                                         switch(r){
743                                         case 'z':
744                                         case 'Z':
745                                         case 'p':
746                                         case 'P':
747                                         case 0x1B:
748                                                 s = 1;
749                                                 send(suspc, &s);
750                                                 break;
751                                         default:
752                                                 send(kbdc, &r);
753                                         }
754                                 break;
755                         }
756                 }
757 }
758
759 void
760 redraw(int new)
761 {
762         Rectangle r;
763         long dx, dy;
764
765         if(new && getwindow(display, Refmesg) < 0)
766                 sysfatal("can't reattach to window");
767         r = screen->r;
768         pos.x = (pos.x - rboard.min.x) / pcsz;
769         pos.y = (pos.y - rboard.min.y) / pcsz;
770         dx = r.max.x - r.min.x;
771         dy = r.max.y - r.min.y - 2*32;
772         DY = dx / NX;
773         if(DY > dy / NY)
774                 DY = dy / NY;
775         DY /= 8;
776         if(DY > 4)
777                 DY = 4;
778         pcsz = DY*8;
779         DMOUSE = pcsz/3;
780         if(pcsz < 8)
781                 sysfatal("screen too small: %d", pcsz);
782         rboard = screen->r;
783         rboard.min.x += (dx-pcsz*NX)/2;
784         rboard.min.y += (dy-pcsz*NY)/2+32;
785         rboard.max.x = rboard.min.x+NX*pcsz;
786         rboard.max.y = rboard.min.y+NY*pcsz;
787         pscore.x = rboard.min.x+8;
788         pscore.y = rboard.min.y-32;
789         scoresz = stringsize(font, "000000");
790         pos.x = pos.x*pcsz + rboard.min.x;
791         pos.y = pos.y*pcsz + rboard.min.y;
792         if(bb){
793                 freeimage(bb);
794                 freeimage(bbmask);
795                 freeimage(bb2);
796                 freeimage(bb2mask);
797         }
798         bb = allocimage(display, Rect(0,0,N*pcsz,N*pcsz), screen->chan, 0, 0);
799         bbmask = allocimage(display, Rect(0,0,N*pcsz,N*pcsz), GREY1, 0, 0);
800         bb2 = allocimage(display, Rect(0,0,N*pcsz,N*pcsz+DY), screen->chan, 0, 0);
801         bb2mask = allocimage(display, bb2->r, GREY1, 0, 0);
802         if(bb==0 || bbmask==0 || bb2==0 || bb2mask==0)
803                 sysfatal("allocimage fail (bb)");
804         draw(screen, screen->r, display->white, nil, ZP);
805         drawboard();
806         setpiece(piece);
807         if(piece)
808                 drawpiece();
809         lastmx = movemouse();
810         newscreen = 1;
811         flushimage(display, 1);
812 }
813
814 void
815 usage(void)
816 {
817         fprint(2, "usage: %s\n", argv0);
818         exits("usage");
819 }
820
821 void
822 threadmain(int argc, char *argv[])
823 {
824         Image *tb;
825         char buf[200];
826         int i, scores;
827         long starttime, endtime;
828
829         ARGBEGIN{
830         default:
831                 usage();
832         }ARGEND
833         if(argc)
834                 usage();
835
836         suspended = 0;
837         setparms();
838         snprint(buf, sizeof(buf), "%ds", N);
839         initdraw(0, 0, buf);
840         mousectl = initmouse(nil, display->image);      /* BUG? */
841         if(mousectl == nil)
842                 sysfatal("[45]s: mouse init failed: %r");
843         kbdctl = initkeyboard(nil);     /* BUG? */
844         if(kbdctl == nil)
845                 sysfatal("[45]s: keyboard init failed: %r");
846         starttime = time(0);
847         srand(starttime);
848         snprint(buf, sizeof(buf), "/sys/games/lib/%dscores", N);
849         scores = open(buf, OWRITE);
850         if(scores < 0)
851                 print("can't open %s: %r\n", buf);
852         tb = 0;
853         if(screen->depth < 3){
854                 tb = allocimage(display, Rect(0,0,16,16), 0, 1, -1);
855                 if(tb == 0)
856                         sysfatal("allocimage fail (tb)");
857         }
858         for(i = 0; i<NCOL; i++){
859                 tx[i] = allocimage(display, Rect(0, 0, 16, 16), screen->chan, 1, txpix[i]);
860                 if(tx[i] == 0)
861                         sysfatal("allocimage fail (tx)");
862                 if(screen->depth < 3){
863                         loadimage(tb, tb->r, txbits[i], 32);
864                         draw(tx[i], tx[i]->r, tb, nil, ZP);
865                 }
866         }
867         if(tb != 0)
868                 freeimage(tb);
869
870         whitemask = allocimage(display, Rect(0,0,1,1), CMAP8, 1, setalpha(DWhite, 0x7F));
871         if(whitemask==0)
872                 sysfatal("allocimage fail (whitemask)");
873
874         threadsetname("4s-5s");
875         timerc= chancreate(sizeof(int), 0);
876         proccreate(timerproc, timerc, 1024);
877         suspc= chancreate(sizeof(int), 0);
878         mousec= chancreate(sizeof(Mouse), 0);
879         kbdc= chancreate(sizeof(Rune), 0);
880         threadcreate(suspproc, nil, 1024);
881         points = 0;
882         memset(board, 0, sizeof(board));
883         redraw(0);
884         if(play() && scores >= 0){
885                 endtime = time(0);
886                 fprint(scores, "%ld\t%s\t%lud\t%ld\n",
887                         points, getuser(), starttime, endtime-starttime);
888         }
889         threadexitsall(nil);
890         exits(0);
891 }