Saving state... place the first few anchors randomly, and then start fillling in?
authorBenjamin Smedberg <benjamin@smedbergs.us>
Fri, 12 Dec 2008 10:35:35 -0500
changeset 2 0830e9aac98b
parent 1 67d051d75975
child 3 c6cb8be67953
push id1
push userbsmedberg@mozilla.com
push dateFri, 12 Dec 2008 21:25:26 +0000
Saving state... place the first few anchors randomly, and then start fillling in?
jswordle.js
wordle.html
new file mode 100644
--- /dev/null
+++ b/jswordle.js
@@ -0,0 +1,461 @@
+const kWidth = 800;
+const kHeight = 400;
+
+const commonWords = [
+  'the',
+  'of',
+  'to',
+  'and',
+  'a',
+  'in',
+  'is',
+  'it',
+  'you',
+  'that',
+  'he',
+  'was',
+  'for',
+  'on',
+  'are',
+  'with',
+  'as',
+  'i',
+  'his',
+  'they',
+  'be',
+  'at',
+  'one',
+  'have',
+  'this',
+  'from',
+  'or',
+  'had',
+  'by',
+  'hot',
+  'word',
+  'but',
+  'what',
+  'some',
+  'we',
+  'can',
+  'out',
+  'other',
+  'were',
+  'all',
+  'there',
+  'when',
+  'up',
+  'use',
+  'your',
+  'how',
+  'said',
+  'an',
+  'each',
+  'she',
+  'which',
+  'do',
+  'their',
+  'time',
+  'if',
+  'will',
+  'way',
+  'about',
+  'many',
+  'then',
+  'them',
+  'write',
+  'would',
+  'like',
+  'so',
+  'these',
+  'her',
+  'long',
+  'thing',
+  'see',
+  'him',
+  'two',
+  'has',
+  'look',
+  'more',
+  'day',
+  'could',
+  'go',
+  'come',
+  'did',
+  'no',
+  'most',
+  'my',
+  'over',
+  'than',
+  'who',
+  'may',
+  'down',
+  'been',
+  'any',
+  'not'
+];
+
+const commonWordMap = {};
+for each (let word in commonWords)
+commonWordMap[word] = true;
+
+function $(id) {
+  return document.getElementById(id);
+}
+
+const statusel = $('status');
+
+function setStatus(msg)
+{
+  statusel.textContent += msg + "\n";
+}
+
+function getOwnProperties(o)
+{
+  for (let p in o)
+    if (o.hasOwnProperty(p))
+      yield [p, o[p]];
+}
+
+function randomInt(min, max)
+{
+  return Math.floor(Math.random() * (max - min)) + min;
+}
+
+function getWordList()
+{
+  let wordmap = {};
+
+  for (let word in getWords()) {
+    if (wordmap.hasOwnProperty(word)) {
+      wordmap[word] += 1;
+    }
+    else {
+      let lcword = word.toLowerCase();
+      if (wordmap.hasOwnProperty(lcword)) {
+        wordmap[lcword] += 1;
+      }
+      else {
+        let properword = word[0].toUpperCase() + word.slice(1).toLowerCase();
+        if (wordmap.hasOwnProperty(properword)) {
+          wordmap[word] = wordmap[properword] + 1;
+          delete wordmap[properword];
+        }
+        else {
+          wordmap[word] = 1;
+        }
+      }
+    }
+  }
+
+  let wordlist = [t for (t in getOwnProperties(wordmap))];
+  let max = wordlist.reduce(function(m, cur) { return Math.max(m, cur[1]); }, 0);
+  let ratio = 88 / max;
+  wordlist = [[word, count * ratio]
+              for each ([word, count] in wordlist)];
+
+  wordlist = wordlist.filter(function(e) { return e[1] > 5; });
+  wordlist.sort(function(a, b) { return a[1] < b[1]; });
+
+  return wordlist;
+}      
+
+function getWords()
+{
+  let req = new XMLHttpRequest();
+  req.open('GET', 'BSBlog.xhtml', false);
+  req.overrideMimeType('text/xml');
+  req.send(null);
+  let entries = req.responseXML.querySelectorAll('content[type="html"]');
+
+  let iframe = $('i');
+  let docelement = iframe.contentDocument.documentElement;
+
+  for (let i = entries.length - 1; i >= 0; --i) {
+    let entry = entries[i];
+    docelement.innerHTML = entry.textContent;
+    
+    let words = docelement.textContent.split(/\b/);
+    for each (let word in words) {
+      if (word.length < 3 || !/[a-zA-Z0-9]/.test(word))
+        continue;
+      if (commonWordMap.hasOwnProperty(word.toLowerCase()))
+        continue;
+      yield word;
+    }
+  }
+}
+
+function hitTest(x, y, width, height, imgdata)
+{
+  let data = imgdata.data;
+
+  if (x < 0 || x >= imgdata.width ||
+      y < 0 || y >= imgdata.height ||
+      height <= 0 || (x + width > imgdata.width) ||
+      width <= 0 || (y + height > imgdata.height))
+    throw new Error("Bad args to hitTest: " + [x, y, width, height]);
+
+  for (let iy = y; iy < y + height; ++iy) {
+    let idx = ((iy * imgdata.width) + x) * 4;
+    let end = idx + width * 4;
+
+    for (; idx < end; ++idx) {
+      let v = data[idx];
+      if (v === undefined)
+	throw new Error("Out of bounds?");
+
+      if (v != 0) {
+	setStatus("hitTest hit something at idx: " + idx);
+	return true;
+      }
+    }
+  }
+
+  return false;
+}
+
+function mergeImageData(fulldata, merge, x, y)
+{
+  for (let iy = 0; iy < merge.height; ++iy) {
+    let idx = iy * merge.width * 4;
+    let end = idx + (merge.width * 4);
+
+    let fullrow = y + iy;
+    let fullidx = (((iy + y) * fulldata.width) + x) * 4;
+
+    for (; idx < end; ++idx, ++fullidx)
+      fulldata.data[fullidx] = merge.data[idx];
+  }
+}
+
+function safeGetImageData(cx, x, y, width, height)
+{
+  if (x < 0) {
+    width += x;
+    x = 0;
+  }
+  if (y < 0) {
+    height += y;
+    y = 0;
+  }
+  if (x + width > kWidth)
+    width = kWidth - x;
+  if (y + height > kHeight)
+    height = kHeight - y;
+
+  try {
+    return cx.getImageData(x, y, width, height);
+  }
+  catch (e) {
+    throw new Error("Bad bounds: " + [x, y, width, height]);
+  }
+}
+
+function compareArrays(a1, a2)
+{
+  if (a1.length != a2.length)
+    throw new Error("Arrays differ in length!");
+
+  for (let i = 0; i < a1.length; ++i)
+    if (a1[i] !== a2[i]) {
+      throw new Error("Arrays differ at index: " + i);
+    }
+}
+
+function drawWord(cx, word, x, y, vertical, size, width, data)
+{
+  setStatus("drawWord: " + [word, x, y, vertical, size, width]);
+
+  cx.strokeRect(x, y, width, size);
+
+  let update = {
+    x: x - size,
+    y: y - size
+  };
+
+  if (vertical) {
+    cx.textAlign = 'right';
+    cx.save();
+    cx.translate(x, y);
+    cx.rotate(270 * Math.PI / 180);
+    cx.fillText(word, x, y);
+    cx.restore();
+
+    update.w = size * 3;
+    update.h = width + size * 2;
+  }
+  else {
+    cx.textAlign = 'left';
+    cx.fillText(word, x, y);
+
+    update.w = width + size * 2;
+    update.h = size * 3;
+  }
+
+  cx.strokeRect(update.x + 2, update.y + 2, update.w - 4, update.h - 4);
+
+  let mdata = safeGetImageData(cx, update.x, update.y, update.w, update.h);
+  mergeImageData(data, mdata, Math.max(update.x, 0), Math.max(update.y, 0));
+}
+
+function draw()
+{
+  statusel.textContent = '';
+
+  let words = getWordList();
+
+  let cx = $('cc').getContext('2d');
+  cx.clearRect(0, 0, kWidth, kHeight);
+
+  cx.textBaseline = 'top';
+  cx.fillStyle = "rgb(200, 0, 0, 0.8)";
+
+  let first = true;
+  let word, size;
+
+  let imgdata = cx.getImageData(0, 0, kWidth, kHeight);
+
+  /* comment out testing code
+
+  let fontsize = 80;
+
+  cx.font = fontsize + 'px Zapfino';
+  let w = cx.measureText("jumping Jacks").width;
+  cx.fillStyle = 'rgba(200, 0, 0, 0.5)';
+  cx.fillRect(100, 100, w, fontsize);
+
+  cx.fillText("jumping Jacks", 100, 100);
+
+  let ir = {x: 100 - fontsize,
+	    y: 100 - fontsize,
+	    width: w + fontsize * 2,
+	    height: fontsize * 3};
+
+  cx.strokeRect(ir.x, ir.y, ir.width, ir.height);
+
+  let smalldata = safeGetImageData(cx, ir.x, ir.y, ir.width, ir.height);
+  mergeImageData(imgdata, smalldata, ir.x, ir.y);
+  */
+
+  let i = 0;
+
+  /* Place the first five words randomly */
+  for (; i < 5; ++i) {
+    let [word, size] = words[i];
+
+    cx.font = size + 'px Baskerville';
+    let width = cx.measureText(word).width;
+
+    let x, y;
+
+    while (true) {
+      x = randomInt(0, kWidth - width);
+      y = randomInt(0, kHeight - size);
+
+      if (!hitTest(x, y, width, size, imgdata))
+	break;
+    }
+    drawWord(cx, word, x, y, false, size, width, imgdata);
+
+    let testdata = cx.getImageData(0, 0, kWidth, kHeight);
+    compareArrays(imgdata.data, testdata.data);
+  }
+}
+
+/*
+
+  for each ([word, size] in words) {
+    cx.font = size + 'px Utopia';
+    let twidth = cx.measureText(word).width;
+
+    let x, y;
+    let vertical = false;
+
+    if (first) {
+      // place the first (largest) piece of text
+      x = Math.random() * (kWidth - twidth);
+      y = Math.random() * (kHeight - size);
+      first = false;
+    }
+    else {
+      let ok = false;
+
+      for (let t = 0; t < 1000 && !ok; ++t) {
+        if (vertical) {
+          x = Math.random() * (kWidth - size);
+          y = Math.random() * (kHeight - twidth);
+
+          if (hitTest(x, y, size, twidth)) {
+            for (y in yOutbound(y, twidth)) {
+              if (!hitTest(x, y, size, twidth)) {
+                ok = true;
+                break;
+              }
+            }
+          }
+          else {
+            for (y in yInbound(y, twidth)) {
+              if (hitTest(x, y, size, twidth)) {
+                ok = true;
+                break;
+              }
+            }
+          }
+        }
+        else {
+          x = Math.random() * (kWidth - twidth);
+          y = Math.random() * (kHeight - size);
+
+          if (hitTest(x, y, twidth, size)) {
+            for (x in xOutbound(x, twidth)) {
+              if (!hitTest(x, y, twidth, size)) {
+                ok = true;
+                break;
+              }
+            }
+          }
+          else {
+            for (x in xInbound(x, twidth)) {
+              if (hitTest(x, y, twidth, size)) {
+                ok = true;
+                break;
+              }
+            }
+          }
+        }
+        if (!ok)
+          vertical = !vertical;
+      }
+      if (!ok) {
+        // alert("Failed to place word: " + word);
+        continue;
+      }
+    }
+
+    if (vertical) {
+      cx.textAlign = 'right';
+      cx.save();
+      cx.translate(x, y);
+      cx.rotate(270 * Math.PI / 180);
+      cx.fillText(word, x, y);
+      cx.restore();
+    }
+    else {
+      cx.textAlign = 'left';
+      cx.fillText(word, x, y);
+    }
+    
+    if (size > 30) {
+      curdata = cx.getImageData(0, 0, kWidth, kHeight).data;
+    }
+    else {
+      // below 30px, we're not likely to fit words in the cracks... just
+      // set pixels to black
+      for (let ix = x + twidth - 1; ix >= x; --ix)
+        for (let iy = y + size - 1; iy >= y; --iy)
+          curdata[(ix + (iy * kWidth)) * 4] = 255;
+    }
+  }
+}
+*/
\ No newline at end of file
--- a/wordle.html
+++ b/wordle.html
@@ -1,359 +1,19 @@
 <head>
   <title>Trying to replicate wordle.net</title>
-
-  <script type="application/javascript;version=1.8">
-  const kWidth = 800;
-  const kHeight = 400;
-
-  const commonWords = [
-    'the',
-    'of',
-    'to',
-    'and',
-    'a',
-    'in',
-    'is',
-    'it',
-    'you',
-    'that',
-    'he',
-    'was',
-    'for',
-    'on',
-    'are',
-    'with',
-    'as',
-    'i',
-    'his',
-    'they',
-    'be',
-    'at',
-    'one',
-    'have',
-    'this',
-    'from',
-    'or',
-    'had',
-    'by',
-    'hot',
-    'word',
-    'but',
-    'what',
-    'some',
-    'we',
-    'can',
-    'out',
-    'other',
-    'were',
-    'all',
-    'there',
-    'when',
-    'up',
-    'use',
-    'your',
-    'how',
-    'said',
-    'an',
-    'each',
-    'she',
-    'which',
-    'do',
-    'their',
-    'time',
-    'if',
-    'will',
-    'way',
-    'about',
-    'many',
-    'then',
-    'them',
-    'write',
-    'would',
-    'like',
-    'so',
-    'these',
-    'her',
-    'long',
-    'thing',
-    'see',
-    'him',
-    'two',
-    'has',
-    'look',
-    'more',
-    'day',
-    'could',
-    'go',
-    'come',
-    'did',
-    'no',
-    'most',
-    'my',
-    'over',
-    'than',
-    'who',
-    'may',
-    'down',
-    'been',
-    'any',
-    'not'
-  ];
-  const commonWordMap = {};
-  for each (let word in commonWords)
-    commonWordMap[word] = true;
-
-  function $(id) {
-    return document.getElementById(id);
-  }
- 
-  function getWordList()
-  {
-    let wordmap = {};
-
-    for (let word in getWords()) {
-      if (wordmap.hasOwnProperty(word)) {
-        wordmap[word] += 1;
-      }
-      else {
-        let lcword = word.toLowerCase();
-        if (wordmap.hasOwnProperty(lcword)) {
-          wordmap[lcword] += 1;
-        }
-        else {
-          let properword = word[0].toUpperCase() + word.slice(1).toLowerCase();
-          if (wordmap.hasOwnProperty(properword)) {
-            wordmap[word] = wordmap[properword] + 1;
-            delete wordmap[properword];
-          }
-          else {
-            wordmap[word] = 1;
-          }
-        }
-      }
-    }
-
-    function yieldWords()
-    {
-      for (let word in wordmap) {
-        if (wordmap.hasOwnProperty(word))
-          yield [word, wordmap[word]];
-      }
-    }
-
-    let wordlist = [t for (t in yieldWords())];
-    let max = wordlist.reduce(function(m, cur) Math.max(m, cur[1]), 0);
-    let ratio = 88 / max;
-    let wordlist = [[word, count * ratio]
-                    for each ([word, count] in wordlist)];
-
-    wordlist = wordlist.filter(function(e) e[1] > 5);
-    wordlist.sort(function(a, b) a[1] < b[1]);
-    return wordlist;
-  }      
-
-  function getWords()
-  {
-    let req = new XMLHttpRequest();
-    req.open('GET', 'BSBlog.xhtml', false);
-    req.overrideMimeType('text/xml');
-    req.send(null);
-    let entries = req.responseXML.querySelectorAll('content[type="html"]');
-
-    let iframe = $('i');
-    let docelement = iframe.contentDocument.documentElement;
-
-    for (let i = entries.length - 1; i >= 0; --i) {
-      let entry = entries[i];
-      docelement.innerHTML = entry.textContent;
-  
-      let words = docelement.textContent.split(/\b/);
-      for each (let word in words) {
-        if (word.length < 3 || !/[a-zA-Z0-9]/.test(word))
-          continue;
-        if (commonWordMap.hasOwnProperty(word.toLowerCase()))
-          continue;
-        yield word;
-      }
-    }
-  }
-
-  function xOutbound(xval, width) {
-    if (xval + width / 2 > kWidth / 2)
-      for (; xval >= 0; --xval)
-        yield xval;
-    else
-      for (; xval < kWidth - width; ++xval)
-        yield xval;
-  }
-
-  function xInbound(xval, width) {
-    if (xval + width / 2 > kWidth / 2)
-      for (; xval < kWidth - width; ++xval)
-        yield xval;
-    else
-      for (; xval >= 0; --xval)
-        yield xval;
-  }
-
-  function yOutbound(yval, height) {
-    if (yval + height / 2 > kHeight / 2)
-      for (; yval >= 0; --yval)
-        yield yval;
-    else
-      for (; yval < kHeight - height; ++yval)
-        yield yval;
-  }
-
-  function yInbound(yval, height) {
-    if (yval + height / 2 > kHeight / 2)
-      for (; yval < kHeight - height; ++yval)
-        yield yval;
-    else
-      for (; yval >= 0; --yval)
-        yield yval;
-  }
-
-  function draw()
-  {
-    let words = getWordList();
-
-    let cx = $('cc').getContext('2d');
-    cx.clearRect(0, 0, kWidth, kHeight);
-
-    cx.textBaseline = 'top';
-    cx.fillStyle = "rgb(200, 0, 0, 0.8)";
-
-    let first = true;
-    let word, size, curdata;
-
-    curdata = cx.getImageData(0, 0, kWidth, kHeight).data;
-
-    function hitTest(x, y, width, height) {
-      for (let iy = y + height - 1; iy >= y; --iy) {
-        let start = ((iy * kWidth) + x) * 4;
-        let end = start + ((width) * 4);
-
-        if (start < 0)
-          alert("in hittest: start < 0");
-        if (end > curdata.length )
-          alert("in hittest: end > length. x");
-
-        for (let i = start; i < end; ++i) {
-          if (curdata[i] != 0) {
-            // alert("hit x: " + ix + " y: " + iy + " c: " + ic);
-            return true;
-          }
-        }
-      }
-      return false;
-    }
-
-    if (hitTest(0, 0, kWidth, kHeight))
-      alert("hitting before I even started!");
-
-    for each ([word, size] in words) {
-      cx.font = size + 'px Utopia';
-      let twidth = cx.measureText(word).width;
-
-      let x, y;
-      let vertical = false;
-
-      if (first) {
-        // place the first (largest) piece of text
-        x = Math.random() * (kWidth - twidth);
-        y = Math.random() * (kHeight - size);
-        first = false;
-      }
-      else {
-        let ok = false;
-
-        for (let t = 0; t < 1000 && !ok; ++t) {
-          if (vertical) {
-            x = Math.random() * (kWidth - size);
-            y = Math.random() * (kHeight - twidth);
-
-            if (hitTest(x, y, size, twidth)) {
-              for (y in yOutbound(y, twidth)) {
-                if (!hitTest(x, y, size, twidth)) {
-                  ok = true;
-                  break;
-                }
-              }
-            }
-            else {
-              for (y in yInbound(y, twidth)) {
-                if (hitTest(x, y, size, twidth)) {
-                  ok = true;
-                  break;
-                }
-              }
-            }
-          }
-          else {
-            x = Math.random() * (kWidth - twidth);
-            y = Math.random() * (kHeight - size);
-
-            if (hitTest(x, y, twidth, size)) {
-              for (x in xOutbound(x, twidth)) {
-                if (!hitTest(x, y, twidth, size)) {
-                  ok = true;
-                  break;
-                }
-              }
-            }
-            else {
-              for (x in xInbound(x, twidth)) {
-                if (hitTest(x, y, twidth, size)) {
-                  ok = true;
-                  break;
-                }
-              }
-            }
-          }
-          if (!ok)
-            vertical = !vertical;
-        }
-        if (!ok) {
-          // alert("Failed to place word: " + word);
-          continue;
-        }
-      }
-
-      if (vertical) {
-        cx.textAlign = 'right';
-        cx.save();
-        cx.translate(x, y);
-        cx.rotate(270 * Math.PI / 180);
-        cx.fillText(word, x, y);
-        cx.restore();
-      }
-      else {
-        cx.textAlign = 'left';
-        cx.fillText(word, x, y);
-      }
-     
-      if (size > 30) {
-        curdata = cx.getImageData(0, 0, kWidth, kHeight).data;
-      }
-      else {
-        // below 30px, we're not likely to fit words in the cracks... just
-        // set pixels to black
-        for (let ix = x + twidth - 1; ix >= x; --ix)
-          for (let iy = y + size - 1; iy >= y; --iy)
-            curdata[(ix + (iy * kWidth)) * 4] = 255;
-      }
-    }
-  }
-  </script>
 </head>
 <body>
   <div>
   <input type="button" value="Get Words" onclick="draw()">
   </div>
 
   <div>
   <canvas width="800" height="400" id="cc" style="border: 1px solid black;"></canvas>
   </div>
 
   <iframe src="about:blank" id="i" style="display: none"></iframe>
+
+  <pre id="status"></pre>
+
+  <script type="application/javascript;version=1.8" src="jswordle.js">
+  </script>
 </body>