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;
using VRage.Game.ModAPI.Ingame;
using VRageMath;
using Xunit;
using P = AutoMiningScript.Program;

namespace AutoMiningScript.Tests
{
    public class ReturnNavigationTests
    {
        internal sealed class Rig
        {
            public readonly P Program = TestRig.Program();
            public readonly P.ShipHardware Ship;
            public readonly P.FlightController Flight;
            public readonly Stub<IMyShipController> Controller;
            public MatrixD Orientation = MatrixD.Identity;
            public readonly Stub<IMyGyro> Gyro;
            public readonly Stub<IMyCameraBlock> Camera;
            public readonly List<Stub<IMyThrust>> Thrusters = new List<Stub<IMyThrust>>();
            public int Rays;
            public bool CameraReady = true;
            public double Charge = 100000;
            public Func<MyDetectedEntityInfo> Hit = () => default(MyDetectedEntityInfo);
            public Rig()
            {
                Ship = new P.ShipHardware(Program) { Radius = 2, BodyHalfSize = new Vector3D(1), BodyMin = new Vector3D(-1), BodyMax = new Vector3D(1) };
                Controller = new Stub<IMyShipController>().Set("CubeGrid", Program.Me.CubeGrid).Set("IsFunctional",true)
                    .Method("get_WorldMatrix", a => Orientation).Method("GetPosition", a => Vector3D.Zero)
                    .Method("CalculateShipMass", a => new MyShipMass(1000, 1000, 1000));
                Ship.Controller = Controller.Value;
                Gyro = new Stub<IMyGyro>().Set("CubeGrid", Program.Me.CubeGrid).Set("IsFunctional", true)
                    .Method("get_WorldMatrix", a => Orientation);
                Ship.Gyros.Add(Gyro.Value);
                foreach (Vector3D axis in new[] { Vector3D.Right, Vector3D.Left, Vector3D.Up, Vector3D.Down, Vector3D.Forward, Vector3D.Backward })
                {
                    MatrixD local = MatrixD.CreateWorld(Vector3D.Zero, -axis, Math.Abs(axis.Y) > .9 ? Vector3D.Forward : Vector3D.Up);
                    var thrust = new Stub<IMyThrust>().Set("CubeGrid", Program.Me.CubeGrid).Set("IsFunctional", true).Set("Enabled", true)
                        .Set("MaxEffectiveThrust", 100000f).Method("get_WorldMatrix", a => local * Orientation);
                    Ship.Thrusters.Add(thrust.Value); Thrusters.Add(thrust);
                }
                Camera = MakeCamera(1, () => Charge, () => Hit());
                Ship.Cameras.Add(Camera.Value);
                Flight = new P.FlightController(Program, Ship);
            }
            public Stub<IMyCameraBlock> MakeCamera(long id, Func<double> range, Func<MyDetectedEntityInfo> hit)
            {
                return new Stub<IMyCameraBlock>().Set("EntityId", id).Set("CubeGrid", Program.Me.CubeGrid).Set("IsWorking", true)
                    .Set("IsFunctional",true).Set("Enabled",true).Set("EnableRaycast",true).Set("RaycastDistanceLimit",-1d)
                    .Set("RaycastConeLimit", 45f).Method("get_AvailableScanRange", a => range())
                    .Method("get_WorldMatrix", a => Orientation).Method("GetPosition", a => Vector3D.Zero)
                    .Method("CanScan", a => {
                        var target = (Vector3D)a[0];
                        var local = Vector3D.TransformNormal(target, MatrixD.Transpose(Orientation));
                        // Real world-target semantics include both camera pitch/yaw and charge.
                        return CameraReady && target.Length() <= range() && -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 => { Rays++; Charge -= ((Vector3D)a[0]).Length(); return hit(); });
            }
            public void Step(Vector3D target, bool faceTravel = false, double recharge = 0)
            { Program.Now += .05; Charge += recharge * .05; Flight.Move(target, Vector3D.Zero, Vector3D.Forward, Vector3D.Up, 10, 1, true, .05, faceTravel); }
            public bool HasTranslation { get { foreach (var t in Thrusters) if (t.Value.ThrustOverridePercentage > .0001) return true; return false; } }
            public static MyDetectedEntityInfo Detection(long entity, double distance=10)
            { return new MyDetectedEntityInfo(entity, "fixture", MyDetectedEntityType.LargeGrid, Vector3D.Forward * distance, MatrixD.Identity, Vector3.Zero, MyRelationsBetweenPlayerAndBlock.NoOwnership, new BoundingBoxD(new Vector3D(-1), new Vector3D(1)), 1); }
        }

