UniCurses на Python 3 под windows

Темы, связанные с проектированием и программированием roguelike-игр

Модераторы: Sanja, Максим Кич

Аватара пользователя
Cfyz
Сообщения: 776
Зарегистрирован: 30 ноя 2006, 10:03
Откуда: Санкт-Петербург
Контактная информация:

Re: UniCurses на Python 3 под windows

Сообщение Cfyz » 04 сен 2016, 21:54

Насколько я понял, это каноничный вариант замены do-while: Emulate a do-while loop in Python?

Даже если break не хочется, read() дважды это все равно не то. Что-либо дублировать в коде вообще нежелательно, а тем более ввод, который должен обрабатываться. В данном случае получается возможность пропустить нажатие кнопки, ведь первый read() фактически игнорируется. Если он не был TK_ESCAPE, то сразу же следует новый read() и значение первого потеряно. Это может быть критично если аналогичные циклы будут использоваться вложенно в обработке диалогов. На это есть очень простое решение: если нам первое значение key не важно, лишь бы в цикл зайти, то пусть оно просто будет 0 и все, а не read().
Пытается раскуклиться

vapekreng
Сообщения: 63
Зарегистрирован: 22 фев 2016, 22:33

Re: UniCurses на Python 3 под windows

Сообщение vapekreng » 05 сен 2016, 20:35

Согласен, про игнор первого ввода не подумал. Век живи - век учись! С нулем идея по душе пришлась - оставил такой вариант:

Код: Выделить всё

from bearlibterminal import terminal

def main_loop():
    key = 0
    while key != terminal.TK_ESCAPE:
        key = terminal.read()

vapekreng
Сообщения: 63
Зарегистрирован: 22 фев 2016, 22:33

Re: UniCurses на Python 3 под windows

Сообщение vapekreng » 11 сен 2016, 20:01

И еще вопрос: у меня есть карта - массив 20 на 80 и объекты: стены, двери, ловушки, предметы, мобы.
Если с объектами, которые не меняют местоположение: стены, пол, двери и ловшки все норм - я их буду хранить, как элементы массива, то с мобами не могу определиться.
Первый вариант: храню мобов в массиве - очень удобно для передвижения. Например, я хочу пойти на одну клетку влево - проверяю Map[x-1][y].mob - если истина, то там моб и вместо движения атака, если ложь, то моба нет и проверяю проходимость. Но вот для определения очередности хода мне нужно просмотреть всех мобов на карте и найти из них того, у кого самый высокий показатель энергии, т.е. каждый ход каждого существа придется делать поиск по 80*20=1600 ячейкам массива, что не айс.
Второй вариант: я буду хранить мобов отдельным массивом, определение очередности хода пойдет на ура, но при проверке занятости клетки нужно будет проверять не только клетку (вдруг стена), но и весь список мобов, которых может быть много.
Третий вариант - хранить и там и там, а при изменении положения синхронизировать, но тут уже дублирование переменных идет по всем мобам...
Посоветуйте, какому варианту отдать предпочтение? Мне хотя бы общее направление, куда копать, там уже найду

Аватара пользователя
Jesus05
Сообщения: 1840
Зарегистрирован: 02 дек 2009, 07:50
Откуда: Норильск, сейчас Санкт-петербург.
Контактная информация:

Re: UniCurses на Python 3 под windows

Сообщение Jesus05 » 12 сен 2016, 06:28

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

Аватара пользователя
Максим Кич
Администратор
Сообщения: 1642
Зарегистрирован: 03 дек 2006, 20:17
Откуда: Витебск, Беларусь
Контактная информация:

Re: UniCurses на Python 3 под windows

Сообщение Максим Кич » 12 сен 2016, 06:29

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

<добрый_инквизитор>
Я же, надеюсь, у тебя игровые объекты в виде классов описаны, мой юный друг
</добрый_инквизитор>
Dump the screen? [y/n]

Аватара пользователя
Cfyz
Сообщения: 776
Зарегистрирован: 30 ноя 2006, 10:03
Откуда: Санкт-Петербург
Контактная информация:

Re: UniCurses на Python 3 под windows

Сообщение Cfyz » 12 сен 2016, 13:05

Определенно третий вариант. Как заметил Максим, дублирования данных не будет -- и в поле mob ячейки, и в массиве мобов ссылки на один и тот же объект (в Python все переменные это ссылки на объект). Но важно избежать дублирования действий по работе с мобом. Нельзя оперировать этими данными на месте в каждом отдельном случае, периодически расставляя в коде конструкции типа

Код: Выделить всё

if some_mob.hp <= 0:
    Map[x][y].mob = None
    Mobs.remove(some_mob)
