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

AutoMiningScript

【SpaceEngineer】全自动挖矿脚本

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

namespace AutoMiningScript.Tests
{
    public class DockRecoveryTests
    {
        // Recorded positions from the stalled 11:06 save. Keep them in the test
        // rather than depending on an untracked local save under obj/.
        static readonly Vector3D SavedForward=new Vector3D(-.7583930492401123,.43053719401359558,.4893641471862793);
        static readonly Vector3D SavedUp=new Vector3D(.62588357925415039,.27143645286560059,.73115783929824829);
        static readonly Vector3D FourPort=new Vector3D(-67812.319486450288,-123701.75128268334,-162123.89634542784);
        static readonly Vector3D FivePort=new Vector3D(-67799.8018148652,-123696.32255362603,-162109.27318864188);
        static readonly Vector3D FourPosition=new Vector3D(-67923.280464637908,-123859.14376708004,-161997.76596521458);
        static readonly Vector3D FourStart=new Vector3D(-67920.109540823731,-123850.31650989386,-162001.60530758521);
        static readonly Vector3D FivePosition=new Vector3D(-67846.562285724911,-123647.61979939672,-162059.99176219723);
        static readonly Vector3D OnePosition=new Vector3D(-67811.91664713982,-123698.84802260133,-162118.48642270884);
        static readonly Vector3D TwoPosition=new Vector3D(-67845.112023729715,-123659.2111615825,-162077.86188009998);
        static readonly Vector3D TwoWaitingPoint=new Vector3D(-67849.776998099245,-123665.0629307388,-162050.74675762336);

        static void SeedLane(FleetStateTests.Rig rig,int miner,long dock,Vector3D position,Vector3D start,Vector3D forward,Vector3D up,string kind="DOCK_REQUEST")
        {
            rig.RegisterAll();
            var saved=rig.Save();
            saved.Set("fleet","LaneCount",1);
            saved.Set("fleet","LaneOwner",rig.Miners[miner].Id);
            saved.Set("fleet","LaneKind",kind);
            saved.Set("fleet","LaneDock",dock);
            saved.Set("fleet","LaneToken","obsolete-before-restart");
            P.Data.PutVector(saved,"fleet","LanePosition",position);
            P.Data.PutVector(saved,"fleet","LaneStart",start);
            P.Data.PutVector(saved,"fleet","LaneForward",forward);
            P.Data.PutVector(saved,"fleet","LaneUp",up);
            saved.Set("fleet","LaneRadius",Math.Max(3,rig.Miners[miner].Radius));
            saved.Set("fleet","LaneClearance",60);
            rig.Fleet.Load(saved);
            // Exercise the same record storage restoration used by live reloads.
            rig.Restore(rig.Checkpoint());
        }

        static FleetStateTests.Rig BlockedReturn(P.FlightState state=P.FlightState.Holding,bool outside=true,string kind="DOCK_REQUEST",bool occupied=false)
        {
            var rig=new FleetStateTests.Rig(2,false);
            rig.Miners[0].State=state;
            rig.Miners[0].Position=new Vector3D(0,0,outside?-100:-15);
            rig.Miners[1].Position=new Vector3D(0,0,-30);
            rig.Miners[1].State=P.FlightState.Holding;
            if(occupied)
            {
                // A different miner is physically connected to the original berth.
                // A future approach line alone no longer prevents an inbound grant.
                rig.SetConnected(0,true);rig.Miners[0].Connected=false;rig.Miners[1].Connected=true;
                rig.Miners[1].ConnectorId=rig.Miners[0].ConnectorId;rig.Miners[0].ConnectorId=9999;
            }
            SeedLane(rig,0,100,Vector3D.Zero,rig.Miners[0].Position,Vector3D.Forward,Vector3D.Up,kind);
            rig.RegisterAll();rig.Tick(30);rig.Sent.Clear();
            return rig;
        }

        static void AssertOriginalLaneRetained(FleetStateTests.Rig rig,bool renewed=false)
        {
            var saved=rig.Save();
            Assert.Equal(1,P.Data.ReadLong(saved,"fleet","LaneCount"));
            Assert.Equal(rig.Miners[0].Id,saved.Get("fleet","LaneOwner").ToString());
            Assert.Equal(100,P.Data.ReadLong(saved,"fleet","LaneDock"));
            if(renewed)
            {
                Assert.NotEmpty(saved.Get("fleet","LaneToken").ToString());
                var grant=P.DockFrame.FromIni(Assert.Single(rig.Sent,p=>p.Kind=="DOCK_GRANT").Body);
                Assert.Equal(100,grant.ConnectorId);Assert.Equal(saved.Get("fleet","LaneToken").ToString(),grant.Token);
            }
            else
            {
                Assert.Equal("",saved.Get("fleet","LaneToken").ToString());
                Assert.DoesNotContain(rig.Sent,p=>p.Kind=="DOCK_GRANT");
            }
        }

        [Fact]
        public void SavedFourCanUseItsFreeBerthBesideTheAlreadyDockedOne()
        {
            var rig=new FleetStateTests.Rig(3,false);
            rig.SetPort(0,FourPort,SavedForward);
            rig.SetPort(1,OnePosition,SavedForward);
            rig.SetPort(2,FivePort,SavedForward);
            var four=rig.Miners[0];four.Id="深空工业4号";four.Position=FourPosition;four.State=P.FlightState.Returning;four.Radius=4.1124754356677382;
            var one=rig.Miners[1];one.Id="深空工业1号";one.Position=OnePosition;one.Radius=4.631461208568588;rig.SetConnected(1,true);
            rig.Miners[2].Position=FivePort+SavedUp*1000;
            SeedLane(rig,0,100,FourPort,FourStart,SavedForward,SavedUp);
            rig.RegisterAll();rig.Tick(30);
            Assert.DoesNotContain(rig.Sent,p=>p.Kind=="DOCK_GRANT");

            rig.Receive("DOCK_REQUEST",four);rig.Tick(12);

            var granted=P.DockFrame.FromIni(Assert.Single(rig.Sent,p=>p.Kind=="DOCK_GRANT"&&p.Target==four.Id).Body);
            Assert.Equal(100,granted.ConnectorId);
            Assert.NotEqual("obsolete-before-restart",granted.Token);
            Assert.NotEmpty(granted.Token);
            Assert.Equal("",granted.Problem);
            Assert.True(one.Connected);
        }

        [Fact]
        public void SavedFiveGetsItsBerthWithoutHoldingTwoForTheFutureApproachLine()
        {
            var rig=new FleetStateTests.Rig(1,false);
            rig.SetPort(0,FivePort,SavedForward);
            var five=rig.Miners[0];five.Id="深空工业5号";five.Position=FivePosition;five.State=P.FlightState.Holding;five.Radius=4.11247548414806;
            five.HasRoute=true;five.RouteTarget=new Vector3D(-67846.136002352228,-123647.842939467,-162060.25410989788);
            var two=new P.Telemetry {Id="深空工业2号",Position=TwoPosition,State=P.FlightState.Holding,Radius=4.1124763592894293,
                HasRoute=true,RouteTarget=TwoWaitingPoint,Battery=1,Hydrogen=1,ThrustMargin=2.2698880858019943};
            rig.Miners.Add(two);
            SeedLane(rig,0,100,FivePort,FivePosition,SavedForward,SavedUp);
            rig.RegisterAll();rig.Tick(30);
            var initial=rig.Sent.Last(p=>p.Kind=="TRAFFIC"&&p.Target==two.Id);
            Assert.False(P.Data.Flag(initial.Body,"traffic","Wait"));
            Assert.False(P.Data.Flag(initial.Body,"traffic","Avoid"));
            rig.Sent.Clear();

            rig.Receive("DOCK_REQUEST",five);rig.Receive("TELEMETRY",two);rig.Tick(12);

            var granted=P.DockFrame.FromIni(Assert.Single(rig.Sent,p=>p.Kind=="DOCK_GRANT"&&p.Target==five.Id).Body);
            Assert.Equal(100,granted.ConnectorId);Assert.NotEmpty(granted.Token);Assert.Equal("",granted.Problem);
            var traffic=rig.Sent.Last(p=>p.Kind=="TRAFFIC"&&p.Target==two.Id);
            Assert.False(P.Data.Flag(traffic.Body,"traffic","Wait"));
            Assert.False(P.Data.Flag(traffic.Body,"traffic","Avoid"));
            var waiting=P.DockFrame.FromIni(rig.Sent.Last(p=>p.Kind=="DOCKPOSE"&&p.Target==five.Id).Body);
            Assert.Equal(granted.Token,waiting.Token);Assert.Equal("",waiting.Problem);
            Assert.Equal(1,P.Data.ReadLong(rig.Save(),"fleet","LaneCount"));
            Assert.Equal(TwoPosition,two.Position);
        }

