Differences between revisions 8 and 9
Revision 8 as of 2008-12-26 16:15:34
Size: 9662
Editor: anonymous
Comment:
Revision 9 as of 2008-12-27 10:57:25
Size: 18486
Editor: anonymous
Comment:
Deletions are marked like this. Additions are marked like this.
Line 90: Line 90:
`MakeCardDLL` extracts the playing card bitmaps from the MS cards.dll which is used by MS for their card games. The cards are numbered resources within the dll with the basic pack being numbered 1 through 52 and the placeholder, card backs etc in the following numbers. In windows XP home the cards.dll is located in the \windows\system32\ directory. `MakeCardDLL` extracts the playing card bitmaps from the MS cards.dll which is used by MS for their card games. The cards are numbered resources within the dll with the basic pack being numbered 1 to 52 and the placeholder card 53, card backs 54 to 65 and the red cross and green circle 67 and 68. In windows XP home the cards.dll is located in the \windows\system32\ directory.
Line 158: Line 158:
Coding the above functions is all you need to use the third party cards.dll. More functions to follow to demonstrate the use of files and workspace variables for card storage and generation. Coding the above functions is all you need to use the third party cards.dll.

`FileCards` extracts the bitmaps from the dll, creates a native text file and uses the `GetBits` function to convert the bitmap data into a suitable form for efficient storage in a native text file and its subsequent retrieval and processing. The file created by running `FileCards` is attached: [[attachment:cards.txt]]

{{{
∇ FileCards;i
                                                       
⍝Make the background and border bit masks ∆bkg and ∆bdr
MakeMasks
                                                       
⍝Tie the cards file
'c:\cards.txt' ⎕ncreate ¯1
                                                       
⍝Get the monchrome masks for the pack
:for i :in ⍳52
    (,GetBits i) ⎕nappend ¯1
:endfor
                                                       
⍝Add blank and border masks
((,∆bkg),,∆bdr) ⎕nappend ¯1
                                                       
⍝Untie cards file
⎕nuntie ¯1
                                             
 ∇
}}}

`GetBits` extracts the bitmap bit data for each card and converts it into a boolean bit mask or masks. The black suit cards are monochrome bitmaps whilst the rest are 24 bit colour bitmaps and need to be processed accordingly. The cards 1 to 10 in all suits can be represented by one mask whilst the picture cards require three masks to represent their constituent colours black, yellow and red. Ravelling the bit masks and storing them in a text file effectively packs the bits into bytes and they show up as characters in the text file.

With this byte structure we have effectively compressed and mildly encoded the data needed to represent a pack of cards. These techniques can be used with all forms of data that can be converted to integers by suitable scaling before encoding and they are the basis of a lot of commercial compression and encoding techniques. Take a look at the AplToUnicode article to see how it is done for the integers representing the unicode utf-8 code points.

{{{
∇ r←GetBits n;hres;hbmp
                                                                                              
⍝Load the windows card bitmap dll and get its handle
hres←⎕wcall 'LoadLibrary' 'c:\windows\system32\cards.dll'
                                                                                              
⍝Get handle to the required bitmap via its resource number
hbmp←⎕wcall 'LoadBitmap' hres n
                                                                                              
⍝Get the bitmap bits
r←⎕wcall 'GetBitmapBits' hbmp 27264 (27264⍴⎕tcnul)
                                                                                              
⍝If get operation unsuccessful release the handles and exit
:if 0=↑r
    :goto rel
                                                                                              
⍝If the number of bytes = 960 its a monochrome bitmap so unpack and reverse black and white
:elseif 960=↑r
    r←∼96 71↑11 ⎕dr 96 10⍴,∊r[2]
                                                                                              
⍝Convert the three colour data to single integer colour value
:else
    r←0 ¯1↓6816 4⍴¯1+⎕av⍳,∊r[2]
    r←96 71⍴256 256 256⊥⍉r
                                                                                              
⍝If it is a picture card split the black, red and yellow data into reverse monochrome masks
    :if ×+/(n≤¨13×⍳4)^n≥¨¯2+13×⍳4
        r←(r=0)⍪(r=255)⍪r=65535
                                                                                              
⍝If it is a red suit non picture card convert to a reverse monochrome mask
    :else
        r←r=255
    :endif
:endif
                                                                                              
⍝Remove the border from the monochrome masks
r[1 2 95 96;]←r[1 2 95 96;]×0
r[;1 71]←r[;1 71]×0
                                                                                              
⍝Release resource handles back to the system
rel:
0 0⍴⎕wcall 'FreeLibrary' hres
0 0⍴⎕wcall 'DeleteObject' hbmp
                                             
 ∇
}}}

Knowing the byte structure in which we stored the cards in the file we can now use `MakeCardF`
to effectively index into the text file to read the bytes we want. The bytes are then unpacked to recover the boolean masks which are then used to recreate cards ready for drawing. I find the use of native text files in this way to be an extremely quick and efficient means of handling certain types of data.

{{{
∇ r←MakeCardF n;v;mb;my;mr
                                                             
⍝Identify position of card mask(s) in the pack
r←¯1↓0,+\v←(52⍴(10⍴1),3⍴3),1 1
                                                             
⍝Tie the cards file
'c:\cards.txt' ⎕ntie ¯1
                                                             
⍝Make the background and border bit masks ∆bkg and ∆bdr
MakeMasks
                                                             
⍝If the card is picture card split out the three colour masks
:if v[n]=3
    mb←96 71⍴11 ⎕dr ⎕nread ¯1 82 852,(852×r[n])
    mr←96 71⍴11 ⎕dr ⎕nread ¯1 82 852,(852×1+r[n])
    my←96 71⍴11 ⎕dr ⎕nread ¯1 82 852,(852×2+r[n])
                                                             
⍝Build the card on a white background with a black border
    r←∆bdr×(16777215×∆bkg-mb+mr+my)+(255×mr)+65535×my
:else
    mb←96 71⍴11 ⎕dr ⎕nread ¯1 82 852,(852×r[n])
    r←∆bdr×(16777215×∆bkg-mb)+mb×0 255 255 255 0 0[⌈n÷13]
:endif
                                                             
⍝Untie the cards file
⎕nuntie ¯1

                                             
 ∇
}}}
                                       
 More functions to follow to demonstrate the use of workspace variables for card storage and generation.

Playing Cards - Under Construction

Having read the SolitaireGame article by StephenTaylor I thought I would set out a series of functions which use the creation of playing cards to demonstrate APL's ability to use data stored in third party resources, native files and the workspace and to manipulate the data using boolean patterns. APL is particularly good at exploiting data patterns.

The functions are written in APL+WIN but they should be readily convertible into any GUI enabled interpreter as they only use a simple form and a picture control for the graphics and ⎕wcall for making low level windows calls.

The main function Solitaire creates a graphical card table complete with the initial deal. See Stephen's article for the logic underpinning the SolitaireGame itself. The functions that follow implement the data handling involved and can be used to create any card game. More importantly they demonstrate some of APL's data handling capabilities. I have deliberately written the functions in what I call "long hand APL" so that they are easy to read and understand.

 ∇  Solitaire;i;j;cbk;n;bkg;ep;c                               
                                                           
⍝Display Card Table                                        
CardTable                                                  
'fm' ⎕wi 'caption' 'Solitaire'                             
                                                           
⍝Get the card back bitmap                                  
cbk←MakeCardDLL 62                                         
                                                           
⍝Get the empty pile bitmap                                 
ep←MakeCardDLL 53                                          
                                                           
⍝Select 8 cards at random                                  
c←10?52                                                    
                                                           
⍝Display discard piles                                     
:for i :in ⍳4                                              
    bkg←(∼∆bkg)×⎕wi 'Get' 7 (180+86×i) 96 71               
    ⎕wi 'Put' (bkg+∆bkg×(1↑,bkg)×∼ep=0) 7 (180+86×i)       
:endfor                                                    
                                                           
⍝Display draw pile                                         
:for i :in ⍳3                                              
    bkg←(∼∆bkg)×⎕wi 'Get' (5+2×i) (5+2×i) 96 71            
    ⎕wi 'Put' (bkg+cbk) (5+2×i) (5+2×i)                    
:endfor                                                    
                                                           
⍝Display first three cards drawn                           
:for i :in ⍳3                                              
    bkg←(∼∆bkg)×⎕wi 'Get' (7+2×i) (80+15×i) 96 71          
    ⎕wi 'Put' (bkg+MakeCardDLL c[i]) (7+2×i) (80+15×i)     
:endfor                                                    
                                                           
⍝Display the seven initial deal piles                      
:for i :in ⍳7                                              
                                                           
⍝Face down cards                                           
    :for j :in ⍳i-1                                        
        bkg←(∼∆bkg)×⎕wi 'Get'(130+3×j) (7+86×i-1) 96 71    
        ⎕wi 'Put' (bkg+cbk) (130+3×j) (7+86×i-1)           
    :endfor                                                
                                                           
⍝Face up cards                                             
    bkg←(∼∆bkg)×⎕wi 'Get' (130+3×i) (7+86×i-1) 96 71       
    ⎕wi 'Put' (bkg+MakeCardDLL c[i+3]) (130+3×i) (7+86×i-1)
:endfor                                                    
                                                           
