Compare commits

..

4 Commits

Author SHA1 Message Date
Joel
5dce3ed955 Inventory System Implemented Into AI 2020-05-10 01:10:42 +02:00
Joel
efd90e9b32 Merge branch 'dev' into Joel-Repo 2020-05-09 14:27:48 +02:00
Joel
32251898bb Merge branch 'Oskar-ML' into Joel-Repo 2020-05-09 14:26:35 +02:00
Joel
c854bf093b asd 2020-05-08 00:37:18 +02:00
77 changed files with 596 additions and 2099858 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 872 KiB

View File

@ -1,38 +0,0 @@
# Final Evaluation
## Introduction
PotatoPlan is an Inteligent Tractor AI Project and is written in C# using Monogame framework.
NuGet Packages used and requeired for the project to work ar as follows:
C5
Microsoft.ML
Microsoft.ML.LightGBM
System.Drawing.Common
In our project agent (tractor) moves on resizable grid, which starting size is dependant on primary screen resolution.
The task of the agent is to go through all soil tiles and plant different types of crops, use proper fertilizer and collect crops when fully grown.
Window can be resized usind WASD. Tractor speed can be changed using UP and DOWN arrow keys, while simulation speed can be changed using LEFT and RIGHT arrow keys.
Also house placement can be changed by left clicking on a tile, and get info about the tile by right clicking.
Apart from Machine Learning Algorithms used in project there are also many different features implemented like:
- Using A* algorithm to find an optimal path to previously selected target.
- Target is found by scoring system which assign score to a tile based on few factors like production rate or distance.
- Dynamically allocated cargo space for each fertilizer based on how often each fertilizer is used.
- Day and night cycle and season system.
- Using noise map generated for rainfall calculations to draw moving clouds.
- ... and few other.
## Machine Learning Algorithms
Project in its current state uses Machine Learning Algorithms to solve 2 problems. Light Gradient-Boosted Trees are used for both problems:
1. Choosing a proper fertilizer which should be applied to current tile, based on few variables like nutrients in soil.
Applying proper fertilizer boosts production rate of a crop (rate of growth of a crop). This part was done by Oskar Nastały.
2. Calculating production rate of a tile based on rainfall and few other variabels. Noise map is generated and used to simulate dynamically changing rainfall.
Then once a day AI is used to calculate base production rate multiplier. This part was done by Joel Städe.
## Examples
### Clouds
![Clouds](https://git.wmi.amu.edu.pl/s425077/PotatoPlan/raw/Joel-ML/Evaluation_examples/Example_clouds.PNG)
### UI
![UI](https://git.wmi.amu.edu.pl/s425077/PotatoPlan/raw/Joel-ML/Evaluation_examples/Example_UI.PNG)

BIN
Game1.bnp

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 MiB

View File

@ -37,18 +37,6 @@
/processorParam:TextureFormat=Color
/build:BerriesIcon.png
#begin Bottom.png
/importer:TextureImporter
/processor:TextureProcessor
/processorParam:ColorKeyColor=255,0,255,255
/processorParam:ColorKeyEnabled=True
/processorParam:GenerateMipmaps=False
/processorParam:PremultiplyAlpha=True
/processorParam:ResizeToPowerOfTwo=False
/processorParam:MakeSquare=False
/processorParam:TextureFormat=Color
/build:Bottom.png
#begin CarronIcon.png
/importer:TextureImporter
/processor:TextureProcessor
@ -61,30 +49,6 @@
/processorParam:TextureFormat=Color
/build:CarronIcon.png;CarrotIcon.png
#begin Cloud.png
/importer:TextureImporter
/processor:TextureProcessor
/processorParam:ColorKeyColor=255,0,255,255
/processorParam:ColorKeyEnabled=True
/processorParam:GenerateMipmaps=False
/processorParam:PremultiplyAlpha=True
/processorParam:ResizeToPowerOfTwo=False
/processorParam:MakeSquare=False
/processorParam:TextureFormat=Color
/build:Cloud.png
#begin Clouds.png
/importer:TextureImporter
/processor:TextureProcessor
/processorParam:ColorKeyColor=255,0,255,255
/processorParam:ColorKeyEnabled=True
/processorParam:GenerateMipmaps=False
/processorParam:PremultiplyAlpha=True
/processorParam:ResizeToPowerOfTwo=False
/processorParam:MakeSquare=False
/processorParam:TextureFormat=Color
/build:Clouds.png
#begin CottonIcon.png
/importer:TextureImporter
/processor:TextureProcessor
@ -140,18 +104,6 @@
/processorParam:TextureFormat=Color
/build:house.png
#begin Left.png
/importer:TextureImporter
/processor:TextureProcessor
/processorParam:ColorKeyColor=255,0,255,255
/processorParam:ColorKeyEnabled=True
/processorParam:GenerateMipmaps=False
/processorParam:PremultiplyAlpha=True
/processorParam:ResizeToPowerOfTwo=False
/processorParam:MakeSquare=False
/processorParam:TextureFormat=Color
/build:Left.png
#begin MaizeIcon.png
/importer:TextureImporter
/processor:TextureProcessor
@ -236,6 +188,18 @@
/processorParam:TextureFormat=Color
/build:Plantable.png
#begin Planted.png
/importer:TextureImporter
/processor:TextureProcessor
/processorParam:ColorKeyColor=255,0,255,255
/processorParam:ColorKeyEnabled=True
/processorParam:GenerateMipmaps=False
/processorParam:PremultiplyAlpha=True
/processorParam:ResizeToPowerOfTwo=False
/processorParam:MakeSquare=False
/processorParam:TextureFormat=Color
/build:Planted.png
#begin ProgressionBar.png
/importer:TextureImporter
/processor:TextureProcessor
@ -272,30 +236,6 @@
/processorParam:TextureFormat=Color
/build:PulsesIcon.png
#begin rain.png
/importer:TextureImporter
/processor:TextureProcessor
/processorParam:ColorKeyColor=255,0,255,255
/processorParam:ColorKeyEnabled=True
/processorParam:GenerateMipmaps=False
/processorParam:PremultiplyAlpha=True
/processorParam:ResizeToPowerOfTwo=False
/processorParam:MakeSquare=False
/processorParam:TextureFormat=Color
/build:rain.png
#begin Right.png
/importer:TextureImporter
/processor:TextureProcessor
/processorParam:ColorKeyColor=255,0,255,255
/processorParam:ColorKeyEnabled=True
/processorParam:GenerateMipmaps=False
/processorParam:PremultiplyAlpha=True
/processorParam:ResizeToPowerOfTwo=False
/processorParam:MakeSquare=False
/processorParam:TextureFormat=Color
/build:Right.png
#begin SeedOilIcon.png
/importer:TextureImporter
/processor:TextureProcessor
@ -356,18 +296,6 @@
/processorParam:TextureFormat=Color
/build:TobaccoIcon.png
#begin Top.png
/importer:TextureImporter
/processor:TextureProcessor
/processorParam:ColorKeyColor=255,0,255,255
/processorParam:ColorKeyEnabled=True
/processorParam:GenerateMipmaps=False
/processorParam:PremultiplyAlpha=True
/processorParam:ResizeToPowerOfTwo=False
/processorParam:MakeSquare=False
/processorParam:TextureFormat=Color
/build:Top.png
#begin Tractor.png
/importer:TextureImporter
/processor:TextureProcessor
@ -404,15 +332,3 @@
/processorParam:TextureFormat=Color
/build:WheatIcon.png
#begin WoodBackground.png
/importer:TextureImporter
/processor:TextureProcessor
/processorParam:ColorKeyColor=255,0,255,255
/processorParam:ColorKeyEnabled=True
/processorParam:GenerateMipmaps=False
/processorParam:PremultiplyAlpha=True
/processorParam:ResizeToPowerOfTwo=False
/processorParam:MakeSquare=False
/processorParam:TextureFormat=Color
/build:WoodBackground.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 872 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +0,0 @@
Mean Absolute Error: 0.0108015636096701
Mean Squared Error: 0.0434908452113952
R Squared: 0.702723944791744

View File

@ -1,2 +0,0 @@
Mean Absolute Error: 187.060835104336
R Squared: 0.913526230109177

BIN
Game1/Content/MLmodel Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 578 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 578 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 776 B

BIN
Game1/Content/Planted.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

View File

@ -2,8 +2,6 @@
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.IO;
using WinForm = System.Windows.Forms;
namespace Game1
{
@ -16,17 +14,11 @@ namespace Game1
SpriteBatch spriteBatch;
SpriteFont Bold;
private Texture2D[] tile = new Texture2D[5];
private Texture2D[] tileConnected = new Texture2D[5];
private Texture2D[] Crops = new Texture2D[12];
private Texture2D tractor;
private Texture2D Background;
private Texture2D house;
private Texture2D markers;
private Texture2D mouseCursor;
private Texture2D Rain;
string directory = Directory.GetCurrentDirectory();
private Texture2D ProgressionBar;
private Texture2D ProgressionBarStatus;
@ -46,10 +38,6 @@ namespace Game1
MouseState state;
private int cloudAnimationSpeed = 3;
private int cloudFrame = 0;
private int cloudSprite;
public Game1()
{
@ -75,20 +63,15 @@ namespace Game1
cropTypesNames[10] = "Tobacco";
cropTypesNames[11] = "Wheat";
Engine.init();
Sources.ML_Joel.Engine.initArea();
//Sources.ML_Joel.Engine.CreateModel();
//Sources.ML_Joel.Engine.CreateModelArea();
//Generates the map with some random values
inventory.initInventorySystem();
string path = directory + "Game1/Content/ML/Fertilizer_Prediction.csv";
int res = WinForm.Screen.PrimaryScreen.Bounds.Height;
input.init(graphics, new Vector2((float)Math.Floor(WinForm.Screen.PrimaryScreen.Bounds.Width/56*0.6f), (float)Math.Floor((WinForm.Screen.PrimaryScreen.Bounds.Height / 56f) - 7)), 56, 0); //Generates the starting size
input.init(graphics, new Vector2(16,16), 56, 1); //Generates the starting size
houseUnit.init(input.getTileSize(), input.getSpacing()); //Generates the house position
tractorUnit.init(houseUnit.GetRectangle(), input); //Generates the Tractor
tractorUnit.updateSizing(input, 0, houseUnit.getVector(), Time); //Updates the first Size of the Tractor
@ -98,7 +81,7 @@ namespace Game1
graphics.PreferredBackBufferWidth = (input.getTileSize() + input.getSpacing()) * (int)input.getSize().X;
graphics.PreferredBackBufferHeight = (input.getTileSize() + input.getSpacing()) * (int)input.getSize().Y + 380;
graphics.PreferredBackBufferHeight = (input.getTileSize() + input.getSpacing()) * (int)input.getSize().Y + 300;
graphics.ApplyChanges();
}
@ -115,15 +98,8 @@ namespace Game1
tile[2] = Content.Load<Texture2D>("Plantable");
tile[3] = Content.Load<Texture2D>("Crop");
tileConnected[0] = Content.Load<Texture2D>("Left");
tileConnected[1] = Content.Load<Texture2D>("Top");
tileConnected[2] = Content.Load<Texture2D>("Right");
tileConnected[3] = Content.Load<Texture2D>("Bottom");
Rain = Content.Load<Texture2D>("Clouds");
Background = Content.Load<Texture2D>("WoodBackground");
Crops[0] = Content.Load<Texture2D>("Markers");
@ -175,7 +151,7 @@ namespace Game1
Time.updateTime(tractorUnit.getSpeed());
//System.Threading.Thread updatethread = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(Update));
base.Update(gameTime);
}
@ -185,73 +161,64 @@ namespace Game1
GraphicsDevice.Clear(Color.FromNonPremultiplied(255,255,255,140));
spriteBatch.Begin();
DrawTiles();
spriteBatch.Draw(markers, new Rectangle((int)tractorUnit.getTargetPosition().X / input.getSpacingTile() * (input.getTileSize() + input.getSpacing()) + input.getTileSize() / 4, (int)tractorUnit.getTargetPosition().Y / input.getSpacingTile() * (input.getTileSize() + input.getSpacing()) + input.getTileSize() / 4, input.getTileSize()/2, input.getTileSize()/2), Color.White);
spriteBatch.Draw(markers, new Rectangle((int)tractorUnit.getTargetPosition().X / input.getSpacingTile() * (input.getTileSize() + input.getSpacing()) + input.getTileSize() / 4, (int)tractorUnit.getTargetPosition().Y / input.getSpacingTile() * (input.getTileSize() + input.getSpacing()) + input.getTileSize() / 4, input.getTileSize()/2, input.getTileSize()/2), Color.Green);
for (int i = 0; i < tractorUnit.getPath().getCount() + 1; i++)
{
spriteBatch.Draw(markers, new Rectangle((int)tractorUnit.getPath().getByIndex(i).getCords().X * (input.getSpacingTile()) + input.getTileSize() / 4, (int)tractorUnit.getPath().getByIndex(i).getCords().Y * (input.getSpacingTile()) + input.getTileSize() / 4, input.getTileSize()/2, input.getTileSize()/2), Color.White);
spriteBatch.Draw(markers, new Rectangle((int)tractorUnit.getPath().getByIndex(i).getCords().X * (input.getSpacingTile()) + input.getTileSize() / 4, (int)tractorUnit.getPath().getByIndex(i).getCords().Y * (input.getSpacingTile()) + input.getTileSize() / 4, input.getTileSize()/2, input.getTileSize()/2), Color.Green);
}
spriteBatch.Draw(markers, new Rectangle((int)tractorUnit.getPath().getFinalDest().getCords().X * (input.getSpacingTile()) + Convert.ToInt32(input.getTileSize() / 6), (int)tractorUnit.getPath().getFinalDest().getCords().Y * (input.getSpacingTile()) + Convert.ToInt32(input.getTileSize() / 6), Convert.ToInt32(input.getTileSize() / 1.5), Convert.ToInt32(input.getTileSize() / 1.5)), Color.White); //Draws the current target of the tractor
spriteBatch.Draw(house, houseUnit.GetRectangle(), Time.GetTimeOfDay());
spriteBatch.Draw(markers, new Rectangle((int)tractorUnit.getPath().getFinalDest().getCords().X * (input.getSpacingTile()) + Convert.ToInt32(input.getTileSize() / 6), (int)tractorUnit.getPath().getFinalDest().getCords().Y * (input.getSpacingTile()) + Convert.ToInt32(input.getTileSize() / 6), Convert.ToInt32(input.getTileSize() / 1.5), Convert.ToInt32(input.getTileSize() / 1.5)), Color.Red); //Draws the current target of the tractor
spriteBatch.Draw(tractor, new Vector2((int)tractorUnit.getPos().X + input.getTileSize() / 2, (int)tractorUnit.getPos().Y + input.getTileSize() / 2), new Rectangle(0, 0, input.getTileSize(), input.getTileSize()), Time.GetTimeOfDay(), tractorUnit.getRotation(), new Vector2(input.getTileSize() / 2, input.getTileSize() / 2), 1.0f, SpriteEffects.None, 1);
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
spriteBatch.Draw(Background, new Rectangle(i * 512, (int)input.getSize().Y * (input.getTileSize() + input.getSpacing()) + j * 512, 512, 512), Color.FromNonPremultiplied(125, 125, 125, 255));
spriteBatch.Draw(ProgressionBar, new Rectangle(i * 227, (int)(input.getSize().Y * (input.getTileSize() + input.getSpacing())), 5, 295), Color.White);
}
for (int i = 0; i < 15; i++)
{
spriteBatch.Draw(ProgressionBar, new Rectangle(0, (int)(input.getSize().Y * (input.getTileSize() + input.getSpacing())) + i * 20, (int)(input.getSize().X * (input.getTileSize() + input.getSpacing())), 1), Color.White);
}
for (int i = 0; i < 5; i++)
{
spriteBatch.Draw(tile[0], new Rectangle(i * 227, (int)(input.getSize().Y * (input.getTileSize() + input.getSpacing())), 5, 500), Color.White);
}
for (int i = 0; i < 20; i++)
{
spriteBatch.Draw(tile[0], new Rectangle(0, (int)(input.getSize().Y * (input.getTileSize() + input.getSpacing())) + i * 20, (int)(input.getSize().X * (input.getTileSize() + input.getSpacing())), 1), Color.White);
}
tractorUnit.drawInventory(input, spriteBatch, Bold, inventory.getPredefinedItems());
spriteBatch.DrawString(Bold, "Time: ", new Vector2(10, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 2), Color.DarkRed);
spriteBatch.DrawString(Bold, "Days " + Time.getDays(), new Vector2(60, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 2), Color.Teal);
spriteBatch.DrawString(Bold, Time.getTimeOfYear(), new Vector2(120, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 2), Color.Teal);
spriteBatch.DrawString(Bold, "Days " + Time.getDays(), new Vector2(60, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 2), Color.DarkBlue);
spriteBatch.DrawString(Bold, "Day Progression: ", new Vector2(10, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 22), Color.DarkRed);
spriteBatch.DrawString(Bold, Time.GetTimeOfDayInt().ToString() + "%", new Vector2(140, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 22), Color.Teal);
spriteBatch.DrawString(Bold, Time.GetTimeOfDayInt().ToString() + "%", new Vector2(140, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 22), Color.DarkBlue);
spriteBatch.DrawString(Bold, "Tractor Properties:", new Vector2(10, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 62), Color.DarkRed);
spriteBatch.DrawString(Bold, "Speed:" + tractorUnit.getSpeed().ToString(), new Vector2(10, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 82), Color.Teal);
spriteBatch.DrawString(Bold, "Tractor Position:" + new Vector2((float)Math.Round(tractorUnit.getPos().X / input.getSpacingTile(), 1), (float)Math.Round(tractorUnit.getPos().Y / input.getSpacingTile(), 1)), new Vector2(10, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 102), Color.Teal);
spriteBatch.DrawString(Bold, "Tractor Rotation:" + Math.Round(tractorUnit.getRotation(), 2).ToString() + " Degrees", new Vector2(10, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 122), Color.Teal);
spriteBatch.DrawString(Bold, "Tractor Speed:" + tractorUnit.getTractorSpeed().ToString(), new Vector2(10, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 142), Color.Teal);
spriteBatch.DrawString(Bold, "Tractor Target:" + tractorUnit.getPath().getFinalDest().getCords().ToString(), new Vector2(10, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 162), Color.Teal);
spriteBatch.DrawString(Bold, "Speed:" + tractorUnit.getSpeed().ToString(), new Vector2(10, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 82) , Color.DarkBlue);
spriteBatch.DrawString(Bold, "Tractor Position:" + new Vector2((float)Math.Round(tractorUnit.getPos().X / input.getSpacingTile(), 1), (float)Math.Round(tractorUnit.getPos().Y / input.getSpacingTile(), 1)), new Vector2(10, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 102), Color.DarkBlue);
spriteBatch.DrawString(Bold, "Tractor Rotation:" + Math.Round(tractorUnit.getRotation(), 2).ToString() + " Degrees", new Vector2(10, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 122), Color.DarkBlue);
spriteBatch.DrawString(Bold, "Tractor Speed:" + tractorUnit.getTractorSpeed().ToString(), new Vector2(10, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 142), Color.DarkBlue);
spriteBatch.DrawString(Bold, "Tractor Target:" + tractorUnit.getPath().getFinalDest().getCords().ToString(), new Vector2(10, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 162), Color.DarkBlue);
spriteBatch.DrawString(Bold, "Map Properties:", new Vector2(10, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 202), Color.DarkRed);
spriteBatch.DrawString(Bold, "Tile Size:" + input.getTileSize().ToString() + "pix", new Vector2(10, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 222), Color.Teal); //Draws the tile size
spriteBatch.DrawString(Bold, "Matrix Size: " + input.getSize().X.ToString() + " X " + input.getSize().Y.ToString(), new Vector2(10, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 242), Color.Teal);
spriteBatch.DrawString(Bold, "House Position: " + houseUnit.getVector() / input.getSpacingTile(), new Vector2(10, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 262), Color.Teal);
tractorUnit.getFarm().drawWeatherInformation(spriteBatch, Bold, input);
spriteBatch.DrawString(Bold, "Tile Size:" + input.getTileSize().ToString() + "pix", new Vector2(10, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 222), Color.DarkBlue); //Draws the tile size
spriteBatch.DrawString(Bold, "Matrix Size: " + input.getSize().X.ToString() + " X " + input.getSize().Y.ToString(), new Vector2(10, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 242), Color.DarkBlue);
spriteBatch.DrawString(Bold, "House Position: " + houseUnit.getVector() / input.getSpacingTile(), new Vector2(10, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 262), Color.DarkBlue);
spriteBatch.DrawString(Bold, "Total Weight: ", new Vector2(700, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 182), Color.DarkRed);
spriteBatch.DrawString(Bold, "(" + tractorUnit.getInventory().getWeight() + "/" + tractorUnit.getInventory().getMaxWeight() + ")", new Vector2(800, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 182), Color.Teal);
spriteBatch.DrawString(Bold, "(" + tractorUnit.getInventory().getWeight() + "/" + tractorUnit.getInventory().getMaxWeight() + ")", new Vector2(800, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 182), Color.DarkBlue);
tractorUnit.drawInventory(input, spriteBatch, Bold, inventory.getPredefinedItems());
InspectTile();
spriteBatch.Draw(mouseCursor, new Rectangle((int)mousePosition.X, (int)mousePosition.Y, 14, 21), Color.White);
spriteBatch.End();
spriteBatch.Begin();
InspectTile();
spriteBatch.End();
base.Draw(gameTime);
}
@ -262,38 +229,11 @@ namespace Game1
{
for (int j = 0; j < input.getSize().Y; j++)
{
Rectangle tilePos = new Rectangle(i * (input.getSpacingTile()), j * (input.getSpacingTile()), input.getTileSize(), input.getTileSize());
spriteBatch.Draw(tile[tractorUnit.getFarm().getCrop(i, j).getStatus()], tilePos, Time.GetTimeOfDay());
if (i > 0)
{
if (tractorUnit.getFarm().getCrop(i - 1, j).getStatus() == 2 || tractorUnit.getFarm().getCrop(i - 1, j).getStatus() == 3)
{
spriteBatch.Draw(tileConnected[0], tilePos, Time.GetTimeOfDay());
}
}
if (j > 0)
{
if (tractorUnit.getFarm().getCrop(i, j - 1).getStatus() == 2 || tractorUnit.getFarm().getCrop(i, j - 1).getStatus() == 3)
{
spriteBatch.Draw(tileConnected[1], tilePos, Time.GetTimeOfDay());
}
}
if (i < input.getSize().X)
{
if (tractorUnit.getFarm().getCrop(i + 1, j).getStatus() == 2 || tractorUnit.getFarm().getCrop(i + 1, j).getStatus() == 3)
{
spriteBatch.Draw(tileConnected[2], tilePos, Time.GetTimeOfDay());
}
}
if (j < input.getSize().Y)
{
if (tractorUnit.getFarm().getCrop(i, j + 1).getStatus() == 2 || tractorUnit.getFarm().getCrop(i, j + 1).getStatus() == 3)
{
spriteBatch.Draw(tileConnected[3], tilePos, Time.GetTimeOfDay());
}
}
if (tilePos.Intersects(mousePosition))
{
spriteBatch.Draw(tile[tractorUnit.getFarm().getCrop(i, j).getStatus()], tilePos, Color.FromNonPremultiplied(0, 0, 20, 40));
@ -320,7 +260,7 @@ namespace Game1
if ((tractorUnit.getFarm().getCrop(i, j).getStatus() == 3))
{
spriteBatch.Draw(ProgressionBar, new Rectangle(i * (input.getSpacingTile()) + input.getTileSize() - input.getTileSize() / 3, j * (input.getSpacingTile()), input.getTileSize() / 3, input.getTileSize()), Color.White);
spriteBatch.Draw(ProgressionBarStatus, new Rectangle(i * (input.getSpacingTile()) + input.getTileSize() - input.getTileSize() / 4, j * (input.getSpacingTile()) + input.getTileSize() / 3, input.getTileSize() / 4, tractorUnit.getFarm().getCrop(i, j).getCropTimerBar((input.getTileSize())) + 1), new Color(tractorUnit.getFarm().getCrop(i, j).getColour()));
spriteBatch.Draw(ProgressionBarStatus, new Rectangle(i * (input.getSpacingTile()) + input.getTileSize() - input.getTileSize() / 4, j * (input.getSpacingTile()) + input.getTileSize() / 3, input.getTileSize() / 4, tractorUnit.getFarm().getCrop(i, j).getCropTimerBar((input.getTileSize())) + 1), Color.White);
}
spriteBatch.Draw(Crops[tractorUnit.getFarm().getCrop(i, j).getCropType()], new Rectangle((int)(i * (input.getSpacingTile()) + input.getTileSize() - input.getTileSize() / 2.5), j * (input.getSpacingTile()), (int)(input.getTileSize() / 2.5), input.getTileSize() / 3), Color.White);
}
@ -328,23 +268,12 @@ namespace Game1
}
}
spriteBatch.Draw(house, houseUnit.GetRectangle(), Time.GetTimeOfDay());
spriteBatch.Draw(tractor, new Vector2((int)tractorUnit.getPos().X + input.getTileSize() / 2, (int)tractorUnit.getPos().Y + input.getTileSize() / 2), new Rectangle(0, 0, input.getTileSize(), input.getTileSize()), Time.GetTimeOfDay(), tractorUnit.getRotation(), new Vector2(input.getTileSize() / 2, input.getTileSize() / 2), 1.0f, SpriteEffects.None, 1);
for (int i = -1; i < input.getSize().X + 1; i++) //Draw the tiles
{
for (int j = -1; j < input.getSize().Y + 1; j++)
{
spriteBatch.Draw(Rain, tractorUnit.getFarm().getRainPosition(input.getTileSize(), i, j, input.getSize()), tractorUnit.getFarm().getDestinationRectangle(i, j, input.getSize()), tractorUnit.getFarm().getRainAmount(i, j, Time.GetTimeOfDay(), input.getSize()));
}
}
}
public void InspectTile()
{
spriteBatch.DrawString(Bold, "Crop:", new Vector2(240, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 2), Color.DarkRed);
spriteBatch.DrawString(Bold, "Selected tile: (" + x.ToString() + ", " + y.ToString() + ")", new Vector2(240, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 22), Color.Teal);
spriteBatch.DrawString(Bold, "Selected tile: (" + x.ToString() + ", " + y.ToString() + ")", new Vector2(240, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 22), Color.DarkBlue);
tractorUnit.getFarm().getCrop(x, y).Inspect(input.getTileSize(), input.getSpacing(), Bold, new SpriteBatch(GraphicsDevice), cropTypesNames);
}
}

