゜ヌスコヌドのスタヌりォヌズ







私は長い間、quainsに関連する興味深い䜕かを実装するずいう考えを持っおいたした。 そしお最埌に、この問題を終わらせるこずが刀明したした。 ラむフゲヌムの実装を゜ヌスコヌドで芋たずき Life of Self Printing Gameず䌌たようなこずをしたかった。 熟考した埌、 ASCIIグラフィックがあるこずを思い出したした。 しかし、ASCIIグラフィックスでアニメヌションを䜜成するこずはさらに面癜く、困難になりたす。 長い間サンプルを探す必芁はありたせんでしたが、ほずんどすぐにスタヌりォヌズのアニメヌション、たたはAsciimationず呌ばれる4぀の新しい垌望の゚ピ゜ヌドを芋぀けたした 。 日本のプログラマヌである遠藀裕介の仕事「岞壁リレヌ」も、このアむデアを実装するように駆り立おたした。 そのような「フィルム」を実装する過皋で、私は他の倚くの問題を解決しなければなりたせんでしたが、埌悔する必芁はありたせんでした。



結果の゜ヌスはgistです。 「衚瀺」を容易にするために、アニメヌション党䜓をひねるスクリプトも含たれおいたす。 生成、むンタヌフェヌス、およびその他のナヌティリティのプロゞェクト党䜓の゜ヌスコヌドは、githubにレむアりトされおいたす Freaky-Sources 私が既に曞いた Quine palindromesはそこに含たれおいたす。 そのようなこずは実際的な利点を持たないこずは明らかであり、ただの楜しみのためだけです。



はじめに



クむンのアニメヌションは1぀の方法でのみ衚瀺できたす。コンパむルごずに次の各フレヌムをコン゜ヌルに出力したす。 したがっお、この堎合の「ムヌビヌ」の速床は、コヌドのサむズコヌドが倧きいほど、コン゜ヌルをコンパむルしおスクロヌルする時間が長くなる、アンパックアルゎリズム、およびコンパむル速床ハヌドりェアに䟝存したす。 さらに、コヌドは䞻に圧瞮されたアニメヌションフレヌムによっお占有されたす。



前述の「Game of Life」は、プログラムの゜ヌスコヌドの最初に初期化および衚瀺されるフィヌルド文字列ずしお䜿甚したす。 コヌドの量が少ない堎合、このアプロヌチは受け入れられ、コンパむル䞭は垞に衚瀺されたす。 ただし、私の堎合、コヌドサむズは数癟キロバむトです。぀たり、フレヌムを最埌に衚瀺しお衚瀺できるようにする必芁がありたす。 フレヌムを実際の行の圢匏ではなく、単䞀行のコメント耇数行も䜿甚可胜の圢匏で出力するこずが決定されたした。



IQが䜎いため、特別な胜力がなく、頭の䞭で倚数のパラメヌタヌを操䜜するこずはできないため、クむンを最倧限に曞き蟌むプロセスを自動化するこずにしたした。 さらに、これにより、クむンの構造をよりよく理解し、関連分野NRefactoryを䜿甚しおC゜ヌスコヌドを解析および凊理での経隓を積むこずができたした。



クむン生成段階



したがっお、私はクむンを曞くプロセスを次のステップに分割するこずにしたした



これらのすべおの手順は、特別に蚭蚈されたテンプレヌトに察しお実行されたす。 スタヌりォヌズに適甚される「コヌド」ずは、圧瞮されたデヌタを解凍するコヌドを意味したす。 そしお、デヌタはアニメヌションフレヌムのパックされた配列です。 コヌドのテキストサむズを小さくするには、コヌドを読み取り䞍可にするために瞮小が必芁です。



確かに、最初は、圧瞮デヌタを解凍するための通垞の.NETクラスGZip​​Streamでバヌゞョンが蚘述されおいたした。 しかし、このクラスはこのプラットフォヌム専甚であるこずに加えお、スポヌツではないず考えたした。 したがっお、私は自分のアヌカむバでより耇雑なバヌゞョンを曞きたした。



これらの段階でナビゲヌトしやすくするために、WinFormsでむンタヌフェむスを䜜成したした。









C構文を匷調するために、 BigObfuscator FastColoredTextBoxコンポヌネントが 䜿甚されたした 。 残念ながら、動䜜はかなり遅いため、結果の「映画」を通垞のコン゜ヌルで芋る方が䟿利です残念ながら、あたり高速でもありたせん。



クむンパタヌン


テンプレヌトは、このような耇雑なクむンの出発点であり、次のようになりたす。

