From 72ddefebc48100681407d0124ad5dc9ca9eaa46d Mon Sep 17 00:00:00 2001 From: Aivo Paas Date: Tue, 12 Nov 2019 19:17:17 +0000 Subject: [PATCH 1/5] Added Clock-Tris game --- apps.json | 12 ++ apps/clock-tris-high | 1 + apps/clock-tris-icon.js | 1 + apps/clock-tris.js | 309 ++++++++++++++++++++++++++++++++++++++++ apps/clock-tris.json | 5 + apps/clock-tris.png | Bin 0 -> 219 bytes 6 files changed, 328 insertions(+) create mode 100644 apps/clock-tris-high create mode 100644 apps/clock-tris-icon.js create mode 100644 apps/clock-tris.js create mode 100644 apps/clock-tris.json create mode 100644 apps/clock-tris.png diff --git a/apps.json b/apps.json index 9c293b480..dc594e0ec 100644 --- a/apps.json +++ b/apps.json @@ -419,5 +419,17 @@ {"name":"-scolor","url":"show-color.js"}, {"name":"*scolor","url":"show-color-icon.js","evaluate":true} ] + }, + { "id": "clotris", + "name": "Clock-Tris", + "icon": "clock-tris.png", + "description": "A fully functional clone of a classic game of falling blocks", + "tags": "", + "storage": [ + {"name":"+clotris","url":"clock-tris.json"}, + {"name":"-clotris","url":"clock-tris.js"}, + {"name":"*clotris","url":"clock-tris-icon.js","evaluate":true}, + {"name":".trishig","url":"clock-tris-high"} + ] } ] diff --git a/apps/clock-tris-high b/apps/clock-tris-high new file mode 100644 index 000000000..c22708346 --- /dev/null +++ b/apps/clock-tris-high @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/apps/clock-tris-icon.js b/apps/clock-tris-icon.js new file mode 100644 index 000000000..f49fee565 --- /dev/null +++ b/apps/clock-tris-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("kEgiBC/AH4ADxlRAOo/JOuo/pyGbAIo//H/4//H+4ATP+oJJH/4//H/4/rACY/X/4AGP6o//H/4//H8IAnH5p1JP6Y//H/4//H8IAxP8KJTCZI//H/4//H5oAxH5YB1OuYA/AB4=")) diff --git a/apps/clock-tris.js b/apps/clock-tris.js new file mode 100644 index 000000000..514a9c2d2 --- /dev/null +++ b/apps/clock-tris.js @@ -0,0 +1,309 @@ +Bangle.setLCDMode("doublebuffered"); + +const storage = require("Storage"); + +var BTN_L = BTN1; +var BTN_R = BTN3; +var BTN_ROT = BTN2; +var BTN_DOWN = BTN5; +var BTN_PAUSE = BTN4; + +const W = g.getWidth(); +const H = g.getHeight(); +const CX = W / 2; +const CY = H / 2; + +const HEIGHT_BUFFER = 4; + +const LINES = 20; +const COLUMNS = 11; +const CELL_SIZE = Math.floor((H - HEIGHT_BUFFER) / (LINES + 1)); + +const BOARD_X = Math.floor((W - CELL_SIZE * COLUMNS) / 2) + 2; +const BOARD_Y = Math.floor((H - CELL_SIZE * (LINES + 1)) / 2); +const BOARD_W = COLUMNS * CELL_SIZE; +const BOARD_H = LINES * CELL_SIZE; + +const TEXT_X = BOARD_X + BOARD_W + 10; + +const BLOCKS = [ + [ + [2, 7], + [2, 6, 2], + [0, 7, 2], + [2, 3, 2] + ], + [ + [1, 3, 2], + [6, 3] + ], + [ + [2, 3, 1], + [3, 6] + ], + [ + [2, 2, 6], + [0, 7, 1], + [3, 2, 2], + [4, 7] + ], + [ + [2, 2, 3], + [1, 7], + [6, 2, 2], + [0, 7, 4] + ], + [ + [2, 2, 2, 2], + [0, 15] + ], + [[3, 3]] +]; + +const COLOR_WHITE = 0b1111111111111111; +const COLOR_BLACK = 0b0000000000000000; + +const BLOCK_COLORS = [ + //0brrrrrggggggbbbbb + 0b0111100000001111, + 0b0000011111100000, + 0b1111100000000011, + 0b0111100111100000, + 0b0000000000011111, + 0b0000001111111111, + 0b1111111111100000 +]; + +const EMPTY_LINE = 0b00000000000000; +const BOUNDARY = 0b10000000000010; +const FULL_LINE = 0b01111111111100; + +let gameOver = false; +let paused = false; +let currentBlock = 0; +let nextBlock = 0; +let x, y; +let points; +let level; +let lines; +let board; +let rotation = 0; +let ticker = null; +let needDraw = true; +let highScore = parseInt(storage.read(".trishig") || 0, 10); + +function getBlock(a, c, d) { + const block = BLOCKS[a % 7]; + return block[(a + c) % block.length]; +} + +function drawBlock(block, screenX, screenY, x, y) { + for (let row in block) { + let mask = block[row]; + for (let col = 0; mask; mask >>= 1, col++) { + if (mask % 2) { + const dx = screenX + (x + col) * CELL_SIZE; + const dy = screenY + (y + row) * CELL_SIZE; + g.fillRect(dx, dy, dx + CELL_SIZE - 3, dy + CELL_SIZE - 3); + } + } + } +} + +function drawBoard() { + g.setColor(COLOR_WHITE); + g.drawRect(BOARD_X - 3, BOARD_Y - 3, BOARD_X + BOARD_W, BOARD_Y + BOARD_H); + drawBlock(board, BOARD_X, BOARD_Y, -2, 0); + + g.setColor(BLOCK_COLORS[currentBlock]); + drawBlock(getBlock(currentBlock, rotation), BOARD_X, BOARD_Y, x - 2, y); +} + +function drawNextBlock() { + g.setFontAlign(0, -1, 0); + g.setColor(COLOR_WHITE); + g.drawString("NEXT BLOCK", BOARD_X / 2, 10); + g.setColor(BLOCK_COLORS[nextBlock]); + drawBlock(getBlock(nextBlock, 0), BOARD_X / 2 - 2 * CELL_SIZE, 25, 0, 0); +} + +function drawTextLine(text, line) { + g.drawString(text, TEXT_X, 10 + line * 15); +} + +function drawGameState() { + g.setFontAlign(-1, -1, 0); + g.setColor(COLOR_WHITE); + let ln = 0; + drawTextLine("CLOCK-TRIS", ln++); + ln++; + drawTextLine("LVL " + level, ln++); + drawTextLine("LNS " + lines, ln++); + drawTextLine("PTS " + points, ln++); + drawTextLine("TOP " + highScore, ln++); +} + +function drawBanner(text) { + g.setFontAlign(0, 0, 0); + g.setColor(COLOR_BLACK); + g.fillRect(CX - 46, CY - 11, CX + 46, CY + 9); + g.setColor(COLOR_WHITE); + g.drawRect(CX - 45, CY - 10, CX + 45, CY + 8); + g.drawString(text, CX, CY); +} + +function drawPaused() { + drawBanner("PAUSED"); +} + +function drawGameOver() { + drawBanner("GAME OVER"); +} + +function draw() { + g.clear(); + g.setColor(COLOR_WHITE); + drawBoard(); + drawNextBlock(); + drawGameState(); + if (paused) { + drawPaused(); + } + if (gameOver) { + drawGameOver(); + } + g.flip(); +} + +function getNextBlock() { + currentBlock = nextBlock; + nextBlock = (Math.random() * BLOCKS.length) | 0; + x = 6; + y = 0; + rotation = 0; +} + +function landBlock(a) { + const block = getBlock(currentBlock, rotation); + for (let row in block) { + board[y + (row | 0)] |= block[row] << x; + } + + let clearedLines = 0; + let keepLine = LINES; + for (let line = LINES - 1; line >= 0; line--) { + if (board[line] === FULL_LINE) { + clearedLines++; + } else { + board[--keepLine] = board[line]; + } + } + + lines += clearedLines; + if (lines > level * 10) { + level++; + setSpeed(); + } + + while (--keepLine > 0) { + board[keepLine] = EMPTY_LINE; + } + if (clearedLines) { + points += 100 * (1 << (clearedLines - 1)); + needDraw = true; + } + + getNextBlock(); + if (!checkMove(0, 0, 0)) { + gameOver = true; + needDraw = true; + highScore = Math.max(points, highScore); + storage.write(".trishig", highScore.toString()); + } +} + +function checkMove(dx, dy, rot) { + if (gameOver) { + startGame(); + return; + } + if (paused) { + return; + } + const block = getBlock(currentBlock, rotation + rot); + for (const row in block) { + const movedBlockRow = block[row] << (x + dx); + if ( + row + y === LINES - 1 || + movedBlockRow & board[y + dy + row] || + movedBlockRow & BOUNDARY + ) { + if (dy) { + landBlock(); + } + return false; + } + } + rotation += rot; + x += dx; + y += dy; + needDraw = true; + return true; +} + +function drawLoop() { + if (needDraw) { + needDraw = false; + draw(); + } + setTimeout(drawLoop, 10); +} + +function gameTick() { + if (!gameOver) { + checkMove(0, 1, 0); + } +} + +function setSpeed() { + if (ticker) { + clearInterval(ticker); + } + ticker = setInterval(gameTick, 1000 - level * 100); +} + +function togglePause() { + if (!gameOver) { + paused = !paused; + needDraw = true; + } +} + +function startGame() { + board = []; + for (let i = 0; i < LINES; i++) { + board[i] = EMPTY_LINE; + } + + gameOver = false; + points = 0; + lines = 0; + level = 0; + getNextBlock(); + setSpeed(); + needDraw = true; +} + +function bindButton(btn, dx, dy, r) { + setWatch(checkMove.bind(null, dx, dy, r), btn, { repeat: true }); +} + +bindButton(BTN_L, -1, 0, 0); +bindButton(BTN_R, 1, 0, 0); +bindButton(BTN_ROT, 0, 0, 1); +bindButton(BTN_DOWN, 0, 1, 0); + +setWatch(togglePause, BTN_PAUSE, { repeat: true }); + +startGame(); +drawLoop(); diff --git a/apps/clock-tris.json b/apps/clock-tris.json new file mode 100644 index 000000000..ddbb7d10c --- /dev/null +++ b/apps/clock-tris.json @@ -0,0 +1,5 @@ +{ + "name":"Clock-Tris", + "icon":"*clotris", + "src":"-clotris" +} \ No newline at end of file diff --git a/apps/clock-tris.png b/apps/clock-tris.png new file mode 100644 index 0000000000000000000000000000000000000000..a951c2dfa715fcc149fc36c2bb643529659846e9 GIT binary patch literal 219 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJ1)eUBArbD$DG3Qb&Koo|6kf1c zuzUCHbq@ogqPVv4%P}diA7IIRE0nT(#j6I-L)!!I%=;cDx;oxX)}Y3@;l>f~>jDR+ z8MuRaX`LVb*I(h9JW(*DA&F_$)Rx735+4K<%nAip^W~czc@~}IoN$Hlurf=>S60yp$^NNLhi?iK$olhDjAE-Xg;P}H$pmOreqd>PZ Pc)I$ztaD0eVqgFOXQ5J` literal 0 HcmV?d00001 From fcb2a168e6c763c011cd574c7fc4c63888a597c9 Mon Sep 17 00:00:00 2001 From: Mark McNelis Date: Tue, 12 Nov 2019 22:16:03 +0000 Subject: [PATCH 2/5] add: simple binary clock --- apps.json | 12 +++++ apps/clock-binary-icon.js | 1 + apps/clock-binary.js | 106 ++++++++++++++++++++++++++++++++++++++ apps/clock-binary.json | 7 +++ apps/clock-binary.png | Bin 0 -> 743 bytes 5 files changed, 126 insertions(+) create mode 100644 apps/clock-binary-icon.js create mode 100644 apps/clock-binary.js create mode 100644 apps/clock-binary.json create mode 100644 apps/clock-binary.png diff --git a/apps.json b/apps.json index 9c293b480..47bc18ef2 100644 --- a/apps.json +++ b/apps.json @@ -419,5 +419,17 @@ {"name":"-scolor","url":"show-color.js"}, {"name":"*scolor","url":"show-color-icon.js","evaluate":true} ] + }, + { "id": "bclock", + "name": "Binary Clock", + "icon": "clock-binary.png", + "description": "A simple binary clock watch face", + "tags": "clock", + "type":"clock", + "storage": [ + {"name":"+bclock","url":"clock-binary.json"}, + {"name":"-bclock","url":"clock-binary.js"}, + {"name":"*bclock","url":"clock-binary-icon.js","evaluate":true} + ] } ] diff --git a/apps/clock-binary-icon.js b/apps/clock-binary-icon.js new file mode 100644 index 000000000..1c167ff57 --- /dev/null +++ b/apps/clock-binary-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH8AAAAAAMGAAAAAAYDAAAAAAwBgAAAABgAwAAAABAAQAAAABAAQAAAABAAQAAAABAAQAAAABAAQAAAABgAwAAAAAwBgAAAAAYDAAAAAAMGAAAAAAH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH8AAAAAAP+AAAAAAf/AAAAAA//gAAAAB//wAAAAB//wAAAAB//wAAAAB//wAAAAB//wAAAAB//wAAAAB//wAAAAA//gAAAAAf/AAAAAAP+AAAAAAH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")) \ No newline at end of file diff --git a/apps/clock-binary.js b/apps/clock-binary.js new file mode 100644 index 000000000..1ece14b81 --- /dev/null +++ b/apps/clock-binary.js @@ -0,0 +1,106 @@ +(() => { + const canvasWidth = 290; + const numberOfColumns = 6; + const drawFullGrid = false; + + const colpos = canvasWidth / numberOfColumns - 10; + const binSize = (canvasWidth / numberOfColumns) / 3; + + const findBinary = target => { + return [ + [0, 0, 0, 0], // 0 + [1, 0, 0, 0], // 1 + [0, 1, 0, 0], // 2 + [1, 1, 0, 0], // 3 + [0, 0, 1, 0], // 4 + [1, 0, 1, 0], // 5 + [0, 1, 1, 0], // 6 + [1, 1, 1, 0], // 7 + [0, 0, 0, 1], // 8 + [1, 0, 0, 1], // 9 + ][target]; + }; + + const getCurrentTime = () => { + const flattenArray = (array = []) => [].concat.apply([], array); + const format = number => { + const numberStr = number.toString(); + return numberStr.length === 1 ? ["0", numberStr] : numberStr.split(""); + }; + const now = new Date(); + return flattenArray([now.getHours(), now.getMinutes(), now.getSeconds()].map(format)); + }; + + let prevFrame = []; + const drawColumn = (position = 0, column = [0, 0, 0, 0]) => { + const maxDotsPerColumn = [2, 4, 3, 4, 3, 4]; + + const columnPos = position * colpos; + let pos = colpos / 2 + 45; + const frame = column.reverse(); + const drawDot = fn => g[fn]((columnPos + colpos / 2), pos, binSize); + + for (let i = 0; i < frame.length; i += 1) { + if (i + maxDotsPerColumn[position] >= 4 || drawFullGrid) { + if (prevFrame && prevFrame[position] && prevFrame[position][i]) { + if (frame[i] !== prevFrame[position][i]) { + // subsequent draw + g.clearRect((columnPos + colpos / 2) - 15, pos - 15, (columnPos + colpos / 2) + 20, pos + 20); + if (frame[i]) { + drawDot('fillCircle'); + } else { + drawDot('drawCircle'); + } + } + } else { + // First draw + if (frame[i]) { + drawDot('fillCircle'); + } else { + drawDot('drawCircle'); + } + } + } + pos += colpos; + } + }; + + const drawClock = () => { + const data = getCurrentTime().map(findBinary); + for (let i = 0; i < data.length; i += 1) { + drawColumn(i, data[i]); + } + prevFrame = data; + }; + + // Themes + const drawTheme = (idx) => () => { + idx += 1; + const themes = [ + [[0, 0, 0], [1, 1, 1]], + [[1, 1, 1], [0, 0, 0]], + [[0, 0, 0], [1, 0, 0]], + [[0, 0, 0], [0, 1, 0]], + [[0, 0, 0], [0, 0, 1]], + ]; + if (idx >= themes.length) idx = 0; + const color = themes[idx]; + g.setBgColor.apply(g, color[0]); + g.setColor.apply(g, color[1]); + g.clear(); + }; + + const nextTheme = drawTheme(0); + setWatch(() => { + prevFrame = []; + Bangle.beep(); + nextTheme(); + }, BTN1, { repeat: true }); + + Bangle.on('lcdPower', on => { + if (on) drawClock(); + }); + + g.clear(); + setInterval(() => { drawClock(); }, 1000); +})(); \ No newline at end of file diff --git a/apps/clock-binary.json b/apps/clock-binary.json new file mode 100644 index 000000000..c00dd9d76 --- /dev/null +++ b/apps/clock-binary.json @@ -0,0 +1,7 @@ +{ + "name":"Binary Clock", + "type":"clock", + "icon":"*bclock", + "src":"-bclock" + } + \ No newline at end of file diff --git a/apps/clock-binary.png b/apps/clock-binary.png new file mode 100644 index 0000000000000000000000000000000000000000..21717834b8888d837c80d695e5a8e279d643b5f3 GIT binary patch literal 743 zcmV?P)EX>4Tx04R}tkv&MmKpe$iTcx5E2P=p;WT-A$C@SKpRVYG*P%E_RU~=gfG-*gu zTpR`0f`cE6RRU6f~epZjz4Dmjw@K7n|a>4rtTK|H-_ z>74h8!>lMN#OK8023?T&k?XR{Z=4Gb`*~*ANT=qB!^A?Njpa6GMMEW?B917kM*04X z%L?Z$&T6^Jn)l={4CSWO9|j z$gzM5R7j2={11M2Yvv~>+@w$(=zOv5j}aia3p8rB{e5iPjT0d73|wg~f29u0e3D*k zX^|tKZyUI{ZfWu!aJd5vKIxJnIZ}Y8Kc5HQ&*+=7K>sb!z2^4T+{ftykfyGZH^9Lm zFj}DOb&q#eSad^gZEa<4bO1wgWnpw> zWFU8GbZ8()Nlj2!fese{006&9L_t(&-tCx45`Z8K1QW{te;H5Sl%<>msOlVDr-dw| zn_0DkquAjA0000S9#_OeYyGaw?k;_otNXws{!M7>+!kA?C-6DmTA^hCqhD7IQ8L9x zK*&~Lc3-RVfIa{K014e!aeXMjY*|!xWO*6wL8!+GX%6%tB4#1^Fn9bkN*M(JOPQO* zB~!fGjfGjttTBRF=&zWqEG1J);=?RumiUky82vkqrKAc000016 Z#0?jkR$B%ZUBv(Z002ovPDHLkV1mx!II92v literal 0 HcmV?d00001 From 3f27b8f5e71a60a408d5ab7c9f2689d148a461ee Mon Sep 17 00:00:00 2001 From: Aivo Paas Date: Tue, 12 Nov 2019 22:55:02 +0000 Subject: [PATCH 3/5] Fix icon and font --- apps/clock-tris-icon.js | 2 +- apps/clock-tris.js | 2 +- apps/clock-tris.png | Bin 219 -> 303 bytes 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/clock-tris-icon.js b/apps/clock-tris-icon.js index f49fee565..5eaff3d1a 100644 --- a/apps/clock-tris-icon.js +++ b/apps/clock-tris-icon.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("kEgiBC/AH4ADxlRAOo/JOuo/pyGbAIo//H/4//H+4ATP+oJJH/4//H/4/rACY/X/4AGP6o//H/4//H8IAnH5p1JP6Y//H/4//H8IAxP8KJTCZI//H/4//H5oAxH5YB1OuYA/AB4=")) +require("heatshrink").decompress(atob("mEwgmIAHmN7oAB7AX/C/4X6a7gDC7oX/C/4X2AB2P/4AB/AvIC/4X/C+IATFQX/F5AX/C/4Xya8AIDCJAX/C/4XpAFw=")) \ No newline at end of file diff --git a/apps/clock-tris.js b/apps/clock-tris.js index 514a9c2d2..70f9036e7 100644 --- a/apps/clock-tris.js +++ b/apps/clock-tris.js @@ -162,7 +162,7 @@ function drawGameOver() { function draw() { g.clear(); - g.setColor(COLOR_WHITE); + g.setFontBitmap(); drawBoard(); drawNextBlock(); drawGameState(); diff --git a/apps/clock-tris.png b/apps/clock-tris.png index a951c2dfa715fcc149fc36c2bb643529659846e9..841182df476663c945afbc1a9107ace795cb4b09 100644 GIT binary patch literal 303 zcmV+~0nq-5P)~KpL6M4W0cJ0tuZeL z$N?o75olCE4&;K-yHKOZMYtT01D$y~PJ}vz7*@Wz18e>4Xde++P9dsXEpxPw9FPM) zvZM$tg|UkoQH5Y?2iU&TK21+bvhmiK-4mDrp9we+ZI%5300000NkvXXu0mjf005(U Bc|HID literal 219 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJ1)eUBArbD$DG3Qb&Koo|6kf1c zuzUCHbq@ogqPVv4%P}diA7IIRE0nT(#j6I-L)!!I%=;cDx;oxX)}Y3@;l>f~>jDR+ z8MuRaX`LVb*I(h9JW(*DA&F_$)Rx735+4K<%nAip^W~czc@~}IoN$Hlurf=>S60yp$^NNLhi?iK$olhDjAE-Xg;P}H$pmOreqd>PZ Pc)I$ztaD0eVqgFOXQ5J` From d7a004cd051fe4fe213c47b041cd788d9d1fd244 Mon Sep 17 00:00:00 2001 From: Aivo Paas Date: Tue, 12 Nov 2019 23:03:59 +0000 Subject: [PATCH 4/5] Fix font and icon yet again --- apps/clock-tris-icon.js | 2 +- apps/clock-tris.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/clock-tris-icon.js b/apps/clock-tris-icon.js index 5eaff3d1a..d24eac516 100644 --- a/apps/clock-tris-icon.js +++ b/apps/clock-tris-icon.js @@ -1 +1 @@ -require("heatshrink").decompress(atob("mEwgmIAHmN7oAB7AX/C/4X6a7gDC7oX/C/4X2AB2P/4AB/AvIC/4X/C+IATFQX/F5AX/C/4Xya8AIDCJAX/C/4XpAFw=")) \ No newline at end of file +require("heatshrink").decompress(atob("mEwiBC/AH4A/AHOQzYBNJ/5f/L/5P/L/5f/J/5f/L/5P/L/4A/AH6/haP5f/L/5f/L/5f/L/5f/L/5f/L/4A/AFP/ABy/lL/5f/L/5f/L/5f/L/5f/L/5f/L/4A/VtK/jL/5f/L/5f/L/5f/L/5f/L/5f/L/4A/X/7RxEa5f/L/5f/L/5f/L/5f/L/5f/L/5ffAH4A/AHYA=")) \ No newline at end of file diff --git a/apps/clock-tris.js b/apps/clock-tris.js index 70f9036e7..12fee4027 100644 --- a/apps/clock-tris.js +++ b/apps/clock-tris.js @@ -162,7 +162,7 @@ function drawGameOver() { function draw() { g.clear(); - g.setFontBitmap(); + g.setFont("6x8"); drawBoard(); drawNextBlock(); drawGameState(); From a3bd3f050421cda6577c35a323efb38a70c766b8 Mon Sep 17 00:00:00 2001 From: jh3y Date: Wed, 13 Nov 2019 01:34:58 +0000 Subject: [PATCH 5/5] feat: add morse code teacher --- apps.json | 12 ++++ apps/morse-code.js | 147 +++++++++++++++++++++++++++++++++++++++++++ apps/morse-code.json | 5 ++ apps/morse-code.png | Bin 0 -> 413 bytes 4 files changed, 164 insertions(+) create mode 100644 apps/morse-code.js create mode 100644 apps/morse-code.json create mode 100644 apps/morse-code.png diff --git a/apps.json b/apps.json index 06e1566cf..8ecfb07df 100644 --- a/apps.json +++ b/apps.json @@ -384,6 +384,18 @@ {"name":"*hrings","url":"hypno-rings-icon.js","evaluate":true} ] }, + { "id": "morse", + "name": "Morse Code", + "icon": "morse-code.png", + "description": "Learn morse code by hearing/seeing/feeling the code. Tap to toggle buzz!", + "tags": "morse,sound,visual,input", + "type":"app", + "storage": [ + {"name":"+morse","url":"morse-code.json"}, + {"name":"-morse","url":"morse-code.js"}, + {"name":"*morse","url":"morse-code-icon.js","evaluate":true} + ] + }, { "id": "blescan", "name": "BLE Scanner", diff --git a/apps/morse-code.js b/apps/morse-code.js new file mode 100644 index 000000000..72e58d6eb --- /dev/null +++ b/apps/morse-code.js @@ -0,0 +1,147 @@ +/** + * Teach a user morse code +*/ +/** + * Constants +*/ +const FONT_NAME = 'Vector12'; +const FONT_SIZE = 80; +const SCREEN_PIXELS = 240; +const UNIT = 100; +const MORSE_MAP = { + A: '.-', + B: '-...', + C: '-.-.', + D: '-..', + E: '.', + F: '..-.', + G: '--.', + H: '....', + I: '..', + J: '.---', + K: '-.-', + L: '.-..', + M: '--', + N: '-.', + O: '---', + P: '.--.', + Q: '--.-', + R: '.-.', + S: '...', + T: '-', + U: '..-', + V: '...-', + W: '.--', + X: '-..-', + Y: '-.--', + Z: '--..', + '1': '.----', + '2': '..---', + '3': '...--', + '4': '....-', + '5': '.....', + '6': '-....', + '7': '--...', + '8': '---..', + '9': '----.', + '0': '-----', +}; + +/** + * Set the local state +*/ +let INDEX = 0; +let BEEPING = false; +let BUZZING = true; +let UNIT_INDEX = 0; +let UNITS = MORSE_MAP[Object.keys(MORSE_MAP)[INDEX]].split(''); +/** + * Utility functions for writing text, changing state +*/ +const writeText = (txt) => { + g.clear(); + const width = g.stringWidth(txt); + g.drawString(txt, (SCREEN_PIXELS / 2) - (width / 2), SCREEN_PIXELS / 2); +}; +const writeLetter = () => { + writeText(Object.keys(MORSE_MAP)[INDEX]); +}; +const writeCode = () => { + writeText(MORSE_MAP[Object.keys(MORSE_MAP)[INDEX]]); +}; +const setUnits = () => { + UNITS = MORSE_MAP[Object.keys(MORSE_MAP)[INDEX]].split(''); +}; +/** + * Bootstrapping +*/ +g.clear(); +g.setFont(FONT_NAME, FONT_SIZE); +g.setColor(0, 1, 0); +g.setFontAlign(-1, 0, 0); +/** + * The length of a dot is one unit + * The length of a dash is three units + * The length of a space is one unit + * The space between letters is three units + * The space between words is seven units +*/ +const beepItOut = () => { + // If we are starting the beeps, use a timeout for pause of three units + const wait = UNIT_INDEX === 0 ? UNIT * 3 : 0; + setTimeout(() => { + Promise.all([ + Bangle.beep(UNITS[UNIT_INDEX] === '.' ? UNIT : 3 * UNIT), + // Could make buzz optional or switchable potentially + BUZZING ? Bangle.buzz(UNITS[UNIT_INDEX] === '.' ? UNIT : 3 * UNIT) : null + ]) + .then(() => { + if (UNITS[UNIT_INDEX + 1]) { + setTimeout(() => { + UNIT_INDEX++; + beepItOut(); + }, UNIT); + } else { + setTimeout(() => { + BEEPING = false; + UNIT_INDEX = 0; + writeLetter(); + }, 3 * UNIT); + } + }); + }, wait); +}; +const startBeep = () => { + if (BEEPING) return; + else { + BEEPING = true; + writeCode(); + beepItOut(); + } +}; + +const step = (positive) => () => { + if (BEEPING) return; + if (positive) { + INDEX = INDEX + 1; + if (INDEX > Object.keys(MORSE_MAP).length - 1) INDEX = 0; + } else { + INDEX = INDEX - 1; + if (INDEX < 0) INDEX = Object.keys(MORSE_MAP).length - 1; + } + setUnits(); + writeLetter(); +}; + +const toggleBuzzing = () => (BUZZING = !BUZZING); + +writeLetter(); + +// Press the middle button to hear the morse code translation +setWatch(startBeep, BTN2, { repeat: true }); +// Allow user to switch between letters +setWatch(step(true), BTN1, { repeat: true }); +setWatch(step(false), BTN3, { repeat: true }); +// Toggle buzzing/beeping with the touchscreen +setWatch(toggleBuzzing, BTN4, { repeat: true }); +setWatch(toggleBuzzing, BTN5, { repeat: true }); \ No newline at end of file diff --git a/apps/morse-code.json b/apps/morse-code.json new file mode 100644 index 000000000..bbd142c18 --- /dev/null +++ b/apps/morse-code.json @@ -0,0 +1,5 @@ +{ + "name":"Morse Code","type":"app", + "icon":"*morse", + "src":"-morse" +} \ No newline at end of file diff --git a/apps/morse-code.png b/apps/morse-code.png new file mode 100644 index 0000000000000000000000000000000000000000..41e1b405f97b1ae13249c44180b42082e116264c GIT binary patch literal 413 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTCmSQK*5Dp-y;YjHK@;M7UB8wRq z_>O=u<5X=vX`rBFiEBhjaDG}zd16s2LwR|*US?i)adKios$PCk`s{Z$QVa}?Ql2i3 zAsLOyQzlLNb>5(b@v?(;Ly*C%#zwZqauOLh7&=p=6cj#9Z=5>!hu+cY{60MQ_bVOh zZT)KByx>Osq&GExMIPpK*$PDeoaY$%UHsxJ^K*}qZ|rYlKR12C|HA=BZ%sYR#Hzgv z|Fr(9JfXfYke|my;*;D03*q_q&K@oJZe)G&m3&OP^TYb8<3~1ROX_=;{d3m(B9VAl ztEY#DCoS*(hU_2gg)b6VK1d`aB_(v|{A&oknDFhug!9aBM?2qR?m2Pz^`cAl8?=7? zpWpcKtZ~gTe~ HDWM4fug{>y literal 0 HcmV?d00001