From a16d5b9dd0d93c0a3969f2eda6ecb0fb30b0222e Mon Sep 17 00:00:00 2001 From: Karthik Shanmugam Date: Sat, 10 Aug 2019 12:00:58 +0530 Subject: [PATCH 1/3] add seed argument to shuffle function --- public/header.js | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/public/header.js b/public/header.js index e24afd3..f5f202f 100644 --- a/public/header.js +++ b/public/header.js @@ -44,10 +44,11 @@ function rand(x) { return Math.floor(Math.random() * x); } -function shuffle(lst) { +function shuffle(lst, seed=null) { + var randf = seed == null? rand: seeded_prng(seed); for (var i = 0; i < lst.length; ++i) { var tmp = lst[i] - var j = rand(i); + var j = randf(i); lst[i] = lst[j]; lst[j] = tmp; } @@ -71,3 +72,36 @@ $.attrHooks['class'] = { return value; } }; + + +// blatantly stolen from https://stackoverflow.com/a/47593316 +function xmur3(str) { + for(var i = 0, h = 1779033703 ^ str.length; i < str.length; i++) + h = Math.imul(h ^ str.charCodeAt(i), 3432918353), + h = h << 13 | h >>> 19; + return function() { + h = Math.imul(h ^ h >>> 16, 2246822507); + h = Math.imul(h ^ h >>> 13, 3266489909); + return (h ^= h >>> 16) >>> 0; + } +} + +function sfc32(a, b, c, d) { + return function(x) { + a >>>= 0; b >>>= 0; c >>>= 0; d >>>= 0; + var t = (a + b) | 0; + a = b ^ b >>> 9; + b = c + (c << 3) | 0; + c = (c << 21 | c >>> 11); + d = d + 1 | 0; + t = t + d | 0; + c = c + t | 0; + var temp = (t >>> 0) / 4294967296; + return Math.floor(temp * x); + } +} + +function seeded_prng(seed) { + var seeder = xmur3(seed); + return sfc32(seeder(), seeder(), seeder(), seeder()); +} From 075c9498d1c58cae00284481591d2a483ff6adec Mon Sep 17 00:00:00 2001 From: Karthik Shanmugam Date: Sat, 10 Aug 2019 12:52:05 +0530 Subject: [PATCH 2/3] implement seeds for powerset --- public/controller.coffee | 2 +- public/model.coffee | 12 ++++++++++-- public/powerset.html | 1 + public/variants.js | 4 ++-- public/view.coffee | 4 ++++ 5 files changed, 18 insertions(+), 5 deletions(-) diff --git a/public/controller.coffee b/public/controller.coffee index 5038cfa..71566ce 100644 --- a/public/controller.coffee +++ b/public/controller.coffee @@ -4,7 +4,7 @@ root.Controller = do -> ## Game Controller start = -> if not Model.loadGame() - Model.newGame() + Model.restart() checkSet = -> if Model.checkSet() Model.clearSet() diff --git a/public/model.coffee b/public/model.coffee index 25d62f6..8034305 100644 --- a/public/model.coffee +++ b/public/model.coffee @@ -8,6 +8,7 @@ root.Model = do -> startTime = null endTime = null phase = null + seed = null getClockTime = -> seconds = Math.floor((endTime - startTime) / 1000) @@ -16,7 +17,7 @@ root.Model = do -> deselectAll = -> selected.slice().forEach(deselect) newGame = -> - deck = variant.makeDeck() + deck = variant.makeDeck(seed) cards = variant.deal(deck) selected = [] startTime = Date.now() @@ -28,6 +29,8 @@ root.Model = do -> View.setLabels(phase) restart = -> + seed = (rand(0x1000000)).toString(16) + View.showSeed(seed) if phase == 'gameover' View.gameOverDone() setTimeout(newGame, 1000) @@ -35,11 +38,15 @@ root.Model = do -> newGame() loadGame = -> + seed = initialSeed gameid = variant.name print 'loading', gameid if typeof(Storage) isnt 'undefined' and localStorage.getItem(gameid)? game = JSON.parse(localStorage.getItem(gameid)) if (game? and game.cards? and game.deck? and game.startTime? and game.selected? and game.phase?) + if seed != null and seed != game.seed + return false + seed = game.seed cards = game.cards deck = game.deck startTime = game.startTime @@ -59,6 +66,7 @@ root.Model = do -> View.addCards(cards) View.layoutCards() View.setLabels(phase) + View.showSeed(seed) return true return false @@ -66,7 +74,7 @@ root.Model = do -> gameid = variant.name if typeof(Storage) isnt 'undefined' gameString = JSON.stringify { - cards: cards, deck: deck, startTime: startTime, phase: phase, + cards: cards, deck: deck, startTime: startTime, phase: phase, seed: seed, selected: [], # do we want to preserve selected cards? endTime: endTime, # may be null } diff --git a/public/powerset.html b/public/powerset.html index bb3fdf9..4d9da90 100644 --- a/public/powerset.html +++ b/public/powerset.html @@ -83,6 +83,7 @@ if not View.isAnimating() if cnt in allowedDimensions currentDimensions = cnt + root.initialSeed = window.location.hash.substr(1) || null root.variant = Variants.powersetWithDimension(currentDimensions) root.Controller.start() setDimension(6) diff --git a/public/variants.js b/public/variants.js index ea0a284..98d712e 100644 --- a/public/variants.js +++ b/public/variants.js @@ -191,12 +191,12 @@ function findPowerSet(cards, previous) { function powersetWithDimension(dim) { return { name: 'Power Set ' + dim, - makeDeck: function() { + makeDeck: function(seed) { var deck = []; for (var i = 1; i < (1 << dim); ++i) { deck.push({type: '2^' + dim, value: i}); } - return shuffle(deck); + return shuffle(deck, seed); }, deal: deal, isSet: isPowerSet, diff --git a/public/view.coffee b/public/view.coffee index 003a1ad..be65a7c 100644 --- a/public/view.coffee +++ b/public/view.coffee @@ -250,6 +250,9 @@ root.View = do -> $cards.forEach ($card) -> $card.remove() $cards = [] + showSeed = (seed) -> + window.location.hash = seed + return { select: (i) -> $cards[i].addClass('selected') @@ -259,6 +262,7 @@ root.View = do -> gameOver: gameOver gameOverDone: gameOverDone clear: clear + showSeed: showSeed setTheme: (theme) -> $body.removeClass('light dark').addClass(theme) toggleFullScreen: toggleFullScreen From 64333a5b5e088dd86e7c33f470c5a1fcbdbbb62f Mon Sep 17 00:00:00 2001 From: Karthik Shanmugam Date: Sat, 10 Aug 2019 13:02:59 +0530 Subject: [PATCH 3/3] unbreak other game modes --- public/controller.coffee | 2 +- public/hiddenset.html | 1 + public/index.html | 1 + public/model.coffee | 2 +- public/superset.html | 1 + public/variants.js | 8 ++++---- 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/public/controller.coffee b/public/controller.coffee index 71566ce..5038cfa 100644 --- a/public/controller.coffee +++ b/public/controller.coffee @@ -4,7 +4,7 @@ root.Controller = do -> ## Game Controller start = -> if not Model.loadGame() - Model.restart() + Model.newGame() checkSet = -> if Model.checkSet() Model.clearSet() diff --git a/public/hiddenset.html b/public/hiddenset.html index e1059ce..dfc1629 100644 --- a/public/hiddenset.html +++ b/public/hiddenset.html @@ -75,6 +75,7 @@ animationTime: 400 } + root.initialSeed = window.location.hash.substr(1) || null root.variant = Variants.hiddenset root.Controller.start() diff --git a/public/index.html b/public/index.html index aed0008..229cf2e 100644 --- a/public/index.html +++ b/public/index.html @@ -78,6 +78,7 @@ animationTime: 400 } + root.initialSeed = window.location.hash.substr(1) || null root.variant = Variants.set root.Controller.start() diff --git a/public/model.coffee b/public/model.coffee index 8034305..ea7475f 100644 --- a/public/model.coffee +++ b/public/model.coffee @@ -38,7 +38,7 @@ root.Model = do -> newGame() loadGame = -> - seed = initialSeed + seed = if initialSeed? then initialSeed else null gameid = variant.name print 'loading', gameid if typeof(Storage) isnt 'undefined' and localStorage.getItem(gameid)? diff --git a/public/superset.html b/public/superset.html index bee23e5..f1dacc7 100644 --- a/public/superset.html +++ b/public/superset.html @@ -75,6 +75,7 @@ animationTime: 400 } + root.initialSeed = window.location.hash.substr(1) || null root.variant = Variants.superset root.Controller.start() diff --git a/public/variants.js b/public/variants.js index 98d712e..3163987 100644 --- a/public/variants.js +++ b/public/variants.js @@ -1,4 +1,4 @@ -function makeStandardDeck() { +function makeStandardDeck(seed) { var deck = [] for(var i = 0; i < 81; ++i) { var cur = i; @@ -8,7 +8,7 @@ function makeStandardDeck() { var z = cur; deck.push({type: '3^4', count: a, color: b, shading: c, shape: z}); } - return shuffle(deck); + return shuffle(deck, seed); } function isNotNull(set) { @@ -133,8 +133,8 @@ superset = { hiddenset = { name: 'Set', - makeDeck: function() { - var ret = makeStandardDeck(); + makeDeck: function(seed) { + var ret = makeStandardDeck(seed); ret[0].hidden = true; return ret; },