Приветствую всех хабровчан! Я являюсь студенткой Финансового Университета при Правительстве РФ и обучаюсь на направлении Прикладная математика и информатика. Сегодня мне хотелось бы поделиться своим туториалом для изучающих Python с нуля. Идея для создания этой статьи появилась на паре практикума по программированию, где данная задача была проектом для сдачи. Если этот туториал зайдет, то будут следующие выпуски.

В этом уроке мы напишем несложную консольную игру Супер Ним по следующим правилам:

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

В нашем случае примем за фишки/пуговицы единицы, а пустые клетки станут нулями. Создадим шахматную доску при помощи списков и random:

from random import randint
print('  '+'a',' '+'b',' '+'c',' '+'d',' '+'e',' '+'f',' '+'g',' '+'h')
list_board = [[],[],[],[],[],[],[],[]]
for el in list_board:
  for i in range(8):
      el.append(randint(0,1))

В данном коде мы создаем доску с рандомно заданными нулями и единицами, а также выводим верхние координаты от a до h.

Теперь создадим координаты для горизонталей(от 1 до 8) и свяжем все буквы и числа с нашими полями:

for idx,i in enumerate(list_board):
  print((chr(ord('1')+idx))+str(i))
x={'a':0,'b':1,'c':2,'d':3,'e':4,'f':5,'g':6,'h':7}
y={'1':0, '2':1, '3':2, '4':3, '5':4, '6':5, '7':6, '8':7}

Попробуем написать логику ввода координат обоими игроками:

arr=[]
def play():
  a=""
  while a!="Игрок 1 победил" and a!="Игрок 2 победил":
      if 1 in list_board[0] or 1 in list_board[1] or 1 in list_board[2] or 1 in list_board[3] or 1 in list_board[4] or 1 in list_board[5] or 1 in list_board[6] or 1 in list_board[7]:
        print("\nИгрок 1 вводит координаты")
        letter=input()
        f(letter)
        arr.append(1)
      if 1 in list_board[0] or 1 in list_board[1] or 1 in list_board[2] or 1 in list_board[3] or 1 in list_board[4] or 1 in list_board[5] or 1 in list_board[6] or 1 in list_board[7]:
        print("\nИгрок 2 вводит координаты")
        letter=input()
        f(letter)
        arr.append(2)
      else:
        if arr[-1]==1:
          a="Игрок 1 победил"
        else:
          a="Игрок 2 победил"
      print(a)

Здесь функция будет проверять, остались ли еще единицы на поле и выводить победителя в случае их отсутствия. Если же они остались, то функция реализует чередование ввода координат игроками. Кроме того, мы создадим некую "базу данных" в виде списка, который хранит в себе номер игрока, который ввел координаты. Именно проверяя последний элемент данного списка, можно определить победителя. Как мы видим, в функции play вызывается функция f, которая принимает за аргумент букву, которую ввел игрок. Давайте ее напишем:

def f(letter):
  string_1 = "abcdefgh"
  string_2="12345678"
  if letter in string_1:
    for i in list_board:
      i[x[letter]]= 0
  if letter in string_2:
    list_board[y[letter]]=[0]*8
  
  print('  '+'a',' '+'b',' '+'c',' '+'d',' '+'e',' '+'f',' '+'g',' '+'h')
  for idx,i in enumerate(list_board):
    print((chr(ord('1')+idx))+str(i))

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

После всего написанного кода мы можем запустить функцию play() и играть. Спасибо за прочтение, надеюсь, данный урок будет полезен новичкам!

Для тех, кто хочет прокачать свои навыки использования python, предлагаю сделать следующее:

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

  • попробовать вывести верхние координаты циклом for(как цифры сбоку)

  • сократить код посредством избавления от большого количества if/else