View File

@ -87,18 +87,8 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Sources\Crops\CropType.cs" />
<Compile Include="Sources\Crops\CropTypesHolder.cs" />
<Compile Include="Sources\Crops\PerlinNoise.cs" />
<Compile Include="Sources\Crops\SoilProperties.cs" />
<Compile Include="Sources\ML\Engine.cs" />
<Compile Include="Sources\ML_Joel\DataModel\InputArea.cs" />
<Compile Include="Sources\ML_Joel\DataModel\Input.cs" />
<Compile Include="Sources\ML_Joel\DataModel\OutputArea.cs" />
<Compile Include="Sources\ML_Joel\DataModel\Output.cs" />
<Compile Include="Sources\ML_Joel\Engine.cs" />
<Compile Include="Sources\ML_Joel\Model.cs" />
<Compile Include="Sources\Objects\DayNightCycle.cs" />
<Compile Include="Sources\Objects\Fertilizer.cs" />
<Compile Include="Sources\Objects\FertilizerHolder.cs" />
<Compile Include="Sources\Objects\HandleRotation.cs" />
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
@ -122,6 +112,7 @@
<Compile Include="Sources\Pathing\A-Star\PathSaver\MinHeap.cs" />
<Compile Include="Sources\Pathing\A-Star\PathSaver\Nodes.cs" />
<Compile Include="Sources\Pathing\A-Star\PathSaver\Path.cs" />
<Compile Include="Sources\Pathing\A-Star\PathSaver\PriorityQueue.cs" />
<Compile Include="Sources\Controlls\Controller.cs" />
<Compile Include="Sources\Pathing\PQEntry.cs" />
<Compile Include="Sources\Pathing\PriorityQueueC5.cs" />
@ -136,7 +127,6 @@
</Reference>
<Reference Include="System" />
<Reference Include="System.Numerics" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
@ -167,15 +157,9 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<PackageReference Include="LibNoise">
<Version>0.2.0</Version>
</PackageReference>
<PackageReference Include="LightGBM">
<Version>2.3.1</Version>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.Common">
<Version>3.6.0</Version>
</PackageReference>
<PackageReference Include="Microsoft.ML">
<Version>1.4.0</Version>
</PackageReference>
@ -197,9 +181,6 @@
<PackageReference Include="System.Collections.Immutable">
<Version>1.7.0</Version>
</PackageReference>
<PackageReference Include="System.Drawing.Common">
<Version>4.7.0</Version>
</PackageReference>
<PackageReference Include="System.Memory">
<Version>4.5.4</Version>
</PackageReference>

View File

