Align Pyramid Solitaire with standard rules and layout

This commit is contained in:
chris 2026-05-25 00:55:05 -04:00
parent 0fcd6b190d
commit 91d4bcc25f
2 changed files with 62 additions and 41 deletions

View File

@ -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 {

View File

@ -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');