Neuro-Flap / Game /NeuroFlap.c
EliezerCarvalho's picture
Upload
c2c8b8d
Raw
History Blame Contribute Delete
11.7 kB
#include "raylib.h"
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#define LARGURA 1800
#define ALTURA 750
#define FPS 60
#define GRAVIDADE 0.5
#define NUMERO_TUBOS 200
#define NUMERO_NEURONIOS_CAMADA_OCULTA 5
double GENES [31] = {
0.259316, 0.580309, -0.687613, -0.960814, 0.490097, -0.276894, 0.642528, -0.022248, -0.820237, 0.257179,
0.144017, -0.591751, 0.374004, 0.655998, -0.413886, -0.418961, 0.109226, 0.023600, 1.047282, -0.148228,
-0.015586, 0.486480, -0.507483, 0.892828, 0.263039, 0.266579, 0.807550, -0.303601, -0.982849, -0.739723,
-0.601028,
};
bool COLISAO_CIMA_REDE_NEURAL = false;
bool COLISAO_BAIXO_REDE_NEURAL = false;
bool COLISAO_CIMA_UTILIZADOR = false;
bool COLISAO_BAIXO_UTILIZADOR = false;
bool GAME_MODE = false; //Jogo não começa logo, espera input do utilizador para começar via JS
bool GAME_MODE_AI = true;
bool RESTART_GAME = false;
bool WINNER = false;
bool CLIQUE = true;
int TUBO_GAP = 180;
int SCORE = 0;
int POS_INICIAL_X_PRIMEIRO_PIPE = 800;
typedef struct {
float POS_EIXO_X;
int ALTURA_TUBO_CIMA;
int ALTURA_TUBO_BAIXO;
} TUBOS;
typedef struct {
bool VIVO;
double POS_INICIAL_X;
double POS_INICIAL_Y; //Input 2 - Representa a altura a que o Flappy encontra-se
double VELOCIDADE_Y; //Input 1 - Representa a velocidade do Flappy no eixo dos Y
double NEURONIO_HIDDEN_LAYER [NUMERO_NEURONIOS_CAMADA_OCULTA];
double OUTPUT_NEURONIO_HIDDEN_LAYER [NUMERO_NEURONIOS_CAMADA_OCULTA];
double OUTPUT;
float CENTRO_COORDENADA_PIPE; //Input 4 - Representa o a coordenada do centro do próximo pipe, uma maneira diferente, em vez de dar o gap entre tubos
int X_TO_NEXTPIPE; //Input 3 - Representa a distância até ao próximo pipe, pipe a pipe
} REDE_NEURAL;
typedef struct {
bool VIVO_UTI;
double POS_INICIAL_X_UTI;
double POS_INICIAL_Y_UTI;
double VELOCIDADE_Y_UTI;
} UTILIZADOR;
double ACTIVATION_FUNCTION_TANH (double x);
double ACTIVATION_FUNCTION_SIGMOID (double x);
void INICIAR_VALORES (REDE_NEURAL *x);
void GAME_ON (); //Função para chamar o inicio do jogo em JS
void JUMP (); //Função para chamar o Jump via JS
double REDE_NEURAL_AG (double INPUT1, double INPUT2, double INPUT3, double INPUT4, REDE_NEURAL *x);
void RUN_REDE_NEURAL (REDE_NEURAL *x, TUBOS y [], Texture2D Flappy_AI);
void RUN_PLAYER1 (TUBOS y [], Texture2D Flappy_UTI);
UTILIZADOR player_1; //IMPORTANTE - Definição da struct UTILIZADOR fora do main para poder declarar a função JUMP sem apontador.
//O motivo é porque em JS quando chamamos uma função, temos de passar os argumentos e com JS não podemos passar apontadores.
//Desta maneira a struct fica como variável global e em C uma variável global é acessivel diretamente a partir de qualquer função
int main () {
srand(time(NULL));
InitWindow (LARGURA, ALTURA, "Neuro Flap");
SetTargetFPS (FPS);
Texture2D Flappy_UTI = LoadTexture("C:/Users/eliez/Desktop/Neuro-Flap/Imagens/flappybird2.png");
Texture2D Flappy_AI = LoadTexture ("C:/Users/eliez/Desktop/Neuro-Flap/Imagens/flappybird1.png");
Texture2D TAP = LoadTexture("C:/Users/eliez/Desktop/Neuro-Flap/Imagens/Logos/tap.png.png");
TUBOS colunas [NUMERO_TUBOS];
for (int i = 0; i < NUMERO_TUBOS; i++) {
int ALTURA_MAX_TUBO_CIMA = ALTURA - TUBO_GAP - 50;
int ALTURA_TUBO_CIMA = rand() % (ALTURA_MAX_TUBO_CIMA - 50) + 50;
int ALTURA_TUBO_BAIXO = ALTURA - ALTURA_TUBO_CIMA - TUBO_GAP;
colunas[i].POS_EIXO_X = POS_INICIAL_X_PRIMEIRO_PIPE;
colunas[i].ALTURA_TUBO_CIMA = ALTURA_TUBO_CIMA;
colunas[i].ALTURA_TUBO_BAIXO = ALTURA_TUBO_BAIXO;
POS_INICIAL_X_PRIMEIRO_PIPE += 380;
}
REDE_NEURAL multilayer_perceptron_ag;
INICIAR_VALORES (&multilayer_perceptron_ag);
while (!WindowShouldClose()) {
if (GAME_MODE == true) {
if (GAME_MODE_AI == true) {
RUN_REDE_NEURAL (&multilayer_perceptron_ag, colunas, Flappy_AI);
}
RUN_PLAYER1 (colunas, Flappy_UTI);
if (WINNER == true) {
GAME_MODE = false;
GAME_MODE_AI = false;
}
for (int i = 0; i < NUMERO_TUBOS; i++) {
colunas[i].POS_EIXO_X -= 3.3f;
}
}
BeginDrawing();
ClearBackground(SKYBLUE);
DrawTextureEx(Flappy_AI, (Vector2){multilayer_perceptron_ag.POS_INICIAL_X, multilayer_perceptron_ag.POS_INICIAL_Y}, 0, 0.45, RAYWHITE);
DrawTextureEx(Flappy_UTI, (Vector2){player_1.POS_INICIAL_X_UTI, player_1.POS_INICIAL_Y_UTI}, 0, 0.45, RAYWHITE);
for (int i = 0; i < NUMERO_TUBOS; i++) {
DrawRectangle (colunas[i].POS_EIXO_X, 0, 95, colunas[i].ALTURA_TUBO_CIMA, RAYWHITE);
DrawRectangle (colunas[i].POS_EIXO_X + 2, (ALTURA - colunas[i].ALTURA_TUBO_BAIXO), 95, colunas[i].ALTURA_TUBO_BAIXO, RAYWHITE);
}
DrawText(TextFormat("%i", SCORE), LARGURA / 2.13, 40, 50, BLACK);
if (GAME_MODE == false && RESTART_GAME == true) {
DrawRectangleRounded((Rectangle){100, ALTURA - 125, 680, 100}, 0.1, 1, DARKBLUE);
DrawText(TextFormat("Para jogar de novo reinicie a página!"), 118, ALTURA - 90, 35, RAYWHITE);
}
if (CLIQUE == true && GAME_MODE == false) {
DrawTextureEx(TAP, (Vector2){240, 220}, 0, 0.5, BLACK);
}
if (GAME_MODE == true) {
CLIQUE = false;
}
EndDrawing();
}
UnloadTexture(Flappy_AI);
UnloadTexture(Flappy_UTI);
UnloadTexture(TAP);
CloseWindow();
return 0;
}
double ACTIVATION_FUNCTION_TANH (double x) {
return tanh (x);
}
double ACTIVATION_FUNCTION_SIGMOID (double x) {
return 1.0 / (1.0 + exp(-x));
}
void INICIAR_VALORES(REDE_NEURAL *x) {
x -> VIVO = true;
x -> POS_INICIAL_X = 200;
x -> POS_INICIAL_Y = 200;
x -> VELOCIDADE_Y = 0;
x -> CENTRO_COORDENADA_PIPE = 0;
x -> X_TO_NEXTPIPE = 0;
player_1.VIVO_UTI = true;
player_1.POS_INICIAL_X_UTI = 200;
player_1.POS_INICIAL_Y_UTI = 150;
player_1.VELOCIDADE_Y_UTI = 0;
}
void GAME_ON () {
GAME_MODE = true;
}
void JUMP () {
player_1.VELOCIDADE_Y_UTI = -8.8f;
}
double REDE_NEURAL_AG (double INPUT1, double INPUT2, double INPUT3, double INPUT4, REDE_NEURAL *x) {
int index = 0;
for (int a = 0; a < NUMERO_NEURONIOS_CAMADA_OCULTA; a++) {
x -> NEURONIO_HIDDEN_LAYER [a] = INPUT1 * GENES [index++] +
INPUT2 * GENES [index++] +
INPUT3 * GENES [index++] +
INPUT4 * GENES [index++];
x -> NEURONIO_HIDDEN_LAYER [a] += GENES [index++];
}
for (int b = 0; b < NUMERO_NEURONIOS_CAMADA_OCULTA; b++) {
x -> OUTPUT_NEURONIO_HIDDEN_LAYER [b] = ACTIVATION_FUNCTION_TANH (x -> NEURONIO_HIDDEN_LAYER [b]);
}
x -> OUTPUT = 0; //Muito importante ser reiniciado porque se não for, vai estar sempre a somar a cada decisão o que distorce a rede por completo! pelo uso de +=
//Em cima já não acontece porque usamos = e dessa maneira sobrepõe o resultado.
for (int c = 0; c < NUMERO_NEURONIOS_CAMADA_OCULTA; c++) {
x -> OUTPUT += x -> OUTPUT_NEURONIO_HIDDEN_LAYER [c] * GENES [index++];
}
x -> OUTPUT += GENES [index++];
x -> OUTPUT = ACTIVATION_FUNCTION_SIGMOID (x -> OUTPUT);
return x -> OUTPUT;
}
void RUN_REDE_NEURAL (REDE_NEURAL *x, TUBOS y [], Texture2D Flappy_AI) { //Structs são passadas por valor (cópia), arrays são passados como ponteiros automaticamente.
int NEXTPIPE = -1;
x -> VELOCIDADE_Y += GRAVIDADE;
x -> POS_INICIAL_Y += x -> VELOCIDADE_Y;
Vector2 FLAPPYHITBOX = {
x -> POS_INICIAL_X + (Flappy_AI.width * 0.45 / 2 + 10),
x -> POS_INICIAL_Y + (Flappy_AI.height * 0.45 / 2)
};
for (int i = 0; i < NUMERO_TUBOS; i++) {
Rectangle PIPECIMA = {y[i].POS_EIXO_X, 0, 95, y[i].ALTURA_TUBO_CIMA};
Rectangle PIPEBAIXO = {y[i].POS_EIXO_X + 2, (ALTURA - y[i].ALTURA_TUBO_BAIXO), 95, y[i].ALTURA_TUBO_BAIXO};
COLISAO_CIMA_REDE_NEURAL = CheckCollisionCircleRec (FLAPPYHITBOX, 20, PIPECIMA);
COLISAO_BAIXO_REDE_NEURAL = CheckCollisionCircleRec (FLAPPYHITBOX, 20, PIPEBAIXO);
if (y[i].POS_EIXO_X + 95 >= x -> POS_INICIAL_X && NEXTPIPE == -1) {
NEXTPIPE = i;
}
if (COLISAO_CIMA_REDE_NEURAL == true || COLISAO_BAIXO_REDE_NEURAL == true) {
GAME_MODE_AI = false;
}
x -> CENTRO_COORDENADA_PIPE = y[i].ALTURA_TUBO_CIMA + (TUBO_GAP / 2.0); //À medida que os pipes avançam
}
if (NEXTPIPE != -1) {
x -> X_TO_NEXTPIPE = (y[NEXTPIPE].POS_EIXO_X + 95) - x -> POS_INICIAL_X;
x -> CENTRO_COORDENADA_PIPE = y[NEXTPIPE].ALTURA_TUBO_CIMA + (TUBO_GAP / 2.0);
}
if (x -> POS_INICIAL_Y > (ALTURA - 45) || x -> POS_INICIAL_Y <= 0) {
GAME_MODE_AI = false;
}
if (NEXTPIPE == -1 && x -> VIVO == true) {
WINNER = true;
}
double DECISAO = REDE_NEURAL_AG ((double) x -> POS_INICIAL_Y / (double) ALTURA, //Input 1 Normalizado
(double) x -> X_TO_NEXTPIPE / (double) LARGURA, // Input 2 Normalizado
(double) x -> VELOCIDADE_Y / (double) 10.0, // Input 3 Normalizado
((double) x -> CENTRO_COORDENADA_PIPE - (double) x -> POS_INICIAL_Y) / (double) ALTURA, // Input 4 Normalizado
x); //Ponteiro para a struct única
if (DECISAO > 0.5) {
x -> VELOCIDADE_Y = -8.8f;
}
}
void RUN_PLAYER1 (TUBOS y [], Texture2D Flappy_UTI) {
int NEXTPIPE2 = -1;
player_1.VELOCIDADE_Y_UTI += GRAVIDADE;
player_1.POS_INICIAL_Y_UTI += player_1.VELOCIDADE_Y_UTI;
/*if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) {
JUMP();
};
*/
Vector2 FLAPPYHITBOX_UTI = {
player_1.POS_INICIAL_X_UTI + (Flappy_UTI.width * 0.45 / 2 + 10),
player_1.POS_INICIAL_Y_UTI + (Flappy_UTI.height * 0.45 / 2)
};
for (int i = 0; i < NUMERO_TUBOS; i++) {
Rectangle PIPECIMA = {y[i].POS_EIXO_X, 0, 95, y[i].ALTURA_TUBO_CIMA};
Rectangle PIPEBAIXO = {y[i].POS_EIXO_X + 2, (ALTURA - y[i].ALTURA_TUBO_BAIXO), 95, y[i].ALTURA_TUBO_BAIXO};
COLISAO_CIMA_UTILIZADOR = CheckCollisionCircleRec (FLAPPYHITBOX_UTI, 20, PIPECIMA);
COLISAO_BAIXO_UTILIZADOR = CheckCollisionCircleRec (FLAPPYHITBOX_UTI, 20, PIPEBAIXO);
if (y[i].POS_EIXO_X + 95 >= player_1.POS_INICIAL_X_UTI && NEXTPIPE2 == -1) {
NEXTPIPE2 = i;
SCORE = NEXTPIPE2;
}
if (COLISAO_CIMA_UTILIZADOR == true || COLISAO_BAIXO_UTILIZADOR == true) {
GAME_MODE = false;
RESTART_GAME = true;
}
}
if (player_1.POS_INICIAL_Y_UTI > (ALTURA - 45) || player_1.POS_INICIAL_Y_UTI <= 0) {
GAME_MODE = false;
RESTART_GAME = true;
}
if (NEXTPIPE2 == -1 && player_1.VIVO_UTI == true) {
WINNER = true;
RESTART_GAME = true;
}
}