Skip to content

Commit 690fa93

Browse files
committed
added ai play screen
1 parent eda0ff5 commit 690fa93

File tree

11 files changed

+149
-27
lines changed

11 files changed

+149
-27
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ add_library(board ${BOARD_FILE})
4949
add_library(screens ${SCREENS_FILES})
5050
add_library(players ${PLAYERS_FILES})
5151
target_link_libraries(players machine matrix genetic helpers ${SDL2_LIBRARIES})
52-
target_link_libraries(screens players board helpers genetic ${SDL2_LIBRARIES} SDL2_ttf nfd)
52+
target_link_libraries(screens players board helpers genetic matrix ${SDL2_LIBRARIES} SDL2_ttf nfd)
5353
target_link_libraries(board helpers players)
5454

5555
target_link_libraries(snake PRIVATE helpers board screens players ${SDL2_LIBRARIES} SDL2_ttf)

game/players/ai_player.cpp

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
#include <ctime>
22
#include <cstdint>
33
#include <sstream>
4+
#include <string>
45
#include "ai_player.h"
56
#include "../../machine/weights.h"
67
#include "../../genetic/chromosome.h"
78
#include "../../helpers/utils.h"
9+
#include "../../matrix/matrix.h"
810
#include "player.h"
911

12+
using std::to_string;
1013
using std::stringstream;
1114
using Machine::Weights;
1215
using Genetic::Chromosome;
1316
using Utils::vec2;
17+
using Matrices::Matrix;
1418

