Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Учебный проект № 2

87
28 сентября 2009 года
Kogrom
2.7K / / 02.02.2008
Учебный проект № 2

Цели проекта:

1. Освоить грамотное использование ООП в проектах среднего размера: применение ООД, паттернов, UML.

2. Изучение рефакторинга и Unit-тестирования.

3. Изучение различных технологий создания ПО: языков, библиотек и т.д.

4. Освоение работы в команде удаленных разработчиков. Разработка методики на основе чего-то вроде Экстремального Программирования (XP).


Предполагаемые условия и средства разработки:

1. Язык разработки – Python. Язык лаконичный, гибкий, в комплект входят "батарейки", то есть, например, не надо искать модуль для Unit-тестирования – он уже включен. Есть сборщик мусора, что упрощает проектирование диаграмм классов, компонент и т.д. Отсутствие этапа компиляции делает более удобным метод создания программы маленькими шажками.

2. Операционная система – обсуждается. Кроссплатформенность предпочтительна.

3. GUI - wxPython. Обсуждается.

4. IDE: обсуждается. Хотя, я остановился пока на редакторе UliPad.

5. Репозиторий: SVN.

7. Сервер для репозитория – code.google.com. Обсуждается.

8. Средства общения: IRC, ICQ, форум.

Идея проекта.

Есть 3 идеи, которые мне могут быть интересны: редактор простейшей анимации, игра, программа регистрации данных. Опишу позже, если найдутся желающие принять участие. Главное, чтобы проект был менее абстрактным, чем предыдущий.

Участники:

1. Организатор. Пока я. Обязанности: освещать ход развития проекта, привлекать людей.

2. Разработчики: студенты, программисты-самоучки для которых и затевается этот проект. Обязанности: проектировать программу, писать код. Требования: уметь читать и понимать прочитанное, проявлять активность.

3. Кураторы: сами разработчики. Обсуждается.

4. Руководитель: нет. Обсуждается.

5. Возможно, художник. Художником могу быть я.


Приблизительная методика разработки:

1. Создается эскиз ТЗ, обсуждается.
2. Создаются эскизы диаграмм компонент, классов. Обсуждаются.
3. Каждый участник получает для разработки один (несколько) компонент из выбранной диаграммы.
4. Каждый участник становится куратором для другого. Он должен иметь доступ к коду курируемого, следить, чтобы тот "не лез в болото".
5. Код каждого участника выкладывается на всеобщее обозрение не реже, чем раз в неделю.
6. Если кто-то не успевает написать код, требующийся для других участников, то его код передается другому участнику в обмен на менее востребованный код.


Выгода:

1. Для разработчиков – опыт, новые умения.
2. Для меня - тоже. Бонус – шаг к созданию новых разделов :)

Жду критики, предложений, желающих присоединиться к проекту.
Страницы:
6
01 октября 2009 года
George
4.1K / / 05.01.2007
а зачем?
29K
01 октября 2009 года
Ander Skirnir
109 / / 08.06.2009
Ну в нём как-раз нет проблем с библиотеками, гуи и таким прочим - там всё на блюдечке в одном .net framework'е.
6
01 октября 2009 года
George
4.1K / / 05.01.2007
ага. и чего тогда там изучать? это ж уже специфика .net, по моему в данном случае акцент делается на немного другое.
29K
01 октября 2009 года
Ander Skirnir
109 / / 08.06.2009
Дак это обычный питон, с биндингами под удобные универсальные библиотечки дотнэта, с которыми наверняка многие работали - не надо искать стороннюю гуишную библиотеку и потом разбираться, как ей пользоваться.
87
01 октября 2009 года
Kogrom
2.7K / / 02.02.2008
Цитата: Ander Skirnir
А может IronPython?



Я против. По синтаксису IronPython стремится к CPython 2.6, но не на 100% соответствует стандарту. Тут мы ничего не выигрываем. Зато тут появляется зависимость от .Net, или Mono. Мне она не нужна.

Плюсов не вижу.

К тому же в стандартном Питоне тоже много чего по умолчанию идет. Даже GUI-шная библиотека. Я говорил про дополнительные библиотеки.

Однако, я еще раз прошу временно забыть про язык - не говорить о нем в этой теме пока.

