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