Skip to content

Commit 781bee7

Browse files
added 'Maze' game
1 parent 8259054 commit 781bee7

5 files changed

Lines changed: 367 additions & 1 deletion

File tree

controls/hackercorner/README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
# Play an interactive game
22

3+
IN PROGRESS
4+
35
Navigate bling in a maze, look for the treasure.
46

57
![](./maze.png)
68

79

810
Hit 'Help' to check your current position against the Maze map.
911

10-
![](./help.png)
12+
![](./help.png)
13+
14+
15+
The Preferences tab gives the option to get help via an Alert Panel or through a Cisco Spark bot.
16+
Note that the Spark option implementation is not finalized (no message will show up in Spark).

controls/maze/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Play an interactive game
2+
3+
Navigate bling in a maze, look for the treasure.
4+
5+
![](./maze.png)
6+
7+
8+
Hit 'Help' to check your current position against the Maze map.
9+
10+
![](./help.png)

controls/maze/maze.js

Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
//
2+
// Copyright (c) 2017 Cisco Systems
3+
// Licensed under the MIT License
4+
//
5+
6+
function debug(entry) {
7+
console.log(entry)
8+
};
9+
10+
function fine(entry) {
11+
console.debug(entry)
12+
};
13+
14+
function Maze(structure, walls, phrases) {
15+
this.structure = structure;
16+
this.walls = walls;
17+
this.phrases = phrases;
18+
};
19+
20+
21+
Maze.prototype.tryMove = function (direction) {
22+
debug(`trying ${direction}`)
23+
var result
24+
switch (direction) {
25+
case 'up':
26+
result = this._move(this.position, -1, 0);
27+
break
28+
case 'down':
29+
result = this._move(this.position, 1, 0)
30+
break
31+
case 'left':
32+
result = this._move(this.position, 0, -1)
33+
break
34+
case 'right':
35+
result = this._move(this.position, 0, 1)
36+
break
37+
default:
38+
throw new Error("unknown direction");
39+
}
40+
41+
this.position = result.pos
42+
result.direction = direction
43+
return result
44+
}
45+
46+
Maze.prototype.up = function () {
47+
return this.tryMove('up');
48+
}
49+
50+
Maze.prototype.down = function () {
51+
return this.tryMove('down');
52+
}
53+
54+
Maze.prototype.left = function () {
55+
return this.tryMove('left');
56+
}
57+
58+
Maze.prototype.right = function () {
59+
return this.tryMove('right');
60+
}
61+
62+
Maze.prototype._move = function (pos, x, y) {
63+
let newX = pos[0] + x
64+
let newY = pos[1] + y
65+
66+
let thing = this.structure[newX][newY]
67+
68+
// What did we meed
69+
var story = this.phrases[thing]
70+
debug('> ' + story)
71+
72+
// If this is a wall, stay at current position
73+
if (this.walls.includes(thing)) {
74+
fine(`still at: ${pos}`)
75+
this.position = pos
76+
return {
77+
'success': false,
78+
'outcome': story,
79+
'thing': this.look(pos),
80+
'pos': pos
81+
}
82+
}
83+
84+
var newPos = [newX, newY]
85+
fine(`now at: ${newPos}`)
86+
this.position = pos
87+
return {
88+
'success': true,
89+
'thing': this.look(newPos),
90+
'pos': newPos
91+
}
92+
}
93+
94+
95+
Maze.prototype.look = function (pos) {
96+
var x = pos[0]
97+
var y = pos[1]
98+
var thing = this.structure[x][y]
99+
return this.phrases[thing];
100+
}
101+
102+
103+
// Use this function if the output supports Newlines
104+
Maze.prototype.buildMap = function () {
105+
var pos = this.position
106+
var poster = ""
107+
for (var y = 0; y < this.structure.length; y++) {
108+
var line = ""
109+
for (var x = 0; x < this.structure[0].length; x++) {
110+
var char = this.structure[y][x]
111+
if ((y == pos[0]) && x == pos[1]) {
112+
char = 'o'
113+
}
114+
line += char
115+
}
116+
poster += line + "\n"
117+
}
118+
debug("poster:\n" + poster)
119+
return poster
120+
}
121+
122+
// Use this function if the output does NOT supports newlines
123+
Maze.prototype.buildMapAsWrapped = function (linewidth, skipBorders) {
124+
125+
var map = ""
126+
var pos = this.position
127+
for (var y = 0; y < this.structure.length; y++) {
128+
if (!(skipBorders && ((y == 0) || (y == this.structure.length - 1)))) {
129+
var line = ""
130+
for (var x = 0; x < this.structure[0].length; x++) {
131+
var char = this.structure[y][x]
132+
if ((y == pos[0]) && x == pos[1]) {
133+
char = 'o'
134+
}
135+
line += char
136+
}
137+
var left = Math.round((linewidth - line.length) / 2)
138+
var mazeline = line.padStart(left + line.length, "-")
139+
map += mazeline.padEnd(linewidth, "-")
140+
map += " "
141+
}
142+
}
143+
debug("maze map:" + map)
144+
return map
145+
}
146+
147+
Maze.prototype.pickInitialPosition = function (emptyChar) {
148+
if (!emptyChar) emptyChar = ' '
149+
150+
// Ping a random number, on an empty spot
151+
while (true) {
152+
var y = Math.round(Math.random() * (this.structure.length - 2) + 1)
153+
var x = Math.round(Math.random() * (this.structure[0].length - 2) + 1)
154+
fine(`picked x: ${x}, y: ${y}`)
155+
156+
if (this.structure[y][x] == emptyChar) {
157+
fine(`position is clear, storing...`)
158+
return this.setInitialPosition(x, y)
159+
}
160+
}
161+
}
162+
163+
Maze.prototype.setInitialPosition = function (x, y) {
164+
this.position = [y, x]
165+
return this.position
166+
}
167+
168+
169+
//
170+
// Utilities
171+
//
172+
173+
function showProgress(count, delay, cb, final, initial) {
174+
var counter = 0
175+
if (!initial) initial = ""
176+
var progress = ".....".padEnd(count, '.')
177+
var timer = setInterval(function (event) {
178+
cb(initial + progress.substring(0, counter + 1))
179+
counter++;
180+
if (counter >= count) {
181+
clearInterval(timer)
182+
cb(final)
183+
}
184+
}, delay)
185+
}
186+
187+
188+
//
189+
// Room Controls
190+
//
191+
const xapi = require('xapi');
192+
193+
194+
function showInstructions(message) {
195+
console.debug('updating instructions')
196+
197+
xapi.command('UserInterface Extensions Widget SetValue', {
198+
WidgetId: 'instructions',
199+
Value: message
200+
})
201+
}
202+
203+
204+
// Displays the outcome of the latest move
205+
function showOutcome(res, initial) {
206+
console.debug('computing instructions from latest move')
207+
208+
if (res.success) {
209+
// Tell the thing met at the new location
210+
showProgress(5, 500, showInstructions, res.thing, initial)
211+
return
212+
}
213+
214+
// Tell the reason why the move was not successful
215+
showProgress(5, 500, showInstructions, res.outcome, initial)
216+
}
217+
218+
219+
220+
// Displays help on the Touch10 or send the configuration to Cisco Spark
221+
function showHelp() {
222+
console.log("showing map in Alert Panel")
223+
224+
// Width depends on device
225+
// [TODO] check device
226+
xapi.status.get("SystemUnit ProductPlatform").then((product) => {
227+
let width = 31 // when the Alert is shown on the device output screen
228+
if (product == "Touch10") {
229+
console.debug('running on a Touch10')
230+
width = 51 // when the Alter is shown on a Touch10
231+
}
232+
233+
// show Alter panel
234+
xapi.command('UserInterface Message Alert Display', {
235+
Title: 'With a little help from ... the bot',
236+
Text: game.buildMapAsWrapped(width, true),
237+
Duration: 5
238+
})
239+
})
240+
}
241+
242+
243+
function onGui(event) {
244+
if (event.Type == 'clicked') {
245+
if (event.WidgetId == 'directions') {
246+
var direction = event.Value
247+
if (direction == 'center') {
248+
showHelp()
249+
return
250+
}
251+
252+
showInstructions(`moving ${direction}`)
253+
var res = game.tryMove(direction)
254+
showOutcome(res, `moving ${direction}`)
255+
return
256+
}
257+
258+
if (event.WidgetId == 'restart') {
259+
restart()
260+
return
261+
}
262+
263+
// No more event listener
264+
return
265+
}
266+
}
267+
xapi.event.on('UserInterface Extensions Widget Action', onGui);
268+
269+
270+
function restart() {
271+
console.log('resetting the maze')
272+
273+
var structure = []
274+
structure[0] = ['|', '-', '-', '-', '-', '-', '|']
275+
structure[1] = ['|', '_', 'C', '_', '_', '_', '|']
276+
structure[2] = ['|', '_', '_', 'X', '_', '_', '|']
277+
structure[3] = ['|', '_', 'X', '?', 'X', '_', '|']
278+
structure[4] = ['|', '_', '_', '_', '_', 'X', '|']
279+
structure[5] = ['|', '-', '-', '-', '-', '-', '|']
280+
281+
var walls = ['|', '-', 'X']
282+
283+
var phrases = {}
284+
phrases['|'] = "cannot get there, this a maze border you just hitted"
285+
phrases['-'] = "cannot get there, this a maze border you just hitted"
286+
phrases['|'] = "cannot get there, this a maze border you just hitted"
287+
phrases['X'] = "ouch, you bumped a wall"
288+
phrases['_'] = "nothing here, let's continue exploring."
289+
phrases['C'] = "hello kitty, you look hungry. Are you lost too? Jump in."
290+
phrases['D'] = "WOW, an agressive dog is lying here. Better run away!"
291+
phrases['?'] = "CONGRATS, you found the treasure!!!"
292+
293+
game = new Maze(structure, walls, phrases)
294+
game.pickInitialPosition('_')
295+
296+
showProgress(10, 200, showInstructions, 'Then here you are: lost in an hostile maze, looking for a treasure. Pick a direction...', 'initializing the Maze')
297+
}
298+
299+
var game;
300+
if (!game) {
301+
// First run:
302+
// - initialize maze
303+
restart()
304+
}

