def __init__(self, app, config): self.app = app self.config = config self.running = False self.bots = [] self.botmove = None self.botsel = None self.botselpos = None self.orientation = 0 self.links = [] self.linkc = self.config['n'] * [0] self.conns = [] self.reddots = [] self.gui = KBGUI(self) self.analyseform = False self.connectform = False self.executeform = False self.clearform = False for i in range(self.config['n']): self.bots.append(Kilobot(self)) for b in self.bots: # set semi-random initial position and orientation b.orientation = random.uniform(0, 360) b.pos = Vec2d( self.config['width'] / 2 + random.uniform(0, b.secretID), (self.config['height'] * 2) / 3 + random.uniform(0, b.secretID))
def __init__(self, app, config): self.app = app self.config = config self.running = False self.bots = [] self.botmove = None self.botsel = None self.botselpos = None self.orientation = 0 self.links = [] self.linkc = self.config["n"] * [0] self.conns = [] self.reddots = [] self.gui = KBGUI(self) self.analyseform = False self.connectform = False self.executeform = False self.clearform = False for i in range(self.config["n"]): self.bots.append(Kilobot(self)) for b in self.bots: # set semi-random initial position and orientation b.orientation = random.uniform(0, 360) b.pos = Vec2d( self.config["width"] / 2 + random.uniform(0, b.secretID), (self.config["height"] * 2) / 3 + random.uniform(0, b.secretID), )
def __init__(self, app, config): ## Simulation - Conf self.app = app self.config = config ## Simulation - Core self.round = 0 self.running = False self.stopped = False self.clock = pygame.time.Clock() # to track FPS self.fps = self.config['fps'] ## Simulation - Noise self.anoise = config['anoise'] self.inoise = config['inoise'] self.hearmin = config['hearmin'] ## Simulation - View if (config['randomseed'] != None): random.seed(config['randomseed']) self.abstract = False self.gui = KBGUI(self) self.designerform = False self.restartform = False ## Simulation - Bots thebot = None thefood= None thebot = imp.load_source("bot", config['program']) #thefood = imp.load_source("bot2", config['foodp']) #print thebot #print thefood #if (self.config['form'] == None): # thebot = imp.load_source("aaa", config['program']) # thefood = imp.load_source("bot2", config['foodp']) # carrega a comida. #else: # thebot = imp.load_source("bot", "./Bots/Renderbot.py") self.bots = [] #self.foods = [] variavel=config['n'] #print variavel #self.bots.append(thefood.load(self)) for j in range(config['n']): self.bots.append(thebot.load(self)) self.setFormation(config['formation'])
def __init__(self, app, config): ## Simulation - Conf self.app = app self.config = config ## Simulation - Core self.round = 0 self.running = False self.stopped = False self.clock = pygame.time.Clock() # to track FPS self.fps = self.config['fps'] ## Simulation - Noise self.anoise = config['anoise'] self.inoise = config['inoise'] self.hearmin = config['hearmin'] ## Simulation - View if (config['randomseed'] != None): random.seed(config['randomseed']) self.abstract = False self.gui = KBGUI(self) self.designerform = False self.restartform = False ## Simulation - Bots thebot = None if (self.config['form'] == None): thebot = imp.load_source("bot", config['program']) else: thebot = imp.load_source("bot", "./Bots/Renderbot.py") self.bots = [] for i in range(config['n']): self.bots.append(thebot.load(self)) self.setFormation(config['formation'])
class KBSimulation: def __init__(self, app, config): ## Simulation - Conf self.app = app self.config = config ## Simulation - Core self.round = 0 self.running = False self.stopped = False self.clock = pygame.time.Clock() # to track FPS self.fps = self.config['fps'] ## Simulation - Noise self.anoise = config['anoise'] self.inoise = config['inoise'] self.hearmin = config['hearmin'] ## Simulation - View if (config['randomseed'] != None): random.seed(config['randomseed']) self.abstract = False self.gui = KBGUI(self) self.designerform = False self.restartform = False ## Simulation - Bots thebot = None thefood= None thebot = imp.load_source("bot", config['program']) #thefood = imp.load_source("bot2", config['foodp']) #print thebot #print thefood #if (self.config['form'] == None): # thebot = imp.load_source("aaa", config['program']) # thefood = imp.load_source("bot2", config['foodp']) # carrega a comida. #else: # thebot = imp.load_source("bot", "./Bots/Renderbot.py") self.bots = [] #self.foods = [] variavel=config['n'] #print variavel #self.bots.append(thefood.load(self)) for j in range(config['n']): self.bots.append(thebot.load(self)) self.setFormation(config['formation']) def setFormation(self, formation): if formation == "PILED": for b in self.bots: b.orientation = random.uniform(0,360) b.pos = Vec2d(self.config['width']/2 + random.uniform(0,b.secretID), self.config['height']/2 + random.uniform(0,b.secretID)) elif formation == "RANDOM": xunit = self.config['width'] / 5 yunit = self.config['height'] / 5 for b in self.bots: b.orientation = random.uniform(0,360) b.pos = Vec2d(random.uniform(yunit, 4*yunit), random.uniform(xunit, 4*xunit)) elif formation == "LINE": for i in range(0,self.config['n']): self.bots[i].pos = Vec2d(self.config['width']/2 + i*34, self.config['height']/2) self.bots[i].orientation = random.uniform(0,360) self.bots[0].orientation = 180 # make first one point left elif formation == "CIRCLE": deg = 360 / (self.config['n']) pi = 3.141592653589793 radius = (self.bots[0].radius * (self.config['n'] + 5)) / pi for i,b in enumerate(self.bots): b.orientation = random.uniform(0,360) b.pos = Vec2d(self.config['width'] / 2, self.config['height'] / 2) b.pos += Vec2d(0, radius).rotated(deg*i) elif formation == "Q": xunit = self.config['width'] / 5 yunit = self.config['height'] / 5 for b in self.bots: if(b.secretID==0): b.orientation = 0 b.pos=Vec2d(150,200) elif(b.secretID==1): b.orientation = 0 b.pos=Vec2d(250,200) else: b.orientation = random.uniform(0,360) b.pos = Vec2d(random.uniform(yunit, 4*yunit), random.uniform(xunit, 4*xunit)) else: print "Unknown formation '%s' - aborting." % (formation) exit(-42) def update_bot_collisions(self, botpairs): for (i,j) in botpairs: a = self.bots[i] b = self.bots[j] dist = a.pos.get_distance(b.pos) bound = 2 * a.radius if dist < 1 + bound: overlap = bound - dist direction = b.pos - a.pos if (direction == Vec2d(0,0)): direction = Vec2d(1,0) direction.length = overlap/2 #b.pos = b.pos + direction a.pos = a.pos - direction b.pos = b.pos #MUDEI def update_secretNxs(self, botpairs): for b in self.bots: b.secretNx = [] for (i,j) in botpairs: a = self.bots[i] b = self.bots[j] dist = int(a.pos.get_distance(b.pos)) if (dist <= self.config['near']): a.secretNx.append(j) # secret b.secretNx.append(i) # neighborhood def update_messaging(self, botpairs): for (i,j) in botpairs: a = self.bots[i] b = self.bots[j] dist = int(a.pos.get_distance(b.pos)) if (a.running and b.running): if (dist < a.rradius and a.tx_enabled == 1): # sends inrange = (a.msgtx[0], a.msgtx[1], a.msgtx[2], dist, 0, 1, 0) b.rxtempbuf.append(inrange) if (dist < b.rradius and b.tx_enabled == 1): inrange = (b.msgtx[0], b.msgtx[1], b.msgtx[2], dist, 0, 1, 0) a.rxtempbuf.append(inrange) if (self.abstract): for b in self.bots: numheard = min(len(b.rxtempbuf), self.config['rxbufsize']) random.shuffle(b.rxtempbuf) # ordering randomization (RX/TX delays in HW) for i in range(0,numheard): if (b.rxbuf[b.rxbufp][self.config['msg_new']] == 0): b.rxbuf[b.rxbufp] = b.rxtempbuf.pop(0) b.rxbufp = (b.rxbufp + 1) % self.config['rxbufsize'] b.rxtempbuf = [] #reset self.noisestat = 1.0 else: # SNIR noiseOK = 0.0 noiseFAIL = 0.0 for b in self.bots: k = len(b.rxtempbuf) if (k == 0): continue k -= 1 # autointerference... heard = [] for m in b.rxtempbuf: randy = random.uniform(50.0,100.0) # delay as random default s = randy / (self.anoise + (k * self.inoise)) if s >= self.hearmin: noiseOK += 1 heard.append(m) else: noiseFAIL += 1 numheard = min(len(heard), self.config['rxbufsize']) random.shuffle(heard) # ordering randomization (RX/TX delays in HW) for i in range(0,numheard): if (b.rxbuf[b.rxbufp][self.config['msg_new']] == 0): b.rxbuf[b.rxbufp] = heard.pop(0) b.rxbufp = (b.rxbufp + 1) % self.config['rxbufsize'] b.rxtempbuf = [] #reset if (self.round % 5 == 0): # too hectic otherwise self.noisestat = 0.0 if noiseOK + noiseFAIL == 0 else (noiseOK / (noiseOK + noiseFAIL)) def stepbots(self): # run a step of the program on each bot for a in self.bots: a.runProgram() def designer(self): self.config['designer'] = True self.running = False def restarter(self): self.running = False def updateUI(self): if (self.designerform): self.designer() self.designerform = False if (self.restartform): self.restarter() self.restartform = False # dodge, collision detection on world borders self.gui.update_border_collisions() # note: before bot collisions def update(self): # called before draw; update variables # process bots in pairs, shuffled botn = range(len(self.bots)) botpairs = [(x, y) for x in botn for y in botn if x > y] random.shuffle(botpairs) # dodge, collision detection on fellow bots self.update_bot_collisions(botpairs) # reset secret (internal) neighborhood information self.update_secretNxs(botpairs) # messaging (note: position must be fixed before messaging) self.update_messaging(botpairs) def run(self): self.running = True # launch ui form = gui.Form() ui = gui.App() ctrl = KBSControl(self) c = gui.Container(align=-1,valign=-1) c.add(ctrl,0,0) ui.init(c) while self.running: self.updateUI() if (not self.stopped): self.stepbots() self.update() self.round += 1 for e in pygame.event.get(): self.gui.event(e) #ui.event(e) self.gui.draw() #ui.paint() pygame.display.flip() self.clock.tick(self.fps) return self.config # running a sim doesn't change the configuration
class KBDesigner: def __init__(self, app, config): self.app = app self.config = config self.running = False self.bots = [] self.botmove = None self.botsel = None self.botselpos = None self.orientation = 0 self.links = [] self.linkc = self.config["n"] * [0] self.conns = [] self.reddots = [] self.gui = KBGUI(self) self.analyseform = False self.connectform = False self.executeform = False self.clearform = False for i in range(self.config["n"]): self.bots.append(Kilobot(self)) for b in self.bots: # set semi-random initial position and orientation b.orientation = random.uniform(0, 360) b.pos = Vec2d( self.config["width"] / 2 + random.uniform(0, b.secretID), (self.config["height"] * 2) / 3 + random.uniform(0, b.secretID), ) """ def parsedatafile(self, targetfile): N = 3 conns = [] orientation = 0 with open(targetfile, 'r') as f: try: for line in f: tok = line.split() if (tok[0] == "KBFORM"): if (tok[1] != str(VERSION)): print "Unknown data type, expecting 'KBFORM %2g'" % (VERSION) quit(-42) print ">> Reading data file" elif (tok[0] == "N"): N = int(tok[1]) elif (tok[0] == "o"): orientation = int(tok[1]) elif (tok[0] == "-"): break elif (tok[0] == "#"): pass else: entry = (int(tok[0]), (int(tok[1]), int(tok[2])), (int(tok[3]), int(tok[4])), int(tok[5])) print tok, " --> ", entry conns.append(entry) except e, msg: print "File read failed:", msg return N, orientation, conns """ """ def render(self): print ">> Rendering" self.reddots = [] self.links = [] for i in range(2,self.config['n']): rules = None for conn in self.conns: if (conn[0] == i): rules = conn break if (rules == None): print "File had no rules for %d, breaking." % (i) break posA = self.bots[rules[1][0]].pos posB = self.bots[rules[2][0]].pos posAt = posA.inttup() posBt = posB.inttup() xA = posAt[0] yA = posAt[1] xB = posBt[0] yB = posBt[1] rA = rules[1][1] rB = rules[2][1] ds = posA.get_dist_sqrd(posB) # print xA, yA, xB, yB, rA, rB, ds Ka = (pow(rA+rB,2) - ds); Ka = 0.01 if Ka < 0 else Ka Kb = (ds - pow(rA-rB,2)); Kb = 0.01 if Kb < 0 else Kb # precision... K = 0.25 * math.sqrt(Ka * Kb) xt = (0.5 * (xB + xA)) + (0.5 * (xB - xA) * ((rA**2) - (rB**2)) / ds) yt = (0.5 * (yB + yA)) + (0.5 * (yB - yA) * ((rA**2) - (rB**2)) / ds) x1 = xt + (2 * (yB - yA) * K / ds) y1 = yt - (2 * (xB - xA) * K / ds) x2 = xt - (2 * (yB - yA) * K / ds) y2 = yt + (2 * (xB - xA) * K / ds) # print i, rules, "posA/B:", posAt, posBt, Vec2d(x1,y1), Vec2d(x2,y2) print " %d:"%(i), Vec2d(x1,y1).inttup(), Vec2d(x2,y2).inttup(), print "choosing", rules[3] if (rules[3] == LEFT): self.reddots.append(Vec2d(x2,y2)) self.bots[i].pos = Vec2d(x1,y1) else: self.reddots.append(Vec2d(x1,y1)) self.bots[i].pos = Vec2d(x2,y2) self.links.append((self.bots[i], self.bots[rules[1][0]])) self.links.append((self.bots[i], self.bots[rules[2][0]])) """ def analyser(self): print "---\n ANALYSER \n---" n = len(self.bots) isLinked = filter((lambda x: self.linkc[x] == 1), range(n)) is2Linked = filter((lambda x: self.linkc[x] == 2), range(n)) isXLinked = filter((lambda x: self.linkc[x] > 2), range(n)) print "n = %03d | L0:%03d L1:%03d L2:%03d LX:%03d" % ( n, n - len(isLinked), len(isLinked), len(is2Linked), len(isXLinked), ) if len(is2Linked + isXLinked) < n: print "DEGREE TEST FAILED: some have degree < 2" else: print "DEGREE TEST OK: all have degree >= 2" def connecter(self): print "---\n CONNECTER \n---" poss = [] for b in self.bots: tup = b.pos.inttup() poss.append(tup) avg = map((lambda x: x / len(self.bots)), reduce((lambda x, y: [x[0] + y[0], x[1] + y[1]]), poss, [0, 0])) print "positions:", poss, "\n", "avg:", avg avg = Vec2d(avg) # computer kNN and find those closest to the center of the bot mass # TODO: make less sloppy; proper generative 2-MST etc order = [] k = self.config["n"] knns = len(self.bots) * [None] for i in range(len(self.bots)): bot = self.bots[i] dist = avg.get_distance(bot.pos) order.append((dist, bot)) knns[i] = [] for bat in self.bots: if bat == bot: continue dist = bot.pos.get_distance(bat.pos) knns[i].append([dist, bat]) knns[i] = sorted(knns[i])[:k] # drop some order = sorted(order) # create links self.links = [] self.linkc = self.config["n"] * [0] # startup a = order[0][1] b = order[1][1] pair = (a, b) if (a.secretID > b.secretID) else (b, a) self.links.append(pair) self.linkc[a.secretID] += 1 self.linkc[b.secretID] += 1 inplace = [a.secretID, b.secretID] # keep track of the existing form dist = int(round(a.pos.get_distance(b.pos))) self.conns = [(1, (0, dist), (0, dist))] # start conns self.orientation = int((b.pos - a.pos).get_angle()) for i in map((lambda x: x[1].secretID), order[2:]): knn = map((lambda x: x[1].secretID), knns[i]) bot = self.bots[i] myid = len(inplace) temp = None for j in knns[i]: # try to connect every bot to form as directly as possible dist = int(round(j[0])) k = j[1].secretID bat = self.bots[k] if k in inplace: # add links by secretID pair = (bot, bat) if i > k else (bat, bot) self.links.append(pair) self.linkc[bot.secretID] += 1 self.linkc[bat.secretID] += 1 # add connection by form index if temp == None: inplace.append(i) temp = [k, inplace.index(k), dist] else: but = self.bots[temp.pop(0)] # trigonometric black magic # we compute the angle of bot in relation to a vector defined by to others (bat and but) angle = (bot.pos - bat.pos).rotated(-(but.pos - bat.pos).get_angle()).get_angle() left = angle < 0 # print bat.secretID, "-->", but.secretID, ":", bot.secretID, "L" if angle < 0 else "R" if left: # we force RIGHTHANDSIDE positioning self.conns.append((myid, temp, [inplace.index(k), dist])) else: self.conns.append((myid, [inplace.index(k), dist], temp)) break print "conns:" for i, conn in enumerate(self.conns): print conn def clearer(self): self.links = [] self.linkc = self.config["n"] * [0] def executor(self): if self.conns == []: print "SANITY CHECK: No conns, won't start execution" return print self.orientation self.config["form"] = { "version": "KBFORM 0.1", "n": self.config["n"], "orientation": self.orientation, "rules": self.conns, } self.config["designer"] = False self.running = False def updateUI(self): # analyse the current form if self.analyseform: self.analyser() self.analyseform = False if self.connectform: self.connecter() self.connectform = False if self.clearform: self.clearer() self.clearform = False if self.executeform: self.executor() self.executeform = False # dodge, collision detection on world borders self.gui.update_border_collisions() # note: before bot collisions def update(self): # called before draw; update variables # process bots in pairs, shuffled botn = range(len(self.bots)) botpairs = [(x, y) for x in botn for y in botn if x > y] random.shuffle(botpairs) # dodge, collision detection on fellow bots for (i, j) in botpairs: a = self.bots[i] b = self.bots[j] dist = a.pos.get_distance(b.pos) bound = 2 * a.radius if dist < 1 + bound: overlap = bound - dist direction = b.pos - a.pos if direction == Vec2d(0, 0): direction = Vec2d(1, 0) direction.length = overlap / 2 b.pos = b.pos + direction a.pos = a.pos - direction def run(self): self.running = True # launch ui form = gui.Form() ui = gui.App() ctrl = KBDControl(self) c = gui.Container(align=-1, valign=-1) c.add(ctrl, 0, 0) ui.init(c) while self.running: self.updateUI() self.update() for e in pygame.event.get(): self.gui.event(e) ui.event(e) self.gui.draw() ui.paint() pygame.display.flip() return self.config
class KBSimulation: def __init__(self, app, config): ## Simulation - Conf self.app = app self.config = config ## Simulation - Core self.round = 0 self.running = False self.stopped = False self.clock = pygame.time.Clock() # to track FPS self.fps = self.config['fps'] ## Simulation - Noise self.anoise = config['anoise'] self.inoise = config['inoise'] self.hearmin = config['hearmin'] ## Simulation - View if (config['randomseed'] != None): random.seed(config['randomseed']) self.abstract = False self.gui = KBGUI(self) self.designerform = False self.restartform = False ## Simulation - Bots thebot = None if (self.config['form'] == None): thebot = imp.load_source("bot", config['program']) else: thebot = imp.load_source("bot", "./Bots/Renderbot.py") self.bots = [] for i in range(config['n']): self.bots.append(thebot.load(self)) self.setFormation(config['formation']) def setFormation(self, formation): if formation == "PILED": for b in self.bots: b.orientation = random.uniform(0, 360) b.pos = Vec2d( self.config['width'] / 2 + random.uniform(0, b.secretID), self.config['height'] / 2 + random.uniform(0, b.secretID)) elif formation == "RANDOM": xunit = self.config['width'] / 5 yunit = self.config['height'] / 5 for b in self.bots: b.orientation = random.uniform(0, 360) b.pos = Vec2d(random.uniform(yunit, 4 * yunit), random.uniform(xunit, 4 * xunit)) elif formation == "LINE": for i in range(0, self.config['n']): self.bots[i].pos = Vec2d(self.config['width'] / 2 + i * 34, self.config['height'] / 2) self.bots[i].orientation = random.uniform(0, 360) self.bots[0].orientation = 180 # make first one point left elif formation == "CIRCLE": deg = 360 / (self.config['n']) pi = 3.141592653589793 radius = (self.bots[0].radius * (self.config['n'] + 5)) / pi for i, b in enumerate(self.bots): b.orientation = random.uniform(0, 360) b.pos = Vec2d(self.config['width'] / 2, self.config['height'] / 2) b.pos += Vec2d(0, radius).rotated(deg * i) else: print "Unknown formation '%s' - aborting." % (formation) exit(-42) def update_bot_collisions(self, botpairs): for (i, j) in botpairs: a = self.bots[i] b = self.bots[j] dist = a.pos.get_distance(b.pos) bound = 2 * a.radius if dist < 1 + bound: overlap = bound - dist direction = b.pos - a.pos if (direction == Vec2d(0, 0)): direction = Vec2d(1, 0) direction.length = overlap / 2 b.pos = b.pos + direction a.pos = a.pos - direction def update_secretNxs(self, botpairs): for b in self.bots: b.secretNx = [] for (i, j) in botpairs: a = self.bots[i] b = self.bots[j] dist = int(a.pos.get_distance(b.pos)) if (dist <= self.config['near']): a.secretNx.append(j) # secret b.secretNx.append(i) # neighborhood def update_messaging(self, botpairs): for (i, j) in botpairs: a = self.bots[i] b = self.bots[j] dist = int(a.pos.get_distance(b.pos)) if (a.running and b.running): if (dist < a.rradius and a.tx_enabled == 1): # sends inrange = (a.msgtx[0], a.msgtx[1], a.msgtx[2], dist, 0, 1, 0) b.rxtempbuf.append(inrange) if (dist < b.rradius and b.tx_enabled == 1): inrange = (b.msgtx[0], b.msgtx[1], b.msgtx[2], dist, 0, 1, 0) a.rxtempbuf.append(inrange) if (self.abstract): for b in self.bots: numheard = min(len(b.rxtempbuf), self.config['rxbufsize']) random.shuffle( b.rxtempbuf) # ordering randomization (RX/TX delays in HW) for i in range(0, numheard): if (b.rxbuf[b.rxbufp][self.config['msg_new']] == 0): b.rxbuf[b.rxbufp] = b.rxtempbuf.pop(0) b.rxbufp = (b.rxbufp + 1) % self.config['rxbufsize'] b.rxtempbuf = [] #reset self.noisestat = 1.0 else: # SNIR noiseOK = 0.0 noiseFAIL = 0.0 for b in self.bots: k = len(b.rxtempbuf) if (k == 0): continue k -= 1 # autointerference... heard = [] for m in b.rxtempbuf: randy = random.uniform(50.0, 100.0) # delay as random default s = randy / (self.anoise + (k * self.inoise)) if s >= self.hearmin: noiseOK += 1 heard.append(m) else: noiseFAIL += 1 numheard = min(len(heard), self.config['rxbufsize']) random.shuffle( heard) # ordering randomization (RX/TX delays in HW) for i in range(0, numheard): if (b.rxbuf[b.rxbufp][self.config['msg_new']] == 0): b.rxbuf[b.rxbufp] = heard.pop(0) b.rxbufp = (b.rxbufp + 1) % self.config['rxbufsize'] b.rxtempbuf = [] #reset if (self.round % 5 == 0): # too hectic otherwise self.noisestat = 0.0 if noiseOK + noiseFAIL == 0 else ( noiseOK / (noiseOK + noiseFAIL)) def stepbots(self): # run a step of the program on each bot for a in self.bots: a.runProgram() def designer(self): self.config['designer'] = True self.running = False def restarter(self): self.running = False def updateUI(self): if (self.designerform): self.designer() self.designerform = False if (self.restartform): self.restarter() self.restartform = False # dodge, collision detection on world borders self.gui.update_border_collisions() # note: before bot collisions def update(self): # called before draw; update variables # process bots in pairs, shuffled botn = range(len(self.bots)) botpairs = [(x, y) for x in botn for y in botn if x > y] random.shuffle(botpairs) # dodge, collision detection on fellow bots self.update_bot_collisions(botpairs) # reset secret (internal) neighborhood information self.update_secretNxs(botpairs) # messaging (note: position must be fixed before messaging) self.update_messaging(botpairs) def run(self): self.running = True # launch ui form = gui.Form() ui = gui.App() ctrl = KBSControl(self) c = gui.Container(align=-1, valign=-1) c.add(ctrl, 0, 0) ui.init(c) while self.running: self.updateUI() if (not self.stopped): self.stepbots() self.update() self.round += 1 for e in pygame.event.get(): self.gui.event(e) ui.event(e) self.gui.draw() ui.paint() pygame.display.flip() self.clock.tick(self.fps) return self.config # running a sim doesn't change the configuration
class KBDesigner: def __init__(self, app, config): self.app = app self.config = config self.running = False self.bots = [] self.botmove = None self.botsel = None self.botselpos = None self.orientation = 0 self.links = [] self.linkc = self.config['n'] * [0] self.conns = [] self.reddots = [] self.gui = KBGUI(self) self.analyseform = False self.connectform = False self.executeform = False self.clearform = False for i in range(self.config['n']): self.bots.append(Kilobot(self)) for b in self.bots: # set semi-random initial position and orientation b.orientation = random.uniform(0, 360) b.pos = Vec2d( self.config['width'] / 2 + random.uniform(0, b.secretID), (self.config['height'] * 2) / 3 + random.uniform(0, b.secretID)) """ def parsedatafile(self, targetfile): N = 3 conns = [] orientation = 0 with open(targetfile, 'r') as f: try: for line in f: tok = line.split() if (tok[0] == "KBFORM"): if (tok[1] != str(VERSION)): print "Unknown data type, expecting 'KBFORM %2g'" % (VERSION) quit(-42) print ">> Reading data file" elif (tok[0] == "N"): N = int(tok[1]) elif (tok[0] == "o"): orientation = int(tok[1]) elif (tok[0] == "-"): break elif (tok[0] == "#"): pass else: entry = (int(tok[0]), (int(tok[1]), int(tok[2])), (int(tok[3]), int(tok[4])), int(tok[5])) print tok, " --> ", entry conns.append(entry) except e, msg: print "File read failed:", msg return N, orientation, conns """ """ def render(self): print ">> Rendering" self.reddots = [] self.links = [] for i in range(2,self.config['n']): rules = None for conn in self.conns: if (conn[0] == i): rules = conn break if (rules == None): print "File had no rules for %d, breaking." % (i) break posA = self.bots[rules[1][0]].pos posB = self.bots[rules[2][0]].pos posAt = posA.inttup() posBt = posB.inttup() xA = posAt[0] yA = posAt[1] xB = posBt[0] yB = posBt[1] rA = rules[1][1] rB = rules[2][1] ds = posA.get_dist_sqrd(posB) # print xA, yA, xB, yB, rA, rB, ds Ka = (pow(rA+rB,2) - ds); Ka = 0.01 if Ka < 0 else Ka Kb = (ds - pow(rA-rB,2)); Kb = 0.01 if Kb < 0 else Kb # precision... K = 0.25 * math.sqrt(Ka * Kb) xt = (0.5 * (xB + xA)) + (0.5 * (xB - xA) * ((rA**2) - (rB**2)) / ds) yt = (0.5 * (yB + yA)) + (0.5 * (yB - yA) * ((rA**2) - (rB**2)) / ds) x1 = xt + (2 * (yB - yA) * K / ds) y1 = yt - (2 * (xB - xA) * K / ds) x2 = xt - (2 * (yB - yA) * K / ds) y2 = yt + (2 * (xB - xA) * K / ds) # print i, rules, "posA/B:", posAt, posBt, Vec2d(x1,y1), Vec2d(x2,y2) print " %d:"%(i), Vec2d(x1,y1).inttup(), Vec2d(x2,y2).inttup(), print "choosing", rules[3] if (rules[3] == LEFT): self.reddots.append(Vec2d(x2,y2)) self.bots[i].pos = Vec2d(x1,y1) else: self.reddots.append(Vec2d(x1,y1)) self.bots[i].pos = Vec2d(x2,y2) self.links.append((self.bots[i], self.bots[rules[1][0]])) self.links.append((self.bots[i], self.bots[rules[2][0]])) """ def analyser(self): print "---\n ANALYSER \n---" n = len(self.bots) isLinked = filter((lambda x: self.linkc[x] == 1), range(n)) is2Linked = filter((lambda x: self.linkc[x] == 2), range(n)) isXLinked = filter((lambda x: self.linkc[x] > 2), range(n)) print "n = %03d | L0:%03d L1:%03d L2:%03d LX:%03d" % ( n, n - len(isLinked), len(isLinked), len(is2Linked), len(isXLinked)) if (len(is2Linked + isXLinked) < n): print "DEGREE TEST FAILED: some have degree < 2" else: print "DEGREE TEST OK: all have degree >= 2" def connecter(self): print "---\n CONNECTER \n---" poss = [] for b in self.bots: tup = b.pos.inttup() poss.append(tup) avg = map((lambda x: x / len(self.bots)), reduce((lambda x, y: [x[0] + y[0], x[1] + y[1]]), poss, [0, 0])) print "positions:", poss, "\n", "avg:", avg avg = Vec2d(avg) # computer kNN and find those closest to the center of the bot mass # TODO: make less sloppy; proper generative 2-MST etc order = [] k = self.config['n'] knns = len(self.bots) * [None] for i in range(len(self.bots)): bot = self.bots[i] dist = avg.get_distance(bot.pos) order.append((dist, bot)) knns[i] = [] for bat in self.bots: if (bat == bot): continue dist = bot.pos.get_distance(bat.pos) knns[i].append([dist, bat]) knns[i] = sorted(knns[i])[:k] # drop some order = sorted(order) # create links self.links = [] self.linkc = self.config['n'] * [0] # startup a = order[0][1] b = order[1][1] pair = (a, b) if (a.secretID > b.secretID) else (b, a) self.links.append(pair) self.linkc[a.secretID] += 1 self.linkc[b.secretID] += 1 inplace = [a.secretID, b.secretID] # keep track of the existing form dist = int(round(a.pos.get_distance(b.pos))) self.conns = [(1, (0, dist), (0, dist))] # start conns self.orientation = int((b.pos - a.pos).get_angle()) for i in map((lambda x: x[1].secretID), order[2:]): knn = map((lambda x: x[1].secretID), knns[i]) bot = self.bots[i] myid = len(inplace) temp = None for j in knns[ i]: # try to connect every bot to form as directly as possible dist = int(round(j[0])) k = j[1].secretID bat = self.bots[k] if (k in inplace): # add links by secretID pair = (bot, bat) if i > k else (bat, bot) self.links.append(pair) self.linkc[bot.secretID] += 1 self.linkc[bat.secretID] += 1 # add connection by form index if (temp == None): inplace.append(i) temp = [k, inplace.index(k), dist] else: but = self.bots[temp.pop(0)] # trigonometric black magic # we compute the angle of bot in relation to a vector defined by to others (bat and but) angle = (bot.pos - bat.pos).rotated( -(but.pos - bat.pos).get_angle()).get_angle() left = angle < 0 # print bat.secretID, "-->", but.secretID, ":", bot.secretID, "L" if angle < 0 else "R" if (left): # we force RIGHTHANDSIDE positioning self.conns.append( (myid, temp, [inplace.index(k), dist])) else: self.conns.append((myid, [inplace.index(k), dist], temp)) break print "conns:" for i, conn in enumerate(self.conns): print conn def clearer(self): self.links = [] self.linkc = self.config['n'] * [0] def executor(self): if (self.conns == []): print "SANITY CHECK: No conns, won't start execution" return print self.orientation self.config['form'] = { 'version': "KBFORM 0.1", 'n': self.config['n'], 'orientation': self.orientation, 'rules': self.conns } self.config['designer'] = False self.running = False def updateUI(self): # analyse the current form if (self.analyseform): self.analyser() self.analyseform = False if (self.connectform): self.connecter() self.connectform = False if (self.clearform): self.clearer() self.clearform = False if (self.executeform): self.executor() self.executeform = False # dodge, collision detection on world borders self.gui.update_border_collisions() # note: before bot collisions def update(self): # called before draw; update variables # process bots in pairs, shuffled botn = range(len(self.bots)) botpairs = [(x, y) for x in botn for y in botn if x > y] random.shuffle(botpairs) # dodge, collision detection on fellow bots for (i, j) in botpairs: a = self.bots[i] b = self.bots[j] dist = a.pos.get_distance(b.pos) bound = 2 * a.radius if dist < 1 + bound: overlap = bound - dist direction = b.pos - a.pos if (direction == Vec2d(0, 0)): direction = Vec2d(1, 0) direction.length = overlap / 2 b.pos = b.pos + direction a.pos = a.pos - direction def run(self): self.running = True # launch ui form = gui.Form() ui = gui.App() ctrl = KBDControl(self) c = gui.Container(align=-1, valign=-1) c.add(ctrl, 0, 0) ui.init(c) while self.running: self.updateUI() self.update() for e in pygame.event.get(): self.gui.event(e) ui.event(e) self.gui.draw() ui.paint() pygame.display.flip() return self.config