@ -2,8 +2,6 @@
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Drawing;
using WinForm = System.Windows.Forms;
class Controller
{
@ -19,7 +17,7 @@ class Controller
public Vector2 updateWindow(int tileSize, int Spacing, Vector2 Size)
{
KeyboardState state = Keyboard.GetState();
if (state.IsKeyDown(Keys.D) && Size.X < Math.Floor(WinForm.Screen.PrimaryScreen.Bounds.Width / (float)tileSize))
if (state.IsKeyDown(Keys.D) && Size.X < 99)
{
Size.X++;
graphics.PreferredBackBufferWidth = (tileSize + Spacing) * (int)Size.X - Spacing;
@ -31,16 +29,16 @@ class Controller
graphics.PreferredBackBufferWidth = (tileSize + Spacing) * (int)Size.X - Spacing;
}
if (state.IsKeyDown(Keys.W) && Size.Y < Math.Floor(WinForm.Screen.PrimaryScreen.Bounds.Height / (float)tileSize) - 7)
if (state.IsKeyDown(Keys.W) && Size.Y < 20)
{
Size.Y++;
graphics.PreferredBackBufferHeight = (tileSize + Spacing) * (int)Size.Y - Spacing + 380;
graphics.PreferredBackBufferHeight = (tileSize + Spacing) * (int)Size.Y - Spacing + 300;
}
if (state.IsKeyDown(Keys.S) && Size.Y > 2)
{
Size.Y--;
graphics.PreferredBackBufferHeight = (tileSize + Spacing) * (int)Size.Y - Spacing + 380;
graphics.PreferredBackBufferHeight = (tileSize + Spacing) * (int)Size.Y - Spacing + 300;
}
return Size;
}

View File

@ -11,17 +11,12 @@ class CropTypes
public string[] soilType = new string[3];
public int[] Times = new int[3];
public string CropName;
public string[] Season = new string[5];
public float Temparature;
public float Humidity;
public float Moisture;
public float Nitrogen;
public float Potassium;
public float Phosphorous;
public float Area;
public int AreaMin;
public int AreaMax;
public CropTypes()

View File

@ -16,7 +16,6 @@ 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;
@ -29,17 +28,12 @@ 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 = 21 + 12.6f;
cropTypes[1].Potassium = 10 + 5.3f;
cropTypes[1].Phosphorous = 20 + 26.0f;
cropTypes[1].Season[0] = "Spring";
cropTypes[1].Season[1] = "Autumn";
cropTypes[1].AreaMin = 1;
cropTypes[1].AreaMax = 8000;
cropTypes[1].Nitrogen = 12.6f;
cropTypes[1].Potassium = 5.3f;
cropTypes[1].Phosphorous = 26.0f;
// Cotton
@ -50,38 +44,24 @@ 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 = 21 + 16.4f;
cropTypes[2].Potassium = 10 + 3.3f;
cropTypes[2].Phosphorous = 20 + 23.8f;
cropTypes[2].Season[0] = "Spring";
cropTypes[2].Season[1] = "Autumn";
cropTypes[2].Season[2] = "Whole Year";
cropTypes[2].AreaMin = 1;
cropTypes[2].AreaMax = 199000;
cropTypes[2].Nitrogen = 16.4f;
cropTypes[2].Potassium = 3.3f;
cropTypes[2].Phosphorous = 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 = 21 + 23.3f;
cropTypes[3].Potassium = 10 + 2.0f;
cropTypes[3].Phosphorous = 20 + 21.6f;
cropTypes[3].Season[0] = "Spring";
cropTypes[3].Season[1] = "Autumn";
cropTypes[3].Season[2] = "Whole Year";
cropTypes[3].Season[3] = "Winter";
cropTypes[3].Season[4] = "Summer";
cropTypes[3].AreaMin = 1;
cropTypes[3].AreaMax = 147000;
cropTypes[3].Nitrogen = 23.3f;
cropTypes[3].Potassium = 2.0f;
cropTypes[3].Phosphorous = 21.6f;
// Maize
@ -89,20 +69,12 @@ 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 = 21 + 18.3f;
cropTypes[4].Potassium = 10 + 5.7f;
cropTypes[4].Phosphorous = 20 + 18.7f;
cropTypes[4].Season[0] = "Spring";
cropTypes[4].Season[1] = "Autumn";
cropTypes[4].Season[2] = "Whole Year";
cropTypes[4].Season[3] = "Winter";
cropTypes[4].Season[4] = "Summer";
cropTypes[4].AreaMin = 1;
cropTypes[4].AreaMax = 73000;
cropTypes[4].Nitrogen = 18.3f;
cropTypes[4].Potassium = 5.7f;
cropTypes[4].Phosphorous = 18.7f;
// Millets
cropTypes[5] = new CropTypes();
@ -111,71 +83,48 @@ 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 = 21 + 23.2f;
cropTypes[5].Potassium = 10 + 0.1f;
cropTypes[5].Phosphorous = 20 + 14.4f;
cropTypes[5].Season[0] = "Spring";
cropTypes[5].Season[1] = "Autumn";
cropTypes[5].Season[2] = "Whole Year";
cropTypes[5].AreaMin = 1;
cropTypes[5].AreaMax = 59000;
cropTypes[5].Nitrogen = 23.2f;
cropTypes[5].Potassium = 0.1f;
cropTypes[5].Phosphorous = 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 = 21 + 19.0f;
cropTypes[6].Potassium = 10 + 2.3f;
cropTypes[6].Phosphorous = 20 + 17.3f;
cropTypes[6].Season[0] = "Whole Year";
cropTypes[6].AreaMin = 25;
cropTypes[6].AreaMax = 25000;
cropTypes[6].Nitrogen = 19.0f;
cropTypes[6].Potassium = 2.3f;
cropTypes[6].Phosphorous = 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 = 21 + 20.8f;
cropTypes[7].Potassium = 10 + 3.7f;
cropTypes[7].Phosphorous = 20 + 16.3f;
cropTypes[7].Season[0] = "Autumn";
cropTypes[7].Season[1] = "Winter";
cropTypes[7].Season[2] = "Summer";
cropTypes[7].AreaMin = 200;
cropTypes[7].AreaMax = 270000;
cropTypes[7].Nitrogen = 20.8f;
cropTypes[7].Potassium = 3.7f;
cropTypes[7].Phosphorous = 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 = 21 + 18.4f;
cropTypes[8].Potassium = 10 + 4.2f;
cropTypes[8].Phosphorous = 20 + 17.5f;
cropTypes[8].Season[0] = "Spring";
cropTypes[8].Season[1] = "Autumn";
cropTypes[8].Season[2] = "Whole Year";
cropTypes[8].Season[3] = "Summer";
cropTypes[8].AreaMin = 40;
cropTypes[8].AreaMax = 140000;
cropTypes[8].Nitrogen = 18.4f;
cropTypes[8].Potassium = 4.2f;
cropTypes[8].Phosphorous = 17.5f;
//Sugarcane
cropTypes[9] = new CropTypes();
@ -184,19 +133,12 @@ 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 = 21 + 14.6f;
cropTypes[9].Potassium = 10 + 4.2f;
cropTypes[9].Phosphorous = 20 + 17.6f;
cropTypes[9].Season[0] = "Spring";
cropTypes[9].Season[1] = "Autumn";
cropTypes[9].Season[2] = "Whole Year";
cropTypes[9].Season[3] = "Winter";
cropTypes[9].AreaMin = 1;
cropTypes[9].AreaMax = 23000;
cropTypes[9].Nitrogen = 14.6f;
cropTypes[9].Potassium = 4.2f;
cropTypes[9].Phosphorous = 17.6f;
//Tobacco
@ -204,18 +146,12 @@ 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 = 21 + 19.1f;
cropTypes[10].Potassium = 10 + 4.9f;
cropTypes[10].Phosphorous = 20 + 19.3f;
cropTypes[10].Season[0] = "Spring";
cropTypes[10].Season[1] = "Autumn";
cropTypes[10].Season[2] = "Whole Year";
cropTypes[10].AreaMin = 1;
cropTypes[10].AreaMax = 9500;
cropTypes[10].Nitrogen = 19.1f;
cropTypes[10].Potassium = 4.9f;
cropTypes[10].Phosphorous = 19.3f;
//Wheat
@ -223,19 +159,12 @@ 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 = 21 + 23.3f;
cropTypes[11].Potassium = 10 + 2.9f;
cropTypes[11].Phosphorous = 20 + 14.4f;
cropTypes[11].Season[0] = "Spring";
cropTypes[11].Season[1] = "Autumn";
cropTypes[11].Season[2] = "Whole Year";
cropTypes[11].Season[3] = "Summer";
cropTypes[11].AreaMin = 1;
cropTypes[11].AreaMax = 266000;
cropTypes[11].Nitrogen = 23.3f;
cropTypes[11].Potassium = 2.9f;
cropTypes[11].Phosphorous = 14.4f;
}

View File

@ -14,23 +14,18 @@ class Crops
private int cropType = 0;
private float Timer = 1;
private int UpdateCrop;
private float fullTimer = 1;
private float fullTimer;
private bool housePos = false;
private Vector2 Size;
private Random r = new Random();
private CropTypes DataSet;
SoilProperties soilProperties = new SoilProperties();
private float ProductionRate;
private float tempRain;
private float prevpred;
private DayNightCycle Time = new DayNightCycle();
private int previousDay = 0;
public void updateCrop(Vector2 newSize, DayNightCycle nTime)
public void updateCrop(Vector2 newSize)
{
Time = nTime;
getProductionRate(DataSet);
if (UpdateCrop == 60)
{
degradeSoil();
@ -50,56 +45,11 @@ class Crops
}
}
public void updateProductionRate()
{
getProductionRate(DataSet);
}
public float getSpeedFactor(float tractorSpeed)
{
if (getCostOnMovement() == 1)
return (1f * tractorSpeed);
return (1f * tractorSpeed) / (getCostOnMovement() / 5.0f);
}
public SoilProperties getSoilProperties()
{
return soilProperties;
}
public void Fertilize(Fertilizer fertilizer)
{
soilProperties.Nitrogen += fertilizer.Nitrogen;
soilProperties.Phosphorous += fertilizer.Phosphorus;
soilProperties.Potassium += fertilizer.Potassium;
if (soilProperties.Nitrogen > 42)
soilProperties.Nitrogen = 42;
if (soilProperties.Phosphorous > 42)
soilProperties.Phosphorous = 42;
if (soilProperties.Potassium > 19)
soilProperties.Potassium = 19;
}
public bool isSaturated()
{
if (soilProperties.Nitrogen > 40 && (soilProperties.Phosphorous > 40 || soilProperties.Potassium > 17))
return true;
if (soilProperties.Phosphorous > 40 && soilProperties.Potassium > 17)
return true;
return false;
}
public bool isSaturated(int threshold)
{
if (soilProperties.Nitrogen > 40 - threshold && (soilProperties.Phosphorous > 40 - threshold || soilProperties.Potassium > 17 - (threshold * 17/40)))
return true;
if (soilProperties.Phosphorous > 40 - threshold && soilProperties.Potassium > 17 - (threshold *17/40))
return true;
return false;
}
public float getCropTimer()
{
return Timer;
@ -120,7 +70,7 @@ class Crops
// Changes the time required for the crops to be harvestable
public void setCropTimer()
{
if (cropType == 1) // Barley
if (cropType == 0) // Carrots
{
Timer = 300;
fullTimer = Timer;
@ -156,108 +106,46 @@ class Crops
{
if (cropType == 0)
{
return 16; //Barley
return 15; //Carrots
}
if (cropType == 1)
else if (cropType == 1)
{
return 16; //Barley
return 30; //Wheat
}
else if (cropType == 2)
{
return 16; //Cotton
}
else if (cropType == 3)
{
return 16; //Ground Nuts
}
else if (cropType == 4)
{
return 16; //Maize
}
else if (cropType == 5)
{
return 16; //Millets
}
else if (cropType == 6)
{
return 16; //Oil Seeds
}
else if (cropType == 7)
{
return 16; //Paddy
}
else if (cropType == 8)
{
return 16; //Pulses
}
else if (cropType == 9)
{
return 16; //Sugarcane
}
else if (cropType == 10)
{
return 16; //Wheat
return 40; //Berries
}
else
{
return 16; //Tobacco
return 50; //Fruit Trees
}
}
}
public void degradeSoil()
{
Random r = new Random();
if (soilProperties.Nitrogen > 4.0f)
{
soilProperties.Nitrogen = soilProperties.Nitrogen - ((soilProperties.NitrogenDegradeRate * (float)Math.Pow(ProductionRate, 2)) * (float)Math.Pow((r.NextDouble() + 0.5f), 2));
soilProperties.Nitrogen = soilProperties.Nitrogen - soilProperties.NitrogenDegradeRate;
}
if (soilProperties.Phosphorous > 0.1f)
{
soilProperties.Phosphorous = soilProperties.Phosphorous - ((soilProperties.PhosphorousDegradeRate * (float)Math.Pow(ProductionRate, 2)) * (float)Math.Pow((r.NextDouble() + 0.5f), 2));
soilProperties.Phosphorous = soilProperties.Phosphorous - soilProperties.PhosphorousDegradeRate;
}
if (soilProperties.Potassium > 0.1f)
{
soilProperties.Potassium = soilProperties.Potassium - ((soilProperties.PotassiumDegradeRate * (float)Math.Pow(ProductionRate, 2)) * (float)Math.Pow((r.NextDouble() + 0.5f), 2));
soilProperties.Potassium = soilProperties.Potassium - soilProperties.PotassiumDegradeRate;
}
}
public void updateRainfall(float Rain)
{
if (Rain >= 0.45f)
{
soilProperties.Rainfall = soilProperties.Rainfall + Rain * 4;
}
}
public void setPrevRainfall()
{
soilProperties.prevRainfall = soilProperties.Rainfall;
if (soilProperties.prevRainfall > 3616)
soilProperties.prevRainfall = 3616;
else if (soilProperties.prevRainfall < 236)
soilProperties.prevRainfall = 236;
soilProperties.Rainfall = 0;
}
public void setCropType(int Type, CropTypes nCropType)
{
if (Timer == fullTimer)
{
CropTypes temp = DataSet;
DataSet = nCropType;
cropType = Type;
if (Status > 1)
{
soilProperties.Area = r.Next(nCropType.AreaMin, nCropType.AreaMax);
if (temp != nCropType)
updateMLModel(DataSet, 1.0f);
}
}
}
public int getStatus()
@ -280,7 +168,6 @@ class Crops
public void setStatus(int newStatus)
{
Status = newStatus;
Timer = getCropTimer();
}
public void setOriginalStatus()
@ -294,7 +181,6 @@ class Crops
housePos = state;
if (state)
{
Timer = 1;
Status = 1;
}
else
@ -308,110 +194,23 @@ class Crops
return housePos;
}
public bool belowCapacity()
{
return ((int)(soilProperties.Nitrogen + soilProperties.Potassium + soilProperties.Phosphorous)) < soilProperties.Capacity;
}
public Vector4 getColour()
{
float r, g, b, a;
float productionRate, overhead;
if (ProductionRate > 1.0f)
{
productionRate = 1.0f;
overhead = ProductionRate - 1.0f;
}
else
{
productionRate = ProductionRate;
overhead = 0.0f;
}
r = (1.0f - productionRate) * 4;
g = 0.0f + productionRate;
b = 0.0f + overhead * 3;
a = 255;
return new Vector4(r, g, b, a);
}
public float getProductionRate(CropTypes Sample)
{
float predProd = 1.0f;
if (Status > 1 && Time.getDays() != previousDay)
{
predProd = updateMLModel(Sample, predProd);
}
prevpred = predProd;
ProductionRate = 1;
float min = 1.0f;
if (DataSet != null)
{
//ProductionRate = ProductionRate + (ProductionRate * compareToDatset(soilProperties.Temperature, Sample.Temparature));
//ProductionRate = ProductionRate + (ProductionRate * compareToDatset(soilProperties.Moisture, Sample.Moisture));
//ProductionRate = ProductionRate + (ProductionRate * compareToDatset(soilProperties.Humidity, Sample.Humidity));
if (DataSet.soilType[0] != null)
{
if (Sample.soilType[0] == soilProperties.soilType)
{
ProductionRate = ProductionRate + 0.1f;
}
if (DataSet.soilType[1] != null)
{
if (Sample.soilType[1] == soilProperties.soilType)
{
ProductionRate = ProductionRate + 0.08f;
}
if (DataSet.soilType[2] != null)
{
if (Sample.soilType[2] == soilProperties.soilType)
{
ProductionRate = ProductionRate + 0.5f;
}
}
}
}
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.Temperature, Sample.Temparature));
ProductionRate = ProductionRate + (ProductionRate * compareToDatset(soilProperties.Moisture, Sample.Moisture));
ProductionRate = ProductionRate + (ProductionRate * compareToDatset(soilProperties.Humidity, Sample.Humidity));
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;
}
ProductionRate = ProductionRate / 1.5f;
ProductionRate = ProductionRate / 20;
}
ProductionRate = (float)Math.Pow(ProductionRate, 2.5f);
return ProductionRate * prevpred;
}
public float updateMLModel(CropTypes Sample, float predProd)
{
previousDay = Time.getDays();
bool correctSeason = false;
foreach (string i in Sample.Season)
{
if (i == Time.getTimeOfYear() || i == "Whole Year")
{
correctSeason = true;
break;
}
}
if (correctSeason)
{
predProd = Game1.Sources.ML_Joel.Engine.PredictProductionwithRainfall(soilProperties, Time);
predProd = (predProd / soilProperties.Area) / 2;
}
else
predProd = 0.20f;
return predProd;
}
public float getProductionRate()
{
return ProductionRate;
}
@ -435,26 +234,26 @@ class Crops
spriteBatch.Begin();
if (housePos)
{
spriteBatch.DrawString(Bold, "Tiletype: House", new Vector2(240, Size.Y * (tileSize + Spacing) + 42), Color.Teal);
spriteBatch.DrawString(Bold, "Tiletype: House", new Vector2(240, Size.Y * (tileSize + Spacing) + 42), Color.DarkBlue);
}
else if (Status == 0)
{
spriteBatch.DrawString(Bold, "Tiletype: Boulders", new Vector2(240, Size.Y * (tileSize + Spacing) + 42), Color.Teal);
spriteBatch.DrawString(Bold, "Tiletype: Boulders", new Vector2(240, Size.Y * (tileSize + Spacing) + 42), Color.DarkBlue);
}
else if (Status == 1)
{
spriteBatch.DrawString(Bold, "Tiletype: Grassfield", new Vector2(240, Size.Y * (tileSize + Spacing) + 42), Color.Teal);
spriteBatch.DrawString(Bold, "Tiletype: Grassfield", new Vector2(240, Size.Y * (tileSize + Spacing) + 42), Color.DarkBlue);
}
else if (Status == 2)
{
spriteBatch.DrawString(Bold, "Tiletype: Soil", new Vector2(240, Size.Y * (tileSize + Spacing) + 42), Color.Teal);
spriteBatch.DrawString(Bold, "Tiletype: Soil", new Vector2(240, Size.Y * (tileSize + Spacing) + 42), Color.DarkBlue);
}
else if (Status == 3)
{
int x = (int)(((float)Timer / fullTimer) * 100);
x = 100 - x;
spriteBatch.DrawString(Bold, "Tiletype: Crop ", new Vector2(240, Size.Y * (tileSize + Spacing) + 42), Color.Teal);
spriteBatch.DrawString(Bold, "Completion: " + x + "%", new Vector2(240, Size.Y * (tileSize + Spacing) + 82), Color.Teal);
spriteBatch.DrawString(Bold, "Tiletype: Crop ", new Vector2(240, Size.Y * (tileSize + Spacing) + 42), Color.DarkBlue);
spriteBatch.DrawString(Bold, "Completion: " + x + "%", new Vector2(240, Size.Y * (tileSize + Spacing) + 82), Color.DarkBlue);
}
if (Status != 3)
{
@ -462,37 +261,29 @@ class Crops
}
if (Status > 1)
{
spriteBatch.DrawString(Bold, "Prefered Crop: " + cropTypesNames[cropType], new Vector2(240, Size.Y * (tileSize + Spacing) + 62), Color.Teal);
spriteBatch.DrawString(Bold, "Prefered Crop: " + cropTypesNames[cropType], new Vector2(240, Size.Y * (tileSize + Spacing) + 62), Color.DarkBlue);
}
else
{
spriteBatch.DrawString(Bold, "None", new Vector2(240, Size.Y * (tileSize + Spacing) + 62), Color.Teal);
spriteBatch.DrawString(Bold, "None", new Vector2(240, Size.Y * (tileSize + Spacing) + 62), Color.DarkBlue);
}
spriteBatch.DrawString(Bold, "Soil Properties:", new Vector2(240, Size.Y * (tileSize + Spacing) + 122), Color.DarkRed);
spriteBatch.DrawString(Bold, "Soil Type: ", new Vector2(240, Size.Y * (tileSize + Spacing) + 142), Color.DarkRed);
spriteBatch.DrawString(Bold, soilProperties.soilType, new Vector2(370, Size.Y * (tileSize + Spacing) + 142), Color.Teal);
spriteBatch.DrawString(Bold, soilProperties.soilType, new Vector2(370, Size.Y * (tileSize + Spacing) + 142), Color.DarkBlue);
spriteBatch.DrawString(Bold, "Temparature: ", new Vector2(240, Size.Y * (tileSize + Spacing) + 162), Color.DarkRed);
spriteBatch.DrawString(Bold, soilProperties.Temperature.ToString(), new Vector2(370, Size.Y * (tileSize + Spacing) + 162), Color.Teal);
spriteBatch.DrawString(Bold, soilProperties.Temperature.ToString(), new Vector2(370, Size.Y * (tileSize + Spacing) + 162), Color.DarkBlue);
spriteBatch.DrawString(Bold, "Moisture: ", new Vector2(240, Size.Y * (tileSize + Spacing) + 182), Color.DarkRed);
spriteBatch.DrawString(Bold, soilProperties.Moisture.ToString(), new Vector2(370, Size.Y * (tileSize + Spacing) + 182), Color.Teal);
spriteBatch.DrawString(Bold, soilProperties.Moisture.ToString(), new Vector2(370, Size.Y * (tileSize + Spacing) + 182), Color.DarkBlue);
spriteBatch.DrawString(Bold, "Humidity: ", new Vector2(240, Size.Y * (tileSize + Spacing) + 202), Color.DarkRed);
spriteBatch.DrawString(Bold, soilProperties.Humidity.ToString(), new Vector2(370, Size.Y * (tileSize + Spacing) + 202), Color.Teal);
spriteBatch.DrawString(Bold, soilProperties.Humidity.ToString(), new Vector2(370, Size.Y * (tileSize + Spacing) + 202), Color.DarkBlue);
spriteBatch.DrawString(Bold, "Phosphorous: ", new Vector2(240, Size.Y * (tileSize + Spacing) + 222), Color.DarkRed);
spriteBatch.DrawString(Bold, Math.Round(soilProperties.Phosphorous,1).ToString(), new Vector2(370, Size.Y * (tileSize + Spacing) + 222), Color.Teal);
spriteBatch.DrawString(Bold, Math.Round(soilProperties.Phosphorous,1).ToString(), new Vector2(370, Size.Y * (tileSize + Spacing) + 222), Color.DarkBlue);
spriteBatch.DrawString(Bold, "Potassium: ", new Vector2(240, Size.Y * (tileSize + Spacing) + 242), Color.DarkRed);
spriteBatch.DrawString(Bold, Math.Round(soilProperties.Potassium, 1).ToString(), new Vector2(370, Size.Y * (tileSize + Spacing) + 242), Color.Teal);
spriteBatch.DrawString(Bold, Math.Round(soilProperties.Potassium, 1).ToString(), new Vector2(370, Size.Y * (tileSize + Spacing) + 242), Color.DarkBlue);
spriteBatch.DrawString(Bold, "Nitrogen: ", new Vector2(240, Size.Y * (tileSize + Spacing) + 262), Color.DarkRed);
spriteBatch.DrawString(Bold, Math.Round(soilProperties.Nitrogen, 1).ToString(), new Vector2(370, Size.Y * (tileSize + Spacing) + 262), Color.Teal);
spriteBatch.DrawString(Bold, Math.Round(soilProperties.Nitrogen, 1).ToString(), new Vector2(370, Size.Y * (tileSize + Spacing) + 262), Color.DarkBlue);
spriteBatch.DrawString(Bold, "Production Rate: ", new Vector2(240, Size.Y * (tileSize + Spacing) + 282), Color.DarkRed);
spriteBatch.DrawString(Bold, Math.Round((ProductionRate * 100), 1).ToString() + "%", new Vector2(370, Size.Y * (tileSize + Spacing) + 282), Color.Teal);
spriteBatch.DrawString(Bold, "Last Years Rainfall: ", new Vector2(240, Size.Y * (tileSize + Spacing) + 302), Color.DarkRed);
spriteBatch.DrawString(Bold, Math.Round((soilProperties.prevRainfall), 1).ToString() + "mm", new Vector2(370, Size.Y * (tileSize + Spacing) + 302), Color.Teal);
spriteBatch.DrawString(Bold, "Rainfall: ", new Vector2(240, Size.Y * (tileSize + Spacing) + 322), Color.DarkRed);
spriteBatch.DrawString(Bold, Math.Round((soilProperties.Rainfall), 1).ToString() + "mm", new Vector2(370, Size.Y * (tileSize + Spacing) + 322), Color.Teal);
spriteBatch.DrawString(Bold, "Rain mm/s: ", new Vector2(240, Size.Y * (tileSize + Spacing) + 342), Color.DarkRed);
spriteBatch.DrawString(Bold, Math.Round((tempRain * 2), 2).ToString() + "mm", new Vector2(370, Size.Y * (tileSize + Spacing) + 342), Color.Teal);
spriteBatch.DrawString(Bold, "Area: ", new Vector2(240, Size.Y * (tileSize + Spacing) + 362), Color.DarkRed);
spriteBatch.DrawString(Bold, soilProperties.Area + "m^2", new Vector2(370, Size.Y * (tileSize + Spacing) + 362), Color.Teal);
spriteBatch.DrawString(Bold, Math.Round((ProductionRate * 100), 1).ToString() + "%", new Vector2(370, Size.Y * (tileSize + Spacing) + 282), Color.DarkBlue);
spriteBatch.End();
}
}

