Results 1 to 7 of 7

Thread: ChatGPT Boids Simulation: Winforms, WPF versions

Threaded View

  1. #1

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

    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: 1385
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.

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