shape & shape : tangram puzzle game

Aplikacja do przetestowania: Tangram

Czym jest tangram? Ja pierwszy raz jak zobaczyłem tą prostą grę stwierdziłem, że to gra dla dzieci i nic w niej skomplikowanego nie ma. Trochę się pomyliłem w tych ocenach i podczas tworzenia tego małego projektu, rozrósł się on do dużego projektu. Przeszkody na które natrafiłem musiałem jakoś przeskoczyć a gdzieniegdzie wymagało to ode mnie dużego kombinowania i pomysłowości. Gra będzie wyglądała tak jak na gifie:

Prawym przyciskiem myszy, będziemy obracać nasze obiekty. Zaczynamy więc od stworzenia formy:

Rodzaj elementu Nazwa elementu Ustawienia
Form Form1 Name: Form1
Text: shape & shape : tangrampuzzle gameCSV
Size: 1091; 808
DoubleBuffered:True
Icon: Sha&sha.ico
Panel Panel3 Name: Panel3
Size: 100;100
Location: 973; 560
Anchor: Bottom, Right
Panel Panel4 Name: Panel3
Size: 100;100
Location: 973; 666
Anchor: Bottom, Right
Timer Timer1 Name: Timer1
Panel Panel2 Name: Panel2
Size: 450; 241
Location: 623; 7
Anchor: top, Right

Poniżej opis ułożenia elementów w panelu 2, który będzie zawierał przyciski i wzór do wykonania.

 
Rodzaj elementu Nazwa elementu Ustawienia
Panel Panel1 Name: Panel1
Size: 240; 240
Location: 0;0
BackColor: Turquoise
Button Button1 Name: Panel3
Size: 201; 23
Location: 246; 168
Text: Skopiuj wzór
Button Button2 Name: Button2
Size: 1201; 46
Location: 246; 5
Font:Microsoft Sans Serif;
15,75pt
Text: Start / Losuj
Button Button3 Name: Button3
Size: 201; 41
Location: 246; 197
Font: Microsoft Sans Serif; 12pt 15,75pt
Text: Poddaje się
Button Button4 Name: Button4
Size: 201; 23
Location: 246; 57
Text: Resetuj
Label Label1 Name: Label1
Size: 149; 39
Location: 278; 100
BackColor: White
BorderStyle: FixedSingle
Font: Microsoft Sans Serif; 14,25pt
TextAlign: MiddleCenter

Reszta elementów, będzie tworzona w sposób dynamiczny. Nasza gra będzie się składać z Formy, modułu i jednej klasy:

  • Form1 – forma
  • GenerujKsztalty – klasa
  • PubliczneElementy – moduł

Czy taka hierarchia jest potrzebna? Oczywiście nie, wszystko można wrzucić do jednej formy i też będzie działać ale tak jest bardziej schludnie i przejrzyście.

Klasa GenerujKsztalty będzie tworzyć nam nasze elementy (głównie bitmapy) i strukturę a moduł PubliczneElementy będzie przechowywał listę wzorów i naszą strukturę. Zaczniemy od przygotowania planszy, to na niej odbywać będzie się gra i nie jest powiązana z innymi elementami, po prostu generuje obrazek tła formy dlatego ją dodamy sobie na początku. Kiedy forma jest już przygotowana, przechodzimy do jej kodu (F7).

Public Class Form1

    Dim trzymany As Integer = 0 'przechowuje aktualny indeks (na liście kolekcjaElementow) trzymanego obiektu
    Dim jestWRuchu As Boolean = False 'wskazuje, czy trzymamy jakiś obiekt
    Dim GenerujKsztalty As New GenerujKsztalty 'inicjujemy nową klasę
    Dim ran As New Random 'wartość losowa
    Dim WybranyWzor As Integer(,) 'tablica położenia elementów wybranego wzoru
    Dim kolekcjaElementow As New List(Of ksztalt) 'przechowuje struktury
    Dim miejsceNamalowaniaKwadratu As Point 'punkt rysowania planszy

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        PrzygotujTapete()
        '  kolekcjaElementow = GenerujKsztalty.GenerujElementyGry()
        '  PrzygotujWzory()
    End Sub

    Dim listaKwadratow(5, 5) As Rectangle 'przechowuje obiekty rectangle naszej planszy

    Private Sub PrzygotujTapete()
        Dim tapeta As New Bitmap(Me.Width, Me.Height)
        Dim g As Graphics = Graphics.FromImage(tapeta)

        'podział formy na prawą i lewą stronę
        'jasny szary
        g.FillRectangle(New SolidBrush(Color.FromArgb(224, 224, 224)),
                        New Rectangle(0, 0, Me.Width - 74 - 400, Me.Height))
        'ciemny szary
        g.FillRectangle(New SolidBrush(Color.Gray), New Rectangle(Me.Width - 74 - 400, 0, 74 + 400, Me.Height))

        'Wielkość planszy ustalamy na 480x480
        'pole będzie miało wymiar 80x80
        'tło planszy (musi być białe!)
        g.FillRectangle(New SolidBrush(Color.White), New Rectangle((Me.Width - 274 - 680) / 2,
                                                                   (Me.Height - 680) / 2, 480, 480))

        'przygotowujemy linie kropkowaną
        Dim dashValues As Single() = {5, 2, 5, 4}
        Dim blackPen As New Pen(Color.Black, 2)
        blackPen.DashPattern = dashValues

        'rysujemy za jej pomocą linie dzielące pola
        g.DrawLine(blackPen, New Point((Me.Width - 274 - 680) / 2 + 80, (Me.Height - 680) / 2),
                   New Point((Me.Width - 274 - 680) / 2 + 80, (Me.Height - 680) / 2 + 480))
        g.DrawLine(blackPen, New Point((Me.Width - 274 - 680) / 2 + 160, (Me.Height - 680) / 2),
                   New Point((Me.Width - 274 - 680) / 2 + 160, (Me.Height - 680) / 2 + 480))
        g.DrawLine(blackPen, New Point((Me.Width - 274 - 680) / 2 + 240, (Me.Height - 680) / 2),
                   New Point((Me.Width - 274 - 680) / 2 + 240, (Me.Height - 680) / 2 + 480))
        g.DrawLine(blackPen, New Point((Me.Width - 274 - 680) / 2 + 320, (Me.Height - 680) / 2),
                   New Point((Me.Width - 274 - 680) / 2 + 320, (Me.Height - 680) / 2 + 480))
        g.DrawLine(blackPen, New Point((Me.Width - 274 - 680) / 2 + 400, (Me.Height - 680) / 2),
                   New Point((Me.Width - 274 - 680) / 2 + 400, (Me.Height - 680) / 2 + 480))

        g.DrawLine(blackPen, New Point((Me.Width - 274 - 680) / 2, (Me.Height - 680) / 2 + 80),
                   New Point((Me.Width - 274 - 680) / 2 + 480, (Me.Height - 680) / 2 + 80))
        g.DrawLine(blackPen, New Point((Me.Width - 274 - 680) / 2, (Me.Height - 680) / 2 + 160),
                   New Point((Me.Width - 274 - 680) / 2 + 480, (Me.Height - 680) / 2 + 160))
        g.DrawLine(blackPen, New Point((Me.Width - 274 - 680) / 2, (Me.Height - 680) / 2 + 240),
                   New Point((Me.Width - 274 - 680) / 2 + 480, (Me.Height - 680) / 2 + 240))
        g.DrawLine(blackPen, New Point((Me.Width - 274 - 680) / 2, (Me.Height - 680) / 2 + 320),
                   New Point((Me.Width - 274 - 680) / 2 + 480, (Me.Height - 680) / 2 + 320))
        g.DrawLine(blackPen, New Point((Me.Width - 274 - 680) / 2, (Me.Height - 680) / 2 + 400),
                   New Point((Me.Width - 274 - 680) / 2 + 480, (Me.Height - 680) / 2 + 400))

        Dim x As Integer = (Me.Width - 274 - 680) / 2
        Dim y As Integer = (Me.Height - 680) / 2

        'Rysujemy pola któr będą ułatwiać grę poprzez auto ułożenie elementów w momęcie ich ułożenia
        'więcej na ten temat powiem na blogu
        For j As Integer = 0 To 5
            For i As Integer = 0 To 5
                listaKwadratow(i, j) = New Rectangle((x - 40) + i * 80, (y - 40) + j * 80, 80, 80)
                'Dim pan As New Panel
                'pan.Location = New Point((x - 40) + i * 80, (y - 40) + j * 80)
                'pan.BackColor = Color.Transparent
                'pan.BorderStyle = BorderStyle.Fixed3D
                'pan.Size = New Size(80, 80)
                'Me.Controls.Add(pan)
            Next
        Next

        'rusujemy ramkę pola gry
        Dim thickRedPen As Pen = New Pen(Color.Tomato, 5)
        g.DrawRectangle(thickRedPen, New Rectangle((Me.Width - 274 - 680) / 2, (Me.Height - 680) / 2, 480, 480))
        'pobieramy punkt rysowania planszy, potrzebny nam będzie do sprawdzenia czy wszystkie elementy zostały 
        'ułożone i nie zostawiliśmy pustych pól
        miejsceNamalowaniaKwadratu = New Point((Me.Width - 274 - 680) / 2, (Me.Height - 680) / 2)
        'ustawiamy tapetę formy
        Me.BackgroundImage = tapeta
    End Sub
