Drag cards to place it.
Valid movements:
- Aces to empty side savers.
- Consecutive same-suit cards over side savers (ascending stair).
- Any card or consecutive group empty spaces on main triangle.
- Any card or consecutive group to covered cards on main triangle.
- Any card or consecutive group to bigger consecutive, different-color card (descending stair).
-
Drag cards to place it.
Valid movements:
- Aces to empty side savers.
- Consecutive same-suit cards over side savers (ascending stair).
- Any card or consecutive group empty spaces on main triangle.
- Any card or consecutive group to covered cards on main triangle.
- Any card or consecutive group to bigger consecutive, different-color card (descending stair).
-
“Cartas” by Jose G Moya Y
https://openprocessing.org/sketch/1304073
License CreativeCommons Attribution NonCommercial ShareAlike
https://creativecommons.org/licenses/by-nc-sa/3.0
{{filePath}}
{{width}} x {{height}}
Report Sketch
Oh, that naughty sketch! Please let us know what the issue is below.
Apply Template
Applying this template will reset your sketch and remove all your changes. Are you sure you would like to continue?
Report Sketch
Report Comment
Please confirm that you would like to report the comment below.
We will review your submission and take any actions necessary per our Community Guidelines. In addition to reporting this comment, you can also block the user to prevent any future interactions.
Please report comments only when necessary. Unnecessary or abusive use of this tool may result in your own account being suspended.
Are you sure you want to delete your sketch?
Any files uploaded will be deleted as well.
Delete Comment?
This will also delete all the replies to this comment.
Delete this tab? Any code in it will be deleted as well.
Select a collection to submit your sketch
We Need Your Support
Since 2008, OpenProcessing has provided tools for creative coders to learn, create, and share over a million open source projects in a friendly environment.
Niche websites like ours need your continued support for future development and maintenance, while keeping it an ad-free platform that respects your data and privacy!
Please consider subscribing below to show your support with a "Plus" badge on your profile and get access to many other features!
Drag cards to place it. Valid movements: - Aces to empty side savers. - Consecutive same-suit cards over side savers (ascending stair). - Any card or consecutive group empty spaces on main triangle. - Any card or consecutive group to covered cards on main triangle. - Any card or consecutive group to bigger consecutive, different-color card (descending stair). -
CC Attribution NonCommercial ShareAlike
Cartas
G Moya Y
xxxxxxxxxx
/*
2-deck French Solitaire game.
This was THE poker-card solitaire game when I was a child and
no windows-style solitaires had arrived to Spain.
It is played with two decks of "poker" / "french" cards.
You can stack cards following these rules:
- The 10 tableau piles (piles on main triangle) are built down by alternate colors
(i.e you can move 2 of diamonds over 3 of spades).
- You can move a pile of cards from Tableau to tableau, as long as it is built down
by alternate colors.
(i.e. you can move [2 of diamonds + 3 of spades] over [4 of hearts]).
- You can move ANY card to an empty tableau pile.
- You can move ANY card to a tableau pile over a "hidden" (face down) card.
- The 8 foundations ("saving" piles for each suit) are built up by suit from Ace
(i.e. you can move 2 hearts to Ace of hears if it is on the Foundation)
- If you deal cards and have empty spaces, you only deal over the empty spaces.
- NEW: But you should not deal if you have face down cards.
- Otherwise, tableau piles built down from King are skipped when dealing.
**********************************************
This Sketch uses svg-cards by D.Belliot.
Open the content of svg-cards.svg with text editor
to see the license
***********************************************
Everything else is
© Copyright 2021 José Gabriel Moya Yangüela
Permision is granted to copy / share / modify on basis of the
Creative Commons Attribution NonCommercial ShareAlike License.
Please contact with author if you want to use it for commercial purposes.
*/
/*
UPDATES:
2021-10-17 Fullscreen mode; winning routine. Added losing routine but it is buggy and will be deleted
2021-10-14 added describeElement on the hope it helps people with screenreaders.
I also documented some variables.
TO-DO
- Improve detect lose. Current check only tests lower cards of each pile, so check is disabled.
- Add "UNDO" (or is it cheating?).
(How to do it? Store initial MAZO and movements, or store each state?).
- Beautify and comment code.
- Translate all comments to english (and variable names, too).
- Add menu? Instructions?
*/
/* This is not used for ALL debugging, just for the most intrusive debug messages: */
var debugging=true;
/* Status variables */
var cardsCreated = false; /* Card images have been created */
var cardsShuffled = false; /* Cards have been shuffled */
var repartido = false; /* TRUE after initial deal / initial board setup */
/* x and y for making the 10 piles at first deal */
/* xreparto is used on each deal after that */
var xreparto = 0;
var yreparto = 0;
/* Triangle columns */
/* Columns 10 to 18 contain "saved" cards (foundations) */
var columnas = [
[]
];
/* Card Height and Width */
var cH, cW;
/* Temporary DIV asking for patience -- DOES NOT DISPLAY!!! */
var myDiv;
// Suit codes ("palo" means suit in Spanish).
// On SVG file, suits are ordered clover/diamond/heart/spade
// I need to get them in heart/spade/diamond/clover order
// (that would allow comparing palo % 2 to get suit color).
var paloCodigo = [2, 3, 1, 0];
// imgFondo: temporary image for back of cards (fondo means background in Spanish).
// imgCarta: temporary image for card.
var imgFondo, imgCarta;
var imagenCarta = []; // Stores card faces.
var imagenFondo = []; // Stores card backs.
var cartas = []; // Stores 8 suits of cards, ordered Ahears,Ahearts,2hearts,2hearts...
/* PLEASE NOTICE the main object used by the game, (carta),
is defined in the createCards() function.
carta = {
"palo": suit: 0=hearts, 1=spade, 2=diamonds, 3=clover,
"valor": face value: 0=ace... 12=king.
"color": color (palo % 2). I think this is really never used. I use palo % 2 instead.
"fondo": back color of card. 1=blue, 0=red since I create the red background first.
"abajo": Is card face down? true if face down, false if face up.
};
*/
var mazo = []; // Stores the shuffled cards
var elemento = 0;
// Human readable card names, so if carta.valor is 0 and carta.palo is 0,
// you can tell it is a "Ace of hearts"
var humanV = ["A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"];
var humanP = ["hearts", "spades", "diamonds", "clover"];
/* For drag and drop */
var moviendoCarta = []; /* Group of cards you are moving */
var moviendoXDist = 0; /* X distance from mouse to center of picked card */
var moviendoYDist = 0; /* Y distance from mouse to center of picked card */
var moviendoColumnaOriginal = -1; /* Original pile you took the card from, so you can return it */
var moviendo = false; /* ¿Are you moving */
/* For re-dealing */
var dealing=false;
const dealStyleUndefined=0; /* Check for empty space and decide */
const dealStyleNormal=1; /* Skip kings, deal everywhere else */
const dealStyleEmptyOnly=2; /* Deal on empty spaces only */
var dealStyle=dealStyleUndefined;
var buttonDiv; /* Div containing buttons */
var buttonFullScreen,buttonNormalScreen; /* Buttons to select between full and normal screen */
var startMenu=true; /* Whether start menu is being shown or not */
/* Preload SVG file */
function preload() {
cardspng = loadImage("svg-cards.svg");
}
function setup() {
createCanvas(windowWidth, windowHeight);
/* Setup main piles */
/* 0-9: tableau piles (columns) */
/* 10-17: foundations (saved cards) */
for (var f = 0; f < 18; f++) {
columnas.push([]);
}
// Make button for fullScreen. Playing with cellphone without fullScreen is difficult.
buttonDiv=createDiv("Select screen mode:");
buttonDiv.center();
let tempDiv1=createDiv("");
tempDiv1.parent(buttonDiv);
let tempDiv2=createDiv("");
tempDiv2.parent(buttonDiv);
buttonFullScreen=createButton("Fullscreen mode");
buttonFullScreen.parent(tempDiv1);
//buttonFullScreen.center();
buttonFullScreen.mousePressed(screenFull);
buttonFullScreen.touchEnded(screenFull);
buttonNormalScreen=createButton("Normal mode");
buttonNormalScreen.parent (tempDiv2);
//buttonNormalScreen.center();
buttonNormalScreen.mousePressed(screenNormal);
buttonNormalScreen.touchEnded(screenNormal);
// it is better to create cards from draw() so we can use noLoop()
//createCards();
frameRate(25);
}
function screenNormal(){
buttonDiv.hide();
startMenu=false;
}
function screenFull(){
//console.log("requesting fullscreen");
fullscreen(true);
resizeCanvas(windowWidth,windowHeight);
//console.log("fullscreen is"+fullscreen());
buttonDiv.hide();
startMenu=false;
}
// Creates individual card faces from the svg file.
function createCards() {
background(0, 255, 0);
// svg-cards contains 13 x 5 cards.
// We need a size that allows for at least 20 cards width.
// Regarding the height, 13/5 cards would be nice.
var W, H; /* Height and Width */
var carta; /* The object we will use to define each card on the deck */
noLoop();
myDiv = createP("Please Wait");
cW = round(width / 13);
W = cW * 13;
H = W * cardspng.height / cardspng.width;
if (H > height) {
//Height should be a multiple of 4 so cH and cW are integers.
H = height - (height % 4);
W = round((H * cardspng.width / cardspng.height) / 13) * 13;
cW = round(W / 13);
}
cH = H / 5;
// MyDiv contains a message tha says "wait..."
// This hides that div.
// On my experience, even when SVG file loads slowly, myDiv is always hidden.
myDiv.hide();
image(cardspng, 0, 0, W, H);
// Get a each card.
for (var g = 0; g < 4; g++) {
gg = paloCodigo[g];
for (var f = 0; f < 13; f++) {
imgCarta = get(cW * f, cH * gg, cW, int(cH));
imagenCarta.push(imgCarta);
/* We use the same loop to define the "carta" object and
load a starting deck of cards.
*/
carta = {
"palo": g,
"valor": f,
"color": g % 2,
"fondo": 0,
"abajo": false
};
carta2 = JSON.parse(JSON.stringify(carta));
carta2.fondo = 1;
cartas.push(carta);
cartas.push(carta2);
//console.log(cartas.length);
}
}
/*
The deck does not have a red background for doubledeck. We will made one
by means of turning blue into red
*/
imgFondo = get(cW * 2, cH * 4, cW, int(cH));
imgFondo.loadPixels();
let r, b;
let pixelCount = 4 * imgFondo.width * imgFondo.height;
//console.log(pixelCount);
for (var h = 0; h < pixelCount; h += 4) {
r = imgFondo.pixels[h]; /* red */
b = imgFondo.pixels[h + 2]; /* blue */
imgFondo.pixels[h] = b;
imgFondo.pixels[h + 2] = r;
}
imgFondo.updatePixels();
imagenFondo.push(imgFondo);
/* If I load image from the screen, push into array,
change color and push again, the resulting pair has
color changed.
So I will load from the screen, change color, push into array,
and load again from the screen before pushing the second
*/
imgFondo = get(cW * 2, cH * 4, cW, int(cH));
imagenFondo.push(imgFondo);
cardsCreated = true;
loop();
}
function shuffleCards() {
let cartasOrdenadas = JSON.parse(JSON.stringify(cartas));
let num = cartas.length;
/* This should be before noLoop(), since cardsCreated ends with loop() */
if (!cardsCreated) {
console.log("Cards shuffled before creation. This should never occur.");
createCards();
}
noLoop();
for (let f = 0; f < num; f++) {
cartaAlAzar = int(Math.random() * cartas.length);
mazo.push(cartas[cartaAlAzar]);
cartas.splice(cartaAlAzar, 1);
}
cartas = JSON.parse(JSON.stringify(cartasOrdenadas));
cardsShuffled = true;
loop();
}
function reparto() {
noLoop();
evenOrOdd = (yreparto % 2);
cartaActual = mazo.pop();
if ((xreparto % 2) == evenOrOdd) {
cartaActual.abajo = false;
} else {
cartaActual.abajo = true;
}
columnas[xreparto].push(cartaActual);
/* DEBUG START ********************
if (cartaActual.abajo) {
CAHidden = "hidden"
} else {
CAHidden = "visible"
}
console.log(
"dealt: " + cartaActual.valor + "." +
cartaActual.palo + CAHidden +
"at " + xreparto + "," + yreparto
);
*********************** DEBUG END */
xreparto++;
if (xreparto > 9) {
yreparto++;
xreparto = yreparto;
if (yreparto > 9) {
repartido = true;
}
}
loop();
}
/*
This is for the "deal more" movement.
Starting deal is covered by reparto()
*/
function deal(){
let cartatmp;
noLoop();
if (xreparto>=10){
dealing=false;
return;
}
if (mazo.length<=0){
dealing=false;
return;
}
/* BEGIN decision of dealing Style */
/*
When xreparto==0 we check the situation and decide
what kind of deal we should use
*/
if ( xreparto==0 || dealStyle==dealStyleUndefined) {
let emptySpaceCount=0;
for(f=0;f<10;f++){
if (columnas[f].length==0){
emptySpaceCount++;
} else if (columnas[f][columnas[f].length-1].abajo) {
// Uncomment this if you want to deal just one card over face down cards
// emptySpaceCount++;
}
}
/* if there are empty spaces we will use dealStyleEmptyOnly */
if (emptySpaceCount>0) {
console.log("Deal Style: EmptyOnly");
dealStyle=dealStyleEmptyOnly;
} else {
dealStyle=dealStyleNormal;
console.log("Deal Style: Normal");
}
}
/* END decision of dealing Style */
let dealHere=false;
switch (dealStyle) {
case dealStyleEmptyOnly:
if (
(columnas[xreparto].length==0)
// Uncomment this if you want to deal just one card over face down cards
/* || (columnas[xreparto][columnas[xreparto].length-1].abajo) */
) {
dealHere=true;
}
break;
case dealStyleNormal:
/* The following condition should never occur */
if (columnas[xreparto].length<=0) break;
valor=12;
palotmp=-1;
for (var f=0;f<columnas[xreparto].length;f++){
cartatmp=columnas[xreparto][f];
if ((cartatmp.valor !=valor) || palotmp == (cartatmp.palo % 2) ){
dealHere=true;
break;
} else {
valor--;
palotmp=cartatmp.palo % 2;
}
}
break;
}
if (dealHere){
cartatmp=mazo.pop();
cartatmp.abajo=false;
columnas[xreparto].push(cartatmp);
}
xreparto++;
checkLose();
loop();
}
function draw() {
//console.log ("Start Draw");
background(0, 255, 0);
if (startMenu){
return;
}
if (!cardsCreated) {
createCards();
return;
} else if (!cardsShuffled) {
shuffleCards();
return;
} else if (!repartido) {
reparto();
} else if (dealing){
deal();
}
/* Draw stack of unused cards */
let cartamazo;
if (mazo.length==0){
stroke(255, 255, 255);
strokeWeight(3);
fill(0,150,0);
rect(12 * cW, 3*cH, cW, cH, cW / 4);
} else {
cartamazo=mazo[mazo.length-1];
cartamazo.abajo=true;
dibujaCarta(cartamazo, 12 * cW, 3*cH);
textAlign(CENTER);
stroke(255, 255, 255);
strokeWeight(1);
fill(0);
describeElement("Deal","There are "+mazo.length+" cards in the dealing stack");
text(""+mazo.length,12.5*cW,3.5*cH);
}
/* Draw empty space */
let salvadas, cartaSalvada;
for (let sqx = 0; sqx < 2; sqx++) {
for (let sqy = 0; sqy < 4; sqy++) {
salvadas = columnas[10 + sqx * 4 + sqy];
if (salvadas.length == 0) {
stroke(255, 255, 255);
strokeWeight(3);
noFill();
describeElement("Foundation","You can drop an ace here.");
rect((10 + sqx) * cW, sqy * cH, cW, cH, cW / 4);
} else {
cartaSalvada = salvadas[salvadas.length - 1];
describeElement("Foundation","You have saved "+nombreHumano(cartaSalvada)+" in the foundation");
dibujaCarta(cartaSalvada, (10 + sqx) * cW, sqy * cH);
}
}
}
let debuggingwas=debugging;
debugging=false;
for (let ff = 0; ff < 10; ff++) {
dibujaColumna(ff);
}
debugging=debuggingwas;
if (moviendo) {
dibujaCartaArrastrada();
}
//console.log ("End Draw");
loop();
}
function mousePressed() {
if (repartido && !dealing) {
if (moviendo) {
dibujaCartaArrastrada();
} else /* !moviendo */ {
col = int(mouseX / cW);
console.log("Trying to drag from :"+col);
if (col>=0 && col <10 ) {
iniciaArrastre (mouseX,mouseY);
} else if (col>=10 && col <12) {
/* Dragging from save space */
iniciaDevolucion(mouseX, mouseY);
} else if (col==12){
switch (int(mouseY/cH)){
case 3:
console.log("Deal cards");
xreparto=0;
dealing=true;
break;
}
}
}
}
return false;
}
/* Starts drag. mx is between playing stacks. */
function iniciaArrastre(mx,my){
let moviendoCartaTemp = {};
let col = int(mx / cW);
for (f = columnas[col].length - 1; f > -1; f--) {
if ((my > (f * cH / 5)) && (my < (cH + f * cH / 5))) {
moviendoCartaTemp = columnas[col][f];
if (moviendoCartaTemp.abajo) {
console.log("Touch hidden card:" + moviendoCartaTemp);
return false;
} else {
console.log("Dragging card" + moviendoCartaTemp + " from " + col + "," + f);
moviendoCarta = columnas[col].splice(f, columnas[col].length - f);
moviendoXDist = mx - (col * cW);
moviendoYDist = my - (f * cH / 5);
moviendoColumnaOriginal = col;
moviendo = true;
/*** DEBUG START *******
debugCardGroup("moving:",moviendoCarta[i]);
**** DEBUG END ****/
}
break;
} else {
if (f > 0) {
/* do not look for cards if not forming a stair */
if (columnas[col][f - 1].abajo) {
console.log("there is a hidden card in between");
break;
}
if (columnas[col][f].valor != (columnas[col][f - 1].valor - 1)) {
console.log("there is a non consecutive card in between");
break;
}
if ((columnas[col][f].palo % 2) == (columnas[col][f - 1].palo % 2)) {
console.log("there is a same color card in between");
break;
}
}
}
}
}
/* Starts drag. mx is on save stacks. */
function iniciaDevolucion(mx,my){
let col,colx,coly,colu;
let lastCardY; /* Solo se usa como índice de fila */
/* El mazo viene dado por: */
/*
Note: here we need to use int instead of round,
while we use round at drop events
*/
col = int(mx / cW); /* col is initially a stack number */
colx=col-10;
if (colx<0 || colx>2){
//console.log("mx="+mx+" colx="+colx);
return;
}
coly=int(my / cH);
if (coly<0 || coly>4) {
//console.log("my="+my+" coly="+colx);
return;
}
col=10+colx*4+coly; /* col reloaded as number of pile */
//console.log("mx="+mx+" colx="+colx+" my="+my+" coly="+coly+" col="+col);
//console.log("Drag from save stack: "+col);
//debugCardGroup(columnas[col]);
if ((columnas[col].length<=0) || !Array.isArray(columnas[col]) ){
/* Empty Stack. Can't get card from it */
console.log("Can't get card from empty stack");
console.log(JSON.stringify(columnas[col]));
} else {
/*** DEBUG START ******
debugCardGroup("column ["+col+"] contained:",columnas[col]);
******* DEBUG END ****/
/*
We don't want a card but a PILE of 1 card
use [.pop()] or .splice(...)
*/
// moviendoCarta = [columnas[col].pop()] ;
moviendoCarta = columnas[col].splice(columnas[col].length - 1,1);
// debugCardGroup("moviendoCarta contains:",moviendoCarta);
moviendoXDist = mx - ((10+colx) * cW);
moviendoYDist = my - (coly * cH);
moviendoColumnaOriginal = col;
moviendo = true;
}
}
function mouseDragged() {
if (repartido && moviendo) {
dibujaCartaArrastrada();
}
return false;
}
function mouseReleased() {
if ( (!repartido) || (dealing) ) return;
if (!moviendo) {
/* I Can't manage to combine mouseClick, mousePressed
and mouseReleased, so I will call some "click" events
when released after not moving
*/
releasedButtonOn(mouseX, mouseY);
return false;
} else {
dropCard(mouseX - moviendoXDist, mouseY - moviendoYDist);
}
return false;
}
/* Manages dropping of cards, chooses between main or save stacks */
function dropCard(xDest,yDest){
let col = round(xDest / cW);
if (col>=0 && col<10) {
dropCardCol(yDest,col);
} else if (col >=10 && col <12){
dropCardSaved(xDest,yDest);
}
}
/* Drops a card into main play stacks */
function dropCardCol(yDest,col){
let moviendoCartaTemp = moviendoCarta[0];
let valid=false;
/*
Y of last card in col should be smaller than yDest, but
smaller than yDest+cH
*/
let lastCardY = columnas[col].length - 1;
let lastCardPos = lastCardY * cH / 5;
//console.log(lastCardY+" "+lastCardPos+" "+yDest);
if (lastCardPos <= yDest && (lastCardPos + cH) >= yDest) {
/******* DEBUG *************
if (lastCardY>0){
console.log("Dropping on last card. Values are: " +
representa(moviendoCartaTemp) + "->" + representa(columnas[col][lastCardY]) +
" color:" + ((moviendoCartaTemp.palo % 2) != (columnas[col][lastCardY].palo % 2)) +
" value:" + (moviendoCartaTemp.valor == (columnas[col][lastCardY].valor - 1))
);
}
************* DEBUG *************/
if (moviendoColumnaOriginal == col) {
/* moving to original column is always valid */
valid = true;
console.log("Moving from " + col + " to " + moviendoColumnaOriginal + " always granted.");
} else if (lastCardY < 0 || !Array.isArray(columnas[col])) {
/* moving to empty column is always valid */
valid = true;
console.log("Moving to empty column" + col + " granted.");
} else if (columnas[col][lastCardY].abajo == true) {
/* moving to a place whith an unrevealed card is valid */
valid = true;
} else if (
((moviendoCartaTemp.palo % 2) != (columnas[col][lastCardY].palo % 2)) &&
(moviendoCartaTemp.valor == (columnas[col][lastCardY].valor - 1))
) {
valid = true;
} /* end if test for validity of last card */
} else {
/* end if test for last card */
console.log("Not last card");
}
if (valid) {
/*** DEBUG START *******
debugCardGroup("drag group contains:",moviendoCarta);
******* DEBUG END ****/
columnas[col] = columnas[col].concat(moviendoCarta);
/*** DEBUG START *******
debugCardGroup("column ["+col+"] contains:",columnas[col]);
******* DEBUG END ****/
moviendo = false;
// moviendoCarta=[]; This destroys original card. Avoid it.
moviendoXDist = 0;
moviendoYDist = 0;
moviendoColumnaOriginal = -1;
}
if (!valid && (moviendoColumnaOriginal >= 0)) {
columnas[moviendoColumnaOriginal] = columnas[moviendoColumnaOriginal].concat(moviendoCarta);
console.log("Returning card(s) to column " + moviendoColumnaOriginal);
/*** DEBUG START *******/
debugCardGroup("drag group contains:", moviendoCarta);
debugCardGroup("column [" + moviendoColumnaOriginal + "] contains:", columnas[moviendoColumnaOriginal]);
/******* DEBUG END ****/
moviendo = false;
// moviendoCarta={}; This destroys the original cards. Avoid it
moviendoXDist = 0;
moviendoYDist = 0;
moviendoColumnaOriginal = -1;
}
/* Si el movimiento fue válido, comprueba si el juego se ha perdido */
/* (la victoria se comprueba solo en dropCardSaved() ) */
if(valid) {
checkLose();
}
}
/* Drop a card into Save stack */
function dropCardSaved(xDest,yDest){
let valid=false;
let col,colx,coly;
let lastCardY; /* Solo se usa como índice de fila */
/* El mazo viene dado por: */
col = round(xDest / cW); /* col is initially a stack number */
colx=col-10;
if (colx<0 || colx>2){
//console.log("xDest="+xDest+" colx="+colx);
return;
}
coly=round(yDest / cH);
if (coly<0 || coly>4) {
//console.log("yDest="+xDest+" coly="+colx);
return;
}
col=10+colx*4+coly; /* col reloaded as number of pile */
if ( moviendoCarta.length!=1) {
console.log("You can only save one card at once");
return;
}
let moviendoCartaTemp=moviendoCarta[0];
if (!Array.isArray(columnas[col])){
lastCardY=-1;
} else {
lastCardY = columnas[col].length - 1;
}
if (lastCardY < 0) {
/* moving to empty save is valid for ACES */
if (moviendoCartaTemp.valor==0) {
valid = true;
console.log("Moving ace to empty save area" + col + " granted.");
} else {
console.log("Moving non-ace to empty save area" + col + " forbidden.");
}
} else if (
(columnas[col][lastCardY].palo==moviendoCartaTemp.palo) &&
(columnas[col][lastCardY].valor==moviendoCartaTemp.valor-1)
) {
/* moving bigger than ace to full save area */
valid = true;
/* Here could be a winning routine if card is a king */
} else {
/* end if test for last card */
console.log("Can't save that card");
}
if (valid) {
/*** DEBUG START *******
debugCardGroup("drag group contains:",moviendoCarta);
******* DEBUG END ****/
columnas[col] = columnas[col].concat(moviendoCarta);
/*** DEBUG START *******/
debugCardGroup("column now ["+col+"] contains:",columnas[col]);
/******* DEBUG END ****/
moviendo = false;
// moviendoCarta=[]; This destroys original card. Avoid it.
moviendoXDist = 0;
moviendoYDist = 0;
moviendoColumnaOriginal = -1;
}
if (!valid && (moviendoColumnaOriginal >= 0)) {
columnas[moviendoColumnaOriginal] = columnas[moviendoColumnaOriginal].concat(moviendoCarta);
console.log("Returning card(s) to column " + moviendoColumnaOriginal);
/*** DEBUG START *******/
debugCardGroup("drag group contains:", moviendoCarta);
debugCardGroup("column [" + moviendoColumnaOriginal + "] contains:", columnas[moviendoColumnaOriginal]);
/******* DEBUG END ****/
moviendo = false;
// moviendoCarta={}; This destroys the original cards. Avoid it
moviendoXDist = 0;
moviendoYDist = 0;
moviendoColumnaOriginal = -1;
}
let win=false;
/* WINNING TEST */
if (valid){
let sum=0;
for (let f=10;f<18;f++){
sum+=columnas[f].length;
}
win=(sum==13*8);
if (win){
window.alert("You won!!");
} else {
/* Losing test in case movement is not a win */
checkLose();
}
}
}
/* Draws the moviendoCarta group */
function dibujaCartaArrastrada() {
let f;
if (!Array.isArray(moviendoCarta) || moviendoCarta.lenght == 0) {
console.log("dibujaCartaArrastrada: Not an array");
return;
}
debugCardGroup("dibujar:", moviendoCarta);
// describes element as "moving X cards from [firstcard] to [lastcard]":
describeElement("DraggedCards","moving "+describeCardGroup(moviendoCarta,true));
dibujaConjunto(
moviendoCarta,
mouseX - moviendoXDist,
mouseY - moviendoYDist
);
}
/* Tells the name of a card */
function representa(cart) {
if (cart === null || !(typeof cart === "object") || !(cart.hasOwnProperty("valor"))) {
console.log("cart is not a card");
return "<not a card>";
}
var codigoCarta = cart.valor + "." + cart.palo;
var nombreCarta = humanV[cart.valor] + " " + humanP[cart.palo];
return codigoCarta + " (" + nombreCarta + ")";
}
function nombreHumano(cart) {
if (cart === null || !(typeof cart === "object") || !(cart.hasOwnProperty("valor"))) {
console.log("cart is not a card");
return "<not a card>";
}
if (cart.abajo) {
return ("a hidden card");
} else {
return humanV[cart.valor] + "of" + humanP[cart.palo];
}
}
function releasedButtonOn(mx, my) {
let col = int(mx / cW);
if (col < 10) {
/* ¿Click en una columna de cartas? */
let lastCardY = columnas[col].length - 1;
let lastCardPos = lastCardY * cH / 5;
if (lastCardPos <= mouseY && (lastCardPos + cH) >= mouseY) {
if (columnas[col][columnas[col].length - 1].abajo) {
columnas[col][columnas[col].length - 1].abajo = false;
}
}
}
}
/* Logs a list with the names of all cards in a group */
function debugCardGroup(message, array) {
let terminator, msg;
if (array === null) {
console.log(message + " is null");
return;
} else if (!Array.isArray(array)) {
console.log(message + " is not array but " + JSON.stringify(array));
return;
}
msg = "" + message + "=[";
for (let i = 0; i < array.length; i++) {
msg = msg + "{" + representa(array[i]) + "}";
//console.log(message + " contains [" + i + "]" + representa(array[i]));
terminator = (i < array.length - 1) ? "," : "]";
msg = msg + terminator;
}
console.log(msg);
}
/*
Generates a description of a card group
You can use it with describeElement.
*/
function describeCardGroup(array, simple) {
let terminator, msg;
if (simple==null) {simple=false;}
if (array === null) {
console.log("describeCardGroup: null array");
return;
} else if (!Array.isArray(array)) {
console.log("describeCardGroup: array not array but " + JSON.stringify(array));
return;
}
msg="contains "+array.length+" cards";
switch ( array.lenght){
case 0:
msg+=".";
break;
case 1:
msg+=": "+nombreHumano(array[1]);
break;
default:
if (simple) {
msg+="starting with ";
msg+=nombreHumano(array[1]);
msg+=" and ending with ";
msg+=nombreHumano(array[array.length])+".";
} else {
msg=msg+":";
for (let i = 0; i < array.length; i++) {
msg = msg + nombreHumano(array[i]) + ",";
terminator = (i < array.length - 1) ? "," : ".";
msg = msg + terminator;
}
}
}
return (msg);
}
/* Draws single card at x,y */
function dibujaCarta(cart, x, y) {
if (cart === null || !(typeof cart === "object") || !(cart.hasOwnProperty("valor"))) {
console.log("cart is not a card");
return;
}
if (cart.abajo) {
image(imagenFondo[cart.fondo], x, y);
} else {
image(imagenCarta[cart.palo * 13 + cart.valor], x, y);
}
}
function dibujaColumna(numMazo) {
if (numMazo < 0 || numMazo > 10) return;
x = cW * numMazo;
// Describes the column as "pile X contains ..."
describeElement("Pile"+numMazo,"Pile "+numMazo+" "+describeCardGroup(columnas[numMazo],false));
dibujaConjunto(columnas[numMazo], numMazo * cW, 0);
}
/* Draws group of cards at x,y */
function dibujaConjunto(cartArray, x, y) {
let f;
if (!Array.isArray(cartArray) || cartArray.lenght == 0) {
console.log("dibujaConjunto: Not an array");
return;
}
if ( debugging ) { debugCardGroup("dibujar:", cartArray); }
for (f = 0; f < cartArray.length; f++) {
// console.log("dibujando:" + representa(cartArray[f]));
dibujaCarta(cartArray[f], x, y + f * cH / 5);
}
}
/* Check for movements left */
/* This function should be improved, since it only checks
the last card of each pile.
Where pile ends with a ladder, each card in the ladder should
be tested.
Also, foundation cards should be tested only if they are useful.
I'm thinking about adding a isCardOnFoundation(value, color) function,
so if you have 1_0 and 3_0 in the piles, you could check for 2_1 on foundation.
*/
function movementsLeft(){
let canmove=false;
let origin, destination;
for (let colfrom=0;colfrom<10;colfrom++){
// if there is an empty column, player can move
if (columnas[colfrom].length==0) {
canmove=true;
break;
}
origin=columnas[colfrom][columnas[colfrom].length];
// if last card of a column is face down, player can move
if (origin.abajo) {
canmove=true;
break;
}
for (let colto=0;colto<18;colto++){
if ( colto <10){
if (columnas[colto].length==0){
canmove=true;
break;
}
destination=columnas[colfrom][columnas[colfrom].length].abajo;
if (destination.abajo) {
canmove=true;
break;
}
if ((destination.palo %2)!==(origin.palo %2)){
if (destination.value==origin.value+1){
canmove=true;
}
}
} else /*colto >=10 */ {
if (columnas[colto].length==0){
// Can move to empty foundation if and only if moving an ace.
if (origin.value==0) {
canmove=true;
}
break;
}
destination=columnas[colfrom][columnas[colfrom].length].abajo;
if (destination.palo ==origin.palo){
if (destination.value==origin.value-1){
canmove=true;
}
}
} /* end if check column number */
} /* end for colto*/
} /* end for colfrom */
/* ¿Can return card from foundation ? */
for (let colfrom=10;colfrom<18;colfrom++){
/* Checking only used foundations */
if (columnas[colfrom].length>0) {
for (let colto=0;colto<10;colto++){
/* Dont try to move from foundation to empty column */
if (columnas[colto].length>0){
destination=columnas[colfrom][columnas[colfrom].length].abajo;
if (destination.abajo) {
break ;
} else {
if ((destination.palo %2)!==(origin.palo %2)){
if (destination.value==origin.value+1){
canmove=true;
}
}
}
} /* end if: columnas[colto].length >0 */
} /* end for colto */
} /* end if columnas[colfrom].length>0 */
} /* end for colfrom */
return canmove;
}
function checkLose(){
if ((mazo.length==0) && (!movementsLeft)){
/* Disable the check since movementsLeft() is buggy*/
// window.alert("Not sure, but it seems you lose!");
}
}
See More Shortcuts