xref: /plan9/sys/src/games/memo.c (revision 3039af76362cf40de5148fe2f6aaa092d1b7d350)
1 /* Federico Benavento <benavento@gmail.com> */
2 #include <u.h>
3 #include <libc.h>
4 #include <draw.h>
5 #include <event.h>
6 
7 enum {
8 	Facesize = 48
9 };
10 
11 
12 void memoinit(void);
13 void redraw(void);
14 void eresized(int);
15 void resize(int i);
16 void afaces(void);
17 void allocblocks(void);
18 Image *openface(char *path);
19 
20 Image *face[18];
21 char buf[100];
22 ushort winflag, level;
23 Image *back;
24 Image *fore;
25 
26 enum
27 {
28 	Eninit,
29 	Eshow,
30 	Ehide,
31 	Edisc,
32 };
33 
34 struct
35 {
36 	Image *face;
37 	Rectangle r;
38 	int	flag;
39 }block[36];
40 
41 char *buttons[] =
42 {
43 	"restart",
44 	"easy",
45 	"hard",
46 	"exit",
47 	0
48 };
49 
50 Menu menu =
51 {
52 	buttons
53 };
54 
55 void
main(int argc,char * argv[])56 main(int argc, char *argv[])
57 {
58 	Mouse m;
59 	int i, j;
60 	ushort ran, score, attempt, prev, br[2];
61 	Image *c[2];
62 	char *fmt;
63 
64 	level = 16;
65 	fmt = "win in %d attempts!";
66 
67 	ARGBEGIN{
68 	default:
69 		goto Usage;
70 	case 'h':
71 		level=36;
72 		break;
73 	}ARGEND
74 
75 	if(argc){
76     Usage:
77 		fprint(2, "usage: %s [-h]\n", argv0);
78 		exits("usage");
79 	}
80 	if(initdraw(0,0,"memo") < 0)
81 		sysfatal("initdraw failed: %r");
82 	srand(time(0));
83 	memoinit();
84 	einit(Emouse);
85 
86     Start:
87 	afaces();
88 	winflag=0;
89 	prev=level+1;
90 	score=attempt=0;
91 	for(i=0;i!=level;i++)
92 		block[i].flag = Eninit;
93 
94 	for(i=0;i!=level/2;i++){
95 		for(j=0;j!=2;){
96 			ran = rand()%level;
97 			if(block[ran].flag == Eninit){
98 				block[ran].face = face[i];
99 				block[ran].flag = Eshow;
100 				j++;
101 			}
102 		}
103 	}
104 	eresized(0);
105 	for(;;m=emouse())
106 		if(m.buttons)
107 			break;
108 
109 	for(i=0;i!=level;i++)
110 		block[i].flag = Ehide;
111 
112 	redraw();
113 	j = 0;
114 	for(;; m=emouse()){
115 		switch(m.buttons){
116 		case 1:
117 			while(m.buttons){
118 				for(i=0;i!=level;i++){
119 					if(i!=prev && ptinrect(m.xy,block[i].r)){
120 						if(block[i].flag==Ehide  && j<2){
121 							block[i].flag = Eshow;
122 							draw(screen, block[i].r, block[i].face, nil, ZP);
123 							c[j] = block[i].face;
124 							br[j] = prev = i;
125 							j++;
126 						}
127 						break;
128 					}
129 				}
130 				m=emouse();
131 			}
132 			break;
133 		case 4:
134 			switch(emenuhit(3, &m, &menu)) {
135 			case 0:	/* restart */
136 				goto Start;
137 				break;
138 			case 1:
139 				level=16;
140 				goto Start;
141 				break;
142 			case 2:
143 				level=36;
144 				goto Start;
145 				break;
146 			case 3:
147 				exits(0);
148 				break;
149 			}
150 		}
151 		if(j==2){
152 			attempt++;
153 			prev = level+1;
154 			j = 0;
155 			if(c[0] == c[1]){
156 				score++;
157 				block[br[0]].flag = Edisc;
158 				block[br[1]].flag = Edisc;
159 			} else{
160 				block[br[0]].flag = Ehide;
161 				block[br[1]].flag = Ehide;
162 			}
163 			redraw();
164 			continue;
165 		}
166 		if(score == level/2){
167 			winflag = 1;
168 			sprint(buf, fmt, attempt);
169 			redraw();
170 			for(;;m=emouse())
171 				if(m.buttons&1 || m.buttons&4)
172 					break;
173 			goto Start;
174 		}
175 	}
176 }
177 
178 void
memoinit(void)179 memoinit(void)
180 {
181 	back = allocimagemix(display, DPalebluegreen,DWhite);
182 	fore = allocimagemix(display, 0x00DDDDFF, 0x00DDDDFF);
183 }
184 
185 void
eresized(int new)186 eresized(int new)
187 {
188 	double sq;
189 	Point p;
190 
191 	if(new && getwindow(display, Refnone) < 0){
192 		fprint(2, "can't reattach to window");
193 		exits("resized");
194 	}
195 
196 	sq = sqrt(level);
197 	p = Pt(Dx(screen->r)+8, Dy(screen->r)+8);
198 	if(!new || !eqpt(p, Pt(Facesize*sq+sq*4+17, Facesize*sq+sq*4+17)))
199 		resize(Facesize*sq+sq*4+17);
200 
201 	allocblocks();
202 	draw(screen, screen->r, back, nil, ZP);
203 	redraw();
204 }
205 
206 void
redraw(void)207 redraw(void)
208 {
209 	int i;
210 	Rectangle r;
211 	Point p;
212 
213 	if(winflag == 1){
214 		p = Pt(Dx(screen->r)/8, Dy(screen->r)/4);
215 		r = screen->r;
216 		r.min = addpt(r.min, p);
217 		r.max = subpt(r.max, p);
218 		draw(screen, r, fore, nil, ZP);
219 		p=addpt(r.min, Pt(5,5));
220 		string(screen,p,display->black,ZP,font,buf);
221 		return;
222 	}
223 
224 	for(i=0;i!=level;i++){
225 		r = block[i].r;
226 		switch(block[i].flag){
227 		case Eshow:
228 			draw(screen, r,block[i].face,nil,ZP);
229 			break;
230 		case Edisc:
231 			draw(screen, r, back, nil, ZP);
232 			break;
233 		case Ehide:
234 			draw(screen, r, fore, nil, ZP);
235 			break;
236 		default:
237 			fprint(2, "something went wrong!");
238 			exits("wrong");
239 			break;
240 		}
241 	}
242 }
243 
244 char *facepaths[] = {
245 	/* logos */
246 	"/lib/face/48x48x4/g/glenda.1",
247 	"/lib/face/48x48x2/p/pjw+9ball.2",
248 
249 	/* /sys/doc/9.ms authors */
250 	"/lib/face/48x48x2/k/ken.1",
251 	"/lib/face/48x48x4/b/bobf.1",
252 	"/lib/face/48x48x4/p/philw.1",
253 	"/lib/face/48x48x4/p/presotto.1",
254 	"/lib/face/48x48x4/r/rob.1",
255 	"/lib/face/48x48x4/s/sean.1",
256 
257 	/* additional authors and luminaries for harder levels */
258 	"/lib/face/48x48x4/b/bwk.1",
259 	"/lib/face/48x48x4/c/cyoung.1",
260 	"/lib/face/48x48x4/d/dmr.1",
261 	"/lib/face/48x48x4/d/doug.1",
262 	"/lib/face/48x48x4/h/howard.1",
263 	"/lib/face/48x48x4/j/jmk.1",
264 	"/lib/face/48x48x4/s/sape.1",
265 	"/lib/face/48x48x4/s/seanq.1",
266 	"/lib/face/48x48x4/t/td.1",
267 	"/lib/face/48x48x8/l/lucent.1",
268 };
269 
270 void
afaces(void)271 afaces(void)
272 {
273 	int i;
274 
275 	for(i=0; i<18; i++)
276 		face[i] = openface(facepaths[i]);
277 }
278 
279 void
resize(int i)280 resize(int i)
281 {
282 	int fd;
283 
284 	fd = open("/dev/wctl", OWRITE);
285 	if(fd >= 0){
286 		fprint(fd, "resize -dx %d -dy %d", i, i);
287 		close(fd);
288 	}
289 }
290 
291 Image *
openimage(char * path)292 openimage(char *path)
293 {
294 	Image *i;
295 	int fd;
296 
297 	fd = open(path, OREAD);
298 	if(fd < 0)
299 		sysfatal("open %s: %r", path);
300 	i = readimage(display, fd, 0);
301 	if(i == nil)
302 		sysfatal("readimage %s: %r", path);
303 	close(fd);
304 	return i;
305 }
306 
307 void
allocblocks(void)308 allocblocks(void)
309 {
310 	Rectangle r, b;
311 	ushort i, x, y, sq;
312 
313 	sq = sqrt(level);
314 	r = insetrect(screen->r, 5);
315 	r.max.x = r.min.x+Facesize*sq+sq*4-1;
316 	r.max.y = r.min.y+Facesize*sq+sq*4-1;
317 	b.max.y = r.min.y;
318 	for(i=level-1, y=0; y!=sq; y++){
319 		b.min.y = b.max.y;
320 		b.max.y = r.min.y+Dy(r)*(y+1)/sq;
321 		b.max.x = r.min.x;
322 		for(x=0; x!=sq; x++, i--){
323 			b.min.x = b.max.x;
324 			b.max.x = r.min.x+Dx(r)*(x+1)/sq;
325 			block[i].r = insetrect(b, 2 );
326 		}
327 	}
328 }
329 
330 Image*
readbit(int fd,ulong chan,char * path)331 readbit(int fd, ulong chan, char *path)
332 {
333 	char buf[4096], hx[4], *p;
334 	uchar data[Facesize*Facesize];	/* more than enough */
335 	int nhx, i, n, ndata, nbit;
336 	Image *img;
337 
338 	n = readn(fd, buf, sizeof buf);
339 	if(n <= 0)
340 		return nil;
341 	if(n >= sizeof buf)
342 		n = sizeof(buf)-1;
343 	buf[n] = '\0';
344 
345 	n = 0;
346 	nhx = 0;
347 	nbit = chantodepth(chan);
348 	ndata = (Facesize*Facesize*nbit)/8;
349 	p = buf;
350 	while(n < ndata) {
351 		p = strpbrk(p+1, "0123456789abcdefABCDEF");
352 		if(p == nil)
353 			break;
354 		if(p[0] == '0' && p[1] == 'x')
355 			continue;
356 
357 		hx[nhx] = *p;
358 		if(++nhx == 2) {
359 			hx[nhx] = 0;
360 			i = strtoul(hx, 0, 16);
361 			data[n++] = ~i;
362 			nhx = 0;
363 		}
364 	}
365 	if(n < ndata)
366 		sysfatal("short face %s", path);
367 
368 	img = allocimage(display, Rect(0,0,Facesize,Facesize), chan, 0, 0);
369 	if(img == nil)
370 		return nil;
371 
372 	loadimage(img, img->r, data, ndata);
373 	return img;
374 }
375 
376 Image*
openface(char * path)377 openface(char *path)
378 {
379 	char *p;
380 	int fd, n;
381 
382 	p = strstr(path, "48x48x");
383 	if(p == nil)
384 		return openimage(path);
385 	n = atoi(p+6);
386 	if(n < 4){
387 		if((fd = open(path, OREAD)) < 0)
388 			sysfatal("open %s: %r", path);
389 		return readbit(fd, n==1 ? GREY1 : GREY2, path);
390 	}
391 	return openimage(path);
392 }
393