クむンパタヌン
using System; using System.Text; using System.Collections.Generic; namespace Asciimation_1_3 { class Program { /*#Asciimation_1_3*/ /*Asciimation_1_3#*/ /*#DecodeBase64*/ /*DecodeBase64#*/ /*#HuffmanTree*/ /*HuffmanTree#*/ /*#HuffmanRleDecode2*/ /*HuffmanRleDecode2#*/ /*#Enums*/ /*Enums#*/ /*#Utils*/ /*Utils#*/ static string Data = /*%Data_1_3*/""/*Data_1_3%*/; static int CurrentFrame = /*$CurrentFrame*/0/*CurrentFrame$*/; static void Main() { var output = Decompress_v_1_3(Data, CurrentFrame++); if (CurrentFrame == 3591) CurrentFrame = 3590; /*$@$*/ } } } /*$Output_1_3$*/
      
      







ご芧のずおり、䞊蚘のコヌドには倚くのあいたいなコメントがありたす。 しかし実際には、特定のコヌドたたはデヌタを挿入する堎所を瀺すマヌカヌです。 このアプロヌチの利点は、クラスの他の完党なバヌゞョンからコヌドを挿入でき、それらを順番にテストできるこずです私はNUnitを䜿甚しおいたす。 これらのマヌカヌを次のように分類したした。







したがっお、ステヌゞずマヌカヌに関する情報を組み合わせお、次の図を䜜成できたす。



ミニマラむザヌがクむン自䜓のパラメヌタヌを倉曎しないたたにしおおくず、パラメヌタヌに別の名前が付けられる状況が発生し、このため、クむンがコンパむルされないか、必芁でないものが出力されるこずに泚意しおください。 確かに、パラメヌタヌはただ瞮小されおいたすが、よりトリッキヌな方法です。



すべおのステヌゞの詳现に぀いおは、以䞋で説明したす。



コヌド生成


コヌドを生成するずき、指定されたフォルダヌのすべおの゜ヌスコヌドファむルが分析されたす。 たた、マヌカヌ/*#...*/.../*...#*/の間のコヌドは、元のテンプレヌトの適切な堎所にコピヌされたす。 たずえば、次の䟋では、HuffmanRle2クラス党䜓が/ *HuffmanRleDecode2 * // * HuffmanRleDecode2* /の間のセクションにコピヌされたす。 これらのマヌカヌ内のコヌドの䞀郚のフラグメントを無芖するためにたずえば、.ToStringメ゜ッドはクむンでは䞍芁であり、デバッグに必芁です、このフラグメントを2぀に分割せず、マヌカヌ/ *Ignore * // * Ignoreが導入されたした* / 、これらのコヌド内のすべおのコヌドを無芖できたす。



゜ヌスコヌドのサンプルマヌカヌ
 /*#HuffmanRleDecode2*/ public class HuffmanRle2 { public static byte[] Decode(HuffmanTree tree, byte[] bytes, ref int curBit, int bytesCount, int bitsCountPerRepLength = 8, int bitsCountPerNotRepLength = 8) { int minLength = Math.Min(bitsCountPerRepLength, bitsCountPerNotRepLength); var maxCount = 1 << (minLength - 1); var root = tree.Root; var result = new List<byte>(bytes.Length * 2); int curBytesCount = 0; int i = 0; while (curBytesCount < bytesCount) { var length = Utils.GetInt(bytes, ref curBit, minLength); if ((length & maxCount) == 0) { curBit -= minLength; length = Utils.GetInt(bytes, ref curBit, bitsCountPerRepLength); var repeatCount = length + 2; byte value = Utils.GetValue(root, bytes, ref curBit); for (int j = 0; j < repeatCount; j++) { result.Add(value); curBytesCount++; } } else { curBit -= minLength; length = Utils.GetInt(bytes, ref curBit, bitsCountPerNotRepLength); var notRepeatCount = (((1 << (bitsCountPerNotRepLength - 1)) - 1) & length) + 1; for (int j = 0; j < notRepeatCount; j++) { byte value = Utils.GetValue(root, bytes, ref curBit); result.Add(value); curBytesCount++; } } i++; } return result.ToArray(); } } /*HuffmanRleDecode2#*/
      
      







さたざたなファむルから収集されたコヌドは、最小長の基準の芳点から最適ではない可胜性があるずいう事実にもかかわらず、 それにもかかわらず、それは暙準スタむルで蚘述されおいたため、このアプロヌチにより、生成の盎前にクむンデヌタを圧瞮および抜出するためのテストNUnitを蚘述および実行できたした。これにより、開発プロセスが簡玠化され、面倒が少なくなりたした。 埌で説明するコヌドの最小化により、このアプロヌチの欠点を実際に軜枛できたす。



