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

Ваш аккаунт

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

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

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

Эксперимент: Крестики-нолики

87
04 июня 2009 года
Kogrom
2.7K / / 02.02.2008
Решил провести эксперимент по проведению битв кода. Суть битвы заключается в следующем: несколькими участниками пошагово создается программа. В каждом ходе должно быть применено улучшающее программу учебное действие.

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

Ход первый: ТЗ

Техническое задание на игру крестики-нолики.

1 этап.

Процесс игры ведется в консоли. Игрок задает положение игры координатой: столбец, строка. Если координата находится вне доски или указывает на занятую ячейку, то выдается сигнал об ошибке и координата запрашивается заново.

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

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

2 этап.

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

---

Вторым ходом по науке дожен быть анализ (возможно, ООА), разбор прецедентов.

Программу планируется сделать в консоли на Питоне (для начала). Таким образом, заодно решается задача изучения Питона на практике.
311
05 июня 2009 года
plastictown
309 / / 08.01.2006
Во время сессии подобное предложение не вызывает положительных эмоций, не говоря уже о слове "Питон", однако в силу экстравагантности и несомненной пользы для общества, +1:)
87
05 июня 2009 года
Kogrom
2.7K / / 02.02.2008
Ход 2.

Прецеденты для первой итерации программы

1. Основной (удачный) сценарий.

1.1. Игрок запускает игру.
1.2. На экран выводится поле с пустыми клетками.
1.3. Программа предлагает ввести игроку координаты символа (крестика) в поле ("столбец, строка").
1.4. Игрок вводит координату.
1.5. Программа выводит поле с символами: крестиками и ноликами.
1.6. Программа оценивает расположение символов на выигрыш, проигрыш, ничью. Если произошло одно из этих событий, то выдается соответствующее сообщение.
1.6.1. Игроку предлагается начать игру заново.
1.6.2. Если игрок соглашается, то переходим к пункту 1.2.
1.6.3. Иначе программа завершается.
1.7. Программа делает свой ход. На начальной итерации просто ставит свой символ (нолик) в свободную клетку.
1.8. То же что и 1.6 с подпунктами.
1.9. Переходим к пункту 1.3.

2. Ветки неудачных сценариев:
2.1. (вариант 1.4) Игрок вводит координату клетки, которая уже занята.
2.2. Программа выдает сообщение, что клетка уже занята.
2.3. Переходим к пункту 1.3 основного сценария.


3.1. (вариант 1.4) Игрок вводит координату клетки, которой нет на игровом поле.
3.2. Программа выдает сообщение, что такой клетки нет.
3.3. Переходим к пункту 1.3 основного сценария.
87
05 июня 2009 года
Kogrom
2.7K / / 02.02.2008
Ход 3.

Анализ (предварительный) программы по существительным.

Есть следующие объекты: игрок, программа (искусственный противник), анализатор выигрыша, поле (доска), клетка и 2 символа – крестик и нолик.

Игрока может представлять контроллер, получающий от игрока событие ввода координаты крестика и передающий его доске. Этот же контроллер может и выдавать информацию игроку. Хотя, можно назначить и другой объект.

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

Доска включает в себя 9 объектов-клеток. Основные функции доски: получать координаты новых символов и выводиться на экран. Доска может просто использовать функции вывода клеток.

Каждая клетка может быть в трёх состояниях: пустая, с крестиком, с ноликом. Так как клетка меняет свое состояние, то нет смысла делать иерархию клеток. Состояние клетки можно выделить в отдельный объект или просто использовать целочисленную переменную.

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

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

------

В области такого анализа я не спец. Хотелось бы услышать адекватную критику.

По науке дальше надо рисовать UML-диаграммы. Но я не нашел подходящего инструмента. Возможно, лучше всего рисовать от руки и фотографировать. Или использовать растровый графический редактор вместо школьной доски. Или можно использовать псевдо UML-графику типа:

[Board]<>------->9[Cell]

Пока не решил, что удобнее.

В принципе, в данном простом случае можно обойтись и без диаграмм. Но это не есть правильно для учебного задания.
87
07 июня 2009 года
Kogrom
2.7K / / 02.02.2008
Ход 4

Диаграмма UML

Автор учебника по UML Крег Ларман пишет, что специализированными редакторами надо пользоваться при составлении документации. Но перед разработкой и во время ее лучше рисовать на доске или на крупных листах от руки. Ибо диаграммы вторичны, должны помогать писать код, а не добовлять мучения в процесс. Потом можно делать качественные фотографии, если получится интересный чертеж.

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

Пока нарисовал черновик одного хода в крестиках-ноликах.
244
08 июня 2009 года
UAS
2.0K / / 19.07.2006
Я бы взялся, да и мне тоже полезно поизучать что-нибудь. Но, увы, сессия дает о себе знать))
Если тема не загнется до начала июля, я бы поучоствовал тоже)
87
08 июня 2009 года
Kogrom
2.7K / / 02.02.2008
Продолжу безумную пляску...

