xref: /openbsd-src/sys/ddb/db_hangman.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: db_hangman.c,v 1.28 2008/04/18 06:42:20 djm 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  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  */
28 
29 #include <sys/param.h>
30 
31 #include <uvm/uvm_extern.h>
32 
33 #include <machine/db_machdep.h>
34 
35 #include <ddb/db_sym.h>
36 #include <ddb/db_extern.h>
37 #include <ddb/db_output.h>
38 
39 #include <dev/cons.h>
40 #include <dev/rndvar.h>
41 
42 #define ABC_ISCLR(c)	sabc->abc[(c)-'a']==0
43 #define ABC_ISWRONG(c)	sabc->abc[(c)-'a']=='_'
44 #define ABC_SETWRONG(c)		(sabc->abc[(c)-'a']='_')
45 #define ABC_SETRIGHT(c)		(sabc->abc[(c)-'a']='+')
46 #define ABC_CLR()	memset(sabc->abc,0,sizeof sabc->abc)
47 struct _abc {
48 	char	abc[26+2];	/* for int32 alignment */
49 };
50 
51 #define	TOLOWER(c)	((c)|0x20)
52 #define	ISLOWALPHA(c)	('a'<=(c) && (c)<='z')
53 #define	ISALPHA(c)	ISLOWALPHA(TOLOWER(c))
54 
55 void	 db_hang(int, char *, struct _abc *);
56 
57 u_long		db_plays, db_guesses;
58 
59 static const char hangpic[]=
60 	"\n88888\r\n"
61 	"9 7 6\r\n"
62 	"97  5\r\n"
63 	"9  423\r\n"
64 	"9   2\r\n"
65 	"9  1 0\r\n"
66 	"9\r\n"
67 	"9  ";
68 static const char substchar[]="\\/|\\/O|/-|";
69 
70 static size_t
71 db_random(size_t mod)
72 {
73 	if (cold)
74 		return (random() % mod);
75 	return (arc4random_uniform(mod));
76 }
77 
78 struct db_hang_forall_arg {
79 	int cnt;
80 	db_sym_t sym;
81 };
82 
83 /*
84  * Horrible abuse of the forall function, but we're not in a hurry.
85  */
86 static void db_hang_forall(db_symtab_t *, db_sym_t, char *, char *, int,
87 			void *);
88 
89 static void
90 db_hang_forall(db_symtab_t *stab, db_sym_t sym, char *name, char *suff, int pre,
91     void *varg)
92 {
93 	struct db_hang_forall_arg *arg = (struct db_hang_forall_arg *)varg;
94 
95 	if (arg->cnt-- == 0)
96 		arg->sym = sym;
97 }
98 
99 static __inline char *
100 db_randomsym(size_t *lenp)
101 {
102 	extern db_symtab_t db_symtabs[];
103 	db_symtab_t *stab;
104 	int nsymtabs, nsyms;
105 	char	*p, *q;
106 	struct db_hang_forall_arg dfa;
107 
108 	for (nsymtabs = 0; db_symtabs[nsymtabs].name != NULL; nsymtabs++)
109 		;
110 
111 	if (nsymtabs == 0)
112 		return (NULL);
113 
114 	stab = &db_symtabs[db_random(nsymtabs)];
115 
116 	dfa.cnt = 0;
117 	X_db_forall(stab, db_hang_forall, &dfa);
118 	nsyms = -dfa.cnt;
119 
120 	if (nsyms == 0)
121 		return (NULL);
122 
123 	dfa.cnt = db_random(nsyms);
124 	X_db_forall(stab, db_hang_forall, &dfa);
125 
126 	q = db_qualify(dfa.sym, stab->name);
127 
128 	/* don't show symtab name if there are less than 3 of 'em */
129 	if (nsymtabs < 3)
130 		while (*q++ != ':');
131 
132 	/* strlen(q) && ignoring underscores and colons */
133 	for ((*lenp) = 0, p = q; *p; p++)
134 		if (ISALPHA(*p))
135 			(*lenp)++;
136 
137 	return (q);
138 }
139 
140 void
141 db_hang(int tries, char *word, struct _abc *sabc)
142 {
143 	const char	*p;
144 	int i;
145 	int c;
146 #ifdef ABC_BITMASK
147 	int m;
148 #endif
149 
150 	for (p = hangpic; *p; p++)
151 		cnputc((*p >= '0' && *p <= '9') ? ((tries <= (*p) - '0') ?
152 		    substchar[(*p) - '0'] : ' ') : *p);
153 
154 	for (p = word; *p; p++) {
155 		c = TOLOWER(*p);
156 		cnputc(ISLOWALPHA(c) && ABC_ISCLR(c) ? '-' : *p);
157 	}
158 
159 #ifdef ABC_WRONGSTR
160 	db_printf(" (%s)\r", ABC_WRONGSTR);
161 #else
162 	db_printf(" (");
163 
164 #ifdef ABC_BITMASK
165 	m = sabc->wrong;
166 	for (i = 'a'; i <= 'z'; ++i, m >>= 1)
167 		if (m&1)
168 			cnputc(i);
169 #else
170 	for (i = 'a'; i <= 'z'; ++i)
171 		if (ABC_ISWRONG(i))
172 			cnputc(i);
173 #endif
174 
175 	db_printf(")\r");
176 #endif
177 }
178 
179 void
180 db_hangman(db_expr_t addr, int haddr, db_expr_t count, char *modif)
181 {
182 	char	*word;
183 	size_t	tries;
184 	size_t	len;
185 	struct _abc sabc[1];
186 	int	skill;
187 
188 	if (modif[0] != 's' || (skill = modif[1] - '0') > 9U)
189 		skill = 3;
190 	word = NULL;
191 	tries = 0;
192 	for (;;) {
193 
194 		if (word == NULL) {
195 			ABC_CLR();
196 
197 			tries = skill + 1;
198 			word = db_randomsym(&len);
199 			if (word == NULL)
200 				break;
201 
202 			db_plays++;
203 		}
204 
205 		{
206 			int c;
207 
208 			db_hang(tries, word, sabc);
209 			c = cngetc();
210 			c = TOLOWER(c);
211 
212 			if (ISLOWALPHA(c) && ABC_ISCLR(c)) {
213 				char	*p;
214 				size_t	n;
215 
216 					/* strchr(word,c) */
217 				for (n = 0, p = word; *p ; p++)
218 					if (TOLOWER(*p) == c)
219 						n++;
220 
221 				if (n) {
222 					ABC_SETRIGHT(c);
223 					len -= n;
224 				} else {
225 					ABC_SETWRONG(c);
226 					tries--;
227 				}
228 			}
229 		}
230 
231 		if (tries && len)
232 			continue;
233 
234 		if (!tries && skill > 2) {
235 			char	*p = word;
236 			for (; *p; p++)
237 				if (ISALPHA(*p))
238 					ABC_SETRIGHT(TOLOWER(*p));
239 		}
240 		if (tries)
241 			db_guesses++;
242 		db_hang(tries, word, sabc);
243 		db_printf("\nScore: %lu/%lu\n", db_plays, db_guesses);
244 		word = NULL;
245 		if (tries)
246 			break;
247 	}
248 }
249