Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New feature: Override player input with machine learning models #17407

Merged
merged 41 commits into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
59cf1fb
Add dummy game ai subsystem
MatPoliquin Jun 23, 2024
2fb1459
First working prototype of a machine learning model that can override…
MatPoliquin Jun 27, 2024
a21e9b3
Update README.md
MatPoliquin Jun 27, 2024
40cb006
Update README.md
MatPoliquin Jun 27, 2024
7d7524e
Fix loading path on Windows
MatPoliquin Jul 4, 2024
3dd87f5
Change ai override to player 2
MatPoliquin Jul 5, 2024
5477094
Added quick menu show game ai option
MatPoliquin Jul 7, 2024
6953b2a
Implemented Quick Menu entry for Game AI options
MatPoliquin Jul 8, 2024
a9eb210
Redirect debug logs to retroarch log system + properly support player…
MatPoliquin Jul 9, 2024
eeaf118
Added support to use framebuffer as input to the AI
MatPoliquin Jul 12, 2024
8b4cf7c
Added pixel format parameter to API
MatPoliquin Jul 14, 2024
718d737
Fix game name
MatPoliquin Jul 19, 2024
022017a
code clean-up of game_ai.cpp
MatPoliquin Jul 19, 2024
f866b6a
Update README.md - Windows Build
MatPoliquin Jul 28, 2024
ee29257
Update README.md
MatPoliquin Jul 28, 2024
7120fa6
Update README.md
MatPoliquin Jan 9, 2025
69e94b7
Merge branch 'libretro:master' into master
MatPoliquin Jan 13, 2025
1bded6b
Update README.md
MatPoliquin Jan 13, 2025
58f9732
Merge branch 'libretro:master' into master
MatPoliquin Jan 13, 2025
b54c65b
Update config.params.sh
MatPoliquin Jan 13, 2025
8b71d8e
Fix compile error in menu_displaylist.c
MatPoliquin Jan 14, 2025
684eefa
Add missing #define in menu_cbs_title.c
MatPoliquin Jan 14, 2025
6841795
Added new game_ai entry in griffin_cpp
MatPoliquin Jan 14, 2025
9e1ca05
Remove GAME_AI entry in msg_hash_us.c
MatPoliquin Jan 14, 2025
ae0a264
Fix compile error in menu_displaylist.h
MatPoliquin Jan 14, 2025
59d0d11
Removed GAME AI references from README.md
MatPoliquin Jan 14, 2025
93b3aac
Fixes coding style + add GameAI lib API header
MatPoliquin Jan 14, 2025
ed40ac3
Convert comment to legacy + remove unused code
MatPoliquin Jan 14, 2025
b868b11
Additional coding style fixes to game_ai.cpp
MatPoliquin Jan 14, 2025
913c4ad
Fix identation issues in game_ai.cpp
MatPoliquin Jan 14, 2025
6c35f84
Removed some debug code in game_ai.cpp
MatPoliquin Jan 14, 2025
aeeb622
Add game_ai_lib in deps
MatPoliquin Jan 14, 2025
65aaa37
Replace assert with retro_assert
MatPoliquin Jan 15, 2025
e0f64fc
Merge branch 'master' into master
MatPoliquin Jan 18, 2025
5711de3
Update Makefile.common
MatPoliquin Jan 18, 2025
d8126dc
Converting game_ai from cpp to c. First step.
MatPoliquin Jan 21, 2025
5d59960
Convert game_ai from CPP to C. STEP 2: add C function calls
MatPoliquin Jan 21, 2025
659c0b0
Convert game_ai from CPP to C. Final Step
MatPoliquin Jan 21, 2025
f751214
Added shutdown function for game ai lib
MatPoliquin Jan 21, 2025
aa4e71e
Update game_ai_lib README
MatPoliquin Jan 21, 2025
d53d1bf
Fix crash when loading/unloading multiple games
MatPoliquin Jan 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Makefile.common
Original file line number Diff line number Diff line change
Expand Up @@ -2648,4 +2648,9 @@ ifeq ($(HAVE_ODROIDGO2), 1)
gfx/drivers/oga_gfx.o
endif