controls/maze/maze.png

103 KB
Loading

controls/maze/maze.xml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<Extensions>
2+
<Version>1.4</Version>
3+
<Panel>
4+
<Icon>Disc</Icon>
5+
<Type>Home</Type>
6+
<Page>
7+
<Name>Play the Maze</Name>
8+
<Row>
9+
<Name>Instructions</Name>
10+
<Widget>
11+
<WidgetId>instructions</WidgetId>
12+
<Name>Then here you are, lost in an hostile maze, looking for the treasure. Pick a direction...</Name>
13+
<Type>Text</Type>
14+
<Options>size=4;align=left;fontSize=normal</Options>
15+
</Widget>
16+
</Row>
17+
<Row>
18+
<Name>Pick a direction</Name>
19+
<Widget>
20+
<WidgetId>directions</WidgetId>
21+
<Name>help</Name>
22+
<Type>DirectionalPad</Type>
23+
<Options>size=4</Options>
24+
</Widget>
25+
</Row>
26+
<Row>
27+
<Name>Restart</Name>
28+
<Widget>
29+
<WidgetId>restart</WidgetId>
30+
<Name>play again</Name>
31+
<Type>Button</Type>
32+
<Options>size=2</Options>
33+
</Widget>
34+
<Widget>
35+
<WidgetId>unused</WidgetId>
36+
<Name> </Name>
37+
<Type>Text</Type>
38+
<Options>size=2</Options>
39+
</Widget>
40+
</Row>
41+
<PageId>PlayMaze</PageId>
42+
<Options>hideRowNames=0</Options>
43+
</Page>
44+
<Name>Play</Name>
45+
</Panel>
46+
</Extensions>

0 commit comments

Comments
 (0)