Da ich wahrscheinlich der einzige bin, der unter Linux und X11 das ganze macht
Bist du nicht. Willkommen im Club
Performance-Probleme wie von dir beschrieben habe ich nicht. Hin und wieder endet die Simulation jedoch mit einem BadWindow X Fehler. Der kommt mutmaßlich daher, dass der Event-Thread beim Beenden immer versucht das Fenster zu unmappen, auch, wenn es bereits zerstört wurde. Ich kenne mich mit X-lowlevel-Details nicht aus, aber ein Verschieben des XWindowUnmap in die Behandlung der ClientMessage sollte das Problem meinem Verständnis nach lösen.
Da Tastendruecke asynchron verarbeitet werden, habe ich aufgegeben ein Interface fuer Menschen zu basteln. Da bevorzuge ich doch eher eine {input update render}-Routine
Da kann ich dir aushelfen. Das Prinzip ist ganz simple:
eine globale Variable, die dekrementierst (inkrementierst) wird, wenn die Pfeil-rauf-Taste gedrückt (losgelassen) wird, und - analog dazu - inkrementiert (dekrementiert) wird, wenn die Pfeil-runter-Taste gedrückt (losgelassen) wird.
Somit enthält diese Variable immer den Beschleunigungsbefehl des Users (-1 wenn nur Up, 0 wenn weder Up noch Down oder aber alle beide, 1 wenn nur Down gedrückt sind).
Diese Variable wird nur vom Event-Thread verändert, vom Simulations-/UI-Upfate-Thread nur gelesen, und muss daher nicht durch ein mutex geschützt werden.
Der "KI" bleibt dann nur noch, den Wert dieser Variablen zurückzugeben.
Das Ganze sieht dann so aus:
In pong.h wird die Deklaration der globalen Variable eingefügt
|
C-/C++-Quelltext
|
1
2
|
// user input
extern int move;
|
In visualizer_x11.cpp wird die Variable definiert
|
C-/C++-Quelltext
|
1
|
int move = 0;
|
und die thread_proc() Funktion angepasst, so dass zum einen KeyRelease-Events vom X-Server angefordert werden, und zum anderen KeyPress- und KeyRelease-Events für die Pfeil-hoch bzw. -runter Tasten verarbeitet werden
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
void* thread_proc(void* p_arg)
{
XEvent evt;
XSelectInput(p_display, window, StructureNotifyMask | KeyPressMask | KeyReleaseMask); // KeyReleaseMask hinzugefügt
bool quit = false;
do
{
XNextEvent(p_display, &evt);
switch(evt.type)
{
case DestroyNotify:
quit = true;
break;
case KeyPress:
if(evt.xkey.keycode == XKeysymToKeycode(p_display, XK_KP_Subtract) && delay < 100) delay += 5;
else if(evt.xkey.keycode == XKeysymToKeycode(p_display, XK_KP_Add) && delay) delay -= 5;
else if (evt.xkey.keycode == XKeysymToKeycode(p_display, XK_Up)) --move; // hinzugefügt
else if (evt.xkey.keycode == XKeysymToKeycode(p_display, XK_Down)) ++move; // hinzugefügt
break;
case KeyRelease: // hinzugefügt
if (evt.xkey.keycode == XKeysymToKeycode(p_display, XK_Up)) ++move; // hinzugefügt
else if (evt.xkey.keycode == XKeysymToKeycode(p_display, XK_Down)) --move; // hinzugefügt
break; // hinzugefügt
case ClientMessage:
if(evt.xclient.message_type == XInternAtom(p_display, "WM_PROTOCOLS", 0) && evt.xclient.data.l[0] == XInternAtom(p_display, "WM_DELETE_WINDOW", 0))
{ // BadWindowError Patch
XUnmapWindow(p_display, window);// BadWindowError Patch
quit = true;
} // BadWindowError Patch
break;
}
}
while(!quit);
//XUnmapWindow(p_display, window); // BadWindowError Patch
window_open = false;
}
|
Zu guter Letzt fehlt nur noch die "KI":
|
C-/C++-Quelltext
|
1
2
3
4
5
6
|
// Steuerung durch den User
int human_pong(const GameState& gs,
uint me)
{
return move;
}
|
Ich muss dich aber vorwarnen: die Steuerung über die Beschleunigung ist recht schwierig und schon gegen die Referenz eine Herausforderung. Wenn deine MaxYSpeedKI so spielt, wie es ihr Name andeutet, wirst du gegen die kaum einen Treffer landen können. Trotzdem viel Spaß
Meine KI (oder das KI-Objekt) muss aber ueber die my_pong-Funktion hinaus erhalten bleiben, z.B. als globale Variable. Sonst vergisst sie staendig von update zu update, was sie eigentlich machen wollte.
Sollte kein Problem sein, schließlich hat
David statische Variablen in diesem Contest erlaubt.
Möge die künstlichere Intelligenz gewinnen !