Для того, чтобы данные надежно были в согласованном состоянии, нужно просто выделить специально ответственного за это. Например, набор функций или методов add_mob/delete_mob/move_mob/find_next_mob и т. д. и работать только через них. Будет заметно сложнее сломать данные что-то забыв или опечатавшись.
Пытается раскуклиться

Аватара пользователя
Максим Кич
Администратор
Сообщения: 1642
Зарегистрирован: 03 дек 2006, 20:17
Откуда: Витебск, Беларусь
Контактная информация:

Re: UniCurses на Python 3 под windows

Сообщение Максим Кич » 12 сен 2016, 13:43

Cfyz писал(а):Например, набор функций или методов add_mob/delete_mob/move_mob/find_next_mob и т. д. и работать только через них. Будет заметно сложнее сломать данные что-то забыв или опечатавшись.
Строго говоря, это должен быть отдельный класс, который и будет содержать в себе и обслуживать очерёдность ходов, причём, игрок и монстр для него должны быть сущностями одной природы. Ну и, попутно, он же будет размещать персонажей на карте. С картой мобы уже будут взаимодействовать непосредственно — я уже описывал пару раз эту схему.
Dump the screen? [y/n]

vapekreng
Сообщения: 63
Зарегистрирован: 22 фев 2016, 22:33

Re: UniCurses на Python 3 под windows

Сообщение vapekreng » 12 сен 2016, 19:45

Послушаю опытных людей и буду бить по третьему варианту. И да, игровые объекты у меня в классах - из-за них в том числе с паскаля и ушел.
Cfyz писал(а):Строго говоря, это должен быть отдельный класс, который и будет содержать в себе и обслуживать очерёдность ходов, причём, игрок и монстр для него должны быть сущностями одной природы.
Классы только месяц назад осваивать начал, так что извините за кучу вопросов. Игрок и моб у меня аналогичные объекты, просто героем можно управлять, а мобами нет :)
Я правильно понимаю, что этот обслуживающий класс, в основном, будет работать с данными других классов (класс мобов, предметов, карты) и осуществлять их взаимодействие?

Аватара пользователя
Максим Кич
Администратор
Сообщения: 1642
Зарегистрирован: 03 дек 2006, 20:17
Откуда: Витебск, Беларусь
Контактная информация:

Re: UniCurses на Python 3 под windows

Сообщение Максим Кич » 13 сен 2016, 12:52

vapekreng писал(а):Послушаю опытных людей и буду бить по третьему варианту. И да, игровые объекты у меня в классах - из-за них в том числе с паскаля и ушел.
Ну, если с Object/Free Pascal, то, вообще говоря, там классы очень хорошо реализованы. Если с Turbo Pascal, то дай попользоваться криогенной капсулой, я тоже хочу залечь лет на 20 )
vapekreng писал(а):Я правильно понимаю, что этот обслуживающий класс, в основном, будет работать с данными других классов (класс мобов, предметов, карты) и осуществлять их взаимодействие?
Смотря, что вкладывать в слово «взаимодействие». Взаимодействовать классы должны друг с другом, по возможности без посредников. Скажем, вопрос мордобития решают два экземпляра Монстров без постороннего участия. Точно так же вопрос занятия/оставления клетки карты должны решать Монстр и Клетка Карты (клетка — отдельный от карты объект со своими свойствами событиями. Это окупится, честное слово!)

В отдельный класс надо собрать вещи, которые экземпляры Монстров не могут делать сами: создавать экземпляр нужного класса по имени/описанию, вызывать событие "ход" в нужной очерёдности, и удалять ссылки из очереди хода. Можно генератор и handler очерёдности хода разнести по двум разным объектам, потому что генератор разрастётся.

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

Но у такого решения есть два больших минуса:
во-первых, требует некоторой академической подготовки и понимания операций на списках, а во-вторых (что важнее!) в дальнейшем может оказаться тяжело внедрить более сложную систему очерёдности хода (с учётом скорости или инициативы, например).
Dump the screen? [y/n]

vapekreng
Сообщения: 63
Зарегистрирован: 22 фев 2016, 22:33

Re: UniCurses на Python 3 под windows

Сообщение vapekreng » 13 сен 2016, 18:04

vapekreng писал(а):Если с Turbo Pascal, то дай попользоваться криогенной капсулой, я тоже хочу залечь лет на 20 )
Именно с него, капсулу не отдам - самому нужна)))
Большое спасибо за развернутый ответ!
vapekreng писал(а):Скажем, вопрос мордобития решают два экземпляра Монстров без постороннего участия.
Соответственно в классе моб делаю метод attack() и die(). Метод атаки (как и любой другой, который может снизить хп) включает в себя проверку на смерть и в её случае вызывает метод смерти, который чистит очередь хода и клетку карты от моба.
vapekreng писал(а):клетка — отдельный от карты объект со своими свойствами событиями. Это окупится, честное слово!
У меня карта-массив, его элементы-тайтлы (клетки), которые, на данный момент, имеют три атрибута: моб, список предметов и рельеф
vapekreng писал(а):Но у такого решения есть два больших минуса:
во-первых, требует некоторой академической подготовки и понимания операций на списках, а во-вторых (что важнее!) в дальнейшем может оказаться тяжело внедрить более сложную систему очерёдности хода (с учётом скорости или инициативы, например).
Систему ходов взял из ADOM - у каждого монстра есть скорость - количество энергии, восстанавливаемое за единицу времени.
1. Если есть мобы с энергией выше нуля, то ходит моб с максимальной энергией, его энергия снижается на цену действия.
2. Если у всех мобов энергия ниже 0, то пропуск хода с восстановлением энергии в количестве равном скорости каждому мобу. Переходим к 1

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

Код: Выделить всё

class mob:
    def __init__(self, hp, speed):
        self.hp=hp
        self.speed=speed
mob1=mob(1,100)
a=mob1
b=mob1
c=[a,b,mob1]
print(a)
print(b)
print(c[0],c[1])
Выдало следующее:
<__main__.mob object at 0x7f43ae5baac8>
<__main__.mob object at 0x7f43ae5baac8>
<__main__.mob object at 0x7f43ae5baac8> <__main__.mob object at 0x7f43ae5baac8>
Тут видно, что хотя имена разные, но объект один и тот же. Дальше попробовал менять значение атрибута - вышло еще более показательно:

Код: Выделить всё

class mob:
    def __init__(self, hp, speed):
        self.hp=hp
        self.speed=speed
mob1=mob(1,100)
a=mob1
b=mob1
c=[a,b,mob1]
print(a.hp)
print(b.hp)
print(c[0].hp,c[1].hp)
mob1.hp=7
print(a.hp)
print(b.hp)
print(c[0].hp,c[1].hp)
1
1
1 1
7
7
7 7
2. Добавление энергии и выбор моба для хода будет идти по списку, а проверка клетки на моба уже по самой клетке.
3. Отрисовка зоны видимости героя перед началом его хода и по завершению хода.
Как считаете, жизнеспособный вариант?

Аватара пользователя
Cfyz
Сообщения: 776
Зарегистрирован: 30 ноя 2006, 10:03
Откуда: Санкт-Петербург
Контактная информация:

Re: UniCurses на Python 3 под windows

Сообщение Cfyz » 16 сен 2016, 01:00

Edit: кажется, пока я набирал ответ, исходное сообщение потерли =)
vaperkreng писал(а):Немного странно ведет себя команда terminal.read(): она считывает каждое нажатие кнопки дважды - один раз когда нажмешь кнопку и еще раз когда отпустишь. Это так и задумано или я что-то не так делаю?
Так было задумано ранее, должны же читаться события отпускания кнопок, чтобы их можно было отбработать если нужно. В теории, не какая ли разница что игнорировать -- лишнее нажатие какой-нибудь запятой или отпускание клавиши? На деле это оказалось не так удобно. Как минимум получилось, что read() не работает как "нажмите любую клавишу" без дополнительной настройки. Поэтому я в итоге поменял поведение на такое, что по умолчанию считываются только нажатия клавиш, а если надо что-то сверх того (отпускания, мышь), то это легко задействовуется отдельно (cм. какую-никакую, а справку по этому фильтру).

Чтобы получить свежую версию библиотеки, надо обновить пакет:

Код: Выделить всё

pip3 install -U bearlibterminal
По поводу кода: глаз зацепился за двойное сравнение с TK_ESCAPE в основном цикле (у меня вообще пунктик по поводу повторяющегося кода). Сравнение перед вызовом move_mob() излишне. Escape в этом случае ничем не отличается от любой другой клавиши и будет "проигнорирован" так же как, скажем, TK_A. Советую как минимум сразу выходить из move_mob() если переданный аргумент key не равен ни одному из допустимых значений (if .. elif .. else return). Еще тип аргумента key в move_mob() совсем не str, python сам по себе не проверяет эти подсказки.

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

Код: Выделить всё

while not terminal.check(terminal.TK_ESCAPE):
    key = terminal.read()
    ...
Суть в том, что прочитанное состояние клавиш доступно до следующего вызова read(), т. е. check(key) фактически означает "была ли нажата клавиша key на момент предыдущего read()".
Но как только придется выходить еще и по закрытию окна или читать события во вложенном цикле, работать не будет ни этот вариант, ни текущий =/.
Пытается раскуклиться