End Class

Co tu się dzieje i co jest co? Po pierwsze, trzeba tutaj napomknąć, że jest to zwykłe malowanie/dodawanie grafiki do/na bitmapę. Najpierw inicjujemy bitmapę, w tym wypadku jest to zmienna „tapeta” na którą nanosimy elementy graficzne przy użyciu zmiennej „g”, na samym końcu ustawiamy bitmapę „tapeta” jako tło formy. Tutaj na szczególną uwagę zasługuje utworzona tablica obiektów „Rectangle”:

    Dim listaKwadratow(5, 5) As Rectangle 'przechowuje obiekty rectangle naszej planszy

Posłuży nam ona do równania położonych obiektów. Aby to zobaczyć, odblokujcie elementy w 57 linijce:

        For j As Integer = 0 To 5
            For i As Integer = 0 To 5
                listaKwadratow(i, j) = New Rectangle((x - 40) + i * 80, (y - 40) + j * 80, 80, 80)
                Dim pan As New Panel
                pan.Location = New Point((x - 40) + i * 80, (y - 40) + j * 80)
                pan.BackColor = Color.Transparent
                pan.BorderStyle = BorderStyle.Fixed3D
                pan.Size = New Size(80, 80)
                Me.Controls.Add(pan)
            Next
        Next

teraz na naszą formę nałożone zostaną przezroczyste panele:

Pod tymi panelami, są obiekty „Rectangle” które będziemy wykorzystywać do centrowania położonych elementów. Spójżcie na obrazek poniżej, przedstawia on sytuacje w której trzymany jest obiekt 1 (trzymane obiekty podświetlają się na czarno). Ciężko jest umieścić go tak aby był na odpowiednim miejscu, co do pixela.

Punkt (0,0) naszego trójkąta jest na obszarze żółtego kwadratu, i to do tego kwadratu nastąpi wyrównanie. Po położeniu obiektu, zostanie on wyrównany do środka tego kwadratu.

To zapewni nam, że ostateczna forma będzie zwarta i będzie tworzyć całość. Potrzebne będzie to do końcowego porównania. Kiedy mamy już gotowe te elementy, odblokujcie element „PrzygotujWzory()”:

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        PrzygotujTapete()
        '  kolekcjaElementow = GenerujKsztalty.GenerujElementyGry()
        PrzygotujWzory()
    End Sub

Przechodzimy sobie teraz do naszego Modułu „PubliczneElement”

Module PubliczneElement
    'Trzworzymy elementy naszej struktury
    Public Structure ksztalt
        Dim ksztalt As Rectangle
        Dim bitmap As Bitmap
        Dim wartosc As Integer
        Dim kat As Integer
        Dim polozenie As Integer()
    End Structure

    'przechowuje wybrany kształt
    Public KsztaltyWzory As New List(Of Integer(,))

    'lista wzorów
    Sub PrzygotujWzory()
        KsztaltyWzory.Add({{12, 0, 4, 3}, {11, 0, 3, 0}, {11, 1, 4, 0}, {12, 2, 2, 3},
                          {13, 4, 1, 0}, {13, 3, 1, 2}, {1, 0, 0, 3}, {1, 0, 2, 2},
                          {3, 1, 0, 1}, {3, 2, 0, 6}, {4, 4, 0, 4}, {2, 5, 2, 2}, {2, 2, 5, 1}, {4, 2, 3, 7}})
        KsztaltyWzory.Add({{12, 0, 4, 0}, {13, 0, 3, 0}, {13, 1, 2, 0}, {12, 0, 2, 2},
                          {11, 2, 1, 0}, {11, 3, 0, 0}, {2, 2, 2, 0}, {4, 2, 3, 2},
                          {3, 3, 2, 2}, {3, 4, 2, 5}, {1, 4, 4, 0}, {1, 4, 0, 1}, {2, 0, 0, 3}, {4, 0, 0, 1}})
        KsztaltyWzory.Add({{11, 0, 3, 0}, {11, 1, 2, 0}, {12, 2, 0, 3}, {13, 2, 3, 3},
                          {12, 0, 4, 3}, {13, 1, 5, 1}, {3, 0, 1, 7}, {1, 0, 0, 2},
                          {1, 4, 0, 1}, {3, 2, 0, 6}, {2, 2, 2, 3}, {4, 1, 4, 3}, {4, 4, 3, 6}, {2, 5, 2, 2}})
        KsztaltyWzory.Add({{11, 1, 2, 0}, {13, 4, 1, 0}, {12, 2, 2, 1}, {13, 2, 1, 3},
                          {12, 3, 3, 0}, {11, 0, 1, 0}, {2, 5, 2, 2}, {2, 2, 5, 1},
                          {3, 2, 3, 7}, {1, 0, 4, 0}, {3, 0, 2, 3}, {1, 0, 0, 2}, {4, 2, 0, 5}, {4, 4, 0, 4}})
        KsztaltyWzory.Add({{13, 0, 1, 3}, {13, 0, 2, 1}, {12, 1, 3, 2}, {11, 2, 3, 0},
                          {12, 2, 4, 0}, {11, 4, 3, 0}, {1, 4, 4, 0}, {4, 0, 0, 5},
                          {3, 0, 4, 0}, {2, 0, 2, 0}, {2, 5, 0, 2}, {1, 2, 0, 1}, {4, 4, 0, 0}, {3, 2, 2, 6}})
        KsztaltyWzory.Add({{11, 4, 3, 0}, {12, 4, 4, 0}, {11, 3, 4, 0}, {13, 0, 0, 1},
                          {13, 0, 0, 2}, {12, 1, 1, 2}, {1, 2, 0, 1}, {2, 1, 2, 1},
                          {4, 0, 2, 1}, {2, 0, 5, 1}, {4, 0, 4, 5}, {3, 2, 3, 2}, {1, 4, 0, 2}, {3, 4, 1, 1}})
        KsztaltyWzory.Add({{11, 3, 0, 0}, {1, 4, 0, 1}, {12, 4, 2, 1}, {11, 3, 2, 0},
                          {12, 2, 2, 3}, {13, 0, 5, 3}, {13, 4, 3, 0}, {2, 0, 2, 0},
                          {4, 1, 2, 6}, {1, 2, 4, 1}, {4, 4, 3, 2}, {3, 1, 1, 6}, {2, 0, 0, 3}, {3, 0, 0, 3}})
        KsztaltyWzory.Add({{12, 2, 2, 3}, {11, 4, 3, 0}, {11, 1, 0, 0}, {12, 0, 4, 3},
                          {13, 1, 5, 1}, {13, 0, 2, 2}, {4, 0, 0, 0}, {2, 1, 1, 0},
                          {4, 3, 4, 7}, {2, 1, 4, 1}, {1, 2, 0, 1}, {3, 2, 2, 6}, {3, 4, 1, 1}, {1, 4, 0, 2}})
        KsztaltyWzory.Add({{12, 0, 4, 3}, {12, 4, 4, 0}, {13, 2, 4, 1}, {11, 2, 1, 0},
                          {13, 2, 5, 3}, {11, 1, 4, 0}, {1, 2, 0, 2}, {1, 2, 2, 0},
                          {3, 4, 2, 3}, {4, 4, 0, 0}, {2, 5, 0, 2}, {2, 0, 0, 0}, {4, 0, 0, 4}, {3, 0, 2, 5}})
        KsztaltyWzory.Add({{11, 1, 0, 0}, {13, 2, 1, 0}, {11, 2, 3, 0}, {13, 5, 2, 0},
                          {12, 2, 4, 0}, {12, 3, 3, 1}, {1, 4, 4, 0}, {3, 0, 4, 0},
                          {2, 0, 2, 0}, {2, 1, 1, 0}, {4, 0, 0, 0}, {4, 2, 0, 4}, {3, 3, 1, 0}, {1, 4, 0, 2}})
    End Sub
End Module

Struktura to po prostu nasz nowy obiekt składający się ze zmiennych. Tutaj na uwagę zasługuje element tworzący listę wzorów. Każdy wzór, który musimy ułożyć posiada listę elementów, ich położenie (współrzędne) i wartość obrotu w prawo.

Potrzeba aż tylu elementów, ponieważ nasz zbiór będzie również przechowywał potencjalny sposób ułożenia elementów:

Teraz zajmiemy się początkowym ułożeniem elementów, czyli generowaniem kształtów do układania. Odblokujcie:

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        PrzygotujTapete()
        kolekcjaElementow = GenerujKsztalty.GenerujElementyGry()
        PrzygotujWzory()
    End Sub

Obiekty będziemy dzielić na dwie grupy, czerwone i turkusowe. Czerwonych jest 6 a turkusowych 8. Turkusowe będą miały wartość od 1-4 a czerwone od 11-13. Aby móc namalować obiekt należy stworzyć dwa elementy, kształt (Rectangle) i obrazek (Bitmap). Wszystko to przygotujemy ręcznie. Każdego elementu jest po dwie sztuki. Gotowe elementy będziemy przechowywać na liście „kolekcjaElementow”.

Public Class GenerujKsztalty

    Public Function GenerujElementyGry()

        Dim coll As New List(Of ksztalt)
        Dim bitmMain As Bitmap
        Dim g As Graphics
        'kolor ramki
        Dim penRed As New Pen(Brushes.Tomato)
        Dim penTur As New Pen(Brushes.PaleTurquoise)
        'szerokość ramki
        penRed.Width = 2
        penTur.Width = 2

        Dim k As New ksztalt 'struktura

        'figury 1
        For i As Integer = 0 To 1
            'tworzymy nową bitmapę o określonej wielkości
            bitmMain = New Bitmap(160, 160)
            g = Graphics.FromImage(bitmMain)
            'rysujemy wypełniony kształt
            g.FillPolygon(Brushes.Turquoise, New PointF() {New Point(0, 0), New Point(80, 80),
                          New Point(160, 0), New Point(160, 160), New Point(0, 160)})
            'rysujemy ramkę
            g.DrawPolygon(penTur, New PointF() {New Point(0, 0), New Point(80, 80), New Point(160, 0),
                          New Point(160, 160), New Point(0, 160)})
            'dodajemy bitmapę do struktury
            k.bitmap = bitmMain
            k.kat = 0 'kąt startowy
            'określamy startowe położenie obiektu i jego wielkość
            k.ksztalt = New Rectangle(765 + 10 * i, 560, 160, 160)
            'numer kształtu
            k.wartosc = 1
            'dodajemy do listy którą zwróci nasza funkcja
            coll.Add(k)
        Next

        'figury 2
        For i As Integer = 0 To 1
            bitmMain = New Bitmap(80, 320)
            g = Graphics.FromImage(bitmMain)
            g.FillPolygon(Brushes.Turquoise, New PointF() {New Point(0, 0), New Point(80, 80),
                          New Point(80, 240), New Point(0, 320)})
            g.DrawPolygon(penTur, New PointF() {New Point(0, 0), New Point(80, 80),
                          New Point(80, 240), New Point(0, 320)})
            k.bitmap = bitmMain
            k.kat = 0
            k.ksztalt = New Rectangle(640 + 10 * i, 420, 80, 320)
            k.wartosc = 2
            coll.Add(k)
        Next

        'figury 3
        For i As Integer = 0 To 1
            bitmMain = New Bitmap(240, 160)
            g = Graphics.FromImage(bitmMain)
            g.FillPolygon(Brushes.Turquoise, New PointF() {New Point(0, 160), New Point(160, 0),
                          New Point(240, 80), New Point(160, 160), New Point(0, 160)})
            g.DrawPolygon(penTur, New PointF() {New Point(0, 160), New Point(160, 0),
                          New Point(240, 80), New Point(160, 160), New Point(0, 160)})
            k.bitmap = bitmMain
            k.kat = 0
            k.ksztalt = New Rectangle(830 + 10 * i, 330 + 10 * i, 240, 160)
            k.wartosc = 3
            coll.Add(k)
        Next

        'figury 4
        For i As Integer = 0 To 1
            bitmMain = New Bitmap(160, 240)
            g = Graphics.FromImage(bitmMain)
            g.FillPolygon(Brushes.Turquoise, New PointF() {New Point(0, 0), New Point(160, 0),
                          New Point(80, 80), New Point(80, 240), New Point(0, 160)})
            g.DrawPolygon(penTur, New PointF() {New Point(0, 0), New Point(160, 0), New Point(80, 80),
                          New Point(80, 240), New Point(0, 160)})
            k.bitmap = bitmMain
            k.kat = 0
            k.ksztalt = New Rectangle(727 + 10 * i, 290 + 10 * i, 160, 240)
            k.wartosc = 4
            coll.Add(k)
        Next



        'figury 11
        For i As Integer = 0 To 1
            bitmMain = New Bitmap(160, 160)
            g = Graphics.FromImage(bitmMain)
            g.FillPolygon(Brushes.Red, New PointF() {New Point(80, 0), New Point(160, 80),
                          New Point(80, 160), New Point(0, 80)})
            g.DrawPolygon(penRed, New PointF() {New Point(80, 0), New Point(160, 80),
                          New Point(80, 160), New Point(0, 80)})
            k.bitmap = bitmMain
            k.kat = 0
            k.ksztalt = New Rectangle(243 + i * 20, 580, 160, 160)
            k.wartosc = 11
            coll.Add(k)
        Next

        'figury 12
        For i As Integer = 0 To 1
            bitmMain = New Bitmap(160, 160)
            g = Graphics.FromImage(bitmMain)
            g.FillPolygon(Brushes.Red, New PointF() {New Point(0, 160), New Point(160, 160), New Point(160, 0)})
            g.DrawPolygon(penRed, New PointF() {New Point(0, 160), New Point(160, 160), New Point(160, 0)})
            k.bitmap = bitmMain
            k.kat = 0
            k.ksztalt = New Rectangle(30 + i * 10, 580, 160, 160)
            k.wartosc = 12
            coll.Add(k)
        Next

        'figury 13
        For i As Integer = 0 To 1
            bitmMain = New Bitmap(80, 240)
            g = Graphics.FromImage(bitmMain)
            g.FillPolygon(Brushes.Red, New PointF() {New Point(0, 80), New Point(80, 0),
                          New Point(80, 160), New Point(0, 240)})
            g.DrawPolygon(penRed, New PointF() {New Point(0, 80), New Point(80, 0),
                          New Point(80, 160), New Point(0, 240)})
            k.bitmap = bitmMain
            k.kat = 0
            k.ksztalt = New Rectangle(500 + i * 20, 525, 80, 240)
            k.wartosc = 13
            coll.Add(k)
        Next

        Return coll
    End Function
