Пишу, по большей части, про историю, свою жизнь и немного про программирование.

«Крестики-нолики» и printf

Очень люблю так называемое ненормальное программирование (нормального мне и на работе хватает), когда программируют ради преодоления каких-то препятствий, а не ради практического результата. С удовольствием занимаюсь им сам и очень люблю находить такие примеры у других.

На днях один из читателей очень меня порадовал игрой «Крестики-нолики», написанной на… языке функции printf! Возможно я когда-то и слышал, что язык printf полный по Тьюрингу, но в голове это не отложилось.

Пример сыгранной партии в «Крестики-нолики»

Для тех, кто не знает, поясню, язык printf — это значки, которыми описывается в каком именно виде нам хочется вывести на экран информацию о переменных. Используется во многих языках, но особенно часто в Си.

В этом языке много интересных конструкций, есть даже такие, которыми можно даже что-то записывать в другие переменные!

Например, вот небольшой пример, который запишет число «42» (длину строки «Answer to the Ultimate Question of Life is») в переменную c:

#include <stdio.h>

int main() {
  int c;
  printf("Answer to the Ultimate Question of Life is%n ", &c);
  printf("%d", c);
}

Как, используя эти значки, можно что-то запрограммировать? С большим трудом. Автор программы нашёл способ описания логических операций, а потом с их помощью реализовал нужную ему логику.

Если закодировать «ложь» пустой строкой, а «истину» строкой длиной один, то логическую операцию «ИЛИ» можно получить, если померить совокупную длину двух входных значений:

// c = strlen("") + strlen("")
printf("%s%s%n", "", "", &c); // в c будет «0»

// c = strlen("1") + strlen("")
printf("%s%s%n", "1", "", &c); // в c будет «1»

«НЕ» получается сложнее, для этого надо использовать несколько хитростей.

Во-первых, у printf есть возможность задавать отступ для значения.

Во-вторых, у %n есть параметр, который выводит длину по модулю 256.

В-третьих, вместо форматирования входных параметров по порядку, есть способ для указания параметра, который мы хотим вывести, по его номеру.

Всё это позволяет закодировать «НЕ» как (strlen(a)+255)%256 или strlen(a)-1:

printf("%1$255s%1$s%2$hhn", a, &c);

Тут написано следующее: вывести первый параметр как строку, дополнив его до длины в 255 символов пробелами (%1$255s), снова вывести первый параметр как строку (%1$s), записать полученную длину по модулю 256 во второй параметр (%2$hhn).

Отсюда легко понять как сделать операцию «И-НЕ»:

printf("%1$254s%1$s%2$s%3$hhn", a, b, &c);

В принципе, этого хватило, что реализовать всю требуемую логику.

Играют два игрока, последовательно вводя номер ячейки, куда делается ход (от единицы до девяти, считая слева направо, сверху вниз). Программа отрисовывает поле и определяет победу одного из игроков или ничью.

Очень заморочено и очень интересная идея!

1 комментарий
Алексей Копылов 2023

«номер ячейки, куда делается ход (от нуля до девяти)»

От 0 до 9 получается 10 ячеек

Евгений Степанищев 2023

Ой, спасибо, поправил! )