        [Theory]
        [InlineData(P.FlightState.Returning)]
        [InlineData(P.FlightState.Holding)]
        public void FreshReturningAndHoldingRequestsReuseAFreeBerthDespiteDistantShipsOnTheApproach(P.FlightState phase)
        {
            var rig=BlockedReturn(phase);
            rig.Receive("DOCK_REQUEST",rig.Miners[0]);rig.Tick(12);
            var granted=P.DockFrame.FromIni(Assert.Single(rig.Sent,p=>p.Kind=="DOCK_GRANT").Body);
            Assert.Equal(100,granted.ConnectorId);
            Assert.NotEmpty(granted.Token);
        }

        [Theory]
        [InlineData(P.FlightState.DockAlign)]
        [InlineData(P.FlightState.DockApproach)]
        [InlineData(P.FlightState.Departing)]
        [InlineData(P.FlightState.DockRetreat)]
        public void ActiveOrRetreatingDockingPhaseRenewsItsOriginalFreeBerth(P.FlightState phase)
        {
            var rig=BlockedReturn(phase);
            rig.Receive("DOCK_REQUEST",rig.Miners[0]);rig.Tick(12);
            AssertOriginalLaneRetained(rig,true);
        }

        [Fact]
        public void NearPortHoldingMinerRenewsItsOriginalFreeBerth()
        {
            var rig=BlockedReturn(P.FlightState.Holding,false);
            rig.Receive("DOCK_REQUEST",rig.Miners[0]);rig.Tick(12);
            AssertOriginalLaneRetained(rig,true);
        }

        [Theory]
        [InlineData(-.1,false)]
        [InlineData(0,false)]
        [InlineData(.1,true)]
        public void ReassignmentUsesTheAssignedConnectorEntryDistanceAndMinerMargin(double extra,bool canReassign)
        {
            var rig=BlockedReturn(occupied:true);
            var entry=P.Data.ReadNumber(rig.Save(),"fleet","LaneApproachDistance",0);Assert.InRange(entry,30,40);
            var distance=entry+rig.Miners[0].Radius+2+extra;
            rig.Miners[0].Position=new Vector3D(0,0,-distance);
            rig.Miners[1].Position=Vector3D.Zero;
            rig.Receive("TELEMETRY",rig.Miners[1]);
            rig.Receive("DOCK_REQUEST",rig.Miners[0]);rig.Tick(12);
            if(!canReassign)AssertOriginalLaneRetained(rig);
            else Assert.Equal(101,P.DockFrame.FromIni(Assert.Single(rig.Sent,p=>p.Kind=="DOCK_GRANT").Body).ConnectorId);
        }

        [Fact]
        public void LegacyLaneGeometryLoadsAsAConnectorAssignmentAndIsNotWrittenBack()
        {
            var rig=new FleetStateTests.Rig(1,false);
            rig.Miners[0].State=P.FlightState.Holding;rig.Miners[0].Position=Vector3D.Forward*100;
            SeedLane(rig,0,100,Vector3D.Zero,Vector3D.Forward*100,Vector3D.Forward,Vector3D.Up);
            var saved=rig.Save();
            Assert.Equal(100,P.Data.ReadLong(saved,"fleet","LaneDock"));Assert.Equal(rig.Miners[0].Id,P.Data.Text(saved,"fleet","LaneOwner"));
            Assert.Equal(Vector3D.Zero,P.Data.ReadVector(saved,"fleet","LanePosition",Vector3D.One));
            Assert.Equal(30,P.Data.ReadNumber(saved,"fleet","LaneApproachDistance",0));
            Assert.Equal(60,P.Data.ReadNumber(saved,"fleet","LaneClearance",0));
            foreach(var key in new[]{"LaneStart","LaneForward","LaneUp","LaneRadius"})Assert.False(saved.ContainsKey("fleet",key));
            Assert.Empty(P.Data.Text(saved,"fleet","LaneToken"));
            rig.RegisterAll();rig.Tick(30);rig.Receive("DOCK_REQUEST",rig.Miners[0]);rig.Tick(12);
            var grant=P.DockFrame.FromIni(Assert.Single(rig.Sent,p=>p.Kind=="DOCK_GRANT").Body);
            Assert.Equal(100,grant.ConnectorId);Assert.Equal(30,grant.ApproachDistance);Assert.NotEmpty(grant.Token);
        }

