...

суббота, 11 января 2014 г.

Игра в 30 команд Ассемблера


/*
* Author: godAlex (C) 2014
* License GNU GPL v 3
* param: -h -a -c -sa -sc -sh
*/
#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

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

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

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;

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

#define MAX_DATA_SIZE 30000
unsigned int data_addres[MAX_DATA_SIZE][CASE_BLOCK_SIZE]; //= {{0,0,1,0,1,0,1,0},{0,0,1,0,1,0,1,0}};
char text[MAX_DATA_SIZE][TEXT_BLOCK_SIZE]; // = { "Hello","Hello 2" }; // варианты для победы
unsigned int data_pos = 0;

int setVariant(int varID,unsigned int variants[CASE_BLOCK_SIZE],char* txt) //(unsigned int MAP_X,unsigned int MAP_0)
{
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;
}
int getFreeVar() {
int p=data_pos;
data_pos++;
if (p>MAX_DATA_SIZE) {
cout<<"Owerflow data pos!"<<endl;
p=-1;
}
#ifdef show_debug
else cout<<"New variant №"<<p<<endl;
#endif
return p;
}

int itrGameHod_all(unsigned int MAP_X,unsigned int MAP_0, bool playOn_X, int *doRecord);

/**
* Возвращает номер ячейки для победы или ничьей или хотя бы какую-либо.
* Играет за нолик (0)
* Тип возвращаемой позиции - int [0,8], без по битной адресации.
*/
int getBestBotHod(unsigned int MAP_X,unsigned int MAP_0) { // TODO непобедимый bot был чтобы.
unsigned int lastMAP_X=MAP_X;
unsigned int lastMAP_0=MAP_0;
unsigned int full_map = MAP_X | MAP_0;

int winLevel=-1; // уровень вероятности победы
int winPos=-1;
//*
for (int i=0; i<9; i++) { // победить
unsigned short p = 1<<i;
if ( (full_map & p) == 0 ) {
int w=testWon( MAP_X, MAP_0 | p );
if (w==win_0) {
winLevel=4;
winPos=p;
}
}
}
// TODO ...
//if (winLevel<1)
for (int i=0; i<9; i++) { // все клетки игрового поля
unsigned short p = 1<<i; // TODO BOT_POSITION_PRIORITY[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_all(MAP_X,MAP_0, true, 0x00);
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;
}

unsigned int winWariantCashe[4]={0,0,0,0}; // Вариантов с победой слишком много, их можно объединить, сократив место.
unsigned int winWariantVer[4]={0,0,0,0}; // Сколько исходов с победой, для статистики
unsigned int setEndGameVariant(unsigned int MAP_X,unsigned int MAP_0, char* txt)
{
unsigned int currentRecordID;
int wonIs = testWon(MAP_X,MAP_0);
winWariantVer[wonIs]++;
if (winWariantCashe[wonIs]==0) {
unsigned 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];
/* было
unsigned 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
* перебор всех вариантов свободных
* playOn_X - 0=за нолик, иначе за крестик
* doRecords - вызывать функцию addNewWariant или только тестировать исходы (==0) Чтобы включить - нужен адрес переменной, в которую запишется номер новой записи
*/
int itrGameHod_all(unsigned int MAP_X,unsigned int MAP_0, bool playOn_X, int *doRecord)
{
unsigned int lastMAP_X=MAP_X;
unsigned int lastMAP_0=MAP_0;
unsigned int full_map = MAP_X | MAP_0;
int winResult=0;

if (playOn_X) { // user, все варианты
for (int i=0; i<CASE_BLOCK_SIZE; i++) {
unsigned int 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;
} else {
w=itrGameHod_all(MAP_X,MAP_0, !playOn_X, 0x00); // iteration
}
MAP_X=lastMAP_X;
MAP_0=lastMAP_0;
}
}
} else { // компьютер, лучший для него вариант
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;
else w=itrGameHod_all(MAP_X,MAP_0, !playOn_X, 0x00); // iteration
MAP_0=lastMAP_0;
}
}

if (doRecord==0) return winResult; // если просто проверка
// TODO в отдельную функцию, или совместить

unsigned int addres[CASE_BLOCK_SIZE]; // адреса (на каждую кнопку)
char currentText[Text_Block_Size]; // текстовое сообщение на этот вариант
printField (MAP_X,MAP_0,currentText);

int currentRecordID=getFreeVar();

