module main author 'Turgut Guneysu' version 1 0 description 'A maze game for the Adafruit CLUE board' variables lineWidth lineColor M1 endZone spriteX spriteY bkgColor mazeBMP spriteR spriteColor spriteIncr mapMaze M2 selection selectionComplete spec ' ' 'H from _ _ len _' 'H from _ _ len _' 'auto auto auto' 0 0 0 spec ' ' 'V from _ _ len _' 'V from _ _ len _' 'auto auto auto' 0 0 0 spec ' ' 'clear display with _' 'clear display with _' 'color' spec ' ' 'drawMaze _' 'drawMaze _' 'auto' '' spec ' ' 'mapMaze _ _ _ H/V _' 'mapMaze _ _ _ H/V _' 'num num num str' 0 0 0 'H' spec ' ' 'set bkg color _' 'set bkg color _' 'color' spec ' ' 'set line color _' 'set line color _' 'color' spec ' ' 'set line width (1-10) _' 'set line width (1-10) _' 'auto' 10 spec ' ' 'Select Maze' 'Select Maze' spec ' ' 'Welcome' 'Welcome' spec ' ' 'check Collision' 'check Collision' spec 'r' 'collisionList _ _' 'collisionList _ _' 'num num' 0 0 spec ' ' 'draw Sprite _' 'draw Sprite _' 'color' spec ' ' 'checkAB' 'checkAB' to 'H from _ _ len _' Sx Sy length { '[tft:line]' Sx Sy (Sx + (length - 1)) Sy lineColor for i (lineWidth - 1) { '[tft:line]' Sx (Sy + i) (Sx + (length - 1)) (Sy + i) lineColor } if (mapMaze == (booleanConstant true)) { 'mapMaze _ _ _ H/V _' Sx Sy length 'H' } } to 'Select Maze' { '[tft:roundedRect]' 10 185 50 50 5 (colorSwatch 46 83 255 255) '[tft:text]' 'M1' 15 195 (colorSwatch 253 255 11 255) 4 false '[tft:roundedRect]' 180 185 50 50 5 (colorSwatch 46 83 255 255) '[tft:text]' 'M2' 183 195 (colorSwatch 253 255 11 255) 4 false '[tft:text]' 'Press' 90 185 (colorSwatch 209 255 6 255) 2 false '[tft:text]' 'A & B to' 75 205 (colorSwatch 209 255 6 255) 2 false '[tft:text]' 'select' 85 225 (colorSwatch 209 255 6 255) 2 false repeatUntil selectionComplete { checkAB if (('[sensors:tiltX]') < 0) { selection = 1 '[tft:rect]' 8 183 54 54 (colorSwatch 246 255 18 255) false '[tft:rect]' 178 183 54 54 (colorSwatch 0 0 0 255) false } (('[sensors:tiltX]') > 0) { selection = 2 '[tft:rect]' 178 183 54 54 (colorSwatch 246 255 18 255) false '[tft:rect]' 8 183 54 54 (colorSwatch 0 0 0 255) false } } } to 'V from _ _ len _' Sx Sy length { '[tft:line]' Sx Sy Sx (Sy + (length - 1)) lineColor for i (lineWidth - 1) { '[tft:line]' (Sx + i) Sy (Sx + i) (Sy + (length - 1)) lineColor } if (mapMaze == (booleanConstant true)) { 'mapMaze _ _ _ H/V _' Sx Sy length 'V' } } to Welcome { '[display:mbDisplayOff]' '[tft:text]' 'Welcome to' 25 1 (colorSwatch 242 255 14 255) 3 false '[tft:text]' 'MAZERUNNER' 0 40 (colorSwatch 255 118 118 255) 4 false '[tft:text]' 'Directions:' 1 90 (colorSwatch 255 255 255 255) 2 false '[tft:text]' 'TILT: move sprite' 12 110 (colorSwatch 255 255 255 255) '[tft:text]' 'But-A: slow down' 1 130 (colorSwatch 255 255 255 255) '[tft:text]' 'But-B: speed up' 1 150 (colorSwatch 255 255 255 255) } to 'check Collision' { if (('[data:find]' 1 ('collisionList _ _' spriteX spriteY)) != -1) { comment 'Process collision' 'play tone' 'E' 2 5 mapMaze = (booleanConstant false) if (selection == 1) { 'drawMaze _' M1 } else { 'drawMaze _' M2 } } if (('[data:find]' 9 ('collisionList _ _' spriteX spriteY)) != -1) { comment 'Process FINISH' 'play tone' 'C' 0 100 'play tone' 'E' 0 100 'play tone' 'G' 0 100 'play tone' 'C' 1 300 mapMaze = (booleanConstant false) if (selection == 1) { 'drawMaze _' M1 } else { 'drawMaze _' M2 } '[tft:roundedRect]' 20 20 200 200 8 (colorSwatch 253 255 20 255) true '[tft:text]' 'Best time' 35 50 (colorSwatch 0 0 0 255) 3 false '[tft:text]' ((timer) / 1000) 50 100 (colorSwatch 0 0 0 255) 5 false '[tft:text]' 'seconds' 35 160 (colorSwatch 0 0 0 255) 3 false waitMillis 3000 'clear display with _' bkgColor spriteX = 0 spriteY = 0 if (selection == 1) { 'drawMaze _' M1 } else { 'drawMaze _' M2 } 'draw Sprite _' spriteColor sendBroadcast 'start' } } to checkAB { if (and (buttonA) (buttonB)) { selectionComplete = (booleanConstant true) '[tft:roundedRect]' 20 20 200 200 8 (colorSwatch 253 255 20 255) true '[tft:text]' 'Maze' 90 50 (colorSwatch 0 0 0 255) 3 false '[tft:text]' ('[data:join]' 'M' selection) 95 100 (colorSwatch 0 0 0 255) 5 false '[tft:text]' 'selected' 45 160 (colorSwatch 0 0 0 255) 3 false waitMillis 3000 return 0 } } to 'clear display with _' color { '[tft:rect]' 0 0 ('[tft:getWidth]') ('[tft:getHeight]') color true comment 'Give the display controller time to finish this operation' waitMillis 1 } to 'collisionList _ _' x y { comment 'Extract surrounding box edge data TFT x/y is 0-based: add 1 to xlate to BMP subtract 1 to allow for i-loop logic' local 'collisionList' ('[data:makeList]') local 'startX' (x - spriteR) local 'startY' (y - spriteR) for i spriteR { '[data:addLast]' (at (startX + i) (at (startY + 1) mazeBMP)) collisionList '[data:addLast]' (at (startX + i) (at (startY + spriteR) mazeBMP)) collisionList } local 'startX' (x + spriteR) local 'startY' (y + spriteR) for i (spriteR - 2) { '[data:addLast]' (at (startX + 1) (at ((startY + i) + 1) mazeBMP)) collisionList '[data:addLast]' (at (startX + spriteR) (at ((startY + i) + 1) mazeBMP)) collisionList } return collisionList } to 'draw Sprite _' color { '[tft:circle]' spriteX spriteY spriteR color } to 'drawMaze _' list { for lines (at 1 list) { local 'line' ('[data:split]' lines ',') 'H from _ _ len _' ((at 1 line) + 0) ((at 2 line) + 0) ((at 3 line) + 0) } for lines (at 2 list) { local 'line' ('[data:split]' lines ',') 'V from _ _ len _' ((at 1 line) + 0) ((at 2 line) + 0) ((at 3 line) + 0) } for lines (at 3 list) { local 'line' ('[data:split]' lines ',') '[tft:rect]' ((at 1 line) + 0) ((at 2 line) + 0) ((at 3 line) + 0) ((at 4 line) + 0) ((at 5 line) + 0) if (and (spriteX == 0) (spriteY == 0)) { spriteX = ((at 1 line) + ((at 3 line) / 2)) spriteY = ((at 2 line) + (at 4 line)) } } comment 'Map the Finish with 9, items are from rect above.' for pixel ((at 4 line) + 0) { for i ((at 3 line) + 0) { atPut (((at 1 line) + 0) + i) (at (((at 2 line) + 0) + pixel) mazeBMP) 9 } } } to 'mapMaze _ _ _ H/V _' x y len hv { for pixel lineWidth { for i len { if (hv == 'H') { atPut (x + i) (at (y + pixel) mazeBMP) 1 } (hv == 'V') { atPut (x + pixel) (at (y + i) mazeBMP) 1 } } } } to 'set bkg color _' color { bkgColor = color } to 'set line color _' color { lineColor = color } to 'set line width (1-10) _' width { if (and (width >= 1) (width <= 10)) { lineWidth = width } else { sayIt 'Width error.' } } script 66 66 { whenStarted M1 = ('[data:makeList]' ('[data:makeList]' '0,0,90' '150,0,90' '50,90,80' '50,190,40' '100,150,100' '190,190,50' '0,230,90' '150,230,90') ('[data:makeList]' '0,0,239' '50,50,150' '90,0,50' '90,190,50' '130,50,50' '150,150,90' '190,50,100' '230,0,240') ('[data:makeList]' '100,0,50,20,65280' '100,220,50,20,16711680')) } script 916 72 { whenBroadcastReceived 'start' comment 'Movement loop' local 'loopspeed' 40 resetTimer forever { if (('[sensors:tiltX]') > 0) { local 'deltaX' spriteIncr } (('[sensors:tiltX]') < 0) { local 'deltaX' (0 - spriteIncr) } if (('[sensors:tiltY]') > 0) { local 'deltaY' spriteIncr } (('[sensors:tiltY]') < 0) { local 'deltaY' (0 - spriteIncr) } 'draw Sprite _' bkgColor spriteX += deltaX spriteY += deltaY 'check Collision' if (spriteX < (lineWidth + spriteR)) { spriteX = ((lineWidth + spriteR) + 3) } (spriteX > (('[tft:getWidth]') - (spriteR + lineWidth))) { spriteX = (('[tft:getWidth]') - ((lineWidth + spriteR) + 3)) } if (spriteY < (lineWidth + spriteR)) { spriteY = ((lineWidth + spriteR) + 3) } (spriteY > (('[tft:getHeight]') - (spriteR + lineWidth))) { spriteY = (('[tft:getHeight]') - ((lineWidth + spriteR) + 3)) } 'draw Sprite _' spriteColor waitMillis loopspeed } } script 524 74 { whenStarted '[display:mbDisplayOff]' 'set bkg color _' (colorSwatch 255 255 255 255) 'set line color _' (colorSwatch 31 137 255 255) 'set line width (1-10) _' 10 'clear display with _' bkgColor mazeBMP = ('[data:makeList]') spriteX = 0 spriteY = 0 spriteR = 6 spriteIncr = 1 spriteColor = (makeColor 0 0 0) selection = 0 selectionComplete = (booleanConstant false) repeat 240 { '[data:addLast]' ('[data:newByteArray]' 240 0) mazeBMP } mapMaze = (booleanConstant true) Welcome 'Select Maze' 'clear display with _' bkgColor if (selection == 1) { 'drawMaze _' M1 } else { 'drawMaze _' M2 } 'draw Sprite _' spriteColor sendBroadcast 'start' } script 68 290 { whenStarted M2 = ('[data:makeList]' ('[data:makeList]' '0,0,100' '0,190,50' '50,50,100' '50,140,50' '0,230,230' '150,0,90' '150,100,50' '150,190,50' '190,50,50' '190,140,50') ('[data:makeList]' '0,0,240' '50,50,100' '100,140,100' '140,50,150' '230,0,240') ('[data:makeList]' '100,0,50,20,65280' '65,65,70,70,16711680')) } script 68 492 { whenButtonPressed 'A' spriteIncr = (maximum 1 (spriteIncr - 1)) } script 70 598 { whenButtonPressed 'B' spriteIncr = (minimum 3 (spriteIncr + 1)) } script 80 726 { 'drawMaze _' M1 spriteX = 175 spriteY = 75 'draw Sprite _' (colorSwatch 0 0 0 255) } module 'Basic Sensors' Input author MicroBlocks version 1 1 tags tilt acceleration light sensor choices accelerometerRange '1' '2' '4' '8' description 'Provides blocks to read tilt in the three axes, acceleration, temperature and light level. Many boards come with this particular set of sensors, such as the micro:bit, the Circuit Playground Express, the Calliope or the Citilab ED1.' spec 'r' '[sensors:tiltX]' 'tilt x' spec 'r' '[sensors:tiltY]' 'tilt y' spec 'r' '[sensors:tiltZ]' 'tilt z' spec 'r' '[sensors:acceleration]' 'acceleration' spec 'r' '[display:lightLevel]' 'light level' spec 'r' '[sensors:temperature]' 'temperature (°C)' spec 'r' '[sensors:magneticField]' 'magnetic field' spec ' ' '_setAccelRange' 'set acceleration range _ g = 100' 'menu.accelerometerRange' '1' to '_setAccelRange' n { '[sensors:setAccelerometerRange]' (0 + n) } module TFT Output author MicroBlocks version 1 2 tags tft graphics draw description 'Draw graphics and write text on boards with a TFT display, such as the M5Stack, M5Stick, Citilab ED1 or (discontinued) IoT-Bus.' spec ' ' '[display:mbDisplayOff]' 'clear display' spec ' ' '[tft:rect]' 'draw rectangle on TFT at x _ y _ width _ height _ color _ : filled _' 'num num num num color bool' 10 10 40 30 nil true spec ' ' '[tft:roundedRect]' 'draw rounded rectangle on TFT at x _ y _ width _ height _ radius _ color _ : filled _' 'num num num num num color bool' 10 10 40 30 8 nil true spec ' ' '[tft:circle]' 'draw circle on TFT at x _ y _ radius _ color _ : filled _' 'num num num color bool' 40 40 30 nil true spec ' ' '[tft:triangle]' 'draw triangle on TFT at x _ y _ , x _ y _ , x _ y _ color _ : filled _' 'num num num num num num color bool' 20 20 30 80 60 5 nil true spec ' ' '[tft:text]' 'write _ on TFT at x _ y _ color _ : scale _ wrap _' 'str num num color num bool' 'Hello World!' 5 5 nil 2 true spec ' ' '[tft:line]' 'draw line on TFT from x _ y _ to x _ y _ color _' 'num num num num color' 12 8 25 15 spec ' ' '[tft:setPixel]' 'set TFT pixel x _ y _ to _' 'auto auto color' '10' '10' spec 'r' 'makeColor' 'color r _ g _ b _ (0-255)' 'auto auto auto' 0 100 100 spec 'r' 'randomColor' 'random color' spec 'r' '[tft:getWidth]' 'TFT width' spec 'r' '[tft:getHeight]' 'TFT height' spec ' ' '[tft:enableDisplay]' 'enable TFT _' 'bool' true spec ' ' '[tft:setBacklight]' 'set TFT backlight _ (0-10)' 'num' 10 to makeColor r g b { r = (maximum 0 (minimum r 255)) g = (maximum 0 (minimum g 255)) b = (maximum 0 (minimum b 255)) return ((r << 16) | ((g << 8) | b)) } to randomColor { local 'n1' (random 100 200) local 'n2' (random 0 100) if (1 == (random 1 3)) { return ((n1 << 16) | (n2 << 8)) } (1 == (random 1 2)) { return ((n2 << 16) | n1) } else { return ((n1 << 8) | n2) } } module Tone Output author MicroBlocks version 1 6 tags tone sound music audio note speaker description 'Audio tone generation. Make music with MicroBlocks!' variables _tonePin _toneInitalized _toneLoopOverhead _toneNoteNames _toneArezzoNotes _toneFrequencies spec ' ' 'play tone' 'play note _ octave _ for _ ms' 'auto num num' 'C' 0 500 spec ' ' 'playMIDIKey' 'play midi key _ for _ ms' 'num num' 60 500 spec ' ' 'play frequency' 'play frequency _ for _ ms' 'num num' 261 500 spec ' ' 'attach buzzer to pin' 'attach buzzer to pin _' 'auto' '' spec 'r' '_measureLoopOverhead' '_measureLoopOverhead' spec 'r' '_baseFreqForNote' '_baseFreqForNote _' 'auto' 'c' spec 'r' '_baseFreqForSemitone' '_baseFreqForSemitone _' 'num' 0 spec ' ' '_toneLoop' '_toneLoop _ for _ ms' 'num num' 440000 100 spec 'r' '_trimmedLowercase' '_trimmedLowercase _' 'str' 'A. b C...' to '_baseFreqForNote' note { comment 'Return the frequency for the given note in the middle-C octave scaled by 1000. For example, return 440000 (440Hz) for A. Note names may be upper or lower case. Note names may be followed by # for a sharp or b for a flat.' local 'normalized note' ('_trimmedLowercase' note) if (_toneNoteNames == 0) { _toneNoteNames = ('[data:makeList]' 'c' 'c#' 'd' 'd#' 'e' 'f' 'f#' 'g' 'g#' 'a' 'a#' 'b' 'c_' 'db' 'd_' 'eb' 'e_' 'e#' 'f_' 'gb' 'g_' 'ab' 'a_' 'bb' 'b_' 'b#') _toneArezzoNotes = ('[data:makeList]' 'do' 'do#' 're' 're#' 'mi' 'fa' 'fa#' 'sol' 'sol#' 'la' 'la#' 'si' 'do_' 'dob' 're_' 'reb' 'mi_' 'mi#' 'fa_' 'solb' 'sol_' 'lab' 'la_' 'sib' 'si_' 'si#') } if (('[data:find]' (v 'normalized note') _toneArezzoNotes) > 0) { return ('_baseFreqForSemitone' ('[data:find]' (v 'normalized note') _toneArezzoNotes)) } else { return ('_baseFreqForSemitone' ('[data:find]' (v 'normalized note') _toneNoteNames)) } } to '_baseFreqForSemitone' semitone { if (_toneFrequencies == 0) {_toneFrequencies = ('[data:makeList]' 261626 277183 293665 311127 329628 349228 369994 391995 415305 440000 466164 493883 246942 277183 277183 311127 311127 349228 329628 369994 369994 415305 415305 466164 466164 523252)} if (and (1 <= semitone) (semitone <= (size _toneFrequencies))) { return (at semitone _toneFrequencies) } else { comment 'Bad note name; return 10 Hz' return 10000 } } to '_measureLoopOverhead' { comment 'Measure the loop overhead on this device' local 'halfCycle' 100 local 'startT' (microsOp) repeat 100 { digitalWriteOp _tonePin false waitMicros halfCycle digitalWriteOp _tonePin false waitMicros halfCycle } local 'usecs' ((microsOp) - startT) return ((usecs - 20000) / 200) } to '_toneLoop' scaledFreq ms { if (_toneInitalized == 0) {'attach buzzer to pin' ''} if ('[io:hasTone]') { '[io:playTone]' _tonePin (scaledFreq / 1000) waitMillis ms '[io:playTone]' _tonePin 0 } else { local 'halfCycle' ((500000000 / scaledFreq) - _toneLoopOverhead) local 'cycles' ((ms * 500) / halfCycle) repeat cycles { digitalWriteOp _tonePin true waitMicros halfCycle digitalWriteOp _tonePin false waitMicros halfCycle } } } to '_trimmedLowercase' s { comment 'Return a copy of the given string without whitespace or periods and all lowercase.' local 'result' (newList (size s)) '[data:delete]' 'all' result for i (size s) { local 'ch' ('[data:unicodeAt]' i s) if (and (ch > 32) (ch != 46)) { if (and (65 <= ch) (ch <= 90)) {ch = (ch + 32)} '[data:addLast]' ch result } } return ('[data:unicodeString]' result) } to 'attach buzzer to pin' pinNumber { if (pinNumber == '') { comment 'Pin number not specified; use default pin for this device' if ((boardType) == 'Citilab ED1') { _tonePin = 26 } ((boardType) == 'M5Stack-Core') { _tonePin = 25 } ((boardType) == 'M5StickC') { _tonePin = 26 } ((boardType) == 'Calliope') { digitalWriteOp 23 true digitalWriteOp 24 true _tonePin = 25 } ((boardType) == 'D1-Mini') { _tonePin = 12 } else { _tonePin = -1 } } else { _tonePin = pinNumber } _toneLoopOverhead = ('_measureLoopOverhead') _toneInitalized = (booleanConstant true) } to 'play frequency' freq ms { '_toneLoop' (freq * 1000) ms } to 'play tone' note octave ms { local 'freq' ('_baseFreqForNote' note) if (freq <= 10000) { waitMillis ms return 0 } if (octave < 0) { repeat (absoluteValue octave) { freq = (freq / 2) } } repeat octave { freq = (freq * 2) } '_toneLoop' freq ms } to playMIDIKey key ms { local 'freq' ('_baseFreqForSemitone' ((key % 12) + 1)) local 'octave' ((key / 12) - 5) if (octave < 0) { repeat (absoluteValue octave) { freq = (freq / 2) } } repeat octave { freq = (freq * 2) } '_toneLoop' freq ms }