Skip to content

Instantly share code, notes, and snippets.

@SoMuchForSubtlety
Last active July 17, 2019 18:34
Show Gist options
  • Select an option

  • Save SoMuchForSubtlety/c2284b2ebbb5f48b580471403bc4b0a0 to your computer and use it in GitHub Desktop.

Select an option

Save SoMuchForSubtlety/c2284b2ebbb5f48b580471403bc4b0a0 to your computer and use it in GitHub Desktop.
simple swarm
import sys
from threading import Thread
from random import seed
from random import random
from datetime import datetime
from PyQt5.QtCore import QCoreApplication, Qt, QLineF, QRectF, QTimer
from PyQt5.QtGui import QPainter, QColor, QFont, QKeySequence, QMouseEvent, QCursor
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QPushButton, QAction, QMessageBox
from PyQt5.QtWidgets import QCheckBox, QProgressBar, QShortcut
import numpy as np
import math
import operator
# how much of the speed is retained after every calculation
slowdown = 1
# how much of the movement vector is executed before recalculating
sim_speed = 0.1
# the distance where we switch from attraction to repulsion
min_desired_distance = 10
# attraction vector multiplier
attraction_force = 0.5
# max allowed speed
max_speed = 20
# max allowed change to speed for each calculation
max_speed_change = 1
# how far cells can see
max_sight = 200
# mow many cells to simulate
population = 100
# how much cells react to the mouse pointer
mouse_attraction = -100000
mouse_location = np.array([500, 500])
def vector(start_point, end_point):
return end_point - start_point
class Flyer():
swarm = []
def __init__(self, position, gui):
self.gui = gui
self.position = position
self.future_position = position
self.heading = np.array([0.0, 0.0])
Flyer.swarm.append(self)
def act(self):
self.heading *= slowdown
new_movement = np.array([0.0, 0.0])
v = vector(self.position, mouse_location)
d = math.hypot(v[0], v[1])
if d < max_sight:
v /= d
new_movement += (v * mouse_attraction)
closest = []
closest_distance = 0
for flyer in Flyer.swarm:
if flyer != self:
v = vector(self.position, flyer.position)
d = math.hypot(v[0], v[1])
if d < max_sight:
if (len(closest) == 0 or d < closest_distance) and d > min_desired_distance:
closest_distance = d
closest.append(flyer)
if len(closest) > 5:
closest.pop(0)
if d < min_desired_distance and d != 0:
v /= d
v *= (5 - d)
new_movement += v
bounds_vector = np.array([0.0, 0.0])
if self.position[0] < 0:
self.position[0] += 1000
#bounds_vector += np.array([100000, 0])
elif self.position[0] > 1000:
self.position[0] -= 1000
#bounds_vector += np.array([-100000, 0])
if self.position[1] < 0:
self.position[1] += 1000
#bounds_vector += np.array([0, 100000])
elif self.position[1] > 1000:
self.position[1] -= 1000
#bounds_vector += np.array([0, -100000])
#new_movement += bounds_vector
# move to closest flyer if none are under 5 away
for flyer in closest:
v = vector(self.position, flyer.position)
v *= attraction_force
new_movement += v
# limit movement
length = math.hypot(new_movement[0], new_movement[1])
if length > max_speed_change:
new_movement /= length
new_movement *= max_speed_change
self.heading += new_movement
length = math.hypot(self.heading[0], self.heading[1])
if length > max_speed:
self.heading /= length
self.heading *= max_speed
self.future_position = self.position + (self.heading * sim_speed)
def execute_move(self):
self.position = self.future_position
class window(QMainWindow):
def __init__(self):
seed(datetime.now())
super(window, self).__init__()
self.setStyleSheet("QMainWindow {background: 'white';}")
self.clock = 500
self.wSize = [1000, 1000]
self.timer = QTimer()
self.timer.timeout.connect(self.updateWorld)
self.timer.start(10)
self.setGeometry(50, 50, self.wSize[0], self.wSize[1])
self.setWindowTitle('AL')
for i in range(0, population):
Flyer(np.array([((random() * 1000)), (random() * 1000)]), self)
self.setHotkeys()
self.home()
def setHotkeys(self):
self.shortcutact = QShortcut(QKeySequence("Right"), self)
self.shortcutact.activated.connect(self.updateWorld)
self.shortcutpr = QShortcut(QKeySequence("Space"), self)
self.shortcutpr.activated.connect(self.PauseResume)
def PauseResume(self):
if self.timer.isActive():
self.timer.stop()
else:
self.timer.start(self.clock)
def home(self):
self.show()
def paintEvent(self, event):
qp = QPainter()
qp.begin(self)
self.paintStuff(event, qp)
qp.end()
def enlarge_window(self, state):
if state == Qt.Checked:
self.setGeometry(50, 50, 1000, 600)
else:
self.setGeometry(50, 50, 500, 300)
def close_application(self):
sys.exit()
def paintStuff(self, event, qp):
qp = QPainter()
qp.begin(self)
self.paintFlyers(event, qp)
qp.end()
def paintFlyers(self, event, qp):
qp.setPen(QColor(0, 0, 0))
pen = qp.pen()
for flyer in Flyer.swarm:
pen.setWidth(1)
pen.setColor(QColor(200, 200, 200))
qp.setPen(pen)
qp.drawLine(flyer.position[0], flyer.position[1], flyer.position[0] +
flyer.heading[0], flyer.position[1]+flyer.heading[1])
pen.setWidth(3)
pen.setColor(QColor(0, 0, 0))
qp.setPen(pen)
qp.drawPoint(flyer.position[0], flyer.position[1])
def updateWorld(self):
point = QCursor.pos()
mouse_location[0] = point.x()
mouse_location[1] = point.y()
length = len(Flyer.swarm)
t1 = Thread(target=act_range(0, int(length/4)))
t2 = Thread(target=act_range(int(length/4), int(length/2)))
t3 = Thread(target=act_range(int(length/2), int((length/4)*3)))
t4 = Thread(target=act_range(int((length/4)*3), length))
t1.start()
t2.start()
t3.start()
t4.start()
t1.join()
t2.join()
t3.join()
t4.join()
for flyer in Flyer.swarm:
flyer.execute_move()
self.repaint()
def act_range(start, end):
for i in range(start, end):
Flyer.swarm[i].act()
if __name__ == "__main__":
def run():
app = QApplication(sys.argv)
Gui = window()
sys.exit(app.exec_())
run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment