xref: /openbsd-src/sys/ddb/db_hangman.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: db_hangman.c,v 1.18 2001/06/29 05:28:18 mickey Exp $	*/
2 
3 /*
4  * Copyright (c) 1996 Theo de Raadt, Michael Shalayeff
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by Michael Shalayeff.
18  * 4. The name of the authors may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  */
33 
34 #include <sys/param.h>
35 
36 #include <vm/vm.h>
37 
38 #include <machine/db_machdep.h>
39 
40 #include <ddb/db_sym.h>
41 #include <ddb/db_extern.h>
42 #include <ddb/db_output.h>
43 
44 #include <dev/cons.h>
45 #include <dev/rndvar.h>
46 
47 #define	TOLOWER(c)	(('A'<=(c)&&(c)<='Z')?(c)-'A'+'a':(c))
48 #define	ISALPHA(c)	(('a'<=(c)&&(c)<='z')||('A'<=(c)&&(c)<='Z'))
49 
50 void	 db_hang __P((int, char *, char *));
51 
52 static int	skill;
53 u_long		db_plays, db_guesses;
54 
55 static __inline size_t
56 db_random(size_t mod)
57 {
58 	return arc4random() % mod;
59 }
60 
61 struct db_hang_forall_arg {
62 	int cnt;
63 	db_sym_t sym;
64 };
65 
66 /*
67  * Horrible abuse of the forall function, but we're not in a hurry.
68  */
69 static void db_hang_forall __P((db_symtab_t *, db_sym_t, char *, char *, int,
70 			void *));
71 
72 static void
73 db_hang_forall(stab, sym, name, suff, pre, varg)
74 	db_symtab_t *stab;
75 	db_sym_t sym;
76 	char *name;
77 	char *suff;
78 	int pre;
79 	void *varg;
80 {
81 	struct db_hang_forall_arg *arg = (struct db_hang_forall_arg *)varg;
82 
83 	if (--arg->cnt == 0)
84 		arg->sym = sym;
85 }
86 
87 static __inline char *
88 db_randomsym(size_t *lenp)
89 {
90 	extern db_symtab_t db_symtabs[];
91 	db_symtab_t *stab;
92 	int nsymtabs, nsyms;
93 	char	*p, *q;
94 	struct db_hang_forall_arg dfa;
95 
96 	for (nsymtabs = 0; db_symtabs[nsymtabs].name != NULL; nsymtabs++)
97 		;
98 
99 	if (nsymtabs == 0)
100 		return (NULL);
101 
102 	stab = &db_symtabs[db_random(nsymtabs)];
103 
104 	dfa.cnt = 1000000;
105 	X_db_forall(stab, db_hang_forall, &dfa);
106 	if (dfa.cnt <= 0)
107 		return (NULL);
108 	nsyms = 1000000 - dfa.cnt;
109 
110 	if (nsyms == 0)
111 		return (NULL);
112 
113 	dfa.cnt = db_random(nsyms);
114 	X_db_forall(stab, db_hang_forall, &dfa);
115 
116 	q = db_qualify(dfa.sym, stab->name);
117 
118 	/* don't show symtab name if there are less than 3 of 'em */
119 	if (nsymtabs < 3)
120 		while(*q++ != ':');
121 
122 	/* strlen(q) && ignoring underscores and colons */
123 	for ((*lenp) = 0, p = q; *p; p++)
124 		if (ISALPHA(*p))
125 			(*lenp)++;
126 
127 	return (q);
128 }
129 
130 static const char hangpic[]=
131 	"\n88888 \r\n"
132 	"9 7 6 \r\n"
133 	"97  5 \r\n"
134 	"9  423\r\n"
135 	"9   2 \r\n"
136 	"9  1 0\r\n"
137 	"9\r\n"
138 	"9  ";
139 static const char substchar[]="\\/|\\/O|/-|";
140 
141 void
142 db_hang(tries, word, abc)
143 	int	tries;
144 	register char	*word;
145 	register char	*abc;
146 {
147 	register const char	*p;
148 
149 	for(p = hangpic; *p; p++)
150 		cnputc((*p >= '0' && *p <= '9') ? ((tries <= (*p) - '0') ?
151 		    substchar[(*p) - '0'] : ' ') : *p);
152 
153 	for (p = word; *p; p++)
154 		cnputc(ISALPHA(*p) && abc[TOLOWER(*p) - 'a'] == '-'? '-' : *p);
155 
156 	db_printf(" (");
157 
158 	for (p = abc; *p; p++)
159 		if (*p == '_')
160 			cnputc('a' + (p - abc));
161 
162 	db_printf(")\r");
163 }
164 
165 
166 static __inline int
167 db_hangon(void)
168 {
169 	static size_t	len;
170 	static size_t	tries;
171 	static char	*word = NULL;
172 	static char	abc[26+1];	/* for '\0' */
173 
174 	if (word == NULL) {
175 		register char	*p;
176 
177 		for (p = abc; p < &abc[sizeof(abc)-1]; p++)
178 			*p = '-';
179 		*p = '\0';
180 
181 		tries = skill + 1;
182 		word = db_randomsym(&len);
183 		if (word == NULL)
184 			return (0);
185 
186 		db_plays++;
187 	}
188 
189 	{
190 		register char	c;
191 
192 		db_hang(tries, word, abc);
193 		c = cngetc();
194 		c = TOLOWER(c);
195 
196 		if (ISALPHA(c) && abc[c - 'a'] == '-') {
197 			register char	*p;
198 			register size_t	n;
199 
200 				/* strchr(word,c) */
201 			for (n = 0, p = word; *p ; p++)
202 				if (TOLOWER(*p) == c)
203 					n++;
204 
205 			if (n) {
206 				abc[c - 'a'] = c;
207 				len -= n;
208 			} else {
209 				abc[c - 'a'] = '_';
210 				tries--;
211 			}
212 		}
213 	}
214 
215 	if (tries && len)
216 		return (1);
217 
218 	if (!tries && skill > 2) {
219 		register char	*p = word;
220 		for (; *p; p++)
221 			if (ISALPHA(*p))
222 				abc[TOLOWER(*p) - 'a'] = *p;
223 	}
224 	db_hang(tries, word, abc);
225 	db_printf("\nScore: %lu/%lu\n", db_plays, ++db_guesses);
226 	word = NULL;
227 
228 	return (!tries);
229 }
230 
231 void
232 db_hangman(addr, haddr, count, modif)
233 	db_expr_t addr;
234 	int	haddr;
235 	db_expr_t count;
236 	char	*modif;
237 {
238 	if (modif[0] == 's' && '0' <= modif[1] && modif[1] <= '9')
239 		skill = modif[1] - '0';
240 	else
241 		skill = 3;
242 
243 	while (db_hangon());
244 }
245