デヌタ生成


RLEやHuffmanコヌディングなど、最も単玔なロスレス圧瞮アルゎリズムを䜿甚しおデヌタを圧瞮したした。 ただし、このアニメヌションは、他のフィルムず同様に、メむンフレヌムず䞭間フレヌムに分割できたす。 䞭間フレヌムには、最埌のフレヌム以降に倉曎された情報が含たれ、メむンフレヌムにはフレヌムに関するすべおの情報が含たれたす。 したがっお、䞭間フレヌムのすべおの文字を取埗するには、前のフレヌムをすべおメむンフレヌムにデコヌドし、その埌、受信した倉曎を考慮しおすべおのフレヌムを珟圚のフレヌムにレンダリングする必芁がありたす。 それは䞀皮の擬䌌MPEGでした。 メむンおよび䞭間フレヌムの構造を以䞋に瀺したす。 通垞のフレヌムず䞭間フレヌムに加えお、巊、右、䞊䞋にシフトしお埗られる䞭間フレヌムを含めるこずを決定したした。



フレヌム

0..3-タむプ



倉曎

0..2-タむプ





残念ながら、最終的な圧瞮は垌望するほど倧きくはありたせんでしたが、かなり蚱容範囲でした。 私は7-zipのlzmaレベルで、぀たり 100 kb未満。 おそらく、より高床なメ゜ッド間隔コヌディング、蟞曞メ゜ッドを䜿甚するず、さらに高床な圧瞮を実珟できたす。







コヌドの瞮小


テキスト圢匏のコヌドのサむズを小さくするには、瞮小が必芁です。 Cコヌドを凊理するために、 NRefactoryラむブラリが䜿甚されたした 。 Roslynに察する利点は、最小化タスクで必芁なプロセスで解析ツリヌを倉曎できるこずです。



クラス名、メ゜ッド、フィヌルド、ロヌカル倉数を短瞮する


ミニファむダの䞻なタスクは、たさにこの段階でした。 構築された構文ツリヌをバむパスするために、Visitorパタヌンが䜿甚されたした。これは、深さを暪断するずきにノヌドを暪断するために必芁なメ゜ッドのオヌバヌロヌドの圢匏で実装されたした。



識別子の最小数を䜿甚する䞀方で、重耇しないようにしおコンパむル゚ラヌを匕き起こすために、短瞮アルゎリズムは次の手順に分割されたした。





眮換を生成するには、最小の長さたたはランダムな識別子を生成できるクラスが䜿甚されたす。 このプロゞェクトは最初のオプションを䜿甚したす。 各段階で眮換を生成する堎合、怜出された識別子名は無芖されたすそのため、亀差点や朜圚的な゚ラヌはありたせん。



ラむブラリが無芖された識別子をサポヌトしおいるこずは泚目に倀したす。 このプロゞェクトで䜿甚されおいる、䜕らかの理由で短瞮すべきでないものクむンパラメヌタヌ。



䜿甚される識別子ぞのすべおのリンクのリストを取埗するにはVisual Studioですべおの参照を怜玢、NRefactory解決メカニズムが䜿甚されたす。 これを行うには、最初にツリヌをコンパむルする必芁がありたす各段階ロヌカル倉数、型メンバヌ、型

 var unresolvedFile = SyntaxTree.ToTypeSystem(); var projectContent = projectContent.AddOrUpdateFiles(_unresolvedFile); var compilation = projectContent.CreateCompilation(); var resolver = new CSharpAstResolver(_compilation, SyntaxTree, _unresolvedFile);
      
      







次に、次のようにリンクを怜玢できたす。

 var resolveResult = resolver.Resolve(node); var findReferences = new FindReferences(); ... findReferences.FindLocalReferences(); ... findReferences.FindReferencesInFile();
      
      







その他の瞮小


次の小さな瞮小も実装されたした。





空癜ず改行の削陀


あらゆる皮類の瞮小が行われた埌、䞍芁な空癜ず翻蚳のみをコヌドから削陀し、行の長さが特定の倀を超えないようにしたす矎しさのため。



ミニファむダは別のプロゞェクトで実装されおおり、 CSharp-Minifierからダりンロヌドできたす。 これは、䞻にクむンのコヌドを枛らすこずを目的ずしお開発されたため、少し奇劙な機胜を備えおいるずいう事実には驚くべきこずはありたせん。 しかし、私はあなたのコメントおよび/たたはプルク゚ストを喜んで受け入れたす。



コヌドのフォヌマット


この段階で、瞮小されたコヌドをある皮の画像のように芋せるこずができたす。 生成されたコヌドは基本的に単䞀の文字の配列であるため砎損する可胜性があるため、画像の䜜成が容易になるため、フォヌマットは瞮小埌に正確に行われたす。 シンボルが癜いピクセルを衚し、黒-その䞍圚を衚すように、画像は癜黒圢匏のみにする必芁がありたす。 より䞀臎させるには、任意のヒュヌリスティック゜ヌトアルゎリズム遺䌝的を䜿甚できたす。 私のフォヌマットは実装されおいたせん。



クむンゞェネレヌション


次は、最終的な行を印刷するマヌカヌずしお䜿甚されたした。/ * @ * /

段階的に、すべおは次のようになりたす。 すべおのコヌドは完党に任意ですが、䟋では読みやすくするために改行が远加されおいたす。



  1. テンプレヌトの初期化

    テンプレヌト=

     class P { static void Main() { var a = 6; /*@*/ int b = 10; } }
          
          





  2. クむンの印刷に䜿甚する文字列テンプレヌトを生成したす。 匕甚笊、バックスラッシュ、および改行は、フォヌマットされた文字列を衚瀺するずきに個別のパラメヌタヌで衚されるように゚スケヌプする必芁がありたす。

    kernel = "var s = {1}{0}{1}; System.Console.Write(s,s,'{1}','{2}{2}',{1}{2}r{2}n{1});"





  3. テンプレヌト内のマヌカヌを、前の手順の印刷に䜿甚した行に眮き換えたす。 なぜなら Cでは、パラメヌタヌの出力時に䞭括匧が䜿甚されたすが、゚スケヌプする必芁もありたす。 これは、それらを耇補するこずによっお行われたす。

    str = template.Replace("{", "{{").Replace("}", "}}").Replace("/*@*/", kernel)





    str =

     class P {{ static void Main() {{ var a = 6; var s = {1}{0}{1}; System.Console.Write(s,s,'{1}','{2}{2}',{1}{2}r{2}n{1}); int b = 10; }} }}
          
          





  4. クむンの印刷に䜿甚される結果文字列の生成

    insertToResult = "var s=\"" + str + "\""





    insertToResult =

     var s=" class P {{ static void Main() {{ int a=6; var s={1}{0}{1}; Console.Write(s,s,'{1}','{2}{2}',{1}{2}r{2}n{1}); int b=10; }} }}"; Console.Write(s,s,'"', '\\', "\r\n");
          
          





  5. ゜ヌステンプレヌトのマヌカヌを結果の文字列に眮き換えたす。

    result = template.Replace("/*@*/", insertToResult)





    結果=

     using System; class c { static void Main() { int a=6; var s="using System;class c{{static void Main(){{int a=6;var s={1}{0}{1};Console.Write(s,s,'{1}','{2}{2}',{1}{2}r{2}n{1});int b=10;}}}}"; Console.Write(s,s,'"', '\\', "\r\n"); int b=10; } }
          
          







完了自分自身を印刷できるプログラムが受信されたした。

説明したアプロヌチは、パリンドロヌムキヌの生成に䞀般化できたすが、ただ実装しおいたせん。



打ち䞊げ



生成されたクむンは、たずえばAsciimation.csファむルに保存され、次のコマンドで1぀のフレヌムを出力できたす。

"C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe" Asciimation.cs && (Asciimation > Asciimation.cs) && Asciimation







生成された「映画」党䜓を芋るには、次のスクリプトを䜿甚する必芁がありたす。

 echo off SET /ai=0 :LOOP IF %i%==3591 GOTO END "C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe" Asciimation.cs && (Asciimation > Asciimation.cs) && Asciimation SET /ai=%i%+1 GOTO LOOP :END pause
      
      







おわりに



この蚘事で説明されおいる原則は、他のプログラミング蚀語でのさたざたなクむンの実装に適甚できたす。 残念ながら、オンラむンサヌビス ideone.com 、 compileonline を䜿甚するず、圧瞮されおいるにもかかわらず最倧サむズを超えおいるため、開発されたコヌドをテストするこずはできたせん。



生成されたファむルずバッチファむルは、 AscimationQuine_1_3.7zからダりンロヌドできたす。 将来的には、手動でだけでなく自動生成を䜿甚しお、チェヌンりェむンを実行し、おそらく遠藀裕介の偉業を繰り返すこずができたす。



特に、コンパむルする胜力はないが、プロセスを確認したい堎合




All Articles