Pull to refresh

Comments 8

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

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

Ещё я не понял, почему класс TTTBoard вместо хранения информации о поле, реализует всю логику игры. Логичнее было бы, если бы какой-нибудь TicTacToe содержал бы в себе Board, который использовал бы только для хранения состояния о поле.

И раз уж используется типизация, то Board можно было бы сделать generic типом, чтобы не гадать, какие значения он хранит:

T = TypeVar('T')

class Board(Generic[T]):
  def __getitem__(self, pos: tuple[int, int]) -> T:
    ...

С формулой выглядит сложновато, я согласен. Однако с многомерными массивами создание будет выглядеть громоздко. Тут выбор из двух вариантов, я выбрал то, что выбрал. По поводу array.array мне остаётся только согласиться с вами, однако статья была ориентирована на новичков, и я старался минимизировать количество сущностей, чтобы сосредоточиться на главном.
Что касается TypeVar — я благодарен вам за это замечание, подумаю над внесением этих в правок в репозиторий.

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

  2. Если уж уходить в многомерку, то структура данных, хранящая всё игровое поле — не нужна и даже вредна. Нужно хранить только координаты «крестиков» и «ноликов», поставленных игроками. К примеру, на игровом поле 1000 x 1000 x 1000, вы можете работать или с одним массивом размером 10Е+9, или с двумя массивами размером примерно по 1000 каждый.


1. Пожалуй, я мог бы сделать проверку всех диагоналей в n-мерном пространстве, но это бы усложнило код и сузило аудиторию. Дочерний класс сделан двумерным by design, поэтому усложнять какой-то один метод нет смысла от слова «совсем». А базовый класс умышленно сделан многомерным, чтоб можно было заново отнаследоваться и сделать так, как вы говорите: консистентно со структурой данных, а точнее, с большей полнотой использования возможностей, предоставляемых базовым классом. Мы же не обязаны их использовать полностью сейчас, если нам сейчас не надо?
2. В случае этой игры и ей подобным ваше предложение вряд ли можно считать приемлемым: память мы не экономим, поскольку не предполагалось создание больших досок, а кроме того, проверка занятости ячейки в моём решении выполняется за O(1), в вашем — за O(Mⁿ). Мы ещё можем поговорить об индексации, но тогда мы придём к структурам, которые, возможно, похожи на те, что уже есть в моём решении. А код к тому времени будет безнадёжно усложнён. Хотя, для каких-то других игр ваш подход и будет оправдан. Мы же почти всегда торгуем памятью ради скорости и наоборот.

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

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

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

Как новичок в программировании, скажу просто, я в а*еу шоке с того уровня математики, которое требуется для реализации, с виду, такой простой задачи, как написание Крестиков-ноликов. Спасибо за статью, надеюсь, когда-нибудь, я полностью пойму всё, что в ней написано =)

На самом деле, как уже заметили выше, не нужен такой уровень математики. Если в базовом классе ограничить сразу количество измерений двумя, то все эти формулы будут не нужны. Начните с этого, попробуйте ;)

Sign up to leave a comment.