View File

@ -1,6 +1,4 @@
using System;
using System.IO;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@ -14,117 +12,36 @@ class Farm
private Random r;
private CropTypesHolder PresetCrops = new CropTypesHolder();
private int Update;
private Astar astar = new Astar();
private PerlinNoise perlin = new PerlinNoise();
private Vector2 RainPosition;
private Vector2 WindSpeed = new Vector2(0,0);
private System.Drawing.Color[][] RainfallMap;
private float[][] whiteNoise;
private float[][] perlinNoise;
private DayNightCycle Time;
private float updatePerc = 0.01f;
private float updateProgress = 0;
private float nextUpdate = 0;
private int productionUpdate = 0;
private string path_base = FindPath();
//initializes the crops
public void init(Vector2 Size, Vector2 housepos)
{
whiteNoise = PerlinNoise.GenerateWhiteNoise(100, 100);
perlinNoise = PerlinNoise.GeneratePerlinNoise(whiteNoise, 1);
PresetCrops.init();
r = new Random();
crops = new Crops[100, 100];
int dirtCount = 0;
for (int i = 0; i < 99; i++)
{
for (int j = 0; j < 99; j++)
{
int x = 0;
if (perlinNoise[i][j] > 0 && perlinNoise[i][j] < 0.15f)
x = 0;
else if (perlinNoise[i][j] >= 0.15f && perlinNoise[i][j] < 0.8f)
x = 1;
else if (perlinNoise[i][j] >= 0.8f)
x = 2;
int x = r.Next(0, 3);
if (x == 0)
{
x = r.Next(0, 2);
}
if (x == 2)
{
x = r.Next(1, 3);
}
crops[i, j] = new Crops();
crops[i, j].setStatus(x);
crops[i, j].setOriginalStatus();
x = r.Next(1, 12);
x = r.Next(0, 12);
crops[i, j].setCropType(x, PresetCrops.getPresetCropTypes(x));
crops[i, j].init();
if (crops[i, j].getStatus() == 2)
{
dirtCount++;
}
}
}
for (int i = 0; i < 99; i++)
{
for (int j = 0; j < 99; j++)
{
if (crops[i, j].getStatus() == 2)
{
if (!astar.isReachable(crops, new Vector2((int)i, (int)j), housepos))
{
dirtCount--;
}
}
}
}
if (dirtCount != 0)
init(Size, housepos);
RainPosition.X = r.Next(0, 1900);
RainPosition.Y = r.Next(0, 1950);
float coef = GetRandomNumber(-1f, 1f);
if (coef < 0)
coef = -1.0f;
else
coef = 1.0f;
WindSpeed.X = GetRandomNumber(0.9f, 1f) / 50 * coef;
WindSpeed.Y = GetRandomNumber(0.9f, 1f) / 50 * coef;
RainfallMap = PerlinNoise.LoadImage(System.IO.Path.Combine(path_base, "Content/Rainfall.png"));
}
public Rectangle getRainPosition(int TileSize, int x, int y, Vector2 Size)
{
float xtrunc = RainPosition.X - (float)Math.Truncate(RainPosition.X);
float ytrunc = RainPosition.Y - (float)Math.Truncate(RainPosition.Y);
if (xtrunc > 0.5)
xtrunc = xtrunc - 1;
if (ytrunc > 0.5)
ytrunc = ytrunc - 1;
xtrunc = -xtrunc;
ytrunc = -ytrunc;
//return new Rectangle((int)(xtrunc * TileSize) + x * TileSize,(int)(ytrunc * TileSize) + y * TileSize, TileSize, TileSize);
return new Rectangle((int)(xtrunc * TileSize) + x * TileSize, (int)(ytrunc * TileSize) + y * TileSize, TileSize, TileSize);
}
public Rectangle getDestinationRectangle(int x, int y, Vector2 Size)
{
Vector2 temp = new Vector2((int)Math.Round(x + RainPosition.X), y + (int)Math.Round(RainPosition.Y));
if (temp.X >= 1999 - Size.X - 1)
temp.X = -(1999 - (int)Math.Round(RainPosition.X));
if (temp.Y >= 1999 - Size.Y - 1)
temp.Y = -(1999 - (int)Math.Round(RainPosition.Y));
return new Rectangle((int)temp.X, (int)temp.Y, 1, 1);
}
public void drawWeatherInformation(SpriteBatch spriteBatch, SpriteFont Bold, Input input)
{
spriteBatch.DrawString(Bold, "WindSpeed: " + Math.Round(WindSpeed.X, 4), new Vector2(10, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 282), Color.Teal);
spriteBatch.DrawString(Bold, " : ", new Vector2(153, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 282), Color.Teal);
spriteBatch.DrawString(Bold, Math.Round(WindSpeed.Y, 4).ToString(), new Vector2(163, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 282), Color.Teal);
}
public void updateFarm(Vector2 Size)
{
@ -135,58 +52,13 @@ class Farm
{
for (int j = 0; j < Size.Y; j++)
{
Vector2 temp = new Vector2((int)Math.Round(i + RainPosition.X), j + (int)Math.Round(RainPosition.Y));
if (temp.X >= 1999 - Size.X - 1)
temp.X = i + ((1999 - (int)Math.Round(RainPosition.X)));
if (temp.Y >= 1999 - Size.Y - 1)
temp.Y = i + ((1999 - (int)Math.Round(RainPosition.Y)));
crops[i, j].updateCrop(Size, Time);
crops[i, j].updateRainfall(RainfallMap[(int)Math.Round(temp.X)][(int)Math.Round(temp.Y)].GetBrightness());
}
}
crops[i, j].updateCrop(Size);
}
}
Update = 0;
}
updateRainMapPosition(Size);
if (productionUpdate == 2)
{
nextUpdate = updateProgress + updatePerc;
for (int i = (int)(updateProgress * Size.X); i < nextUpdate * Size.X; i++)
{
for (int j = 0; j < Size.Y; j++)
{
if (crops[i, j].getStatus() > 1)
{
int x = getHighestProductionRate(i, j);
crops[i, j].setCropType(x, PresetCrops.getPresetCropTypes(x));
crops[i, j].updateProductionRate();
}
}
}
updateProgress = updateProgress + updatePerc;
if (updateProgress >= 1)
{
updateProgress = 0;
}
productionUpdate = 0;
nextUpdate = 0;
}
productionUpdate++;
}
public void updateRainFall(Vector2 Size, DayNightCycle nTime)
{
Time = nTime;
if (nTime.nDay())
{
for (int i = 0; i < Size.X; i++)
{
for (int j = 0; j < Size.Y; j++)
{
crops[i, j].setPrevRainfall();
}
}
}
}
//Changes the properties of the tile when the tractor reaches this tile.
@ -204,7 +76,6 @@ class Farm
}
else if (crops[x, y].getStatus() == 2)
{
crops[x, y].setStatus(3);
crops[x, y].setCropTimer();
}
@ -220,42 +91,6 @@ class Farm
return crops;
}
private void updateRainMapPosition(Vector2 Size)
{
double x, y;
x = WindSpeed.X + GetRandomNumber(-1f, 1f) / 20000;
y = WindSpeed.Y + GetRandomNumber(-1f, 1f) / 20000;
if (x <= 0.02f && x >= -0.02f)
{
WindSpeed.X = (float)x;
}
if (y < 0.02f && y > -0.02f)
{
WindSpeed.Y = (float)y;
}
if (WindSpeed.X > 0 && RainPosition.X < 2000)
RainPosition.X = RainPosition.X + WindSpeed.X;
else if (WindSpeed.X < 0 && RainPosition.X > 0)
RainPosition.X = RainPosition.X + WindSpeed.X;
if (WindSpeed.Y > 0 && RainPosition.Y < 2000)
RainPosition.Y = RainPosition.Y + WindSpeed.Y;
else if (WindSpeed.Y < 0 && RainPosition.Y > 0)
RainPosition.Y = RainPosition.Y + WindSpeed.Y;
if (Math.Round(RainPosition.X) == 1999 && WindSpeed.X > 0)
RainPosition.X = 0;
else if (Math.Round(RainPosition.X) == 1 && WindSpeed.X < 0)
RainPosition.X = 1999;
if (Math.Round(RainPosition.Y) == 1999 && WindSpeed.Y > 0)
RainPosition.Y = 0;
else if (Math.Round(RainPosition.Y) == 1 && WindSpeed.Y < 0)
RainPosition.Y = 1999;
}
public void setNewHousePos(Vector2 pos, bool newState)
{
crops[(int)pos.X, (int)pos.Y].setHousePos(newState);
@ -263,7 +98,7 @@ class Farm
public CropTypes getPresetCropTypes(int Index)
{
return PresetCrops.getPresetCropTypes(Index);
return PresetCrops.getPresetCropTypes(Index - 1);
}
public void setCropType(int x, int y, int Type)
@ -276,19 +111,16 @@ class Farm
for (int i = 0; i < Size.X; i++)
{
for (int j = 0; j < Size.X; j++)
{
if (crops[i, j].getStatus() == 2)
{
int x = getHighestProductionRate(i, j);
crops[i,j].setCropType(x, PresetCrops.getPresetCropTypes(x));
}
}
}
}
private int getHighestProductionRate(int x, int y)
{
int i = 6, holderIndex = 0;
int i = 1, holderIndex = 0;
float holder = 0, SampleHolder = 0;
do
{
@ -303,47 +135,8 @@ class Farm
return holderIndex;
}
public Color getRainAmount(int x, int y, Color color, Vector2 Size)
{
Vector2 temp = new Vector2(x + (int)Math.Round(RainPosition.X), y + (int)Math.Round(RainPosition.Y));
if (temp.X >= 1999)
temp.X = -(1999 - (int)Math.Round(temp.X));
if (temp.Y >= 1999)
temp.Y = -(1999 - (int)Math.Round(temp.Y));
if (temp.X == -1)
temp.X = 1999;
if (temp.Y == -1)
temp.Y = 1999;
if (RainfallMap[(int)temp.X][(int)temp.Y].GetBrightness() < 0.45f)
{
return Color.FromNonPremultiplied(color.R, color.G, color.B, (int)(0));
}
else
{
return Color.FromNonPremultiplied(color.R, color.G, color.B, (int)(255 * RainfallMap[(int)temp.X][(int)temp.Y].GetBrightness()));
}
}
public float getProductionRate(int x, int y, int Type)
{
return crops[x, y].getProductionRate(PresetCrops.getPresetCropTypes(Type));
}
public float GetRandomNumber(double minimum, double maximum)
{
return (float)(Math.Round(r.NextDouble() * (maximum - minimum) + minimum, 2));
}
private static string FindPath()
{
string path = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location).ToString();
while (true)
{
path = Directory.GetParent(path).ToString();
if (path.EndsWith("\\Game1"))
{
return path;
}
}
}
}

