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

AutoMiningScript

【SpaceEngineer】全自动挖矿脚本

公开
关注 0 Fork 0 Star 0
UTF-8
using System;
using System.Linq;
using System.Reflection;
using System.Runtime.Remoting;
using Sandbox.ModAPI.Ingame;
using VRageMath;
using Xunit;
using P=AutoMiningScript.Program;

namespace AutoMiningScript.Tests
{
    public class SurveyNavigationTests
    {
        static readonly BindingFlags Private=BindingFlags.Instance|BindingFlags.NonPublic;
        static T Read<T>(object instance,string name) {return (T)instance.GetType().GetField(name,Private).GetValue(instance);}
        static void Write(object instance,string name,object value) {instance.GetType().GetField(name,Private).SetValue(instance,value);}
        static void Call(object instance,string name,params object[] args) {instance.GetType().GetMethod(name,Private).Invoke(instance,args);}

        sealed class SurveyRig
        {
            public readonly MinerStateTests.MinerRig R=new MinerStateTests.MinerRig();
            public readonly P.FlightController Flight;
            public P.Job Job;
            public MatrixD Orientation=MatrixD.Identity;
            public Vector3D Velocity;
            public bool Ready=true;
            public int NavigationRays,SurveyRays;
            public Func<Vector3D,MyDetectedEntityInfo> Observation=end=>default(MyDetectedEntityInfo);
            public SurveyRig()
            {
                R.Task();Job=Read<P.Job>(R.Miner,"job");Job.Kind=P.JobKind.Survey;Job.Depth=2000;Job.EntityId=0;
                R.State(P.FlightState.Transit);Flight=Read<P.FlightController>(R.Miner,"flight");
                R.Controller.Method("get_WorldMatrix",a=>MatrixD.CreateWorld(R.Position,Orientation.Forward,Orientation.Up))
                    .Method("GetShipVelocities",a=>new MyShipVelocities(Velocity,Vector3D.Zero));
                foreach(var thrust in R.Hardware.Thrusters)
                {
                    var proxy=(Stub<IMyThrust>)RemotingServices.GetRealProxy(thrust);var local=thrust.WorldMatrix;
                    proxy.Values.Remove("WorldMatrix");proxy.Method("get_WorldMatrix",a=>local*Orientation);
                }
                var gyro=(Stub<IMyGyro>)RemotingServices.GetRealProxy(R.Hardware.Gyros[0]);
                gyro.Values.Remove("WorldMatrix");gyro.Method("get_WorldMatrix",a=>Orientation);
                // A single forward camera matches miners 2-4 in the reported save.
                R.Camera.Set("RaycastConeLimit",45f).Set("RaycastDistanceLimit",-1d).Set("EnableRaycast",true)
                    .Method("get_WorldMatrix",a=>MatrixD.CreateWorld(R.Position,Orientation.Forward,Orientation.Up))
                    .Method("CanScan",a=>{
                        Vector3D local=Vector3D.TransformNormal((Vector3D)a[0]-R.Position,MatrixD.Transpose(Orientation));
                        return Ready && -local.Z>0 && Math.Abs(Math.Atan2(local.X,-local.Z))<=Math.PI/4 &&
                            Math.Abs(Math.Atan2(local.Y,Math.Sqrt(local.X*local.X+local.Z*local.Z)))<=Math.PI/4;
                    }).Method("Raycast",a=>{
                        if(R.StateNow==P.FlightState.Survey){SurveyRays++;return Observation((Vector3D)a[0]);}
                        NavigationRays++;return default(MyDetectedEntityInfo);
                    });
            }
            public void Tick(bool integrate=true)
            {
                const double dt=.05;R.Program.Now+=dt;
                if(R.StateNow!=P.FlightState.Transit && R.StateNow!=P.FlightState.Survey)return;
                Call(R.Miner,R.StateNow==P.FlightState.Transit?"Transit":"Survey",dt);
                if(!integrate)return;
                Vector3D acceleration=Vector3D.Zero;
                foreach(var thrust in R.Hardware.Thrusters)acceleration+=thrust.WorldMatrix.Backward*thrust.MaxEffectiveThrust*thrust.ThrustOverridePercentage/1000;
                Velocity+=acceleration*dt;R.Position+=Velocity*dt;
                // Ideal attitude response; this fixture validates phase/camera geometry,
                // not the game's inertia, gyro torque or physics.
                Orientation=Read<MatrixD>(Flight,"previousDesired").GetOrientation();
            }
            public P.JobReport Finish()
            {
                for(int n=0;n<1200 && !R.Sent.Any(p=>p.Kind=="RESULT");n++)Tick();
                Assert.Equal(P.FlightState.Survey,R.StateNow);Assert.True(Read<bool>(R.Miner,"continuing"));
                Assert.Equal(1,SurveyRays);
                return P.JobReport.FromIni(Assert.Single(R.Sent,p=>p.Kind=="RESULT").Body);
            }
        }

