xref: /openbsd/sys/ddb/db_hangman.c (revision 09467b48)
1 /*	$OpenBSD: db_hangman.c,v 1.37 2017/05/30 15:39:05 mpi 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 #include <sys/systm.h>
31 
32 #include <machine/db_machdep.h>
33 
34 #include <ddb/db_sym.h>
35 #include <ddb/db_output.h>
36 
37 #include <dev/cons.h>
38 
39 #define ABC_ISCLR(c)	sabc->abc[(c)-'a']==0
40 #define ABC_ISWRONG(c)	sabc->abc[(c)-'a']=='_'
41 #define ABC_SETWRONG(c)		(sabc->abc[(c)-'a']='_')
42 #define ABC_SETRIGHT(c)		(sabc->abc[(c)-'a']='+')
43 #define ABC_CLR()	memset(sabc->abc,0,sizeof sabc->abc)
44 struct _abc {
45 	char	abc[26+2];	/* for int32 alignment */
46 };
47 
48 #define	TOLOWER(c)	((c)|0x20)
49 #define	ISLOWALPHA(c)	('a'<=(c) && (c)<='z')
50 #define	ISALPHA(c)	ISLOWALPHA(TOLOWER(c))
51 
52 void	 db_hang(int, char *, struct _abc *);
53 
54 u_long		db_plays, db_guesses;
55 
56 static const char hangpic[]=
57 	"\n88888\r\n"
58 	"9 7 6\r\n"
59 	"97  5\r\n"
60 	"9  423\r\n"
61 	"9   2\r\n"
62 	"9  1 0\r\n"
63 	"9\r\n"
64 	"9  ";
65 static const char substchar[]="\\/|\\/O|/-|";
66 
67 struct db_hang_forall_arg {
68 	int cnt;
69 	Elf_Sym *sym;
70 };
71 
72 /*
73  * Horrible abuse of the forall function, but we're not in a hurry.
74  */
75 static void db_hang_forall(Elf_Sym *, char *, char *, int, void *);
76 
77 static void
78 db_hang_forall(Elf_Sym *sym, char *name, char *suff, int pre, void *varg)
79 {
80 	struct db_hang_forall_arg *arg = varg;
81 
82 	if (arg->cnt-- == 0)
83 		arg->sym = sym;
84 }
85 
86 static __inline char *
87 db_randomsym(size_t *lenp)
88 {
89 	int nsyms;
90 	char	*p, *q;
91 	struct db_hang_forall_arg dfa;
92 
93 	dfa.cnt = 0;
94 	db_elf_sym_forall(db_hang_forall, &dfa);
95 	nsyms = -dfa.cnt;
96 
97 	if (nsyms == 0)
98 		return (NULL);
99 
100 	dfa.cnt = arc4random_uniform(nsyms);
101 	db_elf_sym_forall(db_hang_forall, &dfa);
102 
103 	db_symbol_values(dfa.sym, &q, 0);
104 
105 	/* strlen(q) && ignoring underscores and colons */
106 	for ((*lenp) = 0, p = q; *p; p++)
107 		if (ISALPHA(*p))
108 			(*lenp)++;
109 
110 	return (q);
111 }
112 
113 void
114 db_hang(int tries, char *word, struct _abc *sabc)
115 {
116 	const char	*p;
117 	int i;
118 	int c;
119 #ifdef ABC_BITMASK
120 	int m;
121 #endif
122 
123 	for (p = hangpic; *p; p++)
124 		cnputc((*p >= '0' && *p <= '9') ? ((tries <= (*p) - '0') ?
125 		    substchar[(*p) - '0'] : ' ') : *p);
126 
127 	for (p = word; *p; p++) {
128 		c = TOLOWER(*p);
129 		cnputc(ISLOWALPHA(c) && ABC_ISCLR(c) ? '-' : *p);
130 	}
131 
132 #ifdef ABC_WRONGSTR
133 	db_printf(" (%s)\r", ABC_WRONGSTR);
134 #else
135 	db_printf(" (");
136 
137 #ifdef ABC_BITMASK
138 	m = sabc->wrong;
139 	for (i = 'a'; i <= 'z'; ++i, m >>= 1)
140 		if (m&1)
141 			cnputc(i);
142 #else
143 	for (i = 'a'; i <= 'z'; ++i)
144 		if (ABC_ISWRONG(i))
145 			cnputc(i);
146 #endif
147 
148 	db_printf(")\r");
149 #endif
150 }
151 
152 void
153 db_hangman(db_expr_t addr, int haddr, db_expr_t count, char *modif)
154 {
155 	char	*word;
156 	size_t	tries;
157 	size_t	len;
158 	struct _abc sabc[1];
159 	int	skill;
160 
161 	if (modif[0] != 's' || (skill = modif[1] - '0') > 9U)
162 		skill = 3;
163 	word = NULL;
164 	tries = 0;
165 	for (;;) {
166 
167 		if (word == NULL) {
168 			ABC_CLR();
169 
170 			tries = skill + 1;
171 			word = db_randomsym(&len);
172 			if (word == NULL)
173 				break;
174 
175 			db_plays++;
176 		}
177 
178 		{
179 			int c;
180 
181 			db_hang(tries, word, sabc);
182 			c = cngetc();
183 			c = TOLOWER(c);
184 
185 			if (ISLOWALPHA(c) && ABC_ISCLR(c)) {
186 				char	*p;
187 				size_t	n;
188 
189 					/* strchr(word,c) */
190 				for (n = 0, p = word; *p ; p++)
191 					if (TOLOWER(*p) == c)
192 						n++;
193 
194 				if (n) {
195 					ABC_SETRIGHT(c);
196 					len -= n;
197 				} else {
198 					ABC_SETWRONG(c);
199 					tries--;
200 				}
201 			}
202 		}
203 
204 		if (tries && len)
205 			continue;
206 
207 		if (!tries && skill > 2) {
208 			char	*p = word;
209 			for (; *p; p++)
210 				if (ISALPHA(*p))
211 					ABC_SETRIGHT(TOLOWER(*p));
212 		}
213 		if (tries)
214 			db_guesses++;
215 		db_hang(tries, word, sabc);
216 		db_printf("\nScore: %lu/%lu\n", db_plays, db_guesses);
217 		word = NULL;
218 		if (tries)
219 			break;
220 	}
221 }
222