End Class

Teraz możemy wrócić do kodu Formy1. Mimo tego, że utworzyliśmy kształty, one nie pojawią się na naszej formie, jeśli nie zostaną do niej dodane. Nasza forma, musi mieć aktywne zdarzenie Paint:

Kod tego zdarzenia:

    Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
        For i As Integer = 0 To kolekcjaElementow.Count - 1
            e.Graphics.DrawImage(kolekcjaElementow(i).bitmap, kolekcjaElementow(i).ksztalt)
        Next
    End Sub

Nasza forma ma teraz dodane kształty którymi będziemy poruszać:

Teraz zajmiemy się przesuwaniem obiektów. Interesują nas tutaj trzy zdarzenia:

  1. MouseMove – śledzi położenie kursora
  2. MouseDown – moment przyciśnięcia lewego przycisku myszy
  3. Mouseup – moment puszczenia tego przycisku

Zdarzenie MouseMove będzie działało dwojako, ponieważ gdy lewy przycisk myszy będzie wciśnięty, poruszanie myszą spowoduje przesunięcie obiektu, jeśli nie będziemy trzymać lewego przycisku myszy a będziemy znajdować się nad kształtem, jego stan kursora, ze strzałki zostanie zmieniony na rączkę.

    'offset, aby element nie przesówał się do końca strzałki
    Dim OffsetXX As Integer = 0
    Dim OffsetYY As Integer = 0

    Private Sub Form1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseMove
        'jestWRuchu- opcja jest włączona gdy trzymany jest obiekt
        If jestWRuchu = True Then
            'pobieramy "Rectangle" obiektu
            Dim rectnew As Rectangle = kolekcjaElementow(trzymany).ksztalt
            'ustawiamy nowy punkt obiektu
            rectnew.Location = New Point(e.X + OffsetXX, e.Y + OffsetYY)
            'nadpisujemy obiekt o nowych koordynatach na liście 
            kolekcjaElementow(trzymany) = New ksztalt With {
                .bitmap = kolekcjaElementow(trzymany).bitmap,
                .wartosc = kolekcjaElementow(trzymany).wartosc,
                .ksztalt = rectnew,
                .kat = kolekcjaElementow(trzymany).kat}
        Else
            'Jeśli nie trzymamy obiektu, a kursor znajduje się nad obiektem, 
            'zostanie zmieniony jego wygląd na rączkę
            Dim nowyKursor As Cursor = Cursors.Default
            For d As Integer = 0 To kolekcjaElementow.Count - 1
                If (PunktJestNadObiektem(e.X, e.Y, d)) Then
                    nowyKursor = Cursors.Hand
                    trzymany = d
                End If
            Next
            If (Me.Cursor <> nowyKursor) Then
                Me.Cursor = nowyKursor
            End If
        End If
        Me.Invalidate()
    End Sub

    'Funkcja zwraca true, jeśli kursor jest nad kształtem
    Private Function PunktJestNadObiektem(ByVal x As Integer, ByVal y As Integer, ByVal k As Integer) As Boolean
        'Sprawdza nad którym obiektem jest kursor
        If ((x < kolekcjaElementow(k).ksztalt.Left) OrElse (y < kolekcjaElementow(k).ksztalt.Top) OrElse
            (x >= kolekcjaElementow(k).ksztalt.Right) OrElse (y >= kolekcjaElementow(k).ksztalt.Bottom)) Then
            'jeśli nie ma obiektu, zwróci false
            Return False
        End If
        'pobiera pixel kształtu (koordynaty w obiekcie rectangle)
        Dim i As Integer = x - kolekcjaElementow(k).ksztalt.X
        Dim j As Integer = y - kolekcjaElementow(k).ksztalt.Y
        'zwraca true, jeśli wartość alfa koloru jest większa od zera, zero oznacza przezroczystość
        Return (kolekcjaElementow(k).bitmap.GetPixel(i, j).A > 0)
    End Function

Zdarzenie MaouseMove śledzi położenie kursora, jego koordynaty pobieramy za pomocą punktu wykorzystując „ByVal e As System.Windows.Forms.MouseEventArgs”, położenie określają zmienne (e.X, e.Y). Jeśli nie trzymamy obiektu, wtedy koordynaty są na bieżąco sprawdzane, jeśli element jest trzymany, wtedy tworzony jest nowy kształt (Rectangle) w którym zmieniamy położenie (rectnew.Location = New Point(e.X + OffsetXX, e.Y + OffsetYY)) i nadpisujemy go na liście:

Wygląda to pięknie, i będzie jeszcze piękniej. Zajmiemy się teraz zdarzeniem MouseDown, które jest wywoływane w momencie przyciśnięcia lewego przycisku myszy. Zdarzenie to, określi nam który element jest wybrany, zmieni kolor elementu, podświetli go, tak abyśmy się nie pogubili, i przesunie na koniec kolekcji, przesuwając jednocześnie nasz obiekt do przodu.

    Private Sub Form1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseDown
        'Moment klikniecia na ksztalt
        If e.Button = MouseButtons.Left Then
            If (PunktJestNadObiektem(e.X, e.Y, trzymany)) Then
                OffsetXX = kolekcjaElementow(trzymany).ksztalt.X - e.X
                OffsetYY = kolekcjaElementow(trzymany).ksztalt.Y - e.Y

                Dim bit As Bitmap = kolekcjaElementow(trzymany).bitmap
                'podświetlenie
                Dim Xcount As Integer
                For Xcount = 0 To bit.Width - 1
                    Dim Ycount As Integer
                    For Ycount = 0 To bit.Height - 1
                        Dim pixelColor As Color = bit.GetPixel(Xcount, Ycount)
                        If pixelColor = Color.FromArgb(255, 0, 0) Then
                            bit.SetPixel(Xcount, Ycount, Color.Black)
                        ElseIf pixelColor = Color.FromArgb(64, 224, 208) Then
                            bit.SetPixel(Xcount, Ycount, Color.Blue)
                        End If

                    Next Ycount
                Next Xcount
                'nadpisujemy kształt w kolekcji
                kolekcjaElementow.Add(New ksztalt With {
                       .bitmap = bit,
                       .wartosc = kolekcjaElementow(trzymany).wartosc,
                       .ksztalt = kolekcjaElementow(trzymany).ksztalt,
                       .kat = kolekcjaElementow(trzymany).kat})
                'jeśteśmy w ruchu
                jestWRuchu = True
                'przesówamy obiekt do przodu
                kolekcjaElementow.RemoveAt(trzymany)
                trzymany = kolekcjaElementow.Count - 1
            End If
        End If
        'nadpisze nam grafikę 
        Me.Invalidate()
    End Sub

Jeśli nie zastosujemy offsetu, po wybraniu lewym przyciskiem myszy nasz obiekt zostanie zrównany z punktem kursora:

