From 34a0e8bd3a3ecabd2d980d01a0459bbfa9b49fe5 Mon Sep 17 00:00:00 2001 From: BOTLester <58360400+BOTLester@users.noreply.github.com> Date: Sun, 10 May 2020 01:38:08 +0200 Subject: [PATCH] working ML, RC0.1 --- Game1/Game1.csproj | 3 + Game1/Sources/Crops/CropType.cs | 1 + Game1/Sources/Crops/CropTypesHolder.cs | 78 +++-- Game1/Sources/Crops/Crops.cs | 24 +- Game1/Sources/Crops/Farm.cs | 1 + Game1/Sources/Crops/SoilProperties.cs | 1 + Game1/Sources/ML/Engine.cs | 31 ++ Game1/Sources/ML/InOut/BigModelInput.cs | 47 ++- Game1/Sources/ML/InOut/BigModelOutput.cs | 10 +- Game1/Sources/ML/InOut/ModelInput.cs | 43 ++- Game1/Sources/ML/InOut/ModelOutput.cs | 14 +- Game1/Sources/ML/MLModel.cs | 309 +++++++++--------- Game1/Sources/Objects/Fertilizer.cs | 15 + Game1/Sources/Objects/FertilizerHolder.cs | 181 ++++++++++ .../Sources/Objects/InventorySystem/Cargo.cs | 22 +- .../Objects/InventorySystem/Inventory.cs | 34 +- Game1/Sources/Smart/AI.cs | 68 +++- Game1/Sources/Smart/SmartTractor.cs | 2 + 18 files changed, 603 insertions(+), 281 deletions(-) create mode 100644 Game1/Sources/ML/Engine.cs create mode 100644 Game1/Sources/Objects/Fertilizer.cs create mode 100644 Game1/Sources/Objects/FertilizerHolder.cs diff --git a/Game1/Game1.csproj b/Game1/Game1.csproj index 0658f70..f2f8ba8 100644 --- a/Game1/Game1.csproj +++ b/Game1/Game1.csproj @@ -88,7 +88,10 @@ + + + True diff --git a/Game1/Sources/Crops/CropType.cs b/Game1/Sources/Crops/CropType.cs index d796274..1a42e12 100644 --- a/Game1/Sources/Crops/CropType.cs +++ b/Game1/Sources/Crops/CropType.cs @@ -11,6 +11,7 @@ class CropTypes public string[] soilType = new string[3]; public int[] Times = new int[3]; + public string CropName; public float Temparature; public float Humidity; public float Moisture; diff --git a/Game1/Sources/Crops/CropTypesHolder.cs b/Game1/Sources/Crops/CropTypesHolder.cs index 7e591ce..3a11f83 100644 --- a/Game1/Sources/Crops/CropTypesHolder.cs +++ b/Game1/Sources/Crops/CropTypesHolder.cs @@ -16,6 +16,7 @@ class CropTypesHolder cropTypes[0].soilType[0] = "None"; cropTypes[0].soilType[1] = null; cropTypes[0].soilType[2] = null; + cropTypes[0].CropName = "None"; cropTypes[0].Temparature = 0f; cropTypes[0].Humidity = 0f; cropTypes[0].Moisture = 0f; @@ -28,12 +29,13 @@ class CropTypesHolder cropTypes[1].soilType[0] = "Sandy"; cropTypes[1].soilType[1] = null; cropTypes[1].soilType[2] = null; + cropTypes[1].CropName = "Barley"; cropTypes[1].Temparature = 30.1f; cropTypes[1].Humidity = 59.0f; cropTypes[1].Moisture = 41.7f; - cropTypes[1].Nitrogen = 25 + 12.6f; - cropTypes[1].Potassium = 14 + 5.3f; - cropTypes[1].Phosphorous = 30 + 26.0f; + cropTypes[1].Nitrogen = 35 + 12.6f; + cropTypes[1].Potassium = 20 + 5.3f; + cropTypes[1].Phosphorous = 32 + 26.0f; // Cotton @@ -44,24 +46,26 @@ class CropTypesHolder cropTypes[2].Times[1] = 4; cropTypes[2].soilType[2] = "Loamy"; cropTypes[2].Times[2] = 3; + cropTypes[2].CropName = "Cotton"; cropTypes[2].Temparature = 30.4f; cropTypes[2].Humidity = 59.6f; cropTypes[2].Moisture = 63.2f; - cropTypes[2].Nitrogen = 25 + 16.4f; - cropTypes[2].Potassium = 14 + 3.3f; - cropTypes[2].Phosphorous = 30 + 23.8f; + cropTypes[2].Nitrogen = 35 + 16.4f; + cropTypes[2].Potassium = 20 + 3.3f; + cropTypes[2].Phosphorous = 32 + 23.8f; // Ground Nuts cropTypes[3] = new CropTypes(); cropTypes[3].soilType[0] = "Red"; cropTypes[3].soilType[1] = null; cropTypes[3].soilType[2] = null; + cropTypes[3].CropName = "Ground Nuts"; cropTypes[3].Temparature = 30.1f; cropTypes[3].Humidity = 59.1f; cropTypes[3].Moisture = 33.1f; - cropTypes[3].Nitrogen = 25 + 23.3f; - cropTypes[3].Potassium = 14 + 2.0f; - cropTypes[3].Phosphorous = 30 + 21.6f; + cropTypes[3].Nitrogen = 35 + 23.3f; + cropTypes[3].Potassium = 20 + 2.0f; + cropTypes[3].Phosphorous = 32 + 21.6f; // Maize @@ -69,12 +73,13 @@ class CropTypesHolder cropTypes[4].soilType[0] = "Sandy"; cropTypes[4].soilType[1] = null; cropTypes[4].soilType[2] = null; + cropTypes[4].CropName = "Maize"; cropTypes[4].Temparature = 29.0f; cropTypes[4].Humidity = 57.3f; cropTypes[4].Moisture = 42.2f; - cropTypes[4].Nitrogen = 25 + 18.3f; - cropTypes[4].Potassium = 14 + 5.7f; - cropTypes[4].Phosphorous = 30 + 18.7f; + cropTypes[4].Nitrogen = 35 + 18.3f; + cropTypes[4].Potassium = 20 + 5.7f; + cropTypes[4].Phosphorous = 32 + 18.7f; // Millets cropTypes[5] = new CropTypes(); @@ -83,48 +88,52 @@ class CropTypesHolder cropTypes[5].soilType[1] = "Black"; cropTypes[5].Times[0] = 4; cropTypes[5].soilType[2] = null; + cropTypes[5].CropName = "Millets"; cropTypes[5].Temparature = 29.5f; cropTypes[5].Humidity = 57.3f; cropTypes[5].Moisture = 38.5f; - cropTypes[5].Nitrogen = 25 + 23.2f; - cropTypes[5].Potassium = 14 + 0.1f; - cropTypes[5].Phosphorous = 30 + 14.4f; + cropTypes[5].Nitrogen = 35 + 23.2f; + cropTypes[5].Potassium = 20 + 0.1f; + cropTypes[5].Phosphorous = 32 + 14.4f; //Oil Seeds cropTypes[6] = new CropTypes(); cropTypes[6].soilType[0] = "Black"; cropTypes[6].soilType[1] = null; cropTypes[6].soilType[2] = null; + cropTypes[6].CropName = "Oil Seeds"; cropTypes[6].Temparature = 30.3f; cropTypes[6].Humidity = 59.1f; cropTypes[6].Moisture = 32.1f; - cropTypes[6].Nitrogen = 25 + 19.0f; - cropTypes[6].Potassium = 14 + 2.3f; - cropTypes[6].Phosphorous = 30 + 17.3f; + cropTypes[6].Nitrogen = 35 + 19.0f; + cropTypes[6].Potassium = 20 + 2.3f; + cropTypes[6].Phosphorous = 32 + 17.3f; //Paddys cropTypes[7] = new CropTypes(); cropTypes[7].soilType[0] = "Clayey"; cropTypes[7].soilType[1] = null; cropTypes[7].soilType[2] = null; + cropTypes[7].CropName = "Paddys"; cropTypes[7].Temparature = 31.2f; cropTypes[7].Humidity = 60.4f; cropTypes[7].Moisture = 41.5f; - cropTypes[7].Nitrogen = 25 + 20.8f; - cropTypes[7].Potassium = 14 + 3.7f; - cropTypes[7].Phosphorous = 30 + 16.3f; + cropTypes[7].Nitrogen = 35 + 20.8f; + cropTypes[7].Potassium = 20 + 3.7f; + cropTypes[7].Phosphorous = 32 + 16.3f; //Pulses cropTypes[8] = new CropTypes(); cropTypes[8].soilType[0] = "Clayey"; cropTypes[8].soilType[1] = null; cropTypes[8].soilType[2] = null; + cropTypes[8].CropName = "Pulses"; cropTypes[8].Temparature = 27.8f; cropTypes[8].Humidity = 54.9f; cropTypes[8].Moisture = 34.1f; - cropTypes[8].Nitrogen = 25 + 18.4f; - cropTypes[8].Potassium = 14 + 4.2f; - cropTypes[8].Phosphorous = 30 + 17.5f; + cropTypes[8].Nitrogen = 35 + 18.4f; + cropTypes[8].Potassium = 20 + 4.2f; + cropTypes[8].Phosphorous = 32 + 17.5f; //Sugarcane cropTypes[9] = new CropTypes(); @@ -133,12 +142,13 @@ class CropTypesHolder cropTypes[9].soilType[1] = "Black"; cropTypes[9].Times[0] = 4; cropTypes[9].soilType[2] = null; + cropTypes[9].CropName = "Sugarcane"; cropTypes[9].Temparature = 30.0f; cropTypes[9].Humidity = 58.6f; cropTypes[9].Moisture = 51.2f; - cropTypes[9].Nitrogen = 25 + 14.6f; - cropTypes[9].Potassium = 14 + 4.2f; - cropTypes[9].Phosphorous = 30 + 17.6f; + cropTypes[9].Nitrogen = 35 + 14.6f; + cropTypes[9].Potassium = 20 + 4.2f; + cropTypes[9].Phosphorous = 32 + 17.6f; //Tobacco @@ -146,12 +156,13 @@ class CropTypesHolder cropTypes[10].soilType[0] = "Red"; cropTypes[10].soilType[1] = null; cropTypes[10].soilType[2] = null; + cropTypes[10].CropName = "Tobacco"; cropTypes[10].Temparature = 31.9f; cropTypes[10].Humidity = 62.4f; cropTypes[10].Moisture = 31.6f; - cropTypes[10].Nitrogen = 25 + 19.1f; - cropTypes[10].Potassium = 14 + 4.9f; - cropTypes[10].Phosphorous = 30 + 19.3f; + cropTypes[10].Nitrogen = 35 + 19.1f; + cropTypes[10].Potassium = 20 + 4.9f; + cropTypes[10].Phosphorous = 32 + 19.3f; //Wheat @@ -159,12 +170,13 @@ class CropTypesHolder cropTypes[11].soilType[0] = "Loamy"; cropTypes[11].soilType[1] = null; cropTypes[11].soilType[2] = null; + cropTypes[11].CropName = "Wheat"; cropTypes[11].Temparature = 33.1f; cropTypes[11].Humidity = 63.8f; cropTypes[11].Moisture = 50.0f; - cropTypes[11].Nitrogen = 25 + 23.3f; - cropTypes[11].Potassium = 14 + 2.9f; - cropTypes[11].Phosphorous = 30 + 14.4f; + cropTypes[11].Nitrogen = 35 + 23.3f; + cropTypes[11].Potassium = 20 + 2.9f; + cropTypes[11].Phosphorous = 32 + 14.4f; } diff --git a/Game1/Sources/Crops/Crops.cs b/Game1/Sources/Crops/Crops.cs index a0f1b80..b959550 100644 --- a/Game1/Sources/Crops/Crops.cs +++ b/Game1/Sources/Crops/Crops.cs @@ -50,6 +50,22 @@ class Crops return soilProperties; } + public void Fertilize(Fertilizer fertilizer) + { + soilProperties.Nitrogen += fertilizer.Nitrogen; + soilProperties.Phosphorous += fertilizer.Phosphorus; + soilProperties.Potassium += fertilizer.Potassium; + } + + public bool isSaturated() + { + if (soilProperties.Nitrogen > 52 && (soilProperties.Phosphorous > 57 || soilProperties.Potassium > 25)) + return true; + if (soilProperties.Phosphorous > 57 && soilProperties.Potassium > 25) + return true; + return false; + } + public float getCropTimer() { return Timer; @@ -194,6 +210,11 @@ class Crops return housePos; } + public bool belowCapacity() + { + return ((int)(soilProperties.Nitrogen + soilProperties.Potassium + soilProperties.Phosphorous)) < soilProperties.Capacity; + } + public float getProductionRate(CropTypes Sample) { ProductionRate = 1; @@ -205,9 +226,6 @@ class Crops ProductionRate = ProductionRate + (ProductionRate * compareToDatset(soilProperties.Humidity, Sample.Humidity)); min = Math.Min(compareToDatset(soilProperties.Phosphorous, Sample.Phosphorous), Math.Min(compareToDatset(soilProperties.Potassium, Sample.Potassium), compareToDatset(soilProperties.Nitrogen, Sample.Nitrogen))); ProductionRate = ProductionRate + (ProductionRate * min); - //ProductionRate = ProductionRate + (ProductionRate * compareToDatset(soilProperties.Phosphorous, Sample.Phosphorous)); - //ProductionRate = ProductionRate + (ProductionRate * compareToDatset(soilProperties.Potassium, Sample.Potassium)); - //ProductionRate = ProductionRate + (ProductionRate * compareToDatset(soilProperties.Nitrogen, Sample.Nitrogen)); if (ProductionRate < 0) { ProductionRate = 0; diff --git a/Game1/Sources/Crops/Farm.cs b/Game1/Sources/Crops/Farm.cs index 6340681..5a5d0ed 100644 --- a/Game1/Sources/Crops/Farm.cs +++ b/Game1/Sources/Crops/Farm.cs @@ -76,6 +76,7 @@ class Farm } else if (crops[x, y].getStatus() == 2) { + crops[x, y].setStatus(3); crops[x, y].setCropTimer(); } diff --git a/Game1/Sources/Crops/SoilProperties.cs b/Game1/Sources/Crops/SoilProperties.cs index f2f3bd2..f79219b 100644 --- a/Game1/Sources/Crops/SoilProperties.cs +++ b/Game1/Sources/Crops/SoilProperties.cs @@ -20,6 +20,7 @@ class SoilProperties public float NitrogenDegradeRate = 1.0f - (1.0f/55 * 40); public float PotassiumDegradeRate = 1.0f - (1.0f/28 * 23); public float PhosphorousDegradeRate = 1.0f - (1.0f/60 * 37); + public int Capacity = 150; public void setSoilProperties() { diff --git a/Game1/Sources/ML/Engine.cs b/Game1/Sources/ML/Engine.cs new file mode 100644 index 0000000..9f123f1 --- /dev/null +++ b/Game1/Sources/ML/Engine.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.ML; + + +static class Engine +{ + private static MLContext mlContext = new MLContext(seed: 1); + private static PredictionEngine PredictionEngine = MLModel.CreateEngine(); + + public static string PredictFertilizer(Crops crop, CropTypes cropTypes) + { + ModelInput modelInput = new ModelInput + { + Temperature = crop.getSoilProperties().Temperature, + Humidity = crop.getSoilProperties().Humidity, + Moisture = crop.getSoilProperties().Moisture, + Soil_Type = crop.getSoilProperties().soilType, + Crop_Type = cropTypes.CropName, + Nitrogen = crop.getSoilProperties().Nitrogen, + Potassium = crop.getSoilProperties().Potassium, + Phosporous = crop.getSoilProperties().Phosphorous + }; + + return PredictionEngine.Predict(modelInput).Prediction; + } +} + diff --git a/Game1/Sources/ML/InOut/BigModelInput.cs b/Game1/Sources/ML/InOut/BigModelInput.cs index a17dbcb..8681aeb 100644 --- a/Game1/Sources/ML/InOut/BigModelInput.cs +++ b/Game1/Sources/ML/InOut/BigModelInput.cs @@ -5,39 +5,38 @@ using System.Text; using System.Threading.Tasks; using Microsoft.ML.Data; -namespace Game1.Sources.ML + +class BigModelInput { - class BigModelInput - { - [ColumnName("Ca"), LoadColumn(0)] - public float Ca { get; set; } + [ColumnName("Ca"), LoadColumn(0)] + public float Ca { get; set; } - [ColumnName("Mg"), LoadColumn(1)] - public float Mg { get; set; } + [ColumnName("Mg"), LoadColumn(1)] + public float Mg { get; set; } - [ColumnName("K"), LoadColumn(2)] - public float K { get; set; } + [ColumnName("K"), LoadColumn(2)] + public float K { get; set; } - [ColumnName("S"), LoadColumn(3)] - public float S { get; set; } + [ColumnName("S"), LoadColumn(3)] + public float S { get; set; } - [ColumnName("N"), LoadColumn(4)] - public float N { get; set; } + [ColumnName("N"), LoadColumn(4)] + public float N { get; set; } - [ColumnName("Lime"), LoadColumn(5)] - public float Lime { get; set; } + [ColumnName("Lime"), LoadColumn(5)] + public float Lime { get; set; } - [ColumnName("C"), LoadColumn(6)] - public float C { get; set; } + [ColumnName("C"), LoadColumn(6)] + public float C { get; set; } - [ColumnName("P"), LoadColumn(7)] - public float P { get; set; } + [ColumnName("P"), LoadColumn(7)] + public float P { get; set; } - [ColumnName("Moisture"), LoadColumn(8)] - public float Moisture { get; set; } + [ColumnName("Moisture"), LoadColumn(8)] + public float Moisture { get; set; } - [ColumnName("Class"), LoadColumn(9)] - public float Class { get; set; } + [ColumnName("Class"), LoadColumn(9)] + public float Class { get; set; } - } } + diff --git a/Game1/Sources/ML/InOut/BigModelOutput.cs b/Game1/Sources/ML/InOut/BigModelOutput.cs index 908ea8b..fd43b81 100644 --- a/Game1/Sources/ML/InOut/BigModelOutput.cs +++ b/Game1/Sources/ML/InOut/BigModelOutput.cs @@ -5,11 +5,9 @@ using System.Text; using System.Threading.Tasks; using Microsoft.ML.Data; -namespace Game1.Sources.ML +class BigModelOutput { - class BigModelOutput - { - [ColumnName("PredictedLabel")] - public float Prediction { get; set; } - } + [ColumnName("PredictedLabel")] + public float Prediction { get; set; } } + diff --git a/Game1/Sources/ML/InOut/ModelInput.cs b/Game1/Sources/ML/InOut/ModelInput.cs index 2cb09ad..27f4fa1 100644 --- a/Game1/Sources/ML/InOut/ModelInput.cs +++ b/Game1/Sources/ML/InOut/ModelInput.cs @@ -6,35 +6,34 @@ using System.Threading.Tasks; using Microsoft.ML; using Microsoft.ML.Data; -namespace Game1.Sources.ML + +class ModelInput { - class ModelInput - { - [ColumnName("Temperature"), LoadColumn(0)] - public float Temperature { get; set; } + [ColumnName("Temperature"), LoadColumn(0)] + public float Temperature { get; set; } - [ColumnName("Humidity"), LoadColumn(1)] - public float Humidity { get; set; } + [ColumnName("Humidity"), LoadColumn(1)] + public float Humidity { get; set; } - [ColumnName("Moisture"), LoadColumn(2)] - public float Moisture { get; set; } + [ColumnName("Moisture"), LoadColumn(2)] + public float Moisture { get; set; } - [ColumnName("Soil_Type"), LoadColumn(3)] - public String Soil_Type { get; set; } + [ColumnName("Soil_Type"), LoadColumn(3)] + public String Soil_Type { get; set; } - [ColumnName("Crop_Type"), LoadColumn(4)] - public String Crop_Type { get; set; } + [ColumnName("Crop_Type"), LoadColumn(4)] + public String Crop_Type { get; set; } - [ColumnName("Nitrogen"), LoadColumn(5)] - public float Nitrogen { get; set; } + [ColumnName("Nitrogen"), LoadColumn(5)] + public float Nitrogen { get; set; } - [ColumnName("Potassium"), LoadColumn(6)] - public float Potassium { get; set; } + [ColumnName("Potassium"), LoadColumn(6)] + public float Potassium { get; set; } - [ColumnName("Phosphorous"), LoadColumn(7)] - public float Phosporous { get; set; } + [ColumnName("Phosphorous"), LoadColumn(7)] + public float Phosporous { get; set; } - [ColumnName("Fertilizer_Name"), LoadColumn(8)] - public String Fertilizer_Name { get; set; } - } + [ColumnName("Fertilizer_Name"), LoadColumn(8)] + public String Fertilizer_Name { get; set; } } + diff --git a/Game1/Sources/ML/InOut/ModelOutput.cs b/Game1/Sources/ML/InOut/ModelOutput.cs index b0b2660..8d87eed 100644 --- a/Game1/Sources/ML/InOut/ModelOutput.cs +++ b/Game1/Sources/ML/InOut/ModelOutput.cs @@ -5,13 +5,11 @@ using System.Text; using System.Threading.Tasks; using Microsoft.ML.Data; -namespace Game1.Sources.ML +class ModelOutput { - class ModelOutput - { - [ColumnName("PredictedLabel")] - public String Prediction { get; set; } - //public float[] Score { get; set; } - - } + [ColumnName("PredictedLabel")] + public String Prediction { get; set; } + //public float[] Score { get; set; } + } + diff --git a/Game1/Sources/ML/MLModel.cs b/Game1/Sources/ML/MLModel.cs index af57af0..7a1870a 100644 --- a/Game1/Sources/ML/MLModel.cs +++ b/Game1/Sources/ML/MLModel.cs @@ -7,163 +7,162 @@ using Microsoft.ML; using Microsoft.ML.Data; using Microsoft.ML.Trainers.LightGbm; -namespace Game1.Sources.ML + +class MLModel { - class MLModel + private static MLContext mlContext = new MLContext(seed: 1); + private static string path = "C:/Users/Oskar/source/repos/PotatoPlanFinal/Game1/Content/ML/Fertilizer_Prediction.csv"; + private static string modelpath = "C:/Users/Oskar/source/repos/PotatoPlanFinal/Game1/Content/ML/MLmodel"; + private static string report = "C:/Users/Oskar/source/repos/PotatoPlanFinal/Game1/Content/ML/report"; + private static string pathBig = "C:/Users/Oskar/source/repos/PotatoPlanFinal/Game1/Content/ML/BigFertPredict.csv"; + private static string modelpathBig = "C:/Users/Oskar/source/repos/PotatoPlanFinal/Game1/Content/ML/MLmodelBig"; + private static string reportBig = "C:/Users/Oskar/source/repos/PotatoPlanFinal/Game1/Content/ML/report_BigModel"; + + // Loading data, creatin and saving ML model for smaller dataset (100) + public static void CreateModel() { - private static MLContext mlContext = new MLContext(seed: 1); - private static string path = "C:/Users/Oskar/source/repos/PotatoPlanFinal/Game1/Content/ML/Fertilizer_Prediction.csv"; - private static string modelpath = "C:/Users/Oskar/source/repos/PotatoPlanFinal/Game1/Content/ML/MLmodel"; - private static string report = "C:/Users/Oskar/source/repos/PotatoPlanFinal/Game1/Content/ML/report"; - private static string pathBig = "C:/Users/Oskar/source/repos/PotatoPlanFinal/Game1/Content/ML/BigFertPredict.csv"; - private static string modelpathBig = "C:/Users/Oskar/source/repos/PotatoPlanFinal/Game1/Content/ML/MLmodelBig"; - private static string reportBig = "C:/Users/Oskar/source/repos/PotatoPlanFinal/Game1/Content/ML/report_BigModel"; - - // Loading data, creatin and saving ML model for smaller dataset (100) - public static void CreateModel() - { - - IDataView trainingDataView = mlContext.Data.LoadFromTextFile( - path: path, - hasHeader: true, - separatorChar: ',', - allowQuoting: true, - allowSparse: false); - - ModelInput sample = mlContext.Data.CreateEnumerable(trainingDataView, false).ElementAt(0); - ITransformer MLModel = BuildAndTrain(mlContext, trainingDataView, sample, report); - SaveModel(mlContext, MLModel, modelpath, trainingDataView.Schema); - } - - // ... for bigger dataset (1600) - public static void CreateBigModel() - { - - IDataView trainingDataView = mlContext.Data.LoadFromTextFile( - path: pathBig, - hasHeader: true, - separatorChar: ',', - allowQuoting: true, - allowSparse: false); - - BigModelInput sample = mlContext.Data.CreateEnumerable(trainingDataView, false).ElementAt(0); - ITransformer MLModel = BuildAndTrain(mlContext, trainingDataView, sample, reportBig); - SaveModel(mlContext, MLModel, modelpathBig, trainingDataView.Schema); - } - - // Building and training ML model, very small dataset (100 entries) - public static ITransformer BuildAndTrain(MLContext mLContext, IDataView trainingDataView, ModelInput sample, string reportPath) - { - - var options = new LightGbmMulticlassTrainer.Options - { - MaximumBinCountPerFeature = 8, - LearningRate = 0.00025, - NumberOfIterations = 40000, - NumberOfLeaves = 10, - LabelColumnName = "Fertilizer_NameF", - FeatureColumnName = "Features", - - Booster = new DartBooster.Options() - { - MaximumTreeDepth = 10 - } - }; - - var pipeline = mlContext.Transforms - .Text.FeaturizeText("Soil_TypeF", "Soil_Type") - .Append(mlContext.Transforms.Text.FeaturizeText("Crop_TypeF", "Crop_Type")) - .Append(mlContext.Transforms.Concatenate("Features", "Temperature", "Humidity", "Moisture", "Soil_TypeF", "Crop_TypeF", "Nitrogen", "Potassium", "Phosphorous")) - .Append(mLContext.Transforms.NormalizeMinMax("Features")) - .Append(mlContext.Transforms.Conversion.MapValueToKey("Fertilizer_NameF", "Fertilizer_Name"), TransformerScope.TrainTest) - .AppendCacheCheckpoint(mLContext) - .Append(mLContext.MulticlassClassification.Trainers.LightGbm(options)) - .Append(mlContext.Transforms.Conversion.MapKeyToValue("PredictedLabel", "PredictedLabel")); - - Evaluate(mlContext, trainingDataView, pipeline, 10, reportPath, "Fertilizer_NameF"); - ITransformer MLModel = pipeline.Fit(trainingDataView); - - return MLModel; - } - - //Building and training ML model, moderate size dataset (1600 entries) - public static ITransformer BuildAndTrain(MLContext mLContext, IDataView trainingDataView, BigModelInput sample, string reportPath) - { - - var options = new LightGbmMulticlassTrainer.Options - { - MaximumBinCountPerFeature = 10, - LearningRate = 0.001, - NumberOfIterations = 10000, - NumberOfLeaves = 12, - LabelColumnName = "ClassF", - FeatureColumnName = "Features", - - Booster = new DartBooster.Options() - { - MaximumTreeDepth = 12 - } - }; - - var pipeline = mlContext.Transforms - .Concatenate("Features", "Ca", "Mg", "K", "S", "N", "Lime", "C", "P", "Moisture") - .Append(mLContext.Transforms.NormalizeMinMax("Features")) - .Append(mlContext.Transforms.Conversion.MapValueToKey("ClassF", "Class"), TransformerScope.TrainTest) - .AppendCacheCheckpoint(mLContext) - .Append(mLContext.MulticlassClassification.Trainers.LightGbm(options)) - .Append(mlContext.Transforms.Conversion.MapKeyToValue("PredictedLabel", "PredictedLabel")); - - Evaluate(mlContext, trainingDataView, pipeline, 8, reportPath, "ClassF"); - ITransformer MLModel = pipeline.Fit(trainingDataView); - - return MLModel; - } - - public static ITransformer TrainModel(MLContext mlContext, IDataView trainingDataView, IEstimator trainingPipeline) - { - ITransformer model = trainingPipeline.Fit(trainingDataView); - return model; - } - - // Evaluate and save results to a text file - public static void Evaluate(MLContext mlContext, IDataView trainingDataView, IEstimator trainingPipeline, int folds, string reportPath, string labelColumnName) - { - var crossVal = mlContext.MulticlassClassification.CrossValidate(trainingDataView, trainingPipeline, numberOfFolds: folds, labelColumnName: labelColumnName); - - var metricsInMultipleFolds = crossVal.Select(r => r.Metrics); - - var MicroAccuracyValues = metricsInMultipleFolds.Select(m => m.MicroAccuracy); - var LogLossValues = metricsInMultipleFolds.Select(m => m.LogLoss); - var LogLossReductionValues = metricsInMultipleFolds.Select(m => m.LogLossReduction); - string MicroAccuracyAverage = MicroAccuracyValues.Average().ToString("0.######"); - string LogLossAvg = LogLossValues.Average().ToString("0.######"); - string LogLossReductionAvg = LogLossReductionValues.Average().ToString("0.######"); - - var report = File.CreateText(reportPath); - report.Write("Micro Accuracy: " + MicroAccuracyAverage +'\n'+ "LogLoss Average: " + LogLossAvg +'\n'+ "LogLoss Reduction: " + LogLossReductionAvg, 0, 0); - report.Flush(); - report.Close(); - } - - - public static void SaveModel(MLContext mlContext, ITransformer Model, string modelPath, DataViewSchema modelInputSchema) - { - mlContext.Model.Save(Model, modelInputSchema, modelPath); - } - - public static ITransformer LoadModel(bool isBig) - { - if (isBig) - return mlContext.Model.Load(modelpathBig, out DataViewSchema inputSchema); - else - return mlContext.Model.Load(modelpath, out DataViewSchema inputSchema); - } - - public static Microsoft.ML.PredictionEngine CreateEngine() - { - ITransformer mlModel = LoadModel(false); - return mlContext.Model.CreatePredictionEngine(mlModel); - } + + IDataView trainingDataView = mlContext.Data.LoadFromTextFile( + path: path, + hasHeader: true, + separatorChar: ',', + allowQuoting: true, + allowSparse: false); + ModelInput sample = mlContext.Data.CreateEnumerable(trainingDataView, false).ElementAt(0); + ITransformer MLModel = BuildAndTrain(mlContext, trainingDataView, sample, report); + SaveModel(mlContext, MLModel, modelpath, trainingDataView.Schema); } + + // ... for bigger dataset (1600) + public static void CreateBigModel() + { + + IDataView trainingDataView = mlContext.Data.LoadFromTextFile( + path: pathBig, + hasHeader: true, + separatorChar: ',', + allowQuoting: true, + allowSparse: false); + + BigModelInput sample = mlContext.Data.CreateEnumerable(trainingDataView, false).ElementAt(0); + ITransformer MLModel = BuildAndTrain(mlContext, trainingDataView, sample, reportBig); + SaveModel(mlContext, MLModel, modelpathBig, trainingDataView.Schema); + } + + // Building and training ML model, very small dataset (100 entries) + public static ITransformer BuildAndTrain(MLContext mLContext, IDataView trainingDataView, ModelInput sample, string reportPath) + { + + var options = new LightGbmMulticlassTrainer.Options + { + MaximumBinCountPerFeature = 8, + LearningRate = 0.00025, + NumberOfIterations = 40000, + NumberOfLeaves = 10, + LabelColumnName = "Fertilizer_NameF", + FeatureColumnName = "Features", + + Booster = new DartBooster.Options() + { + MaximumTreeDepth = 10 + } + }; + + var pipeline = mlContext.Transforms + .Text.FeaturizeText("Soil_TypeF", "Soil_Type") + .Append(mlContext.Transforms.Text.FeaturizeText("Crop_TypeF", "Crop_Type")) + .Append(mlContext.Transforms.Concatenate("Features", "Temperature", "Humidity", "Moisture", "Soil_TypeF", "Crop_TypeF", "Nitrogen", "Potassium", "Phosphorous")) + .Append(mLContext.Transforms.NormalizeMinMax("Features")) + .Append(mlContext.Transforms.Conversion.MapValueToKey("Fertilizer_NameF", "Fertilizer_Name"), TransformerScope.TrainTest) + .AppendCacheCheckpoint(mLContext) + .Append(mLContext.MulticlassClassification.Trainers.LightGbm(options)) + .Append(mlContext.Transforms.Conversion.MapKeyToValue("PredictedLabel", "PredictedLabel")); + + Evaluate(mlContext, trainingDataView, pipeline, 10, reportPath, "Fertilizer_NameF"); + ITransformer MLModel = pipeline.Fit(trainingDataView); + + return MLModel; + } + + //Building and training ML model, moderate size dataset (1600 entries) + public static ITransformer BuildAndTrain(MLContext mLContext, IDataView trainingDataView, BigModelInput sample, string reportPath) + { + + var options = new LightGbmMulticlassTrainer.Options + { + MaximumBinCountPerFeature = 10, + LearningRate = 0.001, + NumberOfIterations = 10000, + NumberOfLeaves = 12, + LabelColumnName = "ClassF", + FeatureColumnName = "Features", + + Booster = new DartBooster.Options() + { + MaximumTreeDepth = 12 + } + }; + + var pipeline = mlContext.Transforms + .Concatenate("Features", "Ca", "Mg", "K", "S", "N", "Lime", "C", "P", "Moisture") + .Append(mLContext.Transforms.NormalizeMinMax("Features")) + .Append(mlContext.Transforms.Conversion.MapValueToKey("ClassF", "Class"), TransformerScope.TrainTest) + .AppendCacheCheckpoint(mLContext) + .Append(mLContext.MulticlassClassification.Trainers.LightGbm(options)) + .Append(mlContext.Transforms.Conversion.MapKeyToValue("PredictedLabel", "PredictedLabel")); + + Evaluate(mlContext, trainingDataView, pipeline, 8, reportPath, "ClassF"); + ITransformer MLModel = pipeline.Fit(trainingDataView); + + return MLModel; + } + + public static ITransformer TrainModel(MLContext mlContext, IDataView trainingDataView, IEstimator trainingPipeline) + { + ITransformer model = trainingPipeline.Fit(trainingDataView); + return model; + } + + // Evaluate and save results to a text file + public static void Evaluate(MLContext mlContext, IDataView trainingDataView, IEstimator trainingPipeline, int folds, string reportPath, string labelColumnName) + { + var crossVal = mlContext.MulticlassClassification.CrossValidate(trainingDataView, trainingPipeline, numberOfFolds: folds, labelColumnName: labelColumnName); + + var metricsInMultipleFolds = crossVal.Select(r => r.Metrics); + + var MicroAccuracyValues = metricsInMultipleFolds.Select(m => m.MicroAccuracy); + var LogLossValues = metricsInMultipleFolds.Select(m => m.LogLoss); + var LogLossReductionValues = metricsInMultipleFolds.Select(m => m.LogLossReduction); + string MicroAccuracyAverage = MicroAccuracyValues.Average().ToString("0.######"); + string LogLossAvg = LogLossValues.Average().ToString("0.######"); + string LogLossReductionAvg = LogLossReductionValues.Average().ToString("0.######"); + + var report = File.CreateText(reportPath); + report.Write("Micro Accuracy: " + MicroAccuracyAverage +'\n'+ "LogLoss Average: " + LogLossAvg +'\n'+ "LogLoss Reduction: " + LogLossReductionAvg, 0, 0); + report.Flush(); + report.Close(); + } + + + public static void SaveModel(MLContext mlContext, ITransformer Model, string modelPath, DataViewSchema modelInputSchema) + { + mlContext.Model.Save(Model, modelInputSchema, modelPath); + } + + public static ITransformer LoadModel(bool isBig) + { + if (isBig) + return mlContext.Model.Load(modelpathBig, out DataViewSchema inputSchema); + else + return mlContext.Model.Load(modelpath, out DataViewSchema inputSchema); + } + + public static Microsoft.ML.PredictionEngine CreateEngine() + { + ITransformer mlModel = LoadModel(false); + return mlContext.Model.CreatePredictionEngine(mlModel); + } + } + diff --git a/Game1/Sources/Objects/Fertilizer.cs b/Game1/Sources/Objects/Fertilizer.cs new file mode 100644 index 0000000..ae4f067 --- /dev/null +++ b/Game1/Sources/Objects/Fertilizer.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + + +class Fertilizer +{ + public int ID { get; set; } + public string Name { get; set; } + public float Nitrogen { get; set; } + public float Phosphorus { get; set; } + public float Potassium { get; set; } +} \ No newline at end of file diff --git a/Game1/Sources/Objects/FertilizerHolder.cs b/Game1/Sources/Objects/FertilizerHolder.cs new file mode 100644 index 0000000..828e13b --- /dev/null +++ b/Game1/Sources/Objects/FertilizerHolder.cs @@ -0,0 +1,181 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + + +class FertilizerHolder +{ + private Fertilizer[] FertilizerType = new Fertilizer[8]; + + public void init() + { + /* + FertilizerType[0] = new Fertilizer + { + ID = 999, + Name = "None", + Nitrogen = 0.0f / 5, + Phosphorus = 0 * 0.436f / 5, + Potassium = 0 * 0.83f / 5 + }; + + FertilizerType[1] = new Fertilizer + { + ID = 0, + Name = "10-26-26", + Nitrogen = 10.0f / 5, + Phosphorus = 26 * 0.436f / 5, + Potassium = 26 * 0.83f / 5 + }; + + FertilizerType[2] = new Fertilizer + { + ID = 1, + Name = "14-35-14", + Nitrogen = 14.0f / 5, + Phosphorus = 35 * 0.436f / 5, + Potassium = 14 * 0.83f / 5 + }; + + FertilizerType[3] = new Fertilizer + { + ID = 2, + Name = "17-17-17", + Nitrogen = 17.0f / 5, + Phosphorus = 17 * 0.436f / 5, + Potassium = 17 * 0.83f / 5 + }; + + FertilizerType[4] = new Fertilizer + { + ID = 3, + Name = "20-20", + Nitrogen = 20.0f / 5, + Phosphorus = 20 * 0.436f / 5, + Potassium = 0 * 0.83f / 5 + }; + + FertilizerType[5] = new Fertilizer + { + ID = 4, + Name = "28-28", + Nitrogen = 28.0f / 5, + Phosphorus = 28 * 0.436f / 5, + Potassium = 0 * 0.83f / 5 + }; + + FertilizerType[6] = new Fertilizer + { + ID = 5, + Name = "DAP", + Nitrogen = 18.0f / 5, + Phosphorus = 46 * 0.436f / 5, + Potassium = 0 * 0.83f / 5 + }; + + FertilizerType[7] = new Fertilizer + { + ID = 6, + Name = "Urea", + Nitrogen = 46.0f / 5, + Phosphorus = 0 * 0.436f / 5, + Potassium = 0 * 0.83f / 5 + }; + } + */ + FertilizerType[0] = new Fertilizer + { + ID = 999, + Name = "None", + Nitrogen = 0.0f / 5, + Phosphorus = 0 * 0.436f / 5, + Potassium = 0 * 0.83f / 5 + }; + + FertilizerType[1] = new Fertilizer + { + ID = 0, + Name = "10-26-26", + Nitrogen = 26.0f / 5, + Phosphorus = 30 * 0.436f / 5, + Potassium = 6 * 0.83f / 5 + }; + + FertilizerType[2] = new Fertilizer + { + ID = 1, + Name = "14-35-14", + Nitrogen = 24.0f / 5, + Phosphorus = 17 * 0.436f / 5, + Potassium = 22 * 0.83f / 5 + }; + + FertilizerType[3] = new Fertilizer + { + ID = 2, + Name = "17-17-17", + Nitrogen = 17.0f / 5, + Phosphorus = 17 * 0.436f / 5, + Potassium = 17 * 0.83f / 5 + }; + + FertilizerType[4] = new Fertilizer + { + ID = 3, + Name = "20-20", + Nitrogen = 10.0f / 5, + Phosphorus = 10 * 0.436f / 5, + Potassium = 20 * 0.83f / 5 + }; + + FertilizerType[5] = new Fertilizer + { + ID = 4, + Name = "28-28", + Nitrogen = 14.0f / 5, + Phosphorus = 14 * 0.436f / 5, + Potassium = 28 * 0.83f / 5 + }; + + FertilizerType[6] = new Fertilizer + { + ID = 5, + Name = "DAP", + Nitrogen = 12.0f / 5, + Phosphorus = 18 * 0.436f / 5, + Potassium = 34 * 0.83f / 5 + }; + + FertilizerType[7] = new Fertilizer + { + ID = 6, + Name = "Urea", + Nitrogen = 0.0f / 5, + Phosphorus = 23 * 0.436f / 5, + Potassium = 23 * 0.83f / 5 + }; + } + + + public int GetFertilizerID(string name) + { + foreach (Fertilizer fertilizer in FertilizerType) + { + if (fertilizer.Name == name) + return fertilizer.ID; + } + return 0; + } + + public Fertilizer GetFertilizer(string name) + { + foreach (Fertilizer fertilizer in FertilizerType) + { + if (fertilizer.Name == name) + return fertilizer; + } + return FertilizerType[0]; + } +} \ No newline at end of file diff --git a/Game1/Sources/Objects/InventorySystem/Cargo.cs b/Game1/Sources/Objects/InventorySystem/Cargo.cs index 25d8f7c..e454923 100644 --- a/Game1/Sources/Objects/InventorySystem/Cargo.cs +++ b/Game1/Sources/Objects/InventorySystem/Cargo.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; class Cargo { - private Items[,] items = new Items[2, 30]; + private Items[,] items = new Items[2, 351]; private int[] Count = new int[2]; @@ -25,17 +25,17 @@ class Cargo items[0, 6] = new Items("Urea", 1, 6); //Crop Seed Storage - items[1, 0] = new Items("Barley", 1, 0); + items[1, 0] = new Items("Barley", 2, 0); items[1, 1] = new Items("Cotton", 1, 1); - items[1, 2] = new Items("Ground Nuts", 1, 2); - items[1, 3] = new Items("Maize", 1, 3); - items[1, 4] = new Items("Millets", 1, 4); - items[1, 5] = new Items("Oil Seeds", 1, 5); - items[1, 6] = new Items("Paddy", 1, 6); - items[1, 7] = new Items("Pulses", 1, 7); - items[1, 8] = new Items("Sugarcane", 1, 8); - items[1, 9] = new Items("Tobacco", 1, 9); - items[1, 10] = new Items("Wheat", 1, 10); + items[1, 2] = new Items("Ground Nuts", 5, 2); + items[1, 3] = new Items("Maize", 3, 3); + items[1, 4] = new Items("Millets", 4, 4); + items[1, 5] = new Items("Oil Seeds", 4, 5); + items[1, 6] = new Items("Paddy", 5, 6); + items[1, 7] = new Items("Pulses", 5, 7); + items[1, 8] = new Items("Sugarcane", 3, 8); + items[1, 9] = new Items("Tobacco", 2, 9); + items[1, 10] = new Items("Wheat", 2, 10); } public void addItem(Items item, int Type) diff --git a/Game1/Sources/Objects/InventorySystem/Inventory.cs b/Game1/Sources/Objects/InventorySystem/Inventory.cs index 389e3f8..a6e418e 100644 --- a/Game1/Sources/Objects/InventorySystem/Inventory.cs +++ b/Game1/Sources/Objects/InventorySystem/Inventory.cs @@ -11,7 +11,7 @@ using Microsoft.Xna.Framework.Input; class Inventory { private int Weight = 0; - private int maxWeight = 25; + private int maxWeight = 350; private Cargo cargo = new Cargo(); private Cargo itemStorage = new Cargo(); @@ -101,6 +101,7 @@ class Inventory public void removeItem(int Index, int Type) { + Weight = Weight - itemStorage.getItemByIndex(Index, Type).getWeight(); cargo.removeItem(Index, Type); } @@ -109,6 +110,37 @@ class Inventory return itemStorage; } + public void clearInventory() + { + for (int i = 0; i <= 6; i++) + while (useItem(i, 0)) ; + + for (int i = 0; i <= 10; i++) + while (useItem(i, 1)) ; + } + + public void fillWithFertilizer() + { + int i = 0; + while (getWeight() < getMaxWeight()) + { + if (i > 6) + i = 0; + addItem(i, 0); + i++; + } + } + + public bool isMissingFertilizer() + { + for (int i = 0; i <= 6; i++) + { + if (!itemExists(i, 0)) + return true; + } + return false; + } + public void printItems(Input input, SpriteBatch spriteBatch, SpriteFont Bold) { diff --git a/Game1/Sources/Smart/AI.cs b/Game1/Sources/Smart/AI.cs index 02b7ebb..b727887 100644 --- a/Game1/Sources/Smart/AI.cs +++ b/Game1/Sources/Smart/AI.cs @@ -15,6 +15,7 @@ class AI private Vector2 Size; private Vector2 targetPos; private Inventory inventory = new Inventory(); + private FertilizerHolder fertilizerHolder = new FertilizerHolder(); private int Rotation; private Astar astar = new Astar(); @@ -52,6 +53,9 @@ class AI int count = 0; int testsize = 2; Vector2 newTarget; + if (tractorPos != housePos) + if (inventory.getWeight() == inventory.getMaxWeight() || inventory.isMissingFertilizer()) + return housePos; while (true) { for (int x = 0; x < Size.X; x++) @@ -59,38 +63,51 @@ class AI { if (farm.getCrop(x, y).getStatus() >= 2 && tractorPos != new Vector2(x, y)) { - score = calculateSoilScore(x, y); - queue.AddToQueue(x, y, score); - if (farm.getCrop(x, y).getCropTimer() == 1 || farm.getCrop(x, y).getStatus() == 2) - count++; + if (farm.getCrop(x, y).isSaturated() || !farm.getCrop(x, y).belowCapacity()) + { + //do nothing + } + else + { + score = calculateSoilScore(x, y); + queue.AddToQueue(x, y, score); + if (farm.getCrop(x, y).getCropTimer() == 1 || farm.getCrop(x, y).getStatus() == 2) + count++; + } } } if (count > 0) break; - else if (tractorPos != housePos) + else //if (tractorPos != housePos) return newTarget = GetMaxFNode(testsize, queue); - - /* - else if (tractorPos == housePos) - { - List temp = astar.GetAdjacentNodes(tractorPos); - return temp[0].getCords(); - } - */ } newTarget = GetMinFNode(Math.Min(testsize, count), queue); + queue = null; return newTarget; } public Farm changeCropStatus() { - if (farm.getCrop((int)tractorPos.X, (int)tractorPos.Y).getCropTimer() == 1) + int x = (int)tractorPos.X; + int y = (int)tractorPos.Y; + Fertilizer fertilizer = new Fertilizer(); + fertilizerHolder.init(); + if (farm.getCrop(x, y).getCropTimer() == 1) { - farm.setCropStatus(tractorPos.X, tractorPos.Y); - if (farm.getCrop((int)tractorPos.X, (int)tractorPos.Y).getStatus() == 2) + if (farm.getCrop(x, y).getStatus() == 2) { - inventory.addItem(farm.getCrop((int)tractorPos.X, (int)tractorPos.Y).getCropType() - 1, 1); + fertilizer = fertilizerHolder.GetFertilizer(Engine.PredictFertilizer(farm.getCrop(x, y), farm.getPresetCropTypes(farm.getCrop(x, y).getCropType()))); + while (!(farm.getCrop(x, y).isSaturated()) && farm.getCrop(x, y).belowCapacity() && inventory.useItem(fertilizerHolder.GetFertilizerID(fertilizer.Name), 0)) + { + farm.getCrop(x, y).Fertilize(fertilizer); + fertilizer = fertilizerHolder.GetFertilizer(Engine.PredictFertilizer(farm.getCrop(x, y), farm.getPresetCropTypes(farm.getCrop(x, y).getCropType()))); + } + } + farm.setCropStatus(tractorPos.X, tractorPos.Y); + if (farm.getCrop(x, y).getStatus() == 2) + { + inventory.addItem(farm.getCrop(x, y).getCropType() - 1, 1); } } @@ -107,6 +124,7 @@ class AI int score = 0; int statusScore = 0; int timerScore = 0; + int saturationScore = 0; int aproxDistance = (int)(Math.Abs(x - tractorPos.X) + Math.Abs(y - tractorPos.Y)); CropTypesHolder holder = new CropTypesHolder(); holder.init(); @@ -130,7 +148,14 @@ class AI score = (int)(crop.getProductionRate(avgHold) * 10); - return score + (-aproxDistance * 5) + statusScore + timerScore; + if (!crop.isSaturated()) + saturationScore = 5; + else + { + saturationScore = -100; + } + + return score + (-aproxDistance * 5) + statusScore + timerScore + saturationScore; } private float norm(float min, float max, float val) @@ -200,6 +225,13 @@ class AI return minEntry.Coordinates; } + public void reloadCargo() + { + inventory.clearInventory(); + inventory.fillWithFertilizer(); + + } + } diff --git a/Game1/Sources/Smart/SmartTractor.cs b/Game1/Sources/Smart/SmartTractor.cs index 29fdf50..322a712 100644 --- a/Game1/Sources/Smart/SmartTractor.cs +++ b/Game1/Sources/Smart/SmartTractor.cs @@ -22,6 +22,8 @@ class SmartTractor astar.update(farm.getCrops(), Size, tractorPos / (tileSize + Spacing), housePos / (tileSize + Spacing), Rotation); getTargetPosition(ai.newTarget()); astar.update(farm.getCrops(), Size, tractorPos / (tileSize + Spacing), housePos / (tileSize + Spacing), Target / (tileSize + Spacing), Rotation); + if (tractorPos == housePos) + ai.reloadCargo(); return astar.FindPath(true); }