View File

@ -1,333 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Drawing.Imaging;
class PerlinNoise
{
#region Feilds
static Random random = new Random();
#endregion
#region Reusable Functions
public static float[][] GenerateWhiteNoise(int width, int height)
{
float[][] noise = GetEmptyArray<float>(width, height);
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
noise[i][j] = (float)random.NextDouble() % 1;
}
}
return noise;
}
public static float Interpolate(float x0, float x1, float alpha)
{
return x0 * (1 - alpha) + alpha * x1;
}
public static Color Interpolate(Color col0, Color col1, float alpha)
{
float beta = 1 - alpha;
return Color.FromArgb(
255,
(int)(col0.R * alpha + col1.R * beta),
(int)(col0.G * alpha + col1.G * beta),
(int)(col0.B * alpha + col1.B * beta));
}
public static Color GetColor(Color gradientStart, Color gradientEnd, float t)
{
float u = 1 - t;
Color color = Color.FromArgb(
255,
(int)(gradientStart.R * u + gradientEnd.R * t),
(int)(gradientStart.G * u + gradientEnd.G * t),
(int)(gradientStart.B * u + gradientEnd.B * t));
return color;
}
public static Color[][] MapGradient(Color gradientStart, Color gradientEnd, float[][] perlinNoise)
{
int width = perlinNoise.Length;
int height = perlinNoise[0].Length;
Color[][] image = GetEmptyArray<Color>(width, height); //an array of colours
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
image[i][j] = GetColor(gradientStart, gradientEnd, perlinNoise[i][j]);
}
}
return image;
}
public static T[][] GetEmptyArray<T>(int width, int height)
{
T[][] image = new T[width][];
for (int i = 0; i < width; i++)
{
image[i] = new T[height];
}
return image;
}
public static float[][] GenerateSmoothNoise(float[][] baseNoise, int octave)
{
int width = baseNoise.Length;
int height = baseNoise[0].Length;
float[][] smoothNoise = GetEmptyArray<float>(width, height);
int samplePeriod = 1 << octave; // calculates 2 ^ k
float sampleFrequency = 1.0f / samplePeriod;
for (int i = 0; i < width; i++)
{
//calculate the horizontal sampling indices
int sample_i0 = (i / samplePeriod) * samplePeriod;
int sample_i1 = (sample_i0 + samplePeriod) % width; //wrap around
float horizontal_blend = (i - sample_i0) * sampleFrequency;
for (int j = 0; j < height; j++)
{
//calculate the vertical sampling indices
int sample_j0 = (j / samplePeriod) * samplePeriod;
int sample_j1 = (sample_j0 + samplePeriod) % height; //wrap around
float vertical_blend = (j - sample_j0) * sampleFrequency;
//blend the top two corners
float top = Interpolate(baseNoise[sample_i0][sample_j0],
baseNoise[sample_i1][sample_j0], horizontal_blend);
//blend the bottom two corners
float bottom = Interpolate(baseNoise[sample_i0][sample_j1],
baseNoise[sample_i1][sample_j1], horizontal_blend);
//final blend
smoothNoise[i][j] = Interpolate(top, bottom, vertical_blend);
}
}
return smoothNoise;
}
public static float[][] GeneratePerlinNoise(float[][] baseNoise, int octaveCount)
{
int width = baseNoise.Length;
int height = baseNoise[0].Length;
float[][][] smoothNoise = new float[octaveCount][][]; //an array of 2D arrays containing
float persistance = 0.7f;
//generate smooth noise
for (int i = 0; i < octaveCount; i++)
{
smoothNoise[i] = GenerateSmoothNoise(baseNoise, i);
}
float[][] perlinNoise = GetEmptyArray<float>(width, height); //an array of floats initialised to 0
float amplitude = 1.0f;
float totalAmplitude = 0.0f;
//blend noise together
for (int octave = octaveCount - 1; octave >= 0; octave--)
{
amplitude *= persistance;
totalAmplitude += amplitude;
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
perlinNoise[i][j] += smoothNoise[octave][i][j] * amplitude;
}
}
}
//normalisation
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
perlinNoise[i][j] /= totalAmplitude;
}
}
return perlinNoise;
}
public static float[][] GeneratePerlinNoise(int width, int height, int octaveCount)
{
float[][] baseNoise = GenerateWhiteNoise(width, height);
return GeneratePerlinNoise(baseNoise, octaveCount);
}
public static Color[][] MapToGrey(float[][] greyValues)
{
int width = greyValues.Length;
int height = greyValues[0].Length;
Color[][] image = GetEmptyArray<Color>(width, height);
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
int grey = (int)(255 * greyValues[i][j]);
Color color = Color.FromArgb(255, grey, grey, grey);
image[i][j] = color;
}
}
return image;
}
public static void SaveImage(Color[][] image, string fileName)
{
int width = image.Length;
int height = image[0].Length;
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
bitmap.SetPixel(i, j, image[i][j]);
}
}
bitmap.Save(fileName);
}
public static Color[][] LoadImage(string fileName)
{
Bitmap bitmap = new Bitmap(fileName);
int width = bitmap.Width;
int height = bitmap.Height;
Color[][] image = GetEmptyArray<Color>(width, height);
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
image[i][j] = bitmap.GetPixel(i, j);
}
}
return image;
}
public static Color[][] BlendImages(Color[][] image1, Color[][] image2, float[][] perlinNoise)
{
int width = image1.Length;
int height = image1[0].Length;
Color[][] image = GetEmptyArray<Color>(width, height); //an array of colours for the new image
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
image[i][j] = Interpolate(image1[i][j], image2[i][j], perlinNoise[i][j]);
}
}
return image;
}
public static void DemoGradientMap()
{
int width = 256;
int height = 256;
int octaveCount = 8;
Color gradientStart = Color.FromArgb(255, 0, 0);
Color gradientEnd = Color.FromArgb(255, 0, 255);
float[][] perlinNoise = GeneratePerlinNoise(width, height, octaveCount);
Color[][] perlinImage = MapGradient(gradientStart, gradientEnd, perlinNoise);
SaveImage(perlinImage, "perlin_noise.png");
}
public static float[][] AdjustLevels(float[][] image, float low, float high)
{
int width = image.Length;
int height = image[0].Length;
float[][] newImage = GetEmptyArray<float>(width, height);
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
float col = image[i][j];
if (col <= low)
{
newImage[i][j] = 0;
}
else if (col >= high)
{
newImage[i][j] = 1;
}
else
{
newImage[i][j] = (col - low) / (high - low);
}
}
}
return newImage;
}
private static Color[][][] AnimateTransition(Color[][] image1, Color[][] image2, int frameCount)
{
Color[][][] animation = new Color[frameCount][][];
float low = 0;
float increment = 1.0f / frameCount;
float high = increment;
float[][] perlinNoise = AdjustLevels(
GeneratePerlinNoise(image1.Length, image1[0].Length, 9),
0.2f, 0.8f);
for (int i = 0; i < frameCount; i++)
{
AdjustLevels(perlinNoise, low, high);
float[][] blendMask = AdjustLevels(perlinNoise, low, high);
animation[i] = BlendImages(image1, image2, blendMask);
//SaveImage(animation[i], "blend_animation" + i + ".png");
SaveImage(MapToGrey(blendMask), "blend_mask" + i + ".png");
low = high;
high += increment;
}
return animation;
}
#endregion
}

View File

@ -17,13 +17,9 @@ class SoilProperties
public float Nitrogen;
public float Potassium;
public float Phosphorous;
public float Rainfall;
public float prevRainfall;
public float NitrogenDegradeRate = 0.8f / 1.5f;
public float PotassiumDegradeRate = 0.3f / 1.5f;
public float PhosphorousDegradeRate = 0.6f / 1.5f;
public int Capacity = 80;
public int Area;
public float NitrogenDegradeRate = 0.01f;
public float PotassiumDegradeRate = 0.01f;
public float PhosphorousDegradeRate = 0.01f;
public void setSoilProperties()
{
@ -49,13 +45,11 @@ class SoilProperties
soilType = "Clayey";
}
Temperature = GetRandomNumber(22, 30);
Humidity = Temperature * GetRandomNumber(1.9f, 2.1f);
Humidity = Temperature * GetRandomNumber(1.9, 2.1);
Moisture = GetRandomNumber(20, 70);
Nitrogen = GetRandomNumber(4 , 42); //was 4, 60
Potassium = GetRandomNumber(0.01f, 19); // was 0, 28
Phosphorous = GetRandomNumber(0.01f, 42); // was 0, 60
prevRainfall = r.Next(247, 3617);
Area = r.Next(1, 270000);
Nitrogen = GetRandomNumber(4 , 55);
Potassium = GetRandomNumber(0.01f, 28);
Phosphorous = GetRandomNumber(0.01f, 60);
}
public float GetRandomNumber(double minimum, double maximum)

View File

@ -1,39 +0,0 @@
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<ModelInput, ModelOutput> PredictionEngine;
private static ModelOutput modelOutput;
public static void init()
{
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
};
//ModelOutput modelOutput = new ModelOutput();
PredictionEngine.Predict(modelInput, ref modelOutput);
return modelOutput.Prediction;
}
}

View File

@ -5,7 +5,8 @@ using System.Text;
using System.Threading.Tasks;
using Microsoft.ML.Data;
namespace Game1.Sources.ML
{
class BigModelInput
{
[ColumnName("Ca"), LoadColumn(0)]
@ -39,4 +40,4 @@ class BigModelInput
public float Class { get; set; }
}
}

View File

@ -5,9 +5,11 @@ using System.Text;
using System.Threading.Tasks;
using Microsoft.ML.Data;
namespace Game1.Sources.ML
{
class BigModelOutput
{
[ColumnName("PredictedLabel")]
public float Prediction { get; set; }
}
}

View File

@ -6,7 +6,8 @@ using System.Threading.Tasks;
using Microsoft.ML;
using Microsoft.ML.Data;
namespace Game1.Sources.ML
{
class ModelInput
{
[ColumnName("Temperature"), LoadColumn(0)]
@ -36,4 +37,4 @@ class ModelInput
[ColumnName("Fertilizer_Name"), LoadColumn(8)]
public String Fertilizer_Name { get; set; }
}
}

View File

@ -5,6 +5,8 @@ using System.Text;
using System.Threading.Tasks;
using Microsoft.ML.Data;
namespace Game1.Sources.ML
{
class ModelOutput
{
[ColumnName("PredictedLabel")]
@ -12,4 +14,4 @@ class ModelOutput
//public float[] Score { get; set; }
}
}

View File

@ -1,27 +1,23 @@
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Microsoft.ML;
using Microsoft.ML.Data;
using Microsoft.ML.Trainers.LightGbm;
namespace Game1.Sources.ML
{
class MLModel
{
private static MLContext mlContext = new MLContext(seed: 1);
private static string path_base = FindPath();
private static string path = System.IO.Path.Combine(path_base, "Content/ML/Fertilizer_Prediction.csv");
private static string modelpath = System.IO.Path.Combine(path_base, "Content/ML/MLmodel");
private static string report = System.IO.Path.Combine(path_base, "Content/ML/report");
private static string pathBig = System.IO.Path.Combine(path_base, "Content/ML/BigFertPredict.csv");
private static string modelpathBig = System.IO.Path.Combine(path_base, "Content/ML/MLmodelBig");
private static string reportBig = System.IO.Path.Combine(path_base, "Content/ML/report_BigModel");
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()
@ -78,6 +74,7 @@ class MLModel
.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))
@ -162,25 +159,11 @@ class MLModel
return mlContext.Model.Load(modelpath, out DataViewSchema inputSchema);
}
public static PredictionEngine<ModelInput, ModelOutput> CreateEngine()
public static Microsoft.ML.PredictionEngine<ModelInput, ModelOutput> CreateEngine()
{
ITransformer mlModel = LoadModel(false);
return mlContext.Model.CreatePredictionEngine<ModelInput, ModelOutput>(mlModel);
}
private static string FindPath()
{
string path = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location).ToString();
while (true)
{
path = Directory.GetParent(path).ToString();
if (path.EndsWith("\\Game1"))
{
return path;
}
}
}
}

View File

@ -1,24 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.ML.Data;
namespace Game1.Sources.ML_Joel
{
class Input
{
[ColumnName("Season"), LoadColumn(0)]
public String Season { get; set; }
[ColumnName("Crop"), LoadColumn(1)]
public String Crop { get; set; }
[ColumnName("Rainfall"), LoadColumn(2)]
public float Rainfall { get; set; }
[ColumnName("Production"), LoadColumn(3)]
public float Production { get; set; }
}
}

View File

@ -1,27 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.ML.Data;
namespace Game1.Sources.ML_Joel
{
class InputArea
{
[ColumnName("Season"), LoadColumn(0)]
public String Season { get; set; }
[ColumnName("Crop"), LoadColumn(1)]
public String Crop { get; set; }
[ColumnName("Area"), LoadColumn(2)]
public float Area { get; set; }
[ColumnName("Rainfall"), LoadColumn(3)]
public float Rainfall { get; set; }
[ColumnName("Production"), LoadColumn(4)]
public float Production { get; set; }
}
}

View File

@ -1,20 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.ML.Data;
namespace Game1.Sources.ML_Joel
{
class Output
{
//[ColumnName("PredictedLabel")]
public float Prediction { get; set; }
public float Score { get; set; }
//[ColumnName("Score")]
// public float[] Score { get; set; }
}
}

View File

@ -1,15 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.ML.Data;
namespace Game1.Sources.ML_Joel
{
class OutputArea
{
public float Prediction { get; set; }
public float Score { get; set; }
}
}

View File

@ -1,55 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.ML;
namespace Game1.Sources.ML_Joel
{
static class Engine
{
private static MLContext mlContext = new MLContext(seed: 1);
private static PredictionEngine<Input, Output> PredictionEngine;
private static PredictionEngine<InputArea, OutputArea> PredictionEngineArea;
private static OutputArea modelOutput;
public static void CreateModel()
{
Model.CreateModel();
}
public static void CreateModelArea()
{
Model.CreateModelArea();
}
public static void init()
{
PredictionEngine = Model.CreateEngine();
}
public static void initArea()
{
PredictionEngineArea = Model.CreateEngineArea();
}
public static float PredictProductionwithRainfall(SoilProperties soilProperties, DayNightCycle Time)
{
InputArea modelInput = new InputArea
{
Season = Time.getTimeOfYear(),
Area = soilProperties.Area,
Rainfall = soilProperties.prevRainfall,
};
//OutputArea modelOutput = new OutputArea();
PredictionEngineArea.Predict(modelInput, ref modelOutput);
return modelOutput.Score;
}
}
}

View File

@ -1,190 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.ML;
using Microsoft.ML.Data;
using Microsoft.ML.Trainers.LightGbm;
namespace Game1.Sources.ML_Joel
{
class Model
{
private static MLContext mlContext = new MLContext(seed: 1);
private static string path_base = FindPath();
private static string path = System.IO.Path.Combine(path_base, "Content/ML/Rainfall.csv");
private static string modelpath = System.IO.Path.Combine(path_base, "Content/ML/MLmodel_Joel");
private static string report = System.IO.Path.Combine(path_base, "Content/ML/report_Joel");
private static string path_area = System.IO.Path.Combine(path_base, "Content/ML/Rainfall_area.csv");
private static string modelpath_area = System.IO.Path.Combine(path_base, "Content/ML/MLmodel_Joel_area");
private static string report_area = System.IO.Path.Combine(path_base, "Content/ML/report_Joel_area");
// Loading data, creatin and saving ML model for smaller dataset (100)
public static void CreateModel()
{
IDataView trainingDataView = mlContext.Data.LoadFromTextFile<Input>(
path: path,
hasHeader: true,
separatorChar: ',',
allowQuoting: true,
allowSparse: false);
var splitData = mlContext.Data.TrainTestSplit(trainingDataView, testFraction: 0.2);
trainingDataView = splitData.TrainSet;
IDataView testDataView = splitData.TestSet;
Input sample = mlContext.Data.CreateEnumerable<Input>(trainingDataView, false).ElementAt(0);
ITransformer MLModel = BuildAndTrain(mlContext, trainingDataView, testDataView, sample, report);
SaveModel(mlContext, MLModel, modelpath, trainingDataView.Schema);
}
// Building and training ML model
public static ITransformer BuildAndTrain(MLContext mLContext, IDataView trainingDataView, IDataView testDataView, Input sample, string reportPath)
{
var options = new LightGbmRegressionTrainer.Options
{
MaximumBinCountPerFeature = 40,
LearningRate = 0.00020,
NumberOfIterations = 20000,
NumberOfLeaves = 55,
LabelColumnName = "Production",
FeatureColumnName = "Features",
Booster = new DartBooster.Options()
{
MaximumTreeDepth = 10
}
};
var pipeline = mlContext.Transforms
.Text.FeaturizeText("SeasonF", "Season")
.Append(mlContext.Transforms.Text.FeaturizeText("CropF", "Crop"))
.Append(mlContext.Transforms.Concatenate("Features", "SeasonF", "CropF", "Rainfall"))
.AppendCacheCheckpoint(mLContext)
.Append(mLContext.Regression.Trainers.LightGbm(options));
ITransformer MLModel = pipeline.Fit(trainingDataView);
var testEval = MLModel.Transform(testDataView);
Evaluate(mlContext, testEval, pipeline, 10, reportPath, "Production");
return MLModel;
}
public static void CreateModelArea()
{
IDataView trainingDataView = mlContext.Data.LoadFromTextFile<InputArea>(
path: path_area,
hasHeader: true,
separatorChar: ',',
allowQuoting: true,
allowSparse: false);
var splitData = mlContext.Data.TrainTestSplit(trainingDataView, testFraction: 0.2);
trainingDataView = splitData.TrainSet;
IDataView testDataView = splitData.TestSet;
ITransformer MLModel = BuildAndTrainArea(mlContext, trainingDataView, testDataView, report_area);
SaveModel(mlContext, MLModel, modelpath_area, trainingDataView.Schema);
}
// Building and training ML model
public static ITransformer BuildAndTrainArea(MLContext mLContext, IDataView trainingDataView, IDataView testDataView, string reportPath)
{
var options = new LightGbmRegressionTrainer.Options
{
MaximumBinCountPerFeature = 40,
LearningRate = 0.00020,
NumberOfIterations = 20000,
NumberOfLeaves = 55,
LabelColumnName = "Production",
FeatureColumnName = "Features",
Booster = new DartBooster.Options()
{
MaximumTreeDepth = 10
}
};
var pipeline = mlContext.Transforms
.Text.FeaturizeText("SeasonF", "Season")
.Append(mlContext.Transforms.Text.FeaturizeText("CropF", "Crop"))
.Append(mlContext.Transforms.Concatenate("Features", "SeasonF", "CropF", "Area", "Rainfall"))
.AppendCacheCheckpoint(mLContext)
.Append(mLContext.Regression.Trainers.LightGbm(options));
ITransformer MLModel = pipeline.Fit(trainingDataView);
var testEval = MLModel.Transform(testDataView);
Evaluate(mlContext, testEval, pipeline, 10, reportPath, "Production");
return MLModel;
}
public static ITransformer TrainModel(MLContext mlContext, IDataView trainingDataView, IEstimator<ITransformer> 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<ITransformer> trainingPipeline, int folds, string reportPath, string labelColumnName)
{
var eval = mlContext.Regression.Evaluate(trainingDataView, labelColumnName: labelColumnName);
var report = File.CreateText(reportPath);
report.Write("Mean Absolute Error: " + eval.MeanAbsoluteError + '\n' + "R Squared: " + eval.RSquared, 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()
{
return mlContext.Model.Load(modelpath, out DataViewSchema inputSchema);
}
public static PredictionEngine<Input, Output> CreateEngine()
{
ITransformer mlModel = LoadModel();
return mlContext.Model.CreatePredictionEngine<Input, Output>(mlModel);
}
public static ITransformer LoadModelArea()
{
return mlContext.Model.Load(modelpath_area, out DataViewSchema inputSchema);
}
public static PredictionEngine<InputArea, OutputArea> CreateEngineArea()
{
ITransformer mlModel = LoadModelArea();
return mlContext.Model.CreatePredictionEngine<InputArea, OutputArea>(mlModel);
}
private static string FindPath()
{
string path = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location).ToString();
while(true)
{
path = Directory.GetParent(path).ToString();
if (path.EndsWith("\\Game1"))
{
return path;
}
}
}
}
}

View File

@ -8,30 +8,22 @@ using Microsoft.Xna.Framework;
class DayNightCycle
{
private bool Time = true;
private bool Day = true;
private int nightTime = 0;
private int dayTime = 0;
private int lengthOfDay = 20000;
private int lengthOfNight = 20000;
private int Days = 1;
private int DaysOfYear = 0;
private int speed = 1;
private bool nextDay = true;
public void updateTime(int Speed)
{
speed = Speed;
Time = false;
for (int i = 0; i < Speed; i++)
{
if (Day)
if (Time)
{
dayTime++;
if (dayTime == lengthOfDay)
{
Time = false;
Day = false;
dayTime = 0;
}
}
@ -41,47 +33,32 @@ class DayNightCycle
if (nightTime == lengthOfNight)
{
Time = true;
Day = true;
nightTime = 0;
Days++;
DaysOfYear++;
}
}
if (DaysOfYear == 48)
}
}
public string getDayNight()
{
DaysOfYear = 0;
}
}
}
//Season:
//0 - Whole Year
//1 - Spring
//2 - Summer
//3 - Autumn
//4 - Winter
public string getTimeOfYear()
if (Time)
{
if (DaysOfYear < 12)
return "Spring";
else if (DaysOfYear < 24)
return "Summer";
else if (DaysOfYear < 36)
return "Autumn";
return "Day";
}
else
return "Winter";
{
return "Night";
}
}
public Color GetTimeOfDay()
{
int blue, red, brightness, green, potatorate = 255;
int blue, red, brightness;
if (nightTime == 0 && dayTime == 0)
{
red = 1;
blue = 1;
green = 1;
brightness = 1;
}
@ -89,29 +66,20 @@ class DayNightCycle
{
if ((float)dayTime / lengthOfDay < 0.5)
{
blue = (int)(((float)nightTime / lengthOfNight) * potatorate) + (int)((1.0f - (float)dayTime / lengthOfDay) * potatorate);
blue = (int)(((float)nightTime / lengthOfNight) * 255) + (int)((1.0f - (float)dayTime / lengthOfDay) * 255);
}
else
{
blue = (int)(((float)nightTime / lengthOfNight) * potatorate) + (int)(((float)dayTime / lengthOfDay) * potatorate);
blue = (int)(((float)nightTime / lengthOfNight) * 255) + (int)(((float)dayTime / lengthOfDay) * 255);
}
if ((float)nightTime / lengthOfNight < 0.5)
{
red = (int)((1.0 - (float)nightTime / lengthOfNight) * potatorate) + (int)(((float)dayTime / lengthOfDay) * potatorate);
red = (int)((1.0 - (float)nightTime / lengthOfNight) * 255) + (int)(((float)dayTime / lengthOfDay) * 255);
}
else
{
red = (int)(((float)nightTime / lengthOfNight) * potatorate) + (int)(((float)dayTime / lengthOfDay) * potatorate);
}
if ((float)nightTime / lengthOfNight < 0.5)
{
green = (int)((1.0 - (float)nightTime / lengthOfNight) * potatorate) + (int)(((float)dayTime / lengthOfDay) * potatorate);
}
else
{
green = (int)(((float)nightTime / lengthOfNight) * potatorate) + (int)(((float)dayTime / lengthOfDay) * potatorate);
red = (int)(((float)nightTime / lengthOfNight) * 255) + (int)(((float)dayTime / lengthOfDay) * 255);
}
if (Time)
@ -130,20 +98,13 @@ class DayNightCycle
}
}
}
return Color.FromNonPremultiplied(red, green, blue, 255);
}
public bool nDay()
{
if (Time)
return true;
else
return false;
return Color.FromNonPremultiplied(red, 255, blue, brightness);
}
public int GetTimeOfDayInt()
{
if (Day)
if (Time)
{
return (int)(100 * ((float)(dayTime + nightTime) / (lengthOfDay + lengthOfNight))) + 1;
}

View File

@ -1,15 +0,0 @@
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; }
}

View File

@ -1,181 +0,0 @@
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 / 2,
Phosphorus = 0 * 0.436f / 2,
Potassium = 0 * 0.83f / 2
};
FertilizerType[1] = new Fertilizer
{
ID = 0,
Name = "10-26-26",
Nitrogen = 10.0f / 2,
Phosphorus = 26 * 0.436f / 2,
Potassium = 26 * 0.83f / 2
};
FertilizerType[2] = new Fertilizer
{
ID = 1,
Name = "14-35-14",
Nitrogen = 14.0f / 2,
Phosphorus = 35 * 0.436f / 2,
Potassium = 14 * 0.83f / 2
};
FertilizerType[3] = new Fertilizer
{
ID = 2,
Name = "17-17-17",
Nitrogen = 17.0f / 2,
Phosphorus = 17 * 0.436f / 2,
Potassium = 17 * 0.83f / 2
};
FertilizerType[4] = new Fertilizer
{
ID = 3,
Name = "20-20",
Nitrogen = 20.0f / 2,
Phosphorus = 20 * 0.436f / 2,
Potassium = 0 * 0.83f / 2
};
FertilizerType[5] = new Fertilizer
{
ID = 4,
Name = "28-28",
Nitrogen = 28.0f / 2,
Phosphorus = 28 * 0.436f / 2,
Potassium = 0 * 0.83f / 2
};
FertilizerType[6] = new Fertilizer
{
ID = 5,
Name = "DAP",
Nitrogen = 18.0f / 2,
Phosphorus = 46 * 0.436f / 2,
Potassium = 0 * 0.83f / 2
};
FertilizerType[7] = new Fertilizer
{
ID = 6,
Name = "Urea",
Nitrogen = 46.0f / 2,
Phosphorus = 0 * 0.436f / 2,
Potassium = 0 * 0.83f / 2
};
}
*/
FertilizerType[0] = new Fertilizer
{
ID = 999,
Name = "None",
Nitrogen = 0.0f / 2,
Phosphorus = 0 * 0.436f / 2,
Potassium = 0 * 0.83f / 2
};
FertilizerType[1] = new Fertilizer
{
ID = 0,
Name = "10-26-26",
Nitrogen = 17.21f / 2,
Phosphorus = 12.14f / 2,
Potassium = 0.64f / 2
};
FertilizerType[2] = new Fertilizer
{
ID = 1,
Name = "14-35-14",
Nitrogen = 16.89f / 2,
Phosphorus = 6.21f / 2,
Potassium = 5.21f / 2
};
FertilizerType[3] = new Fertilizer
{
ID = 2,
Name = "17-17-17",
Nitrogen = 14.92f / 2,
Phosphorus = 14.42f / 2,
Potassium = 3.0f / 2
};
FertilizerType[4] = new Fertilizer
{
ID = 3,
Name = "20-20",
Nitrogen = 15.39f / 2,
Phosphorus = 15.21f / 2,
Potassium = 9.5f / 2
};
FertilizerType[5] = new Fertilizer
{
ID = 4,
Name = "28-28",
Nitrogen = 9.67f / 2,
Phosphorus = 10.47f / 2,
Potassium = 9.5f / 2
};
FertilizerType[6] = new Fertilizer
{
ID = 5,
Name = "DAP",
Nitrogen = 14.52f / 2,
Phosphorus = 1.77f / 2,
Potassium = 9.5f / 2
};
FertilizerType[7] = new Fertilizer
{
ID = 6,
Name = "Urea",
Nitrogen = 1.81f / 2,
Phosphorus = 21.0f / 2,
Potassium = 9.5f / 2
};
}
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];
}
}

View File

@ -1,25 +1,17 @@
using Microsoft.Xna.Framework;
using System;
class HandleRotation
{
int rotationSpeed = 5, Rotation = 180;
private float oldSpeed, movementSpeed;
private Vector2 oldTile, oldPosition, oldMovementSpeed;
private Vector2 Direction;
public Vector2 UpdatePosition(int Destination, float tractorSpeed, Vector2 Position, Crops crops, Vector2 oldDeltaPosition, Vector2 Target)
public Vector2 UpdatePosition(int Destination, float tractorSpeed, Vector2 Position)
{
if (oldSpeed != crops.getSpeedFactor(tractorSpeed))
{
Position = new Vector2((int)Math.Round(Position.X), (int)Math.Round(Position.Y));
}
if (Destination == 0) // down
Vector2 Direction;
if (Destination == 0)
{
if (Rotation == 0)
{
Direction = new Vector2(0, 1) * movementSpeed;
Direction = new Vector2(0, 1) * tractorSpeed;
Position = Position + Direction;
}
else
@ -38,11 +30,11 @@ class HandleRotation
}
}
}
else if (Destination == 1) // up
else if (Destination == 1)
{
if (Rotation == 180)
{
Direction = new Vector2(0, -1) * movementSpeed;
Direction = new Vector2(0, -1) * tractorSpeed;
Position = Position + Direction;
}
else
@ -58,11 +50,11 @@ class HandleRotation
}
}
else if (Destination == 2) // right
else if (Destination == 2)
{
if (Rotation == 270)
{
Direction = new Vector2(1, 0) * movementSpeed;
Direction = new Vector2(1, 0) * tractorSpeed;
Position = Position + Direction;
}
else
@ -81,11 +73,11 @@ class HandleRotation
}
}
}
else if (Destination == 3) // left
else if (Destination == 3)
{
if (Rotation == 90)
{
Direction = new Vector2(-1, 0) * movementSpeed;
Direction = new Vector2(-1, 0) * tractorSpeed;
Position = Position + Direction;
}
else
@ -105,12 +97,6 @@ class HandleRotation
}
}
oldSpeed = crops.getSpeedFactor(tractorSpeed);
if (oldDeltaPosition.X < 1 && oldDeltaPosition.X > -1 && oldDeltaPosition.Y < 1 && oldDeltaPosition.Y > -1)
{
Position = Target;
}
oldMovementSpeed = Direction;
return Position;
}
@ -118,19 +104,4 @@ class HandleRotation
{
return Rotation;
}
public bool checkTile(Vector2 Position, int tileSize, int Spacing, float tractorSpeed, Crops crop)
{
Vector2 newTile = new Vector2((float)Math.Round(Position.X / (tileSize + Spacing)), (float)Math.Round(Position.Y / (tileSize + Spacing)));
if (oldTile != newTile)
{
oldTile = newTile;
movementSpeed = crop.getSpeedFactor(tractorSpeed);
return true;
}
else
{
return false;
}
}
}

View File

@ -7,7 +7,7 @@ using System.Threading.Tasks;
class Cargo
{
private Items[,] items = new Items[2, 281];
private Items[,] items = new Items[2, 30];
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", 2, 0);
items[1, 0] = new Items("Barley", 1, 0);
items[1, 1] = new Items("Cotton", 1, 1);
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);
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);
}
public void addItem(Items item, int Type)

View File

@ -11,12 +11,9 @@ using Microsoft.Xna.Framework.Input;
class Inventory
{
private int Weight = 0;
private int maxWeight = 280;
private int[] totalHarvested = new int[11];
private int[] totalFertilizerUsed = new int[8];
private int maxWeight = 25;
private Cargo cargo = new Cargo();
private Cargo itemStorage = new Cargo();
private int Refills = 0;
// Item list by index:
// ---------------------------------------
@ -62,10 +59,6 @@ class Inventory
public bool addItem(int newItemIndex, int Type)
{
Items newItem;
if (Type == 1)
{
totalHarvested[newItemIndex]++;
}
newItem = itemStorage.getItemByIndex(newItemIndex, Type);
if (maxWeight >= Weight + newItem.getWeight())
{
@ -83,14 +76,10 @@ class Inventory
// Uses an item from the tractor.
// 0 = Fertilizer
// 1 = Crops
public bool useItem(int Index, int Type, bool isClearing)
public bool useItem(int Index, int Type)
{
if (cargo.itemExists(Index, Type))
{
if (Type == 0 && !isClearing)
{
totalFertilizerUsed[Index]++;
}
removeItem(Index, Type);
return true;
}
@ -112,8 +101,6 @@ class Inventory
public void removeItem(int Index, int Type)
{
Weight = Weight - itemStorage.getItemByIndex(Index, Type).getWeight();
cargo.removeItem(Index, Type);
}
@ -122,102 +109,19 @@ class Inventory
return itemStorage;
}
public void clearInventory()
{
for (int i = 0; i <= 6; i++)
while (useItem(i, 0, true)) ;
for (int i = 0; i <= 10; i++)
while (useItem(i, 1, true)) ;
}
public void fillWithFertilizer()
{
Refills++;
int fert_amount = 7;
float weightPerFertilizer = maxWeight / fert_amount;
float min = weightPerFertilizer / 10;
float max = (2 * weightPerFertilizer) - min;
float expectedUse = Refills * weightPerFertilizer;
double[] useFactor = new double[fert_amount];
for (int i = 0; i < fert_amount; i++)
{
if (Refills <= 1)
useFactor[i] = weightPerFertilizer;
else
if (totalFertilizerUsed[i] / expectedUse + 0.85f > 1.0f)
useFactor[i] = Math.Round(Math.Pow((totalFertilizerUsed[i] / expectedUse) + 0.50f, 0.6f + Math.Min(Refills / 6.0f, 1.0f)) * weightPerFertilizer);
else
useFactor[i] = Math.Round(Math.Pow((totalFertilizerUsed[i] / expectedUse) + 0.50f, 1.0f + Math.Min(Refills / 6.0f, 1.8f)) * weightPerFertilizer);
if (useFactor[i] < min)
useFactor[i] = min;
else if (useFactor[i] > max)
useFactor[i] = max;
}
int j = 0;
bool change = false;
while (getWeight() < getMaxWeight() - fert_amount)
{
if (useFactor[j] > 0)
{
addItem(j, 0);
useFactor[j]--;
change = true;
}
j++;
if (j >= fert_amount)
{
if (!change)
break;
else
{
j = 0;
change = false;
}
}
}
int m = 0;
while (getWeight() < getMaxWeight()- fert_amount)
{
if (m > 6)
m = 0;
addItem(m, 0);
m++;
}
}
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)
{
spriteBatch.DrawString(Bold, "Crops: ", new Vector2(470, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 2), Color.DarkRed);
spriteBatch.DrawString(Bold, "Harvested:", new Vector2(600, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 2), Color.DarkRed);
for (int i = 0; i < 11; i++) //Print Crops
{
spriteBatch.DrawString(Bold, cargo.getCount(i, 1) + " " + itemStorage.getItemByIndex(i, 1).getItemType(), new Vector2(470, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 20 * i + 22), Color.Teal);
spriteBatch.DrawString(Bold, totalHarvested[i].ToString(), new Vector2(620, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 20 * i + 22), Color.Teal);
spriteBatch.DrawString(Bold, cargo.getCount(i, 1) + " " + itemStorage.getItemByIndex(i, 1).getItemType(), new Vector2(470, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 20 * i + 22), Color.DarkBlue);
}
spriteBatch.DrawString(Bold, "Fertilizers: ", new Vector2(700, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 2), Color.DarkRed);
spriteBatch.DrawString(Bold, "Used ", new Vector2(830, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 2), Color.DarkRed);
for (int i = 0; i < 7; i++) //Print Fertilizers
{
spriteBatch.DrawString(Bold, cargo.getCount(i, 0) + " " + itemStorage.getItemByIndex(i, 0).getItemType(), new Vector2(700, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 20 * i + 22), Color.Teal);
spriteBatch.DrawString(Bold, totalFertilizerUsed[i].ToString(), new Vector2(835, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 20 * i + 22), Color.Teal);
}
spriteBatch.DrawString(Bold, cargo.getCount(i, 0) + " " + itemStorage.getItemByIndex(i, 0).getItemType(), new Vector2(700, input.getSize().Y * (input.getTileSize() + input.getSpacing()) + 20 * i + 22), Color.DarkBlue);
}
}
}

View File

@ -1,18 +1,14 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
class Tractor
{
private int Spacing, sizeTile, Speed = 1;
private float tractorSpeed = 1;
private Vector2 Position, TargetPosition, Size, housePos, DeltaPosition;
private Vector2 Position, TargetPosition, Size, housePos;
private Path path = new Path();
private SmartTractor smartTractor = new SmartTractor();
private HandleRotation handleRotation = new HandleRotation();
private int j;
public void updateSizing(Input input, int Status, Vector2 newHousePos, DayNightCycle Time)
{
@ -21,7 +17,7 @@ class Tractor
Size = input.getSize();
updatePosition(input.getSize(), Status);
housePos = newHousePos;
smartTractor.UpdateCrops(Speed, Time);
smartTractor.UpdateCrops(Speed);
}
public void init(Rectangle house, Input input)
@ -30,14 +26,13 @@ class Tractor
Spacing = input.getSpacing();
Position = housePos;
TargetPosition = new Vector2(house.X, house.Y);
smartTractor.init(new Vector2(house.X, house.Y));
smartTractor.init();
}
// Runs when the tractor reaches a tile
private void updateDirection(Vector2 Size, Vector2 newPosition)
{
DeltaPosition = TargetPosition - Position;
handleRotation.checkTile(Position, sizeTile, Spacing, tractorSpeed, smartTractor.getFarm().getCrop((int)Math.Round(Position.X / (sizeTile + Spacing)), (int)Math.Round(Position.Y / (sizeTile + Spacing))));
Vector2 DeltaPosition = TargetPosition - Position;
if (DeltaPosition.X == 0)
{
@ -47,49 +42,30 @@ class Tractor
}
else if (DeltaPosition.Y > 0)
{
Position = handleRotation.UpdatePosition(0, tractorSpeed, Position, smartTractor.getFarm().getCrop((int)Math.Round(Position.X / (sizeTile + Spacing)), (int)Math.Round(Position.Y / (sizeTile + Spacing))), DeltaPosition, TargetPosition);
Position = handleRotation.UpdatePosition(0, tractorSpeed, Position);
}
else if (DeltaPosition.Y < 0)
{
Position = handleRotation.UpdatePosition(1, tractorSpeed, Position, smartTractor.getFarm().getCrop((int)Math.Round(Position.X / (sizeTile + Spacing)), (int)Math.Round(Position.Y / (sizeTile + Spacing))), DeltaPosition, TargetPosition);
Position = handleRotation.UpdatePosition(1, tractorSpeed, Position);
}
}
else if (DeltaPosition.X > 0)
{
Position = handleRotation.UpdatePosition(2, tractorSpeed, Position, smartTractor.getFarm().getCrop((int)Math.Round(Position.X / (sizeTile + Spacing)), (int)Math.Round(Position.Y / (sizeTile + Spacing))), DeltaPosition, TargetPosition);
Position = handleRotation.UpdatePosition(2, tractorSpeed, Position);
}
else if (DeltaPosition.X < 0)
{
Position = handleRotation.UpdatePosition(3, tractorSpeed, Position, smartTractor.getFarm().getCrop((int)Math.Round(Position.X / (sizeTile + Spacing)), (int)Math.Round(Position.Y / (sizeTile + Spacing))), DeltaPosition, TargetPosition);
Position = handleRotation.UpdatePosition(3, tractorSpeed, Position);
}
}
public void updatePosition(Vector2 Size, int Status) // updates the position
{
//farm.updateSize(Size, sizeTile, Spacing);
for (int i = 0; i < Speed; i++)
{
updateDirection(Size, Position);
/*
if (!smartTractor.getWaitTwoFrames())
{
updateDirection(Size, Position);
j = WaitFrame;
}
else if (j != 0)
{
j--;
}
else
{
smartTractor.setWaitTwoFrames(false);
}*/
}
}

View File

@ -4,8 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using C5;
using System.Diagnostics;
using Game1.Sources.ML;
class Astar
{
@ -14,6 +13,7 @@ class Astar
private Vector2 housePos;
private static Crops[,] crops;
private Vector2 Size;
private PriorityQueue allPaths;
private Vector2 targetPos;
private int Rotation;
@ -36,15 +36,20 @@ class Astar
targetPos = newTargetPos;
}
public Nodes getOptimalPath()
{
return allPaths.Peek();
}
// Get all adjacent nodes
public List<Nodes> GetAdjacentNodes(Vector2 currentPos)
{
var adjacentNodes = new List<Nodes>()
{
new Nodes(new Vector2(currentPos.X, currentPos.Y - 1), 0),
new Nodes(new Vector2(currentPos.X, currentPos.Y+1), 0),
new Nodes(new Vector2(currentPos.X + 1, currentPos.Y), 1),
new Nodes(new Vector2(currentPos.X, currentPos.Y + 1), 2),
new Nodes(new Vector2(currentPos.X, currentPos.Y - 1), 2),
new Nodes(new Vector2(currentPos.X - 1, currentPos.Y), -1),
};
@ -75,7 +80,7 @@ class Astar
if (currDir == newDir)
return 0;
else if (Math.Abs(currDir - newDir) == 1 || Math.Abs(currDir - newDir) == 3)
return 3;
return 2;
else if (Math.Abs(currDir - newDir) == 0 || Math.Abs(currDir - newDir) == 2)
return 9;
return 0;
@ -85,13 +90,13 @@ class Astar
public int ConvertRotation()
{
int rotation = 0;
if (Rotation > 135 && Rotation < 225)
if (Rotation == 180)
rotation = 0;
else if (Rotation > 225 && Rotation < 315)
else if (Rotation == 270)
rotation = 1;
else if (Rotation > 315 && Rotation < 45)
else if (Rotation == 0)
rotation = 2;
else if (Rotation > 45 && Rotation < 135)
else if (Rotation == 90)
rotation = -1;
return rotation;
}
@ -128,6 +133,7 @@ class Astar
g = current.getG() + crops[(int)adjacentNode.getCords().X, (int)adjacentNode.getCords().Y].getCostOnMovement() + CalculateRotationCost(direction, adjacentNode.getDirection()); // calculate g - cost from start point
if (!(openList.Exists(adjacentNode.getCords()))) // if adjacent node is not on open list, add it
{
adjacentNode.setG(g);
adjacentNode.setH(ComputeHScore(adjacentNode.getCords(), target.getCords()));
adjacentNode.calculateF();
@ -159,75 +165,7 @@ class Astar
openList.deleteHeap();
closedList.deleteHeap();
return path;
}
public bool isReachable(Crops[,] crops, Vector2 targetPos, Vector2 start)
{
Rotation = 0;
int g = 0;
int direction = ConvertRotation();
Path path = new Path();
MinHeap openList = new MinHeap();
MinHeap closedList = new MinHeap();
Nodes target = new Nodes(targetPos);
Nodes startPos = new Nodes(tractorPos, direction);
Nodes current = null;
openList.Insert(startPos);
while (openList.GetSize() > 0)
{
current = openList.getMin();
closedList.Insert(current);
openList.removeMin();
direction = current.getDirection();
if (current.getCords() == target.getCords())
break;
var adjacentNodes = GetAdjacentNodes(current.getCords());
foreach (var adjacentNode in adjacentNodes)
{
if (closedList.Exists(adjacentNode.getCords())) // check if adjacent node is on closed list, if it is, skip it
continue;
g = current.getG() + crops[(int)adjacentNode.getCords().X, (int)adjacentNode.getCords().Y].getCostOnMovement() + CalculateRotationCost(direction, adjacentNode.getDirection()); // calculate g - cost from start point
if (!(openList.Exists(adjacentNode.getCords()))) // if adjacent node is not on open list, add it
{
adjacentNode.setG(g);
adjacentNode.setH(ComputeHScore(adjacentNode.getCords(), target.getCords()));
adjacentNode.calculateF();
adjacentNode.setParent(current);
openList.Insert(adjacentNode);
}
else
{
if (g + adjacentNode.getH() < adjacentNode.getF()) // check if adjacent node is a better path than the current one
{
adjacentNode.setG(g);
adjacentNode.calculateF();
adjacentNode.setParent(current);
}
}
}
}
while (current != null)
{
path.AddNode(current);
current = current.getParent();
}
openList.deleteHeap();
closedList.deleteHeap();
if (path.getByIndex(0).getCords() != targetPos)
return false;
else
return true;
}
}

View File

@ -6,9 +6,8 @@ using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;
using C5;
class Nodes : IComparable<Nodes>, IEqualityComparer<Nodes>, IPriorityQueueHandle<Nodes>
class Nodes
{
private int F = 0;
private int G = 0;
@ -28,12 +27,6 @@ class Nodes : IComparable<Nodes>, IEqualityComparer<Nodes>, IPriorityQueueHandle
Direction = direction;
}
public Nodes(int f, Vector2 coordinates)
{
Coordinates = coordinates;
F = f;
}
public Nodes(Nodes node)
{
F = node.F;
@ -101,24 +94,4 @@ class Nodes : IComparable<Nodes>, IEqualityComparer<Nodes>, IPriorityQueueHandle
{
return Direction;
}
public int CompareTo(Nodes other)
{
if (this.F < other.F)
return -1;
else if (this.F > other.F)
return 1;
else
return 0;
}
public bool Equals(Nodes source, Nodes other)
{
return (source.Coordinates.X == other.Coordinates.X && source.Coordinates.Y == other.Coordinates.Y);
}
public int GetHashCode(Nodes nodes)
{
return (nodes.Coordinates.GetHashCode());
}
}

View File

@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;
class PriorityQueue
{
public List<Nodes> list;
public int Count { get { return list.Count; } }
public PriorityQueue()
{
list = new List<Nodes>();
}
public PriorityQueue(int count)
{
list = new List<Nodes>(count);
}
public void Enqueue(Nodes x)
{
list.Add(x);
int i = Count - 1;
while (i > 0)
{
int p = (i - 1) / 2;
if (list[p].getF() <= x.getF()) break;
list[i] = list[p];
i = p;
}
if (Count > 0) list[i] = x;
}
public void Dequeue()
{
Nodes min = Peek();
Nodes root = list[Count - 1];
list.RemoveAt(Count - 1);
int i = 0;
while (i * 2 + 1 < Count)
{
int a = i * 2 + 1;
int b = i * 2 + 2;
int c = b < Count && list[b].getF() < list[a].getF() ? b : a;
if (list[c].getF() >= root.getF()) break;
list[i] = list[c];
i = c;
}
if (Count > 0) list[i] = root;
}
public Nodes Peek()
{
if (Count == 0) throw new InvalidOperationException("Queue is empty.");
return list[0];
}
public Boolean Exists(Vector2 coordinates)
{
if (Count == 0)
return false;
foreach(Nodes node in list)
{
if (node.getCords() == coordinates)
return true;
}
return false;
}
public void Clear()
{
list.Clear();
}
}

View File

@ -46,9 +46,4 @@ class PriorityQueueC5
queue.AddAll(entryList);
}
public int getCount()
{
return queue.Count();
}
}

View File

