Created
April 23, 2025 07:08
-
-
Save consti/fc2cee3c989073a84e2b518fe6731971 to your computer and use it in GitHub Desktop.
ExponenTile auto-play
This file contains hidden or 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
(function autoPlayExponenTile() { | |
// Configuration | |
const config = { | |
moveDelay: 500, // Milliseconds between moves | |
maxMoves: 1000, // Safety limit | |
}; | |
let moveCount = 0; | |
let running = false; | |
// Get the actual game board elements | |
function getBoardElements() { | |
// Try different selectors that might match the tile elements | |
const selectors = [ | |
'.board .tile', // Common class naming | |
'[data-value]', // Elements with data-value attribute | |
'[class*="tile"]', // Elements with "tile" in the class name | |
'.grid-container > div', // Grid container children | |
'.game-board > div', // Game board children | |
'[role="button"]', // Interactive elements | |
]; | |
for (const selector of selectors) { | |
const elements = document.querySelectorAll(selector); | |
if (elements.length >= 9) { // Expecting at least a 3x3 grid | |
console.log(`Found ${elements.length} tiles with selector: ${selector}`); | |
return Array.from(elements); | |
} | |
} | |
// Fallback: find elements that look like game tiles based on their appearance | |
const allElements = document.querySelectorAll('div'); | |
const possibleTiles = Array.from(allElements).filter(el => { | |
const style = window.getComputedStyle(el); | |
const rect = el.getBoundingClientRect(); | |
// Look for square elements with similar dimensions that are visible | |
return rect.width > 20 && | |
rect.height > 20 && | |
Math.abs(rect.width - rect.height) < 10 && | |
style.display !== 'none' && | |
style.visibility !== 'hidden'; | |
}); | |
// Group elements by size to find the most common size (likely the game tiles) | |
const sizeGroups = {}; | |
possibleTiles.forEach(el => { | |
const width = Math.round(el.getBoundingClientRect().width); | |
sizeGroups[width] = sizeGroups[width] || []; | |
sizeGroups[width].push(el); | |
}); | |
// Find the size group with the most elements | |
let maxGroup = []; | |
Object.values(sizeGroups).forEach(group => { | |
if (group.length > maxGroup.length) maxGroup = group; | |
}); | |
console.log(`Found ${maxGroup.length} potential tiles based on size analysis`); | |
return maxGroup; | |
} | |
// Find all possible moves on the board | |
function findPossibleMoves() { | |
const tiles = getBoardElements(); | |
const moves = []; | |
// Create a grid representation | |
const size = Math.sqrt(tiles.length); | |
if (size % 1 !== 0) { | |
console.log(`Warning: Tile count (${tiles.length}) is not a perfect square. Using estimated size: ${Math.floor(size)}`); | |
} | |
const gridSize = Math.floor(size); | |
const grid = []; | |
for (let i = 0; i < gridSize; i++) { | |
grid[i] = []; | |
for (let j = 0; j < gridSize; j++) { | |
const index = i * gridSize + j; | |
if (index < tiles.length) { | |
// Try to get the tile value from different sources | |
let value = null; | |
// Try data-value attribute | |
if (tiles[index].hasAttribute('data-value')) { | |
value = parseInt(tiles[index].getAttribute('data-value'), 10); | |
} | |
// Try innerText | |
else if (tiles[index].innerText) { | |
value = parseInt(tiles[index].innerText.trim(), 10); | |
} | |
// Try aria-label | |
else if (tiles[index].hasAttribute('aria-label')) { | |
const label = tiles[index].getAttribute('aria-label'); | |
const match = label.match(/\d+/); | |
if (match) value = parseInt(match[0], 10); | |
} | |
// If we still can't determine the value, try to get it from the CSS classes | |
if (isNaN(value) || value === null) { | |
const classes = tiles[index].className.split(' '); | |
for (const cls of classes) { | |
const match = cls.match(/value-(\d+)/); | |
if (match) { | |
value = parseInt(match[1], 10); | |
break; | |
} | |
} | |
} | |
// If still no value, try to get it from child elements | |
if (isNaN(value) || value === null) { | |
const children = tiles[index].querySelectorAll('*'); | |
for (const child of children) { | |
if (child.innerText && !isNaN(parseInt(child.innerText.trim(), 10))) { | |
value = parseInt(child.innerText.trim(), 10); | |
break; | |
} | |
} | |
} | |
// Default to 0 if still no value | |
if (isNaN(value) || value === null) { | |
value = 0; | |
} | |
grid[i][j] = { | |
element: tiles[index], | |
value: value, | |
x: j, | |
y: i | |
}; | |
} | |
} | |
} | |
// Print the grid for debugging | |
console.log("Grid values:"); | |
const gridValuesStr = grid.map(row => row.map(cell => cell.value).join(' ')).join('\n'); | |
console.log(gridValuesStr); | |
// Check all possible swaps | |
for (let i = 0; i < gridSize; i++) { | |
for (let j = 0; j < gridSize; j++) { | |
// Check right swap | |
if (j < gridSize - 1) { | |
checkAndAddMove(grid, i, j, i, j + 1, moves); | |
} | |
// Check down swap | |
if (i < gridSize - 1) { | |
checkAndAddMove(grid, i, j, i + 1, j, moves); | |
} | |
} | |
} | |
console.log(`Found ${moves.length} possible moves`); | |
return moves; | |
} | |
// Check if swapping two tiles creates a valid move | |
function checkAndAddMove(grid, y1, x1, y2, x2, moves) { | |
const temp = grid[y1][x1].value; | |
grid[y1][x1].value = grid[y2][x2].value; | |
grid[y2][x2].value = temp; | |
// Check for at least 3 in a row or column after swap | |
if (hasMatch(grid, y1, x1) || hasMatch(grid, y2, x2)) { | |
moves.push({ | |
from: grid[y1][x1].element, | |
to: grid[y2][x2].element, | |
score: calculateMoveScore(grid, y1, x1, y2, x2) | |
}); | |
} | |
// Swap back | |
grid[y2][x2].value = grid[y1][x1].value; | |
grid[y1][x1].value = temp; | |
} | |
// Check if there's a match at the given position | |
function hasMatch(grid, y, x) { | |
const value = grid[y][x].value; | |
if (value === 0) return false; | |
// Check horizontal matches | |
let horizontalCount = 1; | |
// Check left | |
for (let j = x - 1; j >= 0 && grid[y][j] && grid[y][j].value === value; j--) { | |
horizontalCount++; | |
} | |
// Check right | |
for (let j = x + 1; j < grid[0].length && grid[y][j] && grid[y][j].value === value; j++) { | |
horizontalCount++; | |
} | |
// Check vertical matches | |
let verticalCount = 1; | |
// Check up | |
for (let i = y - 1; i >= 0 && grid[i] && grid[i][x] && grid[i][x].value === value; i--) { | |
verticalCount++; | |
} | |
// Check down | |
for (let i = y + 1; i < grid.length && grid[i] && grid[i][x] && grid[i][x].value === value; i++) { | |
verticalCount++; | |
} | |
return horizontalCount >= 3 || verticalCount >= 3; | |
} | |
// Calculate a score for a move to prioritize better moves | |
function calculateMoveScore(grid, y1, x1, y2, x2) { | |
let score = 0; | |
const val1 = grid[y1][x1].value; | |
const val2 = grid[y2][x2].value; | |
// Check matches after swap in both directions | |
const directions = [ | |
{ y: y1, x: x1 }, | |
{ y: y2, x: x2 } | |
]; | |
for (const pos of directions) { | |
// Check horizontal matches | |
let hCount = 1; | |
let hSum = grid[pos.y][pos.x].value; | |
// Check left | |
for (let j = pos.x - 1; j >= 0 && grid[pos.y][j] && grid[pos.y][j].value === grid[pos.y][pos.x].value; j--) { | |
hCount++; | |
hSum += grid[pos.y][j].value; | |
} | |
// Check right | |
for (let j = pos.x + 1; j < grid[0].length && grid[pos.y][j] && grid[pos.y][j].value === grid[pos.y][pos.x].value; j++) { | |
hCount++; | |
hSum += grid[pos.y][j].value; | |
} | |
// Check vertical matches | |
let vCount = 1; | |
let vSum = grid[pos.y][pos.x].value; | |
// Check up | |
for (let i = pos.y - 1; i >= 0 && grid[i] && grid[i][pos.x] && grid[i][pos.x].value === grid[pos.y][pos.x].value; i--) { | |
vCount++; | |
vSum += grid[i][pos.x].value; | |
} | |
// Check down | |
for (let i = pos.y + 1; i < grid.length && grid[i] && grid[i][pos.x] && grid[i][pos.x].value === grid[pos.y][pos.x].value; i++) { | |
vCount++; | |
vSum += grid[i][pos.x].value; | |
} | |
// Add to score if there's a match | |
if (hCount >= 3) { | |
// More tiles and higher values are better | |
score += hCount * hSum; | |
} | |
if (vCount >= 3) { | |
score += vCount * vSum; | |
} | |
} | |
return score; | |
} | |
// Perform a move by clicking or simulating swipe | |
function performMove(move) { | |
console.log(`Making move: ${moveCount}`); | |
// Click on the first tile | |
simulateClick(move.from); | |
// Wait a bit and click on the second tile | |
setTimeout(() => { | |
simulateClick(move.to); | |
// Wait for animations to complete | |
setTimeout(() => { | |
if (running) makeNextMove(); | |
}, config.moveDelay); | |
}, 100); | |
} | |
// Simulate a mouse click on an element | |
function simulateClick(element) { | |
const rect = element.getBoundingClientRect(); | |
const x = rect.left + rect.width / 2; | |
const y = rect.top + rect.height / 2; | |
const mouseDownEvent = new MouseEvent('mousedown', { | |
bubbles: true, | |
cancelable: true, | |
view: window, | |
clientX: x, | |
clientY: y | |
}); | |
const mouseUpEvent = new MouseEvent('mouseup', { | |
bubbles: true, | |
cancelable: true, | |
view: window, | |
clientX: x, | |
clientY: y | |
}); | |
const clickEvent = new MouseEvent('click', { | |
bubbles: true, | |
cancelable: true, | |
view: window, | |
clientX: x, | |
clientY: y | |
}); | |
element.dispatchEvent(mouseDownEvent); | |
element.dispatchEvent(mouseUpEvent); | |
element.dispatchEvent(clickEvent); | |
} | |
// Make the next move | |
function makeNextMove() { | |
if (!running || moveCount >= config.maxMoves) { | |
if (moveCount >= config.maxMoves) { | |
console.log("Reached maximum move limit. Stopping."); | |
} | |
running = false; | |
return; | |
} | |
moveCount++; | |
const possibleMoves = findPossibleMoves(); | |
if (possibleMoves.length > 0) { | |
// Sort moves by score to make the best move | |
possibleMoves.sort((a, b) => b.score - a.score); | |
performMove(possibleMoves[0]); | |
} else { | |
console.log("No valid moves found. Game might be over."); | |
running = false; | |
} | |
} | |
// Create public API | |
window.autoPlayExponenTile = { | |
start: () => { | |
moveCount = 0; | |
running = true; | |
makeNextMove(); | |
}, | |
stop: () => { | |
running = false; | |
console.log("Automation stopped."); | |
}, | |
toggle: () => { | |
running = !running; | |
console.log(`ExponenTile automation ${running ? 'started' : 'paused'}`); | |
if (running) makeNextMove(); | |
} | |
}; | |
console.log("ExponenTile automation v2 ready! Type autoPlayExponenTile.start() to begin playing automatically."); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment