Civilization и Total War

 

Имя
Пароль  
Забыли пароль?
Регистрация


CIVru.com / "Совет по концепции" - "Мозговой штурм" / Реализация минимальной модели Ктулху
Версия для печати . Вверх
Автор Сообщение
Ктулху
Участник


Репутация: 889(???)
# Дата: 5 Фев 2010 00:40:03 Поправил: Ктулху Цитата

я долго тужился, пердел, и наконец высрал! ура товарищи!

эээ нахрена смайлы в теге
?

общие примечания. я большой поклонник указателей, так что, не оббесудьте. язык С++. весь код компилировался и работал в Visual Studio 8 Express Edition (WinXP SP3)

хедеры и прочий "мусор"
[code]
#include <iostream>
#include <list>
#include <map>

using namespace std;


enum action_type { MOVE, ATTACK, DEFEND, FORTIFY };

class _item;
class _unit;


класс потребителя
примечания:
методы consume() отвечают за собственно потребление
методы attach()/detach() отвечают за назначение места, откуда будет потребитель брать потребляемый предмет для потребления Широкая улыбка
class _consumer {
protected:
_consumer(_unit *owner, _item *item, const unsigned int how_much)
{
m_source = item;
m_how_much = how_much;

attach(owner);
}

public:
void add_action_consume(action_type action, const _item *item, const unsigned int how_much);

const _item *source() const { return m_source; }
void set_source(_item *source) { m_source = source; }

unsigned int appetite() const { return m_how_much; }
void set_appetite(const unsigned int how_much) { m_how_much = how_much; }

void consume(unsigned int how_many = 1);
void consume(action_type action);

void shortage(const unsigned int) const { cout << "PANIC!! PANIC!!" << endl; }

void attach(_unit *owner) { m_owner = owner; }
void detach() { m_owner = NULL; }

private:
//_consumer();
_consumer(const _consumer & );
_consumer& operator=(const _consumer & );

_unit *m_owner;
_item *m_source;
unsigned int m_how_much;
};


самый важный метод в классе потребителя. как ни странно отвечает за потребление
void _consumer::consume(unsigned int how_many)
{
if(!m_owner || !m_source || !m_how_much) {
if(m_source && m_how_much)
shortage(m_how_much);

return;
}

unsigned int remainder = m_owner->remove(m_source, how_many * m_how_much);

if(remainder)
shortage(remainder);
}


класс как бэ абстрактого "предмета"
наследует класс потребителя, но не все предметы обязаны что-либо потреблять.
кроме поля "вес", ничего не содержит. класс предмет как бэ базовый.
class _item: public _consumer {
public:
_item(_item *item, const unsigned int how_much, const unsigned int weight): _consumer(NULL, item, how_much)
{
/* cannot consume itself */
if(this == item) {
set_source(NULL);
set_appetite(0);
}

m_weight = weight;
}

_item(const unsigned int weight): _consumer(NULL, NULL, 0) { m_weight = weight; }

unsigned int weight() const { return m_weight; }

private:
//_item();
_item(const _item & );
_item &operator=(const _item & );

unsigned int m_weight;
};


класс транспорта
наследует предмет. может потреблять, как воздержатся. появились 2 поля: "ёмкость" и "скорость". их смысловая нагрузка должна быть ясна.
примечание: метод move() внутри себя должен вызывать (в начале, если скорость зависит от кол-ва потребляемого предмета, ежели нет, то имхо в конце) метод consume(MOVE). имеено MOVE ибо это потребление чего-либо при движении.
class _transport: public _item {
public:
_transport(_item *item, unsigned int how_much, unsigned int speed, unsigned int capacity, unsigned int weight)
:_item(item, how_much, weight)
{
m_capacity = capacity;
m_speed = speed;
}

unsigned int capacity() const { return m_capacity; }
unsigned int speed() const { return m_speed; }

void move()
{
/*
bla bla
*/

consume(MOVE);
}

private:
//_transport();
_transport(const _transport & );
_transport &operator=(const _transport & );

/* const */ unsigned int m_capacity;
/* const */ unsigned int m_speed;
};


класс юнита. он же контейнер-перевозчик
реализует методы добавления и изъятия добра из контейнера. метод add() возвращает кол-во предметов, которые не удолось запихнуть в юнит(контейнер). метод remove() возвращает кол-во предметов, которые не удалось взять из контейнера. метод tick() как бэ реализует изменение объекта во времени. поле <m_carrier> - это транспорт, который на данный момент использует юнит.
class _unit {
public:
_unit(_transport *trans)
{
m_carrier = trans;
m_remaining_capacity = m_carrier->capacity();

m_carrier->attach(this);
}

bool empty() const { return m_carry.empty(); }

unsigned int add(_item *item, unsigned int quantity)
{
map<_item *, unsigned int>::iterator it;
unsigned int remainder = 0;
unsigned int weight = quantity * item->weight();

/* we are full */
if(m_remaining_capacity < item->weight())
return quantity;

/* not all of <quantity> of <item> will fit */
if(m_remaining_capacity < weight) {
/* how many <item> will not fit */
remainder = quantity - m_remaining_capacity / item->weight();
/* how much will fit */
weight = m_remaining_capacity;

quantity -= remainder;
}

it = m_carry.find(item);
if(it != m_carry.end())
it->second += quantity;
else if(quantity) {
m_carry[item] = quantity;
item->attach(this);
}

m_remaining_capacity -= weight;

return remainder;
}

unsigned int remove(_item *item, const unsigned int quantity)
{
map<_item *, unsigned int>::iterator it;
unsigned int remainder = 0;

it = m_carry.find(item);

if(it != m_carry.end()) {
const unsigned int weight = item->weight();

if(it->second <= quantity) {
remainder = quantity - it->second;
item->detach();
m_carry.erase(it);
} else if(it->second > quantity)
it->second -= quantity;

m_remaining_capacity += (quantity - remainder) * weight;
} else
remainder = quantity;

return remainder;
}

void move() { m_carrier->move(); }

unsigned int quantity(_item *item) const
{
map<_item *, unsigned int>::const_iterator it;
it = m_carry.find(item);

return it != m_carry.end()? it->second: 0;
}

void tick()
{
/* bla bla */

m_carrier->consume();

map<_item *, unsigned int>::iterator it;
for(it = m_carry.begin(); it != m_carry.end(); ++it)
it->first->consume(it->second);
}

private:
//_unit();
_unit(const _unit & );
_unit &operator=(const _unit & );

/* <m_carrier> carries <m_carry> things */
_transport *m_carrier;
map<_item *, unsigned int> m_carry;

unsigned int m_remaining_capacity;
};


начало всего
как видно, я создал "предмет" еда(весит 1 ед.). он ничего не потребляет. далее идет создание "предмета" щит(весит 3 ед.). щит потребляет еду. создаю "транспорт" лощади, которые так же потребляют еду. их ёмкость 50 единиц. потом создаю "юнит" юнит с транспортом - лощади. добавляю в юнит(контейнер) еду и немного щита(куда же без него!?) и запускаю тики пока кол-во еды в контейнере не обнулится
int main(int, char *[])
{
_item food(1);
_item shit(&food, 1, 3);

_transport horse(&food, 10, 3, 50, 7);

_unit unit(&horse);

unit.add(&food, 30);
unit.add(&shit, 10);

unsigned int tick;
for(tick = 0; unit.quantity(&food); ++tick)
unit.tick();

cout << "ticks elapsed before shortage: " << tick << endl;

getchar();
return 0;
}


Deimon
Кибердемон



Репутация: 1287(600)
# Дата: 5 Фев 2010 17:01:47 Цитата

Перенес для разгрузки основной темы

Aku_Aku
Участник



Репутация: 637(???)
# Дата: 8 Фев 2010 10:48:28 Цитата

Выложу-ка и я свое. Раз уж такая жара.

Будучи адептом ТДД, кодить начинаю с тестов (когда получается)

#! /usr/bin/env python

import unittest
import Squadron

from Squadron import *

class TestSquadronFunctions(unittest.TestCase):

def setUp(self):
self.seq = range(10)

def testSquadronStrike(self):
S1 = Squadron()
S1.amountatfirst = S1.amount = 1000
S1.injury = 1
S1.initiative = 1
S1.survive = 2
S1.order = 10

S2 = Squadron()
S2.initiate("Squadron", 1000, 1, 1, 2, 10)

afterbattle = 1000 - ( (S1.amount/10) * S1.injury )/S2.survive
S1.strike(S2)

self.assertTrue( S2.amount == afterbattle )
self.assertTrue( S1.amount == afterbattle )
#end testSquadronStrike

def testSquadronShot(self):
S1 = Squadron()
S1.initiate("Squadron", 200, 3, 1, 1, 2)
S2 = Squadron()
S2.initiate("Squadron", 1000, 1, 1, 10, 2)

afterbattle = 1000 - ((S1.amount/2) * S1.injury)/S2.survive
S1.shot(S2)

self.assertTrue( S2.amount == afterbattle )
self.assertTrue( S1.amount == S1.amountatfirst )
#end testSquadronShot

def testSquadronDefeated(self):
S1 = Squadron()
S1.initiate("Squadron", 1000, 1, 1, 1, 2)
S2 = Squadron()
S2.initiate("Squadron", 1400, 1, 1, 1, 2)

S1.strike(S2)

self.assertTrue( not S2.isDefeated() )
self.assertTrue( S1.isDefeated() )
#end testSquadronDefeated

def testSquadronInitiative(self):
S1 = Squadron()
S1.initiate("Squadron", 1000, 1, 2, 1, 10)
S2 = Squadron()
S2.initiate("Squadron", 1000, 1, 1, 1, 10)

S1.attack(S2)

self.assertTrue( S2.amount == 481 )
self.assertTrue( S1.amount == 643 )
#end testSquadronInitiative


if __name__ == '__main__':
unittest.main()



А вот собственно реализация того, что выше покрыто тестами.

class Squadron:

def _init__(self):
self.initiate("Squadron", 1, 1, 1, 1, 1)

def isDefeated(self):
if (self.amount < (self.amountatfirst / 2)):
return 1
return 0

def initiate(self,N,a,b,c,d,e):
self.name = N
self.amountatfirst = a
self.amount = a
self.injury = b
self.initiative = c
self.survive = d
self.order = e

def assign(self,S):
self.name = S.name
self.amountatfirst = S.amountatfirst
self.amount = S.amount
self.injury = S.injury
self.initiative = S.initiative
self.survive = S.survive
self.order = S.order

def shot(self, S):
if not self.isDefeated():
first_line = self.amount/self.order
possible_damage = first_line * self.injury
actual_damage = possible_damage/S.survive
#without backfire
S.amount = S.amount - actual_damage

def backfire(self, S):
self.shot(S)

def strike(self, S):
if not self.isDefeated():
first_line = self.amount/self.order
possible_damage = first_line * self.injury
actual_damage = possible_damage/S.survive
S.backfire(self)
S.amount = S.amount - actual_damage

def attack(self, S):
i = 0
if (self.initiative > S.initiative):
i = self.initiative - S.initiative
while i>0:
i = i - 1
if (self.isDefeated() or S.isDefeated()):
break
self.shot(S)

i = 0
if (S.initiative > self.initiative):
i = S.initiative - self.initiative
while i>0:
i = i - 1
if (self.isDefeated() or S.isDefeated()):
break
S.shot(self)

while (not( self.isDefeated() or S.isDefeated() )):
self.strike(S)

def text(self):
t = "active"
if self.isDefeated():
t = "defeated"
return (self.name + "\nAmount: " + repr(self.amount) + "\n" + t + "\nInjury: " + repr(self.injury) + "\nInitiative: " + repr(self.initiative) + "\nSurvive: " + repr(self.survive) + "\nOrder: " + repr(self.order))


Ктулху
Участник


Репутация: 889(???)
# Дата: 8 Фев 2010 13:34:04 Поправил: Ктулху Цитата

если инициативы <self> и <S> равны, то вообще никто никого не атакует. так задумано?
еще момент: прога или поток может в повиснуть в цикле <while(): strike()> если метод <strike()> если по каким-либо причинам будет наносить нулевой урон защитнику

а "i = i - 1" это вообше мегажесть! разве нет операции инкримента!?

заметь, что ты постоянно используешь "not isDefeated()". имхо тада лучше переписать "isDefeated()" в "isFighting()" или "isAlive()"
def isAlive(self):

if (self.amount >= self.amount_at_first / 2):

return 1

return 0


так же код
self.isDefeated() or S.isDefeated()

превращается в
self.isAlive() and S.isAlive()


если допустить, что есть в питоне операции декримента и инткремента, то цикл while в <attack()> можно переписать в виде
while i-- and self.isAlive() and S.isAlive():
self.shot(S)


имхо лучше всегда прерывания цикла вытаскивать в условие цикла. таким образом яснее будет по каким причинам цикл может прерватся. т.е. будет ясна логика цикла.
с {break}ами логика цикла может быть неочевидной если цикл большой.

далее, как я представляю себе условия
S.initiative < self.initiative

и
S.initiative > self.initiative

взамоисключающие?

если так то мона ввести новую переменную(!!) и переписать <attack()> следующим образом
def attack(self, S):

i = 0

self_has_more_init = self.initiative >= S.initiative

if (self_has_more_init):
i = self.initiative - S.initiative
else:
i = S.initiative - self.initiative

while i-- and self.isAlive() and S.isAlive():

if(self_has_more_init):
self.shot(S)
else:
S.shot(self)


while (self.isAlive() and S.isAlive()):
if(self_has_more_init):
self.strike(S)
else:
S.strike(self);

по поводу равенства инициативы: сейчас при равенстве инициативу получает атакующий

нет предела совершенству!
мона <attack()> написать еще вот так:
def attack(self, S):

self_has_more_init = self.initiative >= S.initiative
init = abs(self.initiative - S.initiative)

attacker = self_has_more_init? self: S
defender = self_has_more_init? S: self

while init-- and self.isAlive() and S.isAlive():
attacker.shot(defender)

while (self.isAlive() and S.isAlive()):
attacker.strike(defender)

где <abs()> это модуль цельночисленного аргумента
а тернарный оператор это <условие?(операторы для выполнения если условие истинно): (операторы для выполнения если условие ложно)>

Aku_Aku
Участник



Репутация: 637(???)
# Дата: 8 Фев 2010 14:00:25 Цитата

Ктулху:если инициативы <self> и <S> равны, то вообще никто никого не атакует. так задумано?

Нет конечно.
Это просто означает что ни у кого нет приимущества первой атаки.
То есть это как стенка на стенку в рукопашную -- как сошлись, так и начали друг друга мотузить.
Если же у кого-то есть оружие, тем более стреляющее, то могут выстрелить несколько раз пока другие подходят.


Ктулху:а "i = i - 1" это вообше мегажесть! разве нет операции инкримента!?

В Питоне Улыбка нет.


Ктулху:заметь, что ты постоянно используешь "not isDefeated()". имхо тада лучше переписать "isDefeated()" в "isFighting()" или "isAlive()"

Возможно.
Просто это защелка показывающая когда отряд уже не может сражатся и по сути неуправляем, бежит с поля боя.
Обратная запись не столь мнемонична.


Ктулху:по поводу равенства инициативы: сейчас при равенстве инициативу получает атакующий

Нет. Урон наносится одновременно. Это принципиальный момент.

Ктулху
Участник


Репутация: 889(???)
# Дата: 8 Фев 2010 15:49:29 Поправил: Ктулху Цитата

Aku_Aku:Возможно.
Просто это защелка показывающая когда отряд уже не может сражатся и по сути неуправляем, бежит с поля боя.
Обратная запись не столь мнемонична.

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

например:
код метода <strike()> почти полностью дублирует код метода <shot()>. может так и надо?
но опять же почему не устранить дублирование кода переписав <strike()>?
def strike(self, S):

if not self.isDefeated():

self.shot(S)

S.backfire(self)

S.amount = S.amount - actual_damage



Aku_Aku
Участник



Репутация: 637(???)
# Дата: 8 Фев 2010 16:10:55 Цитата

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

Какие проблемы? Улыбка
код выложен на опенсорсном репозитарии -- любой может брать и изменять как хочется.

На каноничности я же совершенно не настаиваю. Код писался не для того чтобы быть убер-алес, а просто чтобы отпрототипировать подход. Все равно потом на С++ переписывать. Подмигивание

Ктулху:код метода <strike()> почти полностью дублирует код метода <shot()>. может так и надо?
но опять же почему не устранить дублирование кода переписав <strike()>?


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

Но кроме того, есть там и существенная необходимость.

А именноКтулху:S.backfire(self)

нужно вызывать именно там, для того чтобы использовалось правильное количество солдат противника
так как в твоем случае это количество изменится после Ктулху:self.shot(S)

Вообще, я о таких моментах особо не задумывался -- следил только за соответствием тестам. Главное чтобы тест сработал.

Ктулху
Участник


Репутация: 889(???)
# Дата: 8 Фев 2010 16:55:51 Цитата

Aku_Aku:нужно вызывать именно там, для того чтобы использовалось правильное количество солдат противника
так как в твоем случае это количество изменится после Ктулху:self.shot(S)

да, может быть. не суть. имхо важно то, что надо избегать дубликации кода(логики).

Aku_Aku
Участник



Репутация: 637(???)
# Дата: 8 Фев 2010 17:02:49 Цитата

Ктулху:имхо важно то, что надо избегать дубликации кода(логики).

Да, конечно.

Aku_Aku
Участник



Репутация: 637(???)
# Дата: 8 Фев 2010 17:10:18 Поправил: Aku_Aku Цитата

Ктулху:имхо важно то, что надо избегать дубликации кода(логики).

Да, конечно.
Ктулху:нет предела совершенству!
мона <attack()> написать еще вот так:
def attack(self, S):


Или вообще вот так Подмигивание

   def attack(self, S):
if self.initiative < S.initiative:
S.attack(self)
else:
...


я временные локальные переменные, да еще с длинными "говорящими" именами... как-то не уважаю.

Ктулху
Участник


Репутация: 889(???)
# Дата: 8 Фев 2010 18:45:42 Цитата

Aku_Aku:я временные локальные переменные, да еще с длинными "говорящими" именами... как-то не уважаю.
и зря. это называется самодокументированность кода.
потом, хитрый компилятор все-равно развернет временные переменные во время оптимизации.

Aku_Aku:Вообще, я о таких моментах особо не задумывался -- следил только за соответствием тестам. Главное чтобы тест сработал.
работоспособность - необходимое условие для "хорошего" кода. еще есть одно достаточное условие

Aku_Aku
Участник



Репутация: 637(???)
# Дата: 8 Фев 2010 19:02:25 Цитата

Ктулху:и зря. это называется самодокументированность кода.

не уважаю я как-то самодокументированность Подмигивание

Версия для печати . Вверх

ОСТАВЛЯТЬ СООБЩЕНИЯ МОГУТ ТОЛЬКО ЗАРЕГИСТРИРОВАННЫЕ ПОЛЬЗОВАТЕЛИ!

Администрация форума: editors@civru.com
Rambler's Top100
XML [?]