I do *want* the vertical bits, but I'm having second thoughts about doing them up front... saving state so I can play.
authorBenjamin Smedberg <benjamin@smedbergs.us>
Thu, 11 Dec 2008 21:07:15 -0500
changeset 0 75edc5057178
child 1 67d051d75975
push id1
push userbsmedberg@mozilla.com
push dateFri, 12 Dec 2008 21:25:26 +0000
I do *want* the vertical bits, but I'm having second thoughts about doing them up front... saving state so I can play.
wordle.html
new file mode 100644
--- /dev/null
+++ b/wordle.html
@@ -0,0 +1,338 @@
+<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 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) {
+          alert("Failed to place word: " + word);
+          continue;
+        }
+      }
+
+      if (vertical) {
+        cx.textAlign = 'right';
+        cx.restore();
+        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>
+</body>