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.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using Sandbox.ModAPI.Ingame;
using VRage.Game.GUI.TextPanel;
using VRage.Game.ModAPI.Ingame.Utilities;
using VRageMath;
using Xunit;
using P=AutoMiningScript.Program;

namespace AutoMiningScript.Tests
{
    public class ExplorationMapTests
    {
        static MyDetectedEntityInfo Hit(long id=42, MyDetectedEntityType type=MyDetectedEntityType.Asteroid)
        {
            return new MyDetectedEntityInfo(id,"rock",type,new Vector3D(80,60,-20),MatrixD.Identity,Vector3.Zero,
                VRage.Game.MyRelationsBetweenPlayerAndBlock.NoOwnership,new BoundingBoxD(new Vector3D(50,30,-40),new Vector3D(110,90,20)),1);
        }
        static P.ScanRecord Scan(long id=42) { return P.ScanRecord.Capture(Vector3D.Zero,new Vector3D(300,225,-75),Hit(id)); }
        sealed class MapRole:P.RoleLogic
        {
            public readonly List<P.Job> Jobs=new List<P.Job>();
            public readonly List<P.Telemetry> Fleet=new List<P.Telemetry>();
            public P.MiningRegion Region=new P.MiningRegion {Id="search-1",Search=true,Radius=300,Center=Vector3D.Zero};
            public MapRole(P p):base(p){}
            public override List<P.Job> GetJobs(){return Jobs;}
            public override List<P.Telemetry> GetTelemetry(){return Fleet;}
            public override P.MiningRegion MapRegion(string id){return Region.Id==id?Region:null;}
        }
        sealed class Rig
        {
            public readonly P Program=TestRig.Program("[Display]\nBuiltInPage=map\n");
            public readonly List<MySprite> Sprites=new List<MySprite>();
            public readonly P.Dashboard Dashboard;
            public readonly MapRole Role;
            public Rig(int width=1024,int height=512)
            {
                var surface=new Stub<IMyTextSurface>().Set("SurfaceSize",new Vector2(width,height)).Set("TextureSize",new Vector2(width,height))
                    .Method("MeasureStringInPixels",a=>new Vector2(a[0].ToString().Sum(ch=>ch>=0x2e80?32:16),32)*(float)a[2])
                    .Method("DrawFrame",a=>new MySpriteDrawFrame(f=>{Sprites.Clear();f.AddToList(Sprites);}));
                var pb=new Stub<IMyProgrammableBlock>().Set("EntityId",20L).Set("CubeGrid",Program.Me.CubeGrid).Set("CustomData","")
                    .Set("CustomName","PB").Set("SurfaceCount",1).Method("GetSurface",a=>surface.Value);
                TestRig.SetBase(Program,"Me",pb.Value);TestRig.SetBase(Program,"GridTerminalSystem",new Stub<IMyGridTerminalSystem>().Value);
                Dashboard=new P.Dashboard(Program);Role=new MapRole(Program);
            }
            public void Add(P.ScanRecord scan=null,string outcome="",long entity=0)
            {
                Role.Jobs.Add(new P.Job {Id="ray-"+Role.Jobs.Count,RegionId=Role.Region.Id,Kind=P.JobKind.Survey,Entry=Vector3D.Zero,Depth=300,Scan=scan,Outcome=outcome,EntityId=entity});
            }
            public void Draw(double now=0){Program.Now=now;Dashboard.Draw(Role);}
            public string[] Text=>Sprites.Where(s=>s.Type==SpriteType.TEXT).Select(s=>s.Data).ToArray();
            public MySprite[] Cells=>Sprites.Where(s=>s.Color==new Color(34,84,113)).ToArray();
            public void Export(string suffix="")
            {
                string path=Environment.GetEnvironmentVariable("AMS_MAP_PREVIEW");if(string.IsNullOrEmpty(path))return;
                File.WriteAllLines(path+suffix,Sprites.Select(s=>string.Join("\t",new[]{s.Type.ToString(),Convert.ToBase64String(Encoding.UTF8.GetBytes(s.Data??"")),
                    (s.Position??Vector2.Zero).X.ToString(P.Data.Culture),(s.Position??Vector2.Zero).Y.ToString(P.Data.Culture),(s.Size??Vector2.Zero).X.ToString(P.Data.Culture),(s.Size??Vector2.Zero).Y.ToString(P.Data.Culture),
                    s.RotationOrScale.ToString(P.Data.Culture),s.Alignment.ToString(),(s.Color??Color.White).R.ToString(),(s.Color??Color.White).G.ToString(),(s.Color??Color.White).B.ToString(),(s.Color??Color.White).A.ToString()})),Encoding.UTF8);
            }
        }
        [Fact]
        public void ActualHitStopsTheRecordedRayAndCarriesWorldBoundsThroughMessageAndJob()
        {
            var scan=Scan();Assert.Equal(Hit().HitPosition,scan.End);Assert.NotEqual(new Vector3D(300,225,-75),scan.End);
            var report=P.JobReport.FromIni(new P.JobReport {Scan=scan,Outcome="SurveyHit"}.ToIni());
            var job=P.Job.FromIni(new P.Job {Id="j",Scan=report.Scan}.ToIni());
            Assert.Equal(42,job.Scan.Asteroid);Assert.Equal(Hit().BoundingBox.Center,job.Scan.Center);Assert.Equal(new Vector3D(60),job.Scan.Size);Assert.Equal(Vector3D.Zero,job.Scan.Start);
        }
        [Fact]
        public void LegacyMessagesAndJobsHaveNoInventedRayOrSize()
        {
            Assert.Null(P.JobReport.FromIni(new MyIni()).Scan);Assert.Null(P.Job.FromIni(new P.Job {Id="old"}.ToIni()).Scan);
            var r=new Rig();r.Add(outcome:"SurveyEmpty");r.Draw();Assert.Empty(r.Cells);
            Assert.Contains(P.L.F(P.L.DisplayScanSummary,0,1,0,0),r.Text);
        }
        [Fact]
        public void EmptyRayIsRecordedWhilePlanetAndGridAreNotCountedAsAsteroids()
        {
            var end=new Vector3D(20,30,40);var empty=P.ScanRecord.Capture(Vector3D.Zero,end,new MyDetectedEntityInfo());
            Assert.Equal(end,empty.End);Assert.Equal(0,empty.Asteroid);
            Assert.Equal(0,P.ScanRecord.Capture(Vector3D.Zero,end,Hit(type:MyDetectedEntityType.Planet)).Asteroid);
            Assert.Equal(0,P.ScanRecord.Capture(Vector3D.Zero,end,Hit(type:MyDetectedEntityType.LargeGrid)).Asteroid);
        }
        [Fact]
        public void InvalidAndMissingCoordinatesDoNotInventMapEvidence()
        {
            Assert.Null(P.ScanRecord.Capture(new Vector3D(double.NaN),Vector3D.Zero,Hit()));
            var i=new MyIni();i.Set("job","ScanStart","NaN,0,0");Assert.Null(P.ScanRecord.Read(i,"job"));
            var info=new MyDetectedEntityInfo(42,"rock",MyDetectedEntityType.Asteroid,null,MatrixD.Identity,Vector3.Zero,VRage.Game.MyRelationsBetweenPlayerAndBlock.NoOwnership,new BoundingBoxD(),1);
            Assert.Null(P.ScanRecord.Capture(Vector3D.Zero,Vector3D.Forward,info));
        }
        [Fact]
        public void RepeatedHitsCountOneAsteroidAndOneCircularBound()
        {
            var r=new Rig();r.Add(Scan(),"SurveyHit");r.Add(Scan(),"SurveyHit");r.Draw();
            Assert.Contains(P.L.F(P.L.DisplayScanSummary,2,2,1,0),r.Text);
            Assert.Contains(P.L.F(P.L.DisplayRockArea,"8482 m2",1,1,1),r.Text);
            Assert.Single(r.Text,s=>s.StartsWith("A1 "));Assert.DoesNotContain(r.Text,s=>s.StartsWith("A2 "));
        }
        [Fact]
        public void UnknownSizeRemainsUnknownAndCanBeReplacedByMeasuredBounds()
        {
            var r=new Rig();var unknown=Scan();unknown.Size=Vector3D.Zero;r.Add(unknown,"SurveyHit");r.Draw();
            Assert.Contains("A1 ?",r.Text);Assert.Contains(P.L.F(P.L.DisplayScanSummary,1,1,1,1),r.Text);
            r.Add(Scan(),"SurveyHit");r.Draw(1);Assert.Contains("A1 ~8482 m2",r.Text);Assert.Contains(P.L.F(P.L.DisplayScanSummary,2,2,1,0),r.Text);
        }
        [Fact]
        public void CircularBoundContainsTheMeasuredBoxRegardlessOfMapRotation()
        {
            var r=new Rig();r.Role.Region.Direction=Vector3D.Right;r.Role.Region.Up=Vector3D.Up;
            var scan=Scan();scan.Size=new Vector3D(40,50,60);r.Add(scan,"SurveyHit");r.Draw();
            Assert.Contains("A1 ~6048 m2",r.Text);
        }
        [Fact]
        public void UnexecutedAndBlockedWithoutScanDoNotPaintCoverage()
        {
            var r=new Rig();r.Add();r.Add(outcome:"Blocked");r.Draw();Assert.Empty(r.Cells);
            r.Add(new P.ScanRecord {Start=Vector3D.Zero,End=new Vector3D(90,0,0)},"SurveyEmpty");r.Draw(1);
            Assert.NotEmpty(r.Cells);Assert.All(r.Cells,s=>Assert.InRange(s.Position.Value.X,512,555));
            Assert.Contains(P.L.DisplayScanCaution,r.Text);
        }
        [Fact]
        public void ParallelRayEntirelyOutsideSearchDoesNotPaintAnEdge()
        {
            var r=new Rig();r.Add(new P.ScanRecord {Start=new Vector3D(400,-200,0),End=new Vector3D(400,200,0)},"SurveyEmpty");r.Draw();Assert.Empty(r.Cells);
        }
        [Theory]
        [InlineData(512,512)] [InlineData(1024,512)] [InlineData(512,1024)]
        public void SixteenCoincidentMinersKeepDistinctIdRowsAndMarkOffline(int width,int height)
        {
            var r=new Rig(width,height);for(int n=0;n<8;n++)r.Add(Scan(42+n),"SurveyHit");
            for(int n=1;n<=16;n++)r.Role.Fleet.Add(new P.Telemetry {Id="miner-"+n.ToString("00"),State=P.FlightState.Transit,ReceivedAt=n==16?-100:0});
            r.Draw();var labels=r.Sprites.Where(s=>s.Type==SpriteType.TEXT && s.Data.StartsWith("miner-")).ToArray();
            Assert.Equal(16,labels.Length);Assert.Equal(16,labels.Select(s=>s.Position.Value).Distinct().Count());
            Assert.Contains(labels,s=>s.Data.Contains(P.L.DisplayOffline));
            Assert.All(labels,s=>{Assert.InRange(s.Position.Value.X,0,width);Assert.InRange(s.Position.Value.Y,0,height);});
            Assert.All(labels,label=>Assert.Contains(r.Sprites,s=>s.Type==SpriteType.TEXTURE&&s.Color==new Color(9,17,27)
                &&s.Size.Value.X==169&&s.Position.Value.X-s.Size.Value.X/2==label.Position.Value.X-2
                &&s.Position.Value.Y-s.Size.Value.Y/2==label.Position.Value.Y-1));
            var boxes=r.Sprites.Where(s=>s.Type==SpriteType.TEXTURE&&s.Color==new Color(9,17,27)&&(s.Size.Value.X==169||s.Size.Value.X==173)).ToArray();
            Assert.Equal(24,boxes.Length);
            for(int a=0;a<boxes.Length;a++)for(int b=a+1;b<boxes.Length;b++)
                Assert.True(Math.Abs(boxes[a].Position.Value.X-boxes[b].Position.Value.X)>=(boxes[a].Size.Value.X+boxes[b].Size.Value.X)/2
                    ||Math.Abs(boxes[a].Position.Value.Y-boxes[b].Position.Value.Y)>=(boxes[a].Size.Value.Y+boxes[b].Size.Value.Y)/2);
            r.Export("."+width+"x"+height+".tsv");
        }
        [Fact]
        public void OverallScanProgressIncludesOtherRegionsAndExcludesMiningJobs()
        {
            var r=new Rig();r.Add(Scan(),"SurveyHit");r.Add();
            r.Role.Jobs.Add(new P.Job {Id="other-ray",RegionId="search-2",Kind=P.JobKind.Survey,Outcome="SurveyEmpty"});
            r.Role.Jobs.Add(new P.Job {Id="sample",RegionId="deposit-1",Kind=P.JobKind.Probe});
            r.Role.Jobs.Add(new P.Job {Id="manual",RegionId="manual-1",Kind=P.JobKind.Manual});
            r.Draw();Assert.Contains(P.L.F(P.L.DisplayScanProgress,"67%",2,3),r.Text);
        }
        [Fact]
        public void AsteroidUsesRoundOutlineAndDoesNotInferOreFromTheRequestedTarget()
        {
            var r=new Rig();r.Add(Scan(),"SurveyHit");r.Role.Jobs[0].TargetOre="Iron";r.Draw();
            var ring=r.Sprites.Where(s=>s.Type==SpriteType.TEXTURE&&s.Data=="SquareSimple"&&s.Size.Value.Y==2&&s.Position.Value.Y>165&&s.Position.Value.Y<389).ToArray();
            Assert.Equal(32,ring.Length);Assert.Single(ring.Select(s=>s.Color).Distinct());Assert.DoesNotContain("Fe",r.Text);
            var scale=212f/600;var center=new Vector2(512+80*scale,277-60*scale);
            var radius=(float)(new Vector3D(60).Length()*scale/2*Math.Cos(Math.PI/32));
            Assert.All(ring,s=>Assert.InRange((s.Position.Value-center).Length(),radius-.001f,radius+.001f));
            Assert.True(ring.Select(s=>s.RotationOrScale).Distinct().Count()>8);
        }
        [Fact]
        public void ConfirmedOresAggregateAcrossDepositRegionsAndRenderMultipleColoredArcs()
        {
            var r=new Rig();r.Add(Scan(),"SurveyHit");
            r.Role.Jobs.Add(new P.Job {Id="probe-1",RegionId="deposit-1",Kind=P.JobKind.Probe,EntityId=42,ConfirmedOres="Iron,Cobalt,Stone"});
            r.Role.Jobs.Add(new P.Job {Id="probe-2",RegionId="deposit-2",Kind=P.JobKind.Probe,EntityId=42,ConfirmedOres="Cobalt,Nickel"});
            r.Draw();var ring=r.Sprites.Where(s=>s.Type==SpriteType.TEXTURE&&s.Data=="SquareSimple"&&s.Size.Value.Y==2&&s.Position.Value.Y>165&&s.Position.Value.Y<389).ToArray();
            Assert.Equal(32,ring.Length);Assert.Equal(4,ring.Select(s=>s.Color).Distinct().Count());
            foreach(var symbol in new[]{"Fe","Co","St","Ni"})
            {
                var label=Assert.Single(r.Sprites,s=>s.Type==SpriteType.TEXT&&s.Data==symbol);
                Assert.Contains(ring,s=>s.Color==label.Color);
            }
            var restored=P.Job.FromIni(r.Role.Jobs[1].ToIni());Assert.Equal("Iron,Cobalt,Stone",restored.ConfirmedOres);
            Assert.Equal("",P.Job.FromIni(new P.Job {Id="legacy"}.ToIni()).ConfirmedOres);
        }
        [Fact]
        public void AllElevenOreKindsKeepVisibleSymbolsAndDistinctColorsOnOneAsteroid()
        {
            var r=new Rig(512,512);r.Add(Scan(),"SurveyHit");
            r.Role.Jobs[0].ConfirmedOres="Iron,Cobalt,Nickel,Silicon,Magnesium,Silver,Gold,Platinum,Uranium,Ice,Stone";
            r.Draw();var labels=r.Sprites.Where(s=>s.Type==SpriteType.TEXT&&new[]{"Fe","Co","Ni","Si","Mg","Ag","Au","Pt","U","Ice","St"}.Contains(s.Data)).ToArray();
            Assert.Equal(11,labels.Length);Assert.Equal(11,labels.Select(s=>s.Color).Distinct().Count());
            Assert.All(labels,label=>Assert.Contains(r.Sprites,s=>s.Type==SpriteType.TEXTURE&&s.Size.Value.Y==2&&s.Color==label.Color));
        }
        [Fact]
        public void MoreAsteroidsCarouselWithoutLosingTotalOrCoverage()
        {
            var r=new Rig();for(int n=0;n<12;n++)r.Add(Scan(42+n),"SurveyHit");r.Draw();var cells=r.Cells.Length;
            Assert.Contains(r.Text,s=>s.StartsWith("A1 "));Assert.DoesNotContain(r.Text,s=>s.StartsWith("A9 "));
            r.Draw(8);Assert.Contains(r.Text,s=>s.StartsWith("A9 "));Assert.Equal(cells,r.Cells.Length);
            Assert.Contains(P.L.F(P.L.DisplayScanSummary,12,12,12,0),r.Text);
        }
        [Fact]
        public void PlannerCheckpointRetainsBoundsAndClearTasksRemovesThem()
        {
            var planner=new P.TaskPlanner(TestRig.Program());planner.Survey(Vector3D.Zero,Vector3D.Zero,Vector3D.Forward,Vector3D.Up,300);
            var job=planner.Jobs.First();planner.Report(job,new P.JobReport {JobId=job.Id,Outcome="SurveyHit",Position=Scan().End,Direction=Vector3D.Forward,EntityId=42,Scan=Scan()});
            var save=new MyIni();planner.Save(save);var restored=new P.TaskPlanner(TestRig.Program());restored.Load(save);
            Assert.Equal(new Vector3D(60),restored.Find(job.Id).Scan.Size);restored.ClearTasks();Assert.Empty(restored.Jobs);
        }
        [Fact]
        public void CameraSurveySendsActualRayEvidenceToTheFleet()
        {
            var r=new MinerStateTests.MinerRig();r.Task();var flags=BindingFlags.Instance|BindingFlags.NonPublic;
            var job=(P.Job)r.Miner.GetType().GetField("job",flags).GetValue(r.Miner);job.Kind=P.JobKind.Survey;r.CameraHit=Hit();r.State(P.FlightState.Survey);
            r.Miner.GetType().GetMethod("Survey",flags).Invoke(r.Miner,new object[]{.1});
            var report=P.JobReport.FromIni(Assert.Single(r.Sent,p=>p.Kind=="RESULT").Body);
            Assert.Equal(r.Position,report.Scan.Start);Assert.Equal(Hit().HitPosition,report.Scan.End);Assert.True(report.Scan.Sized);
        }
        [Fact]
        public void RepresentativeMapFrameContainsRaysRocksAndMinerIds()
        {
            var r=new Rig();for(int n=0;n<24;n++){double a=n*Math.PI/12;r.Add(new P.ScanRecord {Start=Vector3D.Zero,End=new Vector3D(Math.Cos(a)*280,Math.Sin(a)*280,0)},"SurveyEmpty");}
            r.Add(Scan(),"SurveyHit");var other=Scan(99);other.Center=new Vector3D(-120,-100,0);other.End=other.Center;other.Size=new Vector3D(90,70,30);r.Add(other,"SurveyHit");
            r.Role.Jobs.Add(new P.Job {Id="probe",RegionId="deposit-1",Kind=P.JobKind.Probe,EntityId=42,ConfirmedOres="Iron,Cobalt,Nickel,Silicon,Stone"});
            r.Role.Jobs.Add(new P.Job {Id="probe-2",RegionId="deposit-2",Kind=P.JobKind.Probe,EntityId=99,ConfirmedOres="Gold,Uranium,Ice"});
            for(int n=1;n<=5;n++)r.Role.Fleet.Add(new P.Telemetry {Id=P.L.Language=="zh-CN"?"深空工业"+n+"号":"Miner-"+n,Position=new Vector3D((n-3)*40,40,0),ReceivedAt=n==5?-100:0});
            r.Draw();Assert.NotEmpty(r.Cells);Assert.Contains("A1 ~8482 m2",r.Text);
            r.Export();
        }
    }
}