Bots Home
|
Create an App
Minesweeper
Author:
lemons_
Description
Source Code
Launch Bot
Current Users
Created by:
Lemons_
// Minesweeper by lemons_ // Minesweeper Cell class Cell { constructor(x, y) { this.isBomb = false this.adjacent = 0 this.isMarked = false this.isCleared = false this.x = x this.y = y } display(showBombs) { if (showBombs) { if (this.isBomb) { return this.BOMB } else if (this.adjacent === 0) { return this.EMPTY } else { return this.adjacent } } else { if (this.isCleared) { if (this.adjacent === 0) { return this.EMPTY } else { return this.adjacent } } else if (this.isMarked) { return this.MARKED } else { return this.UNMARKED } } } } Cell.prototype.BOMB = "*" Cell.prototype.MARKED = "P" Cell.prototype.UNMARKED = "?" Cell.prototype.EMPTY = "_" // Minesweeper Grid class Grid { constructor(width, height, numBombs) { this.width = width this.height = height this.rows = [] this.hasExploded = false this.numCellsToClear = (this.width * this.height) - numBombs this.columnHeadings = ` - - ${this.LETTERS.slice(0, this.width).join(" ")}` for (let y = 0; y < this.height; y++) { const row = [] for (let x = 0; x < this.width; x++) { row.push(new Cell(x, y)) } this.rows.push(row) } this.addBombs(numBombs) this.calculateAdjacency() } calculateAdjacency() { for (let y = 0; y < this.height; y++) { for (let x = 0; x < this.width; x++) { const cell = this.cell(x, y) this.CELL_OFFSETS.forEach((offset) => { const adjCell = this.cell(x + offset[0], y + offset[1]) if (adjCell && adjCell.isBomb) { cell.adjacent += 1 } }) } } } get isComplete() { return this.numCellsToClear <= 0 } // cell() - get cell at this position, or null if outside the grid. cell(x, y) { try { return this.rows[y][x] } catch (TypeError) { return null } } // display() display(user, showBombs, title, totalTipped) { const lines = [title, this.columnHeadings] for (let y = 0; y < this.width; y++) { const row = this.rows[y].map((c) => c.display(showBombs || this.hasExploded)) lines.push(`${y}: ${row.join(" ")}`) } lines.push(this.hasExploded ? "Exploded!" : `Tips: ${totalTipped} / Areas left: ${this.numCellsToClear}`) lines.push("/mshelp for full instructions") cb.sendNotice(lines.join("\n"), user, (this.hasExploded ? "#F00" : "#00F"), "#FFF", "bold") } // addBombs() - Add mines to grid addBombs(numBombs) { while (numBombs > 0) { const x = Math.floor(Math.random() * this.width), y = Math.floor(Math.random() * this.height), cell = this.cell(x, y) if (!cell.isBomb) { cell.isBomb = true numBombs-- } } } sweep(cell) { if (cell.isBomb) { this.hasExploded = true } else { cell.isCleared = true this.numCellsToClear -= 1 if (cell.adjacent === 0) { this.sweepAdjacentCells(cell) } } } sweepAdjacentCells(cell) { this.CELL_OFFSETS.forEach((offset) => { const adjCell = this.cell(cell.x + offset[0], cell.y + offset[1]) if (adjCell && !adjCell.isCleared) { adjCell.isCleared = true this.numCellsToClear -= 1 if (adjCell.adjacent === 0) { this.sweepAdjacentCells(adjCell) } } }) } } Grid.prototype.CELL_OFFSETS = [ [-1, -1], [ 0, -1], [ 1, -1], [-1, 0], [ 1, 0], [-1, 1], [ 0, 1], [ 1, 1], ] Grid.prototype.LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toLowerCase().split("") // Game for playing Minesweeper class Game { constructor(width, height, num_bombs) { this.grid = new Grid(width, height, num_bombs) this.totalTipped = 0 } get isPlaying() { return !this.grid.hasExploded && !this.grid.isComplete } // explodedMessages() explodedMessages(user) { cb.sendNotice(`BOOOOOOOOOOOOOM! Clumsy - You set off a mine!`, user) cb.sendNotice(`BOOOOOOOOOOOOOM! ${user} set off a mine, like a complete muppet!`) } // completedMessages() completedMessages(user) { cb.sendNotice(`Whee! You found all the mines!`, user) cb.sendNotice(`Whee! ${user} found all the mines like a complete star!`) } // tipAction() tipAction(amount, x_str, y_str, user) { const position = `${x_str}${y_str}` const x = this.grid.LETTERS.indexOf(x_str.toLowerCase()) if (x < 0 || x >= this.width) { cb.chatNotice(`Invalid column letter: ${position}`, user) return } const y = parseInt(y_str) if (y < 0 || y >= this.height) { cb.chatNotice(`Invalid row number: ${position}`, user) return } const cell = this.grid.cell(x, y) if (cell.isClear) { cb.sendNotice(`Already swept ${position}`, user) } else { switch (amount) { case cb.settings.tokens_to_sweep: this.sweep(cell, user) break case cb.settings.tokens_to_mark: if (!cell.isMarked) { cell.isMarked = true } else { cb.chatNotice(`Already marked: ${position}`, user) } break case cb.settings.tokens_to_unmark: if (cell.isMarked) { cell.isMarked = false } else { cb.chatNotice(`Already unmarked: ${position}`, user) } break } } this.totalTipped += amount this.display() } // sweep() sweep(cell, user) { this.grid.sweep(cell) if (this.grid.hasExploded || this.grid.isComplete) { if (this.grid.hasExploded) { this.explodedMessages(user) } else if (this.grid.isComplete) { this.completedMessages(user) } if (remindEvent) { cb.cancelTimeout(remindEvent) } } else { cb.chatNotice(`Swept for mines and found ${cell.adjacent} nearby`, user) } } // display() display(user, showBombs) { this.grid.display(user, showBombs, this.TITLE, this.totalTipped) } // showHelp showHelp(user) { const msg = [this.TITLE] msg.push("/mshelp - show this message") msg.push("/msshow - show the current board state") if (isAdmin(user)) { msg.push("/msreset - start the game again (ADMIN ONLY)") msg.push("/mssweep b3 - clear cell at column b, row 3 (ADMIN ONLY)") msg.push("/msmark b3 - mark cell at column b, row 3 (ADMIN ONLY)") msg.push("/mssweep b3 - unmark cell at column b, row 3 (ADMIN ONLY)") } msg.push(`To sweep: Tip ${cb.settings.tokens_to_sweep} tokens and ensure the message is formatted "a0"`) msg.push(`To mark: Tip ${cb.settings.tokens_to_mark} tokens and ensure the message is formatted "b3"`) msg.push(`To unmark: Tip ${cb.settings.tokens_to_unmark} tokens and ensure the message is formatted "f7"`) cb.chatNotice(msg.join("\n"), user) } } Game.prototype.TITLE = "--- Minesweeper by lemons_ ---" cb.settings_choices = [ {name: 'width', type: 'int', minValue: 5, maxValue: 26, defaultValue: 10, label: "Board width"}, {name: 'height', type: 'int', minValue: 5, maxValue: 10, defaultValue: 10, label: "Board height"}, {name: 'num_bombs', type: 'int', minValue: 5, maxValue: 100, defaultValue: 25, label: "Number of mines"}, {name: 'goal', type: 'str', label: 'Goal on completion', minLength: 1, maxLength: 255}, {name: 'tokens_to_sweep', type: 'int', defaultValue: 3, minValue: 1, label: "Tokens to sweep a space for nearby mines"}, {name: 'tokens_to_mark', type: 'int', defaultValue: 1, minValue: 1, label: "Tokens to mark space as bomb"}, {name: 'tokens_to_unmark', type: 'int', defaultValue: 2, label: "Tokens to unmark space as bomb"}, {name: 'reminder_delay_minutes', type: 'int', defaultValue: 2, minValue: 1, label: "Remind users every X minutes"}, ] let game = null, remindEvent = null, remindDelay = null // Reset grid const reset = (user) => { game = new Game(cb.settings.width, cb.settings.height, cb.settings.num_bombs) cb.chatNotice(`${user} started Minesweeper (with ${cb.settings.num_bombs} mines)!`) game.display(user, true) game.display() if (remindEvent) { cb.cancelTimeout(remindEvent) } remindEvent = cb.setTimeout(remind, remindDelayMs) } const remind = () => { cb.chatNotice("We are playing Minesweeper! (/mshelp for instructions)") remindEvent = cb.setTimeout(remind, remindDelayMs) } // onStart() cb.onStart((user) => { remindDelayMs = cb.settings.reminder_delay_minutes * 60 * 1000 reset(user.user) }) // onEnter() cb.onEnter((user) => { if (!game.isPlaying) { return } cb.chatNotice(`Welcome ${user.user}, we are playing Minesweeper! (/mshelp for instructions)`) }) // onTip() cb.onTip((tip) => { if (!game.isPlaying) { return } const amount = tip.amount if ([cb.settings.tokens_to_sweep, cb.settings.tokens_to_mark, cb.settings.tokens_to_unmark].includes(amount)) { game.tipAction(amount, tip.message[0], tip.message[1], tip.user) } }) // isAdmin() const isAdmin = (user) => { return (cb.room_slug == user) } // onMessage cb.onMessage((message) => { //cb.chatNotice(`${JSON.stringify(message)} ${message.m} ${message.m.startsWith("/mssweep")} ${isAdmin(message.user)}, ${message.user}`) const admin = isAdmin(message.user) if (message.m.startsWith("/mssweep")) { if (admin) { //cb.chatNotice(`${cb.settings.tokens_to_clear}, ${message.m[9]}, ${message.m[10]}, ${message.user}`) game.tipAction(cb.settings.tokens_to_sweep, message.m[9], message.m[10], message.user) message['X-Spam'] = true } } else if (message.m.startsWith("/msreset")) { if (admin) { reset(message.user) message['X-Spam'] = true } } else if (message.m.startsWith("/msmark")) { if (admin) { game.tipAction(cb.settings.tokens_to_mark, message.m[8], message.m[9], message.user) message['X-Spam'] = true } } else if (message.m.startsWith("/msunmark")) { if (admin) { game.tipAction(cb.settings.tokens_to_unmark, message.m[10], message.m[11], message.user) message['X-Spam'] = true } } else if (message.m.startsWith("/msshow")) { game.display(message.user, admin) message['X-Spam'] = true } else if (message.m.startsWith("/mshelp")) { game.showHelp(message.user) message['X-Spam'] = true } return message })
© Copyright Chaturbate 2011- 2024. All Rights Reserved.