diff --git a/documentation/BBchangelog.txt b/documentation/BBchangelog.txt index b2e68215..09cb4ddd 100644 --- a/documentation/BBchangelog.txt +++ b/documentation/BBchangelog.txt @@ -1,4 +1,22 @@ This changelog only concerns the Basic Bot code. +Changelog 0.3.2: +16 september 2006 + +- Added - Command - " error" will show the in the error part (can be used in conjunction to echo). Does not actually throw an error... +- Change - Command - "stop" does not set the error 5000 any more and notifies the user by dialog box (so not to trash the debug) +- Fixed - Bug - If you would replay best with >1000 frames before, the program would crash. Fixed now. +- Added - Command - "max", "always" and "yes", return the maximum probability and behave the same (but sometimes max makes more sense, and other times always does and the next yes does). +- Added - Command - "never" and "no", both return 0. +- Added - Manual - yay! Look in documentation of this project, BBmanual.txt is what you want. + +Known issues: +- I've seen the bot desyncing once when playing best... +- Rollback, Max frames, Max attempts, Max parts, do nothing. They are for a future feature :) +- The autoload/save thing is still broke. I think fceu somehow remembers the last used directory after loading/saving and uses it as the "working directory". +- The debugger still pops up for exiting botmode, just press run and close it. +- Test crashes (so just dont press it :) +- May create some kind of "expert" button to hide half the current GUI, so new people wont be overwhelmed by the number of controls. + Changelog 0.3.1: 10 september 2006 - Change - Code - Removed the code length limit. You can now enter as much characters as you desire (and the GUI allows). This entails no overhead in variables and save files. As a result older botfiles can no longer be loaded. This change has a high impact on BB. diff --git a/documentation/BBmanual.txt b/documentation/BBmanual.txt index f9adb132..b08f0924 100644 --- a/documentation/BBmanual.txt +++ b/documentation/BBmanual.txt @@ -1,6 +1,6 @@ Manual for BasicBot -Written for v0.3.1 -10 september 2006 +Written for v0.3.2 +16 september 2006 Peter "qFox" van der Zee This is a bot. It uses a combination of probability and scripting to play games. It comes with a rather powerfull scripting language. @@ -82,6 +82,7 @@ a = Returns the flag for button A. See also button. abs(x) = Returns the positive value of x. ac(x) = Same as addcounter(x). addcounter(x) = Add to counter x +always = Returns the max probability, can make code more legible, exactly same as "max". button = Returns the buttons pressed (so far) for this frame. If is 0, all buttons are returned, else only those buttons supplied ( is ANDed with the buttons first). counter(x) = Returns value of counter x. c(x) = Same as counter(x) @@ -95,11 +96,14 @@ lastbutton = Returns the buttons pressed in the previous frame, always returns 0 lb = Same as lastbutton. left = Returns flag for button left. See also button. loop x ; = Execute "x" times. The x may be more then one statement, the ; is mandatory and defines x. You may nest a max of three loops at once. Example: "3 loop 2 + i ac(5) ;" +max = Returns the max probability, can make code more legible, exactly same as "always". mem(x) = Returns the value of address x. This can only be done when a rom is loaded (in fact, the program will crash if somehow not so). It will read the memory of the running game and returns it's value. For the bot, this is probably the most used command since it's the only way to get direct info from the game. Stuff like score, but also environment. Basically anything! To know what value is stored where, you need to check out the game by other tools in FCEU first... memh(x) = Same as mem(x), except it returns the high nibble of a byte (so it ANDs the value with 0xF0 first). meml(x) = Same as mem(x), except it returns the low nibble of a byte (so it ANDs the value with 0x0F first). memw(x) = Same as mem(x), except it returns a word (two bytes), x and x+1 to be precies, as one value. Would be same as "(mem(x)<<8)+mem(x+1)" n = Puts you in negmode, the next number encountered will be interpretered as being negative. This was much much easier opposed to using the prefix '-', since that's also the operator for subtraction. +no = Same as never. +never = returns 0, can make code more legible. rc(x) = Same as resetcounter(x). right = Returns flag for button right. See also button. resetcounter(x) = Resets counter x to 0. Sort of the same as "0 sc(x)". @@ -111,6 +115,7 @@ up = Returns flag for button up. See also button. x = Puts you in hexmode. The next number encountered will be interpretered as a hex number. You may use "0123456789ABCDEF" for a number in this mode. "ABCDEF" will error out when not in hexmode, and is case-sensitive! X,Y,Z = Returns the value entered in the static variables. Note that these values are evaluated only once, at updating time, so something like "frame" for this value is useless. P,Q = Almost the same as X Y and Z, except these return a random value between 0 and P or Q. The returned value remains equal for every attempt, but changes to a new value between 0 and P or Q after each attempt. +yes = Same as always and max. When an erorr is thrown, which should never be while the bot is running, a guiding message is shown in the debug part to explain what went wrong. The bot will stop running when an error is thrown. diff --git a/src/drivers/win/basicbot.cpp b/src/drivers/win/basicbot.cpp index 25ec356c..251e0191 100644 --- a/src/drivers/win/basicbot.cpp +++ b/src/drivers/win/basicbot.cpp @@ -1,11 +1,8 @@ /** * qfox: - * todo: boundrycheck every case in the switch! the string checks might override the boundries and read data its not supposed to. i'm not quite sure if this is an issue in C though. * todo: memi() to fetch a 32bit number? is this desired? * todo: check for existence of argument (low priority) - * todo: let the code with errorchecking run through the code first so subsequent evals are faster. -> done when changing to bytecode/interpreter * todo: cleanup "static"s here and there and read up on what they mean in C, i think i've got them confused - * todo: fix some button so you can test code without having to run it (so setting external input and opening a rom won't be required to test scriptcode). Code is there but you can't do certain commands when no rom is loaded and nothing is init'ed, so i'll put this on hold. **/ /* FCE Ultra - NES/Famicom Emulator @@ -37,9 +34,9 @@ // go up each time something is released to the public, so you'll // know your version is the latest or whatever. Version will be // put in the project as well. A changelog will be kept. -static char BBversion[] = "0.3.1"; +static char BBversion[] = "0.3.2"; // title -static char BBcaption[] = "Basic Bot v0.3.1 by qFox"; +static char BBcaption[] = "Basic Bot v0.3.2 by qFox"; // save/load version static int BBsaveload = 1; @@ -157,6 +154,7 @@ const int BOT_BYTE_LB = 48, BOT_BYTE_C = 23, BOT_BYTE_DOWN = 24, BOT_BYTE_ECHO = 25, + BOT_BYTE_ERROR = 53, BOT_BYTE_FRAME = 26, BOT_BYTE_I = 27, BOT_BYTE_J = 28, @@ -165,10 +163,12 @@ const int BOT_BYTE_LB = 48, BOT_BYTE_LEFT = 30, BOT_BYTE_LOOP = 31, BOT_BYTE_LSHIFT = 51, + BOT_BYTE_MAX = 54, BOT_BYTE_MEM = 32, BOT_BYTE_MEMH = 33, BOT_BYTE_MEML = 34, BOT_BYTE_MEMW = 35, + BOT_BYTE_NEVER = 55, BOT_BYTE_RC = 37, BOT_BYTE_RIGHT = 36, BOT_BYTE_RSHIFT = 52, @@ -643,6 +643,16 @@ static int * ToByteCode(char * formula) GlobalCurrentChar += 4; bytes[pointer++] = BOT_BYTE_ABS; } + else if(*(GlobalCurrentChar+1) == 'l' + &&*(GlobalCurrentChar+2) == 'w' + &&*(GlobalCurrentChar+3) == 'a' + &&*(GlobalCurrentChar+4) == 'y' + &&*(GlobalCurrentChar+5) == 's') + { + // always (=max) + GlobalCurrentChar += 6; + bytes[pointer++] = BOT_BYTE_MAX; + } else { // a() @@ -686,7 +696,7 @@ static int * ToByteCode(char * formula) } else { - BotSyntaxError(2001); + BotSyntaxError(1001); debugS("Unknown Command (c...)"); free(bytes); return NULL; @@ -703,7 +713,7 @@ static int * ToByteCode(char * formula) } else { - BotSyntaxError(3001); + BotSyntaxError(1001); debugS("Unknown Command (d...)"); free(bytes); return NULL; @@ -718,9 +728,18 @@ static int * ToByteCode(char * formula) bytes[pointer++] = BOT_BYTE_ECHO; break; } + if(*(GlobalCurrentChar+1) == 'r' + &&*(GlobalCurrentChar+2) == 'r' + &&*(GlobalCurrentChar+3) == 'o' + &&*(GlobalCurrentChar+4) == 'r') + { + GlobalCurrentChar += 5; + bytes[pointer++] = BOT_BYTE_ERROR; + break; + } else { - BotSyntaxError(4001); + BotSyntaxError(1001); debugS("Unknown Command (e...)"); free(bytes); return NULL; @@ -737,7 +756,7 @@ static int * ToByteCode(char * formula) } else { - BotSyntaxError(5001); + BotSyntaxError(1001); debugS("Unknown Command (f...)"); free(bytes); return NULL; @@ -796,14 +815,21 @@ static int * ToByteCode(char * formula) } else { - BotSyntaxError(7001); + BotSyntaxError(1001); debugS("Unknown Command (l...)"); free(bytes); return NULL; } break; case 'm': - if(*(GlobalCurrentChar+1) == 'e' + if(*(GlobalCurrentChar+1) == 'a' + &&*(GlobalCurrentChar+2) == 'x') + { + // max + GlobalCurrentChar += 3; + bytes[pointer++] = BOT_BYTE_MAX; + } + else if(*(GlobalCurrentChar+1) == 'e' &&*(GlobalCurrentChar+2) == 'm' &&*(GlobalCurrentChar+3) == '(') { @@ -840,16 +866,29 @@ static int * ToByteCode(char * formula) } else { - BotSyntaxError(8001); + BotSyntaxError(1001); debugS("Unknown Command (m...)"); free(bytes); return NULL; } break; case 'n': - // this is a little sketchy, but the principle works. - ++GlobalCurrentChar; - negmode = true; + if(*(GlobalCurrentChar+1) == 'e' + &&*(GlobalCurrentChar+2) == 'v' + &&*(GlobalCurrentChar+3) == 'e' + &&*(GlobalCurrentChar+4) == 'r'|| + *(GlobalCurrentChar+1) == 'o') + { + // never no + GlobalCurrentChar += (*(GlobalCurrentChar+1) == 'e') ? 5 : 2; + bytes[pointer++] = BOT_BYTE_NEVER; + } + else + { + // this is a little sketchy, but the principle works. + ++GlobalCurrentChar; + negmode = true; + } break; case 'r': if(*(GlobalCurrentChar+1) == 'i' @@ -883,7 +922,7 @@ static int * ToByteCode(char * formula) } else { - BotSyntaxError(9001); + BotSyntaxError(1001); debugS("Unknown Command (r...)"); free(bytes); return NULL; @@ -973,12 +1012,28 @@ static int * ToByteCode(char * formula) } else { - BotSyntaxError(12001); + BotSyntaxError(1001); debugS("Unknown Command (v...)"); free(bytes); return NULL; } break; + case 'y': + if(*(GlobalCurrentChar+1) == 'e' + &&*(GlobalCurrentChar+2) == 's') + { + // yes + GlobalCurrentChar += 3; + bytes[pointer++] = BOT_BYTE_MAX; + } + else + { + BotSyntaxError(1001); + debugS("Unknown Command (y...)"); + free(bytes); + return NULL; + } + break; case '0': case '1': case '2': @@ -1222,6 +1277,9 @@ static int Interpreter(bool single) case BOT_BYTE_ECHO: debug(value); break; + case BOT_BYTE_ERROR: + error(value); + break; case BOT_BYTE_FRAME: value = BotFrame; if (single) return value; @@ -1301,6 +1359,10 @@ static int Interpreter(bool single) value <<= Interpreter(true); if (single) return value; break; + case BOT_BYTE_MAX: + value = BOT_MAXPROB; + if (single) return value; + break; case BOT_BYTE_MEM: nextlit = (Interpreter(false) & 0x7FFFFFFF) % 65536; value = ARead[nextlit](nextlit); @@ -1327,6 +1389,10 @@ static int Interpreter(bool single) if (single) return value; break; } + case BOT_BYTE_NEVER: + value = 0; + if (single) return value; + break; case BOT_BYTE_RC: value = (BotCounter[(Interpreter(false) & 0x7FFFFFFF) % 256] = 0); if (single) return 0; @@ -1352,10 +1418,10 @@ static int Interpreter(bool single) if (single) return value; break; case BOT_BYTE_STOP: - // stops the code with an error... - BotSyntaxError(5000); + // stops the code EvaluateError = true; - debugS("Stopped by code"); + MessageBox(hwndBasicBot, "Stopped by code!", "Stop signal", MB_OK); + StopBasicBot(); return 0; case BOT_BYTE_UP: value = 16; @@ -1421,7 +1487,7 @@ static void DebugByteCode(int code[]) **/ void UpdateBasicBot() { - if(hwndBasicBot && BotRunning) + if(hwndBasicBot && BotRunning && !EvaluateError) { // If there is any input on the buffer, dont update yet. // [0] means the number of inputs left on the BotInput buffer @@ -1859,6 +1925,7 @@ static void StartBasicBot() // todo: make sure you are or get into botmode here FCEU_SetBotMode(1); BotRunning = true; + EvaluateError = false; FromGUI(); SetDlgItemText(hwndBasicBot,GUI_BOT_RUN,(LPTSTR)"Stop!"); } @@ -2027,7 +2094,7 @@ static BOOL CALLBACK BasicBotCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARA if(!BotRunning && BotInput[0] == 0) { // feed BotInput the inputs until -1 is reached - for(i = 0; i < (BOT_MAXFRAMES-2) && BestAttempt[i] != -1; i++) + for(i = 0; i < (BOT_MAXFRAMES-3) && BestAttempt[i] != -1; i++) { BotInput[i+2] = BestAttempt[i]; } diff --git a/src/drivers/win/main.cpp b/src/drivers/win/main.cpp index c2fc4c8d..a5ea7cde 100644 --- a/src/drivers/win/main.cpp +++ b/src/drivers/win/main.cpp @@ -377,7 +377,7 @@ void win_AllocBuffers(uint8 **GameMemBlock, uint8 **RAM) { //Bot input // qfox: replaced 4096 by BOT_MAXFRAMES to make it possible to increase the number of frames // the bot can compute in one single attempt. - mapBotInput = CreateFileMapping((HANDLE)0xFFFFFFFF,NULL,PAGE_READWRITE,0, BOT_MAXFRAMES, "fceu.BotInput"); + mapBotInput = CreateFileMapping((HANDLE)0xFFFFFFFF,NULL,PAGE_READWRITE,0, BOT_MAXFRAMES*sizeof(int), "fceu.BotInput"); BotInput = (uint32 *) MapViewOfFile(mapBotInput, FILE_MAP_WRITE, 0, 0, 0); BotInput[0] = 0; }