Musimy więc dopasować tak koordynaty aby kursor myszy znajdował się w miejscu jego ułożenia. teraz po wybraniu lewego przycisku myszy możemy podnosić nasze kształty. Ułożenie kształtów zapewnia zdarzenie MouseUp. Podzielone jest ono na dwie opcje, pierwsza to kliknięcie prawym przyciskiem myszy, spowoduje to obrócenie obiektu, lewy przycisk myszy ułoży obiekt.

    Private Sub Form1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseUp
        'prawy przycisk myszy
        'zmiana ustawienia obiektu o 90 stopni
        If e.Button = MouseButtons.Right Then
            'obrazek trzymanego obiektu
            Dim bit As Bitmap = kolekcjaElementow(trzymany).bitmap
            'obracamy obrazek o 90 stopni w prawo
            bit.RotateFlip(RotateFlipType.Rotate90FlipXY)
            Dim kat As Integer = kolekcjaElementow(trzymany).kat
            Dim rec As Rectangle = kolekcjaElementow(trzymany).ksztalt

            If Not rec.Width = rec.Height Then
                Dim w As Integer = rec.Width
                Dim h As Integer = rec.Height
                rec.Size = New Size(h, w)
                kat += 1
                'podłużne obiekty 
                If kolekcjaElementow(trzymany).wartosc = 13 Or kolekcjaElementow(trzymany).wartosc = 2 Then
                    If (kat Mod 2) = 0 Then
                        If kolekcjaElementow(trzymany).wartosc = 13 Then
                            bit.RotateFlip(RotateFlipType.RotateNoneFlipX)
                        End If
                    End If
                    kat = kat Mod 4
                Else
                    If (kat Mod 4) = 0 And kolekcjaElementow(trzymany).wartosc < 10 Then
                        bit.RotateFlip(RotateFlipType.RotateNoneFlipX)
                    End If
                    kat = kat Mod 8
                End If

            ElseIf kolekcjaElementow(trzymany).wartosc = 12 Or kolekcjaElementow(trzymany).wartosc = 1 Then
                kat += 1
                kat = kat Mod 4
            End If

            'nadpisujemy obiekt na liście
            kolekcjaElementow(trzymany) = New ksztalt With {
                    .bitmap = bit,
                    .wartosc = kolekcjaElementow(trzymany).wartosc,
                    .ksztalt = rec,
                    .kat = kat}
        Else
            If jestWRuchu = True Then

                For i As Integer = 0 To 5
                    For j As Integer = 0 To 5
                        'sprawdzamy wszystkie kwadraty, jeśli w jakimś znajduje się nasz kształt, 
                        'wtedy zostanie on dopasowany
                        If (listaKwadratow(i, j).Location.X <= kolekcjaElementow(trzymany).ksztalt.Location.X And
                            listaKwadratow(i, j).Location.X + 80 > kolekcjaElementow(trzymany).ksztalt.Location.X) _
                            And (listaKwadratow(i, j).Location.Y <= kolekcjaElementow(trzymany).ksztalt.Location.Y And
                            listaKwadratow(i, j).Location.Y + 80 > kolekcjaElementow(trzymany).ksztalt.Location.Y) Then
                            ' Dim pan As New Panel
                            ' pan.Location = New Point(listaKwadratow(i, j).Location.X, listaKwadratow(i, j).Location.Y)
                            ' pan.BorderStyle = BorderStyle.Fixed3D
                            ' pan.Size = New Size(80, 80)
                            ' pan.BackColor = Color.Transparent
                            ' Me.Controls.Add(pan)

                            Dim rectnew As Rectangle = kolekcjaElementow(trzymany).ksztalt
                            rectnew.Location = New Point(listaKwadratow(i, j).Location.X + 40,
                                                         listaKwadratow(i, j).Location.Y + 40)
                            Dim bit As Bitmap = kolekcjaElementow(trzymany).bitmap

                            Dim Xcount As Integer
                            For Xcount = 0 To bit.Width - 1
                                Dim Ycount As Integer
                                For Ycount = 0 To bit.Height - 1
                                    Dim pixelColor As Color = bit.GetPixel(Xcount, Ycount)

                                    If pixelColor = Color.FromArgb(0, 0, 0) Then
                                        bit.SetPixel(Xcount, Ycount, Color.FromArgb(255, 0, 0))
                                    ElseIf pixelColor = Color.FromArgb(0, 0, 255) Then
                                        bit.SetPixel(Xcount, Ycount, Color.FromArgb(64, 224, 208))
                                    End If
                                Next Ycount
                            Next Xcount
                            kolekcjaElementow(trzymany) = New ksztalt With {
                     .bitmap = bit,
                    .wartosc = kolekcjaElementow(trzymany).wartosc,
                    .ksztalt = rectnew,
                    .kat = kolekcjaElementow(trzymany).kat,
                           .polozenie = {i, j}}
                            jestWRuchu = False
                        End If

                    Next
                Next

                'Jeśli nie ułożyłeś elementu na planszy wtdy:
                If jestWRuchu = True Then

                    Dim rectnew As Rectangle = kolekcjaElementow(trzymany).ksztalt
                    rectnew.Location = New Point(e.X + OffsetXX, e.Y + OffsetYY)

                    'element wystaje za krawędź formy
                    If e.X + OffsetXX + rectnew.Width > Me.Width Then
                        rectnew.Location = New Point(Me.Width - rectnew.Width, rectnew.Location.Y)
                    End If
                    If e.Y + OffsetYY + rectnew.Height > Me.Height Then
                        rectnew.Location = New Point(rectnew.Location.X, Me.Height - rectnew.Height)
                    End If

                    If e.X + OffsetXX < 0 Then
                        rectnew.Location = New Point(0, rectnew.Location.Y)
                    End If
                    If e.Y + OffsetYY < 0 Then
                        rectnew.Location = New Point(rectnew.Location.X, 0)
                    End If

                    'zmieniamy kolor na normalny
                    Dim bit As Bitmap = kolekcjaElementow(trzymany).bitmap
                    Dim Xcount As Integer
                    For Xcount = 0 To bit.Width - 1
                        Dim Ycount As Integer
                        For Ycount = 0 To bit.Height - 1
                            Dim pixelColor As Color = bit.GetPixel(Xcount, Ycount)

                            If pixelColor = Color.FromArgb(0, 0, 0) Then
                                bit.SetPixel(Xcount, Ycount, Color.FromArgb(255, 0, 0))
                            ElseIf pixelColor = Color.FromArgb(0, 0, 255) Then
                                bit.SetPixel(Xcount, Ycount, Color.FromArgb(64, 224, 208))
                            End If
                        Next Ycount
                    Next Xcount
                    'nadpisujemy kształt
                    kolekcjaElementow(trzymany) = New ksztalt With {
                       .bitmap = bit,
                       .wartosc = kolekcjaElementow(trzymany).wartosc,
                       .ksztalt = rectnew,
                       .kat = kolekcjaElementow(trzymany).kat}

                    jestWRuchu = False
                End If
            End If
        End If
        Me.Invalidate()
        'sprawdzamy koniec gry
       '''''' koniecGry()
    End Sub

W zależności od tego jaki kształt wybierzemy, można go obracać dwa lub cztery razy o 90 stopni. Jeśli obrócimy określony kształt dwa razy, wtedy następuje zmiana na jego lustrzane odbicie, ma on wtedy 4 stany:

Ten kształt ma cztery stany, dwa zwykłe i dwa lustrzane

Ten kształt ma cztery stany, bez lustrzanego odbicia

Ten kształt jest symetryczny, więc nie ma żadnego stanu.

Najwięcej stanów mają dwa elementy turkusowe, bo aż 8, cztery zwykłe i cztery lustrzane:

