Batch animation frames and buffer status text

This commit is contained in:
2026-08-09 18:40:45 +02:00
parent 2e63f9cbbd
commit dd5f691cab
2 changed files with 14 additions and 7 deletions
+5 -2
View File
@@ -9,8 +9,11 @@ Controls: **Pause/Resume**, **Redo**, **Next**, **Run All 8**, and
**Algorithm Menu**. **No Visual** reruns the selected algorithm without any **Algorithm Menu**. **No Visual** reruns the selected algorithm without any
animation, touch polling, or intentional delay during the timed section and animation, touch polling, or intentional delay during the timed section and
reports its duration with microsecond-derived precision. The bar animation is reports its duration with microsecond-derived precision. The bar animation is
rendered through a PSRAM-backed canvas for tear-free updates. Build with rendered through a PSRAM-backed canvas for tear-free updates. Animation work is
`pio run`, upload with `pio run -t upload`. batched (32 sort operations per candidate frame), the display is capped near
60 FPS, and the independently buffered counter strip updates at 4 Hz so its
rapidly changing digits remain readable without flicker. Build with `pio run`,
upload with `pio run -t upload`.
**All Visual** runs the eight faster animations and then opens a comparison **All Visual** runs the eight faster animations and then opens a comparison
report. **All Fast** benchmarks all eight without intermediate drawing and report. **All Fast** benchmarks all eight without intermediate drawing and
+9 -5
View File
@@ -3,6 +3,8 @@
namespace { namespace {
constexpr int N=48, CX=34, CY=142, CW=1212, CH=410; constexpr int N=48, CX=34, CY=142, CW=1212, CH=410;
constexpr int STATUS_Y=562, STATUS_H=46, OPS_PER_FRAME=32;
constexpr uint32_t FRAME_MS=16, STATUS_MS=250;
constexpr uint16_t BG=0x0841,PANEL=0x18E3,PANEL2=0x2125,MUTED=0xBDF7,CYAN=0x2E9F,YELLOW=0xFDC0,GREEN=0x4EE9; constexpr uint16_t BG=0x0841,PANEL=0x18E3,PANEL2=0x2125,MUTED=0xBDF7,CYAN=0x2E9F,YELLOW=0xFDC0,GREEN=0x4EE9;
struct Rect{int x,y,w,h;bool has(int px,int py)const{return px>=x&&px<x+w&&py>=y&&py<y+h;}}; struct Rect{int x,y,w,h;bool has(int px,int py)const{return px>=x&&px<x+w&&py>=y&&py<y+h;}};
struct Algo{const char* name;const char* lesson;const char* cost;}; struct Algo{const char* name;const char* lesson;const char* cost;};
@@ -17,18 +19,20 @@ const Algo alg[8]={
{"RADIX SORT","Bucket by ones, tens, then hundreds","O(n)"}}; {"RADIX SORT","Bucket by ones, tens, then hundreds","O(n)"}};
struct Result{uint32_t cmp=0,moves=0,us=0;bool ready=false;} results[8]; struct Result{uint32_t cmp=0,moves=0,us=0;bool ready=false;} results[8];
int a[N],base[N],tmp[N],which=0,hotA=-1,hotB=-1; int a[N],base[N],tmp[N],which=0,hotA=-1,hotB=-1;
uint32_t cmp=0,moves=0,startMs=0,startUs=0,pauseStart=0,pauseTotal=0,lastFrame=0; uint32_t cmp=0,moves=0,startMs=0,startUs=0,pauseStart=0,pauseTotal=0,lastFrame=0,lastStatus=0,animationOps=0;
bool paused=false,abortRun=false,redo=false,runAll=false,benchmark=false; bool paused=false,abortRun=false,redo=false,runAll=false,benchmark=false;
M5Canvas barCanvas(&M5.Display); M5Canvas barCanvas(&M5.Display);
M5Canvas statusCanvas(&M5.Display);
const Rect pauseB{18,620,150,70},redoB{178,620,120,70},nextB{308,620,120,70},benchB{438,620,180,70},allB{628,620,170,70},fastAllB{808,620,170,70},menuB{988,620,274,70}; const Rect pauseB{18,620,150,70},redoB{178,620,120,70},nextB{308,620,120,70},benchB{438,620,180,70},allB{628,620,170,70},fastAllB{808,620,170,70},menuB{988,620,274,70};
void label(const char*s,int x,int y,textdatum_t d,const lgfx::IFont*f,uint16_t c=TFT_WHITE,uint16_t bg=BG){M5.Display.setFont(f);M5.Display.setTextDatum(d);M5.Display.setTextColor(c,bg);M5.Display.drawString(s,x,y);} void label(const char*s,int x,int y,textdatum_t d,const lgfx::IFont*f,uint16_t c=TFT_WHITE,uint16_t bg=BG){M5.Display.setFont(f);M5.Display.setTextDatum(d);M5.Display.setTextColor(c,bg);M5.Display.drawString(s,x,y);}
void button(Rect r,const char*s,uint16_t c){M5.Display.fillRoundRect(r.x,r.y,r.w,r.h,14,c);M5.Display.drawRoundRect(r.x,r.y,r.w,r.h,14,TFT_WHITE);M5.Display.setFont(&fonts::FreeSansBold9pt7b);M5.Display.setTextDatum(middle_center);M5.Display.setTextColor(c==YELLOW?BG:TFT_WHITE);M5.Display.drawString(s,r.x+r.w/2,r.y+r.h/2);} void button(Rect r,const char*s,uint16_t c){M5.Display.fillRoundRect(r.x,r.y,r.w,r.h,14,c);M5.Display.drawRoundRect(r.x,r.y,r.w,r.h,14,TFT_WHITE);M5.Display.setFont(&fonts::FreeSansBold9pt7b);M5.Display.setTextDatum(middle_center);M5.Display.setTextColor(c==YELLOW?BG:TFT_WHITE);M5.Display.drawString(s,r.x+r.w/2,r.y+r.h/2);}
void buttons(){button(pauseB,paused?"RESUME":"PAUSE",paused?GREEN:0x39A7);button(redoB,"REDO",0x620B);button(nextB,"NEXT",0x2B8E);button(benchB,"NO VISUAL",YELLOW);button(allB,"ALL VISUAL",0x4A2E);button(fastAllB,"ALL FAST",0x6A2E);button(menuB,"ALGORITHM MENU",0x31A6);} void buttons(){button(pauseB,paused?"RESUME":"PAUSE",paused?GREEN:0x39A7);button(redoB,"REDO",0x620B);button(nextB,"NEXT",0x2B8E);button(benchB,"NO VISUAL",YELLOW);button(allB,"ALL VISUAL",0x4A2E);button(fastAllB,"ALL FAST",0x6A2E);button(menuB,"ALGORITHM MENU",0x31A6);}
void header(){M5.Display.fillRect(0,0,1280,112,PANEL);M5.Display.fillRect(0,108,1280,4,CYAN);label(alg[which].name,32,40,middle_left,&fonts::FreeSansBold24pt7b);label(alg[which].cost,1246,36,middle_right,&fonts::FreeSansBold18pt7b,YELLOW,PANEL);label(alg[which].lesson,34,88,middle_left,&fonts::FreeSans12pt7b,MUTED,PANEL);} void header(){M5.Display.fillRect(0,0,1280,112,PANEL);M5.Display.fillRect(0,108,1280,4,CYAN);label(alg[which].name,32,40,middle_left,&fonts::FreeSansBold24pt7b);label(alg[which].cost,1246,36,middle_right,&fonts::FreeSansBold18pt7b,YELLOW,PANEL);label(alg[which].lesson,34,88,middle_left,&fonts::FreeSans12pt7b,MUTED,PANEL);}
void chart(bool force=false){uint32_t now=millis();if(!force&&now-lastFrame<16)return;lastFrame=now;barCanvas.fillSprite(BG);int gap=3,bw=(CW-(N-1)*gap)/N;for(int i=0;i<N;i++){int h=map(a[i],1,N,12,CH-18);uint16_t c=(i==hotA||i==hotB)?YELLOW:CYAN;barCanvas.fillRoundRect(i*(bw+gap),CH-h,bw,h,min(4,bw/2),c);}barCanvas.pushSprite(CX,CY);char s[110];snprintf(s,sizeof(s),"COMPARISONS %lu MOVES %lu %s",(unsigned long)cmp,(unsigned long)moves,benchmark?"BENCHMARK: DISPLAY OFF":(paused?"PAUSED":"SORTING..."));M5.Display.fillRect(34,562,1212,46,BG);label(s,34,585,middle_left,&fonts::FreeSans12pt7b,MUTED);} void status(bool force=false){uint32_t now=millis();if(!force&&now-lastStatus<STATUS_MS)return;lastStatus=now;char s[110];snprintf(s,sizeof(s),"COMPARISONS %lu MOVES %lu %s",(unsigned long)cmp,(unsigned long)moves,benchmark?"BENCHMARK: DISPLAY OFF":(paused?"PAUSED":"SORTING..."));statusCanvas.fillSprite(BG);statusCanvas.setFont(&fonts::FreeSans12pt7b);statusCanvas.setTextDatum(middle_left);statusCanvas.setTextColor(MUTED);statusCanvas.drawString(s,0,STATUS_H/2);statusCanvas.pushSprite(CX,STATUS_Y);}
void chart(bool force=false){uint32_t now=millis();if(!force&&now-lastFrame<FRAME_MS)return;lastFrame=now;barCanvas.fillSprite(BG);int gap=3,bw=(CW-(N-1)*gap)/N;for(int i=0;i<N;i++){int h=map(a[i],1,N,12,CH-18);uint16_t c=(i==hotA||i==hotB)?YELLOW:CYAN;barCanvas.fillRoundRect(i*(bw+gap),CH-h,bw,h,min(4,bw/2),c);}barCanvas.pushSprite(CX,CY);status(force);}
void touch(){M5.update();auto t=M5.Touch.getDetail();if(!t.wasReleased())return;if(pauseB.has(t.x,t.y)){paused=!paused;if(paused)pauseStart=millis();else pauseTotal+=millis()-pauseStart;buttons();}else if(redoB.has(t.x,t.y)){redo=true;abortRun=true;paused=false;}else if(nextB.has(t.x,t.y)){which=(which+1)%8;abortRun=true;paused=false;runAll=false;benchmark=false;}else if(allB.has(t.x,t.y)){which=0;redo=true;abortRun=true;paused=false;runAll=true;benchmark=false;for(auto&r:results)r=Result{};}else if(fastAllB.has(t.x,t.y)){which=0;redo=true;abortRun=true;paused=false;runAll=true;benchmark=true;for(auto&r:results)r=Result{};}else if(benchB.has(t.x,t.y)){benchmark=true;redo=true;abortRun=true;paused=false;runAll=false;}else if(menuB.has(t.x,t.y)){abortRun=true;paused=false;runAll=false;benchmark=false;}} void touch(){M5.update();auto t=M5.Touch.getDetail();if(!t.wasReleased())return;if(pauseB.has(t.x,t.y)){paused=!paused;if(paused)pauseStart=millis();else pauseTotal+=millis()-pauseStart;buttons();}else if(redoB.has(t.x,t.y)){redo=true;abortRun=true;paused=false;}else if(nextB.has(t.x,t.y)){which=(which+1)%8;abortRun=true;paused=false;runAll=false;benchmark=false;}else if(allB.has(t.x,t.y)){which=0;redo=true;abortRun=true;paused=false;runAll=true;benchmark=false;for(auto&r:results)r=Result{};}else if(fastAllB.has(t.x,t.y)){which=0;redo=true;abortRun=true;paused=false;runAll=true;benchmark=true;for(auto&r:results)r=Result{};}else if(benchB.has(t.x,t.y)){benchmark=true;redo=true;abortRun=true;paused=false;runAll=false;}else if(menuB.has(t.x,t.y)){abortRun=true;paused=false;runAll=false;benchmark=false;}}
bool frame(int x=-1,int y=-1,int ms=2){if(benchmark)return true;hotA=x;hotB=y;chart();uint32_t end=millis()+ms;do{touch();while(paused&&!abortRun){touch();chart(true);delay(12);}if(abortRun)return false;delay(1);}while((int32_t)(end-millis())>0);return true;} bool frame(int x=-1,int y=-1,int ms=0){(void)ms;if(benchmark)return true;hotA=x;hotB=y;if(++animationOps%OPS_PER_FRAME!=0)return true;chart();touch();while(paused&&!abortRun){touch();status();delay(12);}return !abortRun;}
void swapv(int x,int y){if(x==y)return;int v=a[x];a[x]=a[y];a[y]=v;moves+=3;}void writev(int i,int v){a[i]=v;moves++;} void swapv(int x,int y){if(x==y)return;int v=a[x];a[x]=a[y];a[y]=v;moves+=3;}void writev(int i,int v){a[i]=v;moves++;}
bool bubble(){for(int end=N-1;end>0;end--){bool changed=false;for(int i=0;i<end;i++){cmp++;if(a[i]>a[i+1]){swapv(i,i+1);changed=true;}if(!frame(i,i+1))return false;}if(!changed)break;}return true;} bool bubble(){for(int end=N-1;end>0;end--){bool changed=false;for(int i=0;i<end;i++){cmp++;if(a[i]>a[i+1]){swapv(i,i+1);changed=true;}if(!frame(i,i+1))return false;}if(!changed)break;}return true;}
@@ -42,7 +46,7 @@ bool heapify(int size,int root){while(true){int big=root,l=root*2+1,r=l+1;if(l<s
bool heap(){for(int i=N/2-1;i>=0;i--)if(!heapify(N,i))return false;for(int e=N-1;e>0;e--){swapv(0,e);if(!frame(0,e))return false;if(!heapify(e,0))return false;}return true;} bool heap(){for(int i=N/2-1;i>=0;i--)if(!heapify(N,i))return false;for(int e=N-1;e>0;e--){swapv(0,e);if(!frame(0,e))return false;if(!heapify(e,0))return false;}return true;}
bool radix(){for(int exp=1;N/exp>0;exp*=10){int count[10]={0};for(int i=0;i<N;i++)count[(a[i]/exp)%10]++;for(int i=1;i<10;i++)count[i]+=count[i-1];for(int i=N-1;i>=0;i--){tmp[--count[(a[i]/exp)%10]]=a[i];moves++;}for(int i=0;i<N;i++){writev(i,tmp[i]);if(!frame(i,-1,3))return false;}}return true;} bool radix(){for(int exp=1;N/exp>0;exp*=10){int count[10]={0};for(int i=0;i<N;i++)count[(a[i]/exp)%10]++;for(int i=1;i<10;i++)count[i]+=count[i-1];for(int i=N-1;i>=0;i--){tmp[--count[(a[i]/exp)%10]]=a[i];moves++;}for(int i=0;i<N;i++){writev(i,tmp[i]);if(!frame(i,-1,3))return false;}}return true;}
bool sortNow(){switch(which){case 0:return bubble();case 1:return insertion();case 2:return selection();case 3:return shell();case 4:return mergeSort(0,N-1);case 5:return quick(0,N-1);case 6:return heap();default:return radix();}} bool sortNow(){switch(which){case 0:return bubble();case 1:return insertion();case 2:return selection();case 3:return shell();case 4:return mergeSort(0,N-1);case 5:return quick(0,N-1);case 6:return heap();default:return radix();}}
void reset(){for(int i=0;i<N;i++)a[i]=base[i];cmp=moves=pauseTotal=0;paused=abortRun=redo=false;} void reset(){for(int i=0;i<N;i++)a[i]=base[i];cmp=moves=pauseTotal=animationOps=0;paused=abortRun=redo=false;}
void runScreen(){M5.Display.fillScreen(BG);header();buttons();chart(true);} void runScreen(){M5.Display.fillScreen(BG);header();buttons();chart(true);}
bool waitFor(uint32_t ms){uint32_t s=millis();while(millis()-s<ms){touch();if(abortRun)return false;delay(10);}return true;} bool waitFor(uint32_t ms){uint32_t s=millis();while(millis()-s<ms){touch();if(abortRun)return false;delay(10);}return true;}
void result(){hotA=hotB=-1;chart(true);delay(150);Rect c{246,205,788,300};M5.Display.fillRoundRect(c.x,c.y,c.w,c.h,24,PANEL);M5.Display.drawRoundRect(c.x,c.y,c.w,c.h,24,CYAN);label(benchmark?"BENCHMARK DONE!":"SORTED!",640,250,middle_center,&fonts::FreeSansBold24pt7b,GREEN,PANEL);const char* n[]={"COMPARISONS","MOVES","TIME"};for(int i=0;i<3;i++){label(n[i],430,330+i*58,middle_left,&fonts::FreeSansBold12pt7b,MUTED,PANEL);label("...",850,330+i*58,middle_right,&fonts::FreeSansBold18pt7b,YELLOW,PANEL);delay(benchmark?0:160);}char v[32];snprintf(v,sizeof(v),"%lu",(unsigned long)cmp);M5.Display.fillRect(650,305,240,42,PANEL);label(v,850,330,middle_right,&fonts::FreeSansBold18pt7b,YELLOW,PANEL);if(!benchmark)delay(200);snprintf(v,sizeof(v),"%lu",(unsigned long)moves);M5.Display.fillRect(650,363,240,42,PANEL);label(v,850,388,middle_right,&fonts::FreeSansBold18pt7b,YELLOW,PANEL);if(!benchmark)delay(200);snprintf(v,sizeof(v),"%.3f ms",results[which].us/1000.0f);M5.Display.fillRect(650,421,240,42,PANEL);label(v,850,446,middle_right,&fonts::FreeSansBold18pt7b,YELLOW,PANEL);} void result(){hotA=hotB=-1;chart(true);delay(150);Rect c{246,205,788,300};M5.Display.fillRoundRect(c.x,c.y,c.w,c.h,24,PANEL);M5.Display.drawRoundRect(c.x,c.y,c.w,c.h,24,CYAN);label(benchmark?"BENCHMARK DONE!":"SORTED!",640,250,middle_center,&fonts::FreeSansBold24pt7b,GREEN,PANEL);const char* n[]={"COMPARISONS","MOVES","TIME"};for(int i=0;i<3;i++){label(n[i],430,330+i*58,middle_left,&fonts::FreeSansBold12pt7b,MUTED,PANEL);label("...",850,330+i*58,middle_right,&fonts::FreeSansBold18pt7b,YELLOW,PANEL);delay(benchmark?0:160);}char v[32];snprintf(v,sizeof(v),"%lu",(unsigned long)cmp);M5.Display.fillRect(650,305,240,42,PANEL);label(v,850,330,middle_right,&fonts::FreeSansBold18pt7b,YELLOW,PANEL);if(!benchmark)delay(200);snprintf(v,sizeof(v),"%lu",(unsigned long)moves);M5.Display.fillRect(650,363,240,42,PANEL);label(v,850,388,middle_right,&fonts::FreeSansBold18pt7b,YELLOW,PANEL);if(!benchmark)delay(200);snprintf(v,sizeof(v),"%.3f ms",results[which].us/1000.0f);M5.Display.fillRect(650,421,240,42,PANEL);label(v,850,446,middle_right,&fonts::FreeSansBold18pt7b,YELLOW,PANEL);}
@@ -50,5 +54,5 @@ void summary(){M5.Display.fillScreen(BG);M5.Display.fillRect(0,0,1280,104,PANEL)
void shuffle(){for(int i=0;i<N;i++)base[i]=i+1;for(int i=N-1;i>0;i--){int j=random(i+1);int v=base[i];base[i]=base[j];base[j]=v;}} void shuffle(){for(int i=0;i<N;i++)base[i]=i+1;for(int i=N-1;i>0;i--){int j=random(i+1);int v=base[i];base[i]=base[j];base[j]=v;}}
void menu(){M5.Display.fillScreen(BG);M5.Display.fillRect(0,0,1280,102,PANEL);label("SORTING LAB",34,42,middle_left,&fonts::FreeSansBold24pt7b,TFT_WHITE,PANEL);label("Choose one, or compare all eight",1244,42,middle_right,&fonts::FreeSans12pt7b,MUTED,PANEL);for(int i=0;i<8;i++){int col=i%2,row=i/2;Rect r{34+col*614,126+row*112,596,94};uint16_t fill=i==which?0x2B8E:PANEL2;M5.Display.fillRoundRect(r.x,r.y,r.w,r.h,16,fill);M5.Display.drawRoundRect(r.x,r.y,r.w,r.h,16,0x31A6);label(alg[i].name,r.x+24,r.y+31,middle_left,&fonts::FreeSansBold12pt7b,TFT_WHITE,fill);label(alg[i].lesson,r.x+24,r.y+67,middle_left,&fonts::FreeSans9pt7b,MUTED,fill);label(alg[i].cost,r.x+r.w-18,r.y+31,middle_right,&fonts::FreeSansBold12pt7b,YELLOW,fill);}Rect sh{34,598,238,78},oneFast{288,598,278,78},all{582,598,316,78},allFast{914,598,332,78};button(sh,"NEW SHUFFLE",0x620B);button(oneFast,"SELECTED FAST",YELLOW);button(all,"ALL VISUAL",0x4A2E);button(allFast,"ALL FAST",0x6A2E);while(true){M5.update();auto t=M5.Touch.getDetail();if(t.wasReleased()){if(sh.has(t.x,t.y)){shuffle();menu();return;}if(oneFast.has(t.x,t.y)){benchmark=true;runAll=false;return;}if(all.has(t.x,t.y)){which=0;runAll=true;benchmark=false;for(auto&r:results)r=Result{};return;}if(allFast.has(t.x,t.y)){which=0;runAll=true;benchmark=true;for(auto&r:results)r=Result{};return;}for(int i=0;i<8;i++){Rect r{34+(i%2)*614,126+(i/2)*112,596,94};if(r.has(t.x,t.y)){which=i;runAll=false;benchmark=false;return;}}}delay(12);}} void menu(){M5.Display.fillScreen(BG);M5.Display.fillRect(0,0,1280,102,PANEL);label("SORTING LAB",34,42,middle_left,&fonts::FreeSansBold24pt7b,TFT_WHITE,PANEL);label("Choose one, or compare all eight",1244,42,middle_right,&fonts::FreeSans12pt7b,MUTED,PANEL);for(int i=0;i<8;i++){int col=i%2,row=i/2;Rect r{34+col*614,126+row*112,596,94};uint16_t fill=i==which?0x2B8E:PANEL2;M5.Display.fillRoundRect(r.x,r.y,r.w,r.h,16,fill);M5.Display.drawRoundRect(r.x,r.y,r.w,r.h,16,0x31A6);label(alg[i].name,r.x+24,r.y+31,middle_left,&fonts::FreeSansBold12pt7b,TFT_WHITE,fill);label(alg[i].lesson,r.x+24,r.y+67,middle_left,&fonts::FreeSans9pt7b,MUTED,fill);label(alg[i].cost,r.x+r.w-18,r.y+31,middle_right,&fonts::FreeSansBold12pt7b,YELLOW,fill);}Rect sh{34,598,238,78},oneFast{288,598,278,78},all{582,598,316,78},allFast{914,598,332,78};button(sh,"NEW SHUFFLE",0x620B);button(oneFast,"SELECTED FAST",YELLOW);button(all,"ALL VISUAL",0x4A2E);button(allFast,"ALL FAST",0x6A2E);while(true){M5.update();auto t=M5.Touch.getDetail();if(t.wasReleased()){if(sh.has(t.x,t.y)){shuffle();menu();return;}if(oneFast.has(t.x,t.y)){benchmark=true;runAll=false;return;}if(all.has(t.x,t.y)){which=0;runAll=true;benchmark=false;for(auto&r:results)r=Result{};return;}if(allFast.has(t.x,t.y)){which=0;runAll=true;benchmark=true;for(auto&r:results)r=Result{};return;}for(int i=0;i<8;i++){Rect r{34+(i%2)*614,126+(i/2)*112,596,94};if(r.has(t.x,t.y)){which=i;runAll=false;benchmark=false;return;}}}delay(12);}}
} }
void setup(){Serial.begin(115200);auto cfg=M5.config();M5.begin(cfg);M5.Display.setRotation(1);M5.Display.setColorDepth(16);M5.Display.setBrightness(180);barCanvas.setColorDepth(16);barCanvas.setPsram(true);barCanvas.createSprite(CW,CH);randomSeed(esp_random());shuffle();Serial.println("Tab5 Sorting Lab ready");} void setup(){Serial.begin(115200);auto cfg=M5.config();M5.begin(cfg);M5.Display.setRotation(1);M5.Display.setColorDepth(16);M5.Display.setBrightness(180);barCanvas.setColorDepth(16);barCanvas.setPsram(true);barCanvas.createSprite(CW,CH);statusCanvas.setColorDepth(16);statusCanvas.setPsram(true);statusCanvas.createSprite(CW,STATUS_H);randomSeed(esp_random());shuffle();Serial.println("Tab5 Sorting Lab ready");}
void loop(){menu();do{reset();runScreen();startMs=millis();startUs=micros();bool done=sortNow();if(done){uint32_t elapsedUs=benchmark?(micros()-startUs):(millis()-startMs-pauseTotal)*1000UL;results[which]={cmp,moves,elapsedUs,true};Serial.printf("%s: %lu comparisons, %lu moves, %.3f ms%s\n",alg[which].name,(unsigned long)cmp,(unsigned long)moves,elapsedUs/1000.0f,benchmark?" (no visual)":"");if(runAll&&benchmark){if(which<7){which++;continue;}summary();runAll=false;break;}result();if(runAll){if(waitFor(700)&&which<7){which++;continue;}if(which==7){summary();runAll=false;}break;}waitFor(10000);break;}bool again=redo;abortRun=false;if(again)continue;break;}while(true);} void loop(){menu();do{reset();runScreen();startMs=millis();startUs=micros();bool done=sortNow();if(done){uint32_t elapsedUs=benchmark?(micros()-startUs):(millis()-startMs-pauseTotal)*1000UL;results[which]={cmp,moves,elapsedUs,true};Serial.printf("%s: %lu comparisons, %lu moves, %.3f ms%s\n",alg[which].name,(unsigned long)cmp,(unsigned long)moves,elapsedUs/1000.0f,benchmark?" (no visual)":"");if(runAll&&benchmark){if(which<7){which++;continue;}summary();runAll=false;break;}result();if(runAll){if(waitFor(700)&&which<7){which++;continue;}if(which==7){summary();runAll=false;}break;}waitFor(10000);break;}bool again=redo;abortRun=false;if(again)continue;break;}while(true);}