アプリ概要
テーマに関連する英単語を10問タイピング
プロンプト
## 依頼
入力されたテーマに基づく英単語のリストを出力してください。
## 役割
あなたは英単語に詳しい先生です。
## ルール
- 英単語は**カンマ区切り**で出力してください。
- 英単語は**100個以上**出力してください。
- 空白や記号が含まれる単語は除いてください。
- 英単語は重複させないでください。
- 不快な単語は出力しないでください。
入力ソース
<style>
#title {
font-size: 2rem;
padding-bottom: 10px;
}
#inputArea {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100svh;
text-align: center;
}
#theme {
font-size: 1.2em;
padding: 10px;
width: 300px;
margin-bottom: 20px;
border: 2px solid #ccc;
border-radius: 5px;
outline: none;
transition: border-color 0.3s;
}
#theme:focus {
border-color: #007BFF;
}
#error {
color: red;
}
#startButton {
font-size: 1.2em;
padding: 10px 20px;
border: none;
border-radius: 5px;
background-color: #007BFF;
color: #fff;
cursor: pointer;
transition: background-color 0.3s;
}
#startButton:hover {
background-color: #0056b3;
}
#startButton:disabled {
background-color: #cccccc;
cursor: not-allowed;
}
#gameArea {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100svh;
text-align: center;
}
#word {
font-size: 3em;
margin-bottom: 20px;
}
#typedWord {
font-size: 1.5em;
padding: 10px;
width: 300px;
border-bottom: 2px solid #000;
text-align: center;
height: 2.1em;
}
.info {
display: flex;
justify-content: center;
margin-top: 20px;
font-size: 1.1em;
}
.info > div {
margin: 0 10px;
min-width: 3em;
}
</style>
<div id="inputArea">
<h1 id="title">Typing Game</h1>
<input type="text" id="theme" maxlength="30">
<button id="startButton">Start</button>
<div id="error"></div>
</div>
<div id="gameArea" style="display: none;">
<div id="word"></div>
<div id="typedWord"></div>
<div class="info">
<div id="correctWords"></div>
<div id="mistakes"></div>
<div id="accuracy"></div>
<div id="time"></div>
</div>
<div class="info">
<div id="score"></div>
<div id="cpm"></div>
</div>
</div>
<script>
const GENERATED_QUESTION_COUNT = 50;
const QUESTION_COUNT = 10;
const MAX_INPUT_LENGTH = 30;
const getElementById = (id) => {
const element = document.getElementById(id);
if (element == null) {
throw new Error(`Element with id ${id} not found.`);
}
return element;
};
const getWords = async (input) => {
if (input == null || input.trim().length === 0 || input.length > MAX_INPUT_LENGTH) {
return [];
}
try
{
const serverAi = new ServerAI();
const response = await serverAi.getAnswerText('6710797e8f7cb9905ce6eea3', '', input);
const words = response.split(',')
.map(x => x.trim())
.filter(x => /^[a-zA-Z]+$/.test(x.trim()));
return [... new Set(words)];
} catch (error) {
return [];
}
};
const shuffleArray = (array) => {
for (let i = array.length - 1; i >= 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
};
document.addEventListener('DOMContentLoaded', (event) => {
const elements = {
input: {
inputArea: getElementById('inputArea'),
startButton: getElementById('startButton'),
theme: getElementById('theme'),
error: getElementById('error'),
},
game: {
gameArea: getElementById('gameArea'),
word: getElementById('word'),
typedWord: getElementById('typedWord'),
correctWords: getElementById('correctWords'),
mistakes: getElementById('mistakes'),
accuracy: getElementById('accuracy'),
time: getElementById('time'),
score: getElementById('score'),
cpm: getElementById('cpm'),
},
};
const gameState = {
words: [],
wordIndex: 0,
typedWord: '',
key: '',
startTime: 0,
mistakeTime: 0,
gameCleared: false,
};
const gameStats = {
correctWords: 0,
mistakes: 0,
accuracy: 0,
time: 0,
score: 0,
cpm: 0,
};
const setErrorText = (text) => {
elements.input.error.textContent = text;
};
const showGameScreen = () => {
elements.input.inputArea.style.display = 'none';
elements.game.gameArea.style.display = 'flex';
};
const showThemeScreen = () => {
elements.input.inputArea.style.display = 'flex';
elements.game.gameArea.style.display = 'none';
elements.input.theme.focus();
};
const startGame = async (theme) => {
elements.input.startButton.disabled = true;
gameState.words = await getWords(theme);
elements.input.startButton.disabled = false;
if (gameState.words.length < GENERATED_QUESTION_COUNT) {
setErrorText('Failed to get words. Please try again.');
return;
}
shuffleArray(gameState.words);
gameState.words.length = QUESTION_COUNT;
setErrorText('');
resetGame();
showGameScreen();
requestAnimationFrame(gameLoop);
};
const resetGame = () => {
gameState.wordIndex = 0;
gameState.typedWord = '';
gameState.key = '';
gameState.startTime = performance.now();
gameState.mistakeTime = 0;
gameState.gameCleared = false;
gameStats.correctWords = 0;
gameStats.mistakes = 0;
gameStats.accuracy = 0;
gameStats.time = 0;
gameStats.score = 0;
gameStats.cpm = 0;
stateHasChanged();
};
const gameLoop = () => {
if (gameState.gameCleared && gameState.key === 'Enter') {
showThemeScreen();
return;
}
if (gameState.key === 'Escape') {
showThemeScreen();
return;
}
update();
stateHasChanged();
requestAnimationFrame(gameLoop);
};
const update = () => {
if (gameState.gameCleared) {
return;
}
if (gameState.key.length === 1) {
const currentWord = gameState.words[gameState.wordIndex].toLowerCase();
const typedWord = currentWord[gameState.typedWord.length];
if (currentWord[gameState.typedWord.length].toLowerCase() !== gameState.key.toLowerCase()) {
updateMistakes();
}
else {
updateCorrectWords();
if (gameState.typedWord === currentWord) {
updateWord();
}
}
}
if (performance.now() > gameState.mistakeTime + 300) {
gameState.mistakeTime = 0;
}
updateTime();
updateAccuracy();
updateCPM();
updateScore();
};
const updateWord = () => {
gameState.wordIndex++;
gameState.typedWord = '';
if (gameState.wordIndex >= gameState.words.length) {
gameState.gameCleared = true;
return;
}
};
const updateCorrectWords = () => {
gameStats.correctWords++;
gameState.typedWord += gameState.key.toLowerCase();
gameState.key = '';
};
const updateMistakes = () => {
gameStats.mistakes++;
gameState.key = '';
gameState.mistakeTime = performance.now();
};
const updateAccuracy = () => {
const totalCount = gameStats.correctWords + gameStats.mistakes;
gameStats.accuracy = totalCount == 0 ? 0 : (gameStats.correctWords / totalCount) * 100;
};
const updateTime = () => {
gameStats.time = (performance.now() - gameState.startTime) / 1000;
};
const updateScore = () => {
gameStats.score = gameStats.cpm * ((gameStats.accuracy / 100) ** 3);
};
const updateCPM = () => {
const minutes = gameStats.time / 60;
gameStats.cpm = minutes === 0 ? 0 : gameStats.correctWords / minutes;
};
const stateHasChanged = () => {
if (gameState.gameCleared) {
elements.game.word.textContent = 'Game Clear!';
elements.game.typedWord.textContent = 'Press Enter to return';
}
else {
elements.game.word.textContent = gameState.words[gameState.wordIndex];
elements.game.typedWord.textContent = gameState.typedWord;
}
elements.game.correctWords.textContent = `Correct: ${gameStats.correctWords}`;
elements.game.mistakes.textContent = `Mistakes: ${gameStats.mistakes}`;
elements.game.accuracy.textContent = `Accuracy: ${gameStats.accuracy.toFixed(2)}%`;
elements.game.time.textContent = `Time: ${gameStats.time.toFixed(1)}s`;
elements.game.score.textContent = `Score: ${gameStats.score.toFixed(0)}`;
elements.game.cpm.textContent = `CPM: ${gameStats.cpm.toFixed(0)}`;
if (gameState.mistakeTime === 0 || gameState.gameCleared) {
elements.game.typedWord.style.color = 'black';
elements.game.typedWord.style.borderBottomColor = 'black';
}
else {
elements.game.typedWord.style.color = 'red';
elements.game.typedWord.style.borderBottomColor = 'red';
}
};
elements.game.gameArea.addEventListener('click', () => {
if (gameState.gameCleared) {
showThemeScreen();
}
});
elements.input.theme.addEventListener('keydown', (event) => {
if (event.key === 'Enter') {
elements.input.startButton.click();
}
});
elements.input.startButton.addEventListener('click', () => {
const theme = elements.input.theme.value;
startGame(theme);
});
document.addEventListener('keydown', (event) => {
if (elements.game.gameArea.style.display !== 'flex') {
return;
}
gameState.key = event.key;
});
showThemeScreen();
});
</script>