[Системное программирование, Отладка, C] Как я участвовал в IOCCC-'19 (и проиграл). Часть 2: «Симулятор NOR»
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Это вторая часть цикла статей о том, как я участвовал в IOCCC'19
- Как я участвовал в IOCCC-'19 (и проиграл). Часть 1: «Крестики-нолики»
- Как я участвовал в IOCCC-'19 (и проиграл). Часть 2: «Симулятор NOR»
Я надеюсь, что данная статья поможет вам при разборе чужого кода или кода после декомпилятора или обфускатора.
Если вы еще не знаете, что такое IOCCC или вы хотите ознакомиться с более простым вариантом запутанного кода, то рекомендую обратиться к первой части.
Всем остальным я желаю приятного чтения.
Все исходники помещены на github, откуда их можно невозбранно скачать и попробовать скомпилировать.
Исходные данные
Так как исходный код в текстовом виде можно найти по ссылке, покажу как код выглядит графически:
Традиционно сложилось, что «дополнительные баллы» даются, если код отформатирован нестандартно или выглядит как какое-то изображение. Что ж, будем считать, это с этим здесь всё в порядке.
Но что же делает программа?
Она инициализирует графический стек X-сервера, сканирует предоставленный файл конфигурации, рисует электрическую схему согласно файлу и запускает симуляцию, поочередно меняя полярность на 4 ножках входа, используя при этом только NOR (Not-OR) элементы. Отрисовка происходит с шейдером старого LCD-экрана.
В комплекте с программой идут несколько конфигурационных файлов, а именно:
DIP8-4packnot.txt — примерный аналог CMOS 4041/Четырехканальный инвертор
(изображение сжато в 2 раза, чтобы уместиться в рамки приличия. На самом деле забавно, что программа, весом в 3.5 КБ генерирует ряд изображений, который в максимальном сжатии занимают 10.5+МБ)
DIP8-triplexor.txt — примерный аналог CMOS 4030 с объединенными входами и тремя каналами/Трехканальный XOR-gate с объединенными входами
DIP8-fulladder.txt — примерный аналог CMOS 4008, но на два бита/сумматор на 2 бита с выводом бита переноса
Разбор кода
Самой сложной задачей в этот раз было уместиться в ограничение по размеру присылаемой работы. Ограничен не только размер файла, но и количество условных «токенов» — символов операций, ключевых слов языка и пар скобок. Именно для того, чтобы «заэксплойтить» эту особенность парсера-проверяльщика размера в программе объявлено огромное количество define'ов, приводящих блоки кода к одному ключевому слову языка С.
Для начала, заглянем в Makefile, чтобы понять, как код собирается:
#!/usr/bin/env make
PROJECT=prog
CC= gcc
SRC=prog.c
CWARN=-Wall -Wextra -Wno-char-subscripts
CSTD= -std=c99
# Syscalls table
# DS - syscall nanosleep
# DO - syscall open
# DR - syscall read
# DC - syscall close
# X11 structures offsets
# dS - offset of screens in Display
# dR - offset of root in Screen
# dD - offset of root_depth in Screen
# dV - offset of root_visual in Screen
# dG - offset of default_gc in Screen
BITS := $(shell uname -p)
ifeq ($(BITS), x86_64)
ARCH= -m64
CDEFINE= -DDS=35 -DDO=2 -DDR=0 -DDC=3 -DdS=232 -DdR=16 -DdD=56 -DdV=64 -DdG=72
else
ARCH= -m32
CDEFINE= -DDS=162 -DDO=5 -DDR=3 -DDC=6 -DdS=140 -DdR=8 -DdD=36 -DdV=40 -DdG=44
endif
OPT= -g
CFLAGS= ${CWARN} ${CSTD} ${ARCH} ${CDEFINE} ${OPT}
LDFLAGS= -ldl
RM= rm
all: ${PROJECT}
${PROJECT}:
${CC} ${CFLAGS} ${SRC} -o $@ ${LDFLAGS}
clean:
${RM} -f ${PROJECT}
Как видим, эта работа, так же, как и предыдущая активно использует системные вызовы, чтобы избежать уродливых "#include", которые нарушают узор картинки. Запомним этот факт и подготовим строку для препроцессора и линтера:
После препроцессора
SPL
gcc -DDS=35 -DDO=2 -DDR=0 -DDC=3 -DdS=232 -DdR=16 -DdD=56 -DdV=64 -DdG=72 prog.c -ldl -E | indent -kr -brf > /tmp/fmt.c
:
int _x[] = { 15117427, 8413248, 5878632, 13027014, 1 };
extern void *dlsym(void *, char *);
int x_[616][1220];
extern long syscall(long, ...);
extern void *dlopen(char *, int);
char m[19][20], _n[] =
"pu~D--2os" "<<<<<<<<" "<<<DSlyrXuolp}e" "<<<<<<<<"
"D_ny}hyOuqlpyKurxsk<D_ny" "}hyUq}{y" "<<<<<<<<" "DLihUq}{y" "<<<<<<<<"
"<<<DQ}lKurxsk" "<<<<<<<<" "<<DZpiot<";
long w, N[2] = { 0, 1 << 29 };
int rn = 2166136261, _[8][40][64] = { 0 };
long R[2];
void *M, *d, *T;
void b(int t, int x, int y, int c, int l) {
if ((x >= 19 || y >= 19 || x < 0 || y < 0) || (m[y][x] == l)
|| (m[y][x] != c))
return;
m[y][x] = l;
b(t, x - 1, y, c, l);
b(t, x + 1, y, c, l);
b(t, x, y - 1, c, l);
b(t, x, y + 1, c, l);
}
void e(int t, int x, int y, int c, int l) {
if ((x >= 64 || y >= 40 || x < 0 || y < 0) || (_[t][y][x] == l)
|| (_[t][y][x] != c))
return;
_[t][y][x] = l;
e(t, x - 1, y, c, l);
e(t, x + 1, y, c, l);
e(t, x, y - 1, c, l);
e(t, x, y + 1, c, l);
}
void k(int t, int x, int y, int c, int l) {
while (c--) {
_[t][y][x++] = l;
_[t][y++][x++] = l;
}
}
void r(int t, int x, int y, int c, int l) {
while (c--) {
_[t][y][x--] = l;
_[t][y++][x--] = l;
}
}
void u(int t, int x, int y, int c, int l) {
while (c--)
_[t][y++][x] = l;
}
char *z[8] = {
(char[]) {4},
(char[]) {6, 1 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0,
21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2,
63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1,
32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63,
21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1,
36 - 0, 4, 6, 1 * 4 + 0, 3, 31, 8 - 0, 6, 1 * 4 + 2,
3, 33, 38 - 0, 6, 1 * 4 + 3, 3, 30, 38 - 0, 4},
(char[]) {5, 1},
(char[]) {6, 0 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16,
0, 0, 21 + 0 * 3 - 0, 16, 1, 63,
21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0,
4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1,
5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1,
63, 21 + 1 * 3 - 0, 16, 2, 63 * 1,
21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6,
0 * 4 + 0, 3, 31, 8 - 0, 6, 0 * 4 + 2,
3, 33, 38 - 0, 6, 0 * 4 + 3, 3, 30,
38 - 0,
4},
(char[]) {4}, (char[]) {4},
(char[]) {6, 2 * 4 + 1, 0 + 0, 32 - 0, 5 - 3, 16, 0, 0,
21 + 0 * 3 - 3, 16, 1, 63, 21 + 0 * 3 - 3, 16, 2, 63 * 0,
21 - 3, 4, 2, 31 + 0, 36 - 3, 4, 0 + 1, 32 - 1, 5 - 3,
16, 0, 0, 21 + 1 * 3 - 3, 16, 1, 63, 21 + 1 * 3 - 3, 16,
2, 63 * 1, 21 - 3, 4, 2, 31 + 1, 36 - 3, 4, 6, 2 * 4 + 0,
3, 31, 8 - 3, 6, 2 * 4 + 2, 3, 33, 38 - 3, 6, 2 * 4 + 3,
3, 30, 38 - 3, 4},
(char[]) {6, 13, 0, 32, 9 + 0, 12 - 0,
0, 8 + 0 + 0, 21, 12 - 0, 1,
31, 9 + 0, 12 - 0, 1,
55 - 0 - 0, 21, 12 - 0, 0,
32, 9 + 3, 12 - 3, 0,
8 + 3 + 3, 21, 12 - 3, 1,
31, 9 + 3, 12 - 3, 1,
55 - 3 - 3, 21, 12 - 3, 6,
14 + 0, 2, 31 + 0, 13, 4,
1 - 0, 31 + 0, 16, 7, 3,
30 + 3 * 0, 14, 6,
12 + 0 * 4, 3, 32 - 0,
11 + 0 * 8, 6, 14 + 1, 2,
31 + 1, 13, 4, 1 - 1,
31 + 1, 16, 7, 3,
30 + 3 * 1, 14, 6,
12 + 1 * 4, 3, 32 - 1,
11 + 1 * 8,
4}
};
void d_(int t, int p, int q) {
for (int y = 0; y < 40; y++)
for (int x = 0; x < 64; x++)
if (_[t]
[y][x])
x_[y + q * 16 + p * 16][x + 580 + p * 32 - q *
32] = _[t][y][x];
}
int main(int a, char *s[]) {
int h = 127;
while (h--) {
_n[h] ^= 28;
_n[h] -= (_n[h] == 32 ? 32 : 0);
}
T = dlopen(_n, 2);
d = ((void *(*)()) dlsym(T, _n + (1 * 20))) (0);
w = ((long (*)()) dlsym(T, _n + (2 * 20))) (d,
(*(long *)
((char
*) (*(long *) ((char *)
d +
232)) +
16))
, 0, 0, 1220, 616, 1, 0,
0);
M = ((void *(*)()) dlsym(T, _n + (3 * 20))) (d,
(*(long *)
((char
*) (*(long *) ((char *)
d +
232)) +
64)),
(*(long *)
((char
*) (*(long *) ((char *)
d +
232)) +
56)), 2, 0, (char *) x_,
1220, 616, 32, 0);
for (int i = 0; i < 8; i++) {
char *p = z[i];
int c = 0;
while (*p != 4) {
switch (*p) {
case 0:
k(i, p[1], p[2], p[3], c);
p += 4;
break;
case 1:
r(i, p[1], p[2], p[3], c);
p += 4;
break;
case 2:
u(i, p[1], p[2], p[3], c);
p += 4;
break;
case 3:
e(i, p[1]
, p[2], 0, c);
p += 3;
break;
case 5:
p = z[p[1]];
break;
case 6:
c = _x[p[1] / 4] - 1643277 * (p[1] % 4);
p += 2;
break;
}
}
}
while (a++) {
int f = syscall(2, s[1], 0);
syscall(0, f, m, 380);
syscall(3, f);
for (int y = 0; y < 19; y++)
for (int x = 0; x < 19; x++)
if ((x % 14 == 2) && (y % 4 == 3))
m[y][x] = 46;
b(0, 2, 3, m[3][2], a & 1 ? 43 : 45);
b(0, 2, 7, m[7][2], a & 2 ? 43 : 45);
b(0, 2, 11, m[11][2], a & 4 ? 43 : 45);
b(0, 2, 15, m[15][2], a & 8 ? 43 : 45);
for (int i = 0; i < 20; i++)
for (int y = 0; y < 19; y++)
for (int x = 0; x < 19; x++)
if (m[y][x] == 62)
b(0, x + 1, y, m[y][x + 1],
!(m[y - 1][x] == 43 ? 1 : 0
|| m[y + 1][x] == 43 ? 1 : 0) ? 43 : 45);
for (int y = 0; y < 616; y++)
for (int x = 0; x < 1220; x++)
x_[y][x] = 0;
for (int y = 0; y < 19; y++)
for (int x = 0; x < 19; x++)
d_(((m[y][x] >> 4) & 1) << 2 | (m[y][x] & 3), x, y);
for (int y = 0; y < 19; y++)
for (int x = 0; x < 19; x++)
if ((x % 14 == 2) && (y % 4 == 3))
d_(7, x, y);
for (int y = 0; y < 616; y++)
for (int x = 0; x < 1220; x++) {
x_[y][x] &= 14737632 | (31 << ((x % 3) << 3));
x_[y][x] += 986895 & (rn *= 16777619);
} ((long (*)()) dlsym(T, _n + (4 * 20))) (d, w,
(*(long *)
((char
*) (*(long
*) ((char *)
d +
232)) +
72)), M, 0, 0, 0,
0, 1220, 616);
((long (*)()) dlsym(T, _n + (5 * 20))) (d, w);
((long (*)()) dlsym(T, _n + (6 * 20))) (d);
syscall(35, &N, &R);
} return 0;
}
Что ж, по крайней мере одно из условий конкурса мы выполнили, код после препроцессора понятнее не стал.
Начнем с того, что можно выловить с первого взгляда и попытаемся восстановить цепочку событий:
dlsym\dlopen
Очевидно, что чтобы воспользоваться функциями X-сервера и создать окно, а затем в нем что-то отрисовать код должен обратиться к библиотеке XLib. В коде присутствуют функции dlopen/dlsym, позволяющие динамически подгрузить библиотеку, однако на вход им подается какая-то хитрая каша:
char _n[] =
"pu~D--2os" "<<<<<<<<" "<<<DSlyrXuolp}e" "<<<<<<<<"
"D_ny}hyOuqlpyKurxsk<D_ny" "}hyUq}{y" "<<<<<<<<" "DLihUq}{y" "<<<<<<<<"
"<<<DQ}lKurxsk" "<<<<<<<<" "<<DZpiot<";
...
T = dlopen(_n, 2);
Пройдем на шаг назад и проинспектируем следующий код:
int h = 127;
while (h--) {
_n[h] ^= 28;
_n[h] -= (_n[h] == 32 ? 32 : 0);
}
Судя по всему, он некоторым образом преобразует исходный массив, позволяя нам получить читаемые строки. Выполним его отдельно:
https://onlinegdb.com/SJNM9Og7v:
libX11.so
XOpenDisplay
XCreateSimpleWindow
XCreateImage
XPutImage
XMapWindow
XFlush
Для того, чтобы одной строкой вызывать столь различные по формату функции код эксплуатирует тот факт, что в стандарте языка С (void) означает отсутствие параметров, а () — любое число параметров. Остается только привести полученный void * к соответствующему ((long (*)()) типу и вуаля:
w = ((long (*)()) dlsym(T, _n + (2 * 20))) (d, (*(long *)((char *) (*(long *)((char *) d + 232)) + 16))
Зная, что эти преобразования означают теперь мы можем заменить столь необычное использование dlsym сначала на строки:
После замены строк:
SPL
int _x[] = { 15117427, 8413248, 5878632, 13027014, 1 };
extern void *dlsym(void *, char *);
int x_[616][1220];
extern long syscall(long, ...);
extern void *dlopen(char *, int);
char m[19][20];
long w, N[2] = { 0, 1 << 29 };
int rn = 2166136261, _[8][40][64] = { 0 };
long R[2];
void *M, *d, *T;
void b(int t, int x, int y, int c, int l) {
if ((x >= 19 || y >= 19 || x < 0 || y < 0) || (m[y][x] == l)
|| (m[y][x] != c))
return;
m[y][x] = l;
b(t, x - 1, y, c, l);
b(t, x + 1, y, c, l);
b(t, x, y - 1, c, l);
b(t, x, y + 1, c, l);
}
void e(int t, int x, int y, int c, int l) {
if ((x >= 64 || y >= 40 || x < 0 || y < 0) || (_[t][y][x] == l)
|| (_[t][y][x] != c))
return;
_[t][y][x] = l;
e(t, x - 1, y, c, l);
e(t, x + 1, y, c, l);
e(t, x, y - 1, c, l);
e(t, x, y + 1, c, l);
}
void k(int t, int x, int y, int c, int l) {
while (c--) {
_[t][y][x++] = l;
_[t][y++][x++] = l;
}
}
void r(int t, int x, int y, int c, int l) {
while (c--) {
_[t][y][x--] = l;
_[t][y++][x--] = l;
}
}
void u(int t, int x, int y, int c, int l) {
while (c--)
_[t][y++][x] = l;
}
char *z[8] = {
(char[]) {4},
(char[]) {6, 1 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0,
21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2,
63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1,
32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63,
21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1,
36 - 0, 4, 6, 1 * 4 + 0, 3, 31, 8 - 0, 6, 1 * 4 + 2,
3, 33, 38 - 0, 6, 1 * 4 + 3, 3, 30, 38 - 0, 4},
(char[]) {5, 1},
(char[]) {6, 0 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16,
0, 0, 21 + 0 * 3 - 0, 16, 1, 63,
21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0,
4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1,
5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1,
63, 21 + 1 * 3 - 0, 16, 2, 63 * 1,
21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6,
0 * 4 + 0, 3, 31, 8 - 0, 6, 0 * 4 + 2,
3, 33, 38 - 0, 6, 0 * 4 + 3, 3, 30,
38 - 0,
4},
(char[]) {4}, (char[]) {4},
(char[]) {6, 2 * 4 + 1, 0 + 0, 32 - 0, 5 - 3, 16, 0, 0,
21 + 0 * 3 - 3, 16, 1, 63, 21 + 0 * 3 - 3, 16, 2, 63 * 0,
21 - 3, 4, 2, 31 + 0, 36 - 3, 4, 0 + 1, 32 - 1, 5 - 3,
16, 0, 0, 21 + 1 * 3 - 3, 16, 1, 63, 21 + 1 * 3 - 3, 16,
2, 63 * 1, 21 - 3, 4, 2, 31 + 1, 36 - 3, 4, 6, 2 * 4 + 0,
3, 31, 8 - 3, 6, 2 * 4 + 2, 3, 33, 38 - 3, 6, 2 * 4 + 3,
3, 30, 38 - 3, 4},
(char[]) {6, 13, 0, 32, 9 + 0, 12 - 0,
0, 8 + 0 + 0, 21, 12 - 0, 1,
31, 9 + 0, 12 - 0, 1,
55 - 0 - 0, 21, 12 - 0, 0,
32, 9 + 3, 12 - 3, 0,
8 + 3 + 3, 21, 12 - 3, 1,
31, 9 + 3, 12 - 3, 1,
55 - 3 - 3, 21, 12 - 3, 6,
14 + 0, 2, 31 + 0, 13, 4,
1 - 0, 31 + 0, 16, 7, 3,
30 + 3 * 0, 14, 6,
12 + 0 * 4, 3, 32 - 0,
11 + 0 * 8, 6, 14 + 1, 2,
31 + 1, 13, 4, 1 - 1,
31 + 1, 16, 7, 3,
30 + 3 * 1, 14, 6,
12 + 1 * 4, 3, 32 - 1,
11 + 1 * 8,
4}
};
void d_(int t, int p, int q) {
for (int y = 0; y < 40; y++)
for (int x = 0; x < 64; x++)
if (_[t]
[y][x])
x_[y + q * 16 + p * 16][x + 580 + p * 32 - q *
32] = _[t][y][x];
}
int main(int a, char *s[]) {
T = dlopen("libX11.so", 2);
d = ((void *(*)()) dlsym(T, "XOpenDisplay") (0);
w = ((long (*)()) dlsym(T, "XCreateSimpleWindow")) (d,
(*(long *)
((char
*) (*(long *) ((char *)
d +
232)) +
16))
, 0, 0, 1220, 616, 1, 0,
0);
M = ((void *(*)()) dlsym(T, "XCreateImage")) (d,
(*(long *)
((char
*) (*(long *) ((char *)
d +
232)) +
64)),
(*(long *)
((char
*) (*(long *) ((char *)
d +
232)) +
56)), 2, 0, (char *) x_,
1220, 616, 32, 0);
for (int i = 0; i < 8; i++) {
char *p = z[i];
int c = 0;
while (*p != 4) {
switch (*p) {
case 0:
k(i, p[1], p[2], p[3], c);
p += 4;
break;
case 1:
r(i, p[1], p[2], p[3], c);
p += 4;
break;
case 2:
u(i, p[1], p[2], p[3], c);
p += 4;
break;
case 3:
e(i, p[1]
, p[2], 0, c);
p += 3;
break;
case 5:
p = z[p[1]];
break;
case 6:
c = _x[p[1] / 4] - 1643277 * (p[1] % 4);
p += 2;
break;
}
}
}
while (a++) {
int f = syscall(2, s[1], 0);
syscall(0, f, m, 380);
syscall(3, f);
for (int y = 0; y < 19; y++)
for (int x = 0; x < 19; x++)
if ((x % 14 == 2) && (y % 4 == 3))
m[y][x] = 46;
b(0, 2, 3, m[3][2], a & 1 ? 43 : 45);
b(0, 2, 7, m[7][2], a & 2 ? 43 : 45);
b(0, 2, 11, m[11][2], a & 4 ? 43 : 45);
b(0, 2, 15, m[15][2], a & 8 ? 43 : 45);
for (int i = 0; i < 20; i++)
for (int y = 0; y < 19; y++)
for (int x = 0; x < 19; x++)
if (m[y][x] == 62)
b(0, x + 1, y, m[y][x + 1],
!(m[y - 1][x] == 43 ? 1 : 0
|| m[y + 1][x] == 43 ? 1 : 0) ? 43 : 45);
for (int y = 0; y < 616; y++)
for (int x = 0; x < 1220; x++)
x_[y][x] = 0;
for (int y = 0; y < 19; y++)
for (int x = 0; x < 19; x++)
d_(((m[y][x] >> 4) & 1) << 2 | (m[y][x] & 3), x, y);
for (int y = 0; y < 19; y++)
for (int x = 0; x < 19; x++)
if ((x % 14 == 2) && (y % 4 == 3))
d_(7, x, y);
for (int y = 0; y < 616; y++)
for (int x = 0; x < 1220; x++) {
x_[y][x] &= 14737632 | (31 << ((x % 3) << 3));
x_[y][x] += 986895 & (rn *= 16777619);
} ((long (*)()) dlsym(T, "XPutImage")) (d, w,
(*(long *)
((char
*) (*(long
*) ((char *)
d +
232)) +
72)), M, 0, 0, 0,
0, 1220, 616);
((long (*)()) dlsym(T, "XMapWindow")) (d, w);
((long (*)()) dlsym(T, "XFlush")) (d);
syscall(35, &N, &R);
} return 0;
}
А потом и на родные функции:
После замены на ''родные'' функции:
SPL
#include <X11/Xlib.h>
int _x[] = { 15117427, 8413248, 5878632, 13027014, 1 };
int x_[616][1220];
extern long syscall(long, ...);
char m[19][20];
long w, N[2] = { 0, 1 << 29 };
int rn = 2166136261, _[8][40][64] = { 0 };
long R[2];
void *M, *d, *T;
void b(int t, int x, int y, int c, int l) {
if ((x >= 19 || y >= 19 || x < 0 || y < 0) || (m[y][x] == l)
|| (m[y][x] != c))
return;
m[y][x] = l;
b(t, x - 1, y, c, l);
b(t, x + 1, y, c, l);
b(t, x, y - 1, c, l);
b(t, x, y + 1, c, l);
}
void e(int t, int x, int y, int c, int l) {
if ((x >= 64 || y >= 40 || x < 0 || y < 0) || (_[t][y][x] == l)
|| (_[t][y][x] != c))
return;
_[t][y][x] = l;
e(t, x - 1, y, c, l);
e(t, x + 1, y, c, l);
e(t, x, y - 1, c, l);
e(t, x, y + 1, c, l);
}
void k(int t, int x, int y, int c, int l) {
while (c--) {
_[t][y][x++] = l;
_[t][y++][x++] = l;
}
}
void r(int t, int x, int y, int c, int l) {
while (c--) {
_[t][y][x--] = l;
_[t][y++][x--] = l;
}
}
void u(int t, int x, int y, int c, int l) {
while (c--)
_[t][y++][x] = l;
}
char *z[8] = {
(char[]) {4},
(char[]) {6, 1 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0,
21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2,
63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1,
32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63,
21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1,
36 - 0, 4, 6, 1 * 4 + 0, 3, 31, 8 - 0, 6, 1 * 4 + 2,
3, 33, 38 - 0, 6, 1 * 4 + 3, 3, 30, 38 - 0, 4},
(char[]) {5, 1},
(char[]) {6, 0 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16,
0, 0, 21 + 0 * 3 - 0, 16, 1, 63,
21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0,
4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1,
5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1,
63, 21 + 1 * 3 - 0, 16, 2, 63 * 1,
21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6,
0 * 4 + 0, 3, 31, 8 - 0, 6, 0 * 4 + 2,
3, 33, 38 - 0, 6, 0 * 4 + 3, 3, 30,
38 - 0,
4},
(char[]) {4}, (char[]) {4},
(char[]) {6, 2 * 4 + 1, 0 + 0, 32 - 0, 5 - 3, 16, 0, 0,
21 + 0 * 3 - 3, 16, 1, 63, 21 + 0 * 3 - 3, 16, 2, 63 * 0,
21 - 3, 4, 2, 31 + 0, 36 - 3, 4, 0 + 1, 32 - 1, 5 - 3,
16, 0, 0, 21 + 1 * 3 - 3, 16, 1, 63, 21 + 1 * 3 - 3, 16,
2, 63 * 1, 21 - 3, 4, 2, 31 + 1, 36 - 3, 4, 6, 2 * 4 + 0,
3, 31, 8 - 3, 6, 2 * 4 + 2, 3, 33, 38 - 3, 6, 2 * 4 + 3,
3, 30, 38 - 3, 4},
(char[]) {6, 13, 0, 32, 9 + 0, 12 - 0,
0, 8 + 0 + 0, 21, 12 - 0, 1,
31, 9 + 0, 12 - 0, 1,
55 - 0 - 0, 21, 12 - 0, 0,
32, 9 + 3, 12 - 3, 0,
8 + 3 + 3, 21, 12 - 3, 1,
31, 9 + 3, 12 - 3, 1,
55 - 3 - 3, 21, 12 - 3, 6,
14 + 0, 2, 31 + 0, 13, 4,
1 - 0, 31 + 0, 16, 7, 3,
30 + 3 * 0, 14, 6,
12 + 0 * 4, 3, 32 - 0,
11 + 0 * 8, 6, 14 + 1, 2,
31 + 1, 13, 4, 1 - 1,
31 + 1, 16, 7, 3,
30 + 3 * 1, 14, 6,
12 + 1 * 4, 3, 32 - 1,
11 + 1 * 8,
4}
};
void d_(int t, int p, int q) {
for (int y = 0; y < 40; y++)
for (int x = 0; x < 64; x++)
if (_[t]
[y][x])
x_[y + q * 16 + p * 16][x + 580 + p * 32 - q *
32] = _[t][y][x];
}
int main(int a, char *s[]) {
d = XOpenDisplay(0);
w = XCreateSimpleWindow(d, (*(long *) ((char *) (*(long *) ((char *)d + 232)) + 16)), 0, 0, 1220, 616, 1, 0, 0);
M = XCreateImage(d, (*(long *)((char *) (*(long *) ((char *)d + 232)) + 64)), (*(long *)((char *) (*(long *) ((char *)d + 232)) + 56)), 2, 0, (char *) x_, 1220, 616, 32, 0);
for (int i = 0; i < 8; i++) {
char *p = z[i];
int c = 0;
while (*p != 4) {
switch (*p) {
case 0:
k(i, p[1], p[2], p[3], c);
p += 4;
break;
case 1:
r(i, p[1], p[2], p[3], c);
p += 4;
break;
case 2:
u(i, p[1], p[2], p[3], c);
p += 4;
break;
case 3:
e(i, p[1]
, p[2], 0, c);
p += 3;
break;
case 5:
p = z[p[1]];
break;
case 6:
c = _x[p[1] / 4] - 1643277 * (p[1] % 4);
p += 2;
break;
}
}
}
while (a++) {
int f = syscall(2, s[1], 0);
syscall(0, f, m, 380);
syscall(3, f);
for (int y = 0; y < 19; y++)
for (int x = 0; x < 19; x++)
if ((x % 14 == 2) && (y % 4 == 3))
m[y][x] = 46;
b(0, 2, 3, m[3][2], a & 1 ? 43 : 45);
b(0, 2, 7, m[7][2], a & 2 ? 43 : 45);
b(0, 2, 11, m[11][2], a & 4 ? 43 : 45);
b(0, 2, 15, m[15][2], a & 8 ? 43 : 45);
for (int i = 0; i < 20; i++)
for (int y = 0; y < 19; y++)
for (int x = 0; x < 19; x++)
if (m[y][x] == 62)
b(0, x + 1, y, m[y][x + 1],
!(m[y - 1][x] == 43 ? 1 : 0
|| m[y + 1][x] == 43 ? 1 : 0) ? 43 : 45);
for (int y = 0; y < 616; y++)
for (int x = 0; x < 1220; x++)
x_[y][x] = 0;
for (int y = 0; y < 19; y++)
for (int x = 0; x < 19; x++)
d_(((m[y][x] >> 4) & 1) << 2 | (m[y][x] & 3), x, y);
for (int y = 0; y < 19; y++)
for (int x = 0; x < 19; x++)
if ((x % 14 == 2) && (y % 4 == 3))
d_(7, x, y);
for (int y = 0; y < 616; y++)
for (int x = 0; x < 1220; x++) {
x_[y][x] &= 14737632 | (31 << ((x % 3) << 3));
x_[y][x] += 986895 & (rn *= 16777619);
}
XPutImage(d, w, (*(long *)((char *) (*(long *) ((char *)d + 232)) + 72)), M, 0, 0, 0, 0, 1220, 616);
XMapWindow(d, w);
XFlush(d);
syscall(35, &N, &R);
}
return 0;
}
Syscalls
Заменим системные вызовы по тому же принципу, которому мы следовали в первой части:
Код без системных вызовов:
SPL
#include <X11/Xlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int _x[] = { 15117427, 8413248, 5878632, 13027014, 1 };
int x_[616][1220];
char m[19][20];
long w;
int rn = 2166136261, _[8][40][64] = { 0 };
void *M, *d, *T;
void b(int t, int x, int y, int c, int l) {
if ((x >= 19 || y >= 19 || x < 0 || y < 0) || (m[y][x] == l)
|| (m[y][x] != c))
return;
m[y][x] = l;
b(t, x - 1, y, c, l);
b(t, x + 1, y, c, l);
b(t, x, y - 1, c, l);
b(t, x, y + 1, c, l);
}
void e(int t, int x, int y, int c, int l) {
if ((x >= 64 || y >= 40 || x < 0 || y < 0) || (_[t][y][x] == l)
|| (_[t][y][x] != c))
return;
_[t][y][x] = l;
e(t, x - 1, y, c, l);
e(t, x + 1, y, c, l);
e(t, x, y - 1, c, l);
e(t, x, y + 1, c, l);
}
void k(int t, int x, int y, int c, int l) {
while (c--) {
_[t][y][x++] = l;
_[t][y++][x++] = l;
}
}
void r(int t, int x, int y, int c, int l) {
while (c--) {
_[t][y][x--] = l;
_[t][y++][x--] = l;
}
}
void u(int t, int x, int y, int c, int l) {
while (c--)
_[t][y++][x] = l;
}
char *z[8] = {
(char[]) {4},
(char[]) {6, 1 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0,
21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2,
63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1,
32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63,
21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1,
36 - 0, 4, 6, 1 * 4 + 0, 3, 31, 8 - 0, 6, 1 * 4 + 2,
3, 33, 38 - 0, 6, 1 * 4 + 3, 3, 30, 38 - 0, 4},
(char[]) {5, 1},
(char[]) {6, 0 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16,
0, 0, 21 + 0 * 3 - 0, 16, 1, 63,
21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0,
4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1,
5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1,
63, 21 + 1 * 3 - 0, 16, 2, 63 * 1,
21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6,
0 * 4 + 0, 3, 31, 8 - 0, 6, 0 * 4 + 2,
3, 33, 38 - 0, 6, 0 * 4 + 3, 3, 30,
38 - 0,
4},
(char[]) {4}, (char[]) {4},
(char[]) {6, 2 * 4 + 1, 0 + 0, 32 - 0, 5 - 3, 16, 0, 0,
21 + 0 * 3 - 3, 16, 1, 63, 21 + 0 * 3 - 3, 16, 2, 63 * 0,
21 - 3, 4, 2, 31 + 0, 36 - 3, 4, 0 + 1, 32 - 1, 5 - 3,
16, 0, 0, 21 + 1 * 3 - 3, 16, 1, 63, 21 + 1 * 3 - 3, 16,
2, 63 * 1, 21 - 3, 4, 2, 31 + 1, 36 - 3, 4, 6, 2 * 4 + 0,
3, 31, 8 - 3, 6, 2 * 4 + 2, 3, 33, 38 - 3, 6, 2 * 4 + 3,
3, 30, 38 - 3, 4},
(char[]) {6, 13, 0, 32, 9 + 0, 12 - 0,
0, 8 + 0 + 0, 21, 12 - 0, 1,
31, 9 + 0, 12 - 0, 1,
55 - 0 - 0, 21, 12 - 0, 0,
32, 9 + 3, 12 - 3, 0,
8 + 3 + 3, 21, 12 - 3, 1,
31, 9 + 3, 12 - 3, 1,
55 - 3 - 3, 21, 12 - 3, 6,
14 + 0, 2, 31 + 0, 13, 4,
1 - 0, 31 + 0, 16, 7, 3,
30 + 3 * 0, 14, 6,
12 + 0 * 4, 3, 32 - 0,
11 + 0 * 8, 6, 14 + 1, 2,
31 + 1, 13, 4, 1 - 1,
31 + 1, 16, 7, 3,
30 + 3 * 1, 14, 6,
12 + 1 * 4, 3, 32 - 1,
11 + 1 * 8,
4}
};
void d_(int t, int p, int q) {
for (int y = 0; y < 40; y++)
for (int x = 0; x < 64; x++)
if (_[t]
[y][x])
x_[y + q * 16 + p * 16][x + 580 + p * 32 - q *
32] = _[t][y][x];
}
int main(int a, char *s[]) {
d = XOpenDisplay(0);
w = XCreateSimpleWindow(d, (*(long *) ((char *) (*(long *) ((char *)d + 232)) + 16)), 0, 0, 1220, 616, 1, 0, 0);
M = XCreateImage(d, (*(long *)((char *) (*(long *) ((char *)d + 232)) + 64)), (*(long *)((char *) (*(long *) ((char *)d + 232)) + 56)), 2, 0, (char *) x_, 1220, 616, 32, 0);
for (int i = 0; i < 8; i++) {
char *p = z[i];
int c = 0;
while (*p != 4) {
switch (*p) {
case 0:
k(i, p[1], p[2], p[3], c);
p += 4;
break;
case 1:
r(i, p[1], p[2], p[3], c);
p += 4;
break;
case 2:
u(i, p[1], p[2], p[3], c);
p += 4;
break;
case 3:
e(i, p[1]
, p[2], 0, c);
p += 3;
break;
case 5:
p = z[p[1]];
break;
case 6:
c = _x[p[1] / 4] - 1643277 * (p[1] % 4);
p += 2;
break;
}
}
}
while (a++) {
int f = open(s[1], 0);
read(f, m, 380);
close(f);
for (int y = 0; y < 19; y++)
for (int x = 0; x < 19; x++)
if ((x % 14 == 2) && (y % 4 == 3))
m[y][x] = 46;
b(0, 2, 3, m[3][2], a & 1 ? 43 : 45);
b(0, 2, 7, m[7][2], a & 2 ? 43 : 45);
b(0, 2, 11, m[11][2], a & 4 ? 43 : 45);
b(0, 2, 15, m[15][2], a & 8 ? 43 : 45);
for (int i = 0; i < 20; i++)
for (int y = 0; y < 19; y++)
for (int x = 0; x < 19; x++)
if (m[y][x] == 62)
b(0, x + 1, y, m[y][x + 1],
!(m[y - 1][x] == 43 ? 1 : 0
|| m[y + 1][x] == 43 ? 1 : 0) ? 43 : 45);
for (int y = 0; y < 616; y++)
for (int x = 0; x < 1220; x++)
x_[y][x] = 0;
for (int y = 0; y < 19; y++)
for (int x = 0; x < 19; x++)
d_(((m[y][x] >> 4) & 1) << 2 | (m[y][x] & 3), x, y);
for (int y = 0; y < 19; y++)
for (int x = 0; x < 19; x++)
if ((x % 14 == 2) && (y % 4 == 3))
d_(7, x, y);
for (int y = 0; y < 616; y++)
for (int x = 0; x < 1220; x++) {
x_[y][x] &= 14737632 | (31 << ((x % 3) << 3));
x_[y][x] += 986895 & (rn *= 16777619);
}
XPutImage(d, w, (*(long *)((char *) (*(long *) ((char *)d + 232)) + 72)), M, 0, 0, 0, 0, 1220, 616);
XMapWindow(d, w);
XFlush(d);
sleep(1);
}
return 0;
}
XCreateSimpleWindow и смещения
Постараемся разобрать следующую конструкцию:
w = XCreateSimpleWindow(d, (*(long *) ((char *) (*(long *) ((char *)d + 232)) + 16)), 0, 0, 1220, 616, 1, 0, 0);
Куча преобразований типов, которые, на первый взгляд, нужны лишь чтобы запутать читателя, однако каждое преобразование здесь имеет свою цель, а именно:
d — это указатель на структуру Display в контексте Xlib. Он имеет поле-массив, называемое screens и для того, чтобы получить указатель на первый элемент этого массива мы должны отсчитать некоторое количество байт (на x64 — 232) от указателя на Display вперед. Так как Display не char *, то при прямом отсчитывании мы обсчитались бы в sizeof(long *) байт. Поэтому приведем d к char * и сдвинемся на 232 байта:
((char *)d + 232)
Мы получили позицию первого элемента Screens в памяти. Приведем его к полноценному указателю и разыменуем:
(*(long *) ((char *)d + 232))
Теперь внутри структуры Screens мы должны получить указатель на корневое окно, Root. Для этого отойдем от Screens на 16 байт и разыменуем конструкцию:
(*(long *) ((char *) (*(long *) ((char *)d + 232)) + 16))
Эта конструкция на самом деле каждый день используется программистами под Xlib, поскольку её общеупотребимый эквивалент — это
RootWindow(Display, DefaultScreen(Display))
Аналогичным образом заменим соответствующие смещения в других местах, чтобы получить более привычные глазу макросы (заодно поправим косяки indent):
Код после замены смещений:
SPL
#include <X11/Xlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int _x[] = { 15117427, 8413248, 5878632, 13027014, 1 };
int x_[616][1220];
char m[19][20];
int rn = 2166136261, _[8][40][64] = { 0 };
void *T;
Display * display;
Window window;
XImage * image;
void b(int t, int x, int y, int c, int l) {
if ((x >= 19 || y >= 19 || x < 0 || y < 0) || (m[y][x] == l)
|| (m[y][x] != c))
return;
m[y][x] = l;
b(t, x - 1, y, c, l);
b(t, x + 1, y, c, l);
b(t, x, y - 1, c, l);
b(t, x, y + 1, c, l);
}
void e(int t, int x, int y, int c, int l) {
if ((x >= 64 || y >= 40 || x < 0 || y < 0) || (_[t][y][x] == l)
|| (_[t][y][x] != c))
return;
_[t][y][x] = l;
e(t, x - 1, y, c, l);
e(t, x + 1, y, c, l);
e(t, x, y - 1, c, l);
e(t, x, y + 1, c, l);
}
void k(int t, int x, int y, int c, int l) {
while (c--) {
_[t][y][x++] = l;
_[t][y++][x++] = l;
}
}
void r(int t, int x, int y, int c, int l) {
while (c--) {
_[t][y][x--] = l;
_[t][y++][x--] = l;
}
}
void u(int t, int x, int y, int c, int l) {
while (c--)
_[t][y++][x] = l;
}
char *z[8] = {
(char[]) {4},
(char[]) {6, 1 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0,
21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2,
63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1,
32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63,
21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1,
36 - 0, 4, 6, 1 * 4 + 0, 3, 31, 8 - 0, 6, 1 * 4 + 2,
3, 33, 38 - 0, 6, 1 * 4 + 3, 3, 30, 38 - 0, 4},
(char[]) {5, 1},
(char[]) {6, 0 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16,
0, 0, 21 + 0 * 3 - 0, 16, 1, 63,
21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0,
4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1,
5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1,
63, 21 + 1 * 3 - 0, 16, 2, 63 * 1,
21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6,
0 * 4 + 0, 3, 31, 8 - 0, 6, 0 * 4 + 2,
3, 33, 38 - 0, 6, 0 * 4 + 3, 3, 30,
38 - 0,
4},
(char[]) {4},
(char[]) {4},
(char[]) {6, 2 * 4 + 1, 0 + 0, 32 - 0, 5 - 3, 16, 0, 0,
21 + 0 * 3 - 3, 16, 1, 63, 21 + 0 * 3 - 3, 16, 2, 63 * 0,
21 - 3, 4, 2, 31 + 0, 36 - 3, 4, 0 + 1, 32 - 1, 5 - 3,
16, 0, 0, 21 + 1 * 3 - 3, 16, 1, 63, 21 + 1 * 3 - 3, 16,
2, 63 * 1, 21 - 3, 4, 2, 31 + 1, 36 - 3, 4, 6, 2 * 4 + 0,
3, 31, 8 - 3, 6, 2 * 4 + 2, 3, 33, 38 - 3, 6, 2 * 4 + 3,
3, 30, 38 - 3, 4},
(char[]) {6, 13, 0, 32, 9 + 0, 12 - 0,
0, 8 + 0 + 0, 21, 12 - 0, 1,
31, 9 + 0, 12 - 0, 1,
55 - 0 - 0, 21, 12 - 0, 0,
32, 9 + 3, 12 - 3, 0,
8 + 3 + 3, 21, 12 - 3, 1,
31, 9 + 3, 12 - 3, 1,
55 - 3 - 3, 21, 12 - 3, 6,
14 + 0, 2, 31 + 0, 13, 4,
1 - 0, 31 + 0, 16, 7, 3,
30 + 3 * 0, 14, 6,
12 + 0 * 4, 3, 32 - 0,
11 + 0 * 8, 6, 14 + 1, 2,
31 + 1, 13, 4, 1 - 1,
31 + 1, 16, 7, 3,
30 + 3 * 1, 14, 6,
12 + 1 * 4, 3, 32 - 1,
11 + 1 * 8,
4}
};
void d_(int t, int p, int q) {
for (int y = 0; y < 40; y++)
for (int x = 0; x < 64; x++)
if (_[t][y][x])
x_[y + q * 16 + p * 16][x + 580 + p * 32 - q * 32] = _[t][y][x];
}
int main(int a, char *s[]) {
display = XOpenDisplay(0);
window = XCreateSimpleWindow(display,
RootWindow(display, DefaultScreen(display)),
0, 0, 1220, 616, 1, 0, 0);
image = XCreateImage(display,
DefaultVisual(display, DefaultScreen(display)),
DefaultDepth(display, DefaultScreen(display)),
2, 0, (char *) x_, 1220, 616, 32, 0);
for (int i = 0; i < 8; i++) {
char *p = z[i];
int c = 0;
while (*p != 4) {
switch (*p) {
case 0:
k(i, p[1], p[2], p[3], c);
p += 4;
break;
case 1:
r(i, p[1], p[2], p[3], c);
p += 4;
break;
case 2:
u(i, p[1], p[2], p[3], c);
p += 4;
break;
case 3:
e(i, p[1]
, p[2], 0, c);
p += 3;
break;
case 5:
p = z[p[1]];
break;
case 6:
c = _x[p[1] / 4] - 1643277 * (p[1] % 4);
p += 2;
break;
}
}
}
while (a++) {
int f = open(s[1], 0);
read(f, m, 380);
close(f);
for (int y = 0; y < 19; y++)
for (int x = 0; x < 19; x++)
if ((x % 14 == 2) && (y % 4 == 3))
m[y][x] = 46;
b(0, 2, 3, m[3][2], a & 1 ? 43 : 45);
b(0, 2, 7, m[7][2], a & 2 ? 43 : 45);
b(0, 2, 11, m[11][2], a & 4 ? 43 : 45);
b(0, 2, 15, m[15][2], a & 8 ? 43 : 45);
for (int i = 0; i < 20; i++)
for (int y = 0; y < 19; y++)
for (int x = 0; x < 19; x++)
if (m[y][x] == 62)
b(0, x + 1, y, m[y][x + 1],
!(m[y - 1][x] == 43 ? 1 : 0
|| m[y + 1][x] == 43 ? 1 : 0) ? 43 : 45);
for (int y = 0; y < 616; y++)
for (int x = 0; x < 1220; x++)
x_[y][x] = 0;
for (int y = 0; y < 19; y++)
for (int x = 0; x < 19; x++)
d_(((m[y][x] >> 4) & 1) << 2 | (m[y][x] & 3), x, y);
for (int y = 0; y < 19; y++)
for (int x = 0; x < 19; x++)
if ((x % 14 == 2) && (y % 4 == 3))
d_(7, x, y);
for (int y = 0; y < 616; y++)
for (int x = 0; x < 1220; x++) {
x_[y][x] &= 14737632 | (31 << ((x % 3) << 3));
x_[y][x] += 986895 & (rn *= 16777619);
}
XPutImage(display, window,
DefaultGC(display, DefaultScreen(display)),
image, 0, 0, 0, 0, 1220, 616);
XMapWindow(display, window);
XFlush(display);
sleep(1);
}
return 0;
}
Данные изображения
Обратим внимание, что XCreateImage требует на вход указатель на область памяти, где будут храниться данные пикселей. Для нашего вызова функции это переменная «x_». Переименуем ее в pixdata и найдем все места, где она используется:
void d_(int t, int p, int q) {
for (int y = 0; y < 40; y++)
for (int x = 0; x < 64; x++)
if (_[t][y][x])
pixdata[y + q * 16 + p * 16][x + 580 + p * 32 - q * 32] = _[t][y][x]; // Очевидно, что это копирование данных из какого-то источника
}
image = XCreateImage(display,
DefaultVisual(display, DefaultScreen(display)),
DefaultDepth(display, DefaultScreen(display)),
2, 0, (char *) pixdata, 1220, 616, 32, 0); // создание изображения
for (int y = 0; y < 616; y++)
for (int x = 0; x < 1220; x++)
pixdata[y][x] = 0; // "Обнуление" пикселей, закраска черным цветом
for (int y = 0; y < 616; y++)
for (int x = 0; x < 1220; x++) { // Какая-то странная математическая операция, очевидно применяемая ко всему изображению.
pixdata[y][x] &= 14737632 | (31 << ((x % 3) << 3));
pixdata[y][x] += 986895 & (rn *= 16777619);
}
Вычленим блок pixdata[..] = 0 в отдельную функцию и попытаемся разобрать, что же делает первое вхождение:
for (int y = 0; y < 40; y++)
for (int x = 0; x < 64; x++)
if (_[t][y][x])
pixdata[y + q * 16 + p * 16][x + 580 + p * 32 - q * 32] = _[t][y][x];
Если присмотреться к итоговому изображению, сформированному при работе программы, то легко заметить, что 40 и 64 — это размеры отдельного «блока», из которого построена схема.
Следовательно, эта функция рисует отдельный «тайл» на канве основного изображения, а судя по индексации массива "_" переменная «t» отвечает за индекс изображения, а p и q — за координаты x и y. Заодно переименуем "_" в textures:
Код на данный момент:
SPL
#include <X11/Xlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
/* Код после рефакторинга */
/*! \brief Ширина изображения в пикселях */
#define IMAGE_WIDTH *1220)
/*! \brief Высота изображения в пикселях */
#define IMAGE_HEIGHT (616)
/*! \brief Количество текстур */
#define TEXTURE_COUNT (8)
/*! \brief Ширина текстуры в пикселях */
#define TEXTURE_WIDTH (64)
/*! \brief Высота текстур в пикселях */
#define TEXTURE_HEIGHT (40)
/*! \brief Бинарные данные пикселей изображения.
* Типизированы к int для оперирования пикселями вместо каналов */
int pixdata[IMAGE_HEIGHT][IMAGE_WIDTH];
int textures[TEXTURE_COUNT][TEXTURE_HEIGHT][TEXTURE_WIDTH] = { 0 };
/*! \brief Экземпляр дисплея Xlib */
Display * display;
/*! \brief Экземпляр главного окна Xlib */
Window window;
/*! \brief Изображение для вывода на экран */
XImage * image;
/* \brief Обнуляет изображение, заполняя его черным цветом */
static void _image_reset(void) {
for (int y = 0; y < IMAGE_HEIGHT; y++)
for (int x = 0; x < IMAGE_WIDTH; x++)
pixdata[y][x] = 0;
}
/*! \brief Отрисовывает текстуру на главном холсте по указанным координатам
* \param[in] t Индекс текстуры
* \param[in] x X координата тайла
* \param[in] y Y координата тайла */
void _texture_draw(int t, int x, int y) {
for (int ty = 0; ty < TEXTURE_HEIGHT; ty++)
for (int tx = 0; tx < TEXTURE_WIDTH; tx++)
if (textures[t][ty][tx])
pixdata[ty + y * 16 + x * 16]
[tx + 580 + x * 32 - y * 32] = textures[t][ty][tx];
}
/* Код до рефакторинга */
int _x[] = { 15117427, 8413248, 5878632, 13027014, 1 };
char m[19][20];
int rn = 2166136261;
void *T;
void b(int t, int x, int y, int c, int l) {
if ((x >= 19 || y >= 19 || x < 0 || y < 0) || (m[y][x] == l)
|| (m[y][x] != c))
return;
m[y][x] = l;
b(t, x - 1, y, c, l);
b(t, x + 1, y, c, l);
b(t, x, y - 1, c, l);
b(t, x, y + 1, c, l);
}
void e(int t, int x, int y, int c, int l) {
if ((x >= TEXTURE_WIDTH || y >= TEXTURE_HEIGHT ||
x < 0 || y < 0) ||
(textures[t][y][x] == l) ||
(textures[t][y][x] != c))
return;
textures[t][y][x] = l;
e(t, x - 1, y, c, l);
e(t, x + 1, y, c, l);
e(t, x, y - 1, c, l);
e(t, x, y + 1, c, l);
}
void k(int t, int x, int y, int c, int l) {
while (c--) {
textures[t][y][x++] = l;
textures[t][y++][x++] = l;
}
}
void r(int t, int x, int y, int c, int l) {
while (c--) {
textures[t][y][x--] = l;
textures[t][y++][x--] = l;
}
}
void u(int t, int x, int y, int c, int l) {
while (c--)
textures[t][y++][x] = l;
}
char *z[8] = {
(char[]) {4},
(char[]) {6, 1 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0,
21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2,
63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1,
32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63,
21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1,
36 - 0, 4, 6, 1 * 4 + 0, 3, 31, 8 - 0, 6, 1 * 4 + 2,
3, 33, 38 - 0, 6, 1 * 4 + 3, 3, 30, 38 - 0, 4},
(char[]) {5, 1},
(char[]) {6, 0 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16,
0, 0, 21 + 0 * 3 - 0, 16, 1, 63,
21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0,
4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1,
5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1,
63, 21 + 1 * 3 - 0, 16, 2, 63 * 1,
21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6,
0 * 4 + 0, 3, 31, 8 - 0, 6, 0 * 4 + 2,
3, 33, 38 - 0, 6, 0 * 4 + 3, 3, 30,
38 - 0,
4},
(char[]) {4},
(char[]) {4},
(char[]) {6, 2 * 4 + 1, 0 + 0, 32 - 0, 5 - 3, 16, 0, 0,
21 + 0 * 3 - 3, 16, 1, 63, 21 + 0 * 3 - 3, 16, 2, 63 * 0,
21 - 3, 4, 2, 31 + 0, 36 - 3, 4, 0 + 1, 32 - 1, 5 - 3,
16, 0, 0, 21 + 1 * 3 - 3, 16, 1, 63, 21 + 1 * 3 - 3, 16,
2, 63 * 1, 21 - 3, 4, 2, 31 + 1, 36 - 3, 4, 6, 2 * 4 + 0,
3, 31, 8 - 3, 6, 2 * 4 + 2, 3, 33, 38 - 3, 6, 2 * 4 + 3,
3, 30, 38 - 3, 4},
(char[]) {6, 13, 0, 32, 9 + 0, 12 - 0,
0, 8 + 0 + 0, 21, 12 - 0, 1,
31, 9 + 0, 12 - 0, 1,
55 - 0 - 0, 21, 12 - 0, 0,
32, 9 + 3, 12 - 3, 0,
8 + 3 + 3, 21, 12 - 3, 1,
31, 9 + 3, 12 - 3, 1,
55 - 3 - 3, 21, 12 - 3, 6,
14 + 0, 2, 31 + 0, 13, 4,
1 - 0, 31 + 0, 16, 7, 3,
30 + 3 * 0, 14, 6,
12 + 0 * 4, 3, 32 - 0,
11 + 0 * 8, 6, 14 + 1, 2,
31 + 1, 13, 4, 1 - 1,
31 + 1, 16, 7, 3,
30 + 3 * 1, 14, 6,
12 + 1 * 4, 3, 32 - 1,
11 + 1 * 8,
4}
};
int main(int a, char *s[]) {
display = XOpenDisplay(0);
window = XCreateSimpleWindow(display,
RootWindow(display, DefaultScreen(display)),
0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, 1, 0, 0);
image = XCreateImage(display,
DefaultVisual(display, DefaultScreen(display)),
DefaultDepth(display, DefaultScreen(display)),
2, 0, (char *) pixdata, IMAGE_WIDTH, IMAGE_HEIGHT, 32, 0);
for (int i = 0; i < 8; i++) {
char *p = z[i];
int c = 0;
while (*p != 4) {
switch (*p) {
case 0:
k(i, p[1], p[2], p[3], c);
p += 4;
break;
case 1:
r(i, p[1], p[2], p[3], c);
p += 4;
break;
case 2:
u(i, p[1], p[2], p[3], c);
p += 4;
break;
case 3:
e(i, p[1]
, p[2], 0, c);
p += 3;
break;
case 5:
p = z[p[1]];
break;
case 6:
c = _x[p[1] / 4] - 1643277 * (p[1] % 4);
p += 2;
break;
}
}
}
while (a++) {
int f = open(s[1], 0);
read(f, m, 380);
close(f);
for (int y = 0; y < 19; y++)
for (int x = 0; x < 19; x++)
if ((x % 14 == 2) && (y % 4 == 3))
m[y][x] = 46;
b(0, 2, 3, m[3][2], a & 1 ? 43 : 45);
b(0, 2, 7, m[7][2], a & 2 ? 43 : 45);
b(0, 2, 11, m[11][2], a & 4 ? 43 : 45);
b(0, 2, 15, m[15][2], a & 8 ? 43 : 45);
for (int i = 0; i < 20; i++)
for (int y = 0; y < 19; y++)
for (int x = 0; x < 19; x++)
if (m[y][x] == 62)
b(0, x + 1, y, m[y][x + 1],
!(m[y - 1][x] == 43 ? 1 : 0
|| m[y + 1][x] == 43 ? 1 : 0) ? 43 : 45);
_image_reset();
for (int y = 0; y < 19; y++)
for (int x = 0; x < 19; x++)
_texture_draw(((m[y][x] >> 4) & 1) << 2 | (m[y][x] & 3), x, y);
for (int y = 0; y < 19; y++)
for (int x = 0; x < 19; x++)
if ((x % 14 == 2) && (y % 4 == 3))
_texture_draw(7, x, y);
for (int y = 0; y < IMAGE_HEIGHT; y++)
for (int x = 0; x < IMAGE_WIDTH; x++) {
pixdata[y][x] &= 14737632 | (31 << ((x % 3) << 3));
pixdata[y][x] += 986895 & (rn *= 16777619);
}
XPutImage(display, window,
DefaultGC(display, DefaultScreen(display)),
image, 0, 0, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
XMapWindow(display, window);
XFlush(display);
sleep(1);
}
return 0;
}
Чтение карты
Выделим в отдельную функцию строки open..close, где читается содержимое выбранного файла в переменную m (которую переименуем в mapdata).
Почему файл считается в каждом цикле? Так было короче с точки зрения кода и токенов. Около 4 дней занял процесс «утрамбовывания» кода чтобы поместиться в лимиты правил. Если читать файл только один раз то потребовалось бы дополнительное хранилище и какой-то аналог функции memcpy.
Выделена функция _map_read
SPL
#include <X11/Xlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
/* Код после рефакторинга */
/*! \brief Ширина изображения в пикселях */
#define IMAGE_WIDTH (1220)
/*! \brief Высота изображения в пикселях */
#define IMAGE_HEIGHT (616)
/*! \brief Сдвиг верхней грани карты от левой стороны */
#define IMAGE_SHIFTX (580)
/*! \brief Количество текстур */
#define TEXTURE_COUNT (8)
/*! \brief Ширина текстуры в пикселях */
#define TEXTURE_WIDTH (64)
/*! \brief Высота текстур в пикселях */
#define TEXTURE_HEIGHT (40)
/*! \brief Ширина грани текстуры в пикселях */
#define TEXTURE_TOP_WIDTH (64)
/*! \brief Высота грани текстуры в пикселях */
#define TEXTURE_TOP_HEIGHT (32)
/*! \brief Ширина эмулируемого поля */
#define MAP_WIDTH (19)
/*! \brief Высота эмулируемого поля */
#define MAP_HEIGHT (19)
/*! \brief Количество данные в файле-чертеже. Один байт добавлен для символа '\n' */
#define MAP_FILEDATA ((MAP_WIDTH + 1) * MAP_HEIGHT)
/*! \brief Бинарные данные пикселей изображения.
* Типизированы к int для оперирования пикселями вместо каналов */
int pixdata[IMAGE_HEIGHT][IMAGE_WIDTH];
/*! \brief Текстуры блоков, отображаемых на поле */
int textures[TEXTURE_COUNT][TEXTURE_HEIGHT][TEXTURE_WIDTH] = { 0 };
/*! \brief Данные эмулируемого поля.
* Один байт добавлен для упрощения обработки символа '\n' */
char mapdata[MAP_HEIGHT][MAP_WIDTH + 1];
/*! \brief Экземпляр дисплея Xlib */
Display * display;
/*! \brief Экземпляр главного окна Xlib */
Window window;
/*! \brief Изображение для вывода на экран */
XImage * image;
/* \brief Обнуляет изображение, заполняя его черным цветом */
static void _image_reset(void) {
for (int y = 0; y < IMAGE_HEIGHT; y++)
for (int x = 0; x < IMAGE_WIDTH; x++)
pixdata[y][x] = 0;
}
/*! \brief Отрисовывает текстуру на главном холсте по указанным координатам
* \param[in] t Индекс текстуры
* \param[in] x X координата тайла
* \param[in] y Y координата тайла */
static void _texture_draw(int t, int x, int y) {
for (int ty = 0; ty < TEXTURE_HEIGHT; ty++)
for (int tx = 0; tx < TEXTURE_WIDTH; tx++)
if (textures[t][ty][tx])
pixdata[ty +
y * (TEXTURE_TOP_HEIGHT / 2) +
x * (TEXTURE_TOP_HEIGHT / 2)]
[tx +
IMAGE_SHIFTX +
x * (TEXTURE_TOP_WIDTH / 2) -
y * TEXTURE_TOP_HEIGHT] = textures[t][ty][tx];
}
/*! \brief Читает данные файла-чертежа и загружает их в карту
* \param[in] filename Имя файла-чертежа */
static void _map_read(const char * filename) {
int f = open(filename, 0);
read(f, mapdata, MAP_FILEDATA);
close(f);
}
/* Код до рефакторинга */
int _x[] = { 15117427, 8413248, 5878632, 13027014, 1 };
int rn = 2166136261;
void *T;
void b(int t, int x, int y, int c, int l) {
if ((x >= MAP_WIDTH || y >= MAP_HEIGHT ||
x < 0 || y < 0) || (mapdata[y][x] == l)
|| (mapdata[y][x] != c))
return;
mapdata[y][x] = l;
b(t, x - 1, y, c, l);
b(t, x + 1, y, c, l);
b(t, x, y - 1, c, l);
b(t, x, y + 1, c, l);
}
void e(int t, int x, int y, int c, int l) {
if ((x >= TEXTURE_WIDTH || y >= TEXTURE_HEIGHT ||
x < 0 || y < 0) ||
(textures[t][y][x] == l) ||
(textures[t][y][x] != c))
return;
textures[t][y][x] = l;
e(t, x - 1, y, c, l);
e(t, x + 1, y, c, l);
e(t, x, y - 1, c, l);
e(t, x, y + 1, c, l);
}
void k(int t, int x, int y, int c, int l) {
while (c--) {
textures[t][y][x++] = l;
textures[t][y++][x++] = l;
}
}
void r(int t, int x, int y, int c, int l) {
while (c--) {
textures[t][y][x--] = l;
textures[t][y++][x--] = l;
}
}
void u(int t, int x, int y, int c, int l) {
while (c--)
textures[t][y++][x] = l;
}
char *z[8] = {
(char[]) {4},
(char[]) {6, 1 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0,
21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2,
63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1,
32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63,
21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1,
36 - 0, 4, 6, 1 * 4 + 0, 3, 31, 8 - 0, 6, 1 * 4 + 2,
3, 33, 38 - 0, 6, 1 * 4 + 3, 3, 30, 38 - 0, 4},
(char[]) {5, 1},
(char[]) {6, 0 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16,
0, 0, 21 + 0 * 3 - 0, 16, 1, 63,
21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0,
4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1,
5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1,
63, 21 + 1 * 3 - 0, 16, 2, 63 * 1,
21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6,
0 * 4 + 0, 3, 31, 8 - 0, 6, 0 * 4 + 2,
3, 33, 38 - 0, 6, 0 * 4 + 3, 3, 30,
38 - 0,
4},
(char[]) {4},
(char[]) {4},
(char[]) {6, 2 * 4 + 1, 0 + 0, 32 - 0, 5 - 3, 16, 0, 0,
21 + 0 * 3 - 3, 16, 1, 63, 21 + 0 * 3 - 3, 16, 2, 63 * 0,
21 - 3, 4, 2, 31 + 0, 36 - 3, 4, 0 + 1, 32 - 1, 5 - 3,
16, 0, 0, 21 + 1 * 3 - 3, 16, 1, 63, 21 + 1 * 3 - 3, 16,
2, 63 * 1, 21 - 3, 4, 2, 31 + 1, 36 - 3, 4, 6, 2 * 4 + 0,
3, 31, 8 - 3, 6, 2 * 4 + 2, 3, 33, 38 - 3, 6, 2 * 4 + 3,
3, 30, 38 - 3, 4},
(char[]) {6, 13, 0, 32, 9 + 0, 12 - 0,
0, 8 + 0 + 0, 21, 12 - 0, 1,
31, 9 + 0, 12 - 0, 1,
55 - 0 - 0, 21, 12 - 0, 0,
32, 9 + 3, 12 - 3, 0,
8 + 3 + 3, 21, 12 - 3, 1,
31, 9 + 3, 12 - 3, 1,
55 - 3 - 3, 21, 12 - 3, 6,
14 + 0, 2, 31 + 0, 13, 4,
1 - 0, 31 + 0, 16, 7, 3,
30 + 3 * 0, 14, 6,
12 + 0 * 4, 3, 32 - 0,
11 + 0 * 8, 6, 14 + 1, 2,
31 + 1, 13, 4, 1 - 1,
31 + 1, 16, 7, 3,
30 + 3 * 1, 14, 6,
12 + 1 * 4, 3, 32 - 1,
11 + 1 * 8,
4}
};
int main(int a, char *s[]) {
display = XOpenDisplay(0);
window = XCreateSimpleWindow(display,
RootWindow(display, DefaultScreen(display)),
0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, 1, 0, 0);
image = XCreateImage(display,
DefaultVisual(display, DefaultScreen(display)),
DefaultDepth(display, DefaultScreen(display)),
2, 0, (char *) pixdata, IMAGE_WIDTH, IMAGE_HEIGHT, 32, 0);
for (int i = 0; i < 8; i++) {
char *p = z[i];
int c = 0;
while (*p != 4) {
switch (*p) {
case 0:
k(i, p[1], p[2], p[3], c);
p += 4;
break;
case 1:
r(i, p[1], p[2], p[3], c);
p += 4;
break;
case 2:
u(i, p[1], p[2], p[3], c);
p += 4;
break;
case 3:
e(i, p[1]
, p[2], 0, c);
p += 3;
break;
case 5:
p = z[p[1]];
break;
case 6:
c = _x[p[1] / 4] - 1643277 * (p[1] % 4);
p += 2;
break;
}
}
}
while (a++) {
_map_read(s[1]);
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
if ((x % 14 == 2) && (y % 4 == 3))
mapdata[y][x] = 46;
b(0, 2, 3, mapdata[3][2], a & 1 ? 43 : 45);
b(0, 2, 7, mapdata[7][2], a & 2 ? 43 : 45);
b(0, 2, 11, mapdata[11][2], a & 4 ? 43 : 45);
b(0, 2, 15, mapdata[15][2], a & 8 ? 43 : 45);
for (int i = 0; i < 20; i++)
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
if (mapdata[y][x] == 62)
b(0, x + 1, y, mapdata[y][x + 1],
!(mapdata[y - 1][x] == 43 ? 1 : 0
|| mapdata[y + 1][x] == 43 ? 1 : 0) ? 43 : 45);
_image_reset();
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
_texture_draw(((m[y][x] >> 4) & 1) << 2 | (mapdata[y][x] & 3), x, y);
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
if ((x % 14 == 2) && (y % 4 == 3))
_texture_draw(7, x, y);
for (int y = 0; y < IMAGE_HEIGHT; y++)
for (int x = 0; x < IMAGE_WIDTH; x++) {
pixdata[y][x] &= 14737632 | (31 << ((x % 3) << 3));
pixdata[y][x] += 986895 & (rn *= 16777619);
}
XPutImage(display, window,
DefaultGC(display, DefaultScreen(display)),
image, 0, 0, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
XMapWindow(display, window);
XFlush(display);
sleep(1);
}
return 0;
}
Раз уж мы затронули переменную mapdata, обратим внимание на строки и функции, где она изменяется — это функция «b», которую мы пока трогать не будем и «main» где, обратив внимание на содержимое «комплектных» файлов конфигураций проведем рефакторинг:
После рефакторинга по mapdata
SPL
#include <X11/Xlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
/* Код после рефакторинга */
/*! \brief Ширина изображения в пикселях */
#define IMAGE_WIDTH (1220)
/*! \brief Высота изображения в пикселях */
#define IMAGE_HEIGHT (616)
/*! \brief Сдвиг верхней грани карты от левой стороны */
#define IMAGE_SHIFTX (580)
/*! \brief Количество текстур */
#define TEXTURE_COUNT (8)
/*! \brief Ширина текстуры в пикселях */
#define TEXTURE_WIDTH (64)
/*! \brief Высота текстур в пикселях */
#define TEXTURE_HEIGHT (40)
/*! \brief Ширина грани текстуры в пикселях */
#define TEXTURE_TOP_WIDTH (64)
/*! \brief Высота грани текстуры в пикселях */
#define TEXTURE_TOP_HEIGHT (32)
/*! \brief Ширина эмулируемого поля */
#define MAP_WIDTH (19)
/*! \brief Высота эмулируемого поля */
#define MAP_HEIGHT (19)
/*! \brief Количество данные в файле-чертеже. Один байт добавлен для символа '\n' */
#define MAP_FILEDATA ((MAP_WIDTH + 1) * MAP_HEIGHT)
/*! \brief Количество итераций во время расчета NOR-узлов */
#define MAP_ITERATIONS (20)
/*! \brief Содержит структуры синтаксиса файла-конфигурации */
enum map_characters {
MAPCHAR_WIRE = '.', /**< Провод (ASCII = 46) */
MAPCHAR_PLUS = '+', /**< Провод (есть ток) (ASCII = 43) */
MAPCHAR_MINUS = '-', /**< Провод (нет тока) (ASCII = 45) */
MAPCHAR_NOR = '>', /**< NOR-элемент (ASCII = 62) */
}
/*! \brief Бинарные данные пикселей изображения.
* Типизированы к int для оперирования пикселями вместо каналов */
int pixdata[IMAGE_HEIGHT][IMAGE_WIDTH];
/*! \brief Текстуры блоков, отображаемых на поле */
int textures[TEXTURE_COUNT][TEXTURE_HEIGHT][TEXTURE_WIDTH] = { 0 };
/*! \brief Данные эмулируемого поля.
* Один байт добавлен для упрощения обработки символа '\n' */
char mapdata[MAP_HEIGHT][MAP_WIDTH + 1];
/*! \brief Экземпляр дисплея Xlib */
Display * display;
/*! \brief Экземпляр главного окна Xlib */
Window window;
/*! \brief Изображение для вывода на экран */
XImage * image;
/* \brief Обнуляет изображение, заполняя его черным цветом */
static void _image_reset(void) {
for (int y = 0; y < IMAGE_HEIGHT; y++)
for (int x = 0; x < IMAGE_WIDTH; x++)
pixdata[y][x] = 0;
}
/*! \brief Отрисовывает текстуру на главном холсте по указанным координатам
* \param[in] t Индекс текстуры
* \param[in] x X координата тайла
* \param[in] y Y координата тайла */
static void _texture_draw(int t, int x, int y) {
for (int ty = 0; ty < TEXTURE_HEIGHT; ty++)
for (int tx = 0; tx < TEXTURE_WIDTH; tx++)
if (textures[t][ty][tx])
pixdata[ty +
y * (TEXTURE_TOP_HEIGHT / 2) +
x * (TEXTURE_TOP_HEIGHT / 2)]
[tx +
IMAGE_SHIFTX +
x * (TEXTURE_TOP_WIDTH / 2) -
y * TEXTURE_TOP_HEIGHT] = textures[t][ty][tx];
}
/*! \brief Читает данные файла-чертежа и загружает их в карту
* \param[in] filename Имя файла-чертежа */
static void _map_read(const char * filename) {
int f = open(filename, 0);
read(f, mapdata, MAP_FILEDATA);
close(f);
}
/*! \brief Заменяет иллюстративные входы из файла-конфигурации на вход
* в виде провода чтобы работала логика распространения фронта волны */
static void _map_wire_inputs(void) {
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
if ((x % 14 == 2) && (y % 4 == 3))
mapdata[y][x] = MAPCHAR_WIRE;
}
/*! \brief Включает соответствующие входы схемы в зависимости от значения
* счетчика.
* \param[in] counter Счетчик */
static void _map_wire_counter(int counter) {
b(0, 2, 3, mapdata[3][2], counter & 1 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
b(0, 2, 7, mapdata[7][2], counter & 2 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
b(0, 2, 11, mapdata[11][2], counter & 4 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
b(0, 2, 15, mapdata[15][2], counter & 8 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
}
/*! \brief Проводит расчет выходного (результирующего) тока после NOR-узла */
static void _map_process_gates(void) {
for (int i = 0; i < MAP_ITERATIONS; i++)
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
if (mapdata[y][x] == MAPCHAR_NOR)
b(0, x + 1, y, mapdata[y][x + 1],
!(mapdata[y - 1][x] == MAPCHAR_PLUS ? 1 : 0
|| mapdata[y + 1][x] == MAPCHAR_PLUS ? 1 : 0) ?
MAPCHAR_PLUS : MAPCHAR_MINUS);
}
/* Код до рефакторинга */
int _x[] = { 15117427, 8413248, 5878632, 13027014, 1 };
int rn = 2166136261;
void *T;
void b(int t, int x, int y, int c, int l) {
if ((x >= MAP_WIDTH || y >= MAP_HEIGHT ||
x < 0 || y < 0) || (mapdata[y][x] == l)
|| (mapdata[y][x] != c))
return;
mapdata[y][x] = l;
b(t, x - 1, y, c, l);
b(t, x + 1, y, c, l);
b(t, x, y - 1, c, l);
b(t, x, y + 1, c, l);
}
void e(int t, int x, int y, int c, int l) {
if ((x >= TEXTURE_WIDTH || y >= TEXTURE_HEIGHT ||
x < 0 || y < 0) ||
(textures[t][y][x] == l) ||
(textures[t][y][x] != c))
return;
textures[t][y][x] = l;
e(t, x - 1, y, c, l);
e(t, x + 1, y, c, l);
e(t, x, y - 1, c, l);
e(t, x, y + 1, c, l);
}
void k(int t, int x, int y, int c, int l) {
while (c--) {
textures[t][y][x++] = l;
textures[t][y++][x++] = l;
}
}
void r(int t, int x, int y, int c, int l) {
while (c--) {
textures[t][y][x--] = l;
textures[t][y++][x--] = l;
}
}
void u(int t, int x, int y, int c, int l) {
while (c--)
textures[t][y++][x] = l;
}
char *z[8] = {
(char[]) {4},
(char[]) {6, 1 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0,
21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2,
63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1,
32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63,
21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1,
36 - 0, 4, 6, 1 * 4 + 0, 3, 31, 8 - 0, 6, 1 * 4 + 2,
3, 33, 38 - 0, 6, 1 * 4 + 3, 3, 30, 38 - 0, 4},
(char[]) {5, 1},
(char[]) {6, 0 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16,
0, 0, 21 + 0 * 3 - 0, 16, 1, 63,
21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0,
4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1,
5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1,
63, 21 + 1 * 3 - 0, 16, 2, 63 * 1,
21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6,
0 * 4 + 0, 3, 31, 8 - 0, 6, 0 * 4 + 2,
3, 33, 38 - 0, 6, 0 * 4 + 3, 3, 30,
38 - 0,
4},
(char[]) {4},
(char[]) {4},
(char[]) {6, 2 * 4 + 1, 0 + 0, 32 - 0, 5 - 3, 16, 0, 0,
21 + 0 * 3 - 3, 16, 1, 63, 21 + 0 * 3 - 3, 16, 2, 63 * 0,
21 - 3, 4, 2, 31 + 0, 36 - 3, 4, 0 + 1, 32 - 1, 5 - 3,
16, 0, 0, 21 + 1 * 3 - 3, 16, 1, 63, 21 + 1 * 3 - 3, 16,
2, 63 * 1, 21 - 3, 4, 2, 31 + 1, 36 - 3, 4, 6, 2 * 4 + 0,
3, 31, 8 - 3, 6, 2 * 4 + 2, 3, 33, 38 - 3, 6, 2 * 4 + 3,
3, 30, 38 - 3, 4},
(char[]) {6, 13, 0, 32, 9 + 0, 12 - 0,
0, 8 + 0 + 0, 21, 12 - 0, 1,
31, 9 + 0, 12 - 0, 1,
55 - 0 - 0, 21, 12 - 0, 0,
32, 9 + 3, 12 - 3, 0,
8 + 3 + 3, 21, 12 - 3, 1,
31, 9 + 3, 12 - 3, 1,
55 - 3 - 3, 21, 12 - 3, 6,
14 + 0, 2, 31 + 0, 13, 4,
1 - 0, 31 + 0, 16, 7, 3,
30 + 3 * 0, 14, 6,
12 + 0 * 4, 3, 32 - 0,
11 + 0 * 8, 6, 14 + 1, 2,
31 + 1, 13, 4, 1 - 1,
31 + 1, 16, 7, 3,
30 + 3 * 1, 14, 6,
12 + 1 * 4, 3, 32 - 1,
11 + 1 * 8,
4}
};
int main(int a, char *s[]) {
display = XOpenDisplay(0);
window = XCreateSimpleWindow(display,
RootWindow(display, DefaultScreen(display)),
0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, 1, 0, 0);
image = XCreateImage(display,
DefaultVisual(display, DefaultScreen(display)),
DefaultDepth(display, DefaultScreen(display)),
2, 0, (char *) pixdata, IMAGE_WIDTH, IMAGE_HEIGHT, 32, 0);
for (int i = 0; i < 8; i++) {
char *p = z[i];
int c = 0;
while (*p != 4) {
switch (*p) {
case 0:
k(i, p[1], p[2], p[3], c);
p += 4;
break;
case 1:
r(i, p[1], p[2], p[3], c);
p += 4;
break;
case 2:
u(i, p[1], p[2], p[3], c);
p += 4;
break;
case 3:
e(i, p[1]
, p[2], 0, c);
p += 3;
break;
case 5:
p = z[p[1]];
break;
case 6:
c = _x[p[1] / 4] - 1643277 * (p[1] % 4);
p += 2;
break;
}
}
}
while (a++) {
_map_read(s[1]);
_map_wire_inputs();
_map_wire_counter(a);
_map_process_gates();
_image_reset();
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
_texture_draw(((mapdata[y][x] >> 4) & 1) << 2 | (mapdata[y][x] & 3), x, y);
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
if ((x % 14 == 2) && (y % 4 == 3))
_texture_draw(7, x, y);
for (int y = 0; y < IMAGE_HEIGHT; y++)
for (int x = 0; x < IMAGE_WIDTH; x++) {
pixdata[y][x] &= 14737632 | (31 << ((x % 3) << 3));
pixdata[y][x] += 986895 & (rn *= 16777619);
}
XPutImage(display, window,
DefaultGC(display, DefaultScreen(display)),
image, 0, 0, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
XMapWindow(display, window);
XFlush(display);
sleep(1);
}
return 0;
}
Для того, чтобы закончить обработку работы с «mapdata» нужно ответить еще на два вопроса — что такое функция «b»:
void b(int t, int x, int y, int c, int l) {
if ((x >= MAP_WIDTH || y >= MAP_HEIGHT ||
x < 0 || y < 0) || (mapdata[y][x] == l)
|| (mapdata[y][x] != c))
return;
mapdata[y][x] = l;
b(t, x - 1, y, c, l);
b(t, x + 1, y, c, l);
b(t, x, y - 1, c, l);
b(t, x, y + 1, c, l);
}
И что происходит в блоке
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
_texture_draw(((mapdata[y][x] >> 4) & 1) << 2 | (mapdata[y][x] & 3), x, y);
Функция «b»
Если внимательно присмотреться к функции «b», то можно заметить, что она до боли похожа на реализацию алгоритма flood_fill, что совпадает с ее теоретическим назначением — она «заливает» «провод» нужным состоянием, позволяя распространять фронт волны тока до конца провода. Переименуем ее и вынесем в блок «production ready».
Flood fill
SPL
#include <X11/Xlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
/* Код после рефакторинга */
/*! \brief Ширина изображения в пикселях */
#define IMAGE_WIDTH (1220)
/*! \brief Высота изображения в пикселях */
#define IMAGE_HEIGHT (616)
/*! \brief Сдвиг верхней грани карты от левой стороны */
#define IMAGE_SHIFTX (580)
/*! \brief Количество текстур */
#define TEXTURE_COUNT (8)
/*! \brief Ширина текстуры в пикселях */
#define TEXTURE_WIDTH (64)
/*! \brief Высота текстур в пикселях */
#define TEXTURE_HEIGHT (40)
/*! \brief Ширина грани текстуры в пикселях */
#define TEXTURE_TOP_WIDTH (64)
/*! \brief Высота грани текстуры в пикселях */
#define TEXTURE_TOP_HEIGHT (32)
/*! \brief Ширина эмулируемого поля */
#define MAP_WIDTH (19)
/*! \brief Высота эмулируемого поля */
#define MAP_HEIGHT (19)
/*! \brief Количество данные в файле-чертеже. Один байт добавлен для символа '\n' */
#define MAP_FILEDATA ((MAP_WIDTH + 1) * MAP_HEIGHT)
/*! \brief Количество итераций во время расчета NOR-узлов */
#define MAP_ITERATIONS (20)
/*! \brief Содержит структуры синтаксиса файла-конфигурации */
enum map_characters {
MAPCHAR_WIRE = '.', /**< Провод (ASCII = 46) */
MAPCHAR_PLUS = '+', /**< Провод (есть ток) (ASCII = 43) */
MAPCHAR_MINUS = '-', /**< Провод (нет тока) (ASCII = 45) */
MAPCHAR_NOR = '>', /**< NOR-элемент (ASCII = 62) */
};
/*! \brief Бинарные данные пикселей изображения.
* Типизированы к int для оперирования пикселями вместо каналов */
int pixdata[IMAGE_HEIGHT][IMAGE_WIDTH];
/*! \brief Текстуры блоков, отображаемых на поле */
int textures[TEXTURE_COUNT][TEXTURE_HEIGHT][TEXTURE_WIDTH] = { 0 };
/*! \brief Данные эмулируемого поля.
* Один байт добавлен для упрощения обработки символа '\n' */
char mapdata[MAP_HEIGHT][MAP_WIDTH + 1];
/*! \brief Экземпляр дисплея Xlib */
Display * display;
/*! \brief Экземпляр главного окна Xlib */
Window window;
/*! \brief Изображение для вывода на экран */
XImage * image;
/* \brief Обнуляет изображение, заполняя его черным цветом */
static void _image_reset(void) {
for (int y = 0; y < IMAGE_HEIGHT; y++)
for (int x = 0; x < IMAGE_WIDTH; x++)
pixdata[y][x] = 0;
}
/*! \brief Отрисовывает текстуру на главном холсте по указанным координатам
* \param[in] t Индекс текстуры
* \param[in] x X координата тайла
* \param[in] y Y координата тайла */
static void _texture_draw(int t, int x, int y) {
for (int ty = 0; ty < TEXTURE_HEIGHT; ty++)
for (int tx = 0; tx < TEXTURE_WIDTH; tx++)
if (textures[t][ty][tx])
pixdata[ty +
y * (TEXTURE_TOP_HEIGHT / 2) +
x * (TEXTURE_TOP_HEIGHT / 2)]
[tx +
IMAGE_SHIFTX +
x * (TEXTURE_TOP_WIDTH / 2) -
y * TEXTURE_TOP_HEIGHT] = textures[t][ty][tx];
}
/*! \brief Читает данные файла-чертежа и загружает их в карту
* \param[in] filename Имя файла-чертежа */
static void _map_read(const char * filename) {
int f = open(filename, 0);
read(f, mapdata, MAP_FILEDATA);
close(f);
}
/*! \brief Заменяет иллюстративные входы из файла-конфигурации на вход
* в виде провода чтобы работала логика распространения фронта волны */
static void _map_wire_inputs(void) {
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
if ((x % 14 == 2) && (y % 4 == 3))
mapdata[y][x] = MAPCHAR_WIRE;
}
/*! \brief Производит заливку проводника нужным состоянием
* \param[in] t Игнорируется, артефакт автогенерации кода
* \param[in] x X-координата заливки
* \param[in] y Y-координата заливки
* \param[in] c Исходное состояние
* \param[in] l Целевое состояние */
static void _map_fill(int t, int x, int y, int c, int l) {
if ((x >= MAP_WIDTH || y >= MAP_HEIGHT ||
x < 0 || y < 0) || (mapdata[y][x] == l)
|| (mapdata[y][x] != c))
return;
mapdata[y][x] = l;
_map_fill(t, x - 1, y, c, l);
_map_fill(t, x + 1, y, c, l);
_map_fill(t, x, y - 1, c, l);
_map_fill(t, x, y + 1, c, l);
}
/*! \brief Включает соответствующие входы схемы в зависимости от значения
* счетчика.
* \param[in] counter Счетчик */
static void _map_wire_counter(int counter) {
_map_fill(0, 2, 3, mapdata[3][2], counter & 1 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
_map_fill(0, 2, 7, mapdata[7][2], counter & 2 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
_map_fill(0, 2, 11, mapdata[11][2], counter & 4 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
_map_fill(0, 2, 15, mapdata[15][2], counter & 8 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
}
/*! \brief Проводит расчет выходного (результирующего) тока после NOR-узла */
static void _map_process_gates(void) {
for (int i = 0; i < MAP_ITERATIONS; i++)
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
if (mapdata[y][x] == MAPCHAR_NOR)
_map_fill(0, x + 1, y, mapdata[y][x + 1],
!(mapdata[y - 1][x] == MAPCHAR_PLUS ? 1 : 0
|| mapdata[y + 1][x] == MAPCHAR_PLUS ? 1 : 0) ?
MAPCHAR_PLUS : MAPCHAR_MINUS);
}
/* Код до рефакторинга */
int _x[] = { 15117427, 8413248, 5878632, 13027014, 1 };
int rn = 2166136261;
void *T;
void e(int t, int x, int y, int c, int l) {
if ((x >= TEXTURE_WIDTH || y >= TEXTURE_HEIGHT ||
x < 0 || y < 0) ||
(textures[t][y][x] == l) ||
(textures[t][y][x] != c))
return;
textures[t][y][x] = l;
e(t, x - 1, y, c, l);
e(t, x + 1, y, c, l);
e(t, x, y - 1, c, l);
e(t, x, y + 1, c, l);
}
void k(int t, int x, int y, int c, int l) {
while (c--) {
textures[t][y][x++] = l;
textures[t][y++][x++] = l;
}
}
void r(int t, int x, int y, int c, int l) {
while (c--) {
textures[t][y][x--] = l;
textures[t][y++][x--] = l;
}
}
void u(int t, int x, int y, int c, int l) {
while (c--)
textures[t][y++][x] = l;
}
char *z[8] = {
(char[]) {4},
(char[]) {6, 1 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0,
21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2,
63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1,
32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63,
21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1,
36 - 0, 4, 6, 1 * 4 + 0, 3, 31, 8 - 0, 6, 1 * 4 + 2,
3, 33, 38 - 0, 6, 1 * 4 + 3, 3, 30, 38 - 0, 4},
(char[]) {5, 1},
(char[]) {6, 0 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16,
0, 0, 21 + 0 * 3 - 0, 16, 1, 63,
21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0,
4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1,
5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1,
63, 21 + 1 * 3 - 0, 16, 2, 63 * 1,
21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6,
0 * 4 + 0, 3, 31, 8 - 0, 6, 0 * 4 + 2,
3, 33, 38 - 0, 6, 0 * 4 + 3, 3, 30,
38 - 0,
4},
(char[]) {4},
(char[]) {4},
(char[]) {6, 2 * 4 + 1, 0 + 0, 32 - 0, 5 - 3, 16, 0, 0,
21 + 0 * 3 - 3, 16, 1, 63, 21 + 0 * 3 - 3, 16, 2, 63 * 0,
21 - 3, 4, 2, 31 + 0, 36 - 3, 4, 0 + 1, 32 - 1, 5 - 3,
16, 0, 0, 21 + 1 * 3 - 3, 16, 1, 63, 21 + 1 * 3 - 3, 16,
2, 63 * 1, 21 - 3, 4, 2, 31 + 1, 36 - 3, 4, 6, 2 * 4 + 0,
3, 31, 8 - 3, 6, 2 * 4 + 2, 3, 33, 38 - 3, 6, 2 * 4 + 3,
3, 30, 38 - 3, 4},
(char[]) {6, 13, 0, 32, 9 + 0, 12 - 0,
0, 8 + 0 + 0, 21, 12 - 0, 1,
31, 9 + 0, 12 - 0, 1,
55 - 0 - 0, 21, 12 - 0, 0,
32, 9 + 3, 12 - 3, 0,
8 + 3 + 3, 21, 12 - 3, 1,
31, 9 + 3, 12 - 3, 1,
55 - 3 - 3, 21, 12 - 3, 6,
14 + 0, 2, 31 + 0, 13, 4,
1 - 0, 31 + 0, 16, 7, 3,
30 + 3 * 0, 14, 6,
12 + 0 * 4, 3, 32 - 0,
11 + 0 * 8, 6, 14 + 1, 2,
31 + 1, 13, 4, 1 - 1,
31 + 1, 16, 7, 3,
30 + 3 * 1, 14, 6,
12 + 1 * 4, 3, 32 - 1,
11 + 1 * 8,
4}
};
int main(int a, char *s[]) {
display = XOpenDisplay(0);
window = XCreateSimpleWindow(display,
RootWindow(display, DefaultScreen(display)),
0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, 1, 0, 0);
image = XCreateImage(display,
DefaultVisual(display, DefaultScreen(display)),
DefaultDepth(display, DefaultScreen(display)),
2, 0, (char *) pixdata, IMAGE_WIDTH, IMAGE_HEIGHT, 32, 0);
for (int i = 0; i < 8; i++) {
char *p = z[i];
int c = 0;
while (*p != 4) {
switch (*p) {
case 0:
k(i, p[1], p[2], p[3], c);
p += 4;
break;
case 1:
r(i, p[1], p[2], p[3], c);
p += 4;
break;
case 2:
u(i, p[1], p[2], p[3], c);
p += 4;
break;
case 3:
e(i, p[1]
, p[2], 0, c);
p += 3;
break;
case 5:
p = z[p[1]];
break;
case 6:
c = _x[p[1] / 4] - 1643277 * (p[1] % 4);
p += 2;
break;
}
}
}
while (a++) {
_map_read(s[1]);
_map_wire_inputs();
_map_wire_counter(a);
_map_process_gates();
_image_reset();
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
_texture_draw(((mapdata[y][x] >> 4) & 1) << 2 | (mapdata[y][x] & 3), x, y);
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
if ((x % 14 == 2) && (y % 4 == 3))
_texture_draw(7, x, y);
for (int y = 0; y < IMAGE_HEIGHT; y++)
for (int x = 0; x < IMAGE_WIDTH; x++) {
pixdata[y][x] &= 14737632 | (31 << ((x % 3) << 3));
pixdata[y][x] += 986895 & (rn *= 16777619);
}
XPutImage(display, window,
DefaultGC(display, DefaultScreen(display)),
image, 0, 0, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
XMapWindow(display, window);
XFlush(display);
sleep(1);
}
return 0;
}
Странный блок
Теперь осталось разобрать, что происходит в строке
_texture_draw(((mapdata[y][x] >> 4) & 1) << 2 | (mapdata[y][x] & 3), x, y);
Так как мы уже построили полную таблицу состояний, которые могут находиться внутри mapdata то мы можем получить все выходные значения первого параметра:
enum
int
Результат
MAPCHAR_WIRE
46
2
MAPCHAR_PLUS
43
3
MAPCHAR_MINUS
45
1
MAPCHAR_NOR
62
6
MAPCHAR_EMPTY
32
0
Ага, то есть по итогам этого преобразования мы получаем индекс текстуры в массиве текстур.
Для того, чтобы продумать механизм генерации из читаемого и ассоциативно понятного набора символов в индексы текстур понадобилась пара часов. Я выписал символы, которые могли означать провода и NOR-элементы а потом расписав их двоичные значения обводил кружочками уникальные области. Кроме текущего был второй вариант с более сложными вычислениями, но он длиннее, следовательно не подошел по лимиту токенов.
Замечательно, это дает нам возможность вычленить еще одну функцию и объявить enum для каждой текстуры:
После очередного рефакторинга:
SPL
#include <X11/Xlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
/* Код после рефакторинга */
/*! \brief Ширина изображения в пикселях */
#define IMAGE_WIDTH (1220)
/*! \brief Высота изображения в пикселях */
#define IMAGE_HEIGHT (616)
/*! \brief Сдвиг верхней грани карты от левой стороны */
#define IMAGE_SHIFTX (580)
/*! \brief Количество текстур */
#define TEXTURE_COUNT (8)
/*! \brief Ширина текстуры в пикселях */
#define TEXTURE_WIDTH (64)
/*! \brief Высота текстур в пикселях */
#define TEXTURE_HEIGHT (40)
/*! \brief Ширина грани текстуры в пикселях */
#define TEXTURE_TOP_WIDTH (64)
/*! \brief Высота грани текстуры в пикселях */
#define TEXTURE_TOP_HEIGHT (32)
/*! \brief Ширина эмулируемого поля */
#define MAP_WIDTH (19)
/*! \brief Высота эмулируемого поля */
#define MAP_HEIGHT (19)
/*! \brief Количество данные в файле-чертеже. Один байт добавлен для символа '\n' */
#define MAP_FILEDATA ((MAP_WIDTH + 1) * MAP_HEIGHT)
/*! \brief Количество итераций во время расчета NOR-узлов */
#define MAP_ITERATIONS (20)
/*! \brief Содержит структуры синтаксиса файла-конфигурации */
enum map_characters {
MAPCHAR_WIRE = '.', /**< Провод (ASCII = 46) */
MAPCHAR_PLUS = '+', /**< Провод (есть ток) (ASCII = 43) */
MAPCHAR_MINUS = '-', /**< Провод (нет тока) (ASCII = 45) */
MAPCHAR_NOR = '>', /**< NOR-элемент (ASCII = 62) */
MAPCHAR_EMPTY = ' ', /**< Пустой блок (ASCII = 32) */
};
/*! \brief Содержит индексы текстур */
enum textures_indexes {
TEXINDEX_EMPTY = (0), /**< Индекс пустой текстуры */
TEXINDEX_MINUS = (1), /**< Индекс текстуры "выключенного провода" */
TEXINDEX_WIRE = (2), /**< Индекс текстуры нейтрального провода */
TEXINDEX_PLUS = (3), /**< Индекс текстуры "включенного" провода */
/**/
TEXINDEX_NOR = (6) /**< Индекс текстуры NOR-элемента */
};
/*! \brief Аргументы программы */
enum program_arguments {
ARG_PROGRAM, /**< Имя самой программы */
ARG_BLUEPRINT /**< Имя файла-чертежа */
};
/*! \brief Бинарные данные пикселей изображения.
* Типизированы к int для оперирования пикселями вместо каналов */
int pixdata[IMAGE_HEIGHT][IMAGE_WIDTH];
/*! \brief Текстуры блоков, отображаемых на поле */
int textures[TEXTURE_COUNT][TEXTURE_HEIGHT][TEXTURE_WIDTH] = { 0 };
/*! \brief Данные эмулируемого поля.
* Один байт добавлен для упрощения обработки символа '\n' */
char mapdata[MAP_HEIGHT][MAP_WIDTH + 1];
/*! \brief Экземпляр дисплея Xlib */
Display * display;
/*! \brief Экземпляр главного окна Xlib */
Window window;
/*! \brief Изображение для вывода на экран */
XImage * image;
/* \brief Обнуляет изображение, заполняя его черным цветом */
static void _image_reset(void) {
for (int y = 0; y < IMAGE_HEIGHT; y++)
for (int x = 0; x < IMAGE_WIDTH; x++)
pixdata[y][x] = 0;
}
/*! \brief Конвертирует символ из чертежа в индекс текстуры
* \param[in] elem Символ чертежа
* \return Индекс текстуры */
static int _map2texture(char elem) {
return ((elem >> 4) & 1) << 2 | (elem & 3);
}
/*! \brief Собирает изображение из отдельных тайлов согласно карте */
static void _image_compile(void) {
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
_texture_draw(_map2texture(mapdata[y][x]), x, y);
}
/*! \brief Рисует изображение на экране */
static void _image_draw(void) {
XPutImage(display, window,
DefaultGC(display, DefaultScreen(display)),
image, 0, 0, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
XMapWindow(display, window);
XFlush(display);
}
/*! \brief Отрисовывает текстуру на главном холсте по указанным координатам
* \param[in] t Индекс текстуры
* \param[in] x X координата тайла
* \param[in] y Y координата тайла */
static void _texture_draw(int t, int x, int y) {
for (int ty = 0; ty < TEXTURE_HEIGHT; ty++)
for (int tx = 0; tx < TEXTURE_WIDTH; tx++)
if (textures[t][ty][tx])
pixdata[ty +
y * (TEXTURE_TOP_HEIGHT / 2) +
x * (TEXTURE_TOP_HEIGHT / 2)]
[tx +
IMAGE_SHIFTX +
x * (TEXTURE_TOP_WIDTH / 2) -
y * TEXTURE_TOP_HEIGHT] = textures[t][ty][tx];
}
/*! \brief Читает данные файла-чертежа и загружает их в карту
* \param[in] filename Имя файла-чертежа */
static void _map_read(const char * filename) {
int f = open(filename, 0);
read(f, mapdata, MAP_FILEDATA);
close(f);
}
/*! \brief Заменяет иллюстративные входы из файла-конфигурации на вход
* в виде провода чтобы работала логика распространения фронта волны */
static void _map_wire_inputs(void) {
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
if ((x % 14 == 2) && (y % 4 == 3))
mapdata[y][x] = MAPCHAR_WIRE;
}
/*! \brief Производит заливку проводника нужным состоянием
* \param[in] t Игнорируется, артефакт автогенерации кода
* \param[in] x X-координата заливки
* \param[in] y Y-координата заливки
* \param[in] c Исходное состояние
* \param[in] l Целевое состояние */
static void _map_fill(int t, int x, int y, int c, int l) {
if ((x >= MAP_WIDTH || y >= MAP_HEIGHT ||
x < 0 || y < 0) || (mapdata[y][x] == l)
|| (mapdata[y][x] != c))
return;
mapdata[y][x] = l;
_map_fill(t, x - 1, y, c, l);
_map_fill(t, x + 1, y, c, l);
_map_fill(t, x, y - 1, c, l);
_map_fill(t, x, y + 1, c, l);
}
/*! \brief Включает соответствующие входы схемы в зависимости от значения
* счетчика.
* \param[in] counter Счетчик */
static void _map_wire_counter(int counter) {
_map_fill(0, 2, 3, mapdata[3][2], counter & 1 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
_map_fill(0, 2, 7, mapdata[7][2], counter & 2 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
_map_fill(0, 2, 11, mapdata[11][2], counter & 4 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
_map_fill(0, 2, 15, mapdata[15][2], counter & 8 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
}
/*! \brief Проводит расчет выходного (результирующего) тока после NOR-узла */
static void _map_process_gates(void) {
for (int i = 0; i < MAP_ITERATIONS; i++)
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
if (mapdata[y][x] == MAPCHAR_NOR)
_map_fill(0, x + 1, y, mapdata[y][x + 1],
!(mapdata[y - 1][x] == MAPCHAR_PLUS ? 1 : 0
|| mapdata[y + 1][x] == MAPCHAR_PLUS ? 1 : 0) ?
MAPCHAR_PLUS : MAPCHAR_MINUS);
}
/* Код до рефакторинга */
int _x[] = { 15117427, 8413248, 5878632, 13027014, 1 };
int rn = 2166136261;
void *T;
void e(int t, int x, int y, int c, int l) {
if ((x >= TEXTURE_WIDTH || y >= TEXTURE_HEIGHT ||
x < 0 || y < 0) ||
(textures[t][y][x] == l) ||
(textures[t][y][x] != c))
return;
textures[t][y][x] = l;
e(t, x - 1, y, c, l);
e(t, x + 1, y, c, l);
e(t, x, y - 1, c, l);
e(t, x, y + 1, c, l);
}
void k(int t, int x, int y, int c, int l) {
while (c--) {
textures[t][y][x++] = l;
textures[t][y++][x++] = l;
}
}
void r(int t, int x, int y, int c, int l) {
while (c--) {
textures[t][y][x--] = l;
textures[t][y++][x--] = l;
}
}
void u(int t, int x, int y, int c, int l) {
while (c--)
textures[t][y++][x] = l;
}
char *z[8] = {
(char[]) {4},
(char[]) {6, 1 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0,
21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2,
63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1,
32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63,
21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1,
36 - 0, 4, 6, 1 * 4 + 0, 3, 31, 8 - 0, 6, 1 * 4 + 2,
3, 33, 38 - 0, 6, 1 * 4 + 3, 3, 30, 38 - 0, 4},
(char[]) {5, 1},
(char[]) {6, 0 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16,
0, 0, 21 + 0 * 3 - 0, 16, 1, 63,
21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0,
4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1,
5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1,
63, 21 + 1 * 3 - 0, 16, 2, 63 * 1,
21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6,
0 * 4 + 0, 3, 31, 8 - 0, 6, 0 * 4 + 2,
3, 33, 38 - 0, 6, 0 * 4 + 3, 3, 30,
38 - 0,
4},
(char[]) {4},
(char[]) {4},
(char[]) {6, 2 * 4 + 1, 0 + 0, 32 - 0, 5 - 3, 16, 0, 0,
21 + 0 * 3 - 3, 16, 1, 63, 21 + 0 * 3 - 3, 16, 2, 63 * 0,
21 - 3, 4, 2, 31 + 0, 36 - 3, 4, 0 + 1, 32 - 1, 5 - 3,
16, 0, 0, 21 + 1 * 3 - 3, 16, 1, 63, 21 + 1 * 3 - 3, 16,
2, 63 * 1, 21 - 3, 4, 2, 31 + 1, 36 - 3, 4, 6, 2 * 4 + 0,
3, 31, 8 - 3, 6, 2 * 4 + 2, 3, 33, 38 - 3, 6, 2 * 4 + 3,
3, 30, 38 - 3, 4},
(char[]) {6, 13, 0, 32, 9 + 0, 12 - 0,
0, 8 + 0 + 0, 21, 12 - 0, 1,
31, 9 + 0, 12 - 0, 1,
55 - 0 - 0, 21, 12 - 0, 0,
32, 9 + 3, 12 - 3, 0,
8 + 3 + 3, 21, 12 - 3, 1,
31, 9 + 3, 12 - 3, 1,
55 - 3 - 3, 21, 12 - 3, 6,
14 + 0, 2, 31 + 0, 13, 4,
1 - 0, 31 + 0, 16, 7, 3,
30 + 3 * 0, 14, 6,
12 + 0 * 4, 3, 32 - 0,
11 + 0 * 8, 6, 14 + 1, 2,
31 + 1, 13, 4, 1 - 1,
31 + 1, 16, 7, 3,
30 + 3 * 1, 14, 6,
12 + 1 * 4, 3, 32 - 1,
11 + 1 * 8,
4}
};
int main(int argc, char * args[]) {
display = XOpenDisplay(0);
window = XCreateSimpleWindow(display,
RootWindow(display, DefaultScreen(display)),
0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, 1, 0, 0);
image = XCreateImage(display,
DefaultVisual(display, DefaultScreen(display)),
DefaultDepth(display, DefaultScreen(display)),
2, 0, (char *) pixdata, IMAGE_WIDTH, IMAGE_HEIGHT, 32, 0);
for (int i = 0; i < 8; i++) {
char *p = z[i];
int c = 0;
while (*p != 4) {
switch (*p) {
case 0:
k(i, p[1], p[2], p[3], c);
p += 4;
break;
case 1:
r(i, p[1], p[2], p[3], c);
p += 4;
break;
case 2:
u(i, p[1], p[2], p[3], c);
p += 4;
break;
case 3:
e(i, p[1]
, p[2], 0, c);
p += 3;
break;
case 5:
p = z[p[1]];
break;
case 6:
c = _x[p[1] / 4] - 1643277 * (p[1] % 4);
p += 2;
break;
}
}
}
unsigned int counter = 0;
while (counter++) {
_map_read(args[ARG_BLUEPRINT]);
_map_wire_inputs();
_map_wire_counter(counter);
_map_process_gates();
_image_reset();
_image_compile();
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
if ((x % 14 == 2) && (y % 4 == 3))
_texture_draw(7, x, y);
for (int y = 0; y < IMAGE_HEIGHT; y++)
for (int x = 0; x < IMAGE_WIDTH; x++) {
pixdata[y][x] &= 14737632 | (31 << ((x % 3) << 3));
pixdata[y][x] += 986895 & (rn *= 16777619);
}
_image_draw();
sleep(1);
}
return 0;
}
Текстура #7
Если присмотреться к массиву «z», то можно заметить, что он содержит 8 блоков данных, как константа количества текстур. И в нем даже есть два пробела на позициях 4 и 5, прямо как в нашем enum, скорее всего это данные текстур. Однако он содержит 8 изображений, а мы смогли «открыть» только 7. Однако, если мы будем достаточно внимательными, то мы сможем заметить, что есть участок кода, который рисует конкретно 7ю текстуру:
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
if ((x % 14 == 2) && (y % 4 == 3))
_texture_draw(7, x, y);
По позиции уже можно догадаться, что это, но закомментируем эти строки и запустим приложение:
До:
После:
Теперь мы точно знаем, что это текстура отверстия на плате и можем добавить ее в список enum, вынеся её отрисовку в отдельную функцию:
Все текстуры в сборе:
SPL
#include <X11/Xlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
/* Код после рефакторинга */
/*! \brief Ширина изображения в пикселях */
#define IMAGE_WIDTH (1220)
/*! \brief Высота изображения в пикселях */
#define IMAGE_HEIGHT (616)
/*! \brief Сдвиг верхней грани карты от левой стороны */
#define IMAGE_SHIFTX (580)
/*! \brief Количество текстур */
#define TEXTURE_COUNT (8)
/*! \brief Ширина текстуры в пикселях */
#define TEXTURE_WIDTH (64)
/*! \brief Высота текстур в пикселях */
#define TEXTURE_HEIGHT (40)
/*! \brief Ширина грани текстуры в пикселях */
#define TEXTURE_TOP_WIDTH (64)
/*! \brief Высота грани текстуры в пикселях */
#define TEXTURE_TOP_HEIGHT (32)
/*! \brief Ширина эмулируемого поля */
#define MAP_WIDTH (19)
/*! \brief Высота эмулируемого поля */
#define MAP_HEIGHT (19)
/*! \brief Количество данные в файле-чертеже. Один байт добавлен для символа '\n' */
#define MAP_FILEDATA ((MAP_WIDTH + 1) * MAP_HEIGHT)
/*! \brief Количество итераций во время расчета NOR-узлов */
#define MAP_ITERATIONS (20)
/*! \brief Содержит структуры синтаксиса файла-конфигурации */
enum map_characters {
MAPCHAR_WIRE = '.', /**< Провод (ASCII = 46) */
MAPCHAR_PLUS = '+', /**< Провод (есть ток) (ASCII = 43) */
MAPCHAR_MINUS = '-', /**< Провод (нет тока) (ASCII = 45) */
MAPCHAR_NOR = '>', /**< NOR-элемент (ASCII = 62) */
MAPCHAR_EMPTY = ' ', /**< Пустой блок (ASCII = 32) */
};
/*! \brief Содержит индексы текстур */
enum textures_indexes {
TEXINDEX_EMPTY = (0), /**< Индекс пустой текстуры */
TEXINDEX_MINUS = (1), /**< Индекс текстуры "выключенного провода" */
TEXINDEX_WIRE = (2), /**< Индекс текстуры нейтрального провода */
TEXINDEX_PLUS = (3), /**< Индекс текстуры "включенного" провода */
/**/
TEXINDEX_NOR = (6), /**< Индекс текстуры NOR-элемента */
TEXINDEX_HOLE = (7) /**< Индекс текстуры отверстия на плате */
};
/*! \brief Аргументы программы */
enum program_arguments {
ARG_PROGRAM, /**< Имя самой программы */
ARG_BLUEPRINT /**< Имя файла-чертежа */
};
/*! \brief Бинарные данные пикселей изображения.
* Типизированы к int для оперирования пикселями вместо каналов */
int pixdata[IMAGE_HEIGHT][IMAGE_WIDTH];
/*! \brief Текстуры блоков, отображаемых на поле */
int textures[TEXTURE_COUNT][TEXTURE_HEIGHT][TEXTURE_WIDTH] = { 0 };
/*! \brief Данные эмулируемого поля.
* Один байт добавлен для упрощения обработки символа '\n' */
char mapdata[MAP_HEIGHT][MAP_WIDTH + 1];
/*! \brief Экземпляр дисплея Xlib */
Display * display;
/*! \brief Экземпляр главного окна Xlib */
Window window;
/*! \brief Изображение для вывода на экран */
XImage * image;
static void _texture_draw(int t, int x, int y);
/*! \brief Создает изображение и сопутствующие сущности */
static void _image_create(void) {
display = XOpenDisplay(0);
window = XCreateSimpleWindow(display,
RootWindow(display, DefaultScreen(display)),
0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, 1, 0, 0);
image = XCreateImage(display,
DefaultVisual(display, DefaultScreen(display)),
DefaultDepth(display, DefaultScreen(display)),
2, 0, (char *) pixdata, IMAGE_WIDTH, IMAGE_HEIGHT, 32, 0);
}
/* \brief Обнуляет изображение, заполняя его черным цветом */
static void _image_reset(void) {
for (int y = 0; y < IMAGE_HEIGHT; y++)
for (int x = 0; x < IMAGE_WIDTH; x++)
pixdata[y][x] = 0;
}
/*! \brief Конвертирует символ из чертежа в индекс текстуры
* \param[in] elem Символ чертежа
* \return Индекс текстуры */
static int _map2texture(char elem) {
return ((elem >> 4) & 1) << 2 | (elem & 3);
}
/*! \brief Собирает изображение из отдельных тайлов согласно карте */
static void _image_compile(void) {
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
_texture_draw(_map2texture(mapdata[y][x]), x, y);
}
/*! \brief Рисует изображение на экране */
static void _image_draw(void) {
XPutImage(display, window,
DefaultGC(display, DefaultScreen(display)),
image, 0, 0, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
XMapWindow(display, window);
XFlush(display);
}
/*! \brief Рисует отверстия на печатной плате */
static void _image_drill(void) {
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
if ((x % 14 == 2) && (y % 4 == 3))
_texture_draw(TEXINDEX_HOLE, x, y);
}
/*! \brief Отрисовывает текстуру на главном холсте по указанным координатам
* \param[in] t Индекс текстуры
* \param[in] x X координата тайла
* \param[in] y Y координата тайла */
static void _texture_draw(int t, int x, int y) {
for (int ty = 0; ty < TEXTURE_HEIGHT; ty++)
for (int tx = 0; tx < TEXTURE_WIDTH; tx++)
if (textures[t][ty][tx])
pixdata[ty +
y * (TEXTURE_TOP_HEIGHT / 2) +
x * (TEXTURE_TOP_HEIGHT / 2)]
[tx +
IMAGE_SHIFTX +
x * (TEXTURE_TOP_WIDTH / 2) -
y * TEXTURE_TOP_HEIGHT] = textures[t][ty][tx];
}
/*! \brief Читает данные файла-чертежа и загружает их в карту
* \param[in] filename Имя файла-чертежа */
static void _map_read(const char * filename) {
int f = open(filename, 0);
read(f, mapdata, MAP_FILEDATA);
close(f);
}
/*! \brief Заменяет иллюстративные входы из файла-конфигурации на вход
* в виде провода чтобы работала логика распространения фронта волны */
static void _map_wire_inputs(void) {
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
if ((x % 14 == 2) && (y % 4 == 3))
mapdata[y][x] = MAPCHAR_WIRE;
}
/*! \brief Производит заливку проводника нужным состоянием
* \param[in] t Игнорируется, артефакт автогенерации кода
* \param[in] x X-координата заливки
* \param[in] y Y-координата заливки
* \param[in] c Исходное состояние
* \param[in] l Целевое состояние */
static void _map_fill(int t, int x, int y, int c, int l) {
if ((x >= MAP_WIDTH || y >= MAP_HEIGHT ||
x < 0 || y < 0) || (mapdata[y][x] == l)
|| (mapdata[y][x] != c))
return;
mapdata[y][x] = l;
_map_fill(t, x - 1, y, c, l);
_map_fill(t, x + 1, y, c, l);
_map_fill(t, x, y - 1, c, l);
_map_fill(t, x, y + 1, c, l);
}
/*! \brief Включает соответствующие входы схемы в зависимости от значения
* счетчика.
* \param[in] counter Счетчик */
static void _map_wire_counter(int counter) {
_map_fill(0, 2, 3, mapdata[3][2], counter & 1 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
_map_fill(0, 2, 7, mapdata[7][2], counter & 2 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
_map_fill(0, 2, 11, mapdata[11][2], counter & 4 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
_map_fill(0, 2, 15, mapdata[15][2], counter & 8 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
}
/*! \brief Проводит расчет выходного (результирующего) тока после NOR-узла */
static void _map_process_gates(void) {
for (int i = 0; i < MAP_ITERATIONS; i++)
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
if (mapdata[y][x] == MAPCHAR_NOR)
_map_fill(0, x + 1, y, mapdata[y][x + 1],
!(mapdata[y - 1][x] == MAPCHAR_PLUS ? 1 : 0
|| mapdata[y + 1][x] == MAPCHAR_PLUS ? 1 : 0) ?
MAPCHAR_PLUS : MAPCHAR_MINUS);
}
/* Код до рефакторинга */
int _x[] = { 15117427, 8413248, 5878632, 13027014, 1 };
int rn = 2166136261;
void *T;
void e(int t, int x, int y, int c, int l) {
if ((x >= TEXTURE_WIDTH || y >= TEXTURE_HEIGHT ||
x < 0 || y < 0) ||
(textures[t][y][x] == l) ||
(textures[t][y][x] != c))
return;
textures[t][y][x] = l;
e(t, x - 1, y, c, l);
e(t, x + 1, y, c, l);
e(t, x, y - 1, c, l);
e(t, x, y + 1, c, l);
}
void k(int t, int x, int y, int c, int l) {
while (c--) {
textures[t][y][x++] = l;
textures[t][y++][x++] = l;
}
}
void r(int t, int x, int y, int c, int l) {
while (c--) {
textures[t][y][x--] = l;
textures[t][y++][x--] = l;
}
}
void u(int t, int x, int y, int c, int l) {
while (c--)
textures[t][y++][x] = l;
}
char *z[8] = {
(char[]) {4},
(char[]) {6, 1 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0,
21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2,
63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1,
32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63,
21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1,
36 - 0, 4, 6, 1 * 4 + 0, 3, 31, 8 - 0, 6, 1 * 4 + 2,
3, 33, 38 - 0, 6, 1 * 4 + 3, 3, 30, 38 - 0, 4},
(char[]) {5, 1},
(char[]) {6, 0 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16,
0, 0, 21 + 0 * 3 - 0, 16, 1, 63,
21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0,
4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1,
5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1,
63, 21 + 1 * 3 - 0, 16, 2, 63 * 1,
21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6,
0 * 4 + 0, 3, 31, 8 - 0, 6, 0 * 4 + 2,
3, 33, 38 - 0, 6, 0 * 4 + 3, 3, 30,
38 - 0,
4},
(char[]) {4},
(char[]) {4},
(char[]) {6, 2 * 4 + 1, 0 + 0, 32 - 0, 5 - 3, 16, 0, 0,
21 + 0 * 3 - 3, 16, 1, 63, 21 + 0 * 3 - 3, 16, 2, 63 * 0,
21 - 3, 4, 2, 31 + 0, 36 - 3, 4, 0 + 1, 32 - 1, 5 - 3,
16, 0, 0, 21 + 1 * 3 - 3, 16, 1, 63, 21 + 1 * 3 - 3, 16,
2, 63 * 1, 21 - 3, 4, 2, 31 + 1, 36 - 3, 4, 6, 2 * 4 + 0,
3, 31, 8 - 3, 6, 2 * 4 + 2, 3, 33, 38 - 3, 6, 2 * 4 + 3,
3, 30, 38 - 3, 4},
(char[]) {6, 13, 0, 32, 9 + 0, 12 - 0,
0, 8 + 0 + 0, 21, 12 - 0, 1,
31, 9 + 0, 12 - 0, 1,
55 - 0 - 0, 21, 12 - 0, 0,
32, 9 + 3, 12 - 3, 0,
8 + 3 + 3, 21, 12 - 3, 1,
31, 9 + 3, 12 - 3, 1,
55 - 3 - 3, 21, 12 - 3, 6,
14 + 0, 2, 31 + 0, 13, 4,
1 - 0, 31 + 0, 16, 7, 3,
30 + 3 * 0, 14, 6,
12 + 0 * 4, 3, 32 - 0,
11 + 0 * 8, 6, 14 + 1, 2,
31 + 1, 13, 4, 1 - 1,
31 + 1, 16, 7, 3,
30 + 3 * 1, 14, 6,
12 + 1 * 4, 3, 32 - 1,
11 + 1 * 8,
4}
};
int main(int argc, char * args[]) {
_image_create();
for (int i = 0; i < 8; i++) {
char *p = z[i];
int c = 0;
while (*p != 4) {
switch (*p) {
case 0:
k(i, p[1], p[2], p[3], c);
p += 4;
break;
case 1:
r(i, p[1], p[2], p[3], c);
p += 4;
break;
case 2:
u(i, p[1], p[2], p[3], c);
p += 4;
break;
case 3:
e(i, p[1]
, p[2], 0, c);
p += 3;
break;
case 5:
p = z[p[1]];
break;
case 6:
c = _x[p[1] / 4] - 1643277 * (p[1] % 4);
p += 2;
break;
}
}
}
unsigned int counter = 1;
while (counter++) {
_map_read(args[ARG_BLUEPRINT]);
_map_wire_inputs();
_map_wire_counter(counter);
_map_process_gates();
_image_reset();
_image_compile();
_image_drill();
for (int y = 0; y < IMAGE_HEIGHT; y++)
for (int x = 0; x < IMAGE_WIDTH; x++) {
pixdata[y][x] &= 14737632 | (31 << ((x % 3) << 3));
pixdata[y][x] += 986895 & (rn *= 16777619);
}
_image_draw();
sleep(1);
}
return 0;
}
Шейдеры
Котики из предыдущей статьи обещали шейдеры. И они тут есть. А где код, который отвечает за их обработку?
_image_reset(); // Рано
_image_compile(); // Рано
_image_drill(); // Рано
for (int y = 0; y < IMAGE_HEIGHT; y++)
for (int x = 0; x < IMAGE_WIDTH; x++) {
pixdata[y][x] &= 14737632 | (31 << ((x % 3) << 3));
pixdata[y][x] += 986895 & (rn *= 16777619);
}
_image_draw(); // Поздно
Методом исключения понимаем, что зашумленность экрана и эффект LCD-монитора дают эти две строчки. А как они работают?
Начнем с последней:
pixdata[y][x] += 986895 & (rn *= 16777619);
К каждому пикселю прибавляется значение 986895 (что в hex-варианты выглядит как 0x0f0f0f), которое перед этим было посредством операции битовое-И совмещено с результатом умножения rn на 16777619. Если бы rn было ренератором случайных чисел, то это создало бы зернистый «шум» на экране в пределах 16 градаций по каждому каналу. И раз шум появляется, значит rn и есть генератор случайных чисел. Но как это достигается?
int rn = 2166136261;
В самом начале программы переменная rn инициализируется числом 2166136261. А на каждой итерации пикселя умножается на 16777619. Это ничто иное, как генератор псевдослучайных чисел. Вот только вместо линейного хорошо изученного генератора используется алгоритм хэширования FNV без шага с XOR, поскольку нам не нужен конечный результат.
Остается понять, как работает предыдущая строка:
pixdata[y][x] &= 14737632 | (31 << ((x % 3) << 3));
Переведем число 14737632 в шестнадцатиричный формат, так как мы работаем с однобайтными каналами света: 0xe0e0e0. А теперь приняв x равным от 0 до 3 проведем соответствующие вычисления:
0xe0e0e0 | (31 << ((0 % 3) << 3)) = e0e0ff
0xe0e0e0 | (31 << ((1 % 3) << 3)) = e0ffe0
0xe0e0e0 | (31 << ((2 % 3) << 3)) = ffe0e0
Если теперь эти маски применить при помощи операции "&" к цвету пикселя, то мы получим, соответственно, приглушенные R и G, R и B, G и B каналы, что будет выглядеть как эффект от LCD-монитора:
Выделим их в отдельные функции:
SPL
#include <X11/Xlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
/* Код после рефакторинга */
/*! \brief Ширина изображения в пикселях */
#define IMAGE_WIDTH (1220)
/*! \brief Высота изображения в пикселях */
#define IMAGE_HEIGHT (616)
/*! \brief Сдвиг верхней грани карты от левой стороны */
#define IMAGE_SHIFTX (580)
/*! \brief Количество текстур */
#define TEXTURE_COUNT (8)
/*! \brief Ширина текстуры в пикселях */
#define TEXTURE_WIDTH (64)
/*! \brief Высота текстур в пикселях */
#define TEXTURE_HEIGHT (40)
/*! \brief Ширина грани текстуры в пикселях */
#define TEXTURE_TOP_WIDTH (64)
/*! \brief Высота грани текстуры в пикселях */
#define TEXTURE_TOP_HEIGHT (32)
/*! \brief Ширина эмулируемого поля */
#define MAP_WIDTH (19)
/*! \brief Высота эмулируемого поля */
#define MAP_HEIGHT (19)
/*! \brief Количество данные в файле-чертеже. Один байт добавлен для символа '\n' */
#define MAP_FILEDATA ((MAP_WIDTH + 1) * MAP_HEIGHT)
/*! \brief Количество итераций во время расчета NOR-узлов */
#define MAP_ITERATIONS (20)
/*! \brief Содержит структуры синтаксиса файла-конфигурации */
enum map_characters {
MAPCHAR_WIRE = '.', /**< Провод (ASCII = 46) */
MAPCHAR_PLUS = '+', /**< Провод (есть ток) (ASCII = 43) */
MAPCHAR_MINUS = '-', /**< Провод (нет тока) (ASCII = 45) */
MAPCHAR_NOR = '>', /**< NOR-элемент (ASCII = 62) */
MAPCHAR_EMPTY = ' ', /**< Пустой блок (ASCII = 32) */
};
/*! \brief Содержит индексы текстур */
enum textures_indexes {
TEXINDEX_EMPTY = (0), /**< Индекс пустой текстуры */
TEXINDEX_MINUS = (1), /**< Индекс текстуры "выключенного провода" */
TEXINDEX_WIRE = (2), /**< Индекс текстуры нейтрального провода */
TEXINDEX_PLUS = (3), /**< Индекс текстуры "включенного" провода */
/**/
TEXINDEX_NOR = (6), /**< Индекс текстуры NOR-элемента */
TEXINDEX_HOLE = (7) /**< Индекс текстуры отверстия на плате */
};
/*! \brief Аргументы программы */
enum program_arguments {
ARG_PROGRAM, /**< Имя самой программы */
ARG_BLUEPRINT /**< Имя файла-чертежа */
};
/*! \brief Бинарные данные пикселей изображения.
* Типизированы к int для оперирования пикселями вместо каналов */
int pixdata[IMAGE_HEIGHT][IMAGE_WIDTH];
/*! \brief Текстуры блоков, отображаемых на поле */
int textures[TEXTURE_COUNT][TEXTURE_HEIGHT][TEXTURE_WIDTH] = { 0 };
/*! \brief Данные эмулируемого поля.
* Один байт добавлен для упрощения обработки символа '\n' */
char mapdata[MAP_HEIGHT][MAP_WIDTH + 1];
/*! \brief Зерно генератора случайных чисел */
int random = 2166136261;
/*! \brief Экземпляр дисплея Xlib */
Display * display;
/*! \brief Экземпляр главного окна Xlib */
Window window;
/*! \brief Изображение для вывода на экран */
XImage * image;
static void _texture_draw(int t, int x, int y);
/*! \brief Создает изображение и сопутствующие сущности */
static void _image_create(void) {
display = XOpenDisplay(0);
window = XCreateSimpleWindow(display,
RootWindow(display, DefaultScreen(display)),
0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, 1, 0, 0);
image = XCreateImage(display,
DefaultVisual(display, DefaultScreen(display)),
DefaultDepth(display, DefaultScreen(display)),
2, 0, (char *) pixdata, IMAGE_WIDTH, IMAGE_HEIGHT, 32, 0);
}
/* \brief Обнуляет изображение, заполняя его черным цветом */
static void _image_reset(void) {
for (int y = 0; y < IMAGE_HEIGHT; y++)
for (int x = 0; x < IMAGE_WIDTH; x++)
pixdata[y][x] = 0;
}
/*! \brief Конвертирует символ из чертежа в индекс текстуры
* \param[in] elem Символ чертежа
* \return Индекс текстуры */
static int _map2texture(char elem) {
return ((elem >> 4) & 1) << 2 | (elem & 3);
}
/*! \brief Собирает изображение из отдельных тайлов согласно карте */
static void _image_compile(void) {
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
_texture_draw(_map2texture(mapdata[y][x]), x, y);
}
/*! \brief Рисует изображение на экране */
static void _image_draw(void) {
XPutImage(display, window,
DefaultGC(display, DefaultScreen(display)),
image, 0, 0, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
XMapWindow(display, window);
XFlush(display);
}
/*! \brief Рисует отверстия на печатной плате */
static void _image_drill(void) {
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
if ((x % 14 == 2) && (y % 4 == 3))
_texture_draw(TEXINDEX_HOLE, x, y);
}
/*! \brief Применяет шейдер LCD-эффекта на изображение
* \param[in] x X-координата изображения
* \param[in] y Y-координата изображения */
static void _shader_lcd(int x, int y) {
pixdata[y][x] &= 0xe0e0e0 | (31 << ((x % 3) << 3));
}
/*! \brief Применяет шейдер случайного шума на изображение
* \param[in] x X-координата изображения
* \param[in] y Y-координата изображения */
static void _shader_noise(int x, int y) {
pixdata[y][x] += 0x0f0f0f & (random *= 16777619);
}
/*! \brief Накладывает на изображение различные эффекты */
static void _image_postprocess(void) {
for (int y = 0; y < IMAGE_HEIGHT; y++)
for (int x = 0; x < IMAGE_WIDTH; x++) {
_shader_lcd(x, y);
_shader_noise(x, y);
}
}
/*! \brief Отрисовывает текстуру на главном холсте по указанным координатам
* \param[in] t Индекс текстуры
* \param[in] x X координата тайла
* \param[in] y Y координата тайла */
static void _texture_draw(int t, int x, int y) {
for (int ty = 0; ty < TEXTURE_HEIGHT; ty++)
for (int tx = 0; tx < TEXTURE_WIDTH; tx++)
if (textures[t][ty][tx])
pixdata[ty +
y * (TEXTURE_TOP_HEIGHT / 2) +
x * (TEXTURE_TOP_HEIGHT / 2)]
[tx +
IMAGE_SHIFTX +
x * (TEXTURE_TOP_WIDTH / 2) -
y * TEXTURE_TOP_HEIGHT] = textures[t][ty][tx];
}
/*! \brief Читает данные файла-чертежа и загружает их в карту
* \param[in] filename Имя файла-чертежа */
static void _map_read(const char * filename) {
int f = open(filename, 0);
read(f, mapdata, MAP_FILEDATA);
close(f);
}
/*! \brief Заменяет иллюстративные входы из файла-конфигурации на вход
* в виде провода чтобы работала логика распространения фронта волны */
static void _map_wire_inputs(void) {
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
if ((x % 14 == 2) && (y % 4 == 3))
mapdata[y][x] = MAPCHAR_WIRE;
}
/*! \brief Производит заливку проводника нужным состоянием
* \param[in] t Игнорируется, артефакт автогенерации кода
* \param[in] x X-координата заливки
* \param[in] y Y-координата заливки
* \param[in] c Исходное состояние
* \param[in] l Целевое состояние */
static void _map_fill(int t, int x, int y, int c, int l) {
if ((x >= MAP_WIDTH || y >= MAP_HEIGHT ||
x < 0 || y < 0) || (mapdata[y][x] == l)
|| (mapdata[y][x] != c))
return;
mapdata[y][x] = l;
_map_fill(t, x - 1, y, c, l);
_map_fill(t, x + 1, y, c, l);
_map_fill(t, x, y - 1, c, l);
_map_fill(t, x, y + 1, c, l);
}
/*! \brief Включает соответствующие входы схемы в зависимости от значения
* счетчика.
* \param[in] counter Счетчик */
static void _map_wire_counter(int counter) {
_map_fill(0, 2, 3, mapdata[3][2], counter & 1 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
_map_fill(0, 2, 7, mapdata[7][2], counter & 2 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
_map_fill(0, 2, 11, mapdata[11][2], counter & 4 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
_map_fill(0, 2, 15, mapdata[15][2], counter & 8 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
}
/*! \brief Проводит расчет выходного (результирующего) тока после NOR-узла */
static void _map_process_gates(void) {
for (int i = 0; i < MAP_ITERATIONS; i++)
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
if (mapdata[y][x] == MAPCHAR_NOR)
_map_fill(0, x + 1, y, mapdata[y][x + 1],
!(mapdata[y - 1][x] == MAPCHAR_PLUS ? 1 : 0
|| mapdata[y + 1][x] == MAPCHAR_PLUS ? 1 : 0) ?
MAPCHAR_PLUS : MAPCHAR_MINUS);
}
/* Код до рефакторинга */
int _x[] = { 15117427, 8413248, 5878632, 13027014, 1 };
void e(int t, int x, int y, int c, int l) {
if ((x >= TEXTURE_WIDTH || y >= TEXTURE_HEIGHT ||
x < 0 || y < 0) ||
(textures[t][y][x] == l) ||
(textures[t][y][x] != c))
return;
textures[t][y][x] = l;
e(t, x - 1, y, c, l);
e(t, x + 1, y, c, l);
e(t, x, y - 1, c, l);
e(t, x, y + 1, c, l);
}
void k(int t, int x, int y, int c, int l) {
while (c--) {
textures[t][y][x++] = l;
textures[t][y++][x++] = l;
}
}
void r(int t, int x, int y, int c, int l) {
while (c--) {
textures[t][y][x--] = l;
textures[t][y++][x--] = l;
}
}
void u(int t, int x, int y, int c, int l) {
while (c--)
textures[t][y++][x] = l;
}
char *z[8] = {
(char[]) {4},
(char[]) {6, 1 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0,
21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2,
63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1,
32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63,
21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1,
36 - 0, 4, 6, 1 * 4 + 0, 3, 31, 8 - 0, 6, 1 * 4 + 2,
3, 33, 38 - 0, 6, 1 * 4 + 3, 3, 30, 38 - 0, 4},
(char[]) {5, 1},
(char[]) {6, 0 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16,
0, 0, 21 + 0 * 3 - 0, 16, 1, 63,
21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0,
4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1,
5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1,
63, 21 + 1 * 3 - 0, 16, 2, 63 * 1,
21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6,
0 * 4 + 0, 3, 31, 8 - 0, 6, 0 * 4 + 2,
3, 33, 38 - 0, 6, 0 * 4 + 3, 3, 30,
38 - 0,
4},
(char[]) {4},
(char[]) {4},
(char[]) {6, 2 * 4 + 1, 0 + 0, 32 - 0, 5 - 3, 16, 0, 0,
21 + 0 * 3 - 3, 16, 1, 63, 21 + 0 * 3 - 3, 16, 2, 63 * 0,
21 - 3, 4, 2, 31 + 0, 36 - 3, 4, 0 + 1, 32 - 1, 5 - 3,
16, 0, 0, 21 + 1 * 3 - 3, 16, 1, 63, 21 + 1 * 3 - 3, 16,
2, 63 * 1, 21 - 3, 4, 2, 31 + 1, 36 - 3, 4, 6, 2 * 4 + 0,
3, 31, 8 - 3, 6, 2 * 4 + 2, 3, 33, 38 - 3, 6, 2 * 4 + 3,
3, 30, 38 - 3, 4},
(char[]) {6, 13, 0, 32, 9 + 0, 12 - 0,
0, 8 + 0 + 0, 21, 12 - 0, 1,
31, 9 + 0, 12 - 0, 1,
55 - 0 - 0, 21, 12 - 0, 0,
32, 9 + 3, 12 - 3, 0,
8 + 3 + 3, 21, 12 - 3, 1,
31, 9 + 3, 12 - 3, 1,
55 - 3 - 3, 21, 12 - 3, 6,
14 + 0, 2, 31 + 0, 13, 4,
1 - 0, 31 + 0, 16, 7, 3,
30 + 3 * 0, 14, 6,
12 + 0 * 4, 3, 32 - 0,
11 + 0 * 8, 6, 14 + 1, 2,
31 + 1, 13, 4, 1 - 1,
31 + 1, 16, 7, 3,
30 + 3 * 1, 14, 6,
12 + 1 * 4, 3, 32 - 1,
11 + 1 * 8,
4}
};
int main(int argc, char * args[]) {
_image_create();
for (int i = 0; i < 8; i++) {
char *p = z[i];
int c = 0;
while (*p != 4) {
switch (*p) {
case 0:
k(i, p[1], p[2], p[3], c);
p += 4;
break;
case 1:
r(i, p[1], p[2], p[3], c);
p += 4;
break;
case 2:
u(i, p[1], p[2], p[3], c);
p += 4;
break;
case 3:
e(i, p[1]
, p[2], 0, c);
p += 3;
break;
case 5:
p = z[p[1]];
break;
case 6:
c = _x[p[1] / 4] - 1643277 * (p[1] % 4);
p += 2;
break;
}
}
}
unsigned int counter = 1;
while (counter++) {
_map_read(args[ARG_BLUEPRINT]);
_map_wire_inputs();
_map_wire_counter(counter);
_map_process_gates();
_image_reset();
_image_compile();
_image_drill();
_image_postprocess();
_image_draw();
sleep(1);
}
return 0;
}
«e», «k», «r», «u»
Осталось еще 4 функции, которые мы не исследовали и не переименовали — это «e», «k», «r» и «u». Попытаемся осмотреть их, не прибегая к поиску мест, откуда они вызываются:
void e(int t, int x, int y, int c, int l) {
if ((x >= TEXTURE_WIDTH || y >= TEXTURE_HEIGHT ||
x < 0 || y < 0) ||
(textures[t][y][x] == l) ||
(textures[t][y][x] != c))
return;
textures[t][y][x] = l;
e(t, x - 1, y, c, l);
e(t, x + 1, y, c, l);
e(t, x, y - 1, c, l);
e(t, x, y + 1, c, l);
}
Очевидно, что эта функция выглядит и работает как flood_fill, только для массива textures, переименуем ее в _texture_fill.
void k(int t, int x, int y, int c, int l) {
while (c--) {
textures[t][y][x++] = l;
textures[t][y++][x++] = l;
}
}
void r(int t, int x, int y, int c, int l) {
while (c--) {
textures[t][y][x--] = l;
textures[t][y++][x--] = l;
}
}
Функции «k» и «r» на каждую заданную единицу c рисуют два пикселя значением l и перемещаются вправо или влево на два пикселя и вниз на один — значит это функции рисования изометрических линий SW и SE. Переименуем их в _texture_linesw и _texture_linese.
На данный момент уже можно догадаться, что последняя функция «u» рисует линию вертикально вниз:
void u(int t, int x, int y, int c, int l) {
while (c--)
textures[t][y++][x] = l;
}
Переименуем её в _texture_linedown.
Все отдельные функции прошли рефакторинг:
SPL
#include <X11/Xlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
/* Код после рефакторинга */
/*! \brief Ширина изображения в пикселях */
#define IMAGE_WIDTH (1220)
/*! \brief Высота изображения в пикселях */
#define IMAGE_HEIGHT (616)
/*! \brief Сдвиг верхней грани карты от левой стороны */
#define IMAGE_SHIFTX (580)
/*! \brief Количество текстур */
#define TEXTURE_COUNT (8)
/*! \brief Ширина текстуры в пикселях */
#define TEXTURE_WIDTH (64)
/*! \brief Высота текстур в пикселях */
#define TEXTURE_HEIGHT (40)
/*! \brief Ширина грани текстуры в пикселях */
#define TEXTURE_TOP_WIDTH (64)
/*! \brief Высота грани текстуры в пикселях */
#define TEXTURE_TOP_HEIGHT (32)
/*! \brief Ширина эмулируемого поля */
#define MAP_WIDTH (19)
/*! \brief Высота эмулируемого поля */
#define MAP_HEIGHT (19)
/*! \brief Количество данные в файле-чертеже. Один байт добавлен для символа '\n' */
#define MAP_FILEDATA ((MAP_WIDTH + 1) * MAP_HEIGHT)
/*! \brief Количество итераций во время расчета NOR-узлов */
#define MAP_ITERATIONS (20)
/*! \brief Содержит структуры синтаксиса файла-конфигурации */
enum map_characters {
MAPCHAR_WIRE = '.', /**< Провод (ASCII = 46) */
MAPCHAR_PLUS = '+', /**< Провод (есть ток) (ASCII = 43) */
MAPCHAR_MINUS = '-', /**< Провод (нет тока) (ASCII = 45) */
MAPCHAR_NOR = '>', /**< NOR-элемент (ASCII = 62) */
MAPCHAR_EMPTY = ' ', /**< Пустой блок (ASCII = 32) */
};
/*! \brief Содержит индексы текстур */
enum textures_indexes {
TEXINDEX_EMPTY = (0), /**< Индекс пустой текстуры */
TEXINDEX_MINUS = (1), /**< Индекс текстуры "выключенного провода" */
TEXINDEX_WIRE = (2), /**< Индекс текстуры нейтрального провода */
TEXINDEX_PLUS = (3), /**< Индекс текстуры "включенного" провода */
/**/
TEXINDEX_NOR = (6), /**< Индекс текстуры NOR-элемента */
TEXINDEX_HOLE = (7) /**< Индекс текстуры отверстия на плате */
};
/*! \brief Аргументы программы */
enum program_arguments {
ARG_PROGRAM, /**< Имя самой программы */
ARG_BLUEPRINT /**< Имя файла-чертежа */
};
/*! \brief Бинарные данные пикселей изображения.
* Типизированы к int для оперирования пикселями вместо каналов */
int pixdata[IMAGE_HEIGHT][IMAGE_WIDTH];
/*! \brief Текстуры блоков, отображаемых на поле */
int textures[TEXTURE_COUNT][TEXTURE_HEIGHT][TEXTURE_WIDTH] = { 0 };
/*! \brief Данные эмулируемого поля.
* Один байт добавлен для упрощения обработки символа '\n' */
char mapdata[MAP_HEIGHT][MAP_WIDTH + 1];
/*! \brief Зерно генератора случайных чисел */
int random = 2166136261;
/*! \brief Экземпляр дисплея Xlib */
Display * display;
/*! \brief Экземпляр главного окна Xlib */
Window window;
/*! \brief Изображение для вывода на экран */
XImage * image;
static void _texture_draw(int t, int x, int y);
/*! \brief Создает изображение и сопутствующие сущности */
static void _image_create(void) {
display = XOpenDisplay(0);
window = XCreateSimpleWindow(display,
RootWindow(display, DefaultScreen(display)),
0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, 1, 0, 0);
image = XCreateImage(display,
DefaultVisual(display, DefaultScreen(display)),
DefaultDepth(display, DefaultScreen(display)),
2, 0, (char *) pixdata, IMAGE_WIDTH, IMAGE_HEIGHT, 32, 0);
}
/* \brief Обнуляет изображение, заполняя его черным цветом */
static void _image_reset(void) {
for (int y = 0; y < IMAGE_HEIGHT; y++)
for (int x = 0; x < IMAGE_WIDTH; x++)
pixdata[y][x] = 0;
}
/*! \brief Конвертирует символ из чертежа в индекс текстуры
* \param[in] elem Символ чертежа
* \return Индекс текстуры */
static int _map2texture(char elem) {
return ((elem >> 4) & 1) << 2 | (elem & 3);
}
/*! \brief Собирает изображение из отдельных тайлов согласно карте */
static void _image_compile(void) {
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
_texture_draw(_map2texture(mapdata[y][x]), x, y);
}
/*! \brief Рисует изображение на экране */
static void _image_draw(void) {
XPutImage(display, window,
DefaultGC(display, DefaultScreen(display)),
image, 0, 0, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
XMapWindow(display, window);
XFlush(display);
}
/*! \brief Рисует отверстия на печатной плате */
static void _image_drill(void) {
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
if ((x % 14 == 2) && (y % 4 == 3))
_texture_draw(TEXINDEX_HOLE, x, y);
}
/*! \brief Применяет шейдер LCD-эффекта на изображение
* \param[in] x X-координата изображения
* \param[in] y Y-координата изображения */
static void _shader_lcd(int x, int y) {
pixdata[y][x] &= 0xe0e0e0 | (31 << ((x % 3) << 3));
}
/*! \brief Применяет шейдер случайного шума на изображение
* \param[in] x X-координата изображения
* \param[in] y Y-координата изображения */
static void _shader_noise(int x, int y) {
pixdata[y][x] += 0x0f0f0f & (random *= 16777619);
}
/*! \brief Накладывает на изображение различные эффекты */
static void _image_postprocess(void) {
for (int y = 0; y < IMAGE_HEIGHT; y++)
for (int x = 0; x < IMAGE_WIDTH; x++) {
_shader_lcd(x, y);
_shader_noise(x, y);
}
}
/*! \brief Отрисовывает текстуру на главном холсте по указанным координатам
* \param[in] t Индекс текстуры
* \param[in] x X координата тайла
* \param[in] y Y координата тайла */
static void _texture_draw(int t, int x, int y) {
for (int ty = 0; ty < TEXTURE_HEIGHT; ty++)
for (int tx = 0; tx < TEXTURE_WIDTH; tx++)
if (textures[t][ty][tx])
pixdata[ty +
y * (TEXTURE_TOP_HEIGHT / 2) +
x * (TEXTURE_TOP_HEIGHT / 2)]
[tx +
IMAGE_SHIFTX +
x * (TEXTURE_TOP_WIDTH / 2) -
y * TEXTURE_TOP_HEIGHT] = textures[t][ty][tx];
}
/*! \brief Производит закраску области текстуры
* \param[in] t Индекс текстуры
* \param[in] x X-координата начала
* \param[in] y Y-координата начала
* \param[in] source Исходный цвет
* \param[in] target Новый цвет */
static void _texture_fill(int t, int x, int y, int source, int target) {
if ((x >= TEXTURE_WIDTH || y >= TEXTURE_HEIGHT ||
x < 0 || y < 0) ||
(textures[t][y][x] == target) ||
(textures[t][y][x] != source))
return;
textures[t][y][x] = target;
_texture_fill(t, x - 1, y, source, target);
_texture_fill(t, x + 1, y, source, target);
_texture_fill(t, x, y - 1, source, target);
_texture_fill(t, x, y + 1, source, target);
}
/*! \brief Рисует изометрическую линию по направлению SE
* \param[in] t Индекс текстуры
* \param[in] x X-координата начала
* \param[in] y Y-координата начала
* \param[in] c Длина линии по короткой стороне
* \param[in] color Цвет рисования */
static void _texture_linese(int t, int x, int y, int c, int color) {
while (c--) {
textures[t][y][x++] = color;
textures[t][y++][x++] = color;
}
}
/*! \brief Рисует изометрическую линию по направлению SW
* \param[in] t Индекс текстуры
* \param[in] x X-координата начала
* \param[in] y Y-координата начала
* \param[in] c Длина линии по короткой стороне
* \param[in] color Цвет рисования */
static void _texture_linesw(int t, int x, int y, int c, int color) {
while (c--) {
textures[t][y][x--] = color;
textures[t][y++][x--] = color;
}
}
/*! \brief Рисует линию вниз
* \param[in] t Индекс текстуры
* \param[in] x X-координата начала
* \param[in] y Y-координата начала
* \param[in] c Длина линии
* \param[in] color Цвет рисования */
static void _texture_linedown(int t, int x, int y, int c, int color) {
while (c--)
textures[t][y++][x] = color;
}
/*! \brief Читает данные файла-чертежа и загружает их в карту
* \param[in] filename Имя файла-чертежа */
static void _map_read(const char * filename) {
int f = open(filename, 0);
read(f, mapdata, MAP_FILEDATA);
close(f);
}
/*! \brief Заменяет иллюстративные входы из файла-конфигурации на вход
* в виде провода чтобы работала логика распространения фронта волны */
static void _map_wire_inputs(void) {
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
if ((x % 14 == 2) && (y % 4 == 3))
mapdata[y][x] = MAPCHAR_WIRE;
}
/*! \brief Производит заливку проводника нужным состоянием
* \param[in] t Игнорируется, артефакт автогенерации кода
* \param[in] x X-координата заливки
* \param[in] y Y-координата заливки
* \param[in] c Исходное состояние
* \param[in] l Целевое состояние */
static void _map_fill(int t, int x, int y, int c, int l) {
if ((x >= MAP_WIDTH || y >= MAP_HEIGHT ||
x < 0 || y < 0) || (mapdata[y][x] == l)
|| (mapdata[y][x] != c))
return;
mapdata[y][x] = l;
_map_fill(t, x - 1, y, c, l);
_map_fill(t, x + 1, y, c, l);
_map_fill(t, x, y - 1, c, l);
_map_fill(t, x, y + 1, c, l);
}
/*! \brief Включает соответствующие входы схемы в зависимости от значения
* счетчика.
* \param[in] counter Счетчик */
static void _map_wire_counter(int counter) {
_map_fill(0, 2, 3, mapdata[3][2], counter & 1 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
_map_fill(0, 2, 7, mapdata[7][2], counter & 2 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
_map_fill(0, 2, 11, mapdata[11][2], counter & 4 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
_map_fill(0, 2, 15, mapdata[15][2], counter & 8 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
}
/*! \brief Проводит расчет выходного (результирующего) тока после NOR-узла */
static void _map_process_gates(void) {
for (int i = 0; i < MAP_ITERATIONS; i++)
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
if (mapdata[y][x] == MAPCHAR_NOR)
_map_fill(0, x + 1, y, mapdata[y][x + 1],
!(mapdata[y - 1][x] == MAPCHAR_PLUS ? 1 : 0
|| mapdata[y + 1][x] == MAPCHAR_PLUS ? 1 : 0) ?
MAPCHAR_PLUS : MAPCHAR_MINUS);
}
/* Код до рефакторинга */
int _x[] = { 15117427, 8413248, 5878632, 13027014, 1 };
char *z[8] = {
(char[]) {4},
(char[]) {6, 1 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0,
21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2,
63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1,
32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63,
21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1,
36 - 0, 4, 6, 1 * 4 + 0, 3, 31, 8 - 0, 6, 1 * 4 + 2,
3, 33, 38 - 0, 6, 1 * 4 + 3, 3, 30, 38 - 0, 4},
(char[]) {5, 1},
(char[]) {6, 0 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16,
0, 0, 21 + 0 * 3 - 0, 16, 1, 63,
21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0,
4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1,
5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1,
63, 21 + 1 * 3 - 0, 16, 2, 63 * 1,
21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6,
0 * 4 + 0, 3, 31, 8 - 0, 6, 0 * 4 + 2,
3, 33, 38 - 0, 6, 0 * 4 + 3, 3, 30,
38 - 0,
4},
(char[]) {4},
(char[]) {4},
(char[]) {6, 2 * 4 + 1, 0 + 0, 32 - 0, 5 - 3, 16, 0, 0,
21 + 0 * 3 - 3, 16, 1, 63, 21 + 0 * 3 - 3, 16, 2, 63 * 0,
21 - 3, 4, 2, 31 + 0, 36 - 3, 4, 0 + 1, 32 - 1, 5 - 3,
16, 0, 0, 21 + 1 * 3 - 3, 16, 1, 63, 21 + 1 * 3 - 3, 16,
2, 63 * 1, 21 - 3, 4, 2, 31 + 1, 36 - 3, 4, 6, 2 * 4 + 0,
3, 31, 8 - 3, 6, 2 * 4 + 2, 3, 33, 38 - 3, 6, 2 * 4 + 3,
3, 30, 38 - 3, 4},
(char[]) {6, 13, 0, 32, 9 + 0, 12 - 0,
0, 8 + 0 + 0, 21, 12 - 0, 1,
31, 9 + 0, 12 - 0, 1,
55 - 0 - 0, 21, 12 - 0, 0,
32, 9 + 3, 12 - 3, 0,
8 + 3 + 3, 21, 12 - 3, 1,
31, 9 + 3, 12 - 3, 1,
55 - 3 - 3, 21, 12 - 3, 6,
14 + 0, 2, 31 + 0, 13, 4,
1 - 0, 31 + 0, 16, 7, 3,
30 + 3 * 0, 14, 6,
12 + 0 * 4, 3, 32 - 0,
11 + 0 * 8, 6, 14 + 1, 2,
31 + 1, 13, 4, 1 - 1,
31 + 1, 16, 7, 3,
30 + 3 * 1, 14, 6,
12 + 1 * 4, 3, 32 - 1,
11 + 1 * 8,
4}
};
int main(int argc, char * args[]) {
_image_create();
for (int i = 0; i < 8; i++) {
char *p = z[i];
int c = 0;
while (*p != 4) {
switch (*p) {
case 0:
_texture_linese(i, p[1], p[2], p[3], c);
p += 4;
break;
case 1:
_texture_linesw(i, p[1], p[2], p[3], c);
p += 4;
break;
case 2:
_texture_linedown(i, p[1], p[2], p[3], c);
p += 4;
break;
case 3:
_texture_fill(i, p[1], p[2], 0, c);
p += 3;
break;
case 5:
p = z[p[1]];
break;
case 6:
c = _x[p[1] / 4] - 1643277 * (p[1] % 4);
p += 2;
break;
}
}
}
unsigned int counter = 1;
while (counter++) {
_map_read(args[ARG_BLUEPRINT]);
_map_wire_inputs();
_map_wire_counter(counter);
_map_process_gates();
_image_reset();
_image_compile();
_image_drill();
_image_postprocess();
_image_draw();
sleep(1);
}
return 0;
}
Векторная графика
Остался последний шаг — до конца разобраться, что же происходит в последнем, оставленном «на потом», switch-case:
for (int i = 0; i < 8; i++) {
char *p = z[i];
int c = 0;
while (*p != 4) {
switch (*p) {
case 0:
_texture_linese(i, p[1], p[2], p[3], c);
p += 4;
break;
case 1:
_texture_linesw(i, p[1], p[2], p[3], c);
p += 4;
break;
case 2:
_texture_linedown(i, p[1], p[2], p[3], c);
p += 4;
break;
case 3:
_texture_fill(i, p[1], p[2], 0, c);
p += 3;
break;
case 5:
p = z[p[1]];
break;
case 6:
c = _x[p[1] / 4] - 1643277 * (p[1] % 4);
p += 2;
break;
}
}
}
Теперь, когда функции имеют новые, понятные, имена, можно сказать, что этот блок кода интерпретирует данные из массива «z», и согласно инструкциям, содержащимся в нем (например 0, 5, 5, 8, 2 — «нарисовать линию South-East, от [5,5] длиной 8 пикселей по краткой стороне цветом номер 2») рисует весь набор текстур.
Вроде бы всё понятно, но ни одного указания на цвет в программе замечено не было.
c = _x[p[1] / 4] - 1643277 * (p[1] % 4);
Ага, значит массив _x — это палитра, но в очень странном, «сжатом», формате.
int _x[] = { 15117427, 8413248, 5878632, 13027014, 1 };
Так как мы пока не знаем, сколько всего цветов используется в палитре (при установке цвета индекс как минимум делится на 4), переформатируем таблицу данных для отрисовки и группируем числа по категориям, заменив сигнатуры инструкций на константы:
Рефакторинг таблицы векторных инструкций
SPL
#include <X11/Xlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
/* Код после рефакторинга */
/*! \brief Ширина изображения в пикселях */
#define IMAGE_WIDTH (1220)
/*! \brief Высота изображения в пикселях */
#define IMAGE_HEIGHT (616)
/*! \brief Сдвиг верхней грани карты от левой стороны */
#define IMAGE_SHIFTX (580)
/*! \brief Количество текстур */
#define TEXTURE_COUNT (8)
/*! \brief Ширина текстуры в пикселях */
#define TEXTURE_WIDTH (64)
/*! \brief Высота текстур в пикселях */
#define TEXTURE_HEIGHT (40)
/*! \brief Ширина грани текстуры в пикселях */
#define TEXTURE_TOP_WIDTH (64)
/*! \brief Высота грани текстуры в пикселях */
#define TEXTURE_TOP_HEIGHT (32)
/*! \brief Ширина эмулируемого поля */
#define MAP_WIDTH (19)
/*! \brief Высота эмулируемого поля */
#define MAP_HEIGHT (19)
/*! \brief Количество данные в файле-чертеже. Один байт добавлен для символа '\n' */
#define MAP_FILEDATA ((MAP_WIDTH + 1) * MAP_HEIGHT)
/*! \brief Количество итераций во время расчета NOR-узлов */
#define MAP_ITERATIONS (20)
/*! \brief Содержит структуры синтаксиса файла-конфигурации */
enum map_characters {
MAPCHAR_WIRE = '.', /**< Провод (ASCII = 46) */
MAPCHAR_PLUS = '+', /**< Провод (есть ток) (ASCII = 43) */
MAPCHAR_MINUS = '-', /**< Провод (нет тока) (ASCII = 45) */
MAPCHAR_NOR = '>', /**< NOR-элемент (ASCII = 62) */
MAPCHAR_EMPTY = ' ', /**< Пустой блок (ASCII = 32) */
};
/*! \brief Содержит индексы текстур */
enum textures_indexes {
TEXINDEX_EMPTY = (0), /**< Индекс пустой текстуры */
TEXINDEX_MINUS = (1), /**< Индекс текстуры "выключенного провода" */
TEXINDEX_WIRE = (2), /**< Индекс текстуры нейтрального провода */
TEXINDEX_PLUS = (3), /**< Индекс текстуры "включенного" провода */
/**/
TEXINDEX_NOR = (6), /**< Индекс текстуры NOR-элемента */
TEXINDEX_HOLE = (7) /**< Индекс текстуры отверстия на плате */
};
/*! \brief Список инструкций векторного интерпретатора */
enum textures_instructions {
TEXVEC_LINESE = (0), /**< Линия SE */
TEXVEC_LINESW = (1), /**< Линия SW */
TEXVEC_LINEDW = (2), /**< Линия вниз */
TEXVEC_FILL = (3), /**< Заливка */
TEXVEC_EXIT = (4), /**< Конец списка инструкций */
TEXVEC_REPEAT = (5), /**< Повтор инструкций другой текстуры */
TEXVEC_COLOR = (6) /**< Выбор цвета */
};
/*! \brief Аргументы программы */
enum program_arguments {
ARG_PROGRAM, /**< Имя самой программы */
ARG_BLUEPRINT /**< Имя файла-чертежа */
};
/*! \brief Бинарные данные пикселей изображения.
* Типизированы к int для оперирования пикселями вместо каналов */
int pixdata[IMAGE_HEIGHT][IMAGE_WIDTH];
/*! \brief Текстуры блоков, отображаемых на поле */
int textures[TEXTURE_COUNT][TEXTURE_HEIGHT][TEXTURE_WIDTH] = { 0 };
/*! \brief Данные эмулируемого поля.
* Один байт добавлен для упрощения обработки символа '\n' */
char mapdata[MAP_HEIGHT][MAP_WIDTH + 1];
/*! \brief Зерно генератора случайных чисел */
int random = 2166136261;
/*! \brief Экземпляр дисплея Xlib */
Display * display;
/*! \brief Экземпляр главного окна Xlib */
Window window;
/*! \brief Изображение для вывода на экран */
XImage * image;
/*! \brief Векторные инструкции для рисования текстур */
static const char * vecdata[TEXTURE_COUNT] = {
/* TEXINDEX_EMPTY */
(char[]) { TEXVEC_EXIT },
/* TEXINDEX_MINUS */
(char[]) { TEXVEC_COLOR, 5, TEXVEC_LINESE, 32, 5, 16,
TEXVEC_LINESE, 0, 21, 16, TEXVEC_LINESW, 63, 21, 16,
TEXVEC_LINEDW, 0, 21, 4, TEXVEC_LINEDW, 31, 36, 4,
TEXVEC_LINESW, 31, 5, 16, TEXVEC_LINESE, 0, 24, 16,
TEXVEC_LINESW, 63, 24, 16, TEXVEC_LINEDW, 63, 21, 4,
TEXVEC_LINEDW, 32, 36, 4, TEXVEC_COLOR, 4,
TEXVEC_FILL, 31, 8, TEXVEC_COLOR, 6,
TEXVEC_FILL, 33, 38, TEXVEC_COLOR, 7,
TEXVEC_FILL, 30, 38, TEXVEC_EXIT },
/* TEXINDEX_WIRE */
(char[]) { TEXVEC_REPEAT, 1 },
/* TEXINDEX_PLUS */
(char[]) { TEXVEC_COLOR, 1, TEXVEC_LINESE, 32, 5, 16,
TEXVEC_LINESE, 0, 21, 16, TEXVEC_LINESW, 63, 21, 16,
TEXVEC_LINEDW, 63, 21, 4, TEXVEC_LINEDW, 31, 36, 4,
TEXVEC_LINESW, 31, 5, 16, TEXVEC_LINESE, 0, 24, 16,
TEXVEC_LINESW, 63, 24, 16, TEXVEC_LINEDW, 63, 21, 4,
TEXVEC_LINEDW, 32, 36, 4, TEXVEC_COLOR, 0,
TEXVEC_FILL, 31, 8, TEXVEC_COLOR, 2,
TEXVEC_FILL, 33, 38, TEXVEC_COLOR, 3,
TEXVEC_FILL, 30, 38, TEXVEC_EXIT },
/* Не используется */
(char[]) { TEXVEC_EXIT },
/* Не используется */
(char[]) { TEXVEC_EXIT },
/* TEXINDEX_NOR */
(char[]) { TEXVEC_COLOR, 9, TEXVEC_LINESE, 32, 2, 16,
TEXVEC_LINESE, 0, 18, 16, TEXVEC_LINESW, 63, 18, 16,
TEXVEC_LINEDW, 0, 18, 4, TEXVEC_LINEDW, 31, 33, 4,
TEXVEC_LINESW, 31, 2, 16, TEXVEC_LINESE, 0, 21, 16,
TEXVEC_LINESW, 63, 21, 16, TEXVEC_LINEDW, 63, 18, 4,
TEXVEC_LINEDW, 32, 33, 4, TEXVEC_COLOR, 8,
TEXVEC_FILL, 31, 5, TEXVEC_COLOR, 10,
TEXVEC_FILL, 33, 35, TEXVEC_COLOR, 11,
TEXVEC_FILL, 30, 35, TEXVEC_EXIT },
/* TEXINDEX_HOLE */
(char[]) { TEXVEC_COLOR, 13, TEXVEC_LINESE, 32, 9, 12,
TEXVEC_LINESE, 8, 21, 12, TEXVEC_LINESW, 31, 9, 12,
TEXVEC_LINESW, 55, 21, 12, TEXVEC_LINESE, 32, 12, 9,
TEXVEC_LINESE, 14, 21, 9, TEXVEC_LINESW, 31, 12, 9,
TEXVEC_LINESW, 49, 21, 9, TEXVEC_COLOR, 14,
TEXVEC_LINEDW, 31, 13, 4, TEXVEC_LINESW, 31, 16, 7,
TEXVEC_FILL, 30, 14, TEXVEC_COLOR, 12,
TEXVEC_FILL, 32, 11, TEXVEC_COLOR, 15,
TEXVEC_LINEDW, 32, 13, 4, TEXVEC_LINESE, 32, 16, 7,
TEXVEC_FILL, 33, 14, TEXVEC_COLOR, 16,
TEXVEC_FILL, 31, 19, TEXVEC_EXIT }
};
static void _texture_draw(int t, int x, int y);
/*! \brief Создает изображение и сопутствующие сущности */
static void _image_create(void) {
display = XOpenDisplay(0);
window = XCreateSimpleWindow(display,
RootWindow(display, DefaultScreen(display)),
0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, 1, 0, 0);
image = XCreateImage(display,
DefaultVisual(display, DefaultScreen(display)),
DefaultDepth(display, DefaultScreen(display)),
2, 0, (char *) pixdata, IMAGE_WIDTH, IMAGE_HEIGHT, 32, 0);
}
/* \brief Обнуляет изображение, заполняя его черным цветом */
static void _image_reset(void) {
for (int y = 0; y < IMAGE_HEIGHT; y++)
for (int x = 0; x < IMAGE_WIDTH; x++)
pixdata[y][x] = 0;
}
/*! \brief Конвертирует символ из чертежа в индекс текстуры
* \param[in] elem Символ чертежа
* \return Индекс текстуры */
static int _map2texture(char elem) {
return ((elem >> 4) & 1) << 2 | (elem & 3);
}
/*! \brief Собирает изображение из отдельных тайлов согласно карте */
static void _image_compile(void) {
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
_texture_draw(_map2texture(mapdata[y][x]), x, y);
}
/*! \brief Рисует изображение на экране */
static void _image_draw(void) {
XPutImage(display, window,
DefaultGC(display, DefaultScreen(display)),
image, 0, 0, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
XMapWindow(display, window);
XFlush(display);
}
/*! \brief Рисует отверстия на печатной плате */
static void _image_drill(void) {
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
if ((x % 14 == 2) && (y % 4 == 3))
_texture_draw(TEXINDEX_HOLE, x, y);
}
/*! \brief Применяет шейдер LCD-эффекта на изображение
* \param[in] x X-координата изображения
* \param[in] y Y-координата изображения */
static void _shader_lcd(int x, int y) {
pixdata[y][x] &= 0xe0e0e0 | (31 << ((x % 3) << 3));
}
/*! \brief Применяет шейдер случайного шума на изображение
* \param[in] x X-координата изображения
* \param[in] y Y-координата изображения */
static void _shader_noise(int x, int y) {
pixdata[y][x] += 0x0f0f0f & (random *= 16777619);
}
/*! \brief Накладывает на изображение различные эффекты */
static void _image_postprocess(void) {
for (int y = 0; y < IMAGE_HEIGHT; y++)
for (int x = 0; x < IMAGE_WIDTH; x++) {
_shader_lcd(x, y);
_shader_noise(x, y);
}
}
/*! \brief Отрисовывает текстуру на главном холсте по указанным координатам
* \param[in] t Индекс текстуры
* \param[in] x X координата тайла
* \param[in] y Y координата тайла */
static void _texture_draw(int t, int x, int y) {
for (int ty = 0; ty < TEXTURE_HEIGHT; ty++)
for (int tx = 0; tx < TEXTURE_WIDTH; tx++)
if (textures[t][ty][tx])
pixdata[ty +
y * (TEXTURE_TOP_HEIGHT / 2) +
x * (TEXTURE_TOP_HEIGHT / 2)]
[tx +
IMAGE_SHIFTX +
x * (TEXTURE_TOP_WIDTH / 2) -
y * TEXTURE_TOP_HEIGHT] = textures[t][ty][tx];
}
/*! \brief Производит закраску области текстуры
* \param[in] t Индекс текстуры
* \param[in] x X-координата начала
* \param[in] y Y-координата начала
* \param[in] source Исходный цвет
* \param[in] target Новый цвет */
static void _texture_fill(int t, int x, int y, int source, int target) {
if ((x >= TEXTURE_WIDTH || y >= TEXTURE_HEIGHT ||
x < 0 || y < 0) ||
(textures[t][y][x] == target) ||
(textures[t][y][x] != source))
return;
textures[t][y][x] = target;
_texture_fill(t, x - 1, y, source, target);
_texture_fill(t, x + 1, y, source, target);
_texture_fill(t, x, y - 1, source, target);
_texture_fill(t, x, y + 1, source, target);
}
/*! \brief Рисует изометрическую линию по направлению SE
* \param[in] t Индекс текстуры
* \param[in] x X-координата начала
* \param[in] y Y-координата начала
* \param[in] c Длина линии по короткой стороне
* \param[in] color Цвет рисования */
static void _texture_linese(int t, int x, int y, int c, int color) {
while (c--) {
textures[t][y][x++] = color;
textures[t][y++][x++] = color;
}
}
/*! \brief Рисует изометрическую линию по направлению SW
* \param[in] t Индекс текстуры
* \param[in] x X-координата начала
* \param[in] y Y-координата начала
* \param[in] c Длина линии по короткой стороне
* \param[in] color Цвет рисования */
static void _texture_linesw(int t, int x, int y, int c, int color) {
while (c--) {
textures[t][y][x--] = color;
textures[t][y++][x--] = color;
}
}
/*! \brief Рисует линию вниз
* \param[in] t Индекс текстуры
* \param[in] x X-координата начала
* \param[in] y Y-координата начала
* \param[in] c Длина линии
* \param[in] color Цвет рисования */
static void _texture_linedown(int t, int x, int y, int c, int color) {
while (c--)
textures[t][y++][x] = color;
}
/*! \brief Читает данные файла-чертежа и загружает их в карту
* \param[in] filename Имя файла-чертежа */
static void _map_read(const char * filename) {
int f = open(filename, 0);
read(f, mapdata, MAP_FILEDATA);
close(f);
}
/*! \brief Заменяет иллюстративные входы из файла-конфигурации на вход
* в виде провода чтобы работала логика распространения фронта волны */
static void _map_wire_inputs(void) {
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
if ((x % 14 == 2) && (y % 4 == 3))
mapdata[y][x] = MAPCHAR_WIRE;
}
/*! \brief Производит заливку проводника нужным состоянием
* \param[in] t Игнорируется, артефакт автогенерации кода
* \param[in] x X-координата заливки
* \param[in] y Y-координата заливки
* \param[in] c Исходное состояние
* \param[in] l Целевое состояние */
static void _map_fill(int t, int x, int y, int c, int l) {
if ((x >= MAP_WIDTH || y >= MAP_HEIGHT ||
x < 0 || y < 0) || (mapdata[y][x] == l)
|| (mapdata[y][x] != c))
return;
mapdata[y][x] = l;
_map_fill(t, x - 1, y, c, l);
_map_fill(t, x + 1, y, c, l);
_map_fill(t, x, y - 1, c, l);
_map_fill(t, x, y + 1, c, l);
}
/*! \brief Включает соответствующие входы схемы в зависимости от значения
* счетчика.
* \param[in] counter Счетчик */
static void _map_wire_counter(int counter) {
_map_fill(0, 2, 3, mapdata[3][2], counter & 1 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
_map_fill(0, 2, 7, mapdata[7][2], counter & 2 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
_map_fill(0, 2, 11, mapdata[11][2], counter & 4 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
_map_fill(0, 2, 15, mapdata[15][2], counter & 8 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
}
/*! \brief Проводит расчет выходного (результирующего) тока после NOR-узла */
static void _map_process_gates(void) {
for (int i = 0; i < MAP_ITERATIONS; i++)
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
if (mapdata[y][x] == MAPCHAR_NOR)
_map_fill(0, x + 1, y, mapdata[y][x + 1],
!(mapdata[y - 1][x] == MAPCHAR_PLUS ? 1 : 0
|| mapdata[y + 1][x] == MAPCHAR_PLUS ? 1 : 0) ?
MAPCHAR_PLUS : MAPCHAR_MINUS);
}
/* Код до рефакторинга */
int _x[] = { 15117427, 8413248, 5878632, 13027014, 1 };
int main(int argc, char * args[]) {
_image_create();
for (int tex = 0; tex < TEXTURE_COUNT; tex++) {
char * ptr = vecdata[tex];
int c = 0;
while (*ptr != TEXVEC_EXIT) {
switch (*ptr) {
case TEXVEC_LINESE:
_texture_linese(tex, ptr[1], ptr[2], ptr[3], c);
ptr += 4;
break;
case TEXVEC_LINESW:
_texture_linesw(tex, ptr[1], ptr[2], ptr[3], c);
ptr += 4;
break;
case TEXVEC_LINEDW:
_texture_linedown(tex, ptr[1], ptr[2], ptr[3], c);
ptr += 4;
break;
case TEXVEC_FILL:
_texture_fill(tex, ptr[1], ptr[2], 0, c);
ptr += 3;
break;
case TEXVEC_REPEAT:
ptr = vecdata[ptr[1]];
break;
case TEXVEC_COLOR:
c = _x[ptr[1] / 4] - 1643277 * (ptr[1] % 4);
ptr += 2;
break;
}
}
}
unsigned int counter = 1;
while (counter++) {
_map_read(args[ARG_BLUEPRINT]);
_map_wire_inputs();
_map_wire_counter(counter);
_map_process_gates();
_image_reset();
_image_compile();
_image_drill();
_image_postprocess();
_image_draw();
sleep(1);
}
return 0;
}
Максимальное число, которое стоит после инструкции «TEXVEC_COLOR» — это 16. Напишем простейший код, чтобы получить всю таблицу:
Теперь, имея на руках палитру в расшифрованном виде мы можем заменить функцию её использования и написать расшифровки для цветов в константной таблице, а заодно и выделить функцию создания текстур:
Полный код после рефакторинга:
SPL
#include <X11/Xlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
/* Код после рефакторинга */
/*! \brief Ширина изображения в пикселях */
#define IMAGE_WIDTH (1220)
/*! \brief Высота изображения в пикселях */
#define IMAGE_HEIGHT (616)
/*! \brief Сдвиг верхней грани карты от левой стороны */
#define IMAGE_SHIFTX (580)
/*! \brief Количество текстур */
#define TEXTURE_COUNT (8)
/*! \brief Ширина текстуры в пикселях */
#define TEXTURE_WIDTH (64)
/*! \brief Высота текстур в пикселях */
#define TEXTURE_HEIGHT (40)
/*! \brief Ширина грани текстуры в пикселях */
#define TEXTURE_TOP_WIDTH (64)
/*! \brief Высота грани текстуры в пикселях */
#define TEXTURE_TOP_HEIGHT (32)
/*! \brief Ширина эмулируемого поля */
#define MAP_WIDTH (19)
/*! \brief Высота эмулируемого поля */
#define MAP_HEIGHT (19)
/*! \brief Количество данные в файле-чертеже. Один байт добавлен для символа '\n' */
#define MAP_FILEDATA ((MAP_WIDTH + 1) * MAP_HEIGHT)
/*! \brief Количество итераций во время расчета NOR-узлов */
#define MAP_ITERATIONS (20)
/*! \brief Содержит структуры синтаксиса файла-конфигурации */
enum map_characters {
MAPCHAR_WIRE = '.', /**< Провод (ASCII = 46) */
MAPCHAR_PLUS = '+', /**< Провод (есть ток) (ASCII = 43) */
MAPCHAR_MINUS = '-', /**< Провод (нет тока) (ASCII = 45) */
MAPCHAR_NOR = '>', /**< NOR-элемент (ASCII = 62) */
MAPCHAR_EMPTY = ' ', /**< Пустой блок (ASCII = 32) */
};
/*! \brief Содержит индексы текстур */
enum textures_indexes {
TEXINDEX_EMPTY = (0), /**< Индекс пустой текстуры */
TEXINDEX_MINUS = (1), /**< Индекс текстуры "выключенного провода" */
TEXINDEX_WIRE = (2), /**< Индекс текстуры нейтрального провода */
TEXINDEX_PLUS = (3), /**< Индекс текстуры "включенного" провода */
/**/
TEXINDEX_NOR = (6), /**< Индекс текстуры NOR-элемента */
TEXINDEX_HOLE = (7) /**< Индекс текстуры отверстия на плате */
};
/*! \brief Список инструкций векторного интерпретатора */
enum textures_instructions {
TEXVEC_LINESE = (0), /**< Линия SE */
TEXVEC_LINESW = (1), /**< Линия SW */
TEXVEC_LINEDW = (2), /**< Линия вниз */
TEXVEC_FILL = (3), /**< Заливка */
TEXVEC_EXIT = (4), /**< Конец списка инструкций */
TEXVEC_REPEAT = (5), /**< Повтор инструкций другой текстуры */
TEXVEC_COLOR = (6) /**< Выбор цвета */
};
/*! \brief Аргументы программы */
enum program_arguments {
ARG_PROGRAM, /**< Имя самой программы */
ARG_BLUEPRINT /**< Имя файла-чертежа */
};
/*! \brief Бинарные данные пикселей изображения.
* Типизированы к int для оперирования пикселями вместо каналов */
int pixdata[IMAGE_HEIGHT][IMAGE_WIDTH];
/*! \brief Текстуры блоков, отображаемых на поле */
int textures[TEXTURE_COUNT][TEXTURE_HEIGHT][TEXTURE_WIDTH] = { 0 };
/*! \brief Данные эмулируемого поля.
* Один байт добавлен для упрощения обработки символа '\n' */
char mapdata[MAP_HEIGHT][MAP_WIDTH + 1];
/*! \brief Зерно генератора случайных чисел */
int random = 2166136261;
/*! \brief Экземпляр дисплея Xlib */
Display * display;
/*! \brief Экземпляр главного окна Xlib */
Window window;
/*! \brief Изображение для вывода на экран */
XImage * image;
/*! \brief Палитра цветов для векторной отрисовки
* \note Черный имеет цвет {001} для отличия от "прозрачного" черного {000} */
static const int texturepalette[] = {
/* Светлее ▁▁▂▂▃▃▄▄▅▅▆▆▇▇██ Темнее */
/* Светло-коричневый */ 0xe6ac73, 0xcd9966, 0xb48659, 0x9b734c,
/* Темно-коричневый */ 0x806040, 0x674d33, 0x4e3a26, 0x352719,
/* Зеленый */ 0x59b368, 0x40a05b, 0x278d4e, 0x0e7a41,
/* Серый */ 0xc6c6c6, 0xadb3b9, 0x94a0ac, 0x7b8d9f,
/* Черный */ 0x000001
};
/*! \brief Векторные инструкции для рисования текстур */
static const char * vecdata[TEXTURE_COUNT] = {
/* TEXINDEX_EMPTY */
(char[]) { TEXVEC_EXIT },
/* TEXINDEX_MINUS */
(char[]) { TEXVEC_COLOR, 5, TEXVEC_LINESE, 32, 5, 16,
TEXVEC_LINESE, 0, 21, 16, TEXVEC_LINESW, 63, 21, 16,
TEXVEC_LINEDW, 0, 21, 4, TEXVEC_LINEDW, 31, 36, 4,
TEXVEC_LINESW, 31, 5, 16, TEXVEC_LINESE, 0, 24, 16,
TEXVEC_LINESW, 63, 24, 16, TEXVEC_LINEDW, 63, 21, 4,
TEXVEC_LINEDW, 32, 36, 4, TEXVEC_COLOR, 4,
TEXVEC_FILL, 31, 8, TEXVEC_COLOR, 6,
TEXVEC_FILL, 33, 38, TEXVEC_COLOR, 7,
TEXVEC_FILL, 30, 38, TEXVEC_EXIT },
/* TEXINDEX_WIRE */
(char[]) { TEXVEC_REPEAT, 1 },
/* TEXINDEX_PLUS */
(char[]) { TEXVEC_COLOR, 1, TEXVEC_LINESE, 32, 5, 16,
TEXVEC_LINESE, 0, 21, 16, TEXVEC_LINESW, 63, 21, 16,
TEXVEC_LINEDW, 63, 21, 4, TEXVEC_LINEDW, 31, 36, 4,
TEXVEC_LINESW, 31, 5, 16, TEXVEC_LINESE, 0, 24, 16,
TEXVEC_LINESW, 63, 24, 16, TEXVEC_LINEDW, 63, 21, 4,
TEXVEC_LINEDW, 32, 36, 4, TEXVEC_COLOR, 0,
TEXVEC_FILL, 31, 8, TEXVEC_COLOR, 2,
TEXVEC_FILL, 33, 38, TEXVEC_COLOR, 3,
TEXVEC_FILL, 30, 38, TEXVEC_EXIT },
/* Не используется */
(char[]) { TEXVEC_EXIT },
/* Не используется */
(char[]) { TEXVEC_EXIT },
/* TEXINDEX_NOR */
(char[]) { TEXVEC_COLOR, 9, TEXVEC_LINESE, 32, 2, 16,
TEXVEC_LINESE, 0, 18, 16, TEXVEC_LINESW, 63, 18, 16,
TEXVEC_LINEDW, 0, 18, 4, TEXVEC_LINEDW, 31, 33, 4,
TEXVEC_LINESW, 31, 2, 16, TEXVEC_LINESE, 0, 21, 16,
TEXVEC_LINESW, 63, 21, 16, TEXVEC_LINEDW, 63, 18, 4,
TEXVEC_LINEDW, 32, 33, 4, TEXVEC_COLOR, 8,
TEXVEC_FILL, 31, 5, TEXVEC_COLOR, 10,
TEXVEC_FILL, 33, 35, TEXVEC_COLOR, 11,
TEXVEC_FILL, 30, 35, TEXVEC_EXIT },
/* TEXINDEX_HOLE */
(char[]) { TEXVEC_COLOR, 13, TEXVEC_LINESE, 32, 9, 12,
TEXVEC_LINESE, 8, 21, 12, TEXVEC_LINESW, 31, 9, 12,
TEXVEC_LINESW, 55, 21, 12, TEXVEC_LINESE, 32, 12, 9,
TEXVEC_LINESE, 14, 21, 9, TEXVEC_LINESW, 31, 12, 9,
TEXVEC_LINESW, 49, 21, 9, TEXVEC_COLOR, 14,
TEXVEC_LINEDW, 31, 13, 4, TEXVEC_LINESW, 31, 16, 7,
TEXVEC_FILL, 30, 14, TEXVEC_COLOR, 12,
TEXVEC_FILL, 32, 11, TEXVEC_COLOR, 15,
TEXVEC_LINEDW, 32, 13, 4, TEXVEC_LINESE, 32, 16, 7,
TEXVEC_FILL, 33, 14, TEXVEC_COLOR, 16,
TEXVEC_FILL, 31, 19, TEXVEC_EXIT }
};
static void _texture_draw(int t, int x, int y);
/*! \brief Создает изображение и сопутствующие сущности */
static void _image_create(void) {
display = XOpenDisplay(0);
window = XCreateSimpleWindow(display,
RootWindow(display, DefaultScreen(display)),
0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, 1, 0, 0);
image = XCreateImage(display,
DefaultVisual(display, DefaultScreen(display)),
DefaultDepth(display, DefaultScreen(display)),
2, 0, (char *) pixdata, IMAGE_WIDTH, IMAGE_HEIGHT, 32, 0);
}
/* \brief Обнуляет изображение, заполняя его черным цветом */
static void _image_reset(void) {
for (int y = 0; y < IMAGE_HEIGHT; y++)
for (int x = 0; x < IMAGE_WIDTH; x++)
pixdata[y][x] = 0;
}
/*! \brief Конвертирует символ из чертежа в индекс текстуры
* \param[in] elem Символ чертежа
* \return Индекс текстуры */
static int _map2texture(char elem) {
return ((elem >> 4) & 1) << 2 | (elem & 3);
}
/*! \brief Собирает изображение из отдельных тайлов согласно карте */
static void _image_compile(void) {
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
_texture_draw(_map2texture(mapdata[y][x]), x, y);
}
/*! \brief Рисует изображение на экране */
static void _image_draw(void) {
XPutImage(display, window,
DefaultGC(display, DefaultScreen(display)),
image, 0, 0, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
XMapWindow(display, window);
XFlush(display);
}
/*! \brief Рисует отверстия на печатной плате */
static void _image_drill(void) {
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
if ((x % 14 == 2) && (y % 4 == 3))
_texture_draw(TEXINDEX_HOLE, x, y);
}
/*! \brief Применяет шейдер LCD-эффекта на изображение
* \param[in] x X-координата изображения
* \param[in] y Y-координата изображения */
static void _shader_lcd(int x, int y) {
pixdata[y][x] &= 0xe0e0e0 | (31 << ((x % 3) << 3));
}
/*! \brief Применяет шейдер случайного шума на изображение
* \param[in] x X-координата изображения
* \param[in] y Y-координата изображения */
static void _shader_noise(int x, int y) {
pixdata[y][x] += 0x0f0f0f & (random *= 16777619);
}
/*! \brief Накладывает на изображение различные эффекты */
static void _image_postprocess(void) {
for (int y = 0; y < IMAGE_HEIGHT; y++)
for (int x = 0; x < IMAGE_WIDTH; x++) {
_shader_lcd(x, y);
_shader_noise(x, y);
}
}
/*! \brief Отрисовывает текстуру на главном холсте по указанным координатам
* \param[in] t Индекс текстуры
* \param[in] x X координата тайла
* \param[in] y Y координата тайла */
static void _texture_draw(int t, int x, int y) {
for (int ty = 0; ty < TEXTURE_HEIGHT; ty++)
for (int tx = 0; tx < TEXTURE_WIDTH; tx++)
if (textures[t][ty][tx])
pixdata[ty +
y * (TEXTURE_TOP_HEIGHT / 2) +
x * (TEXTURE_TOP_HEIGHT / 2)]
[tx +
IMAGE_SHIFTX +
x * (TEXTURE_TOP_WIDTH / 2) -
y * TEXTURE_TOP_HEIGHT] = textures[t][ty][tx];
}
/*! \brief Производит закраску области текстуры
* \param[in] t Индекс текстуры
* \param[in] x X-координата начала
* \param[in] y Y-координата начала
* \param[in] source Исходный цвет
* \param[in] target Новый цвет */
static void _texture_fill(int t, int x, int y, int source, int target) {
if ((x >= TEXTURE_WIDTH || y >= TEXTURE_HEIGHT ||
x < 0 || y < 0) ||
(textures[t][y][x] == target) ||
(textures[t][y][x] != source))
return;
textures[t][y][x] = target;
_texture_fill(t, x - 1, y, source, target);
_texture_fill(t, x + 1, y, source, target);
_texture_fill(t, x, y - 1, source, target);
_texture_fill(t, x, y + 1, source, target);
}
/*! \brief Рисует изометрическую линию по направлению SE
* \param[in] t Индекс текстуры
* \param[in] x X-координата начала
* \param[in] y Y-координата начала
* \param[in] c Длина линии по короткой стороне
* \param[in] color Цвет рисования */
static void _texture_linese(int t, int x, int y, int c, int color) {
while (c--) {
textures[t][y][x++] = color;
textures[t][y++][x++] = color;
}
}
/*! \brief Рисует изометрическую линию по направлению SW
* \param[in] t Индекс текстуры
* \param[in] x X-координата начала
* \param[in] y Y-координата начала
* \param[in] c Длина линии по короткой стороне
* \param[in] color Цвет рисования */
static void _texture_linesw(int t, int x, int y, int c, int color) {
while (c--) {
textures[t][y][x--] = color;
textures[t][y++][x--] = color;
}
}
/*! \brief Рисует линию вниз
* \param[in] t Индекс текстуры
* \param[in] x X-координата начала
* \param[in] y Y-координата начала
* \param[in] c Длина линии
* \param[in] color Цвет рисования */
static void _texture_linedown(int t, int x, int y, int c, int color) {
while (c--)
textures[t][y++][x] = color;
}
/*! \brief Создает все текстуры, интерпретатор векторных инструкций */
static void _textures_create(void) {
for (int tex = 0; tex < TEXTURE_COUNT; tex++) {
const char * ptr = vecdata[tex];
int c = 0;
while (*ptr != TEXVEC_EXIT) {
switch (*ptr) {
case TEXVEC_LINESE:
_texture_linese(tex, ptr[1], ptr[2], ptr[3], c);
ptr += 4;
break;
case TEXVEC_LINESW:
_texture_linesw(tex, ptr[1], ptr[2], ptr[3], c);
ptr += 4;
break;
case TEXVEC_LINEDW:
_texture_linedown(tex, ptr[1], ptr[2], ptr[3], c);
ptr += 4;
break;
case TEXVEC_FILL:
_texture_fill(tex, ptr[1], ptr[2], 0, c);
ptr += 3;
break;
case TEXVEC_REPEAT:
ptr = vecdata[ptr[1]];
break;
case TEXVEC_COLOR:
c = texturepalette[ptr[1]];
ptr += 2;
break;
}
}
}
}
/*! \brief Читает данные файла-чертежа и загружает их в карту
* \param[in] filename Имя файла-чертежа */
static void _map_read(const char * filename) {
int f = open(filename, 0);
read(f, mapdata, MAP_FILEDATA);
close(f);
}
/*! \brief Заменяет иллюстративные входы из файла-конфигурации на вход
* в виде провода чтобы работала логика распространения фронта волны */
static void _map_wire_inputs(void) {
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
if ((x % 14 == 2) && (y % 4 == 3))
mapdata[y][x] = MAPCHAR_WIRE;
}
/*! \brief Производит заливку проводника нужным состоянием
* \param[in] t Игнорируется, артефакт автогенерации кода
* \param[in] x X-координата заливки
* \param[in] y Y-координата заливки
* \param[in] c Исходное состояние
* \param[in] l Целевое состояние */
static void _map_fill(int t, int x, int y, int c, int l) {
if ((x >= MAP_WIDTH || y >= MAP_HEIGHT ||
x < 0 || y < 0) || (mapdata[y][x] == l)
|| (mapdata[y][x] != c))
return;
mapdata[y][x] = l;
_map_fill(t, x - 1, y, c, l);
_map_fill(t, x + 1, y, c, l);
_map_fill(t, x, y - 1, c, l);
_map_fill(t, x, y + 1, c, l);
}
/*! \brief Включает соответствующие входы схемы в зависимости от значения
* счетчика.
* \param[in] counter Счетчик */
static void _map_wire_counter(int counter) {
_map_fill(0, 2, 3, mapdata[3][2], counter & 1 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
_map_fill(0, 2, 7, mapdata[7][2], counter & 2 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
_map_fill(0, 2, 11, mapdata[11][2], counter & 4 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
_map_fill(0, 2, 15, mapdata[15][2], counter & 8 ? MAPCHAR_PLUS : MAPCHAR_MINUS);
}
/*! \brief Проводит расчет выходного (результирующего) тока после NOR-узла */
static void _map_process_gates(void) {
for (int i = 0; i < MAP_ITERATIONS; i++)
for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++)
if (mapdata[y][x] == MAPCHAR_NOR)
_map_fill(0, x + 1, y, mapdata[y][x + 1],
!(mapdata[y - 1][x] == MAPCHAR_PLUS ? 1 : 0
|| mapdata[y + 1][x] == MAPCHAR_PLUS ? 1 : 0) ?
MAPCHAR_PLUS : MAPCHAR_MINUS);
}
int main(int argc, char * args[]) {
_image_create();
_textures_create();
unsigned int counter = 1;
while (counter++) {
_map_read(args[ARG_BLUEPRINT]);
_map_wire_inputs();
_map_wire_counter(counter);
_map_process_gates();
_image_reset();
_image_compile();
_image_drill();
_image_postprocess();
_image_draw();
sleep(1);
}
return 0;
}
Эпилог
Вот и всё. Несмотря на то, что я не занял никаких призовых мест, это был достаточно любопытный опыт (умерший за два дня до начала приема работ HDD этому поспособствовал). Было что-то неправильное в том, чтобы писать код, максимально не читаемый и одновременно абсолютно валидный с точки зрения языка. Однако нельзя сказать, что это было неинтересно. Когда утилита, подсчитывающая токены говорит, что код нарушает правила превышая лимит на 2000+ токенов, а ты не знаешь, как его сократить — это вызов. Когда размер файла превышает лимиты, а ты хочешь добавить какой-то графический эффект — это вызов. Это было трудно, вместить столько функционала, и тем слаще было читать вывод утилиты, которая после нескольких сотен исправлений наконец-таки сказала, что код можно выгружать. Я бы порекомендовал читателям поучаствовать в конкурсе хотя бы просто для того, чтобы приобщиться и испытать свои силы. Да, никаких наград или призов это не дает, но зато вы будете знать, что под ником zhestokyshveller на сайте IOCCC скрываетесь вы.
===========
Источник:
habr.com
===========
Похожие новости:
- [Программирование] TED на русском — архив
- [Карьера в IT-индустрии, Интервью] Как оценить и выбрать оффер разработчику: на что смотреть, к чему готовиться, какие вопросы задавать
- [Финансы в IT] Как выручка ИТ-компаний Азии может пострадать от санкций США против Huawei
- [Производство и разработка электроники, Гаджеты, Краудсорсинг] На Kickstarter закончился сбор заявок на Flipper Zero
- [Обработка изображений, Поисковые технологии] Технология видео поиска «Video Color»
- [Анализ и проектирование систем, Big Data, Data Engineering] Создание автоматической системы борьбы с злоумышленниками на сайте (фродом)
- [Разработка под iOS, Законодательство в IT, Игры и игровые приставки, IT-компании] Apple удалила аккаунт Epic Games и все игры компании из App Store
- [IT-компании] Как создать успешный Fintech-стартап за границей: 8 рекомендаций от практиков
- [Open source, Программирование, Системное программирование, Компиляторы, Rust] Rust 1.46.0: track_caller и улучшения const fn (перевод)
- [IT-эмиграция, Карьера в IT-индустрии] Переезд инженера в Будапешт: работа, деньги и стоимость жизни
Теги для поиска: #_sistemnoe_programmirovanie (Системное программирование), #_otladka (Отладка), #_c, #_c, #_ioccc, #_konkurs (конкурс), #_sistemnoe_programmirovanie (
Системное программирование
), #_otladka (
Отладка
), #_c
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 16:06
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Это вторая часть цикла статей о том, как я участвовал в IOCCC'19
Я надеюсь, что данная статья поможет вам при разборе чужого кода или кода после декомпилятора или обфускатора. Если вы еще не знаете, что такое IOCCC или вы хотите ознакомиться с более простым вариантом запутанного кода, то рекомендую обратиться к первой части. Всем остальным я желаю приятного чтения. Все исходники помещены на github, откуда их можно невозбранно скачать и попробовать скомпилировать. Исходные данные Так как исходный код в текстовом виде можно найти по ссылке, покажу как код выглядит графически: Традиционно сложилось, что «дополнительные баллы» даются, если код отформатирован нестандартно или выглядит как какое-то изображение. Что ж, будем считать, это с этим здесь всё в порядке. Но что же делает программа? Она инициализирует графический стек X-сервера, сканирует предоставленный файл конфигурации, рисует электрическую схему согласно файлу и запускает симуляцию, поочередно меняя полярность на 4 ножках входа, используя при этом только NOR (Not-OR) элементы. Отрисовка происходит с шейдером старого LCD-экрана. В комплекте с программой идут несколько конфигурационных файлов, а именно: DIP8-4packnot.txt — примерный аналог CMOS 4041/Четырехканальный инвертор (изображение сжато в 2 раза, чтобы уместиться в рамки приличия. На самом деле забавно, что программа, весом в 3.5 КБ генерирует ряд изображений, который в максимальном сжатии занимают 10.5+МБ) DIP8-triplexor.txt — примерный аналог CMOS 4030 с объединенными входами и тремя каналами/Трехканальный XOR-gate с объединенными входами DIP8-fulladder.txt — примерный аналог CMOS 4008, но на два бита/сумматор на 2 бита с выводом бита переноса Разбор кода Самой сложной задачей в этот раз было уместиться в ограничение по размеру присылаемой работы. Ограничен не только размер файла, но и количество условных «токенов» — символов операций, ключевых слов языка и пар скобок. Именно для того, чтобы «заэксплойтить» эту особенность парсера-проверяльщика размера в программе объявлено огромное количество define'ов, приводящих блоки кода к одному ключевому слову языка С. Для начала, заглянем в Makefile, чтобы понять, как код собирается: #!/usr/bin/env make PROJECT=prog CC= gcc SRC=prog.c CWARN=-Wall -Wextra -Wno-char-subscripts CSTD= -std=c99 # Syscalls table # DS - syscall nanosleep # DO - syscall open # DR - syscall read # DC - syscall close # X11 structures offsets # dS - offset of screens in Display # dR - offset of root in Screen # dD - offset of root_depth in Screen # dV - offset of root_visual in Screen # dG - offset of default_gc in Screen BITS := $(shell uname -p) ifeq ($(BITS), x86_64) ARCH= -m64 CDEFINE= -DDS=35 -DDO=2 -DDR=0 -DDC=3 -DdS=232 -DdR=16 -DdD=56 -DdV=64 -DdG=72 else ARCH= -m32 CDEFINE= -DDS=162 -DDO=5 -DDR=3 -DDC=6 -DdS=140 -DdR=8 -DdD=36 -DdV=40 -DdG=44 endif OPT= -g CFLAGS= ${CWARN} ${CSTD} ${ARCH} ${CDEFINE} ${OPT} LDFLAGS= -ldl RM= rm all: ${PROJECT} ${PROJECT}: ${CC} ${CFLAGS} ${SRC} -o $@ ${LDFLAGS} clean: ${RM} -f ${PROJECT} Как видим, эта работа, так же, как и предыдущая активно использует системные вызовы, чтобы избежать уродливых "#include", которые нарушают узор картинки. Запомним этот факт и подготовим строку для препроцессора и линтера: После препроцессораSPLgcc -DDS=35 -DDO=2 -DDR=0 -DDC=3 -DdS=232 -DdR=16 -DdD=56 -DdV=64 -DdG=72 prog.c -ldl -E | indent -kr -brf > /tmp/fmt.c
int _x[] = { 15117427, 8413248, 5878632, 13027014, 1 };
extern void *dlsym(void *, char *); int x_[616][1220]; extern long syscall(long, ...); extern void *dlopen(char *, int); char m[19][20], _n[] = "pu~D--2os" "<<<<<<<<" "<<<DSlyrXuolp}e" "<<<<<<<<" "D_ny}hyOuqlpyKurxsk<D_ny" "}hyUq}{y" "<<<<<<<<" "DLihUq}{y" "<<<<<<<<" "<<<DQ}lKurxsk" "<<<<<<<<" "<<DZpiot<"; long w, N[2] = { 0, 1 << 29 }; int rn = 2166136261, _[8][40][64] = { 0 }; long R[2]; void *M, *d, *T; void b(int t, int x, int y, int c, int l) { if ((x >= 19 || y >= 19 || x < 0 || y < 0) || (m[y][x] == l) || (m[y][x] != c)) return; m[y][x] = l; b(t, x - 1, y, c, l); b(t, x + 1, y, c, l); b(t, x, y - 1, c, l); b(t, x, y + 1, c, l); } void e(int t, int x, int y, int c, int l) { if ((x >= 64 || y >= 40 || x < 0 || y < 0) || (_[t][y][x] == l) || (_[t][y][x] != c)) return; _[t][y][x] = l; e(t, x - 1, y, c, l); e(t, x + 1, y, c, l); e(t, x, y - 1, c, l); e(t, x, y + 1, c, l); } void k(int t, int x, int y, int c, int l) { while (c--) { _[t][y][x++] = l; _[t][y++][x++] = l; } } void r(int t, int x, int y, int c, int l) { while (c--) { _[t][y][x--] = l; _[t][y++][x--] = l; } } void u(int t, int x, int y, int c, int l) { while (c--) _[t][y++][x] = l; } char *z[8] = { (char[]) {4}, (char[]) {6, 1 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0, 21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63, 21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6, 1 * 4 + 0, 3, 31, 8 - 0, 6, 1 * 4 + 2, 3, 33, 38 - 0, 6, 1 * 4 + 3, 3, 30, 38 - 0, 4}, (char[]) {5, 1}, (char[]) {6, 0 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0, 21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63, 21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6, 0 * 4 + 0, 3, 31, 8 - 0, 6, 0 * 4 + 2, 3, 33, 38 - 0, 6, 0 * 4 + 3, 3, 30, 38 - 0, 4}, (char[]) {4}, (char[]) {4}, (char[]) {6, 2 * 4 + 1, 0 + 0, 32 - 0, 5 - 3, 16, 0, 0, 21 + 0 * 3 - 3, 16, 1, 63, 21 + 0 * 3 - 3, 16, 2, 63 * 0, 21 - 3, 4, 2, 31 + 0, 36 - 3, 4, 0 + 1, 32 - 1, 5 - 3, 16, 0, 0, 21 + 1 * 3 - 3, 16, 1, 63, 21 + 1 * 3 - 3, 16, 2, 63 * 1, 21 - 3, 4, 2, 31 + 1, 36 - 3, 4, 6, 2 * 4 + 0, 3, 31, 8 - 3, 6, 2 * 4 + 2, 3, 33, 38 - 3, 6, 2 * 4 + 3, 3, 30, 38 - 3, 4}, (char[]) {6, 13, 0, 32, 9 + 0, 12 - 0, 0, 8 + 0 + 0, 21, 12 - 0, 1, 31, 9 + 0, 12 - 0, 1, 55 - 0 - 0, 21, 12 - 0, 0, 32, 9 + 3, 12 - 3, 0, 8 + 3 + 3, 21, 12 - 3, 1, 31, 9 + 3, 12 - 3, 1, 55 - 3 - 3, 21, 12 - 3, 6, 14 + 0, 2, 31 + 0, 13, 4, 1 - 0, 31 + 0, 16, 7, 3, 30 + 3 * 0, 14, 6, 12 + 0 * 4, 3, 32 - 0, 11 + 0 * 8, 6, 14 + 1, 2, 31 + 1, 13, 4, 1 - 1, 31 + 1, 16, 7, 3, 30 + 3 * 1, 14, 6, 12 + 1 * 4, 3, 32 - 1, 11 + 1 * 8, 4} }; void d_(int t, int p, int q) { for (int y = 0; y < 40; y++) for (int x = 0; x < 64; x++) if (_[t] [y][x]) x_[y + q * 16 + p * 16][x + 580 + p * 32 - q * 32] = _[t][y][x]; } int main(int a, char *s[]) { int h = 127; while (h--) { _n[h] ^= 28; _n[h] -= (_n[h] == 32 ? 32 : 0); } T = dlopen(_n, 2); d = ((void *(*)()) dlsym(T, _n + (1 * 20))) (0); w = ((long (*)()) dlsym(T, _n + (2 * 20))) (d, (*(long *) ((char *) (*(long *) ((char *) d + 232)) + 16)) , 0, 0, 1220, 616, 1, 0, 0); M = ((void *(*)()) dlsym(T, _n + (3 * 20))) (d, (*(long *) ((char *) (*(long *) ((char *) d + 232)) + 64)), (*(long *) ((char *) (*(long *) ((char *) d + 232)) + 56)), 2, 0, (char *) x_, 1220, 616, 32, 0); for (int i = 0; i < 8; i++) { char *p = z[i]; int c = 0; while (*p != 4) { switch (*p) { case 0: k(i, p[1], p[2], p[3], c); p += 4; break; case 1: r(i, p[1], p[2], p[3], c); p += 4; break; case 2: u(i, p[1], p[2], p[3], c); p += 4; break; case 3: e(i, p[1] , p[2], 0, c); p += 3; break; case 5: p = z[p[1]]; break; case 6: c = _x[p[1] / 4] - 1643277 * (p[1] % 4); p += 2; break; } } } while (a++) { int f = syscall(2, s[1], 0); syscall(0, f, m, 380); syscall(3, f); for (int y = 0; y < 19; y++) for (int x = 0; x < 19; x++) if ((x % 14 == 2) && (y % 4 == 3)) m[y][x] = 46; b(0, 2, 3, m[3][2], a & 1 ? 43 : 45); b(0, 2, 7, m[7][2], a & 2 ? 43 : 45); b(0, 2, 11, m[11][2], a & 4 ? 43 : 45); b(0, 2, 15, m[15][2], a & 8 ? 43 : 45); for (int i = 0; i < 20; i++) for (int y = 0; y < 19; y++) for (int x = 0; x < 19; x++) if (m[y][x] == 62) b(0, x + 1, y, m[y][x + 1], !(m[y - 1][x] == 43 ? 1 : 0 || m[y + 1][x] == 43 ? 1 : 0) ? 43 : 45); for (int y = 0; y < 616; y++) for (int x = 0; x < 1220; x++) x_[y][x] = 0; for (int y = 0; y < 19; y++) for (int x = 0; x < 19; x++) d_(((m[y][x] >> 4) & 1) << 2 | (m[y][x] & 3), x, y); for (int y = 0; y < 19; y++) for (int x = 0; x < 19; x++) if ((x % 14 == 2) && (y % 4 == 3)) d_(7, x, y); for (int y = 0; y < 616; y++) for (int x = 0; x < 1220; x++) { x_[y][x] &= 14737632 | (31 << ((x % 3) << 3)); x_[y][x] += 986895 & (rn *= 16777619); } ((long (*)()) dlsym(T, _n + (4 * 20))) (d, w, (*(long *) ((char *) (*(long *) ((char *) d + 232)) + 72)), M, 0, 0, 0, 0, 1220, 616); ((long (*)()) dlsym(T, _n + (5 * 20))) (d, w); ((long (*)()) dlsym(T, _n + (6 * 20))) (d); syscall(35, &N, &R); } return 0; } Что ж, по крайней мере одно из условий конкурса мы выполнили, код после препроцессора понятнее не стал. Начнем с того, что можно выловить с первого взгляда и попытаемся восстановить цепочку событий: dlsym\dlopen Очевидно, что чтобы воспользоваться функциями X-сервера и создать окно, а затем в нем что-то отрисовать код должен обратиться к библиотеке XLib. В коде присутствуют функции dlopen/dlsym, позволяющие динамически подгрузить библиотеку, однако на вход им подается какая-то хитрая каша: char _n[] =
"pu~D--2os" "<<<<<<<<" "<<<DSlyrXuolp}e" "<<<<<<<<" "D_ny}hyOuqlpyKurxsk<D_ny" "}hyUq}{y" "<<<<<<<<" "DLihUq}{y" "<<<<<<<<" "<<<DQ}lKurxsk" "<<<<<<<<" "<<DZpiot<"; ... T = dlopen(_n, 2); Пройдем на шаг назад и проинспектируем следующий код: int h = 127;
while (h--) { _n[h] ^= 28; _n[h] -= (_n[h] == 32 ? 32 : 0); } Судя по всему, он некоторым образом преобразует исходный массив, позволяя нам получить читаемые строки. Выполним его отдельно: https://onlinegdb.com/SJNM9Og7v: libX11.so XOpenDisplay XCreateSimpleWindow XCreateImage XPutImage XMapWindow XFlush Для того, чтобы одной строкой вызывать столь различные по формату функции код эксплуатирует тот факт, что в стандарте языка С (void) означает отсутствие параметров, а () — любое число параметров. Остается только привести полученный void * к соответствующему ((long (*)()) типу и вуаля: w = ((long (*)()) dlsym(T, _n + (2 * 20))) (d, (*(long *)((char *) (*(long *)((char *) d + 232)) + 16))
Зная, что эти преобразования означают теперь мы можем заменить столь необычное использование dlsym сначала на строки: После замены строк:SPLint _x[] = { 15117427, 8413248, 5878632, 13027014, 1 };
extern void *dlsym(void *, char *); int x_[616][1220]; extern long syscall(long, ...); extern void *dlopen(char *, int); char m[19][20]; long w, N[2] = { 0, 1 << 29 }; int rn = 2166136261, _[8][40][64] = { 0 }; long R[2]; void *M, *d, *T; void b(int t, int x, int y, int c, int l) { if ((x >= 19 || y >= 19 || x < 0 || y < 0) || (m[y][x] == l) || (m[y][x] != c)) return; m[y][x] = l; b(t, x - 1, y, c, l); b(t, x + 1, y, c, l); b(t, x, y - 1, c, l); b(t, x, y + 1, c, l); } void e(int t, int x, int y, int c, int l) { if ((x >= 64 || y >= 40 || x < 0 || y < 0) || (_[t][y][x] == l) || (_[t][y][x] != c)) return; _[t][y][x] = l; e(t, x - 1, y, c, l); e(t, x + 1, y, c, l); e(t, x, y - 1, c, l); e(t, x, y + 1, c, l); } void k(int t, int x, int y, int c, int l) { while (c--) { _[t][y][x++] = l; _[t][y++][x++] = l; } } void r(int t, int x, int y, int c, int l) { while (c--) { _[t][y][x--] = l; _[t][y++][x--] = l; } } void u(int t, int x, int y, int c, int l) { while (c--) _[t][y++][x] = l; } char *z[8] = { (char[]) {4}, (char[]) {6, 1 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0, 21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63, 21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6, 1 * 4 + 0, 3, 31, 8 - 0, 6, 1 * 4 + 2, 3, 33, 38 - 0, 6, 1 * 4 + 3, 3, 30, 38 - 0, 4}, (char[]) {5, 1}, (char[]) {6, 0 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0, 21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63, 21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6, 0 * 4 + 0, 3, 31, 8 - 0, 6, 0 * 4 + 2, 3, 33, 38 - 0, 6, 0 * 4 + 3, 3, 30, 38 - 0, 4}, (char[]) {4}, (char[]) {4}, (char[]) {6, 2 * 4 + 1, 0 + 0, 32 - 0, 5 - 3, 16, 0, 0, 21 + 0 * 3 - 3, 16, 1, 63, 21 + 0 * 3 - 3, 16, 2, 63 * 0, 21 - 3, 4, 2, 31 + 0, 36 - 3, 4, 0 + 1, 32 - 1, 5 - 3, 16, 0, 0, 21 + 1 * 3 - 3, 16, 1, 63, 21 + 1 * 3 - 3, 16, 2, 63 * 1, 21 - 3, 4, 2, 31 + 1, 36 - 3, 4, 6, 2 * 4 + 0, 3, 31, 8 - 3, 6, 2 * 4 + 2, 3, 33, 38 - 3, 6, 2 * 4 + 3, 3, 30, 38 - 3, 4}, (char[]) {6, 13, 0, 32, 9 + 0, 12 - 0, 0, 8 + 0 + 0, 21, 12 - 0, 1, 31, 9 + 0, 12 - 0, 1, 55 - 0 - 0, 21, 12 - 0, 0, 32, 9 + 3, 12 - 3, 0, 8 + 3 + 3, 21, 12 - 3, 1, 31, 9 + 3, 12 - 3, 1, 55 - 3 - 3, 21, 12 - 3, 6, 14 + 0, 2, 31 + 0, 13, 4, 1 - 0, 31 + 0, 16, 7, 3, 30 + 3 * 0, 14, 6, 12 + 0 * 4, 3, 32 - 0, 11 + 0 * 8, 6, 14 + 1, 2, 31 + 1, 13, 4, 1 - 1, 31 + 1, 16, 7, 3, 30 + 3 * 1, 14, 6, 12 + 1 * 4, 3, 32 - 1, 11 + 1 * 8, 4} }; void d_(int t, int p, int q) { for (int y = 0; y < 40; y++) for (int x = 0; x < 64; x++) if (_[t] [y][x]) x_[y + q * 16 + p * 16][x + 580 + p * 32 - q * 32] = _[t][y][x]; } int main(int a, char *s[]) { T = dlopen("libX11.so", 2); d = ((void *(*)()) dlsym(T, "XOpenDisplay") (0); w = ((long (*)()) dlsym(T, "XCreateSimpleWindow")) (d, (*(long *) ((char *) (*(long *) ((char *) d + 232)) + 16)) , 0, 0, 1220, 616, 1, 0, 0); M = ((void *(*)()) dlsym(T, "XCreateImage")) (d, (*(long *) ((char *) (*(long *) ((char *) d + 232)) + 64)), (*(long *) ((char *) (*(long *) ((char *) d + 232)) + 56)), 2, 0, (char *) x_, 1220, 616, 32, 0); for (int i = 0; i < 8; i++) { char *p = z[i]; int c = 0; while (*p != 4) { switch (*p) { case 0: k(i, p[1], p[2], p[3], c); p += 4; break; case 1: r(i, p[1], p[2], p[3], c); p += 4; break; case 2: u(i, p[1], p[2], p[3], c); p += 4; break; case 3: e(i, p[1] , p[2], 0, c); p += 3; break; case 5: p = z[p[1]]; break; case 6: c = _x[p[1] / 4] - 1643277 * (p[1] % 4); p += 2; break; } } } while (a++) { int f = syscall(2, s[1], 0); syscall(0, f, m, 380); syscall(3, f); for (int y = 0; y < 19; y++) for (int x = 0; x < 19; x++) if ((x % 14 == 2) && (y % 4 == 3)) m[y][x] = 46; b(0, 2, 3, m[3][2], a & 1 ? 43 : 45); b(0, 2, 7, m[7][2], a & 2 ? 43 : 45); b(0, 2, 11, m[11][2], a & 4 ? 43 : 45); b(0, 2, 15, m[15][2], a & 8 ? 43 : 45); for (int i = 0; i < 20; i++) for (int y = 0; y < 19; y++) for (int x = 0; x < 19; x++) if (m[y][x] == 62) b(0, x + 1, y, m[y][x + 1], !(m[y - 1][x] == 43 ? 1 : 0 || m[y + 1][x] == 43 ? 1 : 0) ? 43 : 45); for (int y = 0; y < 616; y++) for (int x = 0; x < 1220; x++) x_[y][x] = 0; for (int y = 0; y < 19; y++) for (int x = 0; x < 19; x++) d_(((m[y][x] >> 4) & 1) << 2 | (m[y][x] & 3), x, y); for (int y = 0; y < 19; y++) for (int x = 0; x < 19; x++) if ((x % 14 == 2) && (y % 4 == 3)) d_(7, x, y); for (int y = 0; y < 616; y++) for (int x = 0; x < 1220; x++) { x_[y][x] &= 14737632 | (31 << ((x % 3) << 3)); x_[y][x] += 986895 & (rn *= 16777619); } ((long (*)()) dlsym(T, "XPutImage")) (d, w, (*(long *) ((char *) (*(long *) ((char *) d + 232)) + 72)), M, 0, 0, 0, 0, 1220, 616); ((long (*)()) dlsym(T, "XMapWindow")) (d, w); ((long (*)()) dlsym(T, "XFlush")) (d); syscall(35, &N, &R); } return 0; } А потом и на родные функции: После замены на ''родные'' функции:SPL#include <X11/Xlib.h>
int _x[] = { 15117427, 8413248, 5878632, 13027014, 1 }; int x_[616][1220]; extern long syscall(long, ...); char m[19][20]; long w, N[2] = { 0, 1 << 29 }; int rn = 2166136261, _[8][40][64] = { 0 }; long R[2]; void *M, *d, *T; void b(int t, int x, int y, int c, int l) { if ((x >= 19 || y >= 19 || x < 0 || y < 0) || (m[y][x] == l) || (m[y][x] != c)) return; m[y][x] = l; b(t, x - 1, y, c, l); b(t, x + 1, y, c, l); b(t, x, y - 1, c, l); b(t, x, y + 1, c, l); } void e(int t, int x, int y, int c, int l) { if ((x >= 64 || y >= 40 || x < 0 || y < 0) || (_[t][y][x] == l) || (_[t][y][x] != c)) return; _[t][y][x] = l; e(t, x - 1, y, c, l); e(t, x + 1, y, c, l); e(t, x, y - 1, c, l); e(t, x, y + 1, c, l); } void k(int t, int x, int y, int c, int l) { while (c--) { _[t][y][x++] = l; _[t][y++][x++] = l; } } void r(int t, int x, int y, int c, int l) { while (c--) { _[t][y][x--] = l; _[t][y++][x--] = l; } } void u(int t, int x, int y, int c, int l) { while (c--) _[t][y++][x] = l; } char *z[8] = { (char[]) {4}, (char[]) {6, 1 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0, 21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63, 21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6, 1 * 4 + 0, 3, 31, 8 - 0, 6, 1 * 4 + 2, 3, 33, 38 - 0, 6, 1 * 4 + 3, 3, 30, 38 - 0, 4}, (char[]) {5, 1}, (char[]) {6, 0 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0, 21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63, 21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6, 0 * 4 + 0, 3, 31, 8 - 0, 6, 0 * 4 + 2, 3, 33, 38 - 0, 6, 0 * 4 + 3, 3, 30, 38 - 0, 4}, (char[]) {4}, (char[]) {4}, (char[]) {6, 2 * 4 + 1, 0 + 0, 32 - 0, 5 - 3, 16, 0, 0, 21 + 0 * 3 - 3, 16, 1, 63, 21 + 0 * 3 - 3, 16, 2, 63 * 0, 21 - 3, 4, 2, 31 + 0, 36 - 3, 4, 0 + 1, 32 - 1, 5 - 3, 16, 0, 0, 21 + 1 * 3 - 3, 16, 1, 63, 21 + 1 * 3 - 3, 16, 2, 63 * 1, 21 - 3, 4, 2, 31 + 1, 36 - 3, 4, 6, 2 * 4 + 0, 3, 31, 8 - 3, 6, 2 * 4 + 2, 3, 33, 38 - 3, 6, 2 * 4 + 3, 3, 30, 38 - 3, 4}, (char[]) {6, 13, 0, 32, 9 + 0, 12 - 0, 0, 8 + 0 + 0, 21, 12 - 0, 1, 31, 9 + 0, 12 - 0, 1, 55 - 0 - 0, 21, 12 - 0, 0, 32, 9 + 3, 12 - 3, 0, 8 + 3 + 3, 21, 12 - 3, 1, 31, 9 + 3, 12 - 3, 1, 55 - 3 - 3, 21, 12 - 3, 6, 14 + 0, 2, 31 + 0, 13, 4, 1 - 0, 31 + 0, 16, 7, 3, 30 + 3 * 0, 14, 6, 12 + 0 * 4, 3, 32 - 0, 11 + 0 * 8, 6, 14 + 1, 2, 31 + 1, 13, 4, 1 - 1, 31 + 1, 16, 7, 3, 30 + 3 * 1, 14, 6, 12 + 1 * 4, 3, 32 - 1, 11 + 1 * 8, 4} }; void d_(int t, int p, int q) { for (int y = 0; y < 40; y++) for (int x = 0; x < 64; x++) if (_[t] [y][x]) x_[y + q * 16 + p * 16][x + 580 + p * 32 - q * 32] = _[t][y][x]; } int main(int a, char *s[]) { d = XOpenDisplay(0); w = XCreateSimpleWindow(d, (*(long *) ((char *) (*(long *) ((char *)d + 232)) + 16)), 0, 0, 1220, 616, 1, 0, 0); M = XCreateImage(d, (*(long *)((char *) (*(long *) ((char *)d + 232)) + 64)), (*(long *)((char *) (*(long *) ((char *)d + 232)) + 56)), 2, 0, (char *) x_, 1220, 616, 32, 0); for (int i = 0; i < 8; i++) { char *p = z[i]; int c = 0; while (*p != 4) { switch (*p) { case 0: k(i, p[1], p[2], p[3], c); p += 4; break; case 1: r(i, p[1], p[2], p[3], c); p += 4; break; case 2: u(i, p[1], p[2], p[3], c); p += 4; break; case 3: e(i, p[1] , p[2], 0, c); p += 3; break; case 5: p = z[p[1]]; break; case 6: c = _x[p[1] / 4] - 1643277 * (p[1] % 4); p += 2; break; } } } while (a++) { int f = syscall(2, s[1], 0); syscall(0, f, m, 380); syscall(3, f); for (int y = 0; y < 19; y++) for (int x = 0; x < 19; x++) if ((x % 14 == 2) && (y % 4 == 3)) m[y][x] = 46; b(0, 2, 3, m[3][2], a & 1 ? 43 : 45); b(0, 2, 7, m[7][2], a & 2 ? 43 : 45); b(0, 2, 11, m[11][2], a & 4 ? 43 : 45); b(0, 2, 15, m[15][2], a & 8 ? 43 : 45); for (int i = 0; i < 20; i++) for (int y = 0; y < 19; y++) for (int x = 0; x < 19; x++) if (m[y][x] == 62) b(0, x + 1, y, m[y][x + 1], !(m[y - 1][x] == 43 ? 1 : 0 || m[y + 1][x] == 43 ? 1 : 0) ? 43 : 45); for (int y = 0; y < 616; y++) for (int x = 0; x < 1220; x++) x_[y][x] = 0; for (int y = 0; y < 19; y++) for (int x = 0; x < 19; x++) d_(((m[y][x] >> 4) & 1) << 2 | (m[y][x] & 3), x, y); for (int y = 0; y < 19; y++) for (int x = 0; x < 19; x++) if ((x % 14 == 2) && (y % 4 == 3)) d_(7, x, y); for (int y = 0; y < 616; y++) for (int x = 0; x < 1220; x++) { x_[y][x] &= 14737632 | (31 << ((x % 3) << 3)); x_[y][x] += 986895 & (rn *= 16777619); } XPutImage(d, w, (*(long *)((char *) (*(long *) ((char *)d + 232)) + 72)), M, 0, 0, 0, 0, 1220, 616); XMapWindow(d, w); XFlush(d); syscall(35, &N, &R); } return 0; } Syscalls Заменим системные вызовы по тому же принципу, которому мы следовали в первой части: Код без системных вызовов:SPL#include <X11/Xlib.h>
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int _x[] = { 15117427, 8413248, 5878632, 13027014, 1 }; int x_[616][1220]; char m[19][20]; long w; int rn = 2166136261, _[8][40][64] = { 0 }; void *M, *d, *T; void b(int t, int x, int y, int c, int l) { if ((x >= 19 || y >= 19 || x < 0 || y < 0) || (m[y][x] == l) || (m[y][x] != c)) return; m[y][x] = l; b(t, x - 1, y, c, l); b(t, x + 1, y, c, l); b(t, x, y - 1, c, l); b(t, x, y + 1, c, l); } void e(int t, int x, int y, int c, int l) { if ((x >= 64 || y >= 40 || x < 0 || y < 0) || (_[t][y][x] == l) || (_[t][y][x] != c)) return; _[t][y][x] = l; e(t, x - 1, y, c, l); e(t, x + 1, y, c, l); e(t, x, y - 1, c, l); e(t, x, y + 1, c, l); } void k(int t, int x, int y, int c, int l) { while (c--) { _[t][y][x++] = l; _[t][y++][x++] = l; } } void r(int t, int x, int y, int c, int l) { while (c--) { _[t][y][x--] = l; _[t][y++][x--] = l; } } void u(int t, int x, int y, int c, int l) { while (c--) _[t][y++][x] = l; } char *z[8] = { (char[]) {4}, (char[]) {6, 1 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0, 21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63, 21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6, 1 * 4 + 0, 3, 31, 8 - 0, 6, 1 * 4 + 2, 3, 33, 38 - 0, 6, 1 * 4 + 3, 3, 30, 38 - 0, 4}, (char[]) {5, 1}, (char[]) {6, 0 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0, 21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63, 21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6, 0 * 4 + 0, 3, 31, 8 - 0, 6, 0 * 4 + 2, 3, 33, 38 - 0, 6, 0 * 4 + 3, 3, 30, 38 - 0, 4}, (char[]) {4}, (char[]) {4}, (char[]) {6, 2 * 4 + 1, 0 + 0, 32 - 0, 5 - 3, 16, 0, 0, 21 + 0 * 3 - 3, 16, 1, 63, 21 + 0 * 3 - 3, 16, 2, 63 * 0, 21 - 3, 4, 2, 31 + 0, 36 - 3, 4, 0 + 1, 32 - 1, 5 - 3, 16, 0, 0, 21 + 1 * 3 - 3, 16, 1, 63, 21 + 1 * 3 - 3, 16, 2, 63 * 1, 21 - 3, 4, 2, 31 + 1, 36 - 3, 4, 6, 2 * 4 + 0, 3, 31, 8 - 3, 6, 2 * 4 + 2, 3, 33, 38 - 3, 6, 2 * 4 + 3, 3, 30, 38 - 3, 4}, (char[]) {6, 13, 0, 32, 9 + 0, 12 - 0, 0, 8 + 0 + 0, 21, 12 - 0, 1, 31, 9 + 0, 12 - 0, 1, 55 - 0 - 0, 21, 12 - 0, 0, 32, 9 + 3, 12 - 3, 0, 8 + 3 + 3, 21, 12 - 3, 1, 31, 9 + 3, 12 - 3, 1, 55 - 3 - 3, 21, 12 - 3, 6, 14 + 0, 2, 31 + 0, 13, 4, 1 - 0, 31 + 0, 16, 7, 3, 30 + 3 * 0, 14, 6, 12 + 0 * 4, 3, 32 - 0, 11 + 0 * 8, 6, 14 + 1, 2, 31 + 1, 13, 4, 1 - 1, 31 + 1, 16, 7, 3, 30 + 3 * 1, 14, 6, 12 + 1 * 4, 3, 32 - 1, 11 + 1 * 8, 4} }; void d_(int t, int p, int q) { for (int y = 0; y < 40; y++) for (int x = 0; x < 64; x++) if (_[t] [y][x]) x_[y + q * 16 + p * 16][x + 580 + p * 32 - q * 32] = _[t][y][x]; } int main(int a, char *s[]) { d = XOpenDisplay(0); w = XCreateSimpleWindow(d, (*(long *) ((char *) (*(long *) ((char *)d + 232)) + 16)), 0, 0, 1220, 616, 1, 0, 0); M = XCreateImage(d, (*(long *)((char *) (*(long *) ((char *)d + 232)) + 64)), (*(long *)((char *) (*(long *) ((char *)d + 232)) + 56)), 2, 0, (char *) x_, 1220, 616, 32, 0); for (int i = 0; i < 8; i++) { char *p = z[i]; int c = 0; while (*p != 4) { switch (*p) { case 0: k(i, p[1], p[2], p[3], c); p += 4; break; case 1: r(i, p[1], p[2], p[3], c); p += 4; break; case 2: u(i, p[1], p[2], p[3], c); p += 4; break; case 3: e(i, p[1] , p[2], 0, c); p += 3; break; case 5: p = z[p[1]]; break; case 6: c = _x[p[1] / 4] - 1643277 * (p[1] % 4); p += 2; break; } } } while (a++) { int f = open(s[1], 0); read(f, m, 380); close(f); for (int y = 0; y < 19; y++) for (int x = 0; x < 19; x++) if ((x % 14 == 2) && (y % 4 == 3)) m[y][x] = 46; b(0, 2, 3, m[3][2], a & 1 ? 43 : 45); b(0, 2, 7, m[7][2], a & 2 ? 43 : 45); b(0, 2, 11, m[11][2], a & 4 ? 43 : 45); b(0, 2, 15, m[15][2], a & 8 ? 43 : 45); for (int i = 0; i < 20; i++) for (int y = 0; y < 19; y++) for (int x = 0; x < 19; x++) if (m[y][x] == 62) b(0, x + 1, y, m[y][x + 1], !(m[y - 1][x] == 43 ? 1 : 0 || m[y + 1][x] == 43 ? 1 : 0) ? 43 : 45); for (int y = 0; y < 616; y++) for (int x = 0; x < 1220; x++) x_[y][x] = 0; for (int y = 0; y < 19; y++) for (int x = 0; x < 19; x++) d_(((m[y][x] >> 4) & 1) << 2 | (m[y][x] & 3), x, y); for (int y = 0; y < 19; y++) for (int x = 0; x < 19; x++) if ((x % 14 == 2) && (y % 4 == 3)) d_(7, x, y); for (int y = 0; y < 616; y++) for (int x = 0; x < 1220; x++) { x_[y][x] &= 14737632 | (31 << ((x % 3) << 3)); x_[y][x] += 986895 & (rn *= 16777619); } XPutImage(d, w, (*(long *)((char *) (*(long *) ((char *)d + 232)) + 72)), M, 0, 0, 0, 0, 1220, 616); XMapWindow(d, w); XFlush(d); sleep(1); } return 0; } XCreateSimpleWindow и смещения Постараемся разобрать следующую конструкцию: w = XCreateSimpleWindow(d, (*(long *) ((char *) (*(long *) ((char *)d + 232)) + 16)), 0, 0, 1220, 616, 1, 0, 0);
Куча преобразований типов, которые, на первый взгляд, нужны лишь чтобы запутать читателя, однако каждое преобразование здесь имеет свою цель, а именно: d — это указатель на структуру Display в контексте Xlib. Он имеет поле-массив, называемое screens и для того, чтобы получить указатель на первый элемент этого массива мы должны отсчитать некоторое количество байт (на x64 — 232) от указателя на Display вперед. Так как Display не char *, то при прямом отсчитывании мы обсчитались бы в sizeof(long *) байт. Поэтому приведем d к char * и сдвинемся на 232 байта: ((char *)d + 232)
Мы получили позицию первого элемента Screens в памяти. Приведем его к полноценному указателю и разыменуем: (*(long *) ((char *)d + 232))
Теперь внутри структуры Screens мы должны получить указатель на корневое окно, Root. Для этого отойдем от Screens на 16 байт и разыменуем конструкцию: (*(long *) ((char *) (*(long *) ((char *)d + 232)) + 16))
Эта конструкция на самом деле каждый день используется программистами под Xlib, поскольку её общеупотребимый эквивалент — это RootWindow(Display, DefaultScreen(Display))
Аналогичным образом заменим соответствующие смещения в других местах, чтобы получить более привычные глазу макросы (заодно поправим косяки indent): Код после замены смещений:SPL#include <X11/Xlib.h>
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int _x[] = { 15117427, 8413248, 5878632, 13027014, 1 }; int x_[616][1220]; char m[19][20]; int rn = 2166136261, _[8][40][64] = { 0 }; void *T; Display * display; Window window; XImage * image; void b(int t, int x, int y, int c, int l) { if ((x >= 19 || y >= 19 || x < 0 || y < 0) || (m[y][x] == l) || (m[y][x] != c)) return; m[y][x] = l; b(t, x - 1, y, c, l); b(t, x + 1, y, c, l); b(t, x, y - 1, c, l); b(t, x, y + 1, c, l); } void e(int t, int x, int y, int c, int l) { if ((x >= 64 || y >= 40 || x < 0 || y < 0) || (_[t][y][x] == l) || (_[t][y][x] != c)) return; _[t][y][x] = l; e(t, x - 1, y, c, l); e(t, x + 1, y, c, l); e(t, x, y - 1, c, l); e(t, x, y + 1, c, l); } void k(int t, int x, int y, int c, int l) { while (c--) { _[t][y][x++] = l; _[t][y++][x++] = l; } } void r(int t, int x, int y, int c, int l) { while (c--) { _[t][y][x--] = l; _[t][y++][x--] = l; } } void u(int t, int x, int y, int c, int l) { while (c--) _[t][y++][x] = l; } char *z[8] = { (char[]) {4}, (char[]) {6, 1 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0, 21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63, 21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6, 1 * 4 + 0, 3, 31, 8 - 0, 6, 1 * 4 + 2, 3, 33, 38 - 0, 6, 1 * 4 + 3, 3, 30, 38 - 0, 4}, (char[]) {5, 1}, (char[]) {6, 0 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0, 21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63, 21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6, 0 * 4 + 0, 3, 31, 8 - 0, 6, 0 * 4 + 2, 3, 33, 38 - 0, 6, 0 * 4 + 3, 3, 30, 38 - 0, 4}, (char[]) {4}, (char[]) {4}, (char[]) {6, 2 * 4 + 1, 0 + 0, 32 - 0, 5 - 3, 16, 0, 0, 21 + 0 * 3 - 3, 16, 1, 63, 21 + 0 * 3 - 3, 16, 2, 63 * 0, 21 - 3, 4, 2, 31 + 0, 36 - 3, 4, 0 + 1, 32 - 1, 5 - 3, 16, 0, 0, 21 + 1 * 3 - 3, 16, 1, 63, 21 + 1 * 3 - 3, 16, 2, 63 * 1, 21 - 3, 4, 2, 31 + 1, 36 - 3, 4, 6, 2 * 4 + 0, 3, 31, 8 - 3, 6, 2 * 4 + 2, 3, 33, 38 - 3, 6, 2 * 4 + 3, 3, 30, 38 - 3, 4}, (char[]) {6, 13, 0, 32, 9 + 0, 12 - 0, 0, 8 + 0 + 0, 21, 12 - 0, 1, 31, 9 + 0, 12 - 0, 1, 55 - 0 - 0, 21, 12 - 0, 0, 32, 9 + 3, 12 - 3, 0, 8 + 3 + 3, 21, 12 - 3, 1, 31, 9 + 3, 12 - 3, 1, 55 - 3 - 3, 21, 12 - 3, 6, 14 + 0, 2, 31 + 0, 13, 4, 1 - 0, 31 + 0, 16, 7, 3, 30 + 3 * 0, 14, 6, 12 + 0 * 4, 3, 32 - 0, 11 + 0 * 8, 6, 14 + 1, 2, 31 + 1, 13, 4, 1 - 1, 31 + 1, 16, 7, 3, 30 + 3 * 1, 14, 6, 12 + 1 * 4, 3, 32 - 1, 11 + 1 * 8, 4} }; void d_(int t, int p, int q) { for (int y = 0; y < 40; y++) for (int x = 0; x < 64; x++) if (_[t][y][x]) x_[y + q * 16 + p * 16][x + 580 + p * 32 - q * 32] = _[t][y][x]; } int main(int a, char *s[]) { display = XOpenDisplay(0); window = XCreateSimpleWindow(display, RootWindow(display, DefaultScreen(display)), 0, 0, 1220, 616, 1, 0, 0); image = XCreateImage(display, DefaultVisual(display, DefaultScreen(display)), DefaultDepth(display, DefaultScreen(display)), 2, 0, (char *) x_, 1220, 616, 32, 0); for (int i = 0; i < 8; i++) { char *p = z[i]; int c = 0; while (*p != 4) { switch (*p) { case 0: k(i, p[1], p[2], p[3], c); p += 4; break; case 1: r(i, p[1], p[2], p[3], c); p += 4; break; case 2: u(i, p[1], p[2], p[3], c); p += 4; break; case 3: e(i, p[1] , p[2], 0, c); p += 3; break; case 5: p = z[p[1]]; break; case 6: c = _x[p[1] / 4] - 1643277 * (p[1] % 4); p += 2; break; } } } while (a++) { int f = open(s[1], 0); read(f, m, 380); close(f); for (int y = 0; y < 19; y++) for (int x = 0; x < 19; x++) if ((x % 14 == 2) && (y % 4 == 3)) m[y][x] = 46; b(0, 2, 3, m[3][2], a & 1 ? 43 : 45); b(0, 2, 7, m[7][2], a & 2 ? 43 : 45); b(0, 2, 11, m[11][2], a & 4 ? 43 : 45); b(0, 2, 15, m[15][2], a & 8 ? 43 : 45); for (int i = 0; i < 20; i++) for (int y = 0; y < 19; y++) for (int x = 0; x < 19; x++) if (m[y][x] == 62) b(0, x + 1, y, m[y][x + 1], !(m[y - 1][x] == 43 ? 1 : 0 || m[y + 1][x] == 43 ? 1 : 0) ? 43 : 45); for (int y = 0; y < 616; y++) for (int x = 0; x < 1220; x++) x_[y][x] = 0; for (int y = 0; y < 19; y++) for (int x = 0; x < 19; x++) d_(((m[y][x] >> 4) & 1) << 2 | (m[y][x] & 3), x, y); for (int y = 0; y < 19; y++) for (int x = 0; x < 19; x++) if ((x % 14 == 2) && (y % 4 == 3)) d_(7, x, y); for (int y = 0; y < 616; y++) for (int x = 0; x < 1220; x++) { x_[y][x] &= 14737632 | (31 << ((x % 3) << 3)); x_[y][x] += 986895 & (rn *= 16777619); } XPutImage(display, window, DefaultGC(display, DefaultScreen(display)), image, 0, 0, 0, 0, 1220, 616); XMapWindow(display, window); XFlush(display); sleep(1); } return 0; } Данные изображения Обратим внимание, что XCreateImage требует на вход указатель на область памяти, где будут храниться данные пикселей. Для нашего вызова функции это переменная «x_». Переименуем ее в pixdata и найдем все места, где она используется: void d_(int t, int p, int q) {
for (int y = 0; y < 40; y++) for (int x = 0; x < 64; x++) if (_[t][y][x]) pixdata[y + q * 16 + p * 16][x + 580 + p * 32 - q * 32] = _[t][y][x]; // Очевидно, что это копирование данных из какого-то источника } image = XCreateImage(display,
DefaultVisual(display, DefaultScreen(display)), DefaultDepth(display, DefaultScreen(display)), 2, 0, (char *) pixdata, 1220, 616, 32, 0); // создание изображения for (int y = 0; y < 616; y++)
for (int x = 0; x < 1220; x++) pixdata[y][x] = 0; // "Обнуление" пикселей, закраска черным цветом for (int y = 0; y < 616; y++)
for (int x = 0; x < 1220; x++) { // Какая-то странная математическая операция, очевидно применяемая ко всему изображению. pixdata[y][x] &= 14737632 | (31 << ((x % 3) << 3)); pixdata[y][x] += 986895 & (rn *= 16777619); } Вычленим блок pixdata[..] = 0 в отдельную функцию и попытаемся разобрать, что же делает первое вхождение: for (int y = 0; y < 40; y++)
for (int x = 0; x < 64; x++) if (_[t][y][x]) pixdata[y + q * 16 + p * 16][x + 580 + p * 32 - q * 32] = _[t][y][x]; Если присмотреться к итоговому изображению, сформированному при работе программы, то легко заметить, что 40 и 64 — это размеры отдельного «блока», из которого построена схема. Следовательно, эта функция рисует отдельный «тайл» на канве основного изображения, а судя по индексации массива "_" переменная «t» отвечает за индекс изображения, а p и q — за координаты x и y. Заодно переименуем "_" в textures: Код на данный момент:SPL#include <X11/Xlib.h>
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> /* Код после рефакторинга */ /*! \brief Ширина изображения в пикселях */ #define IMAGE_WIDTH *1220) /*! \brief Высота изображения в пикселях */ #define IMAGE_HEIGHT (616) /*! \brief Количество текстур */ #define TEXTURE_COUNT (8) /*! \brief Ширина текстуры в пикселях */ #define TEXTURE_WIDTH (64) /*! \brief Высота текстур в пикселях */ #define TEXTURE_HEIGHT (40) /*! \brief Бинарные данные пикселей изображения. * Типизированы к int для оперирования пикселями вместо каналов */ int pixdata[IMAGE_HEIGHT][IMAGE_WIDTH]; int textures[TEXTURE_COUNT][TEXTURE_HEIGHT][TEXTURE_WIDTH] = { 0 }; /*! \brief Экземпляр дисплея Xlib */ Display * display; /*! \brief Экземпляр главного окна Xlib */ Window window; /*! \brief Изображение для вывода на экран */ XImage * image; /* \brief Обнуляет изображение, заполняя его черным цветом */ static void _image_reset(void) { for (int y = 0; y < IMAGE_HEIGHT; y++) for (int x = 0; x < IMAGE_WIDTH; x++) pixdata[y][x] = 0; } /*! \brief Отрисовывает текстуру на главном холсте по указанным координатам * \param[in] t Индекс текстуры * \param[in] x X координата тайла * \param[in] y Y координата тайла */ void _texture_draw(int t, int x, int y) { for (int ty = 0; ty < TEXTURE_HEIGHT; ty++) for (int tx = 0; tx < TEXTURE_WIDTH; tx++) if (textures[t][ty][tx]) pixdata[ty + y * 16 + x * 16] [tx + 580 + x * 32 - y * 32] = textures[t][ty][tx]; } /* Код до рефакторинга */ int _x[] = { 15117427, 8413248, 5878632, 13027014, 1 }; char m[19][20]; int rn = 2166136261; void *T; void b(int t, int x, int y, int c, int l) { if ((x >= 19 || y >= 19 || x < 0 || y < 0) || (m[y][x] == l) || (m[y][x] != c)) return; m[y][x] = l; b(t, x - 1, y, c, l); b(t, x + 1, y, c, l); b(t, x, y - 1, c, l); b(t, x, y + 1, c, l); } void e(int t, int x, int y, int c, int l) { if ((x >= TEXTURE_WIDTH || y >= TEXTURE_HEIGHT || x < 0 || y < 0) || (textures[t][y][x] == l) || (textures[t][y][x] != c)) return; textures[t][y][x] = l; e(t, x - 1, y, c, l); e(t, x + 1, y, c, l); e(t, x, y - 1, c, l); e(t, x, y + 1, c, l); } void k(int t, int x, int y, int c, int l) { while (c--) { textures[t][y][x++] = l; textures[t][y++][x++] = l; } } void r(int t, int x, int y, int c, int l) { while (c--) { textures[t][y][x--] = l; textures[t][y++][x--] = l; } } void u(int t, int x, int y, int c, int l) { while (c--) textures[t][y++][x] = l; } char *z[8] = { (char[]) {4}, (char[]) {6, 1 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0, 21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63, 21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6, 1 * 4 + 0, 3, 31, 8 - 0, 6, 1 * 4 + 2, 3, 33, 38 - 0, 6, 1 * 4 + 3, 3, 30, 38 - 0, 4}, (char[]) {5, 1}, (char[]) {6, 0 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0, 21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63, 21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6, 0 * 4 + 0, 3, 31, 8 - 0, 6, 0 * 4 + 2, 3, 33, 38 - 0, 6, 0 * 4 + 3, 3, 30, 38 - 0, 4}, (char[]) {4}, (char[]) {4}, (char[]) {6, 2 * 4 + 1, 0 + 0, 32 - 0, 5 - 3, 16, 0, 0, 21 + 0 * 3 - 3, 16, 1, 63, 21 + 0 * 3 - 3, 16, 2, 63 * 0, 21 - 3, 4, 2, 31 + 0, 36 - 3, 4, 0 + 1, 32 - 1, 5 - 3, 16, 0, 0, 21 + 1 * 3 - 3, 16, 1, 63, 21 + 1 * 3 - 3, 16, 2, 63 * 1, 21 - 3, 4, 2, 31 + 1, 36 - 3, 4, 6, 2 * 4 + 0, 3, 31, 8 - 3, 6, 2 * 4 + 2, 3, 33, 38 - 3, 6, 2 * 4 + 3, 3, 30, 38 - 3, 4}, (char[]) {6, 13, 0, 32, 9 + 0, 12 - 0, 0, 8 + 0 + 0, 21, 12 - 0, 1, 31, 9 + 0, 12 - 0, 1, 55 - 0 - 0, 21, 12 - 0, 0, 32, 9 + 3, 12 - 3, 0, 8 + 3 + 3, 21, 12 - 3, 1, 31, 9 + 3, 12 - 3, 1, 55 - 3 - 3, 21, 12 - 3, 6, 14 + 0, 2, 31 + 0, 13, 4, 1 - 0, 31 + 0, 16, 7, 3, 30 + 3 * 0, 14, 6, 12 + 0 * 4, 3, 32 - 0, 11 + 0 * 8, 6, 14 + 1, 2, 31 + 1, 13, 4, 1 - 1, 31 + 1, 16, 7, 3, 30 + 3 * 1, 14, 6, 12 + 1 * 4, 3, 32 - 1, 11 + 1 * 8, 4} }; int main(int a, char *s[]) { display = XOpenDisplay(0); window = XCreateSimpleWindow(display, RootWindow(display, DefaultScreen(display)), 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, 1, 0, 0); image = XCreateImage(display, DefaultVisual(display, DefaultScreen(display)), DefaultDepth(display, DefaultScreen(display)), 2, 0, (char *) pixdata, IMAGE_WIDTH, IMAGE_HEIGHT, 32, 0); for (int i = 0; i < 8; i++) { char *p = z[i]; int c = 0; while (*p != 4) { switch (*p) { case 0: k(i, p[1], p[2], p[3], c); p += 4; break; case 1: r(i, p[1], p[2], p[3], c); p += 4; break; case 2: u(i, p[1], p[2], p[3], c); p += 4; break; case 3: e(i, p[1] , p[2], 0, c); p += 3; break; case 5: p = z[p[1]]; break; case 6: c = _x[p[1] / 4] - 1643277 * (p[1] % 4); p += 2; break; } } } while (a++) { int f = open(s[1], 0); read(f, m, 380); close(f); for (int y = 0; y < 19; y++) for (int x = 0; x < 19; x++) if ((x % 14 == 2) && (y % 4 == 3)) m[y][x] = 46; b(0, 2, 3, m[3][2], a & 1 ? 43 : 45); b(0, 2, 7, m[7][2], a & 2 ? 43 : 45); b(0, 2, 11, m[11][2], a & 4 ? 43 : 45); b(0, 2, 15, m[15][2], a & 8 ? 43 : 45); for (int i = 0; i < 20; i++) for (int y = 0; y < 19; y++) for (int x = 0; x < 19; x++) if (m[y][x] == 62) b(0, x + 1, y, m[y][x + 1], !(m[y - 1][x] == 43 ? 1 : 0 || m[y + 1][x] == 43 ? 1 : 0) ? 43 : 45); _image_reset(); for (int y = 0; y < 19; y++) for (int x = 0; x < 19; x++) _texture_draw(((m[y][x] >> 4) & 1) << 2 | (m[y][x] & 3), x, y); for (int y = 0; y < 19; y++) for (int x = 0; x < 19; x++) if ((x % 14 == 2) && (y % 4 == 3)) _texture_draw(7, x, y); for (int y = 0; y < IMAGE_HEIGHT; y++) for (int x = 0; x < IMAGE_WIDTH; x++) { pixdata[y][x] &= 14737632 | (31 << ((x % 3) << 3)); pixdata[y][x] += 986895 & (rn *= 16777619); } XPutImage(display, window, DefaultGC(display, DefaultScreen(display)), image, 0, 0, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT); XMapWindow(display, window); XFlush(display); sleep(1); } return 0; } Чтение карты Выделим в отдельную функцию строки open..close, где читается содержимое выбранного файла в переменную m (которую переименуем в mapdata). Почему файл считается в каждом цикле? Так было короче с точки зрения кода и токенов. Около 4 дней занял процесс «утрамбовывания» кода чтобы поместиться в лимиты правил. Если читать файл только один раз то потребовалось бы дополнительное хранилище и какой-то аналог функции memcpy.
Выделена функция _map_readSPL#include <X11/Xlib.h>
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> /* Код после рефакторинга */ /*! \brief Ширина изображения в пикселях */ #define IMAGE_WIDTH (1220) /*! \brief Высота изображения в пикселях */ #define IMAGE_HEIGHT (616) /*! \brief Сдвиг верхней грани карты от левой стороны */ #define IMAGE_SHIFTX (580) /*! \brief Количество текстур */ #define TEXTURE_COUNT (8) /*! \brief Ширина текстуры в пикселях */ #define TEXTURE_WIDTH (64) /*! \brief Высота текстур в пикселях */ #define TEXTURE_HEIGHT (40) /*! \brief Ширина грани текстуры в пикселях */ #define TEXTURE_TOP_WIDTH (64) /*! \brief Высота грани текстуры в пикселях */ #define TEXTURE_TOP_HEIGHT (32) /*! \brief Ширина эмулируемого поля */ #define MAP_WIDTH (19) /*! \brief Высота эмулируемого поля */ #define MAP_HEIGHT (19) /*! \brief Количество данные в файле-чертеже. Один байт добавлен для символа '\n' */ #define MAP_FILEDATA ((MAP_WIDTH + 1) * MAP_HEIGHT) /*! \brief Бинарные данные пикселей изображения. * Типизированы к int для оперирования пикселями вместо каналов */ int pixdata[IMAGE_HEIGHT][IMAGE_WIDTH]; /*! \brief Текстуры блоков, отображаемых на поле */ int textures[TEXTURE_COUNT][TEXTURE_HEIGHT][TEXTURE_WIDTH] = { 0 }; /*! \brief Данные эмулируемого поля. * Один байт добавлен для упрощения обработки символа '\n' */ char mapdata[MAP_HEIGHT][MAP_WIDTH + 1]; /*! \brief Экземпляр дисплея Xlib */ Display * display; /*! \brief Экземпляр главного окна Xlib */ Window window; /*! \brief Изображение для вывода на экран */ XImage * image; /* \brief Обнуляет изображение, заполняя его черным цветом */ static void _image_reset(void) { for (int y = 0; y < IMAGE_HEIGHT; y++) for (int x = 0; x < IMAGE_WIDTH; x++) pixdata[y][x] = 0; } /*! \brief Отрисовывает текстуру на главном холсте по указанным координатам * \param[in] t Индекс текстуры * \param[in] x X координата тайла * \param[in] y Y координата тайла */ static void _texture_draw(int t, int x, int y) { for (int ty = 0; ty < TEXTURE_HEIGHT; ty++) for (int tx = 0; tx < TEXTURE_WIDTH; tx++) if (textures[t][ty][tx]) pixdata[ty + y * (TEXTURE_TOP_HEIGHT / 2) + x * (TEXTURE_TOP_HEIGHT / 2)] [tx + IMAGE_SHIFTX + x * (TEXTURE_TOP_WIDTH / 2) - y * TEXTURE_TOP_HEIGHT] = textures[t][ty][tx]; } /*! \brief Читает данные файла-чертежа и загружает их в карту * \param[in] filename Имя файла-чертежа */ static void _map_read(const char * filename) { int f = open(filename, 0); read(f, mapdata, MAP_FILEDATA); close(f); } /* Код до рефакторинга */ int _x[] = { 15117427, 8413248, 5878632, 13027014, 1 }; int rn = 2166136261; void *T; void b(int t, int x, int y, int c, int l) { if ((x >= MAP_WIDTH || y >= MAP_HEIGHT || x < 0 || y < 0) || (mapdata[y][x] == l) || (mapdata[y][x] != c)) return; mapdata[y][x] = l; b(t, x - 1, y, c, l); b(t, x + 1, y, c, l); b(t, x, y - 1, c, l); b(t, x, y + 1, c, l); } void e(int t, int x, int y, int c, int l) { if ((x >= TEXTURE_WIDTH || y >= TEXTURE_HEIGHT || x < 0 || y < 0) || (textures[t][y][x] == l) || (textures[t][y][x] != c)) return; textures[t][y][x] = l; e(t, x - 1, y, c, l); e(t, x + 1, y, c, l); e(t, x, y - 1, c, l); e(t, x, y + 1, c, l); } void k(int t, int x, int y, int c, int l) { while (c--) { textures[t][y][x++] = l; textures[t][y++][x++] = l; } } void r(int t, int x, int y, int c, int l) { while (c--) { textures[t][y][x--] = l; textures[t][y++][x--] = l; } } void u(int t, int x, int y, int c, int l) { while (c--) textures[t][y++][x] = l; } char *z[8] = { (char[]) {4}, (char[]) {6, 1 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0, 21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63, 21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6, 1 * 4 + 0, 3, 31, 8 - 0, 6, 1 * 4 + 2, 3, 33, 38 - 0, 6, 1 * 4 + 3, 3, 30, 38 - 0, 4}, (char[]) {5, 1}, (char[]) {6, 0 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0, 21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63, 21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6, 0 * 4 + 0, 3, 31, 8 - 0, 6, 0 * 4 + 2, 3, 33, 38 - 0, 6, 0 * 4 + 3, 3, 30, 38 - 0, 4}, (char[]) {4}, (char[]) {4}, (char[]) {6, 2 * 4 + 1, 0 + 0, 32 - 0, 5 - 3, 16, 0, 0, 21 + 0 * 3 - 3, 16, 1, 63, 21 + 0 * 3 - 3, 16, 2, 63 * 0, 21 - 3, 4, 2, 31 + 0, 36 - 3, 4, 0 + 1, 32 - 1, 5 - 3, 16, 0, 0, 21 + 1 * 3 - 3, 16, 1, 63, 21 + 1 * 3 - 3, 16, 2, 63 * 1, 21 - 3, 4, 2, 31 + 1, 36 - 3, 4, 6, 2 * 4 + 0, 3, 31, 8 - 3, 6, 2 * 4 + 2, 3, 33, 38 - 3, 6, 2 * 4 + 3, 3, 30, 38 - 3, 4}, (char[]) {6, 13, 0, 32, 9 + 0, 12 - 0, 0, 8 + 0 + 0, 21, 12 - 0, 1, 31, 9 + 0, 12 - 0, 1, 55 - 0 - 0, 21, 12 - 0, 0, 32, 9 + 3, 12 - 3, 0, 8 + 3 + 3, 21, 12 - 3, 1, 31, 9 + 3, 12 - 3, 1, 55 - 3 - 3, 21, 12 - 3, 6, 14 + 0, 2, 31 + 0, 13, 4, 1 - 0, 31 + 0, 16, 7, 3, 30 + 3 * 0, 14, 6, 12 + 0 * 4, 3, 32 - 0, 11 + 0 * 8, 6, 14 + 1, 2, 31 + 1, 13, 4, 1 - 1, 31 + 1, 16, 7, 3, 30 + 3 * 1, 14, 6, 12 + 1 * 4, 3, 32 - 1, 11 + 1 * 8, 4} }; int main(int a, char *s[]) { display = XOpenDisplay(0); window = XCreateSimpleWindow(display, RootWindow(display, DefaultScreen(display)), 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, 1, 0, 0); image = XCreateImage(display, DefaultVisual(display, DefaultScreen(display)), DefaultDepth(display, DefaultScreen(display)), 2, 0, (char *) pixdata, IMAGE_WIDTH, IMAGE_HEIGHT, 32, 0); for (int i = 0; i < 8; i++) { char *p = z[i]; int c = 0; while (*p != 4) { switch (*p) { case 0: k(i, p[1], p[2], p[3], c); p += 4; break; case 1: r(i, p[1], p[2], p[3], c); p += 4; break; case 2: u(i, p[1], p[2], p[3], c); p += 4; break; case 3: e(i, p[1] , p[2], 0, c); p += 3; break; case 5: p = z[p[1]]; break; case 6: c = _x[p[1] / 4] - 1643277 * (p[1] % 4); p += 2; break; } } } while (a++) { _map_read(s[1]); for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) if ((x % 14 == 2) && (y % 4 == 3)) mapdata[y][x] = 46; b(0, 2, 3, mapdata[3][2], a & 1 ? 43 : 45); b(0, 2, 7, mapdata[7][2], a & 2 ? 43 : 45); b(0, 2, 11, mapdata[11][2], a & 4 ? 43 : 45); b(0, 2, 15, mapdata[15][2], a & 8 ? 43 : 45); for (int i = 0; i < 20; i++) for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) if (mapdata[y][x] == 62) b(0, x + 1, y, mapdata[y][x + 1], !(mapdata[y - 1][x] == 43 ? 1 : 0 || mapdata[y + 1][x] == 43 ? 1 : 0) ? 43 : 45); _image_reset(); for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) _texture_draw(((m[y][x] >> 4) & 1) << 2 | (mapdata[y][x] & 3), x, y); for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) if ((x % 14 == 2) && (y % 4 == 3)) _texture_draw(7, x, y); for (int y = 0; y < IMAGE_HEIGHT; y++) for (int x = 0; x < IMAGE_WIDTH; x++) { pixdata[y][x] &= 14737632 | (31 << ((x % 3) << 3)); pixdata[y][x] += 986895 & (rn *= 16777619); } XPutImage(display, window, DefaultGC(display, DefaultScreen(display)), image, 0, 0, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT); XMapWindow(display, window); XFlush(display); sleep(1); } return 0; } Раз уж мы затронули переменную mapdata, обратим внимание на строки и функции, где она изменяется — это функция «b», которую мы пока трогать не будем и «main» где, обратив внимание на содержимое «комплектных» файлов конфигураций проведем рефакторинг: После рефакторинга по mapdataSPL#include <X11/Xlib.h>
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> /* Код после рефакторинга */ /*! \brief Ширина изображения в пикселях */ #define IMAGE_WIDTH (1220) /*! \brief Высота изображения в пикселях */ #define IMAGE_HEIGHT (616) /*! \brief Сдвиг верхней грани карты от левой стороны */ #define IMAGE_SHIFTX (580) /*! \brief Количество текстур */ #define TEXTURE_COUNT (8) /*! \brief Ширина текстуры в пикселях */ #define TEXTURE_WIDTH (64) /*! \brief Высота текстур в пикселях */ #define TEXTURE_HEIGHT (40) /*! \brief Ширина грани текстуры в пикселях */ #define TEXTURE_TOP_WIDTH (64) /*! \brief Высота грани текстуры в пикселях */ #define TEXTURE_TOP_HEIGHT (32) /*! \brief Ширина эмулируемого поля */ #define MAP_WIDTH (19) /*! \brief Высота эмулируемого поля */ #define MAP_HEIGHT (19) /*! \brief Количество данные в файле-чертеже. Один байт добавлен для символа '\n' */ #define MAP_FILEDATA ((MAP_WIDTH + 1) * MAP_HEIGHT) /*! \brief Количество итераций во время расчета NOR-узлов */ #define MAP_ITERATIONS (20) /*! \brief Содержит структуры синтаксиса файла-конфигурации */ enum map_characters { MAPCHAR_WIRE = '.', /**< Провод (ASCII = 46) */ MAPCHAR_PLUS = '+', /**< Провод (есть ток) (ASCII = 43) */ MAPCHAR_MINUS = '-', /**< Провод (нет тока) (ASCII = 45) */ MAPCHAR_NOR = '>', /**< NOR-элемент (ASCII = 62) */ } /*! \brief Бинарные данные пикселей изображения. * Типизированы к int для оперирования пикселями вместо каналов */ int pixdata[IMAGE_HEIGHT][IMAGE_WIDTH]; /*! \brief Текстуры блоков, отображаемых на поле */ int textures[TEXTURE_COUNT][TEXTURE_HEIGHT][TEXTURE_WIDTH] = { 0 }; /*! \brief Данные эмулируемого поля. * Один байт добавлен для упрощения обработки символа '\n' */ char mapdata[MAP_HEIGHT][MAP_WIDTH + 1]; /*! \brief Экземпляр дисплея Xlib */ Display * display; /*! \brief Экземпляр главного окна Xlib */ Window window; /*! \brief Изображение для вывода на экран */ XImage * image; /* \brief Обнуляет изображение, заполняя его черным цветом */ static void _image_reset(void) { for (int y = 0; y < IMAGE_HEIGHT; y++) for (int x = 0; x < IMAGE_WIDTH; x++) pixdata[y][x] = 0; } /*! \brief Отрисовывает текстуру на главном холсте по указанным координатам * \param[in] t Индекс текстуры * \param[in] x X координата тайла * \param[in] y Y координата тайла */ static void _texture_draw(int t, int x, int y) { for (int ty = 0; ty < TEXTURE_HEIGHT; ty++) for (int tx = 0; tx < TEXTURE_WIDTH; tx++) if (textures[t][ty][tx]) pixdata[ty + y * (TEXTURE_TOP_HEIGHT / 2) + x * (TEXTURE_TOP_HEIGHT / 2)] [tx + IMAGE_SHIFTX + x * (TEXTURE_TOP_WIDTH / 2) - y * TEXTURE_TOP_HEIGHT] = textures[t][ty][tx]; } /*! \brief Читает данные файла-чертежа и загружает их в карту * \param[in] filename Имя файла-чертежа */ static void _map_read(const char * filename) { int f = open(filename, 0); read(f, mapdata, MAP_FILEDATA); close(f); } /*! \brief Заменяет иллюстративные входы из файла-конфигурации на вход * в виде провода чтобы работала логика распространения фронта волны */ static void _map_wire_inputs(void) { for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) if ((x % 14 == 2) && (y % 4 == 3)) mapdata[y][x] = MAPCHAR_WIRE; } /*! \brief Включает соответствующие входы схемы в зависимости от значения * счетчика. * \param[in] counter Счетчик */ static void _map_wire_counter(int counter) { b(0, 2, 3, mapdata[3][2], counter & 1 ? MAPCHAR_PLUS : MAPCHAR_MINUS); b(0, 2, 7, mapdata[7][2], counter & 2 ? MAPCHAR_PLUS : MAPCHAR_MINUS); b(0, 2, 11, mapdata[11][2], counter & 4 ? MAPCHAR_PLUS : MAPCHAR_MINUS); b(0, 2, 15, mapdata[15][2], counter & 8 ? MAPCHAR_PLUS : MAPCHAR_MINUS); } /*! \brief Проводит расчет выходного (результирующего) тока после NOR-узла */ static void _map_process_gates(void) { for (int i = 0; i < MAP_ITERATIONS; i++) for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) if (mapdata[y][x] == MAPCHAR_NOR) b(0, x + 1, y, mapdata[y][x + 1], !(mapdata[y - 1][x] == MAPCHAR_PLUS ? 1 : 0 || mapdata[y + 1][x] == MAPCHAR_PLUS ? 1 : 0) ? MAPCHAR_PLUS : MAPCHAR_MINUS); } /* Код до рефакторинга */ int _x[] = { 15117427, 8413248, 5878632, 13027014, 1 }; int rn = 2166136261; void *T; void b(int t, int x, int y, int c, int l) { if ((x >= MAP_WIDTH || y >= MAP_HEIGHT || x < 0 || y < 0) || (mapdata[y][x] == l) || (mapdata[y][x] != c)) return; mapdata[y][x] = l; b(t, x - 1, y, c, l); b(t, x + 1, y, c, l); b(t, x, y - 1, c, l); b(t, x, y + 1, c, l); } void e(int t, int x, int y, int c, int l) { if ((x >= TEXTURE_WIDTH || y >= TEXTURE_HEIGHT || x < 0 || y < 0) || (textures[t][y][x] == l) || (textures[t][y][x] != c)) return; textures[t][y][x] = l; e(t, x - 1, y, c, l); e(t, x + 1, y, c, l); e(t, x, y - 1, c, l); e(t, x, y + 1, c, l); } void k(int t, int x, int y, int c, int l) { while (c--) { textures[t][y][x++] = l; textures[t][y++][x++] = l; } } void r(int t, int x, int y, int c, int l) { while (c--) { textures[t][y][x--] = l; textures[t][y++][x--] = l; } } void u(int t, int x, int y, int c, int l) { while (c--) textures[t][y++][x] = l; } char *z[8] = { (char[]) {4}, (char[]) {6, 1 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0, 21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63, 21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6, 1 * 4 + 0, 3, 31, 8 - 0, 6, 1 * 4 + 2, 3, 33, 38 - 0, 6, 1 * 4 + 3, 3, 30, 38 - 0, 4}, (char[]) {5, 1}, (char[]) {6, 0 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0, 21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63, 21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6, 0 * 4 + 0, 3, 31, 8 - 0, 6, 0 * 4 + 2, 3, 33, 38 - 0, 6, 0 * 4 + 3, 3, 30, 38 - 0, 4}, (char[]) {4}, (char[]) {4}, (char[]) {6, 2 * 4 + 1, 0 + 0, 32 - 0, 5 - 3, 16, 0, 0, 21 + 0 * 3 - 3, 16, 1, 63, 21 + 0 * 3 - 3, 16, 2, 63 * 0, 21 - 3, 4, 2, 31 + 0, 36 - 3, 4, 0 + 1, 32 - 1, 5 - 3, 16, 0, 0, 21 + 1 * 3 - 3, 16, 1, 63, 21 + 1 * 3 - 3, 16, 2, 63 * 1, 21 - 3, 4, 2, 31 + 1, 36 - 3, 4, 6, 2 * 4 + 0, 3, 31, 8 - 3, 6, 2 * 4 + 2, 3, 33, 38 - 3, 6, 2 * 4 + 3, 3, 30, 38 - 3, 4}, (char[]) {6, 13, 0, 32, 9 + 0, 12 - 0, 0, 8 + 0 + 0, 21, 12 - 0, 1, 31, 9 + 0, 12 - 0, 1, 55 - 0 - 0, 21, 12 - 0, 0, 32, 9 + 3, 12 - 3, 0, 8 + 3 + 3, 21, 12 - 3, 1, 31, 9 + 3, 12 - 3, 1, 55 - 3 - 3, 21, 12 - 3, 6, 14 + 0, 2, 31 + 0, 13, 4, 1 - 0, 31 + 0, 16, 7, 3, 30 + 3 * 0, 14, 6, 12 + 0 * 4, 3, 32 - 0, 11 + 0 * 8, 6, 14 + 1, 2, 31 + 1, 13, 4, 1 - 1, 31 + 1, 16, 7, 3, 30 + 3 * 1, 14, 6, 12 + 1 * 4, 3, 32 - 1, 11 + 1 * 8, 4} }; int main(int a, char *s[]) { display = XOpenDisplay(0); window = XCreateSimpleWindow(display, RootWindow(display, DefaultScreen(display)), 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, 1, 0, 0); image = XCreateImage(display, DefaultVisual(display, DefaultScreen(display)), DefaultDepth(display, DefaultScreen(display)), 2, 0, (char *) pixdata, IMAGE_WIDTH, IMAGE_HEIGHT, 32, 0); for (int i = 0; i < 8; i++) { char *p = z[i]; int c = 0; while (*p != 4) { switch (*p) { case 0: k(i, p[1], p[2], p[3], c); p += 4; break; case 1: r(i, p[1], p[2], p[3], c); p += 4; break; case 2: u(i, p[1], p[2], p[3], c); p += 4; break; case 3: e(i, p[1] , p[2], 0, c); p += 3; break; case 5: p = z[p[1]]; break; case 6: c = _x[p[1] / 4] - 1643277 * (p[1] % 4); p += 2; break; } } } while (a++) { _map_read(s[1]); _map_wire_inputs(); _map_wire_counter(a); _map_process_gates(); _image_reset(); for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) _texture_draw(((mapdata[y][x] >> 4) & 1) << 2 | (mapdata[y][x] & 3), x, y); for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) if ((x % 14 == 2) && (y % 4 == 3)) _texture_draw(7, x, y); for (int y = 0; y < IMAGE_HEIGHT; y++) for (int x = 0; x < IMAGE_WIDTH; x++) { pixdata[y][x] &= 14737632 | (31 << ((x % 3) << 3)); pixdata[y][x] += 986895 & (rn *= 16777619); } XPutImage(display, window, DefaultGC(display, DefaultScreen(display)), image, 0, 0, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT); XMapWindow(display, window); XFlush(display); sleep(1); } return 0; } Для того, чтобы закончить обработку работы с «mapdata» нужно ответить еще на два вопроса — что такое функция «b»: void b(int t, int x, int y, int c, int l) {
if ((x >= MAP_WIDTH || y >= MAP_HEIGHT || x < 0 || y < 0) || (mapdata[y][x] == l) || (mapdata[y][x] != c)) return; mapdata[y][x] = l; b(t, x - 1, y, c, l); b(t, x + 1, y, c, l); b(t, x, y - 1, c, l); b(t, x, y + 1, c, l); } И что происходит в блоке for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++) _texture_draw(((mapdata[y][x] >> 4) & 1) << 2 | (mapdata[y][x] & 3), x, y); Функция «b» Если внимательно присмотреться к функции «b», то можно заметить, что она до боли похожа на реализацию алгоритма flood_fill, что совпадает с ее теоретическим назначением — она «заливает» «провод» нужным состоянием, позволяя распространять фронт волны тока до конца провода. Переименуем ее и вынесем в блок «production ready». Flood fillSPL#include <X11/Xlib.h>
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> /* Код после рефакторинга */ /*! \brief Ширина изображения в пикселях */ #define IMAGE_WIDTH (1220) /*! \brief Высота изображения в пикселях */ #define IMAGE_HEIGHT (616) /*! \brief Сдвиг верхней грани карты от левой стороны */ #define IMAGE_SHIFTX (580) /*! \brief Количество текстур */ #define TEXTURE_COUNT (8) /*! \brief Ширина текстуры в пикселях */ #define TEXTURE_WIDTH (64) /*! \brief Высота текстур в пикселях */ #define TEXTURE_HEIGHT (40) /*! \brief Ширина грани текстуры в пикселях */ #define TEXTURE_TOP_WIDTH (64) /*! \brief Высота грани текстуры в пикселях */ #define TEXTURE_TOP_HEIGHT (32) /*! \brief Ширина эмулируемого поля */ #define MAP_WIDTH (19) /*! \brief Высота эмулируемого поля */ #define MAP_HEIGHT (19) /*! \brief Количество данные в файле-чертеже. Один байт добавлен для символа '\n' */ #define MAP_FILEDATA ((MAP_WIDTH + 1) * MAP_HEIGHT) /*! \brief Количество итераций во время расчета NOR-узлов */ #define MAP_ITERATIONS (20) /*! \brief Содержит структуры синтаксиса файла-конфигурации */ enum map_characters { MAPCHAR_WIRE = '.', /**< Провод (ASCII = 46) */ MAPCHAR_PLUS = '+', /**< Провод (есть ток) (ASCII = 43) */ MAPCHAR_MINUS = '-', /**< Провод (нет тока) (ASCII = 45) */ MAPCHAR_NOR = '>', /**< NOR-элемент (ASCII = 62) */ }; /*! \brief Бинарные данные пикселей изображения. * Типизированы к int для оперирования пикселями вместо каналов */ int pixdata[IMAGE_HEIGHT][IMAGE_WIDTH]; /*! \brief Текстуры блоков, отображаемых на поле */ int textures[TEXTURE_COUNT][TEXTURE_HEIGHT][TEXTURE_WIDTH] = { 0 }; /*! \brief Данные эмулируемого поля. * Один байт добавлен для упрощения обработки символа '\n' */ char mapdata[MAP_HEIGHT][MAP_WIDTH + 1]; /*! \brief Экземпляр дисплея Xlib */ Display * display; /*! \brief Экземпляр главного окна Xlib */ Window window; /*! \brief Изображение для вывода на экран */ XImage * image; /* \brief Обнуляет изображение, заполняя его черным цветом */ static void _image_reset(void) { for (int y = 0; y < IMAGE_HEIGHT; y++) for (int x = 0; x < IMAGE_WIDTH; x++) pixdata[y][x] = 0; } /*! \brief Отрисовывает текстуру на главном холсте по указанным координатам * \param[in] t Индекс текстуры * \param[in] x X координата тайла * \param[in] y Y координата тайла */ static void _texture_draw(int t, int x, int y) { for (int ty = 0; ty < TEXTURE_HEIGHT; ty++) for (int tx = 0; tx < TEXTURE_WIDTH; tx++) if (textures[t][ty][tx]) pixdata[ty + y * (TEXTURE_TOP_HEIGHT / 2) + x * (TEXTURE_TOP_HEIGHT / 2)] [tx + IMAGE_SHIFTX + x * (TEXTURE_TOP_WIDTH / 2) - y * TEXTURE_TOP_HEIGHT] = textures[t][ty][tx]; } /*! \brief Читает данные файла-чертежа и загружает их в карту * \param[in] filename Имя файла-чертежа */ static void _map_read(const char * filename) { int f = open(filename, 0); read(f, mapdata, MAP_FILEDATA); close(f); } /*! \brief Заменяет иллюстративные входы из файла-конфигурации на вход * в виде провода чтобы работала логика распространения фронта волны */ static void _map_wire_inputs(void) { for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) if ((x % 14 == 2) && (y % 4 == 3)) mapdata[y][x] = MAPCHAR_WIRE; } /*! \brief Производит заливку проводника нужным состоянием * \param[in] t Игнорируется, артефакт автогенерации кода * \param[in] x X-координата заливки * \param[in] y Y-координата заливки * \param[in] c Исходное состояние * \param[in] l Целевое состояние */ static void _map_fill(int t, int x, int y, int c, int l) { if ((x >= MAP_WIDTH || y >= MAP_HEIGHT || x < 0 || y < 0) || (mapdata[y][x] == l) || (mapdata[y][x] != c)) return; mapdata[y][x] = l; _map_fill(t, x - 1, y, c, l); _map_fill(t, x + 1, y, c, l); _map_fill(t, x, y - 1, c, l); _map_fill(t, x, y + 1, c, l); } /*! \brief Включает соответствующие входы схемы в зависимости от значения * счетчика. * \param[in] counter Счетчик */ static void _map_wire_counter(int counter) { _map_fill(0, 2, 3, mapdata[3][2], counter & 1 ? MAPCHAR_PLUS : MAPCHAR_MINUS); _map_fill(0, 2, 7, mapdata[7][2], counter & 2 ? MAPCHAR_PLUS : MAPCHAR_MINUS); _map_fill(0, 2, 11, mapdata[11][2], counter & 4 ? MAPCHAR_PLUS : MAPCHAR_MINUS); _map_fill(0, 2, 15, mapdata[15][2], counter & 8 ? MAPCHAR_PLUS : MAPCHAR_MINUS); } /*! \brief Проводит расчет выходного (результирующего) тока после NOR-узла */ static void _map_process_gates(void) { for (int i = 0; i < MAP_ITERATIONS; i++) for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) if (mapdata[y][x] == MAPCHAR_NOR) _map_fill(0, x + 1, y, mapdata[y][x + 1], !(mapdata[y - 1][x] == MAPCHAR_PLUS ? 1 : 0 || mapdata[y + 1][x] == MAPCHAR_PLUS ? 1 : 0) ? MAPCHAR_PLUS : MAPCHAR_MINUS); } /* Код до рефакторинга */ int _x[] = { 15117427, 8413248, 5878632, 13027014, 1 }; int rn = 2166136261; void *T; void e(int t, int x, int y, int c, int l) { if ((x >= TEXTURE_WIDTH || y >= TEXTURE_HEIGHT || x < 0 || y < 0) || (textures[t][y][x] == l) || (textures[t][y][x] != c)) return; textures[t][y][x] = l; e(t, x - 1, y, c, l); e(t, x + 1, y, c, l); e(t, x, y - 1, c, l); e(t, x, y + 1, c, l); } void k(int t, int x, int y, int c, int l) { while (c--) { textures[t][y][x++] = l; textures[t][y++][x++] = l; } } void r(int t, int x, int y, int c, int l) { while (c--) { textures[t][y][x--] = l; textures[t][y++][x--] = l; } } void u(int t, int x, int y, int c, int l) { while (c--) textures[t][y++][x] = l; } char *z[8] = { (char[]) {4}, (char[]) {6, 1 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0, 21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63, 21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6, 1 * 4 + 0, 3, 31, 8 - 0, 6, 1 * 4 + 2, 3, 33, 38 - 0, 6, 1 * 4 + 3, 3, 30, 38 - 0, 4}, (char[]) {5, 1}, (char[]) {6, 0 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0, 21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63, 21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6, 0 * 4 + 0, 3, 31, 8 - 0, 6, 0 * 4 + 2, 3, 33, 38 - 0, 6, 0 * 4 + 3, 3, 30, 38 - 0, 4}, (char[]) {4}, (char[]) {4}, (char[]) {6, 2 * 4 + 1, 0 + 0, 32 - 0, 5 - 3, 16, 0, 0, 21 + 0 * 3 - 3, 16, 1, 63, 21 + 0 * 3 - 3, 16, 2, 63 * 0, 21 - 3, 4, 2, 31 + 0, 36 - 3, 4, 0 + 1, 32 - 1, 5 - 3, 16, 0, 0, 21 + 1 * 3 - 3, 16, 1, 63, 21 + 1 * 3 - 3, 16, 2, 63 * 1, 21 - 3, 4, 2, 31 + 1, 36 - 3, 4, 6, 2 * 4 + 0, 3, 31, 8 - 3, 6, 2 * 4 + 2, 3, 33, 38 - 3, 6, 2 * 4 + 3, 3, 30, 38 - 3, 4}, (char[]) {6, 13, 0, 32, 9 + 0, 12 - 0, 0, 8 + 0 + 0, 21, 12 - 0, 1, 31, 9 + 0, 12 - 0, 1, 55 - 0 - 0, 21, 12 - 0, 0, 32, 9 + 3, 12 - 3, 0, 8 + 3 + 3, 21, 12 - 3, 1, 31, 9 + 3, 12 - 3, 1, 55 - 3 - 3, 21, 12 - 3, 6, 14 + 0, 2, 31 + 0, 13, 4, 1 - 0, 31 + 0, 16, 7, 3, 30 + 3 * 0, 14, 6, 12 + 0 * 4, 3, 32 - 0, 11 + 0 * 8, 6, 14 + 1, 2, 31 + 1, 13, 4, 1 - 1, 31 + 1, 16, 7, 3, 30 + 3 * 1, 14, 6, 12 + 1 * 4, 3, 32 - 1, 11 + 1 * 8, 4} }; int main(int a, char *s[]) { display = XOpenDisplay(0); window = XCreateSimpleWindow(display, RootWindow(display, DefaultScreen(display)), 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, 1, 0, 0); image = XCreateImage(display, DefaultVisual(display, DefaultScreen(display)), DefaultDepth(display, DefaultScreen(display)), 2, 0, (char *) pixdata, IMAGE_WIDTH, IMAGE_HEIGHT, 32, 0); for (int i = 0; i < 8; i++) { char *p = z[i]; int c = 0; while (*p != 4) { switch (*p) { case 0: k(i, p[1], p[2], p[3], c); p += 4; break; case 1: r(i, p[1], p[2], p[3], c); p += 4; break; case 2: u(i, p[1], p[2], p[3], c); p += 4; break; case 3: e(i, p[1] , p[2], 0, c); p += 3; break; case 5: p = z[p[1]]; break; case 6: c = _x[p[1] / 4] - 1643277 * (p[1] % 4); p += 2; break; } } } while (a++) { _map_read(s[1]); _map_wire_inputs(); _map_wire_counter(a); _map_process_gates(); _image_reset(); for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) _texture_draw(((mapdata[y][x] >> 4) & 1) << 2 | (mapdata[y][x] & 3), x, y); for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) if ((x % 14 == 2) && (y % 4 == 3)) _texture_draw(7, x, y); for (int y = 0; y < IMAGE_HEIGHT; y++) for (int x = 0; x < IMAGE_WIDTH; x++) { pixdata[y][x] &= 14737632 | (31 << ((x % 3) << 3)); pixdata[y][x] += 986895 & (rn *= 16777619); } XPutImage(display, window, DefaultGC(display, DefaultScreen(display)), image, 0, 0, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT); XMapWindow(display, window); XFlush(display); sleep(1); } return 0; } Странный блок Теперь осталось разобрать, что происходит в строке _texture_draw(((mapdata[y][x] >> 4) & 1) << 2 | (mapdata[y][x] & 3), x, y);
Так как мы уже построили полную таблицу состояний, которые могут находиться внутри mapdata то мы можем получить все выходные значения первого параметра: enum int Результат MAPCHAR_WIRE 46 2 MAPCHAR_PLUS 43 3 MAPCHAR_MINUS 45 1 MAPCHAR_NOR 62 6 MAPCHAR_EMPTY 32 0 Ага, то есть по итогам этого преобразования мы получаем индекс текстуры в массиве текстур. Для того, чтобы продумать механизм генерации из читаемого и ассоциативно понятного набора символов в индексы текстур понадобилась пара часов. Я выписал символы, которые могли означать провода и NOR-элементы а потом расписав их двоичные значения обводил кружочками уникальные области. Кроме текущего был второй вариант с более сложными вычислениями, но он длиннее, следовательно не подошел по лимиту токенов.
Замечательно, это дает нам возможность вычленить еще одну функцию и объявить enum для каждой текстуры: После очередного рефакторинга:SPL#include <X11/Xlib.h>
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> /* Код после рефакторинга */ /*! \brief Ширина изображения в пикселях */ #define IMAGE_WIDTH (1220) /*! \brief Высота изображения в пикселях */ #define IMAGE_HEIGHT (616) /*! \brief Сдвиг верхней грани карты от левой стороны */ #define IMAGE_SHIFTX (580) /*! \brief Количество текстур */ #define TEXTURE_COUNT (8) /*! \brief Ширина текстуры в пикселях */ #define TEXTURE_WIDTH (64) /*! \brief Высота текстур в пикселях */ #define TEXTURE_HEIGHT (40) /*! \brief Ширина грани текстуры в пикселях */ #define TEXTURE_TOP_WIDTH (64) /*! \brief Высота грани текстуры в пикселях */ #define TEXTURE_TOP_HEIGHT (32) /*! \brief Ширина эмулируемого поля */ #define MAP_WIDTH (19) /*! \brief Высота эмулируемого поля */ #define MAP_HEIGHT (19) /*! \brief Количество данные в файле-чертеже. Один байт добавлен для символа '\n' */ #define MAP_FILEDATA ((MAP_WIDTH + 1) * MAP_HEIGHT) /*! \brief Количество итераций во время расчета NOR-узлов */ #define MAP_ITERATIONS (20) /*! \brief Содержит структуры синтаксиса файла-конфигурации */ enum map_characters { MAPCHAR_WIRE = '.', /**< Провод (ASCII = 46) */ MAPCHAR_PLUS = '+', /**< Провод (есть ток) (ASCII = 43) */ MAPCHAR_MINUS = '-', /**< Провод (нет тока) (ASCII = 45) */ MAPCHAR_NOR = '>', /**< NOR-элемент (ASCII = 62) */ MAPCHAR_EMPTY = ' ', /**< Пустой блок (ASCII = 32) */ }; /*! \brief Содержит индексы текстур */ enum textures_indexes { TEXINDEX_EMPTY = (0), /**< Индекс пустой текстуры */ TEXINDEX_MINUS = (1), /**< Индекс текстуры "выключенного провода" */ TEXINDEX_WIRE = (2), /**< Индекс текстуры нейтрального провода */ TEXINDEX_PLUS = (3), /**< Индекс текстуры "включенного" провода */ /**/ TEXINDEX_NOR = (6) /**< Индекс текстуры NOR-элемента */ }; /*! \brief Аргументы программы */ enum program_arguments { ARG_PROGRAM, /**< Имя самой программы */ ARG_BLUEPRINT /**< Имя файла-чертежа */ }; /*! \brief Бинарные данные пикселей изображения. * Типизированы к int для оперирования пикселями вместо каналов */ int pixdata[IMAGE_HEIGHT][IMAGE_WIDTH]; /*! \brief Текстуры блоков, отображаемых на поле */ int textures[TEXTURE_COUNT][TEXTURE_HEIGHT][TEXTURE_WIDTH] = { 0 }; /*! \brief Данные эмулируемого поля. * Один байт добавлен для упрощения обработки символа '\n' */ char mapdata[MAP_HEIGHT][MAP_WIDTH + 1]; /*! \brief Экземпляр дисплея Xlib */ Display * display; /*! \brief Экземпляр главного окна Xlib */ Window window; /*! \brief Изображение для вывода на экран */ XImage * image; /* \brief Обнуляет изображение, заполняя его черным цветом */ static void _image_reset(void) { for (int y = 0; y < IMAGE_HEIGHT; y++) for (int x = 0; x < IMAGE_WIDTH; x++) pixdata[y][x] = 0; } /*! \brief Конвертирует символ из чертежа в индекс текстуры * \param[in] elem Символ чертежа * \return Индекс текстуры */ static int _map2texture(char elem) { return ((elem >> 4) & 1) << 2 | (elem & 3); } /*! \brief Собирает изображение из отдельных тайлов согласно карте */ static void _image_compile(void) { for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) _texture_draw(_map2texture(mapdata[y][x]), x, y); } /*! \brief Рисует изображение на экране */ static void _image_draw(void) { XPutImage(display, window, DefaultGC(display, DefaultScreen(display)), image, 0, 0, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT); XMapWindow(display, window); XFlush(display); } /*! \brief Отрисовывает текстуру на главном холсте по указанным координатам * \param[in] t Индекс текстуры * \param[in] x X координата тайла * \param[in] y Y координата тайла */ static void _texture_draw(int t, int x, int y) { for (int ty = 0; ty < TEXTURE_HEIGHT; ty++) for (int tx = 0; tx < TEXTURE_WIDTH; tx++) if (textures[t][ty][tx]) pixdata[ty + y * (TEXTURE_TOP_HEIGHT / 2) + x * (TEXTURE_TOP_HEIGHT / 2)] [tx + IMAGE_SHIFTX + x * (TEXTURE_TOP_WIDTH / 2) - y * TEXTURE_TOP_HEIGHT] = textures[t][ty][tx]; } /*! \brief Читает данные файла-чертежа и загружает их в карту * \param[in] filename Имя файла-чертежа */ static void _map_read(const char * filename) { int f = open(filename, 0); read(f, mapdata, MAP_FILEDATA); close(f); } /*! \brief Заменяет иллюстративные входы из файла-конфигурации на вход * в виде провода чтобы работала логика распространения фронта волны */ static void _map_wire_inputs(void) { for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) if ((x % 14 == 2) && (y % 4 == 3)) mapdata[y][x] = MAPCHAR_WIRE; } /*! \brief Производит заливку проводника нужным состоянием * \param[in] t Игнорируется, артефакт автогенерации кода * \param[in] x X-координата заливки * \param[in] y Y-координата заливки * \param[in] c Исходное состояние * \param[in] l Целевое состояние */ static void _map_fill(int t, int x, int y, int c, int l) { if ((x >= MAP_WIDTH || y >= MAP_HEIGHT || x < 0 || y < 0) || (mapdata[y][x] == l) || (mapdata[y][x] != c)) return; mapdata[y][x] = l; _map_fill(t, x - 1, y, c, l); _map_fill(t, x + 1, y, c, l); _map_fill(t, x, y - 1, c, l); _map_fill(t, x, y + 1, c, l); } /*! \brief Включает соответствующие входы схемы в зависимости от значения * счетчика. * \param[in] counter Счетчик */ static void _map_wire_counter(int counter) { _map_fill(0, 2, 3, mapdata[3][2], counter & 1 ? MAPCHAR_PLUS : MAPCHAR_MINUS); _map_fill(0, 2, 7, mapdata[7][2], counter & 2 ? MAPCHAR_PLUS : MAPCHAR_MINUS); _map_fill(0, 2, 11, mapdata[11][2], counter & 4 ? MAPCHAR_PLUS : MAPCHAR_MINUS); _map_fill(0, 2, 15, mapdata[15][2], counter & 8 ? MAPCHAR_PLUS : MAPCHAR_MINUS); } /*! \brief Проводит расчет выходного (результирующего) тока после NOR-узла */ static void _map_process_gates(void) { for (int i = 0; i < MAP_ITERATIONS; i++) for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) if (mapdata[y][x] == MAPCHAR_NOR) _map_fill(0, x + 1, y, mapdata[y][x + 1], !(mapdata[y - 1][x] == MAPCHAR_PLUS ? 1 : 0 || mapdata[y + 1][x] == MAPCHAR_PLUS ? 1 : 0) ? MAPCHAR_PLUS : MAPCHAR_MINUS); } /* Код до рефакторинга */ int _x[] = { 15117427, 8413248, 5878632, 13027014, 1 }; int rn = 2166136261; void *T; void e(int t, int x, int y, int c, int l) { if ((x >= TEXTURE_WIDTH || y >= TEXTURE_HEIGHT || x < 0 || y < 0) || (textures[t][y][x] == l) || (textures[t][y][x] != c)) return; textures[t][y][x] = l; e(t, x - 1, y, c, l); e(t, x + 1, y, c, l); e(t, x, y - 1, c, l); e(t, x, y + 1, c, l); } void k(int t, int x, int y, int c, int l) { while (c--) { textures[t][y][x++] = l; textures[t][y++][x++] = l; } } void r(int t, int x, int y, int c, int l) { while (c--) { textures[t][y][x--] = l; textures[t][y++][x--] = l; } } void u(int t, int x, int y, int c, int l) { while (c--) textures[t][y++][x] = l; } char *z[8] = { (char[]) {4}, (char[]) {6, 1 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0, 21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63, 21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6, 1 * 4 + 0, 3, 31, 8 - 0, 6, 1 * 4 + 2, 3, 33, 38 - 0, 6, 1 * 4 + 3, 3, 30, 38 - 0, 4}, (char[]) {5, 1}, (char[]) {6, 0 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0, 21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63, 21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6, 0 * 4 + 0, 3, 31, 8 - 0, 6, 0 * 4 + 2, 3, 33, 38 - 0, 6, 0 * 4 + 3, 3, 30, 38 - 0, 4}, (char[]) {4}, (char[]) {4}, (char[]) {6, 2 * 4 + 1, 0 + 0, 32 - 0, 5 - 3, 16, 0, 0, 21 + 0 * 3 - 3, 16, 1, 63, 21 + 0 * 3 - 3, 16, 2, 63 * 0, 21 - 3, 4, 2, 31 + 0, 36 - 3, 4, 0 + 1, 32 - 1, 5 - 3, 16, 0, 0, 21 + 1 * 3 - 3, 16, 1, 63, 21 + 1 * 3 - 3, 16, 2, 63 * 1, 21 - 3, 4, 2, 31 + 1, 36 - 3, 4, 6, 2 * 4 + 0, 3, 31, 8 - 3, 6, 2 * 4 + 2, 3, 33, 38 - 3, 6, 2 * 4 + 3, 3, 30, 38 - 3, 4}, (char[]) {6, 13, 0, 32, 9 + 0, 12 - 0, 0, 8 + 0 + 0, 21, 12 - 0, 1, 31, 9 + 0, 12 - 0, 1, 55 - 0 - 0, 21, 12 - 0, 0, 32, 9 + 3, 12 - 3, 0, 8 + 3 + 3, 21, 12 - 3, 1, 31, 9 + 3, 12 - 3, 1, 55 - 3 - 3, 21, 12 - 3, 6, 14 + 0, 2, 31 + 0, 13, 4, 1 - 0, 31 + 0, 16, 7, 3, 30 + 3 * 0, 14, 6, 12 + 0 * 4, 3, 32 - 0, 11 + 0 * 8, 6, 14 + 1, 2, 31 + 1, 13, 4, 1 - 1, 31 + 1, 16, 7, 3, 30 + 3 * 1, 14, 6, 12 + 1 * 4, 3, 32 - 1, 11 + 1 * 8, 4} }; int main(int argc, char * args[]) { display = XOpenDisplay(0); window = XCreateSimpleWindow(display, RootWindow(display, DefaultScreen(display)), 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, 1, 0, 0); image = XCreateImage(display, DefaultVisual(display, DefaultScreen(display)), DefaultDepth(display, DefaultScreen(display)), 2, 0, (char *) pixdata, IMAGE_WIDTH, IMAGE_HEIGHT, 32, 0); for (int i = 0; i < 8; i++) { char *p = z[i]; int c = 0; while (*p != 4) { switch (*p) { case 0: k(i, p[1], p[2], p[3], c); p += 4; break; case 1: r(i, p[1], p[2], p[3], c); p += 4; break; case 2: u(i, p[1], p[2], p[3], c); p += 4; break; case 3: e(i, p[1] , p[2], 0, c); p += 3; break; case 5: p = z[p[1]]; break; case 6: c = _x[p[1] / 4] - 1643277 * (p[1] % 4); p += 2; break; } } } unsigned int counter = 0; while (counter++) { _map_read(args[ARG_BLUEPRINT]); _map_wire_inputs(); _map_wire_counter(counter); _map_process_gates(); _image_reset(); _image_compile(); for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) if ((x % 14 == 2) && (y % 4 == 3)) _texture_draw(7, x, y); for (int y = 0; y < IMAGE_HEIGHT; y++) for (int x = 0; x < IMAGE_WIDTH; x++) { pixdata[y][x] &= 14737632 | (31 << ((x % 3) << 3)); pixdata[y][x] += 986895 & (rn *= 16777619); } _image_draw(); sleep(1); } return 0; } Текстура #7 Если присмотреться к массиву «z», то можно заметить, что он содержит 8 блоков данных, как константа количества текстур. И в нем даже есть два пробела на позициях 4 и 5, прямо как в нашем enum, скорее всего это данные текстур. Однако он содержит 8 изображений, а мы смогли «открыть» только 7. Однако, если мы будем достаточно внимательными, то мы сможем заметить, что есть участок кода, который рисует конкретно 7ю текстуру: for (int y = 0; y < MAP_HEIGHT; y++)
for (int x = 0; x < MAP_WIDTH; x++) if ((x % 14 == 2) && (y % 4 == 3)) _texture_draw(7, x, y); По позиции уже можно догадаться, что это, но закомментируем эти строки и запустим приложение: До: После: Теперь мы точно знаем, что это текстура отверстия на плате и можем добавить ее в список enum, вынеся её отрисовку в отдельную функцию: Все текстуры в сборе:SPL#include <X11/Xlib.h>
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> /* Код после рефакторинга */ /*! \brief Ширина изображения в пикселях */ #define IMAGE_WIDTH (1220) /*! \brief Высота изображения в пикселях */ #define IMAGE_HEIGHT (616) /*! \brief Сдвиг верхней грани карты от левой стороны */ #define IMAGE_SHIFTX (580) /*! \brief Количество текстур */ #define TEXTURE_COUNT (8) /*! \brief Ширина текстуры в пикселях */ #define TEXTURE_WIDTH (64) /*! \brief Высота текстур в пикселях */ #define TEXTURE_HEIGHT (40) /*! \brief Ширина грани текстуры в пикселях */ #define TEXTURE_TOP_WIDTH (64) /*! \brief Высота грани текстуры в пикселях */ #define TEXTURE_TOP_HEIGHT (32) /*! \brief Ширина эмулируемого поля */ #define MAP_WIDTH (19) /*! \brief Высота эмулируемого поля */ #define MAP_HEIGHT (19) /*! \brief Количество данные в файле-чертеже. Один байт добавлен для символа '\n' */ #define MAP_FILEDATA ((MAP_WIDTH + 1) * MAP_HEIGHT) /*! \brief Количество итераций во время расчета NOR-узлов */ #define MAP_ITERATIONS (20) /*! \brief Содержит структуры синтаксиса файла-конфигурации */ enum map_characters { MAPCHAR_WIRE = '.', /**< Провод (ASCII = 46) */ MAPCHAR_PLUS = '+', /**< Провод (есть ток) (ASCII = 43) */ MAPCHAR_MINUS = '-', /**< Провод (нет тока) (ASCII = 45) */ MAPCHAR_NOR = '>', /**< NOR-элемент (ASCII = 62) */ MAPCHAR_EMPTY = ' ', /**< Пустой блок (ASCII = 32) */ }; /*! \brief Содержит индексы текстур */ enum textures_indexes { TEXINDEX_EMPTY = (0), /**< Индекс пустой текстуры */ TEXINDEX_MINUS = (1), /**< Индекс текстуры "выключенного провода" */ TEXINDEX_WIRE = (2), /**< Индекс текстуры нейтрального провода */ TEXINDEX_PLUS = (3), /**< Индекс текстуры "включенного" провода */ /**/ TEXINDEX_NOR = (6), /**< Индекс текстуры NOR-элемента */ TEXINDEX_HOLE = (7) /**< Индекс текстуры отверстия на плате */ }; /*! \brief Аргументы программы */ enum program_arguments { ARG_PROGRAM, /**< Имя самой программы */ ARG_BLUEPRINT /**< Имя файла-чертежа */ }; /*! \brief Бинарные данные пикселей изображения. * Типизированы к int для оперирования пикселями вместо каналов */ int pixdata[IMAGE_HEIGHT][IMAGE_WIDTH]; /*! \brief Текстуры блоков, отображаемых на поле */ int textures[TEXTURE_COUNT][TEXTURE_HEIGHT][TEXTURE_WIDTH] = { 0 }; /*! \brief Данные эмулируемого поля. * Один байт добавлен для упрощения обработки символа '\n' */ char mapdata[MAP_HEIGHT][MAP_WIDTH + 1]; /*! \brief Экземпляр дисплея Xlib */ Display * display; /*! \brief Экземпляр главного окна Xlib */ Window window; /*! \brief Изображение для вывода на экран */ XImage * image; static void _texture_draw(int t, int x, int y); /*! \brief Создает изображение и сопутствующие сущности */ static void _image_create(void) { display = XOpenDisplay(0); window = XCreateSimpleWindow(display, RootWindow(display, DefaultScreen(display)), 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, 1, 0, 0); image = XCreateImage(display, DefaultVisual(display, DefaultScreen(display)), DefaultDepth(display, DefaultScreen(display)), 2, 0, (char *) pixdata, IMAGE_WIDTH, IMAGE_HEIGHT, 32, 0); } /* \brief Обнуляет изображение, заполняя его черным цветом */ static void _image_reset(void) { for (int y = 0; y < IMAGE_HEIGHT; y++) for (int x = 0; x < IMAGE_WIDTH; x++) pixdata[y][x] = 0; } /*! \brief Конвертирует символ из чертежа в индекс текстуры * \param[in] elem Символ чертежа * \return Индекс текстуры */ static int _map2texture(char elem) { return ((elem >> 4) & 1) << 2 | (elem & 3); } /*! \brief Собирает изображение из отдельных тайлов согласно карте */ static void _image_compile(void) { for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) _texture_draw(_map2texture(mapdata[y][x]), x, y); } /*! \brief Рисует изображение на экране */ static void _image_draw(void) { XPutImage(display, window, DefaultGC(display, DefaultScreen(display)), image, 0, 0, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT); XMapWindow(display, window); XFlush(display); } /*! \brief Рисует отверстия на печатной плате */ static void _image_drill(void) { for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) if ((x % 14 == 2) && (y % 4 == 3)) _texture_draw(TEXINDEX_HOLE, x, y); } /*! \brief Отрисовывает текстуру на главном холсте по указанным координатам * \param[in] t Индекс текстуры * \param[in] x X координата тайла * \param[in] y Y координата тайла */ static void _texture_draw(int t, int x, int y) { for (int ty = 0; ty < TEXTURE_HEIGHT; ty++) for (int tx = 0; tx < TEXTURE_WIDTH; tx++) if (textures[t][ty][tx]) pixdata[ty + y * (TEXTURE_TOP_HEIGHT / 2) + x * (TEXTURE_TOP_HEIGHT / 2)] [tx + IMAGE_SHIFTX + x * (TEXTURE_TOP_WIDTH / 2) - y * TEXTURE_TOP_HEIGHT] = textures[t][ty][tx]; } /*! \brief Читает данные файла-чертежа и загружает их в карту * \param[in] filename Имя файла-чертежа */ static void _map_read(const char * filename) { int f = open(filename, 0); read(f, mapdata, MAP_FILEDATA); close(f); } /*! \brief Заменяет иллюстративные входы из файла-конфигурации на вход * в виде провода чтобы работала логика распространения фронта волны */ static void _map_wire_inputs(void) { for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) if ((x % 14 == 2) && (y % 4 == 3)) mapdata[y][x] = MAPCHAR_WIRE; } /*! \brief Производит заливку проводника нужным состоянием * \param[in] t Игнорируется, артефакт автогенерации кода * \param[in] x X-координата заливки * \param[in] y Y-координата заливки * \param[in] c Исходное состояние * \param[in] l Целевое состояние */ static void _map_fill(int t, int x, int y, int c, int l) { if ((x >= MAP_WIDTH || y >= MAP_HEIGHT || x < 0 || y < 0) || (mapdata[y][x] == l) || (mapdata[y][x] != c)) return; mapdata[y][x] = l; _map_fill(t, x - 1, y, c, l); _map_fill(t, x + 1, y, c, l); _map_fill(t, x, y - 1, c, l); _map_fill(t, x, y + 1, c, l); } /*! \brief Включает соответствующие входы схемы в зависимости от значения * счетчика. * \param[in] counter Счетчик */ static void _map_wire_counter(int counter) { _map_fill(0, 2, 3, mapdata[3][2], counter & 1 ? MAPCHAR_PLUS : MAPCHAR_MINUS); _map_fill(0, 2, 7, mapdata[7][2], counter & 2 ? MAPCHAR_PLUS : MAPCHAR_MINUS); _map_fill(0, 2, 11, mapdata[11][2], counter & 4 ? MAPCHAR_PLUS : MAPCHAR_MINUS); _map_fill(0, 2, 15, mapdata[15][2], counter & 8 ? MAPCHAR_PLUS : MAPCHAR_MINUS); } /*! \brief Проводит расчет выходного (результирующего) тока после NOR-узла */ static void _map_process_gates(void) { for (int i = 0; i < MAP_ITERATIONS; i++) for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) if (mapdata[y][x] == MAPCHAR_NOR) _map_fill(0, x + 1, y, mapdata[y][x + 1], !(mapdata[y - 1][x] == MAPCHAR_PLUS ? 1 : 0 || mapdata[y + 1][x] == MAPCHAR_PLUS ? 1 : 0) ? MAPCHAR_PLUS : MAPCHAR_MINUS); } /* Код до рефакторинга */ int _x[] = { 15117427, 8413248, 5878632, 13027014, 1 }; int rn = 2166136261; void *T; void e(int t, int x, int y, int c, int l) { if ((x >= TEXTURE_WIDTH || y >= TEXTURE_HEIGHT || x < 0 || y < 0) || (textures[t][y][x] == l) || (textures[t][y][x] != c)) return; textures[t][y][x] = l; e(t, x - 1, y, c, l); e(t, x + 1, y, c, l); e(t, x, y - 1, c, l); e(t, x, y + 1, c, l); } void k(int t, int x, int y, int c, int l) { while (c--) { textures[t][y][x++] = l; textures[t][y++][x++] = l; } } void r(int t, int x, int y, int c, int l) { while (c--) { textures[t][y][x--] = l; textures[t][y++][x--] = l; } } void u(int t, int x, int y, int c, int l) { while (c--) textures[t][y++][x] = l; } char *z[8] = { (char[]) {4}, (char[]) {6, 1 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0, 21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63, 21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6, 1 * 4 + 0, 3, 31, 8 - 0, 6, 1 * 4 + 2, 3, 33, 38 - 0, 6, 1 * 4 + 3, 3, 30, 38 - 0, 4}, (char[]) {5, 1}, (char[]) {6, 0 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0, 21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63, 21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6, 0 * 4 + 0, 3, 31, 8 - 0, 6, 0 * 4 + 2, 3, 33, 38 - 0, 6, 0 * 4 + 3, 3, 30, 38 - 0, 4}, (char[]) {4}, (char[]) {4}, (char[]) {6, 2 * 4 + 1, 0 + 0, 32 - 0, 5 - 3, 16, 0, 0, 21 + 0 * 3 - 3, 16, 1, 63, 21 + 0 * 3 - 3, 16, 2, 63 * 0, 21 - 3, 4, 2, 31 + 0, 36 - 3, 4, 0 + 1, 32 - 1, 5 - 3, 16, 0, 0, 21 + 1 * 3 - 3, 16, 1, 63, 21 + 1 * 3 - 3, 16, 2, 63 * 1, 21 - 3, 4, 2, 31 + 1, 36 - 3, 4, 6, 2 * 4 + 0, 3, 31, 8 - 3, 6, 2 * 4 + 2, 3, 33, 38 - 3, 6, 2 * 4 + 3, 3, 30, 38 - 3, 4}, (char[]) {6, 13, 0, 32, 9 + 0, 12 - 0, 0, 8 + 0 + 0, 21, 12 - 0, 1, 31, 9 + 0, 12 - 0, 1, 55 - 0 - 0, 21, 12 - 0, 0, 32, 9 + 3, 12 - 3, 0, 8 + 3 + 3, 21, 12 - 3, 1, 31, 9 + 3, 12 - 3, 1, 55 - 3 - 3, 21, 12 - 3, 6, 14 + 0, 2, 31 + 0, 13, 4, 1 - 0, 31 + 0, 16, 7, 3, 30 + 3 * 0, 14, 6, 12 + 0 * 4, 3, 32 - 0, 11 + 0 * 8, 6, 14 + 1, 2, 31 + 1, 13, 4, 1 - 1, 31 + 1, 16, 7, 3, 30 + 3 * 1, 14, 6, 12 + 1 * 4, 3, 32 - 1, 11 + 1 * 8, 4} }; int main(int argc, char * args[]) { _image_create(); for (int i = 0; i < 8; i++) { char *p = z[i]; int c = 0; while (*p != 4) { switch (*p) { case 0: k(i, p[1], p[2], p[3], c); p += 4; break; case 1: r(i, p[1], p[2], p[3], c); p += 4; break; case 2: u(i, p[1], p[2], p[3], c); p += 4; break; case 3: e(i, p[1] , p[2], 0, c); p += 3; break; case 5: p = z[p[1]]; break; case 6: c = _x[p[1] / 4] - 1643277 * (p[1] % 4); p += 2; break; } } } unsigned int counter = 1; while (counter++) { _map_read(args[ARG_BLUEPRINT]); _map_wire_inputs(); _map_wire_counter(counter); _map_process_gates(); _image_reset(); _image_compile(); _image_drill(); for (int y = 0; y < IMAGE_HEIGHT; y++) for (int x = 0; x < IMAGE_WIDTH; x++) { pixdata[y][x] &= 14737632 | (31 << ((x % 3) << 3)); pixdata[y][x] += 986895 & (rn *= 16777619); } _image_draw(); sleep(1); } return 0; } Шейдеры Котики из предыдущей статьи обещали шейдеры. И они тут есть. А где код, который отвечает за их обработку? _image_reset(); // Рано
_image_compile(); // Рано _image_drill(); // Рано for (int y = 0; y < IMAGE_HEIGHT; y++) for (int x = 0; x < IMAGE_WIDTH; x++) { pixdata[y][x] &= 14737632 | (31 << ((x % 3) << 3)); pixdata[y][x] += 986895 & (rn *= 16777619); } _image_draw(); // Поздно Методом исключения понимаем, что зашумленность экрана и эффект LCD-монитора дают эти две строчки. А как они работают? Начнем с последней: pixdata[y][x] += 986895 & (rn *= 16777619);
К каждому пикселю прибавляется значение 986895 (что в hex-варианты выглядит как 0x0f0f0f), которое перед этим было посредством операции битовое-И совмещено с результатом умножения rn на 16777619. Если бы rn было ренератором случайных чисел, то это создало бы зернистый «шум» на экране в пределах 16 градаций по каждому каналу. И раз шум появляется, значит rn и есть генератор случайных чисел. Но как это достигается? int rn = 2166136261;
В самом начале программы переменная rn инициализируется числом 2166136261. А на каждой итерации пикселя умножается на 16777619. Это ничто иное, как генератор псевдослучайных чисел. Вот только вместо линейного хорошо изученного генератора используется алгоритм хэширования FNV без шага с XOR, поскольку нам не нужен конечный результат. Остается понять, как работает предыдущая строка: pixdata[y][x] &= 14737632 | (31 << ((x % 3) << 3));
Переведем число 14737632 в шестнадцатиричный формат, так как мы работаем с однобайтными каналами света: 0xe0e0e0. А теперь приняв x равным от 0 до 3 проведем соответствующие вычисления: 0xe0e0e0 | (31 << ((0 % 3) << 3)) = e0e0ff
0xe0e0e0 | (31 << ((1 % 3) << 3)) = e0ffe0 0xe0e0e0 | (31 << ((2 % 3) << 3)) = ffe0e0 Если теперь эти маски применить при помощи операции "&" к цвету пикселя, то мы получим, соответственно, приглушенные R и G, R и B, G и B каналы, что будет выглядеть как эффект от LCD-монитора: Выделим их в отдельные функции:SPL#include <X11/Xlib.h>
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> /* Код после рефакторинга */ /*! \brief Ширина изображения в пикселях */ #define IMAGE_WIDTH (1220) /*! \brief Высота изображения в пикселях */ #define IMAGE_HEIGHT (616) /*! \brief Сдвиг верхней грани карты от левой стороны */ #define IMAGE_SHIFTX (580) /*! \brief Количество текстур */ #define TEXTURE_COUNT (8) /*! \brief Ширина текстуры в пикселях */ #define TEXTURE_WIDTH (64) /*! \brief Высота текстур в пикселях */ #define TEXTURE_HEIGHT (40) /*! \brief Ширина грани текстуры в пикселях */ #define TEXTURE_TOP_WIDTH (64) /*! \brief Высота грани текстуры в пикселях */ #define TEXTURE_TOP_HEIGHT (32) /*! \brief Ширина эмулируемого поля */ #define MAP_WIDTH (19) /*! \brief Высота эмулируемого поля */ #define MAP_HEIGHT (19) /*! \brief Количество данные в файле-чертеже. Один байт добавлен для символа '\n' */ #define MAP_FILEDATA ((MAP_WIDTH + 1) * MAP_HEIGHT) /*! \brief Количество итераций во время расчета NOR-узлов */ #define MAP_ITERATIONS (20) /*! \brief Содержит структуры синтаксиса файла-конфигурации */ enum map_characters { MAPCHAR_WIRE = '.', /**< Провод (ASCII = 46) */ MAPCHAR_PLUS = '+', /**< Провод (есть ток) (ASCII = 43) */ MAPCHAR_MINUS = '-', /**< Провод (нет тока) (ASCII = 45) */ MAPCHAR_NOR = '>', /**< NOR-элемент (ASCII = 62) */ MAPCHAR_EMPTY = ' ', /**< Пустой блок (ASCII = 32) */ }; /*! \brief Содержит индексы текстур */ enum textures_indexes { TEXINDEX_EMPTY = (0), /**< Индекс пустой текстуры */ TEXINDEX_MINUS = (1), /**< Индекс текстуры "выключенного провода" */ TEXINDEX_WIRE = (2), /**< Индекс текстуры нейтрального провода */ TEXINDEX_PLUS = (3), /**< Индекс текстуры "включенного" провода */ /**/ TEXINDEX_NOR = (6), /**< Индекс текстуры NOR-элемента */ TEXINDEX_HOLE = (7) /**< Индекс текстуры отверстия на плате */ }; /*! \brief Аргументы программы */ enum program_arguments { ARG_PROGRAM, /**< Имя самой программы */ ARG_BLUEPRINT /**< Имя файла-чертежа */ }; /*! \brief Бинарные данные пикселей изображения. * Типизированы к int для оперирования пикселями вместо каналов */ int pixdata[IMAGE_HEIGHT][IMAGE_WIDTH]; /*! \brief Текстуры блоков, отображаемых на поле */ int textures[TEXTURE_COUNT][TEXTURE_HEIGHT][TEXTURE_WIDTH] = { 0 }; /*! \brief Данные эмулируемого поля. * Один байт добавлен для упрощения обработки символа '\n' */ char mapdata[MAP_HEIGHT][MAP_WIDTH + 1]; /*! \brief Зерно генератора случайных чисел */ int random = 2166136261; /*! \brief Экземпляр дисплея Xlib */ Display * display; /*! \brief Экземпляр главного окна Xlib */ Window window; /*! \brief Изображение для вывода на экран */ XImage * image; static void _texture_draw(int t, int x, int y); /*! \brief Создает изображение и сопутствующие сущности */ static void _image_create(void) { display = XOpenDisplay(0); window = XCreateSimpleWindow(display, RootWindow(display, DefaultScreen(display)), 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, 1, 0, 0); image = XCreateImage(display, DefaultVisual(display, DefaultScreen(display)), DefaultDepth(display, DefaultScreen(display)), 2, 0, (char *) pixdata, IMAGE_WIDTH, IMAGE_HEIGHT, 32, 0); } /* \brief Обнуляет изображение, заполняя его черным цветом */ static void _image_reset(void) { for (int y = 0; y < IMAGE_HEIGHT; y++) for (int x = 0; x < IMAGE_WIDTH; x++) pixdata[y][x] = 0; } /*! \brief Конвертирует символ из чертежа в индекс текстуры * \param[in] elem Символ чертежа * \return Индекс текстуры */ static int _map2texture(char elem) { return ((elem >> 4) & 1) << 2 | (elem & 3); } /*! \brief Собирает изображение из отдельных тайлов согласно карте */ static void _image_compile(void) { for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) _texture_draw(_map2texture(mapdata[y][x]), x, y); } /*! \brief Рисует изображение на экране */ static void _image_draw(void) { XPutImage(display, window, DefaultGC(display, DefaultScreen(display)), image, 0, 0, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT); XMapWindow(display, window); XFlush(display); } /*! \brief Рисует отверстия на печатной плате */ static void _image_drill(void) { for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) if ((x % 14 == 2) && (y % 4 == 3)) _texture_draw(TEXINDEX_HOLE, x, y); } /*! \brief Применяет шейдер LCD-эффекта на изображение * \param[in] x X-координата изображения * \param[in] y Y-координата изображения */ static void _shader_lcd(int x, int y) { pixdata[y][x] &= 0xe0e0e0 | (31 << ((x % 3) << 3)); } /*! \brief Применяет шейдер случайного шума на изображение * \param[in] x X-координата изображения * \param[in] y Y-координата изображения */ static void _shader_noise(int x, int y) { pixdata[y][x] += 0x0f0f0f & (random *= 16777619); } /*! \brief Накладывает на изображение различные эффекты */ static void _image_postprocess(void) { for (int y = 0; y < IMAGE_HEIGHT; y++) for (int x = 0; x < IMAGE_WIDTH; x++) { _shader_lcd(x, y); _shader_noise(x, y); } } /*! \brief Отрисовывает текстуру на главном холсте по указанным координатам * \param[in] t Индекс текстуры * \param[in] x X координата тайла * \param[in] y Y координата тайла */ static void _texture_draw(int t, int x, int y) { for (int ty = 0; ty < TEXTURE_HEIGHT; ty++) for (int tx = 0; tx < TEXTURE_WIDTH; tx++) if (textures[t][ty][tx]) pixdata[ty + y * (TEXTURE_TOP_HEIGHT / 2) + x * (TEXTURE_TOP_HEIGHT / 2)] [tx + IMAGE_SHIFTX + x * (TEXTURE_TOP_WIDTH / 2) - y * TEXTURE_TOP_HEIGHT] = textures[t][ty][tx]; } /*! \brief Читает данные файла-чертежа и загружает их в карту * \param[in] filename Имя файла-чертежа */ static void _map_read(const char * filename) { int f = open(filename, 0); read(f, mapdata, MAP_FILEDATA); close(f); } /*! \brief Заменяет иллюстративные входы из файла-конфигурации на вход * в виде провода чтобы работала логика распространения фронта волны */ static void _map_wire_inputs(void) { for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) if ((x % 14 == 2) && (y % 4 == 3)) mapdata[y][x] = MAPCHAR_WIRE; } /*! \brief Производит заливку проводника нужным состоянием * \param[in] t Игнорируется, артефакт автогенерации кода * \param[in] x X-координата заливки * \param[in] y Y-координата заливки * \param[in] c Исходное состояние * \param[in] l Целевое состояние */ static void _map_fill(int t, int x, int y, int c, int l) { if ((x >= MAP_WIDTH || y >= MAP_HEIGHT || x < 0 || y < 0) || (mapdata[y][x] == l) || (mapdata[y][x] != c)) return; mapdata[y][x] = l; _map_fill(t, x - 1, y, c, l); _map_fill(t, x + 1, y, c, l); _map_fill(t, x, y - 1, c, l); _map_fill(t, x, y + 1, c, l); } /*! \brief Включает соответствующие входы схемы в зависимости от значения * счетчика. * \param[in] counter Счетчик */ static void _map_wire_counter(int counter) { _map_fill(0, 2, 3, mapdata[3][2], counter & 1 ? MAPCHAR_PLUS : MAPCHAR_MINUS); _map_fill(0, 2, 7, mapdata[7][2], counter & 2 ? MAPCHAR_PLUS : MAPCHAR_MINUS); _map_fill(0, 2, 11, mapdata[11][2], counter & 4 ? MAPCHAR_PLUS : MAPCHAR_MINUS); _map_fill(0, 2, 15, mapdata[15][2], counter & 8 ? MAPCHAR_PLUS : MAPCHAR_MINUS); } /*! \brief Проводит расчет выходного (результирующего) тока после NOR-узла */ static void _map_process_gates(void) { for (int i = 0; i < MAP_ITERATIONS; i++) for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) if (mapdata[y][x] == MAPCHAR_NOR) _map_fill(0, x + 1, y, mapdata[y][x + 1], !(mapdata[y - 1][x] == MAPCHAR_PLUS ? 1 : 0 || mapdata[y + 1][x] == MAPCHAR_PLUS ? 1 : 0) ? MAPCHAR_PLUS : MAPCHAR_MINUS); } /* Код до рефакторинга */ int _x[] = { 15117427, 8413248, 5878632, 13027014, 1 }; void e(int t, int x, int y, int c, int l) { if ((x >= TEXTURE_WIDTH || y >= TEXTURE_HEIGHT || x < 0 || y < 0) || (textures[t][y][x] == l) || (textures[t][y][x] != c)) return; textures[t][y][x] = l; e(t, x - 1, y, c, l); e(t, x + 1, y, c, l); e(t, x, y - 1, c, l); e(t, x, y + 1, c, l); } void k(int t, int x, int y, int c, int l) { while (c--) { textures[t][y][x++] = l; textures[t][y++][x++] = l; } } void r(int t, int x, int y, int c, int l) { while (c--) { textures[t][y][x--] = l; textures[t][y++][x--] = l; } } void u(int t, int x, int y, int c, int l) { while (c--) textures[t][y++][x] = l; } char *z[8] = { (char[]) {4}, (char[]) {6, 1 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0, 21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63, 21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6, 1 * 4 + 0, 3, 31, 8 - 0, 6, 1 * 4 + 2, 3, 33, 38 - 0, 6, 1 * 4 + 3, 3, 30, 38 - 0, 4}, (char[]) {5, 1}, (char[]) {6, 0 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0, 21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63, 21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6, 0 * 4 + 0, 3, 31, 8 - 0, 6, 0 * 4 + 2, 3, 33, 38 - 0, 6, 0 * 4 + 3, 3, 30, 38 - 0, 4}, (char[]) {4}, (char[]) {4}, (char[]) {6, 2 * 4 + 1, 0 + 0, 32 - 0, 5 - 3, 16, 0, 0, 21 + 0 * 3 - 3, 16, 1, 63, 21 + 0 * 3 - 3, 16, 2, 63 * 0, 21 - 3, 4, 2, 31 + 0, 36 - 3, 4, 0 + 1, 32 - 1, 5 - 3, 16, 0, 0, 21 + 1 * 3 - 3, 16, 1, 63, 21 + 1 * 3 - 3, 16, 2, 63 * 1, 21 - 3, 4, 2, 31 + 1, 36 - 3, 4, 6, 2 * 4 + 0, 3, 31, 8 - 3, 6, 2 * 4 + 2, 3, 33, 38 - 3, 6, 2 * 4 + 3, 3, 30, 38 - 3, 4}, (char[]) {6, 13, 0, 32, 9 + 0, 12 - 0, 0, 8 + 0 + 0, 21, 12 - 0, 1, 31, 9 + 0, 12 - 0, 1, 55 - 0 - 0, 21, 12 - 0, 0, 32, 9 + 3, 12 - 3, 0, 8 + 3 + 3, 21, 12 - 3, 1, 31, 9 + 3, 12 - 3, 1, 55 - 3 - 3, 21, 12 - 3, 6, 14 + 0, 2, 31 + 0, 13, 4, 1 - 0, 31 + 0, 16, 7, 3, 30 + 3 * 0, 14, 6, 12 + 0 * 4, 3, 32 - 0, 11 + 0 * 8, 6, 14 + 1, 2, 31 + 1, 13, 4, 1 - 1, 31 + 1, 16, 7, 3, 30 + 3 * 1, 14, 6, 12 + 1 * 4, 3, 32 - 1, 11 + 1 * 8, 4} }; int main(int argc, char * args[]) { _image_create(); for (int i = 0; i < 8; i++) { char *p = z[i]; int c = 0; while (*p != 4) { switch (*p) { case 0: k(i, p[1], p[2], p[3], c); p += 4; break; case 1: r(i, p[1], p[2], p[3], c); p += 4; break; case 2: u(i, p[1], p[2], p[3], c); p += 4; break; case 3: e(i, p[1] , p[2], 0, c); p += 3; break; case 5: p = z[p[1]]; break; case 6: c = _x[p[1] / 4] - 1643277 * (p[1] % 4); p += 2; break; } } } unsigned int counter = 1; while (counter++) { _map_read(args[ARG_BLUEPRINT]); _map_wire_inputs(); _map_wire_counter(counter); _map_process_gates(); _image_reset(); _image_compile(); _image_drill(); _image_postprocess(); _image_draw(); sleep(1); } return 0; } «e», «k», «r», «u» Осталось еще 4 функции, которые мы не исследовали и не переименовали — это «e», «k», «r» и «u». Попытаемся осмотреть их, не прибегая к поиску мест, откуда они вызываются: void e(int t, int x, int y, int c, int l) {
if ((x >= TEXTURE_WIDTH || y >= TEXTURE_HEIGHT || x < 0 || y < 0) || (textures[t][y][x] == l) || (textures[t][y][x] != c)) return; textures[t][y][x] = l; e(t, x - 1, y, c, l); e(t, x + 1, y, c, l); e(t, x, y - 1, c, l); e(t, x, y + 1, c, l); } Очевидно, что эта функция выглядит и работает как flood_fill, только для массива textures, переименуем ее в _texture_fill. void k(int t, int x, int y, int c, int l) {
while (c--) { textures[t][y][x++] = l; textures[t][y++][x++] = l; } } void r(int t, int x, int y, int c, int l) { while (c--) { textures[t][y][x--] = l; textures[t][y++][x--] = l; } } Функции «k» и «r» на каждую заданную единицу c рисуют два пикселя значением l и перемещаются вправо или влево на два пикселя и вниз на один — значит это функции рисования изометрических линий SW и SE. Переименуем их в _texture_linesw и _texture_linese. На данный момент уже можно догадаться, что последняя функция «u» рисует линию вертикально вниз: void u(int t, int x, int y, int c, int l) {
while (c--) textures[t][y++][x] = l; } Переименуем её в _texture_linedown. Все отдельные функции прошли рефакторинг:SPL#include <X11/Xlib.h>
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> /* Код после рефакторинга */ /*! \brief Ширина изображения в пикселях */ #define IMAGE_WIDTH (1220) /*! \brief Высота изображения в пикселях */ #define IMAGE_HEIGHT (616) /*! \brief Сдвиг верхней грани карты от левой стороны */ #define IMAGE_SHIFTX (580) /*! \brief Количество текстур */ #define TEXTURE_COUNT (8) /*! \brief Ширина текстуры в пикселях */ #define TEXTURE_WIDTH (64) /*! \brief Высота текстур в пикселях */ #define TEXTURE_HEIGHT (40) /*! \brief Ширина грани текстуры в пикселях */ #define TEXTURE_TOP_WIDTH (64) /*! \brief Высота грани текстуры в пикселях */ #define TEXTURE_TOP_HEIGHT (32) /*! \brief Ширина эмулируемого поля */ #define MAP_WIDTH (19) /*! \brief Высота эмулируемого поля */ #define MAP_HEIGHT (19) /*! \brief Количество данные в файле-чертеже. Один байт добавлен для символа '\n' */ #define MAP_FILEDATA ((MAP_WIDTH + 1) * MAP_HEIGHT) /*! \brief Количество итераций во время расчета NOR-узлов */ #define MAP_ITERATIONS (20) /*! \brief Содержит структуры синтаксиса файла-конфигурации */ enum map_characters { MAPCHAR_WIRE = '.', /**< Провод (ASCII = 46) */ MAPCHAR_PLUS = '+', /**< Провод (есть ток) (ASCII = 43) */ MAPCHAR_MINUS = '-', /**< Провод (нет тока) (ASCII = 45) */ MAPCHAR_NOR = '>', /**< NOR-элемент (ASCII = 62) */ MAPCHAR_EMPTY = ' ', /**< Пустой блок (ASCII = 32) */ }; /*! \brief Содержит индексы текстур */ enum textures_indexes { TEXINDEX_EMPTY = (0), /**< Индекс пустой текстуры */ TEXINDEX_MINUS = (1), /**< Индекс текстуры "выключенного провода" */ TEXINDEX_WIRE = (2), /**< Индекс текстуры нейтрального провода */ TEXINDEX_PLUS = (3), /**< Индекс текстуры "включенного" провода */ /**/ TEXINDEX_NOR = (6), /**< Индекс текстуры NOR-элемента */ TEXINDEX_HOLE = (7) /**< Индекс текстуры отверстия на плате */ }; /*! \brief Аргументы программы */ enum program_arguments { ARG_PROGRAM, /**< Имя самой программы */ ARG_BLUEPRINT /**< Имя файла-чертежа */ }; /*! \brief Бинарные данные пикселей изображения. * Типизированы к int для оперирования пикселями вместо каналов */ int pixdata[IMAGE_HEIGHT][IMAGE_WIDTH]; /*! \brief Текстуры блоков, отображаемых на поле */ int textures[TEXTURE_COUNT][TEXTURE_HEIGHT][TEXTURE_WIDTH] = { 0 }; /*! \brief Данные эмулируемого поля. * Один байт добавлен для упрощения обработки символа '\n' */ char mapdata[MAP_HEIGHT][MAP_WIDTH + 1]; /*! \brief Зерно генератора случайных чисел */ int random = 2166136261; /*! \brief Экземпляр дисплея Xlib */ Display * display; /*! \brief Экземпляр главного окна Xlib */ Window window; /*! \brief Изображение для вывода на экран */ XImage * image; static void _texture_draw(int t, int x, int y); /*! \brief Создает изображение и сопутствующие сущности */ static void _image_create(void) { display = XOpenDisplay(0); window = XCreateSimpleWindow(display, RootWindow(display, DefaultScreen(display)), 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, 1, 0, 0); image = XCreateImage(display, DefaultVisual(display, DefaultScreen(display)), DefaultDepth(display, DefaultScreen(display)), 2, 0, (char *) pixdata, IMAGE_WIDTH, IMAGE_HEIGHT, 32, 0); } /* \brief Обнуляет изображение, заполняя его черным цветом */ static void _image_reset(void) { for (int y = 0; y < IMAGE_HEIGHT; y++) for (int x = 0; x < IMAGE_WIDTH; x++) pixdata[y][x] = 0; } /*! \brief Конвертирует символ из чертежа в индекс текстуры * \param[in] elem Символ чертежа * \return Индекс текстуры */ static int _map2texture(char elem) { return ((elem >> 4) & 1) << 2 | (elem & 3); } /*! \brief Собирает изображение из отдельных тайлов согласно карте */ static void _image_compile(void) { for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) _texture_draw(_map2texture(mapdata[y][x]), x, y); } /*! \brief Рисует изображение на экране */ static void _image_draw(void) { XPutImage(display, window, DefaultGC(display, DefaultScreen(display)), image, 0, 0, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT); XMapWindow(display, window); XFlush(display); } /*! \brief Рисует отверстия на печатной плате */ static void _image_drill(void) { for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) if ((x % 14 == 2) && (y % 4 == 3)) _texture_draw(TEXINDEX_HOLE, x, y); } /*! \brief Применяет шейдер LCD-эффекта на изображение * \param[in] x X-координата изображения * \param[in] y Y-координата изображения */ static void _shader_lcd(int x, int y) { pixdata[y][x] &= 0xe0e0e0 | (31 << ((x % 3) << 3)); } /*! \brief Применяет шейдер случайного шума на изображение * \param[in] x X-координата изображения * \param[in] y Y-координата изображения */ static void _shader_noise(int x, int y) { pixdata[y][x] += 0x0f0f0f & (random *= 16777619); } /*! \brief Накладывает на изображение различные эффекты */ static void _image_postprocess(void) { for (int y = 0; y < IMAGE_HEIGHT; y++) for (int x = 0; x < IMAGE_WIDTH; x++) { _shader_lcd(x, y); _shader_noise(x, y); } } /*! \brief Отрисовывает текстуру на главном холсте по указанным координатам * \param[in] t Индекс текстуры * \param[in] x X координата тайла * \param[in] y Y координата тайла */ static void _texture_draw(int t, int x, int y) { for (int ty = 0; ty < TEXTURE_HEIGHT; ty++) for (int tx = 0; tx < TEXTURE_WIDTH; tx++) if (textures[t][ty][tx]) pixdata[ty + y * (TEXTURE_TOP_HEIGHT / 2) + x * (TEXTURE_TOP_HEIGHT / 2)] [tx + IMAGE_SHIFTX + x * (TEXTURE_TOP_WIDTH / 2) - y * TEXTURE_TOP_HEIGHT] = textures[t][ty][tx]; } /*! \brief Производит закраску области текстуры * \param[in] t Индекс текстуры * \param[in] x X-координата начала * \param[in] y Y-координата начала * \param[in] source Исходный цвет * \param[in] target Новый цвет */ static void _texture_fill(int t, int x, int y, int source, int target) { if ((x >= TEXTURE_WIDTH || y >= TEXTURE_HEIGHT || x < 0 || y < 0) || (textures[t][y][x] == target) || (textures[t][y][x] != source)) return; textures[t][y][x] = target; _texture_fill(t, x - 1, y, source, target); _texture_fill(t, x + 1, y, source, target); _texture_fill(t, x, y - 1, source, target); _texture_fill(t, x, y + 1, source, target); } /*! \brief Рисует изометрическую линию по направлению SE * \param[in] t Индекс текстуры * \param[in] x X-координата начала * \param[in] y Y-координата начала * \param[in] c Длина линии по короткой стороне * \param[in] color Цвет рисования */ static void _texture_linese(int t, int x, int y, int c, int color) { while (c--) { textures[t][y][x++] = color; textures[t][y++][x++] = color; } } /*! \brief Рисует изометрическую линию по направлению SW * \param[in] t Индекс текстуры * \param[in] x X-координата начала * \param[in] y Y-координата начала * \param[in] c Длина линии по короткой стороне * \param[in] color Цвет рисования */ static void _texture_linesw(int t, int x, int y, int c, int color) { while (c--) { textures[t][y][x--] = color; textures[t][y++][x--] = color; } } /*! \brief Рисует линию вниз * \param[in] t Индекс текстуры * \param[in] x X-координата начала * \param[in] y Y-координата начала * \param[in] c Длина линии * \param[in] color Цвет рисования */ static void _texture_linedown(int t, int x, int y, int c, int color) { while (c--) textures[t][y++][x] = color; } /*! \brief Читает данные файла-чертежа и загружает их в карту * \param[in] filename Имя файла-чертежа */ static void _map_read(const char * filename) { int f = open(filename, 0); read(f, mapdata, MAP_FILEDATA); close(f); } /*! \brief Заменяет иллюстративные входы из файла-конфигурации на вход * в виде провода чтобы работала логика распространения фронта волны */ static void _map_wire_inputs(void) { for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) if ((x % 14 == 2) && (y % 4 == 3)) mapdata[y][x] = MAPCHAR_WIRE; } /*! \brief Производит заливку проводника нужным состоянием * \param[in] t Игнорируется, артефакт автогенерации кода * \param[in] x X-координата заливки * \param[in] y Y-координата заливки * \param[in] c Исходное состояние * \param[in] l Целевое состояние */ static void _map_fill(int t, int x, int y, int c, int l) { if ((x >= MAP_WIDTH || y >= MAP_HEIGHT || x < 0 || y < 0) || (mapdata[y][x] == l) || (mapdata[y][x] != c)) return; mapdata[y][x] = l; _map_fill(t, x - 1, y, c, l); _map_fill(t, x + 1, y, c, l); _map_fill(t, x, y - 1, c, l); _map_fill(t, x, y + 1, c, l); } /*! \brief Включает соответствующие входы схемы в зависимости от значения * счетчика. * \param[in] counter Счетчик */ static void _map_wire_counter(int counter) { _map_fill(0, 2, 3, mapdata[3][2], counter & 1 ? MAPCHAR_PLUS : MAPCHAR_MINUS); _map_fill(0, 2, 7, mapdata[7][2], counter & 2 ? MAPCHAR_PLUS : MAPCHAR_MINUS); _map_fill(0, 2, 11, mapdata[11][2], counter & 4 ? MAPCHAR_PLUS : MAPCHAR_MINUS); _map_fill(0, 2, 15, mapdata[15][2], counter & 8 ? MAPCHAR_PLUS : MAPCHAR_MINUS); } /*! \brief Проводит расчет выходного (результирующего) тока после NOR-узла */ static void _map_process_gates(void) { for (int i = 0; i < MAP_ITERATIONS; i++) for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) if (mapdata[y][x] == MAPCHAR_NOR) _map_fill(0, x + 1, y, mapdata[y][x + 1], !(mapdata[y - 1][x] == MAPCHAR_PLUS ? 1 : 0 || mapdata[y + 1][x] == MAPCHAR_PLUS ? 1 : 0) ? MAPCHAR_PLUS : MAPCHAR_MINUS); } /* Код до рефакторинга */ int _x[] = { 15117427, 8413248, 5878632, 13027014, 1 }; char *z[8] = { (char[]) {4}, (char[]) {6, 1 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0, 21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63, 21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6, 1 * 4 + 0, 3, 31, 8 - 0, 6, 1 * 4 + 2, 3, 33, 38 - 0, 6, 1 * 4 + 3, 3, 30, 38 - 0, 4}, (char[]) {5, 1}, (char[]) {6, 0 * 4 + 1, 0 + 0, 32 - 0, 5 - 0, 16, 0, 0, 21 + 0 * 3 - 0, 16, 1, 63, 21 + 0 * 3 - 0, 16, 2, 63 * 0, 21 - 0, 4, 2, 31 + 0, 36 - 0, 4, 0 + 1, 32 - 1, 5 - 0, 16, 0, 0, 21 + 1 * 3 - 0, 16, 1, 63, 21 + 1 * 3 - 0, 16, 2, 63 * 1, 21 - 0, 4, 2, 31 + 1, 36 - 0, 4, 6, 0 * 4 + 0, 3, 31, 8 - 0, 6, 0 * 4 + 2, 3, 33, 38 - 0, 6, 0 * 4 + 3, 3, 30, 38 - 0, 4}, (char[]) {4}, (char[]) {4}, (char[]) {6, 2 * 4 + 1, 0 + 0, 32 - 0, 5 - 3, 16, 0, 0, 21 + 0 * 3 - 3, 16, 1, 63, 21 + 0 * 3 - 3, 16, 2, 63 * 0, 21 - 3, 4, 2, 31 + 0, 36 - 3, 4, 0 + 1, 32 - 1, 5 - 3, 16, 0, 0, 21 + 1 * 3 - 3, 16, 1, 63, 21 + 1 * 3 - 3, 16, 2, 63 * 1, 21 - 3, 4, 2, 31 + 1, 36 - 3, 4, 6, 2 * 4 + 0, 3, 31, 8 - 3, 6, 2 * 4 + 2, 3, 33, 38 - 3, 6, 2 * 4 + 3, 3, 30, 38 - 3, 4}, (char[]) {6, 13, 0, 32, 9 + 0, 12 - 0, 0, 8 + 0 + 0, 21, 12 - 0, 1, 31, 9 + 0, 12 - 0, 1, 55 - 0 - 0, 21, 12 - 0, 0, 32, 9 + 3, 12 - 3, 0, 8 + 3 + 3, 21, 12 - 3, 1, 31, 9 + 3, 12 - 3, 1, 55 - 3 - 3, 21, 12 - 3, 6, 14 + 0, 2, 31 + 0, 13, 4, 1 - 0, 31 + 0, 16, 7, 3, 30 + 3 * 0, 14, 6, 12 + 0 * 4, 3, 32 - 0, 11 + 0 * 8, 6, 14 + 1, 2, 31 + 1, 13, 4, 1 - 1, 31 + 1, 16, 7, 3, 30 + 3 * 1, 14, 6, 12 + 1 * 4, 3, 32 - 1, 11 + 1 * 8, 4} }; int main(int argc, char * args[]) { _image_create(); for (int i = 0; i < 8; i++) { char *p = z[i]; int c = 0; while (*p != 4) { switch (*p) { case 0: _texture_linese(i, p[1], p[2], p[3], c); p += 4; break; case 1: _texture_linesw(i, p[1], p[2], p[3], c); p += 4; break; case 2: _texture_linedown(i, p[1], p[2], p[3], c); p += 4; break; case 3: _texture_fill(i, p[1], p[2], 0, c); p += 3; break; case 5: p = z[p[1]]; break; case 6: c = _x[p[1] / 4] - 1643277 * (p[1] % 4); p += 2; break; } } } unsigned int counter = 1; while (counter++) { _map_read(args[ARG_BLUEPRINT]); _map_wire_inputs(); _map_wire_counter(counter); _map_process_gates(); _image_reset(); _image_compile(); _image_drill(); _image_postprocess(); _image_draw(); sleep(1); } return 0; } Векторная графика Остался последний шаг — до конца разобраться, что же происходит в последнем, оставленном «на потом», switch-case: for (int i = 0; i < 8; i++) {
char *p = z[i]; int c = 0; while (*p != 4) { switch (*p) { case 0: _texture_linese(i, p[1], p[2], p[3], c); p += 4; break; case 1: _texture_linesw(i, p[1], p[2], p[3], c); p += 4; break; case 2: _texture_linedown(i, p[1], p[2], p[3], c); p += 4; break; case 3: _texture_fill(i, p[1], p[2], 0, c); p += 3; break; case 5: p = z[p[1]]; break; case 6: c = _x[p[1] / 4] - 1643277 * (p[1] % 4); p += 2; break; } } } Теперь, когда функции имеют новые, понятные, имена, можно сказать, что этот блок кода интерпретирует данные из массива «z», и согласно инструкциям, содержащимся в нем (например 0, 5, 5, 8, 2 — «нарисовать линию South-East, от [5,5] длиной 8 пикселей по краткой стороне цветом номер 2») рисует весь набор текстур. Вроде бы всё понятно, но ни одного указания на цвет в программе замечено не было. c = _x[p[1] / 4] - 1643277 * (p[1] % 4);
Ага, значит массив _x — это палитра, но в очень странном, «сжатом», формате. int _x[] = { 15117427, 8413248, 5878632, 13027014, 1 };
Так как мы пока не знаем, сколько всего цветов используется в палитре (при установке цвета индекс как минимум делится на 4), переформатируем таблицу данных для отрисовки и группируем числа по категориям, заменив сигнатуры инструкций на константы: Рефакторинг таблицы векторных инструкцийSPL#include <X11/Xlib.h>
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> /* Код после рефакторинга */ /*! \brief Ширина изображения в пикселях */ #define IMAGE_WIDTH (1220) /*! \brief Высота изображения в пикселях */ #define IMAGE_HEIGHT (616) /*! \brief Сдвиг верхней грани карты от левой стороны */ #define IMAGE_SHIFTX (580) /*! \brief Количество текстур */ #define TEXTURE_COUNT (8) /*! \brief Ширина текстуры в пикселях */ #define TEXTURE_WIDTH (64) /*! \brief Высота текстур в пикселях */ #define TEXTURE_HEIGHT (40) /*! \brief Ширина грани текстуры в пикселях */ #define TEXTURE_TOP_WIDTH (64) /*! \brief Высота грани текстуры в пикселях */ #define TEXTURE_TOP_HEIGHT (32) /*! \brief Ширина эмулируемого поля */ #define MAP_WIDTH (19) /*! \brief Высота эмулируемого поля */ #define MAP_HEIGHT (19) /*! \brief Количество данные в файле-чертеже. Один байт добавлен для символа '\n' */ #define MAP_FILEDATA ((MAP_WIDTH + 1) * MAP_HEIGHT) /*! \brief Количество итераций во время расчета NOR-узлов */ #define MAP_ITERATIONS (20) /*! \brief Содержит структуры синтаксиса файла-конфигурации */ enum map_characters { MAPCHAR_WIRE = '.', /**< Провод (ASCII = 46) */ MAPCHAR_PLUS = '+', /**< Провод (есть ток) (ASCII = 43) */ MAPCHAR_MINUS = '-', /**< Провод (нет тока) (ASCII = 45) */ MAPCHAR_NOR = '>', /**< NOR-элемент (ASCII = 62) */ MAPCHAR_EMPTY = ' ', /**< Пустой блок (ASCII = 32) */ }; /*! \brief Содержит индексы текстур */ enum textures_indexes { TEXINDEX_EMPTY = (0), /**< Индекс пустой текстуры */ TEXINDEX_MINUS = (1), /**< Индекс текстуры "выключенного провода" */ TEXINDEX_WIRE = (2), /**< Индекс текстуры нейтрального провода */ TEXINDEX_PLUS = (3), /**< Индекс текстуры "включенного" провода */ /**/ TEXINDEX_NOR = (6), /**< Индекс текстуры NOR-элемента */ TEXINDEX_HOLE = (7) /**< Индекс текстуры отверстия на плате */ }; /*! \brief Список инструкций векторного интерпретатора */ enum textures_instructions { TEXVEC_LINESE = (0), /**< Линия SE */ TEXVEC_LINESW = (1), /**< Линия SW */ TEXVEC_LINEDW = (2), /**< Линия вниз */ TEXVEC_FILL = (3), /**< Заливка */ TEXVEC_EXIT = (4), /**< Конец списка инструкций */ TEXVEC_REPEAT = (5), /**< Повтор инструкций другой текстуры */ TEXVEC_COLOR = (6) /**< Выбор цвета */ }; /*! \brief Аргументы программы */ enum program_arguments { ARG_PROGRAM, /**< Имя самой программы */ ARG_BLUEPRINT /**< Имя файла-чертежа */ }; /*! \brief Бинарные данные пикселей изображения. * Типизированы к int для оперирования пикселями вместо каналов */ int pixdata[IMAGE_HEIGHT][IMAGE_WIDTH]; /*! \brief Текстуры блоков, отображаемых на поле */ int textures[TEXTURE_COUNT][TEXTURE_HEIGHT][TEXTURE_WIDTH] = { 0 }; /*! \brief Данные эмулируемого поля. * Один байт добавлен для упрощения обработки символа '\n' */ char mapdata[MAP_HEIGHT][MAP_WIDTH + 1]; /*! \brief Зерно генератора случайных чисел */ int random = 2166136261; /*! \brief Экземпляр дисплея Xlib */ Display * display; /*! \brief Экземпляр главного окна Xlib */ Window window; /*! \brief Изображение для вывода на экран */ XImage * image; /*! \brief Векторные инструкции для рисования текстур */ static const char * vecdata[TEXTURE_COUNT] = { /* TEXINDEX_EMPTY */ (char[]) { TEXVEC_EXIT }, /* TEXINDEX_MINUS */ (char[]) { TEXVEC_COLOR, 5, TEXVEC_LINESE, 32, 5, 16, TEXVEC_LINESE, 0, 21, 16, TEXVEC_LINESW, 63, 21, 16, TEXVEC_LINEDW, 0, 21, 4, TEXVEC_LINEDW, 31, 36, 4, TEXVEC_LINESW, 31, 5, 16, TEXVEC_LINESE, 0, 24, 16, TEXVEC_LINESW, 63, 24, 16, TEXVEC_LINEDW, 63, 21, 4, TEXVEC_LINEDW, 32, 36, 4, TEXVEC_COLOR, 4, TEXVEC_FILL, 31, 8, TEXVEC_COLOR, 6, TEXVEC_FILL, 33, 38, TEXVEC_COLOR, 7, TEXVEC_FILL, 30, 38, TEXVEC_EXIT }, /* TEXINDEX_WIRE */ (char[]) { TEXVEC_REPEAT, 1 }, /* TEXINDEX_PLUS */ (char[]) { TEXVEC_COLOR, 1, TEXVEC_LINESE, 32, 5, 16, TEXVEC_LINESE, 0, 21, 16, TEXVEC_LINESW, 63, 21, 16, TEXVEC_LINEDW, 63, 21, 4, TEXVEC_LINEDW, 31, 36, 4, TEXVEC_LINESW, 31, 5, 16, TEXVEC_LINESE, 0, 24, 16, TEXVEC_LINESW, 63, 24, 16, TEXVEC_LINEDW, 63, 21, 4, TEXVEC_LINEDW, 32, 36, 4, TEXVEC_COLOR, 0, TEXVEC_FILL, 31, 8, TEXVEC_COLOR, 2, TEXVEC_FILL, 33, 38, TEXVEC_COLOR, 3, TEXVEC_FILL, 30, 38, TEXVEC_EXIT }, /* Не используется */ (char[]) { TEXVEC_EXIT }, /* Не используется */ (char[]) { TEXVEC_EXIT }, /* TEXINDEX_NOR */ (char[]) { TEXVEC_COLOR, 9, TEXVEC_LINESE, 32, 2, 16, TEXVEC_LINESE, 0, 18, 16, TEXVEC_LINESW, 63, 18, 16, TEXVEC_LINEDW, 0, 18, 4, TEXVEC_LINEDW, 31, 33, 4, TEXVEC_LINESW, 31, 2, 16, TEXVEC_LINESE, 0, 21, 16, TEXVEC_LINESW, 63, 21, 16, TEXVEC_LINEDW, 63, 18, 4, TEXVEC_LINEDW, 32, 33, 4, TEXVEC_COLOR, 8, TEXVEC_FILL, 31, 5, TEXVEC_COLOR, 10, TEXVEC_FILL, 33, 35, TEXVEC_COLOR, 11, TEXVEC_FILL, 30, 35, TEXVEC_EXIT }, /* TEXINDEX_HOLE */ (char[]) { TEXVEC_COLOR, 13, TEXVEC_LINESE, 32, 9, 12, TEXVEC_LINESE, 8, 21, 12, TEXVEC_LINESW, 31, 9, 12, TEXVEC_LINESW, 55, 21, 12, TEXVEC_LINESE, 32, 12, 9, TEXVEC_LINESE, 14, 21, 9, TEXVEC_LINESW, 31, 12, 9, TEXVEC_LINESW, 49, 21, 9, TEXVEC_COLOR, 14, TEXVEC_LINEDW, 31, 13, 4, TEXVEC_LINESW, 31, 16, 7, TEXVEC_FILL, 30, 14, TEXVEC_COLOR, 12, TEXVEC_FILL, 32, 11, TEXVEC_COLOR, 15, TEXVEC_LINEDW, 32, 13, 4, TEXVEC_LINESE, 32, 16, 7, TEXVEC_FILL, 33, 14, TEXVEC_COLOR, 16, TEXVEC_FILL, 31, 19, TEXVEC_EXIT } }; static void _texture_draw(int t, int x, int y); /*! \brief Создает изображение и сопутствующие сущности */ static void _image_create(void) { display = XOpenDisplay(0); window = XCreateSimpleWindow(display, RootWindow(display, DefaultScreen(display)), 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, 1, 0, 0); image = XCreateImage(display, DefaultVisual(display, DefaultScreen(display)), DefaultDepth(display, DefaultScreen(display)), 2, 0, (char *) pixdata, IMAGE_WIDTH, IMAGE_HEIGHT, 32, 0); } /* \brief Обнуляет изображение, заполняя его черным цветом */ static void _image_reset(void) { for (int y = 0; y < IMAGE_HEIGHT; y++) for (int x = 0; x < IMAGE_WIDTH; x++) pixdata[y][x] = 0; } /*! \brief Конвертирует символ из чертежа в индекс текстуры * \param[in] elem Символ чертежа * \return Индекс текстуры */ static int _map2texture(char elem) { return ((elem >> 4) & 1) << 2 | (elem & 3); } /*! \brief Собирает изображение из отдельных тайлов согласно карте */ static void _image_compile(void) { for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) _texture_draw(_map2texture(mapdata[y][x]), x, y); } /*! \brief Рисует изображение на экране */ static void _image_draw(void) { XPutImage(display, window, DefaultGC(display, DefaultScreen(display)), image, 0, 0, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT); XMapWindow(display, window); XFlush(display); } /*! \brief Рисует отверстия на печатной плате */ static void _image_drill(void) { for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) if ((x % 14 == 2) && (y % 4 == 3)) _texture_draw(TEXINDEX_HOLE, x, y); } /*! \brief Применяет шейдер LCD-эффекта на изображение * \param[in] x X-координата изображения * \param[in] y Y-координата изображения */ static void _shader_lcd(int x, int y) { pixdata[y][x] &= 0xe0e0e0 | (31 << ((x % 3) << 3)); } /*! \brief Применяет шейдер случайного шума на изображение * \param[in] x X-координата изображения * \param[in] y Y-координата изображения */ static void _shader_noise(int x, int y) { pixdata[y][x] += 0x0f0f0f & (random *= 16777619); } /*! \brief Накладывает на изображение различные эффекты */ static void _image_postprocess(void) { for (int y = 0; y < IMAGE_HEIGHT; y++) for (int x = 0; x < IMAGE_WIDTH; x++) { _shader_lcd(x, y); _shader_noise(x, y); } } /*! \brief Отрисовывает текстуру на главном холсте по указанным координатам * \param[in] t Индекс текстуры * \param[in] x X координата тайла * \param[in] y Y координата тайла */ static void _texture_draw(int t, int x, int y) { for (int ty = 0; ty < TEXTURE_HEIGHT; ty++) for (int tx = 0; tx < TEXTURE_WIDTH; tx++) if (textures[t][ty][tx]) pixdata[ty + y * (TEXTURE_TOP_HEIGHT / 2) + x * (TEXTURE_TOP_HEIGHT / 2)] [tx + IMAGE_SHIFTX + x * (TEXTURE_TOP_WIDTH / 2) - y * TEXTURE_TOP_HEIGHT] = textures[t][ty][tx]; } /*! \brief Производит закраску области текстуры * \param[in] t Индекс текстуры * \param[in] x X-координата начала * \param[in] y Y-координата начала * \param[in] source Исходный цвет * \param[in] target Новый цвет */ static void _texture_fill(int t, int x, int y, int source, int target) { if ((x >= TEXTURE_WIDTH || y >= TEXTURE_HEIGHT || x < 0 || y < 0) || (textures[t][y][x] == target) || (textures[t][y][x] != source)) return; textures[t][y][x] = target; _texture_fill(t, x - 1, y, source, target); _texture_fill(t, x + 1, y, source, target); _texture_fill(t, x, y - 1, source, target); _texture_fill(t, x, y + 1, source, target); } /*! \brief Рисует изометрическую линию по направлению SE * \param[in] t Индекс текстуры * \param[in] x X-координата начала * \param[in] y Y-координата начала * \param[in] c Длина линии по короткой стороне * \param[in] color Цвет рисования */ static void _texture_linese(int t, int x, int y, int c, int color) { while (c--) { textures[t][y][x++] = color; textures[t][y++][x++] = color; } } /*! \brief Рисует изометрическую линию по направлению SW * \param[in] t Индекс текстуры * \param[in] x X-координата начала * \param[in] y Y-координата начала * \param[in] c Длина линии по короткой стороне * \param[in] color Цвет рисования */ static void _texture_linesw(int t, int x, int y, int c, int color) { while (c--) { textures[t][y][x--] = color; textures[t][y++][x--] = color; } } /*! \brief Рисует линию вниз * \param[in] t Индекс текстуры * \param[in] x X-координата начала * \param[in] y Y-координата начала * \param[in] c Длина линии * \param[in] color Цвет рисования */ static void _texture_linedown(int t, int x, int y, int c, int color) { while (c--) textures[t][y++][x] = color; } /*! \brief Читает данные файла-чертежа и загружает их в карту * \param[in] filename Имя файла-чертежа */ static void _map_read(const char * filename) { int f = open(filename, 0); read(f, mapdata, MAP_FILEDATA); close(f); } /*! \brief Заменяет иллюстративные входы из файла-конфигурации на вход * в виде провода чтобы работала логика распространения фронта волны */ static void _map_wire_inputs(void) { for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) if ((x % 14 == 2) && (y % 4 == 3)) mapdata[y][x] = MAPCHAR_WIRE; } /*! \brief Производит заливку проводника нужным состоянием * \param[in] t Игнорируется, артефакт автогенерации кода * \param[in] x X-координата заливки * \param[in] y Y-координата заливки * \param[in] c Исходное состояние * \param[in] l Целевое состояние */ static void _map_fill(int t, int x, int y, int c, int l) { if ((x >= MAP_WIDTH || y >= MAP_HEIGHT || x < 0 || y < 0) || (mapdata[y][x] == l) || (mapdata[y][x] != c)) return; mapdata[y][x] = l; _map_fill(t, x - 1, y, c, l); _map_fill(t, x + 1, y, c, l); _map_fill(t, x, y - 1, c, l); _map_fill(t, x, y + 1, c, l); } /*! \brief Включает соответствующие входы схемы в зависимости от значения * счетчика. * \param[in] counter Счетчик */ static void _map_wire_counter(int counter) { _map_fill(0, 2, 3, mapdata[3][2], counter & 1 ? MAPCHAR_PLUS : MAPCHAR_MINUS); _map_fill(0, 2, 7, mapdata[7][2], counter & 2 ? MAPCHAR_PLUS : MAPCHAR_MINUS); _map_fill(0, 2, 11, mapdata[11][2], counter & 4 ? MAPCHAR_PLUS : MAPCHAR_MINUS); _map_fill(0, 2, 15, mapdata[15][2], counter & 8 ? MAPCHAR_PLUS : MAPCHAR_MINUS); } /*! \brief Проводит расчет выходного (результирующего) тока после NOR-узла */ static void _map_process_gates(void) { for (int i = 0; i < MAP_ITERATIONS; i++) for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) if (mapdata[y][x] == MAPCHAR_NOR) _map_fill(0, x + 1, y, mapdata[y][x + 1], !(mapdata[y - 1][x] == MAPCHAR_PLUS ? 1 : 0 || mapdata[y + 1][x] == MAPCHAR_PLUS ? 1 : 0) ? MAPCHAR_PLUS : MAPCHAR_MINUS); } /* Код до рефакторинга */ int _x[] = { 15117427, 8413248, 5878632, 13027014, 1 }; int main(int argc, char * args[]) { _image_create(); for (int tex = 0; tex < TEXTURE_COUNT; tex++) { char * ptr = vecdata[tex]; int c = 0; while (*ptr != TEXVEC_EXIT) { switch (*ptr) { case TEXVEC_LINESE: _texture_linese(tex, ptr[1], ptr[2], ptr[3], c); ptr += 4; break; case TEXVEC_LINESW: _texture_linesw(tex, ptr[1], ptr[2], ptr[3], c); ptr += 4; break; case TEXVEC_LINEDW: _texture_linedown(tex, ptr[1], ptr[2], ptr[3], c); ptr += 4; break; case TEXVEC_FILL: _texture_fill(tex, ptr[1], ptr[2], 0, c); ptr += 3; break; case TEXVEC_REPEAT: ptr = vecdata[ptr[1]]; break; case TEXVEC_COLOR: c = _x[ptr[1] / 4] - 1643277 * (ptr[1] % 4); ptr += 2; break; } } } unsigned int counter = 1; while (counter++) { _map_read(args[ARG_BLUEPRINT]); _map_wire_inputs(); _map_wire_counter(counter); _map_process_gates(); _image_reset(); _image_compile(); _image_drill(); _image_postprocess(); _image_draw(); sleep(1); } return 0; } Максимальное число, которое стоит после инструкции «TEXVEC_COLOR» — это 16. Напишем простейший код, чтобы получить всю таблицу: Теперь, имея на руках палитру в расшифрованном виде мы можем заменить функцию её использования и написать расшифровки для цветов в константной таблице, а заодно и выделить функцию создания текстур: Полный код после рефакторинга:SPL#include <X11/Xlib.h>
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> /* Код после рефакторинга */ /*! \brief Ширина изображения в пикселях */ #define IMAGE_WIDTH (1220) /*! \brief Высота изображения в пикселях */ #define IMAGE_HEIGHT (616) /*! \brief Сдвиг верхней грани карты от левой стороны */ #define IMAGE_SHIFTX (580) /*! \brief Количество текстур */ #define TEXTURE_COUNT (8) /*! \brief Ширина текстуры в пикселях */ #define TEXTURE_WIDTH (64) /*! \brief Высота текстур в пикселях */ #define TEXTURE_HEIGHT (40) /*! \brief Ширина грани текстуры в пикселях */ #define TEXTURE_TOP_WIDTH (64) /*! \brief Высота грани текстуры в пикселях */ #define TEXTURE_TOP_HEIGHT (32) /*! \brief Ширина эмулируемого поля */ #define MAP_WIDTH (19) /*! \brief Высота эмулируемого поля */ #define MAP_HEIGHT (19) /*! \brief Количество данные в файле-чертеже. Один байт добавлен для символа '\n' */ #define MAP_FILEDATA ((MAP_WIDTH + 1) * MAP_HEIGHT) /*! \brief Количество итераций во время расчета NOR-узлов */ #define MAP_ITERATIONS (20) /*! \brief Содержит структуры синтаксиса файла-конфигурации */ enum map_characters { MAPCHAR_WIRE = '.', /**< Провод (ASCII = 46) */ MAPCHAR_PLUS = '+', /**< Провод (есть ток) (ASCII = 43) */ MAPCHAR_MINUS = '-', /**< Провод (нет тока) (ASCII = 45) */ MAPCHAR_NOR = '>', /**< NOR-элемент (ASCII = 62) */ MAPCHAR_EMPTY = ' ', /**< Пустой блок (ASCII = 32) */ }; /*! \brief Содержит индексы текстур */ enum textures_indexes { TEXINDEX_EMPTY = (0), /**< Индекс пустой текстуры */ TEXINDEX_MINUS = (1), /**< Индекс текстуры "выключенного провода" */ TEXINDEX_WIRE = (2), /**< Индекс текстуры нейтрального провода */ TEXINDEX_PLUS = (3), /**< Индекс текстуры "включенного" провода */ /**/ TEXINDEX_NOR = (6), /**< Индекс текстуры NOR-элемента */ TEXINDEX_HOLE = (7) /**< Индекс текстуры отверстия на плате */ }; /*! \brief Список инструкций векторного интерпретатора */ enum textures_instructions { TEXVEC_LINESE = (0), /**< Линия SE */ TEXVEC_LINESW = (1), /**< Линия SW */ TEXVEC_LINEDW = (2), /**< Линия вниз */ TEXVEC_FILL = (3), /**< Заливка */ TEXVEC_EXIT = (4), /**< Конец списка инструкций */ TEXVEC_REPEAT = (5), /**< Повтор инструкций другой текстуры */ TEXVEC_COLOR = (6) /**< Выбор цвета */ }; /*! \brief Аргументы программы */ enum program_arguments { ARG_PROGRAM, /**< Имя самой программы */ ARG_BLUEPRINT /**< Имя файла-чертежа */ }; /*! \brief Бинарные данные пикселей изображения. * Типизированы к int для оперирования пикселями вместо каналов */ int pixdata[IMAGE_HEIGHT][IMAGE_WIDTH]; /*! \brief Текстуры блоков, отображаемых на поле */ int textures[TEXTURE_COUNT][TEXTURE_HEIGHT][TEXTURE_WIDTH] = { 0 }; /*! \brief Данные эмулируемого поля. * Один байт добавлен для упрощения обработки символа '\n' */ char mapdata[MAP_HEIGHT][MAP_WIDTH + 1]; /*! \brief Зерно генератора случайных чисел */ int random = 2166136261; /*! \brief Экземпляр дисплея Xlib */ Display * display; /*! \brief Экземпляр главного окна Xlib */ Window window; /*! \brief Изображение для вывода на экран */ XImage * image; /*! \brief Палитра цветов для векторной отрисовки * \note Черный имеет цвет {001} для отличия от "прозрачного" черного {000} */ static const int texturepalette[] = { /* Светлее ▁▁▂▂▃▃▄▄▅▅▆▆▇▇██ Темнее */ /* Светло-коричневый */ 0xe6ac73, 0xcd9966, 0xb48659, 0x9b734c, /* Темно-коричневый */ 0x806040, 0x674d33, 0x4e3a26, 0x352719, /* Зеленый */ 0x59b368, 0x40a05b, 0x278d4e, 0x0e7a41, /* Серый */ 0xc6c6c6, 0xadb3b9, 0x94a0ac, 0x7b8d9f, /* Черный */ 0x000001 }; /*! \brief Векторные инструкции для рисования текстур */ static const char * vecdata[TEXTURE_COUNT] = { /* TEXINDEX_EMPTY */ (char[]) { TEXVEC_EXIT }, /* TEXINDEX_MINUS */ (char[]) { TEXVEC_COLOR, 5, TEXVEC_LINESE, 32, 5, 16, TEXVEC_LINESE, 0, 21, 16, TEXVEC_LINESW, 63, 21, 16, TEXVEC_LINEDW, 0, 21, 4, TEXVEC_LINEDW, 31, 36, 4, TEXVEC_LINESW, 31, 5, 16, TEXVEC_LINESE, 0, 24, 16, TEXVEC_LINESW, 63, 24, 16, TEXVEC_LINEDW, 63, 21, 4, TEXVEC_LINEDW, 32, 36, 4, TEXVEC_COLOR, 4, TEXVEC_FILL, 31, 8, TEXVEC_COLOR, 6, TEXVEC_FILL, 33, 38, TEXVEC_COLOR, 7, TEXVEC_FILL, 30, 38, TEXVEC_EXIT }, /* TEXINDEX_WIRE */ (char[]) { TEXVEC_REPEAT, 1 }, /* TEXINDEX_PLUS */ (char[]) { TEXVEC_COLOR, 1, TEXVEC_LINESE, 32, 5, 16, TEXVEC_LINESE, 0, 21, 16, TEXVEC_LINESW, 63, 21, 16, TEXVEC_LINEDW, 63, 21, 4, TEXVEC_LINEDW, 31, 36, 4, TEXVEC_LINESW, 31, 5, 16, TEXVEC_LINESE, 0, 24, 16, TEXVEC_LINESW, 63, 24, 16, TEXVEC_LINEDW, 63, 21, 4, TEXVEC_LINEDW, 32, 36, 4, TEXVEC_COLOR, 0, TEXVEC_FILL, 31, 8, TEXVEC_COLOR, 2, TEXVEC_FILL, 33, 38, TEXVEC_COLOR, 3, TEXVEC_FILL, 30, 38, TEXVEC_EXIT }, /* Не используется */ (char[]) { TEXVEC_EXIT }, /* Не используется */ (char[]) { TEXVEC_EXIT }, /* TEXINDEX_NOR */ (char[]) { TEXVEC_COLOR, 9, TEXVEC_LINESE, 32, 2, 16, TEXVEC_LINESE, 0, 18, 16, TEXVEC_LINESW, 63, 18, 16, TEXVEC_LINEDW, 0, 18, 4, TEXVEC_LINEDW, 31, 33, 4, TEXVEC_LINESW, 31, 2, 16, TEXVEC_LINESE, 0, 21, 16, TEXVEC_LINESW, 63, 21, 16, TEXVEC_LINEDW, 63, 18, 4, TEXVEC_LINEDW, 32, 33, 4, TEXVEC_COLOR, 8, TEXVEC_FILL, 31, 5, TEXVEC_COLOR, 10, TEXVEC_FILL, 33, 35, TEXVEC_COLOR, 11, TEXVEC_FILL, 30, 35, TEXVEC_EXIT }, /* TEXINDEX_HOLE */ (char[]) { TEXVEC_COLOR, 13, TEXVEC_LINESE, 32, 9, 12, TEXVEC_LINESE, 8, 21, 12, TEXVEC_LINESW, 31, 9, 12, TEXVEC_LINESW, 55, 21, 12, TEXVEC_LINESE, 32, 12, 9, TEXVEC_LINESE, 14, 21, 9, TEXVEC_LINESW, 31, 12, 9, TEXVEC_LINESW, 49, 21, 9, TEXVEC_COLOR, 14, TEXVEC_LINEDW, 31, 13, 4, TEXVEC_LINESW, 31, 16, 7, TEXVEC_FILL, 30, 14, TEXVEC_COLOR, 12, TEXVEC_FILL, 32, 11, TEXVEC_COLOR, 15, TEXVEC_LINEDW, 32, 13, 4, TEXVEC_LINESE, 32, 16, 7, TEXVEC_FILL, 33, 14, TEXVEC_COLOR, 16, TEXVEC_FILL, 31, 19, TEXVEC_EXIT } }; static void _texture_draw(int t, int x, int y); /*! \brief Создает изображение и сопутствующие сущности */ static void _image_create(void) { display = XOpenDisplay(0); window = XCreateSimpleWindow(display, RootWindow(display, DefaultScreen(display)), 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, 1, 0, 0); image = XCreateImage(display, DefaultVisual(display, DefaultScreen(display)), DefaultDepth(display, DefaultScreen(display)), 2, 0, (char *) pixdata, IMAGE_WIDTH, IMAGE_HEIGHT, 32, 0); } /* \brief Обнуляет изображение, заполняя его черным цветом */ static void _image_reset(void) { for (int y = 0; y < IMAGE_HEIGHT; y++) for (int x = 0; x < IMAGE_WIDTH; x++) pixdata[y][x] = 0; } /*! \brief Конвертирует символ из чертежа в индекс текстуры * \param[in] elem Символ чертежа * \return Индекс текстуры */ static int _map2texture(char elem) { return ((elem >> 4) & 1) << 2 | (elem & 3); } /*! \brief Собирает изображение из отдельных тайлов согласно карте */ static void _image_compile(void) { for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) _texture_draw(_map2texture(mapdata[y][x]), x, y); } /*! \brief Рисует изображение на экране */ static void _image_draw(void) { XPutImage(display, window, DefaultGC(display, DefaultScreen(display)), image, 0, 0, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT); XMapWindow(display, window); XFlush(display); } /*! \brief Рисует отверстия на печатной плате */ static void _image_drill(void) { for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) if ((x % 14 == 2) && (y % 4 == 3)) _texture_draw(TEXINDEX_HOLE, x, y); } /*! \brief Применяет шейдер LCD-эффекта на изображение * \param[in] x X-координата изображения * \param[in] y Y-координата изображения */ static void _shader_lcd(int x, int y) { pixdata[y][x] &= 0xe0e0e0 | (31 << ((x % 3) << 3)); } /*! \brief Применяет шейдер случайного шума на изображение * \param[in] x X-координата изображения * \param[in] y Y-координата изображения */ static void _shader_noise(int x, int y) { pixdata[y][x] += 0x0f0f0f & (random *= 16777619); } /*! \brief Накладывает на изображение различные эффекты */ static void _image_postprocess(void) { for (int y = 0; y < IMAGE_HEIGHT; y++) for (int x = 0; x < IMAGE_WIDTH; x++) { _shader_lcd(x, y); _shader_noise(x, y); } } /*! \brief Отрисовывает текстуру на главном холсте по указанным координатам * \param[in] t Индекс текстуры * \param[in] x X координата тайла * \param[in] y Y координата тайла */ static void _texture_draw(int t, int x, int y) { for (int ty = 0; ty < TEXTURE_HEIGHT; ty++) for (int tx = 0; tx < TEXTURE_WIDTH; tx++) if (textures[t][ty][tx]) pixdata[ty + y * (TEXTURE_TOP_HEIGHT / 2) + x * (TEXTURE_TOP_HEIGHT / 2)] [tx + IMAGE_SHIFTX + x * (TEXTURE_TOP_WIDTH / 2) - y * TEXTURE_TOP_HEIGHT] = textures[t][ty][tx]; } /*! \brief Производит закраску области текстуры * \param[in] t Индекс текстуры * \param[in] x X-координата начала * \param[in] y Y-координата начала * \param[in] source Исходный цвет * \param[in] target Новый цвет */ static void _texture_fill(int t, int x, int y, int source, int target) { if ((x >= TEXTURE_WIDTH || y >= TEXTURE_HEIGHT || x < 0 || y < 0) || (textures[t][y][x] == target) || (textures[t][y][x] != source)) return; textures[t][y][x] = target; _texture_fill(t, x - 1, y, source, target); _texture_fill(t, x + 1, y, source, target); _texture_fill(t, x, y - 1, source, target); _texture_fill(t, x, y + 1, source, target); } /*! \brief Рисует изометрическую линию по направлению SE * \param[in] t Индекс текстуры * \param[in] x X-координата начала * \param[in] y Y-координата начала * \param[in] c Длина линии по короткой стороне * \param[in] color Цвет рисования */ static void _texture_linese(int t, int x, int y, int c, int color) { while (c--) { textures[t][y][x++] = color; textures[t][y++][x++] = color; } } /*! \brief Рисует изометрическую линию по направлению SW * \param[in] t Индекс текстуры * \param[in] x X-координата начала * \param[in] y Y-координата начала * \param[in] c Длина линии по короткой стороне * \param[in] color Цвет рисования */ static void _texture_linesw(int t, int x, int y, int c, int color) { while (c--) { textures[t][y][x--] = color; textures[t][y++][x--] = color; } } /*! \brief Рисует линию вниз * \param[in] t Индекс текстуры * \param[in] x X-координата начала * \param[in] y Y-координата начала * \param[in] c Длина линии * \param[in] color Цвет рисования */ static void _texture_linedown(int t, int x, int y, int c, int color) { while (c--) textures[t][y++][x] = color; } /*! \brief Создает все текстуры, интерпретатор векторных инструкций */ static void _textures_create(void) { for (int tex = 0; tex < TEXTURE_COUNT; tex++) { const char * ptr = vecdata[tex]; int c = 0; while (*ptr != TEXVEC_EXIT) { switch (*ptr) { case TEXVEC_LINESE: _texture_linese(tex, ptr[1], ptr[2], ptr[3], c); ptr += 4; break; case TEXVEC_LINESW: _texture_linesw(tex, ptr[1], ptr[2], ptr[3], c); ptr += 4; break; case TEXVEC_LINEDW: _texture_linedown(tex, ptr[1], ptr[2], ptr[3], c); ptr += 4; break; case TEXVEC_FILL: _texture_fill(tex, ptr[1], ptr[2], 0, c); ptr += 3; break; case TEXVEC_REPEAT: ptr = vecdata[ptr[1]]; break; case TEXVEC_COLOR: c = texturepalette[ptr[1]]; ptr += 2; break; } } } } /*! \brief Читает данные файла-чертежа и загружает их в карту * \param[in] filename Имя файла-чертежа */ static void _map_read(const char * filename) { int f = open(filename, 0); read(f, mapdata, MAP_FILEDATA); close(f); } /*! \brief Заменяет иллюстративные входы из файла-конфигурации на вход * в виде провода чтобы работала логика распространения фронта волны */ static void _map_wire_inputs(void) { for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) if ((x % 14 == 2) && (y % 4 == 3)) mapdata[y][x] = MAPCHAR_WIRE; } /*! \brief Производит заливку проводника нужным состоянием * \param[in] t Игнорируется, артефакт автогенерации кода * \param[in] x X-координата заливки * \param[in] y Y-координата заливки * \param[in] c Исходное состояние * \param[in] l Целевое состояние */ static void _map_fill(int t, int x, int y, int c, int l) { if ((x >= MAP_WIDTH || y >= MAP_HEIGHT || x < 0 || y < 0) || (mapdata[y][x] == l) || (mapdata[y][x] != c)) return; mapdata[y][x] = l; _map_fill(t, x - 1, y, c, l); _map_fill(t, x + 1, y, c, l); _map_fill(t, x, y - 1, c, l); _map_fill(t, x, y + 1, c, l); } /*! \brief Включает соответствующие входы схемы в зависимости от значения * счетчика. * \param[in] counter Счетчик */ static void _map_wire_counter(int counter) { _map_fill(0, 2, 3, mapdata[3][2], counter & 1 ? MAPCHAR_PLUS : MAPCHAR_MINUS); _map_fill(0, 2, 7, mapdata[7][2], counter & 2 ? MAPCHAR_PLUS : MAPCHAR_MINUS); _map_fill(0, 2, 11, mapdata[11][2], counter & 4 ? MAPCHAR_PLUS : MAPCHAR_MINUS); _map_fill(0, 2, 15, mapdata[15][2], counter & 8 ? MAPCHAR_PLUS : MAPCHAR_MINUS); } /*! \brief Проводит расчет выходного (результирующего) тока после NOR-узла */ static void _map_process_gates(void) { for (int i = 0; i < MAP_ITERATIONS; i++) for (int y = 0; y < MAP_HEIGHT; y++) for (int x = 0; x < MAP_WIDTH; x++) if (mapdata[y][x] == MAPCHAR_NOR) _map_fill(0, x + 1, y, mapdata[y][x + 1], !(mapdata[y - 1][x] == MAPCHAR_PLUS ? 1 : 0 || mapdata[y + 1][x] == MAPCHAR_PLUS ? 1 : 0) ? MAPCHAR_PLUS : MAPCHAR_MINUS); } int main(int argc, char * args[]) { _image_create(); _textures_create(); unsigned int counter = 1; while (counter++) { _map_read(args[ARG_BLUEPRINT]); _map_wire_inputs(); _map_wire_counter(counter); _map_process_gates(); _image_reset(); _image_compile(); _image_drill(); _image_postprocess(); _image_draw(); sleep(1); } return 0; } Эпилог Вот и всё. Несмотря на то, что я не занял никаких призовых мест, это был достаточно любопытный опыт (умерший за два дня до начала приема работ HDD этому поспособствовал). Было что-то неправильное в том, чтобы писать код, максимально не читаемый и одновременно абсолютно валидный с точки зрения языка. Однако нельзя сказать, что это было неинтересно. Когда утилита, подсчитывающая токены говорит, что код нарушает правила превышая лимит на 2000+ токенов, а ты не знаешь, как его сократить — это вызов. Когда размер файла превышает лимиты, а ты хочешь добавить какой-то графический эффект — это вызов. Это было трудно, вместить столько функционала, и тем слаще было читать вывод утилиты, которая после нескольких сотен исправлений наконец-таки сказала, что код можно выгружать. Я бы порекомендовал читателям поучаствовать в конкурсе хотя бы просто для того, чтобы приобщиться и испытать свои силы. Да, никаких наград или призов это не дает, но зато вы будете знать, что под ником zhestokyshveller на сайте IOCCC скрываетесь вы. =========== Источник: habr.com =========== Похожие новости:
Системное программирование ), #_otladka ( Отладка ), #_c |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 16:06
Часовой пояс: UTC + 5