Возможно, вы уже слышали про игру для настоящих программистов 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); } } } |
В следующей статья, я чуть усложню задание, и решим его уже с помощью интерфейсов.
Комментариев нет:
Отправить комментарий