'fm' ⎕wi 'visible' 1                           
                                                                

This is what you get:

solitaire.jpg

CardTable creates the form and picture control onto which the cards are drawn.

∇ CardTable                                       
                                                
⍝Create a form to hold the picture control      
⎕wself←'fm' ⎕wi 'Create' 'Form'                 
⎕wi 'scale' 5                                   
⎕wi 'color' (40 157 47) ¯1                      
⎕wi 'style' 16                                  
⎕wi 'size' 400 600                              
⎕wi 'caption' 'Card Table'                      
⎕wi 'visible' 0                                 
                                                
⍝Create a picture control to hold the card table
⎕wself←'fm.pic' ⎕wi 'New' 'Picture'             
⎕wi 'scale' 5                                   
⎕wi 'where' 0 0 400 600                         
⎕wi 'imagesize' 400 600                         
⎕wi 'border' 0                                  

MakeCardDLL extracts the playing card bitmaps from the MS cards.dll which is used by MS for their card games. The cards are numbered resources within the dll with the basic pack being numbered 1 to 52 and the placeholder card 53, card backs 54 to 65 and the red cross and green circle 67 and 68. In windows XP home the cards.dll is located in the \windows\system32\ directory.

The bitmaps are modified to make sure they all have black boarders, white backgrounds and rounded corners.

∇ r←MakeCardDLL n;hres;hbmp                                          
                                                                   
⍝Load the windows card bitmap dll and get its handle               
hres←⎕wcall 'LoadLibrary' 'c:\windows\system32\cards.dll'          
                                                                   
⍝Get handle to the required bitmap via its resource number         
hbmp←⎕wcall 'LoadBitmap' hres n                                    
                                                                   
⍝Get the bitmap bits                                               
r←⎕wcall 'GetBitmapBits' hbmp 27264 (27264⍴⎕tcnul)                 
                                                                   
⍝If get operation unsuccessful release the handles and exit        
:if 0=↑r                                                           
    :goto rel                                                      
                                                                   
⍝Make the background and border bit masks ∆bkg and ∆bdr            
MakeMasks                                                          
                                                                   
⍝If the number of bytes = 960 its a black non-picture card         
⍝These are stored in the dll as momochrome bitmaps so unpack bits  
:elseif 960=↑r                                                     
    r←96 71↑11 ⎕dr 96 10⍴,∊r[2]                                    
                                                                   
⍝Add white background                                              
    r←r×∆bkg×16777215                                              
                                                                   
⍝All other cards stored as 24 bit colour bitmaps                   
⍝Convert the colour data to single integer colour values           
:else                                                              
    r←0 ¯1↓6816 4⍴¯1+⎕av⍳,∊r[2]                                    
    r←96 71⍴256 256 256⊥⍉r                                         
:endif                                                             
                                                                   
⍝Convert all borders to black                                      
r←r×∆bdr×∆bkg                                                      
                                                                   
⍝Release resource handles back to the system                       
rel:                                                               
0 0⍴⎕wcall 'FreeLibrary' hres                                      
0 0⍴⎕wcall 'DeleteObject' hbmp                                     

MakeMasks is used to create the masks used to round the corners of the cards. These masks are stored in global variables for later use.

∇ MakeMasks                                        
                                                 
⍝Create a background bit mask with shaped corners
∆bkg←96 71⍴1                                     
∆bkg[1 96;1 2 70 71]←0                           
∆bkg[2 95;1 71]←0                                
                                                 
⍝Create a border bit mask with shaped corners    
∆bdr←96 71⍴1                                     
∆bdr[1 96;2+⍳67]←0                               
∆bdr[2 95;2 70]←0                                
∆bdr[2+⍳92;1 71]←0                               

Coding the above functions is all you need to use the third party cards.dll.

FileCards extracts the bitmaps from the dll, creates a native text file and uses the GetBits function to convert the bitmap data into a suitable form for efficient storage in a native text file and its subsequent retrieval and processing. The file created by running FileCards is attached: cards.txt

∇ FileCards;i                                            
                                                       
⍝Make the background and border bit masks ∆bkg and ∆bdr
MakeMasks                                              
                                                       
⍝Tie the cards file                                    
'c:\cards.txt' ⎕ncreate ¯1                             
                                                       
⍝Get the monchrome masks for the pack                  
:for i :in ⍳52                                         
    (,GetBits i) ⎕nappend ¯1                           
:endfor                                                
                                                       
⍝Add blank and border masks                            
((,∆bkg),,∆bdr) ⎕nappend ¯1                            
                                                       
⍝Untie cards file                                      
⎕nuntie ¯1
                                             