vapekreng
Сообщения: 63
Зарегистрирован: 22 фев 2016, 22:33

Re: UniCurses на Python 3 под windows

Сообщение vapekreng » 17 сен 2016, 14:58

Дело было вечером, делать было нечего. Свой ответ потер потому, что к моменту его написания в глаза бросилось двойное использование read() в цикле - отсюда пошли проблемы. Так как косяк был мой - не видел причины напрягать людей)))
Либу обновил, справку в закладки добавил. Что тип не str увидел, там же шестнадцатеричное число, верно? Сделал проверку и цикл на нажатие клавиши из списка тех, которые уже назначил хоть на что-то, пока этот список тупо в программе прописал, потом в файл перекину. Чуть позже выложу, что получилось)
Скрытый текст: ПОКАЗАТЬ

Код: Выделить всё

import Title
from bearlibterminal import terminal

terminal.open()
terminal.clear()
TestMap = Title.Maps()
Hero = Title.Mobs(5, 5, 0, '455', '@')
TestMap.add_mob(Hero)
TestMap.draw()
t = 0
key = 0
keys = [79, 80, 81, 82, 41]
while key != terminal.TK_ESCAPE:
    t += 1
    terminal.printf(0, 0, str(t))
    terminal.refresh()
    while key not in keys:
        key = terminal.read()
    # В данный момент работают кнопки ESC и стрелки, поэтому такая проверка, позже напишу метод разбора нажатой клавиши
    if key != terminal.TK_ESCAPE:
        TestMap.move_mob(Hero, key)
        TestMap.draw()
        terminal.refresh()
        key = terminal.read()
terminal.close()
Ну и метод карты move_mob()
Скрытый текст: ПОКАЗАТЬ

Код: Выделить всё

    def move_mob(self, mob: Mobs, key: str):
        """

        :param key: str
        :type mob: Mobs
        """
        dx = 0
        dy = 0
        if key == 80:
            dx = -1
            dy = 0
        if key == 79:
            dx = 1
            dy = 0
        if key == 82:
            dx = 0
            dy = -1
        if key == 81:
            dx = 0
            dy = 1
        if self.title[mob.x + dx][mob.y + dy].mob is None and self.title[mob.x + dx][mob.y + dy].relief.passable:
            self.title[mob.x][mob.y].mob = None
            self.title[mob.x + dx][mob.y + dy].mob = mob
            mob.x += dx
            mob.y += dy

            # TODO move_mob добавить проверку проходимости, значения кнопок и названия в словарь
В данный момент пишу класс параметров персонажа (сила, ловкость и т.д.), на очереди зона видимости, потом нормальный обработчик нажатий клавиши

vapekreng
Сообщения: 63
Зарегистрирован: 22 фев 2016, 22:33

Re: UniCurses на Python 3 под windows

Сообщение vapekreng » 17 сен 2016, 15:14

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

vapekreng
Сообщения: 63
Зарегистрирован: 22 фев 2016, 22:33

Re: UniCurses на Python 3 под windows

Сообщение vapekreng » 17 сен 2016, 15:17

Чуть-чуть поменял главный модуль, а то время после первого хода не увеличивалось
Скрытый текст: ПОКАЗАТЬ

Код: Выделить всё

import Title
from bearlibterminal import terminal

terminal.open()
terminal.clear()
TestMap = Title.Maps()
Hero = Title.Mobs(5, 5, 0, '455', '@')
TestMap.add_mob(Hero)
TestMap.draw()
t = 0
terminal.printf(0, 0, str(t))
key = 0
keys = [79, 80, 81, 82, 41]
while key != terminal.TK_ESCAPE:
    terminal.refresh()
    while key not in keys:
        key = terminal.read()
    # В данный момент работают кнопки ESC и стрелки, поэтому такая проверка, позже напишу метод разбора нажатой клавиши
    if key != terminal.TK_ESCAPE:
        TestMap.move_mob(Hero, key)
        t += 1
        terminal.printf(0, 0, str(t))
        TestMap.draw()
        terminal.refresh()
        key = terminal.read()
terminal.close()

vapekreng
Сообщения: 63
Зарегистрирован: 22 фев 2016, 22:33

Re: UniCurses на Python 3 под windows

Сообщение vapekreng » 18 сен 2016, 15:36

vapekreng писал(а):И еще вопрос: при зажатии кнопки, например, вправо, моб движется еще некоторое время после того, как её отпустишь. Я так понимаю, очередь ввода накапливается и, так как у меня сейчас на каждый ход отрисовывается вся карта, карта не успевает прорисовываться, верно? Есть ли возможность читать последнее событие в очереди ввода?
Вопрос снят - сделал вывод только видимой области, а не всей карты - полет нормальный

Ответить

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 24 гостя