dimmesdale has asked for the wisdom of the Perl Monks concerning the following question:

I don't really know where to post this (it's not a problem I'm having, its not actual code. . . yet, but here goes):

I'm planning to work on a quick GUI application with Tk called PerlSol (a solitare program in Perl). I've outlined a basic plan (with some holes, admitingly), and want some input. In particular, it seems to me that using OOP would be the way to go (I'm originally a C/C++ programmer and know a great deal about OOP and how it relates to C/C++, however I'm new to Perl's quirks, so any suggestions as to this would be helpful: I'm planning to read perlboot and perltoot and whatever else may help with this).

The plan (if it can be called that) is presented here, in all its humbleness (its just something quick I want; the program isn't designed to be major, just a small project, but I want some guidance, and a detailed, or not-so-detailed, plan often helps in making code in my opinion).

What I plan to do is make a module that will facilitate the creation of games based on a defined set of rules. For example, for a normal (as I first learnt it) solitare game, you could create seven piles of 1 to 7 cards respectively, only showing the top card, create another pile of cards (the deck), create a waste pile, create four piles to be filled starting with aces, then one could create the 'rules' for these piles (max. number of cards, if there is one, increasing and/or same suit, etc.). The current design plans to facilitate these 'behind the scene' details, so the specific game can be concerned mainly with creating bindings and functions to deal with those bindings. Of course, this is just a projected way of dealing with this (IF ANYTHING BETTER CAN BE DONE, I'D LIKE TO KNOW, I'm posting this so I don't waste too much of my time learning I should be doing something different)

(I make up my own conventions at times, but it should be self-explanatory--it is to me, anyway! I use [something here] to denote that something here is optional. . .I'v seen this various places, the rest follows)

# Don't freak out about not having 'use strict' etc. # :) This is just psuedo code at places my $card = new Card([%attr]); $card->show([x,y]); # displays card on screen, optionally # with the upper-left coordinate # being (x,y) ->suit([val]); # returns (and optionally sets) the # suit to value (data type undecided) ->value([val]); # returns (and optionally sets) the # card number to value (1-13,A,K,Q,J) ->where([pile]); # returns (and optionally sets) the # pile where card is placed ->state([state]); # Sets card to be face up/face down ## "Friend" (from C++, loose usage) functions (designed for ## use with an array of cards (or maybe hash?) IsAlternatingColor(); # e.g., ...black,red,black,red... IsSameColor(); # e.g., ...black,black,black... IsAlternatingSuit(); # e.g., ...diamond,club,heart... IsSameSuit(); # e.g., ...spade,spade,spade... IsIncreasing(); # e.g., 2,3,4,5... IsDecreasing(); # e.g., K,Q,J,10... my $pile = new Pile([%attr]); $pile->maxcards([val]); # returns (and optionally # sets) max. amount of cards # (or NO_LIMIT constant) $pile->nextcard(); # returns suit/value of next # card, beginning at top $pile->get(x); # same thing, but xth card # (0 is top, 1 is second, ...) $pile->rules(rules); # Rules that cards must abide # by (e.g., decreasing) ## A tableau is a 2-d array of piles ## A waste/stock/foundation/etc. is a pile (or a collection ## thereof) ## Terms inspired by some of the free downloadable ## solitare programs, e.g., 1 2 3 Free Solitare ## at: www.123freesolitaire.com/ User-Level notes ---------------- Undo feature Change deck style Change different features of display Deign your own game Next Move AutoPlay (last two use a primitive AI algorithm; of course, I don't plan to do all these things at first, some are just ideas I 'may' choose to implement later -- its just a simple program for someone I know)

Replies are listed 'Best First'.
Re: PerlSol
by djantzen (Priest) on Jul 02, 2002 at 22:34 UTC

    You could do an object hierarchy where specific cards inherit from an abstract Card class, but I think that's overkill. You're probably right to differentiate between cards based on instance variables. However, I would suggest modifying your Card class such that you separate the internal data, e.g., suit, color, number, from methods for displaying and manipulating cards.

    CardModel : ->suit([val]); # returns (and optionally sets) the # suit to value (data type undecided) ->value([val]); # returns (and optionally sets) the # card number to value (1-13,A,K,Q,J) ->isSameColor(CardModel c); ->isSameSuit(CardModel c); CardController: # returns (and optionally sets) the # pile where card is placed ->where(cardmodel, [pile]); # Sets card to be face up/face down ->state(cardmodel, [state]); CardView : # displays card on screen, optionally with the # upper-left coordinate being (x,y) ->show(cardmodel, [x,y]);
    Sorry to sound like a Smalltalk textbook, but this way you have a clean separation of responsibilities, rather than lumping them into a single class.

    My other suggestion is to introduce the concept of the Set, in addition to the Pile. Recall that in Solitaire you can move cards in groups so long as they conform to the rule of descending, alternating order. This could be implemented best I think as a blessed array.

Re: PerlSol (solitaire in Perl)
by jjohn (Beadle) on Jul 03, 2002 at 01:16 UTC

    In particular, it seems to me that using OOP would be the way to go (I'm originally a C/C++ programmer and know a great deal about OOP and how it relates to C/C++, however I'm new to Perl's quirks, so any suggestions as to this would be helpful: I'm planning to read perlboot and perltoot and whatever else may help with this).

    Oddly enough, Dr. Conway's book Object Oriented Perl from Manning is an excellent introduction and academic smackdown on the topic of OOP. Like you, Damian also came from a strong C++ background that informs his take on Perl OO.

    One comment I'll offer is that having your Card object also knowing about how to display itself can get ugly if you intend to support multiple displays (ascii, web, tk, etc). I would think you'd want a Board class that takes a Hand object (a collection of Cards) and Does the Right Thing based on the game logic and additional display rules that are particular to the display type.

    Of course, this may be overthinking the project. Perhaps you should just bang out a quick prototype that works. That will make the redesign go ever so much quicker. Well that's Brooks' theory anyway. :-)

    Cheers.

Don't forget CPAN is your friend
by jaldhar (Vicar) on Jul 03, 2002 at 19:59 UTC

    There is a package on CPAN called Games::Cards which might save a lot of wheel reinventing. Did you check it out?

    --
    જલધર

Re: PerlSol (solitaire in Perl)
by John M. Dlugosz (Monsignor) on Jul 03, 2002 at 20:21 UTC
    I agree that many objects in an OO approach suggest themselves, that would be of general use. "piles" that behave as stacks (push/pop) and show the top card or show no cards would be very common for all draw/discard piles. A "hand" of cards that presents the UI would also be good, but not needed for solitare. A deck would be derived from the stack and include shuffle and deal methods.

    As for rules... that's the hard part. Writing code that uses these objects is straightforward. But describing it more formally so the objects simply interact the way they are required to? Might not be the way to go, since that's not how they operate in real life. The piles are not cogs in a clock or parts in a simulation; they are dumb data and an outside agent manipulates them and knows all the rules and plays.

    However, I like the idea of putting in formal rules of play. This can be done with an expert system and backward-chaining logic. For example, you can only add a card to a pile if specific conditions are met, and this behaves as an exception if the play logic violates it. Then the rules of play are written in procedural logic, but can access the formal rules; e.g. obtain a list of legal discards before deciding which one to play. Then when it does discard (remove card from hand, push onto discard pile) it makes sure that this meets the formal criteria.

Re: PerlSol (solitaire in Perl)
by mattr (Curate) on Jul 04, 2002 at 14:07 UTC
    You might be academically interested in some perl code in Solitaire which is not a game. This is linked from Counterpane.
Re: PerlSol (solitaire in Perl)
by dimmesdale (Friar) on Jul 04, 2002 at 18:19 UTC
    Here's a quick transformation of that into some code (not complete). Some issues I'm trying to think about now are:

    When a value in the card is changed (for example, the x,y coordinates) it needs to be updated (perhaps I should get rid of this and handle it on the Tk/GUI end; possible advantage is that it would extend the module to work with other GUIs or even with text)

    Hmm. . . actually, that idea would make the other issues I was working with go away too. Comments?

    #!/usr/bin/perl ###################################### # cards.pm -- a perl module to allow # # for the easy creation of solitare # # games (easily expanded to others) # ###################################### package cards; use strict; use warnings; ###################################### # Card type and methods # ###################################### sub new { # usage: $caller->new([\%attr]) # keys for %attr: x_pos, y_pos, # suit, value, where, state, # gif, deck (for future use) my $proto = shift; my $self = shift; my $class = ref($proto) || $proto; bless ($self, $class); return $self; } sub show { # usage: $caller->show([\%pos]) # keys for %pos: x_pos, y_pos # return: hash with keys x_pos, # y_pos (of upper-left corner) my $self = shift; if (@_) { $self->{x_pos} = $_->{x_pos}; $self->{y_pos} = $_->{y_pos}; } return { x_pos => $self->{x_pos}, y_pos => $self->{y_pos} }; } sub suit { # usage: $caller->suit([$suit]) # $suit =~ /[CDHScdhs]/ # return: suit of card my $self = shift; if (@_) { $self->{suit} = shift } return $self->{suit}; } sub value { # usage: $caller->value([$val]) # $val =~ /[1-13AKQJ]/ # return: value of card my $self = shift; if (@_) { $self->{value} = shift } return $self->{value}; } sub where { # usage: $caller->where([$pile]) # $pile is of type Pile # return: Pile reference where # card is located my $self = shift; if (@_) { $self->{where} = shift } return $self->{where}; } sub state { # usage: $caller->state([$state]) # $state =~ (up)|(down) # return: returns state of card my $self = shift; if (@_) { $self->{state} = shift } return $self->{state}; } ###################################### # "Friend" functions for rules # ###################################### sub IsAlternatingColor { # usage: IsAlternatingColor(@cards) # where for all i less than $#cards # $cards[i] is of type Card # return: true or false (1 or 0) my $cards = shift; # A zero represents black, a 1 red; orignially set # $old_color to opposite of that of the first card my $old_color = $cards->[0]->suit() =~ /CScs/ ? 1 : 0; my $cur_color; for (@$cards) { $cur_color = $_->suit() =~ /CScs/ ? 0 : 1; return 0 if $cur_color = $old_color; } return 1; } sub IsSameColor { # usage: IsSameColor(@cards) # where for all i less than $#cards # $cards[i] is of type Card # return: true or false (1 or 0) my $cards = shift; # A zero represents black, a 1 red; orignially set # $old_color to the same of that of the first card my $old_color = $cards->[0]->suit() =~ /CScs/ ? 0 : 1; my $cur_color; for (@$cards) { $cur_color = $_->suit() =~ /CScs/ ? 0 : 1; return 0 if $cur_color != $old_color; } return 1; }
Re: PerlSol (solitaire in Perl)
by helgi (Hermit) on Jul 05, 2002 at 14:33 UTC
    Greg Bacon has written a solitaire game in Perl/Tk. I don't propose that you copy him, but his solution may be of some help to you. Regards,
    Helgi Briem