Home Articles FAQs XREF Games Software Instant Books BBS About FOLDOC RFCs Feedback Sitemap
irt.Org

Related items

Kick some booty with invisible Flash!

JavaScripting Essentials

JavaScript Bookmarklets

Why bother with JavaScript?

Writing a midi hifi system in JavaScript

JavaScript Beginners Start Here

Keeping Count of Downloads

JavaScript Games

Online JavaScript Resources

JavaScript Games #2 - Solitaire

You are here: irt.org | Articles | JavaScript | Miscellaneous | JavaScript Games #2 - Solitaire [ previous next ]

Published on: Monday 15th June 1998 By: Keith Drakard

Introduction

Here's the second JavaScript Games article; this one develops a computer version of solitaire (not the card variant). The object of this game is to remove balls from the board until only one remains - in the central hole. The only legal moves are horizontal or vertical jumps with one ball over another into an empty hole on the other side, removing the ball that was jumped over.

Making The Game Board

This is what the solitaire board looks like:

|-xx-|-xx-| 03 | 04 | 05 |-xx-|-xx-|
|-xx-|-xx-| 10 | 11 | 12 |-xx-|-xx-|
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
| 29 | 30 | 31 | 32 | 33 | 34 | 35 |
|-xx-|-xx-| 38 | 39 | 40 |-xx-|-xx-|
|-xx-|-xx-| 45 | 46 | 47 |-xx-|-xx-|

Rather than having to represent this as a 7x7 table by hand, we will use a few JavaScript loops to construct the table for us:

// open the table and create a border around the board:
document.write('<TABLE ALIGN=center BORDER=0 WIDTH=280 CELLSPACING=0
                CELLPADDING=0 BGCOLOR="#00ff00"><TR><TD>&nbsp;&nbsp;
                &nbsp;</TD><TD COLSPAN=7>&nbsp;</TD><TD>&nbsp;&nbsp;
                &nbsp;</TD></TR>');

var count= 1; // holds which cell we are currently dealing with

// construct the table one row at a time:
for (var i=0; i<7; i++) {
 if (i<2 || i>4) {

   // we're doing a row with 2 empty cells, 3 balls, 2 empty cells:
   document.write('<TR><TD>&nbsp;</TD><TD ALIGN=center><IMG SRC=
                   "images/green.gif" WIDTH=40 HEIGHT=40 BORDER=0> 
                   </TD><TD ALIGN=center><IMG SRC="images/green.gif"
                   WIDTH=40 HEIGHT=40 BORDER=0></TD>');
   count+= 2;
   for (var j=0; j<3; j++) {
     document.write('<TD ALIGN=center><A HREF="#" onClick="user_play(
                     \'ball'+ count +'\', 1);"><IMG SRC=
                     "images/ball_1.gif" WIDTH=40 HEIGHT=40 BORDER=0
                     NAME="ball'+ count +'"></A></TD>');
     count++;
   }
   document.write('<TD ALIGN=center><IMG SRC="images/green.gif" WIDTH=40
                   HEIGHT=40 BORDER=0></TD><TD ALIGN=center>
                   <IMG SRC="images/green.gif" WIDTH=40 HEIGHT=40
                   BORDER=0></TD><TD>&nbsp;</TD></TR>\n');
   count+= 2;

 } else {

   // we're doing a full row of 7 balls:
   document.write('<TR><TD>&nbsp;</TD>');
   for (var j=0; j<7; j++) {
     write= 1; if (count == 25) write= 0;
     document.write('<TD ALIGN=center><A HREF="#" onClick="user_play(
                     \'ball'+ count +'\', 1);"><IMG SRC="images/ball_'+
                     write +'.gif" WIDTH=40 HEIGHT=40 BORDER=0
                     NAME="ball'+ count +'"></A></TD>');
     count++;
     }
   }
   document.write('<TD>&nbsp;</TD></TR>\n');
 }
}

// and close the table:
document.write('<TR><TD COLSPAN=9>&nbsp;</TD></TR></TABLE>');

Solitaire Code

Normally, the first thing we should do is to preload the required images but as the images I'm using total only 678 bytes, preloading them is probably a bit over the top:

   

This next section of JavaScript deals with the user's input:

function user_play(where, whatis) {

 // check that we're not already moving balls about:
 if ((whatis && !solving) || (!whatis && solving)) {
   var len= document.images[where].src.length;
   var img= document.images[where].src.substring(len-10, len);

   // click on a hollow
   if (img== "ball_0.gif") {
     if (selected) {
       var sel= selected.substring(4, selected.length);
       var wh= where.substring(4, where.length);

       var middle= check_move(sel, wh);
       if (middle!= "") {
         document.images[selected].src= "images/ball_0.gif";
         document.images[middle].src= "images/ball_0.gif";
         document.images[where].src= "images/ball_2.gif";
         selected= where;

       } else { alert("Only horizontal and vertical jumps over
                       one ball are allowed."); }

     } else { alert("Nothing to move to here.. Select a ball first!"); }

   // click on a unselected (red) ball
   } else if (img== "ball_1.gif") {
     if (selected) {
       document.images[selected].src= "images/ball_1.gif";
     }
     document.images[where].src= "images/ball_2.gif"; selected= where;

   // click on a selected (yellow) ball
   } else if (img== "ball_2.gif") {
     document.images[selected].src= "images/ball_1.gif"; selected= "";

   } else { alert("uh-oh... program error"); }
 }
}

We now have to make sure that the player's move is a valid one. We know which ball has been selected and we also know the target cell is empty, so all we have do is check that the move is in a horizontal or vertical line with just one ball in the middle of them and that the move doesn't run off the edges of the board:

function check_move(from, to) {
 var middle= ""; // holds the board number of the ball in the middle
                 // of "from" and "to"

 from= from* 1; to= to* 1; // convert from string to number
 var diff= from- to; var xpos= (from% 7);

 // first we check to see if "diff" equals either 2, -2, 14 or -14
 // because that holds true for all valid moves only (ie: it is two
 // spaces away in a direct line - due to the setup of the board)
 if (diff== 2) {

   // now we check to see if a wraparound of "from" and "to" has
   // occured on the left hand side of the board
   if (!(xpos==1 || xpos==2)) { middle= "ball"+ (from- 1); }

 } else if (diff== -2) {

   // or alternatively, on the right hand side
   if (!(xpos==6 || xpos==0)) { middle= "ball"+ (from+ 1); }

 } else if (diff== 14) {

   // there's a no need to check this case because of the setup
   // of the board (numbers: left to right, top to bottom)
   middle= "ball"+ (from- 7);

 } else if (diff== -14) {

   // and no need to check this case either
   middle= "ball"+ (from+ 7);

 } else { // illegal move, so "middle" is not set
        }

 // okay, we know it's a valid move, but lets just make sure that
 // there is a marble to be jumped over...
 if (middle!= "") {
   var len= document.images[middle].src.length;
   var img= document.images[middle].src.substring(len-10, len);
   if (img== "ball_0.gif") { middle=""; } // doh! no marble, no move
 }

 // we've done our checks and now we return the board number of the
 // marble in the middle or, if the move is illegal, we return the
 // default ""
 return (middle);

}

That's the main part of the code out of the way, however we still need a restart function in a game like this:

// NB: Inserting images before the solitaire board on the same HTML page
//     will mess things up unless the number of images inserted is added
//     to [i] & [24]
//     Alternatively, convert this to use the image NAME tag...

function set_up() {
 if (document.images && !solving) {
  for (var i=2; i<47; i++) {
    if (!(i>4 && i<9) && (i!=12 && i!=13 && i!=24 && i!=35 && i!=36)
        && !(i>39 && i<44)) {
      document.images[i].src= "images/ball_1.gif";
    }
  }
  document.images[24].src= "images/ball_0.gif";
 }
}

There's a few global variables left to be put in:

// Global variables
var selected= 0;        // currently highlighted ball
var solving= 0;         // state of solving function (0=off, 1=on)

// SOLUTION 1:
var solution= new Array(11, 25, 20, 18, 5, 19, 26, 12, 40, 26, 35, 33,
                  32, 34, 21, 35, 33, 17, 19, 15, 17, 30, 32, 34, 20,
                  18, 16, 3, 5, 19, 33, 45, 31, 24, 38, 47, 45, 31,
                  29, 15, 17, 10, 24, 38, 40, 26, 24, 23, 25, 25);

And to use that "solution" array, we'll provide the icing on the cake - a demo solution function which steps through the array and simulates mouse clicks on the board positions represented by the array:

function solve() {
 if (document.images && !solving) {
   solving= 1; // flag that we're currently involved in solving

   for (var i=0; i<(solution.length); i++) {
     setTimeout("user_play('ball" + solution[i] + "', 0)", i* 300);
   }

   setTimeout("solving= 0", i* 300); // and now we've finished
 }
}

Working Example & Source

View the source code of the working example.

Homework

Further things you could do with this script:

Related items

Kick some booty with invisible Flash!

JavaScripting Essentials

JavaScript Bookmarklets

Why bother with JavaScript?

Writing a midi hifi system in JavaScript

JavaScript Beginners Start Here

Keeping Count of Downloads

JavaScript Games

Online JavaScript Resources

Feedback on 'JavaScript Games #2 - Solitaire'

©2018 Martin Webb