Комментарии (27)


  1. dyadyaSerezha
    01.10.2023 16:29
    +2

    Очень много замечаний по коду. Но просто ввергло в недоумение, зачем использовать конкатенацию тут??

    print(' '+'a',' '+'b' . . .

    В-)


    1. R0bur
      01.10.2023 16:29
      +1

      Для тех, кто хочет прокачать свои навыки использования python, предлагаю сделать следующее:

      ...

      • избавиться от конкатенации в print(' '+'a', ' '+'b, . . .

      ;-)


  1. ImCocos
    01.10.2023 16:29

    Незначительное замечание, лучше перебирать лист через i, item чем через idx, все же общепринято иметь i как индекс элемента


  1. kaibezdushniy
    01.10.2023 16:29

    В этом коде вы часто используете list[0] list[1] вынести их в переменные, и разбейте код на функций, а то так код не очень читаемый)


  1. R0bur
    01.10.2023 16:29
    +5

    У строк исходного текста, приведенных в этом "туториале", есть одно неоспоримое достоинство: будучи сохранёнными в файл они образуют программу, которая работает. Кроме того, некоторая наивность текста программы служит доказательством, что она явилась результатом самостоятельного труда студента, а не сгенерирована, например, Chat-GPT. Благодаря этому она может дать просто массу тем для обсуждения, на её примере можно объяснить многие не совсем очевидные новичкам вопросы.

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


    1. Tzimie
      01.10.2023 16:29
      +2

      Согласен. На этом учатся. Автору - дерзайте


  1. Andrey_Solomatin
    01.10.2023 16:29

    Я являюсь студенткой Финансового Университета при Правительстве РФ и
    обучаюсь на направлении Прикладная математика и информатика.


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

    Формально код работает, но многие вещи можно и нужно сделать намного лучше и проще.

    Простое упражнение, берёте случайного знакомого и спрашиваете его (не показывая код), что делает переменная string_1 в функции f? Если он отвечает что буквы для столбцов шахматной доски, то хорошо. Если нет, то переименовываете и пробуете снова. После нескольких эксперементов на людях можно перейти на резиновую уточку, а потом и вообще на вооброжаемую. Но каждый раз этот вопрос должен быть задан для каждого имени в коде, перед тем как опубликовать код.

    Минусов вам накидали за дело, но не сдавайтесь, перепишите код, с учётом комментариев. В общем обучение это череда ошибок и работа над ними.

    Попробуйте поспрашивать чат GPT по поводу, что можно сделать с кодом и почему так лучше.


    1. GospodinKolhoznik
      01.10.2023 16:29
      +3

      Для такой маленькой функции любые названия сгодятся. Можно было даже короче. Я бы их назвал cs и ns. Ну может быть chars и nums. И точно не ChessBoardHorozintalCoordinatLiteralIndexList. Потому что функция маленькая и все наглядно. А вот то, что функция маленькая и понятная, это заслуга авторши, это гораздо важнее, чем нэйминг локальной константы. Но саму функцию f лучше было как то нагляднее назвать, это несомненно.


      1. Andrey_Solomatin
        01.10.2023 16:29

        Для такой маленькой функции любые названия сгодятся


        Нет.

        И точно не ChessBoardHorozintalCoordinatLiteralIndexList

        Это какая-то фантазия из мира Джавы или С#. Там даже не список.

        chars и nums это про то что внутри, я бы смотрел на ипользование. Например rows и cols.

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

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

        Но на самом деле не коротко и не понятно. Обращение к переменным определённым снаружи, два раза буквы объявлены.


        1. GospodinKolhoznik
          01.10.2023 16:29

          Когда я только начинал изучать Хаскель у меня тоже бомбило от тамошнего повсеместного названия переменных как x, y, xs и ys. Потом, со временем , пришло просветление, что для маленьких изолированных функций это то, что надо. Особенно, если это функции, применяемые к обобщённым типам данных. В общем шаблоны нейминга это хорошо, но как и всякие шаблоны их надо применять не бездумно, а исходя из целесообразности в каждом конкретном случае. В данном случае существующие названия двух списков (списков по сути, а не по имплементации) букв и цифр вполне хорошие, т.к. свою задачу прекрасно выполняют и никаких трудностей не создают. А вот наименование функции не очень хорошее.


          1. Andrey_Solomatin
            01.10.2023 16:29

            Особенно, если это функции, применяемые к обобщённым типам данных


            Если это низкоуровневая функция, которая работает с абстрактными данными, то это норм. Особенно если в проекте так принято. В стандартной библиотеке Питона такое используется, например имя s. https://github.com/python/cpython/blob/main/Lib/string.py#L37

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


      1. domix32
        01.10.2023 16:29

        ага, главное научиться не использовать ключевые слова в качестве имён переменных


        1. Andrey_Solomatin
          01.10.2023 16:29

          Просто надо использовать линтеры, а остальное приложится.

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

          https://docs.astral.sh/ruff/rules/builtin-variable-shadowing/


    1. vladis005 Автор
      01.10.2023 16:29

      Советую вам данный материал для того, чтобы разобраться с условиями и циклами Цикл for в Python — как работает, синтаксис, примеры (pythonchik.ru) и Условный оператор - Основы Python (yandex.ru) . String_1 не делает буквы в данном коде, а используется для проверки, чтобы затем функция f превратила единицы в нули по заданной координате


  1. SuperGarry
    01.10.2023 16:29
    +1

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


  1. zaxarious
    01.10.2023 16:29
    +3

    Щас бы встроенный list своим перекрывать.)


    1. Andrey_Solomatin
      01.10.2023 16:29

      Линтеры такие вещи ловять на отлично.


      1. zaxarious
        01.10.2023 16:29

        Как видишь, авторке это не помогло.))


        1. Andrey_Solomatin
          01.10.2023 16:29

          Так она их и не использует. Не знаю это умышленно или просто в вузе не научили. Скорее второе.


          1. vladis005 Автор
            01.10.2023 16:29

            Я не использовала линтеры, потому что писала это в google colab


            1. Andrey_Solomatin
              01.10.2023 16:29

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


              1. vladis005 Автор
                01.10.2023 16:29

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


                1. Andrey_Solomatin
                  01.10.2023 16:29

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


                  Кто хочет найти решение, тот его найдёт.

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


          1. vladis005 Автор
            01.10.2023 16:29

            Кстати, это будет отличной темой для вашего собственного туториала. Напишите статью на тему того, как подключить какой-нибудь линтер в гугл колаб. Ждем!


            1. Andrey_Solomatin
              01.10.2023 16:29

              Мне эта тема не интересна, не пользуюсь колабом.


              1. vladis005 Автор
                01.10.2023 16:29

                Зря


  1. nameisBegemot
    01.10.2023 16:29

    Немного сахару:

    Вместо

    list_board = [[],[],[],[],[],[],[],[]]

    Можно так

    list_board = [[] for _ in range(5)]