1 /***********************************************************
2 * "Das mit dem Affen..." -- A one-night hack *
3 *----------------------------------------------------------*
4 * �1995 Artsoft Development *
5 * Holger Schemel *
6 * 33659 Bielefeld-Senne *
7 * Telefon: (0521) 493245 *
8 * eMail: aeglos@valinor.owl.de *
9 * aeglos@uni-paderborn.de *
10 * q99492@pbhrzx.uni-paderborn.de *
11 ***********************************************************/
12
13 #define XK_MISCELLANY
14 #define XK_LATIN1
15
16 #include <X11/Xlib.h>
17 #include <X11/Xutil.h>
18 #include <X11/Xos.h>
19 #include <X11/Intrinsic.h>
20 #include <X11/keysymdef.h>
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <strings.h>
25 #include <errno.h>
26 #include <signal.h>
27
28 #include "affe.xbm"
29 #define ICON_WIDTH affe_width
30 #define ICON_HEIGHT affe_height
31 #define ICON_BITS affe_bits
32
33 #define TRUE 1
34 #define FALSE 0
35
36 #define PRESSED TRUE
37 #define UNPRESSED FALSE
38 #define RELEASED FALSE
39
40 #define MIN(a,b) (((a)<(b))?(a):(b))
41 #define MAX(a,b) (((a)>(b))?(a):(b))
42 #define ABS(a) (((a)<0)?(-a):(a))
43
44 #define TILESIZE_X 40
45 #define TILESIZE_Y 40
46 #define TILES_X 4
47 #define TILES_Y 5
48 #define WIN_XPOS 0
49 #define WIN_YPOS 0
50 #define BORDER_X (TILESIZE_X / 2)
51 #define BORDER_Y (TILESIZE_Y / 2)
52 #define WIN_XSIZE ((TILES_X * TILESIZE_X) + (2 * BORDER_X))
53 #define WIN_YSIZE ((TILES_Y * TILESIZE_Y) + (2 * BORDER_Y))
54 #define AFFENFUESSE 13
55
56 #define BACK 0
57 #define FRONT 1
58 #define LIGHT 2
59 #define DARK 3
60 #define EMPTY 4
61 #define INVERT 5
62 #define MAX_COLORS 6
63
64 #ifndef SAVED_GAME_NAME
65 #define SAVED_GAME_NAME "/tmp/affenspiel.saved"
66 #endif
67
68 int playfield[TILES_X][TILES_Y];
69 int startover[TILES_X][TILES_Y] =
70 {
71 1, 1, 0, 5, 5,
72 2, 2, 4, 6, 9,
73 2, 2, 4, 7, 10,
74 3, 3, 0, 8, 8
75 };
76
77 int solution[TILES_X][TILES_Y] =
78 {
79 0, 0, 0, 0, 0,
80 0, 0, 0, 2, 2,
81 0, 0, 0, 2, 2,
82 0, 0, 0, 0, 0
83 };
84
85 Display *display;
86 int screen;
87 Window window;
88 Pixmap pixmap, affe_pixmap;
89 Drawable drawto;
90 GC gc, color_gc[MAX_COLORS];
91 Colormap cmap;
92
93 int width, height;
94 unsigned long pen_fg, pen_bg;
95 unsigned long color[MAX_COLORS];
96
97 char *progname;
98 char *name_of_saved_game;
99 FILE *saved_game=NULL;
100 int game_solved=FALSE;
101
102 void OpenAll(int, char **);
103 void InitDisplay();
104 void InitWindow(int, char **);
105 void CloseAll();
106 void EventLoop();
107 void HandleExposeEvent(XExposeEvent *);
108 void HandleButtonEvent(XButtonEvent *, int);
109 void HandleKeyEvent(XKeyEvent *);
110 void ClearWindow(void);
111 void DisplayDrawBuffer(void);
112 void DrawBorder(int, int, int, int, int);
113 void DrawTile(int, int, int);
114 void DrawTiles();
115 void TryToMoveTile(int, int, int, int);
116 void CheckIfSolved();
117 void SillyEffect();
118 void NewGame();
119 void LoadGame();
120 void SaveGame();
121
main(int argc,char * argv[])122 int main(int argc, char *argv[])
123 {
124 progname = argv[0];
125 name_of_saved_game = (argc>1 ? argv[1] : SAVED_GAME_NAME);
126 OpenAll(argc, argv);
127
128 EventLoop();
129
130 CloseAll();
131 exit(0);
132 }
133
OpenAll(int argc,char * argv[])134 void OpenAll(int argc, char *argv[])
135 {
136 InitDisplay();
137 InitWindow(argc, argv);
138 NewGame();
139 DrawTiles();
140
141 signal(SIGINT, CloseAll);
142 signal(SIGTERM, CloseAll);
143
144 XMapWindow(display, window);
145 XFlush(display);
146 }
147
InitDisplay()148 void InitDisplay()
149 {
150 int i;
151 XColor screen_def, exact_def;
152 char *color_name[MAX_COLORS] =
153 {
154 "gray90",
155 "black",
156 "white",
157 "gray60",
158 "gray80",
159 "gray90"
160 };
161 int mono_color[MAX_COLORS] = { 0, 1, 1, 1, 1, 0 };
162
163 if (!(display=XOpenDisplay(NULL)))
164 {
165 fprintf(stderr,"%s: cannot connect to X server %s\n",
166 progname, XDisplayName(NULL));
167 exit(-1);
168 }
169
170 screen = DefaultScreen(display);
171 cmap = DefaultColormap(display, screen);
172
173 pen_bg = WhitePixel(display,screen);
174 pen_fg = BlackPixel(display,screen);
175
176 for(i=0;i<MAX_COLORS;i++)
177 {
178 if (!XAllocNamedColor(display,cmap,color_name[i],&screen_def,&exact_def) ||
179 screen_def.red - exact_def.red > 0x0800 ||
180 screen_def.green - exact_def.green > 0x0800 ||
181 screen_def.blue - exact_def.blue > 0x0800)
182 color[i] = (mono_color[i] ? pen_fg : pen_bg);
183 else
184 color[i] = screen_def.pixel;
185 }
186 }
187
InitWindow(int argc,char * argv[])188 void InitWindow(int argc, char *argv[])
189 {
190 unsigned int border_width = 4;
191 Pixmap icon_pixmap;
192 XSizeHints size_hints;
193 XWMHints wm_hints;
194 XClassHint class_hints;
195 XTextProperty windowName, iconName;
196 XGCValues gc_values;
197 unsigned long gc_valuemask;
198 char *window_name = "Das mit dem Affen...";
199 char *icon_name = "�ffle";
200 long window_event_mask;
201 int i;
202
203 width = WIN_XSIZE;
204 height = WIN_YSIZE;
205
206 window = XCreateSimpleWindow(display, RootWindow(display, screen),
207 WIN_XPOS, WIN_YPOS, width, height, border_width,
208 pen_fg, pen_bg);
209
210 affe_pixmap = XCreateBitmapFromData(display, window, ICON_BITS,
211 ICON_WIDTH, ICON_HEIGHT);
212 icon_pixmap=affe_pixmap;
213
214 size_hints.flags = PSize | PMinSize | PMaxSize;
215 size_hints.width = size_hints.min_width = size_hints.max_width = width;
216 size_hints.height = size_hints.min_height = size_hints.max_height = height;
217
218 if (!XStringListToTextProperty(&window_name, 1, &windowName))
219 {
220 fprintf(stderr, "%s: structure allocation for windowName failed.\n",
221 progname);
222 exit(-1);
223 }
224
225 if (!XStringListToTextProperty(&icon_name, 1, &iconName))
226 {
227 fprintf(stderr, "%s: structure allocation for iconName failed.\n",
228 progname);
229 exit(-1);
230 }
231
232 wm_hints.initial_state = NormalState;
233 wm_hints.input = True;
234 wm_hints.icon_pixmap = icon_pixmap;
235 wm_hints.flags = StateHint | IconPixmapHint | InputHint;
236
237 class_hints.res_name = progname;
238 class_hints.res_class = "Das mit dem Affen";
239
240 XSetWMProperties(display, window, &windowName, &iconName,
241 argv, argc, &size_hints, &wm_hints,
242 &class_hints);
243
244 /* Select event types wanted */
245 window_event_mask = ExposureMask | ButtonPressMask | ButtonReleaseMask |
246 KeyPressMask | StructureNotifyMask;
247 XSelectInput(display, window, window_event_mask);
248
249 /* create GC for normal drawing */
250 gc_values.graphics_exposures = False;
251 gc_values.foreground = pen_fg;
252 gc_values.background = pen_bg;
253 gc_valuemask = GCGraphicsExposures | GCForeground | GCBackground;
254 gc = XCreateGC(display, window, gc_valuemask, &gc_values);
255
256 /* create GCs for drawing in all (five) colors */
257 for(i=0;i<MAX_COLORS;i++)
258 {
259 gc_values.graphics_exposures = False;
260 gc_values.foreground = color[i];
261 gc_values.background = color[BACK];
262 gc_values.line_width = 1;
263 gc_values.line_style = LineSolid;
264 gc_values.cap_style = CapProjecting;
265 gc_values.join_style = JoinBevel;
266 gc_values.function = (i==INVERT ? GXinvert : GXcopy);
267 gc_valuemask = GCGraphicsExposures | GCForeground | GCBackground |
268 GCLineWidth | GCLineStyle | GCCapStyle | GCJoinStyle |
269 GCFunction;
270 color_gc[i] = XCreateGC(display, window, gc_valuemask, &gc_values);
271 }
272
273 gc = color_gc[FRONT];
274
275 pixmap = XCreatePixmap(display, window,
276 WIN_XSIZE,WIN_YSIZE,
277 XDefaultDepth(display,screen));
278 drawto = pixmap;
279 }
280
CloseAll(void)281 void CloseAll(void)
282 {
283 XFreeGC(display, gc);
284 XCloseDisplay(display);
285 exit(0);
286 }
287
EventLoop(void)288 void EventLoop(void)
289 {
290 XEvent event;
291 int pending;
292
293 for(;;)
294 {
295 pending=XPending(display);
296
297 if (pending) /* got an event */
298 {
299 for(; pending>0; pending--)
300 {
301 XNextEvent(display, &event);
302 switch(event.type)
303 {
304 case Expose:
305 HandleExposeEvent((XExposeEvent *) &event);
306 break;
307 case ButtonPress:
308 HandleButtonEvent((XButtonEvent *) &event, PRESSED);
309 break;
310 case ButtonRelease:
311 HandleButtonEvent((XButtonEvent *) &event, RELEASED);
312 break;
313 case KeyPress:
314 case KeyRelease:
315 HandleKeyEvent((XKeyEvent *) &event);
316 break;
317 default:
318 break;
319 }
320 }
321 }
322 usleep(100000);
323 }
324 }
325
HandleExposeEvent(XExposeEvent * event)326 void HandleExposeEvent(XExposeEvent *event)
327 {
328 int x=event->x, y=event->y;
329 int w=event->width, h=event->height;
330
331 XCopyArea(display,pixmap,window,gc,x,y,w,h,x,y);
332 }
333
HandleButtonEvent(XButtonEvent * event,int button_state)334 void HandleButtonEvent(XButtonEvent *event, int button_state)
335 {
336 int x=event->x, y=event->y, button=event->button;
337 int bx=(x-BORDER_X+TILESIZE_X)/TILESIZE_X-1;
338 int by=(y-BORDER_Y+TILESIZE_Y)/TILESIZE_Y-1;
339 static int old_bx=-1, old_by=-1;
340
341 if (button_state == PRESSED)
342 {
343 old_bx=bx;
344 old_by=by;
345
346 if (bx>=0 && bx<TILES_X && by>=0 && by<TILES_Y && playfield[bx][by])
347 {
348 DrawTile(bx,by,PRESSED);
349 DisplayDrawBuffer();
350 }
351 }
352 else
353 {
354 if (old_bx>=0 && old_bx<TILES_X && old_by>=0 && old_by<TILES_Y)
355 {
356 DrawTile(old_bx,old_by,UNPRESSED);
357 DisplayDrawBuffer();
358
359 if (playfield[old_bx][old_by]==playfield[bx][by])
360 TryToMoveTile(bx,by,
361 x-(BORDER_X+bx*TILESIZE_X+TILESIZE_X/2),
362 y-(BORDER_Y+by*TILESIZE_Y+TILESIZE_Y/2));
363 }
364 }
365 }
366
HandleKeyEvent(XKeyEvent * event)367 void HandleKeyEvent(XKeyEvent *event)
368 {
369 KeySym key = XLookupKeysym(event,event->state);
370
371 switch(key)
372 {
373 case XK_L:
374 case XK_l:
375 if (name_of_saved_game)
376 {
377 LoadGame();
378 DrawTiles();
379 DisplayDrawBuffer();
380 }
381 break;
382 case XK_N:
383 case XK_n:
384 NewGame();
385 DrawTiles();
386 DisplayDrawBuffer();
387 break;
388 case XK_S:
389 case XK_s:
390 if (name_of_saved_game)
391 {
392 SaveGame();
393 DrawTiles();
394 DisplayDrawBuffer();
395 }
396 break;
397 case XK_Q:
398 case XK_q:
399 CloseAll();
400 exit(0);
401 break;
402 default:
403 break;
404 }
405 }
406
ClearWindow()407 void ClearWindow()
408 {
409 XFillRectangle(display,drawto,color_gc[BACK],0,0,WIN_XSIZE,WIN_YSIZE);
410 XFlush(display);
411 }
412
DisplayDrawBuffer()413 void DisplayDrawBuffer()
414 {
415 XCopyArea(display,pixmap,window,gc,0,0,WIN_XSIZE,WIN_YSIZE,0,0);
416 XFlush(display);
417 }
418
DrawBorder(int x1,int y1,int x2,int y2,int pressed)419 void DrawBorder(int x1, int y1, int x2, int y2, int pressed)
420 {
421 int upperleft = (pressed ? DARK : LIGHT);
422 int lowerright = (pressed ? LIGHT : DARK);
423
424 XDrawLine(display,drawto,color_gc[upperleft],x1,y1,x2,y1);
425 XDrawLine(display,drawto,color_gc[upperleft],x1+1,y1+1,x2-1,y1+1);
426 XDrawLine(display,drawto,color_gc[upperleft],x1,y1,x1,y2);
427 XDrawLine(display,drawto,color_gc[upperleft],x1+1,y1+1,x1+1,y2-1);
428 XDrawLine(display,drawto,color_gc[lowerright],x1+1,y2,x2,y2);
429 XDrawLine(display,drawto,color_gc[lowerright],x1+2,y2-1,x2-1,y2-1);
430 XDrawLine(display,drawto,color_gc[lowerright],x2,y1+1,x2,y2);
431 XDrawLine(display,drawto,color_gc[lowerright],x2-1,y1+2,x2-1,y2-1);
432 }
433
DrawTile(int x,int y,int pressed)434 void DrawTile(int x, int y, int pressed)
435 {
436 int x1=BORDER_X+x*TILESIZE_X;
437 int y1=BORDER_Y+y*TILESIZE_Y;
438 int x2=BORDER_X+(x+1)*TILESIZE_X-1;
439 int y2=BORDER_Y+(y+1)*TILESIZE_Y-1;
440
441 if (x>0 && playfield[x-1][y]==playfield[x][y])
442 x1-=TILESIZE_X;
443 if (x<TILES_X-1 && playfield[x+1][y]==playfield[x][y])
444 x2+=TILESIZE_X;
445 if (y>0 && playfield[x][y-1]==playfield[x][y])
446 y1-=TILESIZE_Y;
447 if (y<TILES_Y-1 && playfield[x][y+1]==playfield[x][y])
448 y2+=TILESIZE_Y;
449
450 if (!playfield[x][y])
451 {
452 XFillRectangle(display,drawto,color_gc[EMPTY],x1,y1,x2-x1+1,y2-y1+1);
453 return;
454 }
455
456 if (pressed)
457 DrawBorder(x1,y1,x2,y2,PRESSED);
458 else
459 DrawBorder(x1,y1,x2,y2,UNPRESSED);
460
461 if (x2-x1+1==2*TILESIZE_X && y2-y1+1==2*TILESIZE_Y) /* das mit dem Affen */
462 XCopyPlane(display,affe_pixmap,drawto,gc,
463 0,0,ICON_WIDTH,ICON_HEIGHT-AFFENFUESSE,
464 x1+2,y1+2+(2*TILESIZE_Y-(ICON_HEIGHT-AFFENFUESSE)-4),0x1);
465 }
466
DrawTiles()467 void DrawTiles()
468 {
469 int x,y;
470
471 ClearWindow();
472 DrawBorder(BORDER_X-2,BORDER_Y-2,
473 WIN_XSIZE-BORDER_X+1,WIN_YSIZE-BORDER_Y+1,PRESSED);
474
475 for(y=0;y<TILES_Y;y++)
476 for(x=0;x<TILES_X;x++)
477 DrawTile(x,y,UNPRESSED);
478
479 /* die Fuesse des Affen */
480 XCopyPlane(display,affe_pixmap,drawto,gc,
481 0,ICON_HEIGHT-AFFENFUESSE,ICON_WIDTH,AFFENFUESSE,
482 WIN_XSIZE/2-ICON_WIDTH/2,WIN_YSIZE-BORDER_Y+2,0x1);
483 }
484
TryToMoveTile(int bx,int by,int movehint_x,int movehint_y)485 void TryToMoveTile(int bx, int by, int movehint_x, int movehint_y)
486 {
487 int x,y;
488 int bbx[2],bby[2];
489 int move_x=(movehint_x<0 ? -1 : 1);
490 int move_y=(movehint_y<0 ? -1 : 1);
491 int we_can_move=TRUE;
492 int value=playfield[bx][by];
493
494 bbx[0]=bbx[1]=bx;
495 bby[0]=bby[1]=by;
496
497 if (ABS(movehint_x) > ABS(movehint_y))
498 move_y=0;
499 else
500 move_x=0;
501
502 if (bx>0 && playfield[bx-1][by]==playfield[bx][by])
503 {
504 bbx[0]=bx-1;
505 move_x=1;
506 move_y=0;
507 }
508 if (bx<TILES_X-1 && playfield[bx+1][by]==playfield[bx][by])
509 {
510 bbx[1]=bx+1;
511 move_x=-1;
512 move_y=0;
513 }
514 if (by>0 && playfield[bx][by-1]==playfield[bx][by])
515 {
516 bby[0]=by-1;
517 move_x=0;
518 move_y=1;
519 }
520 if (by<TILES_Y-1 && playfield[bx][by+1]==playfield[bx][by])
521 {
522 bby[1]=by+1;
523 move_x=0;
524 move_y=-1;
525 }
526
527 /* test if we can move */
528 for(x=0;x<2;x++)
529 {
530 for(y=0;y<2;y++)
531 {
532 int new_x=bbx[x]+move_x;
533 int new_y=bby[y]+move_y;
534
535 if (new_x<0 || new_x >=TILES_X || new_y<0 || new_y>=TILES_Y ||
536 (playfield[new_x][new_y]!=playfield[bx][by] &&
537 playfield[new_x][new_y]!=0))
538 we_can_move=FALSE;
539 }
540 }
541
542 /* no success: try the opposite direction... */
543 if (!we_can_move)
544 {
545 move_x=-move_x;
546 move_y=-move_y;
547 we_can_move=TRUE;
548
549 for(x=0;x<2;x++)
550 {
551 for(y=0;y<2;y++)
552 {
553 int new_x=bbx[x]+move_x;
554 int new_y=bby[y]+move_y;
555
556 if (new_x<0 || new_x >=TILES_X || new_y<0 || new_y>=TILES_Y ||
557 (playfield[new_x][new_y]!=playfield[bx][by] &&
558 playfield[new_x][new_y]!=0))
559 we_can_move=FALSE;
560 }
561 }
562 }
563
564 /* don't give up too fast... */
565 if (!we_can_move)
566 {
567 int i;
568 int try_move_x[4] = { 0, 0, -1, 1 };
569 int try_move_y[4] = { -1, 1, 0, 0 };
570
571 for(i=0;i<4;i++)
572 {
573 we_can_move=TRUE;
574
575 for(x=0;x<2;x++)
576 {
577 for(y=0;y<2;y++)
578 {
579 int new_x=bbx[x]+try_move_x[i];
580 int new_y=bby[y]+try_move_y[i];
581
582 if (new_x<0 || new_x >=TILES_X || new_y<0 || new_y>=TILES_Y ||
583 (playfield[new_x][new_y]!=playfield[bx][by] &&
584 playfield[new_x][new_y]!=0))
585 we_can_move=FALSE;
586 }
587 }
588
589 if (we_can_move)
590 {
591 move_x=try_move_x[i];
592 move_y=try_move_y[i];
593 break;
594 }
595 }
596
597 if (!we_can_move)
598 return;
599 }
600
601 /* delete at old position */
602 for(x=0;x<2;x++)
603 for(y=0;y<2;y++)
604 playfield[bbx[x]][bby[y]]=0;
605
606 /* set at new position */
607 for(x=0;x<2;x++)
608 {
609 for(y=0;y<2;y++)
610 {
611 int new_x=bbx[x]+move_x;
612 int new_y=bby[y]+move_y;
613
614 playfield[new_x][new_y]=value;
615 }
616 }
617
618 DrawTiles();
619 DisplayDrawBuffer();
620
621 CheckIfSolved();
622 }
623
CheckIfSolved()624 void CheckIfSolved()
625 {
626 int x,y;
627 int affe_in_einem_stueck=TRUE;
628
629 for(x=0;x<TILES_X;x++)
630 for(y=0;y<TILES_Y;y++)
631 if (solution[x][y] && playfield[x][y]!=solution[x][y])
632 affe_in_einem_stueck=FALSE;
633
634 if (affe_in_einem_stueck)
635 {
636 if (!game_solved)
637 {
638 game_solved=TRUE;
639 SillyEffect();
640 }
641 }
642 else
643 {
644 if (game_solved)
645 game_solved=FALSE;
646 }
647 }
648
SillyEffect()649 void SillyEffect()
650 {
651 int i;
652
653 for(i=0;i<20;i++)
654 {
655 XFillRectangle(display,drawto,color_gc[INVERT],0,0,WIN_XSIZE,WIN_YSIZE);
656 DisplayDrawBuffer();
657 usleep(20000);
658 }
659 }
660
NewGame()661 void NewGame()
662 {
663 int x,y;
664
665 for(y=0;y<TILES_Y;y++)
666 for(x=0;x<TILES_X;x++)
667 playfield[x][y]=startover[x][y];
668 }
669
LoadGame()670 void LoadGame()
671 {
672 int x,y;
673
674 if (!(saved_game=fopen(name_of_saved_game,"r")))
675 {
676 fprintf(stderr,"cannot read '%s'.\n",name_of_saved_game);
677 return;
678 }
679
680 for(y=0;y<TILES_Y;y++)
681 {
682 for(x=0;x<TILES_X;x++)
683 {
684 fscanf(saved_game,"%d ",&playfield[x][y]);
685 }
686 fscanf(saved_game,"\n");
687 }
688
689 fclose(saved_game);
690 }
691
SaveGame()692 void SaveGame()
693 {
694 int x,y;
695
696 if (!(saved_game=fopen(name_of_saved_game,"w")))
697 {
698 fprintf(stderr,"cannot write '%s'.\n",name_of_saved_game);
699 return;
700 }
701
702 for(y=0;y<TILES_Y;y++)
703 {
704 for(x=0;x<TILES_X;x++)
705 {
706 fprintf(saved_game,"%d ",playfield[x][y]);
707 }
708 fprintf(saved_game,"\n");
709 }
710
711 fclose(saved_game);
712 }
713