Wartość obrotu jest szalenie istotna, ponieważ wykorzystywana jest ostatecznie do porównania obrazków i określeniu, czy gra się skończyła. Kiedy już określiliśmy nowy obiekt Rectangle i wygenerowaliśmy nową bitmapę, nadpisujemy ją na liście. Przyszedł czas na omówienie lewego klawisza myszy, na początku sprawdzamy czy nie na relacji „nasz obiekt <=> listaKwadratow(i)” jeśli tak jest nasz element będzie odpowiednio dopasowany. Gdyby tego nie było, nasz element nie byłby równo ułożony. Można odblokować element:

                            pan.Location = New Point(listaKwadratow(i, j).Location.X, 
                                                     listaKwadratow(i, j).Location.Y)
                            pan.BorderStyle = BorderStyle.Fixed3D
                            pan.BackColor = Color.Transparent
                            pan.Size = New Size(80, 80)
                            Me.Controls.Add(pan)

Efektem będzie wyświetlenie kwadratu:

To zapewnia nam spójną całość formy. Dodatkowo do struktury dodajemy element .polozenie który będzie potrzebny do sprawdzenia czy gra jest zakończona. Jeśli kwadrat zostanie znaleziony, tak jak na gifie powyżej, wtedy następuje zmiana koloru i nadpisanie obiektu. Jeśli jednak kwadrat nie zostanie znaleziony a element jest w ruchu, oznacza to, że został ułożony poza planszą. Wtedy należy nadać mu koordynaty (e.X, eY). Jeśli element znajduje się  poza planszą należy go przesunąć.

Mamy już potrzebne elementy. Możemy przesuwać kształty i układać je na planszy. Zaczynamy grę! Tworzymy uchwyt dla przycisku „Start / Losuj” (button2):

    Dim graDziala As Boolean = False 'zmienna okresla stan gry
    Dim StopWatch As Diagnostics.Stopwatch

    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        PrzygotujTapete() 'resetujemy elementy gry
        kolekcjaElementow = GenerujKsztalty.GenerujElementyGry() 'tworzymy listę elementów
        'wybieramy wzór
        WybranyWzor = GenerujKsztalty.generujWzor(Panel1, ran.Next(0, KsztaltyWzory.Count))
        'rysujemy elementy
        Me.Invalidate()
        'zmieniamy stan gry na działającą
        graDziala = True
        'tworzymy timer liczący czas gry
        StopWatch = New Diagnostics.Stopwatch
        Timer1.Start()
        Me.StopWatch.Start()
    End Sub

    'liczy czas ułożenia
    Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
        Dim elapsed As TimeSpan = Me.StopWatch.Elapsed
        Label1.Text = String.Format("{0:00}:{1:00}:{2:00}", Math.Floor(elapsed.TotalHours), elapsed.Minutes, elapsed.Seconds)
    End Sub

Wracamy do klasy GenerujKsztalty, musimy stworzyć funkcję rysującą wybrany wzór, aby było trudniej, będziemy generować tylko czerwone kształty. Oczywiście bitmapa wygenerowana tą metodą będzie dużo mniejsza. Musimy określić punkty na tej bitmapie na których zaczepimy kształty:

    Dim wybrany As Integer 'wybrany wzór, jego indeks
    Public Function generujWzor(ByRef pan As Panel, ByVal k As Integer) As Integer(,)
        Dim pp(5, 5) As Point
        Dim bitmMain As Bitmap
        Dim bitmapaWzoru As New Bitmap(240, 240)
        Dim g As Graphics
        'punkty zaczepienia kształtów
        For j As Integer = 0 To 5
            For i As Integer = 0 To 5
                pp(i, j) = New Point(i * 40, j * 40)
            Next
        Next
        'przeglądamy elementy wzoru, w sumie czternaście kształtów
        For i As Integer = 0 To 13
            Select Case KsztaltyWzory(k)(i, 0)
             'jeśli któryś z kształtów jest czerwony (jego wartość jest 11 lub 12 lub 13)
                Case 11
                    bitmMain = New Bitmap(80, 80)
                    g = Graphics.FromImage(bitmMain)
                    g.FillPolygon(Brushes.Red, New PointF() {New Point(40, 0), New Point(80, 40),
                                  New Point(40, 80), New Point(0, 40)})
                    Graphics.FromImage(bitmapaWzoru).DrawImage(bitmMain, pp(KsztaltyWzory(k)(i, 1),
                                                                            KsztaltyWzory(k)(i, 2)))
                Case 12
                    bitmMain = New Bitmap(80, 80)
                    g = Graphics.FromImage(bitmMain)
                    g.FillPolygon(Brushes.Red, New PointF() {New Point(-1, 80), New Point(80, 80),
                                  New Point(80, -1)})
                    'kształt musi być odpowiednio obrócony
                    For j As Integer = 0 To KsztaltyWzory(k)(i, 3)
                        If Not j = 0 Then
                            bitmMain.RotateFlip(RotateFlipType.Rotate90FlipXY)
                        End If
                    Next
                    Graphics.FromImage(bitmapaWzoru).DrawImage(bitmMain, pp(KsztaltyWzory(k)(i, 1),
                                                                            KsztaltyWzory(k)(i, 2)))
                Case 13
                    bitmMain = New Bitmap(40, 120)
                    g = Graphics.FromImage(bitmMain)
                    g.FillPolygon(Brushes.Red, New PointF() {New Point(-1, 40), New Point(40, -1),
                                  New Point(40, 80), New Point(-1, 120)})
                    'kształt musi być odpowiednio obrócony
                    For j As Integer = 0 To KsztaltyWzory(k)(i, 3)
                        If Not j = 0 Then
                            bitmMain.RotateFlip(RotateFlipType.Rotate90FlipXY)
                            'lustrzane odbicie
                            If (j Mod 2) = 0 Then
                                bitmMain.RotateFlip(RotateFlipType.RotateNoneFlipX)
                            End If
                        End If
                    Next
                    Graphics.FromImage(bitmapaWzoru).DrawImage(bitmMain, pp(KsztaltyWzory(k)(i, 1),
                                                                            KsztaltyWzory(k)(i, 2)))
            End Select
        Next
        'dodajemy grafikę jako tło panelu
        pan.BackgroundImage = bitmapaWzoru
        wybrany = k
        'zwracamy tablicę
        Return KsztaltyWzory(k)
    End Function

Teraz wzór będzie generowany:

Najtrudniejszy element, czyli „KoniecGry()” zostawimy sobie na koniec. Dodajmy teraz funkcjonalność reszty klawiszy:

    'przycisk pokazujący wzór, jednocześnie kopiujący go do Clipboard'a dzięki czemu możemy go 
    'dodać do listy wzorów
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim str As String = "{"
        For i As Integer = 0 To kolekcjaElementow.Count - 1
            If kolekcjaElementow(i).polozenie IsNot Nothing Then
                str += "{" + kolekcjaElementow(i).wartosc.ToString + "," +
                    kolekcjaElementow(i).polozenie(0).ToString + "," +
                    kolekcjaElementow(i).polozenie(1).ToString + "," +
                    kolekcjaElementow(i).kat.ToString + "},"
            End If
        Next
        str = str.Remove(str.Length - 1)
        str += "}"
        MsgBox(str)
        System.Windows.Forms.Clipboard.SetText(str)
    End Sub

    'resetuje grę
    Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
        PrzygotujTapete()
        graDziala = False
        kolekcjaElementow = GenerujKsztalty.GenerujElementyGry()
        WybranyWzor = GenerujKsztalty.generujWzor(Panel1, ran.Next(0, KsztaltyWzory.Count))
        Timer1.Stop()
        Me.StopWatch.Stop()
        Me.Invalidate()
    End Sub

    'przycisk poddania się
    Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
        graDziala = False
        GenerujKsztalty.poddajsie(Panel1)
        Timer1.Stop()
        Me.StopWatch.Stop()
    End Sub