        [Fact]
        public void SurveyTransitKeepsFacingItsTravelUntilTheObservationPointIsReached()
        {
            var r=new SurveyRig();r.Job.Entry=Vector3D.Right*4;r.Orientation=MatrixD.CreateWorld(Vector3D.Zero,Vector3D.Right,Vector3D.Up);
            r.Tick(false);
            Assert.True(Vector3D.Dot(Read<MatrixD>(r.Flight,"previousDesired").Forward,Vector3D.Right)>.999);
            Assert.Equal(P.FlightState.Transit,r.R.StateNow);
        }

        [Theory]
        [InlineData(0d)]
        [InlineData(1d)]
        public void AtmosphericObservationArrivalKeepsGravityUpBeforeTurningDownForTheScan(double lateral)
        {
            var r=new SurveyRig();Vector3D travel=Vector3D.Normalize(new Vector3D(lateral,0,-1));
            r.Job.Entry=travel*4;r.Job.Direction=Vector3D.Down;r.Job.Up=Vector3D.Forward;
            r.Orientation=MatrixD.CreateWorld(Vector3D.Zero,travel,Vector3D.Up);
            r.R.Controller.Method("GetNaturalGravity",a=>Vector3D.Down*9.81);
            r.Tick(false);MatrixD desired=Read<MatrixD>(r.Flight,"previousDesired");
            Assert.True(Vector3D.Dot(desired.Forward,travel)>.999);
            Assert.True(Vector3D.Dot(desired.Up,Vector3D.Up)>.999);
        }

        [Theory]
        [InlineData(-3.336105458482,1.21255582737,-2.10187508931)]
        [InlineData(1.35609391866,-2.42185151840,-2.33387649985)]
        [InlineData(1.83350782323,2.07241880727,-2.22492569889)]
        [InlineData(.601750779737,-1.50573927145,3.02794924745)]
        public void SavedFinalMetresWithOneForwardCameraReachObservationAndPerformTheScan(double x,double y,double z)
        {
            var r=new SurveyRig();r.Job.Entry=new Vector3D(-67227.817161019266,-123791.63501934477,-161805.93741404629);
            r.Job.Direction=new Vector3D(.6281360745318959,.58511134572200962,-.512922786566399);
            r.Job.Up=new Vector3D(-.74590343947643378,.26512158333808944,-.61101440656135975);
            r.R.Position=r.Job.Entry+new Vector3D(x,y,z);
            r.Orientation=MatrixD.CreateWorld(Vector3D.Zero,r.Job.Direction,r.Job.Up);
            Assert.Equal("SurveyEmpty",r.Finish().Outcome);Assert.True(r.NavigationRays>0);
            Assert.InRange(Vector3D.Distance(r.R.Position,r.Job.Entry),0,1.25);
        }

        [Theory]
        [InlineData(.6)]
        [InlineData(.9)]
        public void ObservationDoesNotDemandASecondTighterPositionCorrectionBeforeScanning(double offset)
        {
            var r=new SurveyRig();r.R.Position=Vector3D.Right*offset;r.R.State(P.FlightState.Survey);
            Vector3D observation=r.R.Position;r.Tick();
            Assert.Equal(1,r.SurveyRays);Assert.Equal(P.FlightState.Survey,r.R.StateNow);Assert.True(Read<bool>(r.R.Miner,"continuing"));
            Assert.InRange(Vector3D.Distance(r.R.Position,observation),0,.001);
            Assert.Equal("SurveyEmpty",P.JobReport.FromIni(Assert.Single(r.R.Sent,p=>p.Kind=="RESULT").Body).Outcome);
        }

