Basic Information

This page allows you to edit the existing project (Card Matching Game). The edited project will replace the existing entry in the IoT Projects page. If you want to add a new entry based on the project, please fork from it instead.

* Photo URLs that end with any one of .jpg, .jpeg, .png or that are uploaded to the Gyazo web service in the PNG format (e.g.https://gyazo.com/hogehoge) are supported.

* Node.js entry point main: index.js and dependencies to the npm module dependencies: { f3js: (略) } will be automatically added and thus do not need to be in this form.

Card preview
Card Matching Game
'16/9/5 1:42
A game of card matching
arc@dmz

Private Project

This project is already public and cannot be made private.

Source Code

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(); }
Customization

This content does not allow any customization.

Layout view options
Adding comments to variable declarations in the source code populate various GUI widgets in the "Customization" section depending on their types.

Design Alternatives

Modules

No modules are required for this project.

Layout
Program

Login first to use this feature.