mirror of
https://github.com/osmarks/website
synced 2025-01-25 16:36:53 +00:00
Patch achievement system
This commit is contained in:
parent
bac2a75be6
commit
ab801b8848
@ -1,7 +1,6 @@
|
|||||||
// https://github.com/jakearchibald/idb-keyval/blob/master/dist/idb-keyval-iife.min.js
|
// I cannot be bothered to set up a bundler
|
||||||
// This is small enough that I decided to just embed it directly here to save hassle and network bandwidth.
|
// https://www.npmjs.com/package/idb
|
||||||
const idbk=function(e){"use strict";class t{constructor(e="keyval-store",t="keyval"){this.storeName=t,this._dbp=new Promise((r,n)=>{const o=indexedDB.open(e,1);o.onerror=(()=>n(o.error)),o.onsuccess=(()=>r(o.result)),o.onupgradeneeded=(()=>{o.result.createObjectStore(t)})})}
|
!function(e,t){t(window.idb={})}(this,(function(e){"use strict";let t,n;const r=new WeakMap,o=new WeakMap,s=new WeakMap,i=new WeakMap,a=new WeakMap;let c={get(e,t,n){if(e instanceof IDBTransaction){if("done"===t)return o.get(e);if("objectStoreNames"===t)return e.objectStoreNames||s.get(e);if("store"===t)return n.objectStoreNames[1]?void 0:n.objectStore(n.objectStoreNames[0])}return f(e[t])},set:(e,t,n)=>(e[t]=n,!0),has:(e,t)=>e instanceof IDBTransaction&&("done"===t||"store"===t)||t in e};function d(e){return e!==IDBDatabase.prototype.transaction||"objectStoreNames"in IDBTransaction.prototype?(n||(n=[IDBCursor.prototype.advance,IDBCursor.prototype.continue,IDBCursor.prototype.continuePrimaryKey])).includes(e)?function(...t){return e.apply(p(this),t),f(r.get(this))}:function(...t){return f(e.apply(p(this),t))}:function(t,...n){const r=e.call(p(this),t,...n);return s.set(r,t.sort?t.sort():[t]),f(r)}}function u(e){return"function"==typeof e?d(e):(e instanceof IDBTransaction&&function(e){if(o.has(e))return;const t=new Promise(((t,n)=>{const r=()=>{e.removeEventListener("complete",o),e.removeEventListener("error",s),e.removeEventListener("abort",s)},o=()=>{t(),r()},s=()=>{n(e.error||new DOMException("AbortError","AbortError")),r()};e.addEventListener("complete",o),e.addEventListener("error",s),e.addEventListener("abort",s)}));o.set(e,t)}(e),n=e,(t||(t=[IDBDatabase,IDBObjectStore,IDBIndex,IDBCursor,IDBTransaction])).some((e=>n instanceof e))?new Proxy(e,c):e);var n}function f(e){if(e instanceof IDBRequest)return function(e){const t=new Promise(((t,n)=>{const r=()=>{e.removeEventListener("success",o),e.removeEventListener("error",s)},o=()=>{t(f(e.result)),r()},s=()=>{n(e.error),r()};e.addEventListener("success",o),e.addEventListener("error",s)}));return t.then((t=>{t instanceof IDBCursor&&r.set(t,e)})).catch((()=>{})),a.set(t,e),t}(e);if(i.has(e))return i.get(e);const t=u(e);return t!==e&&(i.set(e,t),a.set(t,e)),t}const p=e=>a.get(e);const l=["get","getKey","getAll","getAllKeys","count"],D=["put","add","delete","clear"],b=new Map;function v(e,t){if(!(e instanceof IDBDatabase)||t in e||"string"!=typeof t)return;if(b.get(t))return b.get(t);const n=t.replace(/FromIndex$/,""),r=t!==n,o=D.includes(n);if(!(n in(r?IDBIndex:IDBObjectStore).prototype)||!o&&!l.includes(n))return;const s=async function(e,...t){const s=this.transaction(e,o?"readwrite":"readonly");let i=s.store;return r&&(i=i.index(t.shift())),(await Promise.all([i[n](...t),o&&s.done]))[0]};return b.set(t,s),s}c=(e=>({...e,get:(t,n,r)=>v(t,n)||e.get(t,n,r),has:(t,n)=>!!v(t,n)||e.has(t,n)}))(c),e.deleteDB=function(e,{blocked:t}={}){const n=indexedDB.deleteDatabase(e);return t&&n.addEventListener("blocked",(()=>t())),f(n).then((()=>{}))},e.openDB=function(e,t,{blocked:n,upgrade:r,blocking:o,terminated:s}={}){const i=indexedDB.open(e,t),a=f(i);return r&&i.addEventListener("upgradeneeded",(e=>{r(f(i.result),e.oldVersion,e.newVersion,f(i.transaction))})),n&&i.addEventListener("blocked",(()=>n())),a.then((e=>{s&&e.addEventListener("close",(()=>s())),o&&e.addEventListener("versionchange",(()=>o()))})).catch((()=>{})),a},e.unwrap=p,e.wrap=f}));
|
||||||
_withIDBStore(e,t){return this._dbp.then(r=>new Promise((n,o)=>{const s=r.transaction(this.storeName,e);s.oncomplete=(()=>n()),s.onabort=s.onerror=(()=>o(s.error)),t(s.objectStore(this.storeName))}))}}let r;function n(){return r||(r=new t),r}return e.Store=t,e.get=function(e,t=n()){let r;return t._withIDBStore("readonly",t=>{r=t.get(e)}).then(()=>r.result)},e.set=function(e,t,r=n()){return r._withIDBStore("readwrite",r=>{r.put(t,e)})},e.del=function(e,t=n()){return t._withIDBStore("readwrite",t=>{t.delete(e)})},e.clear=function(e=n()){return e._withIDBStore("readwrite",e=>{e.clear()})},e.keys=function(e=n()){const t=[];return e._withIDBStore("readonly",e=>{(e.openKeyCursor||e.openCursor).call(e).onsuccess=function(){this.result&&(t.push(this.result.key),this.result.continue())}}).then(()=>t)},e}({});
|
|
||||||
|
|
||||||
// attempt to register service worker
|
// attempt to register service worker
|
||||||
if ("serviceWorker" in navigator) {
|
if ("serviceWorker" in navigator) {
|
||||||
@ -37,7 +36,7 @@ const hashString = function(str, seed = 0) {
|
|||||||
const colHash = (str, saturation = 100, lightness = 70) => `hsl(${hashString(str) % 360}, ${saturation}%, ${lightness}%)`
|
const colHash = (str, saturation = 100, lightness = 70) => `hsl(${hashString(str) % 360}, ${saturation}%, ${lightness}%)`
|
||||||
|
|
||||||
// Arbitrary Points code, wrapped in an IIFE to not pollute the global environment much more than it already is
|
// Arbitrary Points code, wrapped in an IIFE to not pollute the global environment much more than it already is
|
||||||
window.points = (() => {
|
window.points = (async () => {
|
||||||
const achievementInfo = {
|
const achievementInfo = {
|
||||||
test: {
|
test: {
|
||||||
title: "Test",
|
title: "Test",
|
||||||
@ -128,9 +127,81 @@ window.points = (() => {
|
|||||||
conditions: "Solving the puzzle in Heavpoot's Game",
|
conditions: "Solving the puzzle in Heavpoot's Game",
|
||||||
description: `You... did something or other and somehow found the "black moon" thing and guessed/found the answer to the puzzle there, so enjoy this achievement, O6-3234234!`,
|
description: `You... did something or other and somehow found the "black moon" thing and guessed/found the answer to the puzzle there, so enjoy this achievement, O6-3234234!`,
|
||||||
points: 41.5824
|
points: 41.5824
|
||||||
|
},
|
||||||
|
tttWinai1:{
|
||||||
|
title: "Beginner's Luck",
|
||||||
|
conditions: `Beat "AI" 1 on Tic-Tac-Toe`,
|
||||||
|
description: "Congratulations on beating a slightly aware random number generator.",
|
||||||
|
points: 23
|
||||||
|
},
|
||||||
|
tttWinai2:{
|
||||||
|
title: "Superior Algorithms",
|
||||||
|
conditions: `Beat "AI" 2 on Tic-Tac-Toe`,
|
||||||
|
description: "Nonsarcastic congratulations on beating the near-optimal minimax agent.",
|
||||||
|
points: 46
|
||||||
|
},
|
||||||
|
apioformGame: {
|
||||||
|
title: "Relativistic apiohydromagnetoplasmodynamic cryomemetics",
|
||||||
|
conditions: "Finish the Apioform Game tutorial",
|
||||||
|
description: "You have braved the complete lack of guidance or a description in the Apioform Game and apioformed many apioforms.",
|
||||||
|
points: 40
|
||||||
|
},
|
||||||
|
rpnv4recursion: {
|
||||||
|
title: "You are doing it right",
|
||||||
|
conditions: "Recurse to a stack depth of 100 or more on RPNCalc v4",
|
||||||
|
description: "For using RPNCalcV4 as it is meant to be used - highly, highly recursively.",
|
||||||
|
points: 18.324
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const [oldMetrics, oldPoints] = await Promise.all([idb.openDB("arbitrary-metrics"), idb.openDB("arbitrary-points")])
|
||||||
|
const getMetrics = async () => {
|
||||||
|
const metrics = {}
|
||||||
|
const tx = oldMetrics.transaction("metrics", "readonly")
|
||||||
|
for (const key of await tx.store.getAllKeys()) {
|
||||||
|
metrics[key] = await tx.store.get(key)
|
||||||
|
}
|
||||||
|
return metrics
|
||||||
|
}
|
||||||
|
const getPointsData = async () => {
|
||||||
|
const data = {}
|
||||||
|
const tx = oldPoints.transaction("data", "readonly")
|
||||||
|
for (const key of await tx.store.getAllKeys()) {
|
||||||
|
data[key] = await tx.store.get(key)
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
const [metrics, pointsData] = await Promise.all([getMetrics(), getPointsData()])
|
||||||
|
await Promise.all([oldMetrics.close(), oldPoints.close()])
|
||||||
|
const db = await idb.openDB("arbitrary-data", 1, {
|
||||||
|
async upgrade(db, oldVersion, newVersion, tx) {
|
||||||
|
if (!oldVersion || oldVersion < 1) {
|
||||||
|
// create metrics, KV, achievements stores
|
||||||
|
db.createObjectStore("kv")
|
||||||
|
db.createObjectStore("metrics")
|
||||||
|
db.createObjectStore("achievements", {
|
||||||
|
keyPath: "id"
|
||||||
|
})
|
||||||
|
for (const [key, value] of Object.entries(metrics)) {
|
||||||
|
await tx.objectStore("metrics").put(value, key)
|
||||||
|
}
|
||||||
|
for (const achievement of (pointsData["achievements"] || [])) {
|
||||||
|
await tx.objectStore("achievements").put(achievement)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await tx.done
|
||||||
|
},
|
||||||
|
blocked() {
|
||||||
|
console.warn("Database error (older version open)")
|
||||||
|
},
|
||||||
|
blocking() {
|
||||||
|
window.location.reload()
|
||||||
|
},
|
||||||
|
terminated() {
|
||||||
|
console.warn("Database error (unexpectedly closed)")
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const e = (cls, parent, content) => {
|
const e = (cls, parent, content) => {
|
||||||
const element = document.createElement("div")
|
const element = document.createElement("div")
|
||||||
element.classList.add(cls)
|
element.classList.add(cls)
|
||||||
@ -155,32 +226,24 @@ window.points = (() => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const metricsStore = new idbk.Store("arbitrary-metrics", "metrics")
|
|
||||||
const dataStore = new idbk.Store("arbitrary-points", "data")
|
|
||||||
|
|
||||||
const fireUpdatedEvent = () => document.dispatchEvent(new Event("points-update"))
|
const fireUpdatedEvent = () => document.dispatchEvent(new Event("points-update"))
|
||||||
|
|
||||||
let pointsCount
|
let achievementsList
|
||||||
|
|
||||||
const getPoints = async () => {
|
const getAchievements = async tx => {
|
||||||
if (pointsCount) { return pointsCount }
|
if (achievementsList) { return achievementsList }
|
||||||
let value = await idbk.get("points", dataStore)
|
if (!tx) { tx = db.transaction("achievements", "readonly") }
|
||||||
if (value === undefined) {
|
achievementsList = await tx.objectStore("achievements").getAll()
|
||||||
await idbk.set("points", 0, dataStore)
|
return achievementsList
|
||||||
value = 0
|
|
||||||
}
|
|
||||||
pointsCount = value
|
|
||||||
return value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateStoredValue = async (store, name, fn, def) => {
|
const getPoints = async () => (await getAchievements()).map(a => a.points).reduce((x, y) => x + y, 0)
|
||||||
const newValue = fn(await idbk.get(name, store) || def)
|
|
||||||
await idbk.set(name, newValue, store)
|
|
||||||
return newValue
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateMetric = async (name, fn, def) => {
|
const updateMetric = async (name, fn, def) => {
|
||||||
const newValue = await updateStoredValue(metricsStore, name, fn, def)
|
const tx = db.transaction("metrics", "readwrite")
|
||||||
|
const init = await tx.store.get(name) || def
|
||||||
|
const newValue = fn(init)
|
||||||
|
await tx.store.put(newValue, name)
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "achievements":
|
case "achievements":
|
||||||
if (newValue === 1) {
|
if (newValue === 1) {
|
||||||
@ -190,12 +253,6 @@ window.points = (() => {
|
|||||||
}
|
}
|
||||||
return newValue
|
return newValue
|
||||||
}
|
}
|
||||||
const incrementPoints = inc => {
|
|
||||||
pointsCount += inc
|
|
||||||
updateStoredValue(dataStore, "points", x => x + inc, 0)
|
|
||||||
fireUpdatedEvent()
|
|
||||||
}
|
|
||||||
|
|
||||||
// increment pages visited count, since this should be run when a page is visited
|
// increment pages visited count, since this should be run when a page is visited
|
||||||
updateMetric("pagesVisited", x => x + 1, 0)
|
updateMetric("pagesVisited", x => x + 1, 0)
|
||||||
|
|
||||||
@ -205,35 +262,32 @@ window.points = (() => {
|
|||||||
updateMetric("timeSpent", x => x + (elapsedMs / 1000), 0)
|
updateMetric("timeSpent", x => x + (elapsedMs / 1000), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
const setMetric = (metric, value) => idbk.set(metric, value, metricsStore)
|
|
||||||
|
|
||||||
const readAllMetrics = async () => {
|
const readAllMetrics = async () => {
|
||||||
const keys = await idbk.keys(metricsStore)
|
|
||||||
const out = new Map()
|
const out = new Map()
|
||||||
await Promise.all(keys.map(async k => {
|
const tx = db.transaction("metrics", "readonly")
|
||||||
out.set(k, await idbk.get(k, metricsStore))
|
for (const key of await tx.store.getAllKeys()) {
|
||||||
}))
|
out.set(key, await tx.store.get(key))
|
||||||
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
const reset = async () => {
|
const reset = async () => {
|
||||||
pointsCount = 0
|
const tx = db.transaction(["achievements", "metrics"], "readwrite")
|
||||||
|
for (const achievement of await tx.objectStore("achievements").getAllKeys()) {
|
||||||
|
await tx.objectStore("achievements").delete(achievement)
|
||||||
|
}
|
||||||
|
for (const metric of await tx.objectStore("metrics").getAllKeys()) {
|
||||||
|
await tx.objectStore("metrics").delete(metric)
|
||||||
|
}
|
||||||
achievementsList = []
|
achievementsList = []
|
||||||
await idbk.clear(metricsStore)
|
// fireUpdatedEvent()
|
||||||
await idbk.clear(dataStore)
|
// called when achievement is unlocked there anyway
|
||||||
await unlockAchievement("reset")
|
await unlockAchievement("reset")
|
||||||
}
|
}
|
||||||
|
|
||||||
let achievementsList
|
|
||||||
const getAchievements = async () => {
|
|
||||||
if (achievementsList) { return achievementsList }
|
|
||||||
const value = await idbk.get("achievements", dataStore) || []
|
|
||||||
achievementsList = value
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
const unlockAchievement = async id => {
|
const unlockAchievement = async id => {
|
||||||
const achievementsUnlocked = await getAchievements()
|
const tx = db.transaction("achievements", "readwrite")
|
||||||
|
const achievementsUnlocked = await getAchievements(tx)
|
||||||
if (achievementsUnlocked.filter(a => a.id === id).length > 0) { return "already unlocked" }
|
if (achievementsUnlocked.filter(a => a.id === id).length > 0) { return "already unlocked" }
|
||||||
const info = achievementInfo[id]
|
const info = achievementInfo[id]
|
||||||
if (!info) { throw new Error("Achievement not recognized") }
|
if (!info) { throw new Error("Achievement not recognized") }
|
||||||
@ -245,11 +299,10 @@ window.points = (() => {
|
|||||||
page: window.location.pathname,
|
page: window.location.pathname,
|
||||||
points: info.points
|
points: info.points
|
||||||
}
|
}
|
||||||
achievementsList = achievementsList.concat([item])
|
achievementsList.push(item)
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
idbk.set("achievements", achievementsList, dataStore),
|
|
||||||
updateMetric("achievements", x => x + 1, 0),
|
updateMetric("achievements", x => x + 1, 0),
|
||||||
incrementPoints(info.points)
|
tx.objectStore("achievements").put(item),
|
||||||
])
|
])
|
||||||
|
|
||||||
fireUpdatedEvent()
|
fireUpdatedEvent()
|
||||||
@ -303,11 +356,9 @@ window.points = (() => {
|
|||||||
updateMetric,
|
updateMetric,
|
||||||
readAllMetrics,
|
readAllMetrics,
|
||||||
getPoints,
|
getPoints,
|
||||||
incrementPoints,
|
|
||||||
unlockAchievement,
|
unlockAchievement,
|
||||||
getAchievements,
|
getAchievements,
|
||||||
achievementInfo,
|
achievementInfo
|
||||||
setMetric
|
|
||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
|
|
||||||
|
@ -24,5 +24,5 @@ Please enjoy the below "flappy bird" game until your connection comes back, than
|
|||||||
const q = document.querySelector("canvas")
|
const q = document.querySelector("canvas")
|
||||||
let canstart = true
|
let canstart = true
|
||||||
J=q.width=q.height=Math.min(500, document.querySelector("p").clientWidth);Q='50px monospace';with(Math)with(C=q.getContext`2d`)with(X=Y=R=0,init=e=(e=>{w=f=Y=X=0,R=1,H=250,q.focus(),canstart=false,V=-10,P=[],(F=(e=>{e-w>1/60&&(Y&&(V=-10,Y=0),H+=V,V+=.8,P.forEach(e=>e.P-=3),f%50||P.push({P:J,L:150+(0|200*random()),C:'rgb('+255*random()+','+255*random()+','+255*random()+')'}),(P.some(e=>abs(e.L-H+2)>60&&0<e.P&&e.P<25)||H>J||H<0)&&(R=0,setTimeout(e=>{font=Q,fillStyle='red',fillText(':(',50,90),canstart=true},99)),clearRect(0,0,J,J),P.forEach(e=>{with(e){fillStyle=C,fillRect(P,0,20,L-60),fillRect(P,L+60,20,J-(L+60))}}),fillStyle='red',fillRect(20,H,10,10),font=Q,fillStyle='red',fillText(ceil(f/50-10/3),40,40),f++,w=e),R&&requestAnimationFrame(F)}))()}),document)onkeydown=(e=>{' '==e.key&&R?++Y:' '==e.key; if (canstart) {init()}}),onmousedown=(e=>{R&&++Y; if (canstart) {init()}});
|
J=q.width=q.height=Math.min(500, document.querySelector("p").clientWidth);Q='50px monospace';with(Math)with(C=q.getContext`2d`)with(X=Y=R=0,init=e=(e=>{w=f=Y=X=0,R=1,H=250,q.focus(),canstart=false,V=-10,P=[],(F=(e=>{e-w>1/60&&(Y&&(V=-10,Y=0),H+=V,V+=.8,P.forEach(e=>e.P-=3),f%50||P.push({P:J,L:150+(0|200*random()),C:'rgb('+255*random()+','+255*random()+','+255*random()+')'}),(P.some(e=>abs(e.L-H+2)>60&&0<e.P&&e.P<25)||H>J||H<0)&&(R=0,setTimeout(e=>{font=Q,fillStyle='red',fillText(':(',50,90),canstart=true},99)),clearRect(0,0,J,J),P.forEach(e=>{with(e){fillStyle=C,fillRect(P,0,20,L-60),fillRect(P,L+60,20,J-(L+60))}}),fillStyle='red',fillRect(20,H,10,10),font=Q,fillStyle='red',fillText(ceil(f/50-10/3),40,40),f++,w=e),R&&requestAnimationFrame(F)}))()}),document)onkeydown=(e=>{' '==e.key&&R?++Y:' '==e.key; if (canstart) {init()}}),onmousedown=(e=>{R&&++Y; if (canstart) {init()}});
|
||||||
window.addEventListener("load", function() { if ("points" in window) { points.unlockAchievement("offline") }})
|
window.addEventListener("load", function() { if ("points" in window) { points.then(points => points.unlockAchievement("offline")) }})
|
||||||
</script>
|
</script>
|
||||||
|
@ -619,6 +619,9 @@ description: A game about... apioforms... by Heavpoot.
|
|||||||
}
|
}
|
||||||
console.log(kills)
|
console.log(kills)
|
||||||
if (localStorage.tutorial=="true"&&kills>30){
|
if (localStorage.tutorial=="true"&&kills>30){
|
||||||
|
if ("points" in window) {
|
||||||
|
points.then(points => points.unlockAchievement("apioformGame"))
|
||||||
|
}
|
||||||
localStorage.tutorial=false
|
localStorage.tutorial=false
|
||||||
kills=0
|
kills=0
|
||||||
}
|
}
|
||||||
|
@ -377,9 +377,9 @@ const mainLoop = () => {
|
|||||||
|
|
||||||
if (hp <= 0) {
|
if (hp <= 0) {
|
||||||
if ("points" in window) {
|
if ("points" in window) {
|
||||||
if (score >= 10) { points.unlockAchievement("emuwar10") }
|
if (score >= 10) { points.then(points => points.unlockAchievement("emuwar10")) }
|
||||||
points.updateMetric("foesVanquished", function(x) { return x + score }, 0)
|
points.then(points => points.updateMetric("foesVanquished", function(x) { return x + score }, 0))
|
||||||
points.updateMetric("deaths", function(x) { return x + 1 }, 0)
|
points.then(points => points.updateMetric("deaths", function(x) { return x + 1 }, 0))
|
||||||
}
|
}
|
||||||
clear(saybox)
|
clear(saybox)
|
||||||
say(saybox, gameOverMessage + " Press R to restart.")
|
say(saybox, gameOverMessage + " Press R to restart.")
|
||||||
|
@ -29,12 +29,12 @@ description: It is pitch black (if you ignore all of the lighting). You are like
|
|||||||
<script>
|
<script>
|
||||||
function unlockAchievement(name) {
|
function unlockAchievement(name) {
|
||||||
if ("points" in window) {
|
if ("points" in window) {
|
||||||
points.unlockAchievement(name)
|
points.then(points => points.unlockAchievement(name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function updateMetric(name, fn, def) {
|
function updateMetric(name, fn, def) {
|
||||||
if ("points" in window) {
|
if ("points" in window) {
|
||||||
points.updateMetric(name, fn, def)
|
points.then(points => points.updateMetric(name, fn, def))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var state="start"; // SPOILERS SPOILERS OH GOD SPOILERS OH GOD SPOILERS
|
var state="start"; // SPOILERS SPOILERS OH GOD SPOILERS OH GOD SPOILERS
|
||||||
|
@ -125,11 +125,11 @@ url: /infipage/p
|
|||||||
window.addEventListener("load", function() {
|
window.addEventListener("load", function() {
|
||||||
if ("points" in window) {
|
if ("points" in window) {
|
||||||
console.log("running update")
|
console.log("running update")
|
||||||
points.updateMetric("greatestInfipage", function(current) {
|
points.then(points => points.updateMetric("greatestInfipage", function(current) {
|
||||||
console.log("updated", current, pageString)
|
console.log("updated", current, pageString)
|
||||||
if (!current || page.abs().gt(Big(current))) { return pageString }
|
if (!current || page.abs().gt(Big(current))) { return pageString }
|
||||||
else { return current }
|
else { return current }
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
@ -63,8 +63,8 @@ description: Lorem Ipsum (latin-like placeholder text), eternally. Somehow peopl
|
|||||||
var count = 0
|
var count = 0
|
||||||
var unaddedCount = 0
|
var unaddedCount = 0
|
||||||
var handlePoints = throttle(function() {
|
var handlePoints = throttle(function() {
|
||||||
if (count >= 400) { points.unlockAchievement("lorem400") }
|
if (count >= 400) { points.then(p => p.unlockAchievement("lorem400")) }
|
||||||
points.updateMetric("loremParagraphs", function(x) { return x + unaddedCount }, 0).then(function() { unaddedCount = 0 })
|
points.then(p => p.updateMetric("loremParagraphs", function(x) { return x + unaddedCount }, 0).then(function() { unaddedCount = 0 }))
|
||||||
}, 300)
|
}, 300)
|
||||||
|
|
||||||
window.addEventListener("load", function() {
|
window.addEventListener("load", function() {
|
||||||
|
@ -24,6 +24,12 @@ const metricDisplayInfo = {
|
|||||||
heavpootDeaths: { name: "Heavpoot's Game deaths", units: "death" }
|
heavpootDeaths: { name: "Heavpoot's Game deaths", units: "death" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const opponent of ["ai1", "ai2"]) {
|
||||||
|
for (const result of ["Wins", "Losses", "Draws"]) {
|
||||||
|
metricDisplayInfo[`ttt${result}${opponent}`] = { name: `${result} against ${opponent.toUpperCase()}`, units: "game" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const displayMetric = metric => {
|
const displayMetric = metric => {
|
||||||
let name = metric[0]
|
let name = metric[0]
|
||||||
let value = metric[1]
|
let value = metric[1]
|
||||||
@ -40,7 +46,7 @@ const displayMetric = metric => {
|
|||||||
const Metrics = {
|
const Metrics = {
|
||||||
metrics: null,
|
metrics: null,
|
||||||
load: async () => {
|
load: async () => {
|
||||||
Metrics.metrics = await points.readAllMetrics()
|
Metrics.metrics = await (await points).readAllMetrics()
|
||||||
m.redraw()
|
m.redraw()
|
||||||
},
|
},
|
||||||
view: () => m("p", Metrics.metrics === null ? "Loading..." : m("table.metrics", Array.from(Metrics.metrics.entries()).map(displayMetric)))
|
view: () => m("p", Metrics.metrics === null ? "Loading..." : m("table.metrics", Array.from(Metrics.metrics.entries()).map(displayMetric)))
|
||||||
@ -61,9 +67,10 @@ const renderAchievement = a =>
|
|||||||
const Achievements = {
|
const Achievements = {
|
||||||
achievements: [],
|
achievements: [],
|
||||||
load: async () => {
|
load: async () => {
|
||||||
const raw = await points.getAchievements()
|
let rpoints = await points
|
||||||
|
const raw = await rpoints.getAchievements()
|
||||||
Achievements.achievements = raw.map(ach => {
|
Achievements.achievements = raw.map(ach => {
|
||||||
const info = points.achievementInfo[ach.id]
|
const info = rpoints.achievementInfo[ach.id]
|
||||||
const out = {
|
const out = {
|
||||||
title: ach.id || "???",
|
title: ach.id || "???",
|
||||||
description: `Unrecognized achievement ${ach.id}.`,
|
description: `Unrecognized achievement ${ach.id}.`,
|
||||||
@ -82,15 +89,15 @@ const Achievements = {
|
|||||||
const reset = async () => {
|
const reset = async () => {
|
||||||
if (prompt(`This will reset your points, achievements and metrics. If you are sure, please type "yes I am definitely sure".`) === "yes I am definitely sure") {
|
if (prompt(`This will reset your points, achievements and metrics. If you are sure, please type "yes I am definitely sure".`) === "yes I am definitely sure") {
|
||||||
if (confirm("Are you really very sure?")) {
|
if (confirm("Are you really very sure?")) {
|
||||||
await points.reset()
|
await (await points).reset()
|
||||||
window.location.reload()
|
//window.location.reload()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let pointsCount = "[loading...]"
|
let pointsCount = "[loading...]"
|
||||||
|
|
||||||
const reloadPoints = async () => { pointsCount = await points.getPoints() }
|
const reloadPoints = async () => { pointsCount = await (await points).getPoints() }
|
||||||
|
|
||||||
const App = {
|
const App = {
|
||||||
view: () => m("div", [
|
view: () => m("div", [
|
||||||
@ -107,6 +114,6 @@ Metrics.load()
|
|||||||
reloadPoints()
|
reloadPoints()
|
||||||
Achievements.load()
|
Achievements.load()
|
||||||
|
|
||||||
points.unlockAchievement("visitArbitraryPoints")
|
points.then(points => points.unlockAchievement("visitArbitraryPoints"))
|
||||||
|
|
||||||
document.addEventListener("points-update", () => { reloadPoints(); Achievements.load() })
|
document.addEventListener("points-update", () => { reloadPoints(); Achievements.load() })
|
@ -121,7 +121,16 @@ const doIns = (ins, state, handler) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let hasDoneAchievement = false
|
||||||
|
|
||||||
export const step = (state, handler, showIns, maxdepth) => {
|
export const step = (state, handler, showIns, maxdepth) => {
|
||||||
|
if (!hasDoneAchievement && state.stacks.length > 100) {
|
||||||
|
if ("points" in window) {
|
||||||
|
points.then(points => points.unlockAchievement("rpnv4recursion"))
|
||||||
|
}
|
||||||
|
hasDoneAchievement = true
|
||||||
|
}
|
||||||
|
|
||||||
if (state.stacks.length > maxdepth) {
|
if (state.stacks.length > maxdepth) {
|
||||||
throw 'max recursion depth exceeded'
|
throw 'max recursion depth exceeded'
|
||||||
}
|
}
|
||||||
|
@ -321,6 +321,10 @@ slug: tictactoe
|
|||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
// copyright (2022 CE) © © © osmarks.net hypercomputational tetrational metahexagonal industrial - unsigned integer division
|
// copyright (2022 CE) © © © osmarks.net hypercomputational tetrational metahexagonal industrial - unsigned integer division
|
||||||
|
|
||||||
|
let achievementEligiblity = true
|
||||||
|
let lastOpponent = null
|
||||||
|
|
||||||
var workerGlue = `
|
var workerGlue = `
|
||||||
onmessage = event => {
|
onmessage = event => {
|
||||||
var [policy, ...args] = event.data
|
var [policy, ...args] = event.data
|
||||||
@ -407,6 +411,23 @@ slug: tictactoe
|
|||||||
info.removeEventListener("click", el)
|
info.removeEventListener("click", el)
|
||||||
}
|
}
|
||||||
info.addEventListener("click", el)
|
info.addEventListener("click", el)
|
||||||
|
if (achievementEligiblity && opponentIsAI()) {
|
||||||
|
if ("points" in window) {
|
||||||
|
let opp = lastOpponent
|
||||||
|
points.then(points => {
|
||||||
|
if (winner === 1) {
|
||||||
|
points.unlockAchievement(`tttWin${opp}`)
|
||||||
|
points.updateMetric(`tttWins${opp}`, x => x + 1, 0)
|
||||||
|
} else if (winner === 2) {
|
||||||
|
points.updateMetric(`tttLosses${opp}`, x => x + 1, 0)
|
||||||
|
} else if (isDraw) {
|
||||||
|
points.updateMetric(`tttDraws${opp}`, x => x + 1, 0)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
achievementEligiblity = true
|
||||||
|
lastOpponent = null
|
||||||
gameOver = true
|
gameOver = true
|
||||||
})
|
})
|
||||||
return true
|
return true
|
||||||
@ -462,6 +483,10 @@ slug: tictactoe
|
|||||||
if (isNaN(coord) || typeof coord != "number" || start != "cell") { return }
|
if (isNaN(coord) || typeof coord != "number" || start != "cell") { return }
|
||||||
if (grid[coord]) { return }
|
if (grid[coord]) { return }
|
||||||
if (!onTurn(coord) && opponentIsAI()) {
|
if (!onTurn(coord) && opponentIsAI()) {
|
||||||
|
if (lastOpponent && lastOpponent !== opponent.value) {
|
||||||
|
achievementEligiblity = false
|
||||||
|
}
|
||||||
|
lastOpponent = opponent.value
|
||||||
worker.postMessage([opponent.value, grid, currentPlayer])
|
worker.postMessage([opponent.value, grid, currentPlayer])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user