"SET", not to be confused with the data-structure is a card game in which player try to find a combination of 3 cards.
Each cards have 4 attributes: color, number, shape, and filling-pattern. A legal set must either have to contain all variant of a attribute (all color) or just one variant(say red). Therefore, any set that have exactly 2 variant of attribute is therefore always be a illegal set.
a more detailed rule can be find here
here's my implementation of the game. It's my first js program so it is not the most pretty thing.
constants
To start things off I create a set that would contain all the answers in int. They are all precomputed using python. My though process is this: a collection of cards is a cluster of information (sequence doesn't matter). Therefore I give each card a corresponding prime number and calculate all the legal set. For example card #1, #2, and #3 is a set. and their corresponding prime is 2, 3, and 5. Thus you will find 30 in the answer set. By doing so i can be confident that each set would have a distinct value. and thanks to math 2*3*5 = 5*3*2 saving memory space. We need first 81 primes to verify user input so I also include that(ln2)
functions
shuffle takes an array and randomize the order of it. (I find this on the internet)
drawBoard takes no argument and draw the board using the first 12 value of the deck poping them and create appropriate objects.
reload takes no argument and initialize the deck
fetchNew a helper function that takes id as argument and update new information to that object accordance to the deck. Create a blank card + disable the object when no more card in the deck.
checkSet a helper function that takes no argument and checks if the selecitons of 3 cards by the user(selected) contains a legal-set.
updateState the heart of the program. Actively update the board when input were made. call checkset when needed.
How can I improve my code? Maybe organize everything into a class so there's less global values(deck and selections)? Or perhaps using a framework instead of just pure-JS? I am newbie to js and I don't know what the best practice is! Thank you for reading and answering.
let all_answer = new Set([192515, 2529283, 6930437, 4827143, 407563, 1630219, 7518223, 1200149, 1660949, 2703383, 7243799, 5337109, 512029, 30, 856097, 38947, 5742629, 30597157, 2021417, 3534889, 829483, 2969641, 6428717, 59438, 13041709, 2955313, 8466481, 1079347, 1779763, 10295, 2218039, 1916983, 4667447, 5228603, 5648441, 39110717, 55361, 3127361, 37140553, 6649931, 30900299, 4700237, 4038733, 2658389, 2713687, 32857, 9363547, 1943647, 299105, 1476707, 2449511, 475241, 4698217, 37234793, 39489641, 36016237, 520303, 819313, 1138801, 53363, 2678899, 3553393, 4317301, 6410359, 14457, 551033, 3305593, 38635643, 182397, 3532927, 38338687, 1034371, 7934083, 8626309, 4874371, 2123911, 5308549, 4094089, 311435, 3750029, 9955469, 10385, 22673, 497809, 3672211, 19710101, 36368531, 602263, 3870871, 227481, 733337, 866459, 7422109, 1925281, 26789, 2646181, 5187751, 12112039, 612527, 5195951, 36161711, 39091, 2054323, 3532979, 8374, 6656179, 4733107, 5183669, 2369723, 2138299, 51389, 577727, 3889343, 2474177, 2265283, 8636611, 774341, 1925317, 6738119, 4833481, 71882, 250061, 581837, 6351, 2044109, 2740429, 5587151, 3590359, 59119831, 1435867, 40374491, 36495581, 768223, 20705, 3395809, 573667, 116967, 59590891, 238, 243951, 123119, 55537, 3002609, 11438321, 399605, 4141303, 534779, 3240187, 934141, 5382397, 7461119, 5566721, 3217667, 43456771, 4212997, 10502, 5243141, 34377991, 2500877, 2998543, 6633743, 1153297, 3733777, 1022227, 1476883, 2363671, 24857, 190747, 34355483, 1878301, 3602717, 3424543, 36288797, 104737, 76067, 5777701, 36802853, 7979303, 3488041, 985387, 1542449, 3385649, 3283253, 1100087, 379195, 4700477, 514367, 1610047, 2132287, 2935103, 829763, 4034879, 4260163, 1780039, 36475207, 3664201, 5427529, 86349, 139598, 178509, 909649, 231763, 1515859, 3264853, 4127059, 39259, 36639067, 1958239, 2416991, 14691, 84323, 1040741, 5357923, 1470823, 4041061, 891241, 4473187, 3082603, 874861, 2435441, 3043699, 217461, 20914549, 20855, 6304121, 440699, 2556283, 12669, 3058043, 34111867, 5572993, 10068353, 12022147, 2470277, 49543, 4479367, 2736521, 35926409, 1483147, 4094353, 2111479, 36579737, 80282, 6703517, 63043997, 3641761, 4112803, 3897763, 2916773, 5652901, 450983, 328105, 4526, 6920623, 5548463, 151985, 43821487, 3897779, 1051063, 59831, 117177, 418235, 2054587, 3656123, 7393723, 5261759, 1833409, 4424129, 9095621, 36145609, 6603, 3633611, 115149, 4303307, 926159, 7973333, 3723737, 35135963, 4639199, 36250079, 848353, 483, 1591781, 2638309, 4108777, 7205353, 36628973, 494, 358895, 33263, 219633, 150002, 1104371, 2609647, 520693, 1894897, 6541813, 3973621, 506, 1184251, 6345211, 1204733, 9296383, 37323263, 2603521, 3770881, 752131, 1616387, 7160323, 14457349, 30857731, 27145, 158218, 3273227, 4264457, 2091533, 7645711, 1374739, 3279379, 1581589, 36256277, 4166167, 5810711, 10832407, 4987417, 4893211, 1376797, 57889, 1262113, 80417, 9957923, 4526629, 115238, 5536291, 35269153, 131629, 858671, 11174447, 80434, 39479, 3273271, 2742841, 4686397, 4817473, 113221, 164422, 1659461, 7479877, 5356103, 11749957, 33796681, 1245773, 612943, 1710673, 3953239, 148058, 8430173, 2259553, 30487139, 1888871, 457327, 627, 4950643, 2646647, 6984311, 36403831, 36399739, 1872511, 1796737, 2474627, 3467911, 3818119, 6599303, 1927819, 133774, 217743, 1942159, 41857681, 164498, 5747347, 36289171, 5296789, 663, 58007, 665, 8858, 795289, 1499803, 723613, 2056861, 8323739, 3343001, 123554, 7215781, 5583527, 27307, 4942507, 2495153, 35674801, 1796791, 1542839, 762553, 3859129, 6777539, 1606343, 461513, 2597587, 3467987, 5378771, 66263, 8721113, 4715231, 5280479, 2689763, 4190947, 13029, 2851559, 3922663, 9954023, 37313257, 4807403, 500461, 158446, 2806511, 1239793, 2634481, 3193591, 2951929, 3584761, 244479, 6753023, 234241, 59380481, 1417987, 4045571, 4000517, 2347783, 3816199, 23305, 36813577, 4442891, 486157, 549647, 936721, 5511953, 3795731, 3142423, 7858967, 1215259, 3572509, 140065, 37903141, 371495, 2773801, 1948459, 2315051, 1415983, 21855023, 1577777, 21359407, 6736693, 78647, 1792823, 2423609, 2911039, 189249, 582467, 1147721, 5450569, 408397, 242511, 1260367, 3960823, 3361619, 2581333, 1958743, 37905239, 1268573, 3668831, 2337631, 1921891, 2528101, 3822443, 4066159, 2358131, 7396217, 361339, 957307, 426877, 1624957, 478079, 4160383, 2708353, 146306, 123778, 2982787, 2308997, 5630851, 2546567, 103309, 1958797, 4711309, 3007379, 2937749, 3091349, 811927, 36268951, 3261343, 4811683, 5579683, 3464101, 935, 1520551, 478121, 123819, 82861, 8627117, 6929327, 46179247, 33708979, 3214261, 2954167, 3435449, 369595, 2966461, 4115389, 3591103, 7162817, 1649603, 463813, 7015367, 2546633, 5587913, 1506251, 21087179, 3865549, 3703757, 4834253, 431057, 3859409, 2950097, 1700821, 12205013, 8125399, 2116567, 1940441, 2821081, 2069467, 2399197, 34710493, 248799, 3240929, 7805923, 9118693, 3748837, 1001, 52207, 3877873, 1698803, 8315893, 4314103, 30999547, 42302461, 46079, 211967, 1776641, 2184191, 2038787, 5555203, 5635073, 32318467, 848903, 56327, 4264969, 38710279, 2016271, 3173393, 5188627, 3770359, 185365, 599063, 2673689, 4414489, 2016283, 2501659, 3714077, 1317919, 2954279, 2075687, 3716137, 36580393, 2315311, 5366831, 226353, 316465, 3415091, 36256819, 549941, 38233141, 668729, 21345343, 78913, 3167303, 351307, 36756557, 3710033, 87123, 2382931, 1936469, 2522197, 2171993, 3320923, 3963997, 5764189, 3150943, 2593889, 2706533, 54379, 3077231, 969841, 2329721, 2309243, 144509, 347261, 37039231, 44161, 35890307, 5254, 1703047, 3273863, 2081929, 4850827, 44227723, 1524877, 6169741, 4093069, 68719771, 2225309, 5954719, 1234081, 3394723, 201895, 3777703, 1934507, 353455, 1367221, 490679, 994487, 4670647, 5631161, 208059, 212155, 38077, 4549819, 36830393, 13505, 10114241, 5315777, 35742919, 4785353, 40139, 44239, 730319, 556241, 834769, 2168017, 169174, 5170391, 920803, 1066211, 5135593, 19691, 2108651, 1715437, 2460907, 3237103, 4562941, 6132979, 7406837, 595193, 8414459, 2858237, 5186813, 3595519, 2311427, 6384899, 7429, 4738309, 1312007, 2080007, 4105481, 7437, 400655, 654607, 3644689, 58642, 3337487, 4617491, 41553173, 9494, 240919, 35211409, 402715, 5575963, 435485, 4705567, 36181279, 3693857, 5279009, 34352419, 2839847, 7871783, 103721, 3685673, 668971, 3308843, 3697961, 2090293, 2229559, 10796347, 492863, 39605567, 4101443, 791879, 2518343, 4056391, 1862989, 5162317, 3321169, 3810641, 3824981, 167254, 4754777, 6229337, 1498459, 30704987, 36474211, 87397, 2882917, 5279081, 4056427, 13677, 392557, 572783, 6868339, 210295, 1351033, 3872123, 4560251, 736637, 1664381, 2006399, 2256253, 3280253, 3151231, 5561723, 1484167, 5133703, 66953, 4455817, 5354891, 513421, 36439439, 2057617, 3419539, 9794963, 492949, 42179987, 56729, 1979801, 249243, 4019611, 777631, 89503, 140705, 3921311, 77221, 8418727, 3831209, 265643, 1480111, 2626999, 5014967, 2506169, 36416951, 785851, 1646011, 2784703, 1009091, 21093827, 30149, 31204807, 5053901, 1495, 1584599, 5146073, 1852897, 1285601, 6825443, 679397, 2997733, 277991, 3665381, 21995, 3548651, 198129, 6018547, 42845683, 10165751, 353785, 138746, 2539003, 36472313, 3622397, 37283321, 2219519, 3091967, 7466497, 25456133, 398855, 1703431, 669191, 4124171, 2715149, 3657229, 1244689, 2321941, 2506261, 3409429, 351769, 974363, 1510939, 1119773, 1302047, 4271647, 5922337, 4273697, 56867, 2422307, 63017, 1994281, 2444843, 8656429, 37969453, 26159, 4329007, 5748271, 3448373, 3188279, 183867, 3681851, 42559, 59147839, 3565123, 3700297, 75338, 5418571, 5111369, 8080973, 1402447, 116313, 3763801, 5711449, 2725469, 106079, 3636833, 3014243, 35460709, 1818217, 4208233, 46699, 36095593, 30317, 2655857, 401011, 2553461, 796279, 3743351, 38706809, 1277567, 2508421, 525959, 34441, 24205, 257677, 1369741, 3790477, 8232589, 4312717, 532117, 1638037, 2614933, 153242, 21159581, 2397859, 5605027, 411301, 124582, 5428903, 35165863, 3247787, 34897579, 528047, 35573423, 1222321, 153266, 3303089, 8689333, 1257143, 1875641, 5334713, 3436219, 8289979, 5627581, 36312761, 1543873, 3823297, 2727619, 3862211, 2657989, 83654, 186055, 3362503, 2436809, 972491, 2754251, 2127571, 54998, 4028119, 5598937, 29333209, 77531, 51881689, 2293469, 4953821, 4202207, 681697, 2764513, 30387937, 59817697, 2631397, 59857921, 2160359, 4765421, 12199661, 2000627, 2604787, 2942707, 7126771, 48887, 7274231, 5709563, 59369213, 2817791, 847619, 37193477, 1404679, 6733577, 2381579, 1500943, 7954, 48919, 7393049, 83738, 6950681, 36519709, 497441, 3510049, 755491, 810787, 3235619, 59344673, 112423, 5312297, 7143209, 2576173, 3886, 7182127, 2389811, 399157, 2185013, 849719, 46903, 3227449, 2408249, 913211, 4230971, 7262011, 4071233, 530243, 6786883, 14151, 4570957, 22351, 3047249, 5521237, 3809111, 4716377, 18181979, 3610463, 5465953, 2279269, 35540837, 5322599, 5875561, 36351853, 38094701, 554863, 833393, 2246513, 37625713, 2570101, 3647351, 6098809, 970619, 2416507, 1634173, 2404223, 5040001, 391045, 1963909, 2191237, 5264023, 571273, 649097, 122763, 40843, 7530379, 10126, 49039, 1863569, 3590033, 1750933, 3299221, 583573, 44957, 5625757, 2889631, 3600287, 56364961, 2740147, 6965177, 2072507, 3553211, 23205823, 5484481, 243651, 1198019, 292805, 36114371, 1361863, 4702151, 2809801, 587723, 36595661, 225231, 3731407, 4470737, 11644879, 1759187, 4487123, 4827091, 3272663, 36175831, 2932697, 75739, 1720283, 3499997, 5369821, 6094813, 4159457, 4196323, 3565547, 21880811, 86001, 765941, 280567, 1898489, 3291133, 3657727
]);
let primes = [
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419
];
function shuffle(array) {
for (let i = array.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}
function drawBoard() {
mainGrid = document.getElementById("main");
z = 0;
y = 0;
while (z < 3) {
y = 0;
while (y < 4) {
x = deck.pop();
newButton = document.createElement("button");
newButton.setAttribute('id', '(' + z + ',' + y + ')');
newButton.setAttribute('repr', x - 1);
newButton.setAttribute('onclick', 'updateState(this.id);');
newImg = document.createElement("img");
newImg.setAttribute('src', 'https://www.setgame.com/sites/all/modules/setgame_set/assets/images/new/' + x + '.png');
newImg.setAttribute('alt', x);
//newImg.setAttribute('width','22.5%');
newImg.setAttribute('width', '215');
newButton.appendChild(newImg);
mainGrid.appendChild(newButton);
y += 1;
}
lineBreak = document.createElement("br");
mainGrid.appendChild(lineBreak);
z += 1
}
}
function reload() {
deck = Array(81).fill(1).map((_, i) => i + 1);
selected = [];
shuffle(deck);
mainGrid = document.getElementById("main");
while (mainGrid.lastChild) {
mainGrid.removeChild(mainGrid.lastChild);
}
drawBoard();
}
function fetch_new(id) {
let el = document.getElementById(id)
el.removeChild(el.lastChild);
let x = deck.pop()
if (x == undefined) {
el.setAttribute('disabled', 1);
newImg = document.createElement("img");
newImg.setAttribute('src', 'https://www.setgame.com/sites/all/modules/setgame_set/assets/images/new/empty_card.gif');
} else {
el.setAttribute('repr', x - 1);
newImg = document.createElement("img");
newImg.setAttribute('src', 'https://www.setgame.com/sites/all/modules/setgame_set/assets/images/new/' + x + '.png');
}
newImg.setAttribute('alt', x);
newImg.setAttribute('width', '215');
el.appendChild(newImg)
}
function checkSet() {
let el = 0;
let x = 0
let newImg = 0;
let y = 0;
let sum = 1;
while (y < 3) {
el = document.getElementById(selected[y]);
sum *= primes[el.getAttribute("repr")];
el.lastChild.setAttribute('style', 'filter: brightness(100%);');
y += 1;
}
if (all_answer.has(sum)) {
message = "good job! you find a set!";
} else {
selected = [];
message = "very close but, not a set";
return
}
y = 0;
while (y < 3) {
fetch_new(selected.pop());
y += 1;
}
return
}
function updateState(id) {
if (selected.includes(id)) {
selected = selected.filter(function(item) {
return item !== id
})
el = document.getElementById(id);
el.lastChild.setAttribute('style', 'filter: brightness(100%);')
} else {
//let el = document.getElementById(id);
//el.removeChild(el.lastChild);
selected.push(id);
el = document.getElementById(id);
el.lastChild.setAttribute('style', 'filter: brightness(80%);')
}
message = "";
if (selected.length == 3) {
checkSet();
}
span = document.getElementById("history");
txt = document.createTextNode(selected);
if (span.lastChild){
span.removeChild(span.lastChild);
}
span.appendChild(txt);
span = document.getElementById("message");
txt = document.createTextNode(message);
if (span.lastChild){
span.removeChild(span.lastChild);
}
span.appendChild(txt);
}
reload();
<div id="main">
</div>
<br>
you selcted: <span id="history"></span>
<br>
<span id="message"></span>
<br>
<br>
<button onclick=reload()>new game</button>
1 Answer 1
Loved to review this, also really like Set
Your naming convention is not consistent you have lowerCamelCase like
drawBoard
but alsofetch_new
, I would move it all to lowerCamelCase.Using
all_answer
andprimes
is smart, but also it removes all reasoning from the code. I cannot review if you made a mistake in your generation of solutions. If I were to maintain this, that would be the first thing I drop.Indenting is not consistent either
Probably would pass
deck
as a parameter todrawBoard
drawBoard
would be so much more readable ifz
,y
,x
wererow
,col
andcard
repr
handling could better, I still dont know whatrepr
means but also why woulddeck
not immediately contain the right value so you dont need to worry about subtracting one every time you build a DOM element?Ideally you wire javascript to the DOM with javascript, not in HTML
This has globals in unexpected places
deck = Array(81).fill(1).map((_, i) => i + 1);
deck is a global, it probably should not be. If genuinely you wanted this to be a global, then I would have liked it on top of the code, clearly called out as a globalNo need to add
return
as the last statement incheckSet
You sufficiently call
document.getElementById
to warrant a helper functionEven if it is 1 line, I would encapsulate
el.lastChild.setAttribute('style', 'filter: brightness(80%);')
in a function called something likeshowAsSelected
. In general I advocate you look in to https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controllerIn
checkSet
you createel
on top, and then only setting and reading it in thewhile
loop, basically mimicking avar
, I would just do alet
in the loop<div id="main"><div>
can be<div id="main"/>
, same for your other tag pairs without contentIt propably makes more sense to change the label of history/message than to keep adding/deleting text elements