6
01 октября 2009 года
George
4.1K / / 05.01.2007
Цитата: Ander Skirnir
Дак это обычный питон, с биндингами под удобные универсальные библиотечки дотнэта, с которыми наверняка многие работали - не надо искать стороннюю гуишную библиотеку и потом разбираться, как ей пользоваться.


вроде в стандартном питоне для этого есть модуль Tk. А к тому же одной из целей данного проекта является изучение wxWidgets - собственно использование IronPython эту цель "затирает". =)

29K
01 октября 2009 года
Ander Skirnir
109 / / 08.06.2009
Ну я не знал насчёт этого - во всех предыдущих разделах этого топика ни слова о преследовании такой цели :)

Кстати, проект вцелом ставит обьективные цели, но у каждого участника имеются и субьективные. Интересно узнать ожидания. Для меня, например, наиболее приоритетными являются - потренироваться быстро обучаться новым (для меня) методам решения задач, подучить паттерны, поработать в комманде, научиться использовать разные подходы/парадигмы в рамках одной основной без ущерба для архитектуры проэкта.
87
01 октября 2009 года
Kogrom
2.7K / / 02.02.2008
Цитата: Ander Skirnir
Ну я не знал насчёт этого - во всех предыдущих разделах этого топика ни слова о преследовании такой цели


Смотри Первое сообщение. Аж список целей.

87
03 октября 2009 года
Kogrom
2.7K / / 02.02.2008
Как я и обещал, составил некоторые правила разработки, поглядывая на унифицированный процесс (UP).

1. Разработка ведется небольшими итерациями, каждая из которых составляет 2 недели.

2. Перечень составляющих одной итерации:
2.1. Составление (корректировка) требований — 2 дня.
2.2. Объектно ориентированный анализ и составление UML-диаграмм компонентов — 2 дня.
2.3. Реализация: написание Unit-тестов и кода — 6 дней.
2.4. Заморозка разработки.
2.5. Тестирование, правка кода по результатам тестирования — 3 дня.
2.6. Переход к следующей итерации.

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


Расшифровка составляющих одной итерации:

1. Составляем и дополняем ТЗ, корректируем правила стиля. Каждый желающий должен успеть предложить свои поправки. Руководитель компилирует все предложения замечания. Далее работа ведется в соответствии с полученными требованиями.

2. Каждый желающий предлагает новые компоненты, изменения в старых, новые UML-диаграммы. Руководитель компилирует диаграммы, распределяет компоненты между участниками, учитывая их пожелания.

3. Каждый участник получает свой компонент для реализации и компонент для курирования. Участник разбивает свой компонент на классы (объекты) графически или в текстовом виде, советуясь с куратором. Затем составляет Unit-тесты для предполагаемых модулей, классов. После этого пишет код. Куратор следит, чтобы программист не "лез в болото". Конечно, возможен отход от приведенной технологии, если программист не желает изучать TDD и UML, но не рекомендуется.

4. Код должен быть заморожен и выложен для всеобщего обозрения на 10-11 день разработки (или раньше). Если компонент не готов не в каком рабочем виде, то он должен быть передан другим участникам в замен более простого, или разделен на более мелкие составляющие, часть которых тоже передастся другим участникам.

5. Выполняются тесты, выявляющие ошибки, которые не выявили с помощью Unit-тестов. Каждый участник тестирует по возможности чужой код.

Роли:

Руководитель — участник, ответственный за компиляцию требований и общих диаграмм, а также ответственный за распределение компонентов. Руководитель может меняться. Руководитель должен постоянным участником проекта.

Программист — рядовой участник проекта. Участник должен быть подключен к проекту хотя бы в течении одной итерации. В идеале каждый участник должен выполнять роль программиста.

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

Каждый участник должен обмениваться с другими с помощью системы мгновенных сообщений: IRC, ICQ, Jabber. Участники должны показывать свой код кураторам с помощью пастебинов (paste.org.ru и т.п.) и форума.
87
05 октября 2009 года
Kogrom
2.7K / / 02.02.2008
Итак, я начинаю безумную пляску.
Первая итерация будет испытательной (для проверки правил), со сроком до 18.10.2009.

Инструменты: Python 2.6.2, wxPython2.8-win32-unicode-2.8.9.2-py26.
Использую UliPad. OC Windows XP.

Правила довольно гибкие - каждый участник может присоединиться к проекту хоть на одну итерацию. Для учета создал социальную группу:

Учебный проект 2

Итерация начнется с обсуждения ТЗ. Обсуждение продлится до 7 октября. Если возражений не появится, то перейду к анализу и составлению диаграмм. И т.д.

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

Поправка: 2 октября этого года вышла версия 2.6.3 с багфиксами. Вероятно, буду ее использовать.
87
07 октября 2009 года
Kogrom
2.7K / / 02.02.2008
Ок. Поправок к ТЗ не поступило, поэтому приступаю к ОО анализу. Вначале в текстовом виде.

Объекты:

1. Робот (Robot). Выполняет одноходовые команды: двигаться вперед, повернуться вправо/влево, получить данные об объекте, находящемся впереди. Выполняет безходовые команды: получить свою координату на карте (?), получить некоторые данные о зоне в пределах 2-х ходов (см. ТЗ).

Робот хранит (ссылается на) информацию на число сделанных ходов и собранных минералов.

2. Карта или доска (Board). Доска содержит массив (словарь?) ссылок на объекты местности. Доска автоматически огораживается стеной при создании. То есть, возможно, Доска имеет доступ к инструменту создания объектов или сама им является.

Возможно, карта содержит робота.

3. Объекты местности (Decor). Бывают нескольких типов (см. ТЗ). Обладают характеристиками: минерал, ходозатратность, проходимость. Вероятно, не должны хранить свою координату, так как она содержится в доске.

4. Загрузчик сценария для бота.

5. Антивирус или античит. Этот объект должен проверить, что в сценарии нет вредительского, опасного, или жульнического кода.

6. Исполнитель сценария. Должен выполнять сценарий до выполнения некоторого условия, типа: все минералы собраны или прошло определенное количество ходов, или собрано определенное количество минералов.

7. Исполнитель команд в интерактивном режиме. Тут роботом управляет человек, а не сценарий. Необходимо для отладки.

8. Загрузчик карты. Предполагается, что карты будут храниться в отдельных файлах.

9. Вид карты. Вид показывает происходящее на карте в разных режимах. Нужен для отладки, ибо в конечном итоге можно просто загрузить сценарий для робота и на выходе получить информацию о прохождении карты.

10. Интерфейс (Вид) пользователя.

Далее попробую реализовать черновики диаграмм на основе этого анализа. Предполагается делать их до субботы (немного противоречит правилам, но для первой итерации это допустимо).
87
10 октября 2009 года
Kogrom
2.7K / / 02.02.2008
Сделал 3 диаграммы: диаграмму предметной области, и 2 диаграммы взаимодействия, которые посчитал наиболее удобными для реализации. От стандартов UML немного отошел (что допустимо в черновиках), но вроде понятно.

В диаграммах взаимодействия звездочкой помечал абстрактные команды и возвращаемые значения, которые после будут заменены конкретными (возможно, на стадии реализации).

В диаграмме предметной области не показал работу с самодельным скриптом (который хотел изобрести Ander Skirnir), но он почти повторяет работу с питоновским скриптом, исключая антивирус.
87
12 октября 2009 года
Kogrom
2.7K / / 02.02.2008
Всё воскресение читал книгу про TDD, но пока не ясно понял, как начинать от тестирования...

Решил посмотреть на предполагаемые объекты, с целью выяснения, какие из них нужны в первую очередь. Пока вижу следующую начальную очередность:
1. Разрабатываются объекты, создающие карту (для начала можно взять тестовую). Это позволит перейти к работе с роботом.
2. Разрабатываются объекты, необходимые для работы с роботом через оператора. То есть, без загрузки скрипта, а под управлением человека.

В общем, эти 2 процесса я прикинул на диаграммах сообщением раньше. Дальше можно копать вширь и вперед.

Для создания карты хорошо придумать формат карты. Этот вопрос я и буду обсуждать ближайшее время. Думаю, проще будет какой-нибудь человекопонимаемый формат, типа:

Код:
a = wall
b = mine(3)
c = mine(4)
d = pit(4)
e = nope
f = pit(10)

aabcccccccddd-
ffeeeeeaaa---a
-----aaa-----f
------>-------
-----aaa-----f
b----aaa-----f

То есть формат состоит из двух частей: описания объектов и самой карты.

Вместо формы
b = mine(3)
можно использовать
b mine 3
для простоты.

