From 2f1b80c0e839e4f937165f95fdb483b2223f9456 Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 19 Nov 2024 00:31:16 +1100 Subject: [PATCH 01/32] Extra colours added from pull request I have added new colours and tweaked them to be better so that none overlap! --- Spider.pde | 46 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/Spider.pde b/Spider.pde index fd04f1f..743436d 100644 --- a/Spider.pde +++ b/Spider.pde @@ -59,20 +59,42 @@ class Spider{ g.popMatrix(); return value; } - color getColor(){ +color getColor() { int c = swattersSeen.size(); - if(c == 0 || c == 1){ - return color(0,0,0,255); - }else{ - if(c < 6){ - float fac = (c-1)/5.0; - return color(0,fac*140,255-fac*255,255); - }else{ - float fac = min(1,(c-6)/19.0); - return color(255*fac,140-fac*140,0,255); - } + if (c == 0) { + return color(0, 0, 0, 255); // Black + } else { + if (c <= 5) { + float fac = c / 5.0; + return color(0, fac * 140, 255 - fac * 255, 255); // Blue transition + } else if (c <= 20) { + float fac = (c - 5) / 15.0; + return color(fac * 255, 140 + fac * 155, 20, 255); // Green with a higher base for better contrast + } else if (c <= 40) { + float fac = (c - 20) / 20.0; + return color(255, 255 - fac * 115, 50, 255); // Yellow with a richer tone + } else if (c <= 100) { + float fac = (c - 40) / 60.0; + return color(255, 140 - fac * 140, 0, 255); // Pink transition + } else if (c <= 200) { + float fac = (c - 100) / 100.0; + return color(255, 0, fac * 255, 255); // Purple transition + } else if (c <= 500) { + float fac = (c - 200) / 300.0; + return color(255 - fac * 141, 20, 255, 255); // Deeper purple to cyan + } else if (c <= 1000) { + float fac = (c - 500) / 500.0; + return color(114 - fac * 114, fac * 255, 255, 255); // Cyan transition + } else if (c <= 4000) { + float fac = (c - 1000) / 3000.0; + return color(225 + fac * 30, 210 - fac * 20, 30 + fac * 50); // Gold with richer, deeper hues + } else { + float fac = min(1, (c - 4000) / 6000.0); + return color(255, 202 + fac * 53, fac * 255); // Transition to white + } } - } +} + color transitionColor(color a, color b, float prog){ float newR = lerp(red(a), red(b), prog); float newG = lerp(green(a), green(b), prog); From 3f47df1e30b616794474e4854243620c1a287f14 Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 19 Nov 2024 00:52:04 +1100 Subject: [PATCH 02/32] Add other pull request, the anti lag one Only activates the selecting of spider when you click, massively improving performance! --- SpiderEvoSim.pde | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/SpiderEvoSim.pde b/SpiderEvoSim.pde index c0b8850..40136dc 100644 --- a/SpiderEvoSim.pde +++ b/SpiderEvoSim.pde @@ -151,13 +151,19 @@ void setup(){ r.setPointerVisible(false); g = createGraphics(1920,1080,P3D); } -void draw(){ - doMouse(); - doPhysics(); - drawVisuals(); - image(g,0,0); - drawUI(); - frames++; +void draw() { + doMouse(); + doPhysics(); + drawVisuals(); + image(g, 0, 0); + drawUI(); + frames++; + if (camera[1] < -1) { + camera[1] = -1; + } + if (camera[1] > 1) { + camera[1] = 1; + } } void checkHighlight(){ if(!lock_highlight){ From fcfd2e9988fb45f7974c20ab0f97074abb770041 Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 19 Nov 2024 00:54:21 +1100 Subject: [PATCH 03/32] Fixed the UI graph bugs and game freeze Floating point overflow added up as ticks went on, I fixed that so now there is a small stutter but it doesnt get worse with time! (And graphs have no bugs now!) --- SpiderEvoSim.pde | 184 ++++++++++++++++++++++++++++++---------------- sketch.properties | 1 + 2 files changed, 120 insertions(+), 65 deletions(-) create mode 100644 sketch.properties diff --git a/SpiderEvoSim.pde b/SpiderEvoSim.pde index 40136dc..e823d94 100644 --- a/SpiderEvoSim.pde +++ b/SpiderEvoSim.pde @@ -26,9 +26,11 @@ PGraphics g; int playback_speed = 1; int dailyDeaths = 0; int swattersSeenTotal = 0; -float TICKS_PER_DAY = 10000; +float TICKS_PER_DAY = 10000.0f; // Keep as float but use explicit f suffix +float PER_DAY = 10000.0f; boolean TRAP_MOUSE = true; boolean lock_highlight = false; + GLWindow r; int LEG_COUNT = 4; @@ -58,8 +60,8 @@ float sig(float a){ float sig_inv(float a){ return log(a/(1-a)); } -float ticksToDays(float age){ - return age/TICKS_PER_DAY; +float ticksToDays(long age) { + return age / TICKS_PER_DAY; } color darken(color c, float perc){ float newR = red(c)*perc; @@ -92,7 +94,15 @@ float[][] deepCopy(float[][] input){ return result; } - +// Helper function to safely handle large numbers +double safeAdd(double a, double b) { + double result = a + b; + if (result == Double.POSITIVE_INFINITY || result == Double.NEGATIVE_INFINITY) { + println("Warning: Arithmetic overflow in calculations"); + return Double.MAX_VALUE; + } + return result; +} ArrayList createSpiders(Room room){ int START_SPIDER_COUNT = 300; ArrayList result = new ArrayList(0); @@ -173,15 +183,18 @@ void checkHighlight(){ Spider checkHighlightHelper(){ Spider answer = null; float recordLowest = 1; - for(int s = 0; s < spiders.size(); s++){ - float score = spiders.get(s).cursorOnSpider(); - if(score > 0 && score < recordLowest){ - recordLowest = score; - answer = spiders.get(s); + if (mousePressed) { + for (int s = 0; s < spiders.size(); s++) { + float score = spiders.get(s).cursorOnSpider(); + if (score > 0 && score < recordLowest) { + recordLowest = score; + answer = spiders.get(s); + } } } return answer; } + void drawUI(){ noStroke(); fill(0); @@ -198,26 +211,44 @@ void drawUI(){ text(ticksToDate(ticks),20,65); } -String dateNumToMonthString(int d){ - String[] monthNames = {"January","February","March","April","May","June","July","August","September","October","November","December"}; - int[] monthDays = {31,28,31,30,31,30,31,31,30,31,30,31}; - for(int m = 0; m < 12; m++){ - if(d < monthDays[m]){ - return monthNames[m]+" "+(d+1); +String dateNumToMonthString(int d) { + String[] monthNames = {"January","February","March","April","May","June", + "July","August","September","October","November","December"}; + int[] monthDays = {31,28,31,30,31,30,31,31,30,31,30,31}; + + // Bounds check + if (d < 0) { + println("Warning: Negative date value"); + return monthNames[0] + " 1"; } - d -= monthDays[m]; - } - return monthNames[11]+monthDays[11]; -} -String ticksToDate(int t){ - float daysTotalFloat = ticksToDays(t)+0.5; - int daysTotalInt = (int)daysTotalFloat; - float timeOfDay = daysTotalFloat%1.0; - int years = daysTotalInt/365; - int days = daysTotalInt%365; - String[] TOD_LIST = {"Night","Sunrise","Morning","Afternoon","Sunset","Evening"}; - String TOD = TOD_LIST[(int)(timeOfDay*6)]; - return "Year "+(years+1)+", "+dateNumToMonthString(days)+" - "+TOD; + + int totalDays = 0; + for (int m = 0; m < 12; m++) { + if (d < monthDays[m]) { + return monthNames[m] + " " + (d + 1); + } + d -= monthDays[m]; + } + return monthNames[11] + " " + monthDays[11]; +} +String ticksToDate(long t) { + float daysTotalFloat = ticksToDays(t) + 0.5f; + int daysTotalInt = (int)daysTotalFloat; + float timeOfDay = daysTotalFloat % 1.0f; + + // Add bounds checking + if (daysTotalInt > 365_000_000) { + println("Warning: Date calculation overflow"); + return "Year MAX"; + } + + int years = daysTotalInt / 365; + int days = daysTotalInt % 365; + + String[] TOD_LIST = {"Night","Sunrise","Morning","Afternoon","Sunset","Evening"}; + String TOD = TOD_LIST[(int)(timeOfDay * 6)]; + + return "Year " + (years + 1) + ", " + dateNumToMonthString(days) + " - " + TOD; } void doMouse(){ if(TRAP_MOUSE){ @@ -287,43 +318,61 @@ float getBiodiversity(){ } return total_diversity/GENOME_LENGTH*100; } -void collectData(){ - if(ticks%CHANGE_WINDOWS_EVERY == 0){ - for(int w = 0; w < windows.size(); w++){ - windows.get(w).updateShow(); - } - } - if(ticks%TICKS_PER_DAY == 0){ - Float[] datum = new Float[STAT_COUNT]; - for(int d = 0; d < STAT_COUNT; d++){ - datum[d] = new Float(0); - } - for(int s = 0; s < spiders.size(); s++){ - spiders.get(s).writeData(datum); - } - for(int d = 0; d < STAT_COUNT; d++){ - datum[d] /= spiders.size(); +void collectData() { + if (ticks % CHANGE_WINDOWS_EVERY == 0) { + for (int w = 0; w < windows.size(); w++) { + windows.get(w).updateShow(); + } } - datum[1] = (float)dailyDeaths; - datum[2] = (float)(swattersSeenTotal-dailyDeaths); - datum[4] = getBiodiversity(); - dailyDeaths = 0; - swattersSeenTotal = 0; - stats.add(datum); - statNotes.add(""); - float[] graph_dim = {100,120,575,400}; - String[] titles = {"Average Age (days)", "Daily Deaths", - "Daily Swatter Escapes", "Sensitivity (out of 100)", "Total Biodiversity (out of 100)", "Average Swatters Seen"}; - for(int d = 0; d < STAT_COUNT; d++){ - float[] graphData = new float[stats.size()]; - for(int i = 0; i < stats.size(); i++){ - graphData[i] = stats.get(i)[d]; - } - drawGraphOn(statImages[d],graphData,titles[d],graph_dim, color(128,0,0), d); + if (ticks % (long)TICKS_PER_DAY == 0) { + // Keep using Float[] to match Spider.writeData() method + Float[] datum = new Float[STAT_COUNT]; + for (int d = 0; d < STAT_COUNT; d++) { + datum[d] = 0.0f; // Use 0.0f for Float + } + + // Get spider count before division + float spiderCount = spiders.size(); + + for (int s = 0; s < spiders.size(); s++) { + spiders.get(s).writeData(datum); + } + + // Prevent division by zero and ensure precise division + for (int d = 0; d < STAT_COUNT; d++) { + datum[d] = spiderCount > 0 ? datum[d] / spiderCount : 0.0f; + } + + datum[1] = (float)dailyDeaths; + datum[2] = (float)(swattersSeenTotal - dailyDeaths); + datum[4] = getBiodiversity(); + + dailyDeaths = 0; + swattersSeenTotal = 0; + stats.add(datum); + statNotes.add(""); + + float[] graph_dim = {100, 120, 575, 400}; + String[] titles = { + "Average Age (days)", + "Daily Deaths", + "Daily Swatter Escapes", + "Sensitivity (out of 100)", + "Total Biodiversity (out of 100)", + "Average Swatters Seen" + }; + + for (int d = 0; d < STAT_COUNT; d++) { + float[] graphData = new float[stats.size()]; + for (int i = 0; i < stats.size(); i++) { + graphData[i] = stats.get(i)[d]; + } + drawGraphOn(statImages[d], graphData, titles[d], graph_dim, color(128,0,0), d); + } + + sfx[9].play(); + sfx[9].amp(1.0 - min(0.8, (playback_speed-1)/200.0)); } - sfx[9].play(); - sfx[9].amp(1.0-min(0.8,(playback_speed-1)/200.0)); - } } float getUnit(float a, float b){ float diff = b-a; @@ -427,8 +476,13 @@ void drawGraphOn(PGraphics s, float[] data, String title, float[] graph_dim, col s.text(title,graph_dim[0]+graph_dim[2]*0.5,graph_dim[1]-50); s.endDraw(); } -float daylight(){ - return 0.5+0.5*cos(ticksToDays(ticks)*(2*PI)); +float daylight() { + float days = ticksToDays(ticks); + if (Float.isInfinite(days) || Float.isNaN(days)) { + println("Warning: Day calculation overflow in daylight()"); + return 0.5f; + } + return 0.5f + 0.5f * cos(days * (2 * PI)); } void drawVisuals(){ g.beginDraw(); diff --git a/sketch.properties b/sketch.properties new file mode 100644 index 0000000..0537529 --- /dev/null +++ b/sketch.properties @@ -0,0 +1 @@ +main=SpiderEvoSim.pde From 18553ac326095247e5ce93c58369517a1f7a2283 Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 19 Nov 2024 02:04:15 +1100 Subject: [PATCH 04/32] Optimisations! Multhreading, cashing and so much more! --- Spider.pde | 272 ++++++++++++++++++++++++++++------------------- SpiderEvoSim.pde | 70 +++++++++--- 2 files changed, 216 insertions(+), 126 deletions(-) diff --git a/Spider.pde b/Spider.pde index 743436d..7ba2e8f 100644 --- a/Spider.pde +++ b/Spider.pde @@ -12,7 +12,8 @@ class Spider{ int generation; int birth_tick; Spider parent; - ArrayList swattersSeen = new ArrayList(0); + Set swattersSeen = new HashSet<>(); + public Spider(int i, Room room){ parent = null; @@ -43,69 +44,99 @@ class Spider{ return min(max(val,min_),max_); } float cursorOnSpider(){ + // Cache the real coordinates to avoid repeated calls float[] realCoor = room.wallCoor_to_realCoor(coor); g.pushMatrix(); aTranslate(realCoor); float MAX_DIST_MOUSE = 100; float value = -99999; - float x1 = g.screenX(0,0,0); - float y1 = g.screenY(0,0,0); - float x2 = g.screenX(0,MAX_LEG_SPAN,0); - float y2 = g.screenY(0,MAX_LEG_SPAN,0); - float distFromCenter = dist(x1,y1,width/2,height/2); - if(distFromCenter < dist(x1,y1,x2,y2) && distFromCenter < MAX_DIST_MOUSE){ - value = g.screenZ(0,0,0); + + // Calculate screen positions only once + float x1 = g.screenX(0, 0, 0); + float y1 = g.screenY(0, 0, 0); + float x2 = g.screenX(0, MAX_LEG_SPAN, 0); + float y2 = g.screenY(0, MAX_LEG_SPAN, 0); + + // Calculate distance from the center only once + float distFromCenter = dist(x1, y1, width / 2, height / 2); + float distLegSpan = dist(x1, y1, x2, y2); + + // Use pre-calculated distances to optimize logic + if (distFromCenter < distLegSpan && distFromCenter < MAX_DIST_MOUSE) { + value = g.screenZ(0, 0, 0); } + g.popMatrix(); return value; - } +} + color getColor() { int c = swattersSeen.size(); + float fac; + if (c == 0) { return color(0, 0, 0, 255); // Black - } else { - if (c <= 5) { - float fac = c / 5.0; - return color(0, fac * 140, 255 - fac * 255, 255); // Blue transition - } else if (c <= 20) { - float fac = (c - 5) / 15.0; - return color(fac * 255, 140 + fac * 155, 20, 255); // Green with a higher base for better contrast - } else if (c <= 40) { - float fac = (c - 20) / 20.0; - return color(255, 255 - fac * 115, 50, 255); // Yellow with a richer tone - } else if (c <= 100) { - float fac = (c - 40) / 60.0; - return color(255, 140 - fac * 140, 0, 255); // Pink transition - } else if (c <= 200) { - float fac = (c - 100) / 100.0; - return color(255, 0, fac * 255, 255); // Purple transition - } else if (c <= 500) { - float fac = (c - 200) / 300.0; - return color(255 - fac * 141, 20, 255, 255); // Deeper purple to cyan - } else if (c <= 1000) { - float fac = (c - 500) / 500.0; - return color(114 - fac * 114, fac * 255, 255, 255); // Cyan transition - } else if (c <= 4000) { - float fac = (c - 1000) / 3000.0; - return color(225 + fac * 30, 210 - fac * 20, 30 + fac * 50); // Gold with richer, deeper hues - } else { - float fac = min(1, (c - 4000) / 6000.0); - return color(255, 202 + fac * 53, fac * 255); // Transition to white - } + } + + // Define ranges and their color transitions + if (c <= 5) { + fac = c / 5.0; + return createColor(0, fac * 140, 255 - fac * 255); // Blue transition + } + else if (c <= 20) { + fac = (c - 5) / 15.0; + return createColor(fac * 255, 140 + fac * 155, 20); // Green with a higher base for better contrast + } + else if (c <= 40) { + fac = (c - 20) / 20.0; + return createColor(255, 255 - fac * 115, 50); // Yellow with a richer tone + } + else if (c <= 100) { + fac = (c - 40) / 60.0; + return createColor(255, 140 - fac * 140, 0); // Pink transition + } + else if (c <= 200) { + fac = (c - 100) / 100.0; + return createColor(255, 0, fac * 255); // Purple transition + } + else if (c <= 500) { + fac = (c - 200) / 300.0; + return createColor(255 - fac * 141, 20, 255); // Deeper purple to cyan + } + else if (c <= 1000) { + fac = (c - 500) / 500.0; + return createColor(114 - fac * 114, fac * 255, 255); // Cyan transition + } + else if (c <= 4000) { + fac = (c - 1000) / 3000.0; + return createColor(225 + fac * 30, 210 - fac * 20, 30 + fac * 50); // Gold with richer, deeper hues + } + else { + fac = min(1, (c - 4000) / 6000.0); + return createColor(255, 202 + fac * 53, fac * 255); // Transition to white } } - color transitionColor(color a, color b, float prog){ - float newR = lerp(red(a), red(b), prog); - float newG = lerp(green(a), green(b), prog); - float newB = lerp(blue(a), blue(b), prog); - return color(newR, newG, newB); - } +// Helper function for creating color +color createColor(float r, float g, float b) { + return color(r, g, b, 255); // Default alpha is 255 +} + +color transitionColor(color a, color b, float prog) { + return color( + lerp(red(a), red(b), prog), + lerp(green(a), green(b), prog), + lerp(blue(a), blue(b), prog) + ); +} + void drawSpider(Room room){ color c = getColor(); if(this == highlight_spider){ - c = color(0,255,0); + c = color(0,255,0); } + + // Cache the real coordinates to avoid repeated calls float[] realCoor = room.wallCoor_to_realCoor(coor); g.pushMatrix(); aTranslate(realCoor); @@ -114,8 +145,8 @@ color getColor() { g.rotateZ(realCoor[3]); g.beginShape(); for(int i = 0; i < 12; i++){ - float angle = i*2*PI/12; - g.vertex(cos(angle)*BODY_SPAN,2,sin(angle)*BODY_SPAN); + float angle = i*2*PI/12; + g.vertex(cos(angle)*BODY_SPAN, 2, sin(angle)*BODY_SPAN); } g.endShape(CLOSE); g.popMatrix(); @@ -123,44 +154,45 @@ color getColor() { g.stroke(c); g.strokeWeight(3); for(int L = 0; L < LEG_COUNT; L++){ - float[] legRealCoor = room.wallCoor_to_realCoor(leg_coor[L]); - float[] Lcoor = aSubstract(legRealCoor,realCoor); - float[] Mcoor = multi(Lcoor,0.5); - Mcoor[0] -= sin(realCoor[3])*Ldist; - Mcoor[1] += cos(realCoor[3])*Ldist; - g.line(0,0,0,Mcoor[0],Mcoor[1],Mcoor[2]); - g.line(Mcoor[0],Mcoor[1],Mcoor[2],Lcoor[0],Lcoor[1],Lcoor[2]); + // Cache the real coordinates of each leg + float[] legRealCoor = room.wallCoor_to_realCoor(leg_coor[L]); + float[] Lcoor = aSubstract(legRealCoor, realCoor); + float[] Mcoor = multi(Lcoor, 0.5); + Mcoor[0] -= sin(realCoor[3]) * Ldist; + Mcoor[1] += cos(realCoor[3]) * Ldist; + g.line(0, 0, 0, Mcoor[0], Mcoor[1], Mcoor[2]); + g.line(Mcoor[0], Mcoor[1], Mcoor[2], Lcoor[0], Lcoor[1], Lcoor[2]); } g.noStroke(); g.popMatrix(); - - if(getAge() < 200 && parent != null && parent.getAge() >= getAge()){ - float[] parentCoor = room.wallCoor_to_realCoor(parent.coor); - if(realCoor[0] == parentCoor[0] && realCoor[1] == parentCoor[1]){ - return; - } - g.pushMatrix(); - aTranslate(realCoor); - g.rotateZ(realCoor[3]); - g.beginShape(); - g.fill(255); - float WHITE_SPAN = BODY_SPAN*4; - for(int i = 0; i < 12; i++){ - float angle = i*2*PI/12; - g.vertex(cos(angle)*WHITE_SPAN,EPS*2,sin(angle)*WHITE_SPAN); - } - g.endShape(CLOSE); - g.popMatrix(); - - if(dist(realCoor[0],realCoor[1],realCoor[2],parentCoor[0],parentCoor[1],parentCoor[2]) < WHITE_SPAN*10){ - g.stroke(255); - g.strokeWeight(20); - g.line(realCoor[0],realCoor[1],realCoor[2],parentCoor[0],parentCoor[1],parentCoor[2]); - g.noStroke(); - } + // Parent drawing logic (only if needed) + if (getAge() < 200 && parent != null && parent.getAge() >= getAge()){ + float[] parentCoor = room.wallCoor_to_realCoor(parent.coor); + if (realCoor[0] == parentCoor[0] && realCoor[1] == parentCoor[1]) return; + + g.pushMatrix(); + aTranslate(realCoor); + g.rotateZ(realCoor[3]); + g.beginShape(); + g.fill(255); + float WHITE_SPAN = BODY_SPAN * 4; + for (int i = 0; i < 12; i++) { + float angle = i * 2 * PI / 12; + g.vertex(cos(angle) * WHITE_SPAN, EPS * 2, sin(angle) * WHITE_SPAN); + } + g.endShape(CLOSE); + g.popMatrix(); + + if (dist(realCoor[0], realCoor[1], realCoor[2], parentCoor[0], parentCoor[1], parentCoor[2]) < WHITE_SPAN * 10){ + g.stroke(255); + g.strokeWeight(20); + g.line(realCoor[0], realCoor[1], realCoor[2], parentCoor[0], parentCoor[1], parentCoor[2]); + g.noStroke(); + } } - } +} + float[] multi(float[] a, float m){ float[] result = new float[a.length]; for(int i = 0; i < a.length; i++){ @@ -197,40 +229,58 @@ color getColor() { void placeLegs(float[] center, int step, Room room, float darkest_sensed_shadow, ArrayList spiders){ float force_to_right_angles = 0.001; // how strongly should the spider's legs be dragged back into right angles? float first_angle = 0; - for(int L = 0; L < LEG_COUNT; L++){ - int genome_index = L*GENES_PER_LEG+2*step+1; - if(darkest_sensed_shadow < genome[L*GENES_PER_LEG+12]){ // it's below the threshold, so do the dark pattern - genome_index += 6; - } - float distance = genome[genome_index]*MAX_LEG_SPAN; - float delta_x = leg_coor[L][0]-center[0]; - float delta_y = leg_coor[L][1]-center[1]; - float angle = atan2(delta_y,delta_x); - if(L == 0){ - first_angle = angle; - }else{ - float desired_angle = first_angle+PI/2*L; - float move = (desired_angle-angle); - while(move > PI){ - move -= 2*PI; + + // Precompute cos and sin values of angles once + float[] cosAngles = new float[LEG_COUNT]; + float[] sinAngles = new float[LEG_COUNT]; + + for (int L = 0; L < LEG_COUNT; L++) { + int genome_index = L * GENES_PER_LEG + 2 * step + 1; + if (darkest_sensed_shadow < genome[L * GENES_PER_LEG + 12]) { // it's below the threshold, so do the dark pattern + genome_index += 6; } - while(move < -PI){ - move += 2*PI; + float distance = genome[genome_index] * MAX_LEG_SPAN; + + float delta_x = leg_coor[L][0] - center[0]; + float delta_y = leg_coor[L][1] - center[1]; + + // Compute angle only once + float angle = atan2(delta_y, delta_x); + + if (L == 0) { + first_angle = angle; + } else { + float desired_angle = first_angle + PI / 2 * L; + float move = (desired_angle - angle); + while (move > PI) { + move -= 2 * PI; + } + while (move < -PI) { + move += 2 * PI; + } + angle += force_to_right_angles * move; } - angle += force_to_right_angles*move; - } - leg_coor[L][0] = center[0]+cos(angle)*distance; - leg_coor[L][1] = center[1]+sin(angle)*distance; + + // Store precomputed cos and sin values for reuse + cosAngles[L] = cos(angle); + sinAngles[L] = sin(angle); + + // Update leg positions + leg_coor[L][0] = center[0] + cosAngles[L] * distance; + leg_coor[L][1] = center[1] + sinAngles[L] * distance; } - coor = getWeightedCenter(step,room,darkest_sensed_shadow); - for(int d = 0; d < 2; d++){ - if(coor[d] < 0){ - shiftAllBy(d,room.getMaxDim(d)); - }else if(coor[d] >= room.getMaxDim(d)){ - shiftAllBy(d,-room.getMaxDim(d)); - } + + coor = getWeightedCenter(step, room, darkest_sensed_shadow); + + for (int d = 0; d < 2; d++) { + if (coor[d] < 0) { + shiftAllBy(d, room.getMaxDim(d)); + } else if (coor[d] >= room.getMaxDim(d)) { + shiftAllBy(d, -room.getMaxDim(d)); + } } - } +} + void shiftAllBy(int dim, float amt){ coor[dim] += amt; for(int L = 0; L < LEG_COUNT; L++){ @@ -374,7 +424,7 @@ color getColor() { visIndex = totalIndex; totalIndex++; birth_tick = ticks; - swattersSeen = new ArrayList(0); + swattersSeen = new HashSet(); // Creates an empty HashSet } float getSensitivity(){ float sensitivity = 0; diff --git a/SpiderEvoSim.pde b/SpiderEvoSim.pde index e823d94..0c11c7d 100644 --- a/SpiderEvoSim.pde +++ b/SpiderEvoSim.pde @@ -1,5 +1,12 @@ +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; import com.jogamp.newt.opengl.GLWindow; import processing.sound.*; +import java.util.HashSet; +import java.util.Set; + int CENTER_X = 960; // try setting this to 960 or 961 if there is horizontal camera-pan-drifting String[] soundFileNames = {"slap0.wav","slap1.wav","slap2.wav","splat0.wav","splat1.wav","splat2.wav","boop1.wav","boop2.wav","jump.wav","news.wav"}; @@ -103,22 +110,55 @@ double safeAdd(double a, double b) { } return result; } -ArrayList createSpiders(Room room){ - int START_SPIDER_COUNT = 300; - ArrayList result = new ArrayList(0); - for(int s = 0; s < START_SPIDER_COUNT; s++){ - Spider newSpider = new Spider(s, room); - result.add(newSpider); - } - return result; +ArrayList createSpiders(Room room) { + int START_SPIDER_COUNT = 300; + ArrayList result = new ArrayList(); + + // Create a thread pool with a fixed number of threads + ExecutorService executor = Executors.newFixedThreadPool(4); + ArrayList> futureSpiders = new ArrayList>(); + + for (int s = 0; s < START_SPIDER_COUNT; s++) { + final int spiderIndex = s; // Final or effectively final for lambda use + futureSpiders.add(executor.submit(() -> new Spider(spiderIndex, room))); + } + + // Collect results + for (Future future : futureSpiders) { + try { + result.add(future.get()); // Blocks until the thread completes + } catch (Exception e) { + e.printStackTrace(); // Handle exception + } + } + + // Shut down the executor service + executor.shutdown(); + + return result; } -void createSwatters(Room room, int START_SPIDER_COUNT){ - swatters = new ArrayList(0); - for(int s = 0; s < START_SPIDER_COUNT; s++){ - float perc = (s+0.5)/START_SPIDER_COUNT*1.4-0.4; - Swatter newSwatter = new Swatter(s, perc, room, swatters); - swatters.add(newSwatter); - } + +void createSwatters(Room room, int START_SPIDER_COUNT) { + swatters = new ArrayList(); + + ExecutorService executor = Executors.newFixedThreadPool(4); + ArrayList> futureSwatters = new ArrayList>(); + + for (int s = 0; s < START_SPIDER_COUNT; s++) { + final int swatterIndex = s; + float perc = (s + 0.5f) / START_SPIDER_COUNT * 1.4f - 0.4f; + futureSwatters.add(executor.submit(() -> new Swatter(swatterIndex, perc, room, swatters))); + } + + for (Future future : futureSwatters) { + try { + swatters.add(future.get()); + } catch (Exception e) { + e.printStackTrace(); + } + } + + executor.shutdown(); } void setup(){ From 36d4ef91f6278c81d52d32e3730082994f49b769 Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 19 Nov 2024 02:16:50 +1100 Subject: [PATCH 05/32] Reduced hitching optimisations to the collectdata class improve performance and reduce hitching --- SpiderEvoSim.pde | 56 ++++++++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/SpiderEvoSim.pde b/SpiderEvoSim.pde index 0c11c7d..632cf8c 100644 --- a/SpiderEvoSim.pde +++ b/SpiderEvoSim.pde @@ -6,7 +6,7 @@ import com.jogamp.newt.opengl.GLWindow; import processing.sound.*; import java.util.HashSet; import java.util.Set; - +import java.util.Arrays; int CENTER_X = 960; // try setting this to 960 or 961 if there is horizontal camera-pan-drifting String[] soundFileNames = {"slap0.wav","slap1.wav","slap2.wav","splat0.wav","splat1.wav","splat2.wav","boop1.wav","boop2.wav","jump.wav","news.wav"}; @@ -364,34 +364,41 @@ void collectData() { windows.get(w).updateShow(); } } + if (ticks % (long)TICKS_PER_DAY == 0) { - // Keep using Float[] to match Spider.writeData() method + // Initialize the datum array with 0.0f values in one step Float[] datum = new Float[STAT_COUNT]; - for (int d = 0; d < STAT_COUNT; d++) { - datum[d] = 0.0f; // Use 0.0f for Float - } - - // Get spider count before division - float spiderCount = spiders.size(); - - for (int s = 0; s < spiders.size(); s++) { - spiders.get(s).writeData(datum); - } + Arrays.fill(datum, 0.0f); // Fill all with 0.0f at once + + // Get spider count to avoid repeated calls to spiders.size() + int spiderCount = spiders.size(); - // Prevent division by zero and ensure precise division - for (int d = 0; d < STAT_COUNT; d++) { - datum[d] = spiderCount > 0 ? datum[d] / spiderCount : 0.0f; + // If there are spiders, accumulate data into the datum array + if (spiderCount > 0) { + for (int s = 0; s < spiderCount; s++) { + spiders.get(s).writeData(datum); + } + + // Average the data after accumulation + for (int d = 0; d < STAT_COUNT; d++) { + datum[d] /= spiderCount; + } } - + + // Additional data (no need for division) datum[1] = (float)dailyDeaths; datum[2] = (float)(swattersSeenTotal - dailyDeaths); datum[4] = getBiodiversity(); - + + // Reset daily statistics dailyDeaths = 0; swattersSeenTotal = 0; + + // Store the results and notes stats.add(datum); statNotes.add(""); - + + // Prepare for graph rendering float[] graph_dim = {100, 120, 575, 400}; String[] titles = { "Average Age (days)", @@ -401,19 +408,26 @@ void collectData() { "Total Biodiversity (out of 100)", "Average Swatters Seen" }; - + + // Prepare graph data once and reuse it for all graphs for (int d = 0; d < STAT_COUNT; d++) { float[] graphData = new float[stats.size()]; + + // Extract graph data from stats in one pass for (int i = 0; i < stats.size(); i++) { graphData[i] = stats.get(i)[d]; } + + // Render graph for each statistic drawGraphOn(statImages[d], graphData, titles[d], graph_dim, color(128,0,0), d); } - + + // Play sound with adjusted amplitude based on playback speed sfx[9].play(); - sfx[9].amp(1.0 - min(0.8, (playback_speed-1)/200.0)); + sfx[9].amp(1.0 - min(0.8, (playback_speed - 1) / 200.0)); } } + float getUnit(float a, float b){ float diff = b-a; float[] units = {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}; From 3cd855e54927e216c1e4e13164b944491a8cad8f Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 19 Nov 2024 03:00:15 +1100 Subject: [PATCH 06/32] Getbiodiversity and thread optimisations lots of small improvements --- SpiderEvoSim.pde | 87 ++++++++++++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 36 deletions(-) diff --git a/SpiderEvoSim.pde b/SpiderEvoSim.pde index 632cf8c..5117e15 100644 --- a/SpiderEvoSim.pde +++ b/SpiderEvoSim.pde @@ -7,6 +7,8 @@ import processing.sound.*; import java.util.HashSet; import java.util.Set; import java.util.Arrays; +import java.util.concurrent.*; +import java.util.stream.IntStream; int CENTER_X = 960; // try setting this to 960 or 961 if there is horizontal camera-pan-drifting String[] soundFileNames = {"slap0.wav","slap1.wav","slap2.wav","splat0.wav","splat1.wav","splat2.wav","boop1.wav","boop2.wav","jump.wav","news.wav"}; @@ -39,7 +41,6 @@ boolean TRAP_MOUSE = true; boolean lock_highlight = false; GLWindow r; - int LEG_COUNT = 4; int GENES_PER_LEG = STEPS_CYCLE*4+1; //13 int GENOME_LENGTH = LEG_COUNT*GENES_PER_LEG; @@ -110,12 +111,14 @@ double safeAdd(double a, double b) { } return result; } + ArrayList createSpiders(Room room) { int START_SPIDER_COUNT = 300; ArrayList result = new ArrayList(); - // Create a thread pool with a fixed number of threads - ExecutorService executor = Executors.newFixedThreadPool(4); + int numberOfThreads = Math.min(Runtime.getRuntime().availableProcessors(), 4); // Limit to 4 threads or available CPUs + ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads); + ArrayList> futureSpiders = new ArrayList>(); for (int s = 0; s < START_SPIDER_COUNT; s++) { @@ -330,34 +333,45 @@ void doPhysics(){ player.doPhysics(room); } -float getBiodiversity(){ - float[] meanGenome = new float[GENOME_LENGTH]; - for(int g = 0; g < GENOME_LENGTH; g++){ - meanGenome[g] = 0; - } - for(int s = 0; s < spiders.size(); s++){ - for(int g = 0; g < GENOME_LENGTH; g++){ - meanGenome[g] += spiders.get(s).genome[g]; +float getBiodiversity() { + int spiderCount = spiders.size(); + if (spiderCount == 0) return 0; + + // Create arrays for genome mean and variance calculation + float[] meanGenome = new float[GENOME_LENGTH]; + Arrays.fill(meanGenome, 0); + + // Calculate mean values using a parallel stream + spiders.parallelStream().forEach(s -> { + for (int g = 0; g < GENOME_LENGTH; g++) { + meanGenome[g] += s.genome[g]; + } + }); + + // Average the mean values + for (int g = 0; g < GENOME_LENGTH; g++) { + meanGenome[g] /= spiderCount; } - } - for(int g = 0; g < GENOME_LENGTH; g++){ - meanGenome[g] /= spiders.size(); - } - float[] variances = new float[GENOME_LENGTH]; - for(int g = 0; g < GENOME_LENGTH; g++){ - variances[g] = 0; - } - for(int s = 0; s < spiders.size(); s++){ - for(int g = 0; g < GENOME_LENGTH; g++){ - variances[g] += pow(spiders.get(s).genome[g]-meanGenome[g],2); + + // Calculate variance in parallel + float[] variances = new float[GENOME_LENGTH]; + Arrays.fill(variances, 0); + + spiders.parallelStream().forEach(s -> { + for (int g = 0; g < GENOME_LENGTH; g++) { + variances[g] += Math.pow(s.genome[g] - meanGenome[g], 2); + } + }); + + // Calculate the total diversity + float total_diversity = 0; + for (int g = 0; g < GENOME_LENGTH; g++) { + total_diversity += Math.sqrt(variances[g] / spiderCount); } - } - float total_diversity = 0; - for(int g = 0; g < GENOME_LENGTH; g++){ - total_diversity += sqrt(variances[g]/spiders.size()); - } - return total_diversity/GENOME_LENGTH*100; + + return total_diversity / GENOME_LENGTH * 100; } + void collectData() { if (ticks % CHANGE_WINDOWS_EVERY == 0) { for (int w = 0; w < windows.size(); w++) { @@ -366,15 +380,15 @@ void collectData() { } if (ticks % (long)TICKS_PER_DAY == 0) { - // Initialize the datum array with 0.0f values in one step + // Cache the spider count to avoid repeated calls to spiders.size() + int spiderCount = spiders.size(); + + // Initialize the datum array before the conditional block Float[] datum = new Float[STAT_COUNT]; - Arrays.fill(datum, 0.0f); // Fill all with 0.0f at once + Arrays.fill(datum, 0.0f); - // Get spider count to avoid repeated calls to spiders.size() - int spiderCount = spiders.size(); - - // If there are spiders, accumulate data into the datum array if (spiderCount > 0) { + // Accumulate data into the datum array for (int s = 0; s < spiderCount; s++) { spiders.get(s).writeData(datum); } @@ -386,8 +400,8 @@ void collectData() { } // Additional data (no need for division) - datum[1] = (float)dailyDeaths; - datum[2] = (float)(swattersSeenTotal - dailyDeaths); + datum[1] = (float) dailyDeaths; + datum[2] = (float) (swattersSeenTotal - dailyDeaths); datum[4] = getBiodiversity(); // Reset daily statistics @@ -428,6 +442,7 @@ void collectData() { } } + float getUnit(float a, float b){ float diff = b-a; float[] units = {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}; From 7796bd8d5e4d297f46ce1672a79819eb2324eccd Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 19 Nov 2024 03:47:19 +1100 Subject: [PATCH 07/32] Batching attempt Plus some more changes like threads used --- SpiderEvoSim.pde | 106 ++++++++++++++++++++++++++++------------------- 1 file changed, 63 insertions(+), 43 deletions(-) diff --git a/SpiderEvoSim.pde b/SpiderEvoSim.pde index 5117e15..522799f 100644 --- a/SpiderEvoSim.pde +++ b/SpiderEvoSim.pde @@ -2,14 +2,17 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Callable; import java.util.concurrent.Future; -import com.jogamp.newt.opengl.GLWindow; -import processing.sound.*; import java.util.HashSet; import java.util.Set; import java.util.Arrays; import java.util.concurrent.*; import java.util.stream.IntStream; +import java.util.ArrayList; +import java.util.List; +import com.jogamp.newt.opengl.GLWindow; +import processing.sound.*; +int threadsUsed = 4; // Number of threads to be used, can be changed as per the requirement int CENTER_X = 960; // try setting this to 960 or 961 if there is horizontal camera-pan-drifting String[] soundFileNames = {"slap0.wav","slap1.wav","slap2.wav","splat0.wav","splat1.wav","splat2.wav","boop1.wav","boop2.wav","jump.wav","news.wav"}; SoundFile[] sfx; @@ -113,55 +116,73 @@ double safeAdd(double a, double b) { } ArrayList createSpiders(Room room) { - int START_SPIDER_COUNT = 300; - ArrayList result = new ArrayList(); - - int numberOfThreads = Math.min(Runtime.getRuntime().availableProcessors(), 4); // Limit to 4 threads or available CPUs - ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads); - - ArrayList> futureSpiders = new ArrayList>(); - - for (int s = 0; s < START_SPIDER_COUNT; s++) { - final int spiderIndex = s; // Final or effectively final for lambda use - futureSpiders.add(executor.submit(() -> new Spider(spiderIndex, room))); + int START_SPIDER_COUNT = 300; // Number of spiders to create + ArrayList result = new ArrayList<>(); + + int batchSize = 50; // Define the size of each batch + int numberOfBatches = (int) Math.ceil((double) START_SPIDER_COUNT / batchSize); + + ExecutorService executor = Executors.newFixedThreadPool(threadsUsed); // Use threadsUsed variable + List> tasks = new ArrayList<>(); + + for (int batch = 0; batch < numberOfBatches; batch++) { + final int start = batch * batchSize; + final int end = Math.min(start + batchSize, START_SPIDER_COUNT); + + tasks.add(() -> { + for (int s = start; s < end; s++) { + Spider spider = new Spider(s, room); + synchronized (result) { // Ensure thread safety when adding to the result list + result.add(spider); + } + } + return null; + }); } - - // Collect results - for (Future future : futureSpiders) { - try { - result.add(future.get()); // Blocks until the thread completes - } catch (Exception e) { - e.printStackTrace(); // Handle exception - } + + try { + executor.invokeAll(tasks); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + executor.shutdown(); } - // Shut down the executor service - executor.shutdown(); - return result; } void createSwatters(Room room, int START_SPIDER_COUNT) { - swatters = new ArrayList(); - - ExecutorService executor = Executors.newFixedThreadPool(4); - ArrayList> futureSwatters = new ArrayList>(); - - for (int s = 0; s < START_SPIDER_COUNT; s++) { - final int swatterIndex = s; - float perc = (s + 0.5f) / START_SPIDER_COUNT * 1.4f - 0.4f; - futureSwatters.add(executor.submit(() -> new Swatter(swatterIndex, perc, room, swatters))); + swatters = new ArrayList<>(); + + int batchSize = 50; // Define the size of each batch + int numberOfBatches = (int) Math.ceil((double) START_SPIDER_COUNT / batchSize); + + ExecutorService executor = Executors.newFixedThreadPool(threadsUsed); // Use threadsUsed variable + List> tasks = new ArrayList<>(); + + for (int batch = 0; batch < numberOfBatches; batch++) { + final int start = batch * batchSize; + final int end = Math.min(start + batchSize, START_SPIDER_COUNT); + + tasks.add(() -> { + for (int s = start; s < end; s++) { + float perc = (s + 0.5f) / START_SPIDER_COUNT * 1.4f - 0.4f; + Swatter swatter = new Swatter(s, perc, room, swatters); + synchronized (swatters) { // Ensure thread safety when adding to the swatters list + swatters.add(swatter); + } + } + return null; + }); } - - for (Future future : futureSwatters) { - try { - swatters.add(future.get()); - } catch (Exception e) { - e.printStackTrace(); - } + + try { + executor.invokeAll(tasks); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + executor.shutdown(); } - - executor.shutdown(); } void setup(){ @@ -265,7 +286,6 @@ String dateNumToMonthString(int d) { return monthNames[0] + " 1"; } - int totalDays = 0; for (int m = 0; m < 12; m++) { if (d < monthDays[m]) { return monthNames[m] + " " + (d + 1); From 157b2941676cff703df7c2a591a8f31a47285652 Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 19 Nov 2024 12:06:11 +1100 Subject: [PATCH 08/32] FPS Counter --- SpiderEvoSim.pde | 56 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/SpiderEvoSim.pde b/SpiderEvoSim.pde index 522799f..c9a1b88 100644 --- a/SpiderEvoSim.pde +++ b/SpiderEvoSim.pde @@ -225,7 +225,27 @@ void setup(){ r.setPointerVisible(false); g = createGraphics(1920,1080,P3D); } +//FPS counter +ArrayList frameTimestamps = new ArrayList<>(); // To store frame timestamps +float averageFps = 0; // Average FPS over the last 4 seconds + void draw() { + // Capture current time in milliseconds + long currentTime = millis(); + frameTimestamps.add(currentTime); + + // Remove timestamps older than 4 seconds + while (frameTimestamps.size() > 0 && frameTimestamps.get(0) < currentTime - 4000) { + frameTimestamps.remove(0); + } + + // Calculate average FPS + if (frameTimestamps.size() > 1) { + float elapsedSeconds = (frameTimestamps.get(frameTimestamps.size() - 1) - frameTimestamps.get(0)) / 1000.0f; + averageFps = (frameTimestamps.size() - 1) / elapsedSeconds; + } + + // Original draw logic doMouse(); doPhysics(); drawVisuals(); @@ -259,20 +279,28 @@ Spider checkHighlightHelper(){ return answer; } -void drawUI(){ - noStroke(); - fill(0); - float M = 1; - float W = 20; - rect(width/2-M,height/2-W,M*2,W*2); - rect(width/2-W,height/2-M,W*2,M*2); - if(highlight_spider != null){ - PGraphics genomePanel = highlight_spider.drawGenome(); - image(genomePanel,width-genomePanel.width-30,height-genomePanel.height-30); - } - textAlign(LEFT); - textSize(50); - text(ticksToDate(ticks),20,65); +void drawUI() { + noStroke(); + fill(0); + float M = 1; + float W = 20; + rect(width / 2 - M, height / 2 - W, M * 2, W * 2); + rect(width / 2 - W, height / 2 - M, W * 2, M * 2); + + if (highlight_spider != null) { + PGraphics genomePanel = highlight_spider.drawGenome(); + image(genomePanel, width - genomePanel.width - 30, height - genomePanel.height - 30); + } + + textAlign(LEFT); + textSize(50); + fill(255); // Set text color to white for visibility + text(ticksToDate(ticks), 20, 65); + + // Display FPS + textAlign(RIGHT); + textSize(25); + text("FPS: " + nf(averageFps, 0, 2), width - 20, 30); // Display the FPS in the top-right corner } String dateNumToMonthString(int d) { From fcdee327c8c6bee4ecb037e9ba93d69c44acfd9e Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 19 Nov 2024 12:59:30 +1100 Subject: [PATCH 09/32] Optimisation tests complete This was the equal best version... the original --- Spider.pde | 275 +++++++++++++++++++---------------------------- SpiderEvoSim.pde | 223 ++++++++++++-------------------------- 2 files changed, 181 insertions(+), 317 deletions(-) diff --git a/Spider.pde b/Spider.pde index 7ba2e8f..37f77d2 100644 --- a/Spider.pde +++ b/Spider.pde @@ -12,8 +12,7 @@ class Spider{ int generation; int birth_tick; Spider parent; - Set swattersSeen = new HashSet<>(); - + ArrayList swattersSeen = new ArrayList(0); public Spider(int i, Room room){ parent = null; @@ -44,99 +43,70 @@ class Spider{ return min(max(val,min_),max_); } float cursorOnSpider(){ - // Cache the real coordinates to avoid repeated calls float[] realCoor = room.wallCoor_to_realCoor(coor); g.pushMatrix(); aTranslate(realCoor); float MAX_DIST_MOUSE = 100; float value = -99999; - - // Calculate screen positions only once - float x1 = g.screenX(0, 0, 0); - float y1 = g.screenY(0, 0, 0); - float x2 = g.screenX(0, MAX_LEG_SPAN, 0); - float y2 = g.screenY(0, MAX_LEG_SPAN, 0); - - // Calculate distance from the center only once - float distFromCenter = dist(x1, y1, width / 2, height / 2); - float distLegSpan = dist(x1, y1, x2, y2); - - // Use pre-calculated distances to optimize logic - if (distFromCenter < distLegSpan && distFromCenter < MAX_DIST_MOUSE) { - value = g.screenZ(0, 0, 0); + float x1 = g.screenX(0,0,0); + float y1 = g.screenY(0,0,0); + float x2 = g.screenX(0,MAX_LEG_SPAN,0); + float y2 = g.screenY(0,MAX_LEG_SPAN,0); + float distFromCenter = dist(x1,y1,width/2,height/2); + if(distFromCenter < dist(x1,y1,x2,y2) && distFromCenter < MAX_DIST_MOUSE){ + value = g.screenZ(0,0,0); } - g.popMatrix(); return value; -} - -color getColor() { + } + + color getColor() { int c = swattersSeen.size(); - float fac; - if (c == 0) { return color(0, 0, 0, 255); // Black - } - - // Define ranges and their color transitions - if (c <= 5) { - fac = c / 5.0; - return createColor(0, fac * 140, 255 - fac * 255); // Blue transition - } - else if (c <= 20) { - fac = (c - 5) / 15.0; - return createColor(fac * 255, 140 + fac * 155, 20); // Green with a higher base for better contrast - } - else if (c <= 40) { - fac = (c - 20) / 20.0; - return createColor(255, 255 - fac * 115, 50); // Yellow with a richer tone - } - else if (c <= 100) { - fac = (c - 40) / 60.0; - return createColor(255, 140 - fac * 140, 0); // Pink transition - } - else if (c <= 200) { - fac = (c - 100) / 100.0; - return createColor(255, 0, fac * 255); // Purple transition - } - else if (c <= 500) { - fac = (c - 200) / 300.0; - return createColor(255 - fac * 141, 20, 255); // Deeper purple to cyan - } - else if (c <= 1000) { - fac = (c - 500) / 500.0; - return createColor(114 - fac * 114, fac * 255, 255); // Cyan transition - } - else if (c <= 4000) { - fac = (c - 1000) / 3000.0; - return createColor(225 + fac * 30, 210 - fac * 20, 30 + fac * 50); // Gold with richer, deeper hues - } - else { - fac = min(1, (c - 4000) / 6000.0); - return createColor(255, 202 + fac * 53, fac * 255); // Transition to white + } else { + if (c <= 5) { + float fac = c / 5.0; + return color(0, fac * 140, 255 - fac * 255, 255); // Blue transition + } else if (c <= 20) { + float fac = (c - 5) / 15.0; + return color(fac * 255, 140 + fac * 155, 20, 255); // Green with a higher base for better contrast + } else if (c <= 40) { + float fac = (c - 20) / 20.0; + return color(255, 255 - fac * 115, 50, 255); // Yellow with a richer tone + } else if (c <= 100) { + float fac = (c - 40) / 60.0; + return color(255, 140 - fac * 140, 0, 255); // Pink transition + } else if (c <= 200) { + float fac = (c - 100) / 100.0; + return color(255, 0, fac * 255, 255); // Purple transition + } else if (c <= 500) { + float fac = (c - 200) / 300.0; + return color(255 - fac * 141, 20, 255, 255); // Deeper purple to cyan + } else if (c <= 1000) { + float fac = (c - 500) / 500.0; + return color(114 - fac * 114, fac * 255, 255, 255); // Cyan transition + } else if (c <= 4000) { + float fac = (c - 1000) / 3000.0; + return color(225 + fac * 30, 210 - fac * 20, 30 + fac * 50); // Gold with richer, deeper hues + } else { + float fac = min(1, (c - 4000) / 6000.0); + return color(255, 202 + fac * 53, fac * 255); // Transition to white + } } } -// Helper function for creating color -color createColor(float r, float g, float b) { - return color(r, g, b, 255); // Default alpha is 255 -} - -color transitionColor(color a, color b, float prog) { - return color( - lerp(red(a), red(b), prog), - lerp(green(a), green(b), prog), - lerp(blue(a), blue(b), prog) - ); -} - + color transitionColor(color a, color b, float prog){ + float newR = lerp(red(a), red(b), prog); + float newG = lerp(green(a), green(b), prog); + float newB = lerp(blue(a), blue(b), prog); + return color(newR, newG, newB); + } void drawSpider(Room room){ color c = getColor(); if(this == highlight_spider){ - c = color(0,255,0); + c = color(0,255,0); } - - // Cache the real coordinates to avoid repeated calls float[] realCoor = room.wallCoor_to_realCoor(coor); g.pushMatrix(); aTranslate(realCoor); @@ -145,8 +115,8 @@ color transitionColor(color a, color b, float prog) { g.rotateZ(realCoor[3]); g.beginShape(); for(int i = 0; i < 12; i++){ - float angle = i*2*PI/12; - g.vertex(cos(angle)*BODY_SPAN, 2, sin(angle)*BODY_SPAN); + float angle = i*2*PI/12; + g.vertex(cos(angle)*BODY_SPAN,2,sin(angle)*BODY_SPAN); } g.endShape(CLOSE); g.popMatrix(); @@ -154,45 +124,44 @@ color transitionColor(color a, color b, float prog) { g.stroke(c); g.strokeWeight(3); for(int L = 0; L < LEG_COUNT; L++){ - // Cache the real coordinates of each leg - float[] legRealCoor = room.wallCoor_to_realCoor(leg_coor[L]); - float[] Lcoor = aSubstract(legRealCoor, realCoor); - float[] Mcoor = multi(Lcoor, 0.5); - Mcoor[0] -= sin(realCoor[3]) * Ldist; - Mcoor[1] += cos(realCoor[3]) * Ldist; - g.line(0, 0, 0, Mcoor[0], Mcoor[1], Mcoor[2]); - g.line(Mcoor[0], Mcoor[1], Mcoor[2], Lcoor[0], Lcoor[1], Lcoor[2]); + float[] legRealCoor = room.wallCoor_to_realCoor(leg_coor[L]); + float[] Lcoor = aSubstract(legRealCoor,realCoor); + float[] Mcoor = multi(Lcoor,0.5); + Mcoor[0] -= sin(realCoor[3])*Ldist; + Mcoor[1] += cos(realCoor[3])*Ldist; + g.line(0,0,0,Mcoor[0],Mcoor[1],Mcoor[2]); + g.line(Mcoor[0],Mcoor[1],Mcoor[2],Lcoor[0],Lcoor[1],Lcoor[2]); } g.noStroke(); g.popMatrix(); - // Parent drawing logic (only if needed) - if (getAge() < 200 && parent != null && parent.getAge() >= getAge()){ - float[] parentCoor = room.wallCoor_to_realCoor(parent.coor); - if (realCoor[0] == parentCoor[0] && realCoor[1] == parentCoor[1]) return; - - g.pushMatrix(); - aTranslate(realCoor); - g.rotateZ(realCoor[3]); - g.beginShape(); - g.fill(255); - float WHITE_SPAN = BODY_SPAN * 4; - for (int i = 0; i < 12; i++) { - float angle = i * 2 * PI / 12; - g.vertex(cos(angle) * WHITE_SPAN, EPS * 2, sin(angle) * WHITE_SPAN); - } - g.endShape(CLOSE); - g.popMatrix(); - - if (dist(realCoor[0], realCoor[1], realCoor[2], parentCoor[0], parentCoor[1], parentCoor[2]) < WHITE_SPAN * 10){ - g.stroke(255); - g.strokeWeight(20); - g.line(realCoor[0], realCoor[1], realCoor[2], parentCoor[0], parentCoor[1], parentCoor[2]); - g.noStroke(); - } + + if(getAge() < 200 && parent != null && parent.getAge() >= getAge()){ + float[] parentCoor = room.wallCoor_to_realCoor(parent.coor); + if(realCoor[0] == parentCoor[0] && realCoor[1] == parentCoor[1]){ + return; + } + g.pushMatrix(); + aTranslate(realCoor); + g.rotateZ(realCoor[3]); + g.beginShape(); + g.fill(255); + float WHITE_SPAN = BODY_SPAN*4; + for(int i = 0; i < 12; i++){ + float angle = i*2*PI/12; + g.vertex(cos(angle)*WHITE_SPAN,EPS*2,sin(angle)*WHITE_SPAN); + } + g.endShape(CLOSE); + g.popMatrix(); + + if(dist(realCoor[0],realCoor[1],realCoor[2],parentCoor[0],parentCoor[1],parentCoor[2]) < WHITE_SPAN*10){ + g.stroke(255); + g.strokeWeight(20); + g.line(realCoor[0],realCoor[1],realCoor[2],parentCoor[0],parentCoor[1],parentCoor[2]); + g.noStroke(); + } } -} - + } float[] multi(float[] a, float m){ float[] result = new float[a.length]; for(int i = 0; i < a.length; i++){ @@ -229,58 +198,40 @@ color transitionColor(color a, color b, float prog) { void placeLegs(float[] center, int step, Room room, float darkest_sensed_shadow, ArrayList spiders){ float force_to_right_angles = 0.001; // how strongly should the spider's legs be dragged back into right angles? float first_angle = 0; - - // Precompute cos and sin values of angles once - float[] cosAngles = new float[LEG_COUNT]; - float[] sinAngles = new float[LEG_COUNT]; - - for (int L = 0; L < LEG_COUNT; L++) { - int genome_index = L * GENES_PER_LEG + 2 * step + 1; - if (darkest_sensed_shadow < genome[L * GENES_PER_LEG + 12]) { // it's below the threshold, so do the dark pattern - genome_index += 6; + for(int L = 0; L < LEG_COUNT; L++){ + int genome_index = L*GENES_PER_LEG+2*step+1; + if(darkest_sensed_shadow < genome[L*GENES_PER_LEG+12]){ // it's below the threshold, so do the dark pattern + genome_index += 6; + } + float distance = genome[genome_index]*MAX_LEG_SPAN; + float delta_x = leg_coor[L][0]-center[0]; + float delta_y = leg_coor[L][1]-center[1]; + float angle = atan2(delta_y,delta_x); + if(L == 0){ + first_angle = angle; + }else{ + float desired_angle = first_angle+PI/2*L; + float move = (desired_angle-angle); + while(move > PI){ + move -= 2*PI; } - float distance = genome[genome_index] * MAX_LEG_SPAN; - - float delta_x = leg_coor[L][0] - center[0]; - float delta_y = leg_coor[L][1] - center[1]; - - // Compute angle only once - float angle = atan2(delta_y, delta_x); - - if (L == 0) { - first_angle = angle; - } else { - float desired_angle = first_angle + PI / 2 * L; - float move = (desired_angle - angle); - while (move > PI) { - move -= 2 * PI; - } - while (move < -PI) { - move += 2 * PI; - } - angle += force_to_right_angles * move; + while(move < -PI){ + move += 2*PI; } - - // Store precomputed cos and sin values for reuse - cosAngles[L] = cos(angle); - sinAngles[L] = sin(angle); - - // Update leg positions - leg_coor[L][0] = center[0] + cosAngles[L] * distance; - leg_coor[L][1] = center[1] + sinAngles[L] * distance; + angle += force_to_right_angles*move; + } + leg_coor[L][0] = center[0]+cos(angle)*distance; + leg_coor[L][1] = center[1]+sin(angle)*distance; } - - coor = getWeightedCenter(step, room, darkest_sensed_shadow); - - for (int d = 0; d < 2; d++) { - if (coor[d] < 0) { - shiftAllBy(d, room.getMaxDim(d)); - } else if (coor[d] >= room.getMaxDim(d)) { - shiftAllBy(d, -room.getMaxDim(d)); - } + coor = getWeightedCenter(step,room,darkest_sensed_shadow); + for(int d = 0; d < 2; d++){ + if(coor[d] < 0){ + shiftAllBy(d,room.getMaxDim(d)); + }else if(coor[d] >= room.getMaxDim(d)){ + shiftAllBy(d,-room.getMaxDim(d)); + } } -} - + } void shiftAllBy(int dim, float amt){ coor[dim] += amt; for(int L = 0; L < LEG_COUNT; L++){ @@ -424,7 +375,7 @@ color transitionColor(color a, color b, float prog) { visIndex = totalIndex; totalIndex++; birth_tick = ticks; - swattersSeen = new HashSet(); // Creates an empty HashSet + swattersSeen = new ArrayList(0); } float getSensitivity(){ float sensitivity = 0; diff --git a/SpiderEvoSim.pde b/SpiderEvoSim.pde index c9a1b88..8ef4a46 100644 --- a/SpiderEvoSim.pde +++ b/SpiderEvoSim.pde @@ -1,18 +1,6 @@ -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Callable; -import java.util.concurrent.Future; -import java.util.HashSet; -import java.util.Set; -import java.util.Arrays; -import java.util.concurrent.*; -import java.util.stream.IntStream; -import java.util.ArrayList; -import java.util.List; import com.jogamp.newt.opengl.GLWindow; import processing.sound.*; -int threadsUsed = 4; // Number of threads to be used, can be changed as per the requirement int CENTER_X = 960; // try setting this to 960 or 961 if there is horizontal camera-pan-drifting String[] soundFileNames = {"slap0.wav","slap1.wav","slap2.wav","splat0.wav","splat1.wav","splat2.wav","boop1.wav","boop2.wav","jump.wav","news.wav"}; SoundFile[] sfx; @@ -44,6 +32,7 @@ boolean TRAP_MOUSE = true; boolean lock_highlight = false; GLWindow r; + int LEG_COUNT = 4; int GENES_PER_LEG = STEPS_CYCLE*4+1; //13 int GENOME_LENGTH = LEG_COUNT*GENES_PER_LEG; @@ -114,75 +103,22 @@ double safeAdd(double a, double b) { } return result; } - -ArrayList createSpiders(Room room) { - int START_SPIDER_COUNT = 300; // Number of spiders to create - ArrayList result = new ArrayList<>(); - - int batchSize = 50; // Define the size of each batch - int numberOfBatches = (int) Math.ceil((double) START_SPIDER_COUNT / batchSize); - - ExecutorService executor = Executors.newFixedThreadPool(threadsUsed); // Use threadsUsed variable - List> tasks = new ArrayList<>(); - - for (int batch = 0; batch < numberOfBatches; batch++) { - final int start = batch * batchSize; - final int end = Math.min(start + batchSize, START_SPIDER_COUNT); - - tasks.add(() -> { - for (int s = start; s < end; s++) { - Spider spider = new Spider(s, room); - synchronized (result) { // Ensure thread safety when adding to the result list - result.add(spider); - } - } - return null; - }); - } - - try { - executor.invokeAll(tasks); - } catch (InterruptedException e) { - e.printStackTrace(); - } finally { - executor.shutdown(); - } - - return result; +ArrayList createSpiders(Room room){ + int START_SPIDER_COUNT = 300; + ArrayList result = new ArrayList(0); + for(int s = 0; s < START_SPIDER_COUNT; s++){ + Spider newSpider = new Spider(s, room); + result.add(newSpider); + } + return result; } - -void createSwatters(Room room, int START_SPIDER_COUNT) { - swatters = new ArrayList<>(); - - int batchSize = 50; // Define the size of each batch - int numberOfBatches = (int) Math.ceil((double) START_SPIDER_COUNT / batchSize); - - ExecutorService executor = Executors.newFixedThreadPool(threadsUsed); // Use threadsUsed variable - List> tasks = new ArrayList<>(); - - for (int batch = 0; batch < numberOfBatches; batch++) { - final int start = batch * batchSize; - final int end = Math.min(start + batchSize, START_SPIDER_COUNT); - - tasks.add(() -> { - for (int s = start; s < end; s++) { - float perc = (s + 0.5f) / START_SPIDER_COUNT * 1.4f - 0.4f; - Swatter swatter = new Swatter(s, perc, room, swatters); - synchronized (swatters) { // Ensure thread safety when adding to the swatters list - swatters.add(swatter); - } - } - return null; - }); - } - - try { - executor.invokeAll(tasks); - } catch (InterruptedException e) { - e.printStackTrace(); - } finally { - executor.shutdown(); - } +void createSwatters(Room room, int START_SPIDER_COUNT){ + swatters = new ArrayList(0); + for(int s = 0; s < START_SPIDER_COUNT; s++){ + float perc = (s+0.5)/START_SPIDER_COUNT*1.4-0.4; + Swatter newSwatter = new Swatter(s, perc, room, swatters); + swatters.add(newSwatter); + } } void setup(){ @@ -251,7 +187,8 @@ void draw() { drawVisuals(); image(g, 0, 0); drawUI(); - frames++; + frames++; + if (camera[1] < -1) { camera[1] = -1; } @@ -259,6 +196,7 @@ void draw() { camera[1] = 1; } } + void checkHighlight(){ if(!lock_highlight){ highlight_spider = checkHighlightHelper(); @@ -314,6 +252,7 @@ String dateNumToMonthString(int d) { return monthNames[0] + " 1"; } + int totalDays = 0; for (int m = 0; m < 12; m++) { if (d < monthDays[m]) { return monthNames[m] + " " + (d + 1); @@ -381,86 +320,68 @@ void doPhysics(){ player.doPhysics(room); } -float getBiodiversity() { - int spiderCount = spiders.size(); - if (spiderCount == 0) return 0; - - // Create arrays for genome mean and variance calculation - float[] meanGenome = new float[GENOME_LENGTH]; - Arrays.fill(meanGenome, 0); - - // Calculate mean values using a parallel stream - spiders.parallelStream().forEach(s -> { - for (int g = 0; g < GENOME_LENGTH; g++) { - meanGenome[g] += s.genome[g]; - } - }); - - // Average the mean values - for (int g = 0; g < GENOME_LENGTH; g++) { - meanGenome[g] /= spiderCount; +float getBiodiversity(){ + float[] meanGenome = new float[GENOME_LENGTH]; + for(int g = 0; g < GENOME_LENGTH; g++){ + meanGenome[g] = 0; + } + for(int s = 0; s < spiders.size(); s++){ + for(int g = 0; g < GENOME_LENGTH; g++){ + meanGenome[g] += spiders.get(s).genome[g]; } - - // Calculate variance in parallel - float[] variances = new float[GENOME_LENGTH]; - Arrays.fill(variances, 0); - - spiders.parallelStream().forEach(s -> { - for (int g = 0; g < GENOME_LENGTH; g++) { - variances[g] += Math.pow(s.genome[g] - meanGenome[g], 2); - } - }); - - // Calculate the total diversity - float total_diversity = 0; - for (int g = 0; g < GENOME_LENGTH; g++) { - total_diversity += Math.sqrt(variances[g] / spiderCount); + } + for(int g = 0; g < GENOME_LENGTH; g++){ + meanGenome[g] /= spiders.size(); + } + float[] variances = new float[GENOME_LENGTH]; + for(int g = 0; g < GENOME_LENGTH; g++){ + variances[g] = 0; + } + for(int s = 0; s < spiders.size(); s++){ + for(int g = 0; g < GENOME_LENGTH; g++){ + variances[g] += pow(spiders.get(s).genome[g]-meanGenome[g],2); } - - return total_diversity / GENOME_LENGTH * 100; + } + float total_diversity = 0; + for(int g = 0; g < GENOME_LENGTH; g++){ + total_diversity += sqrt(variances[g]/spiders.size()); + } + return total_diversity/GENOME_LENGTH*100; } - void collectData() { if (ticks % CHANGE_WINDOWS_EVERY == 0) { for (int w = 0; w < windows.size(); w++) { windows.get(w).updateShow(); } } - if (ticks % (long)TICKS_PER_DAY == 0) { - // Cache the spider count to avoid repeated calls to spiders.size() - int spiderCount = spiders.size(); - - // Initialize the datum array before the conditional block + // Keep using Float[] to match Spider.writeData() method Float[] datum = new Float[STAT_COUNT]; - Arrays.fill(datum, 0.0f); - - if (spiderCount > 0) { - // Accumulate data into the datum array - for (int s = 0; s < spiderCount; s++) { - spiders.get(s).writeData(datum); - } - - // Average the data after accumulation - for (int d = 0; d < STAT_COUNT; d++) { - datum[d] /= spiderCount; - } + for (int d = 0; d < STAT_COUNT; d++) { + datum[d] = 0.0f; // Use 0.0f for Float } - - // Additional data (no need for division) - datum[1] = (float) dailyDeaths; - datum[2] = (float) (swattersSeenTotal - dailyDeaths); + + // Get spider count before division + float spiderCount = spiders.size(); + + for (int s = 0; s < spiders.size(); s++) { + spiders.get(s).writeData(datum); + } + + // Prevent division by zero and ensure precise division + for (int d = 0; d < STAT_COUNT; d++) { + datum[d] = spiderCount > 0 ? datum[d] / spiderCount : 0.0f; + } + + datum[1] = (float)dailyDeaths; + datum[2] = (float)(swattersSeenTotal - dailyDeaths); datum[4] = getBiodiversity(); - - // Reset daily statistics + dailyDeaths = 0; swattersSeenTotal = 0; - - // Store the results and notes stats.add(datum); statNotes.add(""); - - // Prepare for graph rendering + float[] graph_dim = {100, 120, 575, 400}; String[] titles = { "Average Age (days)", @@ -470,27 +391,19 @@ void collectData() { "Total Biodiversity (out of 100)", "Average Swatters Seen" }; - - // Prepare graph data once and reuse it for all graphs + for (int d = 0; d < STAT_COUNT; d++) { float[] graphData = new float[stats.size()]; - - // Extract graph data from stats in one pass for (int i = 0; i < stats.size(); i++) { graphData[i] = stats.get(i)[d]; } - - // Render graph for each statistic drawGraphOn(statImages[d], graphData, titles[d], graph_dim, color(128,0,0), d); } - - // Play sound with adjusted amplitude based on playback speed + sfx[9].play(); - sfx[9].amp(1.0 - min(0.8, (playback_speed - 1) / 200.0)); + sfx[9].amp(1.0 - min(0.8, (playback_speed-1)/200.0)); } } - - float getUnit(float a, float b){ float diff = b-a; float[] units = {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}; From 93ba524f2b0541177ebb5125653ac97b88fcd98a Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 19 Nov 2024 13:22:31 +1100 Subject: [PATCH 10/32] New loading screen! Shorter loading times because of multiple threads + cool loading screen!!! --- SpiderEvoSim.pde | 75 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 19 deletions(-) diff --git a/SpiderEvoSim.pde b/SpiderEvoSim.pde index 8ef4a46..c1d11e2 100644 --- a/SpiderEvoSim.pde +++ b/SpiderEvoSim.pde @@ -121,52 +121,89 @@ void createSwatters(Room room, int START_SPIDER_COUNT){ } } -void setup(){ - windowImages = new PImage[WINDOW_COUNT]; - for(int w = 0; w < WINDOW_COUNT; w++){ - windowImages[w] = loadImage("windows/w"+nf(w+1,4,0)+".png"); - } - sfx = new SoundFile[soundFileNames.length]; - for(int s = 0; s < soundFileNames.length; s++){ - sfx[s] = new SoundFile(this, "audio/"+soundFileNames[s]); - } +// Global variable to track loading status +boolean resourcesLoaded = false; + +// Must be declared at the top level for Processing's size() to work +void settings() { + size(1920,1080,P3D); +} + void setup() { + // Initialize core components first float[][] walls = {{0,0},{975,0},{975,670},{1100,670},{1100,0},{2100,0},{2100,1000},{1100,1000},{1100,780},{975,780},{975,1000},{0,1000}}; float[] zs = {0,500}; float[] player_coor = {700,700,0,0}; room = new Room(walls,zs); player = new Player(player_coor); + keyHandler = new KeyHandler(); + + // Setup window + r = (GLWindow)surface.getNative(); + r.confinePointer(true); + r.setPointerVisible(false); + g = createGraphics(1920,1080,P3D); + + // Start resource loading thread + thread("loadResources"); +} + +void loadResources() { + // Load windows in batches of 5 + windowImages = new PImage[WINDOW_COUNT]; + for(int w = 0; w < WINDOW_COUNT; w += 5) { + int endIndex = min(w + 5, WINDOW_COUNT); + for(int i = w; i < endIndex; i++) { + windowImages[i] = loadImage("windows/w"+nf(i+1,4,0)+".png"); + } + } + + // Load sound files in batches + sfx = new SoundFile[soundFileNames.length]; + for(int s = 0; s < soundFileNames.length; s += 3) { + int endIndex = min(s + 3, soundFileNames.length); + for(int i = s; i < endIndex; i++) { + sfx[i] = new SoundFile(this, "audio/"+soundFileNames[i]); + } + } + + // Initialize game objects spiders = createSpiders(room); createSwatters(room, 0); - keyHandler = new KeyHandler(); - size(1920,1080,P3D); + // Initialize buttons buttons.add(new Button(0,1300,300,"Increase")); buttons.add(new Button(1,1450,300,"Decrease")); - buttons.add(new Button(2,1800,300,"Enlarge,Swatters")); buttons.add(new Button(3,1950,300,"Shrink,Swatters")); - buttons.add(new Button(4,1800,700,"Speed up,Swatters")); buttons.add(new Button(5,1950,700,"Slow down,Swatters")); - buttons.add(new Button(6,1450,700,"Invent,Swatters")); + // Initialize stat images for(int d = 0; d < STAT_COUNT; d++){ statImages[d] = createGraphics(800,600); } - r = (GLWindow)surface.getNative(); - r.confinePointer(true); - r.setPointerVisible(false); - g = createGraphics(1920,1080,P3D); + // Mark loading as complete + resourcesLoaded = true; } + //FPS counter ArrayList frameTimestamps = new ArrayList<>(); // To store frame timestamps float averageFps = 0; // Average FPS over the last 4 seconds void draw() { - // Capture current time in milliseconds + if (!resourcesLoaded) { + // Show loading screen + background(0); + fill(255); + textAlign(CENTER, CENTER); + textSize(32); + text("Loading...", width/2, height/2); + return; + } + // Capture current time in milliseconds long currentTime = millis(); frameTimestamps.add(currentTime); From 97c2fedef0f2ace684209b841327ef16cf2b777c Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 19 Nov 2024 13:23:29 +1100 Subject: [PATCH 11/32] Unused paramiter removed --- SpiderEvoSim.pde | 1 - 1 file changed, 1 deletion(-) diff --git a/SpiderEvoSim.pde b/SpiderEvoSim.pde index c1d11e2..90e43b8 100644 --- a/SpiderEvoSim.pde +++ b/SpiderEvoSim.pde @@ -289,7 +289,6 @@ String dateNumToMonthString(int d) { return monthNames[0] + " 1"; } - int totalDays = 0; for (int m = 0; m < 12; m++) { if (d < monthDays[m]) { return monthNames[m] + " " + (d + 1); From c7bee57be3f369e9de58aa14d75141767c9c3b89 Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 19 Nov 2024 13:35:22 +1100 Subject: [PATCH 12/32] Actually reduces Jitter When the day ticks over, there is less of a stutter now due to improvements to collect data. --- SpiderEvoSim.pde | 53 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/SpiderEvoSim.pde b/SpiderEvoSim.pde index 90e43b8..16a83f5 100644 --- a/SpiderEvoSim.pde +++ b/SpiderEvoSim.pde @@ -1,5 +1,6 @@ import com.jogamp.newt.opengl.GLWindow; import processing.sound.*; +import java.util.Arrays; int CENTER_X = 960; // try setting this to 960 or 961 if there is horizontal camera-pan-drifting String[] soundFileNames = {"slap0.wav","slap1.wav","slap2.wav","splat0.wav","splat1.wav","splat2.wav","boop1.wav","boop2.wav","jump.wav","news.wav"}; @@ -384,42 +385,50 @@ float getBiodiversity(){ } return total_diversity/GENOME_LENGTH*100; } + void collectData() { if (ticks % CHANGE_WINDOWS_EVERY == 0) { - for (int w = 0; w < windows.size(); w++) { - windows.get(w).updateShow(); + Window[] windowArray = windows.toArray(new Window[0]); + for (Window window : windowArray) { + window.updateShow(); } } + if (ticks % (long)TICKS_PER_DAY == 0) { - // Keep using Float[] to match Spider.writeData() method + // Pre-allocate arrays and reuse them Float[] datum = new Float[STAT_COUNT]; - for (int d = 0; d < STAT_COUNT; d++) { - datum[d] = 0.0f; // Use 0.0f for Float - } + Spider[] spiderArray = spiders.toArray(new Spider[0]); + float spiderCount = spiderArray.length; - // Get spider count before division - float spiderCount = spiders.size(); + // Initialize datum array more efficiently + Arrays.fill(datum, 0.0f); - for (int s = 0; s < spiders.size(); s++) { - spiders.get(s).writeData(datum); + // Process all spiders in a single pass + for (Spider spider : spiderArray) { + spider.writeData(datum); } - // Prevent division by zero and ensure precise division - for (int d = 0; d < STAT_COUNT; d++) { - datum[d] = spiderCount > 0 ? datum[d] / spiderCount : 0.0f; + // Batch process divisions + if (spiderCount > 0) { + float invCount = 1.0f / spiderCount; + for (int d = 0; d < STAT_COUNT; d++) { + datum[d] *= invCount; + } } datum[1] = (float)dailyDeaths; datum[2] = (float)(swattersSeenTotal - dailyDeaths); datum[4] = getBiodiversity(); + // Store data and reset counters dailyDeaths = 0; swattersSeenTotal = 0; stats.add(datum); statNotes.add(""); - float[] graph_dim = {100, 120, 575, 400}; - String[] titles = { + // Cache graph dimensions and titles + final float[] GRAPH_DIM = {100, 120, 575, 400}; + final String[] TITLES = { "Average Age (days)", "Daily Deaths", "Daily Swatter Escapes", @@ -428,18 +437,26 @@ void collectData() { "Average Swatters Seen" }; + // Pre-allocate graph data array + float[] graphData = new float[stats.size()]; + // Cache the color value + color graphColor = color(128, 0, 0); + + // Draw all graphs with minimal object creation for (int d = 0; d < STAT_COUNT; d++) { - float[] graphData = new float[stats.size()]; for (int i = 0; i < stats.size(); i++) { graphData[i] = stats.get(i)[d]; } - drawGraphOn(statImages[d], graphData, titles[d], graph_dim, color(128,0,0), d); + drawGraphOn(statImages[d], graphData, TITLES[d], GRAPH_DIM, graphColor, d); } + // Handle sound + float amp = 1.0f - min(0.8f, (playback_speed-1)/200.0f); sfx[9].play(); - sfx[9].amp(1.0 - min(0.8, (playback_speed-1)/200.0)); + sfx[9].amp(amp); } } + float getUnit(float a, float b){ float diff = b-a; float[] units = {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}; From 8e6ea5d584f19472e25f18a6cf25a9e9ad40659a Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 19 Nov 2024 13:44:00 +1100 Subject: [PATCH 13/32] Small graph drawing optimisation All in the name of less jitters! --- SpiderEvoSim.pde | 191 +++++++++++++++++++++++++++-------------------- 1 file changed, 108 insertions(+), 83 deletions(-) diff --git a/SpiderEvoSim.pde b/SpiderEvoSim.pde index 16a83f5..1ce0aed 100644 --- a/SpiderEvoSim.pde +++ b/SpiderEvoSim.pde @@ -467,98 +467,123 @@ float getUnit(float a, float b){ } return 1; } -void drawGraphOn(PGraphics s, float[] data, String title, float[] graph_dim, color col, int d){ - s.beginDraw(); - s.background(255); - float max = -9999999; - float min = 9999999; - for(int i = 0; i < data.length; i++){ - if(data[i] > max){ - max = data[i]; +void drawGraphOn(PGraphics s, float[] data, String title, float[] graph_dim, color col, int d) { + // Pre-calculate frequently used values + final float graphWidth = graph_dim[2]; + final float graphHeight = graph_dim[3]; + final float graphX = graph_dim[0]; + final float graphY = graph_dim[1]; + final int dataLength = data.length; + + // Find min/max in single pass + float min = Float.MAX_VALUE; + float max = -Float.MAX_VALUE; + for (float value : data) { + if (value > max) max = value; + if (value < min) min = value; } - if(data[i] < min){ - min = data[i]; + if (max == min) max += 0.1; + + final float range = max - min; + final float xScale = graphWidth / (dataLength - 1); + final float yScale = graphHeight / range; + + s.beginDraw(); + s.background(255); + + // Batch similar operations together + s.strokeWeight(4); + s.stroke(140); + s.fill(140); + s.textAlign(CENTER); + + // Cache transformed coordinates + float[] xCoords = new float[dataLength]; + float[] yCoords = new float[dataLength]; + for (int i = 0; i < dataLength; i++) { + xCoords[i] = (i * xScale) + graphX; + yCoords[i] = graphY + graphHeight * (1 - (data[i] - min) / range); } - } - if(max == min){ - max += 0.1; - } - float E_R = 15; - s.strokeWeight(4); - s.stroke(140); - s.fill(140); - s.textAlign(CENTER); - float TS = 23; - s.textSize(TS); - for(int i = 0; i < data.length; i++){ - String str = statNotes.get(i); - if(str.length() >= 1){ - String[] parts = str.split("-"); - float x = ((float)i)/data.length*graph_dim[2]+graph_dim[0]; - float y = graph_dim[1]; - float h = graph_dim[3]; - float y2 = y+h-parts.length*TS+3*TS; - for(int j = 0; j < parts.length; j++){ - s.text(parts[j],x,y2+j*TS+TS); - } - s.line(x,y,x,y2); + + // Draw annotations in batch + s.textSize(23); + for (int i = 0; i < dataLength; i++) { + String str = statNotes.get(i); + if (str.length() >= 1) { + String[] parts = str.split("-"); + float y2 = graphY + graphHeight - parts.length * 23 + 69; // 3 * 23 + + // Draw line first + s.line(xCoords[i], graphY, xCoords[i], y2); + + // Then all text + for (int j = 0; j < parts.length; j++) { + s.text(parts[j], xCoords[i], y2 + j * 23 + 23); + } + } } - } - float unit = getUnit(min, max); - float first_unit = floor(min/unit)*unit; - s.textAlign(RIGHT); - float TS2 = 36; - s.textSize(TS2); - s.strokeWeight(2); - s.stroke(170); - boolean INTEGER_MEASURE = (d == 1 || d == 2); - for(float u = first_unit; u < max; u += unit){ - float x = graph_dim[0]; - float y = (1-(u-min)/(max-min))*graph_dim[3]+graph_dim[1]; - s.line(x,y,x+graph_dim[2],y); - String str = nf(u,0,2); - if(u%1.0 >= 0.999 || u%10 <= 0.001 || INTEGER_MEASURE){ - str = ""+(int)u; + + // Draw grid lines and values + float unit = getUnit(min, max); + float firstUnit = floor(min/unit) * unit; + boolean integerMeasure = (d == 1 || d == 2); + + s.textAlign(RIGHT); + s.textSize(36); + s.strokeWeight(2); + s.stroke(170); + + for (float u = firstUnit; u < max; u += unit) { + float y = graphY + graphHeight * (1 - (u - min) / range); + s.line(graphX, y, graphX + graphWidth, y); + + String str = integerMeasure ? String.valueOf((int)u) : + (u % 1.0 >= 0.999 || u % 10 <= 0.001) ? String.valueOf((int)u) : nf(u, 0, 2); + s.text(str, graphX - 10, y + 8); // 23 * 0.35 ≈ 8 } - s.text(str,x-10,y+TS*0.35); - } - s.strokeWeight(4); - for(int i = 0; i < data.length; i++){ - float x = ((float)i)/data.length*graph_dim[2]+graph_dim[0]; - float y = (1-(data[i]-min)/(max-min))*graph_dim[3]+graph_dim[1]; + + // Draw data points and lines + s.strokeWeight(4); s.fill(col); - if(data.length < 50){ - s.noStroke(); - s.ellipse(x,y,E_R,E_R); + s.stroke(col); + + boolean showPoints = dataLength < 50; + for (int i = 0; i < dataLength - 1; i++) { + if (showPoints) { + s.noStroke(); + s.ellipse(xCoords[i], yCoords[i], 15, 15); + s.stroke(col); + } + s.line(xCoords[i], yCoords[i], xCoords[i + 1], yCoords[i + 1]); } - if(i == data.length-1){ - float TS3 = (data[i] > 10 && !INTEGER_MEASURE) ? 39 : 50; - s.textSize(TS3); - s.textAlign(LEFT); - String str = INTEGER_MEASURE ? (int)data[i]+"" : nf(data[i],0,2); - s.text(str,x+TS3*0.5,y+TS3*0.35); - if(i >= 1){ - float delta = data[i]-data[i-1]; - String delta_str = INTEGER_MEASURE ? (int)delta+"" : nf(delta,0,2); - if(data[i] >= data[i-1]){ - delta_str = "+"+delta_str; + + // Draw final point and values + if (dataLength > 0) { + int last = dataLength - 1; + float textSize = (data[last] > 10 && !integerMeasure) ? 39 : 50; + s.textSize(textSize); + s.textAlign(LEFT); + + String finalValue = integerMeasure ? String.valueOf((int)data[last]) : nf(data[last], 0, 2); + s.text(finalValue, xCoords[last] + textSize * 0.5, yCoords[last] + textSize * 0.35); + + if (last > 0) { + float delta = data[last] - data[last - 1]; + String deltaStr = (delta >= 0 ? "+" : "") + (integerMeasure ? String.valueOf((int)delta) : nf(delta, 0, 2)); + s.textSize(textSize * 0.6); + s.text(deltaStr, xCoords[last] + textSize * 0.5, yCoords[last] + textSize * 1.1); } - s.textSize(TS3*0.6); - s.text(delta_str,x+TS3*0.5,y+TS3*1.1); - } - }else{ - float x2 = ((float)(i+1))/data.length*graph_dim[2]+graph_dim[0]; - float y2 = (1-(data[i+1]-min)/(max-min))*graph_dim[3]+graph_dim[1]; - s.stroke(col); - s.line(x,y,x2,y2); } - } - s.textAlign(CENTER); - s.fill(0); - s.textSize(60); - s.text(title,graph_dim[0]+graph_dim[2]*0.5,graph_dim[1]-50); - s.endDraw(); + + // Draw title + s.textAlign(CENTER); + s.fill(0); + s.textSize(60); + s.text(title, graphX + graphWidth * 0.5, graphY - 50); + + s.endDraw(); } + float daylight() { float days = ticksToDays(ticks); if (Float.isInfinite(days) || Float.isNaN(days)) { From 5db62b064a0619ffd8a59b71766bec43b6615333 Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 19 Nov 2024 14:21:07 +1100 Subject: [PATCH 14/32] some floats turned into ints for performance --- SpiderEvoSim.pde | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/SpiderEvoSim.pde b/SpiderEvoSim.pde index 1ce0aed..f4036ad 100644 --- a/SpiderEvoSim.pde +++ b/SpiderEvoSim.pde @@ -298,25 +298,30 @@ String dateNumToMonthString(int d) { } return monthNames[11] + " " + monthDays[11]; } -String ticksToDate(long t) { - float daysTotalFloat = ticksToDays(t) + 0.5f; - int daysTotalInt = (int)daysTotalFloat; - float timeOfDay = daysTotalFloat % 1.0f; - - // Add bounds checking - if (daysTotalInt > 365_000_000) { + +String ticksToDate(long t) { + // Convert ticks to days and add 0.5 for rounding, avoiding floating-point operations + float daysTotal = ticksToDays(t) + 1; // Add 1 instead of 0.5f for integer-based rounding + if (daysTotal > 365_000_000L) { println("Warning: Date calculation overflow"); return "Year MAX"; } - - int years = daysTotalInt / 365; - int days = daysTotalInt % 365; - - String[] TOD_LIST = {"Night","Sunrise","Morning","Afternoon","Sunset","Evening"}; - String TOD = TOD_LIST[(int)(timeOfDay * 6)]; - + + // Calculate years and remaining days + int years = (int)(daysTotal / 365); + int days = (int)(daysTotal % 365); + + // Determine time of day using integer arithmetic + int timeOfDayTicks = (int)(t % (24L * 60 * 60 * 1000)); // Ticks in one day + int todIndex = (int)((timeOfDayTicks * 6L) / (24L * 60 * 60 * 1000)); // Map ticks to 0-5 range + + String[] TOD_LIST = {"Night", "Sunrise", "Morning", "Afternoon", "Sunset", "Evening"}; + String TOD = TOD_LIST[todIndex]; + + // Construct the result string return "Year " + (years + 1) + ", " + dateNumToMonthString(days) + " - " + TOD; } + void doMouse(){ if(TRAP_MOUSE){ if(frames >= 2){ From 4379d1f010744bb3c6cda94911b79b909ebf6b1f Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 19 Nov 2024 17:36:04 +1100 Subject: [PATCH 15/32] most demanding part of the draw function found Draw visuals. And in that, the draw spiders and popMatrix/endDraw are the only things worth optimising from there! --- SpiderEvoSim.pde | 2 ++ 1 file changed, 2 insertions(+) diff --git a/SpiderEvoSim.pde b/SpiderEvoSim.pde index f4036ad..cafc57c 100644 --- a/SpiderEvoSim.pde +++ b/SpiderEvoSim.pde @@ -616,7 +616,9 @@ void drawVisuals(){ drawButtons(); g.popMatrix(); g.endDraw(); +//Most demanding by far! } + void drawWindows(){ for(int w = 0; w < windows.size(); w++){ windows.get(w).drawWindow(room); From 9f71ced635996a593653a7b6c8e6589248632480 Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 19 Nov 2024 17:37:39 +1100 Subject: [PATCH 16/32] small amount of debugging --- SpiderEvoSim.pde | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/SpiderEvoSim.pde b/SpiderEvoSim.pde index cafc57c..1919d0d 100644 --- a/SpiderEvoSim.pde +++ b/SpiderEvoSim.pde @@ -617,6 +617,28 @@ void drawVisuals(){ g.popMatrix(); g.endDraw(); //Most demanding by far! + checkHighlight(); + long afterCheckHighlight = System.nanoTime(); + + drawSpiders(room); + long afterDrawSpiders = System.nanoTime(); + + drawSwatters(room); + long afterDrawSwatters = System.nanoTime(); + + drawButtons(); + long afterDrawButtons = System.nanoTime(); + + g.popMatrix(); + g.endDraw(); + long endTime = System.nanoTime(); + + // Print profiling results + println("Draw Spiders: " + (afterDrawSpiders - afterCheckHighlight) / 1e6 + " ms"); + println("Draw Swatters: " + (afterDrawSwatters - afterDrawSpiders) / 1e6 + " ms"); + println("Draw Buttons: " + (afterDrawButtons - afterDrawSwatters) / 1e6 + " ms"); + println("Pop/EndDraw: " + (endTime - afterDrawButtons) / 1e6 + " ms"); + println("Total DrawVisuals Time: " + (endTime - startTime) / 1e6 + " ms"); } void drawWindows(){ From f1f60f1715d3d2fcbdf25138ad72077e4094498a Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 19 Nov 2024 18:09:36 +1100 Subject: [PATCH 17/32] Fixed duplicates --- SpiderEvoSim.pde | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/SpiderEvoSim.pde b/SpiderEvoSim.pde index 1919d0d..df71d2c 100644 --- a/SpiderEvoSim.pde +++ b/SpiderEvoSim.pde @@ -611,15 +611,9 @@ void drawVisuals(){ player.drawPlayer(); drawWindows(); checkHighlight(); - drawSpiders(room); - drawSwatters(room); - drawButtons(); - g.popMatrix(); - g.endDraw(); -//Most demanding by far! - checkHighlight(); - long afterCheckHighlight = System.nanoTime(); - + + long startTime = System.nanoTime(); + //Most demanding by far! drawSpiders(room); long afterDrawSpiders = System.nanoTime(); From 78fc2fd4af7227ea67a15d24f097814ae5b812dc Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 19 Nov 2024 18:11:32 +1100 Subject: [PATCH 18/32] Made me commit it for some reason --- SpiderEvoSim.pde | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/SpiderEvoSim.pde b/SpiderEvoSim.pde index df71d2c..3fc470e 100644 --- a/SpiderEvoSim.pde +++ b/SpiderEvoSim.pde @@ -611,6 +611,7 @@ void drawVisuals(){ player.drawPlayer(); drawWindows(); checkHighlight(); +<<<<<<< HEAD long startTime = System.nanoTime(); //Most demanding by far! @@ -633,6 +634,14 @@ void drawVisuals(){ println("Draw Buttons: " + (afterDrawButtons - afterDrawSwatters) / 1e6 + " ms"); println("Pop/EndDraw: " + (endTime - afterDrawButtons) / 1e6 + " ms"); println("Total DrawVisuals Time: " + (endTime - startTime) / 1e6 + " ms"); +======= + drawSpiders(room); + drawSwatters(room); + drawButtons(); + g.popMatrix(); + g.endDraw(); +//Most demanding by far! +>>>>>>> parent of 9f71ced (small amount of debugging) } void drawWindows(){ From 5e16cfc993c7e35828d7f687374058a1236ed133 Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 19 Nov 2024 18:13:12 +1100 Subject: [PATCH 19/32] Revert debugging --- SpiderEvoSim.pde | 30 ++---------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/SpiderEvoSim.pde b/SpiderEvoSim.pde index 3fc470e..614c6bc 100644 --- a/SpiderEvoSim.pde +++ b/SpiderEvoSim.pde @@ -611,37 +611,11 @@ void drawVisuals(){ player.drawPlayer(); drawWindows(); checkHighlight(); -<<<<<<< HEAD - - long startTime = System.nanoTime(); - //Most demanding by far! - drawSpiders(room); - long afterDrawSpiders = System.nanoTime(); - - drawSwatters(room); - long afterDrawSwatters = System.nanoTime(); - - drawButtons(); - long afterDrawButtons = System.nanoTime(); - - g.popMatrix(); - g.endDraw(); - long endTime = System.nanoTime(); - - // Print profiling results - println("Draw Spiders: " + (afterDrawSpiders - afterCheckHighlight) / 1e6 + " ms"); - println("Draw Swatters: " + (afterDrawSwatters - afterDrawSpiders) / 1e6 + " ms"); - println("Draw Buttons: " + (afterDrawButtons - afterDrawSwatters) / 1e6 + " ms"); - println("Pop/EndDraw: " + (endTime - afterDrawButtons) / 1e6 + " ms"); - println("Total DrawVisuals Time: " + (endTime - startTime) / 1e6 + " ms"); -======= - drawSpiders(room); + drawSpiders(room); //Most demanding drawSwatters(room); drawButtons(); g.popMatrix(); - g.endDraw(); -//Most demanding by far! ->>>>>>> parent of 9f71ced (small amount of debugging) + g.endDraw(); //Graph update } void drawWindows(){ From 9154bf2c414fa65143479951dd45b484a526151c Mon Sep 17 00:00:00 2001 From: Rohan Date: Fri, 22 Nov 2024 10:01:06 +1100 Subject: [PATCH 20/32] Revert graph-breaking optimisation --- SpiderEvoSim.pde | 229 ++++++++++++++++++++--------------------------- 1 file changed, 99 insertions(+), 130 deletions(-) diff --git a/SpiderEvoSim.pde b/SpiderEvoSim.pde index 614c6bc..16a83f5 100644 --- a/SpiderEvoSim.pde +++ b/SpiderEvoSim.pde @@ -298,30 +298,25 @@ String dateNumToMonthString(int d) { } return monthNames[11] + " " + monthDays[11]; } - -String ticksToDate(long t) { - // Convert ticks to days and add 0.5 for rounding, avoiding floating-point operations - float daysTotal = ticksToDays(t) + 1; // Add 1 instead of 0.5f for integer-based rounding - if (daysTotal > 365_000_000L) { +String ticksToDate(long t) { + float daysTotalFloat = ticksToDays(t) + 0.5f; + int daysTotalInt = (int)daysTotalFloat; + float timeOfDay = daysTotalFloat % 1.0f; + + // Add bounds checking + if (daysTotalInt > 365_000_000) { println("Warning: Date calculation overflow"); return "Year MAX"; } - - // Calculate years and remaining days - int years = (int)(daysTotal / 365); - int days = (int)(daysTotal % 365); - - // Determine time of day using integer arithmetic - int timeOfDayTicks = (int)(t % (24L * 60 * 60 * 1000)); // Ticks in one day - int todIndex = (int)((timeOfDayTicks * 6L) / (24L * 60 * 60 * 1000)); // Map ticks to 0-5 range - - String[] TOD_LIST = {"Night", "Sunrise", "Morning", "Afternoon", "Sunset", "Evening"}; - String TOD = TOD_LIST[todIndex]; - - // Construct the result string + + int years = daysTotalInt / 365; + int days = daysTotalInt % 365; + + String[] TOD_LIST = {"Night","Sunrise","Morning","Afternoon","Sunset","Evening"}; + String TOD = TOD_LIST[(int)(timeOfDay * 6)]; + return "Year " + (years + 1) + ", " + dateNumToMonthString(days) + " - " + TOD; } - void doMouse(){ if(TRAP_MOUSE){ if(frames >= 2){ @@ -472,123 +467,98 @@ float getUnit(float a, float b){ } return 1; } -void drawGraphOn(PGraphics s, float[] data, String title, float[] graph_dim, color col, int d) { - // Pre-calculate frequently used values - final float graphWidth = graph_dim[2]; - final float graphHeight = graph_dim[3]; - final float graphX = graph_dim[0]; - final float graphY = graph_dim[1]; - final int dataLength = data.length; - - // Find min/max in single pass - float min = Float.MAX_VALUE; - float max = -Float.MAX_VALUE; - for (float value : data) { - if (value > max) max = value; - if (value < min) min = value; +void drawGraphOn(PGraphics s, float[] data, String title, float[] graph_dim, color col, int d){ + s.beginDraw(); + s.background(255); + float max = -9999999; + float min = 9999999; + for(int i = 0; i < data.length; i++){ + if(data[i] > max){ + max = data[i]; } - if (max == min) max += 0.1; - - final float range = max - min; - final float xScale = graphWidth / (dataLength - 1); - final float yScale = graphHeight / range; - - s.beginDraw(); - s.background(255); - - // Batch similar operations together - s.strokeWeight(4); - s.stroke(140); - s.fill(140); - s.textAlign(CENTER); - - // Cache transformed coordinates - float[] xCoords = new float[dataLength]; - float[] yCoords = new float[dataLength]; - for (int i = 0; i < dataLength; i++) { - xCoords[i] = (i * xScale) + graphX; - yCoords[i] = graphY + graphHeight * (1 - (data[i] - min) / range); + if(data[i] < min){ + min = data[i]; } - - // Draw annotations in batch - s.textSize(23); - for (int i = 0; i < dataLength; i++) { - String str = statNotes.get(i); - if (str.length() >= 1) { - String[] parts = str.split("-"); - float y2 = graphY + graphHeight - parts.length * 23 + 69; // 3 * 23 - - // Draw line first - s.line(xCoords[i], graphY, xCoords[i], y2); - - // Then all text - for (int j = 0; j < parts.length; j++) { - s.text(parts[j], xCoords[i], y2 + j * 23 + 23); - } - } + } + if(max == min){ + max += 0.1; + } + float E_R = 15; + s.strokeWeight(4); + s.stroke(140); + s.fill(140); + s.textAlign(CENTER); + float TS = 23; + s.textSize(TS); + for(int i = 0; i < data.length; i++){ + String str = statNotes.get(i); + if(str.length() >= 1){ + String[] parts = str.split("-"); + float x = ((float)i)/data.length*graph_dim[2]+graph_dim[0]; + float y = graph_dim[1]; + float h = graph_dim[3]; + float y2 = y+h-parts.length*TS+3*TS; + for(int j = 0; j < parts.length; j++){ + s.text(parts[j],x,y2+j*TS+TS); + } + s.line(x,y,x,y2); } - - // Draw grid lines and values - float unit = getUnit(min, max); - float firstUnit = floor(min/unit) * unit; - boolean integerMeasure = (d == 1 || d == 2); - - s.textAlign(RIGHT); - s.textSize(36); - s.strokeWeight(2); - s.stroke(170); - - for (float u = firstUnit; u < max; u += unit) { - float y = graphY + graphHeight * (1 - (u - min) / range); - s.line(graphX, y, graphX + graphWidth, y); - - String str = integerMeasure ? String.valueOf((int)u) : - (u % 1.0 >= 0.999 || u % 10 <= 0.001) ? String.valueOf((int)u) : nf(u, 0, 2); - s.text(str, graphX - 10, y + 8); // 23 * 0.35 ≈ 8 + } + float unit = getUnit(min, max); + float first_unit = floor(min/unit)*unit; + s.textAlign(RIGHT); + float TS2 = 36; + s.textSize(TS2); + s.strokeWeight(2); + s.stroke(170); + boolean INTEGER_MEASURE = (d == 1 || d == 2); + for(float u = first_unit; u < max; u += unit){ + float x = graph_dim[0]; + float y = (1-(u-min)/(max-min))*graph_dim[3]+graph_dim[1]; + s.line(x,y,x+graph_dim[2],y); + String str = nf(u,0,2); + if(u%1.0 >= 0.999 || u%10 <= 0.001 || INTEGER_MEASURE){ + str = ""+(int)u; } - - // Draw data points and lines - s.strokeWeight(4); + s.text(str,x-10,y+TS*0.35); + } + s.strokeWeight(4); + for(int i = 0; i < data.length; i++){ + float x = ((float)i)/data.length*graph_dim[2]+graph_dim[0]; + float y = (1-(data[i]-min)/(max-min))*graph_dim[3]+graph_dim[1]; s.fill(col); - s.stroke(col); - - boolean showPoints = dataLength < 50; - for (int i = 0; i < dataLength - 1; i++) { - if (showPoints) { - s.noStroke(); - s.ellipse(xCoords[i], yCoords[i], 15, 15); - s.stroke(col); - } - s.line(xCoords[i], yCoords[i], xCoords[i + 1], yCoords[i + 1]); + if(data.length < 50){ + s.noStroke(); + s.ellipse(x,y,E_R,E_R); } - - // Draw final point and values - if (dataLength > 0) { - int last = dataLength - 1; - float textSize = (data[last] > 10 && !integerMeasure) ? 39 : 50; - s.textSize(textSize); - s.textAlign(LEFT); - - String finalValue = integerMeasure ? String.valueOf((int)data[last]) : nf(data[last], 0, 2); - s.text(finalValue, xCoords[last] + textSize * 0.5, yCoords[last] + textSize * 0.35); - - if (last > 0) { - float delta = data[last] - data[last - 1]; - String deltaStr = (delta >= 0 ? "+" : "") + (integerMeasure ? String.valueOf((int)delta) : nf(delta, 0, 2)); - s.textSize(textSize * 0.6); - s.text(deltaStr, xCoords[last] + textSize * 0.5, yCoords[last] + textSize * 1.1); + if(i == data.length-1){ + float TS3 = (data[i] > 10 && !INTEGER_MEASURE) ? 39 : 50; + s.textSize(TS3); + s.textAlign(LEFT); + String str = INTEGER_MEASURE ? (int)data[i]+"" : nf(data[i],0,2); + s.text(str,x+TS3*0.5,y+TS3*0.35); + if(i >= 1){ + float delta = data[i]-data[i-1]; + String delta_str = INTEGER_MEASURE ? (int)delta+"" : nf(delta,0,2); + if(data[i] >= data[i-1]){ + delta_str = "+"+delta_str; } + s.textSize(TS3*0.6); + s.text(delta_str,x+TS3*0.5,y+TS3*1.1); + } + }else{ + float x2 = ((float)(i+1))/data.length*graph_dim[2]+graph_dim[0]; + float y2 = (1-(data[i+1]-min)/(max-min))*graph_dim[3]+graph_dim[1]; + s.stroke(col); + s.line(x,y,x2,y2); } - - // Draw title - s.textAlign(CENTER); - s.fill(0); - s.textSize(60); - s.text(title, graphX + graphWidth * 0.5, graphY - 50); - - s.endDraw(); + } + s.textAlign(CENTER); + s.fill(0); + s.textSize(60); + s.text(title,graph_dim[0]+graph_dim[2]*0.5,graph_dim[1]-50); + s.endDraw(); } - float daylight() { float days = ticksToDays(ticks); if (Float.isInfinite(days) || Float.isNaN(days)) { @@ -611,13 +581,12 @@ void drawVisuals(){ player.drawPlayer(); drawWindows(); checkHighlight(); - drawSpiders(room); //Most demanding + drawSpiders(room); drawSwatters(room); drawButtons(); g.popMatrix(); - g.endDraw(); //Graph update + g.endDraw(); } - void drawWindows(){ for(int w = 0; w < windows.size(); w++){ windows.get(w).drawWindow(room); From 1aa1cdd2b31a3e5281dc3449ac1e7eb42b65cacc Mon Sep 17 00:00:00 2001 From: Rohan Date: Fri, 22 Nov 2024 11:25:08 +1100 Subject: [PATCH 21/32] Spider class optimisations gains about 8% fps!!! --- Spider.pde | 93 ++++++++++++++++++++++++------------------------------ 1 file changed, 42 insertions(+), 51 deletions(-) diff --git a/Spider.pde b/Spider.pde index 37f77d2..a6cd869 100644 --- a/Spider.pde +++ b/Spider.pde @@ -176,62 +176,53 @@ class Spider{ } return result; } - float[] getWeightedCenter(int step, Room room, float darkest_sensed_shadow){ - float[] sum_coor = {0,0}; - float sum_weight = 0; - for(int L = 0; L < LEG_COUNT; L++){ - int genome_index = L*GENES_PER_LEG+2*step; - if(darkest_sensed_shadow < genome[L*GENES_PER_LEG+12]){ // it's below the threshold, so do the dark pattern - genome_index += 6; - } - float weight = genome[genome_index]; - sum_weight += weight; - for(int d = 0; d < 2; d++){ - sum_coor[d] += leg_coor[L][d]*weight; - } + float[] getWeightedCenter(int step, Room room, float darkest_sensed_shadow) { + float sumWeight = 0; + float sumX = 0, sumY = 0; + for (int L = 0; L < LEG_COUNT; L++) { + int genomeIndex = L * GENES_PER_LEG + 2 * step; + if (darkest_sensed_shadow < genome[L * GENES_PER_LEG + 12]) { + genomeIndex += 6; + } + float weight = genome[genomeIndex]; + sumWeight += weight; + sumX += leg_coor[L][0] * weight; + sumY += leg_coor[L][1] * weight; } - float rx = sum_coor[0]/sum_weight; - float ry = sum_coor[1]/sum_weight; - float[] result = {rx, ry}; - return result; - } - void placeLegs(float[] center, int step, Room room, float darkest_sensed_shadow, ArrayList spiders){ - float force_to_right_angles = 0.001; // how strongly should the spider's legs be dragged back into right angles? - float first_angle = 0; - for(int L = 0; L < LEG_COUNT; L++){ - int genome_index = L*GENES_PER_LEG+2*step+1; - if(darkest_sensed_shadow < genome[L*GENES_PER_LEG+12]){ // it's below the threshold, so do the dark pattern - genome_index += 6; - } - float distance = genome[genome_index]*MAX_LEG_SPAN; - float delta_x = leg_coor[L][0]-center[0]; - float delta_y = leg_coor[L][1]-center[1]; - float angle = atan2(delta_y,delta_x); - if(L == 0){ - first_angle = angle; - }else{ - float desired_angle = first_angle+PI/2*L; - float move = (desired_angle-angle); - while(move > PI){ - move -= 2*PI; + return new float[]{sumX / sumWeight, sumY / sumWeight}; +} + + void placeLegs(float[] center, int step, Room room, float darkest_sensed_shadow, ArrayList spiders) { + float forceToRightAngles = 0.001f; + float firstAngle = 0; + for (int L = 0; L < LEG_COUNT; L++) { + int genomeIndex = L * GENES_PER_LEG + 2 * step + 1; + if (darkest_sensed_shadow < genome[L * GENES_PER_LEG + 12]) { + genomeIndex += 6; } - while(move < -PI){ - move += 2*PI; + float distance = genome[genomeIndex] * MAX_LEG_SPAN; + float deltaX = leg_coor[L][0] - center[0]; + float deltaY = leg_coor[L][1] - center[1]; + float angle = atan2(deltaY, deltaX); + if (L == 0) { + firstAngle = angle; + } else { + float desiredAngle = firstAngle + PI / 2 * L; + float move = desiredAngle - angle; + angle += forceToRightAngles * ((move + PI) % (2 * PI) - PI); } - angle += force_to_right_angles*move; - } - leg_coor[L][0] = center[0]+cos(angle)*distance; - leg_coor[L][1] = center[1]+sin(angle)*distance; + leg_coor[L][0] = center[0] + cos(angle) * distance; + leg_coor[L][1] = center[1] + sin(angle) * distance; } - coor = getWeightedCenter(step,room,darkest_sensed_shadow); - for(int d = 0; d < 2; d++){ - if(coor[d] < 0){ - shiftAllBy(d,room.getMaxDim(d)); - }else if(coor[d] >= room.getMaxDim(d)){ - shiftAllBy(d,-room.getMaxDim(d)); - } + coor = getWeightedCenter(step, room, darkest_sensed_shadow); + for (int d = 0; d < 2; d++) { + if (coor[d] < 0) { + shiftAllBy(d, room.getMaxDim(d)); + } else if (coor[d] >= room.getMaxDim(d)) { + shiftAllBy(d, -room.getMaxDim(d)); + } } - } +} void shiftAllBy(int dim, float amt){ coor[dim] += amt; for(int L = 0; L < LEG_COUNT; L++){ From fc86464cbb9f6433f5a9a30994d3e283c3ff68d4 Mon Sep 17 00:00:00 2001 From: Rohan Date: Sat, 18 Jan 2025 18:44:49 +1100 Subject: [PATCH 22/32] Revert back to commit 3 But with fps counter included. This is because of speed bugs caused by a commit at some point and fps on more powerful hardware actually decreased! --- Spider.pde | 96 ++++++++++++++++++--------------- SpiderEvoSim.pde | 135 ++++++++++++++--------------------------------- 2 files changed, 92 insertions(+), 139 deletions(-) diff --git a/Spider.pde b/Spider.pde index a6cd869..743436d 100644 --- a/Spider.pde +++ b/Spider.pde @@ -59,8 +59,7 @@ class Spider{ g.popMatrix(); return value; } - - color getColor() { +color getColor() { int c = swattersSeen.size(); if (c == 0) { return color(0, 0, 0, 255); // Black @@ -176,53 +175,62 @@ class Spider{ } return result; } - float[] getWeightedCenter(int step, Room room, float darkest_sensed_shadow) { - float sumWeight = 0; - float sumX = 0, sumY = 0; - for (int L = 0; L < LEG_COUNT; L++) { - int genomeIndex = L * GENES_PER_LEG + 2 * step; - if (darkest_sensed_shadow < genome[L * GENES_PER_LEG + 12]) { - genomeIndex += 6; - } - float weight = genome[genomeIndex]; - sumWeight += weight; - sumX += leg_coor[L][0] * weight; - sumY += leg_coor[L][1] * weight; + float[] getWeightedCenter(int step, Room room, float darkest_sensed_shadow){ + float[] sum_coor = {0,0}; + float sum_weight = 0; + for(int L = 0; L < LEG_COUNT; L++){ + int genome_index = L*GENES_PER_LEG+2*step; + if(darkest_sensed_shadow < genome[L*GENES_PER_LEG+12]){ // it's below the threshold, so do the dark pattern + genome_index += 6; + } + float weight = genome[genome_index]; + sum_weight += weight; + for(int d = 0; d < 2; d++){ + sum_coor[d] += leg_coor[L][d]*weight; + } } - return new float[]{sumX / sumWeight, sumY / sumWeight}; -} - - void placeLegs(float[] center, int step, Room room, float darkest_sensed_shadow, ArrayList spiders) { - float forceToRightAngles = 0.001f; - float firstAngle = 0; - for (int L = 0; L < LEG_COUNT; L++) { - int genomeIndex = L * GENES_PER_LEG + 2 * step + 1; - if (darkest_sensed_shadow < genome[L * GENES_PER_LEG + 12]) { - genomeIndex += 6; + float rx = sum_coor[0]/sum_weight; + float ry = sum_coor[1]/sum_weight; + float[] result = {rx, ry}; + return result; + } + void placeLegs(float[] center, int step, Room room, float darkest_sensed_shadow, ArrayList spiders){ + float force_to_right_angles = 0.001; // how strongly should the spider's legs be dragged back into right angles? + float first_angle = 0; + for(int L = 0; L < LEG_COUNT; L++){ + int genome_index = L*GENES_PER_LEG+2*step+1; + if(darkest_sensed_shadow < genome[L*GENES_PER_LEG+12]){ // it's below the threshold, so do the dark pattern + genome_index += 6; + } + float distance = genome[genome_index]*MAX_LEG_SPAN; + float delta_x = leg_coor[L][0]-center[0]; + float delta_y = leg_coor[L][1]-center[1]; + float angle = atan2(delta_y,delta_x); + if(L == 0){ + first_angle = angle; + }else{ + float desired_angle = first_angle+PI/2*L; + float move = (desired_angle-angle); + while(move > PI){ + move -= 2*PI; } - float distance = genome[genomeIndex] * MAX_LEG_SPAN; - float deltaX = leg_coor[L][0] - center[0]; - float deltaY = leg_coor[L][1] - center[1]; - float angle = atan2(deltaY, deltaX); - if (L == 0) { - firstAngle = angle; - } else { - float desiredAngle = firstAngle + PI / 2 * L; - float move = desiredAngle - angle; - angle += forceToRightAngles * ((move + PI) % (2 * PI) - PI); + while(move < -PI){ + move += 2*PI; } - leg_coor[L][0] = center[0] + cos(angle) * distance; - leg_coor[L][1] = center[1] + sin(angle) * distance; + angle += force_to_right_angles*move; + } + leg_coor[L][0] = center[0]+cos(angle)*distance; + leg_coor[L][1] = center[1]+sin(angle)*distance; } - coor = getWeightedCenter(step, room, darkest_sensed_shadow); - for (int d = 0; d < 2; d++) { - if (coor[d] < 0) { - shiftAllBy(d, room.getMaxDim(d)); - } else if (coor[d] >= room.getMaxDim(d)) { - shiftAllBy(d, -room.getMaxDim(d)); - } + coor = getWeightedCenter(step,room,darkest_sensed_shadow); + for(int d = 0; d < 2; d++){ + if(coor[d] < 0){ + shiftAllBy(d,room.getMaxDim(d)); + }else if(coor[d] >= room.getMaxDim(d)){ + shiftAllBy(d,-room.getMaxDim(d)); + } } -} + } void shiftAllBy(int dim, float amt){ coor[dim] += amt; for(int L = 0; L < LEG_COUNT; L++){ diff --git a/SpiderEvoSim.pde b/SpiderEvoSim.pde index 16a83f5..a91ff0f 100644 --- a/SpiderEvoSim.pde +++ b/SpiderEvoSim.pde @@ -1,7 +1,6 @@ import com.jogamp.newt.opengl.GLWindow; import processing.sound.*; -import java.util.Arrays; - +//555Lines = Working branch + FPS counter int CENTER_X = 960; // try setting this to 960 or 961 if there is horizontal camera-pan-drifting String[] soundFileNames = {"slap0.wav","slap1.wav","slap2.wav","splat0.wav","splat1.wav","splat2.wav","boop1.wav","boop2.wav","jump.wav","news.wav"}; SoundFile[] sfx; @@ -122,89 +121,52 @@ void createSwatters(Room room, int START_SPIDER_COUNT){ } } -// Global variable to track loading status -boolean resourcesLoaded = false; - -// Must be declared at the top level for Processing's size() to work -void settings() { - size(1920,1080,P3D); -} +void setup(){ + windowImages = new PImage[WINDOW_COUNT]; + for(int w = 0; w < WINDOW_COUNT; w++){ + windowImages[w] = loadImage("windows/w"+nf(w+1,4,0)+".png"); + } + sfx = new SoundFile[soundFileNames.length]; + for(int s = 0; s < soundFileNames.length; s++){ + sfx[s] = new SoundFile(this, "audio/"+soundFileNames[s]); + } - void setup() { - // Initialize core components first float[][] walls = {{0,0},{975,0},{975,670},{1100,670},{1100,0},{2100,0},{2100,1000},{1100,1000},{1100,780},{975,780},{975,1000},{0,1000}}; float[] zs = {0,500}; float[] player_coor = {700,700,0,0}; room = new Room(walls,zs); player = new Player(player_coor); - keyHandler = new KeyHandler(); - - // Setup window - r = (GLWindow)surface.getNative(); - r.confinePointer(true); - r.setPointerVisible(false); - g = createGraphics(1920,1080,P3D); - - // Start resource loading thread - thread("loadResources"); -} - -void loadResources() { - // Load windows in batches of 5 - windowImages = new PImage[WINDOW_COUNT]; - for(int w = 0; w < WINDOW_COUNT; w += 5) { - int endIndex = min(w + 5, WINDOW_COUNT); - for(int i = w; i < endIndex; i++) { - windowImages[i] = loadImage("windows/w"+nf(i+1,4,0)+".png"); - } - } - - // Load sound files in batches - sfx = new SoundFile[soundFileNames.length]; - for(int s = 0; s < soundFileNames.length; s += 3) { - int endIndex = min(s + 3, soundFileNames.length); - for(int i = s; i < endIndex; i++) { - sfx[i] = new SoundFile(this, "audio/"+soundFileNames[i]); - } - } - - // Initialize game objects spiders = createSpiders(room); createSwatters(room, 0); + keyHandler = new KeyHandler(); + size(1920,1080,P3D); - // Initialize buttons buttons.add(new Button(0,1300,300,"Increase")); buttons.add(new Button(1,1450,300,"Decrease")); + buttons.add(new Button(2,1800,300,"Enlarge,Swatters")); buttons.add(new Button(3,1950,300,"Shrink,Swatters")); + buttons.add(new Button(4,1800,700,"Speed up,Swatters")); buttons.add(new Button(5,1950,700,"Slow down,Swatters")); + buttons.add(new Button(6,1450,700,"Invent,Swatters")); - // Initialize stat images for(int d = 0; d < STAT_COUNT; d++){ statImages[d] = createGraphics(800,600); } - // Mark loading as complete - resourcesLoaded = true; + r = (GLWindow)surface.getNative(); + r.confinePointer(true); + r.setPointerVisible(false); + g = createGraphics(1920,1080,P3D); } - //FPS counter ArrayList frameTimestamps = new ArrayList<>(); // To store frame timestamps float averageFps = 0; // Average FPS over the last 4 seconds void draw() { - if (!resourcesLoaded) { - // Show loading screen - background(0); - fill(255); - textAlign(CENTER, CENTER); - textSize(32); - text("Loading...", width/2, height/2); - return; - } - // Capture current time in milliseconds + // Capture current time in milliseconds long currentTime = millis(); frameTimestamps.add(currentTime); @@ -225,8 +187,7 @@ void draw() { drawVisuals(); image(g, 0, 0); drawUI(); - frames++; - + frames++; if (camera[1] < -1) { camera[1] = -1; } @@ -234,7 +195,6 @@ void draw() { camera[1] = 1; } } - void checkHighlight(){ if(!lock_highlight){ highlight_spider = checkHighlightHelper(); @@ -290,6 +250,7 @@ String dateNumToMonthString(int d) { return monthNames[0] + " 1"; } + int totalDays = 0; for (int m = 0; m < 12; m++) { if (d < monthDays[m]) { return monthNames[m] + " " + (d + 1); @@ -385,50 +346,42 @@ float getBiodiversity(){ } return total_diversity/GENOME_LENGTH*100; } - void collectData() { if (ticks % CHANGE_WINDOWS_EVERY == 0) { - Window[] windowArray = windows.toArray(new Window[0]); - for (Window window : windowArray) { - window.updateShow(); + for (int w = 0; w < windows.size(); w++) { + windows.get(w).updateShow(); } } - if (ticks % (long)TICKS_PER_DAY == 0) { - // Pre-allocate arrays and reuse them + // Keep using Float[] to match Spider.writeData() method Float[] datum = new Float[STAT_COUNT]; - Spider[] spiderArray = spiders.toArray(new Spider[0]); - float spiderCount = spiderArray.length; + for (int d = 0; d < STAT_COUNT; d++) { + datum[d] = 0.0f; // Use 0.0f for Float + } - // Initialize datum array more efficiently - Arrays.fill(datum, 0.0f); + // Get spider count before division + float spiderCount = spiders.size(); - // Process all spiders in a single pass - for (Spider spider : spiderArray) { - spider.writeData(datum); + for (int s = 0; s < spiders.size(); s++) { + spiders.get(s).writeData(datum); } - // Batch process divisions - if (spiderCount > 0) { - float invCount = 1.0f / spiderCount; - for (int d = 0; d < STAT_COUNT; d++) { - datum[d] *= invCount; - } + // Prevent division by zero and ensure precise division + for (int d = 0; d < STAT_COUNT; d++) { + datum[d] = spiderCount > 0 ? datum[d] / spiderCount : 0.0f; } datum[1] = (float)dailyDeaths; datum[2] = (float)(swattersSeenTotal - dailyDeaths); datum[4] = getBiodiversity(); - // Store data and reset counters dailyDeaths = 0; swattersSeenTotal = 0; stats.add(datum); statNotes.add(""); - // Cache graph dimensions and titles - final float[] GRAPH_DIM = {100, 120, 575, 400}; - final String[] TITLES = { + float[] graph_dim = {100, 120, 575, 400}; + String[] titles = { "Average Age (days)", "Daily Deaths", "Daily Swatter Escapes", @@ -437,26 +390,18 @@ void collectData() { "Average Swatters Seen" }; - // Pre-allocate graph data array - float[] graphData = new float[stats.size()]; - // Cache the color value - color graphColor = color(128, 0, 0); - - // Draw all graphs with minimal object creation for (int d = 0; d < STAT_COUNT; d++) { + float[] graphData = new float[stats.size()]; for (int i = 0; i < stats.size(); i++) { graphData[i] = stats.get(i)[d]; } - drawGraphOn(statImages[d], graphData, TITLES[d], GRAPH_DIM, graphColor, d); + drawGraphOn(statImages[d], graphData, titles[d], graph_dim, color(128,0,0), d); } - // Handle sound - float amp = 1.0f - min(0.8f, (playback_speed-1)/200.0f); sfx[9].play(); - sfx[9].amp(amp); + sfx[9].amp(1.0 - min(0.8, (playback_speed-1)/200.0)); } } - float getUnit(float a, float b){ float diff = b-a; float[] units = {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}; From 650b9f987777c06ce9b4d3c74615255772d22ba0 Mon Sep 17 00:00:00 2001 From: Rohan Date: Sat, 18 Jan 2025 19:07:24 +1100 Subject: [PATCH 23/32] Tiny changes Unused variable removed + 582 lines now --- SpiderEvoSim.pde | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/SpiderEvoSim.pde b/SpiderEvoSim.pde index a91ff0f..868a6ac 100644 --- a/SpiderEvoSim.pde +++ b/SpiderEvoSim.pde @@ -1,6 +1,6 @@ import com.jogamp.newt.opengl.GLWindow; import processing.sound.*; -//555Lines = Working branch + FPS counter +//582 Lines = Working branch + FPS counter int CENTER_X = 960; // try setting this to 960 or 961 if there is horizontal camera-pan-drifting String[] soundFileNames = {"slap0.wav","slap1.wav","slap2.wav","splat0.wav","splat1.wav","splat2.wav","boop1.wav","boop2.wav","jump.wav","news.wav"}; SoundFile[] sfx; @@ -250,7 +250,6 @@ String dateNumToMonthString(int d) { return monthNames[0] + " 1"; } - int totalDays = 0; for (int m = 0; m < 12; m++) { if (d < monthDays[m]) { return monthNames[m] + " " + (d + 1); From b157a534a0d26865710fdb78b62b342aa0dbdb7c Mon Sep 17 00:00:00 2001 From: Rohan Date: Sat, 18 Jan 2025 21:10:23 +1100 Subject: [PATCH 24/32] Colour caching to improve performace fps improved from max 52 to max 56! (standing still) we can optimize the color calculations by caching them since they only need to be recalculated when the number of swatters seen changes. --- Spider.pde | 96 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 31 deletions(-) diff --git a/Spider.pde b/Spider.pde index 743436d..ec5fee1 100644 --- a/Spider.pde +++ b/Spider.pde @@ -14,6 +14,10 @@ class Spider{ Spider parent; ArrayList swattersSeen = new ArrayList(0); + // Add these fields at the top of the Spider class + private color cachedColor = -1; // -1 means not calculated yet + private int lastSwatterCount = -1; // Track when we need to recalculate + public Spider(int i, Room room){ parent = null; genome = new float[GENOME_LENGTH]; @@ -59,40 +63,55 @@ class Spider{ g.popMatrix(); return value; } +// Replace the existing getColor() method with this optimized version color getColor() { - int c = swattersSeen.size(); - if (c == 0) { - return color(0, 0, 0, 255); // Black + int currentSwatterCount = swattersSeen.size(); + + // Return cached color if nothing has changed + if (cachedColor != -1 && currentSwatterCount == lastSwatterCount) { + return cachedColor; + } + + // Calculate new color + color newColor; + if (currentSwatterCount == 0) { + newColor = color(0, 0, 0, 255); // Black } else { - if (c <= 5) { - float fac = c / 5.0; - return color(0, fac * 140, 255 - fac * 255, 255); // Blue transition - } else if (c <= 20) { - float fac = (c - 5) / 15.0; - return color(fac * 255, 140 + fac * 155, 20, 255); // Green with a higher base for better contrast - } else if (c <= 40) { - float fac = (c - 20) / 20.0; - return color(255, 255 - fac * 115, 50, 255); // Yellow with a richer tone - } else if (c <= 100) { - float fac = (c - 40) / 60.0; - return color(255, 140 - fac * 140, 0, 255); // Pink transition - } else if (c <= 200) { - float fac = (c - 100) / 100.0; - return color(255, 0, fac * 255, 255); // Purple transition - } else if (c <= 500) { - float fac = (c - 200) / 300.0; - return color(255 - fac * 141, 20, 255, 255); // Deeper purple to cyan - } else if (c <= 1000) { - float fac = (c - 500) / 500.0; - return color(114 - fac * 114, fac * 255, 255, 255); // Cyan transition - } else if (c <= 4000) { - float fac = (c - 1000) / 3000.0; - return color(225 + fac * 30, 210 - fac * 20, 30 + fac * 50); // Gold with richer, deeper hues + if (currentSwatterCount <= 5) { + float fac = currentSwatterCount / 5.0; + newColor = color(0, fac * 140, 255 - fac * 255, 255); // Blue transition + } else if (currentSwatterCount <= 20) { + float fac = (currentSwatterCount - 5) / 15.0; + newColor = color(fac * 255, 140 + fac * 155, 20, 255); // Green with a higher base for better contrast + } else if (currentSwatterCount <= 40) { + float fac = (currentSwatterCount - 20) / 20.0; + newColor = color(255, 255 - fac * 115, 50, 255); // Yellow with a richer tone + } else if (currentSwatterCount <= 100) { + float fac = (currentSwatterCount - 40) / 60.0; + newColor = color(255, 140 - fac * 140, 0, 255); // Red at 100 into Pink transition at 150 + } else if (currentSwatterCount <= 200) { + float fac = (currentSwatterCount - 100) / 100.0; + newColor = color(255, 0, fac * 255, 255); // Purple transition + } else if (currentSwatterCount <= 500) { + float fac = (currentSwatterCount - 200) / 300.0; + newColor = color(255 - fac * 141, 20, 255, 255); // Deeper purple to cyan + } else if (currentSwatterCount <= 1000) { + float fac = (currentSwatterCount - 500) / 500.0; + newColor = color(114 - fac * 114, fac * 255, 255, 255); // Cyan transition + } else if (currentSwatterCount <= 4000) { + float fac = (currentSwatterCount - 1000) / 3000.0; + newColor = color(225 + fac * 30, 210 - fac * 20, 30 + fac * 50); // Gold with richer, deeper hues } else { - float fac = min(1, (c - 4000) / 6000.0); - return color(255, 202 + fac * 53, fac * 255); // Transition to white + float fac = min(1, (currentSwatterCount - 4000) / 6000.0); + newColor = color(255, 202 + fac * 53, fac * 255); // Transition to white } } + + // Cache the results + cachedColor = newColor; + lastSwatterCount = currentSwatterCount; + + return newColor; } color transitionColor(color a, color b, float prog){ @@ -102,11 +121,26 @@ color getColor() { return color(newR, newG, newB); } void drawSpider(Room room){ + float[] realCoor = room.wallCoor_to_realCoor(coor); + + // Quick culling check before any drawing + g.pushMatrix(); + aTranslate(realCoor); + float screenX = g.screenX(0, 0, 0); + float screenY = g.screenY(0, 0, 0); + g.popMatrix(); + + // Check if spider is off screen (with margin for legs) + float margin = MAX_LEG_SPAN * 1.5; // Margin to account for legs + if (screenX < -margin || screenX > width + margin || + screenY < -margin || screenY > height + margin) { + return; // Skip drawing if off screen + } + color c = getColor(); if(this == highlight_spider){ c = color(0,255,0); } - float[] realCoor = room.wallCoor_to_realCoor(coor); g.pushMatrix(); aTranslate(realCoor); g.fill(c); @@ -160,7 +194,7 @@ color getColor() { g.noStroke(); } } - } +} float[] multi(float[] a, float m){ float[] result = new float[a.length]; for(int i = 0; i < a.length; i++){ From 036c29b63527c83fe1bfe8a2e9b7c2a637ed7009 Mon Sep 17 00:00:00 2001 From: Rohan Date: Sat, 18 Jan 2025 22:29:51 +1100 Subject: [PATCH 25/32] Frame rate limit for consistent physics 60fps limit --- SpiderEvoSim.pde | 1 + 1 file changed, 1 insertion(+) diff --git a/SpiderEvoSim.pde b/SpiderEvoSim.pde index 868a6ac..d7a6840 100644 --- a/SpiderEvoSim.pde +++ b/SpiderEvoSim.pde @@ -122,6 +122,7 @@ void createSwatters(Room room, int START_SPIDER_COUNT){ } void setup(){ + frameRate(60); //Frame rate limit for physics windowImages = new PImage[WINDOW_COUNT]; for(int w = 0; w < WINDOW_COUNT; w++){ windowImages[w] = loadImage("windows/w"+nf(w+1,4,0)+".png"); From 2698d2fa8758ef98f7b8f380c62a6881bd458ba4 Mon Sep 17 00:00:00 2001 From: Rohan Date: Sat, 18 Jan 2025 23:17:26 +1100 Subject: [PATCH 26/32] timing checks 6ms for ui, everything else is 0 until speed up sim, where physics at 256x speed is 60ms --- Player.pde | 2 +- SpiderEvoSim.pde | 79 +++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 66 insertions(+), 15 deletions(-) diff --git a/Player.pde b/Player.pde index 91341a7..0db12bf 100644 --- a/Player.pde +++ b/Player.pde @@ -3,7 +3,7 @@ class Player{ float[] lag_coor; float[] velo; float FRICTION = 0.85; - float ACCEL = 2; + float ACCEL = 1.8; float R_ACCEL = 0.05; float THICKNESS = 11; float EPS = 0.1; diff --git a/SpiderEvoSim.pde b/SpiderEvoSim.pde index d7a6840..f9afd90 100644 --- a/SpiderEvoSim.pde +++ b/SpiderEvoSim.pde @@ -166,6 +166,16 @@ void setup(){ ArrayList frameTimestamps = new ArrayList<>(); // To store frame timestamps float averageFps = 0; // Average FPS over the last 4 seconds +// Add to global variables +long physicsTiming = 0; +long renderTiming = 0; +long spiderPhysicsTiming = 0; +long swatterPhysicsTiming = 0; +String physicsStats = ""; +String renderStats = ""; +String spiderStats = ""; +String swatterStats = ""; + void draw() { // Capture current time in milliseconds long currentTime = millis(); @@ -181,13 +191,38 @@ void draw() { float elapsedSeconds = (frameTimestamps.get(frameTimestamps.size() - 1) - frameTimestamps.get(0)) / 1000.0f; averageFps = (frameTimestamps.size() - 1) / elapsedSeconds; } + long startFrame = System.nanoTime(); // Original draw logic doMouse(); + long startPhysics = System.nanoTime(); doPhysics(); + physicsTiming = System.nanoTime() - startPhysics; + + long startRender = System.nanoTime(); drawVisuals(); + renderTiming = System.nanoTime() - startRender; + image(g, 0, 0); drawUI(); + + // Update stats every 30 frames + if (frames % 30 == 0) { + physicsStats = "Physics: " + (physicsTiming / 1_000_000) + "ms"; + renderStats = "Render: " + (renderTiming / 1_000_000) + "ms"; + spiderStats = "Spider Physics: " + (spiderPhysicsTiming / 1_000_000) + "ms"; + swatterStats = "Swatter Physics: " + (swatterPhysicsTiming / 1_000_000) + "ms"; + } + + // Display stats + fill(255); + textAlign(LEFT); + textSize(24); + text(physicsStats, 10, 140); + text(renderStats, 10, 170); + text(spiderStats, 10, 200); + text(swatterStats, 10, 230); + frames++; if (camera[1] < -1) { camera[1] = -1; @@ -217,29 +252,40 @@ Spider checkHighlightHelper(){ } void drawUI() { + // Draw crosshair noStroke(); fill(0); - float M = 1; + float M = 1; // Move constants outside of method if possible float W = 20; - rect(width / 2 - M, height / 2 - W, M * 2, W * 2); - rect(width / 2 - W, height / 2 - M, W * 2, M * 2); + rect(width/2 - M, height/2 - W, M * 2, W * 2); + rect(width/2 - W, height/2 - M, W * 2, M * 2); + // Draw genome panel if spider is highlighted if (highlight_spider != null) { PGraphics genomePanel = highlight_spider.drawGenome(); image(genomePanel, width - genomePanel.width - 30, height - genomePanel.height - 30); } + // Cache text settings to reduce state changes + fill(255); + + // Draw date textAlign(LEFT); textSize(50); - fill(255); // Set text color to white for visibility text(ticksToDate(ticks), 20, 65); - // Display FPS + // Draw FPS with less frequent updates + if (frames % 5 == 0) { // Update FPS display every 10 frames + cachedFpsString = "FPS: " + nf(averageFps, 0, 2); + } textAlign(RIGHT); textSize(25); - text("FPS: " + nf(averageFps, 0, 2), width - 20, 30); // Display the FPS in the top-right corner + text(cachedFpsString, width - 20, 30); } +// Cache FPS string +String cachedFpsString = "FPS: 0.00"; + String dateNumToMonthString(int d) { String[] monthNames = {"January","February","March","April","May","June", "July","August","September","October","November","December"}; @@ -545,15 +591,20 @@ void drawButtons(){ void aTranslate(float[] coor){ g.translate(coor[0],coor[1],coor[2]); } -void iterateSpiders(Room room){ - for(int s = 0; s < spiders.size(); s++){ - spiders.get(s).iterate(room, swatters, spiders); - } +void iterateSpiders(Room room) { + long start = System.nanoTime(); + for(int s = 0; s < spiders.size(); s++) { + spiders.get(s).iterate(room, swatters, spiders); + } + spiderPhysicsTiming = System.nanoTime() - start; } -void iterateSwatters(Room room){ - for(int s = 0; s < swatters.size(); s++){ - swatters.get(s).iterate(room, spiders, swatters); - } + +void iterateSwatters(Room room) { + long start = System.nanoTime(); + for(int s = 0; s < swatters.size(); s++) { + swatters.get(s).iterate(room, spiders, swatters); + } + swatterPhysicsTiming = System.nanoTime() - start; } void iterateButtons(Player player){ for(int b = 0; b < buttons.size(); b++){ From 5e0da3b084edfe075782f63c981e635afe0edaae Mon Sep 17 00:00:00 2001 From: Rohan Date: Sat, 18 Jan 2025 23:50:08 +1100 Subject: [PATCH 27/32] Physics optimisations fps 55 tick. 1024x speed 16fps +1 --- Spider.pde | 64 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/Spider.pde b/Spider.pde index ec5fee1..0a062cf 100644 --- a/Spider.pde +++ b/Spider.pde @@ -277,31 +277,59 @@ color getColor() { move(room, cycle, swatters, spiders); } } - float getDarkestShadow(){ - float darkest_sensed_shadow = 1.0; - for(int s = 0; s < swatters.size(); s++){ - Swatter sw = swatters.get(s); - float x = sw.coor[0]; - float y1 = sw.coor[1]-R; - float y2 = sw.coor[1]+R; - for(int L = 0; L < LEG_COUNT; L++){ - if(abs(leg_coor[L][0]-sw.coor[0]) < R && abs(leg_coor[L][1]-sw.coor[1]) < R){ // it's under the shadow! - if(!swattersSeen.contains(sw.visIndex)){ - swattersSeen.add(sw.visIndex); - swattersSeenTotal++; - } - darkest_sensed_shadow = min(darkest_sensed_shadow,max(sw.percentage,0)); + float getDarkestShadow() { + float darkest_sensed_shadow = 1.0f; + float R2 = R * R; // Square of radius for faster distance checks + + for(Swatter sw : swatters) { + float swX = sw.coor[0]; + float swY = sw.coor[1]; + + // Quick AABB check before detailed collision + float minX = swX - R; + float maxX = swX + R; + float minY = swY - R; + float maxY = swY + R; + + boolean legFound = false; + for(int L = 0; L < LEG_COUNT && !legFound; L++) { + float legX = leg_coor[L][0]; + float legY = leg_coor[L][1]; + + if(legX >= minX && legX <= maxX && + legY >= minY && legY <= maxY) { + + float dx = legX - swX; + float dy = legY - swY; + float distSquared = dx * dx + dy * dy; + + if(distSquared < R2) { + if(!swattersSeen.contains(sw.visIndex)) { + swattersSeen.add(sw.visIndex); + swattersSeenTotal++; + } + darkest_sensed_shadow = Math.min(darkest_sensed_shadow, + Math.max(sw.percentage, 0)); + legFound = true; // Exit early if we found a collision + } + } } - } } return darkest_sensed_shadow; - } - void move(Room room, int cycle, ArrayList swatters, ArrayList spiders){ +} + void move(Room room, int cycle, ArrayList swatters, ArrayList spiders) { + // Cache frequently used values float darkest_sensed_shadow = getDarkestShadow(); int step = cycle/SPIDER_ITER_BUCKETS; + + // Pre-calculate indices and thresholds once + boolean isDarkPattern = darkest_sensed_shadow < genome[12]; + int baseIndex = isDarkPattern ? 6 : 0; + + // Get weighted center in one pass float[] weightedCenter = getWeightedCenter(step, room, darkest_sensed_shadow); placeLegs(weightedCenter, step, room, darkest_sensed_shadow, spiders); - } +} int whereInCycle(int offset){ return (ticks+offset-ITER_TIME+SIBSC)%SIBSC; } From a285782550251d0007f6818220d1822d3a73b218 Mon Sep 17 00:00:00 2001 From: Rohan Date: Sun, 19 Jan 2025 00:45:38 +1100 Subject: [PATCH 28/32] Some more claude optimisation It seems to slightly improve fps 55->56 and 16->17 --- Spider.pde | 135 ++++++++++++++++++++++++++++------------------- SpiderEvoSim.pde | 43 +++++---------- 2 files changed, 93 insertions(+), 85 deletions(-) diff --git a/Spider.pde b/Spider.pde index 0a062cf..a417477 100644 --- a/Spider.pde +++ b/Spider.pde @@ -14,6 +14,10 @@ class Spider{ Spider parent; ArrayList swattersSeen = new ArrayList(0); + // Add these as class fields + private final float HALF_PI = PI / 2; + private final float TWO_PI = PI * 2; + private final float MAX_LEG_SPAN_SQ = MAX_LEG_SPAN * MAX_LEG_SPAN; // Add these fields at the top of the Spider class private color cachedColor = -1; // -1 means not calculated yet private int lastSwatterCount = -1; // Track when we need to recalculate @@ -31,7 +35,7 @@ class Spider{ leg_coor = new float[LEG_COUNT][2]; float ang = random(0,1); for(int L = 0; L < LEG_COUNT; L++){ - float angL = (L+ang)*PI/2; + float angL = (L+ang)*HALF PI; int genome_index = L*GENES_PER_LEG+1; float distance = genome[genome_index]; leg_coor[L][0] = coor[0]+cos(angL)*distance; @@ -209,62 +213,75 @@ color getColor() { } return result; } - float[] getWeightedCenter(int step, Room room, float darkest_sensed_shadow){ - float[] sum_coor = {0,0}; - float sum_weight = 0; - for(int L = 0; L < LEG_COUNT; L++){ - int genome_index = L*GENES_PER_LEG+2*step; - if(darkest_sensed_shadow < genome[L*GENES_PER_LEG+12]){ // it's below the threshold, so do the dark pattern - genome_index += 6; - } - float weight = genome[genome_index]; - sum_weight += weight; - for(int d = 0; d < 2; d++){ - sum_coor[d] += leg_coor[L][d]*weight; - } + float[] getWeightedCenter(int step, Room room, float darkest_sensed_shadow) { + float sumX = 0, sumY = 0; + float sumWeight = 0; + + boolean isDarkPattern = darkest_sensed_shadow < genome[12]; + int baseIndex = isDarkPattern ? 6 : 0; + + for(int L = 0; L < LEG_COUNT; L++) { + int genomeIndex = L * GENES_PER_LEG + 2 * step + baseIndex; + float weight = genome[genomeIndex]; + sumWeight += weight; + + float legX = leg_coor[L][0]; //<>// + float legY = leg_coor[L][1]; + sumX += legX * weight; + sumY += legY * weight; } - float rx = sum_coor[0]/sum_weight; - float ry = sum_coor[1]/sum_weight; - float[] result = {rx, ry}; - return result; - } - void placeLegs(float[] center, int step, Room room, float darkest_sensed_shadow, ArrayList spiders){ - float force_to_right_angles = 0.001; // how strongly should the spider's legs be dragged back into right angles? + + float invWeight = 1.0f / sumWeight; + return new float[] {sumX * invWeight, sumY * invWeight}; +} + void placeLegs(float[] center, int step, Room room, float darkest_sensed_shadow, ArrayList spiders) { + final float force_to_right_angles = 0.001f; + final float TWO_PI = PI * 2; + final float HALF_PI = PI / 2; + float first_angle = 0; - for(int L = 0; L < LEG_COUNT; L++){ - int genome_index = L*GENES_PER_LEG+2*step+1; - if(darkest_sensed_shadow < genome[L*GENES_PER_LEG+12]){ // it's below the threshold, so do the dark pattern - genome_index += 6; - } - float distance = genome[genome_index]*MAX_LEG_SPAN; - float delta_x = leg_coor[L][0]-center[0]; - float delta_y = leg_coor[L][1]-center[1]; - float angle = atan2(delta_y,delta_x); - if(L == 0){ - first_angle = angle; - }else{ - float desired_angle = first_angle+PI/2*L; - float move = (desired_angle-angle); - while(move > PI){ - move -= 2*PI; - } - while(move < -PI){ - move += 2*PI; + boolean isDarkPattern = darkest_sensed_shadow < genome[12]; + int baseIndex = isDarkPattern ? 6 : 0; + + for(int L = 0; L < LEG_COUNT; L++) { + int genomeIndex = L * GENES_PER_LEG + 2 * step + baseIndex + 1; + float distance = genome[genomeIndex] * MAX_LEG_SPAN; + + float dx = leg_coor[L][0] - center[0]; + float dy = leg_coor[L][1] - center[1]; + float angle = atan2(dy, dx); + + if(L == 0) { + first_angle = angle; + } else { + float desired_angle = first_angle + L * HALF_PI; + float move = desired_angle - angle; + + // Normalize angle difference more efficiently + if(move > PI) move -= TWO_PI; + else if(move < -PI) move += TWO_PI; + + angle += force_to_right_angles * move; } - angle += force_to_right_angles*move; - } - leg_coor[L][0] = center[0]+cos(angle)*distance; - leg_coor[L][1] = center[1]+sin(angle)*distance; - } - coor = getWeightedCenter(step,room,darkest_sensed_shadow); - for(int d = 0; d < 2; d++){ - if(coor[d] < 0){ - shiftAllBy(d,room.getMaxDim(d)); - }else if(coor[d] >= room.getMaxDim(d)){ - shiftAllBy(d,-room.getMaxDim(d)); - } + + leg_coor[L][0] = center[0] + cos(angle) * distance; + leg_coor[L][1] = center[1] + sin(angle) * distance; } - } + + coor = center; + checkBoundaries(room); +} + +private void checkBoundaries(Room room) { + float maxX = room.getMaxDim(0); + float maxY = room.getMaxDim(1); + + if(coor[0] < 0) shiftAllBy(0, maxX); + else if(coor[0] >= maxX) shiftAllBy(0, -maxX); + + if(coor[1] < 0) shiftAllBy(1, maxY); + else if(coor[1] >= maxY) shiftAllBy(1, -maxY); +} void shiftAllBy(int dim, float amt){ coor[dim] += amt; for(int L = 0; L < LEG_COUNT; L++){ @@ -277,9 +294,17 @@ color getColor() { move(room, cycle, swatters, spiders); } } - float getDarkestShadow() { + private float lastDarkestShadow = 1.0f; + private int lastCheckTick = -1; + +float getDarkestShadow() { + if (lastCheckTick == ticks) { + return lastDarkestShadow; + } + + lastCheckTick = ticks; float darkest_sensed_shadow = 1.0f; - float R2 = R * R; // Square of radius for faster distance checks + float R2 = R * R; for(Swatter sw : swatters) { float swX = sw.coor[0]; diff --git a/SpiderEvoSim.pde b/SpiderEvoSim.pde index f9afd90..30f7c00 100644 --- a/SpiderEvoSim.pde +++ b/SpiderEvoSim.pde @@ -166,35 +166,26 @@ void setup(){ ArrayList frameTimestamps = new ArrayList<>(); // To store frame timestamps float averageFps = 0; // Average FPS over the last 4 seconds -// Add to global variables +// Global timing variables long physicsTiming = 0; long renderTiming = 0; -long spiderPhysicsTiming = 0; -long swatterPhysicsTiming = 0; String physicsStats = ""; String renderStats = ""; -String spiderStats = ""; -String swatterStats = ""; void draw() { - // Capture current time in milliseconds + // FPS calculation stays the same long currentTime = millis(); frameTimestamps.add(currentTime); - - // Remove timestamps older than 4 seconds while (frameTimestamps.size() > 0 && frameTimestamps.get(0) < currentTime - 4000) { frameTimestamps.remove(0); } - - // Calculate average FPS if (frameTimestamps.size() > 1) { float elapsedSeconds = (frameTimestamps.get(frameTimestamps.size() - 1) - frameTimestamps.get(0)) / 1000.0f; averageFps = (frameTimestamps.size() - 1) / elapsedSeconds; } - long startFrame = System.nanoTime(); - // Original draw logic doMouse(); + long startPhysics = System.nanoTime(); doPhysics(); physicsTiming = System.nanoTime() - startPhysics; @@ -210,8 +201,6 @@ void draw() { if (frames % 30 == 0) { physicsStats = "Physics: " + (physicsTiming / 1_000_000) + "ms"; renderStats = "Render: " + (renderTiming / 1_000_000) + "ms"; - spiderStats = "Spider Physics: " + (spiderPhysicsTiming / 1_000_000) + "ms"; - swatterStats = "Swatter Physics: " + (swatterPhysicsTiming / 1_000_000) + "ms"; } // Display stats @@ -220,8 +209,6 @@ void draw() { textSize(24); text(physicsStats, 10, 140); text(renderStats, 10, 170); - text(spiderStats, 10, 200); - text(swatterStats, 10, 230); frames++; if (camera[1] < -1) { @@ -353,15 +340,15 @@ void mousePressed() { g.popMatrix(); } void doPhysics(){ - iterateButtons(player); - for(int p = 0; p < playback_speed; p++){ - iterateSpiders(room); - iterateSwatters(room); - collectData(); - ticks++; - } - player.takeInputs(keyHandler); - player.doPhysics(room); + iterateButtons(player); + for(int p = 0; p < playback_speed; p++){ + iterateSpiders(room); + iterateSwatters(room); + collectData(); + ticks++; + } + player.takeInputs(keyHandler); + player.doPhysics(room); } float getBiodiversity(){ @@ -592,19 +579,15 @@ void aTranslate(float[] coor){ g.translate(coor[0],coor[1],coor[2]); } void iterateSpiders(Room room) { - long start = System.nanoTime(); for(int s = 0; s < spiders.size(); s++) { spiders.get(s).iterate(room, swatters, spiders); } - spiderPhysicsTiming = System.nanoTime() - start; } void iterateSwatters(Room room) { - long start = System.nanoTime(); - for(int s = 0; s < swatters.size(); s++) { + for(int s = 0; s < swatters.size(); s++) { //<>// swatters.get(s).iterate(room, spiders, swatters); } - swatterPhysicsTiming = System.nanoTime() - start; } void iterateButtons(Player player){ for(int b = 0; b < buttons.size(); b++){ From c196de7fd274fc722caf26531d0c91e8bb081a86 Mon Sep 17 00:00:00 2001 From: Rohan Date: Sun, 19 Jan 2025 01:42:55 +1100 Subject: [PATCH 29/32] caching and batching as well as floating point safety. Fps increase +1 58-59 max fps --- Spider.pde | 6 ++--- SpiderEvoSim.pde | 58 ++++++++++++++++++++++++++++++++---------------- 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/Spider.pde b/Spider.pde index a417477..1bcbf87 100644 --- a/Spider.pde +++ b/Spider.pde @@ -35,7 +35,7 @@ class Spider{ leg_coor = new float[LEG_COUNT][2]; float ang = random(0,1); for(int L = 0; L < LEG_COUNT; L++){ - float angL = (L+ang)*HALF PI; + float angL = (L+ang)*HALF_PI; int genome_index = L*GENES_PER_LEG+1; float distance = genome[genome_index]; leg_coor[L][0] = coor[0]+cos(angL)*distance; @@ -236,11 +236,9 @@ color getColor() { } void placeLegs(float[] center, int step, Room room, float darkest_sensed_shadow, ArrayList spiders) { final float force_to_right_angles = 0.001f; - final float TWO_PI = PI * 2; - final float HALF_PI = PI / 2; float first_angle = 0; - boolean isDarkPattern = darkest_sensed_shadow < genome[12]; + boolean isDarkPattern = darkest_sensed_shadow < genome[12]; //<>// int baseIndex = isDarkPattern ? 6 : 0; for(int L = 0; L < LEG_COUNT; L++) { diff --git a/SpiderEvoSim.pde b/SpiderEvoSim.pde index 30f7c00..a4cce88 100644 --- a/SpiderEvoSim.pde +++ b/SpiderEvoSim.pde @@ -1,6 +1,7 @@ import com.jogamp.newt.opengl.GLWindow; import processing.sound.*; -//582 Lines = Working branch + FPS counter + +//Working branch + FPS counter int CENTER_X = 960; // try setting this to 960 or 961 if there is horizontal camera-pan-drifting String[] soundFileNames = {"slap0.wav","slap1.wav","slap2.wav","splat0.wav","splat1.wav","splat2.wav","boop1.wav","boop2.wav","jump.wav","news.wav"}; SoundFile[] sfx; @@ -9,7 +10,7 @@ Player player; KeyHandler keyHandler; int DIM_COUNT = 3; int SPIDER_ITER_BUCKETS = 4; -float SWAT_SPEED = 0.001; +float SWAT_SPEED = 0.001f; int R = 40; int STEPS_CYCLE = 3; int SIBSC = SPIDER_ITER_BUCKETS*STEPS_CYCLE; @@ -21,7 +22,7 @@ int ticks = 0; int totalIndex = 0; int totalSwatterIndex = 0; float[] camera = {0,0}; -float EPS = 0.04; +float EPS = 0.04f; PGraphics g; int playback_speed = 1; int dailyDeaths = 0; @@ -115,7 +116,7 @@ ArrayList createSpiders(Room room){ void createSwatters(Room room, int START_SPIDER_COUNT){ swatters = new ArrayList(0); for(int s = 0; s < START_SPIDER_COUNT; s++){ - float perc = (s+0.5)/START_SPIDER_COUNT*1.4-0.4; + float perc = (s+0.5f)/START_SPIDER_COUNT*1.4f-0.4f; Swatter newSwatter = new Swatter(s, perc, room, swatters); swatters.add(newSwatter); } @@ -227,11 +228,11 @@ Spider checkHighlightHelper(){ Spider answer = null; float recordLowest = 1; if (mousePressed) { - for (int s = 0; s < spiders.size(); s++) { - float score = spiders.get(s).cursorOnSpider(); - if (score > 0 && score < recordLowest) { - recordLowest = score; - answer = spiders.get(s); + for (Spider s : spiders) { + float score = s.cursorOnSpider(); + if (score > 0 && score < recordLowest) { + recordLowest = score; + answer = s; } } } @@ -314,8 +315,8 @@ String ticksToDate(long t) { void doMouse(){ if(TRAP_MOUSE){ if(frames >= 2){ - camera[0] += (mouseX-CENTER_X)*0.005; - camera[1] += (mouseY-height/2)*0.005; + camera[0] += (mouseX-CENTER_X)*0.005f; + camera[1] += (mouseY-height/2)*0.005f; } r.warpPointer(width/2,height/2); } @@ -339,20 +340,38 @@ void mousePressed() { } g.popMatrix(); } -void doPhysics(){ +void doPhysics(){ //<>// iterateButtons(player); - for(int p = 0; p < playback_speed; p++){ + + // Process multiple ticks in batches + int batchSize = 100; + int remainingTicks = playback_speed; + + while(remainingTicks > 0) { + int currentBatch = Math.min(batchSize, remainingTicks); + processPhysicsBatch(currentBatch); + remainingTicks -= currentBatch; + } + + player.takeInputs(keyHandler); + player.doPhysics(room); +} + +void processPhysicsBatch(int batchSize) { + for(int p = 0; p < batchSize; p++) { iterateSpiders(room); iterateSwatters(room); - collectData(); + if(ticks % (long)TICKS_PER_DAY == 0) { + collectData(); + } ticks++; } - player.takeInputs(keyHandler); - player.doPhysics(room); } float getBiodiversity(){ + int spiderCount = spiders.size(); float[] meanGenome = new float[GENOME_LENGTH]; + java.util.Arrays.fill(meanGenome, 0); for(int g = 0; g < GENOME_LENGTH; g++){ meanGenome[g] = 0; } @@ -362,9 +381,10 @@ float getBiodiversity(){ } } for(int g = 0; g < GENOME_LENGTH; g++){ - meanGenome[g] /= spiders.size(); + meanGenome[g] /= spiderCount; } float[] variances = new float[GENOME_LENGTH]; + java.util.Arrays.fill(variances, 0); for(int g = 0; g < GENOME_LENGTH; g++){ variances[g] = 0; } @@ -375,7 +395,7 @@ float getBiodiversity(){ } float total_diversity = 0; for(int g = 0; g < GENOME_LENGTH; g++){ - total_diversity += sqrt(variances[g]/spiders.size()); + total_diversity += sqrt(variances[g]/spiderCount); } return total_diversity/GENOME_LENGTH*100; } @@ -395,7 +415,7 @@ void collectData() { // Get spider count before division float spiderCount = spiders.size(); - for (int s = 0; s < spiders.size(); s++) { + for (int s = 0; s < spiderCount; s++) { spiders.get(s).writeData(datum); } From e116bdb3bada712ec5acc482522eb25b6efca8d8 Mon Sep 17 00:00:00 2001 From: Rohan Date: Mon, 20 Jan 2025 00:55:03 +1100 Subject: [PATCH 30/32] Spider colours tweaked! --- Spider.pde | 111 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 78 insertions(+), 33 deletions(-) diff --git a/Spider.pde b/Spider.pde index 1bcbf87..61951ff 100644 --- a/Spider.pde +++ b/Spider.pde @@ -67,7 +67,6 @@ class Spider{ g.popMatrix(); return value; } -// Replace the existing getColor() method with this optimized version color getColor() { int currentSwatterCount = swattersSeen.size(); @@ -76,39 +75,84 @@ color getColor() { return cachedColor; } + // Use logarithmic scaling for smoother early progression + float logScale = currentSwatterCount > 0 ? log(currentSwatterCount + 1) / log(10000) : 0; + // Calculate new color color newColor; + + // Swatter count color progression: + // 0 swatters: Black (0,0,0) + // 1 swatters: Pure Blue + // 3 swatters: Transition Blue-teal + // 5 swatters: Light Teal + // 10 swatters: Pure Green + // 20 swatters: Light-ish Lime + // 40 swatters: Rich/normal Yellow + // ~75 swatters: Deepish Orange + // ~150 swatters: Red + // ~375 swatters: Hot Pink + // ~800 swatters: Purple + // ~1200 swatters: Transition Light blue + // ~1600 swatters: Cyan + // ~3200 swatters: Mint + // ~6400 swatters: Gold + // >=00000 swatters: White + if (currentSwatterCount == 0) { - newColor = color(0, 0, 0, 255); // Black + newColor = color(0, 0, 0, 255); // 0 swatters: Pure black + } else if (logScale <= 0.08) { + // Black to blue (0-20 swatters) + float fac = logScale / 0.08; + newColor = color(0, 0, fac * 255, 255); + } else if (logScale <= 0.16) { + // Blue to teal (20-50 swatters) + float fac = (logScale - 0.08) / 0.08; + newColor = color(0, fac * 64, 255, 255); // Changed from 128 to 64 for darker teal + } else if (logScale <= 0.24) { + // Teal to green (50-100 swatters) + float fac = (logScale - 0.16) / 0.08; + newColor = color(0, 128, 255 - fac * 255, 255); + } else if (logScale <= 0.32) { + // Green to lime (100-200 swatters) + float fac = (logScale - 0.24) / 0.08; + newColor = color(fac * 128, 128 + fac * 127, 0, 255); + } else if (logScale <= 0.40) { + // Lime to yellow (200-400 swatters) + float fac = (logScale - 0.32) / 0.08; + newColor = color(128 + fac * 127, 220, 0, 255); // Changed from 255 to 220 for darker yellow + } else if (logScale <= 0.48) { + // Yellow to orange (400-800 swatters) + float fac = (logScale - 0.40) / 0.08; + newColor = color(255, 220 - fac * 92, 0, 255); // Adjusted to match new yellow + } else if (logScale <= 0.56) { + // Orange to red (800-1,500 swatters) + float fac = (logScale - 0.48) / 0.08; + newColor = color(255, 128 - fac * 128, 0, 255); + } else if (logScale <= 0.64) { + // Red to hot pink (1,500-2,500 swatters) + float fac = (logScale - 0.56) / 0.08; + newColor = color(255, 0 + fac * 20, 0 + fac * 147, 255); + } else if (logScale <= 0.72) { + // Hot pink to purple (2,500-4,000 swatters) + float fac = (logScale - 0.64) / 0.08; + newColor = color(255 - fac * 127, 20 - fac * 20, 147 - fac * 19, 255); + } else if (logScale <= 0.80) { + // Purple to cyan (4,000-6,000 swatters) + float fac = (logScale - 0.72) / 0.08; + newColor = color(128 - fac * 128, 0 + fac * 255, 128 + fac * 127, 255); + } else if (logScale <= 0.88) { + // Cyan to mint (6,000-7,500 swatters) + float fac = (logScale - 0.80) / 0.08; + newColor = color(0 + fac * 98, 255, 255 - fac * 111, 255); + } else if (logScale <= 0.96) { + // Mint to gold (7,500-9,000 swatters) + float fac = (logScale - 0.88) / 0.08; + newColor = color(98 + fac * 157, 255 - fac * 40, 144 - fac * 144, 255); } else { - if (currentSwatterCount <= 5) { - float fac = currentSwatterCount / 5.0; - newColor = color(0, fac * 140, 255 - fac * 255, 255); // Blue transition - } else if (currentSwatterCount <= 20) { - float fac = (currentSwatterCount - 5) / 15.0; - newColor = color(fac * 255, 140 + fac * 155, 20, 255); // Green with a higher base for better contrast - } else if (currentSwatterCount <= 40) { - float fac = (currentSwatterCount - 20) / 20.0; - newColor = color(255, 255 - fac * 115, 50, 255); // Yellow with a richer tone - } else if (currentSwatterCount <= 100) { - float fac = (currentSwatterCount - 40) / 60.0; - newColor = color(255, 140 - fac * 140, 0, 255); // Red at 100 into Pink transition at 150 - } else if (currentSwatterCount <= 200) { - float fac = (currentSwatterCount - 100) / 100.0; - newColor = color(255, 0, fac * 255, 255); // Purple transition - } else if (currentSwatterCount <= 500) { - float fac = (currentSwatterCount - 200) / 300.0; - newColor = color(255 - fac * 141, 20, 255, 255); // Deeper purple to cyan - } else if (currentSwatterCount <= 1000) { - float fac = (currentSwatterCount - 500) / 500.0; - newColor = color(114 - fac * 114, fac * 255, 255, 255); // Cyan transition - } else if (currentSwatterCount <= 4000) { - float fac = (currentSwatterCount - 1000) / 3000.0; - newColor = color(225 + fac * 30, 210 - fac * 20, 30 + fac * 50); // Gold with richer, deeper hues - } else { - float fac = min(1, (currentSwatterCount - 4000) / 6000.0); - newColor = color(255, 202 + fac * 53, fac * 255); // Transition to white - } + // Gold to white (9,000-10,000 swatters) + float fac = (logScale - 0.96) / 0.04; + newColor = color(255, 215 + fac * 40, 0 + fac * 255, 255); } // Cache the results @@ -118,12 +162,13 @@ color getColor() { return newColor; } - color transitionColor(color a, color b, float prog){ +color transitionColor(color a, color b, float prog) { float newR = lerp(red(a), red(b), prog); float newG = lerp(green(a), green(b), prog); float newB = lerp(blue(a), blue(b), prog); - return color(newR, newG, newB); - } + float newA = lerp(alpha(a), alpha(b), prog); + return color(newR, newG, newB, newA); +} void drawSpider(Room room){ float[] realCoor = room.wallCoor_to_realCoor(coor); From c4bc14e425c76681aa4d3779619c1467075ded07 Mon Sep 17 00:00:00 2001 From: Rohan Date: Mon, 20 Jan 2025 01:11:08 +1100 Subject: [PATCH 31/32] Comment updates --- Spider.pde | 111 +++++++++++++++++++++++++++-------------------------- 1 file changed, 56 insertions(+), 55 deletions(-) diff --git a/Spider.pde b/Spider.pde index 61951ff..1dbffde 100644 --- a/Spider.pde +++ b/Spider.pde @@ -97,63 +97,64 @@ color getColor() { // ~1600 swatters: Cyan // ~3200 swatters: Mint // ~6400 swatters: Gold - // >=00000 swatters: White + // >=10000 swatters: White if (currentSwatterCount == 0) { - newColor = color(0, 0, 0, 255); // 0 swatters: Pure black - } else if (logScale <= 0.08) { - // Black to blue (0-20 swatters) - float fac = logScale / 0.08; - newColor = color(0, 0, fac * 255, 255); - } else if (logScale <= 0.16) { - // Blue to teal (20-50 swatters) - float fac = (logScale - 0.08) / 0.08; - newColor = color(0, fac * 64, 255, 255); // Changed from 128 to 64 for darker teal - } else if (logScale <= 0.24) { - // Teal to green (50-100 swatters) - float fac = (logScale - 0.16) / 0.08; - newColor = color(0, 128, 255 - fac * 255, 255); - } else if (logScale <= 0.32) { - // Green to lime (100-200 swatters) - float fac = (logScale - 0.24) / 0.08; - newColor = color(fac * 128, 128 + fac * 127, 0, 255); - } else if (logScale <= 0.40) { - // Lime to yellow (200-400 swatters) - float fac = (logScale - 0.32) / 0.08; - newColor = color(128 + fac * 127, 220, 0, 255); // Changed from 255 to 220 for darker yellow - } else if (logScale <= 0.48) { - // Yellow to orange (400-800 swatters) - float fac = (logScale - 0.40) / 0.08; - newColor = color(255, 220 - fac * 92, 0, 255); // Adjusted to match new yellow - } else if (logScale <= 0.56) { - // Orange to red (800-1,500 swatters) - float fac = (logScale - 0.48) / 0.08; - newColor = color(255, 128 - fac * 128, 0, 255); - } else if (logScale <= 0.64) { - // Red to hot pink (1,500-2,500 swatters) - float fac = (logScale - 0.56) / 0.08; - newColor = color(255, 0 + fac * 20, 0 + fac * 147, 255); - } else if (logScale <= 0.72) { - // Hot pink to purple (2,500-4,000 swatters) - float fac = (logScale - 0.64) / 0.08; - newColor = color(255 - fac * 127, 20 - fac * 20, 147 - fac * 19, 255); - } else if (logScale <= 0.80) { - // Purple to cyan (4,000-6,000 swatters) - float fac = (logScale - 0.72) / 0.08; - newColor = color(128 - fac * 128, 0 + fac * 255, 128 + fac * 127, 255); - } else if (logScale <= 0.88) { - // Cyan to mint (6,000-7,500 swatters) - float fac = (logScale - 0.80) / 0.08; - newColor = color(0 + fac * 98, 255, 255 - fac * 111, 255); - } else if (logScale <= 0.96) { - // Mint to gold (7,500-9,000 swatters) - float fac = (logScale - 0.88) / 0.08; - newColor = color(98 + fac * 157, 255 - fac * 40, 144 - fac * 144, 255); - } else { - // Gold to white (9,000-10,000 swatters) - float fac = (logScale - 0.96) / 0.04; - newColor = color(255, 215 + fac * 40, 0 + fac * 255, 255); - } + newColor = color(0, 0, 0, 255); // 0 swatters: Pure black +} else if (logScale <= 0.08) { + // 1-3 swatters: Pure Blue + float fac = logScale / 0.08; + newColor = color(0, 0, fac * 255, 255); +} else if (logScale <= 0.16) { + // 3-5 swatters: Light Teal + float fac = (logScale - 0.08) / 0.08; + newColor = color(0, fac * 64, 255, 255); +} else if (logScale <= 0.24) { + // 5-10 swatters: Pure Green + float fac = (logScale - 0.16) / 0.08; + newColor = color(0, 128, 255 - fac * 255, 255); +} else if (logScale <= 0.32) { + // 10-20 swatters: Light-ish Lime + float fac = (logScale - 0.24) / 0.08; + newColor = color(fac * 128, 128 + fac * 127, 0, 255); +} else if (logScale <= 0.40) { + // 20-40 swatters: Rich/normal Yellow + float fac = (logScale - 0.32) / 0.08; + newColor = color(128 + fac * 127, 220, 0, 255); +} else if (logScale <= 0.48) { + // 40-75 swatters: Deepish Orange + float fac = (logScale - 0.40) / 0.08; + newColor = color(255, 220 - fac * 92, 0, 255); +} else if (logScale <= 0.56) { + // 75-150 swatters: Red + float fac = (logScale - 0.48) / 0.08; + newColor = color(255, 128 - fac * 128, 0, 255); +} else if (logScale <= 0.64) { + // 150-375 swatters: Hot Pink + float fac = (logScale - 0.56) / 0.08; + newColor = color(255, 0 + fac * 20, 0 + fac * 147, 255); +} else if (logScale <= 0.72) { + // 375-800 swatters: Purple + float fac = (logScale - 0.64) / 0.08; + newColor = color(255 - fac * 127, 20 - fac * 20, 147 - fac * 19, 255); +} else if (logScale <= 0.80) { + // 800-1200 swatters: Light Blue + float fac = (logScale - 0.72) / 0.08; + newColor = color(128 - fac * 128, 0 + fac * 255, 128 + fac * 127, 255); +} else if (logScale <= 0.88) { + // 1200-1600 swatters: Cyan + float fac = (logScale - 0.80) / 0.08; + newColor = color(0 + fac * 98, 255, 255 - fac * 111, 255); +} else if (logScale <= 0.96) { + // 1600-6400 swatters: Mint + float fac = (logScale - 0.88) / 0.08; + newColor = color(98 + fac * 157, 255 - fac * 40, 144 - fac * 144, 255); +} else { + // 6400-9999 swatters: Gold + float fac = (logScale - 0.96) / 0.04; + newColor = color(255, 215 + fac * 40, 0 + fac * 255, 255); + // >9999 swatters: White +} // Cache the results cachedColor = newColor; From 02d70ba63cb834f56a54cd80efad71a793b851aa Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 11 Mar 2025 08:34:06 +1100 Subject: [PATCH 32/32] Optimised slightly + Acceleration for player reduced slightly --- Player.pde | 2 +- Spider.pde | 567 +++++++++++++++++++++++++---------------------------- 2 files changed, 264 insertions(+), 305 deletions(-) diff --git a/Player.pde b/Player.pde index 0db12bf..2087308 100644 --- a/Player.pde +++ b/Player.pde @@ -3,7 +3,7 @@ class Player{ float[] lag_coor; float[] velo; float FRICTION = 0.85; - float ACCEL = 1.8; + float ACCEL = 1.75; float R_ACCEL = 0.05; float THICKNESS = 11; float EPS = 0.1; diff --git a/Spider.pde b/Spider.pde index 1dbffde..cfc3ea0 100644 --- a/Spider.pde +++ b/Spider.pde @@ -1,9 +1,12 @@ -class Spider{ +import java.util.HashSet; //<>// //<>// +import java.util.ArrayList; + +class Spider { float BODY_SPAN = 4; float MAX_LEG_SPAN = 37; - float Ldist = MAX_LEG_SPAN*0.25; - int ITER_TIME = (int)random(0,SIBSC); - + float Ldist = MAX_LEG_SPAN * 0.25f; + int ITER_TIME = (int) random(0, SIBSC); + float[] genome; float[] coor; float[][] leg_coor; @@ -12,184 +15,147 @@ class Spider{ int generation; int birth_tick; Spider parent; - ArrayList swattersSeen = new ArrayList(0); - // Add these as class fields + // Replaced ArrayList with HashSet for O(1) membership checks + HashSet swattersSeen = new HashSet<>(); + + // Class fields for performance and caching private final float HALF_PI = PI / 2; private final float TWO_PI = PI * 2; private final float MAX_LEG_SPAN_SQ = MAX_LEG_SPAN * MAX_LEG_SPAN; - // Add these fields at the top of the Spider class - private color cachedColor = -1; // -1 means not calculated yet - private int lastSwatterCount = -1; // Track when we need to recalculate - - public Spider(int i, Room room){ + private color cachedColor = -1; // -1 means not calculated yet + private int lastSwatterCount = -1; // Track when we need to recalculate + + public Spider(int i, Room room) { parent = null; genome = new float[GENOME_LENGTH]; - for(int g = 0; g < GENOME_LENGTH; g++){ - genome[g] = random(0.2,0.4); + for (int g = 0; g < GENOME_LENGTH; g++) { + genome[g] = random(0.2f, 0.4f); } coor = new float[2]; - for(int d = 0; d < 2; d++){ - coor[d] = random(0,room.getMaxDim(d)); + for (int d = 0; d < 2; d++) { + coor[d] = random(0, room.getMaxDim(d)); } leg_coor = new float[LEG_COUNT][2]; - float ang = random(0,1); - for(int L = 0; L < LEG_COUNT; L++){ - float angL = (L+ang)*HALF_PI; - int genome_index = L*GENES_PER_LEG+1; + float ang = random(0, 1); + for (int L = 0; L < LEG_COUNT; L++) { + float angL = (L + ang) * HALF_PI; + int genome_index = L * GENES_PER_LEG + 1; float distance = genome[genome_index]; - leg_coor[L][0] = coor[0]+cos(angL)*distance; - leg_coor[L][1] = coor[1]+sin(angL)*distance; + leg_coor[L][0] = coor[0] + cos(angL) * distance; + leg_coor[L][1] = coor[1] + sin(angL) * distance; } index = i; generation = 0; increment_(); } - float clip_(float val, Room room, int dim){ + + float clip_(float val, Room room, int dim) { float min_ = 0; float max_ = room.getMaxDim(dim); - return min(max(val,min_),max_); + return min(max(val, min_), max_); } - float cursorOnSpider(){ + + float cursorOnSpider() { float[] realCoor = room.wallCoor_to_realCoor(coor); g.pushMatrix(); aTranslate(realCoor); float MAX_DIST_MOUSE = 100; float value = -99999; - float x1 = g.screenX(0,0,0); - float y1 = g.screenY(0,0,0); - float x2 = g.screenX(0,MAX_LEG_SPAN,0); - float y2 = g.screenY(0,MAX_LEG_SPAN,0); - float distFromCenter = dist(x1,y1,width/2,height/2); - if(distFromCenter < dist(x1,y1,x2,y2) && distFromCenter < MAX_DIST_MOUSE){ - value = g.screenZ(0,0,0); + float x1 = g.screenX(0, 0, 0); + float y1 = g.screenY(0, 0, 0); + float x2 = g.screenX(0, MAX_LEG_SPAN, 0); + float y2 = g.screenY(0, MAX_LEG_SPAN, 0); + float distFromCenter = dist(x1, y1, width / 2, height / 2); + if (distFromCenter < dist(x1, y1, x2, y2) && distFromCenter < MAX_DIST_MOUSE) { + value = g.screenZ(0, 0, 0); } g.popMatrix(); return value; } -color getColor() { + + color getColor() { int currentSwatterCount = swattersSeen.size(); - - // Return cached color if nothing has changed if (cachedColor != -1 && currentSwatterCount == lastSwatterCount) { - return cachedColor; + return cachedColor; } - // Use logarithmic scaling for smoother early progression float logScale = currentSwatterCount > 0 ? log(currentSwatterCount + 1) / log(10000) : 0; - - // Calculate new color color newColor; - // Swatter count color progression: - // 0 swatters: Black (0,0,0) - // 1 swatters: Pure Blue - // 3 swatters: Transition Blue-teal - // 5 swatters: Light Teal - // 10 swatters: Pure Green - // 20 swatters: Light-ish Lime - // 40 swatters: Rich/normal Yellow - // ~75 swatters: Deepish Orange - // ~150 swatters: Red - // ~375 swatters: Hot Pink - // ~800 swatters: Purple - // ~1200 swatters: Transition Light blue - // ~1600 swatters: Cyan - // ~3200 swatters: Mint - // ~6400 swatters: Gold - // >=10000 swatters: White - if (currentSwatterCount == 0) { - newColor = color(0, 0, 0, 255); // 0 swatters: Pure black -} else if (logScale <= 0.08) { - // 1-3 swatters: Pure Blue - float fac = logScale / 0.08; - newColor = color(0, 0, fac * 255, 255); -} else if (logScale <= 0.16) { - // 3-5 swatters: Light Teal - float fac = (logScale - 0.08) / 0.08; - newColor = color(0, fac * 64, 255, 255); -} else if (logScale <= 0.24) { - // 5-10 swatters: Pure Green - float fac = (logScale - 0.16) / 0.08; - newColor = color(0, 128, 255 - fac * 255, 255); -} else if (logScale <= 0.32) { - // 10-20 swatters: Light-ish Lime - float fac = (logScale - 0.24) / 0.08; - newColor = color(fac * 128, 128 + fac * 127, 0, 255); -} else if (logScale <= 0.40) { - // 20-40 swatters: Rich/normal Yellow - float fac = (logScale - 0.32) / 0.08; - newColor = color(128 + fac * 127, 220, 0, 255); -} else if (logScale <= 0.48) { - // 40-75 swatters: Deepish Orange - float fac = (logScale - 0.40) / 0.08; - newColor = color(255, 220 - fac * 92, 0, 255); -} else if (logScale <= 0.56) { - // 75-150 swatters: Red - float fac = (logScale - 0.48) / 0.08; - newColor = color(255, 128 - fac * 128, 0, 255); -} else if (logScale <= 0.64) { - // 150-375 swatters: Hot Pink - float fac = (logScale - 0.56) / 0.08; - newColor = color(255, 0 + fac * 20, 0 + fac * 147, 255); -} else if (logScale <= 0.72) { - // 375-800 swatters: Purple - float fac = (logScale - 0.64) / 0.08; - newColor = color(255 - fac * 127, 20 - fac * 20, 147 - fac * 19, 255); -} else if (logScale <= 0.80) { - // 800-1200 swatters: Light Blue - float fac = (logScale - 0.72) / 0.08; - newColor = color(128 - fac * 128, 0 + fac * 255, 128 + fac * 127, 255); -} else if (logScale <= 0.88) { - // 1200-1600 swatters: Cyan - float fac = (logScale - 0.80) / 0.08; - newColor = color(0 + fac * 98, 255, 255 - fac * 111, 255); -} else if (logScale <= 0.96) { - // 1600-6400 swatters: Mint - float fac = (logScale - 0.88) / 0.08; - newColor = color(98 + fac * 157, 255 - fac * 40, 144 - fac * 144, 255); -} else { - // 6400-9999 swatters: Gold - float fac = (logScale - 0.96) / 0.04; - newColor = color(255, 215 + fac * 40, 0 + fac * 255, 255); - // >9999 swatters: White -} + newColor = color(0, 0, 0, 255); + } else if (logScale <= 0.08f) { + float fac = logScale / 0.08f; + newColor = color(0, 0, fac * 180, 255); + } else if (logScale <= 0.16f) { + float fac = (logScale - 0.08f) / 0.08f; + newColor = color(0, fac * 50, 180, 255); + } else if (logScale <= 0.24f) { + float fac = (logScale - 0.16f) / 0.08f; + newColor = color(0, 100, 180 - fac * 100, 255); + } else if (logScale <= 0.32f) { + float fac = (logScale - 0.24f) / 0.08f; + newColor = color(fac * 60, 100 + fac * 60, 40, 255); + } else if (logScale <= 0.40f) { + float fac = (logScale - 0.32f) / 0.08f; + newColor = color(128 + fac * 127, 220, 0, 255); + } else if (logScale <= 0.48f) { + float fac = (logScale - 0.40f) / 0.08f; + newColor = color(255, 220 - fac * 92, 0, 255); + } else if (logScale <= 0.56f) { + float fac = (logScale - 0.48f) / 0.08f; + newColor = color(255, 128 - fac * 128, 0, 255); + } else if (logScale <= 0.64f) { + float fac = (logScale - 0.56f) / 0.08f; + newColor = color(255, fac * 20, fac * 147, 255); + } else if (logScale <= 0.72f) { + float fac = (logScale - 0.64f) / 0.08f; + newColor = color(255 - fac * 127, 20 - fac * 20, 147 - fac * 19, 255); + } else if (logScale <= 0.80f) { + float fac = (logScale - 0.72f) / 0.08f; + newColor = color(128 - fac * 128, fac * 255, 128 + fac * 127, 255); + } else if (logScale <= 0.88f) { + float fac = (logScale - 0.80f) / 0.08f; + newColor = color(fac * 98, 255, 255 - fac * 111, 255); + } else if (logScale <= 0.96f) { + float fac = (logScale - 0.88f) / 0.08f; + newColor = color(98 + fac * 157, 255 - fac * 40, 144 - fac * 144, 255); + } else { + float fac = (logScale - 0.96f) / 0.04f; + newColor = color(255, 215 + fac * 40, fac * 255, 255); + } - // Cache the results cachedColor = newColor; lastSwatterCount = currentSwatterCount; - return newColor; -} + } -color transitionColor(color a, color b, float prog) { + color transitionColor(color a, color b, float prog) { float newR = lerp(red(a), red(b), prog); float newG = lerp(green(a), green(b), prog); float newB = lerp(blue(a), blue(b), prog); float newA = lerp(alpha(a), alpha(b), prog); return color(newR, newG, newB, newA); -} - void drawSpider(Room room){ + } + + void drawSpider(Room room) { float[] realCoor = room.wallCoor_to_realCoor(coor); - - // Quick culling check before any drawing g.pushMatrix(); aTranslate(realCoor); float screenX = g.screenX(0, 0, 0); float screenY = g.screenY(0, 0, 0); g.popMatrix(); - // Check if spider is off screen (with margin for legs) - float margin = MAX_LEG_SPAN * 1.5; // Margin to account for legs + float margin = MAX_LEG_SPAN * 1.5f; if (screenX < -margin || screenX > width + margin || screenY < -margin || screenY > height + margin) { - return; // Skip drawing if off screen + return; } color c = getColor(); - if(this == highlight_spider){ - c = color(0,255,0); + if (this == highlight_spider) { + c = color(0, 255, 0); } g.pushMatrix(); aTranslate(realCoor); @@ -197,31 +163,30 @@ color transitionColor(color a, color b, float prog) { g.pushMatrix(); g.rotateZ(realCoor[3]); g.beginShape(); - for(int i = 0; i < 12; i++){ - float angle = i*2*PI/12; - g.vertex(cos(angle)*BODY_SPAN,2,sin(angle)*BODY_SPAN); + for (int i = 0; i < 12; i++) { + float angle = i * 2 * PI / 12; + g.vertex(cos(angle) * BODY_SPAN, 2, sin(angle) * BODY_SPAN); } g.endShape(CLOSE); g.popMatrix(); g.stroke(c); g.strokeWeight(3); - for(int L = 0; L < LEG_COUNT; L++){ + for (int L = 0; L < LEG_COUNT; L++) { float[] legRealCoor = room.wallCoor_to_realCoor(leg_coor[L]); - float[] Lcoor = aSubstract(legRealCoor,realCoor); - float[] Mcoor = multi(Lcoor,0.5); - Mcoor[0] -= sin(realCoor[3])*Ldist; - Mcoor[1] += cos(realCoor[3])*Ldist; - g.line(0,0,0,Mcoor[0],Mcoor[1],Mcoor[2]); - g.line(Mcoor[0],Mcoor[1],Mcoor[2],Lcoor[0],Lcoor[1],Lcoor[2]); + float[] Lcoor = aSubstract(legRealCoor, realCoor); + float[] Mcoor = multi(Lcoor, 0.5f); + Mcoor[0] -= sin(realCoor[3]) * Ldist; + Mcoor[1] += cos(realCoor[3]) * Ldist; + g.line(0, 0, 0, Mcoor[0], Mcoor[1], Mcoor[2]); + g.line(Mcoor[0], Mcoor[1], Mcoor[2], Lcoor[0], Lcoor[1], Lcoor[2]); } g.noStroke(); g.popMatrix(); - - if(getAge() < 200 && parent != null && parent.getAge() >= getAge()){ + if (getAge() < 200 && parent != null && parent.getAge() >= getAge()) { float[] parentCoor = room.wallCoor_to_realCoor(parent.coor); - if(realCoor[0] == parentCoor[0] && realCoor[1] == parentCoor[1]){ + if (realCoor[0] == parentCoor[0] && realCoor[1] == parentCoor[1]) { return; } g.pushMatrix(); @@ -229,294 +194,288 @@ color transitionColor(color a, color b, float prog) { g.rotateZ(realCoor[3]); g.beginShape(); g.fill(255); - float WHITE_SPAN = BODY_SPAN*4; - for(int i = 0; i < 12; i++){ - float angle = i*2*PI/12; - g.vertex(cos(angle)*WHITE_SPAN,EPS*2,sin(angle)*WHITE_SPAN); + float WHITE_SPAN = BODY_SPAN * 4; + for (int i = 0; i < 12; i++) { + float angle = i * 2 * PI / 12; + g.vertex(cos(angle) * WHITE_SPAN, EPS * 2, sin(angle) * WHITE_SPAN); } g.endShape(CLOSE); g.popMatrix(); - if(dist(realCoor[0],realCoor[1],realCoor[2],parentCoor[0],parentCoor[1],parentCoor[2]) < WHITE_SPAN*10){ + if (dist(realCoor[0], realCoor[1], realCoor[2], parentCoor[0], parentCoor[1], parentCoor[2]) < WHITE_SPAN * 10) { g.stroke(255); g.strokeWeight(20); - g.line(realCoor[0],realCoor[1],realCoor[2],parentCoor[0],parentCoor[1],parentCoor[2]); + g.line(realCoor[0], realCoor[1], realCoor[2], parentCoor[0], parentCoor[1], parentCoor[2]); g.noStroke(); } } -} - float[] multi(float[] a, float m){ + } + + float[] multi(float[] a, float m) { float[] result = new float[a.length]; - for(int i = 0; i < a.length; i++){ - result[i] = a[i]*m; + for (int i = 0; i < a.length; i++) { + result[i] = a[i] * m; } return result; } - float[] aSubstract(float[] a, float[] b){ + + float[] aSubstract(float[] a, float[] b) { float[] result = new float[a.length]; - for(int i = 0; i < a.length; i++){ - result[i] = a[i]-b[i]; + for (int i = 0; i < a.length; i++) { + result[i] = a[i] - b[i]; } return result; } + float[] getWeightedCenter(int step, Room room, float darkest_sensed_shadow) { float sumX = 0, sumY = 0; float sumWeight = 0; - boolean isDarkPattern = darkest_sensed_shadow < genome[12]; int baseIndex = isDarkPattern ? 6 : 0; - for(int L = 0; L < LEG_COUNT; L++) { - int genomeIndex = L * GENES_PER_LEG + 2 * step + baseIndex; - float weight = genome[genomeIndex]; - sumWeight += weight; - - float legX = leg_coor[L][0]; //<>// - float legY = leg_coor[L][1]; - sumX += legX * weight; - sumY += legY * weight; + for (int L = 0; L < LEG_COUNT; L++) { + int genomeIndex = L * GENES_PER_LEG + 2 * step + baseIndex; + float weight = genome[genomeIndex]; + sumWeight += weight; + float legX = leg_coor[L][0]; + float legY = leg_coor[L][1]; + sumX += legX * weight; + sumY += legY * weight; } float invWeight = 1.0f / sumWeight; - return new float[] {sumX * invWeight, sumY * invWeight}; -} + return new float[] { sumX * invWeight, sumY * invWeight }; + } + void placeLegs(float[] center, int step, Room room, float darkest_sensed_shadow, ArrayList spiders) { final float force_to_right_angles = 0.001f; - float first_angle = 0; - boolean isDarkPattern = darkest_sensed_shadow < genome[12]; //<>// + boolean isDarkPattern = darkest_sensed_shadow < genome[12]; int baseIndex = isDarkPattern ? 6 : 0; - for(int L = 0; L < LEG_COUNT; L++) { - int genomeIndex = L * GENES_PER_LEG + 2 * step + baseIndex + 1; - float distance = genome[genomeIndex] * MAX_LEG_SPAN; - - float dx = leg_coor[L][0] - center[0]; - float dy = leg_coor[L][1] - center[1]; - float angle = atan2(dy, dx); - - if(L == 0) { - first_angle = angle; - } else { - float desired_angle = first_angle + L * HALF_PI; - float move = desired_angle - angle; - - // Normalize angle difference more efficiently - if(move > PI) move -= TWO_PI; - else if(move < -PI) move += TWO_PI; - - angle += force_to_right_angles * move; - } - - leg_coor[L][0] = center[0] + cos(angle) * distance; - leg_coor[L][1] = center[1] + sin(angle) * distance; + for (int L = 0; L < LEG_COUNT; L++) { + int genomeIndex = L * GENES_PER_LEG + 2 * step + baseIndex + 1; + float distance = genome[genomeIndex] * MAX_LEG_SPAN; + float dx = leg_coor[L][0] - center[0]; + float dy = leg_coor[L][1] - center[1]; + float angle = atan2(dy, dx); + + if (L == 0) { + first_angle = angle; + } else { + float desired_angle = first_angle + L * HALF_PI; + float move = desired_angle - angle; + if (move > PI) move -= TWO_PI; + else if (move < -PI) move += TWO_PI; + angle += force_to_right_angles * move; + } + + leg_coor[L][0] = center[0] + cos(angle) * distance; + leg_coor[L][1] = center[1] + sin(angle) * distance; } coor = center; checkBoundaries(room); -} + } -private void checkBoundaries(Room room) { + private void checkBoundaries(Room room) { float maxX = room.getMaxDim(0); float maxY = room.getMaxDim(1); - - if(coor[0] < 0) shiftAllBy(0, maxX); - else if(coor[0] >= maxX) shiftAllBy(0, -maxX); - - if(coor[1] < 0) shiftAllBy(1, maxY); - else if(coor[1] >= maxY) shiftAllBy(1, -maxY); -} - void shiftAllBy(int dim, float amt){ + if (coor[0] < 0) shiftAllBy(0, maxX); + else if (coor[0] >= maxX) shiftAllBy(0, -maxX); + if (coor[1] < 0) shiftAllBy(1, maxY); + else if (coor[1] >= maxY) shiftAllBy(1, -maxY); + } + + void shiftAllBy(int dim, float amt) { coor[dim] += amt; - for(int L = 0; L < LEG_COUNT; L++){ + for (int L = 0; L < LEG_COUNT; L++) { leg_coor[L][dim] += amt; } } - void iterate(Room room, ArrayList swatters, ArrayList spiders){ + + void iterate(Room room, ArrayList swatters, ArrayList spiders) { int cycle = whereInCycle(0); - if(cycle%SPIDER_ITER_BUCKETS == 0){ + if (cycle % SPIDER_ITER_BUCKETS == 0) { move(room, cycle, swatters, spiders); } } + private float lastDarkestShadow = 1.0f; private int lastCheckTick = -1; -float getDarkestShadow() { + float getDarkestShadow() { if (lastCheckTick == ticks) { - return lastDarkestShadow; + return lastDarkestShadow; } - lastCheckTick = ticks; float darkest_sensed_shadow = 1.0f; float R2 = R * R; - for(Swatter sw : swatters) { - float swX = sw.coor[0]; - float swY = sw.coor[1]; - - // Quick AABB check before detailed collision - float minX = swX - R; - float maxX = swX + R; - float minY = swY - R; - float maxY = swY + R; - - boolean legFound = false; - for(int L = 0; L < LEG_COUNT && !legFound; L++) { - float legX = leg_coor[L][0]; - float legY = leg_coor[L][1]; - - if(legX >= minX && legX <= maxX && - legY >= minY && legY <= maxY) { - - float dx = legX - swX; - float dy = legY - swY; - float distSquared = dx * dx + dy * dy; - - if(distSquared < R2) { - if(!swattersSeen.contains(sw.visIndex)) { - swattersSeen.add(sw.visIndex); - swattersSeenTotal++; - } - darkest_sensed_shadow = Math.min(darkest_sensed_shadow, - Math.max(sw.percentage, 0)); - legFound = true; // Exit early if we found a collision - } + for (Swatter sw : swatters) { + float swX = sw.coor[0]; + float swY = sw.coor[1]; + float minX = swX - R; + float maxX = swX + R; + float minY = swY - R; + float maxY = swY + R; + + boolean legFound = false; + for (int L = 0; L < LEG_COUNT && !legFound; L++) { + float legX = leg_coor[L][0]; + float legY = leg_coor[L][1]; + if (legX >= minX && legX <= maxX && legY >= minY && legY <= maxY) { + float dx = legX - swX; + float dy = legY - swY; + float distSquared = dx * dx + dy * dy; + if (distSquared < R2) { + if (!swattersSeen.contains(sw.visIndex)) { + swattersSeen.add(sw.visIndex); + swattersSeenTotal++; } + darkest_sensed_shadow = Math.min(darkest_sensed_shadow, Math.max(sw.percentage, 0)); + legFound = true; + } } + } } return darkest_sensed_shadow; -} + } + void move(Room room, int cycle, ArrayList swatters, ArrayList spiders) { - // Cache frequently used values float darkest_sensed_shadow = getDarkestShadow(); - int step = cycle/SPIDER_ITER_BUCKETS; - - // Pre-calculate indices and thresholds once + int step = cycle / SPIDER_ITER_BUCKETS; boolean isDarkPattern = darkest_sensed_shadow < genome[12]; int baseIndex = isDarkPattern ? 6 : 0; - - // Get weighted center in one pass float[] weightedCenter = getWeightedCenter(step, room, darkest_sensed_shadow); placeLegs(weightedCenter, step, room, darkest_sensed_shadow, spiders); -} - int whereInCycle(int offset){ - return (ticks+offset-ITER_TIME+SIBSC)%SIBSC; } - PGraphics drawGenome(){ - PGraphics panel = createGraphics(400,540); + + int whereInCycle(int offset) { + return (ticks + offset - ITER_TIME + SIBSC) % SIBSC; + } + + PGraphics drawGenome() { + PGraphics panel = createGraphics(400, 540); panel.beginDraw(); - panel.background(40,80,120); + panel.background(40, 80, 120); boolean[] shouldBeGreen = new boolean[GENOME_LENGTH]; - for(int g = 0; g < GENOME_LENGTH; g++){ + for (int g = 0; g < GENOME_LENGTH; g++) { shouldBeGreen[g] = false; } - int step = whereInCycle(0)/SPIDER_ITER_BUCKETS; - int step2 = whereInCycle(SPIDER_ITER_BUCKETS/2)/SPIDER_ITER_BUCKETS; + int step = whereInCycle(0) / SPIDER_ITER_BUCKETS; + int step2 = whereInCycle(SPIDER_ITER_BUCKETS / 2) / SPIDER_ITER_BUCKETS; float darkest = getDarkestShadow(); - for(int L = 0; L < LEG_COUNT; L++){ - int index = L*13+step*2+1; - int index2 = L*13+step2*2; - float threshold = genome[L*13+12]; - if(darkest < threshold){ - shouldBeGreen[L*13+12] = true; + for (int L = 0; L < LEG_COUNT; L++) { + int index = L * 13 + step * 2 + 1; + int index2 = L * 13 + step2 * 2; + float threshold = genome[L * 13 + 12]; + if (darkest < threshold) { + shouldBeGreen[L * 13 + 12] = true; index += 6; index2 += 6; } shouldBeGreen[index] = true; shouldBeGreen[index2] = true; } - for(int g = 0; g < GENOME_LENGTH; g++){ - panel.fill(genome[g]*255); + for (int g = 0; g < GENOME_LENGTH; g++) { + panel.fill(genome[g] * 255); panel.stroke(0); panel.strokeWeight(1); - if(shouldBeGreen[g]){ - panel.stroke(0,255,0); + if (shouldBeGreen[g]) { + panel.stroke(0, 255, 0); panel.strokeWeight(2); } - float g1 = g%GENES_PER_LEG; - float g2 = g/GENES_PER_LEG; + float g1 = g % GENES_PER_LEG; + float g2 = g / GENES_PER_LEG; float x; float y; - if(g1 == 12){ + if (g1 == 12) { x = 185; y = 170; - }else{ - x = 20*g1+10; - y = 180-(g1%2)*40+10; - if(g1 >= 6){ + } else { + x = 20 * g1 + 10; + y = 180 - (g1 % 2) * 40 + 10; + if (g1 >= 6) { x += 130; } } - y += 100*g2; - panel.rect(x,y,30,30); + y += 100 * g2; + panel.rect(x, y, 30, 30); } panel.fill(255); panel.textAlign(LEFT); panel.textSize(30); - panel.text("Spider #"+commafy(visIndex+1),20,40); - panel.text("Generation #"+commafy(generation+1),20,70); - panel.text("Age: "+nf(ticksToDays(getAge()),0,2)+" days",20,100); + panel.text("Spider #" + commafy(visIndex + 1), 20, 40); + panel.text("Generation #" + commafy(generation + 1), 20, 70); + panel.text("Age: " + nf(ticksToDays(getAge()), 0, 2) + " days", 20, 100); String p = (howManySwattersSeen() == 1) ? "" : "s"; - panel.text(commafy(howManySwattersSeen())+" swatter"+p+" seen",80,130); + panel.text(commafy(howManySwattersSeen()) + " swatter" + p + " seen", 80, 130); panel.fill(getColor()); panel.stroke(0); - panel.rect(20,109,50,25); + panel.rect(20, 109, 50, 25); panel.endDraw(); return panel; } - int howManySwattersSeen(){ + + int howManySwattersSeen() { return swattersSeen.size(); } - void randomShift(){ - float dsx = random(-MAX_LEG_SPAN/2,MAX_LEG_SPAN/2); - float dsy = random(-MAX_LEG_SPAN/2,MAX_LEG_SPAN/2); - - float dangle = random(0.1*PI,1.9*PI); - for(int L = 0; L < LEG_COUNT; L++){ - float dx = leg_coor[L][0]-coor[0]; - float dy = leg_coor[L][1]-coor[1]; - float dist_ = dist(0,0,dx,dy); - float angle = atan2(dy,dx); - float newAngle = angle+dangle; - float newDX = cos(newAngle)*dist_; - float newDY = sin(newAngle)*dist_; - leg_coor[L][0] = coor[0]+newDX+dsx; - leg_coor[L][1] = coor[1]+newDY+dsy; + + void randomShift() { + float dsx = random(-MAX_LEG_SPAN / 2, MAX_LEG_SPAN / 2); + float dsy = random(-MAX_LEG_SPAN / 2, MAX_LEG_SPAN / 2); + float dangle = random(0.1f * PI, 1.9f * PI); + for (int L = 0; L < LEG_COUNT; L++) { + float dx = leg_coor[L][0] - coor[0]; + float dy = leg_coor[L][1] - coor[1]; + float dist_ = dist(0, 0, dx, dy); + float angle = atan2(dy, dx); + float newAngle = angle + dangle; + float newDX = cos(newAngle) * dist_; + float newDY = sin(newAngle) * dist_; + leg_coor[L][0] = coor[0] + newDX + dsx; + leg_coor[L][1] = coor[1] + newDY + dsy; } coor[0] += dsx; coor[1] += dsy; } - void reincarnate(ArrayList spiders){ + + void reincarnate(ArrayList spiders) { dailyDeaths++; - parent = spiders.get((int)random(0,spiders.size())); - float MUTATION_FACTOR = 0.2; + parent = spiders.get((int) random(0, spiders.size())); + float MUTATION_FACTOR = 0.2f; genome = mutate(parent.genome, MUTATION_FACTOR); coor = deepCopy(parent.coor); leg_coor = deepCopy(parent.leg_coor); randomShift(); - generation = parent.generation+1; + generation = parent.generation + 1; increment_(); } - int getAge(){ - return ticks-birth_tick; + + int getAge() { + return ticks - birth_tick; } - void increment_(){ + + void increment_() { visIndex = totalIndex; totalIndex++; birth_tick = ticks; - swattersSeen = new ArrayList(0); + swattersSeen = new HashSet<>(); } - float getSensitivity(){ + + float getSensitivity() { float sensitivity = 0; - for(int L = 0; L < LEG_COUNT; L++){ - for(int k = 0; k < 6; k++){ - sensitivity += abs(genome[L*13+k]-genome[L*13+6+k]); + for (int L = 0; L < LEG_COUNT; L++) { + for (int k = 0; k < 6; k++) { + sensitivity += abs(genome[L * 13 + k] - genome[L * 13 + 6 + k]); } } - return sensitivity/LEG_COUNT/6*100; + return sensitivity / LEG_COUNT / 6 * 100; } - void writeData(Float[] datum){ + + void writeData(Float[] datum) { datum[0] += ticksToDays(getAge()); datum[3] += getSensitivity(); datum[5] += swattersSeen.size();