ifeq ($(HAVE_GAME_AI),1)
DEFINES += -DHAVE_GAME_AI
OBJ += ai/game_ai.o
MatPoliquin marked this conversation as resolved.
Show resolved Hide resolved
endif

##################################
227 changes: 227 additions & 0 deletions ai/game_ai.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
#include "game_ai.h"
#include <stdio.h>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you rewrite this entire file into C? The 'dep' can still be C++ I guess as long as the RetroArch glue here is at least C.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

#include <assert.h>
MatPoliquin marked this conversation as resolved.
Show resolved Hide resolved
#include <bitset>
#include <iostream>
#include <string>
#include <stdarg.h>

#ifdef _WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#endif

#define GAME_AI_MAX_PLAYERS 2

#include "../../stable-retro-scripts/ef_lib/GameAI.h"
MatPoliquin marked this conversation as resolved.
Show resolved Hide resolved


class GameAIManager {
public:
GameAIManager();

signed short int Input(unsigned int port, unsigned int device, unsigned int idx, unsigned int id, signed short int result);
void Init();
void Load(const char * name, void * ram_ptr, int ram_size, retro_log_printf_t log);
void Think(bool override_p1, bool override_p2, bool show_debug, const void *frame_data, unsigned int frame_width, unsigned int frame_height, unsigned int frame_pitch, unsigned int pixel_format);

private:
creategameai_t CreateGameAI;
GameAI * ga;
volatile void * g_ram_ptr;
volatile int g_ram_size;
volatile signed short int g_buttons_bits[GAME_AI_MAX_PLAYERS];
volatile int g_frameCount;
volatile char game_ai_lib_path[1024];
std::string g_game_name;

};

GameAIManager gameAIMgr;
retro_log_printf_t g_log;

//======================================================
// Helper functions
//======================================================
extern "C" void game_ai_debug_log(int level, const char *fmt, ...)
{
va_list vp;
va_start(vp, fmt);

if(g_log)
MatPoliquin marked this conversation as resolved.
Show resolved Hide resolved
{
g_log((enum retro_log_level)level, fmt, vp);
}

va_end(vp);
}

void array_to_bits_16(volatile signed short & result, const bool b[16])
{
for(int bit=0; bit<=15; bit++){
MatPoliquin marked this conversation as resolved.
Show resolved Hide resolved
result |= b[bit] ? (1 << bit) : 0;
}
}

//======================================================
// GameAIManager::GameAIManager
//======================================================
GameAIManager::GameAIManager()
{
CreateGameAI = nullptr;
ga = nullptr;
g_ram_ptr = nullptr;
g_ram_size = 0;
g_buttons_bits[0] = 0;
g_buttons_bits[1] = 0;
g_frameCount = 0;
game_ai_lib_path[0]='\0';
g_log = nullptr;
}

//======================================================
// GameAIManager::Input
//======================================================
signed short int GameAIManager::Input(unsigned int port, unsigned int device, unsigned int idx, unsigned int id, signed short int result)
{
if(ga == nullptr)
MatPoliquin marked this conversation as resolved.
Show resolved Hide resolved
return 0;

if(port < GAME_AI_MAX_PLAYERS)
MatPoliquin marked this conversation as resolved.
Show resolved Hide resolved
return g_buttons_bits[port];

return 0;
}

