UNPKG

console-fun

Version:

Some stuff in the console: utils, printing, games and other fun

1,681 lines (1,640 loc) 78.3 kB
#!/usr/bin/env node import meow from 'meow'; import * as R from 'rambda'; import { join, repeat, findIndex, propEq, addIndex, filter, adjust, set, lensProp, map, times, compose, sum, prop, isNil, zipObj, range, values, equals, keys, reduce, difference, all } from 'rambda'; import { shuffle } from 'fast-shuffle'; import ansiEscapes from 'ansi-escapes'; import stringWidth from 'string-width'; import chalk from 'chalk'; import delay from 'delay'; import readline from 'readline'; import figureSet from 'figures'; import { coloreme } from 'coloreme'; import fs from 'fs'; import logUpdate from 'log-update'; import terminalSize from 'terminal-size'; const randPos$2 = (yMin = 0) => { const randY = Math.floor(Math.random() * process.stdout.rows); return [ Math.floor(Math.random() * process.stdout.columns), randY > yMin ? randY : Math.floor(yMin + Math.random() * process.stdout.rows) ]; }; const fallingStarsGame = () => { let interval; process.stdout.write(ansiEscapes.eraseScreen); const [x, y] = randPos$2(); process.stdout.write(ansiEscapes.cursorTo(x, y)); let userScore = 0; let starCount = 0; let paused = true; let finished = false; let firstIter = true; const howManyPrint = Math.floor( process.stdout.rows * process.stdout.columns / 70 ); const takenNumbers = []; readline.emitKeypressEvents(process.stdin); process.stdin.setRawMode(true); process.stdin.on("keypress", (_chunk, key) => { if (key && key.name == "s") { if (interval != null) { clearInterval(interval); } fallingStarsGame(); } else if (key && key.name == "c") { process.exit(0); } if (finished) { return; } if (key && key.name == "y") { paused = false; } else if (key && key.name == "p") { paused = !paused; } else if (key.name === "space") { if (!paused && !takenNumbers.includes(starCount) && starCount % 9 === 0) { userScore++; const scoreText2 = chalk.bgYellow.black( `SCORE: ${userScore.toString()}` ); const startingLeftPosition2 = process.stdout.columns - stringWidth(scoreText2); process.stdout.write(ansiEscapes.cursorTo(startingLeftPosition2, 0)); process.stdout.write(scoreText2); takenNumbers.push(starCount); } } }); const drawnPositions = []; process.stdout.write(ansiEscapes.cursorTo(0, 0)); const redYellow = coloreme.redYellow.inverse; process.stdout.write(chalk.bgGreen.hex(redYellow.c)("stars-watcher")); const scoreText = chalk.bgYellow.black(`SCORE: ${userScore.toString()}`); const startingLeftPosition = process.stdout.columns - stringWidth(scoreText); process.stdout.write(ansiEscapes.cursorTo(startingLeftPosition, 0)); process.stdout.write(scoreText); interval = setInterval(async () => { if (paused) { const halfColumns = process.stdout.columns / 2; let y0 = process.stdout.rows / 2 - 6; process.stdout.write(ansiEscapes.cursorTo(0, 1)); process.stdout.write(ansiEscapes.eraseDown); const instructions = [ `Watch appearing stars`, `Press ${chalk.bold("[space]")} on every 9th, 18th... star's appearance` ]; let cnt = 0; instructions.map((instruction, i) => { process.stdout.write(ansiEscapes.cursorNextLine); const instructionWidth = stringWidth(instruction); process.stdout.write( ansiEscapes.cursorTo( halfColumns - Math.floor(instructionWidth / 2), y0 + i ) ); process.stdout.write(instruction); process.stdout.write(ansiEscapes.cursorNextLine); cnt++; }); const prompt = `Are you ready? Press 'y' to start a round`; const promptWidth = stringWidth(prompt); process.stdout.write( ansiEscapes.cursorTo( halfColumns - Math.floor(promptWidth / 2), y0 + cnt + 1 ) ); process.stdout.write(prompt); firstIter = true; return; } if (firstIter) { process.stdout.write(ansiEscapes.cursorTo(0, 1)); process.stdout.write(ansiEscapes.eraseDown); firstIter = false; } else { for (let i in drawnPositions) { process.stdout.write(ansiEscapes.cursorTo(...drawnPositions[i])); process.stdout.write(figureSet.star); } } process.stdout.write(ansiEscapes.cursorTo(0, 1)); process.stdout.write(ansiEscapes.eraseDown); for (let i in drawnPositions) { process.stdout.write(ansiEscapes.cursorTo(...drawnPositions[i])); process.stdout.write(figureSet.star); } const [x2, y2] = randPos$2(); drawnPositions.push([x2, y2]); process.stdout.write(ansiEscapes.cursorTo(x2, y2)); process.stdout.write(figureSet.star); starCount++; if (starCount >= howManyPrint) { clearInterval(interval); finished = true; } if (finished) { process.stdout.write(ansiEscapes.cursorTo(0, 1)); process.stdout.write(ansiEscapes.eraseDown); const status = `GAME OVER`; let x0 = Math.floor(process.stdout.columns / 2 - stringWidth(status) / 2); let y0 = process.stdout.rows / 2 - 4; process.stdout.write( ansiEscapes.cursorTo( process.stdout.columns / 2 - 4, process.stdout.rows / 2 - 3 ) ); process.stdout.write(status); process.stdout.write(ansiEscapes.cursorNextLine); const scoreStr = `SCORE: ${userScore.toString()}`; x0 = process.stdout.columns / 2 - stringWidth(scoreStr) / 2; process.stdout.write(ansiEscapes.cursorTo(x0, y0 + 2)); process.stdout.write(scoreStr); const startGameStr = `Press ${chalk.bold("s")} to start a new round`; x0 = process.stdout.columns / 2 - stringWidth(startGameStr) / 2; process.stdout.write(ansiEscapes.cursorTo(x0, y0 + 5)); process.stdout.write(startGameStr); } }, 1e3); }; const getLines = (text) => { const lines = []; let lineIndex = 0; let acc = ""; for (let i = 0; i < text.length; i++) { const char = text[i]; if (stringWidth(acc + char) <= process.stdout.columns) { if (char === "\n") { lines[lineIndex] ||= ""; lines[lineIndex] += acc; lineIndex++; lines[lineIndex] ||= ""; lines[lineIndex] += char; lineIndex++; acc = ""; } else { acc += char; if (i === text.length - 1) { lines[lineIndex] ||= ""; lines[lineIndex] += acc; } } } else { lines[lineIndex] ||= ""; let isAccModified = false; if (i < text.length) { if (![" ", ".", ",", "!", "?"].includes(text[i])) { const lastSpaceIndex = acc.lastIndexOf(" "); lines[lineIndex] += acc.substring(0, lastSpaceIndex + 1); acc = acc.substring(lastSpaceIndex + 1) + char; isAccModified = true; } } if (!isAccModified) { lines[lineIndex] += acc; acc = char; } lineIndex++; } } return lines; }; const repeatString = (source, count) => join("", repeat(source, count)); const randPos$1 = (yMin = 0) => { const randY = Math.floor(Math.random() * process.stdout.rows); return [ Math.floor(Math.random() * process.stdout.columns), randY > yMin ? randY : Math.floor(yMin + Math.random() * process.stdout.rows) ]; }; const lineByLine = (text, options = {}) => { const intervalMs = options.delay || 1e3; const lines = getLines(text); let paused = false; process.stdout.write(ansiEscapes.clearScreen); readline.emitKeypressEvents(process.stdin); process.stdin.setRawMode(true); process.stdin.on("keypress", (_chunk, key) => { if (key && key.name == "s") { paused = false; } if (key && key.name == "p") { paused = !paused; } if (key && key.name == "c") { process.exit(0); } if (key.ctrl && key.name === "w") { process.exit(0); } }); let m = 0; let line = ""; let newTopCounter = 0; const workingInterval = setInterval(() => { if (paused) { return; } line = lines[m]; if (line !== "\n") { if (options.color) { try { const color = chalk[options.color]; process.stdout.write(color(line)); } catch (e) { process.stdout.write(line); } } else { process.stdout.write(line); } } if (newTopCounter >= process.stdout.rows) { process.stdout.write(ansiEscapes.eraseScreen); process.stdout.write(ansiEscapes.cursorTo(0, 0)); newTopCounter = 0; } else { if (line !== "\n") { process.stdout.write(ansiEscapes.cursorNextLine); } } m++; if (line !== "\n") newTopCounter++; if (m === lines.length) { clearInterval(workingInterval); process.exit(); } }, intervalMs); }; const readFileLineByLine = (file, options = {}) => { const text = fs.readFileSync(file, "utf-8"); lineByLine(text, options); }; const randPos = (yMin = 0) => { const randY = Math.floor(Math.random() * process.stdout.rows); return [ Math.floor(Math.random() * process.stdout.columns), randY > yMin ? randY : Math.floor(yMin + Math.random() * process.stdout.rows) ]; }; const coloredStarsGame = () => { let interval; process.stdout.write(ansiEscapes.eraseScreen); const [x, y] = randPos(); process.stdout.write(ansiEscapes.cursorTo(x, y)); let userScore = 0; let starCount = 0; let coloredStarCount = 0; let paused = true; let finished = false; let firstIter = true; const howManyPrint = Math.floor( process.stdout.rows * process.stdout.columns / 40 ); const takenNumbers = []; readline.emitKeypressEvents(process.stdin); process.stdin.setRawMode(true); process.stdin.on("keypress", (_chunk, key) => { if (key && key.name == "s") { if (interval != null) { clearInterval(interval); } coloredStarsGame(); } else if (key && key.name == "c") { process.exit(0); } if (finished) { return; } if (key && key.name == "y") { paused = false; } else if (key && key.name == "p") { paused = !paused; } else if (key.name === "space") { if (!paused && !takenNumbers.includes(coloredStarCount) && coloredStarCount % 9 === 0) { userScore++; const scoreText2 = chalk.bgYellow(`SCORE: ${userScore.toString()}`); const startingLeftPosition2 = process.stdout.columns - stringWidth(scoreText2); process.stdout.write(ansiEscapes.cursorTo(startingLeftPosition2, 0)); process.stdout.write(scoreText2); takenNumbers.push(coloredStarCount); } } }); const drawnPositions = []; process.stdout.write(ansiEscapes.cursorTo(0, 0)); process.stdout.write(chalk.bgGreen("colored-stars-watcher")); coloreme.yellowBlack.inverse; const scoreText = chalk.bgYellow.red(`SCORE: ${userScore.toString()}`); const startingLeftPosition = process.stdout.columns - stringWidth(scoreText); process.stdout.write(ansiEscapes.cursorTo(startingLeftPosition, 0)); process.stdout.write(scoreText); interval = setInterval(async () => { if (paused) { const halfColumns = process.stdout.columns / 2; let y0 = process.stdout.rows / 2 - 6; process.stdout.write(ansiEscapes.cursorTo(0, 1)); process.stdout.write(ansiEscapes.eraseDown); const instructions = [ `Watch appearing stars`, `Press ${chalk.bold("[space]")} on every 9th, 18th... red colored star's appearance` ]; let cnt = 0; instructions.map((instruction, i) => { process.stdout.write(ansiEscapes.cursorNextLine); const instructionWidth = stringWidth(instruction); process.stdout.write( ansiEscapes.cursorTo( halfColumns - Math.floor(instructionWidth / 2), y0 + i ) ); process.stdout.write(instruction); process.stdout.write(ansiEscapes.cursorNextLine); cnt++; }); const prompt = `Are you ready? Press 'y' to start a round`; const promptWidth = stringWidth(prompt); process.stdout.write( ansiEscapes.cursorTo( halfColumns - Math.floor(promptWidth / 2), y0 + cnt + 1 ) ); process.stdout.write(prompt); firstIter = true; return; } if (firstIter) { process.stdout.write(ansiEscapes.cursorTo(0, 1)); process.stdout.write(ansiEscapes.eraseDown); firstIter = false; } else { for (let i in drawnPositions) { process.stdout.write(ansiEscapes.cursorTo(...drawnPositions[i])); process.stdout.write(figureSet.star); } } process.stdout.write(ansiEscapes.cursorTo(0, 1)); process.stdout.write(ansiEscapes.eraseDown); for (let i in drawnPositions) { process.stdout.write(ansiEscapes.cursorTo(...drawnPositions[i])); const colorFn = drawnPositions[i][2]; process.stdout.write(colorFn(figureSet.star)); } let newRandPos = randPos(); while (R.find( (el) => el[0] === newRandPos[0] && el[1] === newRandPos[1], drawnPositions )) { newRandPos = randPos(); } const [x2, y2] = newRandPos; const colors = ["red", "green", "cyan", "magenta", "white", "blue"].map( (col) => chalk[col] ); let starColor = R.compose(R.head, shuffle)(colors); if (starColor !== chalk.red && starCount > 25 && coloredStarCount < 19 && starCount % 6 === 0) { starColor = chalk.red; } drawnPositions.push([x2, y2, starColor]); process.stdout.write(ansiEscapes.cursorTo(x2, y2)); process.stdout.write(starColor(figureSet.star)); if (starColor === chalk.red) coloredStarCount++; starCount++; if (starCount >= howManyPrint) { clearInterval(interval); finished = true; } if (finished) { process.stdout.write(ansiEscapes.cursorTo(0, 1)); process.stdout.write(ansiEscapes.eraseDown); const status = `GAME OVER`; let x0 = Math.floor(process.stdout.columns / 2 - stringWidth(status) / 2); let y0 = process.stdout.rows / 2 - 4; process.stdout.write( ansiEscapes.cursorTo( process.stdout.columns / 2 - 4, process.stdout.rows / 2 - 3 ) ); process.stdout.write(status); process.stdout.write(ansiEscapes.cursorNextLine); const scoreStr = `SCORE: ${userScore.toString()}`; x0 = process.stdout.columns / 2 - stringWidth(scoreStr) / 2; process.stdout.write(ansiEscapes.cursorTo(x0, y0 + 2)); process.stdout.write(scoreStr); process.stdout.write(ansiEscapes.cursorNextLine); const startGameStr = `Press ${chalk.bold("s")} to start a new round`; x0 = process.stdout.columns / 2 - stringWidth(startGameStr) / 2; process.stdout.write(ansiEscapes.cursorTo(x0, y0 + 5)); process.stdout.write(startGameStr); } }, 1e3); }; const snippet = ` import joinPath from '../Components/Functions/path/joinPath'; import dirname from '../Components/Functions/path/dirname'; import FileMetaData from '../Typings/fileMetaData'; import isTauri from '../Util/is-tauri'; import { CALCULATE_DIRS_SIZE_ENDPOINT, CHECK_EXIST_ENDPOINT, CHECK_ISDIR_ENDPOINT, OPEN_FILE_ENDPOINT } from '../Util/constants'; /** Invoke Rust command to handle files */ class FileAPI { readonly fileName: string | string[]; readonly parentDir: string; /** * Construct FileAPI Class * @param {string} fileName - Your file path * @param {string} parentDir - Parent directory of the file */ constructor(fileName: string | string[], parentDir?: string) { if (parentDir && typeof fileName === 'string') { this.parentDir = parentDir; this.fileName = joinPath(parentDir, fileName); } else this.fileName = fileName; } /** * Read text file * @returns {Promise<any>} */ readFile(): Promise<string> { return new Promise((resolve, reject) => { if (typeof this.fileName === 'string') { if (isTauri) { const { fs } = require('@tauri-apps/api'); fs.readTextFile(this.fileName).then((fileContent: string) => resolve(fileContent)); } else { reject('Read file is currently not supported on web version'); } } else { reject('File name is not a string'); } }); } async readBuffer(): Promise<Buffer> { const Buffer = require('buffer/').Buffer; return new Promise((resolve, reject) => { if (typeof this.fileName === 'string') { if (isTauri) { const { fs } = require('@tauri-apps/api'); resolve(Buffer.from(fs.readBinaryFile(this.fileName).then((fileContent: string) => fileContent))); } else { reject('Read file is currently not supported on web version'); } } }); } /** * Open file on default app * @returns {Promise<void>} */ async openFile(): Promise<void> { if (isTauri) { const { invoke } = require('@tauri-apps/api'); return await invoke('open_file', { filePath: this.fileName }); } else { await fetch(OPEN_FILE_ENDPOINT + this.fileName, { method: 'GET' }); return; } } /** * Get tauri url of local assets * @returns {string} */ readAsset(): string { if (isTauri) { const { tauri } = require('@tauri-apps/api'); return typeof this.fileName === 'string' ? tauri.convertFileSrc(this.fileName) : ''; } } /** * Read file and return as JSON * @returns {Promise<JSON>} */ async readJSONFile(): Promise<JSON> { const content = await this.readFile(); return JSON.parse(content); } /** * Return true if file exist * @returns {boolean} */ async exists(): Promise<boolean> { if (isTauri) { const { invoke } = require('@tauri-apps/api'); return await invoke('file_exist', { filePath: this.fileName }); } else { const exists = await (await fetch(CHECK_EXIST_ENDPOINT + this.fileName, { method: 'GET' })).json(); return exists; } } /** * Create file if it doesn't exist * @returns {Promise<void>} */ async createFile(): Promise<void> { if (typeof this.fileName === 'string') { if (isTauri) { const { invoke } = require('@tauri-apps/api'); await invoke('create_dir_recursive', { dirPath: dirname(this.fileName), }); return await invoke('create_file', { filePath: this.fileName }); } else { return; } } } /** * Read properties of a file * @returns {Promise<FileMetaData>} */ async properties(): Promise<FileMetaData> { if (isTauri) { const { invoke } = require('@tauri-apps/api'); return await invoke('get_file_properties', { filePath: this.fileName }); } } /** * Check if given path is directory * @returns {Promise<boolean>} */ async isDir(): Promise<boolean> { return new Promise((resolve) => { if (isTauri) { const { invoke } = require('@tauri-apps/api'); invoke('is_dir', { path: this.fileName }).then((result: boolean) => resolve(result)); } else { fetch(CHECK_ISDIR_ENDPOINT + this.fileName, { method: 'GET' }) .then((response) => response.json()) .then((result: boolean) => resolve(result)); } }); } /** * Extract icon of executable file * @returns {Promise<string>} */ async extractIcon(): Promise<string> { if (isTauri) { const { invoke } = require('@tauri-apps/api'); return await invoke('extract_icon', { filePath: this.fileName }); } } /** * Calculate total size of given file paths * @returns {number} - Size in bytes */ async calculateFilesSize(): Promise<number> { if (isTauri) { const { invoke } = require('@tauri-apps/api'); return await invoke('calculate_files_total_size', { files: this.fileName }); } else { const paths = Array.isArray(this.fileName) ? this.fileName.join('%2c-%2c') : this.fileName; console.log(paths); const size = await (await fetch(CALCULATE_DIRS_SIZE_ENDPOINT + paths, { method: 'GET' })).json(); return size; } } /** * Compress file(s) to zip file * @returns {Promise<void>} */ async zip(): Promise<void> { if (isTauri) { const { invoke } = require('@tauri-apps/api'); return await invoke('compress_to_zip', { files: typeof this.fileName === 'string' ? [this.fileName] : this.fileName }); } return; } /** * Extract zip file * @param {string} target_dir - Target directory to extract files * @returns {any} */ async unzip(target_dir: string): Promise<void> { if (isTauri) { const { invoke } = require('@tauri-apps/api'); return await invoke('decompress_from_zip', { zipPath: this.fileName, targetDir: target_dir }); } return; } } export default FileAPI;`; const snippet2 = ` import isTauri from '../Util/is-tauri'; import DirectoryAPI from './directory'; /** * Invoke Rust command to operate files/dirs */ class OperationAPI { private src: string; private dest: string; constructor(src: string, dest?: string) { this.src = src; this.dest = dest; } /** * Copy files/dirs * @returns {Promise<void>} */ async copyFile(): Promise<void> { if (isTauri) { const { invoke } = require('@tauri-apps/api'); return await invoke('copy', { src: this.src, dest: this.dest }); } } /** * Rename file/dir * @returns {any} */ async rename(): Promise<void> { if (isTauri) { const { invoke } = require('@tauri-apps/api'); return await invoke('rename', { path: this.src, newPath: this.dest }); } } async cut(): Promise<void> { await this.copyFile(); await this.unlink(); return; } /** * Unlink files/dirs * @returns {Promise<void>} */ async unlink(): Promise<void> { if (isTauri) { const { invoke } = require('@tauri-apps/api'); if (await new DirectoryAPI(this.src).isDir()) { return await invoke('remove_dir', { path: this.src }); } else { return await invoke('remove_file', { path: this.src }); } } } async duplicate(): Promise<void> { this.dest = this.src.split('.').length > 1 ? this.src.split('.').slice(0, -1) + ' - COPY.' + this.src.split('.').slice(-1) : this.src + ' - COPY'; return await this.copyFile(); } } export default OperationAPI; `; const snippet3 = ` import isTauri from '../Util/is-tauri'; /** * Write text into clipboard * @param {string} text - Text you want to write to clipboard * @returns {void} */ const writeTextToClipboard = async (text: string): Promise<void> => { if (isTauri) { const { clipboard } = require('@tauri-apps/api'); return await clipboard.writeText(text); } else { return await navigator.clipboard.writeText(text); } }; /** * Read clipboard text * @returns {Promise<string>} */ const readTextFromClipboard = async (): Promise<string> => { if (isTauri) { const { clipboard } = require('@tauri-apps/api'); return await clipboard.readText(); } else { return await navigator.clipboard.readText(); } }; export { writeTextToClipboard, readTextFromClipboard }; `; const snippet4 = ` /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/no-explicit-any */ import isTauri from '../Util/is-tauri'; import IStorageData from '../Typings/storageData'; // Store fetched data into variable const data: IStorageData = {}; /** * Set information to local storage * @param {string} key - Information key * @param {any} data - Your data * @returns {Promise<void>} */ const set = async (key: string, value: any): Promise<void> => { if (isTauri) { data[key] = value; const { invoke } = require('@tauri-apps/api'); return await invoke('write_data', { key, value }); } else { data[key] = value; localStorage.setItem(key, JSON.stringify(value)); } }; /** * Get information from local storage * @param {string} key - Information key * @param {boolean} force - Force fetcing the latest data * @returns {Promise<any>} - Your data */ const get = async (key: string, force?: boolean): Promise<any> => { interface returnedType { status: boolean; data: JSON; } if (Object.keys(data).includes(key) && !force) { return data[key]; } else { if (isTauri) { const { invoke } = require('@tauri-apps/api'); const returnedData = (await invoke('read_data', { key })) as returnedType; data[key] = returnedData.data; return returnedData.status ? returnedData.data : {}; } else { const storedData = localStorage.getItem(key); if (storedData) { data[key] = JSON.parse(storedData); return data[key]; } else { return {}; } } } }; /** * Remove a data * @param {string} key * @returns {any} */ const remove = async (key: string): Promise<void> => { if (isTauri) { const { invoke } = require('@tauri-apps/api'); await invoke('delete_storage_data', { key }); } else { localStorage.removeItem(key); } }; export default { get, set, remove }; `; const snippets = [snippet, snippet2, snippet3, snippet4]; const hackerTypes = () => { let offset = 0; process.stdout.write(ansiEscapes.clearScreen); readline.emitKeypressEvents(process.stdin); const snippet = shuffle(snippets)[0]; process.stdin.setRawMode(true); process.stdin.on("keypress", (_chunk, key) => { if (key && key.name == "s") ; else if (key && key.name == "p") ; else if (key.ctrl && key.name === "c") { process.exit(0); } else { if (offset < snippet.length) { process.stdout.write(snippet.slice(offset, offset + 1)); offset++; } } }); }; const start = async (roundFigure, { size }) => { let score = 0; let currentFigure = ""; let currentColor = chalk.green; let currentCorrectIndex = 0; const correctIndexes = []; const answeredIndexes = []; const gridSize = size; readline.emitKeypressEvents(process.stdin); process.stdin.setRawMode(true); process.stdin.on("keypress", (_chunk, key) => { if (key && key.name == "q") { process.exit(); } else if (key && key.name == "n") { if (currentFigure === roundFigure) { const lastIndex = correctIndexes[correctIndexes.length - 1]; if (!answeredIndexes.includes(lastIndex)) { score++; answeredIndexes.push(lastIndex); logUpdate(getString()); } } } else if (key.name === "space") { if (currentFigure === roundFigure) { const lastIndex = correctIndexes[correctIndexes.length - 1]; if (!answeredIndexes.includes(lastIndex)) { score++; answeredIndexes.push(lastIndex); logUpdate(getString()); } } } else if (key.name === "down") ; else if (key.name === "up") { process.exit(); } else if (key.name === "s" || key.name === "return") ; else if ("12345678a9".includes(key.name)) { score++; } }); const items = []; for (let i = 0; i < gridSize; i++) { for (let j = 0; j < gridSize; j++) { let rndIndex = Math.floor(Math.random() * gridSize); if (j === rndIndex) { items.push([i, j, figureSet.circle, true]); } else { const figureToInsert = shuffle([ figureSet.triangleLeft, figureSet.triangleRight, figureSet.triangleDown, figureSet.triangleUp ])[0]; items.push([i, j, figureToInsert]); } } process.stdout.write(ansiEscapes.cursorTo(0, 0)); process.stdout.write(ansiEscapes.eraseScreen); process.stdout.write(ansiEscapes.cursorNextLine); process.stdout.write(figureSet.home); } let lineOffset = 10; let displayable = join("", repeat(" ", lineOffset)); const getString = () => { return ` Press ${chalk.bold("[space]")} on '${roundFigure}' appearance. Score: ${score} ${displayable} `; }; let columnCounter = 0; for (let y = 0; y < items.length; y++) { currentColor = y % 2 === 0 ? chalk.cyan : chalk.red; currentFigure = `${items[y][2]}`; if (currentFigure === roundFigure) { correctIndexes.push(currentCorrectIndex); } if (displayable === "") { displayable = ""; } displayable += ` ${currentColor(currentFigure)} `; if (columnCounter + 1 >= gridSize) { displayable += ` ${join("", repeat(" ", lineOffset))}`; columnCounter = -1; } logUpdate(getString()); await delay(items[y][2] === figureSet.heart ? 600 : 450); columnCounter++; currentCorrectIndex++; } logUpdate(getString()); }; const watchFigureGame = async ({ size }) => { const roundFigure = figureSet.circle; readline.emitKeypressEvents(process.stdin); process.stdin.setRawMode(true); process.stdin.on("keypress", async (_chunk, key) => { if (key && key.name == "q") { process.exit(); } else if (key && key.name == "y") { { await start(roundFigure, { size }); } } }); process.stdout.write(ansiEscapes.clearScreen); process.stdout.write(repeatString(" ", 26) + chalk.green("watch-figure")); process.stdout.write(repeatString(ansiEscapes.cursorNextLine, 4)); process.stdout.write(` There are different figures are appearing during a game round. Your task is to watch the ${roundFigure} figure appearance and press ${chalk.bold("[space]")} before the next figure has been shown. Start ${chalk.bold.green("y")} if you are ready. `); }; let biteX = 0; let biteLength = 10; let score = 0; let gameStatus = "RUNNING"; const previousDrawnFigures = []; let roundFigures = []; let nextIndex = 0; const drawFigs = () => { const { rows } = process.stdout; if (previousDrawnFigures.length > 0) { findIndex(propEq(false, "done"), roundFigures); if (nextIndex > -1 && nextIndex < roundFigures.length) { const [x0, y0] = previousDrawnFigures[nextIndex]; process.stdout.write(ansiEscapes.cursorTo(x0, y0)); process.stdout.write(" "); process.stdout.write(ansiEscapes.cursorTo(x0 + 1, y0 + 1)); process.stdout.write(figureSet.hamburger); const remain = addIndex(filter)((_a, i) => { return i !== nextIndex && !roundFigures[i].done; }, previousDrawnFigures); previousDrawnFigures[nextIndex] = [x0 + 1, y0 + 1]; if (y0 + 1 > rows - 2) { if (biteX < x0 && biteX + biteLength >= x0) { score++; roundFigures = adjust( nextIndex, (a) => set(lensProp("done"), true, set(lensProp("win"), true, a)), roundFigures ); nextIndex = findIndex(propEq(false, "done"), roundFigures); } else { roundFigures = adjust( nextIndex, (a) => set(lensProp("done"), true, set(lensProp("win"), false, a)), roundFigures ); nextIndex = findIndex(propEq(false, "done"), roundFigures); } } map((item) => { const [x, y] = item; process.stdout.write(ansiEscapes.cursorTo(x, y)); process.stdout.write(figureSet.hamburger); }, remain); } else { gameStatus = "FINISHED"; nextIndex = -1; } } else { times(() => { const [x, y] = randPos$1(1); process.stdout.write(ansiEscapes.cursorTo(x, y)); previousDrawnFigures.push([x, y]); process.stdout.write(figureSet.hamburger); }, 10); addIndex(map)((item, i) => { roundFigures.push({ index: i, done: false, win: false }); }, previousDrawnFigures); } }; const drawBite = (x) => { const { rows } = process.stdout; biteX = x; process.stdout.write(ansiEscapes.cursorTo(0, rows - 1)); process.stdout.write(ansiEscapes.eraseEndLine); process.stdout.write(ansiEscapes.cursorTo(x, rows - 1)); process.stdout.write(join("", repeat(figureSet.squareBottom, 10))); }; const drawScore = () => { process.stdout.write(ansiEscapes.cursorTo(3, 1)); process.stdout.write(`SCORE: ${score}`); }; const drawStatus = () => { process.stdout.write(ansiEscapes.cursorTo(19, 1)); process.stdout.write(`STATUS: ${gameStatus}`); }; const drawGameOver = () => { const { rows, columns } = process.stdout; process.stdout.write(ansiEscapes.cursorTo(0)); process.stdout.write(ansiEscapes.eraseScreen); process.stdout.write(ansiEscapes.clearScreen); const scoreStr = chalk.green(`SCORE: ${score}`); const scoreStrWidth = stringWidth(scoreStr); process.stdout.write( ansiEscapes.cursorTo( columns / 2 - Math.floor(scoreStrWidth / 2), rows / 2 - 4 ) ); process.stdout.write(scoreStr); process.stdout.write(ansiEscapes.cursorNextLine); const instruction = `Press ${chalk.bold("n")} to start a new game`; const instructionWidth = stringWidth(instruction); process.stdout.write( ansiEscapes.cursorTo( columns / 2 - Math.floor(instructionWidth / 2), rows / 2 - 2 ) ); process.stdout.write(instruction); }; const kardo = async () => { const { rows } = process.stdout; const columns = process.stdout.columns - 12; let secondIter = 0; let x = 15; process.stdout.write(ansiEscapes.cursorForward(7)); process.stdout.write(ansiEscapes.clearScreen); process.stdout.write(ansiEscapes.cursorHide); readline.emitKeypressEvents(process.stdin); process.stdin.setRawMode(true); process.stdin.on("keypress", async (_chunk, key) => { if (gameStatus === "FINISHED") { if (key.name === "n") { gameStatus = "RUNNING"; nextIndex = 0; await kardo(); } } if (key && key.name == "s") ; else if (key && key.name == "c") { process.exit(0); } if (key && key.name == "right") { if (x - 1 < columns) { x++; drawBite(x); } } else if (key && key.name == "p") ; else if (key.name === "left") { if (x > 0) { x--; drawBite(x); } } }); process.stdout.write(ansiEscapes.clearScreen); await delay(1200); let currentIndex = 0; let figures = [ figureSet.circle, figureSet.squareSmall, figureSet.squareSmallFilled, figureSet.heart ]; let interval = setInterval(() => { let curr = 1; process.stdout.write(ansiEscapes.cursorTo(0, 0)); process.stdout.write(ansiEscapes.clearScreen); curr++; if (curr < columns) { drawBite(x); drawFigs(); drawScore(); drawStatus(); if (gameStatus === "FINISHED" || nextIndex >= roundFigures.length) { clearInterval(interval); drawGameOver(); return; } secondIter++; figures[currentIndex]; if (secondIter >= rows - 1) { secondIter = 0; if (currentIndex >= process.stdout.columns - 30 - biteLength * 2) { currentIndex = 1; clearInterval(interval); } } } }, 150); }; const drawHamburgers$1 = () => { process.stdout.write(ansiEscapes.clearScreen); const taken = []; const interval = setInterval(() => { let tries = 0; let x; let y; while (tries < 7) { [x, y] = randPos$1(); if (taken.some((a) => a[0] === x && a[1] === y)) { tries++; } else { taken.push([x, y]); tries = 0; break; } } if (tries > 5) { clearInterval(interval); process.stdout.write(ansiEscapes.cursorTo(0, process.stdout.rows - 1)); process.stdout.write(ansiEscapes.eraseEndLine); process.stdout.write( ansiEscapes.cursorTo( process.stdout.columns / 2, process.stdout.rows - 1 ) ); process.stdout.write( chalk.cyan(`${chalk.bold("r")} - run again ${chalk.bold("q")} - exit`) ); } process.stdout.write(ansiEscapes.cursorTo(x, y)); process.stdout.write(figureSet.hamburger); }); readline.emitKeypressEvents(process.stdin); process.stdin.setRawMode(true); process.stdin.on("keypress", async (_chunk, key) => { if (key.name == "r") { drawHamburgers$1(); } if (key && key.name == "q") { process.exit(); } }); }; const diag = async () => { const sentence = "This comprehensive collection offers the latest trends, ideas, and innovations in both residential and commercial spaces."; process.stdout.write(ansiEscapes.clearScreen); const size = terminalSize(); for (let i = 0; i < sentence.length; i++) { process.stdout.write(sentence[i]); await delay(500); process.stdout.write(ansiEscapes.cursorDown()); process.stdout.write(ansiEscapes.cursorForward()); if (i !== 0 && i % size.rows === 0) { process.stdout.write(ansiEscapes.clearScreen); } } }; const drawMixFigures = ({ colored }) => { process.stdout.write(ansiEscapes.clearScreen); const taken = []; const interval = setInterval(() => { let tries = 0; let x; let y; while (tries < 7) { [x, y] = randPos$1(); if (taken.some((a) => a[0] === x && a[1] === y)) { tries++; } else { taken.push([x, y]); tries = 0; break; } } if (tries > 5) { clearInterval(interval); process.stdout.write(ansiEscapes.cursorTo(0, process.stdout.rows - 1)); process.stdout.write(ansiEscapes.eraseEndLine); process.stdout.write( ansiEscapes.cursorTo( process.stdout.columns / 2, process.stdout.rows - 1 ) ); process.stdout.write( chalk.cyan(`${chalk.bold("r")} - run again ${chalk.bold("q")} - exit`) ); } process.stdout.write(ansiEscapes.cursorTo(x, y)); const figure = shuffle([ figureSet.hamburger, figureSet.squareCenter, figureSet.circle, figureSet.checkboxOff ])[0]; if (colored) { const color = shuffle([ chalk.cyan, chalk.blue, chalk.gray, chalk.green, chalk.yellow, chalk.magenta ])[0]; process.stdout.write(color(figure)); } else { process.stdout.write(figure); } }); readline.emitKeypressEvents(process.stdin); process.stdin.setRawMode(true); process.stdin.on("keypress", async (_chunk, key) => { if (key.name == "r") { drawHamburgers(); } if (key && key.name == "q") { process.exit(); } }); }; const drawY = ({ colored }) => { process.stdout.write(ansiEscapes.clearScreen); let pointIndex = 0; let points = [ [ [15, 16], [17, 16], [19, 16], [21, 17], [23, 19], [25, 21] ], [ [16, 18], [17, 20], [18, 21], [20, 22], [20, 24], [22, 26], [20, 22] ], [ [22, 18], [22, 20], [22, 21], [24, 21], [26, 21], [28, 21], [24, 20] ], [ [15, 18], [12, 10], [12, 11], [14, 12], [15, 14], [16, 13], [14, 15] ] ]; const interval = setInterval(() => { process.stdout.write(ansiEscapes.clearScreen); let currPoints = points[pointIndex]; pointIndex++; addIndex(map)((a, i) => { process.stdout.write(ansiEscapes.cursorTo(a[0], a[1])); const clr = shuffle([ chalk.green, chalk.cyan, chalk.magenta, chalk.black, chalk.gray ])[0]; process.stdout.write( clr(i % 2 === 0 ? figureSet.triangleUp : figureSet.triangleDown) ); }, currPoints); process.stdout.write(currPoints.length.toString()); if (pointIndex >= points.length) { clearInterval(interval); } }, 500); readline.emitKeypressEvents(process.stdin); process.stdin.setRawMode(true); process.stdin.on("keypress", async (_chunk, key) => { if (key.name == "r") { drawY(); } if (key && key.name == "q") { process.exit(); } }); }; const indexedMap$4 = addIndex(map); const sumLengths = compose(sum, map(prop("length"))); const drawTriangles = () => { process.stdout.write(ansiEscapes.clearScreen); let step = 0; let pointIndex = 0; const verticalCount = 9; const horizontalCount = 15; let upToDown = times((i) => [10, i + 5], verticalCount); let leftToRight = filter( (el) => !isNil(el[0]), times( (i) => [i % 2 !== 0 ? null : 11 + i + 1, verticalCount + 4], horizontalCount ) ); let downUp = times( (i) => [11 + horizontalCount, verticalCount + 3 - i], verticalCount - 1 ); let rightLeft = filter( (el) => !isNil(el[0]), times( (i) => [i % 2 !== 0 ? null : 9 + horizontalCount - i, 5], horizontalCount ) ); const directions = ["upDown", "leftRight", "downUp", "rightLeft"]; const directionToFigure = zipObj(directions, [ figureSet.triangleDown, figureSet.triangleRight, figureSet.triangleUp, figureSet.triangleLeft ]); zipObj(directions, range(0, 4)); let all = zipObj(directions, [upToDown, leftToRight, downUp, rightLeft]); const interval = setInterval(() => { sumLengths(values(all)); process.stdout.write(ansiEscapes.clearScreen); indexedMap$4((key, i) => { const items = all[key]; indexedMap$4((item, j) => { process.stdout.write(ansiEscapes.cursorTo(item[0], item[1])); const clr = equals(key === "upDown" ? step + 1 : step, j) ? chalk.green : chalk.gray; const na = j === items.length - 1 ? figureSet.squareCenter : key === "leftRight" ? directionToFigure[key] : directionToFigure[key]; process.stdout.write( key === "leftRight" && j % 2 !== 0 ? clr(na) : clr(na) ); }, items); }, keys(all)); let centerFigure; let currDir = directions[step % directions.length]; let curCol; if (currDir === "upDown") { centerFigure = figureSet.triangleDown; curCol = chalk.cyan; } else if (currDir === "leftRight") { centerFigure = figureSet.triangleRight; curCol = chalk.green; } else if (currDir === "downUp") { centerFigure = figureSet.triangleUp; curCol = chalk.yellow; } else { centerFigure = figureSet.triangleLeft; curCol = chalk.magenta; } process.stdout.write( ansiEscapes.cursorTo( Math.floor((22 + horizontalCount) / 2), Math.floor((10 + verticalCount) / 2) ) ); process.stdout.write(curCol(centerFigure)); step++; pointIndex++; const allValues = values(all); const maxLength = reduce( (prev, current) => prev < current.length ? current.length : prev, allValues[0].length, allValues.slice(1) ); if (pointIndex >= maxLength) { process.stdout.write( ansiEscapes.cursorTo(Math.floor(process.stdout.rows / 2) - 6, 1) ); process.stdout.write( `${chalk.blue.bold("r")} - run again ${chalk.blue.bold("q")} - exit` ); clearInterval(interval); } }, 1e3); readline.emitKeypressEvents(process.stdin); process.stdin.setRawMode(true); process.stdin.on("keypress", async (_chunk, key) => { if (key.name == "r") { drawTriangles(); } if (key && key.name == "q") { process.exit(); } }); }; const drawX$1 = ({ colored }) => { process.stdout.write(ansiEscapes.clearScreen); let step = 0; let pointIndex = 0; let points = [ [ [9, 9], [10, 10], [11, 9], [12, 10], [13, 11], [11, 11] ], [ [11, 9], [12, 10], [8, 7], [8, 10], [8, 8], [11, 11] ] ]; const interval = setInterval(() => { process.stdout.write(ansiEscapes.clearScreen); let currPoints = points[pointIndex]; if (step > 0) { points = addIndex(map)( (a, i) => [ i % 2 === 0 ? a[0] + i : a[0] - i, i % 2 === 0 ? a[1] + i : a[1] - i ], points ); } step++; pointIndex++; map((a) => { process.stdout.write(ansiEscapes.cursorTo(a[0], a[1])); process.stdout.write(figureSet.bullet); }, currPoints); if (pointIndex >= points.length) { clearInterval(interval); } }, 500); readline.emitKeypressEvents(process.stdin); process.stdin.setRawMode(true); process.stdin.on("keypress", async (_chunk, key) => { if (key.name == "r") { drawX$1(); } if (key && key.name == "q") { process.exit(); } }); }; const drawIt = ({ colored }) => { const fncs = shuffle([drawX$1, drawY])[0]; fncs({ colored }); }; const drawBar = () => { const fncs = shuffle([drawTriangles])[0]; fncs(); }; const indexedMap$3 = addIndex(map); compose(sum, map(prop("length"))); const drawCircles = () => { process.stdout.write(ansiEscapes.clearScreen); let step = 0; let pointIndex = 0; let positions = [ [6, 9], [9, 7], [14, 6], [19, 7], [23, 9], [19, 11], [14, 13], [9, 11] ]; const interval = setInterval(() => { process.stdout.write(ansiEscapes.clearScreen); indexedMap$3((item, i) => { step === i ? chalk.green : chalk.cyan; if (step === i) { process.stdout.write(ansiEscapes.cursorTo(item[0], item[1] - 1)); const triColor = step % 2 === 0 ? chalk.green : chalk.blue; process.stdout.write(triColor(figureSet.triangleDown)); } const currentFigure = step === i ? figureSet.circle : figureSet.circleFilled; process.stdout.write(ansiEscapes.cursorTo(item[0], item[1])); process.stdout.write(chalk.green(currentFigure)); }, positions); if (step === positions.length - 1) { step = -1; } step++; pointIndex++; if (pointIndex >= Math.floor(Math.random() * positions.length * 53 + 23)) { process.stdout.write(ansiEscapes.cursorTo(process.stdout.rows / 2, 1)); process.stdout.write( `${chalk.blue.bold("r")} - run again ${chalk.blue.bold("q")} - exit` ); clearInterval(interval); } }, 1e3); readline.emitKeypressEvents(process.stdin); process.stdin.setRawMode(true); process.stdin.on("keypress", async (_chunk, key) => { if (key.name == "r") { drawCircles(); } if (key && key.name == "q") { process.exit(); } }); }; addIndex(map); compose(sum, map(prop("length"))); const drawHisa = () => { process.stdout.write(ansiEscapes.clearScreen); let step = 0; let stepY = 0; let pointIndex = 0; const interval = setInterval(() => { const rows = 12; const cols = 12; const total = rows * cols; process.stdout.write(ansiEscapes.clearScreen); for (let i = 0; i < rows; i++) { for (let j = 0; j < cols; j++) { let currentFigure; if (step === i && stepY === j) { currentFigure = figureSet.lineDownDoubleLeftDoubleRightDouble; } if (isNil(currentFigure)) { currentFigure = figureSet.circleDotted; } if (i % 2 !== 0) { process.stdout.write(ansiEscapes.cursorTo(i + 9, j + 2)); process.stdout.write(chalk.green(currentFigure)); } else { process.stdout.write(ansiEscapes.cursorTo(i + 9, j + 2)); process.stdout.write(chalk.green(currentFigure)); } } } if (step === rows - 1) { step = -1; } if (stepY === cols - 1) { stepY = -1; } { stepY = -1; } step++; stepY++; pointIndex++; if (pointIndex >= Math.floor(Math.random() * total * 2 + 23)) { process.stdout.write(ansiEscapes.cursorTo(process.stdout.rows / 2, 1)); process.stdout.write( `${chalk.blue.bold("r")} - run again ${chalk.blue.bold("q")} - exit` ); clearInterval(interval); } }, 400); readline.emitKeypressEvents(process.stdin); process.stdin.setRawMode(true); process.stdin.on("keypress", async (_chunk, key) => { if (key.name == "r") { drawHisa(); } if (key && key.name == "q") { process.exit(); } }); }; const indexedMap$2 = addIndex(map); compose(sum, map(prop("length"))); const drawMisc = () => { process.stdout.write(ansiEscapes.clearScreen); let step = 0; let pointIndex = 0; let taken = []; let positions = []; let firstSet = [ figureSet.tick, figureSet.info, figureSet.warning, figureSet.cross, figureSet.square, figureSet.squareDarkShade, figureSet.circle, figureSet.circleCross, figureSet.circlePipe, figureSet.checkboxOff, figureSet.checkboxOn, figureSet.bullet, figureSet.home ]; for (let y = 8; y < 23; y++) { for (let u = 9; u < 24; u++) { positions.push([y, u]); } } const interval = setInterval(() => { process.stdout.write(ansiEscapes.clearScreen); indexedMap$2((item, i) => { if (i % 2 === 0) { step === i ? chalk.green : chalk.cyan; if (step === i) { process.stdout.write(ansiEscapes.cursorTo(item[0] - 1, item[1])); const triColor = step % 2 === 0 ? chalk.green : chalk.blue; process.stdout.write(triColor(figureSet.pointer)); } if (taken.length === firstSet.length) { taken = []; } const remained = difference(firstSet, taken); const currentFigure = shuffle(remained)[0]; taken.push(currentFigure); process.stdout.write(ansiEscapes.cursorTo(item[0], item[1])); process.stdout.write(chalk.green(currentFigure)); } }, positions); if (step === positions.length - 1) { step = -1; } step++; pointIndex++; if (pointIndex >= Math.floor(Math.random() * positions.length * 53 + 23)) { process.stdout.write(ansiEscapes.cursorTo(process.stdout.rows / 2, 1)); process.stdout.write( `${chalk.blue.bold("r")} - run again ${chalk.blue.bold("q")} - exit` ); clearInterval(interval); } }, 1e3); readline.emitKeypressEvents(process.stdin); process.stdin.setRawMode(true); process.stdin.on("keypress", async (_chunk, key) => { if (key.name == "r") { drawMisc(); } if (key && key.name == "q") { process.exit(); } }); }; const circledWords = () => { process.stdout.write(ansiEscapes.clearScreen); let offs = 0; let arr = [ { item: "a", coords: [5, 5] }, { item: "b", coords: [8, 6] }, { item: "c", coords: [11, 7] }, { item: "d", coords: [8, 8] }, { item: "e", coords: [5, 9] }, { item: "f", coords: [2, 8] }, { item: "g", coords: [0, 7] }, { item: "h", coords: [