diff --git a/Projects/1-beginner project border radius prewiew/index.html b/Projects/1-beginner project border radius prewiew/index.html new file mode 100644 index 00000000..c225782a --- /dev/null +++ b/Projects/1-beginner project border radius prewiew/index.html @@ -0,0 +1,81 @@ + + + + + + Border-radius Previewer + + + + +
+

Border-radius Previewer

+ +
+ +
+
+ + + 25px +
+
+ + + 25px +
+
+ + + 25px +
+
+ + + 25px +
+ + +
+ + + +
+ +
+ + + +
+
+ + + + \ No newline at end of file diff --git a/Projects/1-beginner project border radius prewiew/script.js b/Projects/1-beginner project border radius prewiew/script.js new file mode 100644 index 00000000..23741572 --- /dev/null +++ b/Projects/1-beginner project border radius prewiew/script.js @@ -0,0 +1,93 @@ +document.addEventListener('DOMContentLoaded', () => { + // --- Select Elements --- + const previewBox = document.getElementById('preview-box'); + const cssOutput = document.getElementById('css-output'); + const copyButton = document.getElementById('copy-button'); + const copyMessage = document.getElementById('copy-message'); + + // Get all input elements for radii (Horizontal: h, Vertical: v) + const hInputs = ['tlh', 'trh', 'brh', 'blh'].map(id => document.getElementById(id)); + const vInputs = ['tlv', 'trv', 'brv', 'blv'].map(id => document.getElementById(id)); + const allInputs = [...hInputs, ...vInputs]; + + // Bonus feature elements + const toggle8Value = document.getElementById('toggle-8-value'); + const verticalControls = document.getElementById('vertical-controls'); + let is8ValueMode = false; + + // --- Core Function: Update Radius --- + function updateRadius() { + // 1. Get all current radius values + const hValues = hInputs.map(input => input.value); + const vValues = vInputs.map(input => input.value); + + // Update display spans next to sliders + hInputs.forEach((input, index) => { + document.getElementById(`${input.id}-value`).textContent = `${hValues[index]}px`; + }); + vInputs.forEach((input, index) => { + document.getElementById(`${input.id}-value`).textContent = `${vValues[index]}px`; + }); + + // 2. Construct the CSS string + const hString = hValues.join('px ') + 'px'; + + let cssString; + if (is8ValueMode) { + const vString = vValues.join('px ') + 'px'; + cssString = `border-radius: ${hString} / ${vString};`; + } else { + // Standard 4-value syntax + cssString = `border-radius: ${hString};`; + } + + // 3. Apply styles and update output (User Stories 1 & 2) + previewBox.style.borderRadius = cssString.replace('border-radius: ', '').replace(';', ''); + cssOutput.value = cssString; + } + + // --- Bonus Feature: Toggle 8-Value Mode --- + toggle8Value.addEventListener('change', () => { + is8ValueMode = toggle8Value.checked; + if (is8ValueMode) { + verticalControls.classList.remove('hidden'); + } else { + verticalControls.classList.add('hidden'); + } + updateRadius(); + }); + + // --- Event Listeners --- + // Update radius whenever any slider moves + allInputs.forEach(input => { + input.addEventListener('input', updateRadius); + }); + + // --- Copy to Clipboard (User Story 3) --- + copyButton.addEventListener('click', () => { + // Select the text in the textarea + cssOutput.select(); + cssOutput.setSelectionRange(0, 99999); // For mobile devices + + try { + // Use the modern Clipboard API + navigator.clipboard.writeText(cssOutput.value).then(() => { + copyMessage.classList.remove('hidden'); + setTimeout(() => { + copyMessage.classList.add('hidden'); + }, 1500); + }); + } catch (err) { + // Fallback for older browsers + document.execCommand('copy'); + copyMessage.textContent = 'Copied! (Fallback)'; + copyMessage.classList.remove('hidden'); + setTimeout(() => { + copyMessage.classList.add('hidden'); + }, 1500); + } + }); + + // Initialize the box on load + updateRadius(); +}); \ No newline at end of file diff --git a/Projects/1-beginner project border radius prewiew/style.css b/Projects/1-beginner project border radius prewiew/style.css new file mode 100644 index 00000000..3b13dd78 --- /dev/null +++ b/Projects/1-beginner project border radius prewiew/style.css @@ -0,0 +1,135 @@ +body { + font-family: sans-serif; + background-color: #f4f4f9; + color: #333; + margin: 0; + padding: 20px; + display: flex; + justify-content: center; +} + +.container { + background-color: #fff; + padding: 30px; + border-radius: 12px; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); + width: 100%; + max-width: 800px; +} + +#preview-box { + width: 200px; + height: 200px; + background-color: #007bff; + margin: 30px auto; + transition: border-radius 0.1s ease; + border: 5px solid #0056b3; +} + +.controls-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 15px; + margin-bottom: 20px; +} + +.control-group { + display: flex; + flex-direction: column; + padding: 10px; + border: 1px solid #ddd; + border-radius: 8px; +} + +input[type="range"] { + width: 100%; + margin: 8px 0; +} + +#css-output { + width: 100%; + height: 60px; + padding: 10px; + margin-bottom: 10px; + border: 1px solid #ccc; + border-radius: 4px; + font-family: monospace; + font-size: 14px; + resize: none; + box-sizing: border-box; +} + +#copy-button { + background-color: #28a745; + color: white; + border: none; + padding: 10px 20px; + border-radius: 4px; + cursor: pointer; + font-size: 16px; + transition: background-color 0.2s; +} + +#copy-button:hover { + background-color: #218838; +} + +/* Bonus Feature Styles */ +#vertical-controls { + grid-column: 1 / -1; /* Span two columns */ + padding-top: 15px; + margin-top: 15px; +} + +.hidden { + display: none; +} + +#copy-message { + color: #28a745; + font-weight: bold; + display: inline-block; + margin-left: 10px; +} + +/* Toggle Switch Styles (Optional) */ +.toggle-switch { + position: relative; + display: inline-block; + width: 60px; + height: 34px; + margin: 10px 0; +} +.toggle-switch input { + opacity: 0; + width: 0; + height: 0; +} +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + transition: 0.4s; + border-radius: 34px; +} +.slider:before { + position: absolute; + content: ""; + height: 26px; + width: 26px; + left: 4px; + bottom: 4px; + background-color: white; + transition: 0.4s; + border-radius: 50%; +} +input:checked + .slider { + background-color: #007bff; +} +input:checked + .slider:before { + transform: translateX(26px); +} \ No newline at end of file diff --git a/Projects/2-intermidoate bit masks app/index.html b/Projects/2-intermidoate bit masks app/index.html new file mode 100644 index 00000000..057bdc85 --- /dev/null +++ b/Projects/2-intermidoate bit masks app/index.html @@ -0,0 +1,47 @@ + + + + + + Bit Masks Timezone Finder + + + + +
+

🌎 Bit Masks Timezone Finder

+ +
+
+ + +
+ +
+ + + +
+
+ +
+

Timezones Defined

+
+ +
+ +
+

Search Results

+

0 cities found.

+ +
+
+ + + + \ No newline at end of file diff --git a/Projects/2-intermidoate bit masks app/script.js b/Projects/2-intermidoate bit masks app/script.js new file mode 100644 index 00000000..7afd9b7a --- /dev/null +++ b/Projects/2-intermidoate bit masks app/script.js @@ -0,0 +1,114 @@ +document.addEventListener('DOMContentLoaded', () => { + // Define all cities with their GMT offsets. + // The 'mask' property will be calculated based on the bit position. + const CITIES_DATA = [ + { name: "Moscow", gmtOffset: 3 }, + { name: "Paris", gmtOffset: 2 }, + { name: "Berlin", gmtOffset: 2 }, + { name: "Brussels", gmtOffset: 2 }, + { name: "Amsterdam", gmtOffset: 2 }, + { name: "Rome", gmtOffset: 2 }, + { name: "London", gmtOffset: 1 }, + { name: "Dublin", gmtOffset: 1 }, + { name: "New York", gmtOffset: -4 }, + { name: "Washington, DC", gmtOffset: -4 }, + { name: "St. Louis", gmtOffset: -5 }, + { name: "Los Angeles", gmtOffset: -7 }, + { name: "Tokyo", gmtOffset: 9 }, + { name: "Beijing", gmtOffset: 8 }, + { name: "Ho Chi Mihn City", gmtOffset: 7 }, + { name: "Mumbai", gmtOffset: 5 }, + ]; + + // --- Developer Note Implementation --- + // Mapping Formula: Bit Position = GMT Offset + 12 + // Range: GMT +12 (pos 24) to GMT -12 (pos 0) + + // Calculate the Bit Mask for each city and populate the mask field. + CITIES_DATA.forEach(city => { + const bitPosition = city.gmtOffset + 12; + // Use the bitwise left shift (<<) to get the mask (2^bitPosition) + city.mask = 1 << bitPosition; + }); + + // --- DOM Elements --- + const gmtInput = document.getElementById('gmt-input'); + const findButton = document.getElementById('find-cities-btn'); + const searchNotCheckbox = document.getElementById('search-not'); + const resultsList = document.getElementById('results-list'); + const summaryCount = document.getElementById('summary-count'); + const cityListDiv = document.getElementById('city-list'); + + // --- Functions --- + + // 1. Initial population of the city list for reference (User Story 1) + function populateCityList() { + const ul = document.createElement('ul'); + CITIES_DATA.forEach(city => { + const li = document.createElement('li'); + const gmtSign = city.gmtOffset >= 0 ? '+' : ''; + li.textContent = `${city.name}: GMT ${gmtSign}${city.gmtOffset}`; + ul.appendChild(li); + }); + cityListDiv.appendChild(ul); + } + + // 2. Core Search Logic (User Story 3) + function findCitiesByGmt() { + const searchOffset = parseInt(gmtInput.value); + const searchNot = searchNotCheckbox.checked; // Bonus Feature + + // Input validation + if (isNaN(searchOffset) || searchOffset < -12 || searchOffset > 12) { + alert("Please enter a valid GMT offset between -12 and 12."); + return; + } + + // Create the search mask based on the GMT offset + const bitPosition = searchOffset + 12; + const searchMask = 1 << bitPosition; + + const matches = []; + + CITIES_DATA.forEach(city => { + // THE CORE BITWISE OPERATION: + // If (city.mask & searchMask) > 0, the bits overlap, meaning the city is in that timezone. + const isMatch = (city.mask & searchMask) > 0; + + // Apply the 'Search NOT' logic (Bonus Feature) + if (searchNot ? !isMatch : isMatch) { + matches.push(city.name); + } + }); + + displayResults(matches); + } + + // 3. Display Results (User Story 3 & Bonus Feature) + function displayResults(matches) { + // Update summary count (Bonus Feature) + summaryCount.textContent = `${matches.length} cities found.`; + + resultsList.innerHTML = ''; + + if (matches.length === 0) { + const li = document.createElement('li'); + li.textContent = 'No cities matched the criteria.'; + resultsList.appendChild(li); + } else { + matches.forEach(name => { + const li = document.createElement('li'); + li.textContent = name; + resultsList.appendChild(li); + }); + } + } + + // --- Initialization and Event Listener --- + + populateCityList(); + findButton.addEventListener('click', findCitiesByGmt); + + // Run an initial search for the default value (GMT +2) + findCitiesByGmt(); +}); \ No newline at end of file diff --git a/Projects/2-intermidoate bit masks app/style.css b/Projects/2-intermidoate bit masks app/style.css new file mode 100644 index 00000000..0512609d --- /dev/null +++ b/Projects/2-intermidoate bit masks app/style.css @@ -0,0 +1,184 @@ +body { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + background-color: #f4f7f6; + color: #333; + padding: 20px; + margin: 0; + display: flex; + justify-content: center; +} + +.app-container { + background-color: #ffffff; + padding: 30px; + border-radius: 12px; + box-shadow: 0 6px 15px rgba(0, 0, 0, 0.1); + width: 100%; + max-width: 700px; +} + +h1 { + text-align: center; + color: #007bff; + margin-bottom: 25px; +} + +h2 { + color: #333; + border-bottom: 2px solid #eee; + padding-bottom: 5px; + margin-top: 20px; +} + +/* --- Controls Panel --- */ +.controls-panel { + display: flex; + flex-direction: column; + gap: 15px; + padding: 15px; + border: 1px solid #ddd; + border-radius: 8px; + margin-bottom: 20px; +} + +.input-group label { + font-weight: 600; +} + +#gmt-input { + padding: 10px; + border: 1px solid #ccc; + border-radius: 4px; + margin-left: 10px; + width: 80px; + text-align: center; + font-size: 1em; +} + +.search-options { + display: flex; + justify-content: space-between; + align-items: center; +} + +#find-cities-btn { + background-color: #28a745; + color: white; + border: none; + padding: 10px 20px; + border-radius: 5px; + cursor: pointer; + font-size: 1em; + transition: background-color 0.2s; +} + +#find-cities-btn:hover { + background-color: #218838; +} + +/* Custom Checkbox (Bonus Feature) */ +.checkbox-container { + display: block; + position: relative; + padding-left: 25px; + margin-bottom: 12px; + cursor: pointer; + font-size: 16px; + user-select: none; + font-weight: 500; +} +.checkbox-container input { + position: absolute; + opacity: 0; + cursor: pointer; + height: 0; + width: 0; +} +.checkmark { + position: absolute; + top: 0; + left: 0; + height: 18px; + width: 18px; + background-color: #eee; + border-radius: 3px; +} +.checkbox-container:hover input ~ .checkmark { + background-color: #ccc; +} +.checkbox-container input:checked ~ .checkmark { + background-color: #007bff; +} +.checkmark:after { + content: ""; + position: absolute; + display: none; +} +.checkbox-container input:checked ~ .checkmark:after { + display: block; +} +.checkbox-container .checkmark:after { + left: 6px; + top: 2px; + width: 5px; + height: 10px; + border: solid white; + border-width: 0 3px 3px 0; + transform: rotate(45deg); +} + + +/* --- City List --- */ +#city-list ul { + list-style: none; + padding: 0; + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 10px; +} + +#city-list li { + background-color: #f8f8f8; + padding: 8px; + border-radius: 4px; + border-left: 4px solid #007bff; + font-size: 0.9em; +} + +/* --- Output Area --- */ +.output-area { + margin-top: 20px; +} + +.summary { + font-weight: bold; + color: #28a745; + margin-bottom: 15px; +} + +#results-list { + list-style-type: disc; + padding-left: 20px; +} + +#results-list li { + margin-bottom: 5px; +} + +hr { + border: 0; + border-top: 1px solid #eee; + margin: 20px 0; +} + +/* Responsive adjustments */ +@media (max-width: 600px) { + .search-options { + flex-direction: column; + align-items: flex-start; + gap: 15px; + } + #city-list ul { + grid-template-columns: repeat(2, 1fr); + } +} \ No newline at end of file diff --git a/Projects/3 advanced battleship bot/BattleshipEngine.js b/Projects/3 advanced battleship bot/BattleshipEngine.js new file mode 100644 index 00000000..0e653018 --- /dev/null +++ b/Projects/3 advanced battleship bot/BattleshipEngine.js @@ -0,0 +1,136 @@ +// BattleshipEngine.js + +/** + * Game board symbols: + * 'W' (Water): Unchecked cell + * 'M' (Miss): Missed shot + * 'H' (Hit): Hit a ship + */ + +class Ship { + constructor(name, size) { + this.name = name; + this.size = size; + this.hits = 0; + this.isSunk = false; + this.coords = []; // Array of {r, c} objects + } +} + +class BattleshipGame { + constructor(boardSize = 10) { + this.boardSize = boardSize; + // Board is initialized to 'W' (Water) + this.board = Array(boardSize).fill(0).map(() => Array(boardSize).fill('W')); + this.ships = this.placeShips(); + this.shotsTaken = 0; + } + + placeShips() { + const shipTypes = [ + { name: "Carrier", size: 5 }, + { name: "Battleship", size: 4 }, + { name: "Cruiser", size: 3 }, + { name: "Submarine", size: 3 }, + { name: "Destroyer", size: 2 }, + ]; + + const placedShips = []; + // Helper to check if a cell is already part of a ship + const isOccupied = (r, c) => this.board[r][c] !== 'W'; + + for (const shipType of shipTypes) { + let placed = false; + while (!placed) { + const isHorizontal = Math.random() > 0.5; + const rStart = Math.floor(Math.random() * this.boardSize); + const cStart = Math.floor(Math.random() * this.boardSize); + + let canPlace = true; + const newShip = new Ship(shipType.name, shipType.size); + const coords = []; + + for (let i = 0; i < shipType.size; i++) { + const r = isHorizontal ? rStart : rStart + i; + const c = isHorizontal ? cStart + i : cStart; + + // Check bounds (0 to boardSize-1) and collision + if (r >= this.boardSize || c >= this.boardSize || r < 0 || c < 0 || isOccupied(r, c)) { + canPlace = false; + break; + } + coords.push({ r, c }); + } + + if (canPlace) { + newShip.coords = coords; + placedShips.push(newShip); + // Mark the board internally for collision check, e.g., using 'S' + coords.forEach(coord => this.board[coord.r][coord.c] = 'S'); + placed = true; + } + } + } + + // Reset the visible board to 'W' where ships were placed + this.board = this.board.map(row => row.map(cell => cell === 'S' ? 'W' : cell)); + + return placedShips; + } + + shoot(r, c) { + this.shotsTaken++; + const cell = this.board[r][c]; + let status = { hit: false, miss: false, sunk: null, message: `Shot at R${r + 1}, C${String.fromCharCode(65 + c)}.` }; + + if (cell === 'H' || cell === 'M') { + status.message = `You already shot at this location! Try again.`; + // Decrement shots taken since it was an invalid shot + this.shotsTaken--; + return status; + } + + let isHit = false; + let hitShip = null; + + // Check if any ship occupies this coordinate + for (const ship of this.ships) { + // Check ship's internal coordinates array + const hitCoordIndex = ship.coords.findIndex(coord => coord.r === r && coord.c === c); + if (hitCoordIndex !== -1) { + ship.hits++; + isHit = true; + hitShip = ship; + break; + } + } + + if (isHit) { + this.board[r][c] = 'H'; + status.hit = true; + status.message = `**HIT!** Target destroyed at R${r + 1}, C${String.fromCharCode(65 + c)}.`; + + if (hitShip.hits === hitShip.size) { + hitShip.isSunk = true; + status.sunk = hitShip.name; + status.message = `💥 **SUNK!** You destroyed the ${hitShip.name}!`; + } + } else { + this.board[r][c] = 'M'; + status.miss = true; + status.message = `**MISS.** Water at R${r + 1}, C${String.fromCharCode(65 + c)}.`; + } + + return status; + } + + isGameOver() { + return this.ships.every(ship => ship.isSunk); + } + + getBoard() { + return this.board; + } +} + +module.exports = BattleshipGame; \ No newline at end of file diff --git a/Projects/3 advanced battleship bot/index.html b/Projects/3 advanced battleship bot/index.html new file mode 100644 index 00000000..e69de29b diff --git a/Projects/3 advanced battleship bot/index.js b/Projects/3 advanced battleship bot/index.js new file mode 100644 index 00000000..90046e9b --- /dev/null +++ b/Projects/3 advanced battleship bot/index.js @@ -0,0 +1,172 @@ +// index.js + +const Discord = require('discord.js'); +const BattleshipGame = require('./BattleshipEngine'); + +// --- Configuration --- +const TOKEN = 'YOUR_BOT_TOKEN_HERE'; // !!! REPLACE ME !!! +const PREFIX = 'bb'; +// Using v12/v13 structure for compatibility. +const client = new Discord.Client(); + +// Map to hold active game instances: Key: UserID, Value: BattleshipGame object +const activeGames = new Map(); + +// Emojis for the visual board (Bonus Feature 2) +const EMOJIS = { + MISS: '⚪', // Missed shot + HIT: '🔴', // Hit ship + WATER: '🟦', // Unchecked water +}; + +// --- Helper Functions --- + +/** + * Creates a Discord Embed for displaying the board graphically. (Bonus Feature 2) + */ +function createBoardEmbed(game, status) { + const board = game.getBoard(); + let boardString = ''; + + // Create column headers (A B C D...) + const colHeaders = ' ' + [...'ABCDEFGHIJ'].join(' '); + boardString += colHeaders + '\n'; + + for (let r = 0; r < board.length; r++) { + // Add row number (1-10) + boardString += `${r + 1} `; + for (let c = 0; c < board[r].length; c++) { + const cell = board[r][c]; + if (cell === 'H') boardString += EMOJIS.HIT; + else if (cell === 'M') boardString += EMOJIS.MISS; + else boardString += EMOJIS.WATER; + boardString += ' '; // Add space for better emoji layout + } + boardString += '\n'; + } + + let statusColor = status.hit ? '#FF0000' : (status.miss ? '#007BFF' : '#343A40'); + + const embed = new Discord.MessageEmbed() + .setTitle('⚓ Battleship Board ⚓') + // Use the board as the main description + .setDescription(boardString) + .addField('Shots Taken', game.shotsTaken.toString(), true) + .addField('Last Action', status.message, true) + .setColor(statusColor) + .setFooter(`Use "${PREFIX} shoot R,C" (e.g., 5,C) to fire.`); + + return embed; +} + +/** + * Sends a help message with rules and commands. (User Story 1) + */ +function sendHelpEmbed() { + return new Discord.MessageEmbed() + .setTitle('Battleship Bot Commands') + .setDescription('Your goal is to sink all 5 opponent ships on a 10x10 grid by guessing their coordinates.') + .setColor('#FFA500') + .addField('Commands:', + `**\`${PREFIX} start\`**: Starts a new game and places ships.\n` + + `**\`${PREFIX} shoot R,C\`**: Target a cell. R=Row (1-10), C=Column (A-J). Example: \`${PREFIX} shoot 5,C\`\n` + + `**\`${PREFIX} help\`**: Displays this message.\n` + + `**\`${PREFIX} surrender\`**: Ends your current game.`, // Bonus Feature 1 + false) + .addField('Board Key:', + `${EMOJIS.WATER} = Water\n${EMOJIS.MISS} = Miss\n${EMOJIS.HIT} = Hit`, + false); +} + + +// --- Discord Event Listeners --- + +client.on('ready', () => { + console.log(`Logged in as ${client.user.tag}!`); + client.user.setActivity(`${PREFIX} help`, { type: 'PLAYING' }); +}); + +client.on('message', message => { + // Ignore messages from bots and non-command messages + if (message.author.bot || !message.content.startsWith(PREFIX)) return; + + // Parse command: bb shoot 5,C -> command='shoot', args=['5,C'] + const args = message.content.slice(PREFIX.length).trim().split(/ +/); + const command = args.shift().toLowerCase(); + const userId = message.author.id; + + // --- Command Routing --- + + // 1. bb help (User Story 1) + if (command === 'help') { + message.channel.send(sendHelpEmbed()); + return; + } + + // 2. bb start (User Story 2) + if (command === 'start') { + if (activeGames.has(userId)) { + return message.reply(`You already have a game running! Use \`${PREFIX} surrender\` to quit the current one.`); + } + + const newGame = new BattleshipGame(); + activeGames.set(userId, newGame); + message.reply('Game started! 🚀 All 5 ships have been placed.'); + + // Send initial board + const initialStatus = { hit: false, miss: false, message: "Welcome to the game! Start firing." }; + message.channel.send(createBoardEmbed(newGame, initialStatus)); + return; + } + + // 3. bb surrender (Bonus Feature 1) + if (command === 'surrender') { + if (activeGames.delete(userId)) { + message.reply(`You surrendered. The game has been ended.`); + } else { + message.reply("You don't have an active game to surrender."); + } + return; + } + + // --- Commands requiring an active game --- + if (!activeGames.has(userId)) { + return message.reply(`You need to start a game first! Use \`${PREFIX} start\`.`); + } + + const game = activeGames.get(userId); + + // 4. bb shoot R,C (User Stories 3, 4, 5) + if (command === 'shoot') { + if (args.length === 0) { + return message.reply('Please provide coordinates in the format R,C (e.g., 5,C).'); + } + + const [coordR, coordC] = args[0].split(','); + const r = parseInt(coordR) - 1; // Row is 0-indexed internally (1-10) + // Convert 'A' to 0, 'B' to 1, etc. + const c = coordC ? coordC.toUpperCase().charCodeAt(0) - 'A'.charCodeAt(0) : NaN; + + if (isNaN(r) || r < 0 || r >= game.boardSize || isNaN(c) || c < 0 || c >= game.boardSize) { + return message.reply('Invalid coordinates. Row must be 1-10, Column must be A-J.'); + } + + // Take the shot and get status + const status = game.shoot(r, c); + + // Display board and status (User Story 4 & Bonus Feature 2) + message.channel.send(createBoardEmbed(game, status)); + + // Check for game over (User Story 5) + if (game.isGameOver()) { + message.reply(`🏆 **GAME OVER! CONGRATULATIONS!** You sunk all ships in ${game.shotsTaken} shots!`); + activeGames.delete(userId); + } + return; + } + + // Fallback for unrecognized commands + message.reply(`Unrecognized command. Type \`${PREFIX} help\` for commands.`); +}); + +client.login(TOKEN); \ No newline at end of file