36
by fossilet
Changed logo; remove other game modes. Start over. |
1 |
/*
|
2 |
4digits - A guess-the-number game, aka Bulls and Cows
|
|
40
by fossilet
1.0 release. |
3 |
Copyright (c) 2004-2011 Pan Yongzhi <http://fourdigits.sourceforge.net>
|
36
by fossilet
Changed logo; remove other game modes. Start over. |
4 |
|
5 |
4digits is a guess-the-number puzzle game. It's called Bulls and Cows,
|
|
6 |
and in China people simply call it Guess-the-Number. The game's
|
|
7 |
objective is to guess a four-digit number in 8 times using as less time
|
|
8 |
as possible. It is similar to Mastermind, but the four digits are
|
|
9 |
different to each other. 4digits has a graphical user interface version
|
|
10 |
4digits and a textual user interface version 4digits-text.
|
|
11 |
||
12 |
4digits is free software; you can redistribute it and/or
|
|
13 |
modify it under the terms of the GNU General Public License as
|
|
14 |
published by the Free Software Foundation; either version 2 of
|
|
15 |
the License, or (at your option) any later version.
|
|
16 |
||
17 |
4digits is distributed in the hope that it will be useful,
|
|
18 |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
19 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
20 |
GNU General Public License for more details.
|
|
21 |
||
22 |
You should have received a copy of the GNU General Public License
|
|
23 |
along with 4digits; if not, write to the Free Software Foundation,
|
|
24 |
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
25 |
*/
|
|
1
by fossilet
Initial import |
26 |
|
27 |
#include <stdio.h> |
|
28 |
#include <stdlib.h> |
|
29 |
#include <time.h> |
|
30 |
#include <ctype.h> |
|
31 |
#include <string.h> |
|
32 |
#include <stdbool.h> |
|
33 |
#include <getopt.h> |
|
34 |
#include <unistd.h> |
|
35 |
||
36
by fossilet
Changed logo; remove other game modes. Start over. |
36 |
//#define DEBUG
|
40
by fossilet
1.0 release. |
37 |
#define VERSION_STRING "1.0, Jul 2011"
|
1
by fossilet
Initial import |
38 |
|
39 |
const char COPYRIGHT[] = "4digits " VERSION_STRING "\n" |
|
36
by fossilet
Changed logo; remove other game modes. Start over. |
40 |
"4digits comes with NO WARRANTY to the extent permitted by law.\n" |
41 |
"This program is free software; you can redistribute it and/or\n" |
|
42 |
"modify it under the terms of the GNU General Public License as\n" |
|
43 |
"published by the Free Software Foundation - version 2. For more\n" |
|
44 |
"information about these matters, see the file named COPYING.\n"; |
|
45 |
const char AUTHOR[] = "Written by Pan Yongzhi.\n"; |
|
46 |
||
47 |
const char HELP[] = |
|
48 |
"4digits, a guess-the-number game.\n" |
|
49 |
"Usage: 4digits [OPTION] ...\n" |
|
50 |
"\n" |
|
51 |
"You are given eight times to guess a four-digit number. You get\n" |
|
52 |
"one A if its value and position are both correct, and you get one\n" |
|
53 |
"B if only its value is correct. You win the game when you get 4A0B.\n" |
|
54 |
"\n" |
|
55 |
"-v, --version \t display the version of 4digits and exit.\n" |
|
56 |
"-h, -?, --help \t print this help.\n" |
|
57 |
"\n" |
|
58 |
"Report bugs at <http://sourceforge.net/projects/fourdigits/>."; |
|
59 |
||
60 |
static const char *optString = "vh?"; |
|
1
by fossilet
Initial import |
61 |
struct globalArgs_t { |
36
by fossilet
Changed logo; remove other game modes. Start over. |
62 |
char *version; // version option |
1
by fossilet
Initial import |
63 |
} globalArgs; |
64 |
||
65 |
static const struct option longOpts[] = { |
|
36
by fossilet
Changed logo; remove other game modes. Start over. |
66 |
{ "version", no_argument, NULL, 'v' }, |
67 |
{ "help", no_argument, NULL, 'h' }, |
|
68 |
{ NULL, no_argument, NULL, 0 } |
|
1
by fossilet
Initial import |
69 |
};
|
70 |
||
71 |
void print_help(void); |
|
72 |
void display_usage(void); |
|
36
by fossilet
Changed logo; remove other game modes. Start over. |
73 |
void gen_rand(int ans_digits[]); |
74 |
int enter_number(void); |
|
75 |
void save_score(const int time_taken); |
|
76 |
void compare(const int *in_digits, const int *ans_digits, int *A, int *B); |
|
77 |
int tenpow(int power); |
|
78 |
||
1
by fossilet
Initial import |
79 |
void print_help(void) { |
36
by fossilet
Changed logo; remove other game modes. Start over. |
80 |
(void)puts(HELP); |
81 |
exit(EXIT_FAILURE); |
|
1
by fossilet
Initial import |
82 |
}
|
83 |
||
84 |
void display_usage(void) { |
|
36
by fossilet
Changed logo; remove other game modes. Start over. |
85 |
(void)puts("Usage: 4digits [OPTION]...\n"); |
86 |
(void)puts("Try `4digits --help' for more information."); |
|
87 |
exit(EXIT_FAILURE); |
|
88 |
}
|
|
89 |
||
1
by fossilet
Initial import |
90 |
/* generate a random number */
|
26
by Fossilet
Version 0.9 release. |
91 |
void gen_rand(int ans_digits[]) { |
36
by fossilet
Changed logo; remove other game modes. Start over. |
92 |
srand((unsigned)time(NULL)); |
93 |
int ans = 1000 + (int)(8999.0*rand()/RAND_MAX); |
|
94 |
for(int i=0; i<4; i++) |
|
95 |
ans_digits[i] = (int)(ans/tenpow(3-i)) % 10; |
|
1
by fossilet
Initial import |
96 |
|
36
by fossilet
Changed logo; remove other game modes. Start over. |
97 |
/* if 4 digits is not different from each other, regenerate it*/
|
98 |
while(ans_digits[0]==ans_digits[1] || ans_digits[1]==ans_digits[2] |
|
99 |
|| ans_digits[2]==ans_digits[3] || ans_digits[0]==ans_digits[2] |
|
100 |
|| ans_digits[0]==ans_digits[3] || ans_digits[1]==ans_digits[3]) |
|
26
by Fossilet
Version 0.9 release. |
101 |
{
|
36
by fossilet
Changed logo; remove other game modes. Start over. |
102 |
ans = 1000 + (int)(8999.0 * ((double)rand()/RAND_MAX)); |
103 |
for(int i=0; i<4; i++) |
|
104 |
ans_digits[i] = (int)(ans / tenpow(3-i))%10; |
|
1
by fossilet
Initial import |
105 |
}
|
106 |
#ifdef DEBUG
|
|
36
by fossilet
Changed logo; remove other game modes. Start over. |
107 |
printf("%d\n", ans); |
1
by fossilet
Initial import |
108 |
#endif
|
109 |
}
|
|
110 |
||
111 |
/* enter a 4-digit number */
|
|
36
by fossilet
Changed logo; remove other game modes. Start over. |
112 |
int enter_number() { |
113 |
char mstr[5]={'\0','\0','\0','\0','\0'}; |
|
114 |
int c; |
|
115 |
int input; |
|
116 |
int in_digits[4]={0,0,0,0}; /* arrays for the 4 digits of input*/ |
|
117 |
bool reinput; |
|
118 |
do { |
|
119 |
reinput = false; |
|
120 |
printf("Input a 4-digit number:"); |
|
121 |
if(fgets(mstr, sizeof mstr, stdin) == NULL) { |
|
122 |
fprintf(stderr, "Something's got wrong, I'd better quit...\n"); |
|
123 |
exit(EXIT_FAILURE); |
|
124 |
}
|
|
125 |
// fgets appends the newline entered, if it appears in the first 4
|
|
126 |
// elements of mstr, then it's sure less than 4 digits are entered
|
|
127 |
bool flag = false; |
|
128 |
if(mstr[0]!='\n'&& mstr[1]!='\n'&& mstr[2]!='\n' &&mstr[3]!='\n') |
|
129 |
/* discard the character */
|
|
130 |
while((c = getchar()) != '\n' && c != EOF) |
|
131 |
flag = true; |
|
132 |
if (flag == true) { |
|
133 |
fprintf(stderr, "Input too long!\n"); |
|
134 |
reinput = true; |
|
135 |
continue; |
|
136 |
}
|
|
137 |
input = atoi(mstr); |
|
138 |
if (input < 1000 || input > 9999) { |
|
139 |
fprintf(stderr, "Must be a number between 1000 and 9999!\n"); |
|
140 |
reinput = true; |
|
141 |
continue; |
|
142 |
}
|
|
143 |
for(int i=0; i<4; i++) |
|
144 |
in_digits[i]=(int) (input / tenpow(3-i) )%10; |
|
145 |
if(in_digits[0]==in_digits[1] || in_digits[1]==in_digits[2] |
|
146 |
|| in_digits[2]==in_digits[3] || in_digits[0]==in_digits[2] |
|
147 |
|| in_digits[0]==in_digits[3] || in_digits[1]==in_digits[3]) |
|
148 |
{
|
|
149 |
fprintf(stderr, "Four digits must be unique.\n"); |
|
150 |
reinput = true; |
|
151 |
continue; |
|
152 |
}
|
|
153 |
}while(reinput); |
|
154 |
return input; |
|
1
by fossilet
Initial import |
155 |
}
|
156 |
||
157 |
/* compare answer and input, refresh A & B */
|
|
36
by fossilet
Changed logo; remove other game modes. Start over. |
158 |
void compare(const int *in_digits, const int *ans_digits, int *a, int *b) { |
159 |
for(register int i=0 ; i<4; i++) |
|
160 |
for(register int j=0 ; j<4; j++) |
|
161 |
if(in_digits[i] == ans_digits[j]) |
|
162 |
(i == j) ? (*a)++ : (*b)++; |
|
1
by fossilet
Initial import |
163 |
}
|
164 |
||
165 |
/* save current score in the score file */
|
|
36
by fossilet
Changed logo; remove other game modes. Start over. |
166 |
void save_score(const int time_taken) { |
167 |
time_t tm = time(NULL); |
|
168 |
struct tm *today = localtime(&tm); |
|
169 |
char tmpbuffer[129]; |
|
170 |
today = localtime(&tm); |
|
171 |
const char *sfpath = getenv("HOME"); |
|
172 |
const char score_filename[] = "/.4digits/4digits-text.4digits.scores"; |
|
173 |
char *scorefile = (char *)malloc( |
|
174 |
strlen(sfpath) + strlen(score_filename) + 1); |
|
175 |
if(!scorefile) { |
|
176 |
fprintf(stderr, "Memory allocation error.\n"); |
|
177 |
exit(EXIT_FAILURE); |
|
178 |
}
|
|
179 |
strcpy(scorefile, sfpath); |
|
180 |
strcat(scorefile, score_filename); |
|
181 |
FILE *sfp = fopen(scorefile, "a+"); |
|
182 |
if (!sfp) { |
|
183 |
fprintf(stderr, "Cannot open score file.\n"); |
|
184 |
exit(EXIT_FAILURE); |
|
185 |
}
|
|
186 |
strftime(tmpbuffer, 128, "%a %b %d %H:%M:%S %Y", today); |
|
187 |
fprintf(sfp, "%s %ds %s\n", getlogin(), time_taken, tmpbuffer); |
|
188 |
free(scorefile); |
|
189 |
}
|
|
190 |
||
191 |
int tenpow(int exponent) { |
|
192 |
int output = 1; |
|
193 |
for(int i=0; i < exponent; i++) |
|
194 |
output *= 10; |
|
195 |
return output; |
|
1
by fossilet
Initial import |
196 |
}
|
197 |
||
198 |
int main(int argc, char *argv[]) { |
|
36
by fossilet
Changed logo; remove other game modes. Start over. |
199 |
int opt = 0; |
200 |
int longIndex = 0; |
|
201 |
||
202 |
/* Initialize globalArgs before we get to work. */
|
|
203 |
globalArgs.version = NULL; /* false */ |
|
204 |
||
205 |
// Process the arguments with getopt_long(), then populate globalArgs
|
|
1
by fossilet
Initial import |
206 |
opt = getopt_long(argc, argv, optString, longOpts, &longIndex); |
36
by fossilet
Changed logo; remove other game modes. Start over. |
207 |
while(opt != -1) { |
208 |
switch(opt) { |
|
209 |
case 'v': |
|
210 |
globalArgs.version = VERSION_STRING; |
|
211 |
printf("%s\n%s", COPYRIGHT, AUTHOR); |
|
212 |
exit(1); |
|
213 |
case 'h': |
|
214 |
print_help(); |
|
215 |
break; |
|
216 |
case '?': /* fall-through is intentional */ |
|
217 |
display_usage(); |
|
218 |
break; |
|
219 |
case 0: /* long option without a short arg */ |
|
220 |
if(strcmp("version", longOpts[longIndex].name) == 0) |
|
221 |
break; |
|
222 |
default: |
|
223 |
/* You won't actually get here. */
|
|
224 |
break; |
|
225 |
}
|
|
226 |
opt = getopt_long(argc, argv, optString, longOpts, &longIndex); |
|
227 |
}
|
|
228 |
||
229 |
int ans_digits[4]; |
|
230 |
gen_rand(ans_digits); /* array for the 4 digits of n*/ |
|
231 |
time_t temp = time(NULL); |
|
232 |
time_t tic = temp; |
|
233 |
int guessed[8] = {0, 0, 0, 0, 0, 0, 0, 0}; |
|
234 |
int i; |
|
235 |
bool dup = false; |
|
236 |
||
237 |
for (int num_guess = 0; num_guess < 8; num_guess++) { |
|
238 |
int A = 0, B = 0; |
|
239 |
int input = enter_number(); |
|
240 |
||
241 |
for(int i=0; i < num_guess; i++) |
|
242 |
// duplicated input
|
|
243 |
if (input == guessed[i]) { |
|
244 |
fprintf(stderr, "You've already guessed it.\n"); |
|
245 |
--num_guess; |
|
246 |
dup = true; |
|
247 |
break; |
|
248 |
}
|
|
249 |
||
250 |
if (dup == true) { |
|
251 |
dup = false; |
|
252 |
continue; |
|
253 |
}
|
|
254 |
||
255 |
int in_digits[4]; /* arrays for the 4 digits of input*/ |
|
256 |
for(i=0; i<4; i++) { |
|
257 |
in_digits[i]=(int) (input / tenpow(3-i) )%10; |
|
258 |
}
|
|
259 |
||
260 |
compare(in_digits, ans_digits, &A, &B); |
|
261 |
printf("%dA%dB ", A, B); |
|
262 |
if (num_guess != 7) |
|
263 |
printf("\t %d times left.\n", 7-num_guess); |
|
264 |
guessed[num_guess] = input; |
|
265 |
||
266 |
if(A == 4) { |
|
267 |
time_t toc = time(NULL); |
|
268 |
int score = (int)(toc-tic); |
|
269 |
printf("You win! :) Used %d sec.\n", score); |
|
270 |
save_score(score); /* save score in the score file */ |
|
271 |
return 0; |
|
272 |
}
|
|
273 |
}
|
|
274 |
printf("\nHaha, you lose. It is "); |
|
275 |
for(int i = 0; i < 4; i++) |
|
276 |
printf("%d", ans_digits[i]); |
|
277 |
printf(".\n"); |
|
278 |
return 0; |
|
1
by fossilet
Initial import |
279 |
}
|