        [Fact]
        public void OpenSpaceReturnFacesTheTravelDirectionBeforeTranslatingWithAForwardCamera()
        {
            var r = new Rig(); var target = Vector3D.Right * 100;
            r.Step(target, true);
            Assert.True(Math.Abs(r.Gyro.Value.Yaw) > .1); Assert.False(r.HasTranslation); Assert.Equal(0, r.Flight.ScanClearance);
            r.Orientation = MatrixD.CreateWorld(Vector3D.Zero, Vector3D.Right, Vector3D.Up);
            for (int n = 0; n < 12; n++) r.Step(target, true);
            Assert.True(r.Flight.ScanClearance > r.Ship.Radius); Assert.True(r.HasTranslation); Assert.True(r.Rays >= 18);
        }

        [Fact]
        public void ACollinearRouteExtensionKeepsItsFreshClearanceAndMotion()
        {
            var r=new Rig();for(int n=0;n<9;n++)r.Step(Vector3D.Forward*100);
            double clear=r.Flight.ScanClearance;Assert.True(clear>0);
            r.Step(Vector3D.Forward*200);
            Assert.True(r.Flight.ScanClearance>=clear);Assert.True(r.HasTranslation);
        }

        [Fact]
        public void ExtendingAShortLegDoesNotExtendItsEvidenceBeforeTheNextSweep()
        {
            var r=new Rig();for(int n=0;n<9;n++)r.Step(Vector3D.Forward*5);
            double clear=r.Flight.ScanClearance;Assert.InRange(clear,5,7);
            r.Step(Vector3D.Forward*100);Assert.InRange(r.Flight.ScanClearance,.01,clear);
        }

        [Fact]
        public void AChangedRouteDirectionStillInvalidatesPreviousClearance()
        {
            var r=new Rig();for(int n=0;n<9;n++)r.Step(Vector3D.Forward*100);
            Assert.True(r.Flight.ScanClearance>0);r.Step(Vector3D.Right*100);
            Assert.Equal(0,r.Flight.ScanClearance);Assert.False(r.HasTranslation);
        }

        [Fact]
        public void DockingDoesNotOptIntoTravelAttitudeOrTranslateWithoutSideCoverage()
        {
            var r = new Rig();
            for (int n = 0; n < 20; n++) r.Step(Vector3D.Right * 100);
            Assert.Equal(0, r.Gyro.Value.Yaw); Assert.False(r.HasTranslation); Assert.Equal(0, r.Flight.ScanClearance);
        }

        [Fact]
        public void GravityReturnRejectsAnUnsupportedTravelAttitudeAndMaintainsTheCurrentHover()
        {
            var r = new Rig(); Vector3D gravity = Vector3D.Down * 9.81;
            r.Controller.Method("GetNaturalGravity", a => gravity);
            // The current vertical engines can hover. The fore/aft engines cannot support
            // the ship if pointing its nose vertically swaps them into the lift direction.
            foreach (var thrust in r.Thrusters)
                if (Math.Abs(Vector3D.Dot(thrust.Value.WorldMatrix.Backward, Vector3D.Forward)) > .9)
                    thrust.Set("MaxEffectiveThrust", 2000f);
            r.Step(Vector3D.Down * 100, true);
            Assert.True(r.Flight.Blocked); Assert.Equal(P.L.F(P.L.MinerHoverReserve, 30d), r.Flight.Problem);
            Assert.Equal(0, r.Gyro.Value.Pitch); Assert.Equal(0, r.Gyro.Value.Yaw); Assert.Equal(0, r.Gyro.Value.Roll);
            Assert.Equal(0, r.Rays); Assert.Equal(0, r.Flight.ScanClearance);
            Vector3D acceleration = gravity;
            foreach (var thrust in r.Thrusters)
                acceleration += thrust.Value.WorldMatrix.Backward * thrust.Value.MaxEffectiveThrust * thrust.Value.ThrustOverridePercentage / 1000;
            Assert.True(acceleration.Length() < .00001, "The rejected turn must retain gravity support without accelerating along the requested route: " + acceleration);
        }