Wracamy do klasy „GenerujKsztalty”, w momencie poddania gry, gracz zostanie poinstruowany o sposobie ułożenia elementów:

 

Jest to bardzo pomocny element, gdyż użytkownik może zarzucić nam, że układanki nie da się ułożyć.

    Public Sub poddajsie(ByRef pan As Panel)
        Dim pp(5, 5) As Point
        Dim bitmMain As Bitmap
        Dim bitmapaWzoru As New Bitmap(240, 240)
        Dim penRed As New Pen(Brushes.Tomato)
        Dim penTur As New Pen(Brushes.PaleTurquoise)
        penRed.Width = 2
        penTur.Width = 2
        Dim g As Graphics
        For j As Integer = 0 To 5
            For i As Integer = 0 To 5
                pp(i, j) = New Point(i * 40, j * 40)
            Next
        Next

        For i As Integer = 0 To 13
            Select Case KsztaltyWzory(wybrany)(i, 0)
                Case 1
                    bitmMain = New Bitmap(80, 80)
                    g = Graphics.FromImage(bitmMain)
                    g.FillPolygon(Brushes.Turquoise, New PointF() {New Point(0, 0),
                                  New Point(40, 40), New Point(80, 0), New Point(80, 80), New Point(0, 80)})
                    g.DrawPolygon(penTur, New PointF() {New Point(0, 0), New Point(40, 40),
                                  New Point(80, 0), New Point(80, 80), New Point(0, 80)})
                    For j As Integer = 0 To KsztaltyWzory(wybrany)(i, 3)
                        If Not j = 0 Then
                            bitmMain.RotateFlip(RotateFlipType.Rotate90FlipXY)
                        End If
                    Next
                    Graphics.FromImage(bitmapaWzoru).DrawImage(bitmMain, pp(KsztaltyWzory(wybrany)(i, 1),
                                                                            KsztaltyWzory(wybrany)(i, 2)))
                Case 2
                    bitmMain = New Bitmap(40, 160)
                    g = Graphics.FromImage(bitmMain)
                    g.FillPolygon(Brushes.Turquoise, New PointF() {New Point(0, 0), New Point(40, 40),
                                  New Point(40, 120), New Point(0, 160)})
                    g.DrawPolygon(penTur, New PointF() {New Point(0, 0), New Point(40, 40), New Point(40, 120),
                                  New Point(0, 160)})
                    For j As Integer = 0 To KsztaltyWzory(wybrany)(i, 3)
                        If Not j = 0 Then
                            bitmMain.RotateFlip(RotateFlipType.Rotate90FlipXY)
                        End If
                    Next
                    Graphics.FromImage(bitmapaWzoru).DrawImage(bitmMain, pp(KsztaltyWzory(wybrany)(i, 1),
                                                                            KsztaltyWzory(wybrany)(i, 2)))
                Case 3
                    bitmMain = New Bitmap(120, 80)
                    g = Graphics.FromImage(bitmMain)
                    g.FillPolygon(Brushes.Turquoise, New PointF() {New Point(0, 80), New Point(80, 0),
                                  New Point(120, 40), New Point(80, 80), New Point(0, 80)})
                    g.DrawPolygon(penTur, New PointF() {New Point(0, 80), New Point(80, 0), New Point(120, 40),
                                  New Point(80, 80), New Point(0, 80)})
                    For j As Integer = 0 To KsztaltyWzory(wybrany)(i, 3)
                        If Not j = 0 Then
                            bitmMain.RotateFlip(RotateFlipType.Rotate90FlipXY)
                            If (j Mod 4) = 0 Then
                                bitmMain.RotateFlip(RotateFlipType.RotateNoneFlipX)
                            End If
                        End If
                    Next
                    Graphics.FromImage(bitmapaWzoru).DrawImage(bitmMain, pp(KsztaltyWzory(wybrany)(i, 1),
                                                                            KsztaltyWzory(wybrany)(i, 2)))
                Case 4
                    bitmMain = New Bitmap(80, 120)
                    g = Graphics.FromImage(bitmMain)
                    g.FillPolygon(Brushes.Turquoise, New PointF() {New Point(0, 0), New Point(80, 0),
                                  New Point(40, 40), New Point(40, 120), New Point(0, 80)})
                    g.DrawPolygon(penTur, New PointF() {New Point(0, 0), New Point(80, 0), New Point(40, 40),
                                  New Point(40, 120), New Point(0, 80)})
                    For j As Integer = 0 To KsztaltyWzory(wybrany)(i, 3)
                        If Not j = 0 Then
                            bitmMain.RotateFlip(RotateFlipType.Rotate90FlipXY)
                            If (j Mod 4) = 0 Then
                                bitmMain.RotateFlip(RotateFlipType.RotateNoneFlipX)
                            End If
                        End If
                    Next
                    Graphics.FromImage(bitmapaWzoru).DrawImage(bitmMain, pp(KsztaltyWzory(wybrany)(i, 1),
                                                                            KsztaltyWzory(wybrany)(i, 2)))
'elementy czerwone
                Case 11
                    bitmMain = New Bitmap(80, 80)
                    g = Graphics.FromImage(bitmMain)
                    g.FillPolygon(Brushes.Red, New PointF() {New Point(40, 0), New Point(80, 40),
                                  New Point(40, 80), New Point(0, 40)})
                    g.DrawPolygon(penRed, New PointF() {New Point(40, 0), New Point(80, 40),
                                  New Point(40, 80), New Point(0, 40)})
                    Graphics.FromImage(bitmapaWzoru).DrawImage(bitmMain, pp(KsztaltyWzory(wybrany)(i, 1),
                                                                            KsztaltyWzory(wybrany)(i, 2)))

                Case 12
                    bitmMain = New Bitmap(80, 80)
                    g = Graphics.FromImage(bitmMain)
                    g.FillPolygon(Brushes.Red, New PointF() {New Point(0, 80), New Point(80, 80), New Point(80, 0)})
                    g.DrawPolygon(penRed, New PointF() {New Point(0, 80), New Point(80, 80), New Point(80, 0)})
                    For j As Integer = 0 To KsztaltyWzory(wybrany)(i, 3)
                        If Not j = 0 Then
                            bitmMain.RotateFlip(RotateFlipType.Rotate90FlipXY)
                        End If
                    Next
                    Graphics.FromImage(bitmapaWzoru).DrawImage(bitmMain, pp(KsztaltyWzory(wybrany)(i, 1),
                                                                            KsztaltyWzory(wybrany)(i, 2)))
                Case 13
                    bitmMain = New Bitmap(40, 120)
                    g = Graphics.FromImage(bitmMain)
                    g.FillPolygon(Brushes.Red, New PointF() {New Point(0, 40), New Point(40, 0), New Point(40, 80),
                                  New Point(0, 120)})
                    g.DrawPolygon(penRed, New PointF() {New Point(0, 40), New Point(40, 0), New Point(40, 80),
                                  New Point(0, 120)})
                    For j As Integer = 0 To KsztaltyWzory(wybrany)(i, 3)
                        If Not j = 0 Then
                            bitmMain.RotateFlip(RotateFlipType.Rotate90FlipXY)
                            If (j Mod 2) = 0 Then
                                bitmMain.RotateFlip(RotateFlipType.RotateNoneFlipX)
                            End If
                        End If
                    Next
                    Graphics.FromImage(bitmapaWzoru).DrawImage(bitmMain, pp(KsztaltyWzory(wybrany)(i, 1),
                                                                            KsztaltyWzory(wybrany)(i, 2)))
            End Select
        Next
        pan.BackgroundImage = bitmapaWzoru
    End Sub

