SitePoint Sponsor

User Tag List

Results 1 to 5 of 5
  1. #1
    Community Advisor bronze trophy
    fretburner's Avatar
    Join Date
    Apr 2013
    Location
    Brazil
    Posts
    1,449
    Mentioned
    45 Post(s)
    Tagged
    13 Thread(s)

    [Javascript] Canvas-based Solitaire game

    After helping a forum member who was trying to do the same thing, I decided to have a go at creating a basic version of Solitaire in JS, using the canvas element.

    I'd appreciate any feedback or suggestions, but I'm especially interested in achieving a good separation of concerns. My goal was to write the game in such a way that some of my objects (the Deck object, for example) would be reusable to create other card games.

    The code is on github here: https://github.com/LordZicon/simple-solitaire

    and a demo can be played here: http://nilsonjacques.com/projects/solitaire/

    Note that I haven't implemented drag-and-drop. The cards are moved by clicking the desired card and then clicking the destination (I plan to add some kind of visual cue to show the currently selected card).

    The game uses a 3rd party library for working with the canvas element.

  2. #2
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,315
    Mentioned
    19 Post(s)
    Tagged
    1 Thread(s)
    Quote Originally Posted by fretburner View Post
    I'd appreciate any feedback or suggestions, but I'm especially interested in achieving a good separation of concerns. My goal was to write the game in such a way that some of my objects (the Deck object, for example) would be reusable to create other card games.
    That kind of reusability is definitely a good goal to have.

    There's likely at least one blocker to achieving that. The Deck "class" has a hard dependency on the Card class, which by itself is fine, except that the Card class has a hard dependency on EaselJS and canvas, which means anyone who wants to use your Deck class is also locked-in to EaselJS and canvas.

    A couple thoughts on how you could solve that:

    1) Separate the representation from the rendering. There should be a Card class that doesn't know anything about any kind of rendering. Its sole job would be to represent the concept of a card, its state and behavior.

    2) Another option would be to inject the Deck class with some sort of CardFactory instance. Then in Deck, you would change new Card() to cardFactory.makeCard(). The idea is to remove Deck's dependency on any particular Card class.

    On a side note, it'd be nice if you could improve the readability of the shuffle method. Right now, it almost looks like it was once minified or obfuscated.
    "First make it work. Then make it better."

  3. #3
    Community Advisor bronze trophy
    fretburner's Avatar
    Join Date
    Apr 2013
    Location
    Brazil
    Posts
    1,449
    Mentioned
    45 Post(s)
    Tagged
    13 Thread(s)
    Hey Jeff, thanks for taking a look.

    Quote Originally Posted by Jeff Mott View Post
    The Deck "class" has a hard dependency on the Card class, which by itself is fine, except that the Card class has a hard dependency on EaselJS and canvas, which means anyone who wants to use your Deck class is also locked-in to EaselJS and canvas.
    Would that necessarily be a problem though? If I was going to create other card games I'd want to reuse the rendering code (and EaselJS) and just change the code that dealt with the rules of the game.

    Quote Originally Posted by Jeff Mott View Post
    1) Separate the representation from the rendering. There should be a Card class that doesn't know anything about any kind of rendering. Its sole job would be to represent the concept of a card, its state and behavior.
    This seems like the preferable solution, as it keeps the responsibilities separate. I'll think I'll probably end up doing this.

    Quote Originally Posted by Jeff Mott View Post
    2) Another option would be to inject the Deck class with some sort of CardFactory instance. Then in Deck, you would change new Card() to cardFactory.makeCard(). The idea is to remove Deck's dependency on any particular Card class.
    This sounds like it would be easier to implement in a language with interfaces? With JS, I don't know how I'd ensure that Deck was being passed a sensible implementation of a Card class.

    Quote Originally Posted by Jeff Mott View Post
    On a side note, it'd be nice if you could improve the readability of the shuffle method. Right now, it almost looks like it was once minified or obfuscated.
    Yeah that's a function I found while searching for how to shuffle an array, I agree it's pretty cryptic. I've found a better function here. Is it a good idea to provide some sort of attribution (via a comment or something) when using functions taken from blog posts?

  4. #4
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,315
    Mentioned
    19 Post(s)
    Tagged
    1 Thread(s)
    Sorry I took so long to reply. I usually get email notifications, but I don't think I did for this one.

    Quote Originally Posted by fretburner View Post
    Would that necessarily be a problem though? If I was going to create other card games I'd want to reuse the rendering code (and EaselJS) and just change the code that dealt with the rules of the game.
    I guess that depends on to what degree you want your objects to be reusable. Right now, if someone wanted to use your Deck or Card classes but didn't want to use EasleJS for the rendering, then they're just outta luck.

    Quote Originally Posted by fretburner View Post
    This sounds like it would be easier to implement in a language with interfaces? With JS, I don't know how I'd ensure that Deck was being passed a sensible implementation of a Card class.
    With straight JavaScript, you probably can't ensure that. Such is life with a loosely typed language. You don't get any built-in type checking.

    If you wanted to enforce type checking, you could use the Google compiler with annotations. Or you could use a super-set language such as TypeScript.

    Quote Originally Posted by fretburner View Post
    Yeah that's a function I found while searching for how to shuffle an array, I agree it's pretty cryptic. I've found a better function here. Is it a good idea to provide some sort of attribution (via a comment or something) when using functions taken from blog posts?
    If the code is posted under some sort of open source license, then it's likely that you're technically required to provide attribution by including the original copyright notice. And even if you're not required, it still seems like the "bro" thing to do.

    Though, at the time, I was more concerned with the lack of readability of the code you were using. Even if you copy-paste code from somewhere, I think it's a good idea to make sure it's well formatted and uses good variable names. Because if there is indeed a problem in the code, it's much harder to spot when it's cryptic.
    "First make it work. Then make it better."

  5. #5
    Gre aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    6,123
    Mentioned
    219 Post(s)
    Tagged
    13 Thread(s)
    Hi fretburner,

    Nice game!

    I've just spent a while going trough the code and, as promised, wanted to add a couple of comments.

    Quote Originally Posted by Jeff Mott View Post
    Quote Originally Posted by fretburner View Post
    Would that necessarily be a problem though? If I was going to create other card games I'd want to reuse the rendering code (and EaselJS) and just change the code that dealt with the rules of the game.
    I guess that depends on to what degree you want your objects to be reusable. Right now, if someone wanted to use your Deck or Card classes but didn't want to use EasleJS for the rendering, then they're just outta luck.
    I'm currently attempting to read a book on Object-Oriented design in Ruby (time permitting).
    One of the points that comes up early on in the book is that every dependency in your code is like a little dot of glue that causes your class to stick to things it touches.
    A few dots are necessary, but when one class refers to another class by name, it creates a major sticky spot which will resist change further down the road.
    Thus, if the goal is to write code that embraces change, I guess this should be avoided and dependency injection preferred.

    Quote Originally Posted by Jeff Mott View Post
    Quote Originally Posted by fretburner View Post
    This sounds like it would be easier to implement in a language with interfaces? With JS, I don't know how I'd ensure that Deck was being passed a sensible implementation of a Card class.
    With straight JavaScript, you probably can't ensure that. Such is life with a loosely typed language. You don't get any built-in type checking.
    Isn't this where duck typing comes into play, i.e. if your object has quack(), walk(), and fly() methods, your code can use it wherever it expects an object that can walk, quack, and fly, without requiring the implementation of some "Duckable" interface. Or, put another way, it doesn't matter what the class of object is, only which methods it responds to.

    Regarding the shuffle code, the Fisher-Yates shuffle algorithm seems to be quite well suited to the job.
    There are quite a lot of readable implementations, such as:

    Code:
    function shuffle(array) {
        var counter = array.length, temp, index;
    
        // While there are elements in the array
        while (counter > 0) {
            // Pick a random index
            index = Math.floor(Math.random() * counter);
    
            // Decrease counter by 1
            counter--;
    
            // And swap the last element with it
            temp = array[counter];
            array[counter] = array[index];
            array[index] = temp;
        }
    
        return array;
    }
    Taken from here: http://stackoverflow.com/questions/6...-in-javascript

    Game-play wise:
    • It would be great to have drag and drop functionality. Even after playing the game for several minutes, I was still trying to drag and drop cards
    • It would be nice to have some kind of feedback that I had won (or lost)
    • If you pile up too many cards, they are cut off by the container
    • The jack, queen, king could look more like jacks, queens and kings (just being picky now)

    In summary - great job. This is better than I could have done.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •