https://github.com/gg-blake/mariokart
Training an ML model to play Mariokart DS
https://github.com/gg-blake/mariokart
desmume gtk nitrosdk pytorch reverse-engineering
Last synced: 9 months ago
JSON representation
Training an ML model to play Mariokart DS
- Host: GitHub
- URL: https://github.com/gg-blake/mariokart
- Owner: gg-blake
- Created: 2025-09-05T20:51:32.000Z (10 months ago)
- Default Branch: main
- Last Pushed: 2025-10-06T15:53:05.000Z (9 months ago)
- Last Synced: 2025-10-06T17:43:53.277Z (9 months ago)
- Topics: desmume, gtk, nitrosdk, pytorch, reverse-engineering
- Language: Python
- Homepage:
- Size: 24.5 MB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Training a model to play Mariokart DS
```
pip install -r requirements.txt
```
# Overview
The project falls down into three main parts
1) Reverse engineering MKDS Rom for car data (Done)
2) Making a visualization for debugging and benchmarking (Done)
3) Building and training the model (In Progess)
# Reverse Engineering Mariokart DS
The reverse engineering process consisted mainly of using [dynamic code analysis](https://en.wikipedia.org/wiki/Dynamic_program_analysis) to extract import values from kart's game state. Values consisted of but were not limited to:
- Car's position
- Car's orientation
- Car's acceleration/Speed
- Car's collected ability
- Enemy positions
## Interpreting WRAM
I used an emulation tool called [Desmume](https://desmume.org/) to run the MKDS rom on my PC. Desmume comes with built in features for watching the memory which I utilized heavily. Additionally, my ML interface utilized [py-desmume](https://py-desmume.readthedocs.io/en/latest/), a python interface for using desmume's frontend. For US versions of MKDS, the pointer to the kart's race data is located at `0x0217ACF8` in
The DS uses a dual CPU system with ARM9 and ARM7 CPUs. Although memory is divided between the two (i.e. they share the same address space), the data that I care about is stored exclusively on the ARM9 CPU's memory. In Mariokart DS, the game state is stored on the Main RAM of the ARM9 CPU from `0x02000000` to `0x023FFFFF`, in [little endian](https://en.wikipedia.org/wiki/Endianness) format.
### Kart Data
The pointer to the kart's game data (position, orientation, powerup, etc.) is located at `0x0217ACF8`, stored as an unsigned 32-bit integer. Starting at the kart's game data in memory, kart values of interest live at the following offsets:
- position vector (`0x80`) (3x `fx32`)
- movement direction vector (`0x68`) (3x `fx32`)
Many game values including vectors are stored as fixed point data types according to the [NitroSDK/NitroMath spec](https://twlsdk.randommeaninglesscharacters.com/docs/nitro/NitroSDK/fx/list_fx.html). Unlike floating point values that have an exponent component to the bit sequence, Mario Kart DS's fixed point datatypes, specifically, `fx32` have a dedicated 1-bit sign, 19-bit integer, and 12-bit fraction.
### Camera Data
The pointer to the car camera's data is located at `0x0217AA4C`, stored as an unsigned 32-bit integer. Starting at the car camera's data in memory, camera values of interest live at the following offsets:
- position vector (`0x24`) (3x `fx32`)
- elevation (`0x178`) (1x `fx32`)
- target position vector (`0x18`) (3x `fx32`)
- field of view (`0x60`) (1x `u16`)
- aspect ratio (`0x6C`) (1x `fx32`)
This is all the camera data that we need to reconstruct the camera's [perspective projection and model view matrices](https://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/#the-model-view-and-projection-matrices). This will come in handy when I discuss the visualization overlay later on.
## Interpreting Course Data
Course data is located as an `.nkm` file within the MKDS ROM. In order the retreive this file, we need to unpack the ROM file. I used [kiwi.ds](https://projectpokemon.org/home/files/file/2073-nds-editor-kiwids/) to retreive the course files. The course files are compressed as `.carc`. To uncompress it, I used [NArchive](https://github.com/nickworonekin/narchive/tree/master/src/Narchive) to extract the course files. I'm mainly focused on reading the checkpoint data for a course, so I ignore the rest of the files except the `.nkm` files.
NKM files are essentially specialized bin files. The spec for this file can be found [here](https://wiki.tockdom.com/wiki/NKM_(File_Format)#cite_note-MoreCPOIInfo-4). The NKM file has a header that specifies the byte offset of each data section in the file. The `CPOI` section contains all the entries for checkpoints on a map. It's section offset is found at `0x2C`. Each data section specifies it name and the number of entries. `CPOI` entries are 36 bytes in size and contain:
1) left position vector (`0x00`) (2x `fx32`)
2) right position vector (`0x08`) (2x `fx32`)
...
5) distance (`0x18`) (1x `fx32`)
...
8) key id (`0x20`) (1x `u16`)
9) respawn id (`0x22`) (1x `u8`)
...
Having this data is useful since I can use this to calculate the player's forward facing distance to wall, assuming the checkpoints are positioned within the bounds of the map.