Значками >, <, A, V на карте обозначается начальное положение и направление робота.
54K
14 октября 2009 года
Zebrahead
1 / / 14.10.2009
альтернативой может быть вариант задания положения объектов, например :
wall 0 0 1 0 1 7 1 8 1 9 1 13 2 5 2 6 2 7 4 5 4 6 4 7 5 5 5 6 5 7
mine 0 2 5 0
и т.д. , тут набор координат "x y" на карте, все зависит от размера карты и количества элементов на ней, можно 1 цифрой задавать координату. Размер файла с картой значения не имеет, формат Kogroma читабельнее, предлагаю на нем остановиться.

направление на север можно обозначать ^.
87
15 октября 2009 года
Kogrom
2.7K / / 02.02.2008
Цитата: Zebrahead
направление на север можно обозначать ^.



Да, так лучше.

87
16 октября 2009 года
Kogrom
2.7K / / 02.02.2008
По собственным правилам я должен выложить код, который делал в течении итерации, чтобы другие участники могли видеть иду ли я в тупик или болото. Поэтому выкладываю код, хотя он пока не идеален.

Код выполняет загрузку тестовой карты-доски из строки.

Сам код (BoardLoader.py):
Код:
#!/usr/bin/env python
#coding=utf-8

TEST_BOARD = """
a = wall
b = mine(3)
c = mine(5)
d = pit(4)
e = pit(6)
.
d----a---a----c
---b---b---b---
---------------
a------>------a
----d----------
-----e-----e---
c----a---a----a
"""

class Decor:
    def __init__(self):
        self.impassable = False
        self.mineral = 0
        self.moveCosts = 0

class Wall(Decor):
    def __init__(self, passable = False):
        Decor.__init__(self)
        self.impassable = not bool(passable)

class Mine(Decor):
    def __init__(self, mineral = 1):
        Decor.__init__(self)
        self.mineral = int(mineral)
       
class Pit(Decor):
    def __init__(self, moveCosts = 1):
        Decor.__init__(self)
        self.moveCosts = int(moveCosts)

class DecorDict:

    def __init__(self):
        self.decors = {}
       
    def MakeDecor(self, data):
        decorTypes = {"wall": Wall, "mine": Mine, "pit": Pit}
        if len(data) > 2:
            tag, type, param = data[0], data[1], data[2]
            if type in decorTypes:
                self.decors[tag] = decorTypes[type](param)

class BoardLoader:
   
    def __init__(self, board):
        self.board = board
        self.decorDict = board.decorDict
       
   
    def GetParts(self, text, re_compile):
        result = re_compile.match(text)
        if result:
            return result.groups()

    def ParseDecorPart(self, decorPart):
        import re
       
        objects = decorPart.split('\n')
        re_compile = re.compile(r"""\s*([a-z])\s*=      # var
                                \s*([a-z]+)\s*          # object
                                (?:\(\s*(\d+)\s*\))?\s* # digit""", re.X)
        for s in objects:
            if s:
                parts = re_compile.match(s)
                if parts:
                    self.decorDict.MakeDecor(parts.groups())
   
    def ParseBoardPart(self, boardPart):
        lines = boardPart.split('\n')
        for s in lines:
            if s:
                self.board.SetLine(s)
   
    def MakeBoard(self, data):
        strings = data.split('.')
        if len(strings) > 1:
            self.ParseDecorPart(strings[0])
            self.ParseBoardPart(strings[1])
   
    def MakeTestBoard(self):
        self.MakeBoard(TEST_BOARD)
   
    def Load(self, type):
        self.MakeTestBoard()


class Board:
    decorDict = DecorDict()
   
    def __init__(self):
        self.table = []
   
    def SetLine(self, line):
        lineList = self.TestLine(line)
        if lineList:
            self.table.append(lineList)
           
    def TestLine(self, line):
        return [i for i in line]
       
    def Load(self, type):
        BoardLoader(self).Load(type)
       
if __name__ == '__main__':
    execfile("TestBoard.py")

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

1. Иерархия классов для объектов местности пока кривая, но сходу ничего лучше я не выдумал.

2. Пока не полностью сделаны проверки на правильность карты.

3. В "майне" запускается юниттест. Спорный прием, но иначе я бы ленился эти тесты запускать.

4. Названия модулей пока не очень удачные.

5. Пока все классы собраны в одном файле. Разделю потом, если будет смысл.

Модуль с юниттестами (TestBoard.py):
Код:
#!/usr/bin/env python
#coding=utf-8

import unittest
from BoardLoader import *

class TestBoardLoader (unittest.TestCase):

    def setUp(self):
        self.b = Board()
        self.b.Load("test")
       
    def testDictOne(self):
        self.assertTrue(self.b.decorDict.decors['a'].impassable == True)
       
    def testDictTwo(self):
        print self.b.decorDict.decors['e'].moveCosts
        self.assertTrue(self.b.decorDict.decors['e'].moveCosts == 6)
       
    def testBoardOne(self):
        self.assertTrue(self.b.table[0][0] == 'd')

    def testBoardTwo(self):
        self.assertTrue(self.b.table[-1][-1] == 'a')
       
class TestBoardLoader2 (unittest.TestCase):
         
    def testDictOne(self):
        b = BoardLoader(Board())
        b.MakeBoard("")
        self.assertFalse(b.board.table, b.board.table)    
   
       
if __name__ == '__main__':
   
    loadCase = unittest.defaultTestLoader.loadTestsFromTestCase        
    suite = loadCase(TestBoardLoader)
    suite.addTest(loadCase(TestBoardLoader2))
    unittest.TextTestRunner(verbosity = 2).run(suite)
    #unittest.main()
   
    #raw_input("press any key")
87
18 октября 2009 года
Kogrom
2.7K / / 02.02.2008
Первая итерация закончилась. Следующая будет длиться до 1 ноября.

Итоги

За эту итерацию были сделаны кое-какие эскизные диаграммы, в аське были обсуждены некоторые вопросы по требованиям к программе, был сделан код загрузки тестовой карты (и немного юниттестов к этому коду).

Планы

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

Кроме того, может быть интересным создание какого-нибудь компонента Вида для карты.

Предложения

Думаю, для начала следует сделать консольную версию программы, а потом уже приделать GUI. Заодно будет интересным посмотреть на феномен легкости переноса из консоли в GUI (шучу).

Для работы в консоли предлагаю использовать следующие команды:

board [param]
script [name]
drive

Подкоманды drive:
turn left (or right) # можно сокращенные варианты tl или tr
move
detect
square

Описание команд.

board – загружает или генерирует карту. Для загрузки карты из файла надо добавить load и имя файла. Если надо загрузить тестовую карту, то надо добавить test. Если надо сгенерировать карту, то надо добавить слово create и некоторые параметры (возможно, размеры). Параметры генерации пока не продуманы.

script – загружает скрипт. Для загрузки необходимо указать имя файла с расширением. Скрипты могут быть двух видов: написанные на усеченном Python (для безопасности), или на специальном языке. Расширение для питоновских скриптов — py или rpy. Для самодельного языка расширение придумает его автор.

drive – переводит робота в режим управления человеком. При этом человеку должны быть доступны все команды управления, которые доступны скриптам.

После получения данной команды интерфейс начинает анализировать только команды управления роботом, а именно:

turn left (or right) — поворачивает робота влево или вправо. При управлении человеком возможно управление командами tl и tr. При выполнении команды теряется один ход. На виде карты поворот должен быть как-то отображен, чтобы человек имел представление о положении робота.

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

detect — определяет тип объекта, который находится перед роботом. То есть эта команда возвращает пользователю свойства объектов того типа, что находится перед ним: ходозатратность, непроходимость, минерал. Команда забирает один ход. Если перед роботом нет никаких объектов, то команда не выполняется.
square — возвращает определенный квадратный кусок карты вокруг робота. Например, размером 7x7 или 9x9 (пока не ясно, как лучше). При выполнении этой команды затрачивается n ходов. Для начала, пусть n=3.

Для выхода из режима управления роботом должна быть предусмотрена команда quit (или можно сделать выход из режима с помощью повторной команды drive).

Распределение работ

Ander Skirnir делает компонент загрузки скриптов, антивирус и генератор карты.

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

Zebrahead должен выбрать себе какие-либо части программы для реализации. Ну и вообще, как-то рассказать, что он хочет и может делать, раз уж записался в группу :)

Распределение работ может быть пересмотрено по требованию участников.
87
21 октября 2009 года
Kogrom
2.7K / / 02.02.2008
Двигаюсь по плану. Два дня по плану должны были обсуждать изменения, теперь перехожу к диаграммам. В этот раз решил поиграться с диаграммами коммуникации (см. внизу сообщения).

