Created
April 13, 2024 05:13
-
-
Save kajiiiro/1d07a0d66966fef11809c89206718106 to your computer and use it in GitHub Desktop.
hanoi sample
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const term = require('terminal-kit').terminal; | |
const TOWER_LABEL_START = 5; // 塔のラベル開始位置 | |
const TOWER_SPACING = 15; // 塔間のスペース | |
const DISKS = [1, 2, 3]; | |
const TOWERS = ['A', 'B', 'C']; | |
const INITIALIZE_TOWER = first(TOWERS); | |
const GOAL_TOWER = last(TOWERS); | |
const MINIMUM_MOVES = calculateMinimumMoves(DISKS.length); | |
const QUIT_WORDS = ['q', 'quit', 'exit', 'end']; | |
/** | |
* 塔のデータ構造はスタック(FILO)とする | |
* そのためにはディスクの大きい順にデータが生成されれば、 | |
* ディスクの小さい順に取り出すことができる | |
*/ | |
let towerState = Object.fromEntries(TOWERS.map(tower => { | |
if (tower === INITIALIZE_TOWER) { | |
return [tower, [...DISKS.reverse()]]; | |
} | |
return [tower, []]; | |
})); | |
let currentMoves = 0; | |
function calculateMinimumMoves(disks) { | |
return Math.pow(2, disks) - 1; | |
} | |
function first(array) { | |
return array[0]; | |
} | |
function last(array) { | |
return array[array.length - 1]; | |
} | |
function isEmpty(array) { | |
return array.length === 0; | |
} | |
function notEmpty(array) { | |
return array.length !== 0; | |
} | |
function alert(message) { | |
term.bgBlack().red(`\n${message}`); | |
} | |
function information(message) { | |
term.bgBlack().green(`\n${message}`); | |
} | |
function warning(message) { | |
term.bgBlack().yellow(`\n${message}`); | |
} | |
function wait(callback) { | |
setTimeout(callback, 700); | |
} | |
function quit() { | |
warning('プログラムを終了します。'); | |
term.processExit(); | |
} | |
function isGoal() { | |
return towerState[GOAL_TOWER].length === DISKS.length; | |
} | |
function isMinimumMoves() { | |
return MINIMUM_MOVES >= currentMoves; | |
} | |
function canStackDist(topDisk, bottomDisk) { | |
return topDisk < bottomDisk; | |
} | |
function printTowers() { | |
term.bgBlack().clear().bgBlack(); | |
// 塔を表示 | |
DISKS.forEach((_, i) => { | |
TOWERS.forEach((tower, j) => { | |
const disk = towerState[tower][DISKS.length - 1 - i] ?? ' '; | |
term.moveTo(TOWER_LABEL_START + TOWER_SPACING * j, i + 1)(`| ${disk} |`); | |
}); | |
}); | |
// 塔IDを表示 | |
TOWERS.forEach((tower, index) => { | |
term.moveTo(TOWER_LABEL_START + TOWER_SPACING * index, DISKS.length + 1)(` ${tower} `); | |
}); | |
// 最短移動数と現在移動数 | |
term.moveTo(1, DISKS.length + 2).bold.magenta(`最短移動数: ${MINIMUM_MOVES} 現在移動数: ${currentMoves}`) | |
// 塔、塔ID、最短手の次の行から入力行とします | |
term.moveTo(1, DISKS.length + 3); | |
} | |
function moveDisk(from, to) { | |
if (isEmpty(towerState[from])) { | |
alert(`${from}塔にはディスクがありません!`); | |
return false; | |
} | |
if (notEmpty(towerState[to]) && !canStackDist(last(towerState[from]), last(towerState[to]))) { | |
alert(`無効な移動です!`); | |
return false; | |
} | |
towerState[to].push(towerState[from].pop()); | |
currentMoves += 1; | |
return true; | |
} | |
function gameLoop() { | |
printTowers(); | |
term.bgBlack().cyan("移動するディスクの塔と目的の塔を選んでください(例: AC): ") | |
term.inputField({}, (error, input) => { | |
if (QUIT_WORDS.includes(input.toLowerCase())) { | |
quit(); | |
return; | |
} | |
const [from, to] = input.toUpperCase().split(''); | |
if (!TOWERS.includes(from) || !TOWERS.includes(to)) { | |
alert('無効な入力です。もう一度試してください。'); | |
wait(gameLoop); | |
return; | |
} | |
if (!moveDisk(from, to)) { | |
wait(gameLoop); | |
return; | |
} | |
if (!isGoal()) { | |
gameLoop(); | |
return; | |
} | |
printTowers(); | |
if (isMinimumMoves()) { | |
term.green('おめでとうございます!').red('\'最短移動数で\'').green('ディスクを移動しました。'); | |
} else { | |
information('おめでとうございます!すべてのディスクを移動しました。'); | |
} | |
term.processExit(); | |
}); | |
} | |
process.on('SIGINT', function () { | |
quit(); | |
}); | |
gameLoop(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment