4 Placed in the public domain.
6 This code is designed for use with Perlenspiel, by Brian Moriarty,
7 which is released under the terms of the GNU LGPL version 3 or
28 [0, 2], [1, 2], [2, 2],
29 [0, 1], [1, 1], [2, 1],
30 [0, 0], [1, 0], [2, 0]
33 var state
= LEVEL_SELECT
;
36 var current_level
= 1;
39 function QueueChange(bead
, sound
) {
42 bead_change
.push(bead
)
47 for (var i
= a
.length
- 1; i
>= 1; --i
) {
48 var j
= PS
.Random(i
) - 1;
57 return a
[PS
.Random(a
.length
) - 1];
60 function CheckExact(pos
) {
61 for (var n
= 1; n
<= 9; ++n
) {
62 var x
= LEVEL_POS
[n
][0];
63 var y
= LEVEL_POS
[n
][1];
64 var on
= PS
.BeadColor(x
, y
) != PS
.COLOR_WHITE
;
65 var should
= pos
.indexOf(n
) >= 0;
66 if ((on
&& !should
) || (!on
&& should
)) {
73 function FollowMeStart(x
, y
) {
75 PS
.BeadColor(PS
.Random(3) - 1, PS
.Random(3) - 1, PS
.COLOR_BLACK
);
78 function FollowMePress(x
, y
) {
79 if (PS
.BeadColor(x
, y
) == PS
.COLOR_BLACK
) {
80 PS
.BeadColor(x
, y
, PS
.COLOR_WHITE
);
85 PS
.BeadColor(PS
.Random(3) - 1, PS
.Random(3) - 1, PS
.COLOR_BLACK
);
86 PS
.AudioPlay(PS
.Xylophone(count
== 3 ? 14 : 13), 0.5);
94 function EnumerateStart() {
97 function EnumeratePress(x
, y
) {
98 for (var n
= 1; n
< LEVEL_IDX
[y
][x
]; ++n
) {
99 if (PS
.BeadColor(LEVEL_POS
[n
][0], LEVEL_POS
[n
][1]) != PS
.COLOR_BLACK
) {
104 if (PS
.BeadColor(x
, y
) == PS
.COLOR_BLACK
) {
107 PS
.BeadColor(x
, y
, PS
.COLOR_BLACK
);
108 if (LEVEL_IDX
[y
][x
] == 9)
110 else if (LEVEL_IDX
[y
][x
] == 3)
111 PS
.AudioPlay(PS
.Xylophone(15), 0.5);
112 else if (LEVEL_IDX
[y
][x
] == 6)
113 PS
.AudioPlay(PS
.Xylophone(16), 0.5);
114 else if (LEVEL_IDX
[y
][x
] > 6)
115 PS
.AudioPlay(PS
.Xylophone(15), 0.5);
116 else if (LEVEL_IDX
[y
][x
] > 3)
117 PS
.AudioPlay(PS
.Xylophone(14), 0.5);
119 PS
.AudioPlay(PS
.Xylophone(13), 0.5);
123 var repeatorder
= [];
124 function RepeatStart() {
126 repeatorder
= Shuffle([1, 2, 3, 4, 5, 6, 7, 8, 9]).slice(0, 6);
127 for (var n
= 0; n
< repeatorder
.length
; ++n
) {
128 QueueChange([[PS
.COLOR_BLACK
, repeatorder
[n
]]],
129 [PS
.Xylophone(2 + n
)]);
133 function RepeatPress(x
, y
) {
134 var n
= repeatorder
.shift();
135 if (LEVEL_IDX
[y
][x
] == n
) {
136 PS
.BeadColor(x
, y
, PS
.COLOR_GREEN
);
137 if (repeatorder
.length
== 0) {
140 PS
.AudioPlay(PS
.Xylophone(9 + repeatorder
.length
), 0.5);
141 PS
.AudioPlay(PS
.Xylophone(9 + repeatorder
.length
), 0.5);
148 var missing_valid
= [];
149 function TickMissingStart() {
153 repeatorder
= Shuffle([1, 2, 3, 4, 5, 6, 7, 8, 9]).slice(0, 6);
154 repeatorder
= Shuffle(repeatorder
.concat(repeatorder
));
155 for (var n
= 0; n
< repeatorder
.length
; ++n
) {
156 if (d
[repeatorder
[n
]]) {
157 QueueChange([[PS
.COLOR_WHITE
, repeatorder
[n
]]],
158 [PS
.Xylophone(15 - n
)]);
160 d
[repeatorder
[n
]] = true;
161 QueueChange([[PS
.COLOR_RED
, repeatorder
[n
]]],
162 [PS
.Xylophone(13 + n
)]);
165 for (var n
= 1; n
<= 9; ++n
)
166 if (repeatorder
.indexOf(n
) < 0)
167 missing_valid
.push(n
);
171 function TickMissingPress(x
, y
) {
172 if (missing_valid
.indexOf(LEVEL_IDX
[y
][x
]) < 0) {
175 PS
.BeadColor(x
, y
, PS
.COLOR_BLACK
);
176 if (CheckExact(missing_valid
)) {
180 PS
.AudioPlay(PS
.Xylophone(count
== 2 ? 14 : 13), 0.5);
187 function ThreeRowGenerate() {
188 third_row
= Shuffle(Choose([
189 [1, 2, 3], [4, 5, 6], [7, 8, 9],
190 [1, 4, 7], [2, 5, 8], [3, 6, 9],
191 [1, 5, 9], [3, 5, 7]]));
192 QueueChange([[PS
.COLOR_BLACK
, third_row
[0]]], [PS
.Xylophone(13)]);
193 QueueChange([[PS
.COLOR_BLACK
, third_row
[1]]], [PS
.Xylophone(13)]);
196 function ThreeRowStart() {
202 function ThreeRowPress(x
, y
) {
203 if (LEVEL_IDX
[y
][x
] == third_row
[2]) {
205 PS
.BeadColor(x
, y
, PS
.COLOR_BLACK
);
206 PS
.Clock(PS
.Clock());
210 PS
.AudioPlay(PS
.Xylophone(14), 0.5);
211 QueueChange([[PS
.COLOR_GREEN
, third_row
[2]],
212 [PS
.COLOR_GREEN
, third_row
[1]],
213 [PS
.COLOR_GREEN
, third_row
[0]]],
215 QueueChange([[PS
.COLOR_WHITE
, third_row
[2]],
216 [PS
.COLOR_WHITE
, third_row
[1]],
217 [PS
.COLOR_WHITE
, third_row
[0]]]);
227 function SudokuRowGenerate() {
228 sudoku_row
= Shuffle(Choose([[1, 8, 6], [4, 8, 3], [4, 2, 9], [7, 2, 6]]));
229 QueueChange([[PS
.COLOR_RED
, sudoku_row
[0]]],
231 QueueChange([[PS
.COLOR_RED
, sudoku_row
[1]]],
235 function SudokuStart() {
241 function SudokuPress(x
, y
) {
242 if (LEVEL_IDX
[y
][x
] == sudoku_row
[2]) {
244 PS
.BeadColor(x
, y
, PS
.COLOR_BLACK
);
248 PS
.AudioPlay(PS
.Xylophone(13), 0.5);
249 QueueChange([[PS
.COLOR_GREEN
, sudoku_row
[2]],
250 [PS
.COLOR_GREEN
, sudoku_row
[1]],
251 [PS
.COLOR_GREEN
, sudoku_row
[0]]],
253 QueueChange([[PS
.COLOR_WHITE
, sudoku_row
[2]],
254 [PS
.COLOR_WHITE
, sudoku_row
[1]],
255 [PS
.COLOR_WHITE
, sudoku_row
[0]]]);
264 function DigitsStart() {
266 QueueChange([[PS
.COLOR_BLACK
, 1]], ["perc_hihat_closed"]);
267 QueueChange([[PS
.COLOR_WHITE
, 1],
268 [PS
.COLOR_BLACK
, 4]], ["perc_hihat_closed"]);
269 QueueChange([[PS
.COLOR_WHITE
, 4],
270 [PS
.COLOR_BLACK
, 7]], ["perc_hihat_closed"]);
271 QueueChange([[PS
.COLOR_WHITE
, 7]]);
273 QueueChange([[PS
.COLOR_BLACK
, 2],
275 [PS
.COLOR_BLACK
, 8]],
277 QueueChange([[PS
.COLOR_GREEN
, 2],
279 [PS
.COLOR_GREEN
, 8]],
281 QueueChange([[PS
.COLOR_WHITE
, 2],
283 [PS
.COLOR_WHITE
, 8]]);
287 function DigitsPress(x
, y
) {
288 if (PS
.BeadColor(x
, y
) == PS
.COLOR_WHITE
) {
289 PS
.BeadColor(x
, y
, PS
.COLOR_BLACK
);
291 PS
.BeadColor(x
, y
, PS
.COLOR_WHITE
);
294 if (count
== 4 && ApproveIfExact([7, 9, 4, 5, 6, 3]))
296 if (count
== 7 && (CheckExact([7, 8, 9, 6, 3])
297 || CheckExact([7, 8, 9, 6, 2]))) {
302 if (PS
.BeadColor(x
, y
) == PS
.COLOR_WHITE
) {
303 PS
.AudioPlay(PS
.Xylophone(10), 0.5);
305 PS
.AudioPlay(PS
.Xylophone(13), 0.3);
310 function ShapesStart() {
312 QueueChange([[PS
.COLOR_BLACK
, 1]], [PS
.Xylophone(13)]);
313 QueueChange([[PS
.COLOR_GREEN
, 1]], [PS
.Xylophone(14)]);
314 QueueChange([[PS
.COLOR_BLACK
, 1]]);
315 QueueChange([[PS
.COLOR_BLACK
, 4]], [PS
.Xylophone(13)]);
316 QueueChange([[PS
.COLOR_BLACK
, 7]], [PS
.Xylophone(13)]);
318 QueueChange([[PS
.COLOR_GREEN
, 1],
320 [PS
.COLOR_GREEN
, 7]],
322 QueueChange([[PS
.COLOR_BLACK
, 1],
324 [PS
.COLOR_BLACK
, 7]]);
328 function ShapesPress(x
, y
) {
329 if (PS
.BeadColor(x
, y
) == PS
.COLOR_WHITE
) {
330 PS
.BeadColor(x
, y
, PS
.COLOR_BLACK
);
332 PS
.BeadColor(x
, y
, PS
.COLOR_WHITE
);
335 if (count
== 3 && (ApproveIfExact([1, 4, 7, 2, 5, 3], PS
.COLOR_BLACK
)
336 || ApproveIfExact([1, 4, 7, 8, 5, 3], PS
.COLOR_BLACK
))) {
338 if (PS
.BeadColor(x
, y
) == PS
.COLOR_WHITE
)
339 PS
.AudioPlay(PS
.Xylophone(10), 0.3);
341 PS
.AudioPlay(PS
.Xylophone(13), 0.5);
342 } else if (count
== 4 && CheckExact([1, 2, 3, 4, 5, 6, 7, 8, 9]))
345 if (PS
.BeadColor(x
, y
) == PS
.COLOR_WHITE
)
346 PS
.AudioPlay(PS
.Xylophone(10), 0.3);
348 PS
.AudioPlay(PS
.Xylophone(13), 0.5);
352 function DieStart() {
354 QueueChange([[PS
.COLOR_BLACK
, 5]], [PS
.Xylophone(13)]);
355 QueueChange([[PS
.COLOR_GREEN
, 5]], [PS
.Xylophone(14)]);
356 QueueChange([[PS
.COLOR_WHITE
, 5]]);
357 QueueChange([[PS
.COLOR_BLACK
, 1]], [PS
.Xylophone(13)]);
358 QueueChange([[PS
.COLOR_BLACK
, 9]], [PS
.Xylophone(13)]);
359 QueueChange([[PS
.COLOR_GREEN
, 1],
360 [PS
.COLOR_GREEN
, 9]], [PS
.Xylophone(14)]);
361 QueueChange([[PS
.COLOR_WHITE
, 1],
362 [PS
.COLOR_WHITE
, 9]]);
366 function ApproveIfExact(a
, color
) {
368 color
= PS
.COLOR_WHITE
;
372 for (var n
= 0; n
< a
.length
; ++n
) {
373 green
.push([PS
.COLOR_GREEN
, a
[n
]]);
374 white
.push([color
, a
[n
]]);
376 QueueChange(green
, [PS
.Xylophone(14)]);
378 PS
.Clock(PS
.Clock());
379 if (PS
.StatusText() != "…")
387 function DiePress(x
, y
) {
388 if (PS
.BeadColor(x
, y
) == PS
.COLOR_WHITE
) {
389 PS
.BeadColor(x
, y
, PS
.COLOR_BLACK
);
391 PS
.BeadColor(x
, y
, PS
.COLOR_WHITE
);
395 if (ApproveIfExact([1, 5, 9]) || ApproveIfExact([7, 5, 3]))
399 if (ApproveIfExact([1, 3, 7, 9]))
403 if (ApproveIfExact([1, 3, 5, 7, 9]))
407 if (CheckExact([1, 2, 3, 7, 8, 9])
408 || CheckExact([1, 4, 7, 3, 6, 9])) {
414 if (PS
.BeadColor(x
, y
) == PS
.COLOR_WHITE
) {
415 PS
.AudioPlay(PS
.Xylophone(14), 0.5);
417 PS
.AudioPlay(PS
.Xylophone(13), 0.3);
423 [FollowMeStart
, FollowMePress
],
424 [ThreeRowStart
, ThreeRowPress
],
425 [EnumerateStart
, EnumeratePress
],
426 [RepeatStart
, RepeatPress
],
427 [TickMissingStart
, TickMissingPress
],
428 [SudokuStart
, SudokuPress
],
429 [ShapesStart
, ShapesPress
],
430 [DieStart
, DiePress
],
431 [DigitsStart
, DigitsPress
],
434 function Reset(color
) {
435 if (max_level
< 10 && PS
.StatusText() != "?")
437 if (max_level
== 10 && PS
.StatusText() != ".")
442 PS
.BeadColor(PS
.ALL
, PS
.ALL
, color
);
446 Reset(PS
.COLOR_GREEN
);
449 if (max_level
< current_level
) {
450 max_level
= current_level
;
451 if (max_level
== 10) {
452 setTimeout(function() {
453 PS
.AudioPlay(PS
.Xylophone(25), 0.5)
455 setTimeout(function() {
456 PS
.AudioPlay(PS
.Xylophone(30), 0.5)
458 setTimeout(function() {
459 PS
.AudioPlay(PS
.Xylophone(30), 0.5)
461 setTimeout(function() {
462 PS
.AudioPlay(PS
.Xylophone(31), 0.5)
464 setTimeout(function() {
465 PS
.AudioPlay(PS
.Xylophone(37), 0.5)
468 PS
.AudioPlay(PS
.Xylophone(34), 0.5);
469 PS
.AudioPlay(PS
.Xylophone(38), 0.5);
470 PS
.AudioPlay(PS
.Xylophone(39), 0.5);
473 PS
.AudioPlay(PS
.Xylophone(34), 0.5);
474 PS
.AudioPlay(PS
.Xylophone(38), 0.5);
475 PS
.AudioPlay(PS
.Xylophone(39), 0.5);
481 PS
.AudioPlay(PS
.Xylophone(2), 0.5);
482 PS
.AudioPlay(PS
.Xylophone(8), 0.5);
483 PS
.AudioPlay(PS
.Xylophone(22), 0.5);
488 PS
.Init = function (options
) {
489 var path
= window
.location
.href
;
490 PS
.AudioPath(path
.substr(0, path
.lastIndexOf("/")) + "/audio/");
494 for (var i
= 1; i
< 39; ++i
)
495 PS
.AudioLoad(PS
.Xylophone(i
));
496 PS
.AudioLoad("perc_hihat_closed");
499 function StartLevel(level
) {
500 if (level
> max_level
)
502 PS
.AudioPlay(PS
.Xylophone(1), 0.5);
503 Reset(PS
.COLOR_WHITE
);
504 current_level
= level
;
505 LEVELS
[current_level
][0]();
509 function LevelSelect() {
510 Reset(PS
.COLOR_WHITE
);
511 for (var n
= 1; n
<= 9; ++n
) {
512 var x
= LEVEL_POS
[n
][0];
513 var y
= LEVEL_POS
[n
][1];
515 PS
.BeadColor(x
, y
, PS
.COLOR_VIOLET
);
516 else if (n
< max_level
)
517 PS
.BeadColor(x
, y
, PS
.COLOR_BLUE
);
520 PS
.BeadColor(PS
.ALL
, PS
.ALL
, PS
.COLOR_BLUE
);
521 state
= LEVEL_SELECT
;
524 PS
.Click = function (x
, y
, data
, options
) {
525 if (PS
.Clock() > 0 && bead_change
.length
)
529 StartLevel(LEVEL_IDX
[y
][x
]);
532 LEVELS
[current_level
][1](x
, y
);
538 StartLevel(current_level
);
545 PS
.Release = function (x
, y
, data
, options
) {
548 PS
.Enter = function (x
, y
, data
, options
) {
549 if (state
== LEVEL_SELECT
&& PS
.BeadColor(x
, y
) != PS
.COLOR_WHITE
) {
550 PS
.AudioPlay( "fx_click" , 0.5);
554 PS
.Leave = function (x
, y
, data
, options
) {
557 PS
.KeyDown = function (key
, shift
, ctrl
, options
) {
558 if (ctrl
&& key
== 32) {
563 case PS
.KEYPAD_1
: case 49: PS
.Click(0, 2); break;
564 case PS
.KEYPAD_2
: case 50: PS
.Click(1, 2); break;
565 case PS
.KEYPAD_3
: case 51: PS
.Click(2, 2); break;
566 case PS
.KEYPAD_4
: case 52: PS
.Click(0, 1); break;
567 case PS
.KEYPAD_5
: case 53: PS
.Click(1, 1); break;
568 case PS
.KEYPAD_6
: case 54: PS
.Click(2, 1); break;
569 case PS
.KEYPAD_7
: case 55: PS
.Click(0, 0); break;
570 case PS
.KEYPAD_8
: case 56: PS
.Click(1, 0); break;
571 case PS
.KEYPAD_9
: case 57: PS
.Click(2, 0); break;
572 case PS
.KEY_ESCAPE
: LevelSelect(); break;
576 PS
.KeyUp = function (key
, shift
, ctrl
, options
) {
579 PS
.Wheel = function (dir
, options
) {
582 PS
.Tick = function (options
) {
583 var beads
= bead_change
.shift();
585 if (PS
.StatusText() != "…")
587 for (var n
= 0; n
< beads
.length
; ++n
) {
589 var x
= LEVEL_POS
[bead
[1]][0];
590 var y
= LEVEL_POS
[bead
[1]][1];
591 PS
.BeadColor(x
, y
, bead
[0]);
595 var sound
= sounds
.shift();
597 for (var n
= 0; n
< sound
.length
; ++n
) {
598 PS
.AudioPlay(sound
[n
], 0.5);
602 if (bead_change
.length
== 0 && PS
.StatusText() != "?")