Skip to content

Instantly share code, notes, and snippets.

@nandakoryaaa
Created June 7, 2025 10:24
Show Gist options
  • Save nandakoryaaa/6b1821e8e0e1ec13abc8dc1dcb795185 to your computer and use it in GitHub Desktop.
Save nandakoryaaa/6b1821e8e0e1ec13abc8dc1dcb795185 to your computer and use it in GitHub Desktop.
A game of guessing 5-letter Russian words - now with Windows Unicode support
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <process.h>
#include <time.h>
#include <string.h>
#include <windows.h>
#define LETTER_CNT 5
static const uint16_t russian_utf16[] = {
// А Б В Г Д Е Ё Ж З И Й К Л М Н О П
1040, 1041, 1042, 1043, 1044, 1045, /*1025,*/ 1046, 1047, 1048, 1049, 1050, 1051, 1052, 1053, 1054, 1055,
// Р С Т У Ф Х Ц Ч Ш Щ Ъ Ы Ь Э Ю Я
1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1069, 1070, 1071,
// а б в г д е ё ж з и й к л м н о п
1072, 1073, 1074, 1075, 1076, 1077, /*1105,*/ 1078, 1079, 1080, 1081, 1082, 1083, 1084, 1085, 1086, 1087,
// р с т у ф х ц ч ш щ ъ ы ь э ю я
1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, 1100, 1101, 1102, 1103
};
const uint16_t russian_utf8[] = {
// А-Я
0x90D0, 0x91D0, 0x92D0, 0x93D0, 0x94D0, 0x95D0, /*0x81D0,*/ 0x96D0,
0x97D0, 0x98D0, 0x99D0, 0x9AD0, 0x9BD0, 0x9CD0, 0x9DD0, 0x9ED0,
0x9FD0, 0xA0D0, 0xA1D0, 0xA2D0, 0xA3D0, 0xA4D0, 0xA5D0, 0xA6D0,
0xA7D0, 0xA8D0, 0xA9D0, 0xAAD0, 0xABD0, 0xACD0, 0xADD0, 0xAED0,
0xAFD0
};
typedef enum {
NO_LETTER, HAS_LETTER, OK_LETTER
} LetterStatus;
typedef struct {
LetterStatus status[LETTER_CNT];
size_t ok_cnt;
} WordStatus;
typedef struct {
char letters[LETTER_CNT];
} InputWord;
static const char* words[10] = {
"сабля", "сакля", "пакля", "сопля", "сосна",
"весна", "осень", "овраг", "насос", "киоск"
};
InputWord get_random_word(const InputWord* dict_words, size_t word_cnt)
{
size_t rand_index = rand() * (word_cnt - 1) / RAND_MAX;
return dict_words[rand_index];
}
uint8_t has_letter(const char* letters, uint32_t test_letter)
{
for (size_t i = 0; i < LETTER_CNT; i++) {
if (letters[i] == test_letter) {
return 1;
}
}
return 0;
}
WordStatus check_word(const char* letters, const char* test_letters)
{
WordStatus ws = { .ok_cnt = 0 };
for (size_t i = 0; i < LETTER_CNT; i++) {
uint32_t letter = letters[i];
uint32_t test_letter = test_letters[i];
if (letter == test_letter) {
// есть на своём месте
ws.status[i] = OK_LETTER;
ws.ok_cnt++;
} else if (has_letter(letters, test_letter)) {
// есть в другом месте
ws.status[i] = HAS_LETTER;
} else {
// нет вообще
ws.status[i] = NO_LETTER;
}
}
return ws;
}
void print_utf_dev(const char* word)
{
wchar_t wbuf[LETTER_CNT + 1];
for (size_t i = 0; i < LETTER_CNT; i++) {
wchar_t wc = russian_utf16[word[i] - 1];
uint8_t low_byte = ((wc >> 6) & 31) | 0xC0;
uint8_t high_byte = (wc & 63) | 0x80;
wbuf[i] = (high_byte << 8 | low_byte);
}
wbuf[LETTER_CNT] = 0;
printf("%s\n", (char*)wbuf);
}
void print_utf8(const char* word)
{
wchar_t wbuf[LETTER_CNT + 1];
for (size_t i = 0; i < LETTER_CNT; i++) {
wbuf[i] = russian_utf8[word[i] - 1];
}
wbuf[LETTER_CNT] = 0;
printf("%s\n", (char*)wbuf);
}
void print_status(const WordStatus* ws)
{
const char status_display[3] = { 'x', '-', '+' };
for (size_t i = 0; i < LETTER_CNT; i++) {
printf("%c", status_display[ws->status[i]]);
}
printf("\n");
}
InputWord from_utf8(const wchar_t* wbuf)
{
InputWord input;
for (size_t pos = 0; pos < LETTER_CNT; pos++) {
wchar_t wc = wbuf[pos];
if ((wc & 0xE0) != 0xC0) {
input.letters[0] = 0;
return input;
}
wc = ((wc >> 8) & 63) | ((wc & 31) << 6);
if (wc < 1040 || wc > 1103) {
input.letters[0] = 0;
return input;
}
if (wc > 1071) {
wc -= 32;
}
input.letters[pos] = wc - 1039;
}
return input;
}
InputWord from_utf16(const wchar_t* wbuf)
{
InputWord input;
for (size_t pos = 0; pos < LETTER_CNT; pos++) {
wchar_t wc = wbuf[pos];
if (wc < 1040 || wc > 1103) {
input.letters[0] = 0;
return input;
}
if (wc > 1071) {
wc -= 32;
}
input.letters[pos] = wc - 1039;
}
return input;
}
InputWord get_word_stdin()
{
InputWord input;
fgets(input.letters, sizeof(input.letters), stdin);
if (input.letters[strlen(input.letters) - 1] != '\n') {
// читать остаток во временный буфер
char buf[100];
do {
fgets(buf, sizeof(buf), stdin);
} while (buf[strlen(buf) - 1] != '\n');
}
return input;
}
InputWord get_word()
{
void* stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
wchar_t wbuf[LETTER_CNT + 3];
unsigned long int char_cnt = 0;
uint32_t read_cnt = 0;
do {
ReadConsoleW(
stdin_handle, wbuf, LETTER_CNT + 2, &char_cnt, NULL
);
read_cnt++;
} while (wbuf[char_cnt - 1] != '\n');
if (read_cnt > 1) {
return (InputWord) { .letters = {0} };
}
return from_utf16(wbuf);
}
int main()
{
srand(time(NULL) ^ getpid());
size_t word_cnt = 10;
InputWord input;
WordStatus ws;
InputWord dict_words[10];
uint8_t result = 1;
for (size_t i = 0; i < word_cnt; i++) {
dict_words[i] = from_utf8((wchar_t*)words[i]);
if (dict_words[i].letters[0] == 0) {
result = 0;
break;
}
}
printf("result: %u\n", result);
while (1) {
InputWord word = get_random_word(dict_words, word_cnt);
for (int try_cnt = 1; try_cnt < 7; try_cnt++) {
printf("try #%u: ", try_cnt);
input = get_word();
ws = check_word(word.letters, input.letters);
print_utf8(input.letters);
print_status(&ws);
if (ws.ok_cnt == LETTER_CNT) {
break;
}
}
if (ws.ok_cnt == LETTER_CNT) {
printf("YOU WON!\n");
} else {
printf("YOU LOST!\n");
}
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment