Align Pyramid Solitaire with standard rules and layout
This commit is contained in:
parent
0fcd6b190d
commit
91d4bcc25f
32
pyramid.css
32
pyramid.css
@ -1,4 +1,4 @@
|
||||
.pyramid-board {
|
||||
#pyramid-board {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
@ -15,23 +15,33 @@
|
||||
max-width: 1000px;
|
||||
}
|
||||
|
||||
.pyramid-layout .card {
|
||||
position: absolute; /* Essential for correct placement in layout */
|
||||
transition: all 0.4s cubic-bezier(0.2, 0.8, 0.2, 1);
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.pyramid-bottom-piles {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
gap: 100px;
|
||||
gap: 80px;
|
||||
align-items: flex-start;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.discard-pile-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pyramid-layout .card {
|
||||
transition: all 0.4s cubic-bezier(0.2, 0.8, 0.2, 1);
|
||||
z-index: 100;
|
||||
/* Make stock look like a stack */
|
||||
#pyramid-stock::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
left: 4px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 2px solid var(--line);
|
||||
border-radius: var(--card-radius);
|
||||
z-index: -1;
|
||||
background: var(--card-bg);
|
||||
}
|
||||
|
||||
.card.is-selected {
|
||||
|
||||
71
pyramid.js
71
pyramid.js
@ -144,47 +144,50 @@
|
||||
const boardRect = gameBoard.getBoundingClientRect();
|
||||
const layoutRect = pyramidLayout.getBoundingClientRect();
|
||||
|
||||
// If the board isn't visible yet, wait for the next frame
|
||||
if (boardRect.width === 0) {
|
||||
// If the board isn't visible yet or dimensions aren't ready, wait for the next frame
|
||||
if (boardRect.width === 0 || layoutRect.width === 0) {
|
||||
requestAnimationFrame(renderBoard);
|
||||
return;
|
||||
}
|
||||
|
||||
const rootStyle = getComputedStyle(document.documentElement);
|
||||
let cardWidth = parseFloat(rootStyle.getPropertyValue('--card-width'));
|
||||
|
||||
// Fallback for cardWidth if parsing fails
|
||||
if (isNaN(cardWidth) || cardWidth === 0) {
|
||||
cardWidth = 90;
|
||||
}
|
||||
if (isNaN(cardWidth) || cardWidth === 0) cardWidth = 80;
|
||||
|
||||
const cardHeight = cardWidth * 1.4;
|
||||
const rowOverlap = 0.45;
|
||||
const horizontalGap = 10;
|
||||
const horizontalGap = cardWidth * 0.15; // Proportional gap
|
||||
|
||||
// Position Pyramid Cards
|
||||
let index = 0;
|
||||
const boardTop = boardRect.top;
|
||||
const boardLeft = boardRect.left;
|
||||
|
||||
// Position Pyramid Cards
|
||||
let index = 0;
|
||||
for (let row = 0; row < 7; row++) {
|
||||
const rowWidth = (row + 1) * cardWidth + row * horizontalGap;
|
||||
const startX = (layoutRect.width - rowWidth) / 2;
|
||||
// Calculate total width of this row to center it
|
||||
const numCards = row + 1;
|
||||
const totalRowWidth = numCards * cardWidth + (numCards - 1) * horizontalGap;
|
||||
const startX = (layoutRect.width - totalRowWidth) / 2;
|
||||
const y = row * (cardHeight * rowOverlap);
|
||||
|
||||
for (let i = 0; i <= row; i++) {
|
||||
const card = pyramid[index];
|
||||
if (card) {
|
||||
const el = cardElements[card.id];
|
||||
// Calculate final positions relative to the board
|
||||
// Absolute position relative to the gameBoard
|
||||
const finalTop = y + (layoutRect.top - boardTop);
|
||||
const finalLeft = startX + i * (cardWidth + horizontalGap) + (layoutRect.left - boardLeft);
|
||||
|
||||
el.style.top = `${finalTop}px`;
|
||||
el.style.left = `${finalLeft}px`;
|
||||
el.style.zIndex = 100 + row;
|
||||
el.style.zIndex = 100 + index;
|
||||
el.classList.add('is-flipped');
|
||||
el.draggable = false;
|
||||
|
||||
// Mark if it's currently playable for visual feedback
|
||||
const exposed = isExposed(card.id);
|
||||
el.classList.toggle('is-exposed', exposed);
|
||||
el.style.opacity = exposed ? '1' : '0.85';
|
||||
// el.style.filter = exposed ? 'none' : 'brightness(0.8)'; // Optional: dim unexposed cards
|
||||
}
|
||||
index++;
|
||||
}
|
||||
@ -194,32 +197,36 @@
|
||||
const sRect = stockEl.getBoundingClientRect();
|
||||
stock.forEach((card, i) => {
|
||||
const el = cardElements[card.id];
|
||||
el.style.top = `${sRect.top - boardRect.top + PILE_BORDER_WIDTH}px`;
|
||||
el.style.left = `${sRect.left - boardRect.left + PILE_BORDER_WIDTH}px`;
|
||||
el.style.top = `${sRect.top - boardTop + PILE_BORDER_WIDTH}px`;
|
||||
el.style.left = `${sRect.left - boardLeft + PILE_BORDER_WIDTH}px`;
|
||||
el.style.zIndex = 10 + i;
|
||||
el.classList.remove('is-flipped');
|
||||
el.classList.remove('is-selected'); // Selection only on top of waste/pyramid
|
||||
el.classList.remove('is-selected');
|
||||
el.classList.remove('is-exposed');
|
||||
});
|
||||
|
||||
// Position Waste
|
||||
const wRect = wasteEl.getBoundingClientRect();
|
||||
waste.forEach((card, i) => {
|
||||
const el = cardElements[card.id];
|
||||
el.style.top = `${wRect.top - boardRect.top + PILE_BORDER_WIDTH}px`;
|
||||
el.style.left = `${wRect.left - boardRect.left + PILE_BORDER_WIDTH}px`;
|
||||
const isTop = (i === waste.length - 1);
|
||||
el.style.top = `${wRect.top - boardTop + PILE_BORDER_WIDTH}px`;
|
||||
el.style.left = `${wRect.left - boardLeft + PILE_BORDER_WIDTH}px`;
|
||||
el.style.zIndex = 50 + i;
|
||||
el.classList.add('is-flipped');
|
||||
el.classList.toggle('is-exposed', isTop);
|
||||
});
|
||||
|
||||
// Position Discard
|
||||
const dRect = discardEl.getBoundingClientRect();
|
||||
discard.forEach((card, i) => {
|
||||
const el = cardElements[card.id];
|
||||
el.style.top = `${dRect.top - boardRect.top + PILE_BORDER_WIDTH}px`;
|
||||
el.style.left = `${dRect.left - boardRect.left + PILE_BORDER_WIDTH}px`;
|
||||
el.style.top = `${dRect.top - boardTop + PILE_BORDER_WIDTH}px`;
|
||||
el.style.left = `${dRect.left - boardLeft + PILE_BORDER_WIDTH}px`;
|
||||
el.style.zIndex = 10 + i;
|
||||
el.classList.add('is-flipped');
|
||||
el.classList.remove('is-selected');
|
||||
el.classList.remove('is-exposed');
|
||||
});
|
||||
|
||||
checkWin();
|
||||
@ -228,12 +235,12 @@
|
||||
function handleCardClick(cardId) {
|
||||
if (!isActive) return;
|
||||
|
||||
// Find card in pyramid or waste
|
||||
// Find card
|
||||
let card = null;
|
||||
let source = '';
|
||||
let index = pyramid.findIndex(c => c && c.id === cardId);
|
||||
if (index !== -1) {
|
||||
card = pyramid[index];
|
||||
let pIdx = pyramid.findIndex(c => c && c.id === cardId);
|
||||
if (pIdx !== -1) {
|
||||
card = pyramid[pIdx];
|
||||
source = 'pyramid';
|
||||
} else if (waste.length > 0 && waste[waste.length - 1].id === cardId) {
|
||||
card = waste[waste.length - 1];
|
||||
@ -245,10 +252,13 @@
|
||||
|
||||
const el = cardElements[card.id];
|
||||
|
||||
// King is special (13)
|
||||
// King is special (13) - remove immediately
|
||||
if (getValueRank(card.value) === 13) {
|
||||
if (selectedCard) {
|
||||
cardElements[selectedCard.id].classList.remove('is-selected');
|
||||
selectedCard = null;
|
||||
}
|
||||
removeCards([card]);
|
||||
selectedCard = null;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -257,15 +267,16 @@
|
||||
el.classList.add('is-selected');
|
||||
} else {
|
||||
if (selectedCard.id === card.id) {
|
||||
// Deselect
|
||||
// Deselect if clicking the same card
|
||||
el.classList.remove('is-selected');
|
||||
selectedCard = null;
|
||||
} else {
|
||||
// Try to match
|
||||
if (getValueRank(selectedCard.value) + getValueRank(card.value) === 13) {
|
||||
removeCards([selectedCard, card]);
|
||||
selectedCard = null;
|
||||
} else {
|
||||
// Invalid pair - switch selection to new card
|
||||
// Invalid pair: switch selection to the new card
|
||||
cardElements[selectedCard.id].classList.remove('is-selected');
|
||||
selectedCard = card;
|
||||
el.classList.add('is-selected');
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user