Ход 5

Перехожу к программированию. Сделаю компонент Board, который будет выводить поле примерно так:
 
Код:
[FONT="Courier New"]   |   |  
---|---|---
   |   |  
---|---|---
   |   |   [/FONT]

Заготовка с классом, которую можно испытать:
Код:
class Board:
    matrix = [[' ' for i in xrange(3)] for i in xrange(3)]

    def Draw(self):
        pass

def main():
    board = Board()
    board.Draw()

if __name__ == "__main__":
    main()


Ну и сама функция рисования. Можно сделать конкретную, типа:
 
Код:
def Draw(self):
        line = "---|---|---"
        for i in xrange(3):
            t = self.matrix
            print " %c | %c | %c " % (t[0], t[1], t[2])
            if i != 2 :
                print line

или универсальную, на произвольное количество клеток:
Код:
def Draw(self):
        drawnMatrix = []
       
        for y in self.matrix:
            s = ""
            for x in y:
                s = s + ' ' + str(x) + " |"
            s = s[:-1]
            drawnMatrix.append(s)
           
            s = ""
            for x in y:
                s = s + "---|"            
            s = s[:-1]
            drawnMatrix.append(s)
        drawnMatrix = drawnMatrix[:-1]
       
        for s in drawnMatrix:
            print s

но вторая трудно читаемая, так что ее можно отложить на определенное время.

Итак, основная работа с GUI сделана, дальше перейду к вводу координат от игрока и логике оппонента.
87
09 июня 2009 года
Kogrom
2.7K / / 02.02.2008
Шаг 6

Как и планировалось приступлю к диалогу с игроком и к оппоненту. Вместо диспетчера пока буду использовать main. Должно получится что-то типа:

Код:
def main():
    board = Board()
    board.Draw()
   
    user = UserContr(board)
    oppnt = Opponent(board)
   
    while(board.Analize()):
       
        user.SetCross()
        oppnt.SetO()
        board.Draw()

Вроде бы, все понятно без комментариев.

Отдельного анализатора делать не буду, так как пока не планировал. Поэтому функцию анализа засуну в "доску".

Слишком маленькие шаги делать скучно, потому создам сразу сравнительно большой код:

Код:
class Board:
    matrix = [[' ' for i in xrange(3)] for i in xrange(3)]

    def Draw(self):
        line = "---|---|---"
        for i in xrange(3):
            t = self.matrix
            print " %c | %c | %c " % (t[0], t[1], t[2])
            if i != 2 :
                print line

    def SetXY(self, x, y, char):
        self.matrix[y][x] = char

    def TestXY(self, x, y):
        if (y < len(self.matrix)) and (x < len(self.matrix[0]))\
           and self.matrix[y][x] == ' ':
            return True
        else:
            return False
       
    def GetSizes(self):
        return (len(self.matrix[0]), len(self.matrix))

    def Analize(self):
        for i in self.matrix:
            for j in i:
                if j == ' ':
                    return True
        return False
               
class Opponent:
    def __init__(self, ptrBoard):
        self.board = ptrBoard
   
    def SetO(self):
        sz = self.board.GetSizes()
        for i in xrange(sz[0]):
            for j in xrange(sz[1]):
                if self.board.TestXY(i, j):
                    print i, j
                    self.board.SetXY(i, j, 'O')
                    return

class UserContr:
    def __init__(self, ptrBoard):
        self.board = ptrBoard

    def SetCross(self):
        x = int(raw_input("Input X: "))
        y = int(raw_input("Input Y: "))
        if board.TestXY(x, y):
            board.SetXY(x, y, 'X')

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

Уточнение. Используется Python 2.6.2. Работа программы проверяется в IDLE.
87
11 июня 2009 года
Kogrom
2.7K / / 02.02.2008
Продолжу свой вечерний сериал...

Шаг 6.5.

Сегодня передышка в коде. Надо определиться с двумя задачами:

1. Алгоритм определения выигрыша.
2. Разделение объектов.

Подробнее.

1. Так как выигрышных комбинаций всего восемь для 9-клеточных крестиков-ноликов (3 по горизонтали, 3 по вертикали, 2 по диагонали), то можно тупо перебирать эти комбинации. Но это не очень эффективный алгоритм и его трудно перенести на пятизначные (в безграничном поле) крестики-нолики. Однако, если учитывать координату последнего хода, то можно перебирать только 2-4 комбинации. Но алгоритм немного усложнится. А может есть алгоритм еще лучше.

2. Так выходит, что матрица с крестиками-ноликами должна быть доступна и объекту доски-вида, и объекту виртуального оппонента (чтобы он выбрал лучшую стратегию), и объекту анализатора. Причем, пока я не нашел, как сделать так, чтобы матрицу мог менять только один объект, а остальные могли только просматривать. Как-то не ООПешно...

Возможно, еще надо подумать над тем, чтобы разделить код на модули.
1
11 июня 2009 года
kot_
7.3K / / 20.01.2000
1. Объект Диспечер участников - управляет объектами типа Участник. Устанавливает очередность ходов, опрашивает флаги состояния матрицы (выиграш, проиграш, ничья, ошибочный ход, успешный ход), читает содержимое матрицы, ведет посчет ходов.
2. базовый тип участника. абстрактный класс.
3. Ему наследуют Игрок, Компьютер(отличаются источником ввода). Реализуют ввод. Конечный автомат - "в игре","выиграш", "проиграш", "ошибочный ход", "ожидание хода противника". т.д. Взаимодействуют с диспечером.
4. Объект матрица - содержит состояние игровой доски. Управляется анализатором ходов, флаги и содержание читаются диспечером игроков.
5. Анализатор ходов - устанавливат флаги состояния матрицы.
6. Диспечер вывода взаимодействует с диспечером игроков.
7. АИ (черный ящик) - взаимодействует с объектами Компьютер и Диспечер участников.
Я в свое время реализовал примерно такое - хотя могу чего и запямятовать.
87
11 июня 2009 года
Kogrom
2.7K / / 02.02.2008
Цитата: kot_
1. Объект Диспечер участников - управляет объектами типа Участник. Устанавливает очередность ходов, опрашивает флаги состояния матрицы (выиграш, проиграш, ничья, ошибочный ход, успешный ход), читает содержимое матрицы, ведет посчет ходов.
2. базовый тип участника. абстрактный класс.
3. Ему наследуют Игрок, Компьютер(отличаются источником ввода). Реализуют ввод. Конечный автомат - "в игре","выиграш", "проиграш", "ошибочный ход", "ожидание хода противника". т.д. Взаимодействуют с диспечером.
4. Объект матрица - содержит состояние игровой доски. Управляется анализатором ходов, флаги и содержание читаются диспечером игроков.
5. Анализатор ходов - устанавливат флаги состояния матрицы.
6. Диспечер вывода взаимодействует с диспечером игроков.
7. АИ (черный ящик) - взаимодействует с объектами Компьютер и Диспечер участников.


(Процитировал все, чтобы видеть на этой странице)

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

Из диаграммы последовательности пока не совсем ясно, зачем нужен объект "Компьютер", но, возможно, я не все правильно изобразил.

1
12 июня 2009 года
kot_
7.3K / / 20.01.2000
Цитата: Kogrom
(Процитировал все, чтобы видеть на этой странице)

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

Из диаграммы последовательности пока не совсем ясно, зачем нужен объект "Компьютер", но, возможно, я не все правильно изобразил.


И еще:
Диспечер игроков (ДИ) и матрица (Диспечер игровой доски(ДИД)) наследуют одному базовому классу. ДИ управляет объектами игроков, ДИД - объектами игрового поля (в частном случае - ячейки для записи).
Диспечер вывода (ДВ) взаимодействует с ДИ и ДИД - с одного получает информацию о состоянии игроков, со второго - информацию о состоянии игровой доски. Вроде так. :)
Объект "компьютер" и "участник" по сути отличаются:
1. как перегружают функцию ввода,
2. и наличием функции-члена опроса ДВ у объекта компьютер. Объект игрок в этом не нуждался. Кстати потом в связи с введением режима сетовой игры эти объекты были переработаны - и все классы игроков были редуцированы до базового, а их функционал был передан в Диспечер ввода (ДВв)
Классы IA, кстати говоря не входят в пространство имен программы - его взаимодействие с программой происходит через объект "компьютер" (в последствии через ДВв)

87
14 июня 2009 года
Kogrom
2.7K / / 02.02.2008
Шаг 7. Создаю работающую заготовку.

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

Код:
class Analyzer:
    matrix = [[' ' for i in xrange(3)] for i in xrange(3)]
    steps = 0
    flags = None

    def TestXY(self, x, y):
        if (y < len(self.matrix)) and (x < len(self.matrix[0]))\
           and self.matrix[y][x] == ' ':
            return True
        else:
            return False

    def SetXY(self, x, y, char):
        if self.TestXY(x, y):
            self.matrix[y][x] = char
            self.SetFlags(x, y, char)
            return True
        return False


    def SetFlags(self, x, y, char):
        if self.FindWin(x, y, char):
            if char == 'O':
                self.flags = "Opponent wins"
            if char == 'X':
                self.flags = "You win"

    def FindWin(self, x, y, char):
        # test vertical
        t = self.matrix
        if (t[0][x] == t[1][x] == t[2][x] == char): return char
       
        # test horizontal
        if (t[y][0] == t[y][1] == t[y][2] == char): return char
       
        # test diagonal 1
        if (t[0][0] == t[1][1] == t[2][2] == char): return char
       
        # test diagonal 2
        if (t[0][2] == t[1][1] == t[2][0] == char): return char
       
        return None

    def StepEnable(self):
        if self.steps < 9:
            return True
        self.flags = "Nobody wins"
        return False
   
class Board:
   
    def Draw(self, matrix):
        line = "---|---|---"
        for i in xrange(3):
            t = matrix
            print " %c | %c | %c " % (t[0], t[1], t[2])
            if i != 2 :
                print line
               
    def PrintResult(self, result):
        print result

               
class Opponent:
    def __init__(self, ptrMatrix):
        self.matrix = ptrMatrix
   
    def GetO(self):
        for i in xrange(3):
            for j in xrange(3):
                if self.matrix[j] == ' ':
                    return (i, j)

class UserContr:

    def GetCross(self):
        x = int(raw_input("Input X: "))
        y = int(raw_input("Input Y: "))
        return (x, y)
       
class Dispatcher:

    def __init__(self):
        pass
   
    def Run(self):
        analizer = Analyzer()
        board = Board()
        board.Draw(analizer.matrix)
   
        user = UserContr()
        oppnt = Opponent(analizer.matrix)
       
        while(analizer.StepEnable()):
           
            coord = user.GetCross()
            while(not analizer.SetXY(coord[0], coord[1], 'X')):
                coord = user.GetCross()

            if analizer.flags:
                board.Draw(analizer.matrix)
                board.PrintResult(analizer.flags)
                return

            analizer.steps = analizer.steps + 1
            if not analizer.StepEnable(): break
           
            coord = oppnt.GetO()
            analizer.SetXY(coord[1], coord[0], 'O')
           
            analizer.steps = analizer.steps + 1
           
            if analizer.flags:
                board.Draw(analizer.matrix)
                board.PrintResult(analizer.flags)
                return
           
            board.Draw(analizer.matrix)
   
        print analizer.flags

if __name__ == "__main__":
    dispatcher = Dispatcher()
    dispatcher.Run()

Играть уже можно (в IDLE). Легко выиграть, легко проиграть, труднее добиться ничьей.
87
14 июня 2009 года
Kogrom
2.7K / / 02.02.2008
Шаг 8. Критика заготовки (Шаг 7)

Недостатки первой версии (в произвольном порядке)

1. Функция Run класса Dispatcher слишком длинная. Фактически этот класс является оберткой для одной функции.
2. В функции Run класса Dispatcher есть ненужное дублирование.
3. Класс Analyzer содержит слишком много функций по сравнению с другими классами. Это маленький недостаток.
4. Путаница с координатами в матрице: установка символа ведется в порядке y, x, а опрос игроков ведется в порядке x, y. В результате, повышается ошибкоемкость кода.
5. Функции некоторых классов не нуждаются в этих классах. Можно убрать классы, либо сделать эти функции статическими.
6. Код программы разросся. Возможно, следует поделить его на модули.
7. Много ненужной конкретики. Например, используется "магическое" число 3, "магические" символы 'X' и 'O'. Кроме того, функция вывода матрицы и функция определения выиграша слишком сильно привязаны к конкретной матрице.
8. Оппонент предсказуем. Он не пытается победить, а просто ставит нолики по порядку.
9. Игрок всегда ходит первым.
10. Нет защиты от дурака: ложный ввод может привести к краху программы.
11. Нет запроса на запуск новой игры. То есть, чтобы сыграть еще раз надо запустить программу заново.

Достоинства:
1. Задачи разделены по классам в соответствии с их смысловым назначением. Почти.
2. Один класс отвечает за ввод, один на вывод. То есть, проще будет перенести в вариант с GUI.

В общем, исправлять надо многое.
87
16 июня 2009 года
Kogrom
2.7K / / 02.02.2008
Думаю, дальше эксперимент проводить бессмысленно.
Совместной публичной разработки так не получится - надо было заранее продумать и договориться как она будет проходить. Если бы я лучше знал Питон, то можно было бы привратить это в мастер-класс. Но пока я его знаю плохо.
29K
17 июня 2009 года
Ander Skirnir
109 / / 08.06.2009
Идея-то интересная, но крайне неудобное для большинства студентов время - сессия :(
В другое время энтузиастов наверняка было бы побольше.

PS: судя по первой странице - человека этак три еще
87
17 июня 2009 года
Kogrom
2.7K / / 02.02.2008
Цитата: Ander Skirnir
В другое время энтузиастов наверняка было бы побольше.



Тогда подожду до августа. Хотя до того времени я могу увлечься каким-нибудь Boo )))

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