/* FCE Ultra - NES/Famicom Emulator * * Copyright notice for this file: * Copyright (C) 2006 Luke Gustafson * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "common.h" #include "../../fceu.h" //mbg merge 7/18/06 added #include "basicbot.h" static HWND hwndBasicBot=0; static int BotRunning = 0; static uint32 rand1=1, rand2=0x1337EA75, rand3=0x0BADF00D; static char Formula[24][1024]; static int BotCounter[256]; static int EditPlayer = 1, BotFrame, BotLoopVar = 0; static int BotAttempts, BotFrames, BotBestScore[4], NewAttempt; static int CurrentAttempt[1024], BestAttempt[1024], AttemptPointer; static int DoAddCounter = 1; char *BasicBotDir = 0; static void SeedRandom(uint32 seed) { rand1 = seed; if(rand1 == 0) rand1 = 1; //I forget if this is necessary. } //Simulation-quality random numbers: taus88 algorithm static unsigned int NextRandom(void) { uint32 b; b = (((rand1 << 13) ^ rand1) >> 19); rand1 = (((rand1 & 4294967294U) << 12) ^ b); b = (((rand2 << 2) ^ rand2) >> 25); rand2 = (((rand2 & 4294967288U) << 4) ^ b); b = (((rand3 << 3) ^ rand3) >> 11); rand3 = (((rand3 & 4294967280U) << 17) ^ b); return (rand1 ^ rand2 ^ rand3); } //Should do something to notify user of an error... eh //void BotError() //{ // //} //recursively evaluate the formula //horribly hacky, not meant to be a real expression evaluator! int EvaluateFormula(char * formula, char **nextposition) { char *currentchar = formula; int value=0; int hexmode = 0; while(1) { switch(*currentchar) { case 0: *nextposition = currentchar; return value; case '(': value = EvaluateFormula(currentchar + 1, ¤tchar); break; case ')': case ':': *nextposition = currentchar + 1; return value; case 'x': hexmode=1; currentchar++; break; case '+': return (value + EvaluateFormula(currentchar+1, nextposition)); case '-': return (value - EvaluateFormula(currentchar+1, nextposition)); case '*': return (value * EvaluateFormula(currentchar+1, nextposition)); case '/': return (value / EvaluateFormula(currentchar+1, nextposition)); case '%': return (value % EvaluateFormula(currentchar+1, nextposition)); case '&': return (value & EvaluateFormula(currentchar+1, nextposition)); case '|': return (value | EvaluateFormula(currentchar+1, nextposition)); case '^': return (value ^ EvaluateFormula(currentchar+1, nextposition)); case '=': return (value == EvaluateFormula(currentchar+1, nextposition) ? 1000: 0); case '>': if(*(currentchar + 1) == '=') return (value >= EvaluateFormula(currentchar+2, nextposition) ? 1000: 0); else return (value > EvaluateFormula(currentchar+1, nextposition) ? 1000: 0); case '<': if(*(currentchar + 1) == '=') return (value <= EvaluateFormula(currentchar+2, nextposition) ? 1000: 0); else return (value < EvaluateFormula(currentchar+1, nextposition) ? 1000: 0); case '!': if(*(currentchar + 1) == '=') { return (value != EvaluateFormula(currentchar+2, nextposition) ? 1000: 0); } else { //BotError(); currentchar++; } case '?': { int temp,temp2=DoAddCounter; if(value) { temp = EvaluateFormula(currentchar+1,¤tchar); DoAddCounter = 0; EvaluateFormula(currentchar,¤tchar); DoAddCounter = temp2; value= temp; } else { DoAddCounter = 0; EvaluateFormula(currentchar+1,¤tchar); DoAddCounter = temp2; temp = EvaluateFormula(currentchar,¤tchar); value= temp; } } case ' ': currentchar++; break; case 'a': if(*(currentchar+1) == 't' &&*(currentchar+2) == 't' &&*(currentchar+3) == 'e' &&*(currentchar+4) == 'm' &&*(currentchar+5) == 'p' &&*(currentchar+6) == 't') { currentchar+=7; value = BotAttempts; } else if(*(currentchar+1) == 'd' &&*(currentchar+2) == 'd' &&*(currentchar+3) == 'c' &&*(currentchar+4) == 'o' &&*(currentchar+5) == 'u' &&*(currentchar+6) == 'n' &&*(currentchar+7) == 't' &&*(currentchar+8) == 'e' &&*(currentchar+9) == 'r' &&*(currentchar+10)== '(') { currentchar += 11; if(DoAddCounter) { value = (BotCounter[(EvaluateFormula(currentchar, ¤tchar) & 0x7FFFFFFF) % 256] += value); } } else if(*(currentchar+1) == 'b' &&*(currentchar+2) == 's' &&*(currentchar+3) == '(') { currentchar += 4; value = EvaluateFormula(currentchar, ¤tchar); if(value < 0) value = -value; } else if(*(currentchar+1) == 'c' &&*(currentchar+2) == '(') { currentchar += 3; if(DoAddCounter) { value = (BotCounter[(EvaluateFormula(currentchar, ¤tchar) & 0x7FFFFFFF) % 256] += value); } } else { value = 1; currentchar++; } break; case 'b': if(*(currentchar+1) == 'u' &&*(currentchar+2) == 't' &&*(currentchar+3) == 't' &&*(currentchar+4) == 'o' &&*(currentchar+5) == 'n') { currentchar += 6; value &= BotInput[1]; } else { value = 2; currentchar++; } break; case 'c': if(*(currentchar+1) == 'o' && *(currentchar+2) == 'u' && *(currentchar+3) == 'n' && *(currentchar+4) == 't' && *(currentchar+5) == 'e' && *(currentchar+6) == 'r' && *(currentchar+7) == '(') { currentchar += 8; value = BotCounter[(EvaluateFormula(currentchar, ¤tchar) & 0x7FFFFFFF) % 256]; } else if(*(currentchar+1) == '(') { currentchar += 2; value = BotCounter[(EvaluateFormula(currentchar, ¤tchar) & 0x7FFFFFFF) % 256]; } else { //BotError(); currentchar++; } break; case 'd': if(*(currentchar+1) == 'o' &&*(currentchar+2) == 'w' &&*(currentchar+3) == 'n') { currentchar += 4; value = 32; } else { currentchar++; } break; case 'f': if(*(currentchar+1) == 'r' && *(currentchar+2) == 'a' && *(currentchar+3) == 'm' && *(currentchar+4) == 'e') { currentchar+= 5; value = BotFrame; } else { //BotError(); currentchar++; } break; case 'i': value = BotLoopVar; currentchar++; break; case 'l': if(*(currentchar+1) == 'e' &&*(currentchar+2) == 'f' &&*(currentchar+3) == 't') { currentchar += 4; value = 64; } else if(*(currentchar+1) == 'o' &&*(currentchar+2) == 'o' &&*(currentchar+3) == 'p' &&*(currentchar+4) == '(') { if(value > 0) { currentchar += 5; if(BotLoopVar == 0) { for(BotLoopVar = 1; BotLoopVar < value && BotLoopVar < 65536; BotLoopVar++) { EvaluateFormula(currentchar, nextposition); } BotLoopVar = 0; EvaluateFormula(currentchar, ¤tchar); } else { //BotError(); } } else { int temp = DoAddCounter; currentchar += 5; DoAddCounter = 0; EvaluateFormula(currentchar, ¤tchar); DoAddCounter = temp; } } else { currentchar++; } break; case 'm': if(*(currentchar+1) == 'e' &&*(currentchar+2) == 'm' &&*(currentchar+3) == '(') { currentchar += 4; int temp = (EvaluateFormula(currentchar, ¤tchar) & 0x7FFFFFFF) % 65536; value = ARead[temp](temp); } else { currentchar++; } break; case 'r': if(*(currentchar+1) == 'a' &&*(currentchar+2) == 'm' &&*(currentchar+3) == '(') { currentchar += 4; value = RAM[(EvaluateFormula(currentchar, ¤tchar) & 0x7FFFFFFF) % 2048]; } else if(*(currentchar+1) == 'i' &&*(currentchar+2) == 'g' &&*(currentchar+3) == 'h' &&*(currentchar+4) == 't') { currentchar += 5; value = 128; } else if(*(currentchar+1) == 'e' &&*(currentchar+2) == 's' &&*(currentchar+3) == 'e' &&*(currentchar+4) == 't' &&*(currentchar+5) == 'c' &&*(currentchar+6) == 'o' &&*(currentchar+7) == 'u' &&*(currentchar+8) == 'n' &&*(currentchar+9) == 't' &&*(currentchar+10) == 'e' &&*(currentchar+11) == 'r' &&*(currentchar+12) == '(') { currentchar += 13; if(DoAddCounter) BotCounter[(EvaluateFormula(currentchar, ¤tchar) & 0x7FFFFFFF) % 256] = 0; value = 0; } else if(*(currentchar+1) == 'c' &&*(currentchar+2) == '(') { currentchar += 3; if(DoAddCounter) BotCounter[(EvaluateFormula(currentchar, ¤tchar) & 0x7FFFFFFF) % 256] = 0; value = 0; } else { currentchar++; } break; case 's': if(*(currentchar+1) == 't' &&*(currentchar+2) == 'a' &&*(currentchar+3) == 'r' &&*(currentchar+4) == 't') { currentchar += 5; value = 8; } else if(*(currentchar+1) == 'e' &&*(currentchar+2) == 'l' &&*(currentchar+3) == 'e' &&*(currentchar+4) == 'c' &&*(currentchar+5) == 't') { currentchar += 6; value = 4; } else if(*(currentchar+1) == 'e' &&*(currentchar+2) == 't' &&*(currentchar+3) == 'c' &&*(currentchar+4) == 'o' &&*(currentchar+5) == 'u' &&*(currentchar+6) == 'n' &&*(currentchar+7) == 't' &&*(currentchar+8) == 'e' &&*(currentchar+9) == 'r' &&*(currentchar+10) == '(') { currentchar += 11; if(DoAddCounter) BotCounter[(EvaluateFormula(currentchar, ¤tchar) & 0x7FFFFFFF) % 256] = value; } else { currentchar++; } break; case 'u': if(*(currentchar+1) == 'p') { currentchar += 2; value = 16; } else { currentchar++; } break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': value *= hexmode?16:10; value += *currentchar - '0'; currentchar++; break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': if(hexmode) { value *= 16; value += *currentchar - 'A' + 10; } else { // BotError(); } currentchar++; break; default: currentchar++; break; } } return 0; } //Update BasicBot input void UpdateBasicBot() { if(hwndBasicBot && BotRunning) { int i,j,k; char *dummy = "dum"; if(BotInput[0]) return; //Create the bot output... BotInput[0] = 1; if(NewAttempt) { BotInput[1] = 65536; NewAttempt = 0; AttemptPointer = 0; BotFrame = 0; memset(BotCounter, 0, 1024); } else { //Check if goal is met if(BotFrame > 1020 || (int) (NextRandom() % 1000) < EvaluateFormula(Formula[20], &dummy)) { int currentscore[4]; int better = 0; NewAttempt = 1; //Check if better score for(i=0; i < 4; i++) { currentscore[i] = EvaluateFormula(Formula[16+i], &dummy); } for(i=0; i < 4; i++) { if(currentscore[i] > BotBestScore[i]) { better = 1; break; } else if(currentscore[i] < BotBestScore[i]) { break; } } //Update best if(better) { char tempstring[4096]; char symbols[] = "ABET^v<>ABET^v<>"; //mbg merge 7/18/06 changed dim to unspecified to leave room for the null int seenplayer2=0; for(i = 0; i < 4; i++) { BotBestScore[i] = currentscore[i]; } for(i = 0; i < AttemptPointer; i++) { BestAttempt[i] = CurrentAttempt[i]; } for(; i < 1024; i++) { BestAttempt[i] = -1; } sprintf(tempstring, "%d %d %d %d (%d attempt)", BotBestScore[0], BotBestScore[1], BotBestScore[2], BotBestScore[3], BotAttempts); SetDlgItemText(hwndBasicBot,1018,tempstring); memset(tempstring, 0, 4096); k = 0; //Make output as text for(i = 0; i < 1024 && BestAttempt[i] != -1 && k < 4095; i++) { tempstring[k] = '0' + ((i/10) % 10); k++; tempstring[k] = '0' + (i%10); k++; tempstring[k] = '.'; k++; seenplayer2=0; for(j = 0; j < 16; j++) { if(BestAttempt[i] & (1 << j)) { if(j >= 8 && seenplayer2==0) { seenplayer2=1; tempstring[k]=','; k++; } tempstring[k] = symbols[j]; k++; } } tempstring[k] = ' '; k++; } SetDlgItemText(hwndBasicBot,1019,tempstring); } } else //Goal not met { //Generate 1 frame of output BotInput[1] = 0; for(i=0;i<16;i++) { if((int) (NextRandom() % 1000) < EvaluateFormula(Formula[i], &dummy)) { BotInput[1] |= 1 << i; } } //Save what we've done if(AttemptPointer < 1024) { CurrentAttempt[AttemptPointer] = BotInput[1]; AttemptPointer++; } //Run extra commands EvaluateFormula(Formula[21],&dummy); EvaluateFormula(Formula[22],&dummy); } } //Update statistics if(BotInput[1] == 65536) { BotAttempts++; } else { BotFrames++; BotFrame++; } if(BotFrames % 100 == 0) { SetDlgItemInt(hwndBasicBot,1037,BotAttempts,FALSE); SetDlgItemInt(hwndBasicBot,1017,BotFrames,FALSE); } } } static void SaveBasicBot() { const char filter[]="Basic Bot(*.bot)\0*.bot\0"; char nameo[2048]; OPENFILENAME ofn; memset(&ofn,0,sizeof(ofn)); ofn.lStructSize=sizeof(ofn); ofn.hInstance=fceu_hInstance; ofn.lpstrTitle="Save Basic Bot As..."; ofn.lpstrFilter=filter; nameo[0]=0; ofn.lpstrFile=nameo; ofn.nMaxFile=256; ofn.Flags=OFN_EXPLORER|OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT; ofn.lpstrInitialDir=BasicBotDir; if(GetSaveFileName(&ofn)) { int i,j; //Save the directory if(ofn.nFileOffset < 1024) { free(BasicBotDir); BasicBotDir=(char*)malloc(strlen(ofn.lpstrFile)+1); //mbg merge 7/18/06 added cast strcpy(BasicBotDir,ofn.lpstrFile); BasicBotDir[ofn.nFileOffset]=0; } //quick get length of nameo for(i=0;i<2048;i++) { if(nameo[i] == 0) { break; } } //add .bot if nameo doesn't have it if((i < 4 || nameo[i-4] != '.') && i < 2040) { nameo[i] = '.'; nameo[i+1] = 'b'; nameo[i+2] = 'o'; nameo[i+3] = 't'; nameo[i+4] = 0; } FILE *fp=FCEUD_UTF8fopen(nameo,"wb"); fputc('b',fp); fputc('o',fp); fputc('t',fp); fputc(0,fp); for(i=0;i<23;i++) { for(j=0;j<1024;j++) { fputc(Formula[i][j],fp); } } fclose(fp); } } //Loads a previously saved file static void LoadBasicBot() { const char filter[]="Basic Bot (*.bot)\0*.bot\0"; char nameo[2048]; OPENFILENAME ofn; memset(&ofn,0,sizeof(ofn)); ofn.lStructSize=sizeof(ofn); ofn.hInstance=fceu_hInstance; ofn.lpstrTitle="Load Basic Bot..."; ofn.lpstrFilter=filter; nameo[0]=0; ofn.lpstrFile=nameo; ofn.nMaxFile=256; ofn.Flags=OFN_EXPLORER|OFN_FILEMUSTEXIST|OFN_HIDEREADONLY; ofn.lpstrInitialDir=BasicBotDir; if(GetOpenFileName(&ofn)) { int i,j; //Save the directory if(ofn.nFileOffset < 1024) { free(BasicBotDir); BasicBotDir=(char*)malloc(strlen(ofn.lpstrFile)+1); //mbg merge 7/18/06 added cast strcpy(BasicBotDir,ofn.lpstrFile); BasicBotDir[ofn.nFileOffset]=0; } FILE *fp=FCEUD_UTF8fopen(nameo,"rb"); if(fgetc(fp) != 'b' || fgetc(fp) != 'o' || fgetc(fp) != 't' || fgetc(fp) != 0) { fclose(fp); return; } for(i=0;i<23;i++) { for(j=0;j<1024;j++) { Formula[i][j] = fgetc(fp); if(Formula[i][j] & 128) { Formula[i][j] = 0; } } } fclose(fp); } } static BOOL CALLBACK BasicBotCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { int i; DSMFix(uMsg); switch(uMsg) { case WM_INITDIALOG: break; case WM_CLOSE: case WM_QUIT: for(i = 0; i < 8; i++) { GetDlgItemText(hwndBasicBot,1000+i,Formula[i + (EditPlayer?0:8)],1024); } for(i = 0; i < 4; i++) { GetDlgItemText(hwndBasicBot,1010+i,Formula[16+i],1024); } GetDlgItemText(hwndBasicBot, 1009, Formula[20],1024); GetDlgItemText(hwndBasicBot, 1020, Formula[21],1024); GetDlgItemText(hwndBasicBot, 1022, Formula[22],1024); DestroyWindow(hwndBasicBot); hwndBasicBot=0; break; case WM_COMMAND: switch(HIWORD(wParam)) { case BN_CLICKED: switch(LOWORD(wParam)) { case 1008: //change player button clicked EditPlayer ^= 1; SetDlgItemText(hwndBasicBot,1008,(LPTSTR)(EditPlayer?"Edit Player 2":"Edit Player 1")); SetDlgItemText(hwndBasicBot,2000,(LPTSTR)(EditPlayer?"Player 1":"Player 2")); for(i = 0; i < 8; i++) { GetDlgItemText(hwndBasicBot,1000+i,Formula[i + (EditPlayer?8:0)],1024); SetDlgItemText(hwndBasicBot,1000+i,Formula[i + (EditPlayer?0:8)]); } break; case 1014: //Save button clicked for(i = 0; i < 8; i++) { GetDlgItemText(hwndBasicBot,1000+i,Formula[i + (EditPlayer?0:8)],1024); } for(i = 0; i < 4; i++) { GetDlgItemText(hwndBasicBot,1010+i,Formula[16+i],1024); } GetDlgItemText(hwndBasicBot, 1009, Formula[20],1024); GetDlgItemText(hwndBasicBot, 1020, Formula[21],1024); GetDlgItemText(hwndBasicBot, 1022, Formula[22],1024); StopSound(); SaveBasicBot(); break; case 1015: //Load button clicked StopSound(); LoadBasicBot(); for(i = 0; i < 8; i++) { SetDlgItemText(hwndBasicBot,1000+i,Formula[i + (EditPlayer?0:8)]); } for(i = 0; i < 4; i++) { SetDlgItemText(hwndBasicBot,1010+i,Formula[16+i]); } SetDlgItemText(hwndBasicBot, 1009, Formula[20]); SetDlgItemText(hwndBasicBot, 1020, Formula[21]); SetDlgItemText(hwndBasicBot, 1022, Formula[22]); break; case 1021: //Play best button clicked if(BotRunning == 0 && BotInput[0] == 0) { for(i = 0; i < 1022 && BestAttempt[i] != -1; i++) { BotInput[i+2] = BestAttempt[i]; } BotInput[0] = i+1; BotInput[1] = 65536; } break; case 1016: //Run button clicked BotRunning ^= 1; if(BotRunning) { BotAttempts = BotFrames = 0; BotBestScore[0] = BotBestScore[1] = BotBestScore[2] = BotBestScore[3] = -999999999; NewAttempt = 1; } for(i = 0; i < 8; i++) { GetDlgItemText(hwndBasicBot,1000+i,Formula[i + (EditPlayer?0:8)],1024); } for(i = 0; i < 4; i++) { GetDlgItemText(hwndBasicBot,1010+i,Formula[16+i],1024); } GetDlgItemText(hwndBasicBot, 1009, Formula[20],1024); GetDlgItemText(hwndBasicBot, 1020, Formula[21],1024); GetDlgItemText(hwndBasicBot, 1022, Formula[22],1024); SetDlgItemText(hwndBasicBot,1016,(LPTSTR)(BotRunning?"Stop":"Run!")); break; case 1036: //Clear button clicked StopSound(); if(MessageBox(hwndBasicBot, "Clear all text?", "Confirm clear", MB_YESNO)==IDYES) { memset(Formula,0, 24*1024); for(i = 0; i < 24; i++) { Formula[i][0] = '0'; } Formula[20][0] = 'f'; Formula[20][1] = 'r'; Formula[20][2] = 'a'; Formula[20][3] = 'm'; Formula[20][4] = 'e'; Formula[20][5] = '='; Formula[20][6] = '1'; for(i = 0; i < 8; i++) { SetDlgItemText(hwndBasicBot,1000+i,Formula[i+(EditPlayer?0:8)]); } for(i = 0; i < 4; i++) { SetDlgItemText(hwndBasicBot,1010+i,Formula[16+i]); } SetDlgItemText(hwndBasicBot, 1009, Formula[20]); SetDlgItemText(hwndBasicBot, 1020, Formula[21]); SetDlgItemText(hwndBasicBot, 1022, Formula[22]); } break; default: break; } } if(!(wParam>>16)) //Close button clicked { switch(wParam&0xFFFF) { case 1: for(i = 0; i < 8; i++) { GetDlgItemText(hwndBasicBot,1000+i,Formula[i + (EditPlayer?0:8)],1024); } for(i = 0; i < 4; i++) { GetDlgItemText(hwndBasicBot,1010+i,Formula[16+i],1024); } GetDlgItemText(hwndBasicBot, 1009, Formula[20],1024); GetDlgItemText(hwndBasicBot, 1020, Formula[21],1024); GetDlgItemText(hwndBasicBot, 1022, Formula[22],1024); DestroyWindow(hwndBasicBot); hwndBasicBot=0; break; } } break; default: break; } return 0; } void CreateBasicBot() { static int BotNeedsInit = 1; int i; if(BotNeedsInit) { BotNeedsInit = 0; SeedRandom(rand()); memset(Formula,0, 24*1024); for(i = 0; i < 24; i++) { Formula[i][0] = '0'; } Formula[20][0] = 'f'; Formula[20][1] = 'r'; Formula[20][2] = 'a'; Formula[20][3] = 'm'; Formula[20][4] = 'e'; Formula[20][5] = '='; Formula[20][6] = '1'; } if(hwndBasicBot) //If already open, give focus { SetFocus(hwndBasicBot); return; } //Create hwndBasicBot=CreateDialog(fceu_hInstance,"BASICBOT",NULL,BasicBotCallB); EditPlayer = 1; BotRunning = 0; for(i = 0; i < 8; i++) { SetDlgItemText(hwndBasicBot,1000+i,Formula[i]); } for(i = 0; i < 4; i++) { SetDlgItemText(hwndBasicBot,1010+i,Formula[16+i]); } SetDlgItemText(hwndBasicBot, 1009, Formula[20]); SetDlgItemText(hwndBasicBot, 1020, Formula[21]); SetDlgItemText(hwndBasicBot, 1022, Formula[22]); }