1519
namespace Players{
1620
AIPlayer::AIPlayer(uint8_t board_w, uint8_t board_h) : Player(board_w, board_h){
@@ -31,9 +35,7 @@ namespace Players{
3135
}
3236

3337
void AIPlayer::setup_nn(){
34-
this->input_layer->set_values(this->input_data);
35-
36-
this->nn->add_layer(this->input_layer);
38+
this->nn->add_layer(2);
3739
this->nn->add_layer(4);
3840
this->nn->add_layer(4);
3941

@@ -79,8 +81,9 @@ namespace Players{
7981
int16_t fx = food.x;
8082
int16_t fy = food.y;
8183

82-
this->input_data->update_value(0, 0, (double)(px-fx)/w);
83-
this->input_data->update_value(0, 1, (double)(py-fy)/h);
84+
Matrix* input = this->nn->get_input_layer()->get_values();
85+
input->update_value(0, 0, (double)(px-fx)/w);
86+
input->update_value(0, 1, (double)(py-fy)/h);
8487
}
8588

8689

@@ -90,7 +93,6 @@ namespace Players{
9093

9194
void AIPlayer::update_dir(){
9295
Matrix* output = this->nn->get_output_layer()->get_values();
93-
9496
size_t new_dir = 0;
9597
double biggest = 0;
9698
for(size_t i = 0; i < 4; i++){
@@ -116,6 +118,20 @@ namespace Players{
116118
filename << ".wg";
117119
this->nn->save_weights(filename.str());
118120
}
121+
122+
void AIPlayer::save_weights(uint32_t gen){
123+
time_t now = time(0);
124+
string time = asctime(localtime(&now));
125+
//remove the $\n at the end of the string
126+
time.pop_back();
127+
128+
stringstream filename;
129+
filename << time;
130+
filename << "gen-";
131+
filename << to_string(gen);
132+
filename << ".wg";
133+
this->nn->save_weights(filename.str());
134+
}
119135

120136
AIPlayer::~AIPlayer(){
121137
delete this->chromosome;

game/players/ai_player.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
using Genetic::Chromosome;
1212
using Matrices::Matrix;
1313
using Machine::NN;
14-
using Machine::Layer;
1514
using Utils::vec2;
1615

1716
namespace Players{
@@ -23,6 +22,8 @@ namespace Players{
2322
~AIPlayer();
2423

2524
void save_weights();
25+
void save_weights(uint32_t gen);
26+
2627
void load_genes_into_weights();
2728
void update_input_data(const vec2& food, uint16_t w, uint16_t h);
2829
void compute_next_dir();
@@ -32,8 +33,6 @@ namespace Players{
3233
private:
3334
Chromosome* chromosome = nullptr;
3435
NN* nn = new NN;
35-
Layer* input_layer = new Layer(2, true);
36-
Matrix* input_data = new Matrix(2, 1);
3736

3837
void setup_nn();
3938
void setup_chromosome();

game/screens/ai_screen.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ namespace Screens{
2929
uint8_t board_w = 43;
3030
uint8_t board_h = 30;
3131

32-
uint8_t gen_time = 30; //in seconds
32+
uint8_t gen_time = 40; //in seconds
3333
uint32_t control_tick = 0;
3434

3535
Population population{3000, board_w, board_h, 20};

game/screens/ai_screen_play.cpp

Lines changed: 92 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@
55
#include <SDL2/SDL_ttf.h>
66
#include <SDL2/SDL_surface.h>
77
#include <SDL2/SDL_stdinc.h>
8+
#include <string>
89
#include "../../nativefiledialog-extended/src/include/nfd.h"
910
#include "screens.h"
11+
#include "start_screen.h"
1012
#include "ai_screen_play.h"
1113
#include "../../helpers/utils.h"
14+
#include "../../helpers/constants.h"
1215

16+
using std::to_string;
1317
using std::cout;
1418
using std::endl;
1519
using std::size_t;
@@ -19,39 +23,119 @@ namespace Screens {
1923
AIPlayScreen::AIPlayScreen(SDL_Renderer* render) : Screen(render){
2024

2125
if(NFD_Init() != NFD_OKAY){
22-
cout << "Failed on load NFD" << endl;
26+
cout << "Failed on load NFD " << NFD_GetError() << endl;
27+
exit(1);
28+
}
29+
30+
if(!this->font){
31+
cout << "Failed on getting font!" << TTF_GetError() << endl;
2332
exit(1);
2433
}
2534

26-
nfdchar_t *outPath;
27-
nfdresult_t result = NFD_OpenDialog(&outPath, NULL, 0, NULL);
35+
nfdresult_t result = NFD_OpenDialog(&this->nn_path, NULL, 0, NULL);
2836

2937
if(result == NFD_OKAY){
30-
this->player = new AIPlayer(this->board_w, this->board_h, parse_nn(outPath));
38+
this->player = new AIPlayer(this->board_w, this->board_h, parse_nn(this->nn_path));
3139
this->board.add_player(this->player);
32-
NFD_FreePath(outPath);
33-
return;
40+
}else{
41+
cout << "You must select a weights file!" << endl;
42+
exit(1);
3443
}
3544

36-
cout << "You must select a weights file!" << endl;
37-
exit(1);
45+
46+
SDL_Surface* score_text_surface = TTF_RenderText_Solid(this->font, "AI Score", this->text_color);
47+
this->score_text_texture = SDL_CreateTextureFromSurface(render, score_text_surface);
48+
this->score_text_shape = SDL_Rect{20, 20, score_text_surface->w, score_text_surface->h};
49+
SDL_FreeSurface(score_text_surface);
50+
51+
if(this->score_text_texture == nullptr){
52+
cout << "Failed on creating score text texture!" << SDL_GetError() << endl;
53+
exit(1);
54+
}
55+
56+
this->left_padding = 10 * SQUARE_SIDE;
3857
}
3958

4059
void AIPlayScreen::execute(bool& game_loop){
60+
bool won = this->player->get_score() >= this->max_score;
61+
this->finished_game = won || this->player->is_dead();
62+
if(this->finished_game){
63+
SDL_Surface* game_over_surface = TTF_RenderText_Solid(this->title_font, won ? "AI Wins!!!" : "Game Over", this->text_color);
64+
SDL_Texture* game_over_texture = SDL_CreateTextureFromSurface(this->render, game_over_surface);
65+
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};
66+
SDL_FreeSurface(game_over_surface);
67+
68+
SDL_Surface* reset_surface = TTF_RenderText_Solid(this->font, "Press 'r' to reset", this->text_color);
69+
SDL_Texture* reset_texture = SDL_CreateTextureFromSurface(this->render, reset_surface);
70+
SDL_Rect reset_shape = SDL_Rect{(WIDTH/2)-(reset_surface->w/2), (HEIGHT/2)+(reset_surface->h)+20, reset_surface->w, reset_surface->h};
71+
SDL_FreeSurface(reset_surface);
72+
73+
SDL_Surface* back_surface = TTF_RenderText_Solid(this->font, "Press 'g' to back to the start screen", this->text_color);
74+
SDL_Texture* back_texture = SDL_CreateTextureFromSurface(this->render, back_surface);
75+
SDL_Rect back_shape = SDL_Rect{(WIDTH/2)-(back_surface->w/2), (HEIGHT/2)+(back_surface->h)+50, back_surface->w, back_surface->h};
76+
SDL_FreeSurface(back_surface);
77+
78+
SDL_SetRenderDrawColor(this->render, 0, 0, 0, 255);
79+
SDL_RenderCopy(this->render, game_over_texture, NULL, &game_over_shape);
80+
SDL_RenderCopy(this->render, reset_texture, NULL, &reset_shape);
81+
SDL_RenderCopy(this->render, back_texture, NULL, &back_shape);
82+
SDL_DestroyTexture(game_over_texture);
83+
SDL_DestroyTexture(back_texture);
84+
SDL_DestroyTexture(reset_texture);
85+
return;
86+
}
87+
88+
this->player->update_input_data(this->board.get_food(), this->board_w, this->board_h);
89+
this->player->compute_next_dir();
90+
this->player->update_dir();
91+
this->board.update_player_pos();
4192
this->render_board(&this->board);
93+
4294
SDL_SetRenderDrawColor(this->render, 0, 0, 0, 255);
95+
SDL_RenderCopy(this->render, this->score_text_texture, NULL, &this->score_text_shape);
96+
97+
if(this->score_texture != nullptr)
98+
SDL_DestroyTexture(this->score_texture);
99+
SDL_Surface* score_surface = TTF_RenderText_Solid(this->font, to_string(this->player->get_score()).c_str(), this->text_color);
100+
this->score_texture = SDL_CreateTextureFromSurface(this->render, score_surface);
101+
this->score_shape = SDL_Rect{20, 60, score_surface->w, score_surface->h};
102+
SDL_FreeSurface(score_surface);
103+
104+
SDL_RenderCopy(this->render, this->score_texture, NULL, &this->score_shape);
43105
}
44106

45107
Screen* AIPlayScreen::key_event(const SDL_Keycode& key){
108+
switch (key) {
109+
case SDLK_g:
110+
if(this->finished_game)
111+
return new StartScreen(this->render);
112+
113+
case SDLK_r:
114+
if(this->finished_game)
115+
this->reset();
116+
117+
default:
118+
break;
119+
}
46120
return nullptr;
47121
}
48122

49123
void AIPlayScreen::close_event(){
50124
}
51125

126+
void AIPlayScreen::reset(){
127+
this->finished_game = false;
128+
delete this->player;
129+
this->player = new AIPlayer{this->board_w, this->board_h, parse_nn(this->nn_path)};
130+
this->board.add_player(this->player);
131+
this->board.random_food();
132+
}
52133

53134
AIPlayScreen::~AIPlayScreen(){
54135
delete this->player;
55136
NFD_Quit();
137+
NFD_FreePath(this->nn_path);
138+
SDL_DestroyTexture(this->score_texture);
139+
SDL_DestroyTexture(this->score_text_texture);
56140
}
57141
}

game/screens/ai_screen_play.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "screens.h"
77
#include "../board.h"
88
#include "../players/ai_player.h"
9+
#include "../../nativefiledialog-extended/src/include/nfd.h"
910

1011
using Screens::Screen;
1112
using Game::Board;
@@ -21,12 +22,25 @@ namespace Screens{
2122
void close_event();
2223

2324
private:
24-
uint8_t board_w = 43;
25+
uint8_t board_w = 45;
2526
uint8_t board_h = 30;
2627
Board board{board_w, board_h};
27-
28+
29+
bool finished_game = false;
30+
2831
uint16_t max_score = 1000;
2932
AIPlayer* player = nullptr;
33+
nfdchar_t* nn_path = nullptr;
34+
35+
TTF_Font* font = TTF_OpenFont("./assets/pressstart.ttf", 20);
36+
TTF_Font* title_font = TTF_OpenFont("./assets/pressstart.ttf", 40);
37+
38+
SDL_Rect score_text_shape;
39+
SDL_Rect score_shape;
40+
SDL_Texture* score_text_texture = nullptr;
41+
SDL_Texture* score_texture = nullptr;
42+
43+
void reset();
3044

3145
};
3246
};

genetic/population.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,10 @@ namespace Genetic{
161161
void Population::next_gen(){
162162
cout << "gen: " << this->gen << endl;
163163
cout << "best fit: " << this->best_fitness << endl;
164-
cout << "best score: " << this->best_score << endl << endl;
164+
cout << "best score: " << this->best_score << endl;
165+
166+
cout << "saving weights..." << endl << endl;
167+
this->get_best_individual()->player->save_weights(this->gen);
165168

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

175179
uint64_t offspring_ch_size = offspring->get_size();
176180

177181
this->clear();
182+
cout << "no problem on delete pointers" << endl;
178183
this->individuals.clear();
184+
cout << "no problem on clear individuals" << endl;
179185
this->food_positions.clear();
186+
cout << "no problem on clear food" << endl;
180187
this->generate_food_positions();
181188
vec2 first_food_pos = this->food_positions.at(0);
182189

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

205211
this->individuals.push_back(ind);
206212
}
213+
cout << "no problem at the end of the function" << endl << endl;
207214
}
208215

209216
Individual** Population::select_parents(){

machine/layer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ namespace Machine{
4949
delete this->values;
5050
this->values = values;
5151
}
52-
52+
5353
void Layer::set_activation_function(void(*activation)(Matrix*)){
5454
if(this->input)
5555
throw invalid_argument("Input layer must not have a activation function!");

machine/machine.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,4 +146,8 @@ namespace Machine {
146146
Layer* NN::get_output_layer(){
147147
return this->layers.at(this->total_layers-1);
148148
}
149+
150+
Layer* NN::get_input_layer(){
151+
return this->layers.at(0);
152+
}
149153
}

machine/machine.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ namespace Machine {
2626
~NN();
2727
void feedforward();
2828
Layer* get_output_layer();
29+
Layer* get_input_layer();
2930
void load_weights(vector<Matrix*> new_weights);
3031

3132
private:

0 commit comments

Comments
 (0)