home *** CD-ROM | disk | FTP | other *** search
/ Dan Appleman's Visual Bas…s Guide to the Win32 API / Dan.Applmans.Visual.Basic.5.0.Programmers.Guide.To.The.Win32.API.1997.Ziff-Davis.Press.CD / VB5PG32.mdf / vbpg32 / samples5 / ch09 / puzzle.frm (.txt) < prev    next >
Encoding:
Visual Basic Form  |  1997-02-16  |  14.0 KB  |  386 lines

  1. VERSION 5.00
  2. Begin VB.Form Puzzle 
  3.    Appearance      =   0  'Flat
  4.    BackColor       =   &H80000005&
  5.    Caption         =   "Puzzle"
  6.    ClientHeight    =   3645
  7.    ClientLeft      =   1095
  8.    ClientTop       =   1770
  9.    ClientWidth     =   3735
  10.    BeginProperty Font 
  11.       Name            =   "MS Sans Serif"
  12.       Size            =   8.25
  13.       Charset         =   0
  14.       Weight          =   700
  15.       Underline       =   0   'False
  16.       Italic          =   0   'False
  17.       Strikethrough   =   0   'False
  18.    EndProperty
  19.    ForeColor       =   &H80000008&
  20.    LinkMode        =   1  'Source
  21.    LinkTopic       =   "Form1"
  22.    PaletteMode     =   1  'UseZOrder
  23.    ScaleHeight     =   3645
  24.    ScaleWidth      =   3735
  25.    Begin VB.PictureBox Picture2 
  26.       Appearance      =   0  'Flat
  27.       BackColor       =   &H80000005&
  28.       ForeColor       =   &H80000008&
  29.       Height          =   2535
  30.       Left            =   1680
  31.       Picture         =   "PUZZLE.frx":0000
  32.       ScaleHeight     =   2505
  33.       ScaleWidth      =   1905
  34.       TabIndex        =   1
  35.       Top             =   360
  36.       Visible         =   0   'False
  37.       Width           =   1935
  38.    End
  39.    Begin VB.PictureBox Picture1 
  40.       Appearance      =   0  'Flat
  41.       BackColor       =   &H80000005&
  42.       ForeColor       =   &H80000008&
  43.       Height          =   1215
  44.       Left            =   240
  45.       ScaleHeight     =   79
  46.       ScaleMode       =   3  'Pixel
  47.       ScaleWidth      =   79
  48.       TabIndex        =   0
  49.       Top             =   360
  50.       Width           =   1215
  51.    End
  52.    Begin VB.Menu MenuScramble 
  53.       Caption         =   "Scramble"
  54.    End
  55.    Begin VB.Menu MenuLoad 
  56.       Caption         =   "Load"
  57.    End
  58.    Begin VB.Menu MenuEmptyCaption 
  59.       Caption         =   "Empty"
  60.       Begin VB.Menu MenuEmpty 
  61.          Caption         =   "Black"
  62.          Checked         =   -1  'True
  63.          Index           =   0
  64.       End
  65.       Begin VB.Menu MenuEmpty 
  66.          Caption         =   "White"
  67.          Index           =   1
  68.       End
  69.       Begin VB.Menu MenuEmpty 
  70.          Caption         =   "Random"
  71.          Index           =   2
  72.       End
  73.    End
  74. Attribute VB_Name = "Puzzle"
  75. Attribute VB_GlobalNameSpace = False
  76. Attribute VB_Creatable = False
  77. Attribute VB_PredeclaredId = True
  78. Attribute VB_Exposed = False
  79. Option Explicit
  80. ' Copyright 
  81.  1997 by Desaware Inc. All Rights Reserved
  82. '   Calculate all source and destination rectangles
  83. '   Call this whenever the form size changes or the
  84. '   image bitmap is changed.
  85. Private Sub CalcRects()
  86.     Dim x%, y%, pos%
  87.     Dim bmsegwidth%, bmsegheight%
  88.     Dim picsegwidth%, picsegheight%
  89.     Dim di&
  90.     ' Find the approx. height and width of each tile on
  91.     ' the puzzle screen.
  92.     picsegwidth% = (PuzzleRect.Right - PuzzleRect.Left) / 5
  93.     picsegheight% = (PuzzleRect.Bottom - PuzzleRect.Top) / 5
  94.     ' Get information on the bitmap in picture2
  95.     ' This loads the BITMAP structure bmInfo with information
  96.     ' on the bitmap.
  97.     di = GetObjectAPI(Picture2.Picture, Len(BMinfo), BMinfo)
  98.     bmsegwidth% = BMinfo.bmWidth / 5
  99.     bmsegheight% = BMinfo.bmHeight / 5
  100.     ' Fill in the rectangle description for each rectangle on
  101.     ' the destination DC
  102.     For y% = 0 To 4
  103.         For x% = 0 To 4
  104.             pos% = y% * 5 + x%
  105.             DestRects(pos%).Top = y% * picsegheight%
  106.             DestRects(pos%).Bottom = (y% + 1) * picsegheight%
  107.             DestRects(pos%).Left = x% * picsegwidth%
  108.             DestRects(pos%).Right = (x% + 1) * picsegwidth%
  109.         Next x%
  110.     Next y%
  111.     ' Fill in the rectangle description for each rectangle on
  112.     ' the source bitmap
  113.     For y% = 0 To 4
  114.         For x% = 0 To 4
  115.             pos% = y% * 5 + x%
  116.             SourceRects(pos%).Top = y% * bmsegheight%
  117.             SourceRects(pos%).Bottom = (y% + 1) * bmsegheight%
  118.             SourceRects(pos%).Left = x% * bmsegwidth%
  119.             SourceRects(pos%).Right = (x% + 1) * bmsegwidth%
  120.             ' Make sure the rectangle does not exceed the
  121.             ' source area for the bitmap or StretchBlt will fail
  122.             If x% = 4 Then SourceRects(pos%).Right = BMinfo.bmWidth
  123.             If y% = 4 Then SourceRects(pos%).Bottom = BMinfo.bmHeight
  124.         Next x%
  125.     Next y%
  126. End Sub
  127. '   Creates a brush to use for the empty square
  128. '   This function demonstrates the creation of device
  129. '   independent bitmaps, converting DIBs to a device dependent
  130. '   bitmap and finally converting a DDB into a brush.
  131. Private Sub CreateEmptyBrush()
  132. Dim compbitmap&
  133. Dim bih As BITMAPINFOHEADER
  134. Dim bi As BITMAPINFO
  135. ReDim colarray&(16)
  136. Dim x%
  137. Dim di&
  138. ' This used to be a string
  139. Dim da(32) As Byte  ' Each byte contains 2 x 4bit pixels
  140. Dim buf$
  141. Dim bufstart&, sourceaddr&
  142. Dim oldbm&
  143.     ' Prepare the bitmap information header
  144.     bih.biSize = 40     ' 40 bytes in this structure
  145.     bih.biWidth = 8     ' 8x8 -we'll be creating a brush
  146.     bih.biHeight = 8    ' from this bimap
  147.     bih.biPlanes = 1    ' DIB's always 1 plane
  148.     bih.biBitCount = 4  ' 16 colors, 4 bits/color
  149.     bih.biCompression = BI_RGB  ' no compression
  150.     bih.biSizeImage = 0         ' Not needed on BI_RGB
  151.     bih.biXPelsPerMeter = 0     ' Not used
  152.     bih.biYPelsPerMeter = 0     ' Not used
  153.     bih.biClrUsed = 16          ' All colors used
  154.     bih.biClrImportant = 0      ' All colors important
  155.     ' Now fill the color array
  156.     For x% = 0 To 15
  157.         colarray&(x%) = QBColor(x%)
  158.     Next x%
  159.     ' Now we need to set the data array - for now, we're
  160.     ' just going to put in random pixel data
  161.     For x% = 1 To 32
  162.         ' Note how we pack two nibbles
  163.         ' The old way
  164.         ' Mid$(da, x%, 1) = Chr$(Int(Rnd * 16) + Int(Rnd * 16) * 16)
  165.         ' The new way
  166.         da(x%) = Int(Rnd * 16) + Int(Rnd * 16) * 16
  167.     Next x%
  168.     ' Now we load the BITMAPINFO structure bi
  169.     LSet bi.bmiHeader = bih
  170.     ' Now copy the color array into the BITMAPINFO
  171.     ' bi.bmiColors string which begins 40 characters after
  172.     ' the start of the structure.
  173.     ' Refer to Chapter 15 for information on the subtleties
  174.     ' of extracting addresses for strings in a structure.
  175.     bufstart& = agGetAddressForObject(bi.bmiHeader.biSize) + 40
  176.     ' Get the address of the start of the colarray color array
  177.     sourceaddr& = agGetAddressForLong(colarray&(0))
  178.     ' And copy the 64 bytes
  179.     agCopyDataBynum sourceaddr&, bufstart&, 64
  180.     ' Now create the bitmap
  181.     compbitmap& = CreateDIBitmap(Puzzle.hdc, bih, CBM_INIT, da(0), bi, DIB_RGB_COLORS)
  182.     ' Now create a brush from this bitmap
  183.     EmptySquareBrush& = CreatePatternBrush(compbitmap)
  184.     ' And delete the source bitmap
  185.     di = DeleteObject&(compbitmap&)
  186. End Sub
  187. Private Sub DoUpdate()
  188. '   Update the picture with rectangles based on the
  189. '   puzzle array
  190.     Dim x%
  191.     For x% = 0 To 24
  192.         UpdateOne x%
  193.     Next x%
  194. End Sub
  195. ' Initialization routine
  196. Private Sub Form_Load()
  197.     Randomize
  198.     SetPuzzleSize   ' Set the size of the puzzle window
  199.     CalcRects       ' Calculate the window tiles
  200.     Scramble        ' Scramble them
  201.     CreateEmptyBrush    ' Create a random brush for the
  202.                         ' empty square.
  203. End Sub
  204. '   When the form is resized, call SetPuzzleSize to
  205. '   adjust the size of the picture window and rescale
  206. '   the image.
  207. Private Sub Form_Resize()
  208.     SetPuzzleSize   ' Set the size of the puzzle window
  209.     CalcRects       ' And recalculate the tiles
  210.     Picture1.Refresh    ' Update the picture control
  211. End Sub
  212. '   Clean up by deleting GDI objects that are no longer
  213. '   needed.
  214. Private Sub Form_Unload(Cancel As Integer)
  215.     Dim di&
  216.     If ShadowDC& Then di = DeleteDC(ShadowDC)
  217.     If EmptySquareBrush& Then di = DeleteObject(EmptySquareBrush)
  218. End Sub
  219. '   Choose the color for the empty square
  220. Private Sub MenuEmpty_Click(Index As Integer)
  221.     Dim x%
  222.     MenuEmpty(EmptySquareMode%).Checked = 0
  223.     MenuEmpty(Index).Checked = -1
  224.     EmptySquareMode% = Index
  225.     DoUpdate
  226. End Sub
  227. '   Bring up the file load dialog box to load a new
  228. '   bitmap into the puzzle.
  229. Private Sub MenuLoad_Click()
  230.     DoTheUpdate = 0 ' Preset the update flag to false
  231.     Puzzle2.Show 1  ' Show the file load form modal
  232.     If DoTheUpdate Then ' A valid bitmap was loaded
  233.         CalcRects   ' Recalculate the tiles
  234.         Scramble    ' And refresh the image
  235.     End If
  236.         
  237. End Sub
  238. '   Rescramble the bitmap image
  239. Private Sub MenuScramble_Click()
  240.     Scramble
  241.     DoUpdate  ' Redraw the puzzle window
  242. End Sub
  243. '   Clicking on a tile next to the empty tile causes that
  244. '   tile to slide into the empty space.
  245. Private Sub Picture1_MouseDown(Button As Integer, Shift As Integer, x As Single, y As Single)
  246.     Dim pt As POINTAPI
  247.     Dim u%
  248.     Dim xpos%, ypos%
  249.     Dim bxpos%, bypos%
  250.     Dim dx%, dy%, tval%, hidden%
  251.     pt.x = x    ' Picture1 scalemode is pixels
  252.     pt.y = y
  253.     ' Find the location of the black square
  254.     ' Tile 24 in the bitmap is the missing piece.
  255.     For hidden% = 0 To 24
  256.         If Position%(hidden%) = 24 Then Exit For
  257.     Next hidden%
  258.     For u% = 0 To 24
  259.         ' Find out which rectangle in the DestRects array
  260.         ' contains the point specified by the mouse click.
  261.         If PtInRect(DestRects(u%), pt.x, pt.y) Then
  262.             Exit For
  263.         End If
  264.     Next u%
  265.     ' Now find the X and Y coordinates for the mouse click
  266.     ' and for the hidden tile.
  267.     xpos% = u% Mod 5
  268.     ypos% = Int(u% / 5)
  269.     bxpos% = hidden% Mod 5
  270.     bypos% = Int(hidden% / 5)
  271.     ' The tile can slide into the empty square if it is
  272.     ' one away from the empty square on the horizontal
  273.     ' or vertical axis (but not both).
  274.     dx% = Abs(xpos% - bxpos%)
  275.     dy% = Abs(ypos% - bypos%)
  276.     If (dx% = 1 And dy% = 0) Or (dx% = 0 And dy% = 1) Then
  277.         tval% = Position%(u%)
  278.         ' So simply swap this tile with the hidden one
  279.         Position%(u%) = Position%(hidden%)
  280.         Position%(hidden%) = tval%
  281.         ' And update both these tiles
  282.         UpdateOne u%
  283.         UpdateOne hidden%
  284.     End If
  285. End Sub
  286. '   Paint picture1 by calling the full puzzle Update routine
  287. Private Sub Picture1_Paint()
  288.     DoUpdate
  289. End Sub
  290. '   Scramble the puzzle array
  291. Private Sub Scramble()
  292. Dim x%, newpos%, hold%
  293.     ' Initialize the positions
  294.     For x% = 0 To 24
  295.         Position%(x%) = x%
  296.     Next x%
  297.     ' Now scramble them
  298.     For x% = 0 To 24
  299.         ' For each source position, choose a random
  300.         ' location and swap the two values.
  301.         ' This is a simple and effective technique to
  302.         ' randomize an array of numbers.
  303.         newpos% = Int(Rnd * 25)
  304.         hold% = Position(x%)
  305.         Position(x%) = Position(newpos%)
  306.         Position(newpos%) = hold%
  307.     Next x%
  308. End Sub
  309. '   Sets the picture1 control to the visible form area
  310. '   Also creates a compatible bitmap to work with
  311. '   Call this any time the size of the form changes
  312. Private Sub SetPuzzleSize()
  313. Dim rc As RECT
  314.     Dim di&
  315.     Picture1.BorderStyle = 0
  316.     Picture1.Left = 0
  317.     Picture1.Top = 0
  318.     di = GetClientRect(Puzzle.hwnd, rc)
  319.     ' Actually, we need not subtract off rc.left and rc.top
  320.     ' below as these fields are always 0 after a call to
  321.     ' GetClientRect
  322.     ' Note the conversion to twips in order to set the
  323.     ' picture size using the VB properties
  324.     ' We could have used the MoveWindow API call as well- or just set it based on the ScaleWidth and ScaleHeight
  325.     ' of the form itself
  326.     Picture1.Width = Screen.TwipsPerPixelX * (rc.Right - rc.Left)
  327.     Picture1.Height = Screen.TwipsPerPixelY * (rc.Bottom - rc.Top)
  328.     ' This line is actually not necessary - we could
  329.     ' have just used a copy of rc because we just set
  330.     ' the client area to that specified by rc!
  331.     GetClientRect Picture1.hwnd, PuzzleRect
  332.     ' Create a compatible memory DC for Picture1
  333.     If ShadowDC Then di = DeleteDC(ShadowDC)
  334.     ShadowDC = CreateCompatibleDC(Picture1.hdc)
  335. End Sub
  336. '   Copies a single tile from the picture2 bitmap to the
  337. '   appropriate space in the picture1 destination.
  338. '   x% is the position on the puzzle to update
  339. Private Sub UpdateOne(x%)
  340.     Dim oldbm&, pos&, oldbrush&
  341.     ' Temporary variables for copying
  342.     Dim sx&, sy&, sw&, sh&, dx&, dy&, dw&, dh&
  343.     Dim di&
  344.     ' Select the bitmap into the ShadowDC
  345.     oldbm& = SelectObject(ShadowDC&, Picture2.Picture)
  346.     ' Select the random brush we created into the picture DC
  347.     If EmptySquareBrush& <> 0 Then oldbrush& = SelectObject(Picture1.hdc, EmptySquareBrush&)
  348.     ' Get the position in the bitmap.
  349.     ' Position 24 is the empty square
  350.     pos& = Position(x)
  351.     ' Calculate the rectangle on the puzzle display being
  352.     ' updataed
  353.     dx& = DestRects(x).Left
  354.     dy& = DestRects(x).Top
  355.     dw& = DestRects(x).Right - dx&
  356.     dh& = DestRects(x).Bottom - dy&
  357.         
  358.     ' The bitmap locations are based on x% - the source
  359.     ' location in the bitmap
  360.     sx& = SourceRects(pos&).Left
  361.     sy& = SourceRects(pos&).Top
  362.     sw& = SourceRects(pos&).Right - sx&
  363.     sh& = SourceRects(pos&).Bottom - sy&
  364.         
  365.     ' Now do the transfer
  366.     ' Transfer all tiles from the source except for tile
  367.     ' number 24 which is the black one.
  368.     If pos <> 24 Then
  369.         di& = StretchBlt(Picture1.hdc, dx, dy, dw, dh, ShadowDC, sx, sy, sw, sh, SRCCOPY)
  370.     Else  ' Tile #24 is empty - use EmptySquareMode% to
  371.           ' determine what type of square to set.
  372.         Select Case EmptySquareMode%
  373.             Case 0
  374.                 di = PatBlt(Picture1.hdc, dx, dy, dw, dh, BLACKNESS)
  375.             Case 1
  376.                 di = PatBlt(Picture1.hdc, dx, dy, dw, dh, WHITENESS)
  377.             Case 2
  378.                 di = PatBlt(Picture1.hdc, dx, dy, dw, dh, PATCOPY)
  379.          End Select
  380.     End If
  381.     ' Select the bitmap out of the shadow DC
  382.     di = SelectObject(ShadowDC, oldbm)
  383.     ' And select the brush back to the original one
  384.     If EmptySquareBrush <> 0 Then di = SelectObject(Picture1.hdc, oldbrush)
  385. End Sub
  386.