Results 1 to 7 of 7

Thread: ChatGPT Boids Simulation: Winforms, WPF versions

  1. #1

    Thread Starter
    Fanatic Member Peter Porter's Avatar
    Join Date
    Jul 2013
    Location
    Germany
    Posts
    539

    ChatGPT Boids Simulation: Winforms, WPF versions

    It took a few prompts for ChatGPT 3.5 to create it, but I had to fix some of it's mistakes. From Wikipedia, for those who don't know what a boids simulation is, it's an artificial life program, developed by Craig Reynolds in 1986, which simulates the flocking behaviour of birds, and related group motion.

    In this boids simulation project you can change the number of boids in the simulation and pause their movement. 4 boids is the smallest size a flock can be. The boids avoid the mouse cursor, so you can observe the new counts of flocks, trios, pairs and lone boids. It runs fine when you resize the form.

    Name:  chatgpt_boids_simulation.jpg
Views: 544
Size:  23.9 KB

    The capture above is from the 1st version of the boids simulation project. The updated winforms and Wpf versions have filled-in triangles, and can handle up to 1000 boids.

    The code below is also from the 1st version. It needs 1 panel docked at the bottom and a PictureBox to fill the remaining space in the Form. It also needs 2 timers named "SimulationTimer" and "CountingTimer", 7 labels, a textbox, and two buttons. Button2 is the pause button.

    The code has enough comments to explain everything. All required classes will be in the next two comments.

    Main class:
    Code:
    Option Strict On
    
    Public Class Form1
        ' Maximum speed of boids
        Private MaxSpeed As Integer = 7
    
        ' Range within which boids perceive and interact with each other
        Public VisualRange As Integer = 40
    
        ' Range within which boids avoid collisions
        Private ProtectedRange As Integer = 20
    
        ' List to store individual boid instances representing the flock.
        Private BoidsList As List(Of Boid)
    
        ' Buffered bitmap for off-screen rendering
        Private buffer As Bitmap
    
        ' Variables for mouse cursor avoidance.
        Private BoidsMousePosition As Point
        Private MouseAvoidanceThreshold As Double = 25
    
        ' Instances for alignment, cohesion, and separation rules for boid behavior.
        ' MaxSpeed parameter is set to the maximum speed allowed for boids.
        Private alignmentRule As New Alignment(MaxSpeed)
        Private cohesionRule As New Cohesion()
        Private separationRule As New Separation()
    
        ' The initial number of boids to be created and initialized
        Private Const InitialBoidCount As Integer = 400
    
        ' Flag to track pause state
        Private IsPaused As Boolean = False
    
        ' BoidCounterInstance represents an instance of the BoidCounter class,
        ' which is responsible for counting various configurations of boids within a specified visual range.
        Public BoidCounterInstance As New BoidCounter()
    
    
        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            ' Set the DoubleBuffered property to True to enable double buffering
            Me.DoubleBuffered = True
    
            ' Initialize the buffered bitmap
            buffer = New Bitmap(PictureBox1.ClientSize.Width, PictureBox1.ClientSize.Height)
    
            ' Set VisualRange for BoidCounterInstance
            BoidCounterInstance.VisualRange = VisualRange
    
            ' Initialize the list of boids
            BoidsList = New List(Of Boid)()
            InitializeBoids(InitialBoidCount)
    
            ' Start the simulation timer
            SimulationTimer.Start()
            CountingTimer.Start()
        End Sub
    
    
        ' Initializes a specified number of boids with random positions and velocities.
        Private Sub InitializeBoids(ByVal count As Integer)
            ' Create a random number generator.
            Dim random As Random = New Random()
    
            ' Generate boids with random positions and velocities.
            For i As Integer = 0 To count - 1
                Dim boid As Boid = New Boid()
                boid.Location = New Point(random.Next(0, PictureBox1.ClientSize.Width), random.Next(0, PictureBox1.ClientSize.Height))
                boid.Velocity = New Point(random.Next(-2, 3), random.Next(-2, 3))
                BoidsList.Add(boid)
            Next
        End Sub
    
    
        Private Sub SimulationTimer_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SimulationTimer.Tick
            If Not IsPaused Then
                ' Update boids position
                For Each boid As Boid In BoidsList
                    UpdateBoid(boid)
                Next
    
                ' Draw boids to the buffer
                DrawBoidsToBuffer()
            End If
        End Sub
    
    
        ' Updates the position and behavior of a given boid based on its interactions with other boids and the environment.
        Private Sub UpdateBoid(ByVal boid As Boid)
            ' Variables to store averages, counts, and distances for boid interactions
            Dim averageXposition, averageYposition, averageXvelocity, averageYvelocity, neighboringBoids, closeDx, closeDy As Double
    
            ' Loop through every other boid in the flock
            For Each otherBoid As Boid In BoidsList
                ' Check if the boid is not itself
                If Not boid.Equals(otherBoid) Then
                    Dim dx As Double = boid.Location.X - otherBoid.Location.X
                    Dim dy As Double = boid.Location.Y - otherBoid.Location.Y
                    Dim squaredDistance As Double = dx * dx + dy * dy
    
                    ' Check if the boids are within the protected range
                    If squaredDistance < ProtectedRange * ProtectedRange Then
                        closeDx += boid.Location.X - otherBoid.Location.X
                        closeDy += boid.Location.Y - otherBoid.Location.Y
                    ElseIf squaredDistance < VisualRange * VisualRange Then
                        ' Update averages for boids within the visual range
                        averageXposition += otherBoid.Location.X
                        averageYposition += otherBoid.Location.Y
                        averageXvelocity += otherBoid.Velocity.X
                        averageYvelocity += otherBoid.Velocity.Y
                        neighboringBoids += 1
                    End If
                End If
            Next
    
            ' Check if there are neighboring boids
            If neighboringBoids > 0 Then
                ' Calculate average velocity and position
                Dim avgVelocity As New Point(CInt(averageXvelocity / neighboringBoids), CInt(averageYvelocity / neighboringBoids))
                Dim avgPosition As New Point(CInt(averageXposition / neighboringBoids), CInt(averageYposition / neighboringBoids))
    
                ' Calculate distance to the mouse
                Dim mouseDistance As Double = Math.Sqrt((boid.Location.X - BoidsMousePosition.X) ^ 2 + (boid.Location.Y - BoidsMousePosition.Y) ^ 2)
    
                ' Skip alignment and cohesion when boid is near the mouse
                If mouseDistance > VisualRange Then
                    ' Apply alignment rule
                    alignmentRule.ApplyAlignmentRule(boid, avgVelocity)
                    ' Apply cohesion rule
                    cohesionRule.ApplyCohesionRule(boid, avgPosition)
                End If
            End If
    
            ' Apply separation rule
            separationRule.ApplySeparationRule(boid, closeDx, closeDy)
    
            ' Move boid
            boid.Location = New Point(boid.Location.X + boid.Velocity.X, boid.Location.Y + boid.Velocity.Y)
    
            ' Apply mouse avoidance
            Dim distanceToMouse As Double = Math.Sqrt((boid.Location.X - BoidsMousePosition.X) ^ 2 + (boid.Location.Y - BoidsMousePosition.Y) ^ 2)
            If distanceToMouse < MouseAvoidanceThreshold Then
                ' Adjust velocity to avoid the mouse more strongly
                Dim angle As Double = Math.Atan2(boid.Location.Y - BoidsMousePosition.Y, boid.Location.X - BoidsMousePosition.X)
                boid.Velocity = New Point(CInt(boid.Velocity.X + Math.Cos(angle) * 4), CInt(boid.Velocity.Y + Math.Sin(angle) * 4))
            End If
    
            ' Keep boids within the PictureBox boundaries
            If boid.Location.X < 0 Then
                boid.Location = New Point(0, boid.Location.Y)
                boid.Velocity = New Point(-boid.Velocity.X, boid.Velocity.Y)
            ElseIf boid.Location.X > PictureBox1.ClientSize.Width Then
                boid.Location = New Point(PictureBox1.ClientSize.Width, boid.Location.Y)
                boid.Velocity = New Point(-boid.Velocity.X, boid.Velocity.Y)
            End If
    
            If boid.Location.Y < 0 Then
                boid.Location = New Point(boid.Location.X, 0)
                boid.Velocity = New Point(boid.Velocity.X, -boid.Velocity.Y)
            ElseIf boid.Location.Y > PictureBox1.ClientSize.Height Then
                boid.Location = New Point(boid.Location.X, PictureBox1.ClientSize.Height)
                boid.Velocity = New Point(boid.Velocity.X, -boid.Velocity.Y)
            End If
    
            ' Normalize velocity
            Dim length As Double = Math.Sqrt(boid.Velocity.X ^ 2 + boid.Velocity.Y ^ 2)
            If length > MaxSpeed Then
                ' Scale velocity back to the maximum speed
                boid.Velocity = New Point(CInt(MaxSpeed * boid.Velocity.X / length), CInt(MaxSpeed * boid.Velocity.Y / length))
            End If
        End Sub
    
    
        Private Sub CountingTimer_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CountingTimer.Tick
            ' Set the BoidsList property before counting
            BoidCounterInstance.BoidsList = BoidsList
    
            ' Counts flocks, trios, pairs, and lone boids
            BoidCounterInstance.CountFlocksTriosPairsLoners(BoidsList)
    
            ' Update labels outside the loop with the calculated counts.
            Label1.Text = "Flocks: " & BoidCounterInstance.FlockCount
            Label2.Text = "Trios: " & BoidCounterInstance.TrioCount
            Label3.Text = "Pairs: " & BoidCounterInstance.PairCount
            Label4.Text = "Loners: " & BoidCounterInstance.LoneCount
        End Sub
    
    
        Private Sub PictureBox1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseMove
            ' Update mouse position
            BoidsMousePosition = e.Location
        End Sub
    
    
        Private Sub DrawBoidsToBuffer()
            ' Draw boids as small narrow triangles on the off-screen buffer
            Using g As Graphics = Graphics.FromImage(buffer)
                g.Clear(Color.White) ' You can choose a different background color if needed
    
                For Each boid As Boid In BoidsList
                    ' Calculate the angle of the velocity vector
                    Dim angle As Double = Math.Atan2(boid.Velocity.Y, boid.Velocity.X) ' The angle now directly represents the direction of the top of the triangle
    
                    ' Calculate the points of the triangle
                    Dim points As Point() = {
                        New Point(boid.Location.X + CInt(7 * Math.Cos(angle)), boid.Location.Y + CInt(8 * Math.Sin(angle))),
                        New Point(boid.Location.X + CInt(3 * Math.Cos(angle - 2 * Math.PI / 3)), boid.Location.Y + CInt(3 * Math.Sin(angle - 2 * Math.PI / 3))),
                        New Point(boid.Location.X + CInt(3 * Math.Cos(angle + 2 * Math.PI / 3)), boid.Location.Y + CInt(3 * Math.Sin(angle + 2 * Math.PI / 3)))
                    }
    
                    ' Draw the triangle (body) on the off-screen buffer
                    g.DrawPolygon(Pens.Blue, points)
                Next
            End Using
            ' Trigger the Paint event to display the buffered bitmap on the PictureBox
            PictureBox1.Invalidate()
        End Sub
    
    
        Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
            ' Draw the buffered image onto the PictureBox
            e.Graphics.DrawImage(buffer, 0, 0)
        End Sub
    
    
        Private Sub PictureBox1_SizeChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles PictureBox1.SizeChanged
            ' Resize the buffered bitmap when the PictureBox size changes
            buffer = New Bitmap(PictureBox1.ClientSize.Width, PictureBox1.ClientSize.Height)
    
            ' Update the label displaying the display area size
            Label7.Text = "Display Area: " & PictureBox1.ClientSize.Width & " x " & PictureBox1.ClientSize.Height
    
            ' Invalidate the form to trigger a redraw
            Me.Invalidate()
        End Sub
    
    
        Private Sub Panel1_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Panel1.Paint
            ' Draw a black line at the top of the panel
            Dim topBorderPen As New Pen(Color.Black)
            e.Graphics.DrawLine(topBorderPen, 0, 0, Panel1.Width, 0)
        End Sub
    
    
        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            ' Attempt to parse the input as an integer
            Dim newBoidCount As Integer
            If Integer.TryParse(TextBox1.Text, newBoidCount) Then
                If CInt(TextBox1.Text) > 1000 Then
                    ' Input integer value is too high, display an error message
                    MessageBox.Show("The integer value is too high")
                Else
                    ' Input is a valid integer, reset the list and initialize new boids
                    BoidsList = New List(Of Boid)()
                    InitializeBoids(newBoidCount)
    
                    ' Check if the simulation is paused and unpause if needed
                    If IsPaused Then
                        IsPaused = False
                        ' Update button text when resumed
                        PauseButton.Text = "Pause"
                    End If
                End If
            Else
                ' Input is not a valid integer, display an error message
                MessageBox.Show("Invalid input for boid count." & vbCrLf & vbCrLf & "Please enter a valid integer value.")
    
                ' clears invalid input
                TextBox1.Text = ""
            End If
        End Sub
    
    
        Private Sub PauseButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles PauseButton.Click
            ' Toggle the pause state
            IsPaused = Not IsPaused
    
            ' Update button text based on the pause state
            If IsPaused Then
                ' Update button text when paused
                PauseButton.Text = "Resume"
            Else
                ' Update button text when resumed
                PauseButton.Text = "Pause"
            End If
        End Sub
    End Class
    The boids simulation was coded in Visual Basic 2010, Framework 4, but the code provided above and in the next two comments should work with any Framework.

    The "ChatGPT Boid Simulation Update.zip" project's and "Wpf Boids Simulation.zip" project's performance is the same. The only thing, when you minimize the ""ChatGPT Boid Simulation Update.zip" version to the taskbar, it crashes. I can't figure out why it does that.



    The capture above was taken soon after I changed the boid count. The boids eventually unite into two or 3 flocks. Large flocks sometimes split-up.

    Update, Dec 8th:
    I updated the WPF boids simulation project zip below. It no longer goes by a previous canvas size when changing the boid count, but the actual canvas size after resizing the window. It also displays the actual canvas size while resizing.
    Attached Files Attached Files
    Last edited by Peter Porter; Dec 8th, 2023 at 09:22 AM.

  2. #2

    Thread Starter
    Fanatic Member Peter Porter's Avatar
    Join Date
    Jul 2013
    Location
    Germany
    Posts
    539

    Re: ChatGPT Boids Simulation

    Boid class:
    Code:
    ' Represents a boid with properties for its location and velocity.
    Public Class Boid
        Public Property Location As Point
        Public Property Velocity As Point
    End Class
    Alignment class:
    Code:
    ' For applying the alignment rule to boids.
    Public Class Alignment
    
        Private MaxSpeed As Integer
    
        ' Constructor to initialize the Alignment class with the maximum speed.
        Public Sub New(ByVal maxSpeed As Integer)
            Me.MaxSpeed = maxSpeed
        End Sub
    
        ' Applies the alignment rule to adjust the boid's velocity based on the average velocity of neighboring boids.
        Public Sub ApplyAlignmentRule(ByVal boid As Boid, ByVal avgVelocity As Point)
            ' Calculate the angle of the new velocity based on the average velocity.
            Dim newVelocityAngle As Double = Math.Atan2(avgVelocity.Y, avgVelocity.X)
    
            ' Set the boid's velocity with the adjusted speed in the direction of the new velocity angle.
            boid.Velocity = New Point(CInt(MaxSpeed * Math.Cos(newVelocityAngle)), CInt(MaxSpeed * Math.Sin(newVelocityAngle)))
        End Sub
    End Class
    Separation class:
    Code:
    ' For applying the separation rule to boids.
    Public Class Separation
    
        ' Applies the separation rule to adjust the boid's velocity based on the distances to close neighbors.
        Public Sub ApplySeparationRule(ByVal boid As Boid, ByVal closeDx As Double, ByVal closeDy As Double)
            ' Separation rule logic
            ' The separation factor determines the strength of the separation effect. Adjust as needed.
            Dim separationFactor As Double = 0.1
    
            ' Adjust the boid's velocity based on the distances to close neighbors and the separation factor.
            boid.Velocity = New Point(CInt(boid.Velocity.X + closeDx * separationFactor), CInt(boid.Velocity.Y + closeDy * separationFactor))
        End Sub
    End Class
    Cohesion class:
    Code:
    ' For applying the cohesion rule to boids.
    Public Class Cohesion
    
        ' The factor determining the strength of the cohesion effect. Adjust as needed.
        Public Property CenteringFactor As Double = 0.1
    
        ' Applies the cohesion rule to adjust the boid's velocity based on the average position of neighboring boids.
        Public Sub ApplyCohesionRule(ByVal boid As Boid, ByVal averagePosition As Point)
            ' Cohesion rule logic
            ' The cohesion factor is a combination of the centering factor and a scaling factor. Adjust as needed.
            Dim cohesionFactor As Double = CenteringFactor * 0.4
    
            ' Adjust the boid's velocity based on the average position of neighboring boids and the cohesion factor.
            boid.Velocity = New Point(CInt(boid.Velocity.X + (averagePosition.X - boid.Location.X) * cohesionFactor),
                                       CInt(boid.Velocity.Y + (averagePosition.Y - boid.Location.Y) * cohesionFactor))
        End Sub
    End Class
    Last edited by Peter Porter; Dec 2nd, 2023 at 05:48 PM.

  3. #3

    Thread Starter
    Fanatic Member Peter Porter's Avatar
    Join Date
    Jul 2013
    Location
    Germany
    Posts
    539

    Re: ChatGPT Boids Simulation

    And lastly the BoidCounter class:
    Code:
    ' BoidCounter class is responsible for counting various configurations of boids within a specified visual range.
    
    Public Class BoidCounter
    
    
        ' Property to set or get the list of boids from the Form1 class.
        Public Property BoidsList As List(Of Boid)
    
        ' Property to set or get the visual range within which boids are considered neighbors.
        Public Property VisualRange As Integer
    
    
        Public FlockCount As Integer = 0 ' Number of flocks
        Public TrioCount As Integer = 0  ' Number of trios
        Public PairCount As Integer = 0  ' Number of pairs
        Public LoneCount As Integer = 0  ' Number of loners
    
    
        ' Counts the number of flocks, trios, pairs, and lone boids based on the specified visual range.
        ' Requires the BoidsList property to be set before calling this method.
        Public Sub CountFlocksTriosPairsLoners(ByVal BoidsList As List(Of Boid))
    
            ' Initialize a set to keep track of visited boids during counting.
            Dim visitedBoids As New HashSet(Of Boid)
    
            ' Reset count variables to zero.
            FlockCount = 0
            TrioCount = 0
            PairCount = 0
            LoneCount = 0
    
            ' Iterate through each boid in the BoidsList.
            For Each boid As Boid In BoidsList
                ' Check if the boid has not been visited before.
                If Not visitedBoids.Contains(boid) Then
                    ' Find the boids to which the boid belongs and mark the visited boids.
                    Dim boids As List(Of Boid) = FindBoidsGroup(boid, visitedBoids)
                    visitedBoids.UnionWith(boids)
    
                    ' Update counts based on boids group size.
                    If boids.Count > 3 Then
                        FlockCount += 1
                    End If
    
                    ' Check if it's a trio.
                    If boids.Count = 3 Then
                        TrioCount += 1
                    End If
    
                    ' Check if it's a pair.
                    If boids.Count = 2 Then
                        PairCount += 1
                    End If
    
                    ' Check if it's a lone boid.
                    If boids.Count = 1 Then
                        LoneCount += 1
                    End If
                End If
            Next
        End Sub
    
        ' Finds and returns the group of boids to which a given boid belongs.
        ' Uses depth-first search to traverse the boid groups.
        ' Parameters:
        '   - boid: The boid for which to find the group.
        '   - visitedBoids: A set to keep track of visited boids during the traversal.
        ' Returns a List(Of Boid) representing the group of boids.
        Private Function FindBoidsGroup(ByVal boid As Boid, ByVal visitedBoids As HashSet(Of Boid)) As List(Of Boid)
            ' Initialize a list to store the boids in the group.
            Dim group As New List(Of Boid)
    
            ' Use a stack to perform depth-first search to find the boids group.
            Dim stack As New Stack(Of Boid)
            stack.Push(boid)
    
            ' Iterate until the stack is empty.
            While stack.Count > 0
    
                ' Pop the current boid from the stack.
                Dim currentBoid As Boid = stack.Pop()
    
                ' If the current boid has not been visited, add it to the group and visit its neighbors.
                If Not visitedBoids.Contains(currentBoid) Then
                    visitedBoids.Add(currentBoid)
                    group.Add(currentBoid)
    
                    ' Visit neighbors and add them to the stack if they are not already visited.
                    For Each neighbor As Boid In BoidsList
                        If Not visitedBoids.Contains(neighbor) AndAlso IsNeighbor(currentBoid, neighbor, VisualRange) Then
                            stack.Push(neighbor)
                        End If
                    Next
                End If
            End While
    
            ' Return the final group of boids.
            Return group
        End Function
    
        ' Determines if two boids are neighbors based on distance.
        Private Function IsNeighbor(ByVal boid1 As Boid, ByVal boid2 As Boid, ByVal visualRange As Double) As Boolean
            ' Calculate the Euclidean distance between the locations of the two boids.
            Dim distance As Double = Math.Sqrt((boid1.Location.X - boid2.Location.X) ^ 2 + (boid1.Location.Y - boid2.Location.Y) ^ 2)
    
            ' Return true if the distance is less than the visual range.
            Return distance < visualRange ' Adjust the distance as needed
        End Function
    End Class
    Last edited by Peter Porter; Dec 2nd, 2023 at 05:48 PM.

  4. #4
    Fanatic Member coolcurrent4u's Avatar
    Join Date
    Apr 2008
    Location
    *****
    Posts
    993

    Re: ChatGPT Boids Simulation: Winforms, WPF versions

    How does chatGPT come in, you used it to generate the codes?
    Programming is all about good logic. Spend more time here


    (Generate pronounceable password) (Generate random number c#) (Filter array with another array)

  5. #5

    Thread Starter
    Fanatic Member Peter Porter's Avatar
    Join Date
    Jul 2013
    Location
    Germany
    Posts
    539

    Re: ChatGPT Boids Simulation: Winforms, WPF versions

    Quote Originally Posted by coolcurrent4u View Post
    How does chatGPT come in, you used it to generate the codes?
    Yes, ChatGPPT generated the codes, but I had to tweak it to get it to work.

    I mentioned this in the first sentence of the first post above.

  6. #6
    Addicted Member
    Join Date
    Jul 2022
    Posts
    253

    Re: ChatGPT Boids Simulation: Winforms, WPF versions

    Would you say that using ChatGPT you were not able to kill two boids with one stone?

  7. #7

    Thread Starter
    Fanatic Member Peter Porter's Avatar
    Join Date
    Jul 2013
    Location
    Germany
    Posts
    539

    Re: ChatGPT Boids Simulation: Winforms, WPF versions

    Quote Originally Posted by jdelano View Post
    Would you say that using ChatGPT you were not able to kill two boids with one stone?
    A little boidie told me to give it a try.

    Seriously, I just wanted to see if ChatGPT could code it. I have no use for a boids simulation.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width