메아리

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이 되는 게 확실하므로 지울 수 있습니다. (마지막 endbL이 확장되며 만들어진 것입니다. 순전히 반복 코드를 위한 부분이죠.) 따라서,

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_가 있습니다. 이 중 addloutp를 제외한 나머지 매크로는 매우 자주 나오기 때문에 나중에 처리하고, 이 두 매크로를 확장해 봅시다.

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;
}

lnstendb 매크로가 더 추가되었습니다! 이제 꼼짝 없이(?) 이 매크로들을 분석해야 하겠는데요, 여기에 대해 살펴 보기 전에 먼저 이 코드를 간단하게 정리해 보도록 하겠습니다.

위의 코드는 맨 바깥이 다음과 같이 되어 있습니다.

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)과 함께 들어 있어서 문장으로 바꾸기 힘들었지만, 이제 독립된 조건이 되었으니 문장으로 바꾸기 더 쉬울 것입니다.

실제로 이 조건이 어떻게 해석되는지는 lnstendb를 살펴 본 뒤로 미루도록 하겠습니다. 일단 똑같은 코드가 나타나는 뒷부분도 같은 방법으로 독립된 문장으로 바꾸면,

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 */
)))

로 해석됩니다. 이 식 역시 lnstendb 사이의 모든 식들이 || 연산자로 연결되어 있음을 알 수 있습니다. (단지 중간에 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 이 시점에는 "무조건" 섹션 구분이 필요함


  1. 라이프 게임(Game of life)은 이름과는 다르게 실제 게임-_-은 아닙니다. 라이프 게임은 무한히 많은 "세포"로 이루어진 2차원 공간 상에서 행해지는데, 각 세포는 "죽어" 있거나 "살아" 있을 수 있고, 주변의 여덟 세포들("이웃") 중 몇 개가 살아 있느냐에 따라 다음 순간("세대"라고 합니다)에 세포가 살아 있느냐 죽어 있느냐가 결정됩니다:

    • 죽은 세포의 이웃 중 정확히 세 개가 살아 있으면 다음 순간에 그 세포는 살아 납니다.
    • 살아 있는 세포의 이웃이 한 개 이하, 또는 네 개 이상 살아 있으면 다음 순간에 그 세포는 죽습니다.

    이 규칙은 간단함에도 불구하고 매우 다양한 패턴을 만들어 내며, 심지어 컴퓨터를 시뮬레이션하는 데 사용될 수도 있습니다. 이런 류의 규칙들을 세포 자동자(cellular automaton)라 하는데 라이프 게임은 세포 자동자 중 매우 잘 알려져 있으며 특히 많이 연구된 대상 중 하나입니다.

  2. a || (b || c) || 0a || b || c || 0은 같은 코드임을 상기합시다. 이제 바깥쪽 ||들을 if 문으로 바꾸면, 전자는 if (!a) if (!(b || c)) ;이 되고 후자는 if (!a) if (!b) if (!c) ;가 됩니다.


Copyright © 1999–2009, Kang Seonghoon.