XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

AutoMiningScript

【SpaceEngineer】全自动挖矿脚本

公开
关注 0 Fork 0 Star 0
UTF-8
using System;
using System.Collections.Generic;
using System.Reflection;
using Sandbox.ModAPI.Ingame;
using VRage.Game.ModAPI.Ingame.Utilities;
using VRageMath;
using Xunit;
using P=AutoMiningScript.Program;

namespace AutoMiningScript.Tests
{
    public class BreadcrumbCompressionTests
    {
        static readonly BindingFlags Private=BindingFlags.Instance|BindingFlags.NonPublic;
        static T Read<T>(object target,string name)=> (T)target.GetType().GetField(name,Private).GetValue(target);
        static void Write(object target,string name,object value)=>target.GetType().GetField(name,Private).SetValue(target,value);
        static void Call(object target,string name,params object[] arguments)=>target.GetType().GetMethod(name,Private).Invoke(target,arguments);
        static List<Vector3D> Path(MinerStateTests.MinerRig r)=>Read<List<Vector3D>>(r.Miner,"breadcrumbs");
        static MinerStateTests.MinerRig Rig()
        {var r=new MinerStateTests.MinerRig();r.Task();r.State(P.FlightState.Transit);return r;}
        static void Record(MinerStateTests.MinerRig r,Vector3D point)
        {r.Position=point;Call(r.Miner,"RecordBreadcrumb");}

        [Theory]
        [InlineData(false)]
        [InlineData(true)]
        public void FiveKilometreStraightTravelRetainsBothEndpointsWithoutExhaustingTheBudget(bool smallNoise)
        {
            var r=Rig();var origin=new Vector3D(-67700,-123600,-162000);
            for(int n=0;n<=500;n++)Record(r,origin+Vector3D.Right*(n*10.1)+Vector3D.Up*(smallNoise && n>1?Math.Sin(n)*.05:0));
            Assert.Equal(P.FlightState.Transit,r.StateNow);Assert.Equal(2,Path(r).Count);
            Assert.Equal(origin,Path(r)[0]);Assert.Equal(r.Position,Path(r)[1]);
        }

        [Theory]
        [InlineData(90)]
        [InlineData(180)]
        [InlineData(1)]
        public void ARealCornerOrReversalKeepsItsTurningPoint(double degrees)
        {
            var r=Rig();Record(r,Vector3D.Zero);Record(r,Vector3D.Right*11);Record(r,Vector3D.Right*22);
            var corner=r.Position;var direction=new Vector3D(Math.Cos(degrees*Math.PI/180),Math.Sin(degrees*Math.PI/180),0);
            Record(r,corner+direction*11);Record(r,corner+direction*22);
            Assert.Equal(3,Path(r).Count);Assert.Equal(corner,Path(r)[1]);Assert.Equal(r.Position,Path(r)[2]);
        }

        [Fact]
        public void AGradualCurveCannotAccumulateUnboundedDeviationFromRetainedChords()
        {
            var r=Rig();var samples=new List<Vector3D>();
            // Adjacent headings differ by only 0.063 degrees; repeatedly refitting the
            // last chord would otherwise hide an increasingly large circular arc.
            for(int n=0;n<=300;n++)
            {double angle=n*.0011;var point=new Vector3D(10000*Math.Sin(angle),10000*(1-Math.Cos(angle)),0);samples.Add(point);Record(r,point);}
            Assert.Equal(P.FlightState.Transit,r.StateNow);Assert.InRange(Path(r).Count,20,127);
            int index=0;
            for(int n=1;n<Path(r).Count;n++)
            {
                var start=Path(r)[n-1];var end=Path(r)[n];var delta=end-start;
                while(index<samples.Count)
                {
                    var point=samples[index++];double t=Math.Max(0,Math.Min(1,Vector3D.Dot(point-start,delta)/delta.LengthSquared()));
                    Assert.InRange(Vector3D.Distance(point,start+delta*t),0,.200001);
                    if(point==end)break;
                }
            }
            Assert.Equal(samples.Count,index);
        }

        [Fact]
        public void RepeatedRealTurnsStillUseTheExisting128PointReturnBudget()
        {
            var r=Rig();var point=Vector3D.Zero;Record(r,point);
            for(int n=1;n<=128;n++){point+=(n%2==0?Vector3D.Right:Vector3D.Up)*11;Record(r,point);}
            Assert.Equal(128,Path(r).Count);Assert.Equal(P.FlightState.Returning,r.StateNow);
            Assert.Equal(P.L.MinerRouteBudgetFull,Read<string>(r.Miner,"reason"));Assert.Equal(127,Read<int>(r.Miner,"returnIndex"));
        }