        [Fact]
        public void AnObstacleDetourFacesItsOwnLegAndStillRequiresACompleteEnvelope()
        {
            var r = new Rig(); r.Hit = () => Rig.Detection(42,.8);
            r.Step(Vector3D.Forward * 100, true);
            var flags = BindingFlags.Instance | BindingFlags.NonPublic;
            Assert.True((bool)typeof(P.FlightController).GetField("hasDetour", flags).GetValue(r.Flight));
            var detour = (Vector3D)typeof(P.FlightController).GetField("detour", flags).GetValue(r.Flight);
            r.Hit = () => default(MyDetectedEntityInfo);
            r.Step(Vector3D.Forward * 100, true);
            Assert.True(Math.Abs(r.Gyro.Value.Yaw) > .1); Assert.False(r.HasTranslation);
            r.Orientation = MatrixD.CreateWorld(Vector3D.Zero, Vector3D.Normalize(detour), Vector3D.Up);
            for (int n = 0; n < 12; n++) r.Step(Vector3D.Forward * 100, true);
            Assert.True(r.Flight.ScanClearance > r.Ship.Radius); Assert.True(r.HasTranslation);
        }

        [Fact]
        public void AnObstructedCameraDoesNotStarveAnotherCameraWithClearSight()
        {
            var r = new Rig(); int obstructed = 0, clear = 0;
            r.Hit = () => { obstructed++; return Rig.Detection(r.Program.Me.CubeGrid.EntityId); };
            r.Ship.Cameras.Add(r.MakeCamera(2, () => 10000, () => { clear++; return default(MyDetectedEntityInfo); }).Value);
            for (int n = 0; n < 30 && r.Flight.ScanClearance == 0; n++) r.Step(Vector3D.Forward * 100);
            Assert.Equal(18, obstructed); Assert.Equal(18, clear); Assert.True(r.Flight.ScanClearance > 0); Assert.True(r.HasTranslation);
        }

        [Fact]
        public void SelfHitsNeverCertifyClearSpace()
        {
            var r = new Rig(); r.Hit = () => Rig.Detection(r.Program.Me.CubeGrid.EntityId);
            for (int n = 0; n < 100; n++) r.Step(Vector3D.Forward * 100);
            Assert.Equal(0, r.Flight.ScanClearance); Assert.False(r.HasTranslation);
        }

        [Fact]
        public void APreviousClearanceExpiresEvenWhileItsReplacementWaitsForCameraCharge()
        {
            var r = new Rig();
            for (int n = 0; n < 9; n++) r.Step(Vector3D.Forward * 100);
            Assert.True(r.Flight.ScanClearance > 0);
            r.CameraReady = false;
            for (int n = 0; n < 31; n++) r.Step(Vector3D.Forward * 100);
            Assert.Equal(0, r.Flight.ScanClearance); Assert.False(r.HasTranslation);
        }

        [Fact]
        public void LimitedCameraRechargeShortensTheEnvelopeInsteadOfRepeatingTheSameUnfinishableScan()
        {
            var r = new Rig(); r.Charge = 0; bool completed = false;
            for (int n = 0; n < 300; n++)
            {
                r.Step(Vector3D.Forward * 100, false, 100);
                if (r.Flight.ScanClearance > 0) { completed = true; break; }
                Assert.False(r.HasTranslation);
            }
            Assert.True(completed); Assert.True(r.Flight.ScanClearance < 36); Assert.True(r.HasTranslation);
        }
    }
}