Skip to content

Commit

Permalink
added ai play screen
Browse files Browse the repository at this point in the history
  • Loading branch information
Dpbm committed May 13, 2024
1 parent eda0ff5 commit 690fa93
Show file tree
Hide file tree
Showing 11 changed files with 149 additions and 27 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ add_library(board ${BOARD_FILE})
add_library(screens ${SCREENS_FILES})
add_library(players ${PLAYERS_FILES})
target_link_libraries(players machine matrix genetic helpers ${SDL2_LIBRARIES})
target_link_libraries(screens players board helpers genetic ${SDL2_LIBRARIES} SDL2_ttf nfd)
target_link_libraries(screens players board helpers genetic matrix ${SDL2_LIBRARIES} SDL2_ttf nfd)
target_link_libraries(board helpers players)

target_link_libraries(snake PRIVATE helpers board screens players ${SDL2_LIBRARIES} SDL2_ttf)
Expand Down
28 changes: 22 additions & 6 deletions game/players/ai_player.cpp
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
#include <ctime>
#include <cstdint>
#include <sstream>
#include <string>
#include "ai_player.h"
#include "../../machine/weights.h"
#include "../../genetic/chromosome.h"
#include "../../helpers/utils.h"
#include "../../matrix/matrix.h"
#include "player.h"

using std::to_string;
using std::stringstream;
using Machine::Weights;
using Genetic::Chromosome;
using Utils::vec2;
using Matrices::Matrix;