На диаграмме введен новый объект HumanView вместо старого BoardView, так как Доску зритель не должен видеть, а робот не может быть зрителем (немного путано).

Кроме диаграммы решил создать "комикс" управления роботом из консоли:

Код:
drive
====================
a-b
->c
---
====================
move
====================
a-b-
-->-
----
==========
c = mine(3)
====================
turn
====================
a-b-
--V-
----
==========
c = mine(3)
====================
move
====================
a-b-
----
--V-
*--a
==========
c = mine(3)
====================
move
====================
a-b-
----
----
*-Va
*--b
==========
c = mine(3)
====================
_

Звёздочкой отмечены неизвестные зрителю клетки.
87
29 октября 2009 года
Kogrom
2.7K / / 02.02.2008
Итак, в соответствии с распорядком выкладываю код.

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

Кода многовато, чтобы вкладывать в сообщении, потому выкладываю в архиве. Всё разворачивается из файла Main.py.

Пока код крив, дизайна никакого нет. Над этим надо будет поработать.

Планируемые задачи для меня: упорядочить код, сделать загрузку и сохранение карт, начать приделывать GUI.

Совместно с другими участниками надо продумать, как лучше подцепить робота к игровым скриптам.

Другим участникам остается генерация карт, загрузка скриптов игрока. Возможно дублирование моих задач. Ну и самое главное - критика моего кода и программы.
6
29 октября 2009 года
George
4.1K / / 05.01.2007
уже хочу поиграть =)
87
31 октября 2009 года
Kogrom
2.7K / / 02.02.2008
Критика модулей Robot и Board.

1. Класс DataBox. По утверждению автора, объекты подобного класса будут передаваться скрипту игрока. Однако и в данном случае не совсем понятно, почему в поле lastDecor записывается строка, а не экземпляр класса Decor, не ясно, почему в поле orient прописан какой-то магический символ. Далее будет показаны еще некоторые дефекты этого класса.

2. Класс HumanView. Функция Update путанная и лезет в области куда не должна: правит поля объекта класса DataBox, занимается обязанностями Вида (Interface). То есть, полностью создает картинку в виде строки, что приемлемо в консольном приложения, но усложняет работу в GUI-приложении.

3. Класс Robot.

3.1. Этот класс выполняет функции контролера. Должен ли? Если должен, то почему классы Модели не скрывает от Вида?

3.2. Функция SetHumanView не нужна. Более, того, не нужно передавать ссылку на объект класса HumanView в конструкторе, но создавать его. Зачем создателю робота знать об этом классе?

3.3. Функция IncMoves лезет в объект класса DataBox и правит поле. Это неверно. Лучше, чтобы этот объект правил свое поле сам. При чем, в классе Robot данная функция даже не нужна.

3.4. Функция Move каждый раз создает словарь moveDict. Возможно, этот словарь надо сделать полем класса. Функция часто обращается к полям объекта класса DataBox, что не есть верно.

3.5. Turn каждый раз формирует 2 одних и тех же словаря. И также лезет корректировать поля класса DataBox.

4. Класс Interface.

4.1. Класс владеет объектом класса Board. Ошибка MVC.

4.2. Класс знает о классе HumanView и о классе Robot. Но достаточно информации об одном Контролере.

4.3. Функция Run путанная.

4.4. PrintMap – слишком примитивно. Надо забрать часть действий из функции Update класса HumanView.

4.5. GetBoardName – пока не ясен смысл.

5. Иерархия Decor.

5.1. Для удаления дублирования (в классе DecorDict) рекомендуется ввести поле имени.

5.2. В классе wall путаницы passable - impassable.

6. Класс DecorDict.

6.1. Возможно, должен быть наследником от стандартного словаря. Или имитировать его работу.

6.2. Каждый раз при вызове MakeDecor создается словарь decorTypes.

7. Класс BoardLoader - особых ошибок не обнаружено. В функции Load неверная функциональность - параметр type не используется.

8. Класс Board.

8.1. Символы типа ">", "<" являются "магическими".

8.2. Функция TestLine не работает согласно имени - не проверяет линию на корректность.
87
02 ноября 2009 года
Kogrom
2.7K / / 02.02.2008
Вторая итерация закончилась. Третья итерация продлится до 15 ноября.

Итоги. Был сделан код с некоторыми зачатками игры.

Минусы. Я так и не смог убедить других участников, что в таких проектах лучше сделать хоть кое-как, чем никак. Хотят удивить шедевром, наверное.

Планы. Для понимания дальнейшего развития структуры проекта мне нужен компонент загрузки скрипта игрока. В эту итерацию я буду делать модель такого компонента. Реальный компонент будет делать Ander Skirnir.

Кроме того, буду корректировать сделанные компоненты. Если останется время, перейду к реализации GUI.

Что касается замечаний, то код я уже поправил процентов на 90 и выложил здесь опять. Не знаю, стало ли лучше - будет понятнее при добавлении других компонент.
87
06 ноября 2009 года
Kogrom
2.7K / / 02.02.2008
Придумал следующую модель выполнения скрипта: в каждом пользовательском скрипте (читай - модуле) будет содержаться функция ScriptRun, которая будет получать на входе ссылку на возвращатель датабоксов (то есть структур, содержащих результат последнего хода).

Функция будет возвращать генератор, который при каждом обращении (next() ) будет выдавать строку с новой командой.

Приблизительно код может выглядеть так:

Код:
def ScriptRun(boxReturner):

    index = 0
    commands = ["move"]*5 + ["tr"]
    oldMineral = 0
   
    while True:
   
        dataBox = boxReturner()
       
        if dataBox.mineral > oldMineral:
            oldMineral = dataBox.mineral
            print "Yes! I find new mineral"
       
        yield commands[index]
           
        index += 1
        if index >= len(commands): index = 0

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

Конечно, в итоге скрипт не должен ничего печатать - это я добавил для наглядности.

Идея реализована в прикрепленном коде игры.
87
10 ноября 2009 года
Kogrom
2.7K / / 02.02.2008
Некоторые идеи по поводу данной темы.

1. Учебного проекта не вышло, ибо я создаю код, как это делал бы один. Ну, может более подробно документирую. Тут вышло хуже, чем было в первом Учебном Проекте.

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

Вывод. Этот проект не нужен. В код я добавлю несколько функций, генератор карт, условие выигрыша и приделаю GUI. Оставлю режим управления человеком и будет мне игрушка вместо лайнсов. Вижу только такую пользу.

Всё.
29K
12 ноября 2009 года
Ander Skirnir
109 / / 08.06.2009
Генератор карт:

Код:
# coding=utf-8

import random