@ -15,10 +15,9 @@ class AI
private Vector2 Size;
private Vector2 targetPos;
private Inventory inventory = new Inventory();
private FertilizerHolder fertilizerHolder = new FertilizerHolder();
private int Rotation;
private PriorityQueueC5 PriorityQueueC5;
private Astar astar = new Astar();
public bool WaitTwoFrames { get; set; }
private Random r = new Random();
@ -46,31 +45,14 @@ class AI
inventory.printItems(input, spriteBatch, Bold);
}
public Path newTarget()
public Vector2 newTarget()
{
PriorityQueueC5 queue = new PriorityQueueC5();
int score = 0;
int count = 0;
int testsize = 2;
Path newTarget;
Nodes nodes;
Random random = new Random();
if (astar.GetAdjacentNodes(tractorPos).Count == 0)
nodes = new Nodes(housePos);
else
{
List<Nodes> templist = astar.GetAdjacentNodes(tractorPos);
nodes = templist[random.Next(0, templist.Count())];
templist.Clear();
}
if (tractorPos != housePos)
if (inventory.getWeight() == inventory.getMaxWeight() || inventory.isMissingFertilizer())
{
astar.update(farm.getCrops(), Size, tractorPos, housePos, housePos, Rotation);
return astar.FindPath(true);
}
Vector2 newTarget;
while (true)
{
for (int x = 0; x < Size.X; x++)
@ -78,57 +60,50 @@ class AI
{
if (farm.getCrop(x, y).getStatus() >= 2 && tractorPos != new Vector2(x, y))
{
if (farm.getCrop(x, y).getStatus() != 2 && (farm.getCrop(x, y).getProductionRate() > 0.99f || !farm.getCrop(x, y).belowCapacity()))
if (farm.getCrop(x, y).getStatus() == 3 && farm.getCrop(x, y).getCropTimer() != 1)
{
//skip growing fields with high production rate
if (housePos == tractorPos)
{
score = calculateSoilScore(x, y);
queue.AddToQueue(x, y, score);
count++;
}
//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)
return housePos;
if (queue.getCount() == 0)
/*
else if (tractorPos == housePos)
{
astar.update(farm.getCrops(), Size, tractorPos, housePos, nodes.getCords(), Rotation);
return newTarget = astar.FindPath(true);
List<Nodes> temp = astar.GetAdjacentNodes(tractorPos);
return temp[0].getCords();
}
*/
}
newTarget = GetMinFNode(Math.Min(testsize, queue.getCount()), queue);
queue = null;
newTarget = GetMinFNode(Math.Min(testsize, count), queue);
return newTarget;
}
public Farm changeCropStatus()
{
int x = (int)tractorPos.X;
int y = (int)tractorPos.Y;
Fertilizer fertilizer = new Fertilizer();
fertilizerHolder.init();
WaitTwoFrames = false;
if (farm.getCrop(x, y).getStatus() >= 2)
{
fertilizer = fertilizerHolder.GetFertilizer(Engine.PredictFertilizer(farm.getCrop(x, y), farm.getPresetCropTypes(farm.getCrop(x, y).getCropType())));
while (!(farm.getCrop(x, y).isSaturated(-1)) && farm.getCrop(x, y).belowCapacity() && inventory.useItem(fertilizerHolder.GetFertilizerID(fertilizer.Name), 0, false))
{
farm.getCrop(x, y).Fertilize(fertilizer);
fertilizer = fertilizerHolder.GetFertilizer(Engine.PredictFertilizer(farm.getCrop(x, y), farm.getPresetCropTypes(farm.getCrop(x, y).getCropType())));
WaitTwoFrames = true;
}
}
if (farm.getCrop(x, y).getCropTimer() == 1)
if (farm.getCrop((int)tractorPos.X, (int)tractorPos.Y).getCropTimer() == 1)
{
farm.setCropStatus(tractorPos.X, tractorPos.Y);
if (farm.getCrop(x, y).getStatus() == 2)
if (farm.getCrop((int)tractorPos.X, (int)tractorPos.Y).getStatus() == 2)
{
inventory.addItem(farm.getCrop(x, y).getCropType() - 1, 1);
inventory.addItem(farm.getCrop((int)tractorPos.X, (int)tractorPos.Y).getCropType() - 1, 1);
}
}
@ -142,38 +117,50 @@ class AI
private int calculateSoilScore(int x, int y)
{
int score = 1;
int statusScore = 1;
int timerScore = 1;
int saturationScore = 1;
int productionRateScore = 1;
int score = 0;
int statusScore = 0;
int aproxDistance = (int)(Math.Abs(x - tractorPos.X) + Math.Abs(y - tractorPos.Y));
CropTypesHolder holder = new CropTypesHolder();
holder.init();
Crops crop = farm.getCrop(x, y);
SoilProperties soilP = crop.getSoilProperties();
int cropType = crop.getCropType();
if (crop.getCropTimer() == 1)
timerScore = 100;
productionRateScore = (int)(crop.getProductionRate() * 25);
score = (int)(crop.getProductionRate() * -50);
CropTypes avgHold = holder.getPresetCropTypes(cropType);
if (crop.getStatus() == 2)
{
statusScore = 50;
productionRateScore = 0;
}
statusScore = 25;
else if (crop.getStatus() == 3)
statusScore = -100;
if (!crop.isSaturated(2))
saturationScore = 5;
statusScore = 5;
else if (crop.getStatus() == 4)
statusScore = -10;
else
statusScore = 1;
float[] currentValue = { soilP.Temperature, soilP.Humidity, soilP.Moisture, soilP.Nitrogen, soilP.Potassium, soilP.Phosphorous };
float[] targetAvg = { avgHold.Temparature, avgHold.Humidity, avgHold.Moisture, avgHold.Nitrogen, avgHold.Potassium, avgHold.Phosphorous };
float[,] minMax = { {22,30}, {22*1.9f, 30*2.1f}, {20, 70}, {4, 55}, {0, 28}, {0, 60} }; //probably should make it dynamic
float[] normVal = normArray(minMax, currentValue);
float[] normAvg = normArray(minMax, targetAvg);
for (int i = 0; i < normVal.Count(); i++)
{
saturationScore = -100;
score = score + (int)Math.Round(System.Convert.ToDouble(10*(1 - Math.Abs(normAvg[i] - normVal[i]))));
}
return score + (-aproxDistance * 10) + statusScore + timerScore + saturationScore;
return score + (-aproxDistance * 3) + statusScore;
}
private float[] normArray(float[,] minMax, float[] values)
{
float[] temp = new float[values.Count()];
if (minMax.GetLength(0) != values.Count())
throw new InvalidOperationException("Values and their MinMax arrays are not of the same length!");
for (int i = 0; i < values.Count(); i++)
temp[i] = norm(minMax[i, 0], minMax[i, 1], values[i]);
return temp;
}
private float norm(float min, float max, float val)
@ -181,13 +168,12 @@ class AI
return ((val - min) / (max - min));
}
public Path GetMinFNode(int testSize, PriorityQueueC5 queue)
public Vector2 GetMinFNode(int testSize, PriorityQueueC5 queue)
{
int index = 0;
int min = 9999;
int min = 999;
Path path = new Path();
List<PQEntry> entryList = new List<PQEntry>();
Path[] pathList = new Path[testSize];
for (int i = 0; i < testSize; i++)
{
entryList.Add(queue.DeleteMax());
@ -196,9 +182,7 @@ class AI
for (int i = 0; i < testSize; i++)
{
Nodes temp = new Nodes(entryList[i].Coordinates);
astar.update(farm.getCrops(), Size, tractorPos, housePos, temp.getCords(), Rotation);
path = astar.FindPath(false);
pathList[i] = path;
Nodes tempF = new Nodes(path.getByIndex(0));
if (min > tempF.getF())
{
@ -206,47 +190,13 @@ class AI
index = i;
}
}
PQEntry minEntry = entryList[index];
entryList.RemoveAt(index);
return pathList[index].FlipArray();
//add the non-minimum entries back to the queue.
queue.AddAll(entryList);
return minEntry.Coordinates;
}
public Path GetMaxFNode(int testSize, PriorityQueueC5 queue)
{
int index = 0;
int max = -9999;
Path path = new Path();
List<PQEntry> entryList = new List<PQEntry>();
Path[] pathList = new Path[testSize];
for (int i = 0; i < testSize; i++)
{
entryList.Add(queue.DeleteMax());
}
for (int i = 0; i < testSize; i++)
{
Nodes temp = new Nodes(entryList[i].Coordinates);
astar.update(farm.getCrops(), Size, tractorPos, housePos, temp.getCords(), Rotation);
path = astar.FindPath(false);
pathList[i] = path;
Nodes tempF = new Nodes(path.getByIndex(0));
if (max < tempF.getF())
{
max = tempF.getF();
index = i;
}
}
return pathList[index].FlipArray();
}
public void reloadCargo()
{
inventory.clearInventory();
inventory.fillWithFertilizer();
}
}

View File

@ -16,18 +16,14 @@ class SmartTractor
//What to do next
public Path returnChoice()
{
//System.Threading.ThreadStart tractorThread = new System.Threading.ThreadStart(farm.UpdatePreferedCrops);
ai.update(farm, Size, tractorPos / (tileSize + Spacing), housePos / (tileSize + Spacing), Target / (tileSize + Spacing), Rotation);
//farm.UpdatePreferedCrops(Size);
farm.UpdatePreferedCrops(Size);
farm = ai.changeCropStatus();
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();
getTargetPosition(ai.newTarget());
astar.update(farm.getCrops(), Size, tractorPos / (tileSize + Spacing), housePos / (tileSize + Spacing), Target / (tileSize + Spacing), Rotation);
return ai.newTarget();
//return astar.FindPath(true);
return astar.FindPath(true);
}
@ -44,14 +40,13 @@ class SmartTractor
tileSize = newTileSize;
Spacing = newSpacing;
Rotation = rotation;
}
public void init(Vector2 nHousePos)
public void init()
{
ai.init();
housePos = nHousePos;
farm.init(new Vector2(100, (GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height / tileSize) - 125 / tileSize), housePos / (tileSize + Spacing));
farm.UpdatePreferedCrops(Size);
}
public void drawInventory(Input input, SpriteBatch spriteBatch, SpriteFont Bold, Cargo itemStorageDefined)
@ -74,27 +69,16 @@ class SmartTractor
farm.setNewHousePos(pos, newState);
}
public void UpdateCrops(int Speed, DayNightCycle nTime)
public void UpdateCrops(int Speed)
{
for (int i = 0; i < Speed; i++)
{
farm.updateFarm(Size);
}
farm.updateRainFall(Size, nTime);
}
public Inventory getInventory()
{
return ai.getInventory();
}
public bool getWaitTwoFrames()
{
return ai.WaitTwoFrames;
}
public void setWaitTwoFrames(bool input)
{
ai.WaitTwoFrames = input;
}
}

View File

@ -1,103 +0,0 @@
Machine Learning Implementation
Introduction
I have implemented a ML to add in season, daily rainfall and area which can be cultivated into the Productivity rate of each crop. This dataset contains over 65000 entries of which we only use around 20000 as those are the only crops that are plantable for our agen and the lowest amount of entries for a single crop is 600. The daily rainfall has been added to SoilProperties in our tileset. Implementation of the needed properties into SoilProperties:
public float Rainfall;
public float prevRainfall;
public int Area;
.
.
.
//Randomize the first days rainfall and the area,
prevRainfall = r.Next(247, 3617);
Area = r.Next(1, 270000);
The dataset is said to be based on real life data. But for our our program is not able to fit the data 1 to 1 because some Crops only had an daily rainfall from 247 to 2000 but the Rainfall of that tile could still be 3617. I havent done anything to mitigate this but it doesnt result in any strange results when passing through our data into the ML Model. It would have been better for the dataset contained rainfall data from 247 to 3617. Then the yield could possibly be lower or higher as rainfall increases or decreases. We added dynamic rainfall in is therefore constricting the rain from going outside of the datasets parameters:
//Sets previous days rainfall to current days rainfall and constrict the total rainfall from going outside of the parameters
soilProperties.prevRainfall = soilProperties.Rainfall;
if (soilProperties.prevRainfall > 3616)
soilProperties.prevRainfall = 3616;
else if (soilProperties.prevRainfall < 236)
soilProperties.prevRainfall = 236;
soilProperties.Rainfall = 0;
Implementation
I have used gradient boosting decision tree algorithm due to the features.
I used Gradient Boosting Decision Tree Algorithm for this task due to many features it has.
First a csv file is loaded:
IDataView trainingDataView = mlContext.Data.LoadFromTextFile<ModelInput>(
path: path,
hasHeader: true,
separatorChar: ',',
allowQuoting: true,
allowSparse: false);
Then it is passed to next function which will train, evaluate and build a model:
- maximizing number of leaves,
- limiting maximum tree depth,
- maximizing the amount of bins per feature,
maintaining high accuracy by:
- low learning rate combine with:
- high number of iterations.
Creating pipeline for the model:
var pipeline = mlContext.Transforms
.Text.FeaturizeText("SeasonF", "Season")
.Append(mlContext.Transforms.Text.FeaturizeText("CropF", "Crop"))
.Append(mlContext.Transforms.Concatenate("Features", "SeasonF", "CropF", "Area", "Rainfall"))
.AppendCacheCheckpoint(mLContext)
.Append(mLContext.Regression.Trainers.LightGbm(options));
.Append(mlContext.Transforms.Conversion.MapKeyToValue("PredictedLabel", "PredictedLabel"));
Integration
Our tileset now has a dynamically moving overlay of clouds moving at the speed of WindSpeed and if the clouds are above at a tile we add to the total rainfall of the day. The rainfall we pass into the ML Model is the previous days rainfall and the first days rainfall is randomized.
We achieved this rainsystem by running a bitmap over our tileset, the bitmap is continues with itself so it runs seamlessly. Depending on the ransparency of each pixel on this bitmap we determine how much it is currently raining on set tile:
if (Rain >= 0.45f)
{
soilProperties.Rainfall = soilProperties.Rainfall + Rain * 4;
}
if the transparency is < 0.45 then there will be no cloud drawn and it will not be raining at all. And this is the result:
(https://git.wmi.amu.edu.pl/s425077/PotatoPlan/raw/Oskar-ML/Example.jpg)
To optimize the program we now update 10% of the columns every second or else it would freeze for seconds at a time and the ML Model is only being fed data once a day as the total rainfall is only updated once a day anyways.

View File

@ -1,120 +0,0 @@
# Machine Learning implementation report
## Introduction
Purpose of my ML implementation is for the agent (tractor) to decide what fertilizer it should use.
It's decision is mostly based on nutrients in soil, but also on few other properties.
Dataset is very small, it contains only 100 entries.
There are 7 types of fertilizers, each of them adding a specific amount of nutrients to the soil.
Example:
FertilizerType[6] = new Fertilizer
{
ID = 5,
Name = "DAP",
Nitrogen = 14.52f / 5,
Phosphorus = 1.77f / 5,
Potassium = 9.5f / 5
};
Unfortunately values of nutrients are not based on real values.
That is because even though dataset intention (by it's creator) was to be used to classify fertilizers, it looks like instead it says what fertilizer WAS used and what will be the results of using that fertilizer on some field.
E.g: Urea has 46% of Nitrogen in it and nothing else. In dataset it was classified as best fertilizer to be used on fields with already really high Nitrogen levels. That would lead to oversaturation with Nitrogen and lack of other nutrients.
So i did some calculations and Urea now looks like this:
FertilizerType[7] = new Fertilizer
{
ID = 6,
Name = "Urea",
Nitrogen = 1.81f / 5,
Phosphorus = 21.0f / 5,
Potassium = 9.5f / 5
};
// an "inversed" and little modified counterpart of real-world version of this fertilizer.
## Implementation
I used Gradient Boosting Decision Tree Algorithm for this task due to many features it has.
First a csv file is loaded:
IDataView trainingDataView = mlContext.Data.LoadFromTextFile<ModelInput>(
path: path,
hasHeader: true,
separatorChar: ',',
allowQuoting: true,
allowSparse: false);
Then it is passed to next function which will train, evaluate and build a model.
Also trainer parameters will be fine-tuned here to prevent overfitting as much as possible by:
- limiting number of leaves,
- limiting maximum tree depth,
- limiting the amount of bins per feature,
while maintaining high accuracy by:
- low learning rate combine with
- high number of iterations.
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
}
};
Creating pipeline for the model:
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.Conversion.MapValueToKey("Fertilizer_NameF", "Fertilizer_Name"), TransformerScope.TrainTest)
.AppendCacheCheckpoint(mLContext)
.Append(mLContext.MulticlassClassification.Trainers.LightGbm(options))
.Append(mlContext.Transforms.Conversion.MapKeyToValue("PredictedLabel", "PredictedLabel"));
Evaluation of the pipeline is done with cross-validation method with 10 folds.
Results are as follow:
Micro Accuracy: 0.95829
LogLoss Average: 0.100171
LogLoss Reduction: 0.933795
Model is created and saved for later use, to skip long trainig and evaluation times.
Later that model is loaded and prediction engine is created when program is started.
## Integration Details
Agent (tractor) navigates trough the grid looking for tiles where it can plant some plants.
Upon planting and visitin already growing plants agent decides if any fertilizer is needed (rule based decision), and what fertilizer to use (using ML prediction engine).
if (farm.getCrop(x, y).getStatus() >= 2)
{
fertilizer = fertilizerHolder.GetFertilizer(Engine.PredictFertilizer(farm.getCrop(x, y), farm.getPresetCropTypes(farm.getCrop(x, y).getCropType())));
while (!(farm.getCrop(x, y).isSaturated(-1)) && 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())));
WaitTwoFrames = true;
}
If field is properly fertilized it will have higher production rate, resulting in faster growth of a plant.
Production rate value is shown in the UI as well as it is represented by the colour of progression bar (right side of every tile).
At 100% bar will pure **Green**. Any value below will make bar more **Red**, while any value above will add **Blue**, eventually turning bar colour into cyan.
Example:
![Progression Bar](https://git.wmi.amu.edu.pl/s425077/PotatoPlan/raw/Oskar-ML/example_img.jpg)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB