余分なライブラリやメガバイトのOSなしで、30行のアセンブラーで完全に役に立たないプログラムを書くことは可能ですか?
この記事では、30行のアセンブラーで三目並べを作成する方法について説明します。 UPDがわずか 20行になりました。 UPD2 AND 単一の条件分岐のない 18または16行。
タスク:動作するゲームの三目並べを作成します。
「ゲーム」のルール:
- 最大30行の純粋なアセンブラコマンド。 1行のdbでマシンコードを記述するという形式の詐欺なし......
- データサイズの制限はありません。 0行JSのゲームには 、HTMLとCSSの形式で多くのデータがありました
- サードパーティのライブラリを使用しないでください。OSを中断せず、BIOSのみを使用してください。
- プログラムは動作するはずです
30行のアセンブラーの実装の複雑さは、アセンブラーで単純なループが数行(コマンド)を取ることです。 各操作は個別のコマンドであり、新しい行を意味します。
アセンブラの知識が乏しい人のためにループに必要な行数を示す例
// C++ for ( int i=0; i<100; i++)...; _asm { // mov ecx,0 ; int i=0 labelForI: ... inc ecx ;i++ cmp ecx,100 ;i<100 jl labelForI // 127 , jmp, ecx, ... mov ecx,0 ; int i=0 labelForI: push ecx ... pop ecx inc ecx ;i++ cmp ecx,99 ;i<100 ja labelEndFor jmp labelForI labelEndFor: // . mov ecx,100 ; int i=100 labelForI: ... dec ecx ;i-- jnz labelForI // mov ecx,100 ; int i=100 labelForI: nop ; nop - , loop labelForI // , , . DB B9h,00,01,00,00,90h,E2h,F8h ; 99 nop (90h), . }
tic-tac-toeの最も簡単な実装は、勝利を決定するためにサイクルで比較する必要があるセルの値を含むマトリックスであり、サイクルで表示されます。 したがって、一見して30行のコードでゲームを作成するのは現実的ではありません。
最初の試みは、配列を減らして最適化することです
競技場をアレイの形で、さらにはマトリックスの形で保存すると、勝利を決定するまでに数サイクルかかるためです。 プレイフィールド全体を、サイズが3 x 3(4 * 4で十分)に4バイト、または2マシンワード、プレーヤーあたり2バイトで収めることができます。 したがって、徹底的な検索により、8つの数値の形式で表される8つの勝利オプション(int map_x、int map_0とのビット一致の比較)に勝利チェックを減らすことができます。
アイデアをテストするために、Cで実装しました。
ゲームは、次の形式で画面に競技場を表示します。
ユーザーは、数字キーパッドの位置にあるセルに対応する数字をクリックします。 たとえば、数字の5は中央、1は左下のセルです。
オプション1、C
/* * File: main.c * Author: godAlex * * . * . * * Created on 29 2014 ., 11:51 */ #include <stdio.h> #include <stdlib.h> #define win_x 1 #define win_0 2 #define win_tie 3 #define win_not 0 unsigned short KEYBOARD_BYTE_OFFSET[] = {0x40,0x80,0x100,0x8,0x10,0x20,0x1,0x2,0x4 }; //{0b001000000,0b010000000,0b100000000,0b000001000,0b000010000,0b000100000,0b000000001,0b000000010,0b000000100 }; unsigned short BOT_POSITION_PRIORITY[] = { 0x10, 0x40, 0x4, 0x100, 0x1, 0x80, 0x2, 0x8, 0x20}; // center bounds other //{0b000010000, 0b001000000,0b000000100,0b100000000,0b000000001,0b010000000,0b000000010,0b000001000,0b000100000 }; unsigned short WIN_MATRIX[] = { 0x1C0, 0x38, 0x7, 0x124, 0x92, 0x49, 0x111, 0x54 }; //{ 0b111000000, 0b000111000, 0b000000111, 0b100100100, 0b010010010, 0b001001001, 0b100010001, 0b001010100 }; // unsigned short MAP_X = 0; unsigned short MAP_0 = 0; unsigned short WIN_MATRIX_NOT_WIN = 0x1FF; //0b111111111; // , xor unsigned short STRING_ENTER_POS = 0x124;//0b100100100; // void specialPrintChar(char *c) { printf(c); // TODO ASM } unsigned short specialReadNumber() { char outC; scanf("%c",&outC); // TODO ASM return outC-'0'; } short testWon() { char i; for (i=0; i<8; i++) { if ( (MAP_X & WIN_MATRIX[i]) == WIN_MATRIX[i] ) return win_x; if ( (MAP_0 & WIN_MATRIX[i]) == WIN_MATRIX[i] ) return win_0; } if ( (MAP_X | MAP_0) == WIN_MATRIX_NOT_WIN ) return win_tie; // return win_not; } void printField() { unsigned short bOfs; for (bOfs=1; bOfs<WIN_MATRIX_NOT_WIN; bOfs=bOfs<<1) { // shl if ( MAP_X & bOfs ) specialPrintChar("X"); else { if ( MAP_0 & bOfs ) specialPrintChar("0"); else specialPrintChar("."); } if ( bOfs & STRING_ENTER_POS ) specialPrintChar("\n"); // } } // - - - - - - - - - - - - - - - - int main(int argc, char** argv) { specialPrintChar("Test XO game!\n"); printField(); // // ... int whoIsWon=win_not; while (whoIsWon==win_not) { short cKey; unsigned short full_map; unsigned short p; // do { do { specialPrintChar("Enter cell position (1-9):\n"); cKey = specialReadNumber(); } while ( cKey<1 || cKey>9 ); p=KEYBOARD_BYTE_OFFSET[cKey-1]; // full_map = MAP_X | MAP_0; // } while ( (full_map & p) !=0); // . MAP_X = MAP_X | p; // printField(); // // test Win whoIsWon=testWon(); if (whoIsWon!=win_not) break; // full_map = MAP_X | MAP_0; for (p=0 ; (full_map & BOT_POSITION_PRIORITY[p]) != 0 ; p++); MAP_0 = MAP_0 | BOT_POSITION_PRIORITY[p]; // specialPrintChar(" the BOT:\n"); printField(); // whoIsWon=testWon(); // test Win }; switch (whoIsWon) { // GoTo label testWon (asm) case win_x: specialPrintChar("Won X!\n"); return 1; case win_0: specialPrintChar("Won 0!\n"); return 2; case win_tie: specialPrintChar("Win nothing!\n"); return 3; } return (EXIT_SUCCESS); }
コードが多すぎます。 結果のプログラムを逆コンパイルすると、printfとscanfの呼び出しをカウントせずに、100を超えるコマンドがあることがわかりました。 アセンブラーに翻訳してみることができますか?
オプション1、関数testWon()、アセンブラーで書き換え
int testWon() { int winResult=-1; // todo to asm _asm { mov ecx,0 ;xor ecx,ecx mov edx, offset WIN_MATRIX lForI: //mov bx,WIN_MATRIX[ecx] mov bx, word ptr [edx] ; !!! add edx,2 ; [edx][ecx] mov ax,MAP_X and ax,bx cmp ax,bx ; TODO test, JE lblWonX mov ax,MAP_0 and ax,bx cmp ax,bx JE lblWon0 inc ecx cmp ecx,8 jne lForI mov ax,MAP_X ; or ax,MAP_0 cmp ax,WIN_MATRIX_NOT_WIN jne lblRet ; mov winResult,win_no ; jmp lblRet ;RET lblWonX: mov winResult,win_x jmp lblRet ;RET lblWon0: mov winResult,win_0 jmp lblRet ;RET lblRet: ;RET } return winResult; }
勝利検証機能のみが約30チームを要します。 そして、結果を画面に表示し、キーボードから位置を読み取るための機能がまだあります。
このオプションは適切ではありません。 ただし、このような実装は、ディスクの最初のセクターの512バイトのサイズのBNZに収まります。
2番目の方法は、JSの0行でHTMLで同じことをする方法ですか?
また、JavaScriptの0行のHTMLでこれをどのように解決できるかを考えたらどうでしょうか。
ゲームの各バージョンを既製の出力行として画面に記録し、各オプションに次のエントリのアドレスを追加します。各オプションは、対応するボタンが押されたときに表示される必要があります。
HTMLでは、これはアンカーと長い「partyka」の助けを借りて実装されます。 アセンブラー、またはCでは、ボタンの押下に応じて、画面にテキストを表示するだけのプログラムを作成する必要があります。
プログラム自体は小さいことが判明しますが、その作業に必要なデータは膨大です。
Cで実装されたアルゴリズムの例:
#include <stdio.h> #include <stdlib.h> unsigned short data_addres[374][9] = {{1, 71, 113, 190, 214, 262, 300, 327, 353}, {1, 1, 2, 16, 24, 31, 45, 52, 65}, ...}; char text[374][13] = {"...\n...\n...\n\0", "...\n...\nX0.\n\0", "...\n..0\nX0X\n\0", ...}; int main(int argc, char** argv) { unsigned int data_pos = 0; while (1) // data_pos != { printf(text[data_pos]); int i; do scanf("%i",&i); while ( i<1 || i>9 ); data_pos=data_addres[data_pos][i-1]; } return (EXIT_SUCCESS); }
そして、アセンブラーで何が起こりますか?
オプション2、29コマンド用のアセンブラー、BIOS割り込みで実行!
SECTION .text org 0x100 ; .com . , . lblShowVariant: mov ax,0x0001 ; clear screen, set graphic mode 40x25, color int 10h mov ax, [data_pos] mov bx, [TEXT_BLOCK_SIZE] mul bx mov bp, text_data add bp,ax ; offset mov cx,[TEXT_BLOCK_SIZE] mov ax,1300h mov bx,0eh ; color mov dx,0500h ; 5 0 int 10h lReadKey: xor ax,ax int 16h ; BIOS read key xor ah,ah sub al,'1' ; al , cmp ax,8 ja lReadKey shl ax,1 ; ax=ax*2 mov bx, data_addres add bx,ax ; bx = data_addres[key] mov ax, [data_pos] mov cx, [CASE_BLOCK_SIZE] mul cx ; cx = [data_pos] add bx,ax ; bx = data_addres[data_pos][key] mov ax,[bx] mov [data_pos],ax ; . jmp lblShowVariant SECTION .data ; Out data on assembler data format data_pos DW 0 ; max=394 CASE_BLOCK_SIZE DW 18 ;bytes (2 byte per 1 case) TEXT_BLOCK_SIZE DW 16 data_addres: DW 1, 42, 72, 100, 139, 167, 198, 272, 341 DW 1, 2, 7, 9, 13, 14, 1, 17, 33 ... text_data: DB "...", 13,10, "...", 13,10, "...", 13,10, " " DB "0..", 13,10, "...", 13,10, "X..", 13,10, " " DB "00.", 13,10, "...", 13,10, "XX.", 13,10, " " DB "You are won!", " " ...
プログラムは機能し、BIOS機能のみが必要です。オペレーティングシステムなしで起動できます。プログラムの先頭でレジスタを修正する必要があるだけです(mov ax、cs; mov ds、ax; mov es、ax ...ブートローダーに登録します。 テキストの色を変更することにより、プログラムの出力を装飾することが判明しました。 勝者の行(PCスピーカー)のテキストにコード7の文字を追加して、サウンドを挿入できます。
半分完了。 現在では、数千のアドレスとゲームオプションで構成されるデータ配列を記述する必要があります。
推定データサイズの計算:
- 9セル(ボタン)、アドレス指定ワード=>競技場の各バージョンに対して18バイト
- 13〜16バイトのテキストボックス
各オプションごとに合計約31〜34バイト。 合計ゲームオプションは9です! (9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1)。 これはたくさんありますが、コンピューターのオプションはデータ生成段階で決定されるため、プレーヤーのオプションセットのみを保存する必要があります。 したがって、オプションはもうありません。9 * 7 * 5 * 3 * 1 =945。最後のセルが満たされる前にいくつかのオプションを完了することができる場合(勝利、敗北)、さらに少ないオプションがあります。 すべてのオプションとアドレスを保存するために必要なメモリの合計は32130バイト(34 * 945)以下で、1ページのメモリ(65535バイト)に収まります。
このデータを取得するために、すべてのオプションを生成し、それらをC / C ++の配列およびアセンブラーのデータとして表示するC ++プログラムが作成されました。 次に、C、アセンブラー、そして0行のJSを含むHTMLで完全なソースコードを出力する機能が追加されました。
30行のアセンブラコード(UPDまたはDOS割り込みを使用する20行、ただし色なし)、12行のC、および0行のJS(HTML)を含むゲームのC ++データジェネレーターソース
/* * Author: godAlex (C) 2014 * version 1.2 * License GNU GPL v 3 * param: -h -a -c -sa -sc -sh * data optimization: -pvp, -pve, -findequal * optimization: -o0, -o1, -o2, -o3 (unstable), -dosint */ #include <cstdlib> #include "iostream" #include "string.h" using namespace std; //------------------------------------------- //#define show_debug 1 #define win_x 1 #define win_0 2 #define win_end 3 #define win_gameNext 0 #define CASE_BLOCK_SIZE 9 #define TEXT_BLOCK_SIZE 16 int Text_Block_Size=13; // 13, 13, 13,10 16. asm. char End_Of_String=0; bool useDOSInt=false; // BIOS bool mode_pve=true; // pvp ( ) bool optimizeDataFind=true; // bool casheWinVar=true; // char *winText[4]; // , . unsigned short KEYBOARD_BYTE_OFFSET[] = {0x40,0x80,0x100,0x8,0x10,0x20,0x1,0x2,0x4 }; //{0b001000000,0b010000000,0b100000000,0b000001000,0b000010000,0b000100000,0b000000001,0b000000010,0b000000100 }; //unsigned short BOT_POSITION_PRIORITY[] = //{ 0x10, 0x40, 0x4, 0x100, 0x1, 0x80, 0x2, 0x8, 0x20}; // center bounds other //{0b000010000, 0b001000000,0b000000100,0b100000000,0b000000001,0b010000000,0b000000010,0b000001000,0b000100000 }; unsigned short WIN_MATRIX[] = { 0x1C0, 0x38, 0x7, 0x124, 0x92, 0x49, 0x111, 0x54 }; //{ 0b111000000, 0b000111000, 0b000000111, 0b100100100, 0b010010010, 0b001001001, 0b100010001, 0b001010100 }; unsigned short MATRIX_FULL = 0x1FF; //0b111111111; unsigned short STRING_ENTER_POS = 0x124;//0b100100100; // mode_pve==false && optimizeDataFind==false, max = 294781 #define MAX_DATA_SIZE 550000 int data_addres[MAX_DATA_SIZE][CASE_BLOCK_SIZE]; char text[MAX_DATA_SIZE][TEXT_BLOCK_SIZE]; int data_pos = 0; // FIXME unsigned short data_hashcashe[MAX_DATA_SIZE][2]; // findGameVar optimizeDataFind //---- int testWon(unsigned short MAP_X,unsigned short MAP_0) { for (int i=0; i<8; i++) { if ( (MAP_X & WIN_MATRIX[i]) == WIN_MATRIX[i] ) return win_x; if ( (MAP_0 & WIN_MATRIX[i]) == WIN_MATRIX[i] ) return win_0; } if ( (MAP_X | MAP_0) == MATRIX_FULL ) return win_end; return win_gameNext; } void printField(unsigned short MAP_X,unsigned short MAP_0, char* result) { //char result[TEXT_BLOCK_SIZE]="...\n...\n...\n"; int p=0; for (unsigned int bOfs=1; bOfs<MATRIX_FULL; bOfs=bOfs<<1) { // shl TODO test owerflow! if ( MAP_X & bOfs ) result[p]='X'; else { if ( MAP_0 & bOfs ) result[p]='0'; else result[p]='.'; } if ( bOfs & STRING_ENTER_POS ) { p++; result[p]='\n'; } p++; } result[p]=End_Of_String; return result; } // ---- int setVariant(int varID, int variants[CASE_BLOCK_SIZE], char* txt) { int i; for (i=0;i<CASE_BLOCK_SIZE;i++) data_addres[varID][i]=variants[i]; // TODO memcopy for (i=0; i<Text_Block_Size && ( txt[i]!=End_Of_String && txt[i]!=0 ) ; i++) text[varID][i]=txt[i]; text[varID][Text_Block_Size-1]=End_Of_String; // . #ifdef show_debug cout<<" set №"<<varID<<" as "<<text[varID]<<endl; #endif return varID; } void addToCasheVar(int i,unsigned short MAP_X,unsigned short MAP_0) // if optimizeDataFind { data_hashcashe[i][0]=MAP_X; data_hashcashe[i][1]=MAP_0; } int getFreeVar() { int p=data_pos; if (p>=MAX_DATA_SIZE-1) { cout<<"Owerflow data pos! " << p <<endl; return -1; } data_pos++; #ifdef show_debug cout<<"New variant №"<<p<<endl; #endif return p; } int itrGameHod_traceAll(unsigned short MAP_X,unsigned short MAP_0, bool playOn_X); /** * -. * (0) * - int [0,8], . */ int getBestBotHod(unsigned short MAP_X,unsigned short MAP_0) { // TODO bot . unsigned short lastMAP_X=MAP_X; unsigned short lastMAP_0=MAP_0; unsigned short full_map = MAP_X | MAP_0; int winLevel=-1; // int winPos=-1; //* //if (winLevel<1) for (int i=0; i<9; i++) { // unsigned short p = 1<<i; if ( (full_map & p) == 0 ) { // MAP_0 |= p; // if (playOn_X) MAP_X |= p; else MAP_0 |= p; int tmpWLvl=0; int w=testWon(MAP_X,MAP_0); if ( w!=win_gameNext ) { if (w==win_0) tmpWLvl=40; } else { w=itrGameHod_traceAll(MAP_X,MAP_0, true); if (w & win_0) tmpWLvl+=4; if (w & win_end) tmpWLvl+=2; if (w & win_x) tmpWLvl+=0; } if (tmpWLvl>winLevel) { //|| (tmpWLvl==winLevel && (rand() % 3 == 1))) { winLevel=tmpWLvl; winPos=i; } MAP_X=lastMAP_X; MAP_0=lastMAP_0; } } //*/ return winPos; } int winWariantCashe[4]={0,0,0,0}; // , , . int winWariantVer[4]={0,0,0,0}; // , int setEndGameVariant(unsigned short MAP_X,unsigned short MAP_0, char* txt) { int currentRecordID; if (casheWinVar) { int wonIs = testWon(MAP_X,MAP_0); winWariantVer[wonIs]++; if (winWariantCashe[wonIs]==0) { int addres[CASE_BLOCK_SIZE]={0,0,0,0,0,0,0,0,0}; // ( ) currentRecordID=getFreeVar(); // setVariant(currentRecordID,addres, txt); winWariantCashe[wonIs]=currentRecordID; } else currentRecordID=winWariantCashe[wonIs]; } else { int addres[CASE_BLOCK_SIZE]={0,0,0,0,0,0,0,0,0}; // ( ) //currentText= printField (MAP_X,MAP_0); // , 2 . int currentRecordID=getFreeVar(); setVariant(currentRecordID,addres, txt); } return currentRecordID; } /* * bot 0, mode_pve==true * * playOn_X - 0= , * */ int itrGameHod_traceAll(unsigned short MAP_X,unsigned short MAP_0, bool playOn_X) { unsigned short lastMAP_X=MAP_X; unsigned short lastMAP_0=MAP_0; unsigned short full_map = MAP_X | MAP_0; int winResult=0; int allWinVariant=win_x | win_0 | win_end; if (playOn_X || !mode_pve) { // user, for (int i=0; i<CASE_BLOCK_SIZE; i++) { unsigned short p = 1<<i; if ( (full_map & p) == 0 ) { // //MAP_X |= p; // if (playOn_X) MAP_X |= p; else MAP_0 |= p; int w=testWon(MAP_X,MAP_0); if (w!=win_gameNext) { winResult |= w; if (winResult==allWinVariant) return winResult; // , = } else w=itrGameHod_traceAll(MAP_X,MAP_0, !playOn_X); // iteration MAP_X=lastMAP_X; MAP_0=lastMAP_0; } } } else { // , ( mode_pve ) int i=getBestBotHod(MAP_X,MAP_0); unsigned short p = 1<<i; if ( (full_map & p) == 0 ) { // MAP_0 |= p; int w=testWon(MAP_X,MAP_0); if (w!=win_gameNext) { winResult |= w; if (winResult==allWinVariant) return winResult; } else w=itrGameHod_traceAll(MAP_X,MAP_0, !playOn_X); // iteration MAP_0=lastMAP_0; } } return winResult; } /** * * -1 */ int findGameVar(unsigned short MAP_X,unsigned short MAP_0) { for ( int i=0; i<data_pos ; i++ ) if ( data_hashcashe[i][0]==MAP_X && data_hashcashe[i][1]==MAP_0 ) return i; return -1; } /* * bot 0 * * playOn_X - 0= , * addNewWariant. . */ int itrGameHod_recordAll(unsigned short MAP_X,unsigned short MAP_0, bool playOn_X) { unsigned short lastMAP_X=MAP_X; unsigned short lastMAP_0=MAP_0; unsigned short full_map = MAP_X | MAP_0; int addres[CASE_BLOCK_SIZE]; // ( ) char currentText[Text_Block_Size]; // printField (MAP_X,MAP_0,currentText); if (optimizeDataFind) { int gVar = findGameVar(MAP_X, MAP_0); if ( gVar >=0 ) return gVar; } int currentRecordID=getFreeVar(); if (currentRecordID==-1) { return -1; } if (optimizeDataFind) addToCasheVar(currentRecordID, MAP_X, MAP_0); if (playOn_X || !mode_pve) { // for (int i=0; i<CASE_BLOCK_SIZE; i++) { unsigned short p = KEYBOARD_BYTE_OFFSET[i]; //1<<i; if ( (full_map & p) == 0 ) { // if (playOn_X) MAP_X |= p; else MAP_0 |= p; int w=testWon(MAP_X,MAP_0); if (w!=win_gameNext) { // out who is won addres[i]=setEndGameVariant(MAP_X,MAP_0, winText[w]); // FIXME TEST! winText[win_x] } else { if (mode_pve) { // BOT, PVE int p2Int=getBestBotHod(MAP_X,MAP_0); // 0, !playOn_X if (p2Int == -1) cout<<"Wrong bot variant!"<<endl; unsigned short p2Bit=1<<p2Int; MAP_0 |= p2Bit; int w=testWon(MAP_X,MAP_0); if (w!=win_gameNext) { // out who is won addres[i]=setEndGameVariant(MAP_X,MAP_0, winText[win_0]); } else { // add new wariant int nextDataAddr=itrGameHod_recordAll(MAP_X,MAP_0, playOn_X); // iteration addres[i] = nextDataAddr; } } else { // PVP int nextDataAddr=itrGameHod_recordAll(MAP_X,MAP_0, !playOn_X); // iteration addres[i] = nextDataAddr; // TODO if == -1 } } MAP_X=lastMAP_X; MAP_0=lastMAP_0; } else { addres[i]=currentRecordID; } } currentRecordID=setVariant(currentRecordID, addres,currentText); return currentRecordID; } else { cout<<"Error! ."<<endl; return -1; } } // -------------- void outDataFormatC() { int minimalCaseSize=1; if (data_pos>0xff) minimalCaseSize=2; if (data_pos>0xffff) minimalCaseSize=4; cout<< "// Out data on C array:"<<endl; cout<<"int data_pos = 0; // max="<<data_pos<<endl; cout<<"int CASE_BLOCK_SIZE = "<<CASE_BLOCK_SIZE<<";"<<endl; cout<<"int TEXT_BLOCK_SIZE = "<<Text_Block_Size<<";"<<endl; cout<<"unsigned "; //data_addres if (minimalCaseSize==1) cout<<"char"; if (minimalCaseSize==2) cout<<"short"; if (minimalCaseSize==4) cout<<"int"; cout<<" data_addres["<<data_pos<<"]["<<CASE_BLOCK_SIZE<<"] = {"; for ( int i=0; i<data_pos; i++) { if (i!=0) cout<<", "; cout<<"{"; for ( int j=0; j<CASE_BLOCK_SIZE; j++) { if (j!=0) cout<<", "; cout<<data_addres[i][j]; } cout<<"}"; } cout<<"};"<<endl; //text cout<<"char text["<<data_pos<<"]["<<Text_Block_Size<<"] = {"; for ( int i=0; i<data_pos; i++) { if (i!=0) cout<<", "; // //cout<<"{"; //for ( int j=0; j<TEXT_BLOCK_SIZE; j++) { // if (j!=0) cout<<", "; // cout<< (int)text[i][j]; //} //cout<<"}"; //cout<< "\""<<text[i]<<"\""<<endl; // cout<<"\""; for ( int j=0; j<Text_Block_Size; j++) { if (text[i][j]>=30) cout<< text[i][j]; else { if (text[i][j]=='\n') cout<<"\\n"; else if (text[i][j]==0) cout<<"\\0"; else cout<< "\\("<<(int)text[i][j]<<")"; } } cout<<"\""; } cout<<"};"<<endl; cout<<"// ---- end of data ----"<<endl; } /** * * @param premul , ( ) */ void outDataFormatAsm(int optLevel) { int minimalCaseSize=2; //if (data_pos>0xff) minimalCaseSize=2; //if (data_pos>0xffff) minimalCaseSize=4; cout<<"; Out data on assembler data format"<<endl; if (optLevel==0) { cout<<"data_pos DW 0 ; max="<<data_pos<<"-1"<<endl; cout<<"CASE_BLOCK_SIZE DW "<<CASE_BLOCK_SIZE*minimalCaseSize<<" ;bytes ("<<minimalCaseSize<<" byte per 1 case)"<<endl; cout<<"TEXT_BLOCK_SIZE DW "<<Text_Block_Size<<endl; cout<<"data_addres:"<<endl; for ( int i=0; i<data_pos; i++) { //if (minimalCaseSize==1) cout<<"DB "; //if (minimalCaseSize==2) cout<<"DW "; //if (minimalCaseSize==4) cout<<"QW "; // data_pos 16- DW cout<<"DW "; for ( int j=0; j<CASE_BLOCK_SIZE; j++) { if (j!=0) cout<<", "; cout<<data_addres[i][j]; } cout<<endl; } cout<<endl; //text cout<<"text_data: \n"; bool textMarker=false; for ( int i=0; i<data_pos; i++) { cout<<"DB "; int maxOutBytes=Text_Block_Size; for ( int j=0; j<maxOutBytes; j++) { if (text[i][j]>=30) { if (!textMarker) { if (j!=0) cout<<", "; cout<<"\""; textMarker=true; } cout<< text[i][j]; } else { if (textMarker) { cout<<"\""; textMarker=false; } if (text[i][j]=='\n') { cout<<", 13,10"; maxOutBytes--; } else if (text[i][j]==0 && End_Of_String!=0) cout<<", \""<<End_Of_String<<"\""; // DOS int 21h. else if (text[i][j]==0) cout<<", \" \""; // FIXME ? else cout<< ", "<<(int)text[i][j]; } } if (textMarker) { cout<<"\""; textMarker=false; } cout<<endl; } } if (optLevel==1) { cout<<"data_pos DW 0 ; max="<<data_pos<<"-1"<<endl; //cout<<"CASE_BLOCK_SIZE DW "<<CASE_BLOCK_SIZE*minimalCaseSize<<" ;bytes ("<<minimalCaseSize<<" byte per 1 case)"<<endl; //cout<<"TEXT_BLOCK_SIZE DW "<<Text_Block_Size<<endl; cout<<"data_addres:"<<endl; //data_addres for ( int i=0; i<data_pos; i++) { cout<<"DW "; for ( int j=0; j<CASE_BLOCK_SIZE; j++) { if (j!=0) cout<<", "; cout<< (data_addres[i][j] * (CASE_BLOCK_SIZE*minimalCaseSize+Text_Block_Size)); // . } cout<<endl; //text cout<<"DB "; bool textMarker=false; int maxOutBytes=Text_Block_Size; for ( int j=0; j<maxOutBytes; j++) { if (text[i][j]>=30) { if (!textMarker) { if (j!=0) cout<<", "; cout<<"\""; textMarker=true; } cout<< text[i][j]; } else { if (textMarker) { cout<<"\""; textMarker=false; } if (text[i][j]=='\n') { // "" cout<<", 13,10"; maxOutBytes--; } else if (text[i][j]==0 && End_Of_String!=0) cout<<", \""<<End_Of_String<<"\""; // DOS int 21h. else if (text[i][j]==0) cout<<", \" \""; // , ? else cout<< ", "<<(int)text[i][j]; } } if (textMarker) { cout<<"\""; textMarker=false; } cout<<endl; } } if (optLevel>=2) { cout<<"data_pos DW d_0"<<endl; for ( int i=0; i<data_pos; i++) { cout<<"d_"<< i <<" "; //data_addres cout<<"DW "; for ( int j=0; j<CASE_BLOCK_SIZE; j++) { if (j!=0) cout<<", "; cout<< "d_"<<data_addres[i][j]; // . } cout<<endl; //text cout<<"DB "; bool textMarker=false; int maxOutBytes=Text_Block_Size; for ( int j=0; j<maxOutBytes; j++) { if (text[i][j]>=30) { if (!textMarker) { if (j!=0) cout<<", "; cout<<"\""; textMarker=true; } cout<< text[i][j]; } else { if (textMarker) { cout<<"\""; textMarker=false; } if (text[i][j]=='\n') { // "" cout<<", 13,10"; maxOutBytes--; } else if (text[i][j]==0 && End_Of_String!=0) cout<<", \""<<End_Of_String<<"\""; // DOS int 21h. else if (text[i][j]==0) cout<<", \" \""; // FIXME ? else cout<< ", "<<(int)text[i][j]; } } if (textMarker) { cout<<"\""; textMarker=false; } cout<<endl; } } cout<<"; ---- end of data ----"<<endl; return; } /** * @param showInfo */ void generator_game_data(bool showInfo) { if (showInfo) cout<<"Start generation."<<endl; for ( int i=0; i<MAX_DATA_SIZE; i++) for ( int j=0; j<Text_Block_Size; j++) text[i][j]=End_Of_String; int startP = itrGameHod_recordAll(0,0, true); if (showInfo) { cout<< "Finish generation. Start game position="<<startP<<endl; cout<< "Data length = "<<data_pos<<endl; int minimalCaseSize=1; if (data_pos>0xff) minimalCaseSize=2; if (data_pos>0xffff) minimalCaseSize=4; cout<< " key array size is "<<(data_pos*CASE_BLOCK_SIZE*minimalCaseSize)<<" byte ("<<minimalCaseSize<<" byte per case)"<<endl; cout<< " text array size is "<<(data_pos*Text_Block_Size*sizeof(char))<<" byte"<<endl; cout<< " : "<<winWariantVer[win_end]<<", 0 "<<winWariantVer[win_0]<<", X "<<winWariantVer[win_x] <<endl; cout<< "Use --help for help." <<endl; } } //------------------------------------------- void outListingC() { cout<<"/*"<<endl; cout<<"* example short command tic-tac-toe. By godAlex generator."<<endl; cout<<"*/"<<endl; cout<<"#include <stdio.h>"<<endl; cout<<"#include <stdlib.h>"<<endl; outDataFormatC(); cout<<"int main(int argc, char** argv) {"<<endl; cout<<" while (1) {"<<endl; cout<<" printf(text[data_pos]);"<<endl; cout<<" int i;"<<endl; cout<<" do scanf(\"%i\",&i); while ( i<1 || i>9 );"<<endl; cout<<" data_pos=data_addres[data_pos][i-1];"<<endl; cout<<" }"<<endl; cout<<" return (EXIT_SUCCESS);"<<endl; cout<<"}"<<endl; } void outListingAsm(int optLevel) { cout<<"SECTION .text"<<endl; cout<<"org 0x100 ; .com "<<endl; // cout<<"push cs"<<endl; // cout<<"pop ds ; "<<endl; cout<<"lblShowVariant: "<<endl; cout<<" mov ax,0x0001 ; clear screen, set graphic mode 40x25, color"<<endl; cout<<" int 10h"<<endl; if (!useDOSInt) { // BIOS if (optLevel==0) { cout<<" mov ax, [data_pos]"<<endl; cout<<" mov bx, [TEXT_BLOCK_SIZE]"<<endl; cout<<" mul bx"<<endl; cout<<" mov bp, text_data"<<endl; cout<<" add bp,ax ; offset "<<endl; } if (optLevel==1) { // -3 cout<<" mov bp, data_addres+"<<(CASE_BLOCK_SIZE*2)<<endl; cout<<" add bp, [data_pos]"<<endl; } if (optLevel>=2) { // -1/-3 cout<<" mov bp, "<<(CASE_BLOCK_SIZE*2)<<endl; cout<<" add bp, [data_pos]"<<endl; } cout<<" mov cx, "<<Text_Block_Size<<" ; [TEXT_BLOCK_SIZE]"<<endl; // [TEXT_BLOCK_SIZE] cout<<" mov ax,1300h"<<endl; cout<<" mov bx,0eh ; color"<<endl; cout<<" mov dx,0500h ; 5 0 "<<endl; cout<<" int 10h"<<endl; } else { // DOS if (optLevel==0) { cout<<" mov ax, [data_pos]"<<endl; cout<<" mov bx, [TEXT_BLOCK_SIZE]"<<endl; cout<<" mul bx"<<endl; cout<<" mov dx, text_data"<<endl; cout<<" add dx,ax ; offset "<<endl; } if (optLevel==1) { // -3 cout<<" mov dx, data_addres+"<<(CASE_BLOCK_SIZE*2)<<endl; cout<<" add dx, [data_pos]"<<endl; } if (optLevel>=2) {// -1 -3 cout<<" mov dx, "<<(CASE_BLOCK_SIZE*2)<<endl; cout<<" add dx, [data_pos]"<<endl; } cout<<" mov ah, 0x9 ; print [ds][dx]"<<endl; cout<<" int 21h ; dos, $"<<endl; } if (optLevel<3) cout<<"lReadKey: "<<endl; //cout<<" rep nop ; , "<<endl; cout<<" xor ax,ax"<<endl; cout<<" int 16h ; BIOS read key"<<endl; cout<<" xor ah,ah"<<endl; cout<<" sub al,'1' ; al , "<<endl; if (optLevel<3) cout<<" cmp ax,8"<<endl; // O3 - , , if (optLevel<3) cout<<" ja lReadKey"<<endl; cout<<" shl ax,1 ; ax=ax*2"<<endl; if (optLevel==0) { cout<<" mov bx, data_addres"<<endl; cout<<" add bx,ax ; bx = data_addres[key]"<<endl; cout<<" mov ax, [data_pos]"<<endl; cout<<" mov cx, [CASE_BLOCK_SIZE]"<<endl; cout<<" mul cx ; cx = [data_pos]"<<endl; cout<<" add bx,ax ; bx = data_addres[data_pos][key]"<<endl; } if (optLevel==1) { // -3 cout<<" add ax, data_addres ; bx = data_addres[key]"<<endl; cout<<" add ax, [data_pos]"<<endl; cout<<" mov bx,ax"<<endl; } if (optLevel>=2) { // -1 ; optLevel>2 -1-3 cout<<" add ax, [data_pos]"<<endl; cout<<" mov bx,ax ; TODO , xlat, ?"<<endl; } cout<<" mov ax,[bx]"<<endl; cout<<" mov [data_pos],ax ; ."<<endl; cout<<" jmp lblShowVariant"<<endl; // ;mov ax, 0x4c00; DOS EXIT, )) // ;int 0x21 cout<<"SECTION .data"<<endl; outDataFormatAsm(optLevel); } void outListingHTML() { cout<<"<html>"<<endl; cout<<"<head>"<<endl; cout<<"<!-- 0 JS -->"<<endl; cout<<"<script type=\"text-javascript\">"<<endl; cout<<"</script>"<<endl; cout<<"<style>"<<endl; cout<<" div {"<<endl; cout<<" height: 100%"<<endl; cout<<" }"<<endl; cout<<" td {"<<endl; cout<<" width:1em;"<<endl; cout<<" height:1em;"<<endl; cout<<" }"<<endl; cout<<"</style>"<<endl; cout<<"</head>"<<endl; cout<<"<body>"<<endl; for ( int i=0; i<data_pos; i++) { cout<<"<div id=\"p"<<i<<"\">"<<endl; if (text[i][0]=='X' || text[i][0]=='0' || text[i][0]=='.') { cout<<"<table border=\"border\">"<<endl; cout<<"<tr>"<<endl; cout<<" <td><a href=\"#p"<<data_addres[i][0]<<"\">"<<text[i][6+2]<<"</a></td>"<<endl; cout<<" <td><a href=\"#p"<<data_addres[i][1]<<"\">"<<text[i][7+2]<<"</a></td>"<<endl; cout<<" <td><a href=\"#p"<<data_addres[i][2]<<"\">"<<text[i][8+2]<<"</a></td>"<<endl; cout<<"</tr><tr>"<<endl; cout<<" <td><a href=\"#p"<<data_addres[i][3]<<"\">"<<text[i][3+1]<<"</a></td>"<<endl; cout<<" <td><a href=\"#p"<<data_addres[i][4]<<"\">"<<text[i][4+1]<<"</a></td>"<<endl; cout<<" <td><a href=\"#p"<<data_addres[i][5]<<"\">"<<text[i][5+1]<<"</a></td>"<<endl; cout<<"</tr><tr>"<<endl; cout<<" <td><a href=\"#p"<<data_addres[i][6]<<"\">"<<text[i][0]<<"</a></td>"<<endl; cout<<" <td><a href=\"#p"<<data_addres[i][7]<<"\">"<<text[i][1]<<"</a></td>"<<endl; cout<<" <td><a href=\"#p"<<data_addres[i][8]<<"\">"<<text[i][2]<<"</a></td>"<<endl; cout<<"</tr>"<<endl; cout<<"</table>"<<endl; } else cout<<"<a href=\"#p"<<data_addres[i][0]<<"\">"<<text[i]<<"</a>"<<endl; cout<<"</div>"<<endl; } cout<<"</body>"<<endl; cout<<"</html>"<<endl; } // ---- int main(int argc, char** argv) { int outFormat=-1; // 0 - assembler data, 1 - C array, -1 - . int optLevel=0; // 0 - , 1 - ( ), 2 - (), 3 - ()) for (int i=1; i<argc; i++) { if ( strcmp(argv[i],"--help")==0 || strcmp(argv[i],"-h")==0 ) { cout<<" . --help ."<<endl; cout<<" --help -h ."<<endl; cout<<" -a ( )"<<endl; cout<<" - "<<endl; cout<<" -sa "<<endl; cout<<" -s C"<<endl; cout<<" -sh HTML"<<endl; cout<<" -pvp ( )"<<endl; cout<<" -nodataopt , "<<endl; cout<<" -nowinopt , "<<endl; cout<<" (??) -nowinopt -nodataopt"<<endl; // TODO check text cout<<" :"<<endl; cout<<" -dosint (-3 ) DOS"<<endl; cout<<" -o0 (28 /25 dosint) . ."<<endl; cout<<" -o1 (-6 22 /19 dosint) . ."<<endl; cout<<" -o2 (-1 21/18 ) . , ()."<<endl; cout<<" -o3 (-2 19/16 ) o2 range check - if . !"<<endl; return 0; } else if ( strcmp(argv[i],"-a")==0 ) outFormat=0; else if ( strcmp(argv[i],"-c")==0 ) outFormat=1; else if ( strcmp(argv[i],"-sa")==0 ) outFormat=2; else if ( strcmp(argv[i],"-sc")==0 ) outFormat=3; else if ( strcmp(argv[i],"-sh")==0 ) outFormat=4; else if ( strcmp(argv[i],"-o0")==0 ) optLevel=0; else if ( strcmp(argv[i],"-o1")==0 ) optLevel=1; // -mul else if ( strcmp(argv[i],"-o2")==0 ) optLevel=2; // o1 + -add else if ( strcmp(argv[i],"-o3")==0 ) optLevel=3; // o2 + -range check else if ( strcmp(argv[i],"-dosint")==0 ) useDOSInt=true; else if ( strcmp(argv[i],"-pvp")==0 ) mode_pve=false; else if ( strcmp(argv[i],"-pve")==0 ) mode_pve=true; else if ( strcmp(argv[i],"-nodataopt")==0 ) optimizeDataFind=false; else if ( strcmp(argv[i],"-nowinopt")==0 ) casheWinVar=false; else { cout<<" \""<<argv[i]<<"\". --help ."<<endl; return 1; } } bool usePCSpeaker=false; if ( outFormat==0 || outFormat==2 ) { if (useDOSInt) End_Of_String = '$'; // DOS Text_Block_Size=13+3; usePCSpeaker=true; // } // init win text messages if (mode_pve) { if (usePCSpeaker) { // winText[win_x] = "You are won!\7\n"; winText[win_end]= "Not win. End\n"; winText[win_0] = "Bot won. 0\7\n"; } else { winText[win_x] = "You are won!"; winText[win_end]= "Not win. End"; winText[win_0] = "Bot won."; } } else { // PVP if (usePCSpeaker) { // winText[win_x] = "X are won!\7\n"; winText[win_end]= "Not win. End\n"; winText[win_0] = "0 won.\7\n"; } else { winText[win_x] = "X are won!"; winText[win_end]= "Not win. End"; winText[win_0] = "0 won."; } } generator_game_data( outFormat<2 ); if (outFormat==0) outDataFormatAsm(optLevel); if (outFormat==1) outDataFormatC(); if (outFormat==2) outListingAsm(optLevel); if (outFormat==3) outListingC(); if (outFormat==4) outListingHTML(); return 0; }
コンパイル、「-help」パラメーターを指定して実行してヘルプを呼び出すか、すぐにソースを取得します。
- 最大30行のアセンブラー:「generator.exe -sa> name.asm」、次にコンパイルします。nasmがある場合は、コマンド「nasm -f bin name.asm -o asm30TTG.com」を使用してコンパイルを行い、次に「asm30TTG.com」を実行して再生します。ゲームから抜け出す方法はありません。彼に十分なスペースがありませんでした。
- UPD:最大20行のアセンブラー:「generator.exe -sa -o1 -dosint> name.asm」、次にコンパイルします。DOS割り込みと修正されたデータ形式を使用して、計算量を削減します。
- UPD2: 18 : «generator.exe -sa -o2 -dosint > name.asm», . -o3, 16 ( if), !
- : «generator.exe -sc > name.c» .
- HTML : «generator.exe -sh > name.html»
UPD:チーム数(19〜20チーム)の観点から、よりコンパクトなソリューションを見つけました。2つの別個の配列の形式ではなく、構造体の配列の形式でデータを提示する場合、アドレスを取得するために必要な乗算は少なくなります。また、レコードごとに1つの数値を乗算する必要がある場合は、事前にすべての乗算を行うことができます。さらに減らすために、DOS割り込みを使用し、呼び出しに費やすコマンドを減らしましたが、色の設定は失われました。
20個のアセンブラーコマンドの例
SECTION .text org 0x100 ; .com lblShowVariant: mov ax,0x0001 ; clear screen, set graphic mode 40x25, color int 10h mov dx, data_addres+18 add dx, [data_pos] mov ah, 0x9 ; print [ds][dx] int 21h ; dos, $ lReadKey: xor ax,ax int 16h ; BIOS read key xor ah,ah sub al,'1' ; al , cmp ax,8 ja lReadKey shl ax,1 ; ax=ax*2 add ax, data_addres ; bx = data_addres[key] add ax, [data_pos] mov bx,ax ; mov ax,[ax] ? , mov ax,[bx] mov [data_pos],ax ; . jmp lblShowVariant SECTION .data ; Out data on assembler data format data_pos DW 0 ; max=394-1 ;CASE_BLOCK_SIZE DW 18 ;bytes (2 byte per 1 case) ;TEXT_BLOCK_SIZE DW 16 data_addres: DW 34, 1428, 2448, 3400, 4726, 5678, 6732, 9248, 11594 DB "...", 13,10, "...", 13,10, "...", 13,10, "$" DW 34, 68, 238, 306, 442, 476, 34, 578, 1122 DB "0..", 13,10, "...", 13,10, "X..", 13,10, "$" DW 68, 68, 102, 136, 136, 136, 68, 68, 170 DB "00.", 13,10, "...", 13,10, "XX.", 13,10, "$" DW 0, 0, 0, 0, 0, 0, 0, 0, 0 DB "You are won!", 7, 13,10, "$" DW 0, 0, 0, 0, 0, 0, 0, 0, 0 ...
UPD2:条件分岐なしの16チーム用。
16個のアセンブラーコマンドの例
SECTION .text org 0x100 ; .com lblShowVariant: mov ax,0x0001 ; clear screen, set graphic mode 40x25, color int 10h mov dx, 18 add dx, [data_pos] mov ah, 0x9 ; print [ds][dx] int 21h ; dos, $ xor ax,ax int 16h ; BIOS read key xor ah,ah sub al,'1' ; al , shl ax,1 ; ax=ax*2 add ax, [data_pos] mov bx,ax ; TODO , xlat, ? mov ax,[bx] mov [data_pos],ax ; . jmp lblShowVariant SECTION .data ; Out data on assembler format data_pos DW d_0 d_0 DW d_1, d_29, d_45, d_56, d_68, d_74, d_77, d_112, d_115 DB "...", 13,10, "...", 13,10, "...", 13,10, "$" d_1 DW d_1, d_2, d_7, d_9, d_13, d_14, d_1, d_16, d_28 DB "0..", 13,10, "...", 13,10, "X..", 13,10, "$" ...
結論として
Java Scriptだけでなく、アセンブラーなどの他の言語でも30行のコードでゲームを作成できます。コンパイラは、最小の実行時間とサイズの両方でプログラムを最適化できます。ただし、アルゴリズムを選択すると、作業が高速化されたり、最適化ができる以上にプログラムのサイズが小さくなります。
UPD2:後に、30チームのサイズは制限ではなく、20チーム(安定)または最大16チーム(範囲外のチェックアウトのために不安定)に縮小できることが判明しました。興味深い機能に気付きました-このコードは条件分岐なしで(「if」なしで)動作できます。
ジェネレーターが動作していたとき、3x3フィールドのゲームチックタックトーのバリアントの数が計算されました。
計算条件 | オプションの数 |
---|---|
PVP、繰り返し* | 294781 |
PVP、繰り返しなし | 7382 |
PVE, | 1036 |
PVE, | 306 |
PVE, | 113 |
* , 255168. (PVE) , .