Skip to content

Instantly share code, notes, and snippets.

@Sammons
Last active October 13, 2022 01:20
Show Gist options
  • Save Sammons/1b932ff7f997f6bb8f0c to your computer and use it in GitHub Desktop.
Save Sammons/1b932ff7f997f6bb8f0c to your computer and use it in GitHub Desktop.
set card game
"use strict"
var properties = {
'color' : [ 'red', 'green', 'purple' ],
'count' : [ 1, 2, 3 ],
'shading' : [ 'solid', 'striped', 'open' ],
'symbol' : [ 'diamond', 'squiggle', 'oval']
};
function Card(characteristics) {
var self = this;
for (var k in characteristics)
self[k] = characteristics[k];
}
function SetGame() {
var self = this;
self.cards = [];
self.tableCards = [];
self.setsChosen = [];
/* permute the properties to get 81 cards */
for (var color in properties.color)
for (var count in properties.count)
for (var shading in properties.shading)
for (var symbol in properties.symbol)
self.cards.push(
new Card({
'number' : self.cards.length,
'color' : properties['color'][color],
'count' : properties['count'][count],
'shading': properties['shading'][shading],
'symbol' : properties['symbol'][symbol] }));
/* method to check that three cards are a "set" */
function cardsAreCompatible(c1, c2, c3) {
/* extract arrays of values for comparison */
var valueExtraction = Object.keys(c1).map(function(k) { return [c1[k], c2[k], c3[k]] });
/* map each array to a boolean, then reduce to find out if everything matched*/
return valueExtraction.map(function(values) {
return values[0] == values[1] && values[1] == values[2] ||
(values[0] != values[1] && values[0] != values[2] && values[1] != values[2]);
}).reduce(function(b1, b2) {
return b1 && b2;
});
};
/* map of unique possible sets */
self._possibleSets = {};
self._invalidSets = {};
/* brute force check every card's compatibility ahead of time, there are 1080 unique possibilities*/
self.cards.forEach(function(c1) {
self.cards.forEach(function(c2) {
if (c1 !== c2) self.cards.forEach(function(c3) {
if (c2 !== c3) {
/* sort to cause key collisions between duplicates */
var cards = [c1.number, c2.number, c3.number].sort()
if (self._possibleSets[cards] || self._invalidSets[cards]) return;
if (cardsAreCompatible(c1,c2,c3)) {
self._possibleSets[cards] = true;
} else {
self._invalidSets[cards] = true;
}
}
})
})
});
/* swap each card with a random location */
self.shuffle = function() {
for (var i = 0; i < self.cards.length; ++i) {
var newLocation = Math.floor(Math.random() * self.cards.length);
var tmp = self.cards[newLocation];
self.cards[newLocation] = self.cards[i];
self.cards[i] = tmp;
}
};
self.findSetsOnTable = function() {
var sets = [];
/* check every combination of the cards on the table against the known sets */
self.tableCards.forEach(function(c1) {
self.tableCards.forEach(function(c2) {
if (c1 !== c2) self.tableCards.forEach(function(c3) {
if (c2 !== c3) {
var cards = [c1.number,c2.number,c3.number].sort();
if(self._possibleSets[cards]) {
sets.push([c1,c2,c3]);
}
}
});
});
});
return sets;
};
/* removes the cards from the table, puts them in the setsChosen */
self.claimSet = function(cards) {
self.setsChosen.push(cards);
var tableCardNumbers = self.tableCards.map(function(c) { return c.number; });
cards.forEach(function(card) {
self.tableCards.splice(tableCardNumbers.indexOf(card.number),1);
});
};
self._noSetsExistOnTable = function() {
return self.findSetsOnTable().length == 0;
};
/* get the next three cards from the deck */
self._nextThree = function() {
var nextThreeCards = self.cards.slice(-3);
self.cards = self.cards.slice(0, -3);
return nextThreeCards;
};
/* deals cards until there is a set on the table or the game ends */
self.dealCards = function() {
while (self._noSetsExistOnTable() || self.tableCards.length < 12) {
if (self.cards.length === 0) return;
self.tableCards = self.tableCards.concat(self._nextThree());
}
};
self.gameOver = function() {
return self._noSetsExistOnTable() && self.cards.length === 0;
}
self.reset = function() {
self.setsChosen.forEach(function(s) {
s.forEach(function(card){
self.cards.push(card);
});
})
self.cards = self.cards.concat(self.tableCards);
self.setsChosen = [];
self.tableCards = [];
}
self.simulateToCompletionAndGetSetsChosen = function() {
var game = self;
var tmpDate = Date.now();
console.log('Game initialized in', (Date.now() - tmpDate)/1000, 'seconds' );
tmpDate = Date.now();
game.shuffle();
console.log('deck shuffled');
game.dealCards();
console.log('initial cards dealt');
while (!game.gameOver()) {
var currentSet = game.findSetsOnTable().pop();
console.log("SET!", currentSet);
game.claimSet(currentSet);
game.dealCards();
console.log('after dealing there are', game.tableCards.length, 'cards on the table and', game.cards.length, 'in the deck');
}
console.log('no more sets found!', game.tableCards.length, 'cards were not matched');
console.log('Game completed in', (Date.now() - tmpDate) / 1000, 'seconds.', game.setsChosen.length, 'sets found' );
return game.setsChosen;
}
}
module.exports = SetGame;
if (module.parent === null) {
/* actually running the code if it isn't being included*/
var game = new SetGame();
console.log(game.simulateToCompletionAndGetSetsChosen());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment