Skip to content

Instantly share code, notes, and snippets.

@ollewelin
Last active November 17, 2017 20:43
Show Gist options
  • Save ollewelin/042de58c2612393eb3d765d6f324a87d to your computer and use it in GitHub Desktop.
Save ollewelin/042de58c2612393eb3d765d6f324a87d to your computer and use it in GitHub Desktop.
Reinforced Machine Learning Pinball Game BASIC VERSION
/// Add gamma parameter and use dice and network probability for make action decisions UP/DOWN.
/// Example of Reinforced Machine Learning attached on a simple Pinball game
/// The enviroment (enviroment = data feedback) for the Agient (Agient = machine learning system)
/// is the raw pixels 50x50 pixels (2500 input nodes) and 200 hidden nodes on 100 frames
/// So the input to hidden weights is 50x50x100x200 x4 bytes (float) = is 200Mbytes huges but it work anyway!!
///Enhancment to do in future.
///TODO: Add some layers of Convolutions (with unsupervised Learning for learning feature patches) will probably enhance preformance.
///TODO: Maybe add bias weigth is a good idee to enhance preformance or stability during training.
///#define USE_PRINT_OUTPUT_NODE_VALUE ///Uncomment this to see print out of output node value. Only used for evaluation
#include <opencv2/highgui/highgui.hpp> // OpenCV window I/O
#include <opencv2/imgproc/imgproc.hpp> //
#include <stdio.h>
///#include <raspicam/raspicam_cv.h>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp> // Basic OpenCV structures (cv::Mat, Scalar)
#include <cstdlib>
#include <ctime>
#include <math.h> // exp
#include <stdlib.h>// exit(0);
#include <iostream>
using namespace std;
using namespace cv;
#include "pinball_game.hpp"
const float Relu_neg_gain = 0.01f;///A small positive value here will prevent ReLU neuoron from dying. 0.0f pure rectify (1.0 full linear = nothing change)
float relu(float input)
{
float output=0;
output = input;
if(input < 0.0)
{
output = input * Relu_neg_gain;
}
return output;
}
int main()
{
FILE *fp2;
int nr_of_episodes=0;
int auto_save_w_counter =100;
const int auto_save_after = 200;///Auto Save weights after this number of episodes
int show_w_counter =19;///Show the weights graphical not ever episodes (to save CPU time)
const int show_w_after = 20;///Show the weights graphical not ever episodes (to save CPU time)
float pix2hid_learning_rate = 0.03f;///0.001
float hid2out_learning_rate = 0.03f;///0.001
float gamma = 0.97f;
printf("Reinforcment Learning Evaluation of pixels data input from a simple Pinball game\n");
int pixel_height = 50;///The input data pixel height, note game_Width = 220
int pixel_width = 50;///The input data pixel width, note game_Height = 200
Mat resized_grapics, test, pix2hid_weight, hid2out_weight;
Size size(pixel_width,pixel_height);//the dst image size,e.g.100x100
pinball_game gameObj1;///Instaniate the pinball game
gameObj1.init_game();///Initialize the pinball game with serten parametrers
gameObj1.slow_motion=0;///0=full speed game. 1= slow down
///========== Setup weights and nodes for the Reinforcemnt learning network ==========================
///This will contain all the training weigts for training. It will have exact same size as the grapics * number of frames * hidden nodes
///Use also here OpenCV Mat so it is easy to Visualize some of the data as well as store the weights
int Nr_of_hidden_nodes = 200;///Number of hidden nodes on one frame weight
float *input_node_stored;///This will have a record of all frames of the resized_grapics used for weight updates
input_node_stored = new float[pixel_width * pixel_height * gameObj1.nr_of_frames];
int visual_nr_of_frames = 20;///Visualization weights of a only few frames othewize the image will be to large
int visual_nr_of_hid_node = 20;///Visualization weights of a only few hidden nodes othewize the image will be to large
pix2hid_weight.create(pixel_height * visual_nr_of_hid_node, pixel_width * visual_nr_of_frames, CV_32FC1);///Visualization weights of a only few frames and hidden nodes othewize the image will be to large CV_32FC1 is pixel format
float *pix2hid_weightB;///File data read/write connect to tied weights
pix2hid_weightB = new float[pixel_height * pixel_width * Nr_of_hidden_nodes * gameObj1.nr_of_frames];///File data is same size as all tied weights pix2hid_weight.create
float *hidden_node;
hidden_node = new float[gameObj1.nr_of_frames * Nr_of_hidden_nodes];///200 hidden nodes and 100 frames for example
float *hidden_delta;
hidden_delta = new float[gameObj1.nr_of_frames * Nr_of_hidden_nodes];///200 hidden nodes and 100 frames for example
hid2out_weight.create(gameObj1.nr_of_frames, Nr_of_hidden_nodes, CV_32FC1);///Use also here OpenCV Mat so it is easy to Visualize the data as well as store weights
float *hid2out_weightB;
hid2out_weightB = new float[gameObj1.nr_of_frames * Nr_of_hidden_nodes];
float *output_node;
output_node = new float[gameObj1.nr_of_frames];
float *output_delta;
output_delta = new float[gameObj1.nr_of_frames];
float *action;
action = new float[gameObj1.nr_of_frames];
///Some reports to user
printf("Number of hidden nodes to one frames = %d\n", Nr_of_hidden_nodes);
printf("Total number of hidden nodes fo all frames together = %d\n", gameObj1.nr_of_frames * Nr_of_hidden_nodes);
printf("Number of output nodes alway equal to the number of frames on one episode = %d\n", gameObj1.nr_of_frames);
///===================================================================================================
///=================== index variable for the weights ====================
int ix=0;///index to f_data[ix]
///=======================================================================
///============ Prepare pointers to make it possible to direct acces Mat data matrix ==================
test = gameObj1.gameGrapics.clone();
resize(test, resized_grapics, size);
float *zero_ptr_res_grap = resized_grapics.ptr<float>(0);///zero_... Always point at first pixel
float *index_ptr_res_grap = resized_grapics.ptr<float>(0);///index_... Adjusted to abritary pixel
float *zero_ptr_pix2hid_w = pix2hid_weight.ptr<float>(0);///Only used for visualization of weights
float *index_ptr_pix2hid_w = pix2hid_weight.ptr<float>(0);///Only used for visualization of weights
float *zero_ptr_hid2out_w = hid2out_weight.ptr<float>(0);///Only used for visualization of weights
float *index_ptr_hid2out_w = hid2out_weight.ptr<float>(0);///Only used for visualization of weights
///====================================================================================================
///================ Initialize weight with random noise =====================================
printf("Insert noise to weights. Please wait...\n");
srand (static_cast <unsigned> (time(0)));///Seed the randomizer (need to do only once)
float start_weight_noise_range = 0.05f;
float Rando=0.0f;
for(int i=0; i<(pixel_height * pixel_width * Nr_of_hidden_nodes * gameObj1.nr_of_frames); i++)
{
Rando = (float) (rand() % 65535) / 65536;//0..1.0 range
Rando -= 0.5f;
Rando *= start_weight_noise_range;
pix2hid_weightB[i] = Rando;///Insert the noise to the weight pixel to hidden
}
printf("Noise to the weight pixel to hidden is inserted\n");
for(int i=0; i<(gameObj1.nr_of_frames * Nr_of_hidden_nodes); i++)
{
Rando = (float) (rand() % 65535) / 65536;//0..1.0 range
Rando -= 0.5f;
Rando *= start_weight_noise_range;
hid2out_weightB[i] = Rando;
}
printf("Noise to the weight hidden to output node is inserted\n");
///==================== End of Initialize weight with random noise ===========================
///============ Regardning Load weights to file ==========================
char filename[100];
printf("Would you like to load stored weights, pix2hid_weight.dat and hid2out_weight.dat <Y>/<N> \n");
char answer_character;
answer_character = getchar();
if(answer_character == 'Y' || answer_character == 'y')
{
sprintf(filename, "pix2hid_weight.dat");
fp2 = fopen(filename, "r");
if (fp2 == NULL)
{
printf("Error while opening file pix2hid_weight.dat");
exit(0);
}
printf("Start so load pix2hid_weight.dat Please wait... The file size is = %d bytes\n", (sizeof pix2hid_weightB[0]) * (pixel_height * pixel_width * Nr_of_hidden_nodes * gameObj1.nr_of_frames));
fread(pix2hid_weightB, sizeof pix2hid_weightB[0], (pixel_height * pixel_width * Nr_of_hidden_nodes * gameObj1.nr_of_frames), fp2);///+1 is because the bias. So the nr FLx_size+1 is the bias weight.
fclose(fp2);
printf("weights are loaded from pix2hid_weight.dat file\n");
sprintf(filename, "hid2out_weight.dat");
fp2 = fopen(filename, "r");
if (fp2 == NULL)
{
printf("Error while opening file hid2out_weight.dat");
exit(0);
}
printf("Start so load hid2out_weight.dat Please wait... The file size is = %d bytes\n", (sizeof hid2out_weightB[0]) * (gameObj1.nr_of_frames * Nr_of_hidden_nodes));
fread(hid2out_weightB, sizeof hid2out_weightB[0], (gameObj1.nr_of_frames * Nr_of_hidden_nodes), fp2);
fclose(fp2);
printf("weights are loaded from hid2out_weight.dat file\n");
}
///============ End of Regardning Load weights to file ==========================
while(1)
{
float dot_product = 0.0f;
gameObj1.start_episode();///Staring a new game turn
float action_dice=0;///
for(int frame_g=0; frame_g<gameObj1.nr_of_frames; frame_g++) ///Loop throue each of the 100 frames
{
action_dice = (float) (rand() % 65535) / 65536;///Update 2017-09-02 9:30 ===== Use dice with the policy network output probability for what action should be done
if(output_node[frame_g-1] > action_dice)
{
action[frame_g] = 1.0f;
gameObj1.move_up = 1;
}
else
{
action[frame_g] = 0.0f;
gameObj1.move_up = 0;
}
output_node[frame_g] = 0.0f;///Start with clear this node
gameObj1.frame = frame_g;
gameObj1.run_episode();
test = gameObj1.gameGrapics.clone();
resize(test, resized_grapics, size);
///=============== Forward data for this frame ==================
///Make the Dot product to this frames hidden nodes and output node
for(int i=0; i<Nr_of_hidden_nodes; i++)
{
hidden_node[frame_g * Nr_of_hidden_nodes + i] = 0.0f;///Start with clear this value before sum up the dot product
dot_product = 0.0f;///Start with clear this value before sum up the dot product
for(int j=0; j<(pixel_height * pixel_width); j++)
{
ix = ((pixel_width * Nr_of_hidden_nodes) * (pixel_height * frame_g + j/pixel_width) + (pixel_width * i) + j%pixel_width);///Prepare the index to point on the right place in the weight matrix pix2hid_weightB[]
index_ptr_res_grap = zero_ptr_res_grap + j;///Prepare the pointer address to point on the right place on the grapical image of this grapical frame
dot_product += pix2hid_weightB[ix] * (*index_ptr_res_grap);///Make the dot product of Weights * Game grapichs
input_node_stored[pixel_width * pixel_height * frame_g + j] = (*index_ptr_res_grap);///Save this grame grapich pixel must read this pixel later when update weights
}
///Relu this dot product
dot_product = relu(dot_product);
hidden_node[frame_g * Nr_of_hidden_nodes + i] = dot_product;///Put this finnish data in the hidden node neuron
ix = (frame_g * Nr_of_hidden_nodes + i);
output_node[frame_g] += hid2out_weightB[ix] * dot_product;///Take this hidden node data how is for the moment => hidden_node[frame_g * Nr_of_hidden_nodes + i] = dot_product;
}
output_node[frame_g] = 1.0/(1.0 + exp(-(output_node[frame_g])));///Sigmoid function. x = 1.0/(1.0 + exp(-(x)))
///=============== End Forward data for this frame ==================
imshow("resized_grapics", resized_grapics);/// resize(src, dst, size);
waitKey(1);
}
#ifdef USE_PRINT_OUTPUT_NODE_VALUE
for(int i=0; i<gameObj1.nr_of_frames; i++)
{
printf("output_node[frame nr %d] = %f\n", gameObj1.nr_of_frames-1-i, output_node[gameObj1.nr_of_frames-1-i]);
}
#endif // USE_PRINT_OUTPUT_NODE_VALUE
///========== Auto save weights to files ====================
if(auto_save_w_counter>auto_save_after)
{
auto_save_w_counter=0;
sprintf(filename, "pix2hid_weight.dat");
fp2 = fopen(filename, "w+");
if (fp2 == NULL)
{
printf("Error while opening file pix2hid_weight.dat");
exit(0);
}
printf("Start so save pix2hid_weight.dat Please wait... The file size is = %d bytes\n", (sizeof pix2hid_weightB[0]) * (pixel_height * pixel_width * Nr_of_hidden_nodes * gameObj1.nr_of_frames));
fwrite(pix2hid_weightB, sizeof pix2hid_weightB[0], (pixel_height * pixel_width * Nr_of_hidden_nodes * gameObj1.nr_of_frames), fp2);
fclose(fp2);
printf("weights are saved at hid2out_weight.dat file\n");
sprintf(filename, "hid2out_weight.dat");
fp2 = fopen(filename, "w+");
if (fp2 == NULL)
{
printf("Error while opening file hid2out_weight.dat");
exit(0);
}
printf("Start so save hid2out_weight.dat Please wait... The file size is = %d bytes\n", (sizeof hid2out_weightB[0]) * (gameObj1.nr_of_frames * Nr_of_hidden_nodes));
fwrite(hid2out_weightB, sizeof hid2out_weightB[0], (gameObj1.nr_of_frames * Nr_of_hidden_nodes), fp2);
fclose(fp2);
printf("weights are saved at hid2out_weight.dat file\n");
}
else
{
auto_save_w_counter++;
}
///========== End Auto save weights to files ====================
///========== Show visualization of the weights not nessesary ==========
if(show_w_counter>show_w_after)
{
show_w_counter=0;
for(int i=0; i<(gameObj1.nr_of_frames * Nr_of_hidden_nodes); i++)
{
///Visualization of all hid2out weights
index_ptr_hid2out_w = zero_ptr_hid2out_w + i;
(*index_ptr_hid2out_w) = hid2out_weightB[i] + 0.5f;///(*index_ptr_hid2out_w) is the pointer to Mat hid2out_weight. +0.5 make a grayscale with gray center 0.5;
}
///int start_show_frame = gameObj1.nr_of_frames - visual_nr_of_frames-1;///Show only the last (20 = visual_nr_of_frames) frame weights
int show_ever5frame_start =0;
for(int si=0; si<(pixel_height * visual_nr_of_hid_node * pixel_width * visual_nr_of_frames); si++) ///si = visualize Mat itterator
{
///Visualize pix2hid weight reagrding only few frames and few hidden nodes connections
///pix2hid_weight.create(pixel_height * visual_nr_of_hid_node, pixel_width * visual_nr_of_frames, CV_32FC1);///
///The pix2hid_weightB[ix] is organized like this;
///ix = ((pixel_width * Nr_of_hidden_nodes) * (pixel_height * frame_g + j/pixel_width) + (pixel_width * i) + j%pixel_width);
///where..
///j is itterator for (pixel_height * pixel_width) is the pixel area of the game (shrinked are rezised image)
///i is itterator for Nr_of_hidden_nodes
///frame_g is of course the frame number
int vis_colum = 0;
vis_colum = si%(pixel_width * visual_nr_of_hid_node);
int vis_row = 0;
vis_row = si/(pixel_width * visual_nr_of_hid_node);
index_ptr_pix2hid_w = zero_ptr_pix2hid_w + si;
///Map over frome the large weight vevtor to visualize Mat
///============== This make so each patch row have a jump by 5 frames in the game ===
show_ever5frame_start = (si/(pixel_height * visual_nr_of_hid_node * pixel_width)) * ((gameObj1.nr_of_frames-1) / visual_nr_of_frames);
///=====================================================================
(*index_ptr_pix2hid_w) = pix2hid_weightB[(vis_row + show_ever5frame_start*pixel_height) * (pixel_width * Nr_of_hidden_nodes) + vis_colum%(pixel_width * Nr_of_hidden_nodes)];///Map over phuu...
(*index_ptr_pix2hid_w) += 0.5f;///make gray scale center at 0.5f
}
imshow("pix2hid_weight", pix2hid_weight);///Only few weights showed
imshow("hid2out_weight", hid2out_weight);
}
else
{
show_w_counter++;
}
///========== End Show visualization of the weights =================
printf("nr_of_episodes =%d\n", nr_of_episodes);
nr_of_episodes++;
float rewards =0.0f;
if(gameObj1.win_this_game == 1)
{
rewards = +4.0f;///Yea.. the Ageint win this episode
}
else
{
rewards = -1.0f;///We lose this episode
}
///================== Make the backprop now when the hole episode is done ==============
for(int frame_g = gameObj1.nr_of_frames; frame_g>0; true) ///This loop throue from last frame to first, will only go thorue here to make backpropagate (not play game in the gameObj1) {
{
frame_g--;///Go from last frame (frame 99) to first frame (frame 0) because then we will decrease the rewards by gamma factor so the rewards get less in the first frames
/// ***** Make a pseudo target value = action[frame_g] ******
///This is the cool stuff about Reinforcment Learning
///You pruduce a pseudo target value = action[frame_g] because the you can imidiet generate a gradient decent for this frame
///even if you don't know yet if this pseudo target is the right (in this case have right polarity +/- only because actions is only 2 = UP/DOWN )
///When the episode is over you can fix this eventually wrong +/- by the rewards
output_delta[frame_g] = (action[frame_g] - output_node[frame_g]) * output_node[frame_g] * (1.0f - output_node[frame_g]);///Backpropagate. Make a gradient decent for this frame even if it may have wrong polarity
for(int i=0; i<Nr_of_hidden_nodes; i++)
{
///**** Backprop delta_hid ****
///delta_hid = delta_out * output_weight[1];
ix = (frame_g * Nr_of_hidden_nodes + i);
hidden_delta[frame_g * Nr_of_hidden_nodes + i] = output_delta[frame_g] * hid2out_weightB[ix];///Relu Backprop to hidden delta
///===== Update weights depend on the stored delta ========
for(int j=0; j<(pixel_height * pixel_width); j++)
{
ix = ((pixel_width * Nr_of_hidden_nodes) * (pixel_height * frame_g + j/pixel_width) + (pixel_width * i) + j%pixel_width);
pix2hid_weightB[ix] += hidden_delta[frame_g * Nr_of_hidden_nodes + i] * input_node_stored[pixel_width * pixel_height * frame_g + j] * pix2hid_learning_rate * rewards;/// input_node_stored = new float[pixel_width * pixel_height * gameObj1.nr_of_frames];
}
ix = (frame_g * Nr_of_hidden_nodes + i);
hid2out_weightB[ix] += output_delta[frame_g] * hidden_node[frame_g * Nr_of_hidden_nodes + i] * hid2out_learning_rate * rewards;///Update weights
///=============End update weights for this position ==============
}
///printf("Rewards %f\n", rewards);
rewards *= gamma;///This will reduce the rewards on the older frames. The last frame (frame 99) will have highest rewards/punishments and the first fram lowers gain of rewards
}
///=================== End Backprop ====================================================
waitKey(1);
}
return 0;
}
#ifndef PINBALL_GAME_H
#define PINBALL_GAME_H
///This is the pinball game how the Reinforcment learning will train on
#include <opencv2/highgui/highgui.hpp> // OpenCV window I/O
#include <opencv2/imgproc/imgproc.hpp> // Gaussian Blur
#include <stdio.h>
///#include <raspicam/raspicam_cv.h>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp> // Basic OpenCV structures (cv::Mat, Scalar)
#include <cstdlib>
#include <ctime>
#include <math.h> // exp
#include <stdlib.h>// exit(0);
#include <iostream>
using namespace std;
using namespace cv;
class pinball_game
{
public:
pinball_game()///Constructor
{
printf("Construct a arcade game object\n");
}
virtual ~pinball_game()///Destructor
{
printf("Destruct game object\n");
}
int game_Width;///Pixel width of game grapics. A constant value set in init_game
int game_Height;///Pixel height of game grapics. A constant value set in init_game
int move_up;///Input Action from Agent. 1= Move up pad. 0= Move down pad
int win_this_game;///1= Catched the ball this episode. 0= miss. This will be used as the reward feedback to the Reinforcment Learning
int nr_of_frames;///The number of frames on one episode. A constant value set in init_game
int slow_motion;///1= slow down speed of game 0= full speed
Mat gameGrapics;///This is the grapics of the game how is the enviroment
void init_game(void);
void start_episode(void);
void run_episode(void);
int frame;
protected:
private:
int pad_position;
int ball_pos_x;
int ball_pos_y;
float ball_angle_derivate;///Example 0 mean strigh forward. +1.0 mean ball go up 1 pixel on one frame 45 deg.
int frame_steps;
int ball_offset_y;///
CvPoint P1;///The ball point OpenCV
CvPoint P2;///The pad point uppe corner OpenCV
CvPoint P3;///The pad point lower corner OpenCV
};
void pinball_game::init_game(void)
{
replay_count=0;//Fix this missing bugg 2017-11-15
slow_motion=0;
move_up=0;///Init down if there was no Agent action done.
win_this_game=0;///Init
frame=0;///Init with frame 0 for the episode
pad_position = game_Height/2;///Start the game at center
game_Width = 220;///
game_Height = 200;///
nr_of_frames = 100;///
gameGrapics.create(game_Height, game_Width, CV_32FC1);
gameGrapics = Scalar(0.0f);///Init with Black
srand (static_cast <unsigned> (time(0)));///Seed the randomizer
}
void pinball_game::start_episode(void)
{
ball_angle_derivate = (float) (rand() % 65535) / 65536;///Set ball shoot angle. Random value 0..1.0 range
ball_angle_derivate *= 6.0;
ball_angle_derivate -= 3.0;/// -0.5..+0.5 will mean +/- 12.5 deg random ball angle
/// ball_angle_derivate *= 1.0;
/// ball_angle_derivate -= 0.5;/// -0.5..+0.5 will mean +/- 12.5 deg random ball angle
frame_steps=0;
ball_offset_y = game_Height/2;///
pad_position = game_Height/2;///Start the game at center
}
void pinball_game::run_episode(void)
{
int circle_zize = 5;
int ball_start_x = 10;///Start 10 pixel inside game plan
int y_bounce_constraints = 20;
int y_pad_constraints = 28;
int pad_width = 3;
int pad_height = 40;
int pad_speed = 4;//4
///The frame loop is outside this class so The Agient cad do actions each frame step
frame_steps++;///This is need to handle bounce. This will reset when bounce
///=========== Draw the ball =================
///Bounce handle
ball_pos_y = ((int) ((float)frame_steps * ball_angle_derivate)) + ball_offset_y;///
if(ball_pos_y > (game_Height-y_bounce_constraints) || ball_pos_y < y_bounce_constraints)
{
frame_steps=0;
ball_angle_derivate = -ball_angle_derivate;
ball_offset_y = ball_pos_y + ball_angle_derivate;
}
ball_pos_x = (frame * 2) + ball_start_x;///Take 2 pixel step forward
P1.x = ball_pos_x;///Set to control grapic OpenCV circle() below
P1.y = ball_pos_y;///Set to control grapic OpenCV circle() below
gameGrapics = Scalar(0.0f);///Begin with all black then draw up grapics ball and pad
circle(gameGrapics, P1, circle_zize, Scalar(1.0), 7, -1);
///===============================
///========= Draw the Pad ==============
if(pad_position > (game_Height-y_pad_constraints))
{
///Allow only move up
if(move_up==1)
{
pad_position = pad_position - pad_speed;
}
else
{
pad_position = (game_Height-y_pad_constraints) + pad_speed;
}
}
else if(pad_position < y_pad_constraints)
{
///Allow only move down
if(move_up==0)
{
pad_position = pad_position + pad_speed;
}
else
{
pad_position = (y_pad_constraints) - pad_speed;
}
}
else
{
///Move up or down
if(move_up==1)
{
pad_position = pad_position - pad_speed;
}
else
{
pad_position = pad_position + pad_speed;
}
}
P2.y = pad_position - (pad_height/2);
P3.y = pad_position + (pad_height/2);
P2.x = - (pad_width/2) + game_Width-10;
P3.x = (pad_width/2) + game_Width-10;
rectangle(gameGrapics, P2, P3, Scalar(0.8), 2);/// C++: void rectangle(Mat& img, Point pt1, Point pt2, const Scalar& color, int thickness=1, int lineType=8, int shift=0)
if(frame > nr_of_frames-2)
{
///This episode is over
///Is the pad catch the ball ??
if(((pad_position + (pad_height/2)) < ball_pos_y) || ((pad_position - (pad_height/2)) > ball_pos_y))
{
///Lose
win_this_game = 0;
printf("Lose \n");
}
else
{
///Win catced
win_this_game = 1;
printf("Win \n");
}
}
imshow("Game", gameGrapics);
if(slow_motion==1)
{
waitKey(20);///Wait 100msec
}
else
{
waitKey(1);///Wait 1msec for only OpenCV grapics
}
}
#endif // PINBALL_GAME_H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment