Skip to content

Instantly share code, notes, and snippets.

@oguz-ismail
Last active October 16, 2023 19:12
Show Gist options
  • Save oguz-ismail/123d1e5bf2d2b1b2696ad48950aee4e3 to your computer and use it in GitHub Desktop.
Save oguz-ismail/123d1e5bf2d2b1b2696ad48950aee4e3 to your computer and use it in GitHub Desktop.
#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