вторник, 31 августа 2010 г.

Разработка приложения JobTerrarium

Возможно, вы уже слышали про игру для настоящих программистов Terrarium? Сегодня я хочу предложить вам написать приложение JobTerrarium.

Техническое задание

Вот действующие лица нашей экосистемы:

живые:

Worker - простой трудяга , подчиненный, водится в ней во множественом числе, живет в ArrayListe

Boss - начальник, подчиняется БигБосу , водится во множественном числе, живет там же

BigBoss - большой начальник, произведен из класса начальников, водится во множествееном числе

Все эти персонажи происходят из класса Employee - наемный сотрудник...

Employee - базовый класс для всех сотрудников, не может существовать обьектов такого типа.

Boss - базовый класс для BigBossa

неживые:

Work - работа , разбросана в некотором количестве по полю, сама не перемещается, характеризуется лишь местонахождением

В начале выполнения программы создается некоторое количество сотрудников разных типов, им присваиваются координаты случайным образом , присваивается зарплата (по умолчанию либо / 100 больше чем по умолчанию ) и имя (по умолчанию если это допустимо, или случайным образом из некоторого пула допустимых имен), по полю "раскидывается" случайное количество работы.

Employee:

Каждый сотрудник имеет зарплату(Salary типа decimal), каждый сотрудник имеет имя(Name типа String), каждый сотрудник имеет настроение(Mood типа bool) каждый сотрудник имеет расположение на поле(типа struct Point, содержащую в себе две переменные X и Y)

Для всех сотрудников реализована возможность, что-то сказать Say(string WhatToSay). Эта функиця должна выводить на консоль(или по Вашему желанию в ListBox/ListCtrl на форме) Имя, Должность сотрудника и собственно произносимую им строку.. Разную в зависимости от того кого он dстретил.

Например, Worker встретив старшего по званию говорит: "Здраствуйте", а встретив Workera "Hi".

На каждом шаге программы сотрудники перемещаются случайным образом (перемещаются на соседнюю клетку) по полю и выполняют действия:

поздороваться, если на данной клетке находится другой служащий.

выполнить работу , выполнется в том случае если на данной клетке находится работа.

После выполнения работы она исчезает в этом и появляется в другом случайно-выбранном месте на поле.

....() - придумайте какое-либо свое действие, которым могли бы заниматься служащие, и реализуйте его. (Например можно создать класс SalaryAddition-Прибака к Зарплате, обьекты данного класса так же расскиданы по полю (случайным образом появляются в месте где был BigBoss) при столкновении с SalaryAddition у работника повышается зарплата а обьект ПрибакаКЗарплате исчезает)

Работники ходят по полю до тех пор, пока не истечет "рабочий день".

Попробуйте обойтись без событий, таймеров, многопоточности.

В цикле do/while проверяется окочание рабочего дня путем сравнения времени окончания работы(устанавливается константой) с текущим временем

Особенности реализации:

Координаты каждого из представитей как живой(Сотрудники) так и неживой(Работа, ДобавкиКЗарплате) природы хранятся в структуре типа Point , которую следует определить самостоятельно.

Работников, Работы, (Прибавки к зарплате) следует хранить в ArrayList(в одном или в двух разных).

Должна быть возможность создать сотрудника (должны быть реализованны соотв. конструкторы) c заданным именем и зарплатой , лишь с заданной зарплатой и без указания, каких бы то ни было параметров. Как было упомянуто ранее создать просто-сотрудника невозможно. Любой сотрудник обязательно является Workerом, Bossом ,BigBossом или (еще каким-то добавленным вами по желанию классом)

BigBoss всегда имеет имя:) Потому для данонго класса должно быть реализовано лишь два конструктора : с заданием только имени, и с заданием имени и зарплаты.

Зарплата по умолчанию отличается у работника, Босса и БигБосса (например 500, 1000

и 1500) Переменные должны иметь минимально необходимую видимость ( protected , private) при необходимости должны быть реализованны функции для доступа к ним.

Программа должна быть написана в 'хорошем' стиле (насколько это непротиворечит вынужденно 'плохому' дизайну), хорошо задокументирована.

Реализация приложения JobTerrarium

Для начала построим структуру классов. Реализуем класс MapObject – баховый класс для всех сущностей нашей системы. Класс имеет всего лишь координаты и метод Show() для отображения сущности на поле.

using System;

using System.Collections.Generic;

using System.Text;

using System.Collections;

using System.Drawing;

namespace Task_2

{

class MapObject

{

protected Point position = new Point();

public Point Position

{

get { return position; }

set { position = value; }

}

public void Place(Point position)

{

this.position = position;

}

public void Show(Graphics g, int cellsize)

{

g.DrawString(this.ToString(), new Font("Arial", 7), new SolidBrush(Color.Red), new RectangleF(position.x * cellsize, position.y * cellsize, cellsize, cellsize));

}

}

}

Далее реализуем класс DynamicMapObject, который будет отвечать за объекты двигающиеся по полю. В этом классе описан только метод Move().

using System;

using System.Collections.Generic;

using System.Text;

namespace Task_2

{

class DymanicMapObject : MapObject

{

public void Move()

{

int dx = Engine.random.Next(-1, 2);

int dy = Engine.random.Next(-1, 2);

if (position.x + dx >= 0 && position.x + dx < Engine.maxPoint.x)

position.x += dx;

if (position.y + dy >= 0 && position.y + dy < Engine.maxPoint.y)

position.y += dy;

}

}

}

Затем реализуем класс Employee, который будет уже базовым для сущностей босс, рабочий и бигбосс. Данный класс уже имеет такие свойства как Salary и Name, а такэе метод Talk() и Say().

using System;

using System.Collections.Generic;

using System.Text;

using Task_2Winforms;

namespace Task_2

{

abstract class Employee : DymanicMapObject

{

public Employee(string name, decimal salary)

{

this.Name = name;

this.salary = salary;

this.mood = true;

Place(new Point(Engine.random.Next(Engine.maxPoint.x), Engine.random.Next(Engine.maxPoint.y)));

}

public Employee() : this("Noname", 0)

{

}

public Employee(decimal salary) : this("Noname", salary)

{

}

private decimal salary;

public decimal Salary

{

get { return salary; }

set { salary = value; }

}

private string name;

public string Name

{

get { return name; }

set { name = value; }

}

private bool mood;

private Point position;

public void Say(string WhatToSay)

{

TextBuffer.WriteLine(this.ToString() + " " + name + " say: \"" + WhatToSay+ "\"");

}

public void Talk(Employee employee)

{

if (employee is Worker)

Say(SayToWorker());

else

{

if (employee is BigBoss)

Say(SayToBigBoss());

else if (employee is Boss)

Say(SayToBoss());

}

}

public virtual string SayToBoss()

{

return "Hello!";

}

public virtual string SayToBigBoss()

{

return "Good morning!";

}

public virtual string SayToWorker()

{

return "Hi!";

}

public override string ToString()

{

return "Employee";

}

}

}

Класс Boss наследуется от Employee и переопределяет методы, с помощью которых разговаривает Employee.

using System;

using System.Collections.Generic;

using System.Text;

namespace Task_2

{

class Boss : Employee

{

public Boss():base("Noname",1000)

{

}

public Boss(decimal salary): base("Noname",salary)

{

}

public Boss(string name, decimal salary)

: base(name, salary)

{

}

public override string SayToWorker()

{

return "Привет, почему не работаешь?";

}

public override string SayToBoss()

{

return "Привет!";

}

public override string SayToBigBoss()

{

return "Здравствуйте!";

}

public override string ToString()

{

return "Boss";

}

}

}

Класс BigBoss наследуется от Boss и переопределяет методы, с помощью которых разговаривает Boss.

using System;

using System.Collections.Generic;

using System.Text;

namespace Task_2

{

class BigBoss : Boss

{

public BigBoss(string name):base(name,1500)

{

}

public BigBoss(string name, decimal salary):base(name,salary)

{

}

public override string SayToWorker()

{

return "Сколько можно бездельничать?";

}

public override string SayToBoss()

{

return "Как работа?";

}

public override string SayToBigBoss()

{

return "Здравствуй.";

}

public override string ToString()

{

return "BigBoss";

}

}

}

Класс Worker наследуется от Employee и переопределяет методы, с помощью которых разговаривает Employee.

using System;

using System.Collections.Generic;

using System.Text;

namespace Task_2

{

class Worker : Employee

{

public Worker():base("Noname",500)

{

}

public Worker(decimal salary): base("Noname",salary)

{

}

public Worker(string name, decimal salary): base(name, salary)

{

}

public override string SayToWorker()

{

return "Привет, как дела?";

}

public override string SayToBoss()

{

return "Здравствуйте, отлично выглядите сегодня.!";

}

public override string SayToBigBoss()

{

return "Здравствуйте!";

}

public override string ToString()

{

return "Worker";

}

}

}

Опишем класс Work, т.к. работа это недвижимый объект на карте, то отнаследуем его от класса MapObject.

using System;

using System.Collections.Generic;

using System.Text;

namespace Task_2

{

class Work:MapObject

{

public override string ToString()

{

return "Work";

}

}

}

В классе SalaryAddition опишем метод AddSalary с помощью которого будет начисляться зарплата и свойство amount – сколько начислять.

using System;

using System.Collections.Generic;

using System.Text;

namespace Task_2

{

class SalaryAddition : MapObject

{

public SalaryAddition(int amount)

{

this.amount = amount;

}

public SalaryAddition():this(100)

{

}

private decimal amount;

public override string ToString()

{

return amount.ToString()+"$";

}

public void AddSalary(Employee emplyoee)

{

emplyoee.Salary += amount;

}

}

}

Теперь все сущности описаны, и мы можем перейти непосредственно к программированию самого движка системы. В конструкторе мы создадим и разместим случайным образом все объекты нашей системы. В методе Run() мы будем перемещать объекты а методк Show() отображать их на экране.

using System;

using System.Collections.Generic;

using System.Text;

using System.Collections;

using System.Drawing;

namespace Task_2

{

class Engine

{

public readonly static Point maxPoint = new Point(10, 10);

public readonly static Random random = new Random();

ArrayList employee;

ArrayList staticObject;

public Engine()

{

employee = new ArrayList();

employee.Add(new Worker(500));

employee.Add(new Worker(500));

employee.Add(new Worker(500));

employee.Add(new Worker(500));

employee.Add(new Worker(500));

employee.Add(new Boss(1000));

employee.Add(new Boss(1000));

employee.Add(new BigBoss("Dr. Evil", 10000));

staticObject = new ArrayList();

staticObject.Add(new Work());

staticObject.Add(new Work());

staticObject.Add(new Work());

staticObject.Add(new Work());

staticObject.Add(new SalaryAddition(200));

staticObject.Add(new SalaryAddition());

staticObject.Add(new SalaryAddition(500));

staticObject.Add(new SalaryAddition());

foreach (Employee emp in employee)

emp.Place(new Point(Engine.random.Next(Engine.maxPoint.x), Engine.random.Next(Engine.maxPoint.y)));

foreach (MapObject wrk in staticObject)

wrk.Place(new Point(Engine.random.Next(Engine.maxPoint.x), Engine.random.Next(Engine.maxPoint.y)));

}

public void Run()

{

foreach (Employee emp in employee)

emp.Move();

for (int i = 0; i < employee.Count; i++)

for (int j = 0; j < employee.Count; j++)

if (i != j && ((Employee)employee[i]).Position == ((Employee)employee[j]).Position)

{

((Employee)employee[i]).Talk((Employee)employee[j]);

}

for (int i = 0; i < employee.Count; i++)

for (int j = 0; j < staticObject.Count; j++)

if (((Employee)employee[i]).Position == ((MapObject)staticObject[j]).Position)

{

(((MapObject)staticObject[j])).Place(new Point(Engine.random.Next(Engine.maxPoint.x), Engine.random.Next(Engine.maxPoint.y)));

if (staticObject[j] is SalaryAddition)

((SalaryAddition)staticObject[j]).AddSalary((Employee)employee[i]);

}

}

public void Show(Graphics g, int cellSize)

{

foreach (MapObject emp in employee)

emp.Show(g, cellSize);

foreach (MapObject wrk in staticObject)

wrk.Show(g, cellSize);

}

}

}

А это уже непосредственно методы формы, при клике на кнопку StartSimulation вся наша система запускается и начинает работу, пока не кончится рабочий день.

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using Task_2;

namespace Task_2Winforms

{

public partial class JobTerrariumForm : Form

{

private const double workdayTime=15;

private int cellHeight;

private int cellWidth;

private System.Drawing.Pen redPen;

private Engine engine = new Engine();

private Graphics graph;

public JobTerrariumForm()

{

InitializeComponent();

cellHeight=pB_JobTerrarium.Height/Engine.maxPoint.y;

cellWidth=pB_JobTerrarium.Width/Engine.maxPoint.x;

pB_JobTerrarium.Image = new Bitmap(pB_JobTerrarium.Width, pB_JobTerrarium.Height);

graph = Graphics.FromImage(pB_JobTerrarium.Image);

}

private void bt_StartSimulation_Click(object sender, EventArgs e)

{

DateTime time = DateTime.Now.AddSeconds(workdayTime);

do

{

graph.Clear(Color.White);

redPen = new Pen(Color.Red, 2);

int x = 0;

int y = 0;

for (int i = 0; i <= 10 - 1; i++)

{

x += cellWidth;

y += cellHeight;

graph.DrawLine(redPen, x, 0, x, this.Width);

graph.DrawLine(redPen, 0, y, this.Height, y);

}

TextBuffer.Clear();

engine.Run();

rTB_WhatToSay.AppendText(TextBuffer.ReadText());

rTB_WhatToSay.Update();

engine.Show(graph, cellWidth);

pB_JobTerrarium.Refresh();

pB_JobTerrarium.Update();

System.Threading.Thread.Sleep(1000);

} while (DateTime.Now < time);

}

}

}

В следующей статья, я чуть усложню задание, и решим его уже с помощью интерфейсов.

Комментариев нет:

Отправить комментарий