//======================================================
// GameAIManager::Init
//======================================================
void GameAIManager::Init()
{
//printf("GAME AI INIT");
MatPoliquin marked this conversation as resolved.
Show resolved Hide resolved

if(CreateGameAI == nullptr)
{
#ifdef _WIN32
HINSTANCE hinstLib;
BOOL fFreeResult, fRunTimeLinkSuccess = FALSE;

hinstLib = LoadLibrary(TEXT("game_ai.dll"));
assert(hinstLib);

char full_module_path[MAX_PATH];
DWORD dwLen = GetModuleFileNameA(hinstLib, (char *) &full_module_path, MAX_PATH);
MatPoliquin marked this conversation as resolved.
Show resolved Hide resolved

_splitpath((const char *) full_module_path, NULL, (char *) game_ai_lib_path, NULL, NULL);
std::cout << game_ai_lib_path << std::endl;
MatPoliquin marked this conversation as resolved.
Show resolved Hide resolved

if (hinstLib != NULL)
{
CreateGameAI = (creategameai_t) GetProcAddress(hinstLib, "CreateGameAI");
MatPoliquin marked this conversation as resolved.
Show resolved Hide resolved

assert(CreateGameAI);
}
#else
void *myso = dlopen("libgame_ai.so", RTLD_NOW);
assert(myso);

dlinfo(myso, RTLD_DI_ORIGIN, (void *) &game_ai_lib_path);

CreateGameAI = reinterpret_cast<creategameai_t>(dlsym(myso, "CreateGameAI"));
assert(CreateGameAI);
#endif
}
}

//======================================================
// GameAIManager::Load
//======================================================
void GameAIManager::Load(const char *name, void *ram_ptr, int ram_size, retro_log_printf_t log)
{
g_game_name = name;
MatPoliquin marked this conversation as resolved.
Show resolved Hide resolved

g_ram_ptr = ram_ptr;
g_ram_size = ram_size;

g_log = log;
}

//======================================================
// GameAIManager::Think
//======================================================
void GameAIManager::Think(bool override_p1, bool override_p2, bool show_debug, const void *frame_data, unsigned int frame_width, unsigned int frame_height, unsigned int frame_pitch, unsigned int pixel_format)
{
if(ga)
MatPoliquin marked this conversation as resolved.
Show resolved Hide resolved
{
ga->SetShowDebug(show_debug);
}

if(ga == nullptr && g_ram_ptr != nullptr)
{
ga = CreateGameAI(g_game_name.c_str());
assert(ga);

std::string data_path((char *)game_ai_lib_path);
data_path += "/data/";
data_path += g_game_name;

ga->Init((void *) g_ram_ptr, g_ram_size);

ga->SetDebugLog(game_ai_debug_log);
}

if (g_frameCount >= 3)
{
if(ga)
MatPoliquin marked this conversation as resolved.
Show resolved Hide resolved
{
bool b[16] = {0};

g_buttons_bits[0]=0;
g_buttons_bits[1]=0;

if (override_p1)
{
ga->Think(b, 0, frame_data, frame_width, frame_height, frame_pitch, pixel_format);
array_to_bits_16(g_buttons_bits[0], b);
}

if (override_p2)
{
ga->Think(b, 1, frame_data, frame_width, frame_height, frame_pitch, pixel_format);
array_to_bits_16(g_buttons_bits[1], b);
}
}

g_frameCount=0;
}
else
{
g_frameCount++;
}
}


//======================================================
// Interface to RA
//======================================================
extern "C" signed short int game_ai_input(unsigned int port, unsigned int device, unsigned int idx, unsigned int id, signed short int result)
{
return gameAIMgr.Input(port,device, idx, id, result);
}

extern "C" void game_ai_init()
{
gameAIMgr.Init();
}

extern "C" void game_ai_load(const char * name, void * ram_ptr, int ram_size, retro_log_printf_t log)
{
gameAIMgr.Load(name, ram_ptr, ram_size, log);
}

extern "C" void game_ai_think(bool override_p1, bool override_p2, bool show_debug, const void *frame_data, unsigned int frame_width, unsigned int frame_height, unsigned int frame_pitch, unsigned int pixel_format)
{
gameAIMgr.Think(override_p1, override_p2, show_debug, frame_data, frame_width, frame_height, frame_pitch, pixel_format);
}

15 changes: 15 additions & 0 deletions ai/game_ai.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma once


#ifdef __cplusplus
#define EXTERNC extern "C"
#else
#define EXTERNC
#endif

