carverjs
Version:
A React library for AI-generated interactive games with reinforcement learning support
7 lines • 98.4 kB
JavaScript
import*as te from'react';import {useState,useRef,useCallback,useMemo,useEffect}from'react';import {AnimatePresence,motion}from'framer-motion';import {Clock,Color,WebGLRenderer,Scene,OrthographicCamera,Group}from'three';import {jsxs,jsx}from'react/jsx-runtime';var be={x:0,y:0},xe={x:0,y:0,z:0},Wt={x:1,y:0},Ht={x:0,y:1},$t={x:1,y:0,z:0},Yt={x:0,y:1,z:0},jt={x:0,y:0,z:1},it=be,ot=xe,ke={position:it,rotation:{angle:0},scale:{x:1,y:1}},Xt={position:ot,rotation:{x:0,y:0,z:0},scale:{x:1,y:1,z:1}},Zt={x:0,y:0,z:0,w:1},Kt={"top-left":{x:0,y:0},"top-center":{x:.5,y:0},"top-right":{x:1,y:0},"middle-left":{x:0,y:.5},"middle-center":{x:.5,y:.5},"middle-right":{x:1,y:.5},"bottom-left":{x:0,y:1},"bottom-center":{x:.5,y:1},"bottom-right":{x:1,y:1}},Me={up:{x:0,y:-1},down:{x:0,y:1},left:{x:-1,y:0},right:{x:1,y:0},forward:{x:0,y:-1},back:{x:0,y:1}},Jt={up:{x:0,y:1,z:0},down:{x:0,y:-1,z:0},left:{x:-1,y:0,z:0},right:{x:1,y:0,z:0},forward:{x:0,y:0,z:-1},back:{x:0,y:0,z:1},in:{x:0,y:0,z:-1},out:{x:0,y:0,z:1}},en={DEGREES_TO_RADIANS:Math.PI/180,RADIANS_TO_DEGREES:180/Math.PI,PI:Math.PI,TWO_PI:Math.PI*2,HALF_PI:Math.PI/2,QUARTER_PI:Math.PI/4};function Z(t){return typeof t=="object"&&t!==null&&typeof t.x=="number"&&typeof t.y=="number"}function Ne(t){return typeof t=="object"&&t!==null&&typeof t.x=="number"&&typeof t.y=="number"&&typeof t.z=="number"}function tn(t){return typeof t=="object"&&t!==null&&typeof t.x=="number"&&typeof t.y=="number"&&typeof t.width=="number"&&typeof t.height=="number"}function nn(t){return typeof t=="object"&&t!==null&&Z(t.position)&&typeof t.rotation=="object"&&typeof t.scale=="object"}function rn(t){return typeof t=="object"&&t!==null&&Z(t.min)&&Z(t.max)}var an="1.0.0",on="2025-01-25";var st={minScenes:3,maxScenes:20,estimatedTimePerScene:4,requiredElements:[],optionalElements:[],supportedAudiences:["beginner","intermediate","advanced"],version:"1.0.0"},lt={maxScenes:15,maxPlayTime:30,mustIncludeAssessments:false,allowBranching:true,difficultyProgression:"linear",contentComplexity:"moderate",maxTokensPerRequest:4e3,generationTimeLimit:3e5},sn={estimatedDuration:3,difficulty:"medium",learningObjectives:[],prerequisites:[],tags:[]},ct={learningStyle:"mixed",pace:"moderate",tone:"casual",language:"en"},ln={contentCoverage:0,engagementScore:0,educationalEffectiveness:0,flowCoherence:0,technicalQuality:0,accessibilityScore:0,overallScore:0},cn=["choice","input","click","drag","quiz","simulation","mini-game","gesture","voice","custom"],dn=["image","video","audio","animation","interactive","3d-model","particle-system"],dt=["narrative","simulation","puzzle","rpg","quiz","adventure","strategy","arcade","educational","sandbox","platformer","action"],yt=["text","markdown","html","video","audio","image","interactive","simulation","assessment"],ut=["easy","medium","hard","expert"],mt=["beginner","intermediate","advanced","expert"],pt=["image","audio","video","font","texture","3d-model","animation","shader","data","script","style","manifest"],yn=["web","mobile","desktop","console","vr","ar"],un=["keyboard","mouse","touch","gamepad","voice","gesture","eye-tracking","motion-controller"],ve={POOR:.3,FAIR:.5,GOOD:.7,EXCELLENT:.9},mn={LOADING_TIME_MS:3e3,MEMORY_USAGE_MB:100,FRAME_RATE_FPS:60,ASSET_SIZE_LIMIT_MB:50},pn=["WCAG-2.1-A","WCAG-2.1-AA","WCAG-2.1-AAA","Section-508","EN-301-549"],gn=["linear","ease","ease-in","ease-out","ease-in-out","cubic-bezier","spring","bounce"],fn=["fade","slide","zoom","flip","dissolve","wipe","custom"],hn=["ESRB","PEGI","CERO","USK","OFLC","custom"];function bn(t){return typeof t=="object"&&t!==null&&typeof t.id=="string"&&typeof t.name=="string"&&typeof t.category=="string"&&typeof t.minScenes=="number"&&typeof t.maxScenes=="number"&&Array.isArray(t.requiredElements)&&Array.isArray(t.optionalElements)&&Array.isArray(t.supportedAudiences)}function xn(t){return typeof t=="object"&&t!==null&&typeof t.contentId=="string"&&typeof t.gameType=="string"&&typeof t.targetAudience=="string"&&typeof t.learningContent=="object"&&typeof t.constraints=="object"}function vn(t){return typeof t=="object"&&t!==null&&typeof t.id=="string"&&typeof t.gameType=="string"&&typeof t.title=="string"&&Array.isArray(t.scenes)&&typeof t.flow=="object"&&typeof t.metadata=="object"}function Sn(t){return typeof t=="object"&&t!==null&&typeof t.id=="string"&&typeof t.type=="string"&&typeof t.title=="string"&&typeof t.description=="string"&&typeof t.content=="object"&&Array.isArray(t.interactions)&&Array.isArray(t.transitions)&&(t.actions===void 0||Array.isArray(t.actions))&&typeof t.metadata=="object"}function Cn(t){return typeof t=="object"&&t!==null&&typeof t.contentId=="string"&&typeof t.title=="string"&&typeof t.description=="string"&&Array.isArray(t.modules)&&Array.isArray(t.chunks)}function Rn(t){return typeof t=="object"&&t!==null&&typeof t.isValid=="boolean"&&Array.isArray(t.errors)&&Array.isArray(t.warnings)}function Gn(t){return typeof t=="object"&&t!==null&&typeof t.startSceneId=="string"&&Array.isArray(t.endSceneIds)&&Array.isArray(t.paths)&&Array.isArray(t.breakpoints)&&Array.isArray(t.continuityElements)}function wn(t){return typeof t=="object"&&t!==null&&typeof t.id=="string"&&typeof t.type=="string"&&typeof t.prompt=="string"}function Tn(t){return typeof t=="object"&&t!==null&&typeof t.id=="string"&&typeof t.name=="string"&&typeof t.type=="string"&&typeof t.url=="string"}function An(t){return typeof t=="object"&&t!==null&&typeof t.contentCoverage=="number"&&typeof t.engagementScore=="number"&&typeof t.educationalEffectiveness=="number"&&typeof t.flowCoherence=="number"&&typeof t.technicalQuality=="number"&&typeof t.accessibilityScore=="number"&&typeof t.overallScore=="number"}function Ve(t,e,n){return {id:t,name:e,category:n,description:`Default configuration for ${e} game type`,...st}}function En(t){return {...lt,...t}}function Pn(t){return {...ct,...t}}function Se(t){return ut.includes(t)}function Dn(t){return mt.includes(t)}function In(t){return dt.includes(t)}function kn(t){return yt.includes(t)}function Mn(t){return pt.includes(t)}function Oe(t){let e={contentCoverage:.2,engagementScore:.2,educationalEffectiveness:.2,flowCoherence:.15,technicalQuality:.15,accessibilityScore:.1};return t.contentCoverage*e.contentCoverage+t.engagementScore*e.engagementScore+t.educationalEffectiveness*e.educationalEffectiveness+t.flowCoherence*e.flowCoherence+t.technicalQuality*e.technicalQuality+t.accessibilityScore*e.accessibilityScore}function Nn(t){return t<ve.POOR?"poor":t<ve.FAIR?"fair":t<ve.GOOD?"good":"excellent"}function Ln(t){return t.reduce((e,n)=>e+n.metadata.estimatedDuration,0)}function Vn(t){let e={easy:0,medium:0,hard:0,expert:0};return t.forEach(n=>{e[n.metadata.difficulty]++;}),e}function On(t,e){if(t.modules.length===0)return 0;let n=new Set;return e.forEach(r=>{r.metadata.relatedChunks?.forEach(a=>{let o=t.chunks.find(i=>i.id===a);o&&n.add(o.moduleId);});}),n.size/t.modules.length}function Ce(t){let e=Date.now().toString(36),n=Math.random().toString(36).substring(2,8);return t?`${t}-${e}-${n}`:`${e}-${n}`}function Le(t){if(t===null||typeof t!="object")return t;if(t instanceof Date)return new Date(t.getTime());if(t instanceof Array)return t.map(e=>Le(e));if(typeof t=="object"){let e={};for(let n in t)t.hasOwnProperty(n)&&(e[n]=Le(t[n]));return e}return t}function gt(t,e){let n={...t};for(let r in e)if(e.hasOwnProperty(r)){let a=e[r],o=n[r];a&&o&&typeof a=="object"&&typeof o=="object"&&!Array.isArray(a)&&!Array.isArray(o)?n[r]=gt(o,a):n[r]=a;}return n}function Bn(t){return t.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").substring(0,50)}function _n(t){if(t<1)return "< 1 min";if(t<60)return `${Math.round(t)} min`;let e=Math.floor(t/60),n=Math.round(t%60);return n===0?`${e}h`:`${e}h ${n}m`}function zn(t){if(t.length<2)return 1;let e={easy:1,medium:2,hard:3,expert:4},n=0;for(let r=1;r<t.length;r++){let a=e[t[r-1].metadata.difficulty],i=e[t[r].metadata.difficulty]-a;i>=0&&i<=1?n+=1:i<0?n+=.5:n+=.2;}return n/(t.length-1)}var Fn="1.0.0",Un="2025-01-25",Qn=["Game Development Community"],qn={"1.0.0":{backwardCompatible:[],forwardCompatible:[],breaking:[]}};var T={create:(t,e)=>({x:t,y:e}),add:(t,e)=>({x:t.x+e.x,y:t.y+e.y}),subtract:(t,e)=>({x:t.x-e.x,y:t.y-e.y}),multiply:(t,e)=>({x:t.x*e,y:t.y*e}),distance:(t,e)=>Math.sqrt(Math.pow(t.x-e.x,2)+Math.pow(t.y-e.y,2)),equals:(t,e)=>t.x===e.x&&t.y===e.y,clone:t=>({x:t.x,y:t.y})},Re={up:{x:0,y:-1},down:{x:0,y:1},left:{x:-1,y:0},right:{x:1,y:0}},F={gridSize:32,animationSpeed:300,debugMode:false,maxEpisodes:1e3,frameRate:60,enableCollisions:true,theme:{},customConfig:{},tilesets:[],renderLayers:[],animations:{},webgl:{features:{}}},O={startPosition:{x:0,y:0},sprite:"",color:"#4CAF50",speed:1,health:1,inventorySize:10,metadata:{}},le=t=>typeof t=="string"?{type:t}:t,Wn=(t,e)=>({label:e||t.type,type:t.type,...t.metadata?.description?{description:t.metadata.description}:{},...t.metadata?.effect?{effect:t.metadata.effect}:{},...t.target&&{target:t.target},...t.payload!==void 0&&{value:t.payload},...t.metadata&&{metadata:t.metadata}}),Hn=(t,e)=>({label:t,type:e?.type||"custom",...e?.description&&{description:e.description},...e?.effect&&{effect:e.effect},...e?.target&&{target:e.target},...e?.value!==void 0&&{value:e.value},metadata:e?.metadata||{}}),$n={bounce:{initial:{y:20,opacity:0},animate:{y:0,opacity:1,transition:{type:"spring",stiffness:300,damping:20}},hover:{y:-2,transition:{type:"spring",stiffness:400}}},slide:{initial:{x:-20,opacity:0},animate:{x:0,opacity:1,transition:{type:"spring",stiffness:250,damping:25}},hover:{x:2,transition:{type:"spring",stiffness:400}}},fade:{initial:{opacity:0},animate:{opacity:1,transition:{duration:.3,ease:"easeOut"}},hover:{opacity:.8,transition:{duration:.2}}},zoom:{initial:{scale:.9,opacity:0},animate:{scale:1,opacity:1,transition:{type:"spring",stiffness:300,damping:25}},hover:{scale:1.05,transition:{type:"spring",stiffness:400}}},rotate:{initial:{rotate:-5,opacity:0},animate:{rotate:0,opacity:1,transition:{type:"spring",stiffness:300,damping:25}},hover:{rotate:1,transition:{type:"spring",stiffness:400}}},custom:{initial:{scale:1,opacity:0},animate:{scale:1,opacity:1},hover:{scale:1.02}}},ft=t=>({startPosition:t?.startPosition||O.startPosition,sprite:t?.sprite??O.sprite,color:t?.color||O.color,speed:t?.speed||O.speed,health:t?.health||O.health,inventorySize:t?.inventorySize||O.inventorySize,metadata:t?.metadata||O.metadata}),Yn=(t,e,n)=>({id:t,name:e,width:n?.width||10,height:n?.height||10,entities:n?.entities||[],player:ft(n?.player),objectives:n?.objectives||[],...n?.background&&{background:n.background},...n?.transitions&&{transitions:n.transitions},...n?.actions&&{actions:n.actions},...n?.config&&{config:n.config},...n?.metadata&&{metadata:n.metadata}}),jn=(t,e,n,r)=>({id:t,type:e,position:n,...r?.sprite&&{sprite:r.sprite},...r?.color&&{color:r.color},...r?.behavior&&{behavior:r.behavior},isCollidable:r?.isCollidable??true,isVisible:r?.isVisible??true,isActive:r?.isActive??true,...r?.metadata&&{metadata:r.metadata},...r?.zIndex!==void 0&&{zIndex:r.zIndex}});var ht=t=>t.actions?t.actions.filter(e=>!!e.nextSceneId).map(e=>({targetScene:e.nextSceneId,trigger:n=>n.metadata?.lastChoiceId===e.id,metadata:{source:"authored-action",actionId:e.id,sceneId:t.id,label:e.label,...e.rewards?{rewards:e.rewards}:{},...e.metadata?{actionMetadata:e.metadata}:{}}})):[],Be=t=>{let e=ht(t);return [...t.transitions?[...t.transitions]:[],...e]};var bt=Object.freeze({x:0,y:0}),b={clone:t=>({x:t.x,y:t.y}),add:(t,e)=>({x:t.x+e.x,y:t.y+e.y}),subtract:(t,e)=>({x:t.x-e.x,y:t.y-e.y}),multiplyScalar:(t,e)=>({x:t.x*e,y:t.y*e}),dot:(t,e)=>t.x*e.x+t.y*e.y,length:t=>Math.sqrt(t.x*t.x+t.y*t.y),normalize:t=>{let e=b.length(t);return e===0?{x:0,y:0}:{x:t.x/e,y:t.y/e}},perpendicular:t=>({x:-t.y,y:t.x})},_e=(t,e,n)=>Math.min(Math.max(t,e),n),ze=class t{constructor(e,n,r,a){this.items=[];this.divided=false;this.boundary=e,this.capacity=n,this.depth=r,this.maxDepth=a;}insert(e){return t.intersects(this.boundary,e.bounds)?this.items.length<this.capacity||this.depth>=this.maxDepth?(this.items.push(e),true):(this.divided||this.subdivide(),this.northeast.insert(e)||this.northwest.insert(e)||this.southeast.insert(e)||this.southwest.insert(e)):false}query(e,n=[]){if(!t.intersects(this.boundary,e))return n;for(let r of this.items)t.intersects(r.bounds,e)&&n.push(r);return this.divided&&(this.northeast.query(e,n),this.northwest.query(e,n),this.southeast.query(e,n),this.southwest.query(e,n)),n}subdivide(){let{minX:e,minY:n,maxX:r,maxY:a}=this.boundary,o=(e+r)/2,i=(n+a)/2,s=this.depth+1;this.northeast=new t({minX:o,minY:n,maxX:r,maxY:i},this.capacity,s,this.maxDepth),this.northwest=new t({minX:e,minY:n,maxX:o,maxY:i},this.capacity,s,this.maxDepth),this.southeast=new t({minX:o,minY:i,maxX:r,maxY:a},this.capacity,s,this.maxDepth),this.southwest=new t({minX:e,minY:i,maxX:o,maxY:a},this.capacity,s,this.maxDepth),this.divided=true;}static intersects(e,n){return !(n.minX>e.maxX||n.maxX<e.minX||n.minY>e.maxY||n.maxY<e.minY)}},Fe=class{constructor(e,n=4,r=6){this.root=new ze(e,n,0,r);}insert(e){this.root.insert(e);}query(e){return this.root.query(e)}},Xn={x:0,y:9.81},Zn=.1,Kn=.1,ce=class{constructor(e={}){this.gravity=e.gravity?b.clone(e.gravity):Xn,this.defaultFriction=e.defaultFriction??Zn,this.defaultBounce=e.defaultBounce??Kn;}step(e,n,r){if(n<=0||e.length===0)return {entities:e,contacts:[]};let a=[];for(let p of e)p.physics&&a.push(this.createBody(p));if(a.length===0)return {entities:e.map(p=>({...p})),contacts:[]};let o=a.map(p=>this.integrate(p,n,r)),i=new Fe({minX:0,minY:0,maxX:r.width,maxY:r.height}),s=o.map(p=>({body:p,bounds:this.computeAABB(p)}));for(let p of s)i.insert(p);let l=this.broadPhasePairs(s,i),c=[],u=o.map(p=>({...p})),d=new Map(u.map(p=>[p.id,p]));for(let{a:p,b:y}of l){let f=d.get(p.id),h=d.get(y.id),m=this.narrowPhase(f,h);m&&(c.push(m),!m.isSensor&&(this.resolveCollision(f,h,m),this.correctPositions(f,h,m)));}return {entities:e.map(p=>{let y=d.get(p.id);if(!y)return {...p};let f={...p,position:{...y.position}};if(!p.physics)return f;let h=p.physics.acceleration?{...p.physics,velocity:{...y.velocity},acceleration:{...p.physics.acceleration}}:{...p.physics,velocity:{...y.velocity}};return {...f,physics:h}}),contacts:c}}createBody(e){let n=e.physics,r=n?.shape??{type:"aabb",width:1,height:1},a=n?.isStatic===true||e.isActive===false,o=n?.velocity?b.clone(n.velocity):bt,i=n?.acceleration?b.clone(n.acceleration):bt,s=a?Number.POSITIVE_INFINITY:Math.max(n?.mass??1,1e-4);return {id:e.id,entity:e,position:{...e.position},velocity:o,acceleration:i,mass:s,inverseMass:a?0:1/s,gravityScale:n?.gravityScale??1,friction:n?.friction??this.defaultFriction,bounce:n?.bounce??this.defaultBounce,isStatic:a,isSensor:n?.isSensor===true,shape:r}}integrate(e,n,r){if(e.isStatic)return {...e,position:{...e.position},velocity:{...e.velocity}};let a=b.multiplyScalar(this.gravity,e.gravityScale),o=b.add(e.acceleration,a),i=b.add(e.velocity,b.multiplyScalar(o,n)),s=Math.max(0,1-e.friction*n),l=b.multiplyScalar(i,s),c=b.add(e.position,b.multiplyScalar(l,n)),u=this.clampToBounds(c,e.shape,r);return {...e,position:u,velocity:l}}clampToBounds(e,n,r){let a=this.getShapeExtents(n);return {x:_e(e.x,a.x,r.width-a.x),y:_e(e.y,a.y,r.height-a.y)}}computeAABB(e){return this.computeShapeAABB(e.shape,e.position)}computeShapeAABB(e,n){switch(e.type){case "aabb":{let r=e.width/2,a=e.height/2;return {minX:n.x-r,minY:n.y-a,maxX:n.x+r,maxY:n.y+a}}case "circle":return {minX:n.x-e.radius,minY:n.y-e.radius,maxX:n.x+e.radius,maxY:n.y+e.radius};case "polygon":{let r=Number.POSITIVE_INFINITY,a=Number.POSITIVE_INFINITY,o=Number.NEGATIVE_INFINITY,i=Number.NEGATIVE_INFINITY;for(let s of e.points){let l=n.x+s.x,c=n.y+s.y;r=Math.min(r,l),a=Math.min(a,c),o=Math.max(o,l),i=Math.max(i,c);}return {minX:r,minY:a,maxX:o,maxY:i}}}}getShapeExtents(e){switch(e.type){case "aabb":return {x:e.width/2,y:e.height/2};case "circle":return {x:e.radius,y:e.radius};case "polygon":{let n=0,r=0;for(let a of e.points)n=Math.max(n,Math.abs(a.x)),r=Math.max(r,Math.abs(a.y));return {x:n,y:r}}}}broadPhasePairs(e,n){let r=[],a=new Set;for(let{body:o,bounds:i}of e){let s=n.query(i);for(let l of s){if(l.body.id===o.id)continue;let c=o.id<l.body.id?`${o.id}:${l.body.id}`:`${l.body.id}:${o.id}`;a.has(c)||(a.add(c),r.push({a:o,b:l.body}));}}return r}narrowPhase(e,n){let{shape:r}=e,{shape:a}=n;if(r.type==="circle"&&a.type==="circle")return this.circleVsCircle(e,n);if(r.type==="circle"&&a.type!=="circle")return this.circleVsPolygon(e,n);if(r.type!=="circle"&&a.type==="circle"){let s=this.circleVsPolygon(n,e);return s?this.flipManifold(s):null}if(r.type==="aabb"&&a.type==="aabb")return this.aabbVsAabb(e,n);let o=this.shapeToPolygon(e),i=this.shapeToPolygon(n);return this.polygonVsPolygon(e,o,n,i)}aabbVsAabb(e,n){let r=e.shape.width/2,a=e.shape.height/2,o=n.shape.width/2,i=n.shape.height/2,s=b.subtract(n.position,e.position),l=r+o-Math.abs(s.x);if(l<=0)return null;let c=a+i-Math.abs(s.y);if(c<=0)return null;if(l<c){let d={x:s.x<0?-1:1,y:0};return this.createManifold(e,n,d,l,[this.midpoint(e.position,n.position)])}let u={x:0,y:s.y<0?-1:1};return this.createManifold(e,n,u,c,[this.midpoint(e.position,n.position)])}circleVsCircle(e,n){let r=e.shape,a=n.shape,o=b.subtract(n.position,e.position),i=b.length(o),s=r.radius+a.radius;if(i===0||i>=s)return null;let l=i===0?{x:0,y:1}:b.multiplyScalar(o,1/i),c=s-i,u=b.add(e.position,b.multiplyScalar(l,r.radius-c/2));return this.createManifold(e,n,l,c,[u])}circleVsPolygon(e,n){let r=e.shape,a=this.shapeToPolygon(n),o=Number.POSITIVE_INFINITY,i=null;for(let g of a.points){let p=b.subtract(g,e.position),y=b.length(p);y<o&&(o=y,i=g);}if(!i)return null;let s=o===0?{x:0,y:1}:b.normalize(b.subtract(i,e.position)),l=this.projectCircle(e.position,r.radius,s),c=this.projectPoints(a.points,s),u=this.intervalOverlap(l,c);if(u<=0)return null;let d=b.subtract(i,b.multiplyScalar(s,u/2));return this.createManifold(e,n,s,u,[d])}polygonVsPolygon(e,n,r,a){let o=Number.POSITIVE_INFINITY,i=null,s=[...n.axes,...a.axes];for(let u of s){let d=this.projectPoints(n.points,u),g=this.projectPoints(a.points,u),p=this.intervalOverlap(d,g);if(p<=0)return null;p<o&&(o=p,i=u);}if(!i)return null;let l=b.subtract(r.position,e.position);b.dot(l,i)<0&&(i=b.multiplyScalar(i,-1));let c=this.midpoint(e.position,r.position);return this.createManifold(e,r,i,o,[c])}shapeToPolygon(e){if(e.shape.type==="polygon"){let o=e.shape.points.map(i=>({x:e.position.x+i.x,y:e.position.y+i.y}));return {points:o,axes:this.computeAxes(o)}}if(e.shape.type==="aabb"){let o=e.shape.width/2,i=e.shape.height/2,s=[{x:e.position.x-o,y:e.position.y-i},{x:e.position.x+o,y:e.position.y-i},{x:e.position.x+o,y:e.position.y+i},{x:e.position.x-o,y:e.position.y+i}];return {points:s,axes:this.computeAxes(s)}}let n=e.shape.radius,r=8,a=[];for(let o=0;o<r;o+=1){let i=Math.PI*2*o/r;a.push({x:e.position.x+Math.cos(i)*n,y:e.position.y+Math.sin(i)*n});}return {points:a,axes:this.computeAxes(a)}}computeAxes(e){let n=[];for(let r=0;r<e.length;r+=1){let a=e[r],o=e[(r+1)%e.length],i=b.subtract(o,a),s=b.normalize(b.perpendicular(i));n.push(s);}return n}projectPoints(e,n){let r=Number.POSITIVE_INFINITY,a=Number.NEGATIVE_INFINITY;for(let o of e){let i=b.dot(o,n);r=Math.min(r,i),a=Math.max(a,i);}return {min:r,max:a}}projectCircle(e,n,r){let a=b.dot(e,r);return {min:a-n,max:a+n}}intervalOverlap(e,n){return Math.min(e.max,n.max)-Math.max(e.min,n.min)}resolveCollision(e,n,r){if(e.isStatic&&n.isStatic)return;let a=b.subtract(n.velocity,e.velocity),o=b.dot(a,r.normal);if(o>0)return;let s=-(1+Math.min(e.bounce,n.bounce))*o/(e.inverseMass+n.inverseMass),l=b.multiplyScalar(r.normal,s);e.isStatic||(e.velocity=b.subtract(e.velocity,b.multiplyScalar(l,e.inverseMass))),n.isStatic||(n.velocity=b.add(n.velocity,b.multiplyScalar(l,n.inverseMass)));let c=b.normalize(b.subtract(a,b.multiplyScalar(r.normal,o)));if(c.x===0&&c.y===0)return;let u=-b.dot(a,c)/(e.inverseMass+n.inverseMass),d=Math.sqrt(e.friction*n.friction),g=b.multiplyScalar(c,_e(u,-d,d));e.isStatic||(e.velocity=b.subtract(e.velocity,b.multiplyScalar(g,e.inverseMass))),n.isStatic||(n.velocity=b.add(n.velocity,b.multiplyScalar(g,n.inverseMass)));}correctPositions(e,n,r){if(e.isStatic&&n.isStatic)return;let i=Math.max(r.penetration-.01,0)/(e.inverseMass+n.inverseMass)*.8,s=b.multiplyScalar(r.normal,i);e.isStatic||(e.position=b.subtract(e.position,b.multiplyScalar(s,e.inverseMass))),n.isStatic||(n.position=b.add(n.position,b.multiplyScalar(s,n.inverseMass)));}flipManifold(e){return {a:e.b,b:e.a,normal:b.multiplyScalar(e.normal,-1),penetration:e.penetration,contacts:e.contacts,isSensor:e.isSensor}}createManifold(e,n,r,a,o){let i=e.isSensor||n.isSensor,s=e.entity.physics?{...e.entity,position:{...e.position},physics:e.entity.physics.acceleration?{...e.entity.physics,velocity:{...e.velocity},acceleration:{...e.entity.physics.acceleration}}:{...e.entity.physics,velocity:{...e.velocity}}}:{...e.entity,position:{...e.position}},l=n.entity.physics?{...n.entity,position:{...n.position},physics:n.entity.physics.acceleration?{...n.entity.physics,velocity:{...n.velocity},acceleration:{...n.entity.physics.acceleration}}:{...n.entity.physics,velocity:{...n.velocity}}}:{...n.entity,position:{...n.position}};return {a:s,b:l,normal:b.normalize(r),penetration:a,contacts:o.map(c=>({...c})),isSensor:i}}midpoint(e,n){return {x:(e.x+n.x)/2,y:(e.y+n.y)/2}}};var de={isValidPosition:(t,e,n)=>t.x>=0&&t.x<e&&t.y>=0&&t.y<n,isValidEntity:t=>!!(t.id&&t.type&&t.position),isValidScene:t=>!!(t.id&&t.width>0&&t.height>0),isValidGameModel:t=>!!(t.id&&t.scenes.length>0&&t.initialScene)},Ue={isVector2D:t=>typeof t=="object"&&t!==null&&typeof t.x=="number"&&typeof t.y=="number",isGameEntity:t=>typeof t=="object"&&t!==null&&typeof t.id=="string"&&typeof t.type=="string"&&Ue.isVector2D(t.position),isGameAction:t=>typeof t=="object"&&t!==null&&typeof t.type=="string"},U=class{constructor(e,n={},r=1e3){this.validateGameModel(e),this.gameModel=e,this.eventCallbacks=n,this.actionHistory=[],this.stateHistory=[],this.maxHistorySize=r,this.currentState=this.initializeGameState(),this.physicsEngine=new ce;}executeAction(e,n="human"){try{this.validateAction(e),this.addToHistory(this.currentState,e);let r=this.processAction(this.currentState,e);return this.currentState=r,this.eventCallbacks.onAction?.(e,n),this.eventCallbacks.onStateChange?.(r),r.isGameOver&&this.eventCallbacks.onGameOver?.(r),this.checkSceneTransitions(r),r}catch(r){throw this.eventCallbacks.onError?.(r),r}}getCurrentState(){return this.cloneState(this.currentState)}reset(){return this.actionHistory=[],this.stateHistory=[],this.currentState=this.initializeGameState(),this.eventCallbacks.onStateChange?.(this.currentState),this.cloneState(this.currentState)}getActionHistory(){return [...this.actionHistory]}getStateHistory(){return [...this.stateHistory]}undo(){if(this.stateHistory.length<2)return null;this.stateHistory.pop(),this.actionHistory.pop();let e=this.stateHistory[this.stateHistory.length-1];return this.currentState=this.cloneState(e),this.eventCallbacks.onStateChange?.(this.currentState),this.cloneState(this.currentState)}isValidPosition(e){let n=this.getCurrentScene();return de.isValidPosition(e,n.width,n.height)?!this.hasCollision(e):false}getAvailableActions(){let e=[],n=this.currentState.playerPosition,r=this.getCurrentScene(),a=Date.now();return ["up","down","left","right"].forEach(i=>{let s=T.add(n,Re[i]);this.isValidPosition(s)&&e.push({type:"move",direction:i,timestamp:a});}),this.getNearbyEntities(n).forEach(i=>{(i.type==="collectible"||i.type==="portal")&&e.push({type:"interact",target:i.id,timestamp:a});}),r.actions?.forEach(i=>{(!i.condition||i.condition(this.currentState))&&e.push({type:"choice",payload:i,timestamp:a,metadata:{actionId:i.id,sceneId:r.id,label:i.label,nextSceneId:i.nextSceneId,source:"authored-action",rewards:i.rewards,actionMetadata:i.metadata}});}),e}calculateStepResult(e,n,r){let a=this.calculateReward(e,n,r),o=r.isGameOver||this.allObjectivesCompleted(r);return {nextState:r,reward:a,done:o,info:{action:n,scoreChange:r.score-e.score,healthChange:r.playerHealth-e.playerHealth,objectivesCompleted:this.getCompletedObjectives(r).length}}}initializeGameState(){let e=this.gameModel.scenes.find(a=>a.id===this.gameModel.initialScene);if(!e)throw new Error(`Initial scene '${this.gameModel.initialScene}' not found`);let n={...O,...e.player},r=this.buildRendererState(e);return {currentScene:e.id,playerPosition:T.clone(n.startPosition),entities:e.entities.map(a=>({...a})),score:0,episode:1,isGameOver:false,objectives:e.objectives.map(a=>({...a,isCompleted:false})),inventory:[],playerHealth:n.health,gameTime:0,metadata:{},stateHash:this.generateStateHash(e.id,n.startPosition,0),renderLayers:r.renderLayers,tilesets:r.tilesets,tileLayers:r.tileLayers,animations:r.animations,rendererState:r.rendererState}}buildRendererState(e){let n=this.gameModel.config??F,r=n.renderLayers??F.renderLayers,a=new Map,o=(C,w)=>{if(!C.id)return;let L=a.get(C.id),q=C.zIndex??L?.zIndex??w;a.set(C.id,{...L,...C,zIndex:q});};r.forEach((C,w)=>o(C,w)),(e.renderLayers??[]).forEach((C,w)=>o(C,r.length+w));let i=Array.from(a.values()).map((C,w)=>({...C,zIndex:C.zIndex??w})).sort((C,w)=>(C.zIndex??0)-(w.zIndex??0)),s=new Map;(n.tilesets??F.tilesets).forEach(C=>{s.set(C.id,{...C});}),(e.tilesets??[]).forEach(C=>{s.set(C.id,{...C});});let l=Array.from(s.values()),c=[...e.tileLayers??[]].map(C=>({...C,data:[...C.data]})).sort((C,w)=>(C.zIndex??0)-(w.zIndex??0)),u={...n.animations??{},...e.animations??{}},d=n.webgl??F.webgl,g=e.webgl,p=g?.camera??d?.camera,y=g?.backgroundColor??d?.backgroundColor,f=g?.antialias??d?.antialias,h=g?.pixelRatio??d?.pixelRatio,m={features:{...F.webgl?.features??{},...d?.features??{},...g?.features??{}},layers:i,tilesets:l,tileLayers:c,...p!==void 0&&{camera:p},...y!==void 0&&{backgroundColor:y},...f!==void 0&&{antialias:f},...h!==void 0&&{pixelRatio:h}};return {renderLayers:i,tilesets:l,tileLayers:c,animations:u,rendererState:m}}processAction(e,n){let r=this.cloneState(e);switch(n.type){case "move":r=this.processMovement(r,n);break;case "interact":r=this.processInteraction(r,n);break;case "choice":r=this.processSceneChoice(r,n);break;case "reset":return this.initializeGameState();default:r=this.processCustomAction(r,n);}return r={...r,gameTime:r.gameTime+1,stateHash:this.generateStateHash(r.currentScene,r.playerPosition,r.gameTime)},r=this.processEntityBehaviors(r),r=this.checkObjectives(r),r=this.checkGameOverConditions(r),r}processMovement(e,n){if(!n.direction)throw new Error("Movement action requires direction");let r=e.playerPosition,a=Re[n.direction],o=T.add(r,a);if(!this.isValidPosition(o))return e;let i=e.entities.find(l=>l.type==="collectible"&&T.equals(l.position,o)),s={...e,playerPosition:o};return i&&(s=this.collectEntity(s,i)),s}processInteraction(e,n){if(!n.target)throw new Error("Interaction action requires target entity ID");let r=e.entities.find(i=>i.id===n.target);if(!r||T.distance(e.playerPosition,r.position)>1.5)return e;let o=e;switch(r.type){case "collectible":o=this.collectEntity(o,r);break;case "portal":o=this.processPortal(o,r);break;case "enemy":o=this.processEnemyInteraction(o,r);break;default:if(r.behavior?.onCollision){let i={id:"player",type:"player",position:e.playerPosition};try{let s=r.behavior.onCollision(i,r);Array.isArray(s)&&s.length>0&&(o=s.reduce((c,u)=>this.processAction(c,u),o));}catch(s){console.warn("Error in entity onCollision behavior:",s);}}}return o}collectEntity(e,n){return {...e,entities:e.entities.filter(r=>r.id!==n.id),inventory:[...e.inventory,n.id],score:e.score+(n.metadata?.points||10)}}processPortal(e,n){let r=n.metadata?.targetScene;return r?this.transitionToScene(e,r,{source:"portal",portalId:n.id}):e}processSceneChoice(e,n){let r=this.getCurrentScene(),a=this.resolveSceneAction(r,n);if(!a)return e;let o={...e,metadata:{...e.metadata,lastChoiceId:a.id,lastChoiceLabel:a.label,lastChoiceRewards:a.rewards,lastChoiceMetadata:a.metadata}};if(a.rewards){let{score:i,health:s,items:l}=a.rewards;typeof i=="number"&&(o={...o,score:o.score+i}),typeof s=="number"&&(o={...o,playerHealth:o.playerHealth+s}),l&&l.length>0&&(o={...o,inventory:[...new Set([...o.inventory,...l])]});}return a.nextSceneId&&(o=this.transitionToScene(o,a.nextSceneId,{source:"authored-action",actionId:a.id})),o}resolveSceneAction(e,n){if(!e.actions||e.actions.length===0)return;let r=n.payload,a=n.metadata?.actionId??r?.id;if(a)return e.actions.find(o=>o.id===a);if(r)return e.actions.find(o=>o.id===r.id)}transitionToScene(e,n,r){let a=this.gameModel.scenes.find(s=>s.id===n);if(!a)return e;this.eventCallbacks.onSceneChange?.(e.currentScene,n);let o={...O,...a.player},i=this.buildRendererState(a);return {...e,currentScene:n,playerPosition:T.clone(o.startPosition),entities:a.entities.map(s=>({...s})),objectives:[...e.objectives,...a.objectives.map(s=>({...s,isCompleted:false}))],metadata:{...e.metadata,lastTransition:{from:e.currentScene,to:n,...r??{}}},renderLayers:i.renderLayers,tilesets:i.tilesets,tileLayers:i.tileLayers,animations:i.animations,rendererState:i.rendererState}}processEnemyInteraction(e,n){let r=n.metadata?.damage||1,a=Math.max(0,e.playerHealth-r);return {...e,playerHealth:a,isGameOver:a<=0}}processCustomAction(e,n){return e}processEntityBehaviors(e){let n={...e},r=this.gameModel.config?.frameRate??F.frameRate,a=r>0?1/r:1/F.frameRate,o=this.getCurrentScene(),i=n.entities.map(y=>{if(y.behavior?.onUpdate&&y.isActive!==false)try{let f=y.behavior.onUpdate(y,a);if(f!==void 0&&typeof f=="object"&&f!==null&&!Array.isArray(f))return {...y,...f}}catch(f){console.warn("Error in entity onUpdate behavior:",f);}return y}),s=this.physicsEngine.step(i,a,{width:o.width,height:o.height}),l=new Map(s.entities.map(y=>[y.id,y])),c=[],u=y=>{let f=l.get(y.a.id)??y.a,h=l.get(y.b.id)??y.b;if(f.behavior?.onCollision)try{let m=f.behavior.onCollision(f,h);Array.isArray(m)&&c.push(...m);}catch(m){console.warn("Error in entity onCollision behavior:",m);}if(h.behavior?.onCollision)try{let m=h.behavior.onCollision(h,f);Array.isArray(m)&&c.push(...m);}catch(m){console.warn("Error in entity onCollision behavior:",m);}};for(let y of s.contacts)u(y);let d={...n.metadata};if(c.length>0)d={...d,pendingPhysicsActions:c};else if("pendingPhysicsActions"in d){let{pendingPhysicsActions:y,...f}=d;d=f;}let g=this.applyRenderLayerOrdering(s.entities.map(y=>({...y})),n.renderLayers),p={...n,entities:g,metadata:d};return this.updateEntityAnimations(p,a)}checkObjectives(e){let n=e.objectives.map(r=>{if(r.isCompleted)return r;let a=false;switch(r.type){case "collect":a=e.inventory.includes(r.target||"");break;case "reach":let o=e.entities.find(s=>s.id===r.target);o&&(a=T.equals(e.playerPosition,o.position));break;case "score":let i=r.target?parseInt(r.target):100;a=e.score>=i;break;case "custom":a=r.condition?r.condition(e):false;break}return a&&!r.isCompleted?(this.eventCallbacks.onObjectiveComplete?.(r),{...r,isCompleted:true}):r});return {...e,objectives:n}}checkGameOverConditions(e){if(e.isGameOver)return e;if(e.playerHealth<=0)return {...e,isGameOver:true};let n=e.objectives.filter(a=>a.isRequired!==false);return n.length>0&&n.every(a=>a.isCompleted)?{...e,isGameOver:true}:e}calculateReward(e,n,r){let a=0;a+=r.score-e.score,a+=(r.playerHealth-e.playerHealth)*10;let o=this.getCompletedObjectives(r).length,i=this.getCompletedObjectives(e).length;return a+=(o-i)*100,a-=1,r.isGameOver&&r.playerHealth<=0&&(a-=100),r.isGameOver&&this.allObjectivesCompleted(r)&&(a+=1e3),a}applyRenderLayerOrdering(e,n){if(!n||n.length===0)return e;let r=new Map;return n.forEach((o,i)=>{r.set(o.id,o.zIndex??i);}),e.map((o,i)=>{let l=(o.renderLayerId?r.get(o.renderLayerId):void 0)??o.zIndex??i;return o.zIndex===l?o:{...o,zIndex:l}}).map((o,i)=>({entity:o,index:i})).sort((o,i)=>{let s=o.entity.zIndex??o.index,l=i.entity.zIndex??i.index;return s!==l?s-l:o.index-i.index}).map(({entity:o})=>o)}updateEntityAnimations(e,n){if(!e.entities.length)return e;let r=new Map,a=this.gameModel.config.animations??F.animations;Object.entries(a??{}).forEach(([l,c])=>{r.set(l,c);}),Object.entries(e.animations??{}).forEach(([l,c])=>{r.set(l,c);});let o=Math.max(n,0)*1e3,i=false,s=e.entities.map(l=>{let c=l.animationState||(l.defaultAnimationId?{animationId:l.defaultAnimationId,frameIndex:0,elapsed:0}:void 0);if(!c||c.isPaused)return l;let u=new Map(r);l.animations?.forEach(L=>{u.set(L.id,L);});let d=c.animationId;!u.has(d)&&l.defaultAnimationId&&(d=l.defaultAnimationId);let g=u.get(d);if(!g||g.frames.length===0)return l;let p=c.playbackRate??g.playbackRate??1,y=(c.elapsed??0)+o*Math.max(p,0),f=c.frameIndex??0,h=g,m=1e3,C=0;for(;C++<m&&h.frames.length>0;){let L=Math.min(f,h.frames.length-1),q=h.frames[L]?.duration??0;if(q<=0||y<q)break;if(y-=q,f+=1,!(f<h.frames.length)){if(h.loop===false){f=h.frames.length-1,y=Math.min(y,h.frames[f]?.duration??0);break}if(h.next&&u.has(h.next)){d=h.next,h=u.get(d),f=0;continue}f=0;}}let w={...c,animationId:d,frameIndex:f,elapsed:y};return w.animationId===c.animationId&&w.frameIndex===c.frameIndex&&w.elapsed===c.elapsed?l:(i=true,{...l,animationState:w})});return i?{...e,entities:s}:e}hasCollision(e){return this.currentState.entities.some(n=>n.isCollidable!==false&&n.type!=="collectible"&&T.equals(n.position,e))}getNearbyEntities(e,n=1.5){return this.currentState.entities.filter(r=>T.distance(e,r.position)<=n)}getCurrentScene(){let e=this.gameModel.scenes.find(n=>n.id===this.currentState.currentScene);if(!e)throw new Error(`Scene '${this.currentState.currentScene}' not found`);return e}getCompletedObjectives(e){return e.objectives.filter(n=>n.isCompleted)}allObjectivesCompleted(e){let n=e.objectives.filter(r=>r.isRequired!==false);return n.length>0&&n.every(r=>r.isCompleted)}checkSceneTransitions(e){let n=this.getCurrentScene(),r=Be(n);if(r.length)for(let a of r){let o=false;if(typeof a.trigger=="string"){let i=e.entities.find(s=>s.id===a.trigger);o=!i||T.equals(e.playerPosition,i.position);}else o=a.trigger(e);if(o){this.eventCallbacks.onSceneChange?.(e.currentScene,a.targetScene);break}}}cloneState(e){let n=e.renderLayers?.map(({id:s,label:l,zIndex:c,visible:u,debugColor:d,metadata:g})=>({id:s,...l!==void 0&&{label:l},...c!==void 0&&{zIndex:c},...u!==void 0&&{visible:u},...d!==void 0&&{debugColor:d},...g!==void 0&&{metadata:{...g}}})),r=e.tilesets?.map(({id:s,texture:l,tileWidth:c,tileHeight:u,columns:d,rows:g,margin:p,spacing:y,tileMetadata:f,metadata:h})=>({id:s,texture:l,tileWidth:c,tileHeight:u,...d!==void 0&&{columns:d},...g!==void 0&&{rows:g},...p!==void 0&&{margin:p},...y!==void 0&&{spacing:y},...f!==void 0&&{tileMetadata:Object.fromEntries(Object.entries(f).map(([m,C])=>[Number(m),{...C}]))},...h!==void 0&&{metadata:{...h}}})),a=e.tileLayers?.map(({id:s,tilesetId:l,data:c,width:u,height:d,zIndex:g,renderLayerId:p,visible:y,parallaxFactor:f,metadata:h})=>({id:s,tilesetId:l,data:[...c],width:u,height:d,...g!==void 0&&{zIndex:g},...p!==void 0&&{renderLayerId:p},...y!==void 0&&{visible:y},...f!==void 0&&{parallaxFactor:f},...h!==void 0&&{metadata:{...h}}})),o=e.animations?Object.fromEntries(Object.entries(e.animations).map(([s,l])=>[s,{...l,frames:l.frames.map(c=>({...c}))}])):void 0,i=e.rendererState?{...e.rendererState.features!==void 0&&{features:{...e.rendererState.features}},...e.rendererState.layers!==void 0&&{layers:e.rendererState.layers.map(({id:s,label:l,zIndex:c,visible:u,debugColor:d,metadata:g})=>({id:s,...l!==void 0&&{label:l},...c!==void 0&&{zIndex:c},...u!==void 0&&{visible:u},...d!==void 0&&{debugColor:d},...g!==void 0&&{metadata:{...g}}}))},...e.rendererState.tilesets!==void 0&&{tilesets:e.rendererState.tilesets.map(({id:s,texture:l,tileWidth:c,tileHeight:u,columns:d,rows:g,margin:p,spacing:y,tileMetadata:f,metadata:h})=>({id:s,texture:l,tileWidth:c,tileHeight:u,...d!==void 0&&{columns:d},...g!==void 0&&{rows:g},...p!==void 0&&{margin:p},...y!==void 0&&{spacing:y},...f!==void 0&&{tileMetadata:Object.fromEntries(Object.entries(f).map(([m,C])=>[Number(m),{...C}]))},...h!==void 0&&{metadata:{...h}}}))},...e.rendererState.tileLayers!==void 0&&{tileLayers:e.rendererState.tileLayers.map(({id:s,tilesetId:l,data:c,width:u,height:d,zIndex:g,renderLayerId:p,visible:y,parallaxFactor:f,metadata:h})=>({id:s,tilesetId:l,data:[...c],width:u,height:d,...g!==void 0&&{zIndex:g},...p!==void 0&&{renderLayerId:p},...y!==void 0&&{visible:y},...f!==void 0&&{parallaxFactor:f},...h!==void 0&&{metadata:{...h}}}))},...e.rendererState.camera!==void 0&&{camera:{...e.rendererState.camera}},...e.rendererState.backgroundColor!==void 0&&{backgroundColor:e.rendererState.backgroundColor},...e.rendererState.antialias!==void 0&&{antialias:e.rendererState.antialias},...e.rendererState.pixelRatio!==void 0&&{pixelRatio:e.rendererState.pixelRatio}}:void 0;return {...e,entities:e.entities.map(s=>({...s})),objectives:e.objectives.map(s=>({...s})),inventory:[...e.inventory],metadata:{...e.metadata},...n!==void 0&&{renderLayers:n},...r!==void 0&&{tilesets:r},...a!==void 0&&{tileLayers:a},...o!==void 0&&{animations:o},...i!==void 0&&{rendererState:i}}}generateStateHash(e,n,r){return `${e}_${n.x}_${n.y}_${r}`}addToHistory(e,n){this.stateHistory.push(this.cloneState(e)),this.actionHistory.push({...n}),this.stateHistory.length>this.maxHistorySize&&(this.stateHistory.shift(),this.actionHistory.shift());}validateGameModel(e){if(!de.isValidGameModel(e))throw new Error("Invalid game model provided to GameEngine")}validateAction(e){if(!Ue.isGameAction(e))throw new Error("Invalid game action provided to GameEngine")}};function Jn(t,e,n){return new U(t,e,n)}var er={maxStepsPerEpisode:1e3,rewardFunction:(t,e,n)=>tr(t,e,n),stateEncoding:"grid",actionSpace:"discrete",deterministic:false,seed:Math.floor(Math.random()*1e6),observationShape:[10,10,4],enableActionMasking:true},K=class{constructor(e,n={}){this.gameModel=e,this.config={...er,...n},this.gameEngine=new U(e),this.stepCount=0,this.episodeNumber=0,this.isInitialized=false,this.actionSpace=this.computeActionSpace(),this.observationSpace=this.computeObservationSpace(),this.currentState=this.gameEngine.getCurrentState(),this.isInitialized=true;}reset(){this.currentState=this.gameEngine.reset(),this.stepCount=0,this.episodeNumber++;let e=this.encodeState(this.currentState),n=this.getEnvInfo(false);return {observation:e,info:n}}step(e){if(!this.isInitialized)throw new Error("Environment not initialized. Call reset() first.");this.stepCount++;let n=typeof e=="number"?this.actionSpace[e]:e;if(!n)throw new Error(`Invalid action: ${e}`);let r=this.cloneState(this.currentState);try{this.currentState=this.gameEngine.executeAction(n,"agent");}catch(c){return console.warn("Invalid action attempted:",n,c),{observation:this.encodeState(this.currentState),reward:-10,terminated:false,truncated:false,info:this.getEnvInfo(false)}}let a=this.config.rewardFunction(r,n,this.currentState),o=this.isTerminated(),i=this.isTruncated(),s=this.encodeState(this.currentState),l=this.getEnvInfo(i);return {observation:s,reward:a,terminated:o,truncated:i,info:l}}getObservation(){return this.encodeState(this.currentState)}getActionSpace(){return {type:this.config.actionSpace,shape:[this.actionSpace.length],actions:[...this.actionSpace]}}getObservationSpace(){return {shape:this.observationSpace,type:"box",low:0,high:1}}getActionMask(){if(!this.config.enableActionMasking)return new Array(this.actionSpace.length).fill(true);let e=this.gameEngine.getAvailableActions();return this.actionSpace.map(n=>e.some(r=>this.actionsEqual(n,r)))}render(e="human"){return e==="human"?this.renderHuman():this.renderRgbArray()}getGameState(){return this.cloneState(this.currentState)}getCurrentScene(){return this.gameModel.scenes.find(e=>e.id===this.currentState.currentScene)||null}calculateDistanceReward(e,n,r=10){let a=T.distance(e,n);return Math.max(0,(r-a)/r)}isValidPosition(e){let n=this.getCurrentScene();return n?e.x>=0&&e.x<n.width&&e.y>=0&&e.y<n.height:false}getAvailableActions(){return this.gameEngine.getAvailableActions()}setConfig(e){this.config={...this.config,...e},e.rewardFunction&&(this.config.rewardFunction=e.rewardFunction);}exportState(){return {gameState:this.cloneState(this.currentState),stepCount:this.stepCount,episodeNumber:this.episodeNumber,config:this.config}}importState(e){this.currentState=this.cloneState(e.gameState),this.stepCount=e.stepCount,this.episodeNumber=e.episodeNumber,e.config&&this.setConfig(e.config);}encodeState(e){switch(this.config.stateEncoding){case "grid":return this.encodeGridState(e);case "vector":return this.encodeVectorState(e);case "custom":return this.encodeCustomState(e);default:return this.encodeGridState(e)}}encodeGridState(e){if(!this.getCurrentScene())return [];let[r,a,o]=this.config.observationShape,i=new Array(r*a*o).fill(0),s=e.playerPosition.y*r+e.playerPosition.x;return s>=0&&s<r*a&&(i[s]=1),e.entities.forEach(l=>{let c=l.position.y*r+l.position.x;if(c>=0&&c<r*a){let u=3;l.type==="obstacle"||l.type==="wall"?u=1:l.type==="collectible"&&(u=2);let d=u*r*a+c;d<i.length&&(i[d]=1);}}),i}encodeVectorState(e){let n=[],r=this.getCurrentScene();r?(n.push(e.playerPosition.x/r.width),n.push(e.playerPosition.y/r.height)):n.push(0,0),n.push(Math.min(e.score/1e3,1)),n.push(e.playerHealth/100),n.push(e.gameTime/1e3);let a=e.objectives.filter(i=>i.isCompleted).length;n.push(e.objectives.length>0?a/e.objectives.length:0);let o={obstacle:0,collectible:0,enemy:0,portal:0};return e.entities.forEach(i=>{i.type in o&&o[i.type]++;}),n.push(...Object.values(o).map(i=>Math.min(i/10,1))),n}encodeCustomState(e){return this.encodeVectorState(e)}computeActionSpace(){let e=[];["up","down","left","right"].forEach(r=>{e.push({type:"move",direction:r,timestamp:Date.now()});}),e.push({type:"interact",timestamp:Date.now()}),e.push({type:"reset",timestamp:Date.now()});let n=new Map;return this.gameModel.scenes.forEach(r=>{r.actions?.forEach(a=>{let o=`${r.id}:${a.id}`;n.has(o)||n.set(o,{type:"choice",timestamp:Date.now(),payload:a,metadata:{actionId:a.id,sceneId:r.id,label:a.label,nextSceneId:a.nextSceneId,source:"authored-action"}});});}),e.push(...n.values()),e}computeObservationSpace(){switch(this.config.stateEncoding){case "grid":return this.config.observationShape;case "vector":return [13];case "custom":return this.config.observationShape;default:return this.config.observationShape}}isTerminated(){return this.currentState.isGameOver}isTruncated(){return this.stepCount>=this.config.maxStepsPerEpisode}getEnvInfo(e){return {stepCount:this.stepCount,episodeNumber:this.episodeNumber,truncated:e,metadata:{scene:this.currentState.currentScene,score:this.currentState.score,health:this.currentState.playerHealth,objectivesCompleted:this.currentState.objectives.filter(n=>n.isCompleted).length,totalObjectives:this.currentState.objectives.length}}}renderHuman(){let e=this.getCurrentScene();if(!e)return "No current scene";let n=`=== Episode ${this.episodeNumber}, Step ${this.stepCount} ===
`;n+=`Scene: ${e.name}
`,n+=`Score: ${this.currentState.score}, Health: ${this.currentState.playerHealth}
`,n+=`Player: (${this.currentState.playerPosition.x}, ${this.currentState.playerPosition.y})
`;let r=[];for(let a=0;a<e.height;a++)r[a]=new Array(e.width).fill(".");return this.currentState.entities.forEach(a=>{if(a.isVisible!==false){let o=a.type==="obstacle"?"#":a.type==="collectible"?"$":a.type==="enemy"?"E":a.type==="portal"?"P":"?";r[a.position.y][a.position.x]=o;}}),r[this.currentState.playerPosition.y][this.currentState.playerPosition.x]="@",n+=`
`,r.forEach(a=>{n+=a.join(" ")+`
`;}),n}renderRgbArray(){let e=this.getCurrentScene();if(!e)return [];let n=[];for(let r=0;r<e.height;r++)for(let a=0;a<e.width;a++){let o=[240,240,240],i=this.currentState.entities.find(s=>s.position.x===a&&s.position.y===r&&s.isVisible!==false);if(i)switch(i.type){case "obstacle":case "wall":o=[255,0,0];break;case "collectible":o=[255,255,0];break;case "enemy":o=[255,100,0];break;case "portal":o=[128,0,255];break}this.currentState.playerPosition.x===a&&this.currentState.playerPosition.y===r&&(o=[0,255,0]),n.push(...o);}return n}actionsEqual(e,n){return e.type===n.type&&e.direction===n.direction&&e.target===n.target&&e.metadata?.actionId===n.metadata?.actionId}cloneState(e){return {...e,entities:e.entities.map(n=>({...n})),objectives:e.objectives.map(n=>({...n})),inventory:[...e.inventory],metadata:{...e.metadata}}}};function tr(t,e,n){let r=0;r+=n.score-t.score,r+=(n.playerHealth-t.playerHealth)*10;let a=n.objectives.filter(i=>i.isCompleted).length,o=t.objectives.filter(i=>i.isCompleted).length;return r+=(a-o)*100,r-=1,n.isGameOver&&(n.playerHealth<=0?r-=100:r+=1e3),r}function nr(t,e="training",n){let a={...{training:{maxStepsPerEpisode:1e3,stateEncoding:"grid",deterministic:false,enableActionMasking:true},evaluation:{maxStepsPerEpisode:2e3,stateEncoding:"grid",deterministic:true,enableActionMasking:false},debug:{maxStepsPerEpisode:100,stateEncoding:"vector",deterministic:true,enableActionMasking:true},custom:{}}[e],...n};return new K(t,a)}var Y=class extends Error{constructor(e){super(e),this.name="WebGLRendererError";}},H=class extends Y{constructor(e){super(e),this.name="WebGLUnavailableError";}},ue=class extends Y{constructor(e){super(e),this.name="WebGLInitializationError";}},ye={spriteBatching:true,particles:true,layers:true,tilesets:true},me=class{constructor(e,n={}){this.renderer=null;this.scene=null;this.camera=null;this.canvas=null;this.width=1;this.height=1;this.features={...ye};this.layerGroups=new Map;this.tilesets=new Map;this.tileLayers=[];this.spriteBatches=new Map;this.particleEmitters=new Map;this.diagnostics={spriteBatchCount:0,spriteBatches:{},particleEmitters:{},renderLayers:[],tilesets:[],tileLayers:[],initialized:false};this.container=e,this.options=n,this.clock=new Clock,n.features&&(this.features={...ye,...n.features}),n.snapshot&&(this.snapshot=n.snapshot),this.initializeContext(this.snapshot);}configure(e,n){e&&(this.snapshot=e),n?.features?this.features={...ye,...this.features,...n.features}:this.snapshot?.features&&(this.features={...ye,...this.snapshot.features});let r=n?.backgroundColor??this.snapshot?.backgroundColor??this.options.backgroundColor;r&&this.renderer&&this.renderer.setClearColor(new Color(r));let a=n?.pixelRatio??this.snapshot?.pixelRatio??this.options.pixelRatio;this.renderer&&typeof a=="number"&&this.renderer.setPixelRatio(a),e?.layers&&this.features.layers!==false&&this.syncLayerGroups(e.layers),e?.tilesets&&this.features.tilesets!==false&&this.syncTilesets(e.tilesets),e?.tileLayers&&this.features.tilesets!==false&&(this.tileLayers=e.tileLayers.map(o=>({...o,data:[...o.data]}))),this.updateDiagnostics();}render(e,n,r,a={}){if((!this.renderer||!this.scene||!this.camera)&&this.initializeContext(a.snapshot??this.snapshot??e.rendererState),this.configure(a.snapshot??this.snapshot??e.rendererState,a.overrides),this.features.spriteBatching!==false?this.rebuildSpriteBatches(e):this.rebuildSpriteBatches(e,false),this.features.particles!==false?this.updateParticles(e,r):this.particleEmitters.clear(),this.updateCamera(e,n,a.overrides),this.updateDiagnostics(),this.renderer&&this.scene&&this.camera)try{this.renderer.render(this.scene,this.camera);}catch(o){this.handleError(o);}}resize(e,n){!this.renderer||!this.camera||(this.width=Math.max(1,Math.round(e)),this.height=Math.max(1,Math.round(n)),this.renderer.setSize(this.width,this.height,false),this.camera.left=0,this.camera.right=this.width,this.camera.top=0,this.camera.bottom=-this.height,this.camera.updateProjectionMatrix());}setCamera(e,n){this.camera&&(this.camera.position.set(e.x,-e.y,this.camera.position.z),typeof n=="number"&&n>0&&(this.camera.zoom=n,this.camera.updateProjectionMatrix()));}setFeatures(e){this.features={...ye,...e};}dispose(){this.renderer?.dispose(),this.spriteBatches.clear(),this.particleEmitters.clear(),this.layerGroups.clear(),this.tilesets.clear(),this.tileLayers=[],this.canvas?.remove(),this.renderer=null,this.scene=null,this.camera=null,this.canvas=null,this.diagnostics={spriteBatchCount:0,spriteBatches:{},particleEmitters:{},renderLayers:[],tilesets:[],tileLayers:[],initialized:false};}buildTileLayer(e){let n=[];for(let r=0;r<e.height;r++){let a=r*e.width;n.push(e.data.slice(a,a+e.width));}return n}getDiagnostics(){return {...this.diagnostics}}initializeContext(e){if(this.renderer||typeof document>"u"){!this.renderer&&typeof document>"u"&&this.handleError(new H("WebGL not available in the current environment"));return}if(!this.isWebGLSupported()){this.handleError(new H("WebGL context could not be created"));return}this.canvas=document.createElement("canvas"),this.container.appendChild(this.canvas);let n=this.container.clientWidth||800,r=this.container.clientHeight||600;this.width=n,this.height=r;try{this.renderer=new WebGLRenderer({canvas:this.canvas,antialias:this.options.antialias??e?.antialias??!0,alpha:!0});}catch{this.handleError(new ue("Failed to initialize Three.js renderer"));return}this.renderer.setSize(n,r,false);let a=this.options.pixelRatio??e?.pixelRatio;typeof a=="number"&&this.renderer.setPixelRatio(a),this.scene=new Scene,this.camera=new OrthographicCamera(0,n,0,-r,-1e3,1e3),this.camera.position.set(n/2,-r/2,100),this.camera.lookAt(n/2,-r/2,0);let o=this.options.backgroundColor??e?.backgroundColor;o&&this.renderer.setClearColor(new Color(o)),this.clock.start(),this.configure(e);}isWebGLSupported(){try{let e=document.createElement("canvas");return !!(e.getContext("webgl")||e.getContext("experimental-webgl"))}catch{return false}}syncLayerGroups(e){if(!this.scene)return;let n=new Set(this.layerGroups.keys());e.forEach((r,a)=>{let o=r.id;if(!o)return;let i=this.layerGroups.get(o);i||(i=new Group,i.name=o,this.layerGroups.set(o,i),this.scene.add(i)),i.renderOrder=typeof r.zIndex=="number"?r.zIndex:a,n.delete(o);}),n.forEach(r=>{let a=this.layerGroups.get(r);a&&this.scene&&this.scene.remove(a),this.layerGroups.delete(r);});}syncTilesets(e){let n=new Set(this.tilesets.keys());e.fo