- Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathreact.blc
More file actions
Latest commit
320 lines (251 loc) · 7.63 KB
/
Copy pathreact.blc
File metadata and controls
320 lines (251 loc) · 7.63 KB
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
// reactor.blc
//
// A simple reaction game for the M5StickC.
// Constants
@[CConst (binding ="MILLIS_PER_TICK", header = "env.h")]
extern const MILLIS_PER_TICK: nat32
const DIMM_UP = 0: nat8
const DIMM_DOWN = 1: nat8
const TIME_TO_FADE = 5000: nat32
const FADE_DURATION = 5000: nat32
const SCORE_STATE_NOT_STARTED = 0: nat8
const SCORE_STATE_OK = 1: nat8
const SCORE_STATE_TOO_EARLY = 2: nat8
const SCORE_STATE_TOO_LATE = 3: nat8
const PRESENT_HELP = 0: nat8
const PRESENT_PLAY = 1: nat8
const PRESENT_HIGHSCORE = 2: nat8
// Types
/// Captures the score of a game play.
struct Score
var state: nat8
var waitTime: nat32
var reactionTime: nat32
end
// Helpers
@[CFunction (source = "env.h")]
extern function makeRandomNat32 (fromIncl: nat32, toExcl: nat32) returns nat32
/// The output will reflect the input as long as enabled is `true` - `false` otherwise.
activity BoolGate (inValue: bool, enabled: bool) (outValue: bool)
repeat
if enabled then
outValue = inValue
else
outValue = false
end
await true
end
end
/// Delays the trail for the given period.
activity Delay (millis: nat32)
var ticks = millis / MILLIS_PER_TICK
if ticks == 0 then
ticks = 1 // Need to wait at least one tick
end
repeat
ticks = ticks - 1
await true
until ticks == 0 end
end
/// Keeps track of the time now long it is run.
activity Timer () (millis: nat32)
repeat
await true
millis = millis + MILLIS_PER_TICK
end
end
@[CFunction (source = "env.h")]
extern singleton function dimScreen (percentage: nat8)
/// Dimms the screen up or down in the given duration.
activity Dimmer (direction: nat8, millis: nat32)
var level: int8
var increment: int8
if direction == DIMM_UP then
level = 0
increment = 1
else
level = 100
increment = -1
end
let stepDuration = millis / 100 // We use 100 steps - calc how long each takes
var ticks: nat8 = 0
repeat
dimScreen(level as! nat8)
run Delay(stepDuration)
level = level + increment
ticks = ticks + 1
until ticks == 100 end
dimScreen(level as! nat8)
end
/// Dimms down the screen after a specified delay. If button is pressed during dimming, the screen will bright up immediately again.
activity DimmDownController (buttonPressed: bool, waitTime: nat32, fadeTime: nat32) (didStartDimming: bool)
repeat
run Delay(waitTime)
didStartDimming = true
when buttonPressed abort
run Dimmer(DIMM_DOWN, fadeTime)
await false
end
dimScreen(100)
await true
didStartDimming = false
end
end
/// When a button press occurs, waits for a small duration to see whether a double click follows or not.
/// Returns false for single click, true for double click
activity DoubleClickController (buttonPressed: bool) returns bool
await buttonPressed
when buttonPressed abort
run Delay(300)
return false
end
return true
end
/// Waits for a button press to continue. Optionaly dimms down the screen.
/// Returns false for single-click, true for double-click
activity PressToContinueController (buttonPressed: bool, wantsDimming: bool) returns bool
var isDoubleClick: bool
if wantsDimming then
var continueButtonPressed: bool
var didStartDimming: bool
cobegin
run isDoubleClick = DoubleClickController(continueButtonPressed)
with weak
run DimmDownController(buttonPressed, TIME_TO_FADE, FADE_DURATION)(didStartDimming)
with weak
run BoolGate(buttonPressed, not didStartDimming)(continueButtonPressed)
end
else
run isDoubleClick = DoubleClickController(buttonPressed)
end
return isDoubleClick
end
// Generic Screens
@[CFunction (source = "env.h")]
extern function displayCountDownScreen (i: nat8)
/// A transitional screen counting down every second from the given initial value.
activity CountDownScreen (counts: nat8)
var i = counts
repeat
displayCountDownScreen(i)
run Delay(1000)
i = i - 1
until i == 0 end
end
// Splash Screen
@[CFunction (source = "env.h")]
extern function displaySplashScreen ()
activity SplashScreen ()
displaySplashScreen()
run Dimmer(DIMM_UP, 5000)
end
// Start Screen
@[CFunction (source = "env.h")]
extern function displayStartScreen ()
activity StartScreen () returns nat8
@[CInput (binding ="buttonPressed", header = "env.h")]
extern let buttonPressed: bool
displayStartScreen()
run let isDoubleClick = PressToContinueController(buttonPressed, true)
if isDoubleClick then
return PRESENT_HELP
else
return PRESENT_PLAY
end
end
// Help Screen
@[CFunction (source = "env.h")]
extern function displayHelpScreen ()
activity HelpScreen ()
@[CInput (binding ="buttonPressed", header = "env.h")]
extern let buttonPressed: bool
displayHelpScreen()
run _ = PressToContinueController(buttonPressed, true)
end
// Play Screen
@[CFunction (source = "env.h")]
extern function displayPlayStartScreen ()
@[CFunction (source = "env.h")]
extern function displayPlaySignalScreen ()
activity PlayStartScreen (buttonPressed: bool) (score: Score) returns bool
displayPlayStartScreen()
when buttonPressed abort
score.waitTime = makeRandomNat32(1000, 5000)
run Delay(score.waitTime)
return true
end
score.state = SCORE_STATE_TOO_EARLY
return false
end
activity PlaySignalScreen (buttonPressed: bool) (score: Score)
var time: nat32
var didTimeOut: bool
displayPlaySignalScreen()
cobegin weak
await buttonPressed
score.reactionTime = time
with weak
run Timer()(time)
with weak
run Delay(3000)
didTimeOut = true
end
if didTimeOut then
score.state = SCORE_STATE_TOO_LATE
else
score.state = SCORE_STATE_OK
end
end
activity PlayScreen () returns Score
@[CInput (binding ="buttonPressed", header = "env.h")]
extern let buttonPressed: bool
var score: Score
run CountDownScreen(3)
run let isValid = PlayStartScreen(buttonPressed)(score)
if isValid then
run PlaySignalScreen(buttonPressed)(score)
end
return score
end
// Result Screen
@[CFunction (source = "env.h")]
extern function displayResultIsValidScreen (score: Score)
@[CFunction (source = "env.h")]
extern function displayResultTooEarlyScreen ()
@[CFunction (source = "env.h")]
extern function displayResultTooLateScreen ()
activity ResultScreen (score: Score)
@[CInput (binding ="buttonPressed", header = "env.h")]
extern let buttonPressed: bool
if score.state == SCORE_STATE_OK then
displayResultIsValidScreen(score)
elseif score.state == SCORE_STATE_TOO_EARLY then
displayResultTooEarlyScreen()
else
displayResultTooLateScreen()
end
run _ = PressToContinueController(buttonPressed, true)
end
// Highscore Screen
activity HighscoreScreen ()
// Not yet implemented.
await true
end
// Main
@[EntryPoint]
activity Main()
run SplashScreen()
repeat
run let selection = StartScreen()
if selection == PRESENT_HELP then
run HelpScreen()
elseif selection == PRESENT_PLAY then
var score: Score
run score = PlayScreen()
run ResultScreen(score)
run HighscoreScreen()
elseif selection == PRESENT_HIGHSCORE then
run HighscoreScreen()
end
end
end