// μεταβλητή για ΕΜΦΑΝΙΣΗ τοίχου (πάχος) int wallW = 15; // μεταβλητές για ΕΜΦΑΝΙΣΗ μπάλας int ballX, ballY; int ballDiam = 10; int ballSpeedX, ballSpeedY; int nextBallX, nextBallY; // μεταβλητές για ΕΜΦΑΝΙΣΗ ρακέτας int padH = 100; int padW = 15; int padX, padY; // y του πάνω αριστερά σημείου int padSpeed = 30; int nextPadY; // Υπερπαράμετροι εκπαίδευσης float alpha = 0.1; // learning rate float gamma = 0.95; // discount factor float epsilon = 1.0; // exploration rate float reward = 0; // το άμεσο reward σε κάθε απόφαση int action; // j στον Q, που δείχνει την επιλεγμένη ενέργεια στη γραμμή currState int currState; // i στον S όπου βρίσκεται στο current state int nextState; // i στον S όπου βρίσκεται το state που θα πάει από currState αφού κάνει action int nextMaxQ; // j στον Q με την μέγιστη τιμή στη γραμμή nextState int numActions = 3; // up, down, stay δηλ. 3 δυνατές ενέργειες της ρακέτας σε κάθε κατάσταση int numPosX = 12; // πόσες δαφορετικές θέσεις αναγνωρίζουμε στον οριζόντιο άξονα int numPosY = 12; // πόσες δαφορετικές θέσεις αναγνωρίζουμε στον κάθετο άξονα int numDir = 4; // πόσες διαφορετικές κατευθύνσεις αναγνωρίζουμε για την κίνηση της μπάλας // 1 προς κατω+δεξιά, 2 προς πανω+δεξιά, -1 προς κάτω+αριστερά, -2, προς πάνω+αριστερά String[] S; // states: bX,bY,pY,bDir δηλ. 4 ακέραιες τιμές διαχωρισμένες με κόμμα float[][] Q; // q-value ανά action σε κάθε state int numStates = numPosX * numPosY * numPosY * numDir; // πλήθος όλων των δυνατών states // μεταβλητές για ΕΠΕΙΣΟΔΙΑ boolean endOfEpisode = false; int numEp = 0; int countOK = 0; int countNOK = 0; void setup() { size(600, 400); // μέγεθος παραθύρου // Δημιουργούνται οι πίνακες S, Q (ως χώρος) S = new String[numStates]; Q = new float[numStates][numActions]; initTables(); printTables(); initGraphics(); } // τέλος της setup void draw() { displayGraphics(true); action = (int) random(0,3); calculateNextGraphicValues(); updateGraphics(); } // τέλος της draw void initGraphics() { fill(0, 255, 0); // το χρώμα των σχημάτων είναι πράσινο ballX = width/2; // η μπάλα ξεκινάει στη μέση του παραθύρου ballY = height/2; ballSpeedX = 2; // τυχαία αρχική ταχύτητα σε κάθε άξονα ballSpeedY = -2; padX = width - 30; // η ρακέτα απέχει 30px από το άκρο του παραθύρου padY = height/2-padH/2; // αρχική θέση στη μέση του παραθύρου action = int(random(0, 3)); // τυχαίο αρχικό action, incl 0, excl 3 } // Function για την εμφάνιση των γραφικων void displayGraphics(boolean ok) { if (!ok) return; background(0); // Eμφάνιση της μπάλας ellipse(ballX, ballY, ballDiam, ballDiam); // Eμφάνιση του απέναντι τοίχου (πλάτος 15, ύψος όσο το παράθυρο) rect(0, 0, wallW, height); // Eμφάνιση της ρακέτας rect(padX, padY, padW, padH); } // Function για υπολογισμό των τιμών εμφάνισης για μπάλα και ρακέτα // στην επόμενη κατάσταση void calculateNextGraphicValues() { // Υπολογισμός μεταβλητών εμφάνισης της ρακέτας στην επόμενη κατάσταση if (action == 0) nextPadY = padY - padSpeed; // up else if (action == 1) nextPadY = padY + padSpeed; // down else nextPadY = padY; // stay //Αν πάει να βγει από το παράθυρο, να παραμένει εκεί if (nextPadY <= 0) nextPadY = 0; if (nextPadY >= height-padH) nextPadY = height-padH; // Υπολογισμός μεταβλητών εμφάνισης της μπάλας στην επόμενη κατάσταση nextBallX = ballX + ballSpeedX; nextBallY = ballY + ballSpeedY; // Αν η μπάλα ακουμπήσει τον απέναντι τοίχο, αλλάζει κατεύθυνση // στον άξονα x και επιταχύνεται. if (nextBallX < wallW + ballDiam/2) ballSpeedX = ballSpeedX * -1; // Αν η μπάλα ακουμπήσει στο πάνω ή κάτω άκρο του παραθύρου, αλλάζει κατεύθυνση // στον άξονα y. if ( nextBallY >= height - ballDiam/2 || nextBallY <= ballDiam/2 ) { ballSpeedY = ballSpeedY * -1; } // Αν η μπάλα ακουμπήσει τη ρακέτα, αλλάζει κατεύθυνση στον άξονα x. // Άρα η ταχύτητα speedX πολλαπλασιάζεται με -1. if (nextBallX + ballDiam/2 >= padX && nextBallX + ballDiam/2 <= padX+padW && nextBallY - ballDiam/2 >= nextPadY && nextBallY + ballDiam/2 <= nextPadY + padH) { ballSpeedX = ballSpeedX * -1; endOfEpisode = true; countOK++; println("OK: ", countOK, " NOK: ", countNOK); } // Αν η μπάλα προσπεράσει τη ρακέτα, την επαναφέρουμε στο κέντρο //else if (nextBallX + ballDiam/2 > padX) if (nextBallX + ballDiam/2 > padX + padW) { nextBallX = width/2; // η μπάλα ξεκινάει στη μέση του παραθύρου nextBallY = height/2; ballSpeedX = 2; ballSpeedY = 2; padX = width - 30; // η ρακέτα απέχει 30px από το άκρο του παραθύρου padY = height/2-padH/2; // αρχική θέση στη μέση του παραθύρου endOfEpisode = true; countNOK++; println("OK: ", countOK, " NOK: ", countNOK); } } void updateGraphics() { // Ενημέρωση των μεταβλητών εμφάνισης της μπάλας κ ρακέτας ballX = nextBallX; ballY = nextBallY; padY = nextPadY; }