Skip to content

Commit 45a2de0

Browse files
Merge pull request #3 from GreenWizard2015/dev
Dev
2 parents 7971060 + bcb125f commit 45a2de0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+2889
-1531
lines changed

.github/FUNDING.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
patreon: GreenWizard
2+
buy_me_a_coffee: greenwizard89

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ __pycache__
44
/.project
55
/Data/
66
/.vscode
7+
/debug.log

App/AppModes.py

Lines changed: 28 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,11 @@
11
import numpy as np
2-
import pygame
32
import pygame.locals as G
43
from App.Utils import Colors, normalized
54
from App.CSpinningTarget import CSpinningTarget
65
import time
76
import scipy.interpolate as sInterp
8-
9-
class CAppMode:
10-
def __init__(self, app):
11-
self._app = app
12-
self._paused = True
13-
return
14-
15-
def on_event(self, event):
16-
if event.type == G.KEYDOWN:
17-
if event.key in [G.K_p, G.K_RETURN]:
18-
self._paused = not self._paused
19-
return
20-
21-
def on_render(self, window):
22-
if self._paused:
23-
self._app.drawText('Paused', (55, 55), Colors.RED)
24-
return
25-
26-
def accept(self, tracked):
27-
if self._paused: return
28-
self._app._dataset.store(tracked, np.array(self._pos))
29-
return
30-
pass
7+
from App.CGameMode import CGameMode
8+
from App.CAppMode import CAppMode
319

3210
class CMoveToGoal(CAppMode):
3311
def __init__(self, app):
@@ -94,16 +72,22 @@ def on_event(self, event):
9472
return
9573

9674
if G.K_RIGHT == event.key:
75+
self._reset(clockwise=False)
76+
self._active = True
77+
return
78+
79+
if G.K_LEFT == event.key:
80+
self._reset(clockwise=True)
9781
self._active = True
9882
return
9983
return
10084

101-
def accept(self, tracked):
85+
def on_sample(self, tracked):
10286
if self._active:
103-
super().accept(tracked)
87+
super().on_sample(tracked)
10488
return
10589

106-
def _reset(self):
90+
def _reset(self, clockwise=False):
10791
path = np.array([
10892
[-1, 1],
10993
[ 1, 1],
@@ -112,7 +96,9 @@ def _reset(self):
11296
[-1, 1],
11397
], np.float32)
11498
lvl = (self._maxLevel - self._level) / ((2.0 * self._maxLevel) + 0)
115-
self._pos, self._goal, *self._path = 0.5 + lvl * path
99+
path = 0.5 + lvl * path
100+
if clockwise: path = path[::-1]
101+
self._pos, self._goal, *self._path = path
116102
self._active = False
117103
self._transitionStart = None
118104
return
@@ -150,9 +136,9 @@ def on_tick(self, deltaT):
150136
self._startT = time.time()
151137
return
152138

153-
def accept(self, tracked):
139+
def on_sample(self, tracked):
154140
if self._active:
155-
super().accept(tracked)
141+
super().on_sample(tracked)
156142
return
157143

158144
def on_render(self, window):
@@ -173,13 +159,23 @@ def __init__(self, app):
173159
self._pos = np.zeros((2, )) + 0.5
174160
self._target = CSpinningTarget(app)
175161
self._points = None
162+
self._scale = 1.0
176163
self._newSpline(extend=False)
177164
return
165+
166+
def _updateScale(self):
167+
newScale = np.random.uniform(0.1, 0.2) + self._scale
168+
self._scale = newScale
169+
if 1.0 < self._scale: self._scale = 0.0
170+
return newScale
178171

179172
def _newSpline(self, extend=True):
180173
self._T = 0.0
181174
N = 3
182-
points = np.random.normal(size=(4, 2), loc=0.5, scale=0.5)
175+
scale = self._updateScale()
176+
points = np.random.uniform(size=(N + 1, 2)) - 0.5
177+
points /= np.linalg.norm(points, axis=-1, keepdims=True) + 1e-6
178+
points = 0.5 + (points * scale)
183179
if extend:
184180
points = np.concatenate([self._points[-N:], points], axis=0)
185181

@@ -189,7 +185,7 @@ def _newSpline(self, extend=True):
189185

190186
speed = np.random.uniform(0.15, 1.0, size=1)[0]
191187
T = distance[-1] / speed
192-
self._maxT = np.clip(T, 20, 40)
188+
self._maxT = np.clip(T, N * 3, N * 10)
193189
distance /= distance[-1]
194190

195191
shift = distance[N - 1] if extend else 0.0
@@ -260,78 +256,7 @@ def on_event(self, event):
260256
return
261257
pass
262258
#####################
263-
# TODO: find a way to collect data during the game mode and then use it to train the model
264-
# Maybe store the last 2 seconds before the hit
265-
class CGameMode:
266-
def __init__(self, app):
267-
self._app = app
268-
self._pos = np.zeros((2, )) + 0.5
269-
self._T = 0.0
270-
self._currentRadius = 0.0
271-
self._radiusPerSecond = 0.01
272-
self._hits = 0
273-
self._maxHits = 3
274-
275-
self._totalTime = 0
276-
self._totalHits = 0
277-
self._totalDistance = 0
278-
return
279-
280-
def on_tick(self, deltaT):
281-
self._T += deltaT
282-
T = self._T
283-
self._currentRadius = T * self._radiusPerSecond
284-
return
285-
286-
def on_render(self, window):
287-
wh = np.array(window.get_size())
288-
pos = tuple(np.multiply(wh, self._pos).astype(np.int32))
289-
self._app.drawObject(pos, color=Colors.RED, R=3)
290-
# second circle
291-
R = np.multiply(wh, self._currentRadius).min().astype(np.int32)
292-
pygame.draw.circle(window, Colors.RED, pos, int(R), width=1)
293-
294-
# score at the top center
295-
if 0 < self._totalHits:
296-
self._app.drawText(
297-
'Hits: %d, mean accuracy: %.4f, time: %.1f' % (
298-
self._totalHits, self._totalDistance / self._totalHits, self._totalTime / self._totalHits
299-
),
300-
pos=(wh[0] // 2, 80),
301-
color=Colors.BLACK,
302-
)
303-
return
304-
305-
def on_event(self, event):
306-
return
307-
308-
def play(self, pos, tracked):
309-
pos = np.array(pos).reshape((2, ))
310-
# check if the click is inside the circle
311-
D = np.square(np.subtract(pos, self._pos)).sum()
312-
D = np.sqrt(D)
313-
if D < self._currentRadius:
314-
self._hit(D)
315-
return
316-
317-
def _hit(self, D):
318-
self._totalHits += 1
319-
self._totalDistance += D
320-
self._totalTime += self._T
321-
322-
self._T = 0.0
323-
self._currentRadius = 0.0
324-
325-
self._hits += 1
326-
if self._maxHits <= self._hits:
327-
self._pos = np.random.uniform(size=(2, ))
328-
self._hits = 0
329-
return
330-
pass
331-
332-
#####################
333259
APP_MODES = [
334-
CGameMode,
335260
CLookAtMode,
336261
CCornerMode,
337262
CSplineMode,

App/CAppMode.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import pygame as G
2+
import numpy as np
3+
4+
class CAppMode:
5+
def __init__(self, app):
6+
self._app = app
7+
self._paused = True
8+
return
9+
10+
def on_event(self, event):
11+
if event.type == G.KEYDOWN:
12+
if event.key in [G.K_p, G.K_RETURN]:
13+
self._paused = not self._paused
14+
15+
if event.key == G.K_SPACE:
16+
self._paused = True
17+
return
18+
19+
def on_render(self, window):
20+
return
21+
22+
def on_sample(self, tracked):
23+
if self._paused: return
24+
self._app._dataset.store(tracked, np.array(self._pos))
25+
return
26+
27+
def on_prediction(self, pos, data):
28+
return
29+
30+
@property
31+
def paused(self): return self._paused
32+
pass

App/CBackground.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import pygame
2+
import pygame.locals as G
3+
import numpy as np
4+
import time
5+
from App.Utils import Colors
6+
7+
class CBackground:
8+
def __init__(self):
9+
self._backgroundDynamic = False
10+
return
11+
12+
def on_tick(self, deltaT):
13+
return
14+
15+
def _brightness(self):
16+
T = time.time()
17+
amplitude = 1.0 / 2.0
18+
duration = 30.0
19+
# smooth brightness change over N seconds
20+
sin = np.sin(2.0 * np.pi * T / duration)
21+
res = 1.0 + amplitude * sin
22+
return res
23+
24+
def on_render(self, window):
25+
bg = Colors.SILVER
26+
if self._backgroundDynamic:
27+
# take color from Colors.asList based on current time, change every 5 seconds
28+
bg = Colors.asList[int(time.time() / 5) % len(Colors.asList)]
29+
# apply brightness
30+
bg = np.multiply(bg, self._brightness()).clip(0, 255).astype(np.uint8)
31+
window.fill(bg)
32+
return
33+
34+
def on_event(self, event):
35+
if not(event.type == G.KEYDOWN): return
36+
# toggle background dynamic (B)
37+
if G.K_b == event.key:
38+
self._backgroundDynamic = not self._backgroundDynamic
39+
return
40+
pass

0 commit comments

Comments
 (0)