GetBits extracts the bitmap bit data for each card and converts it into a boolean bit mask or masks. The black suit cards are monochrome bitmaps whilst the rest are 24 bit colour bitmaps and need to be processed accordingly. The cards 1 to 10 in all suits can be represented by one mask whilst the picture cards require three masks to represent their constituent colours black, yellow and red. Ravelling the bit masks and storing them in a text file effectively packs the bits into bytes and they show up as characters in the text file.

With this byte structure we have effectively compressed and mildly encoded the data needed to represent a pack of cards. These techniques can be used with all forms of data that can be converted to integers by suitable scaling before encoding and they are the basis of a lot of commercial compression and encoding techniques. Take a look at the AplToUnicode article to see how it is done for the integers representing the unicode utf-8 code points.

∇ r←GetBits n;hres;hbmp                                                                         
                                                                                              
⍝Load the windows card bitmap dll and get its handle                                          
hres←⎕wcall 'LoadLibrary' 'c:\windows\system32\cards.dll'                                     
                                                                                              
⍝Get handle to the required bitmap via its resource number                                    
hbmp←⎕wcall 'LoadBitmap' hres n                                                               
                                                                                              
⍝Get the bitmap bits                                                                          
r←⎕wcall 'GetBitmapBits' hbmp 27264 (27264⍴⎕tcnul)                                            
                                                                                              
⍝If get operation unsuccessful release the handles and exit                                   
:if 0=↑r                                                                                      
    :goto rel                                                                                 
                                                                                              
⍝If the number of bytes = 960 its a monochrome bitmap so unpack and reverse black and white   
:elseif 960=↑r                                                                                
    r←∼96 71↑11 ⎕dr 96 10⍴,∊r[2]                                                              
                                                                                              
⍝Convert the three colour data to single integer colour value                                 
:else                                                                                         
    r←0 ¯1↓6816 4⍴¯1+⎕av⍳,∊r[2]                                                               
    r←96 71⍴256 256 256⊥⍉r                                                                    
                                                                                              
⍝If it is a picture card split the black, red and yellow data into reverse monochrome masks   
    :if ×+/(n≤¨13×⍳4)^n≥¨¯2+13×⍳4                                                             
        r←(r=0)⍪(r=255)⍪r=65535                                                               
                                                                                              
⍝If it is a red suit non picture card convert to a reverse monochrome mask                    
    :else                                                                                     
        r←r=255                                                                               
    :endif                                                                                    
:endif                                                                                        
                                                                                              
⍝Remove the border from the monochrome masks                                                  
r[1 2 95 96;]←r[1 2 95 96;]×0                                                                 
r[;1 71]←r[;1 71]×0                                                                           
                                                                                              
⍝Release resource handles back to the system                                                  
rel:                                                                                          
0 0⍴⎕wcall 'FreeLibrary' hres                                                                 
0 0⍴⎕wcall 'DeleteObject' hbmp 
                                             

Knowing the byte structure in which we stored the cards in the file we can now use MakeCardF to effectively index into the text file to read the bytes we want. The bytes are then unpacked to recover the boolean masks which are then used to recreate cards ready for drawing. I find the use of native text files in this way to be an extremely quick and efficient means of handling certain types of data.

∇ r←MakeCardF n;v;mb;my;mr                                     
                                                             
⍝Identify position of card mask(s) in the pack               
r←¯1↓0,+\v←(52⍴(10⍴1),3⍴3),1 1                               
                                                             
⍝Tie the cards file                                          
'c:\cards.txt' ⎕ntie ¯1                                      
                                                             
⍝Make the background and border bit masks ∆bkg and ∆bdr      
MakeMasks                                                    
                                                             
⍝If the card is picture card split out the three colour masks
:if v[n]=3                                                   
    mb←96 71⍴11 ⎕dr ⎕nread ¯1 82 852,(852×r[n])              
    mr←96 71⍴11 ⎕dr ⎕nread ¯1 82 852,(852×1+r[n])            
    my←96 71⍴11 ⎕dr ⎕nread ¯1 82 852,(852×2+r[n])            
                                                             
⍝Build the card on a white background with a black border    
    r←∆bdr×(16777215×∆bkg-mb+mr+my)+(255×mr)+65535×my        
:else                                                        
    mb←96 71⍴11 ⎕dr ⎕nread ¯1 82 852,(852×r[n])              
    r←∆bdr×(16777215×∆bkg-mb)+mb×0 255 255 255 0 0[⌈n÷13]    
:endif                                                       
                                                             
⍝Untie the cards file                                        
⎕nuntie ¯1                                               

                                             
  • More functions to follow to demonstrate the use of workspace variables for card storage and generation.

Author: GrahamSteer

PlayingCards (last edited 2012-02-18 15:11:38 by KaiJaeger)