// SPDX-License-Identifier: MIT // Copyright (c) 2026 Incoprea // Three-by-three sandpile // Not compiled or tested in-world // Setup: Enter1-9 to add a grain, or step to topple each eligible cell once. Owner-touch initializes; enter reset to restart. Text input closes after90seconds. // Stop / restore: Close or ignore the text box, or remove the script. It changes no world objects, avatar state or inventory. // Notes: Original bounded, manually stepped educational toy. Not a calibrated simulator, real-world control system, or live scientific measurement. Sequential toppling pass; repeat step until stable. Grains leaving the finite boundary are counted and lost. // License: see LICENSE in this distribution. // Original Incoprea. MIT. Not compiled or tested in-world. integer channel; integer handle; list piles; integer lost; close() { if(handle)llListenRemove(handle); handle=0; llSetTimerEvent(0.0); } start() { piles=[0,0,0,0,0,0,0,0,0]; lost=0; } string view() { return "Rows: "+llList2CSV(llList2List(piles,0,2))+" / "+llList2CSV(llList2List(piles,3,5))+" / "+llList2CSV(llList2List(piles,6,8))+" | boundary loss "+(string)lost; } show() { llOwnerSay(view()); if(!handle) { channel=-1000-(integer)llFrand(1000000.0); handle=llListen(channel,"",llGetOwner(),""); } llTextBox(llGetOwner(),"Enter1-9 to add a grain, or step to topple each eligible cell once. Type reset to restart.",channel); llSetTimerEvent(90.0); } default { touch_start(integer n) { if(llDetectedKey(0)!=llGetOwner())return; close(); start(); show(); } listen(integer incomingChannel,string name,key id,string s) { if(!handle||incomingChannel!=channel||id!=llGetOwner())return; s=llToLower(llStringTrim(s,STRING_TRIM)); if(s=="reset")start(); else { if(s=="step") { integer i; for(i=0;i<9;++i)if(llList2Integer(piles,i)>=4) { piles=llListReplaceList(piles,[llList2Integer(piles,i)-4],i,i); list neighbors=[]; if(i%3>0)neighbors+=[i-1]; else ++lost; if(i%3<2)neighbors+=[i+1]; else ++lost; if(i/3>0)neighbors+=[i-3]; else ++lost; if(i/3<2)neighbors+=[i+3]; else ++lost; integer j; for(j=0;j=0&&cell<9&&(string)(cell+1)==s&&llList2Integer(piles,cell)<99)piles=llListReplaceList(piles,[llList2Integer(piles,cell)+1],cell,cell); } } show(); } timer() { close(); } }