最大1KBのゆるい鳥

30週間の小文字のJSが1週間経ちましたが、 PhaserでFlappy Bird開発する記事(パートI)に触発され、 MinkowskiがFlappy Birdプレイしたとき 、JavaScriptでASCIIバージョンのゲーム「Flappy Bird」を記述し、これに適合するように努力しました1024文字。



ここで何が発生した(そして再生する)か、 ここで圧縮されていないソースを見ることができます

実装の詳細については、猫をお願いします。



ゲームについて



あなたがウィキで見つけることができる、またはオリジナルのゲームをプレイしようとすることで退屈な詳細を省きます。 つまり、障害物の間を飛んでいる鳥を制御します。 上キーを使用すると、少し上に押して操作できます。



フィールド



私は十分な真正性を求めて努力しませんでした、そして制限は非常に厳しいです。 したがって、最大の犠牲を払って、試行錯誤を使用して、多かれ少なかれ最適なフィールドサイズ(スケーリングなし)を選択しました-各30文字の10行:

 -+++++ ---- +++++ ----- +++++ -----
  +++++ +++++ +++++     
  +++++ +++++ +++++     
  +++++                        
                              
  0                            
           +++++ +++++     
  +++++ +++++ +++++     
  +++++ +++++ +++++     
 -++++ ---- +++++ ----- +++++ -----
 123


ゼロ-プレイヤーの位置(左の2列目を選択)、左下の123はポイントです。



フィールドアニメーション



最初のタスクは、競技場を移動する方法、障害物を右から左に移動する方法を学ぶことです。 フィールドセルを2次元配列に格納することもできますが、別の方法で行います。 行の1つを見て、その中のスペースをゼロに、バリアを1に置き換えてみましょう。

011111000011111000001111100000

これが数値のバイナリ表現であると推測するのは難しくありません(521110496)。 ビット単位の左シフト操作を使用して、左に移動する方が簡単です。 整数値の長さの制限を覚えておいてください。 30バイトの制限を維持するには、それらをマスクしてリッピングし、シフト後に不要なものをすべて切り取ります。

 = 011111000011111000001111100000;  =  << 1;  =  & 2^30; //  = 111110000111110000011111000000
      
      





2番目のタスクは、同じ幅の障害物を維持することです。 4つの可能なケース(最初のfalseの前)は次のようになります。

 ????010 - true -> 000011 ???0110 - true -> 000111 ??01110 - true -> 001111 ?011110 - true -> 011111 0111110 - false
      
      





論理は簡単です。2番目のビットが1の場合、障害物の幅の2番目のビットの次のビットが0であれば、 です。

適用するフィールドのすべての行をシフトする合計:

  <<= 1;  &= 2^30; if ( & 2) { for ( = 2;  <= _; ++) { if (!( & 1 << )) {  |= 1; break; } } }
      
      







得点



この場所には、採点するのに十分な条件があります。 2つの障害物が間隔なしで移動できない場合、前の列に障害物が正確に存在する場合、プレーヤーが位置するフィールドの各空の列にポイントを追加します(28と29は、どちらか1つに低いインデックスを持つ2つの隣接する列のインデックスです) 、-プレーヤー):

  += (__ & 1 << 29) && !(__ & 1 << 28) ? 1 : 0;
      
      







新しい障害



これは少し複雑です。 私は条件に耐えようとしました:



これは、次の形式で視覚化できます。

 000011111 -> 0% (0 * 12.5) 0 000111110 -> 0% (0 * 12.5) 1 001111100 -> 12.5% (1 * 12.5) 2 011111000 -> 25.0% (2 * 12.5) 3 111110000 -> 37.5% (3 * 12.5) .. 111100000 -> 50.0% (4 * 12.5) 111000000 -> 62.5% (5 * 12.5) 110000000 -> 75.0% (6 * 12.5) 100000000 -> 87.5% (7 * 12.5) 000000000 -> 100.0% (8 * 12.5)
      
      





最初の列はフィールドの極端なビットであり、その後に新しい障害の確率の割合が続きます。 8は最大間隔であり、ゲームで最適であり、計算に便利です。100を8で割って、具体的な値を取得できます。 右側の列は、マスクのビット単位の左シフトであり、これを使用して障害物間の現在のギャップの長さを検索および計算します。

ポイントは小さい:別のユニットに出会うまで、マスクユニットをビット単位で左に移動します。 現時点では、現在の期間と確率を知って、新しい障害を作成しようとしています。

     0   { if (__ & 1 << ) { if ( > 1 && ( - 2) * 125 +  > (124..999)) { //   } break; } }
      
      





すべてを10倍して、非整数値を取り除きました。 100%を除く:1000(100%* 10)からユニットを取得しました。ユニットはアプリケーションの余分なバイト全体であるためです! そして、覚えているように、バイトを保存する必要があります。

障害物自体を追加することは難しい作業ではありませんが、通行不能なセクションの作成を除外するために、次の障害物を追加しました:次の障害物はそれぞれ、前の障害物よりも1つ大きい/小さい、または2つ以上または5つ以上である必要があります。 さらに、フライトのギャップを維持します-3行。 取得するもの:

 //  -   ,   ,     = (   > 2 ?  - 1 : 2   < 5 ?  + 1 : 5 );    0   {  |= 1; }     + 3   {  |= 1; }
      
      







レンダリング



トリックはありません。 古いチューブテレビのように、行で、列で実行し、フィールドセルを蓄積します。

  = '';    ( ) {    ( ) {  +=  == 28 &&  == _ ? "0" : (  & 1 <<  ? "+" : ( ! ||  == _ - 1 ? "-" : " " ) ); }  += "\n"; } _;
      
      





個別に、プレーヤーとフィールド自体の上下の境界線を確認して描画します。



移動する



ゲームの世界は準備が整い、機能しています。 キャラクターを復活させるために残っています。 これを最大限に簡略化しました。 放物線、重力、加速、慣性の飛行はありません(おそらく少しを除く)。 上キーでインパルス(上方向への移動範囲)を設定します。 そして、まだゼロに達していない場合は、アニメーションの各反復でこの勢いを減らします。 テストでは、それは非常に悲惨に見え、鳥は明確に三角形の経路に沿って移動しました。 したがって、初期の運動量をわずかに増やし、運動量が1に等しい場合は「慣性による」運動を追加しました。

 if (  ) {  ,   = 3; } //  if ( > 1) { --; --_ || ; //  } else if () { --; //  } else { _ < 9 ? _++ : ; }
      
      





勢いはその価値だけでなく、キャラクターの位置も制御します。 さらに、フィールドの境界に到達したかどうかを確認します。

別途、障害物との衝突を確認します。

 if (_ & 1 << 28) { ; }
      
      







クリックして



これで、メイン作業が完了しました。 レイアウトを追加し、アプリケーションを匿名関数でラップして、いくつかのブラウザーでチェックインします。

圧縮前の結果
 <script> (function(){ var run = 0, imp = 0; function up(){ run = 1; var pos = 2, rows = [1, 1, 1, 1, 0, 0, 0, 1, 1, 1], rowsLen = 10, fieldWidth = 30, fieldMask = Math.pow(2, fieldWidth) - 1, profit = 0, hTop = 4, row, col, timer = setInterval(function(){ /** * Move user */ if (imp > 1) { imp--; // up --pos || _stop(); } else if (imp) { imp--; } else { // down pos < 9 ? pos++ : _stop(); } /** * Move field * * 0111110 - false * ?011110 - true -> 011111 * ??01110 - true -> 001111 * ???0110 - true -> 000111 * ????010 - true -> 000011 */ for (row = rowsLen; row--;) { rows[row] <<= 1; rows[row] &= fieldMask; if (rows[row] & 2) { for (w = 2; w <= 5; w++) { if (!(rows[row] & 1 << w)) { rows[row] |= 1; break; } } } } /** * Add new objects * * * 000011111 -> 0% (0 * 12.5) 0 * 000111110 -> 0% (0 * 12.5) 1 * 001111100 -> 12.5% (1 * 12.5) 2 * 011111000 -> 25.0% (2 * 12.5) 3 * 111110000 -> 37.5% (3 * 12.5) .. * 111100000 -> 50.0% (4 * 12.5) * 111000000 -> 62.5% (5 * 12.5) * 110000000 -> 75.0% (6 * 12.5) * 100000000 -> 87.5% (7 * 12.5) * 000000000 -> 100.0% (8 * 12.5) */ for (var tryNum = 0; true; tryNum++) { if (rows[0] & 1 << tryNum) { if (tryNum > 1 && (tryNum - 2) * 125 + profit > _rnd(124, 999)) { hTop = _rnd(hTop > 2 ? hTop - 1 : 2, hTop < 5 ? hTop + 1 : 5); // 2..5, prev +/- 1 for (h = 0; h < hTop; h++) { rows[h] |= 1; } for (h = hTop + 3; h < rowsLen; h++) { rows[h] |= 1; } } break; } } /** * Render */ var text = ''; for (row = 0; row < rowsLen; row++) { for (col = 29; col >= 0; col--) { text += col == 28 && row == pos ? "0" : ( rows[row] & 1 << col ? "+" : ( !row || row == rowsLen - 1 ? "-" : " " ) ); } text += "\n"; } profit += (rows[0] & 1 << 29) && !(rows[0] & 1 << 28) ? 1 : 0; text += "\n"+profit; pre.innerHTML = text; if (rows[pos] & 1 << 28) { _stop(); } }, 250); var _rnd = function(min, max){ return Math.floor(Math.random() * (max - min + 1)) + min; } var _stop = function(){ clearInterval(timer); run && alert(':('); run = 0; } } onkeyup = function(e){ e.which == 38 && (run ? imp = 3 : up()); }; })() </script> <body onload=""><pre id="pre">press up!
      
      







UglifyJSのようなオプティマイザーを使用してJSを実行し、次の場所に転送します。

 <body onload='..'
      
      





合計:785バイト。 これは制限ではないと確信しています!



参照資料



上記に加えて、興味深いものになります。




All Articles