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