class LevelGenerator:
    ## scale - коэффициент активной части карты
    ## obj_occ - коэффициент вероятности появления игровых обьектов
    ## dividor - делитель вероятности генерации пустот на смежных клетках
    def __init__ (self, wall_char = 'a', space_char = '-', scale = 0.5, obj_occ = 0.5, dividor = 10):
        self.wall_char, self.space_char, self.scale, self.obj_occ, self.dividor = \
        wall_char,      space_char,      scale,      obj_occ,      dividor

    def generate (self, width = 10, height = 10, objects = []):
        self.objects = objects
       
        ## массивы клеток и вероятностей соответственно :
        self.board = [[self.wall_char] * width for _ in [self.wall_char] * height]
        prob = [[0.25] * width for _ in [0.25] * height]
       
        ## первая клетка :
        cell = [random.randint(0, width - 1), random.randint(0, height - 1)]
        x, y = cell
       
        prob[x][y] = 0.0
        self.board[x][y] = self.space_char     
       
        ## отображение направления в координаты :
        dir_map = { '>' : lambda: [x + 1, y],
                    '<' : lambda: [x - 1, y],
                    'v' : lambda: [x, y + 1],
                    '^' : lambda: [x, y - 1] }
                   
        dir_invert = { '>' : '<', '<' : '>', '^' : 'v', 'v' : '^'}
       
        dmapx = lambda arg : dir_map[arg]()[0]
        dmapy = lambda arg : dir_map[arg]()[1]
       
        ## координатные предикаты :
        valid_x = lambda arg : 1 if 0 <= arg < width else 0
        valid_y = lambda arg : 1 if 0 <= arg < height else 0
       
        ## фильтр для списка :
        without = lambda lexem : lambda arg : 1 if arg != lexem else 0
       
        ## цикл линейной пошаговой стадии генерации, i - кол-во шагов :
        i = int(width * height * self.scale)
        while i > 0:
            ## список допустимых направлений :
            valid_directions = filter(without(None), [d if valid_x(dmapx(d)) and valid_y(dmapy(d)) and prob[dmapx(d)][dmapy(d)] else None for d in ['>', '<', 'v', '^']])
                               
            ## выбор одного из направлений и осуществление хода :
            s, roll = 0.0, random.random()
            for d in valid_directions:
                if s <= roll < s + prob[dmapx(d)][dmapy(d)]:               
                    ## уменьшение вероятностей вокруг предыдущей клетки :
                    for dd in filter(without(d), ['>', '<', 'v', '^']):
                        if valid_x(dmapx(dd)) and valid_y(dmapy(dd)):
                            prob[dmapx(dd)][dmapy(dd)] /= self.dividor
                           
                    ## установка координат на новую клетку и её заполнение :
                    x, y = dmapx(d), dmapy(d)
                    prob[x][y] = 0.0
                   
                    self.board[x][y] = self.space_char
                   
                    if objects:
                        s, roll = 0.0, random.random()
                        for obj in objects:
                            if s <= roll <= s + obj[2] * self.obj_occ:
                                self.board[x][y] = obj[0]
                                break
                            else:
                                s += obj[2] * self.obj_occ
                   
                    ## корректировка вероятностей вокруг новой клетки :
                    m = 0.0
                    future_directions = filter(without(dir_invert[d]), ['>', '<', 'v', '^'])
                    for dd in future_directions:
                        if valid_x(dmapx(dd)) and valid_y(dmapy(dd)):
                            m += prob[dmapx(dd)][dmapy(dd)]
                       
                    if m < 0.999:
                        m = (1.0 - m) / len(future_directions)
                        for dd in future_directions:
                            if valid_x(dmapx(dd)) and valid_y(dmapy(dd)):
                                prob[dmapx(dd)][dmapy(dd)] += m                    
                    break
                else:
                    s += prob[dmapx(d)][dmapy(d)]
                    continue
           
            i -= 1
       
    def save (self, filename):
        ## сохранение в файл
        f = open(filename, 'w')
       
        f.write(self.wall_char + ' = wall\n')
       
        for obj in self.objects:
            f.write(obj[0] + ' = ' + obj[1] + '\n')
           
        f.write('.\n')                                 
       
        i = 0
        while i < len(self.board):         
            for c in self.board:
                f.write(c)
            f.write(self.wall_char + '\n')
            i += 1
       
        f.close()      
   
LG = LevelGenerator(dividor = 10, obj_occ = 0.1)
LG.generate(50, 50, [['b', 'mine(3)', 0.5], ['d', 'pit(4)', 0.5]])
LG.save('c:\\test44.txt')


Более читабельный вариант: http://paste.org.ru/?19rwj9
Пример сгенерированной карты: http://paste.org.ru/?c85y4s

Пока что не описал алгоритм - а по коду в нём разобраться довольно сложно. В следующей версии - будет.
87
12 ноября 2009 года
Kogrom
2.7K / / 02.02.2008
Критика по генератору карт. К сожалению, не успел осилить весь код, только начал. Поэтому только пара замечаний.

1. Функция generate слишком длинная, хорошо бы разбить её ни части. Хотя, не утверждаю, что это позволит уменьшить дублирование, но скорее всего позволит лучше понять.

2. Не ясен код типа:

self.board = [[self.wall_char] * width for _ in [self.wall_char] * height]

почему не

self.board = [[self.wall_char] * width for _ in xrange(height)]

вроде будет то же самое, но короче, понятнее, и, вероятно, эффективнее.

Ну и маленькое замечание. Такой код не очень читается (наверное лучше инициализировать эти переменные по одной):

 
Код:
self.wall_char, self.space_char, self.scale, self.obj_occ, self.dividor = \
        wall_char,         space_char,         scale,         obj_occ,       dividor


да и символ '\' считается в данном использовании устаревшим. Заменяют на что-то вроде такого:
 
Код:
self.wall_char, self.space_char, self.scale, self.obj_occ, self.dividor = (
        wall_char,         space_char,         scale,         obj_occ,       dividor)
87
13 ноября 2009 года
Kogrom
2.7K / / 02.02.2008
Цитата: Kogrom
2. Не ясен код типа:

self.board = [[self.wall_char] * width for _ in [self.wall_char] * height]

почему не

self.board = [[self.wall_char] * width for _ in xrange(height)]



Я тоже не блещу знанием...

self.board = [[self.wall_char] * width] * height

Может и ещё понятнее можно.

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

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог