1+ /*
2+ @title : max_move
3+ @tags : ['beginner']
4+ @addedOn : 2025-11-06
5+ @author : nick extra levels by: sam, minghao
6+
7+ Check the tutorial in the bottom right, the run button is in the top right.
8+ Make sure to remix this tutorial if you want to save your progress!
9+ */
10+
11+ function wait ( time ) {
12+ let startTime_ = performance . now ( ) ;
13+ while ( performance . now ( ) - startTime_ < time ) { }
14+ }
15+
16+ // define the sprites in our game
17+ const player = "p" ;
18+ const box = "b" ;
19+ const goal = "g" ;
20+ const wall = "w" ;
21+
22+ // create sound
23+ const melody = tune `
24+ 500: D5~500,
25+ 15500`
26+
27+ // assign bitmap art to each sprite
28+ setLegend (
29+ [ player , bitmap `
30+ ................
31+ ................
32+ ................
33+ .......0........
34+ .....00.000.....
35+ ....0.....00....
36+ ....0.0.0..0....
37+ ....0......0....
38+ ....0......0....
39+ ....00....0.....
40+ ......00000.....
41+ ......0...0.....
42+ ....000...000...
43+ ................
44+ ................
45+ ................` ] ,
46+ [ box , bitmap `
47+ ................
48+ ................
49+ ................
50+ ...88888888888..
51+ ...8....8....8..
52+ ...8....8....8..
53+ ...8....8....8..
54+ ...8....8....8..
55+ ...88888888888..
56+ ...8....8....8..
57+ ...8....8....8..
58+ ...8....8....8..
59+ ...8....8....8..
60+ ...88888888888..
61+ ................
62+ ................` ] ,
63+ [ goal , bitmap `
64+ ................
65+ ................
66+ ................
67+ ....444444......
68+ ...44....44.....
69+ ...4......4.....
70+ ...4.......4....
71+ ...4.......4....
72+ ...4.......4....
73+ ...44......4....
74+ ....4......4....
75+ ....44....44....
76+ .....444444.....
77+ ................
78+ ................
79+ ................` ] ,
80+ [ wall , bitmap `
81+ 0000000000000000
82+ 0000000000000000
83+ 0000000000000000
84+ 0000000000000000
85+ 0000000000000000
86+ 0000000000000000
87+ 0000000000000000
88+ 0000000000000000
89+ 0000000000000000
90+ 0000000000000000
91+ 0000000000000000
92+ 0000000000000000
93+ 0000000000000000
94+ 0000000000000000
95+ 0000000000000000
96+ 0000000000000000` ]
97+ ) ;
98+
99+ // create game levels
100+ let level = 0 ; // this tracks the level we are on
101+ const levels = [
102+ map `
103+ w...g
104+ .....
105+ .....
106+ p..w.` ,
107+ map `
108+ p..
109+ .b.
110+ ..g` ,
111+ map `
112+ p.wg
113+ .b..
114+ ....
115+ ..w.` ,
116+ map `
117+ pb..
118+ ...b
119+ ....
120+ .bbg` ,
121+ map `
122+ ...
123+ .p.
124+ ...` ,
125+ map `
126+ ppw.
127+ ..wg
128+ ....
129+ ...g` ,
130+ map `
131+ pp.w.
132+ ...g.
133+ .b.w.
134+ ..bg.` ,
135+ map `
136+ pp...
137+ ..wg.
138+ .b.w.
139+ ..bg.` ,
140+ map `
141+ ....p.
142+ p...w.
143+ w.bb..
144+ ..b...
145+ g.gwb.
146+ ......`
147+ ] ;
148+
149+ // set the map displayed to the current level
150+ const currentLevel = levels [ level ] ;
151+ setMap ( currentLevel ) ;
152+
153+ setSolids ( [ player , box , wall ] ) ; // other sprites cannot go inside of these sprites
154+
155+ // allow certain sprites to push certain other sprites
156+ setPushables ( {
157+ [ player ] : [ box ] ,
158+ [ box ] : [ box ]
159+ } ) ;
160+
161+ function onGoal ( playerSprite ) {
162+ let sprites = getTile ( playerSprite . x , playerSprite . y ) ;
163+ let result = false ;
164+ for ( let i = 0 ; i < sprites . length ; i ++ ) {
165+ if ( sprites [ i ] . type === "g" ) {
166+ result = true ;
167+ break ;
168+ }
169+ }
170+ return result ;
171+ }
172+
173+
174+ function getPlayerSprite ( ) {
175+ let finalSprite ;
176+ let found = false ;
177+ getAll ( player ) . forEach ( ( playerSprite ) => {
178+ if ( found ) return ;
179+ let valid = true ;
180+ getTile ( playerSprite . x , playerSprite . y ) . forEach ( ( sprite ) => {
181+ if ( sprite . type == goal ) valid = false ;
182+ } ) ;
183+ if ( valid ) {
184+ finalSprite = playerSprite ;
185+ }
186+ } ) ;
187+ return finalSprite ;
188+ }
189+
190+ var isMoving = false ;
191+
192+ // inputs for player movement control
193+ onInput ( "w" , ( ) => {
194+ if ( isMoving ) return ;
195+ else isMoving = true ;
196+ let playerSprite = getPlayerSprite ( ) ;
197+ let lastY = playerSprite . y + 1 ;
198+ function move ( ) {
199+ if ( ! ( lastY != playerSprite . y && playerSprite . y > 0 && ! onGoal ( playerSprite ) ) ) {
200+ isMoving = false ;
201+ setTimeout ( afterMove , 1 ) ;
202+ return ;
203+ }
204+ lastY = playerSprite . y ;
205+ playerSprite . y -= 1 ;
206+ playTune ( melody ) ;
207+ setTimeout ( move , 20 ) ;
208+ }
209+ move ( ) ;
210+ } ) ;
211+
212+ onInput ( "s" , ( ) => {
213+ if ( isMoving ) return ;
214+ else isMoving = true ;
215+ let playerSprite = getPlayerSprite ( ) ;
216+ let lastY = playerSprite . y - 1 ;
217+ function move ( ) {
218+ if ( ! ( lastY != playerSprite . y && playerSprite . y < height ( ) - 1 && ! onGoal ( playerSprite ) ) ) {
219+ isMoving = false ;
220+ setTimeout ( afterMove , 1 ) ;
221+ return ;
222+ }
223+ lastY = playerSprite . y ;
224+ playerSprite . y += 1 ; // positive y is downwards
225+ playTune ( melody ) ;
226+ setTimeout ( move , 20 ) ;
227+ }
228+ move ( ) ;
229+ } ) ;
230+
231+ onInput ( "a" , ( ) => {
232+ if ( isMoving ) return ;
233+ else isMoving = true ;
234+ let playerSprite = getPlayerSprite ( ) ;
235+ let lastX = playerSprite . x + 1 ;
236+ function move ( ) {
237+ if ( ! ( lastX != playerSprite . x && playerSprite . x > 0 && ! onGoal ( playerSprite ) ) ) {
238+ isMoving = false ;
239+ setTimeout ( afterMove , 1 ) ;
240+ return ;
241+ }
242+ lastX = playerSprite . x ;
243+ playerSprite . x -= 1 ;
244+ playTune ( melody ) ;
245+ setTimeout ( move , 20 ) ;
246+ }
247+ move ( ) ;
248+ } ) ;
249+
250+ onInput ( "d" , ( ) => {
251+ if ( isMoving ) return ;
252+ else isMoving = true ;
253+ let playerSprite = getPlayerSprite ( ) ;
254+ let lastX = playerSprite . x - 1 ;
255+ function move ( ) {
256+ if ( ! ( lastX != playerSprite . x && playerSprite . x < width ( ) - 1 && ! onGoal ( playerSprite ) ) ) {
257+ isMoving = false ;
258+ setTimeout ( afterMove , 1 ) ;
259+ return ;
260+ }
261+ lastX = playerSprite . x ;
262+ playerSprite . x += 1 ;
263+ playTune ( melody ) ;
264+ setTimeout ( move , 20 ) ;
265+ }
266+ move ( ) ;
267+ } ) ;
268+
269+ // input to reset level
270+ onInput ( "j" , ( ) => {
271+ const currentLevel = levels [ level ] ; // get the original map of the level
272+
273+ // make sure the level exists before we load it
274+ if ( currentLevel !== undefined ) {
275+ clearText ( "" ) ;
276+ setMap ( currentLevel ) ;
277+ }
278+ } ) ;
279+
280+ // this gets run after every move
281+ function afterMove ( ) {
282+ clearText ( ) ;
283+
284+ // count the number of tiles with goals
285+ const targetNumber = tilesWith ( goal ) . length ;
286+
287+ // count the number of tiles with goals and players
288+ const numberCovered = tilesWith ( goal , player ) . length ;
289+
290+ // if the number of goals is the same as the number of goals covered
291+ // all goals are covered and we can go to the next level
292+ if ( numberCovered === targetNumber ) {
293+ // increase the current level number
294+ level = level + 1 ;
295+
296+ const currentLevel = levels [ level ] ;
297+
298+ // make sure the level exists and if so set the map
299+ // otherwise, we have finished the last level, there is no level
300+ // after the last level
301+ if ( currentLevel !== undefined ) {
302+ setMap ( currentLevel ) ;
303+ } else {
304+ addText ( "you win!" , { y : 4 , color : color `3` } ) ;
305+ }
306+ }
307+ }
308+
309+ addText ( "WASD, j to restart" , { y : 4 , color : color `3` } ) ;
0 commit comments