1 #include <stdlib.h>
2 #include <sys/time.h>
3 #include <unistd.h>
4 #include <SDL.h>
5
6 #include "gfx.h"
7 #include "font.h"
8 surface tilesgs;
9
10 int bgsetnumber;
11 int tilesetnumber;
12 char *msg;
13
14 char *clickonatile="Click on a tile";
15 char *clickonanothertile="Click on another tile";
16
nomem(int code)17 void nomem(int code)
18 {
19 printf("No memory: %d\n",code);
20 exit(1);
21 }
22 #define TILELOCKED 1
23 #define TILEGONE 2
24 #define TILERIGHT1 4
25 #define TILERIGHT2 8
26 #define TILETOP 16
27 #define TILELEFT 32
28 #define TILEBOTTOM 64
29 #define TILEBRIGHT 128
30 #define TILESELECT 256
31 struct tile
32 {
33 unsigned char x,y;
34 int sx,sy;
35 unsigned h,t;
36 unsigned int flags;
37 } tiles[144];
38 int tilecount;
39
40 int solution[144];
41
42 unsigned char *shadowplane;
43
randomize(void)44 void randomize(void)
45 {
46 struct timeval tv;
47 gettimeofday(&tv,0);
48 srand(tv.tv_sec+tv.tv_usec);
49 }
50
51 int removed,backed;
52
53 /* depth,x,y */
54 int layout[]={
55 1,2,0,
56 1,4,0,
57 1,6,0,
58 1,8,0,
59 1,10,0,
60 1,12,0,
61 1,14,0,
62 1,16,0,
63 1,18,0,
64 1,20,0,
65 1,22,0,
66 1,24,0,
67 1,6,2,
68 2,8,2,
69 2,10,2,
70 2,12,2,
71 2,14,2,
72 2,16,2,
73 2,18,2,
74 1,20,2,
75 1,4,4,
76 1,6,4,
77 2,8,4,
78 3,10,4,
79 3,12,4,
80 3,14,4,
81 3,16,4,
82 2,18,4,
83 1,20,4,
84 1,22,4,
85 1,2,6,
86 1,4,6,
87 1,6,6,
88 2,8,6,
89 3,10,6,
90 4,12,6,
91 4,14,6,
92 3,16,6,
93 2,18,6,
94 1,20,6,
95 1,22,6,
96 1,24,6,
97 1,0,7,
98 1,26,7,
99 1,28,7,
100 -5,13,7,
101 1,2,8,
102 1,4,8,
103 1,6,8,
104 2,8,8,
105 3,10,8,
106 4,12,8,
107 4,14,8,
108 3,16,8,
109 2,18,8,
110 1,20,8,
111 1,22,8,
112 1,24,8,
113 1,4,10,
114 1,6,10,
115 2,8,10,
116 3,10,10,
117 3,12,10,
118 3,14,10,
119 3,16,10,
120 2,18,10,
121 1,20,10,
122 1,22,10,
123 1,6,12,
124 2,8,12,
125 2,10,12,
126 2,12,12,
127 2,14,12,
128 2,16,12,
129 2,18,12,
130 1,20,12,
131 1,2,14,
132 1,4,14,
133 1,6,14,
134 1,8,14,
135 1,10,14,
136 1,12,14,
137 1,14,14,
138 1,16,14,
139 1,18,14,
140 1,20,14,
141 1,22,14,
142 1,24,14,
143 0,
144 };
145
setfreebits(void)146 void setfreebits(void)
147 {
148 int k,tc,x,y;
149 unsigned char bitmap[40][40];
150 struct tile *at;
151
152 memset(bitmap,0,sizeof(bitmap));
153 at=tiles;
154 tc=0;
155 while(tc<144)
156 {
157 at=tiles+tc++;
158 at->flags&=~(TILELOCKED|TILERIGHT1|TILERIGHT2|TILETOP|TILEBOTTOM|TILELEFT);
159 if(at->flags&TILEGONE) continue;
160 k=1<<at->h;
161 x=at->x+2;
162 y=at->y+2;
163 bitmap[x][y]|=k;
164 bitmap[x+1][y]|=k;
165 bitmap[x][y+1]|=k;
166 bitmap[x+1][y+1]|=k;
167 }
168 tc=0;
169 while(tc<144)
170 {
171 at=tiles+tc++;
172 k=1<<at->h;
173 x=at->x+2;
174 y=at->y+2;
175 if(((bitmap[x-1][y]|bitmap[x-1][y+1]) &
176 (bitmap[x+2][y]|bitmap[x+2][y+1]) & k) ||
177 ((bitmap[x][y]|bitmap[x+1][y]|bitmap[x][y+1]|bitmap[x+1][y+1])
178 & (k<<1)))
179 at->flags|=TILELOCKED;
180 if(!((bitmap[x][y-1]|bitmap[x+1][y-1])&k))
181 at->flags|=TILETOP;
182 if(!(bitmap[x+2][y]&k))
183 at->flags|=TILERIGHT1;
184 if(!(bitmap[x+2][y+1]&k))
185 at->flags|=TILERIGHT2;
186 if(!((bitmap[x-1][y]|bitmap[x-1][y+1])&k))
187 at->flags|=TILELEFT;
188 if(!((bitmap[x][y+2]|bitmap[x+1][y+2])&k))
189 at->flags|=TILEBOTTOM;
190 }
191 }
192
intcomp(const void * a,const void * b)193 int intcomp(const void *a,const void *b)
194 {
195 return *(int *)a - *(int *)b;
196 }
match(int a,int b)197 int match(int a,int b)
198 {
199 if(a<34 && b<34)
200 return a==b;
201 if(a>=36 && a<=39 && b>=36 && b<=39) return 1;
202 return a>=40 && a<=43 && b>=40 && b<=43;
203 }
204
scanlayout(int * p)205 void scanlayout(int *p)
206 {
207 int i,j,k,h,a,b;
208 int shuffle[144];
209 int pairs[144];
210
211 for(i=0;i<144;++i)
212 {
213 if(i<34*4) j=i>>2;
214 else j=i-136+36;
215 shuffle[i]=j | (rand()&0xffff00);
216 }
217 qsort(shuffle,144,sizeof(int),intcomp);
218 for(i=0;i<144;++i) shuffle[i]&=255;
219 k=0;
220 for(i=0;i<143;++i)
221 {
222 a=shuffle[i];
223 if(a<0) continue;
224 for(j=i+1;j<144;++j)
225 {
226 b=shuffle[j];
227 if(b<0) continue;
228 if(match(a,b))
229 {
230 pairs[k++]=a;
231 pairs[k++]=b;
232 shuffle[i]=-1;
233 shuffle[j]=-1;
234 break;
235 }
236 }
237 }
238
239 p=layout;
240 tilecount=0;
241 while((h=*p++))
242 {
243 i=*p++;
244 j=*p++;
245 if(h<0)
246 {
247 tiles[tilecount].x=i;
248 tiles[tilecount].y=j;
249 tiles[tilecount].h=-1-h;
250 tiles[tilecount].t=pairs[tilecount];
251 tiles[tilecount].flags=0;
252 ++tilecount;
253 } else
254 for(k=0;k<h;++k)
255 {
256 tiles[tilecount].x=i;
257 tiles[tilecount].y=j;
258 tiles[tilecount].h=k;
259 tiles[tilecount].t=pairs[tilecount];
260 tiles[tilecount].flags=0;
261 ++tilecount;
262 }
263 }
264 retry:
265 for(i=0;i<144;++i)
266 tiles[i].flags=0;
267 k=0;
268 while(k<144)
269 {
270 setfreebits();
271 j=0;
272 for(i=0;i<144;++i)
273 {
274 if(tiles[i].flags&(TILEGONE|TILELOCKED)) continue;
275 //printf("%d:(%d,%d,%d)\n",j,tiles[i].x,tiles[i].y,tiles[i].h);
276 shuffle[j++]=i | (rand()&0xffff00);
277 }
278 if(j<2) goto retry;
279 qsort(shuffle,j,sizeof(int),intcomp);
280 //printf("%d:%d free:taking %d and %d\n",k,j,shuffle[0]&255,shuffle[1]&255);
281 i=shuffle[0]&255;
282 solution[k]=i;
283 tiles[i].flags|=TILEGONE;
284 tiles[i].t=pairs[k++];
285 i=shuffle[1]&255;
286 solution[k]=i;
287 tiles[i].flags|=TILEGONE;
288 tiles[i].t=pairs[k++];
289 }
290 for(i=0;i<144;++i)
291 tiles[i].flags&=~TILEGONE;
292 removed=0;
293 backed=0; // change this to 72 to have solution visible on startup...
294 }
295
clearshadowplane(void)296 void clearshadowplane(void)
297 {
298 memset(shadowplane,0,(vxsize+7)*vysize>>3);
299 }
300
shadowdot(unsigned int x,unsigned int y,int onoff)301 void shadowdot(unsigned int x,unsigned int y,int onoff)
302 {
303 unsigned char *p,bit;
304 if(x>=vxsize || y>=vysize) return;
305 p=shadowplane+(x>>3)+y*((vxsize+7)>>3);
306 bit=1<<(x&7);
307 if(onoff)
308 *p|=bit;
309 else
310 *p&=~bit;
311 }
312
shadowrect(int x,int y,int sizex,int sizey,int onoff)313 void shadowrect(int x,int y,int sizex,int sizey,int onoff)
314 {
315 int i;
316 if(y<0)
317 {
318 sizey+=y;
319 y=0;
320 }
321 if(x<0)
322 {
323 sizex+=x;
324 x=0;
325 }
326 if(x+sizex>vxsize)
327 sizex=vxsize-x;
328 if(y+sizey>vysize)
329 sizey=vysize-y;
330 while(sizey-->0)
331 {
332 i=sizex;
333 while(i-->0)
334 shadowdot(x+i,y,onoff);
335 ++y;
336 }
337 }
338
shadowsolidrect(int x,int y,int xsize,int ysize,int rgb)339 void shadowsolidrect(int x,int y,int xsize,int ysize,int rgb)
340 {
341 solidrect(x,y,xsize,ysize,rgb>>16,rgb>>8,rgb);
342 shadowrect(x,y,xsize,ysize,0);
343 }
344 #define BORDER 0x303030
345
doit(void)346 void doit(void)
347 {
348 int i,j,k,h,h2;
349 int x,y,flags;
350
351 copyfromback(0);
352 drawprintfxy(vxsize-140,vysize-80,msg);
353
354 setfreebits();
355 clearshadowplane();
356 for(i=0;i<144;++i)
357 {
358 j=tiles[i].h<<2;
359 tiles[i].sx=tiles[i].x*21+2+j;
360 tiles[i].sy=tiles[i].y*24+10-j;
361 }
362 for(h=0;h<7;++h)
363 {
364 for(i=0;i<144;++i)
365 {
366 h2=tiles[i].h;
367 if(h2<h) continue;
368 if(tiles[i].flags&TILEGONE) continue;
369 h2=(h2-h)<<3;
370 x=tiles[i].sx+h2;
371 y=tiles[i].sy-h2;
372 flags=tiles[i].flags;
373 if(flags&TILETOP)
374 {
375 shadowrect(x+6,y-8,40,1,1);
376 shadowrect(x+4,y-7,43,1,1);
377 shadowrect(x+2,y-6,43,1,1);
378 shadowrect(x+1,y-5,43,1,1);
379 shadowrect(x,y-4,43,1,1);
380 shadowrect(x-1,y-3,43,1,1);
381 shadowrect(x-2,y-2,43,1,1);
382 shadowrect(x-3,y-1,43,1,1);
383 }
384 if(flags&TILERIGHT1)
385 {
386 shadowrect(x+46,y-6,2,23,1);
387 shadowrect(x+45,y-6,1,24,1);
388 shadowrect(x+44,y-5,1,24,1);
389 shadowrect(x+43,y-4,1,24,1);
390 shadowrect(x+42,y-3,1,24,1);
391 shadowrect(x+41,y-2,1,24,1);
392 shadowrect(x+40,y-1,1,24,1);
393 }
394 if(flags&TILERIGHT2)
395 {
396 shadowrect(x+47,y+24-8,1,24,1);
397 shadowrect(x+46,y+24-7,1,24,1);
398 shadowrect(x+45,y+24-6,1,24,1);
399 shadowrect(x+44,y+24-5,1,24,1);
400 shadowrect(x+43,y+24-4,1,24,1);
401 shadowrect(x+42,y+24-3,1,24,1);
402 shadowrect(x+41,y+24-2,1,24,1);
403 shadowrect(x+40,y+24-1,1,24,1);
404 }
405 shadowrect(x+38,y,2,2,1);
406 }
407
408 for(i=0;i<tilecount;++i)
409 {
410 if(tiles[i].h!=h) continue;
411 flags=tiles[i].flags;
412 if(flags&TILEGONE) continue;
413 x=tiles[i].sx;
414 y=tiles[i].sy;
415 k=tiles[i].t;
416 if(flags&TILELEFT)
417 {
418 shadowsolidrect(x-4,y+2,2,48,0xe7e3c7);
419 shadowsolidrect(x-6,y+4,2,48,0xf7f3f7);
420 }
421 shadowsolidrect(x+38,y+2,2,48,BORDER);
422 shadowsolidrect(x-2,y,42,2,BORDER);
423 if(flags&TILEBOTTOM)
424 {
425 shadowsolidrect(x-2,y+48,40,2,0xe7e3c7);
426 shadowsolidrect(x-4,y+50,40,2,0xf7f3f7);
427 } else
428 {
429 shadowsolidrect(x-2,y+48,42,2,BORDER);
430 }
431 gstoback(x-2,y+2,&tilesgs,42*(k%9),48*(k/9),40,46);
432 if(flags&(TILEBRIGHT|TILESELECT))
433 if(!(flags&TILEBRIGHT) || !(flags&TILESELECT))
434 lightenrect(x-2,y+2,40,46);
435 shadowrect(x-2,y+2,40,46,0);
436 }
437 }
438 applyshadowplane(shadowplane);
439 }
440
newbgset(void)441 void newbgset(void)
442 {
443 int i,j,sx,sy;
444 char temp[256];
445 surface bgset;
446
447 for(;;)
448 {
449 ++bgsetnumber;
450 sprintf(temp,"/usr/local/share/sdl_lopan/data/bg%d.pcx",bgsetnumber);
451 i=readpcx(temp,&bgset);
452 if(i)
453 {
454 if(!bgsetnumber) return;
455 bgsetnumber=-1;
456 } else
457 {
458 j=0;
459 sy=bgset.ysize;
460 while(j<vysize)
461 {
462 i=0;
463 if(j+sy>vysize) sy=vysize-j;
464 sx=bgset.xsize;
465 while(i<vxsize)
466 {
467 if(i+sx>vxsize) sx=vxsize-i;
468 gstoback(i,j,&bgset,0,0,sx,sy);
469 i+=sx;
470 }
471 j+=sy;
472 }
473 copytoback(0);
474 freegs(&bgset);
475 break;
476 }
477 }
478 }
newtileset(void)479 int newtileset(void)
480 {
481 char temp[256];
482 int err;
483
484 freegs(&tilesgs);
485 for(;;)
486 {
487 ++tilesetnumber;
488 sprintf(temp,"/usr/local/share/sdl_lopan/data/tiles%d.pcx",tilesetnumber);
489 err=readpcx(temp,&tilesgs);
490 if(!err) return 0;
491 else if(!tilesetnumber) return err;
492 else tilesetnumber=-1;
493 }
494 }
495
processmouse(int code,int mx,int my)496 int processmouse(int code,int mx,int my)
497 {
498 int i,j,x,y;
499
500 if(code!=MYMOUSE1DOWN) return 0;
501 j=-1;
502 for(i=0;i<144;++i)
503 {
504 x=tiles[i].sx;
505 y=tiles[i].sy;
506 if(mx>=x && mx<x+40 && my>=y && my<y+46)
507 {
508 if(tiles[i].flags&(TILELOCKED|TILEGONE)) continue;
509 if(j<0 || tiles[i].h>tiles[j].h)
510 j=i;
511 }
512 }
513 if(j<0 || (tiles[j].flags&TILELOCKED)) return 0;
514 tiles[j].flags^=TILESELECT;
515 if(tiles[j].flags&TILESELECT)
516 {
517 for(i=0;i<144;++i)
518 if(i!=j && (tiles[i].flags&TILESELECT))
519 break;
520 if(i<144)
521 {
522 if(match(tiles[i].t,tiles[j].t))
523 {
524 backed=0;
525 solution[removed++]=i;
526 solution[removed++]=j;
527 tiles[i].flags|=TILEGONE;
528 tiles[j].flags|=TILEGONE;
529 tiles[i].flags&=~TILESELECT;
530 msg=clickonatile;
531 }
532 tiles[j].flags&=~TILESELECT;
533 } else msg=clickonanothertile;
534 } else
535 msg=clickonatile;
536 return 1;
537 }
538
main(int argc,char ** argv)539 int main(int argc, char **argv)
540 {
541 int i;
542 int code;
543 unsigned char redraw;
544 int backup;
545
546 bgsetnumber=tilesetnumber=-1;
547 randomize();
548 opendisplay(640,400);
549 atexit(closedisplay);
550 initfont();
551 tilesgs.pic=0;
552 if((i=newtileset()))
553 {
554 printf("Failed to load a tile set, code %d\n",i);
555 exit(2);
556 }
557 shadowplane=malloc((vxsize+7)*vysize>>3);
558 if(!shadowplane) nomem(20);
559
560 /*
561 for(j=0;j<vysize;++j)
562 for(i=0;i<vxsize;++i)
563 if((rand()&255)<64)
564 rgbdot(i,j,0x51,0x71,0x10);
565 else
566 rgbdot(i,j,0x00,0x71,0x00);
567 */
568 solidrect(0,0,vxsize,vysize,0x3c,0x54,0x0c);
569 msg=clickonatile;
570
571 copytoback(0);
572 // newbgset();
573
574 scanlayout(layout);
575 redraw=1;
576 backup=0;
577 while(!exitflag)
578 {
579 delay(1);
580
581 while(scaninput(),(code=nextcode())>=0)
582 {
583 if(code&MYMOUSE)
584 {
585 int x,y;
586 x=nextcode();
587 y=nextcode();
588 redraw|=processmouse(code,x,y);
589 } else if(code==13)
590 {
591 scanlayout(layout);
592 i=0;
593 redraw=1;
594 } else if(code==MYLEFT) backup=1;
595 else if(code==MYRIGHT) backup=-1;
596 else if(code==MYF1)
597 {
598 newtileset();
599 redraw=1;
600 }
601 else if(code==MYF2)
602 {
603 newbgset();
604 redraw=1;
605 } else if(code==0x1b) exitflag=1;
606 }
607 if(redraw) {doit();copyup();redraw=0;}
608 if(backup)
609 {
610 if(backup>0)
611 {
612 if(removed)
613 {
614 tiles[solution[--removed]].flags&=~TILEGONE;
615 tiles[solution[--removed]].flags&=~TILEGONE;
616 ++backed;
617 redraw=1;
618 }
619 } else
620 {
621 if(backed)
622 {
623 --backed;
624 tiles[solution[removed++]].flags|=TILEGONE;
625 tiles[solution[removed++]].flags|=TILEGONE;
626 redraw=1;
627 }
628 }
629 backup=0;
630 }
631 }
632 return 0;
633
634 }
635