Wolfensteiny 3D — реверс-инжиниринг 251 байтов JavaScript

    При написании кода многие не задумываются ни о чем, кроме логики самой программы. Меньшее число людей думают об оптимизации кода по времени, по памяти. Но лишь единицы доходят до последнего уровня — сжатии программы до рекордно маленького размера.

    Посмотрите, например, на результат работы всего 251 байта JavaScript:


    Ну, давайте разбираться, как это работает!

    Откуда это?
    Этот код, как и то многое, к чему я обращался в этой статье, находится на сайте p01.org великолепного Mathieu 'p01' Henri, разработчика на JavaScript и не только, частенько занимающегося сжатием кода до невозможных размеров. Исходный материал этой статьи тут.

    Итак, перед вами те самые 251 байт исходного кода.

    <body onload=E=c.getContext("2d"),setInterval(F="t+=.2,Q=Math.cos;c.height=300;for(x=h;x--;)for(y=h;y--;E.fillRect(x*4,y*4,b-d?4:D/2,D/2))for(D=0;'.'<F[D*y/h-D/2|0?1:(d=t+D*Q(T=x/h-.5+Q(t)/8)&7)|(3.5+D*Q(T-8))<<3]&&D<8;b=d)D+=.1",t=h=75)><canvas id=c>
    

    Понятно, что ничего не понятно.

    Делаем код читаемым


    Первым делом весь JavaScript-код я вынес в отдельный тег, для удобства.

    Видно, что переменные E, h, Q, F и другие — константы, которые можно заменить их значениями/самими объектами, а также поменять названия.

    var context = c.getContext("2d")
    var F="t+=.2,Q=Math.cos;c.height=300;for(x=h;x--;)for(y=h;y--;E.fillRect(x*4,y*4,b-d?4:D/2,D/2))for(D=0;'.'<F[D*y/h-D/2|0?1:(d=t+D*Q(T=x/h-.5+Q(t)/8)&7)|(3.5+D*Q(T-8))<<3]&&D<8;b=d)D+=.1"
    var t = 75
    var size = 75
    
    function render(){
        t += 0.2;
        c.height=300;
        for(let x = size; x--;)
            for(let y = size; y--; context.fillRect(x * 4,y * 4,b - d? 4 : D / 2, D / 2))
                for(var D = 0; '.' < F[D * y / size - D / 2 | 0 ? 1 : (d = t + D * Math.cos(T = x / size - 0.5 + Math.cos(t) / 8) & 7) | (3.5 + D * Math.cos(T - 8)) << 3] && D < 8; b = d)
                    D += 0.1
    }
    
    setInterval(render, 75);
    

    Здесь код из строки уже вынесен в функцию, а сама строка — нетронута, она понадобится нам в дальнейшем.

    Теперь преобразуем два внешних цикла в while.

    function render(){
        t += 0.2;
        c.height=300;
        let x = size;
        while(x > 0){
            let y = size;
            while(y > 0){
                for(var D = 0; '.' < F[D * y / size - D / 2 | 0 ? 1 : (d = t + D * Math.cos(T = x / size - 0.5 + Math.cos(t) / 8) & 7) | (3.5 + D * Math.cos(T - 8)) << 3] && D < 8; b = d)
                    D += 0.1
                
                context.fillRect(x * 4,y * 4,b - d? 4 : D / 2, D / 2);
                y--;
            }
            
            x--;
        }
    }
    

    Как мы это видим?


    Давайте поймём, почему мы вообще это видим. Если посмотреть на картинку ещё раз, можно понять многое.

    Картинка кликабельна

    Вот, что мы видим:

    1. Чем дальше объект, тем он темнее
    2. Скошенная от нас часть встречающихся препятствий залита по-другому, линиями, а не точками

    В коде рисование отражено так:

    //      То, что определяет, заливать с фиксированной шириной или нет
    //            Координаты точки  ||    Ширина и высота точки
    //                 |     |      ||        |      |
    //                 ↓     ↓      ↓↓        ↓      ↓
    context.fillRect(x * 4,y * 4,b - d? 4 : D / 2, D / 2);
    

    Почему мы видим объёмные объекты в этом половодье черных точек? Ведь нам приходится довольствоваться лишь различными оттенками чёрного — размерами черных точек (менять цвет мы не можем, E.fillStyle — слишком длинно!). На самом деле, это работает просто, потому что на двумерной картинке наш глаз опирается в основном на тени и яркость освещения.

    Представьте себя, идущим по тёмному коридору, у вас в руках лишь фонарик. Вы светите перед собой и видите, что одни предметы ближе и светлее (фонарик светит, препятствие яркое, нет теней), а другие дальше и темнее (свет рассеян, слаб, и видим темноту — и чувствуем расстояние). Так и здесь — чем дальше объект (больше D), тем больше по размеру мы рисуем черный квадрат на экране.
    Но как мы узнаём, что надо делать ярким, а что нет?

    Просчитываем пиксель


    Теперь разберемся с этим монстром:

    for(var D = 0; '.' < F[D * y / size - D / 2 | 0 ? 1 : (d = t + D * Math.cos(T = x / size - 0.5 + Math.cos(t) / 8) & 7) | (3.5 + D * Math.cos(T - 8)) << 3] && D < 8; b = d)
            D += 0.1
    

    Итак. Всё это выражение представляет собой алгоритм рендеринга (fixed step raymarching), позволяющий находить пересечение луча с блоками. Для каждого пикселя экрана мы запускаем луч, и идем по нему с фиксированным шагом 0.1, и как только встречаем препятствие — заканчиваем алгоритм, и рисуем пиксель на экране, зная дальность до препятствия.

    Начнем читать этот код по частям.

    Условие D * y / size - D / 2 | 0 можно представить в виде $D * (\frac{y}{size} - \frac{1}{2}) = D * (\frac{y - size/2}{size}) < 1$, тогда выражение в скобках будет показывать «отклонение» y от центра экрана (в долях экрана). Так мы пытаемся понять, находится ли луч в пределах между полом и потолком или нет. Поэтому, если мы касаемся пола (или потолка), мы выходим из цикла дальше, к отрисовке, и рисуем пиксель.
    А если не касаемся, то продолжаем вычисления: ищем текущие координаты луча.

    var T = x / size - .5 + Math.cos(t) / 8; // Math.cos(t) заставляет камеру 
                                             // вращаться со временем
    
    var xcoord = t + depth * Math.cos(T);
    var ycoord = 3.5 + depth * Math.cos(T - 8); // 
    

    Почему cos(T - 8)?
    Так получается, что $cos(x-8) \approx sin(x)$ с точностью 0.15 радиан. Всё потому, что

    $\frac{5\pi}{2} \approx 8.15 \approx 8$


    , и тогда

    $cos(\alpha-8) \approx cos(\alpha-\frac{5\pi}{2}) = cos(\alpha - \frac{\pi}{2})=sin(\alpha)$



    Стоит поговорить, как вообще проверяется принадлежность точки в пространстве блоку. Сама карта берется из исходного кода (F) и выглядит так:
    t+=.2,Q= ----> ░█░█░█░░
    Math.cos ----> ░░░░█░░░
    ;c.heigh ----> ░░█░░░░░       А это -
    t=300;fo ----> ░░░░░░░░ <---- сам проход,
    r(x=h;x- ----> ░█░░░░░█       по которому летит камера
    -;)for(y ----> █░█░░░█░
    =h;y--;E ----> ░░░░██░░
    .fillRec ----> █░░░░░░░
    

    Так это выглядит в движении, здесь обозначена область видимости камеры.
    Темными помечены те клетки, код символа которых меньше кода точки — "." — то есть символы !"#$%&'()*+,-.. Теперь мы округляем координаты луча, и пытаемся узнать — буква в данных «координатах»-индексе темная (препятствие) или нет (летим лучом дальше).

    Так как индекс один, а координаты — две, то мы используем хак:

    var boxIndex = xcoord & 7 | ycoord << 3;
    

    В итоге, получаем число, отражающее номер блока (ну или пустоты).

    Вернёмся к коду. Теперь он выглядит поприличнее.

    Код немного потолстел
    function render(){
        t += 0.2;
        c.height=300;
        let x = size;
        while(x > 0){
            let y = size;
            while(y > 0){
                var depth = 0
                while(depth < 8){
                    depth += 0.1
                    
                    var T = x / size - .5 + Math.cos(t) / 8; // Поворот камеры
                    var isFloorOrCeiling = depth * y / size - depth / 2 | 0; // Мы уперлись в потолок или пол?
    
                    if(isFloorOrCeiling)
                        break;
    
                    var xcoord = t + depth * Math.cos(T) & 7;
                    var ycoord = 3.5 + depth * Math.sin(T); // cos - 8 -> sin
                    
                    boxIndex = xcoord | ycoord << 3; // Находим индекс в исходном коде,
                    // своего рода карте
    
                    if ('.' >= F[boxIndex])
                        break;
                    b = xcoord; // Зачем это? Сейчас узнаем!
                }
                
                context.fillRect(x * 4, y * 4, b - xcoord ? 4 : depth / 2, depth / 2)
                y--;
            }
    
            x--;
        }
    }
    


    Обратно в отрисовку


    К чему нам всё это было? Теперь, после выполнения этого алгоритма, мы знаем дальность до объекта, и можем его нарисовать. Но один вопрос остался без ответа: как отличить потолок от отдельного блока? Ведь расстояние до потолка и до блока — числа, которые ничем не отличаются! На самом деле, мы уже ответили на этот вопрос.

    //       То, что определяет, заливать фиксированной шириной или нет
    //                                 ||
    //                                 ↓↓
    context.fillRect(x * 4, y * 4, b - xcoord ? 4 : depth / 2, depth / 2);
    

    В коде есть одно условие, связанное с переменной b, и влияющее на ширину «большого черного пикселя»: b - xcoord ? 4 : depth / 2. Давайте уберем это условие, и посмотрим, что будет без него:

    Совсем не видно границ между блоками и потолком! (кликабельно)

    Условие b - xcoord даст нам константную ширину тогда, когда изменение координаты равно 0. А когда это может не произойти? Это не происходит лишь тогда, когда мы не доходим до (2) строчки в коде:

    // ....
    var xcoord = t + depth * Math.cos(T) & 7; // <--- Должны побывать здесь (1)
    // ...
    if ('.' >= F[boxIndex]) // <--- Программа выходит здесь (3)
        break;
    b = xcoord; // <--- Не должны прийти сюда (2)
    // ....
    

    Значит, программа выходит из цикла раньше, на строчке (3), когда луч идет в непрозрачный блок в направлении почти перпендикулярном её стенке, то есть попадает в «лицевую» грань блока. Таким образом, все блоки оказываются отличающимися от пола и потолка.

    Вот, именно так и получается эта прекрасная 3D-картинка, которая не только радует глаз, но и заставляет думать, как и почему это работает. Посмотреть этот код в действии можно здесь (оф. сайт разработчика этого чуда).
    Поддержать автора
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 16

      +1
      Посмотрите ещё Mars от Tim Clarke. Где-то и исходник был — там достаточно маленькая программа.
        0

        Спасибо за совет, обязательно посмотрю. В любом случае, чем короче и лаконичнее программа, тем она лучше. Не всегда красота требует полотен кода.

          +2
          В любом случае, чем короче и лаконичнее программа, тем она лучше.


          (Улыбка Чеширского Кота): как вам, например, такое? :) Оно правда играет в шахматы. Не очень сильно, но играет. Вопрос в том, как? :)

          Шахматы для AVR
          avrengine.h

          #ifndef AVR_ENGINE_H
          #define AVR_ENGINE_H
          
          #include <stdio.h>
          #include <stdlib.h>
          #include "stdafx.h"
          
          /***************************************************************************/
          /*                               AVR-Max,                                  */
          /* A chess program smaller than 2KB (of non-blank source), by H.G. Muller  */
          /* AVR ATMega88V port, iterative Negamax, by Andre Adrian                  */
          /***************************************************************************/
          /* 14dez2008 adr: merge avr_c_io and uMax                                  */
          /* 18dez2008 adr: return -l if Negamax stack underrun                      */
          /* 24dez2008 adr: add functions 1=new game, 2=set level, 3=show PV         */
          /* 26dez2008 adr: bug fix, undo modifications on uMax 4.8                  */
          /* 29dez2009 adr: Key Debouncing with inhibit (Sperrzeit nach Loslassen)   */
          
          /* Stufe (Level)
             1 Blitz      = min. 0.5s, typ. 7s
             2 2*Blitz    = min. 1s,   typ. 15s    
             3 4*Blitz    = min. 2s,   typ. 22s
             4 Aktiv/2    = min. 4s,   typ. 
             5 Aktiv      = min. 8s,   typ. 30s
             6 2*Aktiv    = min. 16s,  typ.
             7 Turnier/2  = min. 32s,  typ. 
             8 Turnier    = min. 64s,  typ. 
            FN 2*Turnier  = min. 128s, typ.
            CL 4*Turnier  = min. 256s, typ.
          */
          
          struct STRUCT{
          short q,l,e;          /* Args: (q,l)=window, e=current eval. score         */
          short m,v,            /* m=value of best move so far, v=current evaluation */
           V,P;
          unsigned char E,z,n;  /* Args: E=e.p. sqr.; z=level 1 flag; n=depth        */          
          signed char r;        /* step vector current ray                           */
          unsigned char j,      /* j=loop over directions (rays) counter             */ 
           B,d,                 /* B=board scan start, d=iterative deepening counter */ 
           h,C,                 /* h=new ply depth?, C=ply depth?                    */
           u,p,                 /* u=moving piece, p=moving piece type               */
           x,y,                 /* x=origin square, y=target square of current move  */
           F,                   /* F=e.p., castling skipped square                   */
           G,                   /* G=castling R origin (corner) square               */
           H,t,                 /* H=capture square, t=piece on capture square       */
           X,Y,                 /* X=origin, Y=target square of best move so far     */
           a;                   /* D() return address state                          */
          };     /* _=working set, A=stack array, J=stack pointer     */
          
          /* Attention: union TIMER assumes little endian CPU or compiler!           */
          typedef union {
            long w;
          } TIMER;
          
          /***************************************************************************/
          /*                               micro-Max,                                */
          /* A chess program smaller than 2KB (of non-blank source), by H.G. Muller  */
          /***************************************************************************/
          /* version 4.8 (1953 characters) features:                                 */
          /* - recursive negamax search                                              */
          /* - all-capture MVV/LVA quiescence search                                 */
          /* - (internal) iterative deepening                                        */
          /* - best-move-first 'sorting'                                             */
          /* - a hash table storing score and best move                              */
          /* - futility pruning                                                      */
          /* - king safety through magnetic, frozen king in middle-game              */
          /* - R=2 null-move pruning                                                 */
          /* - keep hash and repetition-draw detection                               */
          /* - better defense against passers through gradual promotion              */
          /* - extend check evasions in inner nodes                                  */
          /* - reduction of all non-Pawn, non-capture moves except hash move (LMR)   */
          /* - full FIDE rules (expt under-promotion) and move-legality checking     */
          
          void D();                                       
          void Init(void);
          bool AVRMove(unsigned char *c,bool &mate);
          
          #endif
          


          avrengine.c

          
          #include "avrengine.h"
          
          /***************************************************************************/
          /*                               AVR-Max,                                  */
          /* A chess program smaller than 2KB (of non-blank source), by H.G. Muller  */
          /* AVR ATMega88V port, iterative Negamax, by Andre Adrian                  */
          /***************************************************************************/
          /* 14dez2008 adr: merge avr_c_io and uMax                                  */
          /* 18dez2008 adr: return -l if Negamax stack underrun                      */
          /* 24dez2008 adr: add functions 1=new game, 2=set level, 3=show PV         */
          /* 26dez2008 adr: bug fix, undo modifications on uMax 4.8                  */
          /* 29dez2009 adr: Key Debouncing with inhibit (Sperrzeit nach Loslassen)   */
          
          /* Stufe (Level)
             1 Blitz      = min. 0.5s, typ. 7s
             2 2*Blitz    = min. 1s,   typ. 15s    
             3 4*Blitz    = min. 2s,   typ. 22s
             4 Aktiv/2    = min. 4s,   typ. 
             5 Aktiv      = min. 8s,   typ. 30s
             6 2*Aktiv    = min. 16s,  typ.
             7 Turnier/2  = min. 32s,  typ. 
             8 Turnier    = min. 64s,  typ. 
            FN 2*Turnier  = min. 128s, typ.
            CL 4*Turnier  = min. 256s, typ.
          */
          
          unsigned char st=20;           /* Spielstufe (Level)                      */ //4+2
          
          /* This is the AVR ATMega8 Selbstbau Schachcomputer specific part         */
          
          #define WIEDERHOLMS	330
          #define INHIBITMS   100     // Sperrzeit nach Loslassen
          
          #define WIEDERHOL	((WIEDERHOLMS+8)/16)		// Einheit 16ms
          #define INHIBIT     ((INHIBITMS+8)/16)
          
          #define SPACE	36
          
          #define FN  '9'
          #define CL  ':'
          #define GO  ';'
          
          /* better readability of AVR Program memory arrays */
          #define o(ndx)	(signed char)o[ndx]
          #define w(ndx)	(signed char)w[ndx]
          
          #define M 0x88                              /* Unused bits in valid square */  
          #define S 128                               /* Sign bit of char            */
          #define I 8000                              /* Infinity score              */
          #define U 18                                /* D() Stack array size        */
          
          /* better readability of working struct variables */
          #define q _.q
          #define l _.l
          #define e _.e
          #define E _.E
          #define z _.z
          #define n _.n
          #define m _.m
          #define v _.v
          #define V _.V
          #define P _.P
          #define r _.r
          #define j _.j
          #define B _.B
          #define d _.d
          #define h _.h
          #define C _.C
          #define u _.u
          #define p _.p
          #define x _.x
          #define y _.y
          #define F _.F
          #define G _.G
          #define H _.H
          #define t _.t
          #define X _.X
          #define Y _.Y
          #define a _.a
          
          /***************************************************************************/
          /*                               micro-Max,                                */
          /* A chess program smaller than 2KB (of non-blank source), by H.G. Muller  */
          /***************************************************************************/
          /* version 4.8 (1953 characters) features:                                 */
          /* - recursive negamax search                                              */
          /* - all-capture MVV/LVA quiescence search                                 */
          /* - (internal) iterative deepening                                        */
          /* - best-move-first 'sorting'                                             */
          /* - a hash table storing score and best move                              */
          /* - futility pruning                                                      */
          /* - king safety through magnetic, frozen king in middle-game              */
          /* - R=2 null-move pruning                                                 */
          /* - keep hash and repetition-draw detection                               */
          /* - better defense against passers through gradual promotion              */
          /* - extend check evasions in inner nodes                                  */
          /* - reduction of all non-Pawn, non-capture moves except hash move (LMR)   */
          /* - full FIDE rules (expt under-promotion) and move-legality checking     */
          
          STRUCT _, A[U],*J=A+U;     /* _=working set, A=stack array, J=stack pointer     */
          
          short Q,                                    /* pass updated eval. score    */
          K,                                          /* input move, origin square   */
          i,s,                                        /* temp. evaluation term       */
          Dq,Dl,De,                         /* D() arguments */
          DD;                               /* D() return value */
          
          const signed char w[] = {
            0,2,2,7,-1,8,12,23};                         /* relative piece values    */
          
          const signed char o[] = {
            -16,-15,-17,0,1,16,0,1,16,15,17,0,14,18,31,33,0,    /* step-vector lists */
            7,-1,11,6,8,3,6,                             /* 1st dir. in o[] per piece*/
            6,3,5,7,4,5,3,6};                            /* initial piece setup      */
            
          unsigned char L,                               /* input move, target square*/
          b[129],                                        /* board: half of 16x8+dummy*/
          c[5],                                          /* input move ASCII buffer  */     
          k,                                          /* moving side 8=white 16=black*/
          O,                                          /* pass e.p. flag at game level*/
          R,                                          /* captured non pawn material  */
          DE,Dz,Dn,                                      /* D() arguments            */
          Da,                                            /* D() state                */
          W,                                           /* @ temporary                */
          hv;                                            /* zeige Hauptvariante      */
          
          
          void D()
          {                       
          D:
           if (--J<A) 
           {               
            ++J;
            DD=-l;
            goto R;
           }
           q=Dq;
           l=Dl;
           e=De;
           E=DE;
           z=Dz;
           n=Dn;                          
           a=Da;                                        
           --q;                                          
           k^=24;                                        
           d=X=Y=0;                                      
           while(d++<n || d<3 || z&K==I && ((K=X,L=Y&~M,d=3)))
           {
            x=B=X;                                       
            h=Y&S;                                       
            if (d<3) P=I;
            else 
            {
             *J=_;
             Dq=-l;
             Dl=1-l;
             De=-e;
             DE=S;
             Dz=0;
             Dn=d-3;   
             Da=0;
             goto D;                                
          R0:
             _=*J;
             P=DD;                                  
            }
            m=-P<l|R>35?d>2?-I:e:-P;
            do
            {
             u=b[x];
             if (u&k)
             {
              r=p=u&7;
              j=o(p+16);
              while(r=p>2&r<0?-r:-o(++j))
              {
          A:
               y=x;
          	 F=G=S;
               do
          	 {
                H=y=h?Y^h:y+r;
                if (y&M) break;
                m=E-S&b[E]&&y-E<2&E-y<2?I:m;
                if (p<3&y==E) H^=16;
                t=b[H];
          	  if (t&k|p<3&!(y-x&7)-!t) break;
                i=37*w(t&7)+(t&192);
                m=i<0?I:m;
                if (m>=l&d>1) goto J;
                v=d-1?e:i-p;
                if (d-!t>1)
                {
          	   v=p<6?b[x+8]-b[y+8]:0;
                 b[G]=b[H]=b[x]=0;
          	   b[y]=u|32;
                 if (!(G&M)) b[F]=k+6,v+=50;
                 v-=p-4|R>29?0:20;
                 if (p<3)
                 {
          	    v-=9*((x-2&M||b[x-2]-u)+(x+2&M||b[x+2]-u)-1+(b[x^16]==k+36))-(R>>2);
                  V=y+r+1&S?647-p:2*(u&y+16&32);
                  b[y]+=V;
          		i+=V;
                 }
                 v+=e+i;
          	   V=m>q?m:q;
                 C=d-1-(d>5&p>2&!t&!h);
                 C=R>29|d<3|P-I?C:d;
                 do
                  if (C>2|v>V)
                  {
          		 *J=_;
          		 Dq=-l;
          		 Dl=-V;
          		 De=-v;
          		 DE=F;
          		 Dz=0;
          		 Dn=C;
                   Da=1;
          		 goto D;
          R1:     
                   _=*J;
          		 s=-DD;
                  }
          		else s=v;
                 while (s>q&++C<d);
          	   v=s;
                 if (z&&K-I&&v+I&&x==K&y==L)
                 {
          	    Q=-e-i;
          		O=F;
                  R+=i>>7;
          		++J;
          		DD=l;
          		goto R;
                 }
                 b[G]=k+6;
          	   b[F]=b[y]=0;
          	   b[x]=u;
          	   b[H]=t;
                }
                if(v>m) m=v,X=x,Y=y|S&F;
                if (h)
          	  {
          	   h=0;
          	   goto A;
          	  }
                if (x+r-y|u&32|p>2&(p-4|j-7|| b[G=x+3^r>>1&7]-k-6 ||b[G^1]|b[G^2])) t+=p<5;
                                                                                   else F=y;
               }
          	 while (!t);
             }
            }
           }
           while ((x=x+9&~M)-B);
          J:
           if (m>I-M|m<M-I) d=98;
           m=m+I|P==I?m:0;
           }
           k^=24;
           ++J;
           DD=m+=m<e;
          R:
           if (J!=A+U) switch(a)
           {
            case 0:goto R0;
            case 1:goto R1;
           }
           else return;
          }
          
          
          void Init(void)
          {
            k=16;O=Q=R=0;
            for(W=0;W<sizeof(b);++W)b[W]=0;
            W=8;
            while(W--) 
            {
             b[W]=(b[W+112]=o(W+24)+8)+8;b[W+16]=18;b[W+96]=9;/* initial board setup*/
             L=8;
             while(L--) b[16*L+W+8]=(W-4)*(W-4)+(L+L-7)*(L+L-7)/4;/* center-pts table   */
            }
          }
          
          bool AVRMove(unsigned char *c,bool &mate)
          {
           mate=false;
           if(*c-GO)//если ходит человек
           {
            K=*c-16*c[1]+799,L=c[2]-16*c[3]+799;     /* parse entered move */
           }
           else//если ходит компьютер
           {
            K=I;
           }
             Dq=-I;Dl=I;De=Q;DE=O;Dz=1;Dn=3;
             Da=0;                           
             D();
             if (*c-GO) //ввод доступных ходов
             {
              if (I==DD)//ход разрешён 
          	{         
          	}
              else//ход запрещён
              {
               return(false);     
              } 	
              *c=GO;
             } 
             else //ход компьютера
             {
          	c[0]='A'+(K&7);
          	c[1]='8'-(K>>4);
          	c[2]='A'+(L&7);
          	c[3]='8'-(L>>4);
          	return(true);
             }     
             if (!(DD>-I+1)) mate=true;
           return(true);
          }
          


            +3

            Ух, новый челлендж. Спасибо, возьму на заметку! Может, даже разберу, если очень интересно получится.

          +2
          В любом случае, чем короче и лаконичнее программа, тем она лучше.

          https://codegolf.stackexchange.com/ читаете? Содержит тонны всевозможных извращений над кодом.

            +1
            Хм. Сохранил когда-то Mars Engine на Java Script. А теперь в поиске фиг его нахожу. Так как Java Script я не знаю и выделить программу чтобы сюда вставить и оно работало прямо на страничке не могу (так-то для С++ под Direct Draw я код давно вынул и переделал), то прикладываю целиком сохранённый html-файл: может, вам удастся выковырять оттуда программу на Java Script.
            0
            Это напоминает мне DOSовский вирус, который размножается и наносит вред. Длина — 33 байта.
          0
          Программист, который пишет программу, должен понимать, что когда-нибудь, вероятно, её может править дугой программист.
          Или ты сам через десять лет.
          Поэтому, основное требование к программе — это её наглядность.
          Разумеется, не в программах для первых машин, написанных в кодах с многочисленными ветвлениями. Там экономия памяти мгла иметь и имела значение.
          Сейчас об этом говорить не приходится.
            +1

            В общем случае вы, безусловно, правы, но здесь самоцель кода — его размер. Люди меряются не столько его функциональностью, читабельностью, сколько его размером и скоростью. Они просто экспериментируют. Разбираться в этом очень интересно потому, что здесь содержится максимальная концентрация алгоритмов, хаков, уловок и простых, самых первых человеческих изысканий в области компьютеров, это очень полезно. Может, и пригодится в жизни.

            +3
            Напомнили об .kkrieger
              +1
              На Dwitter еще посмотрите. Там много таких же демок.
                0

                Да, спасибо за совет. Вообще, таких демок красивая можно много найти, разбираться в них очень интересно.

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

              Самое читаемое