        [Fact]
        public void LongRangeAsteroidHitBecomesAHigherPriorityProbeAtTheActualHitPoint()
        {
            var r=new SurveyRig();var planner=new P.TaskPlanner(r.R.Program);
            planner.Survey(Vector3D.Zero,Vector3D.Zero,Vector3D.Forward,Vector3D.Up,2000);
            r.Job=planner.Jobs.First();Write(r.R.Miner,"job",r.Job);
            r.R.Position=r.Job.Entry+Vector3D.Right*4;r.Orientation=MatrixD.CreateWorld(Vector3D.Zero,r.Job.Direction,r.Job.Up);
            var surface=r.Job.Entry+r.Job.Direction*100;
            r.Observation=end=>new MyDetectedEntityInfo(42,"Asteroid",MyDetectedEntityType.Asteroid,surface,MatrixD.Identity,Vector3.Zero,
                VRage.Game.MyRelationsBetweenPlayerAndBlock.NoOwnership,new BoundingBoxD(surface-new Vector3D(10),surface+new Vector3D(10)),1);
            P.JobReport report=r.Finish();Assert.Equal("SurveyHit",report.Outcome);planner.Report(r.Job,report);
            P.Job probe=Assert.Single(planner.Jobs,j=>j.Kind==P.JobKind.Probe);
            Assert.Equal(surface,probe.Entry);Assert.Equal(42,probe.EntityId);Assert.Equal(40,probe.Depth);
            Assert.True(probe.Priority>r.Job.Priority);Assert.NotEqual(Vector3D.Zero,probe.Direction);
        }

        [Fact]
        public void MissingNavigationCoverageReportsTheBlockerInsteadOfOnlyTheOriginalTaskMessage()
        {
            var r=new SurveyRig();r.R.Position=Vector3D.Backward*30;r.Ready=false;
            r.Tick(false);Assert.Equal(P.FlightState.Transit,r.R.StateNow);
            Assert.NotEmpty(r.Flight.Problem);Assert.Contains(r.Flight.Problem,Read<string>(r.R.Miner,"reason"));
            Assert.Equal(0,r.SurveyRays);Assert.DoesNotContain(r.R.Sent,p=>p.Kind=="RESULT");
        }

        [Fact]
        public void ObservationWithoutAChargedCameraWaitsWithoutReportingEmptySpace()
        {
            var r=new SurveyRig();r.Ready=false;r.R.State(P.FlightState.Survey);
            for(int n=0;n<30;n++)r.Tick();
            Assert.Equal(P.FlightState.Survey,r.R.StateNow);Assert.Equal(0,r.SurveyRays);
            Assert.DoesNotContain(r.R.Sent,p=>p.Kind=="RESULT");Assert.Equal(P.L.MinerWaitSurveyScan,Read<string>(r.R.Miner,"reason"));
            r.Ready=true;Assert.Equal("SurveyEmpty",r.Finish().Outcome);
        }
        [Fact]
        public void ProbeTransitReachesTheAlignmentToleranceBeforeTurningToTheBore()
        {
            var r=new SurveyRig();r.Job.Kind=P.JobKind.Probe;r.Job.Depth=40;
            Vector3D approach=r.Job.Entry-r.Job.Direction*22;r.R.Position=approach+Vector3D.Right*.8;
            r.Orientation=MatrixD.CreateWorld(Vector3D.Zero,Vector3D.Left,Vector3D.Up);r.Ready=false;
            r.Tick(false);Assert.Equal(P.FlightState.Transit,r.R.StateNow);
            r.Ready=true;for(int n=0;n<600 && r.R.StateNow==P.FlightState.Transit;n++)r.Tick();
            Assert.Equal(P.FlightState.Align,r.R.StateNow);Assert.InRange(Vector3D.Distance(approach,r.R.Position),0,.5);
            r.Velocity=Vector3D.Zero;r.R.Camera.Method("Raycast",a=>MinerStateTests.MinerRig.Hit(MyDetectedEntityType.Asteroid));
            for(int n=0;n<10 && r.R.StateNow==P.FlightState.Align;n++)
            {Call(r.R.Miner,"Align",.05);r.Orientation=Read<MatrixD>(r.Flight,"previousDesired").GetOrientation();}
            Assert.Equal(P.FlightState.Drilling,r.R.StateNow);
        }
    }
}