Tour de IOCCC: 1993/dgibson
#include <stdio.h>
#define maxc (prs >> 16)
#define maxr (prs & 0xff00) >> 8
#define find (col > 1)
#define endb || (prs & 16) && X(0) || chpa || (eols, 0)
#define lnst doln ? 0 : (init, 0) || (prs & 128) && X(0)
#define L endb) || 0 || (find ? 0 : (rrr++, rrc = 0)) || (lnst
#define X(p) (dfc++ && ((prs & 48) != 32 || rrc < maxc) && ++ccl && \
(find ? (bat = bit, bit = but, but = p, ccl == col && \
(ret = bat + bit + but) + 1) : (las = las * 2 + \
(((main(ccl + 1, cro - 1) + main(ccl + 1, cro) + \
main(ccl + 1, cro + 1) - p) | p) == 3), occ |= las & 1, \
printf(" %c", "_O"[las & 1]), ccl == 2 && (sta |= (las & 3) << 6),\
0)))
#define _ || (find || rrc++, 0) || X(0)
#define O || (find || rrc++, 0) || X(1)
#define chpa (find && col > ccl && ((ret = bit + but) + 1))
#define eols (!find && (cro == 1 ? (sta |= occ << 3) : cro == 2 && \
(sta |= occ << 2), sta |= (las & 3) << 4))
#define init (find ? (ret = bit = but = 0) : (ocp = occ, las = occ = 0, \
printf("\nL")), dfc = (prs & 192) != 64, ccl = 0)
#define doln !(dfl++ && (((prs & 3) != 2) || rrr < maxr) && \
(++cro == row || !find))
#define recu (col =- col, (ccl = find ? rcf : rcl), cro = rro, 0 _ || \
(rrc >= maxc ? 0 : (find ? rcf = ccl : (rcl = ccl), \
main(-col, row))))
#define addl (lnst || (rro = cro, find ? rcf = ccl : (rrc = 0, rcl = ccl), \
0) || main(-col, row) || (ccl = rcl + 1, 0) endb)
#define outp (find || printf("\n\nGEN %d STAT %ld\nEND\n", ++gen, \
(long)ccl << 16 | cro << 8 | (sta | occ | ocp << 1) ^ 0x66))
#define GEN endb) || (prs & 1) && addl || (outp, 0)), ret)); } int gen =
#define STAT ;long prs =
#define END ;
#define LIFE int bat, bit, but, las, gen, ret, rcl, rcf, rro, rrc, rrr, \
occ, ocp, sta, dfc; long prs; main(int col, int row) { \
int dfl = ((prs & 12) != 4), ccl = 0, cro = 0; return \
col == 1 && printf("LIFE\n"), (col < 0 ? recu : \
(((ret = 0) || (prs & 8) && addl || 0 && (0
#include "life.d"
매크로 이름에서 라이프 게임1인 건 알 수 있습니다만, life.d
파일은 어디 있으며 main 함수는 왜 없을까요?
뭘 하는 프로그램인가?
이 프로그램은 라이프 게임을 좀 특이한 방법으로 구현합니다. 실제로 구경을 하기 위해서는 먼저 최초 상태를 다음과 같이 넣어 줘야 합니다.
$ cat > life.d
LIFE
L _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
L _ _ O _ _ _ _ _ _ _ _ O _ _ _ _ O _ _ _
L _ O _ _ _ _ _ O _ _ O _ _ _ _ O _ _ _ _
L _ O _ _ _ _ _ _ O O O _ _ _ _ O _ _ _ _
L _ O _ _ O _ _ _ _ _ _ _ _ _ _ O _ _ O _
L _ O O O _ _ _ _ _ _ _ _ _ _ _ O O O _ _
L _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
GEN 0
END
^D
(위에서 ^D
는 Ctrl-D로 입력해야 하며, 파일의 끝을 나타냅니다.) 아니면 적절한 텍스트 편집기로 넣어 주던지, 하여튼 life.d를 위와 같이 만듭니다. 이것이 원래 상태입니다.
이제 다음과 같이 하면 실시간으로(?) 변하는 모습을 볼 수 있습니다. (bash에서 테스트했으며, 다른 셸에도 비슷하게 가능합니다.)
$ while true; do gcc dgibson.c -o dgibson && ./dgibson | tee life.d; done
LIFE
L _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
L _ O O _ _ _ _ _ O _ O O _ _ _ O O _ _ _
L O O O _ _ _ _ _ O O O _ _ _ O O O _ _ _
L O O _ O _ _ _ _ _ O _ _ _ _ O O _ O _ _
L _ O O O _ _ _ _ _ _ _ _ _ _ _ O O O _ _
L _ _ O _ _ _ _ _ _ _ _ _ _ _ _ _ O _ _ _
GEN 1 STAT 1312933
END
LIFE
L _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
L _ O _ O _ _ _ _ _ O _ O O _ _ O _ O _ _
L _ _ _ _ O _ _ _ _ O _ _ O _ _ _ _ _ O _
L _ _ _ _ O _ _ _ _ O O O _ _ _ _ _ _ O _
L _ O _ _ O _ _ _ _ _ _ _ _ _ _ O _ _ O _
L _ _ O O O _ _ _ _ _ _ _ _ _ _ _ O O O _
L _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
GEN 2 STAT 1312772
END
LIFE
L _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ O O O _ _ _ _ _ _ _
L _ _ _ O O _ _ _ O O _ _ O _ _ _ _ O O _
L _ _ _ O O O _ _ _ O O O _ _ _ _ _ O O O
L _ _ O _ O O _ _ _ _ O _ _ _ _ _ O _ O O
L _ _ O O O _ _ _ _ _ _ _ _ _ _ _ O O O _
L _ _ _ O _ _ _ _ _ _ _ _ _ _ _ _ _ O _
GEN 3 STAT 1247057
END
(이하 생략)
즉 요는, 이 프로그램은 life.d
가 있을 때 컴파일하면 그 다음 상태에 해당하는 life.d
를 출력합니다! 이 출력을 다시 life.d
에 저장하고 반복하면 다음 단계를 순서대로 볼 수 있습니다.
나중에 가면 아주 느릿느릿해지긴 합니다만, 저 같은 경우 2분 정도 실행한 끝에 다음 결과를 얻을 수 있었습니다.
LIFE
L _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ _ O _ O _ O _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ O _ _ _ _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ O _ _ _ O _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ _ _ _ O _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ O _ O _ _ _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ O O _ _ _ _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ O _ _ _ _ _ _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ O _ _ O O _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ _ O _ O _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ _ _ _ _ O _ _ _ _ O _ _ _
L _ _ _ _ _ _ _ _ _ _ _ _ _ O O _ _ _ O O O _ _
L _ _ _ _ _ _ _ _ _ _ _ _ O _ _ O O _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ _ _ _ O _ O _ O O _ O O _
L _ _ _ _ _ _ _ _ _ _ _ _ _ _ O _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ _ _ O O _ O O _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ _ _ O _ _ _ O _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ _ _ _ O O O _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ _ _ O O _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ _ O _ O _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ _ _ O _ _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ O O O _ _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ O _ O _ _ _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ O _ O _ _ _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ O O _ _ _ _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ _ O _ _ _ _ _ _ _ _ _ _ _ _ _
L _ _ _ _ _ _ _ _ O O O O _ _ _ _ _ _ _ _ _ _ _
L _ O _ O _ _ _ _ O _ _ _ O _ _ O _ O _ _ _ _ _
L _ _ _ _ O _ _ _ _ O _ _ O _ _ _ _ _ O _ _ _ _
L _ _ _ _ O _ _ _ _ O O O _ _ _ _ _ _ O _ _ _ _
L _ O _ _ O _ _ _ _ _ _ _ _ _ _ O _ _ O _ _ _ _
L _ _ O O O _ _ _ _ _ _ _ _ _ _ _ O O O _ _ _ _
L _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
GEN 54 STAT 1519364
END
어떻게 동작하는가?
life.d
는 다음과 같은 형태로 되어 있는 적법한 C 코드입니다. (미리 매크로가 선언되었다는 전제 하에)
/* 프로그램의 시작 부분 */
LIFE
/* 현재 상태. L은 줄의 시작을, _와 O는 셀을 나타내는 매크로. */
L _ _ _ _ _
L _ _ _ O _
L _ O _ O _
L _ _ O O _
L _ _ _ _ _
/* 세대 번호와 통계. */
GEN 4 STAT 328960
/* 프로그램의 끝 부분 */
END
먼저 LIFE와 GEN, STAT, 그리고 END 매크로는 고정이므로, 이들만 확장해 보겠습니다. 직접 cpp 등으로 전처리를 해 보면 아시겠지만 무작정 확장하면 코드가 매우 복잡해집니다. 따라서 순서대로 하나 하나 해 보도록 하죠. 한 번만 매크로 치환을 하면 다음과 같이 됩니다.
#include <stdio.h>
int bat, bit, but, las, gen, ret, rcl, rcf, rro, rrc, rrr, occ, ocp, sta, dfc;
long prs;
main(int col, int row) {
int dfl = ((prs & 12) != 4), ccl = 0, cro = 0;
return col == 1 && printf("LIFE\n"),
(col < 0 ? recu :
(((ret = 0) || (prs & 8) && addl || 0 && (0
L _ _ _ _ _
L _ _ _ O _
L _ O _ O _
L _ _ O O _
L _ _ _ _ _
endb) || (prs & 1) && addl || (outp, 0)), ret)
);
}
int gen = 4;
long prs = 328960;
수식이 중간에서 뚝 끊겨 있습니다. -_-; 이로부터 예상할 수 있는 것은, 중간의 L
, _
, O
매크로들이 사실은 테이블 참조를 대신하는 것이며, 이 매크로들을 통과하면 해당하는 세포가 현재 살아 있는지 죽어 있는지 확인할 수 있을 거라는 점입니다.
또한 여기에는 다른 매크로들도 가득 들어 있는데, 지금 현재 단계에서는 recu
, addl
, endb
세 개의 매크로가 치환되어야 합니다. 그나마 다행인 것은 endb
를 뺀 거의 모든 매크로가 완전한 하나의 식으로 치환되기 때문에 분석하기 그나마 수월하다는 점이겠습니다.
우선 매크로를 살펴 보면 main 함수 말고 다른 함수가 만들어지거나 하는 경우는 없으므로, 이 main 함수는 거의 모든 코드가 return에 들어 있음을 알 수 있습니다. 따라서 첫번째 콤마 앞의 문장은 별도로 분리할 수 있습니다. (&&
대신 if 문을 쓰는 것도 잊지 맙시다.)
#include <stdio.h>
int bat, bit, but, las, gen, ret, rcl, rcf, rro, rrc, rrr, occ, ocp, sta, dfc;
long prs;
main(int col, int row) {
int dfl = ((prs & 12) != 4), ccl = 0, cro = 0;
if (col == 1) printf("LIFE\n");
return (col < 0 ? recu :
(((ret = 0) || (prs & 8) && addl || 0 && (0
L _ _ _ _ _
L _ _ _ O _
L _ O _ O _
L _ _ O O _
L _ _ _ _ _
endb) || (prs & 1) && addl || (outp, 0)), ret)
);
}
int gen = 4;
long prs = 328960;
남아 있는 반환값은 거대한 삼항 연산자 ?:
로 둘러 쌓여 있는데 이는 물론 if 문으로 고칠 수 있습니다. 물론 중간에 매크로가 치환되는 도중에 이 식이 끝날 경우를 배제할 수는 없는데요, 그런 경우가 발생하지 않음은 매크로에 등장하는 괄호 갯수와 짝을 검사해서 증명할 수 있겠습니다. TODO 여기에 대해서 좀 정확히 설명할 것
또한 recu
는 코드 상에서 여기 단 하나만 나오기 때문에 안심하고 치환할 수 있습니다. 따라서,
#include <stdio.h>
int bat, bit, but, las, gen, ret, rcl, rcf, rro, rrc, rrr, occ, ocp, sta, dfc;
long prs;
main(int col, int row) {
int dfl = ((prs & 12) != 4), ccl = 0, cro = 0;
if (col == 1) printf("LIFE\n");
if (col < 0) {
return (col =- col, (ccl = find ? rcf : rcl), cro = rro, 0 _ ||
(rrc >= maxc ? 0 : (find ? rcf = ccl : (rcl = ccl),
main(-col, row))));
} else {
return (((ret = 0) || (prs & 8) && addl || 0 && (0
L _ _ _ _ _
L _ _ _ O _
L _ O _ O _
L _ _ O O _
L _ _ _ _ _
endb) || (prs & 1) && addl || (outp, 0)), ret);
}
}
int gen = 4;
long prs = 328960;
치환된 recu
도 그렇게 간단하지는 않습니다. 먼저 콤마로 구분된 문장들은 별도의 문장으로 분리하고, 삼항 연산자와 ||
를 if 문으로 바꾸도록 하겠습니다. 순서대로 차근 차근 하면 어렵지 않은데 여기서는 너무 길어지므로 생략하고, 결과적으로는 다음 코드가 됩니다. (자세한 방법은 1988/litmaath에 서술되어 있습니다.)
#include <stdio.h>
int bat, bit, but, las, gen, ret, rcl, rcf, rro, rrc, rrr, occ, ocp, sta, dfc;
long prs;
main(int col, int row) {
int dfl = ((prs & 12) != 4), ccl = 0, cro = 0;
if (col == 1) printf("LIFE\n");
if (col < 0) {
col = -col;
ccl = (find ? rcf : rcl);
cro = rro;
if (0 _) {
return 1; /* (0 _)가 참이므로 나머지에 관계 없이 항상 참 */
} else {
if (!(rrc >= maxc)) {
if (find) rcf = ccl;
else rcl = ccl;
}
return main(-col, row);
}
} else {
return (((ret = 0) || (prs & 8) && addl || 0 && (0
L _ _ _ _ _
L _ _ _ O _
L _ O _ O _
L _ _ O O _
L _ _ _ _ _
endb) || (prs & 1) && addl || (outp, 0)), ret);
}
}
int gen = 4;
long prs = 328960;
코드가 슬슬 길어지려는 조짐이 보이고 있으므로 두번째 문장을 별도의 함수로 분리하겠습니다. 이 문장은 dfl
, ccl
, cro
모두에 의존하므로 이들의 값을 넘겨 줘야 제대로 동작하겠죠.
#include <stdio.h>
#include <assert.h>
int bat, bit, but, las, gen, ret, rcl, rcf, rro, rrc, rrr, occ, ocp, sta, dfc;
long prs;
int main_nonnegcol(int col, int row, int dfl, int ccl, int cro) {
assert(col >= 0);
return (((ret = 0) || (prs & 8) && addl || 0 && (0
L _ _ _ _ _
L _ _ _ O _
L _ O _ O _
L _ _ O O _
L _ _ _ _ _
endb) || (prs & 1) && addl || (outp, 0)), ret);
}
main(int col, int row) {
int dfl = ((prs & 12) != 4), ccl = 0, cro = 0;
if (col == 1) printf("LIFE\n");
if (col < 0) {
col = -col;
ccl = (find ? rcf : rcl);
cro = rro;
if (0 _) {
return 1; /* (0 _)가 참이므로 나머지에 관계 없이 항상 참 */
} else {
if (!(rrc >= maxc)) {
if (find) rcf = ccl;
else rcl = ccl;
}
return main(-col, row);
}
} else {
return main_nonnegcol(col, row, dfl, ccl, cro);
}
}
int gen = 4;
long prs = 328960;
이제 main_nonnegcol 함수를 집중적으로 살펴 봅시다. 앞과 같은 방법으로 ||
를 치환하면 다음과 같은 코드를 얻습니다.
int main_nonnegcol(int col, int row, int dfl, int ccl, int cro) {
assert(col >= 0);
if (!(ret = 0))
if (!((prs & 8) && addl))
if (!(0 && (0
L _ _ _ _ _
L _ _ _ O _
L _ O _ O _
L _ _ O O _
L _ _ _ _ _
endb)))
if (!((prs & 1) && addl))
if (!((outp, 0)))
;
return ret;
}
여기서는 a || ...
를 모두 if (!a) ...
꼴로 변환했습니다. 전체 ||
수식은 항상 0으로 평가될 수 밖에 없기 때문에 if 안에 들어 가는 마지막 문장은 빈 문장이어도 됩니다. 이제 ret = 0
과 같이 항상 0이라서 그 시점에서 문장으로 바꿀 수 있는 것들을 정리합니다.
int main_nonnegcol(int col, int row, int dfl, int ccl, int cro) {
assert(col >= 0);
ret = 0;
if (!((prs & 8) && addl))
if (!(0 && (0
L _ _ _ _ _
L _ _ _ O _
L _ O _ O _
L _ _ O O _
L _ _ _ _ _
endb)))
if (!((prs & 1) && addl))
outp;
return ret;
}
다음으로 L
매크로를 확장하도록 하겠습니다. 이제 대략적으로 구조가 짐작이 가기 시작하네요.
int main_nonnegcol(int col, int row, int dfl, int ccl, int cro) {
assert(col >= 0);
ret = 0;
if (!((prs & 8) && addl))
if (!(0 && (0
endb) || 0 || (find ? 0 : (rrr++, rrc = 0)) || (lnst
_ _ _ _ _
endb) || 0 || (find ? 0 : (rrr++, rrc = 0)) || (lnst
_ _ _ O _
endb) || 0 || (find ? 0 : (rrr++, rrc = 0)) || (lnst
_ O _ O _
endb) || 0 || (find ? 0 : (rrr++, rrc = 0)) || (lnst
_ _ O O _
endb) || 0 || (find ? 0 : (rrr++, rrc = 0)) || (lnst
_ _ _ _ _
endb)))
if (!((prs & 1) && addl))
outp;
return ret;
}
이해를 돕기 위해서 괄호를 넣고 들여쓰기도 넣겠습니다.
int main_nonnegcol(int col, int row, int dfl, int ccl, int cro) {
assert(col >= 0);
ret = 0;
if (!((prs & 8) && addl))
if (!(
(0 && (0 endb)) ||
0 ||
(find ? 0 : (rrr++, rrc = 0)) ||
(lnst _ _ _ _ _ endb) ||
0 ||
(find ? 0 : (rrr++, rrc = 0)) ||
(lnst _ _ _ O _ endb) ||
0 ||
(find ? 0 : (rrr++, rrc = 0)) ||
(lnst _ O _ O _ endb) ||
0 ||
(find ? 0 : (rrr++, rrc = 0)) ||
(lnst _ _ O O _ endb) ||
0 ||
(find ? 0 : (rrr++, rrc = 0)) ||
(lnst _ _ _ _ _ endb)
))
if (!((prs & 1) && addl))
outp;
return ret;
}
중간의 ||
연산자들의 묶음은 각각 if 문 하나씩으로 고쳐 쓸 수 있습니다.2 또한 0
과 같이 전혀 의미 없는 항들은 지울 수 있고, (0 && (0 endb))
도 결과가 0이 되는 게 확실하므로 지울 수 있습니다. (마지막 endb
는 L
이 확장되며 만들어진 것입니다. 순전히 반복 코드를 위한 부분이죠.) 따라서,
int main_nonnegcol(int col, int row, int dfl, int ccl, int cro) {
assert(col >= 0);
ret = 0;
if (!((prs & 8) && addl))
if (!(find ? 0 : (rrr++, rrc = 0)))
if (!(lnst _ _ _ _ _ endb))
if (!(find ? 0 : (rrr++, rrc = 0)))
if (!(lnst _ _ _ O _ endb))
if (!(find ? 0 : (rrr++, rrc = 0)))
if (!(lnst _ O _ O _ endb))
if (!(find ? 0 : (rrr++, rrc = 0)))
if (!(lnst _ _ O O _ endb))
if (!(find ? 0 : (rrr++, rrc = 0)))
if (!(lnst _ _ _ _ _ endb))
if (!((prs & 1) && addl))
outp;
return ret;
}
find ? 0 : (rrr++, rrc = 0)
은 항상 0을 반환하므로, 이 조건은 다음과 같이 별도의 문장으로 다시 쓸 수 있겠습니다.
int main_nonnegcol(int col, int row, int dfl, int ccl, int cro) {
assert(col >= 0);
ret = 0;
if (!((prs & 8) && addl)) {
(find ? 0 : (rrr++, rrc = 0));
if (!(lnst _ _ _ _ _ endb)) {
(find ? 0 : (rrr++, rrc = 0));
if (!(lnst _ _ _ O _ endb)) {
(find ? 0 : (rrr++, rrc = 0));
if (!(lnst _ O _ O _ endb)) {
(find ? 0 : (rrr++, rrc = 0));
if (!(lnst _ _ O O _ endb)) {
(find ? 0 : (rrr++, rrc = 0));
if (!(lnst _ _ _ _ _ endb)) {
if (!((prs & 1) && addl))
outp;
}
}
}
}
}
}
return ret;
}
(들여쓰기가 이상하다고 생각하시면 한 줄에 탭 열 개를 넣어 보시고 생각을 바꾸시길 바랍니다.) 삼항 연산자도 덜어 내면 다음과 같은 코드가 되겠지요.
int main_nonnegcol(int col, int row, int dfl, int ccl, int cro) {
assert(col >= 0);
ret = 0;
if (!((prs & 8) && addl)) {
if (!find) rrr++, rrc = 0;
if (!(lnst _ _ _ _ _ endb)) {
if (!find) rrr++, rrc = 0;
if (!(lnst _ _ _ O _ endb)) {
if (!find) rrr++, rrc = 0;
if (!(lnst _ O _ O _ endb)) {
if (!find) rrr++, rrc = 0;
if (!(lnst _ _ O O _ endb)) {
if (!find) rrr++, rrc = 0;
if (!(lnst _ _ _ _ _ endb)) {
if (!((prs & 1) && addl))
outp;
}
}
}
}
}
}
return ret;
}
이제 남아 있는 매크로는 addl
, lnst
, endb
, find
, outp
, 그리고 물론 O
와 _
가 있습니다. 이 중 addl
과 outp
를 제외한 나머지 매크로는 매우 자주 나오기 때문에 나중에 처리하고, 이 두 매크로를 확장해 봅시다.
int main_nonnegcol(int col, int row, int dfl, int ccl, int cro) {
assert(col >= 0);
ret = 0;
if (!((prs & 8) && (lnst ||
(rro = cro, find ? rcf = ccl : (rrc = 0, rcl = ccl), 0) ||
main(-col, row) || (ccl = rcl + 1, 0) endb))) {
if (!find) rrr++, rrc = 0;
if (!(lnst _ _ _ _ _ endb)) {
if (!find) rrr++, rrc = 0;
if (!(lnst _ _ _ O _ endb)) {
if (!find) rrr++, rrc = 0;
if (!(lnst _ O _ O _ endb)) {
if (!find) rrr++, rrc = 0;
if (!(lnst _ _ O O _ endb)) {
if (!find) rrr++, rrc = 0;
if (!(lnst _ _ _ _ _ endb)) {
if (!((prs & 1) && (lnst ||
(rro = cro, find ? rcf = ccl : (rrc = 0, rcl = ccl), 0) ||
main(-col, row) || (ccl = rcl + 1, 0) endb))) {
(find || printf("\n\nGEN %d STAT %ld\nEND\n", ++gen,
(long)ccl << 16 | cro << 8 | (sta | occ | ocp << 1) ^ 0x66));
}
}
}
}
}
}
}
return ret;
}
lnst
와 endb
매크로가 더 추가되었습니다! 이제 꼼짝 없이(?) 이 매크로들을 분석해야 하겠는데요, 여기에 대해 살펴 보기 전에 먼저 이 코드를 간단하게 정리해 보도록 하겠습니다.
위의 코드는 맨 바깥이 다음과 같이 되어 있습니다.
ret = 0;
if (!((prs & 8) && (lnst || ... endb))) {
...
}
return ret;
if 문의 조건이 성립하지 않으면 반환하는 것은 ret
인 게 당연하니까, 이 코드는 다음과 같이 고칠 수 있고:
ret = 0;
if ((prs & 8) && (lnst || ... endb)) return ret;
...
return ret;
if 문을 두 개로 나누면,
ret = 0;
if (prs & 8) {
if (lnst || ... endb) return ret;
}
...
return ret;
네. 좀 복잡한 문장을 고칠 여지가 생겼습니다! 지금까지는 안쪽 조건, 즉 lnst || ... endb
가 다른 조건(prs & 8
)과 함께 들어 있어서 문장으로 바꾸기 힘들었지만, 이제 독립된 조건이 되었으니 문장으로 바꾸기 더 쉬울 것입니다.
실제로 이 조건이 어떻게 해석되는지는 lnst
와 endb
를 살펴 본 뒤로 미루도록 하겠습니다. 일단 똑같은 코드가 나타나는 뒷부분도 같은 방법으로 독립된 문장으로 바꾸면,
int main_nonnegcol(int col, int row, int dfl, int ccl, int cro) {
assert(col >= 0);
ret = 0;
if (prs & 8) {
if (lnst ||
(rro = cro, find ? rcf = ccl : (rrc = 0, rcl = ccl), 0) ||
main(-col, row) || (ccl = rcl + 1, 0) endb) return ret;
}
if (!find) rrr++, rrc = 0;
if (!(lnst _ _ _ _ _ endb)) {
if (!find) rrr++, rrc = 0;
if (!(lnst _ _ _ O _ endb)) {
if (!find) rrr++, rrc = 0;
if (!(lnst _ O _ O _ endb)) {
if (!find) rrr++, rrc = 0;
if (!(lnst _ _ O O _ endb)) {
if (!find) rrr++, rrc = 0;
if (!(lnst _ _ _ _ _ endb)) {
if (prs & 1) {
if (lnst ||
(rro = cro, find ? rcf = ccl : (rrc = 0, rcl = ccl), 0) ||
main(-col, row) || (ccl = rcl + 1, 0) endb) return ret;
}
(find || printf("\n\nGEN %d STAT %ld\nEND\n", ++gen,
(long)ccl << 16 | cro << 8 | (sta | occ | ocp << 1) ^ 0x66));
}
}
}
}
}
return ret;
}
이제 다시 원래 주제로 돌아 와서, 매크로 선언을 다시 한 번 들여다 봅시다.
#define endb || (prs & 16) && X(0) || chpa || (eols, 0)
#define lnst doln ? 0 : (init, 0) || (prs & 128) && X(0)
#define _ || (find || rrc++, 0) || X(0)
#define O || (find || rrc++, 0) || X(1)
구조가 보이시나요?
if (!(lnst _ _ O O _ endb))
...라는 문장은,
if (!(
doln ? 0 : (init, 0) || (prs & 128) && X(0) /* <- lnst */
|| (find || rrc++, 0) || X(0) /* <- _ */
|| (find || rrc++, 0) || X(0) /* <- _ */
|| (find || rrc++, 0) || X(1) /* <- O */
|| (find || rrc++, 0) || X(1) /* <- O */
|| (find || rrc++, 0) || X(0) /* <- _ */
|| (prs & 16) && X(0) || chpa || (eols, 0) /* <- endb */
))
로 해석됩니다. 볼 수 있듯이 거의 모든 수식이 ||
연산자로 연결되어 있는데 lnst
의 맨 처음에 doln ? 0 : ...
이 그걸 다 묶고 있지요. 한편,
if (!((prs & 8) && (lnst ||
(rro = cro, find ? rcf = ccl : (rrc = 0, rcl = ccl), 0) ||
main(-col, row) || (ccl = rcl + 1, 0) endb)))
...라는 문장은,
if (!((prs & 8) && (
doln ? 0 : (init, 0) || (prs & 128) && X(0) /* <- lnst */
|| (rro = cro, find ? rcf = ccl : (rrc = 0, rcl = ccl), 0) ||
main(-col, row) || (ccl = rcl + 1, 0)
|| (prs & 16) && X(0) || chpa || (eols, 0) /* <- endb */
)))
로 해석됩니다. 이 식 역시 lnst
와 endb
사이의 모든 식들이 ||
연산자로 연결되어 있음을 알 수 있습니다. (단지 중간에 O
와 _
만 안 들어 갔을 뿐이지요.)
TODO 중간 설명 생략
#define _ ((find || rrc++, 0) || X(0))
#define O ((find || rrc++, 0) || X(1))
if (!(
doln ? 0 : (init, 0) || (prs & 128) && X(0)
|| _ || _ || O || O || _
|| (prs & 16) && X(0) || chpa || (eols, 0)
))
TODO 중간 설명 생략
int cond;
cond = (doln ? 0 : (init, 0) || (prs & 128) && X(0)
|| _ || _ || O || O || _
|| (prs & 16) && X(0) || chpa || (eols, 0));
if (!cond) ...
TODO 중간 설명 생략
int cond = 0;
if (!doln) {
init;
if ((prs & 128) && X(0)) cond = 1;
else if (_) cond = 1;
else if (_) cond = 1;
else if (O) cond = 1;
else if (O) cond = 1;
else if (_) cond = 1;
else if ((prs & 16) && X(0)) cond = 1;
else if (chpa) cond = 1;
else eols;
}
if (!cond) ...
TODO 중간 설명 생략
int cond = 0;
if (!doln) {
(find ? (ret = bit = but = 0) : (ocp = occ, las = occ = 0,
printf("\nL")), dfc = (prs & 192) != 64, ccl = 0);
if ((prs & 128) && X(0)) cond = 1;
else if (_) cond = 1;
else if (_) cond = 1;
else if (O) cond = 1;
else if (O) cond = 1;
else if (_) cond = 1;
else if ((prs & 16) && X(0)) cond = 1;
else if ((find && col > ccl && ((ret = bit + but) + 1))) cond = 1;
else {
(!find &&
(cro == 1 ? (sta |= occ << 3) : cro == 2 && (sta |= occ << 2),
sta |= (las & 3) << 4));
}
}
if (!cond) ...
TODO 중간 설명 생략
int cond = 0;
if (!doln) {
if (find) {
ret = bit = but = 0;
} else {
ocp = occ;
las = occ = 0;
printf("\nL");
}
dfc = (prs & 192) != 64;
ccl = 0;
if ((prs & 128) && X(0)) cond = 1;
else if (_) cond = 1;
else if (_) cond = 1;
else if (O) cond = 1;
else if (O) cond = 1;
else if (_) cond = 1;
else if ((prs & 16) && X(0)) cond = 1;
else if (find && col > ccl && ((ret = bit + but) + 1)) cond = 1;
else if (!find) {
if (cro == 1) sta |= occ << 3;
else if (cro == 2) sta |= occ << 2;
sta |= (las & 3) << 4;
}
}
if (!cond) ...
TODO 중간 설명 생략 (bit, but의 origin)
#define X(p) (dfc++ && ((prs & 48) != 32 || rrc < maxc) && ++ccl && \
(find ? (bat = bit, bit = but, but = p, ccl == col && \
/* ... */
TODO 중간 설명 생략
int cond = 0;
if (!doln) {
if (find) {
ret = bit = but = 0;
} else {
ocp = occ;
las = occ = 0;
printf("\nL");
}
dfc = (prs & 192) != 64;
ccl = 0;
if ((prs & 128) && X(0)) cond = 1;
else if (_) cond = 1;
else if (_) cond = 1;
else if (O) cond = 1;
else if (O) cond = 1;
else if (_) cond = 1;
else if ((prs & 16) && X(0)) cond = 1;
else if (find && col > ccl) {
ret = bit + but;
cond = 1;
} else if (!find) {
if (cro == 1) sta |= occ << 3;
else if (cro == 2) sta |= occ << 2;
sta |= (las & 3) << 4;
}
}
if (!cond) ...
TODO 중간 설명 생략
int cond = 0;
if (!!(dfl++ && (((prs & 3) != 2) || rrr < maxr) && (++cro == row || !find))) {
if (find) {
ret = bit = but = 0;
} else {
ocp = occ;
las = occ = 0;
printf("\nL");
}
dfc = (prs & 192) != 64;
ccl = 0;
if ((prs & 128) && X(0)) cond = 1;
else if ((find || rrc++, 0) || X(0)) cond = 1;
else if ((find || rrc++, 0) || X(0)) cond = 1;
else if ((find || rrc++, 0) || X(1)) cond = 1;
else if ((find || rrc++, 0) || X(1)) cond = 1;
else if ((find || rrc++, 0) || X(0)) cond = 1;
else if ((prs & 16) && X(0)) cond = 1;
else if (find && col > ccl) {
ret = bit + but;
cond = 1;
} else if (!find) {
if (cro == 1) sta |= occ << 3;
else if (cro == 2) sta |= occ << 2;
sta |= (las & 3) << 4;
}
}
if (!cond) ...
TODO 중간 설명 생략
int cond = 0;
if (dfl++ && (((prs & 3) != 2) || rrr < maxr) && (++cro == row || !find)) {
if (find) {
ret = bit = but = 0;
} else {
ocp = occ;
las = occ = 0;
printf("\nL");
}
dfc = (prs & 192) != 64;
ccl = 0;
if ((prs & 128) && X(0)) cond = 1;
else {
if (!find) rrc++;
if (X(0)) cond = 1; else {
if (!find) rrc++;
if (X(0)) cond = 1; else {
if (!find) rrc++;
if (X(1)) cond = 1; else {
if (!find) rrc++;
if (X(1)) cond = 1; else {
if (!find) rrc++;
if (X(0)) cond = 1; else {
if ((prs & 16) && X(0)) cond = 1;
else if (find && col > ccl) {
ret = bit + but;
cond = 1;
} else if (!find) {
if (cro == 1) sta |= occ << 3;
else if (cro == 2) sta |= occ << 2;
sta |= (las & 3) << 4;
}
}
}
}
}
}
}
}
if (!cond) ...
TODO 중간 설명 생략
int main_nonnegcol(int col, int row, int dfl, int ccl, int cro) {
int cond;
assert(col >= 0);
ret = 0;
if (prs & 8) {
if (lnst ||
(rro = cro, find ? rcf = ccl : (rrc = 0, rcl = ccl), 0) ||
main(-col, row) || (ccl = rcl + 1, 0) endb) return ret;
}
if (!find) rrr++, rrc = 0;
cond = 0;
/* 열심히 cond를 설정한다 (X(0), X(0), X(0), X(0), X(0) 사용) */
if (!cond) {
if (!find) rrr++, rrc = 0;
cond = 0;
/* 열심히 cond를 설정한다 (X(0), X(0), X(0), X(1), X(0) 사용) */
if (!cond) {
if (!find) rrr++, rrc = 0;
cond = 0;
/* 열심히 cond를 설정한다 (X(0), X(1), X(0), X(1), X(0) 사용) */
if (!cond) {
if (!find) rrr++, rrc = 0;
cond = 0;
/* 열심히 cond를 설정한다 (X(0), X(0), X(1), X(1), X(0) 사용) */
if (!cond) {
if (!find) rrr++, rrc = 0;
cond = 0;
/* 열심히 cond를 설정한다 (X(0), X(0), X(0), X(0), X(0) 사용) */
if (!cond) {
if (prs & 1) {
if (lnst ||
(rro = cro, find ? rcf = ccl : (rrc = 0, rcl = ccl), 0) ||
main(-col, row) || (ccl = rcl + 1, 0) endb) return ret;
}
(find || printf("\n\nGEN %d STAT %ld\nEND\n", ++gen,
(long)ccl << 16 | cro << 8 | (sta | occ | ocp << 1) ^ 0x66));
}
}
}
}
}
return ret;
}
TODO 중간 설명 생략
if (dfl++ && (((prs & 3) != 2) || rrr < maxr) && (++cro == row || !find)) {
if (find) {
ret = bit = but = 0;
} else {
ocp = occ;
las = occ = 0;
printf("\nL");
}
dfc = (prs & 192) != 64;
ccl = 0;
if ((prs & 128) && X(0)) return ret;
if (!find) rrc++;
if (X(0)) return ret;
if (!find) rrc++;
if (X(0)) return ret;
if (!find) rrc++;
if (X(1)) return ret;
if (!find) rrc++;
if (X(1)) return ret;
if (!find) rrc++;
if (X(0)) return ret;
if ((prs & 16) && X(0)) return ret;
if (find && col > ccl) {
ret = bit + but;
return ret;
} else if (!find) {
if (cro == 1) sta |= occ << 3;
else if (cro == 2) sta |= occ << 2;
sta |= (las & 3) << 4;
}
}
/* cond가 1이 아니면 항상 이 코드를 실행하게 되므로 if 문이 필요 없다. */
...
TODO 중간 설명 생략
int main_nonnegcol(int col, int row, int dfl, int ccl, int cro) {
assert(col >= 0);
ret = 0;
if (prs & 8) {
if (lnst ||
(rro = cro, find ? rcf = ccl : (rrc = 0, rcl = ccl), 0) ||
main(-col, row) || (ccl = rcl + 1, 0) endb) return ret;
}
if (!find) rrr++, rrc = 0;
/* 첫번째 줄을 처리한다. (본래 L _ _ _ _ _ 였음) */
if (dfl++ && (((prs & 3) != 2) || rrr < maxr) && (++cro == row || !find)) {
if (find) {
ret = bit = but = 0;
} else {
ocp = occ;
las = occ = 0;
printf("\nL");
}
dfc = (prs & 192) != 64;
ccl = 0;
if ((prs & 128) && X(0)) return ret;
if (!find) rrc++;
if (X(0)) return ret;
if (!find) rrc++;
if (X(0)) return ret;
if (!find) rrc++;
if (X(0)) return ret;
if (!find) rrc++;
if (X(0)) return ret;
if (!find) rrc++;
if (X(0)) return ret;
if ((prs & 16) && X(0)) return ret;
if (find && col > ccl) {
ret = bit + but;
return ret;
} else if (!find) {
if (cro == 1) sta |= occ << 3;
else if (cro == 2) sta |= occ << 2;
sta |= (las & 3) << 4;
}
}
/* ... 두번째 줄부터 다섯번째 줄까지는 생략 ... */
if (prs & 1) {
if (lnst ||
(rro = cro, find ? rcf = ccl : (rrc = 0, rcl = ccl), 0) ||
main(-col, row) || (ccl = rcl + 1, 0) endb) return ret;
}
(find || printf("\n\nGEN %d STAT %ld\nEND\n", ++gen,
(long)ccl << 16 | cro << 8 | (sta | occ | ocp << 1) ^ 0x66));
return ret;
}
TODO 중간 설명 생략
int main_nonnegcol(int col, int row, int dfl, int ccl, int cro) {
assert(col >= 0);
ret = 0;
if (prs & 8) {
if (dfl++ && (((prs & 3) != 2) || rrr < maxr) && (++cro == row || !find)) {
if (find) {
ret = bit = but = 0;
} else {
ocp = occ;
las = occ = 0;
printf("\nL");
}
dfc = (prs & 192) != 64;
ccl = 0;
if ((prs & 128) && X(0)) return ret;
if ((rro = cro, find ? rcf = ccl : (rrc = 0, rcl = ccl), 0)) return ret;
if (main(-col, row)) return ret;
if ((ccl = rcl + 1, 0)) return ret;
if ((prs & 16) && X(0)) return ret;
if (find && col > ccl) {
ret = bit + but;
return ret;
} else if (!find) {
if (cro == 1) sta |= occ << 3;
else if (cro == 2) sta |= occ << 2;
sta |= (las & 3) << 4;
}
}
}
if (!find) rrr++, rrc = 0;
/* ... 생략 ... */
if (prs & 1) {
/* ... if (prs & 8)과 똑같은 코드이므로 생략 ... */
}
(find || printf("\n\nGEN %d STAT %ld\nEND\n", ++gen,
(long)ccl << 16 | cro << 8 | (sta | occ | ocp << 1) ^ 0x66));
return ret;
}
TODO 중간 설명 생략
int main_nonnegcol(int col, int row, int dfl, int ccl, int cro) {
assert(col >= 0);
ret = 0;
if ((prs & 8) && dfl++ && (((prs & 3) != 2) || rrr < maxr) && (++cro == row || !find)) {
if (find) {
ret = bit = but = 0;
} else {
ocp = occ;
las = occ = 0;
printf("\nL");
}
dfc = (prs & 192) != 64;
ccl = 0;
if ((prs & 128) && X(0)) return ret;
rro = cro;
if (find) {
rcf = ccl;
} else {
rrc = 0;
rcl = ccl;
}
if (main(-col, row)) return ret;
ccl = rcl + 1;
if ((prs & 16) && X(0)) return ret;
if (find && col > ccl) {
ret = bit + but;
return ret;
} else if (!find) {
if (cro == 1) sta |= occ << 3;
else if (cro == 2) sta |= occ << 2;
sta |= (las & 3) << 4;
}
}
if (!find) rrr++, rrc = 0;
/* ... 생략 ... */
if ((prs & 1) && dfl++ && (((prs & 3) != 2) || rrr < maxr) && (++cro == row || !find)) {
/* ... 생략 ... */
}
if (!find) {
printf("\n\nGEN %d STAT %ld\nEND\n", ++gen,
(long)ccl << 16 | cro << 8 | (sta | occ | ocp << 1) ^ 0x66);
}
return ret;
}
TODO 중간 설명 생략
if (X(p)) return ret;
TODO 중간 설명 생략
if ((dfc++ && ((prs & 48) != 32 || rrc < maxc) && ++ccl &&
(find ? (bat = bit, bit = but, but = p, ccl == col &&
(ret = bat + bit + but) + 1) : (las = las * 2 +
(((main(ccl + 1, cro - 1) + main(ccl + 1, cro) +
main(ccl + 1, cro + 1) - p) | p) == 3), occ |= las & 1,
printf(" %c", "_O"[las & 1]), ccl == 2 && (sta |= (las & 3) << 6),
0)))) return ret;
TODO 중간 설명 생략
if (
dfc++ &&
((prs & 48) != 32 || rrc < maxc) &&
++ccl &&
(find ?
(bat = bit,
bit = but,
but = p,
ccl == col && (ret = bat + bit + but) + 1) :
(las = las * 2 + (((main(ccl + 1, cro - 1) +
main(ccl + 1, cro) +
main(ccl + 1, cro + 1) - p) | p) == 3),
occ |= las & 1,
printf(" %c", "_O"[las & 1]),
ccl == 2 && (sta |= (las & 3) << 6),
0)
)
) return ret;
TODO 중간 설명 생략
if (dfc++ && ((prs & 48) != 32 || rrc < maxc) && ++ccl) {
if (find) {
bat = bit;
bit = but;
but = p;
if (ccl == col) {
ret = bat + bit + but;
if (ret + 1) return ret;
} else {
las = las * 2 + (((main(ccl + 1, cro - 1) +
main(ccl + 1, cro) +
main(ccl + 1, cro + 1) - p) | p) == 3);
occ |= las & 1;
printf(" %c", "_O"[las & 1]);
if (ccl == 2) sta |= (las & 3) << 6;
}
}
}
TODO 중간 설명 생략
if (dfc++ && ((prs & 48) != 32 || rrc < maxc) && ++ccl && find) {
bat = bit;
bit = but;
but = p;
if (ccl == col) {
ret = bat + bit + but;
return ret;
} else {
las = las * 2 + (((main(ccl + 1, cro - 1) +
main(ccl + 1, cro) +
main(ccl + 1, cro + 1) - p) | p) == 3);
occ |= las & 1;
printf(" %c", "_O"[las & 1]);
if (ccl == 2) sta |= (las & 3) << 6;
}
}
TODO 중간 설명 생략
#include <stdio.h>
#include <assert.h>
int bat, bit, but, las, gen, ret, rcl, rcf, rro, rrc, rrr, occ, ocp, sta, dfc;
long prs;
int main_nonnegcol(int col, int row, int dfl, int ccl, int cro) {
assert(col >= 0);
ret = 0;
if ((prs & 8) && dfl++ && (((prs & 3) != 2) || rrr < maxr) && (++cro == row || !find)) {
if (find) {
ret = bit = but = 0;
} else {
ocp = occ;
las = occ = 0;
printf("\nL");
}
dfc = (prs & 192) != 64;
ccl = 0;
if ((prs & 128) && dfc++ && ((prs & 48) != 32 || rrc < maxc) && ++ccl && find) {
bat = bit;
bit = but;
but = 0;
if (ccl == col) {
ret = bat + bit + but;
return ret;
} else {
las = las * 2 + (((main(ccl + 1, cro - 1) +
main(ccl + 1, cro) +
main(ccl + 1, cro + 1) - 0) | 0) == 3);
occ |= las & 1;
printf(" %c", "_O"[las & 1]);
if (ccl == 2) sta |= (las & 3) << 6;
}
}
rro = cro;
if (find) {
rcf = ccl;
} else {
rrc = 0;
rcl = ccl;
}
if (main(-col, row)) return ret;
ccl = rcl + 1;
if ((prs & 128) && dfc++ && ((prs & 48) != 32 || rrc < maxc) && ++ccl && find) {
/* ... 생략 ... */
}
if (find && col > ccl) {
ret = bit + but;
return ret;
} else if (!find) {
if (cro == 1) sta |= occ << 3;
else if (cro == 2) sta |= occ << 2;
sta |= (las & 3) << 4;
}
}
if (!find) rrr++, rrc = 0;
/* ... 생략 ... */
if ((prs & 1) && dfl++ && (((prs & 3) != 2) || rrr < maxr) && (++cro == row || !find)) {
/* ... 생략 ... */
}
if (!find) {
printf("\n\nGEN %d STAT %ld\nEND\n", ++gen,
(long)ccl << 16 | cro << 8 | (sta | occ | ocp << 1) ^ 0x66);
}
return ret;
}
main(int col, int row) {
int dfl = ((prs & 12) != 4), ccl = 0, cro = 0;
if (col == 1) printf("LIFE\n");
if (col < 0) {
col = -col;
ccl = (find ? rcf : rcl);
cro = rro;
/* 아래 코드는 if (0 || _)를 확장한 것임. */
if (!find) rrc++;
if (dfc++ && ((prs & 48) != 32 || rrc < maxc) && ++ccl && find) {
/* ... 생략 ... */
/* (return ret 대신에 return 1을 쓰는 것만 다름) */
}
if (!(rrc >= maxc)) {
if (find) rcf = ccl;
else rcl = ccl;
}
return main(-col, row);
} else {
return main_nonnegcol(col, row, dfl, ccl, cro);
}
}
int gen = 4;
long prs = 328960;
TODO ...이 시점에서 뭐라고 말해 줘야 합니까 도대체
TODO 이 시점에는 "무조건" 섹션 구분이 필요함
라이프 게임(Game of life)은 이름과는 다르게 실제 게임-_-은 아닙니다. 라이프 게임은 무한히 많은 "세포"로 이루어진 2차원 공간 상에서 행해지는데, 각 세포는 "죽어" 있거나 "살아" 있을 수 있고, 주변의 여덟 세포들("이웃") 중 몇 개가 살아 있느냐에 따라 다음 순간("세대"라고 합니다)에 세포가 살아 있느냐 죽어 있느냐가 결정됩니다:
- 죽은 세포의 이웃 중 정확히 세 개가 살아 있으면 다음 순간에 그 세포는 살아 납니다.
- 살아 있는 세포의 이웃이 한 개 이하, 또는 네 개 이상 살아 있으면 다음 순간에 그 세포는 죽습니다.
이 규칙은 간단함에도 불구하고 매우 다양한 패턴을 만들어 내며, 심지어 컴퓨터를 시뮬레이션하는 데 사용될 수도 있습니다. 이런 류의 규칙들을 세포 자동자(cellular automaton)라 하는데 라이프 게임은 세포 자동자 중 매우 잘 알려져 있으며 특히 많이 연구된 대상 중 하나입니다. ↩
a || (b || c) || 0
과a || b || c || 0
은 같은 코드임을 상기합시다. 이제 바깥쪽||
들을 if 문으로 바꾸면, 전자는if (!a) if (!(b || c)) ;
이 되고 후자는if (!a) if (!b) if (!c) ;
가 됩니다. ↩