From e44f7a754791d28542175e6b98273f17b14ad20f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Alexandre=20C=C3=B4t=C3=A9?= Date: Thu, 16 Dec 2021 10:00:21 -0500 Subject: [PATCH] Tidy up the PR --- frotz/Makefile | 4 +- frotz/src/common/frotz.h | 30 +- frotz/src/common/object.c | 50 ---- frotz/src/interface/frotz_interface.c | 399 ++++++++------------------ frotz/src/interface/frotz_interface.h | 3 +- frotz/src/interface/md5.c | 129 +++++---- frotz/src/ztools/infodump.c | 11 - jericho/jericho.py | 126 +++----- jericho/util.py | 92 ------ tools/test_games.py | 32 --- 10 files changed, 222 insertions(+), 654 deletions(-) diff --git a/frotz/Makefile b/frotz/Makefile index 0ad9ad0a..1a363a37 100644 --- a/frotz/Makefile +++ b/frotz/Makefile @@ -10,8 +10,8 @@ RANLIB = /usr/bin/ranlib CC = gcc # Enable compiler warnings. This is an absolute minimum. -#CFLAGS += -Wall -Wextra -std=gnu99 -fPIC -Werror=implicit-function-declaration -CFLAGS += -w +CFLAGS += -Wall -Wextra -std=gnu99 -fPIC -Werror=implicit-function-declaration +CFLAGS = -w # Define your optimization flags. # diff --git a/frotz/src/common/frotz.h b/frotz/src/common/frotz.h index f32c49e2..36da3508 100644 --- a/frotz/src/common/frotz.h +++ b/frotz/src/common/frotz.h @@ -550,32 +550,6 @@ extern char *option_zcode_path; /* dg */ extern long reserve_mem; - -#define MOVE_DIFF_CNT 16 -#define ATTR_SET_CNT 16 -#define ATTR_CLR_CNT 16 -#define PROP_PUT_CNT 64 - -// Keep track of the last n=16 changes to object tree -extern int move_diff_cnt; -extern zword move_diff_objs[MOVE_DIFF_CNT]; -extern zword move_diff_dest[MOVE_DIFF_CNT]; - -// Keep track of the last n=16 changes to obj attributes -extern int attr_diff_cnt; -extern zword attr_diff_objs[ATTR_SET_CNT]; -extern zword attr_diff_nb[ATTR_SET_CNT]; - -// Keep track of the last n=16 clears of obj attributes -extern int attr_clr_cnt; -extern zword attr_clr_objs[ATTR_CLR_CNT]; -extern zword attr_clr_nb[ATTR_CLR_CNT]; - -// Keep track of the last n=32 changes to obj properties -extern int prop_put_cnt; -extern zword prop_put_objs[PROP_PUT_CNT]; -extern zword prop_put_nb[PROP_PUT_CNT]; - // Keep track of up to n=16 changes to special ram locations defined by the game extern int ram_diff_cnt; extern zword ram_diff_addr[16]; @@ -703,7 +677,7 @@ void z_window_style (void); void init_err (void); void runtime_error (int); - + /* Error codes */ #define ERR_TEXT_BUF_OVF 1 /* Text buffer overflow */ #define ERR_STORE_RANGE 2 /* Store out of dynamic memory */ @@ -741,7 +715,7 @@ void runtime_error (int); #define ERR_REMOVE_OBJECT_0 31 /* @remove_object called with object 0 */ #define ERR_GET_NEXT_PROP_0 32 /* @get_next_prop called with object 0 */ #define ERR_NUM_ERRORS (32) - + /* There are four error reporting modes: never report errors; report only the first time a given error type occurs; report every time an error occurs; or treat all errors as fatal diff --git a/frotz/src/common/object.c b/frotz/src/common/object.c index 75afb023..f33fdac9 100644 --- a/frotz/src/common/object.c +++ b/frotz/src/common/object.c @@ -34,19 +34,6 @@ #define O4_PROPERTY_OFFSET 12 #define O4_SIZE 14 -int move_diff_cnt; -zword move_diff_objs[MOVE_DIFF_CNT]; -zword move_diff_dest[MOVE_DIFF_CNT]; -int attr_diff_cnt; -zword attr_diff_objs[ATTR_SET_CNT]; -zword attr_diff_nb[ATTR_SET_CNT]; -int attr_clr_cnt; -zword attr_clr_objs[ATTR_CLR_CNT]; -zword attr_clr_nb[ATTR_CLR_CNT]; -int prop_put_cnt; -zword prop_put_objs[PROP_PUT_CNT]; -zword prop_put_nb[PROP_PUT_CNT]; - /* * object_address * @@ -454,14 +441,6 @@ void z_clear_attr (void) if (zargs[1] > ((h_version <= V3) ? 31 : 47)) runtime_error (ERR_ILL_ATTR); - /* If we are monitoring attribute assignment display a short note */ - - if (attr_clr_cnt < ATTR_CLR_CNT) { - attr_clr_objs[attr_clr_cnt] = zargs[0]; - attr_clr_nb[attr_clr_cnt] = zargs[1]; - attr_clr_cnt++; - } - if (f_setup.attribute_assignment) { stream_mssg_on (); print_string ("@clear_attr "); @@ -1066,14 +1045,6 @@ void z_insert_obj (void) zword obj1_addr; zword obj2_addr; - /* If we are monitoring object movements display a short note */ - - if (move_diff_cnt < MOVE_DIFF_CNT) { - move_diff_objs[move_diff_cnt] = obj1; - move_diff_dest[move_diff_cnt] = obj2; - move_diff_cnt++; - } - if (f_setup.object_movement) { stream_mssg_on (); print_string ("@move_obj "); @@ -1186,12 +1157,6 @@ void z_put_prop (void) SET_WORD (prop_addr, v) } - if (prop_put_cnt < PROP_PUT_CNT) { - prop_put_objs[prop_put_cnt] = zargs[0]; - prop_put_nb[prop_put_cnt] = zargs[1]; - prop_put_cnt++; - } - }/* z_put_prop */ @@ -1204,13 +1169,6 @@ void z_put_prop (void) void z_remove_obj (void) { - /* If we are monitoring object movements display a short note */ - if (move_diff_cnt < 16) { - move_diff_objs[move_diff_cnt] = zargs[0]; - move_diff_dest[move_diff_cnt] = (zword) 0; - move_diff_cnt++; - } - if (f_setup.object_movement) { stream_mssg_on (); print_string ("@remove_obj "); @@ -1244,14 +1202,6 @@ void z_set_attr (void) if (zargs[1] > ((h_version <= V3) ? 31 : 47)) runtime_error (ERR_ILL_ATTR); - /* If we are monitoring attribute assignment display a short note */ - - if (attr_diff_cnt < ATTR_SET_CNT) { - attr_diff_objs[attr_diff_cnt] = zargs[0]; - attr_diff_nb[attr_diff_cnt] = zargs[1]; - attr_diff_cnt++; - } - if (f_setup.attribute_assignment) { stream_mssg_on (); print_string ("@set_attr "); diff --git a/frotz/src/interface/frotz_interface.c b/frotz/src/interface/frotz_interface.c index 0d290970..b563e0c7 100644 --- a/frotz/src/interface/frotz_interface.c +++ b/frotz/src/interface/frotz_interface.c @@ -59,7 +59,7 @@ extern void insert_tree(zword obj1, zword obj2); extern void insert_obj(zword obj1, zword obj2); extern void seed_random (int value); extern void set_random_seed (int seed); -// extern void sum(FILE*, char*); +extern void sum(FILE*, char*); extern void dumb_free(); extern zword first_property (zword); @@ -81,18 +81,17 @@ char world[256 + 8192] = ""; // Upper + lower screens. int emulator_halted = 0; char halted_message[] = "Emulator halted due to runtime error.\n"; -// Track the addresses and values of special per-game ram locations. -int num_special_addrs = 0; -zword *special_ram_addrs = NULL; -zbyte *special_ram_values = NULL; -zobject* old_objs = NULL; -zobject* new_objs = NULL; +// Track the objects. +zobject* prev_objs_state = NULL; +zobject* curr_objs_state = NULL; -int ram_diff_cnt; -zword ram_diff_addr[16]; -zword ram_diff_value[16]; -bool state_has_changed = FALSE; +// Track the value of special per-game ram locations. +zbyte* prev_special_ram = NULL; +zbyte* curr_special_ram = NULL; + +bool objects_state_changed = FALSE; +bool special_ram_changed = FALSE; // Runs a single opcode on the Z-Machine @@ -327,18 +326,11 @@ void shutdown() { reset_memory(); dumb_free(); free_setup(); - if (special_ram_values != NULL) { - free(special_ram_values); - special_ram_values = NULL; - } - if (new_objs != NULL) { - free(new_objs); - new_objs = NULL; - } - if (old_objs != NULL) { - free(old_objs); - old_objs = NULL; - } + + free(prev_objs_state); prev_objs_state = NULL; + free(curr_objs_state); curr_objs_state = NULL; + free(prev_special_ram); prev_special_ram = NULL; + free(curr_special_ram); curr_special_ram = NULL; } // Save the state of the game into a string buffer @@ -1408,18 +1400,6 @@ int halted() { return emulator_halted; } -int ignore_moved_obj(zword obj_num, zword dest_num) { - return (*ignore_moved_obj_fns[ROM_IDX])(obj_num, dest_num); -} - -int ignore_attr_diff(zword obj_num, zword dest_num) { - return (*ignore_attr_diff_fns[ROM_IDX])(obj_num, dest_num); -} - -int ignore_attr_clr(zword obj_num, zword dest_num) { - return (*ignore_attr_clr_fns[ROM_IDX])(obj_num, dest_num); -} - void clean_world_objs(zobject* objs) { return (*clean_world_objs_fns[ROM_IDX])(objs); } @@ -1450,70 +1430,67 @@ void take_intro_actions() { // Returns the number of special ram addresses int get_special_ram_size() { + int num_special_addrs; + get_ram_addrs(&num_special_addrs); return num_special_addrs; } // Returns the current values of the special ram addresses -void get_special_ram(unsigned char *ram) { - int i; - for (i=0; i 0) { - special_ram_values = (zbyte*) malloc(num_special_addrs * sizeof(zbyte)); - } - - // Init objs. - if (old_objs != NULL) free(old_objs); - if (new_objs != NULL) free(new_objs); - - old_objs = calloc(get_num_world_objs() + 1, sizeof(zobject)); - new_objs = calloc(get_num_world_objs() + 1, sizeof(zobject)); -} +// Create memory used to hold the special ram values. +void init_special_ram_tracker() { + free(prev_special_ram); prev_special_ram = NULL; + free(curr_special_ram); curr_special_ram = NULL; + prev_special_ram = malloc(get_special_ram_size() * sizeof(zbyte)); + curr_special_ram = malloc(get_special_ram_size() * sizeof(zbyte)); +} + +// Create memory used to hold the objects' state. +void init_objects_tracker() { + free(prev_objs_state); prev_objs_state = NULL; + free(curr_objs_state); curr_objs_state = NULL; + prev_objs_state = calloc(get_num_world_objs() + 1, sizeof(zobject)); + curr_objs_state = calloc(get_num_world_objs() + 1, sizeof(zobject)); +} + +// // Records changes to the special ram addresses and updates their values. +// void update_ram_diff() { +// int i; +// zbyte curr_ram_value; +// zword addr; +// for (i=0; i 0) return halted_message; - // Swap old_objs and new_objs. - zobject* tmp; - tmp = old_objs; - old_objs = new_objs; - new_objs = tmp; + // Swap prev_objs_state and curr_objs_state. + zobject* tmp_objs_state; + tmp_objs_state = prev_objs_state; + prev_objs_state = curr_objs_state; + curr_objs_state = tmp_objs_state; + + // Swap prev_special_ram and curr_special_ram. + zword* tmp_special_ram; + tmp_special_ram = prev_special_ram; + prev_special_ram = curr_special_ram; + curr_special_ram = tmp_special_ram; // Clear the object, attr, and ram diffs - move_diff_cnt = 0; - attr_diff_cnt = 0; - attr_clr_cnt = 0; - prop_put_cnt = 0; - ram_diff_cnt = 0; - update_special_ram(); + // ram_diff_cnt = 0; + //update_special_ram(); last_ret_pc = getRetPC(); dumb_set_next_action(next_action); @@ -1610,10 +1590,12 @@ char* jericho_step(char *next_action) { run_free(); // Check for changes to special ram - update_ram_diff(); + //update_ram_diff(); update_objs_tracker(); - state_has_changed = memcmp(old_objs, new_objs, (get_num_world_objs() + 1) * sizeof(zobject)) != 0; - // printf("%s =(%d)= %s <== %s", current_state_hash, state_has_changed, last_state_hash, next_action); + update_special_ram_tracker(); + + objects_state_changed = memcmp(prev_objs_state, curr_objs_state, (get_num_world_objs() + 1) * sizeof(zobject)) != 0; + special_ram_changed = memcmp(prev_special_ram, curr_special_ram, get_special_ram_size() * sizeof(zword)) != 0; // Retrieve and concatenate upper and lower screens. strcpy(world, dumb_get_lower_screen()); @@ -1633,60 +1615,20 @@ void set_narrative_text(char* text) { strcpy(world, text); } -bool get_state_changed() { - return state_has_changed; +bool get_objects_state_changed() { + return objects_state_changed; } -void set_state_changed(bool value) { - state_has_changed = value; +void set_objects_state_changed(bool value) { + objects_state_changed = value; } -// Returns a world diff that ignores selected objects -// objs and dest are a 64-length pre-zeroed arrays. -void get_cleaned_world_diff(zword *objs, zword *dest) { - int i; - int j = 0; - for (i=0; i 0 || victory() > 0) { return 1; } - if (ram_diff_cnt > 0) { - return 1; - } + // if (ram_diff_cnt > 0) { + // return 1; + // } - return objs_has_changed || last_ret_pc != getRetPC(); + return objects_state_changed || special_ram_changed || last_ret_pc != getRetPC(); } @@ -1735,7 +1675,6 @@ void get_object(zobject *obj, zword obj_num) { free(buf); } - (*obj).parent = get_parent(obj_num); (*obj).sibling = get_sibling(obj_num); (*obj).child = get_child(obj_num); @@ -1831,7 +1770,10 @@ void get_world_objects(zobject *objs, int clean) { (byte & 0x02 ? '1' : '0'), \ (byte & 0x01 ? '1' : '0') -void print_object2(zobject* obj) { +// Print all information related to a given object. +// Useful for debugging. +void print_zobject(zobject* obj) { + // printf("Obj%d: %s Parent%d Sibling%d Child%d", obj->num, obj->name, obj->parent, obj->sibling, obj->child); printf(" Attributes ["); @@ -1846,114 +1788,27 @@ void print_object2(zobject* obj) { } printf("]\n"); - - - //printf("Obj%d: %s Parent%d Sibling%d Child%d Attributes %s Properties %s\n", obj->num, obj->name, obj->parent, obj->sibling, obj->child, obj->attr, obj->properties); - // ""\ - // .format(self.num, self.name, self.parent, self.sibling, self.child, - // np.nonzero(self.attr)[0].tolist(), [p for p in self.properties if p != 0]) - - // typedef struct { - // unsigned int num; - // char name[64]; - // int parent; - // int sibling; - // int child; - // char attr[4]; - // unsigned char properties[16]; - // } - } - - -int checksum(char* str, int len) { - int i; - int chk = 0x12345678; - - for (i = 0; i != len; i++) { - chk += ((int)(str[i]) * (i + 1)); } - return chk; -} - void get_world_state_hash(char* md5_hash) { - // zobject* obj = calloc(1, sizeof(zobject)); - - // byte digest[16]; - // byte *buf = calloc(1152, 1); - // // byte digest[16]; - // // int i, n; - // MD5state *s = nil; - - // for (int i=1; i<=get_num_world_objs(); ++i) { - // get_object(obj, (zword) i); - // // TODO: call clean_obj function. - // obj->sibling = 0; - // obj->child = 0; - - // //memcpy(buf, obj, sizeof(zobject)); - // s = md5(buf, 1152, 0, s); - // } - // return 0; - - // s = md5(buf, 0, digest, s); - - // for(int j=0;j<16;j++) { - // sprintf(&md5_hash[2*j], "%.2X", digest[j]); - // } - //printf("md5 (obj ): %s\n", md5_hash); - - // for(i=0;i<16;i++) { - // sprintf(&md5_hash[2*i], "%.2X", digest[i]); - // } - - // // Compute MD5 hash. - // s = nil; - // n = 0; - - // //buf = calloc(256,64); - // for (size_t i; i <= sizeof(zobject); i += 128*64) { - // //i = fread(buf+n, 1, 128*64-n, fd); - // if(i <= 0) - // break; - // n += i; - // if(n & 0x3f) - // continue; - // s = md5(buf, n, 0, s); - // n = 0; - // } - // md5(buf, n, digest, s); - // for(i=0;i<16;i++) { - // sprintf(&hash[2*i], "%.2X", digest[i]); - // } - // free(buf); - - // free(buf); - // free(obj); - // return 0; - - //// - int n_objs = get_num_world_objs() + 1; // Add extra spot for zero'th object size_t objs_size = n_objs * sizeof(zobject); - size_t ram_size = get_special_ram_size() * sizeof(unsigned char); - unsigned char* ram = malloc(ram_size); - get_special_ram(ram); int retPC = getRetPC(); size_t state_size = objs_size + ram_size + sizeof(int); char* state = malloc(state_size); - memcpy(state, new_objs, objs_size); - memcpy(&state[objs_size], ram, ram_size); + + memcpy(state, curr_objs_state, objs_size); + memcpy(&state[objs_size], curr_special_ram, ram_size); memcpy(&state[objs_size+ram_size], &retPC, sizeof(int)); FILE* fp = fmemopen(state, state_size, "rb"); sum(fp, md5_hash); fclose(fp); - // printf("md5 (objs): %s\n", md5_hash); + // printf("md5 (objs): %s\n", md5_hash); // For debugging. free(state); } @@ -1969,23 +1824,10 @@ void teleport_tree(zword obj, zword dest) { insert_tree(obj, dest); } -void test() { - int i; - for (i=0; i %d\n", i, move_diff_objs[i], move_diff_dest[i]); - } - for (i=0; i %d\n", i, attr_diff_objs[i], attr_diff_nb[i]); - } - for (i=0; i %d\n", i, attr_clr_objs[i], attr_clr_nb[i]); - } -} - // Given a list of action candidates, find the ones that lead to valid world changes. -// candidate_actions contains a string with all the candidate actions, seperated by ';' -// valid_actions will be written with each of the identified valid actions seperated by ';' -// diff_array will be written with the world_diff for each valid_action indicating +// 'candidate_actions' contains a string with all the candidate actions, seperated by ';' +// 'valid_actions' will be written with each of the identified valid actions seperated by ';' +// 'hashes' will be written with the resulting state hashe for each valid action indicating // which of the valid actions are equivalent to each other in terms of their state hashes. // Returns the number of valid actions found. int filter_candidate_actions(char *candidate_actions, char *valid_actions, char *hashes) { @@ -1993,35 +1835,28 @@ int filter_candidate_actions(char *candidate_actions, char *valid_actions, char char *act = NULL; char *act_newline = NULL; char *text; - short orig_score; int valid_cnt = 0; int v_idx = 0; // Variables used to store & reset game state unsigned char ram_cpy[h_dynamic_size]; unsigned char stack_cpy[STACK_SIZE*sizeof(zword)]; - int pc_cpy; - int sp_cpy; - int fp_cpy; - int next_opcode_cpy; - int frame_count_cpy; - long rngA_cpy; - int rngInterval_cpy; - int rngCounter_cpy; // Save the game state getRAM(ram_cpy); getStack(stack_cpy); - pc_cpy = getPC(); - sp_cpy = getSP(); - fp_cpy = getFP(); - next_opcode_cpy = get_opcode(); - frame_count_cpy = getFrameCount(); - rngA_cpy = getRngA(); - rngInterval_cpy = getRngInterval(); - rngCounter_cpy = getRngCounter(); - - orig_score = get_score(); + int pc_cpy = getPC(); + int sp_cpy = getSP(); + int fp_cpy = getFP(); + int next_opcode_cpy = get_opcode(); + int frame_count_cpy = getFrameCount(); + long rngA_cpy = getRngA(); + int rngInterval_cpy = getRngInterval(); + int rngCounter_cpy = getRngCounter(); + bool objects_state_changed_cpy = get_objects_state_changed(); + bool special_ram_changed_cpy = get_special_ram_changed(); + + short orig_score = get_score(); act = strtok(candidate_actions, ";"); while (act != NULL) @@ -2061,9 +1896,13 @@ int filter_candidate_actions(char *candidate_actions, char *valid_actions, char set_opcode(next_opcode_cpy); setFrameCount(frame_count_cpy); update_objs_tracker(); + update_special_ram_tracker(); + set_objects_state_changed(objects_state_changed_cpy); + set_special_ram_changed(special_ram_changed_cpy); act = strtok(NULL, ";"); free(act_newline); } + return valid_cnt; } diff --git a/frotz/src/interface/frotz_interface.h b/frotz/src/interface/frotz_interface.h index b4894ede..50a914bc 100644 --- a/frotz/src/interface/frotz_interface.h +++ b/frotz/src/interface/frotz_interface.h @@ -51,10 +51,9 @@ extern int getRAMSize(); extern void getRAM(unsigned char *ram); -// int filter_candidate_actions(char *candidate_actions, char *valid_actions, zword *diff_array); int filter_candidate_actions(char *candidate_actions, char *valid_actions, char *hashes); -extern char world[256 + 8192]; +extern char world[256 + 8192]; // Upper + lower screens. extern int tw_max_score; diff --git a/frotz/src/interface/md5.c b/frotz/src/interface/md5.c index 8ddd64a2..97f7f010 100644 --- a/frotz/src/interface/md5.c +++ b/frotz/src/interface/md5.c @@ -1,6 +1,5 @@ #include #include -// #include #include "md5.h" extern int enc64(char*,byte*,int); @@ -68,76 +67,76 @@ typedef struct Table Table tab[] = { /* round 1 */ - { 0xd76aa478, 0, S11}, - { 0xe8c7b756, 1, S12}, - { 0x242070db, 2, S13}, - { 0xc1bdceee, 3, S14}, - { 0xf57c0faf, 4, S11}, - { 0x4787c62a, 5, S12}, - { 0xa8304613, 6, S13}, - { 0xfd469501, 7, S14}, - { 0x698098d8, 8, S11}, - { 0x8b44f7af, 9, S12}, - { 0xffff5bb1, 10, S13}, - { 0x895cd7be, 11, S14}, - { 0x6b901122, 12, S11}, - { 0xfd987193, 13, S12}, - { 0xa679438e, 14, S13}, + { 0xd76aa478, 0, S11}, + { 0xe8c7b756, 1, S12}, + { 0x242070db, 2, S13}, + { 0xc1bdceee, 3, S14}, + { 0xf57c0faf, 4, S11}, + { 0x4787c62a, 5, S12}, + { 0xa8304613, 6, S13}, + { 0xfd469501, 7, S14}, + { 0x698098d8, 8, S11}, + { 0x8b44f7af, 9, S12}, + { 0xffff5bb1, 10, S13}, + { 0x895cd7be, 11, S14}, + { 0x6b901122, 12, S11}, + { 0xfd987193, 13, S12}, + { 0xa679438e, 14, S13}, { 0x49b40821, 15, S14}, /* round 2 */ - { 0xf61e2562, 1, S21}, - { 0xc040b340, 6, S22}, - { 0x265e5a51, 11, S23}, - { 0xe9b6c7aa, 0, S24}, - { 0xd62f105d, 5, S21}, - { 0x2441453, 10, S22}, - { 0xd8a1e681, 15, S23}, - { 0xe7d3fbc8, 4, S24}, - { 0x21e1cde6, 9, S21}, - { 0xc33707d6, 14, S22}, - { 0xf4d50d87, 3, S23}, - { 0x455a14ed, 8, S24}, - { 0xa9e3e905, 13, S21}, - { 0xfcefa3f8, 2, S22}, - { 0x676f02d9, 7, S23}, + { 0xf61e2562, 1, S21}, + { 0xc040b340, 6, S22}, + { 0x265e5a51, 11, S23}, + { 0xe9b6c7aa, 0, S24}, + { 0xd62f105d, 5, S21}, + { 0x2441453, 10, S22}, + { 0xd8a1e681, 15, S23}, + { 0xe7d3fbc8, 4, S24}, + { 0x21e1cde6, 9, S21}, + { 0xc33707d6, 14, S22}, + { 0xf4d50d87, 3, S23}, + { 0x455a14ed, 8, S24}, + { 0xa9e3e905, 13, S21}, + { 0xfcefa3f8, 2, S22}, + { 0x676f02d9, 7, S23}, { 0x8d2a4c8a, 12, S24}, /* round 3 */ - { 0xfffa3942, 5, S31}, - { 0x8771f681, 8, S32}, - { 0x6d9d6122, 11, S33}, - { 0xfde5380c, 14, S34}, - { 0xa4beea44, 1, S31}, - { 0x4bdecfa9, 4, S32}, - { 0xf6bb4b60, 7, S33}, - { 0xbebfbc70, 10, S34}, - { 0x289b7ec6, 13, S31}, - { 0xeaa127fa, 0, S32}, - { 0xd4ef3085, 3, S33}, - { 0x4881d05, 6, S34}, - { 0xd9d4d039, 9, S31}, - { 0xe6db99e5, 12, S32}, - { 0x1fa27cf8, 15, S33}, - { 0xc4ac5665, 2, S34}, + { 0xfffa3942, 5, S31}, + { 0x8771f681, 8, S32}, + { 0x6d9d6122, 11, S33}, + { 0xfde5380c, 14, S34}, + { 0xa4beea44, 1, S31}, + { 0x4bdecfa9, 4, S32}, + { 0xf6bb4b60, 7, S33}, + { 0xbebfbc70, 10, S34}, + { 0x289b7ec6, 13, S31}, + { 0xeaa127fa, 0, S32}, + { 0xd4ef3085, 3, S33}, + { 0x4881d05, 6, S34}, + { 0xd9d4d039, 9, S31}, + { 0xe6db99e5, 12, S32}, + { 0x1fa27cf8, 15, S33}, + { 0xc4ac5665, 2, S34}, /* round 4 */ - { 0xf4292244, 0, S41}, - { 0x432aff97, 7, S42}, - { 0xab9423a7, 14, S43}, - { 0xfc93a039, 5, S44}, - { 0x655b59c3, 12, S41}, - { 0x8f0ccc92, 3, S42}, - { 0xffeff47d, 10, S43}, - { 0x85845dd1, 1, S44}, - { 0x6fa87e4f, 8, S41}, - { 0xfe2ce6e0, 15, S42}, - { 0xa3014314, 6, S43}, - { 0x4e0811a1, 13, S44}, - { 0xf7537e82, 4, S41}, - { 0xbd3af235, 11, S42}, - { 0x2ad7d2bb, 2, S43}, - { 0xeb86d391, 9, S44}, + { 0xf4292244, 0, S41}, + { 0x432aff97, 7, S42}, + { 0xab9423a7, 14, S43}, + { 0xfc93a039, 5, S44}, + { 0x655b59c3, 12, S41}, + { 0x8f0ccc92, 3, S42}, + { 0xffeff47d, 10, S43}, + { 0x85845dd1, 1, S44}, + { 0x6fa87e4f, 8, S41}, + { 0xfe2ce6e0, 15, S42}, + { 0xa3014314, 6, S43}, + { 0x4e0811a1, 13, S44}, + { 0xf7537e82, 4, S41}, + { 0xbd3af235, 11, S42}, + { 0x2ad7d2bb, 2, S43}, + { 0xeb86d391, 9, S44}, }; int debug; @@ -230,7 +229,7 @@ md5(byte *p, uint len, byte *digest, MD5state *s) d = s->state[3]; decode(x, p, 64); - + for(i = 0; i < 64; i++){ t = tab + i; switch(i>>4){ @@ -250,7 +249,7 @@ md5(byte *p, uint len, byte *digest, MD5state *s) a += x[t->x] + t->sin; a = (a << t->rot) | (a >> (32 - t->rot)); a += b; - + /* rotate variables */ tmp = d; d = c; diff --git a/frotz/src/ztools/infodump.c b/frotz/src/ztools/infodump.c index 9330d476..ea0b91db 100644 --- a/frotz/src/ztools/infodump.c +++ b/frotz/src/ztools/infodump.c @@ -256,17 +256,6 @@ void print_verbs (const char *name) load_cache (); fix_dictionary (); show_verbs (0); - show_objects (0); - show_header(); - close_story (); -} - -void print_action_table (const char *name) -{ - open_story (name); - configure (V1, V8); - load_cache (); - fix_dictionary (); close_story (); } diff --git a/jericho/jericho.py b/jericho/jericho.py index 03a3a361..f4b6a68e 100644 --- a/jericho/jericho.py +++ b/jericho/jericho.py @@ -268,8 +268,6 @@ def _load_frotz_lib(): frotz_lib.get_world_state_hash.argtypes = [] frotz_lib.get_world_state_hash.restype = int - frotz_lib.get_cleaned_world_diff.argtypes = [c_void_p, c_void_p] - frotz_lib.get_cleaned_world_diff.restype = None frotz_lib.game_over.argtypes = [] frotz_lib.game_over.restype = int frotz_lib.victory.argtypes = [] @@ -295,10 +293,15 @@ def _load_frotz_lib(): frotz_lib.set_narrative_text.argtypes = [c_char_p] frotz_lib.set_narrative_text.restype = None - frotz_lib.get_state_changed.argtypes = [] - frotz_lib.get_state_changed.restype = int - frotz_lib.set_state_changed.argtypes = [c_int] - frotz_lib.set_state_changed.restype = None + frotz_lib.get_objects_state_changed.argtypes = [] + frotz_lib.get_objects_state_changed.restype = int + frotz_lib.set_objects_state_changed.argtypes = [c_int] + frotz_lib.set_objects_state_changed.restype = None + + frotz_lib.get_special_ram_changed.argtypes = [] + frotz_lib.get_special_ram_changed.restype = int + frotz_lib.set_special_ram_changed.argtypes = [c_int] + frotz_lib.set_special_ram_changed.restype = None frotz_lib.getPC.argtypes = [] frotz_lib.getPC.restype = int @@ -364,6 +367,9 @@ def _load_frotz_lib(): frotz_lib.update_objs_tracker.argtypes = [] frotz_lib.update_objs_tracker.restype = None + frotz_lib.update_special_ram_tracker.argtypes = [] + frotz_lib.update_special_ram_tracker.restype = None + return frotz_lib @@ -502,7 +508,7 @@ def step(self, action): score = self.frotz_lib.get_score() reward = score - old_score return next_state, reward, (self.game_over() or self.victory()),\ - {'moves':self.get_moves(), 'score':score, 'victory': self.victory()} + {'moves':self.get_moves(), 'score':score} def close(self): ''' Cleans up the FrotzEnv, freeing any allocated memory. ''' @@ -585,8 +591,8 @@ def get_valid_actions(self, use_object_tree=True, use_ctypes=True, use_parallel= interactive_objs = self._identify_interactive_objects(use_object_tree=use_object_tree) best_obj_names = self._score_object_names(interactive_objs) candidate_actions = self.act_gen.generate_actions(best_obj_names) - diff2acts = self._filter_candidate_actions(candidate_actions, use_ctypes, use_parallel) - valid_actions = [max(v, key=utl.verb_usage_count) for v in diff2acts.values()] + hash2acts = self._filter_candidate_actions(candidate_actions, use_ctypes, use_parallel) + valid_actions = [max(v, key=utl.verb_usage_count) for v in hash2acts.values()] return valid_actions def set_state(self, state): @@ -605,7 +611,7 @@ def set_state(self, state): >>> env.set_state(state) # Whew, let's try something else ''' - ram, stack, pc, sp, fp, frame_count, opcode, rng, narrative, state_changed = state + ram, stack, pc, sp, fp, frame_count, opcode, rng, narrative, objs_state_changed, special_ram_changed = state self.frotz_lib.setRng(*rng) self.frotz_lib.setRAM(as_ctypes(ram)) self.frotz_lib.setStack(as_ctypes(stack)) @@ -615,8 +621,10 @@ def set_state(self, state): self.frotz_lib.set_opcode(opcode) self.frotz_lib.setFrameCount(frame_count) self.frotz_lib.set_narrative_text(narrative) - self.frotz_lib.set_state_changed(state_changed) + self.frotz_lib.set_objects_state_changed(objs_state_changed) + self.frotz_lib.set_special_ram_changed(special_ram_changed) self.frotz_lib.update_objs_tracker() + self.frotz_lib.update_special_ram_tracker() def get_state(self): ''' @@ -644,8 +652,9 @@ def get_state(self): frame_count = self.frotz_lib.getFrameCount() rng = self.frotz_lib.getRngA(), self.frotz_lib.getRngInterval(), self.frotz_lib.getRngCounter() narrative = self.frotz_lib.get_narrative_text() - state_changed = self.frotz_lib.get_state_changed() - state = ram, stack, pc, sp, fp, frame_count, opcode, rng, narrative, state_changed + objs_state_changed = self.frotz_lib.get_objects_state_changed() + special_ram_changed = self.frotz_lib.get_special_ram_changed() + state = ram, stack, pc, sp, fp, frame_count, opcode, rng, narrative, objs_state_changed, special_ram_changed return state def get_calls_stack(self): @@ -770,25 +779,6 @@ def get_world_state_hash(self): md5_hash = (b"").join(md5_hash.view(c_char)).decode('cp1252') return md5_hash - def get_world_state_hash_old(self): - - import hashlib - world_objs_str = ', '.join([str(o) for o in self.get_world_objects(clean=True)]) - special_ram_addrs = self._get_special_ram() - - ram, stack, pc, sp, fp, frame_count, opcode, rng, narrative = self.get_state() - # Return address of current routine call. (See frotz/src/common/process.c:569) - stack = stack.view("uint16") - ret_pc = stack[fp+2] | (stack[fp+2+1] << 9) - - # print(sum(stack), pc, sp, fp, ret_pc, frame_count, opcode) - # print(pc, str(special_ram_addrs) + str(ret_pc)) - - world_state_str = world_objs_str + str(special_ram_addrs) + str(ret_pc) - m = hashlib.md5() - m.update(world_state_str.encode('utf-8')) - return m.hexdigest() - def get_moves(self): ''' Returns the integer number of moves taken by the player in the current episode. ''' return self.frotz_lib.get_moves() @@ -831,51 +821,6 @@ def _world_changed(self): ''' return self.frotz_lib.world_changed() > 0 - def _get_world_diff(self): - ''' - Returns the difference in the world object tree, set attributes, and\ - cleared attributes for the last timestep. - - :returns: A Tuple of tuples containing 1) tuple of world objects that \ - have moved in the Object Tree, 2) tuple of objects whose attributes have\ - changed, 3) tuple of world objects whose attributes have been cleared, and - 4) tuple of special ram locations whose values have changed. - ''' - objs = np.zeros(64+64, dtype=np.uint16) - dest = np.zeros(64+64, dtype=np.uint16) - self.frotz_lib.get_cleaned_world_diff(as_ctypes(objs), as_ctypes(dest)) - # First 16 spots allocated for objects that have moved - moved_objs = [] - for i in range(16): - if objs[i] == 0 and dest[i] == 0: - break - moved_objs.append((objs[i], dest[i])) - # Second 16 spots allocated for objects that have had attrs set - set_attrs = [] - for i in range(16, 32): - if objs[i] == 0 or dest[i] == 0: - break - set_attrs.append((objs[i], dest[i])) - # Third 16 spots allocated for objects that have had attrs cleared - cleared_attrs = [] - for i in range(32, 48): - if objs[i] == 0 or dest[i] == 0: - break - cleared_attrs.append((objs[i], dest[i])) - # Fourth 16 spots allocated to ram locations & values that have changed - prop_diffs = [] - for i in range(48, 48+64): - if objs[i] == 0: - break - prop_diffs.append((objs[i], dest[i])) - # Fourth 16 spots allocated to ram locations & values that have changed - ram_diffs = [] - for i in range(48+64, 48+64+16): - if objs[i] == 0: - break - ram_diffs.append((objs[i], dest[i])) - return (tuple(moved_objs), tuple(set_attrs), tuple(cleared_attrs), tuple(prop_diffs), tuple(ram_diffs)) - def _score_object_names(self, interactive_objs): """ Attempts to choose a sensible name for an object, typically a noun. """ def score_fn(obj): @@ -976,21 +921,18 @@ def _identify_interactive_objects(self, observation='', use_object_tree=False): desc2obj = {} # Filter out objs that aren't examinable name2desc = {} - for obj in objs: if obj[0] in name2desc: ex = name2desc[obj[0]] else: self.set_state(state) ex = utl.clean(self.step('examine ' + obj[0])[0]) - name2desc[obj[0]] = ex if utl.recognized(ex): if ex in desc2obj: desc2obj[ex].append(obj) else: desc2obj[ex] = [obj] - # Add 'all' as a wildcard object desc2obj['all'] = [('all', 'NOUN', 'LOC')] self.set_state(state) @@ -998,9 +940,9 @@ def _identify_interactive_objects(self, observation='', use_object_tree=False): def _filter_candidate_actions(self, candidate_actions, use_ctypes=False, use_parallel=False): """ - Given a list of candidate actions, returns a dictionary mapping world_diff - to the list of candidate actions that cause this diff. Only actions that - cause a valid world diff are returned. + Given a list of candidate actions, returns a dictionary mapping state hashes + to the list of candidate actions that cause this state change. Only actions that + cause a valid world change are returned. :param candidate_actions: Candidate actions to test for validity. :type candidate_actions: list @@ -1008,7 +950,7 @@ def _filter_candidate_actions(self, candidate_actions, use_ctypes=False, use_par :type use_ctypes: boolean :param use_parallel: Uses the parallized implementation of valid action filtering. :type use_parallel: boolean - :returns: Dictionary of world_diff to list of actions. + :returns: Dictionary of state hash to list of actions. """ if self.game_over() or self.victory() or self._emulator_halted(): @@ -1017,7 +959,7 @@ def _filter_candidate_actions(self, candidate_actions, use_ctypes=False, use_par candidate_actions = [act.action if isinstance(act, defines.TemplateAction) else act for act in candidate_actions] state = self.get_state() - diff2acts = defaultdict(list) + hash2acts = defaultdict(list) if use_parallel: state = self.get_state() @@ -1025,10 +967,10 @@ def _filter_candidate_actions(self, candidate_actions, use_ctypes=False, use_par self.pool = mp.Pool(initializer=init_worker, initargs=(self.story_file.decode(), self._seed)) args = [(state, actions, use_ctypes) for actions in chunk(candidate_actions, n=self.pool._processes)] - list_diff2acts = self.pool.map(worker, args) - for d in list_diff2acts: + list_hash2acts = self.pool.map(worker, args) + for d in list_hash2acts: for k, v in d.items(): - diff2acts[k].extend(v) + hash2acts[k].extend(v) elif use_ctypes: candidate_str = (";".join(candidate_actions)).encode('utf-8') @@ -1045,7 +987,7 @@ def _filter_candidate_actions(self, candidate_actions, use_ctypes=False, use_par valid_acts = valid_str.decode('cp1252').strip().split(';')[:-1] for i in range(valid_cnt): - diff2acts[hashes[i]].append(valid_acts[i]) + hash2acts[hashes[i].decode('cp1252')].append(valid_acts[i]) else: orig_score = self.get_score() @@ -1062,11 +1004,11 @@ def _filter_candidate_actions(self, candidate_actions, use_ctypes=False, use_par if '(Taken)' in obs: continue - diff = self._get_world_diff() - diff2acts[diff].append(act) + hash = self.get_world_state_hash() + hash2acts[hash].append(act) self.set_state(state) - return diff2acts + return hash2acts def _get_ram(self): """ diff --git a/jericho/util.py b/jericho/util.py index 0c973091..d32a80c3 100644 --- a/jericho/util.py +++ b/jericho/util.py @@ -14,12 +14,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import io import os -import sys -import time -import threading - from typing import List from . import defines @@ -231,90 +226,3 @@ def chunk(items: List, n: int) -> List[List]: """ k, m = divmod(len(items), n) return list(items[i*k+min(i, m):(i+1)*k+min(i+1, m)] for i in range(n)) - - -class CaptureStdout: - """ - Capture the standard output stream from Python and shared libraries. - - References - ---------- - https://stackoverflow.com/a/29834357 - """ - escape_char = b"\b" - - def __init__(self, threaded=True): - self.threaded = threaded - self._stdout_fd = os.dup(1) # Keep a copy of the handle. - self._stdout_io = sys.stdout # Keep a reference to the text buffer. - - self.text_c = "" - self.text_py = "" - - # Create a pipe so the stream can be captured: - self.pipe_out, self.pipe_in = os.pipe() - - def __enter__(self): - self.start() - return self - - def __exit__(self, type, value, traceback): - self.stop() - - def start(self): - """ - Start capturing the stream data. - """ - - self.text_c = "" - sys.stdout = io.StringIO() - - # Replace the original stream with our write pipe: - os.dup2(self.pipe_in, 1) - if self.threaded: - # Start thread that will read the stream: - self.workerThread = threading.Thread(target=self.readOutput) - self.workerThread.start() - # Make sure that the thread is running and os.read() has executed: - time.sleep(0.01) - - def stop(self): - """ - Stop capturing the stream data and save the text in `capturedtext`. - """ - # Print the escape character to make the readOutput method stop: - os.write(self.pipe_in, self.escape_char) - - if self.threaded: - # wait until the thread finishes so we are sure that - # we have until the last character: - self.workerThread.join() - else: - self.readOutput() - - # Close the pipe - os.close(self.pipe_in) - os.close(self.pipe_out) - os.dup2(self._stdout_fd, 1) - - sys.stdout.seek(0) - self.text_py = sys.stdout.read() - - sys.stdout = self._stdout_io - - def readOutput(self): - """ - Read the stream data (one byte at a time) - and save the text in `capturedtext`. - """ - while True: - char = os.read(self.pipe_out, 1) - if not char or self.escape_char in char: - break - - try: - self.text_c += char.decode(errors='replace') - except UnicodeDecodeError: - pass - #self.text_c += str(char) - #self.text_c += f"*** UnicodeDecodeError {str(char)} at c{len(self.text_c)}!" diff --git a/tools/test_games.py b/tools/test_games.py index 09fd87ec..d54cbaa9 100644 --- a/tools/test_games.py +++ b/tools/test_games.py @@ -704,21 +704,11 @@ def parse_args(): skip = True break - # world_diff = env_._get_world_diff() - # moved_objs, set_attrs, cleared_attrs, changed_props, _ = world_diff - # skip = False - # for obj in moved_objs + set_attrs + cleared_attrs + changed_props: - # if obj[0] in SKIP_PRECHECK_STATE.get(rom, {}).get("ignore_objects", []): - # skip = True - # break - if skip: break if env_._world_changed(): # if last_hash != env_.get_world_state_hash(): - # env_objs = env.get_world_objects(clean=True) - # env_objs_ = env_.get_world_objects(clean=True) objs1 = env.get_world_objects(clean=False) objs2 = env_.get_world_objects(clean=False) @@ -805,28 +795,6 @@ def parse_args(): breakpoint() - # else: - # world_diff = env._get_world_diff() - # if len(world_diff[-1]) > 1: - # print(colored("Multiple special RAM addressed have been triggered!", 'yellow')) - # print(f"RAM: {world_diff[-1]}") - # breakpoint() - - # else: - # changed_objs = [(o1, o2) for o1, o2 in zip(env_objs_cleaned, last_env_objs_cleaned) if o1 != o2] - - # world_diff = env._get_world_diff() - # if len(changed_objs) > 0 and len(world_diff[-1]) > 0: - # print(colored("Special RAM address triggered as well as object modifications!", 'yellow')) - # print(colored("Is the special RAM address really needed, then?", 'yellow')) - # print(f"RAM: {world_diff[-1]}") - # for o1, o2 in changed_objs: - # print(o2) # New - # print(o1) # Old - - # breakpoint() - - if not env.victory(): print(colored("FAIL", 'red')) if args.debug: