I tried this as a programming exercise. It's working as intended, but I feel like the code is a mess and I could have used the advantages of object oriented programming much more (like inheritance for fish and shark from the Cell class, and probably much more). Can someone guide me a bit through this code example and show me how to make this more object-oriented?
The code simulates an ocean populated by fish and sharks:
- Sharks hunt fish
- Fish swim and breed
- Sharks die of starvation after x years
The ocean, fish and sharks are all represented by a Cell class. The objects are stored in a 2D array and then painted onto the Windows form.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;
namespace WaTor
{
public partial class Form1 : Form
{
//Gritsize width(x) and height(y)
const int TableX = 10;
const int TableY = 10;
const int pixelSizeX = 19;
const int pixelSizeY = 19;
//Fish Properties
int initialNumberOfFish = 2;
int initialNumberOfSharks = 2;
int ageToBreedFish = 5;
int ageToBreedShark = 10;
int yearToStarvation = 5;
int sharkScent = 3;
Random rnd = new Random();
Cell[,] myArray = new Cell[TableX, TableY];
public Form1()
{
InitializeComponent();
intitializeArray();
}
private void intitializeArray()
{
int x = 0; //Position on X-Axis
int y = 0; //Position on Y-Axis
for (int i = 0; i < myArray.GetLength(0); i++)
{
for (int j = 0; j < myArray.GetLength(1); j++)
{
Cell newCellX = new Cell();
myArray[i,j] = newCellX;
myArray[i,j].posX = x;
myArray[i,j].posY = y;
x = x + pixelSizeX+1;
}
x = 0;
y = y + pixelSizeY+1;
}
}
//Only used to paint the results
private void Canvas_Paint(object sender, PaintEventArgs e)
{
Graphics g = Canvas.CreateGraphics();
for (int i = 0; i < myArray.GetLength(0); i++)
{
for (int j = 0; j < myArray.GetLength(1); j++)
{
Brush b = new SolidBrush(myArray[i,j].cellColor);
Rectangle r = new Rectangle(myArray[i,j].posX, myArray[i,j].posY, pixelSizeX, pixelSizeY);
g.FillRectangle(b, r);
}
}
}
//Only used when initializing a new simulation
public void populate()
{
//Clean the grid
for (int i = 0; i < myArray.GetLength(0); i++)
{
for (int j = 0; j < myArray.GetLength(1); j++)
{
kill(i, j);
}
}
//Distribute Fish randomly
int PosX=0;
int PosY=0;
bool isEmpty=false;
Random rndX = new Random();
Random rndY = new Random();
PosX = rndX.Next(0, TableX);
for (int i = 0; i < initialNumberOfFish; i++)
{
while (!isEmpty)
{
PosX = rndX.Next(TableX);
PosY = rndY.Next(TableY);
if (myArray[PosX, PosY].fishType == fishTypeEnum.empty)
isEmpty = true;
}
myArray[PosX, PosY].fishType = fishTypeEnum.fish;
myArray[PosX, PosY].cellColor = Color.Green;
myArray[PosX, PosY].age = rndX.Next(0, ageToBreedFish);
isEmpty = false;
}
//Distribute Sharks randomly
for (int i = 0; i < initialNumberOfSharks; i++)
{
while (!isEmpty)
{
PosX = rndX.Next(0, TableX);
PosY = rndY.Next(0, TableY);
if (myArray[PosX, PosY].fishType != fishTypeEnum.shark)
isEmpty = true;
}
myArray[PosX, PosY].fishType = fishTypeEnum.shark;
myArray[PosX, PosY].cellColor = Color.Red;
myArray[PosX, PosY].age = rndX.Next(0, yearToStarvation);
myArray[PosX, PosY].ageStarvation = myArray[PosX, PosY].age;
isEmpty = false;
}
}
//A tick is a 'round' where fish swim and breed, sharks hunt and breed
public void tick()
{
bool reproduce;
for (int i = 0; i < myArray.GetLength(0); i++)
{
for (int j = 0; j < myArray.GetLength(1); j++)
{
//If Cell is Shark and hasent moved
if ((myArray[i, j].fishType == fishTypeEnum.shark) && (!myArray[i, j].moved))
{
//Sharks starve
Debug.WriteLine("Hai alter"+myArray[i,j].age);
if (myArray[i, j].ageStarvation > yearToStarvation)
{
kill(i, j);
Debug.WriteLine("Hai verhungert");
}
//If not starved shark hunts
hunt(i, j);
}
else
//If Cell is Fish and hasent moved
if ((myArray[i, j].fishType == fishTypeEnum.fish) && (!myArray[i, j].moved))
{
reproduce = false;
if (myArray[i,j].age >= ageToBreedFish)
{
reproduce = true;
}
move(i, j, fishTypeEnum.fish, reproduce);
}
}
}
//End Tick
for (int i = 0; i < myArray.GetLength(0); i++)
{
for (int j = 0; j < myArray.GetLength(1); j++)
{
myArray[i, j].moved = false;
}
}
}
private void kill(int x, int y)
{
myArray[x, y].age = 0;
myArray[x, y].fishType = fishTypeEnum.empty;
myArray[x, y].cellColor = Color.Blue;
myArray[x, y].moved = false;
}
private void hunt(int x, int y)
{
Random rnd = new Random();
int direction = 0;
int newX = x;
int newY = y;
bool moved = false;
//Check for Fish nearby
direction = fishNearBy(x, y, sharkScent);
Debug.WriteLine("direction: " + direction);
if (direction == 0)
direction = rnd.Next(1, 5);
{
switch (direction)
{
case 1://East. If Index 0 move to TableEnde
if (newY > 0)
{
newY = newY - 1;
moved = true;
}
else
{
newY = TableY - 1;
moved = true;
}
break;
case 2://West. If TableEnde move to 0.
if (newY < TableY - 1)
{
newY = newY + 1;
moved = true;
}
else
{
newY = 0;
moved = true;
}
break;
case 3://North. If top reached try again
if (newX > 0)
{
newX = newX - 1;
moved = true;
}
break;
case 4://South. If bottom reached try again
if (newX < TableX - 1)
{
newX = newX + 1;
moved = true;
}
break;
default:
break;
}//switch
}//if
if (myArray[newX, newY].fishType == fishTypeEnum.fish)
{
Debug.WriteLine("Fisch gefangen");
eatFish(newX, newY);
myArray[x, y].ageStarvation = 0; ;
}
if (moved)
{
Debug.WriteLine("Hai bewegen");
myArray[newX, newY].cellColor = myArray[x, y].cellColor;
myArray[newX, newY].fishType = myArray[x, y].fishType;
myArray[newX, newY].age = myArray[x, y].age + 1;
myArray[newX, newY].ageStarvation = myArray[x, y].ageStarvation + 1;
myArray[newX, newY].moved = true;
//Shark will breed
if (myArray[newX, newY].age >= ageToBreedShark)
{
myArray[newX, newY].age = 0;
myArray[newX, newY].ageStarvation = 0;
myArray[x, y].age = 0;
myArray[x, y].ageStarvation = 0;
myArray[x, y].moved = true;
}
else
{
kill(x, y);
}
}
}
private void eatFish(int x, int y)
{
kill(x, y);
}
private void resetXY(ref int x, ref int y, int a,int b)
{
x = a;
y = b;
}
//Searches for fish. howFar determines how many cells away sharks can find fish
private int fishNearBy(int x, int y, int howFar)
{
int checkX = x;
int checkY = y;
int howClose =howFar+2;
bool fishFound = false;
int direction=0; //1=East, 2=West, 3=North, 4=South
//Array for saving the direction. Distance initialized with howfar(maxlength)
int[] directionArray = new int[5];
for (int i = 0; i < directionArray.GetLength(0); i++)
directionArray[i] = howFar + 1;
//Check East
for (int i = 0; i < howFar; i++)
{
if (checkY < 0)
{
checkY = TableY - 1;
}
if (myArray[x, checkY].fishType == fishTypeEnum.fish)
{
//Update distance
directionArray[1] = i;
fishFound = true;
break;
}
checkY--;
directionArray[1] = 99;
}
resetXY(ref checkX, ref checkY, x, y);
//Check West
for (int i = 0; i < howFar; i++)
{
if (checkY >= TableY)
{
checkY = 0;
}
if (myArray[x, checkY].fishType == fishTypeEnum.fish)
{
//Update distance
directionArray[2] = i;
fishFound = true;
break;
}
checkY++;
directionArray[2] = 99;
}
resetXY(ref checkX, ref checkY, x, y);
//Check North
for (int i = 0; i < howFar; i++)
{
if (checkX <= 0)
{
checkX = 0;
}
if (myArray[checkX, y].fishType == fishTypeEnum.fish)
{
//Update distance
directionArray[3] = i;
fishFound = true;
break;
}
checkX--;
directionArray[3] = 99;
}
resetXY(ref checkX, ref checkY, x, y);
//Check South
for (int i = 0; i < howFar; i++)
{
if (checkX >= TableX)
{
checkX = TableX-1;
}
if (myArray[checkX, y].fishType == fishTypeEnum.fish)
{
//Update distance
directionArray[4] = i;
fishFound = true;
break;
}
checkX++;
directionArray[4] = 99;
}
if (fishFound)
{
//Find the fish that is closest to the shark
for (int i = 1; i < directionArray.GetLength(0); i++)
{
Debug.WriteLine("Fish found, Index: " + Array.IndexOf(directionArray, directionArray[i]) + " Entfernung: " + directionArray[i]);
if (directionArray[i] < howClose)
{
Debug.WriteLine("Fish found, direction: " + Array.IndexOf(directionArray, directionArray[i]));
howClose = directionArray[i];
}
direction = Array.IndexOf(directionArray, howClose);
}
}
return direction;
}
private void move(int x, int y,fishTypeEnum type,bool repro)
{
int newX=x;
int newY=y;
int direction = 4;
bool cellIsFree = false;
bool noFreeCells = false;
int numberOfTries = 0;
int maxTries = 8;
if ((type == fishTypeEnum.fish))
{
while (!cellIsFree && !noFreeCells)
{
direction = rnd.Next(1, 5);//Direction between 1-4
switch (direction)
{
case 1://East. If Index 0 move to TableEnde
if (newY > 0)
{
newY = newY - 1;
}
else
newY = TableY - 1;
break;
case 2://West. If TableEnde move to 0.
if (newY < TableY - 1)
{
newY = newY + 1;
}
else
newY = 0;
break;
case 3://North. If top reached try again
if (newX > 0)
{
newX = newX - 1;
}
break;
case 4://South. If bottom reached try again
if (newX < TableX - 1)
{
newX = newX + 1;
}
break;
}//switch
if (myArray[newX, newY].fishType == fishTypeEnum.empty)
{
cellIsFree = true;
}
numberOfTries++;
if (numberOfTries > maxTries)
noFreeCells = true;
}//if
}//while (!cellIsFree)
//Debug.WriteLine("NewX=" + newX + "NewY=" + newY);
myArray[newX, newY].cellColor = myArray[x, y].cellColor;
myArray[newX, newY].fishType = myArray[x, y].fishType;
myArray[newX, newY].moved = true;
myArray[newX, newY].age = myArray[x, y].age + 1;
//If fish ist not reproducing remove the previous fish
if (!repro)
{
kill(x, y);
}
else
{
myArray[newX, newY].age = 0;
myArray[x, y].age = 0;
myArray[x, y].moved = true;
}
}
private void button2_Click(object sender, EventArgs e)
{
tick();
Canvas.Refresh();
}
private void button1_Click(object sender, EventArgs e)
{
populate();
Canvas.Refresh();
}
}
}
The Cell class is used to store information about the cells:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
namespace WaTor
{
//Fishtype
enum fishTypeEnum { fish, shark, empty };
class Cell
{
//Color
public Color cellColor { get; set; }
//Age
public int age {get;set;}
//Age used for starvation
public int ageStarvation { get; set; }
//Set&Get Fishtype
public fishTypeEnum fishType {get;set;}
//Check if moved during tick
public bool moved { get; set; }
//Position
public int posX {get;set;}
public int posY {get;set;}
//Constructor
public Cell()
{
cellColor = Color.Blue;
posX = 0;
posY = 0;
age = 0;
ageStarvation = 0;
moved = false;
fishType = fishTypeEnum.empty;
}
}
}