RetroArch/deps/game_ai_lib/games/NHL94GameAI.cpp
Mathieu Poliquin 66e23fca79
New feature: Override player input with machine learning models (#17407)
* Add dummy game ai subsystem

* First working prototype of a machine learning model that can override player input

* Update README.md

* Update README.md

* Fix loading path on Windows

* Change ai override to player 2

* Added quick menu show game ai option

* Implemented Quick Menu entry for Game AI options

* Redirect debug logs to retroarch log system + properly support player override

* Added support to use framebuffer as input to the AI

* Added pixel format parameter to API

* Fix game name

* code clean-up of game_ai.cpp

* Update README.md - Windows Build

* Update README.md

* Update README.md

* Update README.md

* Update config.params.sh

turn off GAME_AI feature by default

* Fix compile error in menu_displaylist.c

* Add missing #define in menu_cbs_title.c

* Added new game_ai entry in griffin_cpp

* Remove GAME_AI entry in  msg_hash_us.c

* Fix compile error in menu_displaylist.h

* Removed GAME AI references from README.md

* Fixes coding style + add GameAI lib API header

* Convert comment to legacy + remove unused code

* Additional coding style fixes to game_ai.cpp

* Fix identation issues in game_ai.cpp

* Removed some debug code in game_ai.cpp

* Add game_ai_lib in deps

* Replace assert with retro_assert

* Update Makefile.common

* Converting game_ai from cpp to c. First step.

* Convert game_ai from CPP to C. STEP 2: add C function calls

* Convert game_ai from CPP to C. Final Step

* Added shutdown function for game ai lib

* Update game_ai_lib README

* Fix crash when loading/unloading multiple games
2025-01-21 13:05:43 +01:00

225 lines
No EOL
7 KiB
C++

#include "NHL94GameAI.h"
#include <cstdlib>
#include <iostream>
#include <assert.h>
#include <random>
//=======================================================
// NHL94GameAI::Init
//=======================================================
void NHL94GameAI::Init(void * ram_ptr, int ram_size)
{
LoadConfig();
InitRAM(ram_ptr, ram_size);
static_assert(NHL94NeuralNetInput::NN_INPUT_MAX == 16);
isShooting = false;
}
//=======================================================
// NHL94GameAI::SetModelInputs
//=======================================================
void NHL94GameAI::SetModelInputs(std::vector<float> & input, const NHL94Data & data)
{
// players
input[NHL94NeuralNetInput::P1_X] = (float)data.p1_x / (float) NHL94NeuralNetInput::MAX_PLAYER_X;
input[NHL94NeuralNetInput::P1_Y] = (float)data.p1_y / (float) NHL94NeuralNetInput::MAX_PLAYER_Y;
input[NHL94NeuralNetInput::P2_X] = (float)data.p2_x / (float) NHL94NeuralNetInput::MAX_PLAYER_X;
input[NHL94NeuralNetInput::P2_Y] = (float) data.p2_y / (float) NHL94NeuralNetInput::MAX_PLAYER_Y;
input[NHL94NeuralNetInput::G2_X] = (float) data.g2_x / (float) NHL94NeuralNetInput::MAX_PLAYER_X;
input[NHL94NeuralNetInput::G2_Y] = (float) data.g2_y / (float) NHL94NeuralNetInput::MAX_PLAYER_Y;
input[NHL94NeuralNetInput::P1_VEL_X] = (float) data.p1_vel_x / (float) NHL94NeuralNetInput::MAX_VEL_XY;
input[NHL94NeuralNetInput::P1_VEL_Y] = (float) data.p1_vel_y / (float) NHL94NeuralNetInput::MAX_VEL_XY;
input[NHL94NeuralNetInput::P2_VEL_X] = (float) data.p2_vel_x / (float) NHL94NeuralNetInput::MAX_VEL_XY;
input[NHL94NeuralNetInput::P2_VEL_Y] = (float) data.p2_vel_y / (float) NHL94NeuralNetInput::MAX_VEL_XY;
// puck
input[NHL94NeuralNetInput::PUCK_X] = (float) data.puck_x / (float) NHL94NeuralNetInput::MAX_PLAYER_X;
input[NHL94NeuralNetInput::PUCK_Y] = (float) data.puck_y / (float) NHL94NeuralNetInput::MAX_PLAYER_Y;
input[NHL94NeuralNetInput::PUCK_VEL_X] = (float) data.puck_vel_x / (float) NHL94NeuralNetInput::MAX_VEL_XY;
input[NHL94NeuralNetInput::PUCK_VEL_Y] = (float) data.puck_vel_y / (float) NHL94NeuralNetInput::MAX_VEL_XY;
input[NHL94NeuralNetInput::P1_HASPUCK] = data.p1_haspuck ? 0.0 : 1.0;
input[NHL94NeuralNetInput::G1_HASPUCK] = data.g1_haspuck ? 0.0 : 1.0;
}
//=======================================================
// NHL94GameAI::GotoTarget
//=======================================================
void NHL94GameAI::GotoTarget(std::vector<float> & input, int vec_x, int vec_y)
{
if (vec_x > 0)
input[NHL94Buttons::INPUT_LEFT] = 1;
else
input[NHL94Buttons::INPUT_RIGHT] = 1;
if (vec_y > 0)
input[NHL94Buttons::INPUT_DOWN] = 1;
else
input[NHL94Buttons::INPUT_UP] = 1;
}
//=======================================================
// isInsideAttackZone
//=======================================================
static bool isInsideAttackZone(NHL94Data & data)
{
if (data.attack_zone_y > 0 && data.p1_y >= data.attack_zone_y)
{
return true;
}
else if (data.attack_zone_y < 0 && data.p1_y <= data.attack_zone_y)
{
return true;
}
return false;
}
//=======================================================
// isInsideScoreZone
//=======================================================
static bool isInsideScoreZone(NHL94Data & data)
{
if (data.p1_y < data.score_zone_top && data.p1_y > data.score_zone_bottom)
{
return true;
}
return false;
}
//=======================================================
// isInsideDefenseZone
//=======================================================
static bool isInsideDefenseZone(NHL94Data & data)
{
if (data.defense_zone_y > 0 && data.p1_y >= data.defense_zone_y)
{
return true;
}
else if (data.defense_zone_y < 0 && data.p1_y <= data.defense_zone_y)
{
return true;
}
return false;
}
//=======================================================
// NHL94GameAI::Think
//=======================================================
void NHL94GameAI::Think(bool buttons[GAMEAI_MAX_BUTTONS], int player, const void *frame_data, unsigned int frame_width, unsigned int frame_height, unsigned int frame_pitch, unsigned int pixel_format)
{
NHL94Data data;
data.Init(retro_data);
if(player == 1)
{
data.Flip();
if(data.period % 2 == 0)
{
data.FlipZones();
}
}
else if (player == 0)
{
if(data.period % 2 == 1)
{
data.FlipZones();
}
}
std::vector<float> input(16);
std::vector<float> output(12);
this->SetModelInputs(input, data);
if (data.p1_haspuck)
{
DebugPrint("have puck");
if (isInsideAttackZone(data))
{
DebugPrint(" in attackzone");
models["ScoreGoal"]->Forward(output, input);
output[NHL94Buttons::INPUT_C] = 0;
output[NHL94Buttons::INPUT_B] = 0;
if (isInsideScoreZone(data))
{
if (data.p1_vel_x >= 30 && data.puck_x > -23 && data.puck_x < 0)
{
DebugPrint("Shoot");
output[NHL94Buttons::INPUT_C] = 1;
isShooting = true;
}
else if(data.p1_vel_x <= -30 && data.puck_x < 23 && data.puck_x > 0)
{
DebugPrint("Shoot");
output[NHL94Buttons::INPUT_C] = 1;
isShooting = true;
}
}
}
else
{
this->GotoTarget(output, data.p1_x, -data.attack_zone_y);
}
}
else if (data.g1_haspuck)
{
if (rand() > (RAND_MAX / 2))
output[NHL94Buttons::INPUT_B] = 1;
}
else
{
DebugPrint("Don't have puck");
isShooting = false;
if (isInsideDefenseZone(data) && data.p2_haspuck)
{
DebugPrint(" DefenseModel->Forward");
models["DefenseZone"]->Forward(output, input);
}
else
{
DebugPrint(" GOTO TARGET");
GotoTarget(output, data.p1_x - data.puck_x, data.p1_y - data.puck_y);
}
if (isShooting)
{
//output[NHL94Buttons::INPUT_MODE] = 1;
DebugPrint("Shooting");
output[NHL94Buttons::INPUT_C] = 1;
}
}
assert(output.size() <= 16);
for (int i=0; i < output.size(); i++)
{
buttons[i] = output[i] >= 1.0 ? 1 : 0;
}
buttons[NHL94Buttons::INPUT_START] = 0;
buttons[NHL94Buttons::INPUT_MODE] = 0;
buttons[NHL94Buttons::INPUT_A] = 0;
//buttons[NHL94Buttons::INPUT_B] = 0;
//buttons[NHL94Buttons::INPUT_C] = 0;
buttons[NHL94Buttons::INPUT_X] = 0;
buttons[NHL94Buttons::INPUT_Y] = 0;
buttons[NHL94Buttons::INPUT_Z] = 0;
//Flip directions
if(data.period % 2 != player)
{
std::swap(buttons[NHL94Buttons::INPUT_UP], buttons[NHL94Buttons::INPUT_DOWN]);
std::swap(buttons[NHL94Buttons::INPUT_LEFT], buttons[NHL94Buttons::INPUT_RIGHT]);
}
}