        [Fact]
        public void RestoringACompressedPathKeepsItsPriorSegmentAndStartsANewBearing()
        {
            var r=Rig();Record(r,Vector3D.Zero);Record(r,Vector3D.Right*11);Record(r,Vector3D.Right*1000);
            var saved=new MyIni();r.Miner.Save(saved);Assert.Equal(2,saved.Get("miner","BreadcrumbCount").ToInt32());
            var restored=Rig();restored.Miner.Load(saved);Assert.Equal(Path(r),Path(restored));
            restored.State(P.FlightState.Transit);Record(restored,Vector3D.Right*1011);Record(restored,Vector3D.Right*1022);
            Assert.Equal(3,Path(restored).Count);Assert.Equal(Vector3D.Right*1000,Path(restored)[1]);
        }

        [Theory]
        [InlineData(P.FlightState.Returning,1,0,2)]
        [InlineData(P.FlightState.Returning,-1,0,0)]
        [InlineData(P.FlightState.Holding,-1,0,0)]
        [InlineData(P.FlightState.DockAlign,-1,0,0)]
        [InlineData(P.FlightState.DockApproach,-1,0,0)]
        [InlineData(P.FlightState.DockRetreat,-1,2,0)]
        [InlineData(P.FlightState.Fault,-1,3,0)]
        [InlineData(P.FlightState.Transit,-1,1,3)]
        [InlineData(P.FlightState.Drilling,-1,0,3)]
        [InlineData(P.FlightState.Fault,-1,0,3)]
        public void ReloadResumesOnlyUnfinishedReturnLegsAndRetainsOutboundPaths(P.FlightState state,int remaining,int failures,int expected)
        {
            var r=Rig();var points=new[]{Vector3D.Zero,Vector3D.Right*20,Vector3D.Right*20+Vector3D.Up*1000};
            Path(r).AddRange(points);r.State(state);Write(r.Miner,"returnIndex",remaining);Write(r.Miner,"retry",failures);
            var saved=new MyIni();r.Miner.Save(saved);
            Assert.Equal(3,Path(r).Count);Assert.Equal(expected,saved.Get("miner","BreadcrumbCount").ToInt32());
            var restored=Rig();restored.Connector.Set("Status",MyShipConnectorStatus.Unconnected);restored.Miner.Load(saved);
            Assert.Equal(expected,Path(restored).Count);
            for(int n=0;n<expected;n++)Assert.Equal(points[n],Path(restored)[n]);
            restored.Miner.Command("return");
            Assert.Equal(P.FlightState.Returning,restored.StateNow);Assert.Equal(expected-1,Read<int>(restored.Miner,"returnIndex"));
            Assert.Null(Read<P.DockFrame>(restored.Miner,"dock"));
        }

        [Fact]
        public void ReturningAlongACompressedSegmentStillRequiresFreshEnvelopeRays()
        {
            var r=Rig();for(int n=0;n<=200;n++)Record(r,Vector3D.Forward*(n*11));
            r.State(P.FlightState.Returning);Write(r.Miner,"returnIndex",Path(r).Count-1);
            r.Controller.Method("get_WorldMatrix",a=>MatrixD.CreateWorld(r.Position,Vector3D.Backward,Vector3D.Up));
            bool ready=false;int rays=0;
            r.Camera.Set("RaycastConeLimit",45f).Set("RaycastDistanceLimit",-1d)
                .Method("get_WorldMatrix",a=>r.Controller.Value.WorldMatrix).Method("CanScan",a=>ready)
                .Method("Raycast",a=>{rays++;return default(MyDetectedEntityInfo);});
            var flight=Read<P.FlightController>(r.Miner,"flight");
            for(int n=0;n<30;n++){r.Program.Now+=.05;Call(r.Miner,"ReturnHome",.05);}
            Assert.Equal(0,rays);Assert.Equal(0,flight.CommandSpeed);Assert.All(r.Hardware.Thrusters,t=>Assert.Equal(0,t.ThrustOverridePercentage));
            ready=true;for(int n=0;n<15;n++){r.Program.Now+=.05;Call(r.Miner,"ReturnHome",.05);}
            Assert.True(rays>=18);Assert.True(flight.ScanClearance>0);Assert.True(flight.CommandSpeed>0);
        }
    }
}