if (playOn_X) { // за человека
for (int i=0; i<CASE_BLOCK_SIZE; i++) {
unsigned int p = KEYBOARD_BYTE_OFFSET[i]; //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) {
// out wo is won
if (w==win_x) addres[i]=setEndGameVariant(MAP_X,MAP_0, "You are won!");
if (w==win_end) addres[i]=setEndGameVariant(MAP_X,MAP_0, "Not win. End");
} else {
int p2Int=getBestBotHod(MAP_X,MAP_0); // за 0, то есть за !playOn_X
// TODO if p2Int != -1
short p2Bit=1<<p2Int;
MAP_0 |= p2Bit;
int w=testWon(MAP_X,MAP_0);
if (w!=win_gameNext) {
// out wo is won
if (w==win_0) addres[i]=setEndGameVariant(MAP_X,MAP_0, "Bot won. 0");
if (w==win_end) addres[i]=setEndGameVariant(MAP_X,MAP_0, "Not win. End");
//addres[i]=0;// TODO addres end game
} else {
// add new wariant
int nextDataAddr;
w=itrGameHod_all(MAP_X,MAP_0, playOn_X, &nextDataAddr); // iteration
addres[i] = nextDataAddr;
}
}
MAP_X=lastMAP_X;
MAP_0=lastMAP_0;
} else {
addres[i]=currentRecordID;// TODO addres current data
}
}
currentRecordID=setVariant(currentRecordID, addres,currentText);
*doRecord=currentRecordID;
} else {
cout<<"Error! Этот вариант вызова не предусмотрен."<<endl;
}
return winResult;
}

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;

}

void outDataFormatAsm()
{
int minimalCaseSize=1;
if (data_pos>0xff) minimalCaseSize=2;
if (data_pos>0xffff) minimalCaseSize=4;

cout<<"; Out data on assembler data format"<<endl;
cout<<"data_pos DW 0 ; max="<<data_pos<<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++) {
if (minimalCaseSize==1) cout<<"DB ";
if (minimalCaseSize==2) cout<<"DW ";
if (minimalCaseSize==4) cout<<"QW "; // data_pos wrote as DW, see up
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) cout<<", \""<<End_Of_String<<"\""; // FIXME откуда нули?
else // TODO это только под DOS int 21h.
*/
else if (text[i][j]==0) cout<<", \" \""; // FIXME откуда нули?
else // TODO это только под DOS int 21h.
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<data_pos; i++) for ( int j=0; j<Text_Block_Size; j++) text[i][j]=End_Of_String;
int startP;
itrGameHod_all(0,0, true, &startP);
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;
}
}
//-------------------------------------------
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 (true) {"<<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()
{
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;
/*
; DOS
; mov ax, [data_pos]
; mov bx, [TEXT_BLOCK_SIZE]
; mul bx
; mov dx, text_data
; add dx,ax ; offset на текст
; mov ah, 0x9 ; print [dx]
; int 0x21 ; dos, dx-указывает на строку, завершающуюся $
*/
//; BIOS
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;
cout<<" mov cx,[TEXT_BLOCK_SIZE]"<<endl;
cout<<" mov ax,1300h"<<endl;
cout<<" mov bx,0eh ; color"<<endl;
cout<<" mov dx,0500h ; 5 строка 0 позиция для вывода"<<endl;
cout<<" int 10h"<<endl;
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;
cout<<" cmp ax,8"<<endl;
cout<<" ja lReadKey"<<endl;
cout<<" shl ax,1 ; ax=ax*2"<<endl;
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;
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;

// TODO если вставить код 07 в сообщения о победе - будет звуковой сигнал (PC speaker))
outDataFormatAsm();
}
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<<"</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
if (argc==2) {
if ( strcmp(argv[1],"--help")==0 ) {
cout<<"Неверное количество аргументов. Введите параметр --help для справки."<<endl;
cout<<" --help вывод этой справки."<<endl;
cout<<" -a вывод данных в формате для ассемблера (по умолчанию)"<<endl;
cout<<" -с вывод данных в формате С массива"<<endl;
cout<<" -sa вывод готовой программы на ассемблере"<<endl;
cout<<" -sс вывод готовой программы на C"<<endl;
cout<<" -sh вывод в HTML"<<endl;
return 0;
}
if ( strcmp(argv[1],"-a")==0 ) outFormat=0;
if ( strcmp(argv[1],"-c")==0 ) outFormat=1;
if ( strcmp(argv[1],"-sa")==0 ) outFormat=2;
if ( strcmp(argv[1],"-sc")==0 ) outFormat=3;
if ( strcmp(argv[1],"-sh")==0 ) outFormat=4;
} else outFormat=0; // Установка формата по умолчанию. default - assembler out (0-asm, 1-C array))
if ( argc>2 || outFormat==-1 ) {
cout<<"Неверное количество или значения аргументов. Введите параметр --help для справки."<<endl;
return 1;
}

if ( outFormat==0 || outFormat==2 ) {
//End_Of_String = '$'; // вариант конца строки для DOS
Text_Block_Size=13+3;
}
generator_game_data( outFormat<2);

if (outFormat==0) outDataFormatAsm();
if (outFormat==1) outDataFormatC();

if (outFormat==2) outListingAsm();
if (outFormat==3) outListingC();
if (outFormat==4) outListingHTML();

return 0;
}




This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers.


Комментариев нет:

Отправить комментарий