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