Last active
February 7, 2025 06:05
-
-
Save YangSeungWon/4d2f9504f36609d2b9bf916c3647482e to your computer and use it in GitHub Desktop.
Fruit box helper - 사과게임 도움 스크립트
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
(function () { | |
console.log("[Fruit Box Helper] 스크립트 시작"); | |
let lastMapState = ''; | |
let gridWidth = null; | |
let gridHeight = null; | |
let gridMinX = null; | |
let gridMinY = null; | |
// 게임 객체 감시 함수 | |
function waitForGame(callback) { | |
const interval = 200; // 200ms마다 체크 | |
setInterval(() => { | |
if (window.exportRoot?.mm?.mg) { | |
callback(window.exportRoot.mm.mg); | |
} | |
}, interval); | |
} | |
// 격자 크기 초기화 | |
function initializeGrid(mg) { | |
let minX = Infinity, maxX = -Infinity; | |
let minY = Infinity, maxY = -Infinity; | |
// 범위 찾기 | |
for (const prop in mg) { | |
if (mg.hasOwnProperty(prop) && prop.indexOf("mk") === 0) { | |
const apple = mg[prop]; | |
if (apple && typeof apple.nu === 'number') { | |
minX = Math.min(minX, apple.x); | |
maxX = Math.max(maxX, apple.x); | |
minY = Math.min(minY, apple.y); | |
maxY = Math.max(maxY, apple.y); | |
} | |
} | |
} | |
gridWidth = (maxX - minX) / (17 - 1); | |
gridHeight = (maxY - minY) / (10 - 1); | |
gridMinX = minX; | |
gridMinY = minY; | |
console.log("격자 크기 초기화 완료"); | |
} | |
// 사과 데이터 추출 함수 | |
function extractMapData(mg) { | |
const apples = []; | |
// 격자 크기가 아직 계산되지 않았다면 초기화 | |
if (gridWidth === null) { | |
initializeGrid(mg); | |
} | |
// 2D 배열 초기화 (10x17 크기) | |
const map = Array(10).fill().map(() => Array(17).fill(null)); | |
// 사과 데이터 채우기 | |
for (const prop in mg) { | |
if (mg.hasOwnProperty(prop) && prop.indexOf("mk") === 0) { | |
const apple = mg[prop]; | |
if (apple && typeof apple.nu === 'number' && !apple.flDroped) { | |
const col = Math.floor((apple.x - gridMinX) / gridWidth); | |
const row = Math.floor((apple.y - gridMinY) / gridHeight); | |
if (row >= 0 && row < 10 && col >= 0 && col < 17) { | |
map[row][col] = apple.nu; | |
apples.push({ row, col, num: apple.nu }); | |
} | |
} | |
} | |
} | |
return { map, apples }; | |
} | |
function findAllCombinations(apples) { | |
const combinations = []; | |
for (let i = 0; i < apples.length; i++) { | |
for (let j = i + 1; j < apples.length; j++) { | |
const apple1 = apples[i]; | |
const apple2 = apples[j]; | |
// 사각형 영역 계산 | |
const minRow = Math.min(apple1.row, apple2.row); | |
const maxRow = Math.max(apple1.row, apple2.row); | |
const minCol = Math.min(apple1.col, apple2.col); | |
const maxCol = Math.max(apple1.col, apple2.col); | |
// 영역 내 사과들 찾기 | |
const applesInRect = apples.filter(a => | |
a.row >= minRow && a.row <= maxRow && | |
a.col >= minCol && a.col <= maxCol | |
); | |
const sum = applesInRect.reduce((acc, a) => acc + a.num, 0); | |
if (sum === 10) { | |
const maxNum = Math.max(...applesInRect.map(a => a.num)); | |
// 적은 개수의 사과 우선, 같은 개수면 큰 숫자 우선. 1과 2는 아끼기 | |
let score = 1000 | |
score -= applesInRect.length * 100 | |
score -= applesInRect.filter(a => a.num === 1).length * 50 | |
score -= applesInRect.filter(a => a.num === 2).length * 30 | |
score -= Math.abs(5 - minRow) * 5 | |
score -= Math.abs(8 - minCol) * 5 | |
score -= Math.abs(5 - maxRow) * 5 | |
score -= Math.abs(8 - maxCol) * 5 | |
score += (maxRow - minRow) * 20 | |
score += (maxCol - minCol) * 20 | |
score += maxNum | |
combinations.push({ | |
apples: applesInRect, | |
score: score | |
}); | |
} | |
} | |
} | |
// 점수순으로 정렬 | |
combinations.sort((a, b) => b.score - a.score); | |
return combinations; | |
} | |
function createController() { | |
removeController(); | |
const controller = document.createElement('div'); | |
controller.id = 'fruit-box-controller'; | |
const canvas = document.querySelector('canvas'); | |
const rect = canvas.getBoundingClientRect(); | |
Object.assign(controller.style, { | |
position: 'absolute', | |
left: `${rect.left}px`, | |
top: `${rect.bottom + 10}px`, | |
padding: '10px', | |
backgroundColor: 'rgba(0, 0, 0, 0.8)', | |
borderRadius: '5px', | |
color: 'white', | |
fontFamily: 'Arial', | |
fontSize: '14px', | |
display: 'flex', | |
alignItems: 'center', | |
gap: '10px', | |
zIndex: '10000' | |
}); | |
const label = document.createElement('label'); | |
label.textContent = '표시할 조합 수: '; | |
const slider = document.createElement('input'); | |
Object.assign(slider, { | |
type: 'range', | |
min: '1', | |
max: '10', | |
value: maxDisplayCount, | |
style: 'width: 100px' | |
}); | |
const valueDisplay = document.createElement('span'); | |
valueDisplay.textContent = maxDisplayCount; | |
slider.addEventListener('input', (e) => { | |
maxDisplayCount = parseInt(e.target.value); | |
valueDisplay.textContent = maxDisplayCount; | |
// 강제로 lastMapState를 초기화하여 다시 렌더링되도록 함 | |
lastMapState = ''; | |
if (lastCombinations) { | |
createOverlays(lastCombinations); | |
// 콘솔 출력도 업데이트 | |
console.clear(); | |
console.log('=== Fruit Box 맵 ==='); | |
console.log(mapToString(lastMap, lastCombinations)); | |
console.log('\n=== 발견된 조합들 ==='); | |
lastCombinations.slice(0, maxDisplayCount).forEach((combo, index) => { | |
const maxNum = Math.max(...combo.apples.map(c => c.num)); | |
console.log(`${index + 1}. ${combo.apples.map(c => c.num).join(' + ')} = 10 (최대 숫자: ${maxNum})`); | |
}); | |
} | |
}); | |
controller.appendChild(label); | |
controller.appendChild(slider); | |
controller.appendChild(valueDisplay); | |
document.body.appendChild(controller); | |
} | |
function removeController() { | |
const existing = document.getElementById('fruit-box-controller'); | |
if (existing) { | |
existing.remove(); | |
} | |
} | |
// 전역 변수 추가 | |
let maxDisplayCount = 3; // 기본값 | |
let lastMap = null; | |
let lastCombinations = null; | |
function createOverlays(combinations) { | |
removeOverlays(); | |
// maxDisplayCount만큼만 표시 | |
combinations.slice(0, maxDisplayCount).forEach((combo, index) => { | |
const overlay = document.createElement('div'); | |
overlay.id = `fruit-box-overlay-${index}`; | |
const minRow = Math.min(...combo.apples.map(a => a.row)); | |
const maxRow = Math.max(...combo.apples.map(a => a.row)); | |
const minCol = Math.min(...combo.apples.map(a => a.col)); | |
const maxCol = Math.max(...combo.apples.map(a => a.col)); | |
const canvas = document.querySelector('canvas'); | |
const rect = canvas.getBoundingClientRect(); | |
const canvasPaddingLeft = 67; | |
const canvasPaddingTop = 73; | |
const canvasPaddingRight = 88; | |
const canvasPaddingBottom = 68; | |
const cellWidth = (rect.width - canvasPaddingLeft - canvasPaddingRight) / 17; | |
const cellHeight = (rect.height - canvasPaddingTop - canvasPaddingBottom) / 10; | |
const isBest = index === 0; | |
Object.assign(overlay.style, { | |
position: 'absolute', | |
left: `${rect.left + minCol * cellWidth + canvasPaddingLeft + 4}px`, | |
top: `${rect.top + minRow * cellHeight + canvasPaddingTop + 4}px`, | |
width: `${(maxCol - minCol + 1) * cellWidth - 8}px`, | |
height: `${(maxRow - minRow + 1) * cellHeight - 8}px`, | |
border: `3px solid ${isBest ? 'black' : '#666666'}`, | |
backgroundColor: isBest ? 'rgba(255, 255, 0, 0.2)' : 'rgba(128, 128, 128, 0.2)', | |
pointerEvents: 'none', | |
zIndex: isBest ? '9999' : '9998', | |
boxSizing: 'border-box' | |
}); | |
document.body.appendChild(overlay); | |
}); | |
} | |
function removeOverlays() { | |
const overlays = document.querySelectorAll('[id^="fruit-box-overlay-"]'); | |
overlays.forEach(overlay => overlay.remove()); | |
} | |
function mapToString(map, combinations) { | |
return map.map((row, rowIdx) => { | |
return row.map((cell, colIdx) => { | |
if (cell === null) return ' · '; | |
const isHighlighted = combinations[0]?.apples.some(h => h.row === rowIdx && h.col === colIdx); | |
if (isHighlighted) { | |
return `\x1b[1m\x1b[33m[${cell}]\x1b[0m`; | |
} | |
return ` ${cell} `; | |
}).join(''); | |
}).join('\n'); | |
} | |
function renderMap(map, combinations) { | |
lastMap = map; | |
lastCombinations = combinations; | |
const mapStr = mapToString(map, combinations); | |
const fullDisplay = mapStr; | |
if (fullDisplay !== lastMapState) { | |
console.clear(); | |
console.log('=== Fruit Box 맵 ==='); | |
console.log(fullDisplay); | |
if (combinations.length > 0) { | |
console.log('\n=== 발견된 조합들 ==='); | |
combinations.slice(0, maxDisplayCount).forEach((combo, index) => { | |
const maxNum = Math.max(...combo.apples.map(c => c.num)); | |
console.log(`${index + 1}. ${combo.apples.map(c => c.num).join(' + ')} = 10 (최대 숫자: ${maxNum})`); | |
}); | |
createOverlays(combinations); | |
createController(); | |
} else { | |
removeOverlays(); | |
removeController(); | |
} | |
lastMapState = fullDisplay; | |
} | |
} | |
// 메인 실행 로직 | |
waitForGame((mg) => { | |
const { map, apples } = extractMapData(mg); | |
const combinations = findAllCombinations(apples); | |
renderMap(map, combinations); | |
}); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
이거 써보고싶어서 가입했는데요.. 할 줄을 아예 모르겠어요.. 그냥 다운받아서 fruit-box-helper 실행하니까 "4행7번째줄 ";"가 필요합니다."라고 오류메세지 나오고 그래서 찾아보다가 뒤지다가 크롬 확장프로그램 Tampermonkey 받아서 여기다가 스크립트 입력하라고 해서 했는데도 안되더라구요. 이거 혹시 어떻게 해야 쓸 수 있나요 ㅠ? 바보라서 죄송합니다