From 31f9808a93597adecd17232d595bc4ca7106ebaa Mon Sep 17 00:00:00 2001 From: Aly Date: Mon, 8 Mar 2021 19:55:25 -0800 Subject: [PATCH] Initial commit --- .gitignore | 1 + README.md | 36 ++++++++++++ dolphin.ml | 103 ++++++++++++++++++++++++++++++++++ main.ml | 20 +++++++ pretty.ml | 5 ++ save_context.ml | 143 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 308 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 dolphin.ml create mode 100644 main.ml create mode 100644 pretty.ml create mode 100644 save_context.ml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5bddc53 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +main.lua diff --git a/README.md b/README.md new file mode 100644 index 0000000..6c460e1 --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +# ootAI + +An experiment to create an AI that plays OoT Randomizers automatically, getting +as many checks as possible in an efficient manner. + +## how does it work + +This runs using the Lua Core fork of the Dolphin Emulator project. As of right +now it only works on ROMs generated by the OoT Randomizer. + +Eventually I'll have an explanation here on how it does pathfinding and memory +reading, but I don't know how to do enough of that yet. + +## how to build + +1. Install [AmuletML](https://amulet.works/). +2. Run `amc compile main.ml -o main.lua --lib /path/to/amulet/lib` +3. Copy everything between the first `do` and the last `end` from `main.lua` +4. Create a new script in the Dolphin Lua Core Scripts directory: + +```lua +function onScriptStart() end + +function onScriptCancel() end + +function onScriptUpdate() + -- paste what you copied here +end + +function onStateLoaded() end + +function onStateSaved() end +``` + +This script should now run properly when you start it from the Dolphin scripts +menu. diff --git a/dolphin.ml b/dolphin.ml new file mode 100644 index 0000000..a8039c8 --- /dev/null +++ b/dolphin.ml @@ -0,0 +1,103 @@ +open import "prelude.ml" + +type u8 = + U8 of int + +instance show u8 begin + let show (U8 x) = (show x) ^ "_u8" +end + +type s8 = + S8 of int + +instance show s8 begin + let show (S8 x) = + let signfix = + if x > 127 then + negate (256 - x) + else + x + (show signfix) ^ "_s8" +end + +type u16 = + U16 of int + +instance show u16 begin + let show (U16 x) = (show x) ^ "_u16" +end + +type s16 = + S16 of int + +instance show s16 begin + let show (S16 x) = + let signfix = + if x > 32767 then + negate (65536 - x) + else + x + (show signfix) ^ "_s16" +end + +type u32 = + U32 of int + +type s32 = + S32 of int + +instance show s32 begin + let show (S32 x) = + let signfix = + if x > 2147483647 then + negate (4294967296 - x) + else + x + (show signfix) ^ "_s32" +end + +module Dolphin = struct + external val read_value_8 : int -> () -> int = + "function(addr, n) return ReadValue8(addr) end" + external val read_value_16 : int -> () -> int = + "function(addr, n) return ReadValue16(addr) end" + external val read_value_32 : int -> () -> int = + "function(addr, n) return ReadValue32(addr) end" + external val read_value_float : int -> () -> float = + "function(addr, n) return ReadValueFloat(addr) end" + external val read_value_string : int -> int -> () -> string = + "function(addr, len, n) return ReadValueString(addr, len) end" + + external val msg_box : string -> int -> () -> () = + "function(message, delay, n) MsgBox(message, delay) end" + external val set_screen_text : string -> () -> () = + "function(message, n) SetScreenText(message) end" +end + +class decode 'a begin + val decode : int -> () -> 'a +end + +instance decode u8 begin + let decode addr x = U8 (Dolphin.read_value_8 addr x) +end + +instance decode s8 begin + let decode addr x = S8 (Dolphin.read_value_8 addr x) +end + +instance decode u16 begin + let decode addr x = U16 (Dolphin.read_value_16 addr x) +end + +instance decode s16 begin + let decode addr x = S16 (Dolphin.read_value_16 addr x) +end + +instance decode u32 begin + let decode addr x = U32 (Dolphin.read_value_32 addr x) +end + +instance decode s32 begin + let decode addr x = S32 (Dolphin.read_value_32 addr x) +end diff --git a/main.ml b/main.ml new file mode 100644 index 0000000..1cd8c81 --- /dev/null +++ b/main.ml @@ -0,0 +1,20 @@ +open import "prelude.ml" + +open import "./save_context.ml" + +open import "./pretty.ml" +open import "./dolphin.ml" + +let base_address = 0xf64120 + +let g_save_ctx_address = 0x11a5d0 + +let decode_save_ctx : () -> save_context = decode (base_address + g_save_ctx_address) + +let main x = + let save_ctx = decode_save_ctx x + let display_save_ctx = "Save Context: " ^ (pretty save_ctx 0) + Dolphin.set_screen_text display_save_ctx x + +let () = + main () diff --git a/pretty.ml b/pretty.ml new file mode 100644 index 0000000..c20eadc --- /dev/null +++ b/pretty.ml @@ -0,0 +1,5 @@ +open import "prelude.ml" + +class pretty 'a begin + val pretty : 'a -> int -> string +end diff --git a/save_context.ml b/save_context.ml new file mode 100644 index 0000000..5230d7d --- /dev/null +++ b/save_context.ml @@ -0,0 +1,143 @@ +open import "prelude.ml" + +open import "./pretty.ml" + +open import "./dolphin.ml" + +type item_equips = ItemEquips of { + button_item_b: u8, + button_item_c_left: u8, + button_item_c_down: u8, + button_item_c_right: u8, + button_slot_c_left: u8, + button_slot_c_down: u8, + button_slot_c_right: u8, + equipment: u16 +} + +instance decode item_equips begin + let decode addr x = ItemEquips { + button_item_b = decode (addr + 0x00) x, + button_item_c_left = decode (addr + 0x01) x, + button_item_c_down = decode (addr + 0x02) x, + button_item_c_right = decode (addr + 0x03) x, + button_slot_c_left = decode (addr + 0x04) x, + button_slot_c_down = decode (addr + 0x05) x, + button_slot_c_right = decode (addr + 0x06) x, + equipment = decode (addr + 0x08) x + } +end + +instance pretty item_equips begin + let pretty (ItemEquips item_equips) w = + let rec smul s n = + if n <= 0 then + "" + else + s ^ (smul s (n - 1)) + let prep = smul " " w + let header = "ItemEquips {\n" + let button_item_b = header ^ prep ^ " button_item_b = " ^ (show item_equips.button_item_b) ^ ",\n" + let button_item_c_left = button_item_b ^ prep ^ " button_item_c_left = " ^ (show item_equips.button_item_c_left) ^ ",\n" + let button_item_c_down = button_item_c_left ^ prep ^ " button_item_c_down = " ^ (show item_equips.button_item_c_down) ^ ",\n" + let button_item_c_right = button_item_c_down ^ prep ^ " button_item_c_right = " ^ (show item_equips.button_item_c_right) ^ ",\n" + let button_slot_c_left = button_item_c_right ^ prep ^ " button_slot_c_left = " ^ (show item_equips.button_slot_c_left) ^ ",\n" + let button_slot_c_down = button_slot_c_left ^ prep ^ " button_slot_c_down = " ^ (show item_equips.button_slot_c_down) ^ ",\n" + let button_slot_c_right = button_slot_c_down ^ prep ^ " button_slot_c_right = " ^ (show item_equips.button_slot_c_right) ^ ",\n" + let equipment = button_slot_c_right ^ prep ^ " equipment = " ^ (show item_equips.equipment) ^ ",\n" + equipment ^ prep ^ "}" +end + +type save_context = SaveContext of { + entrance_index: s32, + link_age: s32, + cutscene_index: s32, + day_time: u16, + night_flag: s32, + num_days: s32, + claim_days: s32, + (* char newf[6], *) + deaths: s16, + (* char playerName[8], *) + n64dd_flag: s16, + health_capacity: s16, + health: s16, + magic_level: s8, + magic: s8, + rupees: s16, + sword_health: u16, + navi_timer: u16, + magic_acquired: u8, + (* char unk_3B[0x01], *) + double_magic: u8, + double_defense: u8, + bgs_flag: u8, + ocarina_game_reward: u8, + child_equips: item_equips, + adult_equips: item_equips +} + +instance decode save_context begin + let decode addr x = SaveContext { + entrance_index = decode (addr + 0x0000) x, + link_age = decode (addr + 0x0004) x, + cutscene_index = decode (addr + 0x0008) x, + day_time = decode (addr + 0x000C) x, + night_flag = decode (addr + 0x0010) x, + num_days = decode (addr + 0x0014) x, + claim_days = decode (addr + 0x0018) x, + (* newf, *) + deaths = decode (addr + 0x0022) x, + n64dd_flag = decode (addr + 0x002C) x, + health_capacity = decode (addr + 0x002E) x, + health = decode (addr + 0x0030) x, + magic_level = decode (addr + 0x0032) x, + magic = decode (addr + 0x0033) x, + rupees = decode (addr + 0x0034) x, + sword_health = decode (addr + 0x0036) x, + navi_timer = decode (addr + 0x0038) x, + magic_acquired = decode (addr + 0x003A) x, + double_magic = decode (addr + 0x003C) x, + double_defense = decode (addr + 0x003D) x, + bgs_flag = decode (addr + 0x003E) x, + ocarina_game_reward = decode (addr + 0x003F) x, + child_equips = decode (addr + 0x0040) x, + adult_equips = decode (addr + 0x004A) x + } +end + +instance pretty save_context begin + let pretty (SaveContext save_context) w = + let rec smul s n = + if n <= 0 then + "" + else + s ^ (smul s (n - 1)) + let x = w + 2 + let prep = smul " " w + let header = "SaveContext {\n" + let entrance_index = header ^ prep ^ " entrance_index = " ^ (show save_context.entrance_index) ^ ",\n" + let link_age = entrance_index ^ prep ^ " link_age = " ^ (show save_context.link_age) ^ ",\n" + let cutscene_index = link_age ^ prep ^ " cutscene_index = " ^ (show save_context.cutscene_index) ^ ",\n" + let day_time = cutscene_index ^ prep ^ " day_time = " ^ (show save_context.day_time) ^ ",\n" + let night_flag = day_time ^ prep ^ " night_flag = " ^ (show save_context.night_flag) ^ ",\n" + let num_days = night_flag ^ prep ^ " num_days = " ^ (show save_context.num_days) ^ ",\n" + let claim_days = num_days ^ prep ^ " claim_days = " ^ (show save_context.claim_days) ^ ",\n" + let deaths = claim_days ^ prep ^ " deaths = " ^ (show save_context.deaths) ^ ",\n" + let n64dd_flag = deaths ^ prep ^ " n64dd_flag = " ^ (show save_context.n64dd_flag) ^ ",\n" + let health_capacity = n64dd_flag ^ prep ^ " health_capacity = " ^ (show save_context.health_capacity) ^ ",\n" + let health = health_capacity ^ prep ^ " health = " ^ (show save_context.health) ^ ",\n" + let magic_level = health ^ prep ^ " magic_level = " ^ (show save_context.magic_level) ^ ",\n" + let magic = magic_level ^ prep ^ " magic = " ^ (show save_context.magic) ^ ",\n" + let rupees = magic ^ prep ^ " rupees = " ^ (show save_context.rupees) ^ ",\n" + let sword_health = rupees ^ prep ^ " sword_health = " ^ (show save_context.sword_health) ^ ",\n" + let navi_timer = sword_health ^ prep ^ " navi_timer = " ^ (show save_context.navi_timer) ^ ",\n" + let magic_acquired = navi_timer ^ prep ^ " magic_acquired = " ^ (show save_context.magic_acquired) ^ ",\n" + let double_magic = magic_acquired ^ prep ^ " double_magic = " ^ (show save_context.double_magic) ^ ",\n" + let double_defense = double_magic ^ prep ^ " double_defense = " ^ (show save_context.double_defense) ^ ",\n" + let bgs_flag = double_defense ^ prep ^ " bgs_flag = " ^ (show save_context.bgs_flag) ^ ",\n" + let ocarina_game_reward = bgs_flag ^ prep ^ " ocarina_game_reward = " ^ (show save_context.ocarina_game_reward) ^ ",\n" + let child_equips = ocarina_game_reward ^ prep ^ " child_equips = " ^ (pretty save_context.child_equips x) ^ ",\n" + let adult_equips = child_equips ^ prep ^ " adult_equips = " ^ (pretty save_context.adult_equips x) ^ ", \n" + adult_equips ^ prep ^ "}" +end