namespace Players{
AIPlayer::AIPlayer(uint8_t board_w, uint8_t board_h) : Player(board_w, board_h){
Expand All @@ -31,9 +35,7 @@ namespace Players{
}

void AIPlayer::setup_nn(){
this->input_layer->set_values(this->input_data);

this->nn->add_layer(this->input_layer);
this->nn->add_layer(2);
this->nn->add_layer(4);
this->nn->add_layer(4);

Expand Down Expand Up @@ -79,8 +81,9 @@ namespace Players{
int16_t fx = food.x;
int16_t fy = food.y;

this->input_data->update_value(0, 0, (double)(px-fx)/w);
this->input_data->update_value(0, 1, (double)(py-fy)/h);
Matrix* input = this->nn->get_input_layer()->get_values();
input->update_value(0, 0, (double)(px-fx)/w);
input->update_value(0, 1, (double)(py-fy)/h);
}


Expand All @@ -90,7 +93,6 @@ namespace Players{

void AIPlayer::update_dir(){
Matrix* output = this->nn->get_output_layer()->get_values();

size_t new_dir = 0;
double biggest = 0;
for(size_t i = 0; i < 4; i++){
Expand All @@ -116,6 +118,20 @@ namespace Players{
filename << ".wg";
this->nn->save_weights(filename.str());
}

void AIPlayer::save_weights(uint32_t gen){
time_t now = time(0);
string time = asctime(localtime(&now));
//remove the $\n at the end of the string
time.pop_back();

stringstream filename;
filename << time;
filename << "gen-";
filename << to_string(gen);
filename << ".wg";
this->nn->save_weights(filename.str());
}

AIPlayer::~AIPlayer(){
delete this->chromosome;
Expand Down
5 changes: 2 additions & 3 deletions game/players/ai_player.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
using Genetic::Chromosome;
using Matrices::Matrix;
using Machine::NN;
using Machine::Layer;
using Utils::vec2;

namespace Players{
Expand All @@ -23,6 +22,8 @@ namespace Players{
~AIPlayer();

void save_weights();
void save_weights(uint32_t gen);

void load_genes_into_weights();
void update_input_data(const vec2& food, uint16_t w, uint16_t h);
void compute_next_dir();
Expand All @@ -32,8 +33,6 @@ namespace Players{
private:
Chromosome* chromosome = nullptr;
NN* nn = new NN;
Layer* input_layer = new Layer(2, true);
Matrix* input_data = new Matrix(2, 1);

void setup_nn();
void setup_chromosome();
Expand Down
2 changes: 1 addition & 1 deletion game/screens/ai_screen.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ namespace Screens{
uint8_t board_w = 43;
uint8_t board_h = 30;

uint8_t gen_time = 30; //in seconds
uint8_t gen_time = 40; //in seconds
uint32_t control_tick = 0;

Population population{3000, board_w, board_h, 20};
Expand Down
100 changes: 92 additions & 8 deletions game/screens/ai_screen_play.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@
#include <SDL2/SDL_ttf.h>
#include <SDL2/SDL_surface.h>
#include <SDL2/SDL_stdinc.h>
#include <string>
#include "../../nativefiledialog-extended/src/include/nfd.h"
#include "screens.h"
#include "start_screen.h"
#include "ai_screen_play.h"
#include "../../helpers/utils.h"
#include "../../helpers/constants.h"

using std::to_string;
using std::cout;
using std::endl;
using std::size_t;
Expand All @@ -19,39 +23,119 @@ namespace Screens {
AIPlayScreen::AIPlayScreen(SDL_Renderer* render) : Screen(render){

if(NFD_Init() != NFD_OKAY){
cout << "Failed on load NFD" << endl;
cout << "Failed on load NFD " << NFD_GetError() << endl;
exit(1);
}

if(!this->font){
cout << "Failed on getting font!" << TTF_GetError() << endl;
exit(1);
}

nfdchar_t *outPath;
nfdresult_t result = NFD_OpenDialog(&outPath, NULL, 0, NULL);
nfdresult_t result = NFD_OpenDialog(&this->nn_path, NULL, 0, NULL);

if(result == NFD_OKAY){
this->player = new AIPlayer(this->board_w, this->board_h, parse_nn(outPath));
this->player = new AIPlayer(this->board_w, this->board_h, parse_nn(this->nn_path));
this->board.add_player(this->player);
NFD_FreePath(outPath);
return;
}else{
cout << "You must select a weights file!" << endl;
exit(1);
}

cout << "You must select a weights file!" << endl;
exit(1);

SDL_Surface* score_text_surface = TTF_RenderText_Solid(this->font, "AI Score", this->text_color);
this->score_text_texture = SDL_CreateTextureFromSurface(render, score_text_surface);
this->score_text_shape = SDL_Rect{20, 20, score_text_surface->w, score_text_surface->h};
SDL_FreeSurface(score_text_surface);

if(this->score_text_texture == nullptr){
cout << "Failed on creating score text texture!" << SDL_GetError() << endl;
exit(1);
}

this->left_padding = 10 * SQUARE_SIDE;
}

void AIPlayScreen::execute(bool& game_loop){
bool won = this->player->get_score() >= this->max_score;
this->finished_game = won || this->player->is_dead();
if(this->finished_game){
SDL_Surface* game_over_surface = TTF_RenderText_Solid(this->title_font, won ? "AI Wins!!!" : "Game Over", this->text_color);
SDL_Texture* game_over_texture = SDL_CreateTextureFromSurface(this->render, game_over_surface);
SDL_Rect game_over_shape = SDL_Rect{(WIDTH/2)-(game_over_surface->w/2), (HEIGHT/2)-(game_over_surface->h), game_over_surface->w, game_over_surface->h};
SDL_FreeSurface(game_over_surface);

SDL_Surface* reset_surface = TTF_RenderText_Solid(this->font, "Press 'r' to reset", this->text_color);
SDL_Texture* reset_texture = SDL_CreateTextureFromSurface(this->render, reset_surface);
SDL_Rect reset_shape = SDL_Rect{(WIDTH/2)-(reset_surface->w/2), (HEIGHT/2)+(reset_surface->h)+20, reset_surface->w, reset_surface->h};
SDL_FreeSurface(reset_surface);

SDL_Surface* back_surface = TTF_RenderText_Solid(this->font, "Press 'g' to back to the start screen", this->text_color);
SDL_Texture* back_texture = SDL_CreateTextureFromSurface(this->render, back_surface);
SDL_Rect back_shape = SDL_Rect{(WIDTH/2)-(back_surface->w/2), (HEIGHT/2)+(back_surface->h)+50, back_surface->w, back_surface->h};
SDL_FreeSurface(back_surface);

SDL_SetRenderDrawColor(this->render, 0, 0, 0, 255);
SDL_RenderCopy(this->render, game_over_texture, NULL, &game_over_shape);
SDL_RenderCopy(this->render, reset_texture, NULL, &reset_shape);
SDL_RenderCopy(this->render, back_texture, NULL, &back_shape);
SDL_DestroyTexture(game_over_texture);
SDL_DestroyTexture(back_texture);
SDL_DestroyTexture(reset_texture);
return;
}

this->player->update_input_data(this->board.get_food(), this->board_w, this->board_h);
this->player->compute_next_dir();
this->player->update_dir();
this->board.update_player_pos();
this->render_board(&this->board);

SDL_SetRenderDrawColor(this->render, 0, 0, 0, 255);
SDL_RenderCopy(this->render, this->score_text_texture, NULL, &this->score_text_shape);

if(this->score_texture != nullptr)
SDL_DestroyTexture(this->score_texture);
SDL_Surface* score_surface = TTF_RenderText_Solid(this->font, to_string(this->player->get_score()).c_str(), this->text_color);
this->score_texture = SDL_CreateTextureFromSurface(this->render, score_surface);
this->score_shape = SDL_Rect{20, 60, score_surface->w, score_surface->h};
SDL_FreeSurface(score_surface);

SDL_RenderCopy(this->render, this->score_texture, NULL, &this->score_shape);
}

Screen* AIPlayScreen::key_event(const SDL_Keycode& key){
switch (key) {
case SDLK_g:
if(this->finished_game)
return new StartScreen(this->render);

case SDLK_r:
if(this->finished_game)
this->reset();

default:
break;
}
return nullptr;
}

void AIPlayScreen::close_event(){
}

void AIPlayScreen::reset(){
this->finished_game = false;
delete this->player;
this->player = new AIPlayer{this->board_w, this->board_h, parse_nn(this->nn_path)};
this->board.add_player(this->player);
this->board.random_food();
}

AIPlayScreen::~AIPlayScreen(){
delete this->player;
NFD_Quit();
NFD_FreePath(this->nn_path);
SDL_DestroyTexture(this->score_texture);
SDL_DestroyTexture(this->score_text_texture);
}
}
18 changes: 16 additions & 2 deletions game/screens/ai_screen_play.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "screens.h"
#include "../board.h"
#include "../players/ai_player.h"
#include "../../nativefiledialog-extended/src/include/nfd.h"

using Screens::Screen;
using Game::Board;
Expand All @@ -21,12 +22,25 @@ namespace Screens{
void close_event();

private:
uint8_t board_w = 43;
uint8_t board_w = 45;
uint8_t board_h = 30;
Board board{board_w, board_h};


bool finished_game = false;

uint16_t max_score = 1000;
AIPlayer* player = nullptr;
nfdchar_t* nn_path = nullptr;

TTF_Font* font = TTF_OpenFont("./assets/pressstart.ttf", 20);
TTF_Font* title_font = TTF_OpenFont("./assets/pressstart.ttf", 40);

SDL_Rect score_text_shape;
SDL_Rect score_shape;
SDL_Texture* score_text_texture = nullptr;
SDL_Texture* score_texture = nullptr;

void reset();

};
};
11 changes: 9 additions & 2 deletions genetic/population.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,10 @@ namespace Genetic{
void Population::next_gen(){
cout << "gen: " << this->gen << endl;
cout << "best fit: " << this->best_fitness << endl;
cout << "best score: " << this->best_score << endl << endl;
cout << "best score: " << this->best_score << endl;

cout << "saving weights..." << endl << endl;
this->get_best_individual()->player->save_weights(this->gen);

this->gen++;
this->best_score = 0;
Expand All @@ -170,17 +173,20 @@ namespace Genetic{
Individual** parents = this->select_parents();
Chromosome* offspring = this->generate_offspring(parents[0]->player->get_chromossome(), parents[1]->player->get_chromossome());
delete parents;
cout << "no problem on delete parents..." << endl;
Gene* offspring_genes = offspring->get_genes();

uint64_t offspring_ch_size = offspring->get_size();

this->clear();
cout << "no problem on delete pointers" << endl;
this->individuals.clear();
cout << "no problem on clear individuals" << endl;
this->food_positions.clear();
cout << "no problem on clear food" << endl;
this->generate_food_positions();
vec2 first_food_pos = this->food_positions.at(0);

this->individuals.clear();
for(size_t i = 0; i < this->total_ind; i++){
Individual* ind = new Individual;
ind->board = new Board(board_w, board_h);
Expand All @@ -204,6 +210,7 @@ namespace Genetic{

this->individuals.push_back(ind);
}
cout << "no problem at the end of the function" << endl << endl;
}

Individual** Population::select_parents(){
Expand Down
2 changes: 1 addition & 1 deletion machine/layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ namespace Machine{
delete this->values;
this->values = values;
}

void Layer::set_activation_function(void(*activation)(Matrix*)){
if(this->input)
throw invalid_argument("Input layer must not have a activation function!");
Expand Down
4 changes: 4 additions & 0 deletions machine/machine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,8 @@ namespace Machine {
Layer* NN::get_output_layer(){
return this->layers.at(this->total_layers-1);
}

Layer* NN::get_input_layer(){
return this->layers.at(0);
}
}
1 change: 1 addition & 0 deletions machine/machine.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ namespace Machine {
~NN();
void feedforward();
Layer* get_output_layer();
Layer* get_input_layer();
void load_weights(vector<Matrix*> new_weights);

private:
Expand Down
3 changes: 0 additions & 3 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@ using Screens::Screen;
using Screens::StartScreen;

int main(){
setlocale(LC_ALL, "en_US.UTF-8");
setlocale(LC_NUMERIC, "en_US.UTF-8");

if(SDL_Init(SDL_INIT_EVERYTHING) < 0){
cout << "SDL_Init Error: " << SDL_GetError() << endl;
return 1;
Expand Down

0 comments on commit 690fa93

Please sign in to comment.