# Set using Bonsai

2013-05-25

Created this a while back. Not sure if there was a purpose or maybe I was just bored. Either way... This is a simple implementation of the game Set. It's not complete (it will not check if there's actually a combination on the current table, so it will never warn you for it or add more cards to the table) but I'll leave that as an exercise for the reader ;) I've coded it a while back and it's just been sitting here so I just wanted to put this in a blog and move on :)

Go paste this at the Orbit editor. (gist).

Code:
`var deck = [];var selections = [];var visibleCards = 0; function start(){ var newDeck = createDeck(); deck = []; shuffle(newDeck); drawTable();}function createDeck(){ var deck = []; for (var i=0, len=3*3*3*3; i<len; ++i) { deck.push(toCard(i)); } return deck;}function shuffle(newDeck){ while (newDeck.length) deck.push(newDeck.splice(Math.floor(newDeck.length*Math.random()), 1)[0]);} function toCard(i){ return { color:Math.floor(i%3), shape:Math.floor(i/3)%3, fill:Math.floor(i/9)%3, count:Math.floor((i/27)%3)+1 };} function drawCard(card,x,y){ card.x = x; card.y = y; card.group = new Group().attr({x:x,y:y,cursor:'pointer'}).addTo(stage).on('pointerup', onCardClick(card)); card.bs = new Rect(0,0,50,96,5).stroke('black', 1).addTo(card.group); if (card.count === 1) { createCardShape(card, 17, 40); } else if (card.count === 2) { createCardShape(card, 17, 27); createCardShape(card, 17, 57); } else { createCardShape(card, 17, 12); createCardShape(card, 17, 40); createCardShape(card, 17, 68); } ++visibleCards; return card;}function createCardShape(card, x,y){ var bs = null; if (card.shape === 0) bs = new Circle(x+8, y+8, 8); else if (card.shape === 1) bs = new Rect(x,y,16,16); else bs = new Path().attr({x:x,y:y}).moveTo(8,0).lineTo(16,16).lineTo(0,16).lineTo(8,0); var color = 'orange'; if (card.color === 1) color = 'blue'; else if (card.color === 2) color = 'green'; var shape = card.shape; var fill = card.fill; bs.attr({fillColor:fill?color:'', strokeColor:color, strokeWidth:2, opacity:fill===1?.3:1}).addTo(card.group); return bs;} function onCardClick(card){ return function(){ if (selections.indexOf(card) >= 0) { // deselect card card.bs.attr({fillColor: ''}); selections.splice(selections.indexOf(card), 1); if (selections.length) selections[0].bs.attr({fillColor:'yellow'}); if (selections.length>1) selections[1].bs.attr({fillColor:'yellow'}); } else if (selections.length < 3) { selections.push(card); card.bs.attr({fillColor: 'yellow'}); if (selections.length === 3) { // check if selection conditions hold if (isSet(selections[0],selections[1],selections[2])) { // replace cards selections.forEach(function(s){ s.group.destroy(); }) visibleCards -= 3; if (!deck.length && !visibleCards) finished(); if (visibleCards < 12) { for (var i=0; i<3 && deck.length; ++i) { drawCard(deck.pop(), selections[i ].x, selections[i ].y); } } selections = []; } else { for (var i=0; i<3; ++i) { selections[i ].bs.attr({fillColor:'red'}); } } } } };}function sameOrUnique(a,b,c){ if (a === b && b === c) return true; if (a !== b && b !== c && a !== c) return true; return false;}function isSet(a,b,c){ return ( sameOrUnique(a.color, b.color, c.color) && sameOrUnique(a.shape, b.shape, c.shape) && sameOrUnique(a.fill, b.fill, c.fill) && sameOrUnique(a.count, b.count, c.count) );} function drawTable(){ for (var i=0; i<12; ++i) { if (deck.length) drawCard(deck.pop(), 10+(i%3*60), 10+(Math.floor(i/3)*110)); }} function finished(){ setInterval(function(){ var bs = drawCard(toCard(Math.floor(Math.random()*81)), 300, 100).group; bs .animate('2s', {opacity:0}) .animate('2s', {x:Math.random()*600}, {easing:'bounceIn'}) .animate('2s', {y:500}, {easing:'bounceOut', onEnd:function(){ bs.destroy(); bs=null; }}); }, 500);} start();`