/**Yeah well... this is TASbot.It's derived from BasicBot.When extending basicbot I ran into quite a few issues. In the end I decidedto rewrite the whole thing from scratch. This is the result.qFox, december 2006*/#include "common.h" // windows stuff, amongst others#include "../../fceu.h" // fceu internal dependancies#include "basicbot.h" // duh#include "../../input.h" // fceu_botmode() fceu_setbotmode()#include <ctime> // time is fed to the randomizer as seed// version stuffstatic char TB_version[] = "0.1.0a"; // versionstatic char TB_caption[] = "TAS Bot v0.1.0a by qFox"; // for titlestatic int File_version = 1; // when saving / loading, if a older file// does not have the same lay-out as the// current version, it can not be loaded.// GUIHWND hwndTASbot = 0; // GUI handle// parserchar * BotCurrentChar; // points to the current position of the string// the parser is currently processingint BotCurrentCode; // the same, for bytecodesint BotCurrentField; // points to the field currently parsingint BotByteCodeField; // same, but for the bytecode interpreterbool ProcessCode = true; // This boolean tells the code whether or not to process// some code. This is mainly used in branching (iif's)// to skip over the part of the code that is not to be// executed. It still needs to be parsed because the// pointer needs to be moved ahead.// attempt input sequences (BOT_MAXFRAMES is defined in header)static uint32 BotCurrentAttempt[BOT_MAXFRAMES]; // Contains all the frames of the current attempt.static uint32 BotBestAttempt[BOT_MAXFRAMES]; // Contains all the frames of the best attempt so far.static uint32 BotAttemptPointer; // Points to the last frame of the current attempt.// Togglesbool BotRunning = false; // is the bot currently running?bool Bot_Update_gui_next = false; // will run fromGUI asapbool Bot_Reset_stats = false; // will end current attempt and reset all statsbool Bot_Eval_error = false; // if true, bot will stop at first opportunity// defines#define BOT_MAX_COUNTERS 256int BotCounter[BOT_MAX_COUNTERS];#define BOT_MAX_SCORES 6#define BOT_MAX_PROB 1000// statsint BotFrame,BotTotalFrames,BotAttempt,BotTotalAttempts,BotOks,BotInvalids,BotSegments,BotBestScore[BOT_MAX_SCORES];// static varsint Bot_X,Bot_Y,Bot_Z,Bot_P, Bot_Prand,Bot_Q, Bot_Qrand;int BotLastbutton;// symbols to refer to the gui control-idsenum CODEFIELDS { //Buttons CODE_BUTTON_START_MARKER, CODE_1_A, CODE_1_B, CODE_1_SELECT, CODE_1_START, CODE_1_UP, CODE_1_DOWN, CODE_1_LEFT, CODE_1_RIGHT, CODE_2_A, CODE_2_B, CODE_2_SELECT, CODE_2_START, CODE_2_UP, CODE_2_DOWN, CODE_2_LEFT, CODE_2_RIGHT, CODE_BUTTON_END_MARKER, // Conditions CODE_OK, CODE_INVALID, CODE_SEGMENT, CODE_STOP, // scores CODE_SCORE1, CODE_SCORE2, CODE_SCORE3, CODE_SCORE4, CODE_SCORE5, CODE_SCORE6, // static variables CODE_X, CODE_Y, CODE_Z, CODE_P, CODE_Q, // pre/post code CODE_PRE_FRAME, CODE_POST_FRAME, CODE_PRE_ATTEMPT, CODE_POST_ATTEMPT, CODE_PRE_SEGMENT, CODE_POST_SEGMENT, // textual controls, not to be parsed CODE_COMMENT_MARKER, CODE_COMMENTS, CODE_ROM, CODE_TITLE1, CODE_TITLE2, CODE_TITLE3, CODE_TITLE4, CODE_TITLE5, CODE_TITLE6, BOT_FORMULAS};// these are the label-symbols in the GUI. they are in sync with the CODEFIELDS enumerationint labels[] = { // Buttons CODE_BUTTON_START_MARKER, BOT_GUI_A_1, BOT_GUI_B_1, BOT_GUI_SELECT_1, BOT_GUI_START_1, BOT_GUI_UP_1, BOT_GUI_DOWN_1, BOT_GUI_LEFT_1, BOT_GUI_RIGHT_1, BOT_GUI_A_2, BOT_GUI_B_2, BOT_GUI_SELECT_2, BOT_GUI_START_2, BOT_GUI_UP_2, BOT_GUI_DOWN_2, BOT_GUI_LEFT_2, BOT_GUI_RIGHT_2, CODE_BUTTON_END_MARKER, // Conditions BOT_GUI_OK, BOT_GUI_INVALID, BOT_GUI_SEGMENT, BOT_GUI_STOP, // Scores BOT_GUI_SCORE1, BOT_GUI_SCORE2, BOT_GUI_SCORE3, BOT_GUI_SCORE4, BOT_GUI_SCORE5, BOT_GUI_SCORE6, // Static variables BOT_GUI_X, BOT_GUI_Y, BOT_GUI_Z, BOT_GUI_P, BOT_GUI_Q, // pre/post code BOT_GUI_PRE_FRAME, BOT_GUI_POST_FRAME, BOT_GUI_PRE_ATTEMPT, BOT_GUI_POST_ATTEMPT, BOT_GUI_PRE_SEGMENT, BOT_GUI_POST_SEGMENT, // from here on down put inputs that // are not to be parsed, but visual only CODE_COMMENT_MARKER, BOT_GUI_COMMENTS, BOT_GUI_ROM, BOT_GUI_TITLE1, BOT_GUI_TITLE2, BOT_GUI_TITLE3, BOT_GUI_TITLE4, BOT_GUI_TITLE5, BOT_GUI_TITLE6, BOT_FORMULAS};/*** the commands for the "scripting language"*/enum COMMANDS { BOT_BYTE_EOL, BOT_BYTE_LB, BOT_BYTE_RB, BOT_BYTE_LIT, BOT_BYTE_COLON, BOT_BYTE_SCOLON, BOT_BYTE_ADD, BOT_BYTE_SUB, BOT_BYTE_MUL, BOT_BYTE_DIV, BOT_BYTE_MOD, BOT_BYTE_AND, BOT_BYTE_OR, BOT_BYTE_XOR, BOT_BYTE_EQ, BOT_BYTE_GTEQ, BOT_BYTE_GT, BOT_BYTE_LTEQ, BOT_BYTE_LT, BOT_BYTE_UEQ, BOT_BYTE_IIF, BOT_BYTE_ATTEMPT, BOT_BYTE_AC, BOT_BYTE_ABS, BOT_BYTE_BUTTON, BOT_BYTE_C, BOT_BYTE_DOWN, BOT_BYTE_ECHO, BOT_BYTE_ERROR, BOT_BYTE_FRAME, BOT_BYTE_I, BOT_BYTE_INVALIDS, BOT_BYTE_J, BOT_BYTE_K, BOT_BYTE_LAST, BOT_BYTE_LEFT, BOT_BYTE_LOOP, BOT_BYTE_LSHIFT, BOT_BYTE_MAX, BOT_BYTE_MEM, BOT_BYTE_MEMH, BOT_BYTE_MEML, BOT_BYTE_MEMW, BOT_BYTE_NEVER, BOT_BYTE_OKS, BOT_BYTE_RC, BOT_BYTE_RIGHT, BOT_BYTE_RSHIFT, BOT_BYTE_SELECT, BOT_BYTE_START, BOT_BYTE_SC, BOT_BYTE_SCORE1, BOT_BYTE_SCORE2, BOT_BYTE_SCORE3, BOT_BYTE_SCORE4, BOT_BYTE_SCORE5, BOT_BYTE_SCORE6, BOT_BYTE_STOP, BOT_BYTE_UP, BOT_BYTE_P, BOT_BYTE_Q, BOT_BYTE_X, BOT_BYTE_Y, BOT_BYTE_Z};char * Formula[BOT_FORMULAS]; // These hold BOT_FORMULAS formula's entered in the GUI:int * Bytecode[BOT_FORMULAS]; // Byte code converted formulas// variables to hold the data for the "last" segmentint BotPrevSegNum = 0,BotPrevSegFrame = 0,BotPrevSegAttempt = 0,BotPrevSegLastbutton = 0,BotPrevSegScore1 = 0,BotPrevSegScore2 = 0,BotPrevSegScore3 = 0,BotPrevSegScore4 = 0,BotPrevSegScore5 = 0;// variables to hold the data for the "best" segmentint BotBestSegNum = 0,BotBestSegFrame = 0,BotBestSegAttempt = 0,BotBestSegLastbutton = 0,BotBestSegScore1 = 0,BotBestSegScore2 = 0,BotBestSegScore3 = 0,BotBestSegScore4 = 0,BotBestSegScore5 = 0;/*** Get the len of a string, which is the position* of the first '\0' from the start**/unsigned int strlen(char * str){ char * was = str; while (*str != 0) ++str; return str-was;}/*** same as strlen, but counts for ints instead**/unsigned int intlen(int * bytes){ int * was = bytes; while (*bytes != 0) ++bytes; return bytes-was;}/*** Get the length of a textfield ("edit control") or textarea* It's more of a macro really.* Returns 0 when an error occurs, the length of current * contents in any other case (check LastError in case of 0)*/unsigned int FieldLength(HWND winhandle,int controlid){ HWND hwndItem = GetDlgItem(winhandle,controlid); return SendMessage(hwndItem,WM_GETTEXTLENGTH,0,0);}/*** show a messagebox*/static void mboxS(char * txt){ MessageBox(hwndTASbot, txt, "Debug", MB_OK);}static void mboxI(int n){ char t[20]; sprintf(t,"n = %d",n); mboxS(t);}/*** get a random number from 0 to n-1 (n is not included!)*/static int bot_rand(int n){ return (rand()%n);}/*** Convert string code to byte code* This function is NOT called recursively!* - formula is the string to parse* - codefield is the field for which this code is meant (used for debugging)**/static int * convert(char * formula, int codefield){ // get the len of the formula int len = strlen(formula); // create some new space with the len of the formula // its very unlikely that the bytecode will be any // longer then the formula, the only exception is a // formula with a single digit. for this reason // we'll add a 10 byte padding#define EXTRABYTES 30 int * bytes = new int[len+EXTRABYTES]; memset(bytes,0,sizeof(int)*(len+EXTRABYTES)); int pointer = 0; // reset values Bot_Eval_error = false; // set the global pointer to the string at the start of the string BotCurrentChar = formula; // first we want to quickly check whether the brackets are matching or not // same for ? and :, both are pairs and should not exist alone, also they // can not appear out of order (so no "?;:" ";?:" or something) // also, char * quickscan = formula; // to print an errormsg char errormsg[200]; // counters int iifs = 0, iifs2 = 0, bracks = 0; // until EOL while (*quickscan != 0 && !Bot_Eval_error) { switch(*quickscan) { case 'l': if (*(quickscan+1)=='o'&&*(quickscan+2)=='o'&&*(quickscan+3)=='p') ++iifs2; break; case '?': ++iifs; break; case ':': --iifs; ++iifs2; if (iifs < 0) BotSyntaxError(1004); break; case ';': --iifs2; if (iifs2 < 0) BotSyntaxError(1004); break; case '(': ++bracks; break; case ')': --bracks; if (bracks < 0) BotSyntaxError(1004); break; default: break; } ++quickscan; } if (Bot_Eval_error || iifs || bracks || iifs2) { BotSyntaxError(1004); sprintf(errormsg,"[%d] ?:;() dont match!",codefield); debugS(errormsg); delete bytes; return NULL; } bool hexmode = false; bool negmode = false; // two stacks, used to branch properly // code adds current pos to the stack if one of the commands // is encountered. once a proper jump point is reached, put // the location back at the position that's on the top of the // stack (popping it off). for the iif, this means first // pushing the qmarkstack, when reaching the colon pop the // qmarkstack and put the position there, then push the // colonstack. when the end is reached, pop the colonstack and // put the position there. this allows easy jumping. loop just // needs to jump over its argument when it has looped enough. int qmarkstack[100]; int colonstack[100]; // stack-counters int qpoint=-1,cpoint=-1,lpoint=-1; int pointerStart = (int)BotCurrentChar; while(*BotCurrentChar != 0 && !Bot_Eval_error) { switch(*BotCurrentChar) { case 0: // stringterminator, end of code // no more input, so return break; case '(': ++BotCurrentChar; bytes[pointer++] = BOT_BYTE_LB; break; case ')': ++BotCurrentChar; bytes[pointer++] = BOT_BYTE_RB; break; case 'x': ++BotCurrentChar; hexmode = true; break; case '+': ++BotCurrentChar; bytes[pointer++] = BOT_BYTE_ADD; break; case '-': ++BotCurrentChar; bytes[pointer++] = BOT_BYTE_SUB; break; case '*': ++BotCurrentChar; bytes[pointer++] = BOT_BYTE_MUL; break; case '/': ++BotCurrentChar; bytes[pointer++] = BOT_BYTE_DIV; break; case '%': ++BotCurrentChar; bytes[pointer++] = BOT_BYTE_MOD; break; case '&': ++BotCurrentChar; bytes[pointer++] = BOT_BYTE_AND; break; case '|': ++BotCurrentChar; bytes[pointer++] = BOT_BYTE_OR; break; case '^': ++BotCurrentChar; bytes[pointer++] = BOT_BYTE_XOR; break; case '=': ++BotCurrentChar; bytes[pointer++] = BOT_BYTE_EQ; break; case '>': if(*(BotCurrentChar + 1) == '=') { BotCurrentChar += 2; bytes[pointer++] = BOT_BYTE_GTEQ; } else if(*(BotCurrentChar + 1) == '>') { BotCurrentChar += 2; bytes[pointer++] = BOT_BYTE_RSHIFT; } else { ++BotCurrentChar; bytes[pointer++] = BOT_BYTE_GT; } break; case '<': if(*(BotCurrentChar + 1) == '=') { BotCurrentChar += 2; bytes[pointer++] = BOT_BYTE_LTEQ; } else if(*(BotCurrentChar + 1) == '<') { BotCurrentChar += 2; bytes[pointer++] = BOT_BYTE_LSHIFT; } else { ++BotCurrentChar; bytes[pointer++] = BOT_BYTE_LT; } break; case '!': if(*(BotCurrentChar + 1) == '=') { BotCurrentChar += 2; bytes[pointer++] = BOT_BYTE_UEQ; } else { BotSyntaxError(1001); // "!" is not a valid command, but perhaps we could // use it as a prefix, checking if the next is 1000 // (or 0) or not and inverting it. For now error it. sprintf(errormsg,"[%d] Unknown command (!...)",codefield); debugS(errormsg); delete bytes; return NULL; } break; case '?': ++BotCurrentChar; // the bytecode will look similar to this: // xyz?1abcd:2efghi;3 // 1 will contain the adres of 2 (+1), 2 contains the adres of 3 // if true, it skips over 1, jumping to 3 when reaching : // else it just jumps to 2 // this is basic branching using a stack :) bytes[pointer++] = BOT_BYTE_IIF; qmarkstack[++qpoint] = pointer++; break; case ':': { ++BotCurrentChar; // colon encountered bytes[pointer++] = BOT_BYTE_COLON; // next position will contain jump to end of second statement colonstack[++cpoint] = pointer++; // put the number of positions after this target as jump target after the ? int qmarkpos = qmarkstack[qpoint--]; bytes[qmarkpos] = pointer - qmarkpos; break; } case ';': { ++BotCurrentChar; bytes[pointer++] = BOT_BYTE_SCOLON; // semicolon encountered // had to use the semicolon to simplify determining end of second arg... // next position is the target of the colon int colonpos = colonstack[cpoint--]; bytes[colonpos] = pointer - colonpos; break; } case 10: case 13: case 9: case ' ': ++BotCurrentChar; break; case 'a': if(*(BotCurrentChar+1) == 't' &&*(BotCurrentChar+2) == 't' &&*(BotCurrentChar+3) == 'e' &&*(BotCurrentChar+4) == 'm' &&*(BotCurrentChar+5) == 'p' &&*(BotCurrentChar+6) == 't') { // attempt BotCurrentChar += 7; bytes[pointer++] = BOT_BYTE_ATTEMPT; } else if((*(BotCurrentChar+1) == 'd' &&*(BotCurrentChar+2) == 'd' &&*(BotCurrentChar+3) == 'c' &&*(BotCurrentChar+4) == 'o' &&*(BotCurrentChar+5) == 'u' &&*(BotCurrentChar+6) == 'n' &&*(BotCurrentChar+7) == 't' &&*(BotCurrentChar+8) == 'e' &&*(BotCurrentChar+9) == 'r' &&*(BotCurrentChar+10)== '(') || (*(BotCurrentChar+1) == 'c' &&*(BotCurrentChar+2) == '(')) { // addcounter() ac() BotCurrentChar += (*(BotCurrentChar+1) == 'd') ? 11 : 3; bytes[pointer++] = BOT_BYTE_AC; } else if(*(BotCurrentChar+1) == 'b' &&*(BotCurrentChar+2) == 's' &&*(BotCurrentChar+3) == '(') { // abs() BotCurrentChar += 4; bytes[pointer++] = BOT_BYTE_ABS; } else if(*(BotCurrentChar+1) == 'l' &&*(BotCurrentChar+2) == 'w' &&*(BotCurrentChar+3) == 'a' &&*(BotCurrentChar+4) == 'y' &&*(BotCurrentChar+5) == 's') { // always (=max) BotCurrentChar += 6; bytes[pointer++] = BOT_BYTE_MAX; } else { // a ++BotCurrentChar; bytes[pointer++] = BOT_BYTE_LIT; bytes[pointer++] = 1; } break; case 'b': if(*(BotCurrentChar+1) == 'u' &&*(BotCurrentChar+2) == 't' &&*(BotCurrentChar+3) == 't' &&*(BotCurrentChar+4) == 'o' &&*(BotCurrentChar+5) == 'n') { // button BotCurrentChar += 6; bytes[pointer++] = BOT_BYTE_BUTTON; } else { // button B ++BotCurrentChar; bytes[pointer++] = BOT_BYTE_LIT; bytes[pointer++] = 2; } break; case 'c': if((*(BotCurrentChar+1) == 'o' && *(BotCurrentChar+2) == 'u' && *(BotCurrentChar+3) == 'n' && *(BotCurrentChar+4) == 't' && *(BotCurrentChar+5) == 'e' && *(BotCurrentChar+6) == 'r' && *(BotCurrentChar+7) == '(') || (*(BotCurrentChar+1) == '(')) { // counter() BotCurrentChar += (*(BotCurrentChar+1) == 'o') ? 8 : 2; bytes[pointer++] = BOT_BYTE_C; } else { BotSyntaxError(1001); sprintf(errormsg,"[%d] Unknown Command (c...)",codefield); debugS(errormsg); delete bytes; return NULL; } break; case 'd': if(*(BotCurrentChar+1) == 'o' &&*(BotCurrentChar+2) == 'w' &&*(BotCurrentChar+3) == 'n') { // down BotCurrentChar += 4; bytes[pointer++] = BOT_BYTE_DOWN; } else { BotSyntaxError(1001); sprintf(errormsg,"[%d] Unknown Command (d...)",codefield); debugS(errormsg); delete bytes; return NULL; } break; case 'e': if(*(BotCurrentChar+1) == 'c' &&*(BotCurrentChar+2) == 'h' &&*(BotCurrentChar+3) == 'o') { BotCurrentChar += 4; bytes[pointer++] = BOT_BYTE_ECHO; break; } if(*(BotCurrentChar+1) == 'r' &&*(BotCurrentChar+2) == 'r' &&*(BotCurrentChar+3) == 'o' &&*(BotCurrentChar+4) == 'r') { BotCurrentChar += 5; bytes[pointer++] = BOT_BYTE_ERROR; break; } else { BotSyntaxError(1001); sprintf(errormsg,"[%d] Unknown Command (e...)",codefield); debugS(errormsg); delete bytes; return NULL; } case 'f': if(*(BotCurrentChar+1) == 'r' && *(BotCurrentChar+2) == 'a' && *(BotCurrentChar+3) == 'm' && *(BotCurrentChar+4) == 'e') { // frame BotCurrentChar += 5; bytes[pointer++] = BOT_BYTE_FRAME; } else { BotSyntaxError(1001); sprintf(errormsg,"[%d] Unknown Command (f...)",codefield); debugS(errormsg); delete bytes; return NULL; } break; case 'i': if(*(BotCurrentChar+1) == 'n' &&*(BotCurrentChar+2) == 'v' &&*(BotCurrentChar+3) == 'a' &&*(BotCurrentChar+4) == 'l' &&*(BotCurrentChar+5) == 'i' &&*(BotCurrentChar+6) == 'd' &&*(BotCurrentChar+7) == 's') { // invalids BotCurrentChar += 8; bytes[pointer++] = BOT_BYTE_INVALIDS; } else { ++BotCurrentChar; bytes[pointer++] = BOT_BYTE_I; } break; case 'j': ++BotCurrentChar; bytes[pointer++] = BOT_BYTE_J; break; case 'k': ++BotCurrentChar; bytes[pointer++] = BOT_BYTE_K; break; case 'l': if((*(BotCurrentChar+1) == 'b') || (*(BotCurrentChar+1) == 'a' &&*(BotCurrentChar+2) == 's' &&*(BotCurrentChar+3) == 't' &&*(BotCurrentChar+4) == 'b' &&*(BotCurrentChar+5) == 'u' &&*(BotCurrentChar+6) == 't' &&*(BotCurrentChar+7) == 't' &&*(BotCurrentChar+8) == 'o' &&*(BotCurrentChar+9) == 'n')) { // lastbutton lb BotCurrentChar += (*(BotCurrentChar+1) == 'b') ? 2 : 10; bytes[pointer++] = BOT_BYTE_LAST; } else if(*(BotCurrentChar+1) == 'e' &&*(BotCurrentChar+2) == 'f' &&*(BotCurrentChar+3) == 't') { // left BotCurrentChar += 4; bytes[pointer++] = BOT_BYTE_LEFT; } else if(*(BotCurrentChar+1) == 'o' &&*(BotCurrentChar+2) == 'o' &&*(BotCurrentChar+3) == 'p') { // loop // loop and iif share the same limiter ';' // and since you cant desync nesting them anyways, its // safe to use the colonstack to add the loops, because // if a loop is added while inside a colon-semicolon // statement, it needs to be closed before leaving this // block anyways. (make sure the code checks for this!) BotCurrentChar += 4; bytes[pointer++] = BOT_BYTE_LOOP; colonstack[++cpoint] = pointer++; } else { BotSyntaxError(1001); sprintf(errormsg,"[%d] Unknown Command (l...)",codefield); debugS(errormsg); delete bytes; return NULL; } break; case 'm': if(*(BotCurrentChar+1) == 'a' &&*(BotCurrentChar+2) == 'x') { // max BotCurrentChar += 3; bytes[pointer++] = BOT_BYTE_MAX; } else if(*(BotCurrentChar+1) == 'e' &&*(BotCurrentChar+2) == 'm' &&*(BotCurrentChar+3) == '(') { // mem() BotCurrentChar += 4; bytes[pointer++] = BOT_BYTE_MEM; } else if(*(BotCurrentChar+1) == 'e' &&*(BotCurrentChar+2) == 'm' &&*(BotCurrentChar+3) == 'h' &&*(BotCurrentChar+4) == '(') { // memh() BotCurrentChar += 5; bytes[pointer++] = BOT_BYTE_MEMH; } else if(*(BotCurrentChar+1) == 'e' &&*(BotCurrentChar+2) == 'm' &&*(BotCurrentChar+3) == 'l' &&*(BotCurrentChar+4) == '(') { // meml() BotCurrentChar += 5; bytes[pointer++] = BOT_BYTE_MEML; } else if(*(BotCurrentChar+1) == 'e' &&*(BotCurrentChar+2) == 'm' &&*(BotCurrentChar+3) == 'w' &&*(BotCurrentChar+4) == '(') { // memw() BotCurrentChar += 5; bytes[pointer++] = BOT_BYTE_MEMW; } else { BotSyntaxError(1001); sprintf(errormsg,"[%d] Unknown Command (m...)",codefield); debugS(errormsg); delete bytes; return NULL; } break; case 'n': if(*(BotCurrentChar+1) == 'e' &&*(BotCurrentChar+2) == 'v' &&*(BotCurrentChar+3) == 'e' &&*(BotCurrentChar+4) == 'r'|| *(BotCurrentChar+1) == 'o') { // never no BotCurrentChar += (*(BotCurrentChar+1) == 'e') ? 5 : 2; bytes[pointer++] = BOT_BYTE_NEVER; } else { // this is a little sketchy, but the principle works. ++BotCurrentChar; negmode = true; } break; case 'o': if(*(BotCurrentChar+1) == 'k' &&*(BotCurrentChar+2) == 's') { // oks BotCurrentChar += 3; bytes[pointer++] = BOT_BYTE_OKS; } else { BotSyntaxError(11001); sprintf(errormsg,"[%d] Unknown Command (o...)",codefield); debugS(errormsg); delete bytes; return NULL; } break; case 'r': if(*(BotCurrentChar+1) == 'i' &&*(BotCurrentChar+2) == 'g' &&*(BotCurrentChar+3) == 'h' &&*(BotCurrentChar+4) == 't') { // right BotCurrentChar += 5; bytes[pointer++] = BOT_BYTE_RIGHT; } else if((*(BotCurrentChar+1) == 'e' &&*(BotCurrentChar+2) == 's' &&*(BotCurrentChar+3) == 'e' &&*(BotCurrentChar+4) == 't' &&*(BotCurrentChar+5) == 'c' &&*(BotCurrentChar+6) == 'o' &&*(BotCurrentChar+7) == 'u' &&*(BotCurrentChar+8) == 'n' &&*(BotCurrentChar+9) == 't' &&*(BotCurrentChar+10) == 'e' &&*(BotCurrentChar+11) == 'r' &&*(BotCurrentChar+12) == '(' )||( *(BotCurrentChar+1) == 'c' &&*(BotCurrentChar+2) == '(')) { // resetcounter() rc() BotCurrentChar += (*(BotCurrentChar+1) == 'e') ? 13 : 3; bytes[pointer++] = BOT_BYTE_RC; } else { BotSyntaxError(1001); sprintf(errormsg,"[%d] Unknown Command (r...)",codefield); debugS(errormsg); delete bytes; return NULL; } break; case 's': if(*(BotCurrentChar+1) == 't' &&*(BotCurrentChar+2) == 'a' &&*(BotCurrentChar+3) == 'r' &&*(BotCurrentChar+4) == 't') { // start BotCurrentChar += 5; bytes[pointer++] = BOT_BYTE_START; } else if(*(BotCurrentChar+1) == 'c' &&*(BotCurrentChar+2) == 'o' &&*(BotCurrentChar+3) == 'r' &&*(BotCurrentChar+4) == 'e' &&(*(BotCurrentChar+5) == '1' || *(BotCurrentChar+5) == '2' || *(BotCurrentChar+5) == '3' || *(BotCurrentChar+5) == '4' || *(BotCurrentChar+5) == '5' || *(BotCurrentChar+5) == '6')) { // score 1-6 // returns the current max tie 1-6 switch ((int)*(BotCurrentChar+5)) { case '1': bytes[pointer++] = BOT_BYTE_SCORE1; break; case '2': bytes[pointer++] = BOT_BYTE_SCORE2; break; case '3': bytes[pointer++] = BOT_BYTE_SCORE3; break; case '4': bytes[pointer++] = BOT_BYTE_SCORE4; break; case '5': bytes[pointer++] = BOT_BYTE_SCORE5; break; case '6': bytes[pointer++] = BOT_BYTE_SCORE6; break; default: sprintf(errormsg,"[%d] Serious error here",codefield); debugS(errormsg); } BotCurrentChar += 6; } else if(*(BotCurrentChar+1) == 'e' &&*(BotCurrentChar+2) == 'l' &&*(BotCurrentChar+3) == 'e' &&*(BotCurrentChar+4) == 'c' &&*(BotCurrentChar+5) == 't') { // select BotCurrentChar += 6; bytes[pointer++] = BOT_BYTE_SELECT; } // why was there no short version for setcounter()? There is now... :) else if ((*(BotCurrentChar+1) == 'c') || (*(BotCurrentChar+1) == 'e' &&*(BotCurrentChar+2) == 't' &&*(BotCurrentChar+3) == 'c' &&*(BotCurrentChar+4) == 'o' &&*(BotCurrentChar+5) == 'u' &&*(BotCurrentChar+6) == 'n' &&*(BotCurrentChar+7) == 't' &&*(BotCurrentChar+8) == 'e' &&*(BotCurrentChar+9) == 'r' &&*(BotCurrentChar+10) == '(')) { // setcounter() sc() BotCurrentChar += (*(BotCurrentChar+1) == 'c') ? 3 : 11; bytes[pointer++] = BOT_BYTE_SC; } // qfox: Added stop command. Should do exactly the same as pressing the "stop" button.\ // But probably won't yet :) else if(*(BotCurrentChar+1) == 't' &&*(BotCurrentChar+2) == 'o' &&*(BotCurrentChar+3) == 'p') { // stop BotCurrentChar += 4; bytes[pointer++] = BOT_BYTE_STOP; } else { BotSyntaxError(10001); sprintf(errormsg,"[%d] Unknown Command (s...)",codefield); debugS(errormsg); delete bytes; return NULL; } break; case 'u': if(*(BotCurrentChar+1) == 'p') { // up BotCurrentChar += 2; bytes[pointer++] = BOT_BYTE_UP; } else { BotSyntaxError(11001); sprintf(errormsg,"[%d] Unknown Command (u...)",codefield); debugS(errormsg); delete bytes; return NULL; } break; case 'v': if(*(BotCurrentChar+1) == 'a' &&*(BotCurrentChar+2) == 'l' &&*(BotCurrentChar+3) == 'u' &&*(BotCurrentChar+4) == 'e') { // value // "value" is completely transparent, more like a nop (or no-operation). // could be used in a iif where there is no else, or to make it more // clear how value is used internally... BotCurrentChar += 5; } else { BotSyntaxError(1001); sprintf(errormsg,"[%d] Unknown Command (v...)",codefield); debugS(errormsg); delete bytes; return NULL; } break; case 'y': if(*(BotCurrentChar+1) == 'e' &&*(BotCurrentChar+2) == 's') { // yes BotCurrentChar += 3; bytes[pointer++] = BOT_BYTE_MAX; } else { BotSyntaxError(1001); sprintf(errormsg,"[%d] Unknown Command (y...)",codefield); debugS(errormsg); delete bytes; return NULL; } break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': // Not quite sure how the original literals worked, but it doesn't work in my // implementation so I had to rewrite it. When a digit is encountered it will // start reading character (starting at the first). "value" is multiplied by 10 // and the next digit is added to it. When a non-digit char is encountered, the // code stops parsing this number and behaves like everywhere else. (returning // it if ret is true, breaking else). // If hexmode, this is done below, taking note of the additional "numbers". if (!hexmode) { // keep on reading input until no more numbers are encountered (0-9) int ahead = 0; int value = 0; while (*(BotCurrentChar+ahead) >= '0' && *(BotCurrentChar+ahead) <= '9') { if (negmode) value = (value*10)-(*(BotCurrentChar+ahead) - '0'); else value = (value*10)+(*(BotCurrentChar+ahead) - '0'); ++ahead; } // I don't think ahead needs to be incremented before adding it to // currentchar, because the while increments it for _every_ character. BotCurrentChar += ahead; bytes[pointer++] = BOT_BYTE_LIT; bytes[pointer++] = value; negmode = false; break; } case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': // Hexmode does things a liiiitle differently. We need to multiply each number // by 16 instead of 10, and if the character is ABCDEF we need to subtract 'A' // and add 10, instead of just subtracting '0'. Otherwise same principle. if(hexmode) { // Keep on reading input until no more numbers are encountered (0-9) int ahead = 0; int value = 0; // number-size check (int is max) -> do it when converting to bytecode while ((*(BotCurrentChar+ahead) >= '0' && *(BotCurrentChar+ahead) <= '9') || (*(BotCurrentChar+ahead) >= 'A' && *(BotCurrentChar+ahead) <= 'F')) { // next code is a little redundant, but it's either redundant or even harder to read. if (*(BotCurrentChar+ahead) >= '0' && *(BotCurrentChar+ahead) <= '9') if (negmode) value = (value*16)-(*(BotCurrentChar+ahead) - '0'); else value = (value*16)+(*(BotCurrentChar+ahead) - '0'); else if (negmode) value = (value*16)-((*(BotCurrentChar+ahead) - 'A')+10); else value = (value*16)+((*(BotCurrentChar+ahead) - 'A')+10); ++ahead; } BotCurrentChar += ahead; bytes[pointer++] = BOT_BYTE_LIT; bytes[pointer++] = value; negmode = false; hexmode = false; } else { // If not in hexmode, we cant use these characters. BotSyntaxError(1003); sprintf(errormsg,"[%d] Not in hexmode!",codefield); debugS(errormsg); delete bytes; return NULL; } break; case 'P': ++BotCurrentChar; bytes[pointer++] = BOT_BYTE_P; break; case 'Q': ++BotCurrentChar; bytes[pointer++] = BOT_BYTE_Q; break; case 'X': ++BotCurrentChar; bytes[pointer++] = BOT_BYTE_X; break; case 'Y': ++BotCurrentChar; bytes[pointer++] = BOT_BYTE_Y; break; case 'Z': ++BotCurrentChar; bytes[pointer++] = BOT_BYTE_Z; break; default: // Unknown characters should error out. You can use spaces. BotSyntaxError(12001); sprintf(errormsg,"[%d][%d] Unknown character... '%c'",codefield,BotCurrentChar-pointerStart,(*BotCurrentChar)); debugS(errormsg); delete bytes; return NULL; } } return bytes;}/*** The bytecode interpreter*/static int Interpreter(bool single){ int value = 0; int nextlit; while (Bytecode[BotByteCodeField][BotCurrentCode] != 0) { switch (Bytecode[BotByteCodeField][BotCurrentCode++]) { case 0: break; case BOT_BYTE_LB: value = Interpreter(false); if (single) return value; break; case BOT_BYTE_RB: return value; case BOT_BYTE_LIT: value = Bytecode[BotByteCodeField][BotCurrentCode++]; if (single) return value; break; case BOT_BYTE_ADD: value += Interpreter(true); if (single) return value; break; case BOT_BYTE_SUB: value -= Interpreter(true); if (single) return value; break; case BOT_BYTE_MUL: value *= Interpreter(true); if (single) return value; break; case BOT_BYTE_DIV: { // we need to set up a zero-division-exception-trap here int zerotrap = Interpreter(true); if (value == 0 || zerotrap == 0) value = 0; else value /= zerotrap; if (single) return value; break; } case BOT_BYTE_MOD: { // we need to set up a zero-division-exception-trap here int zerotrap = Interpreter(true); if (value == 0 || zerotrap == 0) value = 0; else value %= zerotrap; if (single) return value; break; } case BOT_BYTE_AND: value &= Interpreter(true); if (single) return value; break; case BOT_BYTE_OR: value |= Interpreter(true); if (single) return value; break; case BOT_BYTE_XOR: value ^= Interpreter(true); if (single) return value; break; case BOT_BYTE_EQ: value = ((value == Interpreter(true)) ? BOT_MAX_PROB : 0); if (single) return value; break; case BOT_BYTE_GTEQ: value = ((value >= Interpreter(true)) ? BOT_MAX_PROB : 0); if (single) return value; break; case BOT_BYTE_GT: value = ((value > Interpreter(true)) ? BOT_MAX_PROB : 0); if (single) return value; break; case BOT_BYTE_LTEQ: value = ((value <= Interpreter(true)) ? BOT_MAX_PROB : 0); if (single) return value; break; case BOT_BYTE_LT: value = ((value < Interpreter(true)) ? BOT_MAX_PROB : 0); if (single) return value; break; case BOT_BYTE_UEQ: value = ((value != Interpreter(true)) ? BOT_MAX_PROB : 0); if (single) return value; break; case BOT_BYTE_IIF: if (value == 0) BotCurrentCode += Bytecode[BotByteCodeField][BotCurrentCode]; else BotCurrentCode += 1; value = Interpreter(false); break; case BOT_BYTE_COLON: // move the pointer beyond the "else" part BotCurrentCode += Bytecode[BotByteCodeField][BotCurrentCode]; return value; case BOT_BYTE_SCOLON: // limiter for iif and loop return value; case BOT_BYTE_ATTEMPT: value = BotAttempt; if (single) return value; break; case BOT_BYTE_AC: value = (BotCounter[(Interpreter(false) & 0x7FFFFFFF) % 256] += value); if (single) return value; break; case BOT_BYTE_ABS: value = Interpreter(false); if (value < 0) value = (0 - value); if (single) return value; break; case BOT_BYTE_BUTTON: if (value == 0) value = BotInput[1]; else value &= BotInput[1]; if (single) return value; break; case BOT_BYTE_C: value = BotCounter[(Interpreter(false) & 0x7FFFFFFF) % 256]; if (single) return value; break; case BOT_BYTE_DOWN: value = 32; if (single) return value; break; case BOT_BYTE_ECHO: debug(value); break; case BOT_BYTE_ERROR: error(value); break; case BOT_BYTE_FRAME: value = BotFrame; if (single) return value; break; case BOT_BYTE_I: value = BotLoopVarI; if (single) return value; break; case BOT_BYTE_INVALIDS: value = BotInvalids; if (single) return value; break; case BOT_BYTE_J: value = BotLoopVarJ; if (single) return value; break; case BOT_BYTE_K: value = BotLoopVarK; if (single) return value; break; case BOT_BYTE_LAST: value = BotLastbutton; if (single) return value; break; case BOT_BYTE_LEFT: value = 64; if (single) return value; break; case BOT_BYTE_LOOP: if (value != 0) { int tel = value; // increase the position of the codepointer first // then back it up // at the end of the loop, go back one to get the // position where the code continues (after loop) int pos = ++BotCurrentCode; if (BotLoopVarI == 0) { for (; BotLoopVarI < tel; ++BotLoopVarI) { Interpreter(false); BotCurrentCode = pos; } BotLoopVarI = 0; } else if (BotLoopVarJ == 0) { for (; BotLoopVarJ < tel; ++BotLoopVarJ) { Interpreter(false); BotCurrentCode = pos; } BotLoopVarJ = 0; } else if (BotLoopVarK == 0) { for (; BotLoopVarK < tel; ++BotLoopVarK) { Interpreter(false); BotCurrentCode = pos; } BotLoopVarK = 0; } else { // well... this should never happen, if the encoder // checks for a max loop depth of 3... MessageBoxA(hwndTASbot, "wtf1", "oopsie", MB_OK); } // go back one and fetch the position of where to continue BotCurrentCode += Bytecode[BotByteCodeField][BotCurrentCode-1]-1; } else { BotCurrentCode += Bytecode[BotByteCodeField][BotCurrentCode]; } if (single) return value; break; case BOT_BYTE_LSHIFT: value <<= Interpreter(true); if (single) return value; break; case BOT_BYTE_MAX: value = BOT_MAX_PROB; if (single) return value; break; case BOT_BYTE_MEM: nextlit = (Interpreter(false) & 0x7FFFFFFF) % 65536; value = ARead[nextlit](nextlit); if (single) return value; break; case BOT_BYTE_MEMH: nextlit = (Interpreter(false) & 0x7FFFFFFF) % 65536; value = (ARead[nextlit](nextlit) & 0xF0) >> 8; if (single) return value; break; case BOT_BYTE_MEML: nextlit = (Interpreter(false) & 0x7FFFFFFF) % 65536; value = ARead[nextlit](nextlit) & 0x0F; if (single) return value; break; case BOT_BYTE_MEMW: { int nextlit = Interpreter(false); int v1 = (nextlit & 0x7FFFFFFF) % 65536; int v2 = ((nextlit+1) & 0x7FFFFFFF) % 65536; v1 = ARead[v1](v1); v2 = ARead[v2](v2); value = (v1<<8)+v2; if (single) return value; break; } case BOT_BYTE_NEVER: value = 0; if (single) return value; break; case BOT_BYTE_OKS: value = BotOks; if (single) return value; break; case BOT_BYTE_RC: value = (BotCounter[(Interpreter(false) & 0x7FFFFFFF) % 256] = 0); if (single) return 0; break; case BOT_BYTE_RIGHT: value = 128; if (single) return value; break; case BOT_BYTE_RSHIFT: value >>= Interpreter(true); if (single) return value; break; case BOT_BYTE_START: value = 8; if (single) return value; break; case BOT_BYTE_SELECT: value = 4; if (single) return value; break; case BOT_BYTE_SC: BotCounter[(Interpreter(false) & 0x7FFFFFFF) % 256] = value; if (single) return value; break; case BOT_BYTE_SCORE1: value = BotBestScore[0]; if (single) return value; break; case BOT_BYTE_SCORE2: value = BotBestScore[1]; if (single) return value; break; case BOT_BYTE_SCORE3: value = BotBestScore[2]; if (single) return value; break; case BOT_BYTE_SCORE4: value = BotBestScore[3]; if (single) return value; break; case BOT_BYTE_SCORE5: value = BotBestScore[4]; if (single) return value; break; case BOT_BYTE_SCORE6: value = BotBestScore[5]; if (single) return value; break; case BOT_BYTE_STOP: // stops the code Bot_Eval_error = true; MessageBox(hwndTASbot, "Stopped by code!", "Stop signal", MB_OK); StopBasicBot(); return 0; case BOT_BYTE_UP: value = 16; if (single) return value; break; case BOT_BYTE_P: value = Bot_Prand; if (single) return value; break; case BOT_BYTE_Q: value = Bot_Qrand; if (single) return value; break; case BOT_BYTE_X: value = Bot_X; if (single) return value; break; case BOT_BYTE_Y: value = Bot_Y; if (single) return value; break; case BOT_BYTE_Z: value = Bot_Z; if (single) return value; break; default: break; } } return value;}/*** Front end for the interpreter*/static int evaluate(int codefield){ BotCurrentCode = 0; BotByteCodeField = codefield; return Interpreter(false);}/*** Get a string showing the first x chars of the bytecode*/static char * DebugByteCode(int code[]){ static int len = intlen(code); char result[len*3]; // for the love of god, why cant c++ just init this space :/ memset(result, 0, len*3); for (int i=0; i<len; ++i) sprintf(result,"%s %d",result,code[i]); return result; //SetDlgItemTextA(hwndTASbot,GUI_BOT_KEYS,result);}/*** set a textfield to something**/void toField(int field, char * text){ SetDlgItem(hwndTASbot,field,text,TRUE);}/*** get text from field**/char * fromField(int field){ unsigned int count = FieldLength(hwndTASbot,field); char *t = new char[count+1]; GetDlgItemTextA(hwndTASbot,field,t,count+1); return t;}/*** put all the texts in memory to gui**/void toGUI(){ for (int i=0; i<BOT_FORMULAS; ++i) { toField(labels[i],Formula[i]); } debugS("Updated GUI");}/*** get all the texts from gui**/void fromGUI(){ for (int i=0; i<BOT_FORMULAS; ++i) { Formula[i] = fromField(labels[i]); if (i < CODE_COMMENT_MARKER) { Bytecode[i] = convert(Formula[i],i); } }}/*** Called from windows. main gui (=dialog) control.**/static BOOL CALLBACK TASBotCallBack (HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { DSMFix(uMsg); // stops sound on 5 of the messages... switch(uMsg) { case WM_INITDIALOG: // cant change gui controls in this event // seems to be a inherited "feature", for backwards // compatibility. break; case WM_CLOSE: case WM_QUIT: DestroyWindow(hwndTASbot); hwndTASbot = 0; break; case WM_COMMAND: switch(HIWORD(wParam)) { case BN_CLICKED: switch (LOWORD(wParam)) { case BOT_GUI_TEST: mboxI(bot_rand(5)); break; default: break; } } break; // WM_COMMAND default: break; } return 0;}void CreateWindow(){ // create window CreateDialog(fceu_hInstance,"TASBOT",NULL,TASBotCallBack); // randomize randomizer srand(time(0));}