<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<title>マイ電卓&ゲーム (表示画像化版)</title>
<style>
/* 基本設定 */
* {
box-sizing: border-box;
touch-action: manipulation;
-webkit-tap-highlight-color: transparent;
}
body {
margin: 0;
padding: 0;
height: 100dvh;
width: 100vw;
display: flex;
flex-direction: column;
background-color: #000;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
overflow: hidden;
}
/* 画面上部エリア(表示部を大きく確保) */
#display-container {
flex: 1;
display: flex;
flex-direction: column;
justify-content: flex-end; /* 下揃え */
padding: 10px 20px;
overflow: hidden;
}
/* 履歴表示エリア */
#history {
width: 100%;
height: 20%; /* 履歴エリアもある程度確保 */
overflow-y: auto;
display: flex;
flex-direction: column;
justify-content: flex-end;
align-items: flex-end;
color: #888;
font-size: 1.0rem;
line-height: 1.4;
padding-bottom: 5px;
margin-bottom: 5px;
border-bottom: 1px solid #333;
}
.history-item {
font-family: monospace;
display: flex;
align-items: center;
gap: 10px;
width: 100%;
justify-content: flex-end;
}
.m-tag {
color: #FFD60A;
font-weight: bold;
display: inline-block;
min-width: 30px;
text-align: right;
}
/* メイン数値表示部分 */
#display {
width: 100%;
/* テキストの場合のフォントサイズ(画像がない文字用) */
color: white;
font-size: 4.5rem;
font-weight: 300;
text-align: right;
word-break: break-all;
line-height: 1.2;
/* 表示領域を大きく使う */
flex: 1;
overflow-y: auto;
display: flex;
flex-wrap: wrap;
justify-content: flex-end; /* 右寄せ */
align-content: flex-end; /* 下寄せ */
align-items: center;
}
/* 数字・記号画像のスタイル */
.disp-img {
height: 4.5rem; /* フォントサイズに合わせる */
width: auto;
margin: 0 2px;
vertical-align: middle;
}
/* === ボタンエリア(高さを75%程度に縮小 -> 全体の40%程度にする) === */
.buttons {
height: 40%; /* 以前の55%から縮小 */
padding-bottom: env(safe-area-inset-bottom);
margin-bottom: 10px;
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(5, 1fr);
gap: 8px;
padding-left: 15px;
padding-right: 15px;
}
button {
border: none;
font-size: 1.5rem; /* ボタン文字も少し小さく調整 */
border-radius: 1000px;
cursor: pointer;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
transition: background-color 0.3s ease;
height: 100%;
}
button:active {
filter: brightness(1.2);
transform: scale(0.98);
transition: none;
}
.number { background-color: #333333; }
.func { background-color: #a5a5a5; color: black; font-weight: 500; }
/* 2nd/GAME ボタン */
.shift-btn {
background-color: #a5a5a5;
color: black;
font-size: 1.2rem;
font-weight: bold;
}
.game-ready-btn {
background-color: #FFD60A !important;
color: black !important;
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
.m-key {
background-color: #FFD60A;
color: black;
font-weight: bold;
}
.replay-active {
font-size: 1.1rem !important;
background-color: #30d158 !important;
color: white !important;
animation: pulse 2s infinite;
}
.m-clear-active {
font-size: 0.9rem !important;
font-weight: bold;
}
.mode-active-green {
background-color: #30d158 !important;
color: white !important;
font-weight: bold;
}
.shift-active {
background-color: #fff !important;
color: #000 !important;
}
.game-dimmed {
opacity: 0.2;
pointer-events: none;
}
.dimmed {
opacity: 0.3;
pointer-events: none;
}
/* === 右列グラデーション設定 === */
#btn-div { background-color: #FF3B30; color: white; }
#btn-mul { background-color: #FF2D55; color: white; }
#btn-sub { background-color: #E040D0; color: white; }
#btn-add { background-color: #C644FC; color: white; }
/* 2ndモード */
.style-base { background-color: #007AFF !important; color: white !important; font-size: 1.2rem; font-weight: bold; }
.style-time { background-color: #4054EB !important; color: white !important; font-size: 1.2rem; font-weight: bold; }
.style-paren-open { background-color: #6848F3 !important; color: white !important; font-weight: bold; }
.style-paren-close { background-color: #8E44FC !important; color: white !important; font-weight: bold; }
#btn-equal {
background-color: #AF52DE;
color: white;
}
#btn-dot { background-color: #333333; color: white; }
/* ゲーム用スタイル調整 */
.game-container {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: flex-end;
}
.game-info-row {
display: flex;
justify-content: space-between;
width: 100%;
margin-bottom: 10px;
}
.game-stage-text { font-size: 1.5rem; color: #FFD60A; font-weight: bold; }
.game-timer-text { font-size: 1.5rem; color: white; font-weight: bold; }
.timer-warning { color: #FF3B30 !important; }
.game-main-row {
display: flex;
justify-content: flex-end; /* 右寄せ */
align-items: center;
width: 100%;
flex-wrap: wrap;
gap: 10px;
}
.game-problem-container {
display: flex;
align-items: center;
}
.game-input-container {
border-bottom: 2px solid #FFD60A;
display: flex;
align-items: center;
min-width: 50px;
justify-content: flex-end;
margin-left: 20px;
}
/* Result画面 */
.game-result-title { font-size: 4rem; font-weight: bold; color: white; }
.game-result-sub { font-size: 2rem; color: #FFD60A; }
.game-best-time { font-size: 1.5rem; color: #888; }
.new-record { color: #FF3B30 !important; font-weight: bold; animation: flash 1s infinite; }
/* Scrollbar消去 */
#display::-webkit-scrollbar, #history::-webkit-scrollbar { display: none; }
</style>
</head>
<body>
<div id="display-container">
<div id="history"></div>
<div id="display">
<img src="./pic4/0.png" class="disp-img" alt="0">
</div>
</div>
<div class="buttons" id="keypad">
<button class="func" id="btn-clear" onclick="handleClear()">C</button>
<button class="func" onclick="deleteLast()">←</button>
<button class="shift-btn" id="btn-shift" onclick="toggleShiftMode()">2nd</button>
<button class="operator" id="btn-div" onclick="appendDisplay('/')">÷</button>
<button class="number" onclick="appendDisplay('7')">7</button>
<button class="number" onclick="appendDisplay('8')">8</button>
<button class="number" onclick="appendDisplay('9')">9</button>
<button class="operator" id="btn-mul" onclick="appendDisplay('*')">×</button>
<button class="number" onclick="appendDisplay('4')">4</button>
<button class="number" onclick="appendDisplay('5')">5</button>
<button class="number" onclick="appendDisplay('6')">6</button>
<button class="operator" id="btn-sub" onclick="appendDisplay('-')">−</button>
<button class="number" onclick="appendDisplay('1')">1</button>
<button class="number" onclick="appendDisplay('2')">2</button>
<button class="number" onclick="appendDisplay('3')">3</button>
<button class="operator" id="btn-add" onclick="appendDisplay('+')">+</button>
<button class="m-key" id="btn-m" onclick="toggleMemoryMode()">M</button>
<button class="number" onclick="appendDisplay('0')">0</button>
<button class="number" id="btn-dot" onclick="appendDisplay('.')">.</button>
<button class="operator" id="btn-equal" onclick="handleEqual()">=</button>
</div>
<script>
const display = document.getElementById('display');
const historyDiv = document.getElementById('history');
const keypad = document.getElementById('keypad');
const btnShift = document.getElementById('btn-shift');
const btnDiv = document.getElementById('btn-div');
const btnMul = document.getElementById('btn-mul');
const btnSub = document.getElementById('btn-sub');
const btnAdd = document.getElementById('btn-add');
const btnDot = document.getElementById('btn-dot');
const btnEqual = document.getElementById('btn-equal');
const btnClear = document.getElementById('btn-clear');
const btnM = document.getElementById('btn-m');
const operators = ['/', '*', '-', '+'];
const unitNames = ["秒", "分", "時", "日", "週", "月", "年"];
// 画面表示用の内部変数(これまでinnerTextから取得していたものを変数管理へ)
let currentDisplayString = "0";
let timeState = 0;
let historyList = [];
const STORAGE_KEY = 'my_calculator_history_v1';
const STORAGE_KEY_BEST = 'my_calculator_best_time_v1';
let isMemoryMode = false;
let isShiftMode = false;
let memoryInput = "";
// 進数変換用
let conversionState = 0;
let conversionTimer = null;
let tempDecimalValue = null;
// === ゲームモード用 ===
let isGameMode = false;
let gameStage = 1;
const TOTAL_STAGES = 20;
const TIME_LIMIT_PER_QUESTION = 10.0;
let gameProblemText = "";
let gameAnswer = 0;
let gameStartTime = 0;
let gameUserInput = "";
let finalTimeElapsed = 0;
let questionTimerInterval = null;
let questionTimeLeft = 0;
let resultScreenState = 0;
let lastWrongInput = "";
let isNewRecord = false;
// 初期化
window.addEventListener('DOMContentLoaded', () => {
const savedData = localStorage.getItem(STORAGE_KEY);
if (savedData) {
try {
historyList = JSON.parse(savedData);
renderHistory();
} catch (e) {
historyList = [];
}
}
// 初期表示
renderDisplay();
});
// 画像マッピング
const imageMap = {
'0': './pic4/0.png',
'1': './pic4/1.png',
'2': './pic4/2.png',
'3': './pic4/3.png',
'4': './pic4/4.png',
'5': './pic4/5.png',
'6': './pic4/6.png',
'7': './pic4/7.png',
'8': './pic4/8.png',
'9': './pic4/9.png',
'+': './pic4/p.png',
'-': './pic4/m.png',
'*': './pic4/t.png', // 表示用。論理演算子は * だが、表示ロジックで変換する
'/': './pic4/d.png' // 同上
};
// 文字列をHTML(画像タグ列)に変換して表示する関数
function renderDisplay() {
// 表示文字列が空の場合は0にする(念のため)
if (currentDisplayString === "") currentDisplayString = "0";
let html = "";
const str = currentDisplayString;
// 特殊な状態(ERROR, TIME UPなど)は画像化できないのでそのままテキスト表示
if (str === "ERROR" || str === "TIME UP" || str === "GAME OVER" || str === "CLEARED" || str === "FINISHED") {
display.innerHTML = str;
return;
}
// 一文字ずつ解析
for (let char of str) {
// 乗算・除算記号の対応(内部データが * / の場合)
let imgKey = char;
if (imageMap[imgKey]) {
html += `<img src="${imageMap[imgKey]}" class="disp-img" alt="${char}">`;
} else {
// 画像がない文字(ドットなど)はそのままテキスト表示
// 見やすくするためにspanで囲む
html += `<span style="font-size:inherit;">${char}</span>`;
}
}
display.innerHTML = html;
}
// 文字列を画像化するヘルパー関数(ゲーム表示用などで使う)
function stringToImages(text) {
let html = "";
// 表示用に変換(×÷など)
let dispText = text.replace(/\*/g, '*').replace(/\//g, '/').replace(/×/g, '*').replace(/÷/g, '/');
// ※ generateProblemで '×' を使っているので、それを imageMapのキーである '*' に読み替える
for (let char of dispText) {
if (imageMap[char]) {
html += `<img src="${imageMap[char]}" class="disp-img" alt="${char}">`;
} else {
html += `<span style="font-size:inherit;">${char}</span>`;
}
}
return html;
}
// 値セット・取得のラップ関数
function getDisplayValue() {
return currentDisplayString;
}
function setDisplayValue(val) {
currentDisplayString = String(val);
renderDisplay();
}
function saveHistory() {
localStorage.setItem(STORAGE_KEY, JSON.stringify(historyList));
}
function getBestTime() {
const t = localStorage.getItem(STORAGE_KEY_BEST);
return t ? parseFloat(t) : null;
}
function saveBestTime(time) {
if (time === null) {
localStorage.removeItem(STORAGE_KEY_BEST);
} else {
localStorage.setItem(STORAGE_KEY_BEST, time);
}
}
function renderHistory() {
historyDiv.innerHTML = "";
historyList.forEach((item, index) => {
const div = document.createElement('div');
div.className = 'history-item';
const mNumber = index + 1;
div.innerHTML = `<span>${item.text} = ${item.result}</span> <span class="m-tag">M${mNumber}</span>`;
historyDiv.appendChild(div);
});
historyDiv.scrollTop = historyDiv.scrollHeight;
}
// === メイン入力ハンドラ ===
function appendDisplay(input) {
// ゲームモード中の入力
if (isGameMode) {
if (gameUserInput === "FINISHED") return;
if (operators.includes(input) || input === '.' || input === '(' || input === ')') {
return;
}
gameUserInput += input;
updateGameDisplay();
return;
}
if (currentDisplayString === "ERROR") {
setDisplayValue("0");
}
if (isMemoryMode) {
if (!operators.includes(input) && input !== '.' && input !== '(' && input !== ')') {
memoryInput += input;
updateMemoryButtons();
}
return;
}
const currentVal = getDisplayValue();
const lastChar = currentVal.slice(-1);
if (currentVal === '0' && !operators.includes(input) && input !== '.') {
setDisplayValue(input);
return;
}
if (operators.includes(input) && operators.includes(lastChar)) {
return;
}
setDisplayValue(currentVal + input);
}
// === 2nd(切替)モード ===
function toggleShiftMode() {
if (isGameMode) return;
if (isMemoryMode) {
startGame();
return;
}
if (conversionState !== 0) {
resetConversion();
return;
}
isShiftMode = !isShiftMode;
if (isShiftMode) {
btnShift.classList.add("shift-active");
btnDiv.className = "operator style-base";
btnMul.className = "operator style-time";
btnSub.className = "operator style-paren-open";
btnAdd.className = "operator style-paren-close";
btnDiv.innerText = "進数";
btnDiv.onclick = handleBaseConversion;
btnMul.innerText = unitNames[timeState];
btnMul.onclick = cycleTime;
btnSub.innerText = "(";
btnSub.onclick = () => {
appendDisplay('(');
toggleShiftMode();
};
btnAdd.innerText = ")";
btnAdd.onclick = () => {
appendDisplay(')');
toggleShiftMode();
};
} else {
btnShift.classList.remove("shift-active");
btnDiv.className = "operator";
btnMul.className = "operator";
btnSub.className = "operator";
btnAdd.className = "operator";
btnDiv.innerText = "÷";
btnDiv.onclick = () => appendDisplay('/');
btnMul.innerText = "×";
btnMul.onclick = () => appendDisplay('*');
btnSub.innerText = "−";
btnSub.onclick = () => appendDisplay('-');
btnAdd.innerText = "+";
btnAdd.onclick = () => appendDisplay('+');
}
}
// ==========================================
// GAME MODE LOGIC
// ==========================================
function startGame() {
isGameMode = true;
gameStage = 1;
gameStartTime = Date.now();
finalTimeElapsed = 0;
resultScreenState = 0;
lastWrongInput = "";
isNewRecord = false;
endMemoryMode();
if(isShiftMode) toggleShiftMode();
btnClear.innerText = "Exit";
btnM.innerText = "M";
btnM.classList.remove("replay-active");
restoreNormalButtons();
resetAllButtonStates();
disableGameIrrelevantButtons(true);
generateProblem();
}
function restoreNormalButtons() {
btnDiv.innerText = "÷";
btnDiv.onclick = () => appendDisplay('/');
btnDiv.classList.remove("m-clear-active");
}
function resetAllButtonStates() {
const allButtons = keypad.querySelectorAll('button');
allButtons.forEach(btn => {
btn.classList.remove('game-dimmed');
btn.classList.remove('dimmed');
});
}
function disableGameIrrelevantButtons(disable) {
const allButtons = keypad.querySelectorAll('button');
allButtons.forEach(btn => {
const isNumber = btn.classList.contains('number');
const isClear = (btn.id === 'btn-clear');
const isBack = (btn.innerText === '←');
const isEqual = (btn.id === 'btn-equal');
if (!isNumber && !isClear && !isBack && !isEqual) {
if (disable) btn.classList.add('game-dimmed');
else btn.classList.remove('game-dimmed');
}
});
if (disable) btnDot.classList.add('game-dimmed');
else btnDot.classList.remove('game-dimmed');
}
function setGameFinishedButtonState() {
const allButtons = keypad.querySelectorAll('button');
allButtons.forEach(btn => {
if (btn.id === 'btn-m' || btn.id === 'btn-clear' || btn.id === 'btn-equal' || btn.id === 'btn-div') {
btn.classList.remove('game-dimmed');
} else {
btn.classList.add('game-dimmed');
}
});
}
function handleBestTimeClear() {
if (confirm("BEST TIME CLEAR OK?")) {
saveBestTime(null);
alert("CLEARED");
updateGameDisplay();
}
}
function generateProblem() {
stopQuestionTimer();
let num1, num2, num3, op1, op2;
let exprText = "";
let result = 0;
let safety = 0;
while (true) {
safety++;
if (safety > 100) break;
if (gameStage <= 4) {
num1 = rand(1, 15);
num2 = rand(1, 15);
result = num1 + num2;
exprText = `${num1}+${num2}`;
}
else if (gameStage <= 8) {
num1 = rand(1, 20);
num2 = rand(1, 20);
if (num1 < num2) [num1, num2] = [num2, num1];
result = num1 - num2;
exprText = `${num1}-${num2}`;
}
else if (gameStage <= 12) {
num1 = rand(2, 9);
num2 = rand(2, 9);
result = num1 * num2;
exprText = `${num1}×${num2}`;
}
else if (gameStage <= 16) {
num1 = rand(1, 15);
num2 = rand(1, 15);
num3 = rand(1, 15);
op1 = Math.random() < 0.5 ? '+' : '-';
op2 = Math.random() < 0.5 ? '+' : '-';
let temp = num1;
if (op1 === '+') temp += num2; else temp -= num2;
if (op2 === '+') temp += num3; else temp -= num3;
if (temp < 0) continue;
result = temp;
exprText = `${num1}${op1}${num2}${op2}${num3}`;
}
else {
num1 = rand(2, 9);
num2 = rand(2, 9);
num3 = rand(2, 9);
const ops = ['+', '-', '*'];
op1 = ops[Math.floor(Math.random() * 3)];
op2 = ops[Math.floor(Math.random() * 3)];
let logicOp1 = op1 === '*' ? '*' : op1;
let logicOp2 = op2 === '*' ? '*' : op2;
let dispOp1 = op1 === '*' ? '×' : op1;
let dispOp2 = op2 === '*' ? '×' : op2;
let logic = `${num1}${logicOp1}${num2}${logicOp2}${num3}`;
let temp = eval(logic);
if (temp < 0 || !Number.isInteger(temp)) continue;
result = temp;
exprText = `${num1}${dispOp1}${num2}${dispOp2}${num3}`;
}
break;
}
gameProblemText = exprText;
gameAnswer = result;
gameUserInput = "";
startQuestionTimer();
updateGameDisplay();
}
function rand(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function startQuestionTimer() {
questionTimeLeft = TIME_LIMIT_PER_QUESTION;
questionTimerInterval = setInterval(() => {
questionTimeLeft -= 0.1;
if (questionTimeLeft <= 0) {
questionTimeLeft = 0;
gameOver("TIME UP");
return;
}
updateGameDisplay();
}, 100);
}
function stopQuestionTimer() {
if (questionTimerInterval) {
clearInterval(questionTimerInterval);
questionTimerInterval = null;
}
}
function updateGameDisplay() {
// ゲーム終了後
if (isGameMode && gameUserInput === "FINISHED") {
// State 1: レビュー画面
if (resultScreenState === 1) {
display.innerHTML = `
<div class="review-container" style="text-align:right;">
<div class="review-problem" style="color:white; font-size:1.5rem;">${gameProblemText}</div>
<div class="review-correct" style="color:#007AFF; font-weight:bold; font-size:1.5rem;">Ans: ${gameAnswer}</div>
<div class="review-wrong" style="color:#FF3B30; font-weight:bold; font-size:1.5rem;">You: ${lastWrongInput}</div>
</div>
`;
return;
}
// State 2: ベストタイム画面
if (resultScreenState === 2) {
const best = getBestTime();
const bestText = best !== null ? `${best} 秒` : "NO DATA";
display.innerHTML = `
<div class="best-time-display-container" style="text-align:right;">
<div class="best-time-label" style="color:#FFD60A; font-size:1.5rem;">BEST TIME</div>
<div class="best-time-value" style="color:white; font-size:3rem;">${bestText}</div>
</div>
`;
return;
}
// State 0: 結果発表
const resultTitle = (lastWrongInput === "CLEARED") ? "CLEAR" : (lastWrongInput === "TIME UP" ? "TIME UP" : "GAME OVER");
const colorStyle = (resultTitle === "CLEAR") ? "" : "color: #FF3B30;";
let subText = "";
let bestTimeHTML = "";
if (resultTitle === "CLEAR") {
subText = `${finalTimeElapsed} 秒`;
const currentBest = getBestTime();
if (isNewRecord) {
bestTimeHTML = `<div class="new-record" style="font-size:1.2rem; color:#FF3B30;">NEW RECORD!</div>`;
}
} else {
subText = `STAGE ${gameStage}`;
}
display.innerHTML = `
<div style="text-align: right;">
<div class="game-result-title" style="${colorStyle} font-size:3rem;">${resultTitle}</div>
<div class="game-result-sub" style="font-size:1.5rem;">${subText}</div>
${bestTimeHTML}
</div>`;
return;
}
// ■ 通常プレイ画面(画像化適用)
const timeStr = Math.max(0, questionTimeLeft).toFixed(1);
const timerClass = questionTimeLeft < 3.0 ? "timer-warning" : "";
// 問題文と入力を画像化
const problemImages = stringToImages(gameProblemText);
const inputImages = stringToImages(gameUserInput);
display.innerHTML = `
<div class="game-container">
<div class="game-info-row">
<span class="game-stage-text">STAGE ${gameStage}</span>
<span class="game-timer-text ${timerClass}">${timeStr}</span>
</div>
<div class="game-main-row">
<div class="game-problem-container">
${problemImages}
</div>
<div class="game-input-container">
${inputImages}
</div>
</div>
</div>
`;
}
function checkGameAnswer() {
if (gameUserInput === "") return;
const userVal = parseInt(gameUserInput, 10);
if (userVal === gameAnswer) {
if (gameStage >= TOTAL_STAGES) {
gameClear();
} else {
gameStage++;
generateProblem();
}
} else {
gameOver("WRONG");
}
}
function prepareGameFinishedState() {
isGameMode = true;
gameUserInput = "FINISHED";
btnM.innerText = "REPLAY";
btnM.classList.add("replay-active");
btnDiv.innerText = "M・CLEAR";
btnDiv.onclick = handleBestTimeClear;
btnDiv.classList.add("m-clear-active");
setGameFinishedButtonState();
}
function gameOver(reason) {
stopQuestionTimer();
if (reason === "TIME UP") {
lastWrongInput = "TIME UP";
} else {
lastWrongInput = gameUserInput;
}
resultScreenState = 0;
isNewRecord = false;
prepareGameFinishedState();
updateGameDisplay();
}
function gameClear() {
stopQuestionTimer();
lastWrongInput = "CLEARED";
finalTimeElapsed = parseFloat(((Date.now() - gameStartTime) / 1000).toFixed(1));
const currentBest = getBestTime();
isNewRecord = false;
if (currentBest === null || finalTimeElapsed < currentBest) {
saveBestTime(finalTimeElapsed);
isNewRecord = true;
}
resultScreenState = 0;
prepareGameFinishedState();
updateGameDisplay();
}
function quitGame() {
stopQuestionTimer();
isGameMode = false;
disableGameIrrelevantButtons(false);
btnClear.innerText = "C";
btnM.innerText = "M";
btnM.classList.remove("replay-active");
btnShift.innerText = "2nd";
btnShift.classList.remove("game-ready-btn");
restoreNormalButtons();
resetAllButtonStates();
setDisplayValue("0");
}
// ==========================================
// EXISTING LOGIC
// ==========================================
function handleBaseConversion() {
if (!isShiftMode) return;
if (conversionTimer) {
clearTimeout(conversionTimer);
conversionTimer = null;
}
if (conversionState === 0) {
try {
let expression = getDisplayValue();
let result = new Function('return ' + expression)();
result = parseFloat(result.toPrecision(12));
if (!isFinite(result) || isNaN(result)) throw new Error();
tempDecimalValue = result;
let intVal = parseInt(result, 10);
// 16進数などは画像化できないのでテキストで表示
currentDisplayString = intVal.toString(16).toUpperCase();
renderDisplay();
conversionState = 1;
btnDiv.innerText = "16進";
disableAllButtonsExceptBase(true);
} catch (e) {
showError();
return;
}
} else if (conversionState === 1) {
let intVal = parseInt(tempDecimalValue, 10);
currentDisplayString = intVal.toString(2);
renderDisplay();
conversionState = 2;
btnDiv.innerText = "2進";
} else {
resetConversion();
return;
}
conversionTimer = setTimeout(() => {
resetConversion();
}, 1000);
}
function resetConversion() {
if (conversionTimer) {
clearTimeout(conversionTimer);
conversionTimer = null;
}
if (tempDecimalValue !== null) {
setDisplayValue(tempDecimalValue);
tempDecimalValue = null;
}
conversionState = 0;
disableAllButtonsExceptBase(false);
if (isShiftMode) {
toggleShiftMode();
}
}
function disableAllButtonsExceptBase(shouldDisable) {
const allButtons = keypad.querySelectorAll('button');
allButtons.forEach(btn => {
if (btn.id !== 'btn-div') {
if (shouldDisable) {
btn.classList.add('dimmed');
} else {
btn.classList.remove('dimmed');
}
}
});
}
function cycleTime() {
if (!isShiftMode) return;
if (isMemoryMode) return;
try {
let rawResult = eval(getDisplayValue());
let val = parseFloat(rawResult.toPrecision(12));
if (timeState === 0) val = val / 60;
else if (timeState === 1) val = val / 60;
else if (timeState === 2) val = val / 24;
else if (timeState === 3) val = val / 7;
else if (timeState === 4) val = val / (365.25 / 12 / 7);
else if (timeState === 5) val = val / 12;
else if (timeState === 6) val = val * 365.25 * 24 * 60 * 60;
timeState++;
if (timeState >= unitNames.length) timeState = 0;
setDisplayValue(val);
btnMul.innerText = unitNames[timeState];
} catch (e) {
showError();
}
}
function toggleMemoryMode() {
if (isGameMode && gameUserInput === "FINISHED") {
startGame();
return;
}
if (isGameMode) return;
if (conversionState !== 0) resetConversion();
if (isShiftMode) toggleShiftMode();
if (isMemoryMode) {
endMemoryMode();
} else {
isMemoryMode = true;
memoryInput = "";
btnM.style.filter = "brightness(0.8)";
btnEqual.classList.add("mode-active-green");
btnClear.classList.add("mode-active-green");
btnShift.innerText = "GAME";
btnShift.classList.add("game-ready-btn");
updateMemoryButtons();
}
}
function updateMemoryButtons() {
if (memoryInput === "") {
btnEqual.innerText = "E";
btnClear.innerText = "C";
} else {
btnEqual.innerText = "E(" + memoryInput + ")";
btnClear.innerText = "C(" + memoryInput + ")";
}
}
function endMemoryMode() {
isMemoryMode = false;
memoryInput = "";
btnM.style.filter = "none";
btnEqual.innerText = "=";
btnEqual.classList.remove("mode-active-green");
btnClear.innerText = "C";
btnClear.classList.remove("mode-active-green");
btnShift.innerText = "2nd";
btnShift.classList.remove("game-ready-btn");
}
function handleClear() {
if (isGameMode) {
quitGame();
return;
}
if (conversionState !== 0) {
resetConversion();
return;
}
if (isMemoryMode) {
if (memoryInput === "") {
if (confirm("MEMORY ALL CLEAR OK?")) {
historyList = [];
saveHistory();
renderHistory();
endMemoryMode();
}
return;
}
if (memoryInput !== "") {
const targetIndex = parseInt(memoryInput) - 1;
if (targetIndex >= 0 && targetIndex < historyList.length) {
historyList.splice(targetIndex, 1);
saveHistory();
renderHistory();
endMemoryMode();
} else {
memoryInput = "";
updateMemoryButtons();
}
}
return;
}
setDisplayValue("0");
}
function handleEqual() {
if (isGameMode) {
if (gameUserInput === "FINISHED") {
resultScreenState = (resultScreenState + 1) % 3;
updateGameDisplay();
return;
}
checkGameAnswer();
return;
}
if (conversionState !== 0) {
resetConversion();
return;
}
if (isMemoryMode) {
if (memoryInput === "") {
if (historyList.length === 0) {
endMemoryMode();
return;
}
if (confirm("MEMORY ALL SUM OK?")) {
let totalSum = 0;
let expressionParts = [];
historyList.forEach(item => {
const val = Number(item.result);
totalSum += val;
expressionParts.push(val);
});
const sumExpression = expressionParts.join("+");
const lastHistory = historyList[historyList.length - 1];
if (!lastHistory || (lastHistory.text !== sumExpression || lastHistory.result != totalSum)) {
historyList.push({ text: sumExpression, result: totalSum });
saveHistory();
}
renderHistory();
endMemoryMode();
const currentVal = getDisplayValue();
if (currentVal === "0") {
setDisplayValue(totalSum);
} else {
setDisplayValue(currentVal + totalSum);
}
}
return;
}
const targetIndex = parseInt(memoryInput) - 1;
if (targetIndex >= 0 && targetIndex < historyList.length) {
endMemoryMode();
const val = historyList[targetIndex].result;
const currentVal = getDisplayValue();
if (currentVal === "0") {
setDisplayValue(val);
} else {
setDisplayValue(currentVal + val);
}
} else {
endMemoryMode();
}
return;
}
calculateResult();
}
function deleteLast() {
if (isGameMode) {
if (gameUserInput === "FINISHED") return;
gameUserInput = gameUserInput.slice(0, -1);
updateGameDisplay();
return;
}
if (conversionState !== 0) return;
if (isMemoryMode) {
memoryInput = memoryInput.slice(0, -1);
updateMemoryButtons();
return;
}
const currentVal = getDisplayValue();
if (currentVal === "ERROR") {
setDisplayValue("0");
return;
}
if (currentVal.length > 1) {
setDisplayValue(currentVal.slice(0, -1));
} else {
setDisplayValue("0");
}
}
function showError() {
setDisplayValue("ERROR");
setTimeout(() => {
if (getDisplayValue() === "ERROR") setDisplayValue("0");
}, 1500);
}
function validateExpression(expr) {
const openParens = (expr.match(/\(/g) || []).length;
const closeParens = (expr.match(/\)/g) || []).length;
if (openParens !== closeParens) return false;
if (/[0-9.]\s*\(/.test(expr)) return false;
if (/\)\s*[0-9.]/.test(expr)) return false;
if (/\(\s*\)/.test(expr)) return false;
return true;
}
function calculateResult() {
let expression = getDisplayValue();
if (!validateExpression(expression)) {
showError();
return;
}
try {
let result = new Function('return ' + expression)();
result = parseFloat(result.toPrecision(12));
if (!isFinite(result) || isNaN(result)) {
showError();
return;
}
const lastHistory = historyList[historyList.length - 1];
if (!lastHistory || (lastHistory.text !== expression || lastHistory.result != result)) {
historyList.push({ text: expression, result: result });
saveHistory();
renderHistory();
}
setDisplayValue(result);
} catch (error) {
showError();
}
}
</script>
</body>
</html>
使用変数
| * | |
| 2nd(切替)モード | |
| allButtons | |
| alt | |
| appendDisplay -------( Function ) | |
| best | |
| bestText | |
| bestTimeHTML | |
| btn | |
| btnAdd | |
| btnClear | |
| btnDiv | |
| btnDot | |
| btnEqual | |
| btnM | |
| btnMul | |
| btnShift | |
| btnSub | |
| calculateResult -------( Function ) | |
| charset | |
| checkGameAnswer -------( Function ) | |
| class | |
| className | |
| closeParens | |
| colorStyle | |
| content | |
| conversionState | |
| conversionTimer | |
| currentBest | |
| currentVal | |
| cycleTime -------( Function ) | |
| deleteLast -------( Function ) | |
| disableAllButtonsExceptBase -------( Function ) | |
| disableGameIrrelevantButtons -------( Function ) | |
| display | |
| dispOp1 | |
| dispOp2 | |
| dispText | |
| div | |
| endMemoryMode -------( Function ) | |
| expression | |
| expressionParts | |
| exprText | |
| filter | |
| finalTimeElapsed | |
| fit | |
| gameAnswer | |
| gameClear -------( Function ) | |
| gameOver -------( Function ) | |
| gameProblemText | |
| gameStage | |
| gameStartTime | |
| gameUserInput | |
| generateProblem -------( Function ) | |
| getBestTime -------( Function ) | |
| getDisplayValue -------( Function ) | |
| handleBaseConversion -------( Function ) | |
| handleBestTimeClear -------( Function ) | |
| handleClear -------( Function ) | |
| handleEqual -------( Function ) | |
| historyDiv | |
| historyList | |
| html | |
| id | |
| imageMap | |
| imgKey | |
| innerHTML | |
| innerText | |
| input | |
| inputImages | |
| intVal | |
| isBack | |
| isClear | |
| isEqual | |
| isGameMode | |
| isMemoryMode | |
| isNewRecord | |
| isNumber | |
| isShiftMode | |
| item | |
| keypad | |
| lang | |
| lastChar | |
| lastHistory | |
| lastWrongInput | |
| length | |
| LIMIT_PER_QUESTION | |
| logic | |
| logicOp1 | |
| logicOp2 | |
| memoryInput | |
| mNumber | |
| name | |
| num1 | |
| num2 | |
| num3 | |
| onclick | |
| op1 | |
| op2 | |
| openParens | |
| operators | |
| ops | |
| prepareGameFinishedState -------( Function ) | |
| problemImages | |
| questionTimeLeft | |
| quitGame -------( Function ) | |
| rand -------( Function ) | |
| rawResult | |
| reason | |
| renderDisplay -------( Function ) | |
| renderHistory -------( Function ) | |
| resetAllButtonStates -------( Function ) | |
| resetConversion -------( Function ) | |
| restoreNormalButtons -------( Function ) | |
| result | |
| resultScreenState | |
| resultTitle | |
| rrentDisplayString | |
| safety | |
| saveBestTime -------( Function ) | |
| savedData | |
| saveHistory -------( Function ) | |
| scalable | |
| scale | |
| scrollTop | |
| setDisplayValue -------( Function ) | |
| setGameFinishedButtonState -------( Function ) | |
| showError -------( Function ) | |
| src | |
| startGame -------( Function ) | |
| startQuestionTimer -------( Function ) | |
| stionTimerInterval | |
| stopQuestionTimer -------( Function ) | |
| STORAGE_KEY | |
| STORAGE_KEY_BEST | |
| str | |
| stringToImages -------( Function ) | |
| style | |
| subText | |
| sumExpression | |
| t | |
| targetIndex | |
| temp | |
| tempDecimalValue | |
| time | |
| timerClass | |
| timeState | |
| timeStr | |
| toggleMemoryMode -------( Function ) | |
| toggleShiftMode -------( Function ) | |
| totalSum | |
| TOTAL_STAGES | |
| unitNames | |
| updateGameDisplay -------( Function ) | |
| updateMemoryButtons -------( Function ) | |
| userVal | |
| val | |
| validateExpression -------( Function ) | |
| width | |
| ゲームモード用 | |
| メイン入力ハンドラ | |
| 全体の40%程度にする) | |
| 右列グラデーション設定 |