Provide the source code of a microcontroller or tiny computer in JavaScript. Node.js-based computers are supported. Require f3js
package and use its API to design the device enclosure.
//upmを編集したため、ダウンロードするだけでは動作しません
//#ビルド方法
//arduinoの端末上で
//$ cd /usr/lib/node/jsupm_i2clcd
//$ mv jsupm_i2clcd.node jsupm_i2clcd.node.old
//$ wget --no-check-certificate 'https://drive.google.com/uc?export=download&id=0B4Hp2uhJD5H7dHBBMWdGcmktSzQ' -O jsupm_i2clcd.node
//#元に戻す場合
//$ cd /usr/lib/node/jsupm_i2clcd
//$ rm -f jsupm_i2clcd.node
//$ mv jsupm_i2clcd.node.old jsupm_i2clcd.node
var upmBuzzer = require("jsupm_buzzer");
var groveSensor = require('jsupm_grove');
var LCD = require("jsupm_i2clcd");
var joystick = require('jsupm_joystick12');
var tm1637 = require('jsupm_tm1637');
var myBuzzer = new upmBuzzer.Buzzer(6);
var display = new tm1637.TM1637(0,1);
var sw = new groveSensor.GroveButton(4);
var lcd = new LCD.Jhd1313m1 (6, 0x3E, 0x62);
var js =
[new joystick.Joystick12(0,1),
new joystick.Joystick12(2,3)];
var button =
[new groveSensor.GroveButton(2),
new groveSensor.GroveButton(3)];
var f3js = require('f3js')
, c = f3js.createContainer()
, x = 10
, y = 10
, width = 160
, height = 105
, thickness = 42
, jw = 7 /* かみ合わせ幅 [5, 10] */
, jh = 2 /* 板厚[mm] [1, 5] */;
c.x = x;
c.y = y;
var f3js_contr = require('f3js')
, c_contr1 = f3js_contr.createContainer()
, x_contr1 = 340
, y_contr1 = 10
, c_contr2 = f3js_contr.createContainer()
, x_contr2 = 395
, y_contr2 = 10
, width_contr = 140
, height_contr = 50
, thickness_contr = 23
, jw_contr = 7
, jh_contr = 2;
c_contr1.x = x_contr1;
c_contr1.y = y_contr1;
c_contr2.x = x_contr2;
c_contr2.y = y_contr2;
// 本体側
// 表の面を作る
var p = c.jrc(0, 0, width, height, jw, jh);
// 表面にいろいろ配置する
c.add(lcd, { x: 80 + 0, y: 30 + 0 });
c.add(sw, { x: 140, y: 40 });
c.add(myBuzzer, { x: 40, y: 70 });
// 配線用の穴を開ける
c.drawRectangle(136, 33, 8, 2);
c.drawRectangle(137, 38, 12, 10);
c.drawRectangle(135, 89, 10, 10);
c.drawRectangle(15, 89, 10, 10);
c.drawRectangle(88.5, 61.5, 32, 17);
c.drawCircle(113, 60, 2);
c.drawCircle(113, 80, 2);
c.drawCircle(83, 70, 2);
c.drawCircle(70, 155.5, 1.5);
c.drawCircle(70, 183, 1.5);
// 側面と裏面を作る
var ps = p.extrude(45);
ps[0].x = width + 5;
ps[0].y = jh;
ps[1].x = width + 5;
ps[1].y = (thickness + 5) * 2 + jh;
ps[2].x = width + 5;
ps[2].y = thickness + 5 + jh;
ps[3].x = width + 5;
ps[3].y = (thickness + 5) * 3 + jh;
ps[4].x = 0; // 最後の要素が裏面
ps[4].y = height + 5; // 表の面の真下に配置する
for (var i = 0; i < ps.length; i ++) {
c.add(ps[i]);
}
// 側面の一つに電源用の穴を開ける
ps[1].drawRectangle(jh + 5, thickness - jh - 5, height - jh*2 - 10, -13);
// コントローラ側
// 表面
var p_contr1 = c_contr1.jrc(0, 0, height_contr, width_contr, jw_contr, jh_contr);
var p_contr2 = c_contr2.jrc(0, 0, height_contr, width_contr, jw_contr, jh_contr);
// 側面と裏面
var ps_contr1 = p_contr1.extrude(23);
ps_contr1[0].x = -height_contr - 5;
ps_contr1[0].y = (thickness + 5) * 2 + jh_contr;
ps_contr1[1].x = -width_contr - 5;
ps_contr1[1].y = width_contr * 2 - thickness_contr * 2 + 5;
ps_contr1[2].x = -height_contr - 5;
ps_contr1[2].y = (thickness + 5) * 2 + thickness_contr + jh_contr * 2;
ps_contr1[3].x = -width_contr - 5;
ps_contr1[3].y = width_contr * 2 - thickness_contr + jh_contr + 5;
ps_contr1[4].x = 0;
ps_contr1[4].y = width_contr + 5;
for (i = 0; i < ps_contr1.length; i ++) {
c_contr1.add(ps_contr1[i]);
}
var ps_contr2 = p_contr2.extrude(23);
ps_contr2[0].x = (-height_contr - 5) * 2;
ps_contr2[0].y = (thickness + 5) * 2 + thickness_contr * 2 + jh_contr * 3;
ps_contr2[1].x = -width_contr * 2 - height_contr - 5 * 3;
ps_contr2[1].y = width_contr * 2 - thickness_contr * 2 + 5;
ps_contr2[2].x = (-height_contr - 5) * 2;
ps_contr2[2].y = (thickness + 5) * 2 + thickness_contr * 3 + jh_contr * 4;
ps_contr2[3].x = -width_contr * 2 - height_contr - 5 * 3;
ps_contr2[3].y = width_contr * 2 - thickness_contr + jh_contr + 5;
ps_contr2[4].x = 0;
ps_contr2[4].y = width_contr + 5;
for (i = 0; i < ps_contr2.length; i ++) {
c_contr2.add(ps_contr2[i]);
}
//ボタン類
var b1 = c_contr1.add(button[0], { x: 29, y: width_contr + 40 });
var b2 = c_contr2.add(button[1], { x: 29, y: width_contr + 40 });
b1.rotate(-90);
b2.rotate(-90);
var st1 = c_contr1.add(js[0], { x: 25, y: 110 });
var st2 = c_contr2.add(js[1], { x: 25, y: 110 });
st1.rotate(-90);
st2.rotate(-90);
//ボタン類用の穴
c.drawRectangle(351, 175, 8, 10);
c.drawRectangle(362, 175, 2, 10);
c.drawRectangle(406, 175, 8, 10);
c.drawRectangle(417, 175, 2, 10);
c.drawCircle(355, 255, 12);
c.drawCircle(410, 255, 12);
//配線用の穴
c.drawRectangle(105, 244, 10, 10);
c.drawRectangle(105, 268, 10, 10);
// 以降はゲームのソースコード
var picture1 = new LCD.uint8Array(8);
picture1.setitem(0, 31);
picture1.setitem(1, 27);
picture1.setitem(2, 21);
picture1.setitem(3, 27);
picture1.setitem(4, 21);
picture1.setitem(5, 27);
picture1.setitem(6, 21);
picture1.setitem(7, 31);
var picture2 = new LCD.uint8Array(8);
picture2.setitem(0, 31);
picture2.setitem(1, 27);
picture2.setitem(2, 21);
picture2.setitem(3, 21);
picture2.setitem(4, 21);
picture2.setitem(5, 21);
picture2.setitem(6, 27);
picture2.setitem(7, 31);
var ten = new LCD.uint8Array(8);
ten.setitem(0, 0);
ten.setitem(1, 18);
ten.setitem(2, 21);
ten.setitem(3, 21);
ten.setitem(4, 21);
ten.setitem(5, 21);
ten.setitem(6, 18);
ten.setitem(7, 0);
var downArrow = new LCD.uint8Array(8);
downArrow.setitem(0, 4);
downArrow.setitem(1, 4);
downArrow.setitem(2, 4);
downArrow.setitem(3, 4);
downArrow.setitem(4, 4);
downArrow.setitem(5, 21);
downArrow.setitem(6, 14);
downArrow.setitem(7, 4);
var upArrow = new LCD.uint8Array(8);
upArrow.setitem(0, 4);
upArrow.setitem(1, 14);
upArrow.setitem(2, 21);
upArrow.setitem(3, 4);
upArrow.setitem(4, 4);
upArrow.setitem(5, 4);
upArrow.setitem(6, 4);
upArrow.setitem(7, 4);
lcd.createChar(0,picture1);
lcd.createChar(1,picture2);
lcd.createChar(2,ten);
lcd.createChar(3,downArrow);
lcd.createChar(4,upArrow);
var cards = [];
var back = ["[0]", "[1]"];
var flip = "*";
var isAnimating = false;
var flipCount = 0;
var pair = [0, 0];
var height = 15;
var neut = [{x:0,y:0}, {x:0,y:0}];
var page = 1;
var scene = "title";
var changingScene = false;
var maxTryLimit = 10;
var tryLimit = maxTryLimit;
var mode = 0;
var turn = 0;
var input = [{x:0, y:0, button:false}, {x:0, y:0, button:false}];
var waitingButton = false;
var cPos = {x:0,y:0}
var level = 1;
var score = [0, 0];
var lastPage = 1;
var gameLength = "full";
init();
initTitle();
setInterval(function()
{
getInput();
if (sw.value() == 0 && scene != "off") {
lcd.displayOff();
lcd.backlightOff();
lcd.setColor(0,0,0);
scene = "off";
}
switch (scene) {
case "title":
if (changingScene) {
changingScene = !changingScene;
initTitle();
}
startScreen();
break;
case "levelSelect":
if (changingScene) {
changingScene = !changingScene;
level = 1;
lcd.clear();
lcd.setCursor(0,0);
lcd.write("Select Level");
lcd.setCursor(1,3);
lcd.write("NORMAL");
lcd.setCursor(0,15);
lcd.write("[4]");
lcd.setCursor(1,15);
lcd.write("[3]");
}
if (input[0].x < 0) {
if (level < 2) {
level++;
}
} else if (input[0].x > 0) {
if (level > 0) {
level--;
}
}
var c = [" EASY", "NORMAL", " HARD"];
lcd.setCursor(1,3);
lcd.write(c[level]);
if (input[0].button) {
changeScene("lengthSelect");
}
break;
case "lengthSelect":
if (changingScene) {
changingScene = !changingScene;
gameLength = "full";
lcd.clear();
lcd.setCursor(0,0);
lcd.write("Select Mode");
lcd.setCursor(1,3);
lcd.write(" FULL GAME");
lcd.setCursor(0,15);
lcd.write("[4]");
lcd.setCursor(1,15);
lcd.write("[3]");
}
if (input[0].x < 0) {
if (gameLength == "half") {
gameLength = "full";
lcd.setCursor(1,3);
lcd.write(" FULL");
}
} else if (input[0].x > 0) {
if (gameLength == "full") {
gameLength = "half";
lcd.setCursor(1,3);
lcd.write(" HALF");
}
}
if (input[0].button) {
if (mode == 0) {
changeScene("singlePlay");
} else {
changeScene("multiPlay");
}
}
break;
case "singlePlay":
gamePlay();
break;
case "multiPlay":
gamePlay();
break;
case "victory":
if (changingScene) {
changingScene = !changingScene;
lcd.clear();
}
lcd.setCursor(0,0);
lcd.write("Success!!!");
if (input[0].button) {
changeScene("title");
}
break;
case "defeat":
if (changingScene) {
changingScene = !changingScene;
lcd.clear();
melody_mro();
}
lcd.setCursor(0,0);
lcd.write("Failure");
lcd.setCursor(1,0);
lcd.write("Score: " + score[0]);
if (input[0].button) {
changeScene("title");
}
break;
case "off":
if (sw.value() > 0) {
lcd.displayOn();
lcd.backlightOn();
init();
scene = "title";
changingScene = true;
}
break;
case "result":
var s1 = score[0];
var s2 = score[1];
lcd.clear();
lcd.setCursor(0,0);
if (s1 > s2) {
lcd.write("Winner: 1P");
} else if (s2 > s1) {
lcd.write("Winner: 2P");
} else {
lcd.write("Draw Game");
}
if (input[0].button) {
changeScene("title");
}
break;
case "interval":
break;
}
}, 100);
function changeScene(s)
{
changingScene = true;
scene = "interval";
setTimeout(function() {
scene = s;
}, 500);
}
function gamePlay()
{
if (changingScene) {
changingScene = !changingScene;
turn = 0;
initGamePlay();
}
if (waitingButton) {
if (input[0].button || input[0].x != 0 || input[0].y != 0 || input[1].button || input[1].x != 0 || input[1].y != 0) {
waitingButton = !waitingButton;
lcd.setColor(100,255,100);
faceDown(pair[0], pair[1]);
}
} else {
mainLoop();
}
showScore();
showPageInfo();
showCards();
showCursor();
endGamePlay();
}
function endGamePlay()
{
if (mode == 0) {
if (score[0] >= cards.length) {
melody_ff();
changeScene("victory");
}
if (tryLimit <= 0) {
changeScene("defeat");
}
} else {
if (score[0] + score[1] >= cards.length) {
melody_ff();
changeScene("result");
}
}
}
function startScreen()
{
if (input[0].y == -1) {
writePair("[", "]", 2);
writePair(" ", " ", 6);
mode = 0;
} else if (input[0].y == 1) {
writePair(" ", " ", 2);
writePair("[", "]", 6);
mode = 1;
}
if (input[0].button) {
if (mode == 0) {
changeScene("levelSelect");
} else {
changeScene("lengthSelect");
}
}
}
function writePair(c1, c2, pos)
{
lcd.setCursor(1,pos);
lcd.write(c1);
lcd.setCursor(1,pos+3);
lcd.write(c2);
}
function init()
{
neut[0].x = js[0].getXInput();
neut[0].y = js[0].getYInput();
neut[1].x = js[1].getXInput();
neut[1].y = js[1].getYInput();
myBuzzer.setVolume(0.05);
lcd.setColor(100,255,100);
}
function initTitle()
{
lcd.clear();
lcd.setColor(100,255,100);
mode = 0;
tryLimit = maxTryLimit;
lcd.setCursor(0,2);
lcd.write("[0]");
lcd.write("Select Mode");
lcd.write("[0]");
lcd.setCursor(1,2);
lcd.write("[1P]")
lcd.setCursor(1,7);
lcd.write("2P");
}
function initGamePlay()
{
lcd.clear();
var type = ["A","2","3","4","5","6","7","8","9","[2]","J","Q","K"];
var num;
if (gameLength == "half") {
num = 2;
} else {
num = 4;
}
var deck = new Array(num*13);
for (var i = 0; i < 13; i++) {
for (var j = 0; j < num; j++) {
deck[i*num+j] = type[i];
}
}
deck.sort(function() {
return Math.random() - 0.5;
});
for (var i = 0; i < deck.length; i++) {
cards[i] = {name:deck[i], valid:true, state:"back", pic:back[i%2]};
}
flipCount == 0;
turn = 0;
var tls = [40, 30, 20];
tryLimit = tls[level];
if (gameLength == "half") {
tryLimit /= 2;
}
score[0] = 0; score[1] = 0;
page = 1;
lastPage = 1;
cPos.x = 0; cPos.y = 0;
}
function mainLoop()
{
moveCursor();
var index = (cPos.x+(page-1)*2)*height + cPos.y;
if (index >= cards.length || cPos.y >= height) {
return;
}
if (input[turn].button && flipCount < 2 && cards[index].state == "back") {
if (flipCount == 0 && isAnimating) {
return;
}
faceUp(index);
return;
}
if (flipCount >= 2) {
checkPair();
return;
}
}
function getInput()
{
for (i = 0; i < 2; i++) {
var y = js[i].getXInput() - neut[i].x;
var x = -js[i].getYInput() + neut[i].y;
if (x > 0.1) {
input[i].x = 1;
} else if (x < -0.1) {
input[i].x = -1;
} else {
input[i].x = 0;
}
if (y > 0.05) {
input[i].y = 1;
} else if (y < -0.05) {
input[i].y = -1;
} else {
input[i].y = 0;
}
if (button[i].value() > 0) {
input[i].button = true;
} else {
input[i].button = false;
}
}
}
function moveCursor()
{
if (input[turn].x == 1) {
if (cPos.x == 0) {
cPos.x++;
playMove();
} else if (page == 1) {
cPos.x--;
page++;
playMove();
}
} else if (input[turn].x == -1) {
if (cPos.x == 1) {
cPos.x--;
playMove();
} else if (page == 2) {
cPos.x++;
page--;
playMove();
}
}
if (input[turn].y == 1 && cPos.y < height) {
cPos.y++;
playMove();
} else if (input[turn].y == -1 && cPos.y > 0) {
cPos.y--;
playMove();
}
}
function faceUp(i)
{
cards[i].state = "front";
pair[flipCount] = i;
flipCount++;
}
function checkPair()
{
var p0 = pair[0];
var p1 = pair[1];
flipCount = 0;
var success = cards[pair[0]].name == cards[pair[1]].name;
if (!success) {
lcd.setColor(255,100,100);
incorrect();
tryLimit--;
} else {
correct();
}
if (success) {
pickCard(p0);
pickCard(p1);
} else {
waitingButton = true;
}
}
function faceDown(i1, i2)
{
cards[i1].state = "back";
cards[i2].state = "back";
if (mode == 1) {
turn ^= 1;
}
}
function pickCard(i)
{
cards[i].state = "invalid";
score[turn]++;
}
function showScore()
{
if (mode == 0) {
display.writeString(("000"+tryLimit).slice(-4));
display.setColon(false);
} else {
display.writeString(("0"+score[0]).slice(-2)+("0"+score[1]).slice(-2));
display.setColon(true);
}
}
function showCursor()
{
lcd.cursorBlinkOn();
lcd.setCursor(cPos.x, cPos.y);
}
function showPageInfo()
{
if (page == 1) {
lcd.setCursor(1,15);
lcd.write("[3]");
lcd.setCursor(0,15);
lcd.write(" ");
} else {
if (lastPage != page) {
lcd.clear();
}
lcd.setCursor(0,15);
lcd.write("[4]");
lcd.setCursor(1,15);
lcd.write(" ");
}
lastPage = page;
}
function showCards()
{
cards.forEach(function(card, num) {
var y = num%height;
var x = (num-y)/height;
if (page == 1) {
if (x < 2) {
lcd.setCursor(x, y);
switch (card.state) {
case "front":
lcd.write(card.name);
break;
case "flip":
lcd.write(flip);
break;
case "back":
lcd.write(card.pic);
break;
case "invalid":
lcd.write(" ");
break;
}
}
} else {
if (x >= 2) {
lcd.setCursor(x-2, y);
switch (card.state) {
case "front":
lcd.write(card.name);
break;
case "flip":
lcd.write(flip);
break;
case "back":
lcd.write(card.pic);
break;
case "invalid":
lcd.write(" ");
break;
}
}
}
});
}
var defVolume = 0.05;
function stopSound()
{
myBuzzer.stopSound();
}
function playMove()
{
myBuzzer.setVolume(defVolume*2);
myBuzzer.playSound(500,10000);
stopSound();
}
function correct(){
myBuzzer.setVolume(defVolume);
myBuzzer.playSound(1900, 100000);
myBuzzer.playSound(upmBuzzer.SOL, 200000);
stopSound();
}
function incorrect(){
myBuzzer.setVolume(defVolume);
myBuzzer.playSound(10000, 100000);
myBuzzer.playSound(10000, 200000);
stopSound();
}
//宣言文でconstを用いるとプレビューが表示されなくなるようです
function melody_ff()
{
myBuzzer.setVolume(defVolume);
var F = upmBuzzer.FA;
var G = upmBuzzer.SOL;
var A = upmBuzzer.LA;
var FOUR = 300000;
var THREE = 100000;
var chords = [A,A,A,A,F,G,A,A,G,A];
var length = [THREE,THREE,THREE,FOUR,FOUR,FOUR,THREE,THREE,THREE,FOUR*3];
var chordIndex = 0;
if (chords.length != 0)
{
for(chordIndex = 0; chordIndex < chords.length; chordIndex++){
//Play sound for one second
myBuzzer.playSound(chords[chordIndex], length[chordIndex]);
myBuzzer.stopSound();
}
//Reset the sound to start from the beginning.
if (chordIndex > chords.length - 1)
chordIndex = 0;
}
stopSound();
}
function melody_mro(){
myBuzzer.setVolume(defVolume);
var G = upmBuzzer.SOL;
var HIF = (upmBuzzer.FA / 2);
var HIE = (upmBuzzer.MI / 2);
var HID = (upmBuzzer.RE / 2);
var HIC = (upmBuzzer.DO / 2);
var B = upmBuzzer.SI;
var E = upmBuzzer.MI;
var C = upmBuzzer.DO;
var EIGHT = 150000;
var THREE = 200000;
var chords = [B,HIF,HIF,HIF,HIE,HID,HIC,E,E,C];
var length = [EIGHT,EIGHT*2,EIGHT,THREE,THREE,THREE,EIGHT,EIGHT*2,EIGHT,EIGHT*2];
var chordIndex = 0;
if (chords.length != 0)
{
for(chordIndex = 0; chordIndex < chords.length; chordIndex++){
//Play sound for one second
myBuzzer.playSound(chords[chordIndex], length[chordIndex]);
myBuzzer.stopSound();
}
//Reset the sound to start from the beginning.
if (chordIndex > chords.length - 1)
chordIndex = 0;
}
stopSound();
}
var var32783 = false; // *proposed* ブザーのボリューム