wangfukang 4 days ago
parent
commit
33434c3ba4
  1. 205
      package1/planet/adventure.vue

205
package1/planet/adventure.vue

@ -54,7 +54,14 @@
<view class="tips">
<text>规则点选未被压住的卡牌底部槽内3张相同自动消除槽满则失败</text>
<text>地狱难度 · 剩余 {{remainingCount}} · 当前可点 {{availableCount}} · {{riskText}}</text>
<text>陷阱难度 · 剩余 {{remainingCount}} · 当前可点 {{availableCount}} · {{riskText}}</text>
</view>
<view class="debug-panel">
<view><text>{{availableCount}}</text><text>可点</text></view>
<view><text>{{slots.length}}/{{slotLimit}}</text><text>槽位</text></view>
<view><text>{{directComboCount}}</text><text>可消</text></view>
<view><text>{{trapGroupCount}}</text><text>陷阱组</text></view>
</view>
<view class="board">
@ -139,7 +146,7 @@
startTs: 0,
cards: [],
slots: [],
slotLimit: 5,
slotLimit: 7,
moveCount: 0,
modal: { show: false, title: '', sub: '' }
}
@ -181,6 +188,22 @@
availableCount() {
return this.cards.filter(item => !item.removed && !item.selected && !this.isLocked(item)).length
},
directComboCount() {
const map = {}
this.cards.forEach(item => {
if (!item.removed && !item.selected && !this.isLocked(item)) {
map[item.icon] = (map[item.icon] || 0) + 1
}
})
return Object.keys(map).filter(k => map[k] >= 3).length
},
trapGroupCount() {
const map = {}
this.cards.forEach(item => {
if (item.trap) map[item.icon] = true
})
return Object.keys(map).length
},
riskText() {
if (this.slots.length >= this.slotLimit - 1) return '危险:差1格就满'
if (this.slots.length >= this.slotLimit - 2) return '注意槽位'
@ -256,12 +279,12 @@
})
},
buildLevel() {
const icons = ['🍔', '🥤', '📚', '🏀', '🎧', '🚲', '🍜', '📦', '☕', '🧋', '🎮', '🌟', '🍟', '🥪']
const icons = ['🍔', '🥤', '📚', '🏀', '🎧', '🚲', '🍜', '📦', '☕', '🧋']
let cards = []
for (let attempt = 0; attempt < 8; attempt++) {
const seed = this.levelSeed() + '_try_' + attempt
const positions = this.buildCardPositions(seed)
cards = this.assignSolvableIcons(positions, icons, seed)
cards = this.assignTrapLevelIcons(positions, icons, seed)
if (cards.length) break
}
if (!cards.length) {
@ -273,25 +296,29 @@
},
buildCardPositions(seed) {
const cards = []
const layerCounts = [42, 36, 30, 18]
const template = this.levelTemplate('hell')
const layerCounts = template.layerCounts
const rand = this.seededRandom(seed + '_pos')
let idx = 0
layerCounts.forEach((count, layer) => {
for (let i = 0; i < count; i++) {
const col = i % 7
const row = Math.floor(i / 7)
const zone = template.zones[layer]
const col = i % zone.cols
const row = Math.floor(i / zone.cols)
const dx = Math.floor(rand() * 18)
const dy = Math.floor(rand() * 14)
const card = {
id: 'c' + idx,
icon: '',
layer,
x: 12 + col * 70 + (layer * 16) + ((row % 2) * 10) + dx,
y: 16 + row * 62 + (layer * 34) + dy,
x: zone.x + col * zone.gapX + (layer * zone.offsetX) + ((row % 2) * zone.stagger) + dx,
y: zone.y + row * zone.gapY + (layer * zone.offsetY) + dy,
style: '',
removed: false,
selected: false,
solutionStep: 0
solutionStep: 0,
trap: false,
keyCard: false
}
card.style = `left:${card.x}rpx;top:${card.y}rpx;z-index:${card.layer + 1};`
cards.push(card)
@ -300,6 +327,43 @@
})
return cards
},
levelTemplate(type) {
const templates = {
simple: {
layerCounts: [36, 30, 24],
zones: [
{ x: 20, y: 18, cols: 6, gapX: 82, gapY: 72, offsetX: 8, offsetY: 18, stagger: 8 },
{ x: 70, y: 72, cols: 5, gapX: 78, gapY: 70, offsetX: 10, offsetY: 22, stagger: 10 },
{ x: 96, y: 110, cols: 5, gapX: 74, gapY: 66, offsetX: 12, offsetY: 24, stagger: 10 }
]
},
normal: {
layerCounts: [36, 30, 24],
zones: [
{ x: 18, y: 18, cols: 6, gapX: 80, gapY: 70, offsetX: 12, offsetY: 24, stagger: 10 },
{ x: 78, y: 76, cols: 5, gapX: 76, gapY: 66, offsetX: 14, offsetY: 28, stagger: 12 },
{ x: 110, y: 116, cols: 4, gapX: 74, gapY: 62, offsetX: 16, offsetY: 30, stagger: 12 }
]
},
difficult: {
layerCounts: [36, 30, 24],
zones: [
{ x: 14, y: 16, cols: 6, gapX: 80, gapY: 70, offsetX: 14, offsetY: 28, stagger: 12 },
{ x: 92, y: 76, cols: 5, gapX: 70, gapY: 62, offsetX: 18, offsetY: 32, stagger: 14 },
{ x: 132, y: 122, cols: 4, gapX: 66, gapY: 58, offsetX: 20, offsetY: 34, stagger: 14 }
]
},
hell: {
layerCounts: [36, 30, 24],
zones: [
{ x: 12, y: 16, cols: 6, gapX: 82, gapY: 72, offsetX: 10, offsetY: 22, stagger: 10 },
{ x: 86, y: 72, cols: 5, gapX: 70, gapY: 60, offsetX: 18, offsetY: 34, stagger: 16 },
{ x: 126, y: 112, cols: 4, gapX: 64, gapY: 54, offsetX: 22, offsetY: 38, stagger: 16 }
]
}
}
return templates[type] || templates.hell
},
assignSolvableIcons(positions, icons, seed) {
// 线
//
@ -330,8 +394,121 @@
})
return positions
},
assignTrapLevelIcons(positions, icons, seed) {
const rand = this.seededRandom(seed + '_trap')
const cards = positions.slice()
const trapIcons = this.seededShuffle(icons.slice(), seed + '_trap_icons').slice(0, 4)
const remaining = cards.slice()
const groups = []
const trapPlans = this.buildTrapPlans(cards, trapIcons, seed)
if (trapPlans.length < 4) return []
while (remaining.length) {
const available = remaining.filter(card => !this.isBlockedIn(card, remaining))
if (available.length < 3) return []
const readyTrap = trapPlans.find(plan => {
return !plan.used && plan.cards.every(card => remaining.indexOf(card) >= 0) && !this.isBlockedIn(plan.key, remaining)
})
const normalAvailable = available.filter(card => !card.reservedTrap)
const picked = readyTrap ? readyTrap.cards : this.pickSolutionTriple(normalAvailable, rand)
if (!picked || picked.length < 3) return []
groups.push({
cards: picked,
trapIcon: readyTrap ? readyTrap.icon : ''
})
if (readyTrap) readyTrap.used = true
picked.forEach(card => {
const idx = remaining.indexOf(card)
if (idx >= 0) remaining.splice(idx, 1)
})
}
if (trapPlans.some(plan => !plan.used)) return []
const trapIndexes = []
groups.forEach((group, index) => {
if (group.trapIcon) trapIndexes.push(index)
})
const groupIcons = this.buildPressureIconOrder(groups.length, icons, trapIcons, trapIndexes, seed)
groups.forEach((group, step) => {
const icon = group.trapIcon || groupIcons[step] || icons[step % icons.length]
group.cards.forEach(card => {
card.icon = icon
card.solutionStep = step + 1
if (group.trapIcon) {
card.trap = true
}
})
})
cards.forEach(card => {
card.reservedTrap = false
card.style = `left:${card.x}rpx;top:${card.y}rpx;z-index:${card.layer + 1};`
})
return cards
},
buildTrapPlans(cards, trapIcons, seed) {
const startAvailable = cards.filter(card => !this.isBlockedIn(card, cards))
const baitPool = this.seededShuffle(startAvailable.filter(card => !this.isCenterCard(card)), seed + '_bait')
const keyPool = this.seededShuffle(cards.filter(card => card.layer === 0 && this.isCenterCard(card)), seed + '_key')
const plans = []
trapIcons.forEach(icon => {
const baitA = baitPool.shift()
const baitB = baitPool.shift()
const key = keyPool.shift()
if (!baitA || !baitB || !key) return
;[baitA, baitB, key].forEach(card => {
card.reservedTrap = true
card.trap = true
})
key.keyCard = true
plans.push({
icon,
cards: [baitA, baitB, key],
key,
used: false
})
})
return plans
},
buildPressureIconOrder(count, icons, trapIcons, trapIndexes, seed) {
const rand = this.seededRandom(seed + '_pressure')
const safeIcons = icons.filter(icon => trapIcons.indexOf(icon) < 0)
const order = new Array(count)
trapIndexes.forEach((idx, i) => {
order[idx] = trapIcons[i % trapIcons.length]
})
const usage = {}
trapIcons.forEach(icon => {
usage[icon] = 1
})
for (let i = 0; i < count; i++) {
if (order[i]) continue
const pool = i < 12 ? icons : safeIcons
let icon = pool[i % pool.length]
let guard = 0
while ((usage[icon] || 0) >= 3 && guard < pool.length + icons.length) {
icon = pool[(i + guard + 1) % pool.length]
guard++
}
if ((usage[icon] || 0) >= 3) {
icon = icons.find(item => (usage[item] || 0) < 3) || icons[i % icons.length]
}
order[i] = icon
usage[icon] = (usage[icon] || 0) + 1
}
for (let i = 0; i < order.length; i += 6) {
const part = order.slice(i, i + 6)
this.shuffleWithRandom(part, rand)
for (let j = 0; j < part.length; j++) order[i + j] = part[j]
}
return order
},
isCenterCard(card) {
return card.x >= 160 && card.x <= 400 && card.y >= 120 && card.y <= 440
},
pickSolutionTriple(available, rand) {
const pool = available.slice()
if (pool.length < 3) return []
this.shuffleWithRandom(pool, rand)
const first = pool.shift()
pool.sort((a, b) => this.cardDistanceScore(b, first) - this.cardDistanceScore(a, first))
@ -525,6 +702,10 @@
.hero-title { margin-top: 10rpx; font-size: 42rpx; font-weight: 900; }
.hero-sub, .tips { margin-top: 16rpx; color: #6B817D; font-size: 24rpx; line-height: 1.55; }
.tips { display: flex; flex-direction: column; gap: 6rpx; }
.debug-panel { margin-top: 16rpx; display: flex; gap: 10rpx; }
.debug-panel view { flex: 1; border-radius: 20rpx; padding: 12rpx 6rpx; background: rgba(255,255,255,0.76); text-align: center; border: 1rpx solid rgba(53,214,166,0.16); }
.debug-panel text:first-child { display: block; color: #22B889; font-size: 28rpx; font-weight: 900; }
.debug-panel text:last-child { display: block; margin-top: 4rpx; color: #8AA09C; font-size: 20rpx; }
.college-picker { margin-top: 18rpx; padding: 18rpx 22rpx; border-radius: 26rpx; background: rgba(255,255,255,0.72); display: flex; align-items: center; justify-content: space-between; color: #42635E; font-size: 24rpx; }
.college-picker text:last-child { color: #22B889; font-weight: 900; }
.rocket { margin-top: 22rpx; display: flex; align-items: center; gap: 16rpx; }
@ -553,8 +734,8 @@
.slot-wrap { margin-top: 24rpx; }
.slot-title { color: #6B817D; font-size: 24rpx; margin-bottom: 14rpx; }
.slots { display: flex; gap: 10rpx; }
.slot { flex: 1; height: 74rpx; border-radius: 20rpx; background: rgba(255,255,255,0.8); border: 2rpx dashed rgba(53,214,166,0.28); display: flex; align-items: center; justify-content: center; }
.slot text { font-size: 38rpx; }
.slot { flex: 1; height: 66rpx; border-radius: 18rpx; background: rgba(255,255,255,0.8); border: 2rpx dashed rgba(53,214,166,0.28); display: flex; align-items: center; justify-content: center; }
.slot text { font-size: 34rpx; }
.actions { margin-top: 28rpx; display: flex; gap: 18rpx; }
.sub-btn, .main-btn { flex: 1; height: 82rpx; line-height: 82rpx; text-align: center; border-radius: 999rpx; font-size: 28rpx; font-weight: 900; }
.sub-btn { background: rgba(255,255,255,0.78); color: #42635E; }

Loading…
Cancel
Save