Last active
October 16, 2023 19:12
-
-
Save oguz-ismail/123d1e5bf2d2b1b2696ad48950aee4e3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <assert.h> | |
#include <math.h> | |
#include <signal.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <time.h> | |
#include <unistd.h> | |
#ifdef __WIN32 | |
#ifdef __linux__ | |
#undef __WIN32 | |
#else | |
#include <windows.h> | |
#include <Fcntl.h> | |
#endif | |
#else | |
#include <termios.h> | |
static struct termios orig_termios; | |
#endif | |
static void enter_raw_mode(void); | |
#define NUMBER_LEN 4 | |
#define MAX_TRIES 9 | |
#define DIGITS "123456789" | |
#define NUM_DIGITS (sizeof DIGITS - 1) | |
#define INDICES_MAX (pow(NUM_DIGITS, NUMBER_LEN) - 1) | |
static char secret[NUMBER_LEN + 1], guess[NUMBER_LEN + 1]; | |
static void | |
pick_the_number(void) { | |
static char digits[] = DIGITS; | |
int indices; | |
size_t i, j; | |
char tmp; | |
indices = rand() / ((RAND_MAX / (INDICES_MAX + 1)) + 1); | |
for (i = 0; i < NUMBER_LEN; i++) { | |
j = indices % NUM_DIGITS; | |
tmp = digits[i]; | |
digits[i] = digits[j]; | |
digits[j] = tmp; | |
indices /= NUM_DIGITS; | |
} | |
strncpy(secret, digits, NUMBER_LEN); | |
} | |
static int | |
read_guess(void) { | |
size_t i; | |
int c; | |
i = 0; | |
while ((c = getchar()) != EOF) | |
#ifdef __WIN32 | |
if (c == VK_BACK) { | |
#else | |
if (c == orig_termios.c_cc[VERASE]) { | |
#endif | |
if (i == 0) { | |
fputc('\a', stderr); | |
} | |
else { | |
fputs("\b \b", stderr); | |
i--; | |
} | |
} | |
else if (c == '\r') { | |
if (i < NUMBER_LEN) | |
fputc('\a', stderr); | |
else | |
return 1; | |
} | |
#ifndef __WIN32 | |
else if (c == orig_termios.c_cc[VINTR]) { | |
killpg(getpgrp(), SIGINT); | |
} | |
else if (c == orig_termios.c_cc[VEOF] && i == 0) { | |
break; | |
} | |
#endif | |
else { | |
if (i >= NUMBER_LEN) { | |
fputc('\a', stderr); | |
} | |
else if (strchr(DIGITS, c) == NULL) { | |
fputc('\a', stderr); | |
} | |
else if (memchr(guess, c, i) != NULL) { | |
fputc('\a', stderr); | |
} | |
else { | |
fputc(c, stderr); | |
guess[i] = c; | |
i++; | |
} | |
} | |
return 0; | |
} | |
static void | |
print_count(int count, const char *what) { | |
if (count > 0) { | |
printf("%d %s", count, what); | |
if (count > 1) | |
putchar('s'); | |
} | |
} | |
static int | |
evaluate_guess(void) { | |
size_t i; | |
int bulls, cows; | |
bulls = 0; | |
cows = 0; | |
for (i = 0; i < NUMBER_LEN; i++) | |
if (guess[i] == secret[i]) | |
bulls++; | |
for (i = 0; i < NUMBER_LEN; i++) | |
if (strchr(secret, guess[i]) != NULL) | |
cows++; | |
cows -= bulls; | |
print_count(bulls, "bull"); | |
if (bulls > 0 && cows > 0) | |
fputs(" and ", stdout); | |
print_count(cows, "cow"); | |
fputs("\r\n", stdout); | |
return (bulls == NUMBER_LEN); | |
} | |
int | |
main(void) { | |
int tries; | |
enter_raw_mode(); | |
srand(time(NULL)); | |
pick_the_number(); | |
fprintf(stderr, "What is my number? \ | |
%d different digits and %d guesses\r\n", NUMBER_LEN, MAX_TRIES); | |
for (tries = 1; tries <= MAX_TRIES; tries++) { | |
fprintf(stderr, "%d\t", tries); | |
if (!read_guess()) { | |
assert(!ferror(stdin)); | |
return 1; | |
} | |
fputc('\t', stderr); | |
if (evaluate_guess()) | |
break; | |
} | |
if (tries > MAX_TRIES) | |
printf("You lose. It was %s.\r\n", secret); | |
else | |
fputs("You win!\r\n", stdout); | |
} | |
#ifndef __WIN32 | |
static void | |
exit_raw_mode(void) { | |
tcsetattr(2, TCSAFLUSH, &orig_termios); | |
} | |
#endif | |
static void | |
enter_raw_mode(void) { | |
#ifdef __WIN32 | |
HANDLE console; | |
DWORD mode; | |
console = GetStdHandle(STD_INPUT_HANDLE); | |
assert(GetConsoleMode(console, &mode)); | |
mode &= ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT); | |
assert(SetConsoleMode(console, mode)); | |
_setmode(_fileno(stdin), _O_BINARY); | |
#else | |
struct termios raw; | |
assert(tcgetattr(2, &orig_termios) != -1); | |
raw = orig_termios; | |
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); | |
raw.c_oflag &= ~OPOST; | |
raw.c_cflag |= CS8; | |
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); | |
raw.c_cc[VMIN] = 1; | |
raw.c_cc[VTIME] = 0; | |
atexit(exit_raw_mode); | |
assert(tcsetattr(2, TCSAFLUSH, &raw) != -1); | |
#endif | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment