Open Flowers This is the message passing approach to the patience solitaire game Flower Garden. This is the variant Flower Venus Garden with Laughing Flowers based on a relaxed ruleset. You can move only one card at time but from anywhere to anywhere. Sequences in the flower beds have to be build by suit. The foundations are built from ace to king but cards can be moved back into the game. You can double click a card and if possible it will find its place in the foundations. You can place up to sixteen cards in the bouquet. There is a good chance of winning the game. (The layout of the bouquet, i.e. the reserve of cards at the bottom of the table is done automatically but only after a card has been moved successfully. Rethinking and correcting this is left as exercise for the interested player.) Description & Rules One deck of 52 cards. < from wikipedia Thirty-six cards are dealt in to six columns, each containing six cards. The columns are called the "flower beds" and the entire tableau is sometimes called "the garden." The sixteen leftover cards become the reserve, or "the bouquet." The top cards of each flower-bed and all of the cards in the bouquet are available for play. Cards can only be moved one at a time and can be built either on the foundations or on the other flower beds. The foundations are built up by suit, from Ace to King (a general idea of the game is to release the aces first). The cards in the garden, on the other hand, can be built down regardless of suit and any empty flower bed can be filled with any card. The cards in the bouquet can be used to aid in building, be put into the foundations, or fill an empty flower bed. The game is won when all cards end up in the foundations. > end from wikipedia NaL (Not a License) This is free source code - free as in "free like a bird". This is open source and marketing (which is a little bit like gardening): if someone gives you a pen at no cost and you have to refill it to write down you're favourite poem with it and someone else sings it and the producer of that song makes a whole lot of money with it then never forget: they could have used any pen to do the job. Don't break my pencils! Technical Note: While it is written in a rather traditional way without mixins, traits or an emphasis on surrogates it might contain ideas, concepts and terms that go beyond today's message passing "customs". A new message passing primitive "broadcast" and the term hub for certain classes are consequences of thinking about the future of message passing, parallel objects and one new program code abstraction system "to rule them all" especially when it comes to open source and the user's ability to adapt and program the shiny some thousand money units worth machine in front of him. From LISP machines to BSD to Mach message passing microkernel to NextStep to ... but that is far away: for me it was from QBASIC to RISC assembler to NetBeans to Dr. Racket to whatever comes in handy. Source Tree Layout This implementation of Flower Garden started as one file and the one file approach ended at about 1300 lines of text. Then some parts have been cut out and are now called sections and the whole was renamed "Open Flowers". Afterwards with features like the hint system and additional animations the complexity has risen as have the prerequisites on the side of the programmer to substantially change the program. /Open Flowers/README.txt This little text. /Open Flowers/Open Flowers.rkt: Classes: stack and subclasses, garden (program action coordinator), flower-garden (GUI shell with menu) Pseudo classes: flower, flower-garden-table /Open Flowers/sections/pretext.rkt (section via include) The original pretext with extensions to the Algorithmic Language Scheme, Card Logic and debugging/developer utilities. /Open Flowers/sections/flower-dialog.rkt (module via require) The flower color picker. Class: flower-dialog /Open Flowers/sections/layout96l.rkt (section via include) The current table and card layout 9 cards wide 6 cards high with the foundations on the left side. Class: layout96l /Open Flowers/sections/layout77.rkt (DEFUNCT) The original layout 7 cards high and 7 cards wide. It's interface is not up to date anymore. Class: layout77 (not up to date) /Open Flowers/card-faces/ OXYGEN removed - both Oxygen and White Oxygen need some corrections to enhance readability. They're currently unusable. The loader is provided nonetheless. (This folder contains alternate card faces based on the Oxygen set and a loader to create a deck that can be used in Open Flowers. The Oxygen Card Faces are in the public domain. See the corresponding /svg/ files for further information.) Sketch: Object Relations / Data Structure (w/o the implicit relations of the Virtual Playing Cards Library) table (sfg, pseudo subclass) <1- <1-1> flower-dialog \ / = | layout hub <1- program action coordinator (sfg) ||<1-1> gui shell = = = | | | foundation (sfg) <n-1>/ | | | flower-bed (sfg) <n-1>-/ | | | bouquet (sfg) <1-1>---/ \ \ \ \ \ n> flowers (sfg, pseudo class) \ n> flowers (sfg, pseudo class) n> flowers (sfg, pseudo class) <1-1>: a one to one relations where both objects know of each other object <1- object: the left object is unaware of the hosting by the right object; the right object hosts one left object object <n-1> object: the right object can host several left objects which are aware of the hosting (i.e. have a back link) sfg: stateful game object (may be part of the game state) || or =: Parallel Object Interface The interface can handle concurrent requests. (In this case it manages message passing traffic with a semaphore and is generally not really part of the data graph. The layout hub is almost completely stateless at its interface, i.e. with respect to incoming traffic, but it is not used concurrently.) Sketch: Class Hierarchy object \ \ \ \ \ \ \ garden (program action coordinator) \ \ flower-garden (gui shell) \ stack: foundation, flower-bed, bucket layout dialog: flower-dialog Virtual Playing Cards Library Extension card: pseudo subclass flowers (toplevel) table: pseudo subclass table (in garden) Description of the User Interaction Most of the event handling is done by the views card<%>, table<%> and region. (See Event Handling) According to the rules only one card may be moved at any time. Parallel Objects (Shared Unit: Display via /table/ object / Animation System) We need the parallel objects approach with the serializing application interface because of the nested event handling. Without this synchronization we can start a new deal, i.e. /reset-game/, and while its animation is running we can start another new deal via the shell's menu. Sometimes the request seems to be ignored even without synchronization but usually it's possible. At a first glance this gives us the expected behavior: the first deal is interrupted and the second new deal starts. But after the second deal completes the first deal continues usually with putting the rest of it's cards (which are kept locally in the environment of /initial-deal/) into the bucket. After these nested deals we are left with a lot of cards in the bucket and some empty spots within the flower bed stacks. To avoid this and other race conditions we choose to ignore user requests from the shell as long as we are busy. A better tactic would to build in the possibility to cancel the initial deal and then start the second new deal on the user's behalf. Another issue arises with the card wiggling which shows the dealer's hint: it eventually puts the card back at the original position where the wiggle started. If we make it start a new game during the wiggling /wiggle-flower-in-garden/ will put back the card to its original position but only after the inital deal has changed all the positions of all the other cards. Same for our window cleaner. The current implementation does away with the mess by using a /semaphore/. There is second line of defence to protect against programming errors because it's easy to tinker around with the /semaphore/ in garden class, i.e. the program action coordinator, and to use it incorrectly. That second security measure is based on a non atomic boolean. No matter if the /set!/ operation on a boolean is atomic or not - there will always be - at least in theory - that some processor cycles long hole where we have checked the mutex but haven't set it yet. Yet this added layer of protection against misuse of the /semaphore/ works quite well while developing. To avoid further complications when using the all purpose semaphore metapher directly we encapsulate it with two wrappers: /ignore-when-busy/ and /queue-with-busy/. This is all we need and we sometimes queue events to not "block" the main thread too much. Other times we queue events because the Virtual Playing Cards Library queues events and we want to have a certain order. Queuing two callbacks consecutively seems to provoke a dead lock like behavior which might be not any more than an arbitrary restriction on how we can use the /run loop/ but it may even be another consequence of the nested event handling creating the need to queue again. Thus we chain the queuing of the callbacks in the first place. See the implementation of the /game-is-won/ situation. The user requests originating from the callbacks of the Virtual Playing Cards Library need to be serialized in time, too. NB When /ignore-when-busy/ is called while we are busy it will display a debug level message with "code red" and ring the bell. Usually if we decide for this kind of audible feedback it would be better to implement it in the shell / application class. Event Handling There is no documented message passing protocol for the event stream coming from region and table objects. There is no well defined order but we can look at the order of events arriving and think about it. A drag starts out with a single click event. That single click event might or might not match our interactor model but we need to make its consequences unhappen because the user did not click but drag and we got to know about this only later on. There is a place to do that: see set-interactive-region-callback! in stack% add-region. A double click is a time and coordinate dependent event usually composed of two single clicks. Depending on the libraries and low level routines in use we might expect to see the following sequences in the event stream on the same card in a short time interval: a period "." denotes a single click event, a colon ":" denotes a double click event .. two clicks no double click event : one double click event - no single click events .: one single click event and one double click event ..: two single click events and one double click event :.. one double click event and two single click events .:. one single click event then a double click event then a single click event again We can adapt our interaction model or Interactor (see Garnet) to avoid misinterpreting events. A single click sets the selection and the next click clears the selection. A double click clears the selection. With this behaviour in mind there remain two cases of event sequences which are not handled in the right way: the first one (..) which is degenerate because there is no double click event at all. We could try to look at the time codes of the events and determine if it was a double click or not but we cannot check the pointer coordinates here which is crucial to recognize double clicks and drags. Then the last sequence (.:.) is problematic, too. And testing suggests that this is our favourite one and only sequence of events. On the developer's machine each double click event is surrouned by single clicks and we end up with a card that has already been moved by the double click event which cleared selection and we now receive a single click event for that card: it ends up selected (in a foundation). Please bear in mind that a click event is a synthesized composed event itself: usually a click is defined as the following sequence of low level events: mouse button down, mouse button up (within the same spot region in screen coordinates). The Virtual Playing Cards Library does not use that definition of a click. Another aspect of our event handling here to keep in mind is the fact that we do not recieve a stream of events as a stream of events but the user defined thunks are invoked as event handlers at certain not well defined moments not in time but in the execution order of the Virtual Playing Cards Library routines intertwined with garbage collection and operating system interaction. Interaction Models have to be learned by the user. Some are nowadays common accepted standard (even if not well documented) but everything changes: where you're favourite lisp based action game developing environment from back then allowed you to choose one of a set of actions and then apply it on some object today we expect to select the object.e.g. file and then choose one of its associated actions, e.g. delete. This is true on most modern graphical user interaction shells. But when you do graphic design and photo retouch we first choose the action or tool and then apply it to some part of the object to design. Vice verse these behaviours and people will tell you that it's completely broken even if they're already used to both interaction models. I don't touch I click - but that's another chapter and out of the scope of this little card game. Some interaction models to not work out anyway: the select card then destination approach falls for Moon Flower Garden because the bucket is full of cards. Do you want to move the flower to the bucket or do you want to select a card in the bucket? Animations (outdated, see mred/15064 in Racket's bug database) It seems the animations of the Virtual Playing Cards Library are primarily intended to visualize algorithms or better: their time ordered consequences. There is a problem where the animation of a dragged card doesn't start at the final position of the drag when/where the handler is called but it starts at the original location of the card which might be percieved as an unpleasant flickering. That behaviour of the animation engine depends on a set home-region but not only. Not setting the home-region at all in the class definition of stack% gives us the correct starting positions for the animations after a drag from the Bouquet to a Flower Bed. All drags originating from Flower Beds do still animate with the described wrong starting position. This might have to do with one of the messages in reconfigure-cards but after all: the animation engine does not feature any fine grained control and is abusing the cards<%> configuration in an undocumented way. Nevertheless the wrong starting position seems to be a real bug of the animation engine. BTW It doesn't animate snap backs either ... Perhaps they want to fix it. (mred/15064) Bugs and Maintenance Bug (mred/15065): in a new game after program start the second card in a flower bed which was face down could be moved. As reconfigure-cards in the flower-bed class is rather clear about this issue and because it happened during the animation of an unrelated move and is part of the Virtual Playing Cards Library's special behaviours. DONE -Dev: no more bangs in message passing- Bug (mred/15065): during an animation an accidental double click (without the final button up) onto the table background started a selection with a rectangle whose border was the inverse color of the current table background. The z-axis order of the cards and perhaps other things were screwed up afterwards. Bug (Mac OS X, mred/12252): Racket cancels shutdown, restart, logout. FIXED garden%: policy an all entry points: ignore when busy -Bug: There are still some race conditions left with the animation system. Put one of the aces on the foundations, ask for a new deal during the animation and after the next deal it ended up in the bucket. Then it jumped to the next foundation of the previous animation. Investigating stock games: Rummy does not have any other user controls. Spider's animations are usually to fast to enter the race condition but you might want to undo a move (with CMD-Z as a keyboard shortcut on the Mac) during the animation. Crazy 8s features an on-table user control: the clear button. It is blocked during animation. Then there is an option button in the panel but it creates another table on screen. Blackjack: on-table controls n/a during animation. Aces: undo via keyboard shortcut during animation leaves the cards on the stacks but face down. Clicking them provokes another deal onto the stacks from the deck and other non regular behavior. Memory and Go Fish do not have any in-game user controls. It doesn't seem to be possible to protect from these race conditions completely as they originate from the nested event event handling of custom user controls and we don't know about ongoing animations in the Library. Perhaps some accessible mutex / semaphore on animation would be helpful. It doesn't seem to be possible to protect from these race conditions completely as they originate from the nested event event handling of custom user controls and we don't know about ongoing animations in the Library. Perhaps some accessible mutex / semaphore on animation would be helpful. The games without controls from the toolkit incl. menu but with on-table button regions only do not have that problem.- Dev: A better syntax / notation for the following sequence in interfaces with ignore-when-busy policy would be helpful. (define (proc/private ...) ...) (define/public (proc ...) (ignore-when-busy {lambda () (apply proc/private ...)})) Perhaps its better to seperate these interfaces or to use something like: {interface #:policy ignore-while-busy method1 method2 ...} Dev: We cannot listen on booleans??? We need a better way to connect menu items with states to disable and enable them automagically. Dev: The different layout variants are out of sync and are copy&paste work. Use a common-layout superclass instead.