notebook/app/views/lab/crossword.html.erb
2021-05-27 02:44:38 -07:00

656 lines
20 KiB
Plaintext

<%
words_with_clues = {
'Andrew': 'the man who loves you the most',
'Amy': 'the best wife in the world',
'Amsterdam': 'our first stop in europe',
'England': 'our second country in europe',
'Italy': 'our last country in europe',
'Vesuvius': 'birthday volcano',
'Bethany': 'your maid of honor',
'Clayton': 'the bestest of men (for a wedding)',
'honeymoon': 'Niagara Falls',
'rednovalabs': 'KC office of love',
'Lucy': 'our little wocky slush',
'Dimitri': 'our little fuzzy bitch boy',
'humming': 'type of porch-nesting bird',
'raytown': "the city of Benetti's",
'flanders': "sad valentine's dog",
'amycakes': "Amy's delicious Valentine's dessert",
'mashed': 'the best form of potatoes',
'Ireland': 'the european country we biked the most in',
'sunflower': "Amy's favorite flower",
"mountain": 'engaged on top of a _____',
'Norway': 'the best country to get engaged in',
'Joplin': 'the city Dimitri came from',
'Independence': 'the city of our wedding venue',
'rain': 'what fell from the sky while we got married',
'Cancun': "bachelor party destination",
'Jumeirah': "the Burj Al Arab is next to _____ Beach",
'marriage': 'this bwings us togeder, spelled correctly',
# movies
'delivery': "Kiki's _____ Service",
'neighbor': 'My _____ Totoro',
'castle': "Howl's Moving _____",
# Movies: Princess Bride
'Rugen': "This Count killed Inigo's father",
'never': "when to go against a Sicilian when death is on the line",
'iocane': 'powder the Dread Pirate Roberts cultivated an immunity for',
'Buttercup': 'the name of the eponymous Princess Bride',
'Inconceivable': "Vizzini's oft-misused word that Inigo questions",
'wife': "'I'm not a witch, I'm your ______!'",
# Movies: Disney
# TV: The Office
'Mose': "Dwight's cousin at Schute Farms",
'Foreman': "Michael burned his foot on this George ______ grill",
'refrigeration': 'Bob Vance of Vance _____',
'Meredith': 'Michael hits her with a car',
'Colorado': 'the state Michael and Holly eventually move away to',
'Sprinkles': "Angela's cold, dead cat, thanks to Dwight",
'Hunter': "Jan Levinson's musically-talented assistant",
'Pete': "Plop's actual name in The Office",
'pita': "Ryan caused the fire while heating up this cheesy bread",
'Treble': "Andy's a capella group from Cornell: Here Comes _____",
'bacon': "Michael Scott loves to wake up to the smell of this meat",
"Scrantonicity": "Kevin Malone's band",
"Electric": "Scranton's nickname is The _____ City",
"lithium": "what kind of batteries do Scott's Tots get instead of tuition?",
"Dementors": "the worst part of prison, according to Prison Mike",
"jello": "edible substance that Jim puts office supplies in",
"MISSsterious": "women's suit brand unknowingly donned by Michael Scott",
"Kelly": "who needs to access their 'uncrazy' side?",
"ignorant": "what kind of slut is Dwight?",
"superstitious": "Michael Scott is a little stitious, but he's not _____",
"both": "Would Michael Scott rather be feared or loved?",
"Creed": "The Office coworker that tries to set Jim up with his daughter",
"king": "the level of lizard royalty Robert California claims to be",
"rabies": "what disease does Michael Scott organize a fun run for?",
"Susan": "Gabe Lewis's middle name in The Office",
"broccoli": "what vegetable does Michael Scott force-feed Kevin?",
"Drummer": "Angela Kinsley's favorite song is about a boy with this occupation",
"crossword": "What kind of puzzle is Stanley almost always seen doing on The Office?",
# TV: 30 Rock
'Rockefeller': 'The "Rock" in 30 Rock is short for this',
'Oprah': 'Liz Lemon mistakes a 12-year-old girl for this celebrity',
'Greenzo': "David Schwimmer's eco-friendly mascot on 30 Rock",
'Jenna': 'who would literally kill herself if Jack hired another blonde woman?',
# TV: P&R
'recreation': 'Parks and ______',
'Gryzzl': 'this hip startup is trying to buy the Newport land',
'Janet': "the aristrocratic Ms. Snakehole's first name",
'Barkley': "Bobby Newport's campign lobbyist's last name",
'Batman': "Ben's Treat-yo-self costume",
"Indiana": "Pawnee's state",
"Bloosh": "Annabel Porter's internet lifestyle magazine",
'Hogwarts': "what Andy mistakes Buckingham Palace for",
'Tammy': 'the strangely irresistable name for Ron Swanson',
# Books
'Monte': 'The Count of _____ Cristo',
'revenge': 'what Edmon Dantes wants most',
'Anne': 'the youngest Bronte sister',
'secrets': 'Harry Potter and the Chamber of _____',
'GGM': 'a master of literary time travel',
'Goldfinch': 'The _____, starring Theodore Decker',
'Bassoon': 'Raine Wilson is The _____ King',
'Rye': 'J.D. Salinger says the Catcher is in here',
'Kite': "what Amir runs to fetch in Afghanistan",
'death': 'What Gabriel Garcia Marquez chronicled and foretold',
'Cholera': 'Love in the Time of _____',
'one': 'how many hundred years of solitude?',
'Alchemist': 'the titular wiseman to Santigo, a young Spanish shepard',
'phantom': 'the kind of toolbooth that can suddenly appear',
'three': 'how many musketeers?',
'innocence': "the age Edith Wharton is most concerned about",
"Narnia": "Lucy, Edmund, Peter, and Susan visit here",
'Caspian': "The Dawn Treader's captain",
'unusual': 'ROUS: Rodents of _____ Size',
'Florin': 'in what fictional country does The Princess Bride take place?',
# Music
'ironic': 'like rain on your wedding day',
'toro': '___ y Moi',
'Chazwick': "Toro y Moi's real first name",
'JamesMurphy': "he's spinning at Chazwick's house",
'Daft': "the punk playing at James Murphy's house",
# memes
'tiktok': 'the successor to vine',
'roadworkahead': 'yeah, i sure hope it do'
}
selected_words = words_with_clues.to_a.sample(15).to_h
%>
<style>
html{
height:100%;
}
body{
height:100%;
min-width: 400px;
margin:0;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.container{
position: absolute;
bottom: 0; top: 0; left: 0; right: 0;
text-align: center;
}
.center{
margin: 0 auto;
}
.btn{
margin: 8px 4px 0;
width: 172px;
height: 28px;
}
.line{
height: 2em;
}
.word, .clue{
display: inline-block;
height: 1.5em;
padding: 0 5px;
}
.word{
text-align: right;
width: 100px;
}
.clue{
width: 500px;
padding: 0.5em;
}
.clue:hover {
background: pink;
}
.crossword{
display: block;
background-color: rgb(32,32,32);
}
.square{
margin: 0 1px 1px 0;
display: inline-block;
font: 24px Calibri;
width: 1.25em;
height: 1.25em;
line-height: 1.25em;
vertical-align: middle;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.letter{
background-color: rgb(255,255,255);
-webkit-touch-callout: text;
-webkit-user-select: text;
-khtml-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
}
.char:focus{
-webkit-box-shadow: 0 0 0 1px yellow;
-moz-box-shadow: 0 0 0 1px yellow;
box-shadow: inset 0 0 0 1px yellow;
background: pink;
}
.char {
font-size:24px;
text-transform: uppercase;
outline: 0;
border: 0;
padding: 0;
margin: -1px 0 0 -1px;
width: 1.35em;
height: 1.35em;
text-align: center;
background: none;
}
.hide{
visibility: hidden;
}
.clueReadOnly
{
border: 0 ;
outline: 0;
color:#303030 !important;
background:none;
}
.strikethrough {
text-decoration: line-through;
}
</style>
<div class="container">
<button class="btn" id="btnCreate">Solve</button>
<br/><br/>
<div class="center crossword" id="crossword"></div><br/>
<div class="center">
<% selected_words.each do |word, clue| %>
<div class="line">
<input class="word" type="text" value="<%= word %>" />
<input class="clue" value="<%= clue %>" />
</div>
<% end %>
</div>
</div>
<script type="text/javascript">
//---------------------------------//
// GLOBAL VARIABLES //
//---------------------------------//
var board, wordArr, wordBank, wordsActive, mode;
var Bounds = {
top:0, right:0, bottom:0, left:0,
Update:function(x,y){
this.top = Math.min(y,this.top);
this.right = Math.max(x,this.right);
this.bottom = Math.max(y,this.bottom);
this.left = Math.min(x,this.left);
},
Clean:function(){
this.top = 999;
this.right = 0;
this.bottom = 0;
this.left = 999;
}
};
//---------------------------------//
// MAIN //
//---------------------------------//
function Play(){
var letterArr = document.getElementsByClassName('letter');
for(var i = 0; i < letterArr.length; i++){
letterArr[i].innerHTML = "<input class='char' type='text' maxlength='1'></input>";
}
mode = 0;
ToggleInputBoxes(false);
}
function Create(){
if (mode === 0){
ToggleInputBoxes(true);
document.getElementById("crossword").innerHTML = BoardToHtml(" ");
document.getElementById('btnCreate').innerText = 'New crossword';
mode = 1;
}
else{
GetWordsFromInput();
for(var i = 0, isSuccess=false; i < 10 && !isSuccess; i++){
CleanVars();
isSuccess = PopulateBoard();
}
document.getElementById("crossword").innerHTML =
(isSuccess) ? BoardToHtml(" ") : "Failed to find crossword." ;
document.getElementById('btnCreate').innerText = 'Solve';
Play();
}
}
function ToggleInputBoxes(active){
var w=document.getElementsByClassName('word'),
d=document.getElementsByClassName('clue');
for(var i=0;i<w.length; i++){
if(active===true){
RemoveClass(w[i], 'hide');
RemoveClass(d[i], 'clueReadOnly');
d[i].disabled = '';
}
else{
AddClass(w[i], 'hide');
AddClass(d[i], 'clueReadOnly');
d[i].disabled = 'readonly';
}
}
}
function GetWordsFromInput(){
wordArr = [];
for(var i=0,val,w=document.getElementsByClassName("word");i<w.length;i++){
val = w[i].value.toUpperCase();
if (val !== null && val.length > 1){wordArr.push(val);}
}
}
function CleanVars(){
Bounds.Clean();
wordBank = [];
wordsActive = [];
board = [];
for(var i = 0; i < 32; i++){
board.push([]);
for(var j = 0; j < 32; j++){
board[i].push(null);
}
}
}
function PopulateBoard(){
PrepareBoard();
for(var i=0,isOk=true,len=wordBank.length; i<len && isOk; i++){
isOk = AddWordToBoard();
}
return isOk;
}
function PrepareBoard(){
wordBank=[];
for(var i = 0, len = wordArr.length; i < len; i++){
wordBank.push(new WordObj(wordArr[i]));
}
for(i = 0; i < wordBank.length; i++){
for(var j = 0, wA=wordBank[i]; j<wA.char.length; j++){
for(var k = 0, cA=wA.char[j]; k<wordBank.length; k++){
for(var l = 0,wB=wordBank[k]; k!==i && l<wB.char.length; l++){
wA.totalMatches += (cA === wB.char[l])?1:0;
}
}
}
}
}
// TODO: Clean this guy up
function AddWordToBoard(){
var i, len, curIndex, curWord, curChar, curMatch, testWord, testChar,
minMatchDiff = 9999, curMatchDiff;
if(wordsActive.length < 1){
curIndex = 0;
for(i = 0, len = wordBank.length; i < len; i++){
if (wordBank[i].totalMatches < wordBank[curIndex].totalMatches){
curIndex = i;
}
}
wordBank[curIndex].successfulMatches = [{x:12,y:12,dir:0}];
}
else{
curIndex = -1;
for(i = 0, len = wordBank.length; i < len; i++){
curWord = wordBank[i];
curWord.effectiveMatches = 0;
curWord.successfulMatches = [];
for(var j = 0, lenJ = curWord.char.length; j < lenJ; j++){
curChar = curWord.char[j];
for (var k = 0, lenK = wordsActive.length; k < lenK; k++){
testWord = wordsActive[k];
for (var l = 0, lenL = testWord.char.length; l < lenL; l++){
testChar = testWord.char[l];
if (curChar === testChar){
curWord.effectiveMatches++;
var curCross = {x:testWord.x,y:testWord.y,dir:0};
if(testWord.dir === 0){
curCross.dir = 1;
curCross.x += l;
curCross.y -= j;
}
else{
curCross.dir = 0;
curCross.y += l;
curCross.x -= j;
}
var isMatch = true;
for(var m = -1, lenM = curWord.char.length + 1; m < lenM; m++){
var crossVal = [];
if (m !== j){
if (curCross.dir === 0){
var xIndex = curCross.x + m;
if (xIndex < 0 || xIndex >= board.length){
isMatch = false;
break;
}
crossVal.push(board[xIndex][curCross.y]);
crossVal.push(board[xIndex][curCross.y + 1]);
crossVal.push(board[xIndex][curCross.y - 1]);
}
else{
var yIndex = curCross.y + m;
if (yIndex < 0 || yIndex >= board[curCross.x].length){
isMatch = false;
break;
}
crossVal.push(board[curCross.x][yIndex]);
crossVal.push(board[curCross.x + 1][yIndex]);
crossVal.push(board[curCross.x - 1][yIndex]);
}
if(m > -1 && m < lenM-1){
if (crossVal[0] !== curWord.char[m]){
if (crossVal[0] !== null){
isMatch = false;
break;
}
else if (crossVal[1] !== null){
isMatch = false;
break;
}
else if (crossVal[2] !== null){
isMatch = false;
break;
}
}
}
else if (crossVal[0] !== null){
isMatch = false;
break;
}
}
}
if (isMatch === true){
curWord.successfulMatches.push(curCross);
}
}
}
}
}
curMatchDiff = curWord.totalMatches - curWord.effectiveMatches;
if (curMatchDiff<minMatchDiff && curWord.successfulMatches.length>0){
curMatchDiff = minMatchDiff;
curIndex = i;
}
else if (curMatchDiff <= 0){
return false;
}
}
}
if (curIndex === -1){
return false;
}
var spliced = wordBank.splice(curIndex, 1);
wordsActive.push(spliced[0]);
var pushIndex = wordsActive.length - 1,
rand = Math.random(),
matchArr = wordsActive[pushIndex].successfulMatches,
matchIndex = Math.floor(rand * matchArr.length),
matchData = matchArr[matchIndex];
wordsActive[pushIndex].x = matchData.x;
wordsActive[pushIndex].y = matchData.y;
wordsActive[pushIndex].dir = matchData.dir;
for(i = 0, len = wordsActive[pushIndex].char.length; i < len; i++){
var xIndex = matchData.x,
yIndex = matchData.y;
if (matchData.dir === 0){
xIndex += i;
board[xIndex][yIndex] = wordsActive[pushIndex].char[i];
}
else{
yIndex += i;
board[xIndex][yIndex] = wordsActive[pushIndex].char[i];
}
Bounds.Update(xIndex,yIndex);
}
return true;
}
function BoardToHtml(blank){
for(var i=Bounds.top-1, str=""; i<Bounds.bottom+2; i++){
str+="<div class='row'>";
for(var j=Bounds.left-1; j<Bounds.right+2; j++){
str += BoardCharToElement(board[j][i]);
}
str += "</div>";
}
return str;
}
function BoardCharToElement(c){
var arr=(c)?['square','letter']:['square'];
return EleStr('div',[{a:'class',v:arr}],c);
}
//---------------------------------//
// OBJECT DEFINITIONS //
//---------------------------------//
function WordObj(stringValue){
this.string = stringValue;
this.char = stringValue.split("");
this.totalMatches = 0;
this.effectiveMatches = 0;
this.successfulMatches = [];
}
//---------------------------------//
// EVENTS //
//---------------------------------//
function RegisterEvents(){
document.getElementById("crossword").onfocus = function (){
return false; }
document.getElementById("btnCreate").addEventListener('click', Create, false);
}
RegisterEvents();
//---------------------------------//
// HELPER FUNCTIONS //
//---------------------------------//
function EleStr(e,c,h){
h = (h)?h:"";
for(var i=0,s="<"+e+" "; i<c.length; i++){
s+=c[i].a+ "='"+ArrayToString(c[i].v," ")+"' ";
}
return (s+">"+h+"</"+e+">");
}
function toggle_strike_through(self) {
console.log('toggling');
console.log(this);
console.log(self);
}
function ArrayToString(a,s){
if(a===null||a.length<1)return "";
if(s===null)s=",";
for(var r=a[0],i=1;i<a.length;i++){r+=s+a[i];}
return r;
}
function AddClass(ele,classStr){
ele.className = ele.className.replaceAll(' '+classStr,'')+' '+classStr;
}
function RemoveClass(ele,classStr){
ele.className = ele.className.replaceAll(' '+classStr,'');
}
function ToggleClass(ele,classStr){
var str = ele.className.replaceAll(' '+classStr,'');
ele.className = (str.length===ele.className.length)?str+' '+classStr:str;
}
String.prototype.replaceAll = function (replaceThis, withThis) {
var re = new RegExp(replaceThis,"g");
return this.replace(re, withThis);
};
//---------------------------------//
// INITIAL LOAD //
//---------------------------------//
Create();
</script>
<script>
$(document).ready(function () {
$('.line').click(function () {
$(this).find('.clue').toggleClass('strikethrough');
});
})
</script>