xref: /original-bsd/games/snake/snake/move.c (revision c577960b)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 static char sccsid[] = "@(#)move.c	5.6 (Berkeley) 06/18/88";
20 #endif /* not lint */
21 
22 /*************************************************************************
23  *
24  *	MOVE LIBRARY
25  *
26  *	This set of subroutines moves a cursor to a predefined
27  *	location, independent of the terminal type.  If the
28  *	terminal has an addressable cursor, it uses it.  If
29  *	not, it optimizes for tabs (currently) even if you don't
30  *      have them.
31  *
32  *	At all times the current address of the cursor must be maintained,
33  *	and that is available as structure cursor.
34  *
35  *	The following calls are allowed:
36  *		move(sp)	move to point sp.
37  *		up()		move up one line.
38  *		down()		move down one line.
39  *		bs()		move left one space (except column 0).
40  *		nd()		move right one space(no write).
41  *		clear()		clear screen.
42  *		home()		home.
43  *		ll()		move to lower left corner of screen.
44  *		cr()		carriage return (no line feed).
45  *		printf()	just like standard printf, but keeps track
46  *				of cursor position. (Uses pstring).
47  *		aprintf()	same as printf, but first argument is &point.
48  *				(Uses pstring).
49  *		pstring(s)	output the string of printing characters.
50  *				However, '\r' is interpreted to mean return
51  *				to column of origination AND do linefeed.
52  *				'\n' causes <cr><lf>.
53  *		putpad(str)	calls tputs to output character with proper
54  *					padding.
55  *		outch()		the output routine for a character used by
56  *					tputs. It just calls putchar.
57  *		pch(ch)		output character to screen and update
58  *					cursor address (must be a standard
59  *					printing character). WILL SCROLL.
60  *		pchar(ps,ch)	prints one character if it is on the
61  *					screen at the specified location;
62  *					otherwise, dumps it.(no wrap-around).
63  *
64  *		getcap()	initializes strings for later calls.
65  *		cap(string)	outputs the string designated in the termcap
66  *					data base. (Should not move the cursor.)
67  *		done()		returns the terminal to intial state and exits.
68  *
69  *		point(&p,x,y)	return point set to x,y.
70  *
71  *		baudrate(x)	returns the baudrate of the terminal.
72  *		delay(t)	causes an approximately constant delay
73  *					independent of baudrate.
74  *					Duration is ~ t/20 seconds.
75  *
76  ******************************************************************************/
77 
78 #include "snake.h"
79 
80 int CMlength;
81 int NDlength;
82 int BSlength;
83 int delaystr[10];
84 short ospeed;
85 
86 static char str[80];
87 
88 move(sp)
89 struct point *sp;
90 {
91 	int distance;
92 	int tabcol,ct;
93 	struct point z;
94 
95 	if (sp->line <0 || sp->col <0 || sp->col > COLUMNS){
96 		printf("move to [%d,%d]?",sp->line,sp->col);
97 		return;
98 	}
99 	if (sp->line >= LINES){
100 		move(point(&z,sp->col,LINES-1));
101 		while(sp->line-- >= LINES)putchar('\n');
102 		return;
103 	}
104 
105 	if (CM != 0) {
106 		char *cmstr = tgoto(CM, sp->col, sp->line);
107 
108 		CMlength = strlen(cmstr);
109 		if(cursor.line == sp->line){
110 			distance = sp->col - cursor.col;
111 			if(distance == 0)return;	/* Already there! */
112 			if(distance > 0){	/* Moving to the right */
113 				if(distance*NDlength < CMlength){
114 					right(sp);
115 					return;
116 				}
117 				if(TA){
118 					ct=sp->col&7;
119 					tabcol=(cursor.col|7)+1;
120 					do{
121 						ct++;
122 						tabcol=(tabcol|7)+1;
123 					}
124 					while(tabcol<sp->col);
125 					if(ct<CMlength){
126 						right(sp);
127 						return;
128 					}
129 				}
130 			} else {		/* Moving to the left */
131 				if (-distance*BSlength < CMlength){
132 					gto(sp);
133 					return;
134 				}
135 			}
136 			if(sp->col < CMlength){
137 				cr();
138 				right(sp);
139 				return;
140 			}
141 				/* No more optimizations on same row. */
142 		}
143 		distance = sp->col - cursor.col;
144 		distance = distance > 0 ?
145 			distance*NDlength : -distance * BSlength;
146 if(distance < 0)printf("ERROR: distance is negative: %d",distance);
147 		distance += abs(sp->line - cursor.line);
148 		if(distance >= CMlength){
149 			putpad(cmstr);
150 			cursor.line = sp->line;
151 			cursor.col = sp->col;
152 			return;
153 		}
154 	}
155 
156 	/*
157 	 * If we get here we have a terminal that can't cursor
158 	 * address but has local motions or one which can cursor
159 	 * address but can get there quicker with local motions.
160 	 */
161 	 gto(sp);
162 }
163 gto(sp)
164 struct point *sp;
165 {
166 
167 	int distance,f,tfield,j;
168 
169 	if (cursor.line > LINES || cursor.line <0 ||
170 	    cursor.col <0 || cursor.col > COLUMNS)
171 		printf("ERROR: cursor is at %d,%d\n",
172 			cursor.line,cursor.col);
173 	if (sp->line > LINES || sp->line <0 ||
174 	    sp->col <0 || sp->col >  COLUMNS)
175 		printf("ERROR: target is %d,%d\n",sp->line,sp->col);
176 	tfield = (sp->col) >> 3;
177 	if (sp->line == cursor.line){
178 		if (sp->col > cursor.col)right(sp);
179 		else{
180 			distance = (cursor.col -sp->col)*BSlength;
181 			if (((TA) &&
182 			     (distance > tfield+((sp->col)&7)*NDlength)
183 			    ) ||
184 			    (((cursor.col)*NDlength) < distance)
185 			   ){
186 				cr();
187 				right(sp);
188 			}
189 			else{
190 				while(cursor.col > sp->col) bs();
191 			}
192 		}
193 		return;
194 	}
195 				/*must change row */
196 	if (cursor.col - sp->col > (cursor.col >> 3)){
197 		if (cursor.col == 0)f = 0;
198 		else f = -1;
199 	}
200 	else f = cursor.col >> 3;
201 	if (((sp->line << 1) + 1 < cursor.line - f) && (HO != 0)){
202 			/*
203 			 * home quicker than rlf:
204 			 * (sp->line + f > cursor.line - sp->line)
205 			 */
206 		putpad(HO);
207 		cursor.col = cursor.line = 0;
208 		gto(sp);
209 		return;
210 	}
211 	if (((sp->line << 1) > cursor.line + LINES+1 + f) && (LL != 0)){
212 		/* home,rlf quicker than lf
213 		 * (LINES+1 - sp->line + f < sp->line - cursor.line)
214 		 */
215 		if (cursor.line > f + 1){
216 		/* is home faster than wraparound lf?
217 		 * (cursor.line + 20 - sp->line > 21 - sp->line + f)
218 		 */
219 			ll();
220 			gto(sp);
221 			return;
222 		}
223 	}
224 	if ((LL != 0) && (sp->line > cursor.line + (LINES >> 1) - 1))
225 		cursor.line += LINES;
226 	while(sp->line > cursor.line)down();
227 	while(sp->line < cursor.line)up();
228 	gto(sp);		/*can recurse since cursor.line = sp->line */
229 }
230 
231 right(sp)
232 struct point *sp;
233 {
234 	int field,tfield;
235 	int tabcol,strlength;
236 
237 	if (sp->col < cursor.col)
238 		printf("ERROR:right() can't move left\n");
239 	if(TA){		/* If No Tabs: can't send tabs because ttydrive
240 			 * loses count with control characters.
241 			 */
242 		field = cursor.col >> 3;
243 /*
244  *	This code is useful for a terminal which wraps around on backspaces.
245  *	(Mine does.)  Unfortunately, this is not specified in termcap, and
246  *	most terminals don't work that way.  (Of course, most terminals
247  *	have addressible cursors, too).
248  */
249 		if (BW && (CM == 0) &&
250 		    ((sp->col << 1) - field > (COLUMNS - 8) << 1 )
251 		   ){
252 	 		if (cursor.line == 0){
253 	 			outch('\n');
254 	 		}
255 	 		outch('\r');
256 	 		cursor.col = COLUMNS + 1;
257 	 		while(cursor.col > sp->col)bs();
258 	 		if (cursor.line != 0) outch('\n');
259 	 		return;
260 	 	}
261 
262 		tfield = sp->col >> 3;
263 
264 		while (field < tfield){
265 			putpad(TA);
266 			cursor.col = ++field << 3;
267 		}
268 		tabcol = (cursor.col|7) + 1;
269 		strlength = (tabcol - sp->col)*BSlength + 1;
270 		/* length of sequence to overshoot */
271 		if (((sp->col - cursor.col)*NDlength > strlength) &&
272 		    (tabcol < COLUMNS)
273 		   ){
274 			/*
275 			 * Tab past and backup
276 			 */
277 			putpad(TA);
278 			cursor.col = (cursor.col | 7) + 1;
279 			while(cursor.col > sp->col)bs();
280 		}
281 	}
282 	while (sp->col > cursor.col){
283 		nd();
284 	}
285 }
286 
287 cr(){
288 	outch('\r');
289 	cursor.col = 0;
290 }
291 
292 clear(){
293 	int i;
294 
295 	if (CL){
296 		putpad(CL);
297 		cursor.col=cursor.line=0;
298 	} else {
299 		for(i=0; i<LINES; i++) {
300 			putchar('\n');
301 		}
302 		cursor.line = LINES - 1;
303 		home();
304 	}
305 }
306 
307 home(){
308 	struct point z;
309 
310 	if(HO != 0){
311 		putpad(HO);
312 		cursor.col = cursor.line = 0;
313 		return;
314 	}
315 	z.col = z.line = 0;
316 	move(&z);
317 }
318 
319 ll(){
320 	int j,l;
321 	struct point z;
322 
323 	l = lcnt + 2;
324 	if(LL != NULL && LINES==l){
325 		putpad(LL);
326 		cursor.line = LINES-1;
327 		cursor.col = 0;
328 		return;
329 	}
330 	z.col = 0;
331 	z.line = l-1;
332 	move(&z);
333 }
334 
335 up(){
336 	putpad(UP);
337 	cursor.line--;
338 }
339 
340 down(){
341 	putpad(DO);
342 	cursor.line++;
343 	if (cursor.line >= LINES)cursor.line=LINES-1;
344 }
345 bs(){
346 	if (cursor.col > 0){
347 		putpad(BS);
348 		cursor.col--;
349 	}
350 }
351 
352 nd(){
353 	putpad(ND);
354 	cursor.col++;
355 	if (cursor.col == COLUMNS+1){
356 		cursor.line++;
357 		cursor.col = 0;
358 		if (cursor.line >= LINES)cursor.line=LINES-1;
359 	}
360 }
361 
362 pch(c)
363 {
364 	outch(c);
365 	if(++cursor.col >= COLUMNS && AM) {
366 		cursor.col = 0;
367 		++cursor.line;
368 	}
369 }
370 
371 aprintf(ps,st,v0,v1,v2,v3,v4,v5,v6,v7,v8,v9)
372 struct point *ps;
373 char *st;
374 int v0,v1,v2,v3,v4,v5,v6,v7,v8,v9;
375 
376 {
377 	struct point p;
378 
379 	p.line = ps->line+1; p.col = ps->col+1;
380 	move(&p);
381 	(void)sprintf(str,st,v0,v1,v2,v3,v4,v5,v6,v7,v8,v9);
382 	pstring(str);
383 }
384 
385 printf(st,v0,v1,v2,v3,v4,v5,v6,v7,v8,v9)
386 char *st;
387 int v0,v1,v2,v3,v4,v5,v6,v7,v8,v9;
388 {
389 	(void)sprintf(str,st,v0,v1,v2,v3,v4,v5,v6,v7,v8,v9);
390 	pstring(str);
391 }
392 
393 pstring(s)
394 char *s;{
395 	struct point z;
396 	int stcol;
397 
398 	stcol = cursor.col;
399 	while (s[0] != '\0'){
400 		switch (s[0]){
401 		case '\n':
402 			move(point(&z,0,cursor.line+1));
403 			break;
404 		case '\r':
405 			move(point(&z,stcol,cursor.line+1));
406 			break;
407 		case '\t':
408 			z.col = (((cursor.col + 8) >> 3) << 3);
409 			z.line = cursor.line;
410 			move(&z);
411 			break;
412 		case '\b':
413 			bs();
414 			break;
415 		case CTRL('g'):
416 			outch(CTRL('g'));
417 			break;
418 		default:
419 			if (s[0] < ' ')break;
420 			pch(s[0]);
421 		}
422 		s++;
423 	}
424 }
425 
426 pchar(ps,ch)
427 struct point *ps;
428 char ch;{
429 	struct point p;
430 	p.col = ps->col + 1; p.line = ps->line + 1;
431 	if (
432 		(p.col >= 0) &&
433 		(p.line >= 0) &&
434 		(
435 			(
436 				(p.line < LINES) &&
437 				(p.col < COLUMNS)
438 			) ||
439 			(
440 	    			(p.col == COLUMNS) &&
441 				(p.line < LINES-1)
442 			)
443 	  	)
444 	){
445 		move(&p);
446 		pch(ch);
447 	}
448 }
449 
450 
451 outch(c)
452 {
453 	putchar(c);
454 }
455 
456 putpad(str)
457 char *str;
458 {
459 	if (str)
460 		tputs(str, 1, outch);
461 }
462 baudrate()
463 {
464 
465 	switch (orig.sg_ospeed){
466 	case B300:
467 		return(300);
468 	case B1200:
469 		return(1200);
470 	case B4800:
471 		return(4800);
472 	case B9600:
473 		return(9600);
474 	default:
475 		return(0);
476 	}
477 }
478 delay(t)
479 int t;
480 {
481 	int k,j;
482 
483 	k = baudrate() * t / 300;
484 	for(j=0;j<k;j++){
485 		putchar(PC);
486 	}
487 }
488 
489 done()
490 {
491 	cook();
492 	exit(0);
493 }
494 
495 cook()
496 {
497 	delay(1);
498 	putpad(TE);
499 	putpad(KE);
500 	fflush(stdout);
501 	stty(0, &orig);
502 #ifdef TIOCSLTC
503 	ioctl(0, TIOCSLTC, &olttyc);
504 #endif
505 }
506 
507 raw()
508 {
509 	stty(0, &new);
510 #ifdef TIOCSLTC
511 	ioctl(0, TIOCSLTC, &nlttyc);
512 #endif
513 }
514 
515 struct point *point(ps,x,y)
516 struct point *ps;
517 int x,y;
518 {
519 	ps->col=x;
520 	ps->line=y;
521 	return(ps);
522 }
523 
524 char *ap;
525 
526 getcap()
527 {
528 	char *getenv();
529 	char *term;
530 	char *xPC;
531 	struct point z;
532 	int stop();
533 
534 	term = getenv("TERM");
535 	if (term==0) {
536 		fprintf(stderr, "No TERM in environment\n");
537 		exit(1);
538 	}
539 
540 	switch (tgetent(tbuf, term)) {
541 	case -1:
542 		fprintf(stderr, "Cannot open termcap file\n");
543 		exit(2);
544 	case 0:
545 		fprintf(stderr, "%s: unknown terminal", term);
546 		exit(3);
547 	}
548 
549 	ap = tcapbuf;
550 
551 	LINES = tgetnum("li");
552 	COLUMNS = tgetnum("co");
553 	if (!lcnt)
554 		lcnt = LINES - 2;
555 	if (!ccnt)
556 		ccnt = COLUMNS - 3;
557 
558 	AM = tgetflag("am");
559 	BW = tgetflag("bw");
560 
561 	ND = tgetstr("nd", &ap);
562 	UP = tgetstr("up", &ap);
563 
564 	DO = tgetstr("do", &ap);
565 	if (DO == 0)
566 		DO = "\n";
567 
568 	BS = tgetstr("bc", &ap);
569 	if (BS == 0 && tgetflag("bs"))
570 		BS = "\b";
571 	if (BS)
572 		xBC = *BS;
573 
574 	TA = tgetstr("ta", &ap);
575 	if (TA == 0 && tgetflag("pt"))
576 		TA = "\t";
577 
578 	HO = tgetstr("ho", &ap);
579 	CL = tgetstr("cl", &ap);
580 	CM = tgetstr("cm", &ap);
581 	LL = tgetstr("ll", &ap);
582 
583 	KL = tgetstr("kl", &ap);
584 	KR = tgetstr("kr", &ap);
585 	KU = tgetstr("ku", &ap);
586 	KD = tgetstr("kd", &ap);
587 	Klength = strlen(KL);
588 		/*	NOTE:   If KL, KR, KU, and KD are not
589 		 *		all the same length, some problems
590 		 *		may arise, since tests are made on
591 		 *		all of them together.
592 		 */
593 
594 	TI = tgetstr("ti", &ap);
595 	TE = tgetstr("te", &ap);
596 	KS = tgetstr("ks", &ap);
597 	KE = tgetstr("ke", &ap);
598 
599 	xPC = tgetstr("pc", &ap);
600 	if (xPC)
601 		PC = *xPC;
602 
603 	NDlength = strlen(ND);
604 	BSlength = strlen(BS);
605 	if ((CM == 0) &&
606 		(HO == 0 | UP==0 || BS==0 || ND==0)) {
607 		fprintf(stderr, "Terminal must have addressible ");
608 		fprintf(stderr, "cursor or home + 4 local motions\n");
609 		exit(5);
610 	}
611 	if (tgetflag("os")) {
612 		fprintf(stderr, "Terminal must not overstrike\n");
613 		exit(5);
614 	}
615 	if (LINES <= 0 || COLUMNS <= 0) {
616 		fprintf(stderr, "Must know the screen size\n");
617 		exit(5);
618 	}
619 
620 	gtty(0, &orig);
621 	new=orig;
622 	new.sg_flags &= ~(ECHO|CRMOD|ALLDELAY|XTABS);
623 	new.sg_flags |= CBREAK;
624 	signal(SIGINT,stop);
625 	ospeed = orig.sg_ospeed;
626 #ifdef TIOCGLTC
627 	ioctl(0, TIOCGLTC, &olttyc);
628 	nlttyc = olttyc;
629 	nlttyc.t_suspc = '\377';
630 	nlttyc.t_dsuspc = '\377';
631 #endif
632 	raw();
633 
634 	if ((orig.sg_flags & XTABS) == XTABS) TA=0;
635 	putpad(KS);
636 	putpad(TI);
637 	point(&cursor,0,LINES-1);
638 }
639