Kod jest trochę długi, ponieważ przy użyciu tablicy wzoru, musimy namalować i ułożyć elementy. Przyszedł czas na ostatni, najtrudniejszy element naszej układanki. Sprawdzenie czy wzór jest taki sam jak to co ułożyliśmy. Na pewno przyszło wam do głowy aby po prostu porównać koordynaty wszystkich elementów i wzoru. Niestety nie będzie tak łatwo. Zobaczcie obrazek poniżej:

Aż trzy elementy na dole mają różne współrzędne. Podczas sprawdzania współrzędnych tak duża różnica elementów jest niedopuszczalna. Lecz użytkownik ułożył obrazek zgodnie ze wzorem. Miałem dużo pomysłów na to jak to zrobić ale jeden okazał się najlepszy. Stworzymy mała bitmapę z czerwonymi elementami. Wracamy do GenerujKsztalty i dodajemy ostatnią funkcję zwracającą bitmapę:

    Public Function GenerujPorownanie(ByVal t As Integer(,)) As Bitmap
        Dim pp(5, 5) As Point
        Dim bitmMain As Bitmap
        Dim bitmapaWzoru As New Bitmap(240, 240)
        Dim g As Graphics
        For j As Integer = 0 To 5
            For i As Integer = 0 To 5
                pp(i, j) = New Point(i * 40, j * 40)
            Next
        Next
        For i As Integer = 0 To 5
            Select Case t(i, 0)
                Case 11
                    bitmMain = New Bitmap(80, 80)
                    g = Graphics.FromImage(bitmMain)
                    g.FillPolygon(Brushes.Red, New PointF() {New Point(40, 0), New Point(80, 40),
                                  New Point(40, 80), New Point(0, 40)})
                    Graphics.FromImage(bitmapaWzoru).DrawImage(bitmMain, pp(t(i, 1), t(i, 2)))
                Case 12
                    bitmMain = New Bitmap(80, 80)
                    g = Graphics.FromImage(bitmMain)
                    g.FillPolygon(Brushes.Red, New PointF() {New Point(-1, 80), New Point(80, 80), 
                                                                          New Point(80, -1)})
                    For j As Integer = 0 To t(i, 3)
                        If Not j = 0 Then
                            bitmMain.RotateFlip(RotateFlipType.Rotate90FlipXY)
                        End If
                    Next
                    Graphics.FromImage(bitmapaWzoru).DrawImage(bitmMain, pp(t(i, 1), t(i, 2)))
                Case 13
                    bitmMain = New Bitmap(40, 120)
                    g = Graphics.FromImage(bitmMain)
                    g.FillPolygon(Brushes.Red, New PointF() {New Point(-1, 40), New Point(40, -1),
                                  New Point(40, 80), New Point(-1, 120)})
                    For j As Integer = 0 To t(i, 3)
                        If Not j = 0 Then
                            bitmMain.RotateFlip(RotateFlipType.Rotate90FlipXY)
                            If (j Mod 2) = 0 Then
                                bitmMain.RotateFlip(RotateFlipType.RotateNoneFlipX)
                            End If
                        End If
                    Next
                    Graphics.FromImage(bitmapaWzoru).DrawImage(bitmMain, pp(t(i, 1), t(i, 2)))
            End Select
        Next
        Return bitmapaWzoru
    End Function

Nie różni się ona od innych zamieszczonych w tej klasie elementów, teraz w form1 odblokujmy w zdarzeniu MouseUp:

        koniecGry()

I dodajmy tą metodę do kodu:

    Private Sub koniecGry()
        If graDziala = True Then
            Dim ListaElementow(6, 4) As Integer
            Dim licznik As Integer = 0
            'Sprawdza czy wszystkie elementy czerwone są ułożone
            For i As Integer = 0 To kolekcjaElementow.Count - 1
                If kolekcjaElementow(i).wartosc > 10 Then
                    If kolekcjaElementow(i).polozenie Is Nothing Then
                        'jeśli nie wszystkie czerwone elementy są ułożone 
                        'zakończ metodę
                        Exit Sub
                    Else
                        ListaElementow(licznik, 0) = kolekcjaElementow(i).wartosc
                        ListaElementow(licznik, 1) = kolekcjaElementow(i).polozenie(0)
                        ListaElementow(licznik, 2) = kolekcjaElementow(i).polozenie(1)
                        ListaElementow(licznik, 3) = kolekcjaElementow(i).kat
                        licznik += 1
                    End If
                End If
            Next

            'generuje dwie bitmapy
            'pierwszą za pomocą pobranych wyżej współżędnych elementów i wartości obrotu
            'ułożonych elementów
            Dim tenObrazek As Bitmap = GenerujKsztalty.GenerujPorownanie(ListaElementow)
            Panel3.BackgroundImage = tenObrazek
            'drugą bitmapę
            'tworzymy na podstawie wzoru
            Dim obrazekdoporownania As Bitmap = GenerujKsztalty.GenerujPorownanie(WybranyWzor)
            Panel4.BackgroundImage = obrazekdoporownania
            'porównujemy pixele tych dwóch bitmap
            For i As Integer = 0 To tenObrazek.Width - 1
                For j As Integer = 0 To tenObrazek.Height - 1
                    If Not tenObrazek.GetPixel(i, j) = obrazekdoporownania.GetPixel(i, j) Then
                        'jeśli coś się nie zgadza, wtedy zakończ metodę
                        Exit Sub
                    End If
                Next
            Next

            'jesli czerwone elementy są prawidłowo ułożone
            'sprawdzamy czy na planszy nie ma białych pól
            'jeśli elementy turkusowe są nieprawidłowo ułożone
            'na planszy pozostaną białe pola ponieważ takiego koloru jest plansza

            Dim bitmap As Bitmap = New Bitmap(500, 550)
            Dim g As Graphics = Graphics.FromImage(bitmap)
            Dim bmp As Bitmap = New Bitmap(Me.Width, Me.Height)
            'rysujemy kawałek formy do bitmapy
            Me.DrawToBitmap(bmp, New Rectangle(0, 0, Me.Width, Me.Height))
            'rysuje bmp na bitmap
            g.DrawImage(bmp, 0, 0,
                    New Rectangle(miejsceNamalowaniaKwadratu.X, miejsceNamalowaniaKwadratu.Y, 500, 550),
                    GraphicsUnit.Pixel)
            For i As Integer = 0 To bitmap.Width - 1
                For j As Integer = 0 To bitmap.Height - 1
                    'sprawdza czy bitmapa posiada białe kolory
                    If bitmap.GetPixel(i, j) = Color.FromArgb(255, 255, 255) Then
                        'jeśli tak, wtedy zakończ metodę
                        Exit Sub
                    End If
                Next
            Next

            'czerwone elementy są ułożone prawidłowo i plansza jest wypełniona kształtami
            'KONIEC GRY
            Timer1.Stop()
            Me.StopWatch.Stop()
            MessageBox.Show("Osiągnięto czas:" + vbNewLine + Label1.Text, "Koniec gry!", MessageBoxButtons.OK)
        End If
    End Sub

Kiedy ułożymy wzór, w prawym dolnym rogu w panelach 3 i 4, pojawi się bitmapa do porównań. Panele te są niepotrzebne więc można je usunąć, ale dodałem je aby pokazać wam jak to działa:

Dobra, gra gotowa, teraz należy powymyślać więcej wzorów. Tutaj zdam się na was. Jeśli macie jakieś ciekawe wzory, napiszcie, przydadzą się. Pozdrawiam

Projekt do pobrania: Tangram visualMonsters.cba.pl

 

 

 

Permalink do tego artykułu: https://visualmonsters.cba.pl/shape-tangram-puzzle/

Dodaj komentarz

Twój adres email nie będzie publikowany.