From d7f3a7729c6663d7a3b1a2cd700c87832937bd15 Mon Sep 17 00:00:00 2001 From: Bruno Rodrigues Date: Thu, 16 Jan 2025 13:01:19 +0000 Subject: [PATCH 1/4] created config files for UI, SIM, rename variables/methods/functions using underscore. overall cleanup, Color Enum, --- config/sim_config.json | 20 ++ config/ui_config.json | 15 + enums.py | 13 + jes.py | 46 +-- jes_button.py | 22 +- jes_creature.py | 137 ++++----- jes_dataviz.py | 226 +++++++------- jes_dump.py | 20 +- jes_shapes.py | 66 +++-- jes_sim.py | 371 ++++++++++++----------- jes_slider.py | 2 +- jes_species_info.py | 9 +- jes_ui.py | 660 +++++++++++++++++++++-------------------- utils.py | 102 ++++--- 14 files changed, 907 insertions(+), 802 deletions(-) create mode 100644 config/sim_config.json create mode 100644 config/ui_config.json create mode 100644 enums.py diff --git a/config/sim_config.json b/config/sim_config.json new file mode 100644 index 0000000..6a96d2d --- /dev/null +++ b/config/sim_config.json @@ -0,0 +1,20 @@ +{ + "stabilization_time": 200, + "trial_time": 300, + "beat_time": 20, + "beat_fade_time": 5, + "c_dim": [4, 4], + "beats_per_cycle": 3, + "node_coor_count": 4, + "y_clips": [-10000000, 0], + "ground_friction_coef": 25, + "gravity_acceleration_coef": 0.002, + "calming_friction_coef": 0.7, + "typical_friction_coef": 0.8, + "muscle_coef": 0.08, + "traits_per_box": 3, + "traits_extra": 1, + "mutation_rate": 0.07, + "big_mutation_rate": 0.025, + "units_per_meter": 0.05 +} \ No newline at end of file diff --git a/config/ui_config.json b/config/ui_config.json new file mode 100644 index 0000000..1f746c7 --- /dev/null +++ b/config/ui_config.json @@ -0,0 +1,15 @@ +{ + "title": "Jelly Evolution Simulator", + "fps": 30, + "window_width": 1920, + "window_height": 1080, + "movie_single_dim": [650, 650], + "graph_coor": [850, 50, 900, 500], + "sac_coor": [850, 560, 900, 300], + "genealogy_coor": [20, 105, 530, 802, 42], + "column_margin": 330, + "mosaic_dim": [10, 24, 24, 30], + "menu_text_up": 180, + "cm_margin_1": 20, + "cm_margin_2": 1 +} \ No newline at end of file diff --git a/enums.py b/enums.py new file mode 100644 index 0000000..9ed62f4 --- /dev/null +++ b/enums.py @@ -0,0 +1,13 @@ +from enum import Enum + + +class Color(tuple, Enum): + BLACK = (0,0,0) + GRAY25 = (70,70,70) + GRAY50 = (128,128,128) + GRAYISH = (108, 118, 155) + WHITE = (255,255,255) + RED = (255,0,0) + GREEN = (0,255,0) + MOSAIC = (80, 80, 80) + SIGN = (150, 100, 50) \ No newline at end of file diff --git a/jes.py b/jes.py index 49d9ecb..a0e06d4 100644 --- a/jes.py +++ b/jes.py @@ -1,5 +1,12 @@ from jes_sim import Sim from jes_ui import UI +from utils import read_config + +sim_config_file: str = 'config/sim_config.json' +ui_config_file: str = 'config/ui_config.json' + +sim_config: dict = read_config(filename=sim_config_file) +ui_config: dict = read_config(filename=ui_config_file) c_input = input("How many creatures do you want?\n100: Lightweight\n250: Standard (if you don't type anything, I'll go with this)\n500: Strenuous (this is what my carykh video used)\n") if c_input == "": @@ -8,33 +15,30 @@ # Simulation # population size is 250 here, because that runs faster. You can increase it to 500 to replicate what was in my video, but do that at your own risk! -sim = Sim(_c_count=int(c_input), _stabilization_time=200, _trial_time=300, -_beat_time=20, _beat_fade_time=5, _c_dim=[4,4], -_beats_per_cycle=3, _node_coor_count=4, # x_position, y_position, x_velocity, y_velocity -_y_clips=[-10000000,0], _ground_friction_coef=25, -_gravity_acceleration_coef=0.002, _calming_friction_coef=0.7, -_typical_friction_coef=0.8, _muscle_coef=0.08, -_traits_per_box=3, # desired width, desired height, rigidity -_traits_extra=1, # heartbeat (time) -_mutation_rate=0.07, _big_mutation_rate=0.025, -_UNITS_PER_METER=0.05) + + +sim: Sim = Sim(creature_count=int(c_input), config=sim_config) # Cosmetic UI variables -ui = UI(_W_W=1920, _W_H=1080, _MOVIE_SINGLE_DIM=(650,650), -_GRAPH_COOR=(850,50,900,500), _SAC_COOR=(850,560,900,300), _GENEALOGY_COOR=(20,105,530,802,42), -_COLUMN_MARGIN=330, _MOSAIC_DIM=[10,24,24,30], #_MOSAIC_DIM=[10,10,17,22], -_MENU_TEXT_UP=180, _CM_MARGIN1=20, _CM_MARGIN2=1) + +ui: UI = UI(config=ui_config) + +# ui = UI(_W_W=1920, _W_H=1080, _MOVIE_SINGLE_DIM=(650,650), +# _GRAPH_COOR=(850,50,900,500), _SAC_COOR=(850,560,900,300), _GENEALOGY_COOR=(20,105,530,802,42), +# _COLUMN_MARGIN=330, _MOSAIC_DIM=[10,24,24,30], #_MOSAIC_DIM=[10,10,17,22], +# _MENU_TEXT_UP=180, _CM_MARGIN1=20, _CM_MARGIN2=1) sim.ui = ui ui.sim = sim -ui.addButtonsAndSliders() +ui.add_buttons_and_sliders() -sim.initializeUniverse() +sim.initialize_universe() + while ui.running: - sim.checkALAP() - ui.detectMouseMotion() - ui.detectEvents() - ui.detectSliders() - ui.doMovies() + sim.check_alap() + ui.detect_mouse_motion() + ui.detect_events() + ui.detect_sliders() + ui.do_movies() ui.drawMenu() ui.show() \ No newline at end of file diff --git a/jes_button.py b/jes_button.py index 74a94a0..0a0504b 100644 --- a/jes_button.py +++ b/jes_button.py @@ -1,29 +1,29 @@ import pygame -from jes_shapes import centerText +from jes_shapes import center_text import time class Button: - def __init__(self,ui,pdim,pnames,pfunc): + def __init__(self, ui, pdim, pnames, pfunc): self.dim = pdim # Dim is a list of 4 parameters: x, y, width, height self.names = pnames self.setting = 0 self.timeOfLastClick = 0 self.func = pfunc - ui.buttonList.append(self) + ui.button_list.append(self) - def drawButton(self, screen, font): + def draw_button(self, screen, font): x, y, w, h = self.dim name = self.names[self.setting] - sliderSurface = pygame.Surface((w,h), pygame.SRCALPHA, 32) - sliderSurface.fill((30,150,230)) + slider_surface = pygame.Surface((w,h), pygame.SRCALPHA, 32) + slider_surface.fill((30,150,230)) if name == "Turn off ALAP" or name[:4] == "Stop" or name[:4] == "Hide": - sliderSurface.fill((128,255,255)) - centerText(sliderSurface, name, w/2, h/2, (0,0,0), font) + slider_surface.fill((128,255,255)) + center_text(slider_surface, name, w / 2, h / 2, (0, 0, 0), font) - screen.blit(sliderSurface,(x,y)) + screen.blit(slider_surface,(x,y)) - def click(self): - self.setting = (self.setting+1)%len(self.names) + def click(self) -> None: + self.setting = (self.setting + 1)%len(self.names) self.timeOfLastClick = time.time() self.func(self) \ No newline at end of file diff --git a/jes_creature.py b/jes_creature.py index aa72e5e..af82df2 100644 --- a/jes_creature.py +++ b/jes_creature.py @@ -1,74 +1,74 @@ import pygame -from utils import arrayLerp, dist_to_text, speciesToColor, listLerp, lerp -from jes_shapes import drawRect, drawTextRect, centerText, drawClock +from pygame import Surface + +from enums import Color +from utils import array_lerp, dist_to_text, species_to_color, list_lerp, lerp +from jes_shapes import drawRect, drawTextRect, center_text, draw_clock import numpy as np import math -from jes_species_info import SpeciesInfo import random class Creature: - def __init__(self,d,pIDNumber,parent_species,_sim,_ui): + def __init__(self, d, p_id_number, parent_species, _sim, _ui) -> None: self.dna = d self.calmState = None self.icons = [None]*2 - self.iconCoor = None - self.IDNumber = pIDNumber + self.icon_coor = None + self.IDNumber = p_id_number self.fitness = None self.rank = None self.living = True - self.species = self.getSpecies(parent_species) + self.species = self.get_species(parent_species) self.sim = _sim self.ui = _ui self.codonWithChange = None - def getSpecies(self, parent_species): + def get_species(self, parent_species): if parent_species == -1: return self.IDNumber else: return parent_species - def drawCell(self,surface,nodeState,frame,transform,x,y): - tx,ty,s = transform - color = self.traitsToColor(self.dna,x,y,frame) - points = [None]*4 + def draw_cell(self, surface, node_state, frame, transform, x, y) -> None: + tx, ty, s = transform + color = self.traits_to_color(self.dna, x, y, frame) + points = [None] * 4 for p in range(4): px = x if p == 1 or p == 2: px += 1 py = y+p//2 - points[p] = [tx+nodeState[px,py,0]*s,ty+nodeState[px,py,1]*s] - pygame.draw.polygon(surface,color,points) + points[p] = [tx + node_state[px,py,0] * s, ty + node_state[px,py,1] * s] + + pygame.draw.polygon(surface, color, points) - def drawEnvironment(self,surface,nodeState,frame,transform): - BLACK = (0,0,0) - WHITE = (255,255,255) - SIGN_COLOR = (150,100,50) + def draw_environment(self, surface, transform) -> None: #sky - drawRect(surface,transform,None,BLACK) + drawRect(surface, transform,None, Color.BLACK) #signs - font = self.ui.bigFont if transform[2] >= 50 else self.ui.smallFont + font = self.ui.big_font if transform[2] >= 50 else self.ui.small_font for meters in range(0,3000,100): - u = meters*self.sim.UNITS_PER_METER - drawRect(surface,transform,[u-0.2,-6,u+0.2,0],SIGN_COLOR) - drawTextRect(surface,transform,[u-1.5,-6.8,u+1.5,-5.4],SIGN_COLOR,WHITE,f"{meters}cm",font) + u = meters*self.sim.units_per_meter + drawRect(surface,transform,[u-0.2,-6,u+0.2,0], Color.SIGN) + drawTextRect(surface,transform,[u-1.5,-6.8,u+1.5,-5.4], Color.SIGN, Color.WHITE,f"{meters}cm",font) #ground - drawRect(surface,transform,[None,0,None,None],WHITE) + drawRect(surface,transform,[None,0,None,None], Color.WHITE) - def drawCreature(self, surface, nodeState, frame, transform, drawLabels, shouldDrawClock): - if drawLabels: - self.drawEnvironment(surface,nodeState,frame,transform) + def draw_creature(self, surface, node_state, frame, transform, draw_labels:bool, should_draw_clock: bool): + if draw_labels: + self.draw_environment(surface, transform) - cellSurface = pygame.Surface(surface.get_size(), pygame.SRCALPHA, 32) + cell_surface = pygame.Surface(surface.get_size(), pygame.SRCALPHA, 32) for x in range(self.sim.CW): for y in range(self.sim.CH): - self.drawCell(cellSurface,nodeState,frame,transform,x,y) - surface.blit(cellSurface,(0,0)) + self.draw_cell(cell_surface, node_state, frame, transform, x, y) + surface.blit(cell_surface,(0,0)) - if drawLabels: + if draw_labels: tx,ty,s = transform - avg_x = np.mean(nodeState[:,:,0],axis=(0,1)) + avg_x = np.mean(node_state[:, :, 0], axis=(0, 1)) lx = tx+avg_x*s ly = 20 lw = 100 @@ -76,34 +76,36 @@ def drawCreature(self, surface, nodeState, frame, transform, drawLabels, shouldD ar = 15 pygame.draw.rect(surface, (255,0,0),(lx-lw/2,ly,lw,lh)) pygame.draw.polygon(surface,(255,0,0),((lx,ly+lh+ar),(lx-ar,ly+lh),(lx+ar,ly+lh))) - centerText(surface, f"{dist_to_text(avg_x,True,self.sim.UNITS_PER_METER)}", lx, ly+18, self.ui.WHITE, self.ui.smallFont) + center_text(surface, f"{dist_to_text(avg_x, True, self.sim.units_per_meter)}", lx, ly + 18, Color.WHITE, self.ui.small_font) ratio = 1-frame/self.sim.trial_time - if shouldDrawClock: - drawClock(surface,[40,40,32],ratio,str(math.ceil(ratio*self.sim.trial_time/self.ui.FPS)),self.ui.smallFont) + + if should_draw_clock: + draw_clock(surface, [40, 40, 32], ratio, str(math.ceil(ratio * self.sim.trial_time / self.ui.fps)), self.ui.small_font) - def drawIcon(self, ICON_DIM, BG_COLOR, BEAT_FADE_TIME): - icon = pygame.Surface(ICON_DIM, pygame.SRCALPHA, 32) - icon.fill(BG_COLOR) - transform = [ICON_DIM[0]/2,ICON_DIM[0]/(self.sim.CW+2),ICON_DIM[0]/(self.sim.CH+2.85)] - self.drawCreature(icon,self.calmState,BEAT_FADE_TIME,transform,False,False) - R = ICON_DIM[0]*0.09 - R2 = ICON_DIM[0]*0.12 - pygame.draw.circle(icon,speciesToColor(self.species, self.ui),(ICON_DIM[0]-R2,R2),R) + def draw_icon(self, icon_dim, bg_color, beat_fade_time: int) -> Surface: + icon: Surface = pygame.Surface(icon_dim, pygame.SRCALPHA, 32) + icon.fill(bg_color) + transform = [icon_dim[0] / 2, icon_dim[0] / (self.sim.CW + 2), icon_dim[0] / (self.sim.CH + 2.85)] + self.draw_creature(icon, self.calmState, beat_fade_time, transform, False, False) + r = icon_dim[0] * 0.09 + r2 = icon_dim[0] * 0.12 + pygame.draw.circle(icon, species_to_color(self.species, self.ui), (icon_dim[0] - r2, r2), r) + return icon - def saveCalmState(self, arr): + def save_calm_state(self, arr): self.calmState = arr - def getMutatedDNA(self, sim): + def get_mutated_dna(self, sim): mutation = np.clip(np.random.normal(0.0, 1.0, self.dna.shape[0]),-99,99) result = self.dna + sim.mutation_rate*mutation - newSpecies = self.species + new_species = self.species big_mut_loc = 0 if random.uniform(0,1) < self.sim.big_mutation_rate: # do a big mutation - newSpecies = sim.species_count + new_species = sim.species_count sim.species_count += 1 cell_x = random.randint(0,self.sim.CW-1) cell_y = random.randint(0,self.sim.CH-1) @@ -120,19 +122,20 @@ def getMutatedDNA(self, sim): if i == 2 and result[big_mut_loc+i] < 0.5: result[big_mut_loc+i] = 0.5 - return result, newSpecies, big_mut_loc + return result, new_species, big_mut_loc - def traitsToColor(self, dna, x, y, frame): - beat = self.sim.frameToBeat(frame) - beat_prev = (beat+self.sim.beats_per_cycle-1)%self.sim.beats_per_cycle - prog = self.sim.frameToBeatFade(frame) + def traits_to_color(self, dna, x, y, frame): + beat = self.sim.frame_to_beat(frame) + beat_prev = (beat+ self.sim.beats_per_cycle - 1) % self.sim.beats_per_cycle + prog = self.sim.frame_to_beat_fade(frame) - locationIndex = x*self.sim.CH+y - DNAIndex = (locationIndex*self.sim.beats_per_cycle+beat)*self.sim.traits_per_box - DNAIndex_prev = (locationIndex*self.sim.beats_per_cycle+beat_prev)*self.sim.traits_per_box - traits = dna[DNAIndex:DNAIndex+self.sim.traits_per_box] - traits_prev = dna[DNAIndex_prev:DNAIndex_prev+self.sim.traits_per_box] - traits = arrayLerp(traits_prev,traits,prog) + location_index = x * self.sim.CH + y + dna_index = (location_index*self.sim.beats_per_cycle+beat)*self.sim.traits_per_box + dna_index_prev = (location_index*self.sim.beats_per_cycle+beat_prev)*self.sim.traits_per_box + + traits = dna[dna_index:dna_index+self.sim.traits_per_box] + traits_prev = dna[dna_index_prev:dna_index_prev+self.sim.traits_per_box] + traits = array_lerp(traits_prev, traits, prog) red = min(max(int(128+traits[0]*128),0),255) green = min(max(int(128+traits[1]*128),0),255) @@ -140,13 +143,15 @@ def traitsToColor(self, dna, x, y, frame): color_result = (red,green,255,alpha) if self.codonWithChange is not None: - nextGreen = 0 - if self.codonWithChange >= DNAIndex and self.codonWithChange < DNAIndex+self.sim.traits_per_box: - nextGreen = 1 - prevGreen = 0 - if self.codonWithChange >= DNAIndex_prev and self.codonWithChange < DNAIndex_prev+self.sim.traits_per_box: - prevGreen = 1 - green_ness = lerp(prevGreen,nextGreen,prog) - color_result = listLerp(color_result,(0,255,0,255),green_ness) + next_green = 0 + if dna_index <= self.codonWithChange < dna_index+self.sim.traits_per_box: + next_green = 1 + + prev_green = 0 + if dna_index_prev <= self.codonWithChange < dna_index_prev+self.sim.traits_per_box: + prev_green = 1 + + green_ness = lerp(prev_green,next_green,prog) + color_result = list_lerp(color_result, (0, 255, 0, 255), green_ness) return color_result \ No newline at end of file diff --git a/jes_dataviz.py b/jes_dataviz.py index 95828de..99ca36a 100644 --- a/jes_dataviz.py +++ b/jes_dataviz.py @@ -1,113 +1,119 @@ import numpy as np -from utils import getUnit, dist_to_text, species_to_name, speciesToColor -from jes_shapes import centerText, rightText, alignText, drawArrow, drawSpeciesCircle + +from enums import Color +from utils import get_unit, dist_to_text, species_to_name, species_to_color +from jes_shapes import right_text, align_text, draw_species_circle import math import pygame -import random import bisect -def drawAllGraphs(sim, ui): - drawLineGraph(sim.percentiles, ui.graph, [70,0,30,30], sim.UNITS_PER_METER, ui.smallFont) - drawSAC(sim.species_pops, ui.sac, [70,0], ui) - drawGeneGraph(sim.species_info, sim.prominent_species, ui.gene_graph, sim, ui, ui.tinyFont) -def drawLineGraph(data,graph,margins,u,font): - BLACK = (0,0,0) - GRAY25 = (70,70,70) - GRAY50 = (128,128,128) - WHITE = (255,255,255) - RED = (255,0,0) +# BLACK = (0,0,0) +# GRAY25 = (70,70,70) +# GRAY50 = (128,128,128) +# WHITE = (255,255,255) +# RED = (255,0,0) +# GREEN = (0,255,0) + +def draw_all_graphs(sim, ui): + draw_line_graph(sim.percentiles, ui.graph, [70, 0, 30, 30], sim.units_per_meter, ui.small_font) + draw_sac(sim.species_pops, ui.sac, [70, 0], ui) + draw_gene_graph(sim.species_info, sim.prominent_species, ui.gene_graph, sim, ui, ui.tiny_font) + +def draw_line_graph(data, graph, margins, u, font) -> None: - graph.fill(BLACK) - W = graph.get_width()-margins[0]-margins[1] - H = graph.get_height()-margins[2]-margins[3] - LEFT = margins[0] - RIGHT = graph.get_width()-margins[1] - BOTTOM = graph.get_height()-margins[3] + graph.fill(Color.BLACK) + w = graph.get_width()-margins[0]-margins[1] + h = graph.get_height()-margins[2]-margins[3] + left = margins[0] + right = graph.get_width()-margins[1] + bottom = graph.get_height()-margins[3] - minVal = np.amin(data) - maxVal = np.amax(data) - unit = getUnit((maxVal-minVal)/u)*u - tick = math.floor(minVal/unit)*unit-unit - while tick <= maxVal+unit: - ay = BOTTOM-H*(tick-minVal)/(maxVal-minVal) - pygame.draw.line(graph, GRAY25, (LEFT, ay), (RIGHT, ay), width=1) - rightText(graph, dist_to_text(tick, False, u), LEFT-7, ay, GRAY50, font) + min_val = np.amin(data) + max_val = np.amax(data) + unit = get_unit((max_val - min_val) / u) * u + tick = math.floor(min_val/unit)*unit-unit + while tick <= max_val+unit: + ay = bottom-h*(tick-min_val)/(max_val-min_val) + pygame.draw.line(graph, Color.GRAY25, (left, ay), (right, ay), width=1) + right_text(graph, dist_to_text(tick, False, u), left - 7, ay, Color.GRAY50, font) tick += unit - toShow = [0,1,2,3,4,5,6,7,8,9,10,20,30,40,50,60,70,80,90,91,92,93,94,95,96,97,98,99,100] - LEN = len(data) - for g in range(LEN): - for p in toShow: - prevVal = 0 if g == 0 else data[g-1][p] - nextVal = data[g][p] + to_show = [0,1,2,3,4,5,6,7,8,9,10,20,30,40,50,60,70,80,90,91,92,93,94,95,96,97,98,99,100] + data_len = len(data) + for g in range(data_len): + for p in to_show: + prev_val = 0 if g == 0 else data[g-1][p] + next_val = data[g][p] - x1 = LEFT+(g/LEN)*W - x2 = LEFT+((g+1)/LEN)*W - y1 = BOTTOM-H*(prevVal-minVal)/(maxVal-minVal) - y2 = BOTTOM-H*(nextVal-minVal)/(maxVal-minVal) + x1 = left+(g/data_len)*w + x2 = left+((g+1)/data_len)*w + y1 = bottom-h*(prev_val-min_val)/(max_val-min_val) + y2 = bottom-h*(next_val-min_val)/(max_val-min_val) - IMPORTANT = (p%10 == 0) - thickness = 2 if IMPORTANT else 1 - color = WHITE if IMPORTANT else GRAY50 + important: bool = (p%10 == 0) + thickness = 2 if important else 1 + color = Color.WHITE if important else Color.GRAY50 if p == 50: - color = RED + color = Color.RED thickness = 3 + pygame.draw.line(graph, color, (x1, y1), (x2, y2), width=thickness) -def drawSAC(data,sac,margins,ui): +def draw_sac(data, sac, margins, ui) -> None: sac.fill((0,0,0)) for g in range(len(data)): - scanDownTrapezoids(data, g, sac, margins, ui) + scan_down_trapezoids(data, g, sac, margins, ui) -def scanDownTrapezoids(data, g, sac, margins, ui): - W = sac.get_width()-margins[0]-margins[1] - H = sac.get_height() - LEN = len(data) - LEFT = margins[0] - RIGHT = sac.get_width()-margins[1] - x1 = LEFT+(g/LEN)*W - x2 = LEFT+((g+1)/LEN)*W +def scan_down_trapezoids(data, g, sac, margins, ui) -> None: + w = sac.get_width()-margins[0]-margins[1] + h = sac.get_height() + len_data = len(data) + left = margins[0] + + x1 = left+(g/len_data)*w + x2 = left+((g+1)/len_data)*w keys = sorted(list(data[g].keys())) c_count = data[g][keys[-1]][2] # ending index of the last entry - FAC = H/c_count + fac = h/c_count if g == 0: for sp in data[g].keys(): pop = data[g][sp] - points = [[x1,H/2],[x1,H/2],[x2,H-pop[1]*FAC],[x2,H-pop[2]*FAC]] - pygame.draw.polygon(sac,speciesToColor(sp, ui),points) + points = [[x1,h/2],[x1,h/2],[x2,h-pop[1]*fac],[x2,h-pop[2]*fac]] + pygame.draw.polygon(sac, species_to_color(sp, ui), points) else: - trapezoidHelper(sac, data, g, g-1, 0, c_count, x1, x2, FAC, 0, ui) + trapezoid_helper(sac, data, g, g - 1, 0, c_count, x1, x2, fac, 0, ui) -def getRangeEvenIfNone(dicty, key): +def get_range_even_if_none(dicty, key): keys = sorted(list(dicty.keys())) if key in keys: return dicty[key] else: - n = bisect.bisect(keys, key+0.5) + n = bisect.bisect(keys, key + 0.5) if n >= len(keys): val = dicty[keys[n-1]][2] else: val = dicty[keys[n]][1] - return [0,val,val] + return [0, val, val] -def trapezoidHelper(sac, data, g1, g2, i_start, i_end, x1, x2, FAC, level, ui): - pop2 = [0,0,0] - H = sac.get_height() +def trapezoid_helper(sac, data, g1, g2, i_start, i_end, x1, x2, fac, level, ui) -> None: + pop2 = [0, 0, 0] + h = sac.get_height() for sp in data[g1].keys(): pop1 = data[g1][sp] if level == 0 and pop1[1] != pop2[2]: #there was a gap - trapezoidHelper(sac, data, g2, g1, pop2[2], pop1[1], x2, x1, FAC, 1, ui) - pop2 = getRangeEvenIfNone(data[g2],sp) - points = [[x1,H-pop2[1]*FAC],[x1,H-pop2[2]*FAC],[x2,H-pop1[2]*FAC],[x2,H-pop1[1]*FAC]] - pygame.draw.polygon(sac,speciesToColor(sp, ui),points) + trapezoid_helper(sac, data, g2, g1, pop2[2], pop1[1], x2, x1, fac, 1, ui) + + pop2 = get_range_even_if_none(data[g2], sp) + points = [[x1, h - pop2[1] * fac], [x1, h - pop2[2] * fac], [x2, h - pop1[2] * fac], [x2, h - pop1[1] * fac]] + pygame.draw.polygon(sac, species_to_color(sp, ui), points) -def drawGeneGraph(species_info, ps, gg, sim, ui, font): # ps = prominent_species - R = ui.GENEALOGY_COOR[4] - H = gg.get_height()-R*2 - W = gg.get_width()-R*2 +def draw_gene_graph(species_info, ps, gg, sim, ui, font) -> None: # ps = prominent_species + r = ui.genealogy_coor[4] + h = gg.get_height()-r*2 + w = gg.get_width()-r*2 gg.fill((0,0,0)) if len(sim.creatures) == 0: return @@ -115,92 +121,86 @@ def drawGeneGraph(species_info, ps, gg, sim, ui, font): # ps = prominent_specie for level in range(len(ps)): for i in range(len(ps[level])): s = ps[level][i] - x = (i+0.5)/(len(ps[level]))*W+R - y = (level)/(len(ps)-0.8)*H+R + x = (i+0.5)/(len(ps[level])) * w + r + y = level / (len(ps)-0.8) * h + r species_info[s].coor = (x,y) for level in range(len(ps)): for i in range(len(ps[level])): s = ps[level][i] - drawSpeciesCircle(gg,s,species_info[s].coor,R,sim,species_info,font,True,ui) + draw_species_circle(gg, s, species_info[s].coor, r, sim, species_info, font, True, ui) -def displayAllGraphs(screen, sim, ui): - WHITE = (255,255,255) - blitGraphsandMarks(screen, sim, ui) - blitGGandMarks(screen, sim, ui) +def display_all_graphs(screen, sim, ui) -> None: + blit_graphsand_marks(screen, sim, ui) + blit_g_gand_marks(screen, sim, ui) if sim.last_gen_run_time >= 0: - rightText(screen, f"Last gen runtime: {sim.last_gen_run_time:.3f}s", 1200,28, WHITE, ui.smallFont) + right_text(screen, f"Last gen runtime: {sim.last_gen_run_time:.3f}s", 1200, 28, Color.WHITE, ui.small_font) -def blitGraphsandMarks(screen, sim, ui): - screen.blit(ui.graph,ui.GRAPH_COOR[0:2]) - screen.blit(ui.sac,ui.SAC_COOR[0:2]) - GREEN = (0,255,0) - WHITE = (255,255,255) - a = int(ui.genSlider.val) - b = int(ui.genSlider.val_max) +def blit_graphsand_marks(screen, sim, ui): + screen.blit(ui.graph, ui.graph_coor[0:2]) + screen.blit(ui.sac, ui.sac_coor[0:2]) + + a = int(ui.gen_slider.val) + b = int(ui.gen_slider.val_max) a2 = min(a,b-1) if b == 0: return if a < b: frac = (a+1)/b - lineX = ui.SAC_COOR[0]+70+(ui.graph.get_width()-70)*frac - lineYs = [[50,550],[560,860]] - for lineY in lineYs: - pygame.draw.line(screen, GREEN, (lineX, lineY[0]), (lineX, lineY[1]), width=2) + line_x = ui.sac_coor[0] + 70 + (ui.graph.get_width() - 70) * frac + line_ys = [[50,550],[560,860]] + for lineY in line_ys: + pygame.draw.line(screen, Color.GREEN, (line_x, lineY[0]), (line_x, lineY[1]), width=2) frac = (a2+1)/b - lineX = ui.SAC_COOR[0]+70+(ui.graph.get_width()-70)*frac + line_x = ui.sac_coor[0] + 70 + (ui.graph.get_width() - 70) * frac median = sim.percentiles[a2][50] - rightText(screen, f"Median: {dist_to_text(median, True, sim.UNITS_PER_METER)}", 1800,28, WHITE, ui.smallFont) + right_text(screen, f"Median: {dist_to_text(median, True, sim.units_per_meter)}", 1800, 28, Color.WHITE, ui.small_font) - top_species = getTopSpecies(sim, a2) + top_species = get_top_species(sim, a2) for sp in sim.species_pops[a2].keys(): pop = sim.species_pops[a2][sp] - if pop[0] >= sim.c_count*sim.S_VISIBLE: - speciesI = (pop[1]+pop[2])/2 - speciesY = 560+300*(1-speciesI/sim.c_count) + if pop[0] >= sim.creature_count*sim.S_VISIBLE: + species_i = (pop[1]+pop[2])/2 + species_y = 560+300*(1 - species_i / sim.creature_count) name = species_to_name(sp, ui) - color = speciesToColor(sp, ui) - OUTLINE = ui.WHITE if sp == top_species else None - alignText(screen, f"{name}: {pop[0]}", lineX+10, speciesY, color, ui.smallFont, 0.0, [ui.BLACK,OUTLINE]) + color = species_to_color(sp, ui) + outline = Color.WHITE if sp == top_species else None + align_text(screen, f"{name}: {pop[0]}", line_x + 10, species_y, color, ui.small_font, 0.0, [Color.BLACK, outline]) -def blitGGandMarks(screen, sim, ui): - screen.blit(ui.gene_graph,ui.GENEALOGY_COOR[0:2]) - R = 42 - a = int(ui.genSlider.val) - b = int(ui.genSlider.val_max) +def blit_g_gand_marks(screen, sim, ui): + screen.blit(ui.gene_graph, ui.genealogy_coor[0:2]) + r = 42 + a = int(ui.gen_slider.val) + b = int(ui.gen_slider.val_max) a2 = min(a,b-1) if b == 0: return - top_species = getTopSpecies(sim, a2) + top_species = get_top_species(sim, a2) for sp in sim.species_pops[a2].keys(): info = sim.species_info[sp] if not info.prominent: continue - pop = sim.species_pops[a2][sp][0] + # pop = sim.species_pops[a2][sp][0] circle_count = 2 if sp == top_species else 1 - cx = info.coor[0]+ui.GENEALOGY_COOR[0] - cy = info.coor[1]+ui.GENEALOGY_COOR[1] + cx = info.coor[0] + ui.genealogy_coor[0] + cy = info.coor[1] + ui.genealogy_coor[1] for c in range(circle_count): - pygame.draw.circle(screen, ui.WHITE, (cx,cy), R+3+6*c, 3) + pygame.draw.circle(screen, Color.WHITE, (cx,cy), r+3+6*c, 3) if ui.species_storage is not None: sp = ui.species_storage if sp in sim.species_pops[a2]: circle_count = 2 if sp == top_species else 1 for c in range(circle_count): - pygame.draw.circle(screen, ui.WHITE, ui.storage_coor, R+3+6*c, 3) + pygame.draw.circle(screen, Color.WHITE, ui.storage_coor, r+3+6*c, 3) - - - - -def getTopSpecies(sim, g): +def get_top_species(sim, g): data = sim.species_pops[g] return max(data, key=data.get) \ No newline at end of file diff --git a/jes_dump.py b/jes_dump.py index 52d6b23..4b13624 100644 --- a/jes_dump.py +++ b/jes_dump.py @@ -119,7 +119,7 @@ def DUDtoPRESTRI(DUD, TOTAL): def doSpeciesInfo(self,nsp,best_of_each_species): p1 = 0 # 'p' stands for 'pointer' - while p1 < self.c_count: + while p1 < self.creature_count: s = data[p1] p2 = bisect.bisect(data, s+0.5) pop = p2-p1 @@ -129,7 +129,7 @@ def doSpeciesInfo(self,nsp,best_of_each_species): if pop > sp.apex_pop: # This species reached its highest population sp.apex_pop = pop sp.reps[2] = best_of_each_species[s] # apex representative - if pop > self.c_count*0.10 and not sp.prominent: #prominent threshold + if pop > self.creature_count*0.10 and not sp.prominent: #prominent threshold sp.becomeProminent() sp.reps[3] = best_of_each_species[s] # most-recent representative @@ -153,7 +153,7 @@ def doSpeciesInfo(self,nsp,best_of_each_species): p1 = p2 = 0 record = 0 recordHolder = -1 - while p1 < sim.c_count: + while p1 < sim.creature_count: s = data[p1] p2 = bisect.bisect(data, s+0.5) pop = p2-p1 @@ -181,17 +181,17 @@ def doSpeciesInfo(self,nsp,best_of_each_species): p1 = p2 = 0 - while p1 < sim.c_count: + while p1 < sim.creature_count: s = sim.species_pops[a2,p1] p2 = bisect.bisect(sim.species_pops[a2], s+0.5) pop = p2-p1 - if pop >= sim.c_count*sim.S_VISIBLE: + if pop >= sim.creature_count*sim.S_VISIBLE: speciesI = (p1+p2)/2 - speciesY = 560+300*(1-speciesI/sim.c_count) + speciesY = 560+300*(1 - speciesI / sim.creature_count) name = species_to_name(s) color = speciesToColor(s) OUTLINE = ui.WHITE if s == top_species else None - alignText(screen, f"{name}: {int(pop)}", lineX+10, speciesY, color, ui.smallFont, 0.0, [ui.BLACK,OUTLINE]) + alignText(screen, f"{name}: {int(pop)}", lineX + 10, speciesY, color, ui.small_font, 0.0, [ui.BLACK, OUTLINE]) p1 = p2 @@ -222,7 +222,7 @@ def doSpeciesInfo(self,nsp,best_of_each_species): p1 = p2 = 0 - while p1 < sim.c_count: + while p1 < sim.creature_count: s = sim.species_pops[a2,p1] p2 = bisect.bisect(sim.species_pops[a2], s+0.5) info = sim.species_info[s] @@ -232,8 +232,8 @@ def doSpeciesInfo(self,nsp,best_of_each_species): if s == top_species: circle_count += 1 for c in range(circle_count): - cx = info.coor[0]+ui.GENEALOGY_COOR[0] - cy = info.coor[1]+ui.GENEALOGY_COOR[1] + cx = info.coor[0]+ui.genealogy_coor[0] + cy = info.coor[1]+ui.genealogy_coor[1] pygame.draw.circle(screen, ui.WHITE, (cx,cy), R+3+6*c, 3) p1 = p2 diff --git a/jes_shapes.py b/jes_shapes.py index c58cb19..8dd1ca3 100644 --- a/jes_shapes.py +++ b/jes_shapes.py @@ -1,7 +1,9 @@ import pygame import math import copy -from utils import lerp, speciesToColor, species_to_name + +from enums import Color +from utils import lerp, species_to_color, species_to_name import numpy as np import time @@ -13,7 +15,7 @@ def drawTextRect(surface,transform,coor,color1,color2,text,font): centerY = (y1+y2)/2 text_x = centerX*s+tx text_y = centerY*s+ty - centerText(surface, text, text_x, text_y, color2, font) + center_text(surface, text, text_x, text_y, color2, font) def drawRect(surface,transform,coor,color): W = surface.get_width() @@ -40,7 +42,7 @@ def drawRingLight(w, h, thickness): pygame.draw.rect(ringlight,BRIGHT,(w-thickness,0,thickness,h)) return ringlight -def drawX(iconCoor, I, color, screen): +def draw_x(iconCoor, I, color, screen): for L in range(2): i1 = I*0.02 i2 = I*0.06+3 @@ -52,51 +54,51 @@ def drawX(iconCoor, I, color, screen): P[1] += iconCoor[1] pygame.draw.polygon(screen,color,points) -def centerText(theScreen, stri, x, y, color, font): - alignText(theScreen, stri, x, y, color, font, 0.5, None) +def center_text(theScreen, stri, x, y, color, font) -> None: + align_text(theScreen, stri, x, y, color, font, 0.5, None) -def rightText(theScreen, stri, x, y, color, font): - alignText(theScreen, stri, x, y, color, font, 1.0, None) +def right_text(theScreen, stri, x, y, color, font) -> None: + align_text(theScreen, stri, x, y, color, font, 1.0, None) def expand(coor, amount): return [coor[0]-amount, coor[1]-amount, coor[2]+amount*2, coor[3]+amount*2] -def alignText(theScreen, stri, x, y, color, font, align, bg_color): - textSurface = font.render(stri, False, color) - coor = (x-textSurface.get_width()*align,y-textSurface.get_height()/2) +def align_text(the_screen, stri, x, y, color, font, align, bg_color) -> None: + text_surface = font.render(stri, False, color) + coor = (x-text_surface.get_width()*align,y-text_surface.get_height()/2) if bg_color is not None: - coor = (coor[0]-4,coor[1],textSurface.get_width()+8,textSurface.get_height()) + coor = (coor[0]-4,coor[1],text_surface.get_width()+8,text_surface.get_height()) if bg_color[1] is not None: - pygame.draw.rect(theScreen,bg_color[1],expand(coor,2)) - pygame.draw.rect(theScreen,bg_color[0],coor) - theScreen.blit(textSurface, coor) + pygame.draw.rect(the_screen, bg_color[1], expand(coor, 2)) + pygame.draw.rect(the_screen, bg_color[0], coor) + the_screen.blit(text_surface, coor) -def drawClock(surface,coor,the_ratio,text,font): - WHITE = (255,255,255) +def draw_clock(surface, coor, the_ratio, text, font) -> None: GRAYISH = (115,125,160) - BLACK = (0,0,0) + x,y,r = coor - P = 30 + P = 30 for p in range(P): ratio1 = p/P ratio2 = (p+1)/P ang1 = (ratio1-0.25)*2*math.pi ang2 = (ratio2-0.25)*2*math.pi points = [[x,y],[x+r*math.cos(ang1),y+r*math.sin(ang1)],[x+r*math.cos(ang2),y+r*math.sin(ang2)]] - pygame.draw.polygon(surface,GRAYISH,points) + pygame.draw.polygon(surface, GRAYISH, points) if the_ratio > ratio2: - pygame.draw.polygon(surface,WHITE,points) + pygame.draw.polygon(surface, Color.WHITE, points) + elif the_ratio > ratio1: points2 = copy.deepcopy(points) prog = (the_ratio-ratio1)/(ratio2-ratio1) points2[2][0] = lerp(points[1][0],points[2][0],prog) points2[2][1] = lerp(points[1][1],points[2][1],prog) - pygame.draw.polygon(surface,WHITE,points2) + pygame.draw.polygon(surface, Color.WHITE,points2) - centerText(surface, text, x, y, BLACK, font) + center_text(surface, text, x, y, Color.BLACK, font) -def drawArrow(screen, _start, _end, margin, head, color): +def draw_arrow(screen, _start, _end, margin, head, color) -> None: start = np.array(_start) end = np.array(_end) total_dist = np.linalg.norm(start-end) @@ -113,22 +115,22 @@ def drawArrow(screen, _start, _end, margin, head, color): flare = [near_end[0]+math.cos(new_angle)*head, near_end[1]+math.sin(new_angle)*head] pygame.draw.line(screen, color, near_end, flare, width=2) -def drawSpeciesCircle(screen, s, coor, R, sim, species_info, font, shouldDrawArrow, ui): - color = speciesToColor(s, ui) +def draw_species_circle(screen, s, coor, R, sim, species_info, font, should_draw_arrow: bool, ui) -> None: + color = species_to_color(s, ui) name = species_to_name(s, ui) info = species_info[s] cx, cy = coor pygame.draw.circle(screen,color,coor,R) - centerText(screen, name, cx, cy-22, (0,0,0), font) + center_text(screen, name, cx, cy - 22, (0, 0, 0), font) - creature = sim.getCreatureWithID(info.reps[2]) + creature = sim.get_creature_with_id(info.reps[2]) tiny_icon = pygame.transform.scale(creature.icons[0], (50,50)) screen.blit(tiny_icon,(cx-25,cy-11)) - if shouldDrawArrow: - ancestorID = species_info[s].ancestorID - if ancestorID is None: - drawArrow(screen,(cx,-R*2),(cx,cy),R,R/2,color) + if should_draw_arrow: + ancestor_id = species_info[s].ancestorID + if ancestor_id is None: + draw_arrow(screen, (cx, -R * 2), (cx, cy), R, R / 2, color) else: - drawArrow(screen,species_info[ancestorID].coor,info.coor,R,R/2,color) \ No newline at end of file + draw_arrow(screen, species_info[ancestor_id].coor, info.coor, R, R / 2, color) \ No newline at end of file diff --git a/jes_sim.py b/jes_sim.py index e065a5e..e5351d1 100644 --- a/jes_sim.py +++ b/jes_sim.py @@ -1,151 +1,168 @@ import numpy as np -from utils import getDistanceArray, applyMuscles + +from enums import Color +from utils import apply_muscles from jes_creature import Creature from jes_species_info import SpeciesInfo -from jes_dataviz import drawAllGraphs +from jes_dataviz import draw_all_graphs import time import random class Sim: - def __init__(self, _c_count, _stabilization_time, _trial_time, _beat_time, - _beat_fade_time, _c_dim, _beats_per_cycle, _node_coor_count, - _y_clips, _ground_friction_coef, _gravity_acceleration_coef, - _calming_friction_coef, _typical_friction_coef, _muscle_coef, - _traits_per_box, _traits_extra, _mutation_rate, _big_mutation_rate, _UNITS_PER_METER): - self.c_count = _c_count #creature count - self.species_count = _c_count #species count - self.stabilization_time = _stabilization_time - self.trial_time = _trial_time - self.beat_time = _beat_time - self.beat_fade_time = _beat_fade_time - self.c_dim = _c_dim - self.CW, self.CH = self.c_dim - self.beats_per_cycle = _beats_per_cycle - self.node_coor_count = _node_coor_count - self.y_clips = _y_clips - self.ground_friction_coef = _ground_friction_coef - self.gravity_acceleration_coef = _gravity_acceleration_coef - self.calming_friction_coef = _calming_friction_coef - self.typical_friction_coef = _typical_friction_coef - self.muscle_coef = _muscle_coef - - self.traits_per_box = _traits_per_box - self.traits_extra = _traits_extra - self.trait_count = self.CW*self.CH*self.beats_per_cycle*self.traits_per_box+self.traits_extra - - self.mutation_rate = _mutation_rate - self.big_mutation_rate = _big_mutation_rate - - self.S_VISIBLE = 0.05 #what proportion of the population does a species need to appear on the SAC graph? - self.S_NOTABLE = 0.10 #what proportion of the population does a species need to appear in the genealogy? - self.HUNDRED = 100 # change this if you want to change the resolution of the percentile-tracking - self.UNITS_PER_METER = _UNITS_PER_METER + def __init__(self, creature_count: int, config: dict) -> None: + self._creature_count: int = creature_count # creature count + self.species_count: int = creature_count # species count + self.stabilization_time: int = config.get('stabilization_time') + self.trial_time: int = config.get('trial_time') + self.beat_time: int = config.get('beat_time') + self.beat_fade_time: int = config.get('beat_fade_time') + self.c_dim: list[int] = config.get('c_dim') + self.CW, self.CH = self.c_dim + self.beats_per_cycle: int = config.get('beats_per_cycle') + self.node_coor_count: int = config.get('node_coor_count') + self.y_clips: list[int] = config.get('y_clips') + self.ground_friction_coef: int = config.get('ground_friction_coef') + self.gravity_acceleration_coef: float = config.get('gravity_acceleration_coef') + self.calming_friction_coef: float = config.get('calming_friction_coef') + self.typical_friction_coef: float = config.get('typical_friction_coef') + self.muscle_coef: float = config.get('muscle_coef') + + self.traits_per_box: int = config.get('traits_per_box') + self.traits_extra: int = config.get('traits_extra') + self.trait_count = self.CW * self.CH * self.beats_per_cycle * self.traits_per_box + self.traits_extra + + self.mutation_rate: float = config.get('mutation_rate') + self.big_mutation_rate: float = config.get('big_mutation_rate') + + self.S_VISIBLE: float = 0.05 #what proportion of the population does a species need to appear on the SAC graph? + self.S_NOTABLE: float = 0.10 #what proportion of the population does a species need to appear in the genealogy? + self.HUNDRED: int = 100 # change this if you want to change the resolution of the percentile-tracking + self.units_per_meter: float = config.get('units_per_meter') self.creatures = None - self.rankings = np.zeros((0,self.c_count), dtype=int) - self.percentiles = np.zeros((0,self.HUNDRED+1)) - self.species_pops = [] - self.species_info = [] - self.prominent_species = [] + self.rankings = np.zeros((0,self.creature_count), dtype=int) + self.percentiles = np.zeros((0,self.HUNDRED + 1)) + self.species_pops: list = [] + self.species_info: list = [] + self.prominent_species: list = [] self.ui = None - self.last_gen_run_time = -1 + self.last_gen_run_time: int = -1 - def initializeUniverse(self): - self.creatures = [[None]*self.c_count] - for c in range(self.c_count): - self.creatures[0][c] = self.createNewCreature(c) - self.species_info.append(SpeciesInfo(self,self.creatures[0][c], None)) + def initialize_universe(self): + self.creatures = [[None] * self.creature_count] + + for c in range(self.creature_count): + self.creatures[0][c] = self.create_new_creature(creature_id=c) + self.species_info.append(SpeciesInfo(self, self.creatures[0][c], None)) # We want to make sure that all creatures, even in their # initial state, are in calm equilibrium. They shouldn't # be holding onto potential energy (e.g. compressed springs) - self.getCalmStates(0,0,self.c_count,self.stabilization_time,True) #Calm the creatures down so no potential energy is stored + self.get_calm_states(0, 0, self.creature_count, self.stabilization_time) #Calm the creatures down so no potential energy is stored - for c in range(self.c_count): + for c in range(self.creature_count): for i in range(2): - self.creatures[0][c].icons[i] = self.creatures[0][c].drawIcon(self.ui.ICON_DIM[i], self.ui.MOSAIC_COLOR, self.beat_fade_time) + self.creatures[0][c].icons[i] = self.creatures[0][c].draw_icon(self.ui.icon_dim[i], Color.MOSAIC, self.beat_fade_time) - self.ui.drawCreatureMosaic(0) - - def createNewCreature(self, idNumber): + self.ui.draw_creature_mosaic(0) + + @property + def creature_count(self): + return self._creature_count + + @creature_count.setter + def creature_count(self, value: int): + self._creature_count = value + + def create_new_creature(self, creature_id) -> Creature: dna = np.clip(np.random.normal(0.0, 1.0, self.trait_count),-3,3) - return Creature(dna, idNumber, -1, self, self.ui) + return Creature(dna, creature_id, -1, self, self.ui) - def getCalmStates(self, gen, startIndex, endIndex, frameCount, calmingRun): - param = self.simulateImport(gen, startIndex, endIndex, False) - nodeCoor, muscles, _ = self.simulateRun(param, frameCount, True) - for c in range(self.c_count): - self.creatures[gen][c].saveCalmState(nodeCoor[c]) + def get_calm_states(self, gen, start_index, end_index, frame_count) -> None: + param = self.simulate_import(gen, start_index, end_index, False) + node_coor, muscles, _ = self.simulate_run(param, frame_count, True) + + for c in range(self.creature_count): + self.creatures[gen][c].save_calm_state(node_coor[c]) - def getStartingNodeCoor(self, gen, startIndex, endIndex, fromCalmState): - COUNT = endIndex-startIndex - n = np.zeros((COUNT,self.CH+1,self.CW+1,self.node_coor_count)) - if not fromCalmState or self.creatures[gen][0].calmState is None: + def get_starting_node_coor(self, gen, start_index, end_index, from_calm_state): + count = end_index - start_index + n = np.zeros((count,self.CH+1,self.CW+1,self.node_coor_count)) + + if not from_calm_state or self.creatures[gen][0].calmState is None: # create grid of nodes along perfect gridlines - coorGrid = np.mgrid[0:self.CW+1,0:self.CH+1] - coorGrid = np.swapaxes(np.swapaxes(coorGrid,0,1),1,2) - n[:,:,:,0:2] = coorGrid + coor_grid = np.mgrid[0:self.CW+1,0:self.CH+1] + coor_grid = np.swapaxes(np.swapaxes(coor_grid,0,1),1,2) + n[:,:,:,0:2] = coor_grid + else: # load calm state into nodeCoor - for c in range(startIndex,endIndex): - n[c-startIndex,:,:,:] = self.creatures[gen][c].calmState - n[c-startIndex,:,:,1] -= self.CH # lift the creature above ground level + for c in range(start_index, end_index): + n[c - start_index, :, :, :] = self.creatures[gen][c].calmState + n[c - start_index, :, :, 1] -= self.CH # lift the creature above ground level + return n - def getMuscleArray(self, gen, startIndex, endIndex): - COUNT = endIndex-startIndex - m = np.zeros((COUNT,self.CH,self.CW,self.beats_per_cycle,self.traits_per_box+1)) # add one trait for diagonal length. - DNA_LEN = self.CH*self.CW*self.beats_per_cycle*self.traits_per_box - for c in range(startIndex,endIndex): - dna = self.creatures[gen][c].dna[0:DNA_LEN].reshape(self.CH,self.CW,self.beats_per_cycle,self.traits_per_box) - m[c-startIndex,:,:,:,:self.traits_per_box] = 1.0+(dna)/3.0 - m[:,:,:,:,3] = np.sqrt(np.square(m[:,:,:,:,0])+np.square(m[:,:,:,:,1])) # Set diagonal tendons + def get_muscle_array(self, gen, start_index, end_index): + count = end_index - start_index + m = np.zeros((count, self.CH, self.CW, self.beats_per_cycle, self.traits_per_box + 1)) # add one trait for diagonal length. + dna_len = self.CH * self.CW * self.beats_per_cycle * self.traits_per_box + + for c in range(start_index, end_index): + dna = self.creatures[gen][c].dna[0:dna_len].reshape(self.CH,self.CW,self.beats_per_cycle,self.traits_per_box) + m[c - start_index, :, :, :, :self.traits_per_box] = 1.0 + dna / 3.0 + + m[:,:,:,:,3] = np.sqrt(np.square(m[:,:,:,:,0]) + np.square(m[:,:,:,:,1])) # Set diagonal tendons + return m - def simulateImport(self, gen, startIndex, endIndex, fromCalmState): - nodeCoor = self.getStartingNodeCoor(gen,startIndex,endIndex,fromCalmState) - muscles = self.getMuscleArray(gen,startIndex,endIndex) - currentFrame = 0 - return nodeCoor, muscles, currentFrame + def simulate_import(self, gen, start_index, end_index, from_calm_state): + node_coor = self.get_starting_node_coor(gen, start_index, end_index, from_calm_state) + muscles = self.get_muscle_array(gen, start_index, end_index) + current_frame: int = 0 - def frameToBeat(self, f): - return (f//self.beat_time)%self.beats_per_cycle + return node_coor, muscles, current_frame + + def frame_to_beat(self, f): + return (f//self.beat_time) % self.beats_per_cycle - def frameToBeatFade(self, f): - prog = f%self.beat_time - return min(prog/self.beat_fade_time,1) + def frame_to_beat_fade(self, f): + prog = f % self.beat_time + return min(prog / self.beat_fade_time, 1) - def simulateRun(self, param, frameCount, calmingRun): - nodeCoor, muscles, startCurrentFrame = param - friction = self.calming_friction_coef if calmingRun else self.typical_friction_coef - CEILING_Y = self.y_clips[0] - FLOOR_Y = self.y_clips[1] + def simulate_run(self, param, frame_count, calming_run): + node_coor, muscles, start_current_frame = param + friction = self.calming_friction_coef if calming_run else self.typical_friction_coef + ceiling_y = self.y_clips[0] + floor_y = self.y_clips[1] - for f in range(frameCount): - currentFrame = startCurrentFrame+f + for f in range(frame_count): + current_frame = start_current_frame+f beat = 0 - if not calmingRun: - beat = self.frameToBeat(currentFrame) - nodeCoor[:,:,:,3] += self.gravity_acceleration_coef + if not calming_run: + beat = self.frame_to_beat(current_frame) + node_coor[:,:,:,3] += self.gravity_acceleration_coef # decrease y-velo (3rd node coor) by G - applyMuscles(nodeCoor,muscles[:,:,:,beat,:],self.muscle_coef) - nodeCoor[:,:,:,2:4] *= friction - nodeCoor[:,:,:,0:2] += nodeCoor[:,:,:,2:4] # all node's x and y coordinates are adjusted by velocity_x and velocity_y - if not calmingRun: # dealing with collision with the ground. - nodesTouchingGround = np.ma.masked_where(nodeCoor[:,:,:,1] >= FLOOR_Y, nodeCoor[:,:,:,1]) - m = nodesTouchingGround.mask.astype(float) # mask that only countains 1's where nodes touch the floor - pressure = nodeCoor[:,:,:,1]-FLOOR_Y - groundFrictionMultiplier = 0.5**(m*pressure*self.ground_friction_coef) + apply_muscles(node_coor, muscles[:, :, :, beat, :], self.muscle_coef) + node_coor[:,:,:,2:4] *= friction + node_coor[:,:,:,0:2] += node_coor[:,:,:,2:4] + # all node's x and y coordinates are adjusted by velocity_x and velocity_y + if not calming_run: # dealing with collision with the ground. + nodes_touching_ground = np.ma.masked_where(node_coor[:,:,:,1] >= floor_y, node_coor[:,:,:,1]) + m = nodes_touching_ground.mask.astype(float) # mask that only countains 1's where nodes touch the floor + pressure = node_coor[:,:,:,1] - floor_y + ground_friction_multiplier = 0.5 ** (m*pressure*self.ground_friction_coef) - nodeCoor[:,:,:,1] = np.clip(nodeCoor[:,:,:,1], CEILING_Y, FLOOR_Y) # clip nodes below the ground back to ground level - nodeCoor[:,:,:,2] *= groundFrictionMultiplier # any nodes touching the ground must be slowed down by ground friction. + node_coor[:,:,:,1] = np.clip(node_coor[:,:,:,1], ceiling_y, floor_y) # clip nodes below the ground back to ground level + node_coor[:,:,:,2] *= ground_friction_multiplier # any nodes touching the ground must be slowed down by ground friction. - if calmingRun: # If it's a calming run, then take the average location of all nodes to center it at the origin. - nodeCoor[:,:,:,0] -= np.mean(nodeCoor[:,:,:,0], axis=(1,2), keepdims=True) - return nodeCoor, muscles, startCurrentFrame+frameCount + if calming_run: # If it's a calming run, then take the average location of all nodes to center it at the origin. + node_coor[:,:,:,0] -= np.mean(node_coor[:,:,:,0], axis=(1,2), keepdims=True) + + return node_coor, muscles, start_current_frame + frame_count - def doSpeciesInfo(self,nsp,best_of_each_species): - nsp = dict(sorted(nsp.items())) - running = 0 + def do_species_info(self, nsp, best_of_each_species) -> None: + nsp: dict = dict(sorted(nsp.items())) + running: int = 0 for sp in nsp.keys(): pop = nsp[sp][0] nsp[sp][1] = running @@ -157,92 +174,100 @@ def doSpeciesInfo(self,nsp,best_of_each_species): if pop > info.apex_pop: # This species reached its highest population info.apex_pop = pop info.reps[2] = best_of_each_species[sp] # apex representative - if pop >= self.c_count*self.S_NOTABLE and not info.prominent: #prominent threshold + if pop >= self.creature_count*self.S_NOTABLE and not info.prominent: #prominent threshold info.becomeProminent() - def checkALAP(self): + def check_alap(self) -> None: if self.ui.ALAPButton.setting == 1: # We're already ALAP-ing! - self.doGeneration(self.ui.doGenButton) + self.do_generation(self.ui.doGenButton) - def doGeneration(self, button): + def do_generation(self, button): generation_start_time = time.time() #calculates how long each generation takes to run - gen = len(self.creatures)-1 - creatureState = self.simulateImport(gen, 0, self.c_count, True) - nodeCoor, muscles, _ = self.simulateRun(creatureState, self.trial_time, False) - finalScores = nodeCoor[:,:,:,0].mean(axis=(1, 2)) # find each creature's average X-coordinate + gen = len(self.creatures) - 1 + creature_state = self.simulate_import(gen, 0, self.creature_count, True) + node_coor, muscles, _ = self.simulate_run(creature_state, self.trial_time, False) + final_scores = node_coor[:,:,:,0].mean(axis=(1, 2)) # find each creature's average X-coordinate # Tallying up all the data - currRankings = np.flip(np.argsort(finalScores),axis=0) - newPercentiles = np.zeros((self.HUNDRED+1)) - newSpeciesPops = {} + curr_rankings = np.flip(np.argsort(final_scores), axis=0) + new_percentiles = np.zeros((self.HUNDRED + 1)) + new_species_pops = {} best_of_each_species = {} - for rank in range(self.c_count): - c = currRankings[rank] - self.creatures[gen][c].fitness = finalScores[c] + + for rank in range(self.creature_count): + c = curr_rankings[rank] + self.creatures[gen][c].fitness = final_scores[c] self.creatures[gen][c].rank = rank species = self.creatures[gen][c].species - if species in newSpeciesPops: - newSpeciesPops[species][0] += 1 + if species in new_species_pops: + new_species_pops[species][0] += 1 else: - newSpeciesPops[species] = [1,None,None] + new_species_pops[species] = [1, None, None] if species not in best_of_each_species: best_of_each_species[species] = self.creatures[gen][c].IDNumber - self.doSpeciesInfo(newSpeciesPops,best_of_each_species) + + self.do_species_info(new_species_pops, best_of_each_species) for p in range(self.HUNDRED+1): - rank = min(int(self.c_count*p/self.HUNDRED),self.c_count-1) - c = currRankings[rank] - newPercentiles[p] = self.creatures[gen][c].fitness - - currCreatures = self.creatures[-1] - nextCreatures = [None]*self.c_count - for rank in range(self.c_count//2): - winner = currRankings[rank] - loser = currRankings[(self.c_count-1)-rank] - if random.uniform(0,1) < rank/self.c_count: + rank = min(int(self.creature_count * p / self.HUNDRED), self.creature_count - 1) + c = curr_rankings[rank] + new_percentiles[p] = self.creatures[gen][c].fitness + + next_creatures = [None] * self.creature_count + + for rank in range(self.creature_count // 2): + winner = curr_rankings[rank] + loser = curr_rankings[(self.creature_count - 1) - rank] + if random.uniform(0,1) < rank/self.creature_count: ph = loser loser = winner winner = ph - nextCreatures[winner] = None - if random.uniform(0,1) < rank/self.c_count*2.0: # A 1st place finisher is guaranteed to make a clone, but as we get closer to the middle the odds get more likely we just get 2 mutants. - nextCreatures[winner] = self.mutate(self.creatures[gen][winner],(gen+1)*self.c_count+winner) + + next_creatures[winner] = None + if random.uniform(0,1) < rank/self.creature_count*2.0: # A 1st place finisher is guaranteed to make a clone, but as we get closer to the middle the odds get more likely we just get 2 mutants. + next_creatures[winner] = self.mutate(self.creatures[gen][winner], (gen+1) * self.creature_count + winner) else: - nextCreatures[winner] = self.clone(self.creatures[gen][winner],(gen+1)*self.c_count+winner) - nextCreatures[loser] = self.mutate(self.creatures[gen][winner],(gen+1)*self.c_count+loser) + next_creatures[winner] = self.clone(self.creatures[gen][winner], (gen+1) * self.creature_count + winner) + + next_creatures[loser] = self.mutate(self.creatures[gen][winner], (gen+1) * self.creature_count + loser) self.creatures[gen][loser].living = False - self.creatures.append(nextCreatures) - self.rankings = np.append(self.rankings,currRankings.reshape((1,self.c_count)),axis=0) - self.percentiles = np.append(self.percentiles,newPercentiles.reshape((1,self.HUNDRED+1)),axis=0) - self.species_pops.append(newSpeciesPops) + self.creatures.append(next_creatures) + self.rankings = np.append(self.rankings, curr_rankings.reshape((1,self.creature_count)), axis=0) + self.percentiles = np.append(self.percentiles,new_percentiles.reshape((1,self.HUNDRED+1)), axis=0) + self.species_pops.append(new_species_pops) - drawAllGraphs(self, self.ui) + draw_all_graphs(self, self.ui) - self.getCalmStates(gen+1,0,self.c_count,self.stabilization_time,True) + self.get_calm_states(gen + 1, 0, self.creature_count, self.stabilization_time) + #Calm the creatures down so no potential energy is stored - for c in range(self.c_count): + + for c in range(self.creature_count): for i in range(2): - self.creatures[gen+1][c].icons[i] = self.creatures[gen+1][c].drawIcon(self.ui.ICON_DIM[i], self.ui.MOSAIC_COLOR, self.beat_fade_time) + self.creatures[gen+1][c].icons[i] = self.creatures[gen+1][c].draw_icon(self.ui.icon_dim[i], Color.MOSAIC, self.beat_fade_time) - self.ui.genSlider.val_max = gen+1 - self.ui.genSlider.manualUpdate(gen) - self.last_gen_run_time = time.time()-generation_start_time - - self.ui.CLH = [None, None, None] - self.ui.detectMouseMotion() - - def getCreatureWithID(self, ID): - return self.creatures[ID//self.c_count][ID%self.c_count] - - def clone(self, parent, newID): - return Creature(parent.dna, newID, parent.species, self, self.ui) - - def mutate(self, parent, newID): - newDNA, newSpecies, cwc = parent.getMutatedDNA(self) - newCreature = Creature(newDNA, newID, newSpecies, self, self.ui) - if newCreature.species != parent.species: - self.species_info.append(SpeciesInfo(self,newCreature,parent)) - newCreature.codonWithChange = cwc - return newCreature \ No newline at end of file + self.ui.gen_slider.val_max = gen + 1 + self.ui.gen_slider.manualUpdate(gen) + self.last_gen_run_time = time.time() - generation_start_time + + self.ui.creature_location_highlight = [None, None, None] + self.ui.detect_mouse_motion() + + def get_creature_with_id(self, creature_id): + return self.creatures[creature_id // self.creature_count][creature_id % self.creature_count] + + def clone(self, parent, new_id) -> Creature: + return Creature(parent.dna, new_id, parent.species, self, self.ui) + + def mutate(self, parent, new_id) -> Creature: + new_dna, new_species, cwc = parent.get_mutated_dna(self) + new_creature = Creature(new_dna, new_id, new_species, self, self.ui) + + if new_creature.species != parent.species: + self.species_info.append(SpeciesInfo(self, new_creature,parent)) + new_creature.codonWithChange = cwc + + return new_creature \ No newline at end of file diff --git a/jes_slider.py b/jes_slider.py index 608fc85..be4008b 100644 --- a/jes_slider.py +++ b/jes_slider.py @@ -10,7 +10,7 @@ def __init__(self,ui,pdim,pval,pval_min,pval_max,psnap_to_int,pupdate_live,pupda self.snap_to_int = psnap_to_int self.update_live = pupdate_live self.update_function = pupdate_function - ui.sliderList.append(self) + ui.slider_list.append(self) def drawSlider(self, screen): x, y, w, h, dw = self.dim diff --git a/jes_species_info.py b/jes_species_info.py index 7d79462..8950b20 100644 --- a/jes_species_info.py +++ b/jes_species_info.py @@ -1,5 +1,4 @@ import numpy as np -from hashlib import sha256 import math class SpeciesInfo: @@ -14,7 +13,7 @@ def __init__(self, _sim, me, ancestor): self.apex_pop = 0 self.reign = [] - self.reps = np.zeros((4), dtype=int) # Representative ancestor, first, apex, and last creatures of this species. + self.reps = np.zeros(4, dtype=int) # Representative ancestor, first, apex, and last creatures of this species. self.prominent = False if ancestor is not None: @@ -49,11 +48,11 @@ def insertIntoProminentSpeciesList(self): pL.insert(insert_index,i) def getWhen(self, index): - return math.floor(self.reps[index]//self.sim.c_count) + return math.floor(self.reps[index] // self.sim.creature_count) def getPerformance(self, sim, index): - gen = math.floor(self.reps[index]//self.sim.c_count) - c = self.reps[index]%self.sim.c_count + gen = math.floor(self.reps[index] // self.sim.creature_count) + c = self.reps[index]%self.sim.creature_count creature = sim.creatures[gen][c] return creature.fitness diff --git a/jes_ui.py b/jes_ui.py index 7eb7a81..723e26b 100644 --- a/jes_ui.py +++ b/jes_ui.py @@ -1,384 +1,398 @@ import pygame -from utils import arrayLerp, getUnit, hue_to_rgb, speciesToColor, species_to_name, dist_to_text, bound, getDist, arrayIntMultiply -from jes_dataviz import displayAllGraphs, drawAllGraphs -from jes_shapes import drawRect, drawRingLight, drawX, centerText, alignText, rightText, drawClock, drawSpeciesCircle +from pygame import Surface +from pygame.font import Font + +from enums import Color +from jes_creature import Creature +from utils import species_to_color, species_to_name, dist_to_text, bound, get_dist, array_int_multiply +from jes_dataviz import display_all_graphs, draw_all_graphs +from jes_shapes import drawRingLight, draw_x, center_text, align_text, draw_species_circle from jes_slider import Slider from jes_button import Button import time import numpy as np -import math + import random class UI: - def __init__(self, _W_W, _W_H, _MOVIE_SINGLE_DIM, _GRAPH_COOR, _SAC_COOR, _GENEALOGY_COOR, - _COLUMN_MARGIN, _MOSAIC_DIM, _MENU_TEXT_UP, _CM_MARGIN1, _CM_MARGIN2): - self.sliderList = [] - self.buttonList = [] + def __init__(self, config: dict): + self.slider_list: list = [] + self.button_list: list = [] + self.title: str = config.get('title') + pygame.display.set_caption(self.title) pygame.font.init() - self.bigFont = pygame.font.Font('./visuals/Arial.ttf', 60) - self.smallFont = pygame.font.Font('./visuals/Arial.ttf', 30) - self.tinyFont = pygame.font.Font('./visuals/Arial.ttf', 21) - self.BACKGROUND_PIC = pygame.image.load("visuals/background.png") - self.W_W = _W_W - self.W_H = _W_H - self.MOVIE_SINGLE_DIM = _MOVIE_SINGLE_DIM - self.INFO_W = self.MOVIE_SINGLE_DIM[0] - - self.GRAPH_COOR = _GRAPH_COOR - self.graph = pygame.Surface(self.GRAPH_COOR[2:4], pygame.SRCALPHA, 32) - self.SAC_COOR = _SAC_COOR - self.sac = pygame.Surface(self.SAC_COOR[2:4], pygame.SRCALPHA, 32) - self.GENEALOGY_COOR = _GENEALOGY_COOR - self.gene_graph = pygame.Surface(self.GENEALOGY_COOR[2:4], pygame.SRCALPHA, 32) - - self.COLUMN_MARGIN = _COLUMN_MARGIN - self.MOSAIC_DIM = _MOSAIC_DIM - self.MENU_TEXT_UP = _MENU_TEXT_UP - - self.CM_MARGIN1 = _CM_MARGIN1 - self.CM_MARGIN2 = _CM_MARGIN2 - self.MS_W = self.W_W-self.CM_MARGIN1*2 # mosaic screen width - self.MS_WC = self.MS_W-self.INFO_W-self.COLUMN_MARGIN # mosaic screen width (just creatures) - self.MS_H = self.W_H-self.MENU_TEXT_UP-self.CM_MARGIN1*2 + self.big_font: Font = pygame.font.Font('./visuals/Arial.ttf', 60) + self.small_font: Font = pygame.font.Font('./visuals/Arial.ttf', 30) + self.tiny_font: Font = pygame.font.Font('./visuals/Arial.ttf', 21) + self.background_pic = pygame.image.load("visuals/background.png") + + self.window_width: int = config.get('window_width') + self.window_height: int = config.get('window_height') + self.movie_single_dim: list[int] = config.get('movie_single_dim') + self.info_w: int = config.get('movie_single_dim')[0] + + self.graph_coor: list[int] = config.get('graph_coor') + self.graph = pygame.Surface(self.graph_coor[2:4], pygame.SRCALPHA, 32) + self.sac_coor: list[int] = config.get('sac_coor') + self.sac = pygame.Surface(self.sac_coor[2:4], pygame.SRCALPHA, 32) + self.genealogy_coor: list = config.get('genealogy_coor') + self.gene_graph: Surface = pygame.Surface(self.genealogy_coor[2:4], pygame.SRCALPHA, 32) + + self.column_margin: int = config.get('column_margin') + self.mosaic_dim: list[int] = config.get('mosaic_dim') + self.menu_text_up: int = config.get('menu_text_up') + + self.cm_margin_1: int = config.get('cm_margin_1') + self.cm_margin_2: int = config.get('cm_margin_2') + + self.mosaic_screen_width: int = self.window_width - self.cm_margin_1 * 2 + self.mosaic_screen_width_creatures: int = self.mosaic_screen_width - self.info_w - self.column_margin # mosaic screen width (just creatures) + self.mosaic_screen_height = self.window_height - self.menu_text_up - self.cm_margin_1 * 2 - s1 = int((self.MS_WC)/self.MOSAIC_DIM[0]-self.CM_MARGIN2*2) - s2 = int((self.MS_WC)/self.MOSAIC_DIM[1]-self.CM_MARGIN2*2) - self.ICON_DIM = ((s1,s1),(s2,s2),(s2,s2)) + s1: int = int(self.mosaic_screen_width_creatures / self.mosaic_dim[0] - self.cm_margin_2 * 2) + s2: int = int(self.mosaic_screen_width_creatures / self.mosaic_dim[1] - self.cm_margin_2 * 2) + self.icon_dim = ((s1, s1), (s2, s2), (s2, s2)) - self.mosaicVisible = False - self.CLH = [None,None,None] # Creature Location Highlight. First: is it in the mosaic (0), or top-3? (1). Second: Index of highlighted creature? Third: rank of creature? - self.creatureHighlight = [] - self.sliderDrag = None + self.mosaic_visible: bool = False + self.creature_location_highlight: list = [None, None, None] # Creature Location Highlight. First: is it in the mosaic (0), or top-3? (1). Second: Index of highlighted creature? Third: rank of creature? + self.creature_highlight: list = [] + self.slider_drag = None - self.visualSimMemory = [] - self.movieScreens = [] + self.visual_sim_memory: list = [] + self.movie_screens: list = [] self.sim = None - self.screen = pygame.display.set_mode((self.W_W,self.W_H)) - self.mosaicScreen = pygame.Surface((self.MS_WC,self.MS_H), pygame.SRCALPHA, 32) - self.infoBarScreen = pygame.Surface((self.INFO_W,self.MS_H), pygame.SRCALPHA, 32) - self.previewLocations = [[570,105,250,250],[570,365,250,250],[570,625,250,250]] - self.salt = str(random.uniform(0,1)) - self.sc_colors = {} # special-case colors: species colored by the user, not RNG + self.screen = pygame.display.set_mode((self.window_width, self.window_height)) + self.mosaic_screen: Surface = pygame.Surface((self.mosaic_screen_width_creatures, self.mosaic_screen_height), pygame.SRCALPHA, 32) + self.info_bar_screen: Surface = pygame.Surface((self.info_w, self.mosaic_screen_height), pygame.SRCALPHA, 32) + self.preview_locations: list[list[int]] = [[570, 105, 250, 250], [570, 365, 250, 250], [570, 625, 250, 250]] + self.salt: str = str(random.uniform(0,1)) + self.sc_colors: dict = {} # special-case colors: species colored by the user, not RNG # variables for the "Watch sample" button - self.sampleFrames = 0 - self.sample_i = 0 - - self.FPS = 30 - pygame.time.Clock().tick(self.FPS) - - #colors - self.GREEN = (0,255,0) - self.GRAYISH = (108,118,155) - self.BLACK = (0,0,0) - self.WHITE = (255,255,255) - self.MOSAIC_COLOR = (80,80,80) - self.SAMPLE_FREEZE_TIME = 90 - self.showXs = True + self.sample_frames: int = 0 + self.sample_i: int = 0 + + self.fps= config.get('fps') + + pygame.time.Clock().tick(self.fps) + + self.sample_freeze_time: int = 90 + self.show_xs: bool = True self.species_storage = None - self.storage_coor = (660,52) - self.running = True - - def addButtonsAndSliders(self): - self.genSlider = Slider(self,(40,self.W_H-100,self.W_W-80,60,140),0,0,0,True,True,self.updateGenSlider) - - buttonCoor = [] + self.storage_coor = (660, 52) + self.running: bool = True + + def add_buttons_and_sliders(self): + self.gen_slider = Slider(self, (40, self.window_height - 100, self.window_width - 80, 60, 140), 0, 0, 0, True, True, self.updateGenSlider) + + button_coor = [] for i in range(6): - buttonCoor.append((self.W_W-1340+220*i,self.W_H-self.MENU_TEXT_UP,200,60)) - self.showCreaturesButton = Button(self,buttonCoor[0],["Show creatures","Hide creatures"],self.toggleCreatures) - self.sortButton = Button(self,buttonCoor[1],["Sort by ID","Sort by fitness","Sort by weakness"],self.toggleSort) - self.styleButton = Button(self,buttonCoor[2],["Big Icons","Small Icons", "Species Tiles"],self.toggleStyle) - self.sampleButton = Button(self,buttonCoor[3],["Watch sample","Stop sample"],self.startSample) - self.doGenButton = Button(self,buttonCoor[4],["Do a generation"],self.sim.doGeneration) - self.ALAPButton = Button(self,buttonCoor[5],["Turn on ALAP","Turn off ALAP"],self.doNothing) + button_coor.append((self.window_width - 1340 + 220 * i, self.window_height - self.menu_text_up, 200, 60)) + + self.showCreaturesButton = Button(self, button_coor[0],["Show creatures", "Hide creatures"], self.toggleCreatures) + self.sortButton = Button(self,button_coor[1],["Sort by ID", "Sort by fitness", "Sort by weakness"], self.toggleSort) + self.styleButton = Button(self, button_coor[2], ["Big Icons", "Small Icons", "Species Tiles"], self.toggle_style) + self.sampleButton = Button(self, button_coor[3], ["Watch sample", "Stop sample"], self.start_sample) + self.doGenButton = Button(self, button_coor[4], ["Do a generation"], self.sim.do_generation) + self.ALAPButton = Button(self, button_coor[5], ["Turn on ALAP", "Turn off ALAP"], self.do_nothing) def reverse(self, i): - return self.sim.c_count-1-i + return self.sim.creature_count-1-i - def detectMouseMotion(self): + def detect_mouse_motion(self): if self.sampleButton.setting == 1: return - gen = self.genSlider.val - mouseX, mouseY = pygame.mouse.get_pos() - newCLH = [None,None,None] - if self.mosaicVisible: - rel_mouseX = mouseX-self.CM_MARGIN1 - rel_mouseY = mouseY-self.CM_MARGIN1 - if rel_mouseX >= 0 and rel_mouseX < self.MS_WC and rel_mouseY >= 0 and rel_mouseY < self.MS_H: - DIM = self.MOSAIC_DIM[self.styleButton.setting] - SPACING = self.MS_WC/DIM - ix = min(int(rel_mouseX/SPACING),DIM) - iy = int(rel_mouseY/SPACING) - i = iy*DIM+ix - if i >= 0 and i < self.sim.c_count: + gen = self.gen_slider.val + mouse_x, mouse_y = pygame.mouse.get_pos() + new_clh = [None,None,None] + if self.mosaic_visible: + rel_mouse_x = mouse_x-self.cm_margin_1 + rel_mouse_y = mouse_y-self.cm_margin_1 + if 0 <= rel_mouse_x < self.mosaic_screen_width_creatures and 0 <= rel_mouse_y < self.mosaic_screen_height: + dim = self.mosaic_dim[self.styleButton.setting] + spacing = self.mosaic_screen_width_creatures / dim + ix = min(int(rel_mouse_x/spacing),dim) + iy = int(rel_mouse_y/spacing) + i = iy*dim+ix + if 0 <= i < self.sim.creature_count: sort = self.sortButton.setting if sort == 0 or gen >= len(self.sim.rankings): - newCLH = [0,i,i] + new_clh = [0,i,i] elif sort == 1: - newCLH = [0,self.sim.rankings[gen][i],i] + new_clh = [0,self.sim.rankings[gen][i],i] elif sort == 2: - newCLH = [0,self.sim.rankings[gen][self.reverse(i)],i] + new_clh = [0,self.sim.rankings[gen][self.reverse(i)],i] - elif gen >= 0 and gen < len(self.sim.rankings): + elif 0 <= gen < len(self.sim.rankings): # rolling mouse over the Best+Median+Worst previews - for r in range(len(self.previewLocations)): - PL = self.previewLocations[r] - if mouseX >= PL[0] and mouseX < PL[0]+PL[2] and mouseY >= PL[1] and mouseY < PL[1]+PL[3]: + for r in range(len(self.preview_locations)): + pl = self.preview_locations[r] + if pl[0] <= mouse_x < pl[0]+pl[2] and pl[1] <= mouse_y < pl[1] + pl[3]: index = self.sim.rankings[gen][self.r_to_rank(r)] - newCLH = [1,index,r] + new_clh = [1,index,r] # rolling mouse over species circles - rX = mouseX-self.GENEALOGY_COOR[0] - rY = mouseY-self.GENEALOGY_COOR[1] - if rX >= 0 and rX < self.GENEALOGY_COOR[2] and rY >= 0 and rY < self.GENEALOGY_COOR[3]: - answer = self.getRollOver(rX,rY) + r_x = mouse_x-self.genealogy_coor[0] + r_y = mouse_y-self.genealogy_coor[1] + if 0 <= r_x < self.genealogy_coor[2] and 0 <= r_y < self.genealogy_coor[3]: + answer = self.get_roll_over(r_x, r_y) if answer is not None: - newCLH = [2, answer] + new_clh = [2, answer] # rolling over storage - if self.species_storage is not None and getDist(mouseX,mouseY,self.storage_coor[0],self.storage_coor[1]) <= self.GENEALOGY_COOR[4]: - newCLH = [2, self.species_storage] + if self.species_storage is not None and get_dist(mouse_x, mouse_y, self.storage_coor[0], self.storage_coor[1]) <= self.genealogy_coor[4]: + new_clh = [2, self.species_storage] - if newCLH[1] != self.CLH[1]: - self.CLH = newCLH - if self.CLH[1] is None: - self.clearMovies() - elif self.CLH[0] == 2: # a species was highlighted - info = self.sim.species_info[self.CLH[1]] - L = len(info.reps) - self.visualSimMemory = [] - self.creatureHighlight = [] - self.movieScreens = [] - for i in range(L): - ID = info.reps[i] - gen = ID//self.sim.c_count - c = ID%self.sim.c_count - self.creatureHighlight.append(self.sim.creatures[gen][c]) - self.visualSimMemory.append(self.sim.simulateImport(gen,c,c+1,True)) - self.movieScreens.append(None) - self.drawInfoBarSpecies(self.CLH[1]) + if new_clh[1] != self.creature_location_highlight[1]: + self.creature_location_highlight = new_clh + if self.creature_location_highlight[1] is None: + self.clear_movies() + elif self.creature_location_highlight[0] == 2: # a species was highlighted + info = self.sim.species_info[self.creature_location_highlight[1]] + l = len(info.reps) + self.visual_sim_memory = [] + self.creature_highlight = [] + self.movie_screens = [] + for i in range(l): + some_id = info.reps[i] + gen = some_id // self.sim.creature_count + c = some_id % self.sim.creature_count + + self.creature_highlight.append(self.sim.creatures[gen][c]) + self.visual_sim_memory.append(self.sim.simulate_import(gen, c, c + 1, True)) + self.movie_screens.append(None) + + self.draw_info_bar_species(self.creature_location_highlight[1]) else: # a creature was highlighted! - self.creatureHighlight = [self.sim.creatures[gen][self.CLH[1]]] - self.visualSimMemory = [self.sim.simulateImport(gen, self.CLH[1], self.CLH[1]+1,True)] - self.movieScreens = [None]*1 - self.drawInfoBarCreature(self.sim.creatures[gen][self.CLH[1]]) - - def clearMovies(self): - self.visualSimMemory = [] - self.creatureHighlight = [] - self.movieScreens = [] - self.CLH = [None,None,None] + self.creature_highlight = [self.sim.creatures[gen][self.creature_location_highlight[1]]] + self.visual_sim_memory = [self.sim.simulate_import(gen, self.creature_location_highlight[1], self.creature_location_highlight[1] + 1, True)] + self.movie_screens = [None] * 1 + self.draw_info_bar_creature(self.sim.creatures[gen][self.creature_location_highlight[1]]) + + def clear_movies(self) -> None: + self.visual_sim_memory = [] + self.creature_highlight = [] + self.movie_screens = [] + self.creature_location_highlight = [None, None, None] - def getRollOver(self,mouseX,mouseY): + def get_roll_over(self, mouse_x, mouse_y): answer = None ps = self.sim.prominent_species for level in range(len(ps)): for i in range(len(ps[level])): s = ps[level][i] - sX,sY = self.sim.species_info[s].coor - if getDist(mouseX,mouseY,sX,sY) <= self.GENEALOGY_COOR[4]: + s_x, s_y = self.sim.species_info[s].coor + if get_dist(mouse_x, mouse_y, s_x, s_y) <= self.genealogy_coor[4]: answer = s return answer - def drawCreatureMosaic(self, gen): - self.mosaicScreen.fill(self.MOSAIC_COLOR) - for c in range(self.sim.c_count): + def draw_creature_mosaic(self, gen) -> None: + self.mosaic_screen.fill(Color.MOSAIC) + + for c in range(self.sim.creature_count): i = c if self.sim.creatures[gen][c].rank is not None: if self.sortButton.setting == 1: i = self.sim.creatures[gen][c].rank elif self.sortButton.setting == 2: i = self.reverse(self.sim.creatures[gen][c].rank) - DIM = self.MOSAIC_DIM[self.styleButton.setting] - x = i%DIM - y = i//DIM + dim = self.mosaic_dim[self.styleButton.setting] + x = i % dim + y = i//dim creature = self.sim.creatures[gen][c] - SPACING = self.MS_WC/DIM - creature.iconCoor = (x*SPACING+self.CM_MARGIN2, y*SPACING+self.CM_MARGIN2, SPACING, SPACING) - if creature.iconCoor[1] < self.mosaicScreen.get_height(): + spacing = self.mosaic_screen_width_creatures / dim + creature.icon_coor = (x * spacing + self.cm_margin_2, y * spacing + self.cm_margin_2, spacing, spacing) + if creature.icon_coor[1] < self.mosaic_screen.get_height(): s = self.styleButton.setting if s <= 1: - self.mosaicScreen.blit(creature.icons[s], creature.iconCoor) + self.mosaic_screen.blit(creature.icons[s], creature.icon_coor) elif s == 2: - EXTRA = 1 - pygame.draw.rect(self.mosaicScreen,speciesToColor(creature.species, self),(creature.iconCoor[0],creature.iconCoor[1],SPACING+EXTRA,SPACING+EXTRA)) - if not creature.living and self.showXs: + extra = 1 + pygame.draw.rect(self.mosaic_screen, species_to_color(creature.species, self), (creature.icon_coor[0], creature.icon_coor[1], spacing + extra, spacing + extra)) + if not creature.living and self.show_xs: color = (255,0,0) if s <= 1 else (0,0,0) - drawX(creature.iconCoor, self.ICON_DIM[s][0], color, self.mosaicScreen) + draw_x(creature.icon_coor, self.icon_dim[s][0], color, self.mosaic_screen) + + def draw_info_bar_creature(self, creature: Creature) -> None: + x_center = int(self.info_w * 0.5) + self.info_bar_screen.fill(Color.MOSAIC) - def drawInfoBarCreature(self, creature): - X_center = int(self.INFO_W*0.5) - self.infoBarScreen.fill(self.MOSAIC_COLOR) - stri = [f"Creature #{creature.IDNumber}",f"Species: {species_to_name(creature.species, self)}", f"Untested"] + stri: list[str] = [f"Creature #{creature.IDNumber}", f"Species: {species_to_name(creature.species, self)}", "Untested"] if creature.fitness is not None: fate = "Living" if creature.living else "Killed" - stri = [f"Creature #{creature.IDNumber}",f"Species: {species_to_name(creature.species, self)}", f"Fitness: {dist_to_text(creature.fitness, True, self.sim.UNITS_PER_METER)}", f"Rank: {creature.rank+1} - {fate}"] + stri = [f"Creature #{creature.IDNumber}",f"Species: {species_to_name(creature.species, self)}", f"Fitness: {dist_to_text(creature.fitness, True, self.sim.units_per_meter)}", f"Rank: {creature.rank + 1} - {fate}"] for i in range(len(stri)): - color = self.WHITE + color = Color.WHITE if stri[i][0:7] == "Species": - color = speciesToColor(creature.species, self) - centerText(self.infoBarScreen, stri[i], X_center, self.MOVIE_SINGLE_DIM[1]+40+42*i, color, self.smallFont) + color = species_to_color(creature.species, self) + + center_text(self.info_bar_screen, stri[i], x_center, self.movie_single_dim[1] + 40 + 42 * i, color, self.small_font) - def drawMovieGrid(self, screen, coor, mask, titles, colors, font): - LMS = len(self.movieScreens) - per_row = 1 if LMS == 1 else LMS//2 - for i in range(LMS): + def draw_movie_grid(self, screen, coor, mask, titles, colors, font) -> None: + lms = len(self.movie_screens) + per_row = 1 if lms == 1 else lms//2 + for i in range(lms): if mask is not None and not mask[i]: continue - ms = self.movieScreens[i] - W = ms.get_width() - H = ms.get_height() - x = coor[0]+(i%per_row)*W - y = coor[1]+(i//per_row)*H + ms = self.movie_screens[i] + w = ms.get_width() + h = ms.get_height() + x = coor[0]+(i%per_row) * w + y = coor[1]+(i//per_row) * h screen.blit(ms,(x,y)) if titles is not None: - centerText(screen, titles[i], x+W/2, y+H-30, colors[i], font) + center_text(screen, titles[i], x + w / 2, y + h - 30, colors[i], font) - def drawMovieQuad(self, species): - L = 4 + def draw_movie_quad(self, species) -> None: + l = 4 info = self.sim.species_info[species] a_name = species_to_name(info.ancestorID, self) s_name = species_to_name(species, self) titles = ["Ancestor","First","Apex","Last"] - mask = [True]*4 - for i in range(L): + mask = [True] * l + + for i in range(l): if (info.ancestorID is None and i == 0) or (i >= 2 and info.getWhen(i) == info.getWhen(i-1)): mask[i] = False continue + stri = a_name if i == 0 else s_name performance = info.getPerformance(self.sim, i) - titles[i] = f"G{info.getWhen(i)}: {titles[i]} {stri} ({dist_to_text(performance, True, self.sim.UNITS_PER_METER)})" - coor = (self.CM_MARGIN1+self.MS_WC,0) - self.drawMovieGrid(self.screen, coor, mask, titles, [self.GRAYISH]*L, self.tinyFont) + titles[i] = f"G{info.getWhen(i)}: {titles[i]} {stri} ({dist_to_text(performance, True, self.sim.units_per_meter)})" + + coor = (self.cm_margin_1 + self.mosaic_screen_width_creatures, 0) + self.draw_movie_grid(self.screen, coor, mask, titles, [Color.GRAYISH] * l, self.tiny_font) - def drawInfoBarSpecies(self, species): - self.infoBarScreen.fill(self.MOSAIC_COLOR) + def draw_info_bar_species(self, species) -> None: + self.info_bar_screen.fill(Color.MOSAIC) info = self.sim.species_info[species] a_name = species_to_name(info.ancestorID, self) s_name = species_to_name(species, self) - now = min(self.genSlider.val, len(self.sim.species_pops)-1) + now = min(self.gen_slider.val, len(self.sim.species_pops) - 1) now_pop = 0 extinct_string = " (Extinct)" if species in self.sim.species_pops[now]: now_pop = self.sim.species_pops[now][species][0] extinct_string = "" strings = [f"Species {s_name}",f"Ancestor {a_name}",f"Lifespan: G{info.getWhen(1)} - G{info.getWhen(3)}{extinct_string}", f"Population: {info.apex_pop} at apex (G{info.getWhen(2)}) | {now_pop} now (G{now})"] - colors = [self.WHITE]*len(strings) - colors[0] = speciesToColor(species, self) + colors = [Color.WHITE]*len(strings) + colors[0] = species_to_color(species, self) if info.ancestorID is None: strings[1] = "Primordial species" else: - colors[1] = speciesToColor(info.ancestorID, self) + colors[1] = species_to_color(info.ancestorID, self) for i in range(len(strings)): - X_center = int(self.INFO_W*(0.5 if i == 3 else 0.3)) - centerText(self.infoBarScreen, strings[i], X_center, self.MOVIE_SINGLE_DIM[1]+40+42*i, colors[i], self.smallFont) + x_center = int(self.info_w * (0.5 if i == 3 else 0.3)) + center_text(self.info_bar_screen, strings[i], x_center, self.movie_single_dim[1] + 40 + 42 * i, colors[i], self.small_font) - self.drawLightboard(self.infoBarScreen,species,now,(self.INFO_W*0.6,self.MOVIE_SINGLE_DIM[1]+10,self.INFO_W*0.37, self.MS_H-self.MOVIE_SINGLE_DIM[1]-20)) + self.draw_lightboard(self.info_bar_screen, species, now, (self.info_w * 0.6, self.movie_single_dim[1] + 10, self.info_w * 0.37, self.mosaic_screen_height - self.movie_single_dim[1] - 20)) - def drawLightboard(self, screen, species, gen, coor): - DIM = self.MOSAIC_DIM[-1] - R = coor[2]/DIM - for c in range(self.sim.c_count): - x = coor[0]+R*(c%DIM) - y = coor[1]+R*(c//DIM) + def draw_lightboard(self, screen, species, gen, coor) -> None: + dim = self.mosaic_dim[-1] + r = coor[2]/dim + for c in range(self.sim.creature_count): + x = coor[0] + r * (c % dim) + y = coor[1] + r * (c // dim) col = (0,0,0) creature = self.sim.creatures[gen][self.sim.rankings[gen][c]] if creature.species == species: - col = speciesToColor(species, self) - pygame.draw.rect(screen,col,(x,y,R,R)) - - def drawMenuText(self): - y = self.W_H-self.MENU_TEXT_UP - titleSurface = self.bigFont.render("Jelly Evolution Simulator", False, self.GRAYISH) - self.screen.blit(titleSurface,(40,20)) - a = str(int(self.genSlider.val)) - b = str(int(self.genSlider.val_max)) - genSurface = self.bigFont.render("Generation "+a+" / "+b, False, (255,255,255)) - self.screen.blit(genSurface,(40,y)) + col = species_to_color(species, self) + pygame.draw.rect(screen,col,(x,y,r,r)) + + def draw_menu_text(self) -> None: + y = self.window_height - self.menu_text_up + title_surface = self.big_font.render(self.title, False, Color.GRAYISH) + self.screen.blit(title_surface,(40,20)) + a = str(int(self.gen_slider.val)) + b = str(int(self.gen_slider.val_max)) + gen_surface = self.big_font.render("Generation " + a + " / " + b, False, (255, 255, 255)) + self.screen.blit(gen_surface,(40,y)) if self.species_storage is not None: s = self.species_storage - R = self.GENEALOGY_COOR[4] - drawSpeciesCircle(self.screen,s,self.storage_coor,R,self.sim,self.sim.species_info,self.tinyFont,False,self) + R = self.genealogy_coor[4] + draw_species_circle(self.screen, s, self.storage_coor, R, self.sim, self.sim.species_info, self.tiny_font, False, self) def r_to_rank(self,r): - return 0 if r == 0 else (self.sim.c_count-1 if r == 2 else self.sim.c_count//2) + return 0 if r == 0 else (self.sim.creature_count - 1 if r == 2 else self.sim.creature_count // 2) - def drawPreviews(self): - gen = self.genSlider.val - if gen >= 0 and gen < len(self.sim.rankings): + def draw_previews(self) -> None: + gen = self.gen_slider.val + if 0 <= gen < len(self.sim.rankings): names = ["Best","Median","Worst"] for r in range(3): r_i = self.r_to_rank(r) index = self.sim.rankings[gen][r_i] creature = self.sim.creatures[gen][index] - DIM = (self.previewLocations[r][2],self.previewLocations[r][3]) - preview = creature.drawIcon(DIM, self.MOSAIC_COLOR, self.sim.beat_fade_time) - centerText(preview, f"{names[r]} creature", DIM[0]/2, DIM[1]-20, self.WHITE, self.smallFont) - alignText(preview, dist_to_text(creature.fitness, True,self.sim.UNITS_PER_METER), 10, 20, self.WHITE, self.smallFont, 0.0,None) - self.screen.blit(preview,(self.previewLocations[r][0],self.previewLocations[r][1])) + DIM = (self.preview_locations[r][2], self.preview_locations[r][3]) + preview = creature.draw_icon(DIM, Color.MOSAIC, self.sim.beat_fade_time) + center_text(preview, f"{names[r]} creature", DIM[0] / 2, DIM[1] - 20, Color.WHITE, self.small_font) + align_text(preview, dist_to_text(creature.fitness, True, self.sim.units_per_meter), 10, 20, Color.WHITE, self.small_font, 0.0, None) + self.screen.blit(preview, (self.preview_locations[r][0], self.preview_locations[r][1])) - def doMovies(self): - L = len(self.visualSimMemory) - MSCALE = [1, 1, 0.5, 0.70] # movie screen scale + def do_movies(self): + l = len(self.visual_sim_memory) + mscale = [1, 1, 0.5, 0.70] # movie screen scale if self.sampleButton.setting == 1: self.sample_frames += 1 - if self.sample_frames >= self.sim.trial_time+self.SAMPLE_FREEZE_TIME: + if self.sample_frames >= self.sim.trial_time+self.sample_freeze_time: self.startSampleHelper() - for i in range(L): - if self.visualSimMemory[i][2] < self.sim.trial_time: - self.visualSimMemory[i] = self.sim.simulateRun(self.visualSimMemory[i], 1, False) - DIM = arrayIntMultiply(self.MOVIE_SINGLE_DIM, MSCALE[self.CLH[0]]) - self.movieScreens[i] = pygame.Surface(DIM, pygame.SRCALPHA, 32) + for i in range(l): + if self.visual_sim_memory[i][2] < self.sim.trial_time: + self.visual_sim_memory[i] = self.sim.simulate_run(self.visual_sim_memory[i], 1, False) + + dim = array_int_multiply(self.movie_single_dim, mscale[self.creature_location_highlight[0]]) + self.movie_screens[i] = pygame.Surface(dim, pygame.SRCALPHA, 32) - nodeArr, _, currentFrame = self.visualSimMemory[i] - s = DIM[0]/(self.sim.CW+2)*0.5 # visual transform scale + node_arr, _, current_frame = self.visual_sim_memory[i] + s = dim[0]/(self.sim.CW+2)*0.5 # visual transform scale - averageX = np.mean(nodeArr[:,:,:,0]) - transform = [DIM[0]/2-averageX*s,DIM[1]*0.8,s] - self.creatureHighlight[i].drawCreature(self.movieScreens[i],nodeArr[0],currentFrame,transform,True,(i == 0)) + average_x = np.mean(node_arr[:,:,:,0]) + transform = [dim[0]/2 - average_x*s,dim[1]*0.8,s] + self.creature_highlight[i].draw_creature(self.movie_screens[i], node_arr[0], current_frame, transform, True, (i == 0)) - def getHighlightedSpecies(self): - gen = self.genSlider.val - if self.CLH[0] == 2: - return self.CLH[1] - elif self.CLH[0] == 0 or self.CLH[0] == 1: - return self.sim.creatures[gen][self.CLH[1]].species + def get_highlighted_species(self): + gen = self.gen_slider.val + if self.creature_location_highlight[0] == 2: + return self.creature_location_highlight[1] + elif self.creature_location_highlight[0] == 0 or self.creature_location_highlight[0] == 1: + return self.sim.creatures[gen][self.creature_location_highlight[1]].species + return None - def detectEvents(self): + def detect_events(self) -> None: for event in pygame.event.get(): if event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE or event.key == 27: # pressing escape self.running = False new_gen = None if event.key == pygame.K_LEFT: - new_gen = max(0,self.genSlider.val-1) + new_gen = max(0, self.gen_slider.val - 1) if event.key == pygame.K_RIGHT: - new_gen = min(self.genSlider.val_max,self.genSlider.val+1) + new_gen = min(self.gen_slider.val_max, self.gen_slider.val + 1) if new_gen is not None: - self.genSlider.manualUpdate(new_gen) - self.clearMovies() - self.detectMouseMotion() + self.gen_slider.manualUpdate(new_gen) + self.clear_movies() + self.detect_mouse_motion() if event.key == 120: # pressing X will hide the Xs showing killed creatures - self.showXs = (not self.showXs) - self.drawCreatureMosaic(self.genSlider.val) + self.show_xs = (not self.show_xs) + self.draw_creature_mosaic(self.gen_slider.val) elif event.key == 115: # pressing S will store the species of the creature you're rolling over into "storage". - self.species_storage = self.getHighlightedSpecies() + self.species_storage = self.get_highlighted_species() elif event.key == 99: # pressing C will change the highlighted species's color. - c = self.getHighlightedSpecies() + c = self.get_highlighted_species() if c is not None: self.sc_colors[c] = str(random.uniform(0,1)) - drawAllGraphs(self.sim, self) - self.clearMovies() - self.detectMouseMotion() + draw_all_graphs(self.sim, self) + self.clear_movies() + self.detect_mouse_motion() elif event.key == 13: # pressing Enter - self.sim.doGeneration(None) + self.sim.do_generation(None) elif event.key == 113: # pressing 'Q' self.showCreaturesButton.timeOfLastClick = time.time() self.showCreaturesButton.setting = 1-self.showCreaturesButton.setting @@ -387,125 +401,125 @@ def detectEvents(self): elif event.type == pygame.MOUSEBUTTONDOWN: mouseX, mouseY = pygame.mouse.get_pos() - for slider in self.sliderList: + for slider in self.slider_list: s_x, s_y, s_w, s_h, s_dw = slider.dim if mouseX >= s_x and mouseX < s_x+s_w and mouseY >= s_y and mouseY < s_y+s_h: - self.sliderDrag = slider + self.slider_drag = slider break - for button in self.buttonList: + for button in self.button_list: s_x, s_y, s_w, s_h = button.dim if mouseX >= s_x and mouseX < s_x+s_w and mouseY >= s_y and mouseY < s_y+s_h: button.click() elif event.type == pygame.MOUSEBUTTONUP: - if self.sliderDrag is not None: - self.sliderDrag.updateVal() - self.sliderDrag = None + if self.slider_drag is not None: + self.slider_drag.updateVal() + self.slider_drag = None def drawMenu(self): - self.screen.blit(self.BACKGROUND_PIC,(0,0)) - self.drawMenuText() - self.drawPreviews() - displayAllGraphs(self.screen, self.sim, self) + self.screen.blit(self.background_pic, (0, 0)) + self.draw_menu_text() + self.draw_previews() + display_all_graphs(self.screen, self.sim, self) self.drawSlidersAndButtons() - self.displayCreatureMosaic(self.screen) - self.displayMovies(self.screen) + self.display_creature_mosaic(self.screen) + self.display_movies(self.screen) + + def display_creature_mosaic(self, screen): + time_since_last_press = time.time()-self.showCreaturesButton.timeOfLastClick + pan_time = 0.2 + frac = bound(time_since_last_press/pan_time) - def displayCreatureMosaic(self, screen): - timeSinceLastPress = time.time()-self.showCreaturesButton.timeOfLastClick - PAN_TIME = 0.2 - frac = bound(timeSinceLastPress/PAN_TIME) - panelY = 0 - if self.mosaicVisible: - panelY = self.CM_MARGIN1-self.mosaicScreen.get_height()*(1-frac) - screen.blit(self.mosaicScreen,(self.CM_MARGIN1,panelY)) - if not self.mosaicVisible and frac < 1: - self.screen.blit(self.mosaicScreen,(self.CM_MARGIN1,self.CM_MARGIN1-self.mosaicScreen.get_height()*frac)) + if self.mosaic_visible: + panel_y = self.cm_margin_1 - self.mosaic_screen.get_height() * (1 - frac) + screen.blit(self.mosaic_screen, (self.cm_margin_1, panel_y)) + if not self.mosaic_visible and frac < 1: + self.screen.blit(self.mosaic_screen, (self.cm_margin_1, self.cm_margin_1 - self.mosaic_screen.get_height() * frac)) - def displayMovies(self, screen): - if self.CLH[0] == None: + def display_movies(self, screen): + if self.creature_location_highlight[0] is None: return - if self.CLH[0] == 3: - LMS = len(self.movieScreens) - species_names = [None]*LMS - species_colors = [None]*LMS - for i in range(LMS): - sp = self.creatureHighlight[i].species + + if self.creature_location_highlight[0] == 3: + lms = len(self.movie_screens) + species_names = [None] * lms + species_colors = [None] * lms + for i in range(lms): + sp = self.creature_highlight[i].species species_names[i] = species_to_name(sp, self) - species_colors[i] = speciesToColor(sp, self) - self.drawMovieGrid(screen, (0,0), [True]*LMS, species_names, species_colors, self.smallFont) + species_colors[i] = species_to_color(sp, self) + self.draw_movie_grid(screen, (0, 0), [True] * lms, species_names, species_colors, self.small_font) return - gen = self.genSlider.val - coor = (self.CM_MARGIN1+self.MS_WC,0) - self.screen.blit(self.infoBarScreen,coor) - if self.CLH[0] == 2: - self.drawMovieQuad(self.CLH[1]) + + gen = self.gen_slider.val + coor = (self.cm_margin_1 + self.mosaic_screen_width_creatures, 0) + self.screen.blit(self.info_bar_screen, coor) + if self.creature_location_highlight[0] == 2: + self.draw_movie_quad(self.creature_location_highlight[1]) return - self.screen.blit(self.movieScreens[0],coor) - if self.CLH[0] == 1: - DIM = self.previewLocations[self.CLH[2]] - self.screen.blit(drawRingLight(DIM[2],DIM[3],6),(DIM[0],DIM[1])) + self.screen.blit(self.movie_screens[0], coor) + if self.creature_location_highlight[0] == 1: + dim = self.preview_locations[self.creature_location_highlight[2]] + self.screen.blit(drawRingLight(dim[2],dim[3],6),(dim[0],dim[1])) else: - coor = self.sim.creatures[gen][self.CLH[1]].iconCoor - x = coor[0]+self.CM_MARGIN1 - y = coor[1]+self.CM_MARGIN1 + coor = self.sim.creatures[gen][self.creature_location_highlight[1]].icon_coor + x = coor[0]+self.cm_margin_1 + y = coor[1]+self.cm_margin_1 self.screen.blit(drawRingLight(coor[2],coor[3],6),(x,y)) - - - - def detectSliders(self): - if self.sliderDrag is not None: + + def detect_sliders(self): + if self.slider_drag is not None: mouseX, mouseY = pygame.mouse.get_pos() - s_x, s_y, s_w, s_h, s_dw = self.sliderDrag.dim + s_x, s_y, s_w, s_h, s_dw = self.slider_drag.dim ratio = bound(((mouseX-s_dw*0.5)-s_x)/(s_w-s_dw)) - s_range = self.sliderDrag.val_max - self.sliderDrag.val_min - self.sliderDrag.tval = ratio*s_range+self.sliderDrag.val_min - if self.sliderDrag.snap_to_int: - self.sliderDrag.tval = round(self.sliderDrag.tval) - if self.sliderDrag.update_live: - self.sliderDrag.updateVal() + s_range = self.slider_drag.val_max - self.slider_drag.val_min + self.slider_drag.tval = ratio * s_range + self.slider_drag.val_min + if self.slider_drag.snap_to_int: + self.slider_drag.tval = round(self.slider_drag.tval) + if self.slider_drag.update_live: + self.slider_drag.updateVal() def drawSlidersAndButtons(self): - for slider in self.sliderList: + for slider in self.slider_list: slider.drawSlider(self.screen) - for button in self.buttonList: - button.drawButton(self.screen, self.smallFont) + for button in self.button_list: + button.draw_button(self.screen, self.small_font) # Button and slider functions def updateGenSlider(self, gen): - self.drawCreatureMosaic(gen) + self.draw_creature_mosaic(gen) def toggleCreatures(self, button): - self.mosaicVisible = (button.setting == 1) + self.mosaic_visible = (button.setting == 1) def toggleSort(self, button): - self.drawCreatureMosaic(self.genSlider.val) + self.draw_creature_mosaic(self.gen_slider.val) - def toggleStyle(self, button): - self.drawCreatureMosaic(self.genSlider.val) + def toggle_style(self, button): + self.draw_creature_mosaic(self.gen_slider.val) - def doNothing(self, button): - a = 5 + def do_nothing(self, button): + pass - def startSample(self, button): + def start_sample(self, button): if button.setting == 1: self.sample_i = 0 self.startSampleHelper() def startSampleHelper(self): L = 8 - self.creatureHighlight = [] - self.visualSimMemory = [] - self.movieScreens = [] - self.CLH = [3,0] + self.creature_highlight = [] + self.visual_sim_memory = [] + self.movie_screens = [] + self.creature_location_highlight = [3, 0] self.sample_frames = 0 for i in range(L): - gen = self.genSlider.val - c = (self.sample_i+i)%self.sim.c_count - self.creatureHighlight.append(self.sim.creatures[gen][c]) - self.visualSimMemory.append(self.sim.simulateImport(gen,c,c+1,True)) - self.movieScreens.append(None) + gen = self.gen_slider.val + c = (self.sample_i+i)%self.sim.creature_count + self.creature_highlight.append(self.sim.creatures[gen][c]) + self.visual_sim_memory.append(self.sim.simulate_import(gen, c, c + 1, True)) + self.movie_screens.append(None) self.sample_i += L def show(self): diff --git a/utils.py b/utils.py index 3a849d3..f383d6f 100644 --- a/utils.py +++ b/utils.py @@ -1,20 +1,21 @@ +import json from hashlib import sha256 import numpy as np import math -def arrayLerp(arrA, arrB, x): - return arrA+(arrB-arrA)*x +def array_lerp(arr_a, arr_b, x): + return arr_a+(arr_b - arr_a)*x -def listLerp(listA, listB, x): - listResult = [None]*len(listA) - for i in range(len(listA)): - listResult[i] = listA[i]+(listB[i]-listA[i])*x - return listResult +def list_lerp(list_a, list_b, x): + list_result = [None]*len(list_a) + for i in range(len(list_a)): + list_result[i] = list_a[i] + (list_b[i] - list_a[i]) * x + return list_result -def getUnit(r): +def get_unit(r): _list = [0.000001,0.000002,0.000005,0.00001,0.00002,0.00005,0.0001,0.0002,0.0005,0.001,0.002,0.005,0.01,0.02,0.05,0.1,0.2,0.5,1,2,5,10,20,50,100,200,500,1000,2000,5000,10000,20000,50000,100000,200000,500000,1000000] choice = 0 - while(_list[choice] < r*0.2): + while _list[choice] < r * 0.2: choice += 1 return _list[choice] @@ -33,7 +34,7 @@ def hue_to_rgb(hue): r = (0.4+0.6*h,0.4,1) else: r = (1,0.4-0.4*h,1-h) - return (255*r[0], 200*r[1],255*r[2]) + return 255*r[0], 200*r[1], 255*r[2] def species_to_name(s, ui): salted = str(s)+ui.salt @@ -57,16 +58,17 @@ def species_to_name(s, ui): letter = letter.upper() name += letter result = result//option_count + return name def brighten(color, b): if b >= 1: fac = b-1 - return (lerp(color[0],255,fac),lerp(color[1],255,fac),lerp(color[2],255,fac)) + return lerp(color[0],255,fac),lerp(color[1],255,fac),lerp(color[2],255,fac) else: - return (color[0]*b, color[1]*b, color[2]*b) + return color[0]*b, color[1]*b, color[2]*b -def speciesToColor(s, ui): +def species_to_color(s, ui): salted = str(s)+ui.salt if s in ui.sc_colors: salted = ui.sc_colors[s]+ui.salt @@ -89,30 +91,27 @@ def dist_to_text(dist, sigfigs, u): else: return str(int(dist/u))+"cm" -def getDistanceArray(a,b): +def get_distance_array(a, b): x_dist = a[:,:,:,0]-b[:,:,:,0] y_dist = a[:,:,:,1]-b[:,:,:,1] return np.sqrt(np.square(x_dist)+np.square(y_dist)) -def applyMuscles(n,m,muscle_coef): - xNeighborDists = getDistanceArray(n[:,:-1,:],n[:,1:,:]) - yNeighborDists = getDistanceArray(n[:,:,:-1],n[:,:,1:]) - posDiagNeighborDists = getDistanceArray(n[:,:-1,:-1],n[:,1:,1:]) - negDiagNeighborDists = getDistanceArray(n[:,:-1,1:],n[:,1:,:-1]) - - MAs = [None]*6 - segments = [[0,0,1,0],[0,1,1,1], - [0,0,0,1],[1,0,1,1],[0,0,1,1],[0,1,1,0]] - segments2 = [[0,0,1,0],[0,1,1,1], - [0,0,0,1],[1,0,1,1],[0,0,1,1],[0,1,1,0]] +def apply_muscles(n, m, muscle_coef) -> None : + x_neighbor_dists = get_distance_array(n[:, :-1, :], n[:, 1:, :]) + y_neighbor_dists = get_distance_array(n[:, :, :-1], n[:, :, 1:]) + pos_diag_neighbor_dists = get_distance_array(n[:, :-1, :-1], n[:, 1:, 1:]) + neg_diag_neighbor_dists = get_distance_array(n[:, :-1, 1:], n[:, 1:, :-1]) + m_as = [None] * 6 + segments = [[0,0,1,0],[0,1,1,1],[0,0,0,1],[1,0,1,1],[0,0,1,1],[0,1,1,0]] + - MAs[0] = getMuscleAttraction(xNeighborDists[:,:,:-1],m[:,:,:,0],muscle_coef) - MAs[1] = getMuscleAttraction(xNeighborDists[:,:,1:],m[:,:,:,0],muscle_coef) - MAs[2] = getMuscleAttraction(yNeighborDists[:,:-1,:],m[:,:,:,1],muscle_coef) - MAs[3] = getMuscleAttraction(yNeighborDists[:,1:,:],m[:,:,:,1],muscle_coef) - MAs[4] = getMuscleAttraction(posDiagNeighborDists,m[:,:,:,3],muscle_coef) - MAs[5] = getMuscleAttraction(negDiagNeighborDists,m[:,:,:,3],muscle_coef) + m_as[0] = get_muscle_attraction(x_neighbor_dists[:, :, :-1], m[:, :, :, 0], muscle_coef) + m_as[1] = get_muscle_attraction(x_neighbor_dists[:, :, 1:], m[:, :, :, 0], muscle_coef) + m_as[2] = get_muscle_attraction(y_neighbor_dists[:, :-1, :], m[:, :, :, 1], muscle_coef) + m_as[3] = get_muscle_attraction(y_neighbor_dists[:, 1:, :], m[:, :, :, 1], muscle_coef) + m_as[4] = get_muscle_attraction(pos_diag_neighbor_dists, m[:, :, :, 3], muscle_coef) + m_as[5] = get_muscle_attraction(neg_diag_neighbor_dists, m[:, :, :, 3], muscle_coef) # The array n is a 100 x 5 x 5 x 4 dimensional array, # and it encodes the position and velocity data for all 100 creatures on a frame. @@ -121,14 +120,14 @@ def applyMuscles(n,m,muscle_coef): # Dimension 2: 5 nodes across the x-dimensional # Dimension 3: 5 nodes across the y-dimensional # Dimension 4: Which coordinate to do you want (x, y, vx, vy) - _, CW, CH, __ = n.shape - CW -= 1 - CH -= 1 + _, cw, ch, __ = n.shape + cw -= 1 + ch -= 1 for dire in range(6): s = segments[dire] - sli1 = n[:,s[0]:s[0]+CW,s[1]:s[1]+CW] - sli2 = n[:,s[2]:s[2]+CH,s[3]:s[3]+CH] + sli1 = n[:,s[0]:s[0]+cw,s[1]:s[1]+cw] + sli2 = n[:,s[2]:s[2]+ch,s[3]:s[3]+ch] delta_x = sli1[:,:,:,0]-sli2[:,:,:,0] delta_y = sli1[:,:,:,1]-sli2[:,:,:,1] @@ -137,19 +136,28 @@ def applyMuscles(n,m,muscle_coef): delta_nx = delta_x/delta_magnitude delta_ny = delta_y/delta_magnitude - n[:,s[0]:s[0]+CW,s[1]:s[1]+CW,2] += delta_nx*MAs[dire] - n[:,s[0]:s[0]+CW,s[1]:s[1]+CW,3] += delta_ny*MAs[dire] - n[:,s[2]:s[2]+CH,s[3]:s[3]+CH,2] -= delta_nx*MAs[dire] - n[:,s[2]:s[2]+CH,s[3]:s[3]+CH,3] -= delta_ny*MAs[dire] + n[:,s[0]:s[0]+cw,s[1]:s[1]+cw,2] += delta_nx*m_as[dire] + n[:,s[0]:s[0]+cw,s[1]:s[1]+cw,3] += delta_ny*m_as[dire] + n[:,s[2]:s[2]+ch,s[3]:s[3]+ch,2] -= delta_nx*m_as[dire] + n[:,s[2]:s[2]+ch,s[3]:s[3]+ch,3] -= delta_ny*m_as[dire] -def getMuscleAttraction(dists,m,muscle_coef): - return (m-dists)*muscle_coef +def get_muscle_attraction(dists, m, muscle_coef): + return (m-dists) * muscle_coef -def getDist(x1, y1, x2, y2): +def get_dist(x1, y1, x2, y2): return np.linalg.norm(np.array([x2-x1,y2-y1])) -def arrayIntMultiply(arr, factor): - result = [None]*len(arr) +def array_int_multiply(arr, factor): + + result = [None] * len(arr) + for i in range(len(arr)): - result[i] = int(arr[i]*factor) - return result \ No newline at end of file + result[i] = int(arr[i] * factor) + + return result + +def read_config(filename: str) -> dict: + with open(filename, 'r') as f: + result: dict = json.load(f) + + return result \ No newline at end of file From 9adaa1715899c89deec33bfac3777c8454e1366e Mon Sep 17 00:00:00 2001 From: Bruno Rodrigues Date: Thu, 16 Jan 2025 13:18:54 +0000 Subject: [PATCH 2/4] created config files for UI, SIM, rename variables/methods/functions using underscore. overall cleanup, Color Enum, --- jes.py | 7 +- jes_creature.py | 12 ++-- jes_dump.py | 2 +- jes_shapes.py | 2 +- jes_sim.py | 10 +-- jes_species_info.py | 39 +++++------ jes_ui.py | 160 +++++++++++++++++++++++++------------------- 7 files changed, 126 insertions(+), 106 deletions(-) diff --git a/jes.py b/jes.py index a0e06d4..64e36db 100644 --- a/jes.py +++ b/jes.py @@ -23,11 +23,6 @@ ui: UI = UI(config=ui_config) -# ui = UI(_W_W=1920, _W_H=1080, _MOVIE_SINGLE_DIM=(650,650), -# _GRAPH_COOR=(850,50,900,500), _SAC_COOR=(850,560,900,300), _GENEALOGY_COOR=(20,105,530,802,42), -# _COLUMN_MARGIN=330, _MOSAIC_DIM=[10,24,24,30], #_MOSAIC_DIM=[10,10,17,22], -# _MENU_TEXT_UP=180, _CM_MARGIN1=20, _CM_MARGIN2=1) - sim.ui = ui ui.sim = sim ui.add_buttons_and_sliders() @@ -40,5 +35,5 @@ ui.detect_events() ui.detect_sliders() ui.do_movies() - ui.drawMenu() + ui.draw_menu() ui.show() \ No newline at end of file diff --git a/jes_creature.py b/jes_creature.py index af82df2..6d45bb7 100644 --- a/jes_creature.py +++ b/jes_creature.py @@ -14,18 +14,18 @@ def __init__(self, d, p_id_number, parent_species, _sim, _ui) -> None: self.calmState = None self.icons = [None]*2 self.icon_coor = None - self.IDNumber = p_id_number + self.id_number = p_id_number self.fitness = None self.rank = None self.living = True self.species = self.get_species(parent_species) self.sim = _sim self.ui = _ui - self.codonWithChange = None + self.codon_with_change = None def get_species(self, parent_species): if parent_species == -1: - return self.IDNumber + return self.id_number else: return parent_species @@ -142,13 +142,13 @@ def traits_to_color(self, dna, x, y, frame): alpha = min(max(int(155+traits[2]*100),64),255) #alpha can't go below 25% color_result = (red,green,255,alpha) - if self.codonWithChange is not None: + if self.codon_with_change is not None: next_green = 0 - if dna_index <= self.codonWithChange < dna_index+self.sim.traits_per_box: + if dna_index <= self.codon_with_change < dna_index+self.sim.traits_per_box: next_green = 1 prev_green = 0 - if dna_index_prev <= self.codonWithChange < dna_index_prev+self.sim.traits_per_box: + if dna_index_prev <= self.codon_with_change < dna_index_prev+self.sim.traits_per_box: prev_green = 1 green_ness = lerp(prev_green,next_green,prog) diff --git a/jes_dump.py b/jes_dump.py index 4b13624..e398a74 100644 --- a/jes_dump.py +++ b/jes_dump.py @@ -130,7 +130,7 @@ def doSpeciesInfo(self,nsp,best_of_each_species): sp.apex_pop = pop sp.reps[2] = best_of_each_species[s] # apex representative if pop > self.creature_count*0.10 and not sp.prominent: #prominent threshold - sp.becomeProminent() + sp.become_prominent() sp.reps[3] = best_of_each_species[s] # most-recent representative p1 = p2 diff --git a/jes_shapes.py b/jes_shapes.py index 8dd1ca3..fc8b1ae 100644 --- a/jes_shapes.py +++ b/jes_shapes.py @@ -129,7 +129,7 @@ def draw_species_circle(screen, s, coor, R, sim, species_info, font, should_draw screen.blit(tiny_icon,(cx-25,cy-11)) if should_draw_arrow: - ancestor_id = species_info[s].ancestorID + ancestor_id = species_info[s].ancestor_id if ancestor_id is None: draw_arrow(screen, (cx, -R * 2), (cx, cy), R, R / 2, color) else: diff --git a/jes_sim.py b/jes_sim.py index e5351d1..464fdd8 100644 --- a/jes_sim.py +++ b/jes_sim.py @@ -175,11 +175,11 @@ def do_species_info(self, nsp, best_of_each_species) -> None: info.apex_pop = pop info.reps[2] = best_of_each_species[sp] # apex representative if pop >= self.creature_count*self.S_NOTABLE and not info.prominent: #prominent threshold - info.becomeProminent() + info.become_prominent() def check_alap(self) -> None: - if self.ui.ALAPButton.setting == 1: # We're already ALAP-ing! - self.do_generation(self.ui.doGenButton) + if self.ui.alap_button.setting == 1: # We're already ALAP-ing! + self.do_generation(self.ui.do_gen_button) def do_generation(self, button): generation_start_time = time.time() #calculates how long each generation takes to run @@ -206,7 +206,7 @@ def do_generation(self, button): else: new_species_pops[species] = [1, None, None] if species not in best_of_each_species: - best_of_each_species[species] = self.creatures[gen][c].IDNumber + best_of_each_species[species] = self.creatures[gen][c].id_number self.do_species_info(new_species_pops, best_of_each_species) @@ -268,6 +268,6 @@ def mutate(self, parent, new_id) -> Creature: if new_creature.species != parent.species: self.species_info.append(SpeciesInfo(self, new_creature,parent)) - new_creature.codonWithChange = cwc + new_creature.codon_with_change = cwc return new_creature \ No newline at end of file diff --git a/jes_species_info.py b/jes_species_info.py index 8950b20..6ed9f6f 100644 --- a/jes_species_info.py +++ b/jes_species_info.py @@ -5,10 +5,10 @@ class SpeciesInfo: def __init__(self, _sim, me, ancestor): self.sim = _sim self.speciesID = me.species - self.ancestorID = None + self.ancestor_id = None self.level = 0 if ancestor is not None: - self.ancestorID = ancestor.species + self.ancestor_id = ancestor.species self.level = self.sim.species_info[ancestor.species].level+1 self.apex_pop = 0 @@ -17,40 +17,41 @@ def __init__(self, _sim, me, ancestor): self.prominent = False if ancestor is not None: - self.reps[0] = ancestor.IDNumber - self.reps[1] = me.IDNumber + self.reps[0] = ancestor.id_number + + self.reps[1] = me.id_number self.coor = None - def becomeProminent(self): # if you are prominent, all your ancestors become prominent. + def become_prominent(self): # if you are prominent, all your ancestors become prominent. self.prominent = True - self.insertIntoProminentSpeciesList() - if self.ancestorID is not None: # you have a parent - ancestor = self.sim.species_info[self.ancestorID] + self.insert_into_prominent_species_list() + if self.ancestor_id is not None: # you have a parent + ancestor = self.sim.species_info[self.ancestor_id] if not ancestor.prominent: - ancestor.becomeProminent() + ancestor.become_prominent() - def insertIntoProminentSpeciesList(self): + def insert_into_prominent_species_list(self): i = self.speciesID p = self.sim.prominent_species while len(p) <= self.level: # this level doesn't exist yet. Add new levels of the genealogy tree to acommodate you p.append([]) - pL = p[self.level] + p_l = p[self.level] insert_index = 0 - for index in range(len(pL)): # inefficient sorting thing, but there are <50 species so who cares - other = pL[index] - ancestorCompare = 0 if self.level == 0 else self.sim.species_info[other].ancestorID-self.ancestorID - if ancestorCompare == 0: #siblings + for index in range(len(p_l)): # inefficient sorting thing, but there are <50 species so who cares + other = p_l[index] + ancestor_compare = 0 if self.level == 0 else self.sim.species_info[other].ancestor_id - self.ancestor_id + if ancestor_compare == 0: #siblings if other < i: insert_index = index+1 else: #not siblings trick to avoid family trees tangling (all siblings should be adjacent) - if ancestorCompare < 0: + if ancestor_compare < 0: insert_index = index+1 - pL.insert(insert_index,i) + p_l.insert(insert_index,i) - def getWhen(self, index): + def get_when(self, index): return math.floor(self.reps[index] // self.sim.creature_count) - def getPerformance(self, sim, index): + def get_performance(self, sim, index): gen = math.floor(self.reps[index] // self.sim.creature_count) c = self.reps[index]%self.sim.creature_count creature = sim.creatures[gen][c] diff --git a/jes_ui.py b/jes_ui.py index 723e26b..ecb7e34 100644 --- a/jes_ui.py +++ b/jes_ui.py @@ -1,9 +1,12 @@ +from typing import Optional + import pygame from pygame import Surface from pygame.font import Font from enums import Color from jes_creature import Creature +from jes_species_info import SpeciesInfo from utils import species_to_color, species_to_name, dist_to_text, bound, get_dist, array_int_multiply from jes_dataviz import display_all_graphs, draw_all_graphs from jes_shapes import drawRingLight, draw_x, center_text, align_text, draw_species_circle @@ -83,26 +86,35 @@ def __init__(self, config: dict): self.storage_coor = (660, 52) self.running: bool = True + self.gen_slider: Slider = Optional[Slider] + self.show_creatures_button: Button = Optional[Button] + self.sort_button: Button = Optional[Button] + self.style_button: Button = Optional[Button] + self.sample_button: Button = Optional[Button] + self.do_gen_button: Button = Optional[Button] + self.alap_button: Button = Optional[Button] + + def add_buttons_and_sliders(self): - self.gen_slider = Slider(self, (40, self.window_height - 100, self.window_width - 80, 60, 140), 0, 0, 0, True, True, self.updateGenSlider) + self.gen_slider = Slider(self, (40, self.window_height - 100, self.window_width - 80, 60, 140), 0, 0, 0, True, True, self.update_gen_slider) button_coor = [] for i in range(6): button_coor.append((self.window_width - 1340 + 220 * i, self.window_height - self.menu_text_up, 200, 60)) - self.showCreaturesButton = Button(self, button_coor[0],["Show creatures", "Hide creatures"], self.toggleCreatures) - self.sortButton = Button(self,button_coor[1],["Sort by ID", "Sort by fitness", "Sort by weakness"], self.toggleSort) - self.styleButton = Button(self, button_coor[2], ["Big Icons", "Small Icons", "Species Tiles"], self.toggle_style) - self.sampleButton = Button(self, button_coor[3], ["Watch sample", "Stop sample"], self.start_sample) - self.doGenButton = Button(self, button_coor[4], ["Do a generation"], self.sim.do_generation) - self.ALAPButton = Button(self, button_coor[5], ["Turn on ALAP", "Turn off ALAP"], self.do_nothing) + self.show_creatures_button = Button(self, button_coor[0], ["Show creatures", "Hide creatures"], self.toggle_creatures) + self.sort_button = Button(self, button_coor[1], ["Sort by ID", "Sort by fitness", "Sort by weakness"], self.toggle_sort) + self.style_button = Button(self, button_coor[2], ["Big Icons", "Small Icons", "Species Tiles"], self.toggle_style) + self.sample_button = Button(self, button_coor[3], ["Watch sample", "Stop sample"], self.start_sample) + self.do_gen_button = Button(self, button_coor[4], ["Do a generation"], self.sim.do_generation) + self.alap_button = Button(self, button_coor[5], ["Turn on ALAP", "Turn off ALAP"], self.do_nothing) def reverse(self, i): return self.sim.creature_count-1-i def detect_mouse_motion(self): - if self.sampleButton.setting == 1: + if self.sample_button.setting == 1: return gen = self.gen_slider.val mouse_x, mouse_y = pygame.mouse.get_pos() @@ -111,13 +123,13 @@ def detect_mouse_motion(self): rel_mouse_x = mouse_x-self.cm_margin_1 rel_mouse_y = mouse_y-self.cm_margin_1 if 0 <= rel_mouse_x < self.mosaic_screen_width_creatures and 0 <= rel_mouse_y < self.mosaic_screen_height: - dim = self.mosaic_dim[self.styleButton.setting] + dim = self.mosaic_dim[self.style_button.setting] spacing = self.mosaic_screen_width_creatures / dim ix = min(int(rel_mouse_x/spacing),dim) iy = int(rel_mouse_y/spacing) i = iy*dim+ix if 0 <= i < self.sim.creature_count: - sort = self.sortButton.setting + sort = self.sort_button.setting if sort == 0 or gen >= len(self.sim.rankings): new_clh = [0,i,i] elif sort == 1: @@ -194,18 +206,18 @@ def draw_creature_mosaic(self, gen) -> None: for c in range(self.sim.creature_count): i = c if self.sim.creatures[gen][c].rank is not None: - if self.sortButton.setting == 1: + if self.sort_button.setting == 1: i = self.sim.creatures[gen][c].rank - elif self.sortButton.setting == 2: + elif self.sort_button.setting == 2: i = self.reverse(self.sim.creatures[gen][c].rank) - dim = self.mosaic_dim[self.styleButton.setting] + dim = self.mosaic_dim[self.style_button.setting] x = i % dim y = i//dim creature = self.sim.creatures[gen][c] spacing = self.mosaic_screen_width_creatures / dim creature.icon_coor = (x * spacing + self.cm_margin_2, y * spacing + self.cm_margin_2, spacing, spacing) if creature.icon_coor[1] < self.mosaic_screen.get_height(): - s = self.styleButton.setting + s = self.style_button.setting if s <= 1: self.mosaic_screen.blit(creature.icons[s], creature.icon_coor) elif s == 2: @@ -219,10 +231,10 @@ def draw_info_bar_creature(self, creature: Creature) -> None: x_center = int(self.info_w * 0.5) self.info_bar_screen.fill(Color.MOSAIC) - stri: list[str] = [f"Creature #{creature.IDNumber}", f"Species: {species_to_name(creature.species, self)}", "Untested"] + stri: list[str] = [f"Creature #{creature.id_number}", f"Species: {species_to_name(creature.species, self)}", "Untested"] if creature.fitness is not None: fate = "Living" if creature.living else "Killed" - stri = [f"Creature #{creature.IDNumber}",f"Species: {species_to_name(creature.species, self)}", f"Fitness: {dist_to_text(creature.fitness, True, self.sim.units_per_meter)}", f"Rank: {creature.rank + 1} - {fate}"] + stri = [f"Creature #{creature.id_number}", f"Species: {species_to_name(creature.species, self)}", f"Fitness: {dist_to_text(creature.fitness, True, self.sim.units_per_meter)}", f"Rank: {creature.rank + 1} - {fate}"] for i in range(len(stri)): color = Color.WHITE @@ -248,20 +260,20 @@ def draw_movie_grid(self, screen, coor, mask, titles, colors, font) -> None: def draw_movie_quad(self, species) -> None: l = 4 - info = self.sim.species_info[species] - a_name = species_to_name(info.ancestorID, self) + info: SpeciesInfo = self.sim.species_info[species] + a_name = species_to_name(info.ancestor_id, self) s_name = species_to_name(species, self) titles = ["Ancestor","First","Apex","Last"] mask = [True] * l for i in range(l): - if (info.ancestorID is None and i == 0) or (i >= 2 and info.getWhen(i) == info.getWhen(i-1)): + if (info.ancestor_id is None and i == 0) or (i >= 2 and info.get_when(i) == info.get_when(i - 1)): mask[i] = False continue stri = a_name if i == 0 else s_name - performance = info.getPerformance(self.sim, i) - titles[i] = f"G{info.getWhen(i)}: {titles[i]} {stri} ({dist_to_text(performance, True, self.sim.units_per_meter)})" + performance = info.get_performance(self.sim, i) + titles[i] = f"G{info.get_when(i)}: {titles[i]} {stri} ({dist_to_text(performance, True, self.sim.units_per_meter)})" coor = (self.cm_margin_1 + self.mosaic_screen_width_creatures, 0) self.draw_movie_grid(self.screen, coor, mask, titles, [Color.GRAYISH] * l, self.tiny_font) @@ -270,7 +282,7 @@ def draw_movie_quad(self, species) -> None: def draw_info_bar_species(self, species) -> None: self.info_bar_screen.fill(Color.MOSAIC) info = self.sim.species_info[species] - a_name = species_to_name(info.ancestorID, self) + a_name = species_to_name(info.ancestor_id, self) s_name = species_to_name(species, self) now = min(self.gen_slider.val, len(self.sim.species_pops) - 1) now_pop = 0 @@ -278,13 +290,13 @@ def draw_info_bar_species(self, species) -> None: if species in self.sim.species_pops[now]: now_pop = self.sim.species_pops[now][species][0] extinct_string = "" - strings = [f"Species {s_name}",f"Ancestor {a_name}",f"Lifespan: G{info.getWhen(1)} - G{info.getWhen(3)}{extinct_string}", f"Population: {info.apex_pop} at apex (G{info.getWhen(2)}) | {now_pop} now (G{now})"] + strings = [f"Species {s_name}",f"Ancestor {a_name}",f"Lifespan: G{info.get_when(1)} - G{info.get_when(3)}{extinct_string}", f"Population: {info.apex_pop} at apex (G{info.get_when(2)}) | {now_pop} now (G{now})"] colors = [Color.WHITE]*len(strings) colors[0] = species_to_color(species, self) - if info.ancestorID is None: + if info.ancestor_id is None: strings[1] = "Primordial species" else: - colors[1] = species_to_color(info.ancestorID, self) + colors[1] = species_to_color(info.ancestor_id, self) for i in range(len(strings)): x_center = int(self.info_w * (0.5 if i == 3 else 0.3)) center_text(self.info_bar_screen, strings[i], x_center, self.movie_single_dim[1] + 40 + 42 * i, colors[i], self.small_font) @@ -313,8 +325,8 @@ def draw_menu_text(self) -> None: self.screen.blit(gen_surface,(40,y)) if self.species_storage is not None: s = self.species_storage - R = self.genealogy_coor[4] - draw_species_circle(self.screen, s, self.storage_coor, R, self.sim, self.sim.species_info, self.tiny_font, False, self) + r = self.genealogy_coor[4] + draw_species_circle(self.screen, s, self.storage_coor, r, self.sim, self.sim.species_info, self.tiny_font, False, self) def r_to_rank(self,r): return 0 if r == 0 else (self.sim.creature_count - 1 if r == 2 else self.sim.creature_count // 2) @@ -327,34 +339,39 @@ def draw_previews(self) -> None: r_i = self.r_to_rank(r) index = self.sim.rankings[gen][r_i] creature = self.sim.creatures[gen][index] - DIM = (self.preview_locations[r][2], self.preview_locations[r][3]) - preview = creature.draw_icon(DIM, Color.MOSAIC, self.sim.beat_fade_time) - center_text(preview, f"{names[r]} creature", DIM[0] / 2, DIM[1] - 20, Color.WHITE, self.small_font) + dim = (self.preview_locations[r][2], self.preview_locations[r][3]) + preview = creature.draw_icon(dim, Color.MOSAIC, self.sim.beat_fade_time) + center_text(preview, f"{names[r]} creature", dim[0] / 2, dim[1] - 20, Color.WHITE, self.small_font) align_text(preview, dist_to_text(creature.fitness, True, self.sim.units_per_meter), 10, 20, Color.WHITE, self.small_font, 0.0, None) self.screen.blit(preview, (self.preview_locations[r][0], self.preview_locations[r][1])) - - - - def do_movies(self): + + def do_movies(self) -> None: l = len(self.visual_sim_memory) mscale = [1, 1, 0.5, 0.70] # movie screen scale - if self.sampleButton.setting == 1: + if self.sample_button.setting == 1: self.sample_frames += 1 if self.sample_frames >= self.sim.trial_time+self.sample_freeze_time: - self.startSampleHelper() + self.start_sample_helper() + for i in range(l): if self.visual_sim_memory[i][2] < self.sim.trial_time: self.visual_sim_memory[i] = self.sim.simulate_run(self.visual_sim_memory[i], 1, False) - dim = array_int_multiply(self.movie_single_dim, mscale[self.creature_location_highlight[0]]) - self.movie_screens[i] = pygame.Surface(dim, pygame.SRCALPHA, 32) - - node_arr, _, current_frame = self.visual_sim_memory[i] - s = dim[0]/(self.sim.CW+2)*0.5 # visual transform scale - - average_x = np.mean(node_arr[:,:,:,0]) - transform = [dim[0]/2 - average_x*s,dim[1]*0.8,s] - self.creature_highlight[i].draw_creature(self.movie_screens[i], node_arr[0], current_frame, transform, True, (i == 0)) + try: + dim = array_int_multiply(self.movie_single_dim, mscale[self.creature_location_highlight[0]]) + + + self.movie_screens[i] = pygame.Surface(dim, pygame.SRCALPHA, 32) + + node_arr, _, current_frame = self.visual_sim_memory[i] + s = dim[0]/(self.sim.CW+2)*0.5 # visual transform scale + + average_x = np.mean(node_arr[:,:,:,0]) + transform = [dim[0]/2 - average_x*s,dim[1]*0.8,s] + self.creature_highlight[i].draw_creature(self.movie_screens[i], node_arr[0], current_frame, transform, True, (i == 0)) + + except Exception as _: + pass def get_highlighted_species(self): gen = self.gen_slider.val @@ -391,41 +408,45 @@ def detect_events(self) -> None: draw_all_graphs(self.sim, self) self.clear_movies() self.detect_mouse_motion() + elif event.key == 13: # pressing Enter self.sim.do_generation(None) + elif event.key == 113: # pressing 'Q' - self.showCreaturesButton.timeOfLastClick = time.time() - self.showCreaturesButton.setting = 1-self.showCreaturesButton.setting - self.toggleCreatures(self.showCreaturesButton) + self.show_creatures_button.timeOfLastClick = time.time() + self.show_creatures_button.setting = 1 - self.show_creatures_button.setting + self.toggle_creatures(self.show_creatures_button) elif event.type == pygame.MOUSEBUTTONDOWN: - mouseX, mouseY = pygame.mouse.get_pos() + mouse_x, mouse_y = pygame.mouse.get_pos() for slider in self.slider_list: s_x, s_y, s_w, s_h, s_dw = slider.dim - if mouseX >= s_x and mouseX < s_x+s_w and mouseY >= s_y and mouseY < s_y+s_h: + if s_x <= mouse_x < s_x+s_w and s_y <= mouse_y < s_y + s_h: self.slider_drag = slider break for button in self.button_list: s_x, s_y, s_w, s_h = button.dim - if mouseX >= s_x and mouseX < s_x+s_w and mouseY >= s_y and mouseY < s_y+s_h: + if s_x <= mouse_x < s_x+s_w and s_y <= mouse_y < s_y + s_h: button.click() elif event.type == pygame.MOUSEBUTTONUP: if self.slider_drag is not None: self.slider_drag.updateVal() self.slider_drag = None - def drawMenu(self): + def draw_menu(self) -> None: self.screen.blit(self.background_pic, (0, 0)) self.draw_menu_text() self.draw_previews() + display_all_graphs(self.screen, self.sim, self) - self.drawSlidersAndButtons() + + self.draw_sliders_and_buttons() self.display_creature_mosaic(self.screen) self.display_movies(self.screen) def display_creature_mosaic(self, screen): - time_since_last_press = time.time()-self.showCreaturesButton.timeOfLastClick + time_since_last_press = time.time()-self.show_creatures_button.timeOfLastClick pan_time = 0.2 frac = bound(time_since_last_press/pan_time) @@ -435,7 +456,7 @@ def display_creature_mosaic(self, screen): if not self.mosaic_visible and frac < 1: self.screen.blit(self.mosaic_screen, (self.cm_margin_1, self.cm_margin_1 - self.mosaic_screen.get_height() * frac)) - def display_movies(self, screen): + def display_movies(self, screen) -> None: if self.creature_location_highlight[0] is None: return @@ -447,6 +468,7 @@ def display_movies(self, screen): sp = self.creature_highlight[i].species species_names[i] = species_to_name(sp, self) species_colors[i] = species_to_color(sp, self) + self.draw_movie_grid(screen, (0, 0), [True] * lms, species_names, species_colors, self.small_font) return @@ -469,9 +491,9 @@ def display_movies(self, screen): def detect_sliders(self): if self.slider_drag is not None: - mouseX, mouseY = pygame.mouse.get_pos() + mouse_x, mouse_y = pygame.mouse.get_pos() s_x, s_y, s_w, s_h, s_dw = self.slider_drag.dim - ratio = bound(((mouseX-s_dw*0.5)-s_x)/(s_w-s_dw)) + ratio = bound(((mouse_x-s_dw*0.5)-s_x)/(s_w-s_dw)) s_range = self.slider_drag.val_max - self.slider_drag.val_min self.slider_drag.tval = ratio * s_range + self.slider_drag.val_min @@ -480,20 +502,20 @@ def detect_sliders(self): if self.slider_drag.update_live: self.slider_drag.updateVal() - def drawSlidersAndButtons(self): + def draw_sliders_and_buttons(self): for slider in self.slider_list: slider.drawSlider(self.screen) for button in self.button_list: button.draw_button(self.screen, self.small_font) # Button and slider functions - def updateGenSlider(self, gen): + def update_gen_slider(self, gen): self.draw_creature_mosaic(gen) - def toggleCreatures(self, button): + def toggle_creatures(self, button): self.mosaic_visible = (button.setting == 1) - def toggleSort(self, button): + def toggle_sort(self, button): self.draw_creature_mosaic(self.gen_slider.val) def toggle_style(self, button): @@ -505,22 +527,24 @@ def do_nothing(self, button): def start_sample(self, button): if button.setting == 1: self.sample_i = 0 - self.startSampleHelper() + self.start_sample_helper() - def startSampleHelper(self): - L = 8 + def start_sample_helper(self): + l = 8 self.creature_highlight = [] self.visual_sim_memory = [] self.movie_screens = [] self.creature_location_highlight = [3, 0] self.sample_frames = 0 - for i in range(L): + for i in range(l): gen = self.gen_slider.val c = (self.sample_i+i)%self.sim.creature_count self.creature_highlight.append(self.sim.creatures[gen][c]) self.visual_sim_memory.append(self.sim.simulate_import(gen, c, c + 1, True)) self.movie_screens.append(None) - self.sample_i += L - - def show(self): + + self.sample_i += l + + @staticmethod + def show(): pygame.display.flip() From a1c90b0bb0b23a0efb44e0ef25c43cc645d39edc Mon Sep 17 00:00:00 2001 From: Bruno Rodrigues Date: Thu, 16 Jan 2025 13:20:59 +0000 Subject: [PATCH 3/4] created config files for UI, SIM, rename variables/methods/functions using underscore. overall cleanup, Color Enum, --- jes.py | 4 ---- jes_ui.py | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/jes.py b/jes.py index 64e36db..5f74610 100644 --- a/jes.py +++ b/jes.py @@ -12,10 +12,6 @@ if c_input == "": c_input = "250" -# Simulation -# population size is 250 here, because that runs faster. You can increase it to 500 to replicate what was in my video, but do that at your own risk! - - sim: Sim = Sim(creature_count=int(c_input), config=sim_config) diff --git a/jes_ui.py b/jes_ui.py index ecb7e34..ff6b80c 100644 --- a/jes_ui.py +++ b/jes_ui.py @@ -370,7 +370,7 @@ def do_movies(self) -> None: transform = [dim[0]/2 - average_x*s,dim[1]*0.8,s] self.creature_highlight[i].draw_creature(self.movie_screens[i], node_arr[0], current_frame, transform, True, (i == 0)) - except Exception as _: + except TypeError as _: pass def get_highlighted_species(self): From 665aef12471d079ca1904f210d927671052b129d Mon Sep 17 00:00:00 2001 From: Bruno Rodrigues Date: Thu, 16 Jan 2025 13:51:48 +0000 Subject: [PATCH 4/4] created config files for UI, SIM, rename variables/methods/functions using underscore. overall cleanup, Color Enum, --- jes_button.py | 17 +-- jes_creature.py | 10 +- jes_dataviz.py | 9 +- jes_dump.py | 254 -------------------------------------------- jes_shapes.py | 91 ++++++++-------- jes_sim.py | 2 +- jes_slider.py | 33 +++--- jes_species_info.py | 6 +- jes_ui.py | 54 ++++++---- utils.py | 1 + 10 files changed, 120 insertions(+), 357 deletions(-) delete mode 100644 jes_dump.py diff --git a/jes_button.py b/jes_button.py index 0a0504b..a7021f3 100644 --- a/jes_button.py +++ b/jes_button.py @@ -1,17 +1,18 @@ +from typing import Callable + import pygame from jes_shapes import center_text import time class Button: - def __init__(self, ui, pdim, pnames, pfunc): + def __init__(self, pdim, pnames, callback_func: Callable) -> None: self.dim = pdim # Dim is a list of 4 parameters: x, y, width, height self.names = pnames self.setting = 0 - self.timeOfLastClick = 0 - self.func = pfunc - ui.button_list.append(self) - - def draw_button(self, screen, font): + self.time_of_last_click = 0 + self._func = callback_func + + def draw_button(self, screen, font) -> None: x, y, w, h = self.dim name = self.names[self.setting] @@ -25,5 +26,5 @@ def draw_button(self, screen, font): def click(self) -> None: self.setting = (self.setting + 1)%len(self.names) - self.timeOfLastClick = time.time() - self.func(self) \ No newline at end of file + self.time_of_last_click = time.time() + self._func(self) \ No newline at end of file diff --git a/jes_creature.py b/jes_creature.py index 6d45bb7..31cdb52 100644 --- a/jes_creature.py +++ b/jes_creature.py @@ -3,7 +3,7 @@ from enums import Color from utils import array_lerp, dist_to_text, species_to_color, list_lerp, lerp -from jes_shapes import drawRect, drawTextRect, center_text, draw_clock +from jes_shapes import draw_rect, draw_text_rect, center_text, draw_clock import numpy as np import math import random @@ -44,17 +44,17 @@ def draw_cell(self, surface, node_state, frame, transform, x, y) -> None: def draw_environment(self, surface, transform) -> None: #sky - drawRect(surface, transform,None, Color.BLACK) + draw_rect(surface, transform, None, Color.BLACK) #signs font = self.ui.big_font if transform[2] >= 50 else self.ui.small_font for meters in range(0,3000,100): u = meters*self.sim.units_per_meter - drawRect(surface,transform,[u-0.2,-6,u+0.2,0], Color.SIGN) - drawTextRect(surface,transform,[u-1.5,-6.8,u+1.5,-5.4], Color.SIGN, Color.WHITE,f"{meters}cm",font) + draw_rect(surface, transform, [u - 0.2, -6, u + 0.2, 0], Color.SIGN) + draw_text_rect(surface, transform, [u - 1.5, -6.8, u + 1.5, -5.4], Color.SIGN, Color.WHITE, f"{meters}cm", font) #ground - drawRect(surface,transform,[None,0,None,None], Color.WHITE) + draw_rect(surface, transform, [None, 0, None, None], Color.WHITE) def draw_creature(self, surface, node_state, frame, transform, draw_labels:bool, should_draw_clock: bool): if draw_labels: diff --git a/jes_dataviz.py b/jes_dataviz.py index 99ca36a..b85eac4 100644 --- a/jes_dataviz.py +++ b/jes_dataviz.py @@ -8,14 +8,7 @@ import bisect -# BLACK = (0,0,0) -# GRAY25 = (70,70,70) -# GRAY50 = (128,128,128) -# WHITE = (255,255,255) -# RED = (255,0,0) -# GREEN = (0,255,0) - -def draw_all_graphs(sim, ui): +def draw_all_graphs(sim, ui) -> None: draw_line_graph(sim.percentiles, ui.graph, [70, 0, 30, 30], sim.units_per_meter, ui.small_font) draw_sac(sim.species_pops, ui.sac, [70, 0], ui) draw_gene_graph(sim.species_info, sim.prominent_species, ui.gene_graph, sim, ui, ui.tiny_font) diff --git a/jes_dump.py b/jes_dump.py deleted file mode 100644 index e398a74..0000000 --- a/jes_dump.py +++ /dev/null @@ -1,254 +0,0 @@ -"""def getSpecies(self): - BOXES = CW*CH*BEATS_PER_CYCLE - TOTAL = TRAITS_PER_BOX*BOXES - dna_reshaped = np.zeros((TRAITS_PER_BOX,BOXES)) - for t in range(TRAITS_PER_BOX): - for u in range(BOXES): - dna_reshaped[t][u] = self.dna[u*TRAITS_PER_BOX+t] - dna_up_down = np.zeros((TOTAL)) - for t in range(TRAITS_PER_BOX): - for u in range(BOXES): - val = 1 if dna_reshaped[t][u] >= np.mean(dna_reshaped[t]) else 0 - dna_up_down[t*BOXES+u] = val - - pre_stri = DUDtoPRESTRI(dna_up_down[128:144], 16) #I'm only taking rigidity on Frame 1 into account now. - self.dudly = pre_stri - _hex = sha256(pre_stri.encode('utf-8')).hexdigest() - return int(_hex, 16)%SPECIES_COUNT""" - - - - - """if data[g,s] >= 1 or (g >= 1 and data[g-1,s] >= 1): - y1a = H/2 - y1b = H/2 - y2a = np.sum(data[g,0:s])*FAC - y2b = np.sum(data[g,0:s+1])*FAC - if g >= 1: - y1a = np.sum(data[g-1,0:s])*FAC - y1b = np.sum(data[g-1,0:s+1])*FAC - - pygame.draw.polygon(sac,speciesToColor(s),points)""" - - - -""" -for s in range(CREATURE_COUNT): - d = species_pops[a2,s] - if d >= CREATURE_COUNT*0.05: - speciesI = runningTotal+d*0.5 - speciesY = 560+300*speciesI/CREATURE_COUNT - name = species_to_name(s) - color = speciesToColor(s) - alignText(screen, f"{name}: {int(d)}", 860+880*frac, speciesY, color, smallFont, 0.0, True) - runningTotal += d - """ - - - - - """CREATURE_COUNT = 250 -SPECIES_COUNT = CREATURE_COUNT -STABILIZATION_TIME = 200 -TRIAL_TIME = 300 -BEAT_TIME = 20 -BEAT_FADE_TIME = 5 -CW = 4 -CH = 4 -CREATURE_DIM = [CW,CH] -BEATS_PER_CYCLE = 3 -NODE_COOR_COUNT = 4 - -CEILING_Y = -10000000 -FLOOR_Y = 0 -GROUND_FRICTION_COEF = 25 # the higher the number, the more ground friction is applied. - -GRAVITY_ACCELERATION_COEF = 0.002 -CALMING_FRICTION_COEF = 0.7 -TYPICAL_FRICTION_COEF = 0.8 -MUSCLE_COEF = 0.08 - -TRAITS_PER_BOX = 3 -TRAITS_EXTRA = 1 # heartbeat (time) -TOTAL_TRAIT_COUNT = CW*CH*BEATS_PER_CYCLE*TRAITS_PER_BOX+TRAITS_EXTRA""" - - - -""" -W_W = 1920 -W_H = 1078 - -MOVIE_SINGLE_DIM = (650,650) -GRAPH_DIM = (1000,500) -graph = pygame.Surface(GRAPH_DIM, pygame.SRCALPHA, 32) -SAC_DIM = (1000,300) -sac = pygame.Surface(SAC_DIM, pygame.SRCALPHA, 32) - - -CREATURES_PER_ROW = 14 -CREATURES_PER_COLUMN = 8 -MENU_TEXT_UP = 180""" - - -def DUDtoPRESTRI(DUD, TOTAL): - stri = "" - PER = 1 - for i in range(math.ceil(TOTAL/PER)): - _sum = 0 - for j in range(PER): - _sum *= 2 - if i*PER+j < len(DUD) and DUD[i*PER+j] == 1: - _sum += 1 - stri += chr(_sum+41) - return stri - - - -BG_COLOR = (0,0,0) - RED = (255,0,0) - GREEN = (0,255,0) - GRAYISH = (108,118,155) - BLACK = (0,0,0) - WHITE = (255,255,255) - GRAY25 = (70,70,70) - GRAY50 = (128,128,128) - MOSAIC_BG_COLOR = (80,80,80) - BRIGHT = (255,255,0,200) - SIGN_COLOR = (150,100,50) - - - def doSpeciesInfo(self,nsp,best_of_each_species): - p1 = 0 # 'p' stands for 'pointer' - while p1 < self.creature_count: - s = data[p1] - p2 = bisect.bisect(data, s+0.5) - pop = p2-p1 - - sp = self.species_info[s] - sp.latest_pop = pop - if pop > sp.apex_pop: # This species reached its highest population - sp.apex_pop = pop - sp.reps[2] = best_of_each_species[s] # apex representative - if pop > self.creature_count*0.10 and not sp.prominent: #prominent threshold - sp.become_prominent() - - sp.reps[3] = best_of_each_species[s] # most-recent representative - p1 = p2 - - - - p1 = 0 # 'p' stands for 'pointer' - while p1 < c_count: - s = data[g,p1] - p2 = bisect.bisect(data[g], s+0.5) - points = [[x1,H/2],[x1,H/2],[x2,H-p2*FAC],[x2,H-p1*FAC]] - pygame.draw.polygon(sac,speciesToColor(s),points) - p1 = p2 - - - - - - data = sim.species_pops[g] - p1 = p2 = 0 - record = 0 - recordHolder = -1 - while p1 < sim.creature_count: - s = data[p1] - p2 = bisect.bisect(data, s+0.5) - pop = p2-p1 - if pop > record: - record = pop - recordHolder = s - p1 = p2 - return recordHolder - - - -p1 = p2 = q1 = q2 = i_start - H = sac.get_height() - while q1 < i_end: - s = data[g1,q1] - q2 = bisect.bisect(data[g1], s+0.5) - p1 = bisect.bisect(data[g2], s-0.5) - if p1 != p2 and level == 0: #there was a gap. (A species that existed in the previous gen, but not in this one) - trapezoidHelper(sac, data, g2, g1, p2, p1, x2, x1, FAC, 1) - p2 = bisect.bisect(data[g2], s+0.5) - points = [[x1,H-p1*FAC],[x1,H-p2*FAC],[x2,H-q2*FAC],[x2,H-q1*FAC]] - pygame.draw.polygon(sac,speciesToColor(s),points) - q1 = q2 - - - -p1 = p2 = 0 - while p1 < sim.creature_count: - s = sim.species_pops[a2,p1] - p2 = bisect.bisect(sim.species_pops[a2], s+0.5) - pop = p2-p1 - if pop >= sim.creature_count*sim.S_VISIBLE: - speciesI = (p1+p2)/2 - speciesY = 560+300*(1 - speciesI / sim.creature_count) - name = species_to_name(s) - color = speciesToColor(s) - OUTLINE = ui.WHITE if s == top_species else None - alignText(screen, f"{name}: {int(pop)}", lineX + 10, speciesY, color, ui.small_font, 0.0, [ui.BLACK, OUTLINE]) - p1 = p2 - - -"""top_species = getTopSpecies(sim, a2) - pop = data[a2][top_species] - speciesI = (pop[1]+pop[2])/2 - speciesY = 560+300*(1-speciesI/sim.c_count) - name = species_to_name(s) - color = speciesToColor(s) - OUTLINE = ui.WHITE if s == top_species else None - alignText(screen, f"{name}: {int(pop)}", lineX+10, speciesY, color, ui.smallFont, 0.0, [ui.BLACK,OUTLINE]) - - p1 = p2 = 0 - while p1 < sim.c_count: - s = sim.species_pops[a2,p1] - p2 = bisect.bisect(sim.species_pops[a2], s+0.5) - pop = p2-p1 - if pop >= sim.c_count*sim.S_VISIBLE: - speciesI = (p1+p2)/2 - speciesY = 560+300*(1-speciesI/sim.c_count) - name = species_to_name(s) - color = speciesToColor(s) - OUTLINE = ui.WHITE if s == top_species else None - alignText(screen, f"{name}: {int(pop)}", lineX+10, speciesY, color, ui.smallFont, 0.0, [ui.BLACK,OUTLINE]) - p1 = p2""" - - - - p1 = p2 = 0 - - while p1 < sim.creature_count: - s = sim.species_pops[a2,p1] - p2 = bisect.bisect(sim.species_pops[a2], s+0.5) - info = sim.species_info[s] - if info.prominent: - pop = p2-p1 - circle_count = 1 - if s == top_species: - circle_count += 1 - for c in range(circle_count): - cx = info.coor[0]+ui.genealogy_coor[0] - cy = info.coor[1]+ui.genealogy_coor[1] - pygame.draw.circle(screen, ui.WHITE, (cx,cy), R+3+6*c, 3) - p1 = p2 - - - - """for i in range(L): - if info.ancestorID is None and i == 0: - continue - if : # Two movies are showing the same thing, which we don't need to do - continue - ms = self.movieScreens[i] - MSCALE = int(math.sqrt(L)) - W = ms.get_width() - H = ms.get_height() - x = (i%MSCALE)*W - y = (i//MSCALE)*H - self.infoBarScreen.blit(ms,(x,y)) - centerText(self.infoBarScreen, titles[i], x+W/2, y+H-30, self.GRAYISH, self.tinyFont)""" \ No newline at end of file diff --git a/jes_shapes.py b/jes_shapes.py index fc8b1ae..16c3543 100644 --- a/jes_shapes.py +++ b/jes_shapes.py @@ -2,63 +2,67 @@ import math import copy +from pygame import Surface + from enums import Color from utils import lerp, species_to_color, species_to_name import numpy as np import time -def drawTextRect(surface,transform,coor,color1,color2,text,font): +def draw_text_rect(surface, transform, coor, color1, color2, text, font): tx, ty, s = transform x1,y1,x2,y2 = coor - drawRect(surface,transform,coor,color1) - centerX = (x1+x2)/2 - centerY = (y1+y2)/2 - text_x = centerX*s+tx - text_y = centerY*s+ty + draw_rect(surface, transform, coor, color1) + center_x = (x1+x2)/2 + center_y = (y1+y2)/2 + text_x = center_x*s+tx + text_y = center_y*s+ty center_text(surface, text, text_x, text_y, color2, font) -def drawRect(surface,transform,coor,color): - W = surface.get_width() - H = surface.get_height() - if coor == None: +def draw_rect(surface, transform, coor, color): + w = surface.get_width() + h = surface.get_height() + if coor is None: x1 = y1 = x2 = y2 = None else: x1,y1,x2,y2 = coor tx, ty, s = transform ax1 = 0 if x1 is None else tx+x1*s ay1 = 0 if y1 is None else ty+y1*s - ax2 = W if x2 is None else tx+x2*s - ay2 = H if y2 is None else ty+y2*s - if ax1 < W and ax2 > 0 and ay1 < H and ay2 > 0: + ax2 = w if x2 is None else tx+x2*s + ay2 = h if y2 is None else ty+y2*s + if ax1 < w and ax2 > 0 and ay1 < h and ay2 > 0: pygame.draw.rect(surface,color,(ax1,ay1,ax2-ax1,ay2-ay1)) -def drawRingLight(w, h, thickness): +def draw_ring_light(w, h, thickness) -> Surface: s = math.sin(time.time()*(2*math.pi)*3)*0.5+0.5 - BRIGHT = (255*s,255*s,0,200) - ringlight = pygame.Surface((w,h), pygame.SRCALPHA, 32) - pygame.draw.rect(ringlight,BRIGHT,(0,0,w,thickness)) - pygame.draw.rect(ringlight,BRIGHT,(0,h-thickness,w,thickness)) - pygame.draw.rect(ringlight,BRIGHT,(0,0,thickness,h)) - pygame.draw.rect(ringlight,BRIGHT,(w-thickness,0,thickness,h)) - return ringlight + bright = (255*s,255*s,0,200) + ring_light: Surface = pygame.Surface((w,h), pygame.SRCALPHA, 32) + pygame.draw.rect(ring_light,bright,(0,0,w,thickness)) + pygame.draw.rect(ring_light,bright,(0,h-thickness,w,thickness)) + pygame.draw.rect(ring_light,bright,(0,0,thickness,h)) + pygame.draw.rect(ring_light,bright,(w-thickness,0,thickness,h)) + + return ring_light -def draw_x(iconCoor, I, color, screen): +def draw_x(icon_coor, i, color, screen) -> None: for L in range(2): - i1 = I*0.02 - i2 = I*0.06+3 - points = [[i1,i2],[i2,i1],[I-i1,I-i2],[I-i2,I-i1]] + i1 = i * 0.02 + i2 = i * 0.06 + 3 + points = [[i1,i2], [i2,i1], [i - i1, i - i2], [i - i2, i - i1]] for P in points: if L == 1: - P[0] = I-P[0] - P[0] += iconCoor[0] - P[1] += iconCoor[1] + P[0] = i - P[0] + P[0] += icon_coor[0] + P[1] += icon_coor[1] + pygame.draw.polygon(screen,color,points) -def center_text(theScreen, stri, x, y, color, font) -> None: - align_text(theScreen, stri, x, y, color, font, 0.5, None) +def center_text(the_screen, stri, x, y, color, font) -> None: + align_text(the_screen, stri, x, y, color, font, 0.5, None) -def right_text(theScreen, stri, x, y, color, font) -> None: - align_text(theScreen, stri, x, y, color, font, 1.0, None) +def right_text(the_screen, stri, x, y, color, font) -> None: + align_text(the_screen, stri, x, y, color, font, 1.0, None) def expand(coor, amount): return [coor[0]-amount, coor[1]-amount, coor[2]+amount*2, coor[3]+amount*2] @@ -73,18 +77,19 @@ def align_text(the_screen, stri, x, y, color, font, align, bg_color) -> None: pygame.draw.rect(the_screen, bg_color[0], coor) the_screen.blit(text_surface, coor) -def draw_clock(surface, coor, the_ratio, text, font) -> None: - GRAYISH = (115,125,160) +def draw_clock(surface, coor: tuple, the_ratio, text, font) -> None: + grayish_color: tuple[int, int, int] = (115,125,160) x,y,r = coor - P = 30 - for p in range(P): - ratio1 = p/P - ratio2 = (p+1)/P + my_range: int = 30 + + for p in range(my_range): + ratio1 = p/my_range + ratio2 = (p+1)/my_range ang1 = (ratio1-0.25)*2*math.pi ang2 = (ratio2-0.25)*2*math.pi points = [[x,y],[x+r*math.cos(ang1),y+r*math.sin(ang1)],[x+r*math.cos(ang2),y+r*math.sin(ang2)]] - pygame.draw.polygon(surface, GRAYISH, points) + pygame.draw.polygon(surface, grayish_color, points) if the_ratio > ratio2: pygame.draw.polygon(surface, Color.WHITE, points) @@ -115,13 +120,13 @@ def draw_arrow(screen, _start, _end, margin, head, color) -> None: flare = [near_end[0]+math.cos(new_angle)*head, near_end[1]+math.sin(new_angle)*head] pygame.draw.line(screen, color, near_end, flare, width=2) -def draw_species_circle(screen, s, coor, R, sim, species_info, font, should_draw_arrow: bool, ui) -> None: +def draw_species_circle(screen, s, coor, radius, sim, species_info, font, should_draw_arrow: bool, ui) -> None: color = species_to_color(s, ui) name = species_to_name(s, ui) info = species_info[s] cx, cy = coor - pygame.draw.circle(screen,color,coor,R) + pygame.draw.circle(screen, color, coor, radius) center_text(screen, name, cx, cy - 22, (0, 0, 0), font) creature = sim.get_creature_with_id(info.reps[2]) @@ -131,6 +136,6 @@ def draw_species_circle(screen, s, coor, R, sim, species_info, font, should_draw if should_draw_arrow: ancestor_id = species_info[s].ancestor_id if ancestor_id is None: - draw_arrow(screen, (cx, -R * 2), (cx, cy), R, R / 2, color) + draw_arrow(screen, (cx, -radius * 2), (cx, cy), radius, radius / 2, color) else: - draw_arrow(screen, species_info[ancestor_id].coor, info.coor, R, R / 2, color) \ No newline at end of file + draw_arrow(screen, species_info[ancestor_id].coor, info.coor, radius, radius / 2, color) \ No newline at end of file diff --git a/jes_sim.py b/jes_sim.py index 464fdd8..83086de 100644 --- a/jes_sim.py +++ b/jes_sim.py @@ -250,7 +250,7 @@ def do_generation(self, button): self.creatures[gen+1][c].icons[i] = self.creatures[gen+1][c].draw_icon(self.ui.icon_dim[i], Color.MOSAIC, self.beat_fade_time) self.ui.gen_slider.val_max = gen + 1 - self.ui.gen_slider.manualUpdate(gen) + self.ui.gen_slider.manual_update(gen) self.last_gen_run_time = time.time() - generation_start_time self.ui.creature_location_highlight = [None, None, None] diff --git a/jes_slider.py b/jes_slider.py index be4008b..d18b57b 100644 --- a/jes_slider.py +++ b/jes_slider.py @@ -1,7 +1,9 @@ +from typing import Callable + import pygame class Slider: - def __init__(self,ui,pdim,pval,pval_min,pval_max,psnap_to_int,pupdate_live,pupdate_function): + def __init__(self, pdim, callback_func: Callable, pval: int = 0, pval_min: int = 0, pval_max: int = 0, psnap_to_int: bool = True, pupdate_live: bool = True ) -> None: self.dim = pdim # Dim is a list of 5 parameters: x, y, width, height, draggable_width self.val = pval self.val_min = pval_min @@ -9,26 +11,25 @@ def __init__(self,ui,pdim,pval,pval_min,pval_max,psnap_to_int,pupdate_live,pupda self.tval = self.val self.snap_to_int = psnap_to_int self.update_live = pupdate_live - self.update_function = pupdate_function - ui.slider_list.append(self) - - def drawSlider(self, screen): + self._func = callback_func + + def draw_slider(self, screen): x, y, w, h, dw = self.dim - ratio = (self.tval-self.val_min)/self.getLength() - sliderSurface = pygame.Surface((w,h), pygame.SRCALPHA, 32) - sliderSurface.fill((80,80,80)) - pygame.draw.rect(sliderSurface,(230,230,230),(ratio*(w-dw),0,dw,h)) - screen.blit(sliderSurface,(x,y)) + ratio = (self.tval-self.val_min)/self.get_length() + slider_surface = pygame.Surface((w,h), pygame.SRCALPHA, 32) + slider_surface.fill((80,80,80)) + pygame.draw.rect(slider_surface,(230,230,230),(ratio*(w-dw),0,dw,h)) + screen.blit(slider_surface,(x,y)) - def getLength(self): + def get_length(self): return max(self.val_max-self.val_min, 1) - def updateVal(self): + def update_val(self): if self.tval != self.val: self.val = self.tval - self.update_function(self.val) + self._func(self.val) - def manualUpdate(self, val): + def manual_update(self, val): self.tval = val - self.updateVal() - self.update_function(self.val) \ No newline at end of file + self.update_val() + self._func(self.val) \ No newline at end of file diff --git a/jes_species_info.py b/jes_species_info.py index 6ed9f6f..e0ec7fa 100644 --- a/jes_species_info.py +++ b/jes_species_info.py @@ -2,9 +2,9 @@ import math class SpeciesInfo: - def __init__(self, _sim, me, ancestor): + def __init__(self, _sim, me, ancestor) -> None: self.sim = _sim - self.speciesID = me.species + self.species_id = me.species self.ancestor_id = None self.level = 0 if ancestor is not None: @@ -31,7 +31,7 @@ def become_prominent(self): # if you are prominent, all your ancestors become p ancestor.become_prominent() def insert_into_prominent_species_list(self): - i = self.speciesID + i = self.species_id p = self.sim.prominent_species while len(p) <= self.level: # this level doesn't exist yet. Add new levels of the genealogy tree to acommodate you p.append([]) diff --git a/jes_ui.py b/jes_ui.py index ff6b80c..531b8fb 100644 --- a/jes_ui.py +++ b/jes_ui.py @@ -9,7 +9,7 @@ from jes_species_info import SpeciesInfo from utils import species_to_color, species_to_name, dist_to_text, bound, get_dist, array_int_multiply from jes_dataviz import display_all_graphs, draw_all_graphs -from jes_shapes import drawRingLight, draw_x, center_text, align_text, draw_species_circle +from jes_shapes import draw_ring_light, draw_x, center_text, align_text, draw_species_circle from jes_slider import Slider from jes_button import Button import time @@ -19,8 +19,6 @@ class UI: def __init__(self, config: dict): - self.slider_list: list = [] - self.button_list: list = [] self.title: str = config.get('title') pygame.display.set_caption(self.title) pygame.font.init() @@ -86,6 +84,9 @@ def __init__(self, config: dict): self.storage_coor = (660, 52) self.running: bool = True + self.slider_list: list[Slider] = [] + self.button_list: list[Button] = [] + self.gen_slider: Slider = Optional[Slider] self.show_creatures_button: Button = Optional[Button] self.sort_button: Button = Optional[Button] @@ -96,19 +97,32 @@ def __init__(self, config: dict): def add_buttons_and_sliders(self): - self.gen_slider = Slider(self, (40, self.window_height - 100, self.window_width - 80, 60, 140), 0, 0, 0, True, True, self.update_gen_slider) + self.gen_slider: Slider = Slider(pdim=(40, self.window_height - 100, self.window_width - 80, 60, 140), callback_func=self.update_gen_slider) + + self.slider_list.append(self.gen_slider) button_coor = [] for i in range(6): button_coor.append((self.window_width - 1340 + 220 * i, self.window_height - self.menu_text_up, 200, 60)) - self.show_creatures_button = Button(self, button_coor[0], ["Show creatures", "Hide creatures"], self.toggle_creatures) - self.sort_button = Button(self, button_coor[1], ["Sort by ID", "Sort by fitness", "Sort by weakness"], self.toggle_sort) - self.style_button = Button(self, button_coor[2], ["Big Icons", "Small Icons", "Species Tiles"], self.toggle_style) - self.sample_button = Button(self, button_coor[3], ["Watch sample", "Stop sample"], self.start_sample) - self.do_gen_button = Button(self, button_coor[4], ["Do a generation"], self.sim.do_generation) - self.alap_button = Button(self, button_coor[5], ["Turn on ALAP", "Turn off ALAP"], self.do_nothing) - + self.show_creatures_button = Button(button_coor[0], ["Show creatures", "Hide creatures"], self.toggle_creatures) + self.button_list.append(self.show_creatures_button) + + self.sort_button = Button(button_coor[1], ["Sort by ID", "Sort by fitness", "Sort by weakness"], self.toggle_sort) + self.button_list.append(self.sort_button) + + self.style_button = Button(button_coor[2], ["Big Icons", "Small Icons", "Species Tiles"], self.toggle_style) + self.button_list.append(self.style_button) + + self.sample_button = Button(button_coor[3], ["Watch sample", "Stop sample"], self.start_sample) + self.button_list.append(self.sample_button) + + self.do_gen_button = Button(button_coor[4], ["Do a generation"], self.sim.do_generation) + self.button_list.append(self.do_gen_button) + + self.alap_button = Button(button_coor[5], ["Turn on ALAP", "Turn off ALAP"], self.do_nothing) + self.button_list.append(self.alap_button) + def reverse(self, i): return self.sim.creature_count-1-i @@ -393,7 +407,7 @@ def detect_events(self) -> None: if event.key == pygame.K_RIGHT: new_gen = min(self.gen_slider.val_max, self.gen_slider.val + 1) if new_gen is not None: - self.gen_slider.manualUpdate(new_gen) + self.gen_slider.manual_update(new_gen) self.clear_movies() self.detect_mouse_motion() if event.key == 120: # pressing X will hide the Xs showing killed creatures @@ -413,7 +427,7 @@ def detect_events(self) -> None: self.sim.do_generation(None) elif event.key == 113: # pressing 'Q' - self.show_creatures_button.timeOfLastClick = time.time() + self.show_creatures_button.time_of_last_click = time.time() self.show_creatures_button.setting = 1 - self.show_creatures_button.setting self.toggle_creatures(self.show_creatures_button) @@ -429,9 +443,10 @@ def detect_events(self) -> None: s_x, s_y, s_w, s_h = button.dim if s_x <= mouse_x < s_x+s_w and s_y <= mouse_y < s_y + s_h: button.click() + elif event.type == pygame.MOUSEBUTTONUP: if self.slider_drag is not None: - self.slider_drag.updateVal() + self.slider_drag.update_val() self.slider_drag = None def draw_menu(self) -> None: @@ -446,7 +461,7 @@ def draw_menu(self) -> None: self.display_movies(self.screen) def display_creature_mosaic(self, screen): - time_since_last_press = time.time()-self.show_creatures_button.timeOfLastClick + time_since_last_press = time.time()-self.show_creatures_button.time_of_last_click pan_time = 0.2 frac = bound(time_since_last_press/pan_time) @@ -481,12 +496,12 @@ def display_movies(self, screen) -> None: self.screen.blit(self.movie_screens[0], coor) if self.creature_location_highlight[0] == 1: dim = self.preview_locations[self.creature_location_highlight[2]] - self.screen.blit(drawRingLight(dim[2],dim[3],6),(dim[0],dim[1])) + self.screen.blit(draw_ring_light(dim[2], dim[3], 6), (dim[0], dim[1])) else: coor = self.sim.creatures[gen][self.creature_location_highlight[1]].icon_coor x = coor[0]+self.cm_margin_1 y = coor[1]+self.cm_margin_1 - self.screen.blit(drawRingLight(coor[2],coor[3],6),(x,y)) + self.screen.blit(draw_ring_light(coor[2], coor[3], 6), (x, y)) def detect_sliders(self): @@ -500,11 +515,12 @@ def detect_sliders(self): if self.slider_drag.snap_to_int: self.slider_drag.tval = round(self.slider_drag.tval) if self.slider_drag.update_live: - self.slider_drag.updateVal() + self.slider_drag.update_val() def draw_sliders_and_buttons(self): for slider in self.slider_list: - slider.drawSlider(self.screen) + slider.draw_slider(self.screen) + for button in self.button_list: button.draw_button(self.screen, self.small_font) diff --git a/utils.py b/utils.py index f383d6f..72a40fe 100644 --- a/utils.py +++ b/utils.py @@ -77,6 +77,7 @@ def species_to_color(s, ui): brightness = (math.floor(int(_hex, 16)//10000)%100)/100 color = hue_to_rgb(hue) new_color = brighten(color, 0.85+0.6*brightness) + return new_color def bound(x):