One-handed Solitaire algorithm
Overall it's pretty good, but there might be something iffy going on with the rules you use.

I had honestly never heard of one-handed solitaire, but if I understand it correctly, there are no special rules for the last cards. You either have exactly 4 cards left, and win if you can discard them all, or you lose.

But you have a last2 rule, where you change the rules to look at the 1st and 2nd card if there are less than four cards left. I imagine this skews the results.

Anyway, in terms of code:


Don't mix plain console.log and a custom logging function.
Put the code for playing 1 round into a function. Right now it's all inside a loop that runs 10000 times. It'd be a lot simpler to just call play() and get a won/lost boolean back. You can always loop that.
It'd be simpler to unshift cards onto the hand. Then you'd always be comparing hand[0] to hand[3] instead of having to subtract stuff from hand.length. Splicing also becomes just splice(0, 4) or splice(1, 2)
You can use concat instead of Array.prototype.push.apply (or the slightly shorter alternative [].push.apply). The difference, however, is that concat returns a new array instead of modifying the receiver like push does.
You can simplify Card and Deck a little by using a list of values for rank as well as suit:

function Card(suit, rank) {
  this.suit = suit;
  this.rank = rank;
}

Card.prototype = {
  toString: function () {
    return this.rank + this.suit;
  }
};

Card.suits = ["♠︎", "♥︎", "♣︎", "♦︎"];
Card.ranks = ["A", 2, 3, 4, 5, 6, 7, 8, 9, 10, "J", "Q", "K"];


function Deck() {
  var cards, shuffled, i;

  cards = Card.suits.reduce(function (cards, suit) {
    return cards.concat(Card.ranks.map(function (rank) {
      return new Card(suit, rank);
    }));
  }, []);

  shuffled = [];
  while(cards.length) {
    i = Math.random() * cards.length | 0;
    shuffled.push(cards.splice(i, 1)[0]);
  }

  return shuffled;
}


I've named Deck with PascalCase as though it's a constructor, which it isn't quite. But you can call it as new Deck or just Deck() and either thing'll work, as it explicitly returns something.


You could also take a different tack altogether. You don't really need a hand array: You can just examine the shuffled deck directly. For instance:

function play() {
  var deck = new Deck,
      i = deck.length,
      a, b;

  while(true) {
    // decrement if we're too close to the end of the deck
    i = Math.min(i, deck.length-4);

    // if we had to decrement past zero, game's over
    if(i &lt; 0) {
      return deck.length === 0;
    }

    // the cards to examine
    a = deck[i],
    b = deck[i+3];

    // match cards or decrement
    if(a.rank === b.rank) {
      deck.splice(i, 4);
    } else if(a.suit === b.suit) {
      deck.splice(i + 1, 2);
    } else {
      i--;
    }
  }
}


(Edit: I updated the code based on @Jonah's comment below to examine the deck from the end, rather than the beginning.)

Running 300,000 games, I get a 0.730% chance of winning, which is pretty close to 1 in 140.

Edit: It makes for a nice animation - see snippet below



var frameTime = 100;
var button = document.getElementsByTagName("button")[0];
var span = document.getElementsByTagName("span")[0];

function Card(suit, rank) {
  this.suit = suit;
  this.rank = rank;
}

Card.suits = ["♠︎", "♥︎", "♣︎", "♦︎"];
Card.ranks = ["A", 2, 3, 4, 5, 6, 7, 8, 9, 10, "J", "Q", "K"];


function Deck() {
  var cards, shuffled, i;
  
  cards = Card.suits.reduce(function (cards, suit) {
    return cards.concat(Card.ranks.map(function (rank) {
      return new Card(suit, rank);
    }));
  }, []);
  
  shuffled = [];
  while(cards.length) {
    i = Math.random() * cards.length | 0;
    shuffled.push(cards.splice(i, 1)[0]);
  }
  
  return shuffled;
}

function play() {
  var deck = new Deck,
      i = deck.length - 4;
  
  button.disabled = true;
  span.innerHTML = "";
  
  function step() {
    var a, b;
    
    i = Math.min(i, deck.length - 4);
    drawState(deck, i);
    
    if(i &lt; 0) {
      if(deck.length) {
        span.innerHTML = "Lost (" + deck.length + " cards remain)";
      } else {
        span.innerHTML = "Won!";
      }
      button.disabled = false;
      return;
    }

    a = deck[i];
    b = deck[i+3];
    
    if(a.rank === b.rank) {
      deck.splice(i, 4);
    } else if(a.suit === b.suit) {
      deck.splice(i+1, 2);
    } else {
      i--;
    }
    
    setTimeout(step, frameTime);
  }
  
  step();
}

function drawState(deck, offset) {
  var canvas = document.getElementsByTagName("canvas")[0],
      ctx = canvas.getContext("2d");
  
  canvas.width = 520;
  canvas.height = 35;
  
  function drawTriangle(x, y) {
    ctx.beginPath();
    ctx.moveTo(x, y);
    ctx.lineTo(x + 3, y + 5);
    ctx.lineTo(x - 3, y + 5);
    ctx.closePath()
    ctx.fill();
  }
  
  if(offset &gt;= 0) {
    drawTriangle(offset * 10 + 5, 25);
    drawTriangle((offset + 3) * 10 + 5, 25);
    ctx.fillStyle = "rgba(0, 0, 0, 0.1)";
    ctx.fillRect(offset * 10, 0, 40, 22);
  }
  
  ctx.fillStyle = "#000";
  ctx.font = "10px serif";
  ctx.textAlign = "center";
  
  deck.forEach(function (card, x) {
    x = x * 10 + 5;
    ctx.fillText(card.rank, x, 10);
    ctx.fillText(card.suit, x, 20);
  });
}


button.addEventListener("click", play, false);
play();
&lt;canvas&gt;&lt;/canvas&gt;
&lt;br&gt;
&lt;button&gt;Play again&lt;/button&gt;
&lt;span&gt;&lt;/span&gt;