1
0
anagram-games/www/bagels/index.html

333 lines
12 KiB
HTML

<!doctype html>
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
<title>Anagram Bagels</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
<link rel="stylesheet" type="text/css" href="main.css" />
<script src="../random.js"></script>
<script src="../scramblestring.js"></script>
<script type="text/javascript" src="../wordlist/typo.js"></script>
<script src="../wordlist/wordlist.js"></script>
<script src="./bagels.js"></script>
<script src="../entryui.js"></script>
<script >
class BagelsUI extends AnagramEntryUI {
recolorAvailableLetters() {
let idx = this.focusedTextbox;
document.querySelectorAll('#available_letters button').forEach(b => {
// clear class list
while (b.classList.length > 0) {
b.classList.remove(b.classList.item(0));
}
let letter = b.innerText;
if (letter == '\xA0') {
b.classList.add('space');
return;
}
let info = this.game.knownInformation[letter];
let matchHere = this.game.knownMatches.has(idx);
let numEntered = 0;
let numEnteredAndKnown = 0;
for (let i=0; i < this.game.numLetters; i++) {
if (i == idx) continue;
if (this.letters[i] == letter) {
numEntered++;
numEnteredAndKnown++;
} else if (info.knownMatches.has(i)) {
numEnteredAndKnown++;
}
}
let notHere = false;
if (info.knownNotHere.has(idx)) {
notHere = true;
} else if (matchHere && info.knownMatches.has(idx)) {
notHere = false;
} else if (matchHere && !info.knownMatches.has(idx)) {
notHere = true;
} else if (info.knownElsewheres.has(idx)) {
notHere = true;
} else if (numEntered >= info.max) {
notHere = true;
} else if (numEnteredAndKnown >= info.max) {
notHere = true;
}
let cls = 'unknown';
if (info.max == 0) {
cls = 'unused';
} else if (info.knownMatches.size) {
cls = 'match';
} else if (info.knownElsewheres.size) {
cls = 'elsewhere';
}
b.classList.add(cls);
if (notHere) b.classList.add('not_here');
});
}
submitWord(word) {
if (this.won) return;
let guess;
if (word) {
guess = word;
} else {
if (!this.letters.every(letter => letter != '')) return;
guess = this.letters.join('');
}
let results = this.game.makeGuess(guess);
if (results == null) return;
this.saveGuess(guess);
let resultDisplay = document.createElement('tr');
resultDisplay.classList.add('guess');
for (let i = 0; i < this.game.numLetters; i++) {
let letter = document.createElement('td');
letter.innerText = guess[i];
letter.classList.add(getLetterGuessResultClass(results.letters[i]));
resultDisplay.appendChild(letter);
}
let link = document.createElement('a');
link.classList.add('dictionary_link');
link.href = 'https://en.wiktionary.org/wiki/' + guess;
link.target = '_blank';
link.innerText = '📖';
let linkCell = document.createElement('td');
linkCell.classList.add('dictionary_link');
linkCell.appendChild(link);
resultDisplay.appendChild(linkCell);
let guesses = document.getElementById('guesses');
guesses.appendChild(resultDisplay);
this.clearUnlocked();
this.recolorAvailableLetters();
if (results.correct) {
this.won = true;
document.getElementById('letters_entry').style.display = 'none';
document.getElementById('submit_buttons').style.display = 'none';
document.getElementById('endgame').style.display = '';
}
}
get defaultSettings() {
let default_settings = {
'min_word_length': 4,
'max_word_length': 7,
'min_matches': 1,
'max_matches': 50000,
'num_random_letters': 10,
};
return default_settings;
}
get settingsKey() {
return 'anagram-bagels-settings';
}
constructor(game) {
super();
let ui = this;
let difficulty = document.forms['difficulty'];
let min_word_length = difficulty.elements['min_word_length'];
let max_word_length = difficulty.elements['max_word_length'];
let min_matches = difficulty.elements['min_matches'];
let max_matches = difficulty.elements['max_matches'];
let num_random_letters = difficulty.elements['num_random_letters'];
min_word_length.addEventListener('change', _ => {
if (min_word_length.checkValidity() &&
Number(min_word_length.value) > Number(max_word_length.value)) {
max_word_length.value = min_word_length.value;
}
ui.saveSettings();
});
max_word_length.addEventListener('change', _ => {
if (max_word_length.checkValidity() &&
Number(min_word_length.value) > Number(max_word_length.value)) {
min_word_length.value = max_word_length.value;
}
ui.saveSettings();
});
min_matches.addEventListener('change', _ => {
if (min_matches.checkValidity() &&
Number(min_matches.value) > Number(max_matches.value)) {
max_matches.value = min_matches.value;
}
ui.saveSettings();
});
max_matches.addEventListener('change', _ => {
if (max_matches.checkValidity() &&
Number(min_matches.value) > Number(max_matches.value)) {
min_matches.value = max_matches.value;
}
ui.saveSettings();
});
num_random_letters.addEventListener('change', _ => {
ui.saveSettings();
});
this.nextGame(game);
}
get gameClass() {
return Bagels;
}
initialize(game) {
this.won = false;
this.game = game;
scrambleString(game.fragment, ['A', 'B']).then(fragment => {
document.getElementById('permalink').href = '#' + fragment;
document.getElementById('permalink_input').value
= window.location.href.split('#')[0] + '#' + fragment;
});
this.initializeInputs(game.numLetters);
clearElement(document.getElementById('guesses'));
this.doPostInitialize();
}
}
function loaded() {
dictionaryPromise.then(_ =>
topWordsPromise
.then(_ => {
let fragment = window.location.hash.substring(1);
return unscrambleString(fragment).then(unscrambled => {
history.pushState("", document.title, window.location.pathname + window.location.search);
return Bagels.fromFragment(unscrambled);
});
})
.then(game => new BagelsUI(game)));
}
</script>
<template id="letter_entry">
<td class="letter_entry">
<input type="checkbox" class="lock" />
<div class="lock">
<label class="lock_label" />
</div>
<div class="letter">
<span class="letter">&nbsp;</span>
</div>
<div class="focus_indicator"></div>
</td>
</template>
</head>
<body onload="loaded();">
<label id="settings_toggle_label" for="settings_toggle"></label>
<input type="checkbox" id="settings_toggle"></input>
<div id="settings">
<h1>Settings</h1>
<a href="#" target="_blank" id="permalink">Permalink</a>
to current puzzle:
<input contenteditable id="permalink_input" />
<button id="copy_permalink">Copy</button><br />
<select id="available_games"></select>
<button id="switch_game">Switch Game</button>
<h2>Difficulty</h2>
<form id="difficulty" onsubmit="return false;">
Word length:
<input type="number" name="min_word_length" placeholder="min"
step="1" min="2" max="19" required
/>-<input type="number" name="max_word_length" placeholder="max"
step="1" min="2" max="19" required
/><br />
# of matches in top 50k words:
<input type="number" name="min_matches" placeholder="min"
step="1" min="1" max="50000" required
/>-<input type="number" name="max_matches" placeholder="max"
step="1" min="1" max="50000" required
/><br />
# of extra random letters:
<input type="number" name="num_random_letters"
step="1" min="0" max="24" required /><br />
</form>
<button id="newgame_button">Generate new game</button>
<h1>How to Play</h1>
<h2>Gameplay</h2>
<p>
Anagram Bagels is a variant of the classic game Bagels (also
known as <a href="https://en.wikipedia.org/wiki/Bulls_and_Cows"
>Bulls and Cows</a> or
<a href="https://en.wikipedia.org/wiki/Mastermind_(board_game)"
>Mastermind</a>) where the secret clue and guesses must all be
English words. The computer randomly selects a word and a set
of letters that may be used in guesses and tells you how many
letters are in the word. In response to each guess, the computer
responds with whether each letter was in the correct place, a
correct letter but in the wrong place, or an incorrect letter.
For example, if the secret word was <q>swept</q> and you guessed
<q>trees</q>, the computer would respond as follows:
<table class="guesses">
<tr class="guess">
<td class="elsewhere">t</td>
<td class="unused">r</td>
<td class="match">e</td>
<td class="unused">e</td>
<td class="elsewhere">s</td>
</tr>
</table>
This indicates that the middle <q>e</q> is the correct letter in the
correct position, the secret word has a <q>t</q> and <q>s</q> in it,
but not in those positions, and that the secret word does not have
an <q>r</q> or another <q>e</q>.
</p>
<h2>Interface</h2>
<p>
You can enter letters in any order; the current letter being edited
is highlighted with a yellow border. Clicking/tapping on the position
for another letter will select it. In order to aid in entering
letters out of order, letters may be frozen by clicking the
sun (&#x1f31e;) above the entry box which will toggle it to a
snowflake (&#x2744;) to indicate that letter is frozen. If no letter
had been entered in that position but a previous guess had revealed
the correct letter for that position, it will be copied in
automatically.
</p>
<p>
The buttons for entering letters will change color to convey the
knowledge from the computer's responses. The colors will match
those of the responses, and, additionally, will be slightly grayed
out variants of those colors if the next letter to be entered is
a position that is definitely not that letter (e.g. if another
letter is already known match there).
</p>
</div>
<table id="guesses_and_entry">
<tbody id="guesses" class="guesses"></tbody>
<tr id="letters_entry"></tr>
</table>
<div id="submit_buttons">
<button id="clear">Clear All</button>
<button id="clear_nonlocked">Clear Non-Frozen</button>
<button id="submit">Submit Word</button>
<label>
<input type="checkbox" id="auto_submit" checked />
Auto-Submit
</label>
</div>
<div id="endgame">
<button id="newgame">Next Game</button>
</div>
<div id="available_letters_display">
<button id="shuffle"></button>
<div id="available_letters"></div>
</div>
</body>
</html>