## File: 4digits-text.c

package info (click to toggle)
4digits 1.1.4-1
 `123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308` ``````/* 4digits - A guess-the-number game, aka Bulls and Cows Copyright (c) 2004- Yongzhi Pan 4digits is a guess-the-number puzzle game. It's called Bulls and Cows, and in China people simply call it Guess-the-Number. The game's objective is to guess a four-digit number in 8 times using as less time as possible. It is similar to Mastermind, but the four digits are different to each other. 4digits has a graphical user interface version 4digits and a textual user interface version 4digits-text. 4digits is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 4digits is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with 4digits; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define _(str) gettext(str) #define N_(str) str // For distribution packaging #define LOCALE_PATH "/usr/share/locale/" //#define LOCALE_PATH "locale/" //#define DEBUG #define VERSION_STRING "1.1.4" const char VERSION[] = "4digits " VERSION_STRING; const char COPYRIGHT[] = N_( "4digits comes with NO WARRANTY to the extent permitted by law.\n" "This program is free software; you can redistribute it and/or\n" "modify it under the terms of the GNU General Public License as\n" "published by the Free Software Foundation - version 2. For more\n" "information about these matters, see the file named COPYING.\n"); const char AUTHOR[] = N_("Written by Pan Yongzhi.\n"); const char HELP[] = N_( "4digits, a guess-the-number game.\n" "Usage: 4digits [OPTION] ...\n" "\n" "You are given eight times to guess a four-digit number. You get\n" "one A if its value and position are both correct, and you get one\n" "B if only its value is correct. You win the game when you get 4A0B.\n" "\n" "-v, --version \t display the version of 4digits and exit.\n" "-h, -?, --help \t print this help.\n" "\n" "Report bugs at ."); static const char *optString = "vh?"; struct globalArgs_t { char *version; // version option } globalArgs; static const struct option longOpts[] = { { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, { NULL, no_argument, NULL, 0 } }; void err_exit(const char* msg); void gen_rand(int ans_digits[]); int enter_number(void); void save_score(const int time_taken); void compare(const int *in_digits, const int *ans_digits, int *A, int *B); int tenpow(int power); void err_exit(const char* msg) { fprintf(stderr, "%s\n", msg); exit(EXIT_FAILURE); } /* generate a random number */ void gen_rand(int ans_digits[]) { srand((unsigned)time(NULL)); int ans = 1000 + (int)(8999.0*rand()/RAND_MAX); for(int i=0; i<4; i++) ans_digits[i] = (int)(ans/tenpow(3-i)) % 10; /* if 4 digits is not different from each other, regenerate it*/ while(ans_digits[0]==ans_digits[1] || ans_digits[1]==ans_digits[2] || ans_digits[2]==ans_digits[3] || ans_digits[0]==ans_digits[2] || ans_digits[0]==ans_digits[3] || ans_digits[1]==ans_digits[3]) { ans = 1000 + (int)(8999.0 * ((double)rand()/RAND_MAX)); for(int i=0; i<4; i++) ans_digits[i] = (int)(ans / tenpow(3-i))%10; } #ifdef DEBUG printf("%d\n", ans); #endif } /* enter a 4-digit number */ int enter_number() { char mstr[5]={'\0','\0','\0','\0','\0'}; int c; int input; int in_digits[4]={0,0,0,0}; /* arrays for the 4 digits of input*/ bool reinput; do { reinput = false; printf(_("Input a 4-digit number:")); if(fgets(mstr, sizeof mstr, stdin) == NULL) err_exit(_("Something's got wrong, I'd better quit...\n")); // fgets appends the newline entered, if it appears in the first 4 // elements of mstr, then it's sure less than 4 digits are entered bool flag = false; if(mstr[0]!='\n'&& mstr[1]!='\n'&& mstr[2]!='\n' &&mstr[3]!='\n') /* discard the character */ while((c = getchar()) != '\n' && c != EOF) flag = true; if (flag == true) { fprintf(stderr, _("Input too long!\n")); reinput = true; continue; } input = atoi(mstr); if (input < 1000 || input > 9999) { fprintf(stderr, _("Must be a number between 1000 and 9999!\n")); reinput = true; continue; } for(int i=0; i<4; i++) in_digits[i]=(int) (input / tenpow(3-i) )%10; if(in_digits[0]==in_digits[1] || in_digits[1]==in_digits[2] || in_digits[2]==in_digits[3] || in_digits[0]==in_digits[2] || in_digits[0]==in_digits[3] || in_digits[1]==in_digits[3]) { fprintf(stderr, _("Four digits must be unique.\n")); reinput = true; continue; } }while(reinput); return input; } /* compare answer and input, refresh A & B */ void compare(const int *in_digits, const int *ans_digits, int *a, int *b) { for(register int i=0 ; i<4; i++) for(register int j=0 ; j<4; j++) if(in_digits[i] == ans_digits[j]) (i == j) ? (*a)++ : (*b)++; } /* save current score in the score file */ void save_score(const int time_taken) { time_t tm = time(NULL); struct tm *today = localtime(&tm); char tmpbuffer[129]; today = localtime(&tm); char appdata_dir[4096]; //XXX why _PC_PATH_MAX is only 4? const char *score_filename = "4digits.4digits.scores"; strcpy(appdata_dir, getenv("HOME")); strcat(appdata_dir, "/.4digits/"); char *scorefile = (char*)malloc(strlen(appdata_dir) + strlen(score_filename) + 1); if(!scorefile) err_exit(_("Memory allocation error.\n")); strcpy(scorefile, appdata_dir); strcat(scorefile, score_filename); FILE *sfp = fopen(scorefile, "a+"); if (!sfp) { if (errno == ENOENT) { DIR *dp = opendir(appdata_dir); if(!dp) if (errno == ENOENT) { int ret = mkdir(appdata_dir, 0700); if (ret == -1) err_exit(_("Cannot open score file.\n")); sfp = fopen(scorefile, "a+"); } } else err_exit(_("Cannot open score file.\n")); } strftime(tmpbuffer, 128, "%a %b %d %H:%M:%S %Y", today); struct passwd *pwd; pwd = getpwuid(geteuid()); // but getenv("USERNAME") conforms to C99 thus is more portable. fprintf(sfp, "%s %ds %s\n", pwd->pw_name, time_taken, tmpbuffer); free(scorefile); } int tenpow(int exponent) { int output = 1; for(int i=0; i < exponent; i++) output *= 10; return output; } int main(int argc, char *argv[]) { int opt = 0; int longIndex = 0; /* Initialize globalArgs before we get to work. */ globalArgs.version = NULL; /* false */ //prepare i18n setlocale(LC_ALL, ""); bindtextdomain("4digits", LOCALE_PATH); textdomain("4digits"); // Process the arguments with getopt_long(), then populate globalArgs opt = getopt_long(argc, argv, optString, longOpts, &longIndex); while(opt != -1) { switch(opt) { case 'v': globalArgs.version = VERSION_STRING; printf("%s\n%s\n%s", VERSION, _(COPYRIGHT), _(AUTHOR)); exit(1); case 'h': err_exit(_(HELP)); break; case '?': /* fall-through is intentional */ err_exit(_("Usage: 4digits [OPTION]...\n" "Try `4digits --help' for more information.")); break; case 0: /* long option without a short arg */ if(strcmp("version", longOpts[longIndex].name) == 0) break; break; default: /* You won't actually get here. */ break; } opt = getopt_long(argc, argv, optString, longOpts, &longIndex); } int ans_digits[4]; gen_rand(ans_digits); /* array for the 4 digits of n*/ time_t temp = time(NULL); time_t tic = temp; int guessed[8] = {0, 0, 0, 0, 0, 0, 0, 0}; int i; bool dup = false; for (int num_guess = 0; num_guess < 8; num_guess++) { int A = 0, B = 0; int input = enter_number(); for(int i=0; i < num_guess; i++) // duplicated input if (input == guessed[i]) { fprintf(stderr, _("You've already guessed it.\n")); --num_guess; dup = true; break; } if (dup == true) { dup = false; continue; } int in_digits[4]; /* arrays for the 4 digits of input*/ for(i=0; i<4; i++) { in_digits[i]=(int) (input / tenpow(3-i) )%10; } compare(in_digits, ans_digits, &A, &B); printf("%dA%dB ", A, B); if (num_guess != 7) printf(ngettext("\t %d time left.\n", "\t %d times left.\n", 7-num_guess), 7-num_guess); guessed[num_guess] = input; if(A == 4) { time_t toc = time(NULL); int score = (int)(toc-tic); printf(_("You win! :) Used %d sec.\n"), score); save_score(score); /* save score in the score file */ return 0; } } printf(_("\nHaha, you lose. It is ")); for(int i = 0; i < 4; i++) printf("%d", ans_digits[i]); printf(".\n"); return 0; } ``````