#include <libretro.h>

EXTERNC signed short int game_ai_input(unsigned int port, unsigned int device, unsigned int idx, unsigned int id, signed short int result);
EXTERNC void game_ai_init();
EXTERNC void game_ai_load(const char * name, void * ram_ptr, int ram_size, retro_log_printf_t log);
EXTERNC void game_ai_think(bool override_p1, bool override_p2, bool show_debug, const void *frame_data, unsigned int frame_width, unsigned int frame_height, unsigned int frame_pitch, unsigned int pixel_format);
5 changes: 5 additions & 0 deletions configuration.c
Original file line number Diff line number Diff line change
Expand Up @@ -2227,6 +2227,11 @@ static struct config_bool_setting *populate_settings_bool(
SETTING_BOOL("gcdwebserver_alert", &settings->bools.gcdwebserver_alert, true, true, false);
#endif

#ifdef HAVE_GAME_AI
//SETTING_BOOL("ai_player_override", &settings->bools.ai_player_override, true, 0, false);
MatPoliquin marked this conversation as resolved.
Show resolved Hide resolved
SETTING_BOOL("quick_menu_show_game_ai", &settings->bools.quick_menu_show_game_ai, true, 1, false);
#endif

*size = count;

return tmp;
Expand Down
8 changes: 8 additions & 0 deletions configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -1095,6 +1095,14 @@ typedef struct settings
#if defined(HAVE_COCOATOUCH)
bool gcdwebserver_alert;
#endif

#ifdef HAVE_GAME_AI
bool quick_menu_show_game_ai;
bool game_ai_override_p1;
bool game_ai_override_p2;
bool game_ai_show_debug;
#endif

} bools;

uint8_t flags;
Expand Down
8 changes: 8 additions & 0 deletions griffin/griffin_cpp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,11 @@ FONTS
#include "../deps/discord-rpc/src/connection_unix.cpp"
#endif
#endif


/*============================================================
GAME AI
============================================================ */
#if defined(HAVE_GAME_AI)
#include "../ai/game_ai.cpp"
#endif
13 changes: 13 additions & 0 deletions input/input_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
#include "../tasks/tasks_internal.h"
#include "../verbosity.h"

#include "../ai/game_ai.h"

#define HOLD_BTN_DELAY_SEC 2

/* Depends on ASCII character values */
Expand Down Expand Up @@ -6816,6 +6818,17 @@ int16_t input_driver_state_wrapper(unsigned port, unsigned device,
}
#endif

#ifdef HAVE_GAME_AI
if(settings->bools.game_ai_override_p1 && port == 0)
{
result |= game_ai_input(port, device, idx, id, result);
}
if(settings->bools.game_ai_override_p2 && port == 1)
{
result |= game_ai_input(port, device, idx, id, result);
}
#endif

return result;
}

Expand Down
22 changes: 22 additions & 0 deletions intl/msg_hash_lbl.h
Original file line number Diff line number Diff line change
Expand Up @@ -6624,3 +6624,25 @@ MSG_HASH(
MENU_ENUM_LABEL_GAMEMODE_ENABLE,
"game_mode_enable"
)
#ifdef HAVE_GAME_AI
MSG_HASH(
MENU_ENUM_LABEL_CORE_GAME_AI_OPTIONS,
"core_game_ai_options"
)
MSG_HASH(
MENU_ENUM_LABEL_QUICK_MENU_SHOW_GAME_AI,
"quick_menu_show_game_ai"
)
MSG_HASH(
MENU_ENUM_LABEL_GAME_AI_OVERRIDE_P1,
"game_ai_override_p1"
)
MSG_HASH(
MENU_ENUM_LABEL_GAME_AI_OVERRIDE_P2,
"game_ai_override_p2"
)
MSG_HASH(
MENU_ENUM_LABEL_GAME_AI_SHOW_DEBUG,
"game_ai_show_debug"
)
#endif
Loading
Loading