        [Fact]
        public void RegistrationAloneDoesNotAuthorizeReplacingRestoredOccupancy()
        {
            var rig=BlockedReturn();
            rig.Receive("TELEMETRY",rig.Miners[0]);rig.Tick(12);
            AssertOriginalLaneRetained(rig);
        }

        [Fact]
        public void StaleTelemetryCannotReleaseAPreviouslyConsentedReservation()
        {
            var rig=BlockedReturn(P.FlightState.Holding,false,occupied:true);
            rig.Receive("DOCK_REQUEST",rig.Miners[0]);
            rig.Miners[0].Position=new Vector3D(0,0,-100);
            rig.Receive("TELEMETRY",rig.Miners[0]);
            rig.Program.Now+=4;rig.Tick(12);
            AssertOriginalLaneRetained(rig);
        }

        [Fact]
        public void LostMinerReservationRemainsProtectedAfterReload()
        {
            var rig=BlockedReturn();
            rig.Program.Now+=60;rig.Receive("TELEMETRY",rig.Miners[1]);rig.Tick(12);
            AssertOriginalLaneRetained(rig);
        }

        [Fact]
        public void ValidCurrentGrantIsNotReassignedByAnotherRequest()
        {
            var rig=new FleetStateTests.Rig(2,false);
            rig.Miners[0].Position=new Vector3D(0,0,-100);rig.Miners[0].State=P.FlightState.Returning;
            rig.Miners[1].Position=new Vector3D(1000,0,-30);
            rig.RegisterAll();rig.Tick(30);rig.Receive("DOCK_REQUEST",rig.Miners[0]);rig.Tick(12);
            var original=P.DockFrame.FromIni(Assert.Single(rig.Sent,p=>p.Kind=="DOCK_GRANT").Body);
            Assert.Equal(100,original.ConnectorId);

            rig.Miners[1].Position=new Vector3D(0,0,-30);rig.Receive("TELEMETRY",rig.Miners[1]);rig.Sent.Clear();
            rig.Receive("DOCK_REQUEST",rig.Miners[0]);rig.Tick(12);

            Assert.All(rig.Sent.Where(p=>p.Kind=="DOCK_GRANT"),p=>
            {
                var repeated=P.DockFrame.FromIni(p.Body);
                Assert.Equal(original.Token,repeated.Token);
                Assert.Equal(original.ConnectorId,repeated.ConnectorId);
            });
            Assert.Equal(original.Token,rig.Save().Get("fleet","LaneToken").ToString());
        }

        [Fact]
        public void OccupiedDockExplainsWhyAnInboundRequestIsWaiting()
        {
            var rig=new FleetStateTests.Rig(1,true);
            var waiting=rig.Miners[0];waiting.Connected=false;waiting.ConnectorId=9999;
            waiting.Position=new Vector3D(0,0,-100);waiting.State=P.FlightState.Holding;
            rig.RegisterAll();rig.Tick(30);rig.Receive("DOCK_REQUEST",waiting);rig.Tick(12);
            Assert.DoesNotContain(rig.Sent,p=>p.Kind=="DOCK_GRANT");
            var pose=P.DockFrame.FromIni(rig.Sent.Last(p=>p.Kind=="DOCKPOSE"&&p.Target==waiting.Id).Body);
            Assert.Equal("",pose.Token);
            Assert.Equal(P.L.FleetNoFreeDock,pose.Problem);
        }

        [Fact]
        public void AnUnstableDockWindowDoesNotDelayAnInboundConnectorAssignment()
        {
            var rig=new FleetStateTests.Rig(1,false,"[Dock]\nStableSeconds=30\n");
            var waiting=rig.Miners[0];waiting.Position=new Vector3D(0,0,-100);waiting.State=P.FlightState.Holding;
            rig.RegisterAll();rig.Receive("DOCK_REQUEST",waiting);
            var pose=P.DockFrame.FromIni(Assert.Single(rig.Sent,p=>p.Kind=="DOCK_GRANT"&&p.Target==waiting.Id).Body);
            Assert.NotEmpty(pose.Token);
            Assert.Equal("",pose.Problem);
            Assert.Equal(0,rig.Program.Now);
        }
    }
}