Сегодня мы создадим всем известную игру камень, ножницы, бумага. В этом нам поможет ЯП python и библиотека tkinter, но если вы не знаете что это такое, советую почитать данную статью.




Первое, что нам нужно, это начальная структура, окошко, у меня оно будет выглядеть так:


from tkinter import *
import random as rdm


class Main(Frame):
    def __init__(self, root):
        super(Main, self).__init__(root)
        self.startUI()

    def startUI(self):
        pass


if __name__ == '__main__':
    root = Tk()
    root.geometry("500x500+200+200")
    root.title("Камень, ножницы, бумага")
    root.resizable(False, False)
    root["bg"] = "#FFF"
    app = Main(root)
    app.pack()
    root.mainloop()

Здесь мы создаём неизменяемое окно 500 на 500 с заголовком «Камень, ножницы, бумага» и белым фоном. Именно в это окошко мы будем добавлять кнопочки, счетчики и т.д.



Теперь в наш метод startUI добавим такие строчки:


btn = Button(root, text="Камень", font=("Times New Roman", 15))
btn = Button(root, text="Ножницы", font=("Times New Roman", 15))
btn3 = Button(root, text="Бумага", font=("Times New Roman", 15))

btn.place(x=10, y=100, width=120, height=50)
btn2.place(x=155, y=100, width=120, height=50)
btn3.place(x=300, y=100, width=120, height=50)

Эти 7 строчек добавят в наше окно 3 кнопки которые нечего не делают. Мы исправим это позже.



Пользователь делает свой выбор, нажимая на одну из 3 кнопок, это круто, но нам нужен оппонент, именно для этого нужен модуль random.



А вот теперь мы добавим функцию, которая будет обрабатывать выбор, и выдавать ответ, кто же выиграл в этом раунде. Сделаем это вот таким образом:


btn = Button(root, text="Камень", font=("Times New Roman", 15),
    command=lambda x=1: self.btn_click(x))
btn2 = Button(root, text="Ножницы", font=("Times New Roman", 15),
    command=lambda x=2: self.btn_click(x))
btn3 = Button(root, text="Бумага", font=("Times New Roman", 15),
    command=lambda x=3: self.btn_click(x))

Что тут происходит?


Всё очень просто. Грубо говоря, если игрок нажмет камень, отправится 1, если ножницы, то 2, а если бумага, то 3, причем не только отправится, но и выведется в консоль.
На счет компьютера. Он свой выбор делает, но его выбор никуда не идёт.



Перед тем, как делать логику, нам нужно передать игроку результат, и для этого мы будем использовать Label. Добавим в startUI такие строчки:


self.lbl = Label(root, text="Начало игры!", bg="#FFF", 
    font=("Times New Roman", 21, "bold"))
self.lbl.place(x=120, y=25)

self.lbl2 = Label(root, justify="left", 
    font=("Times New Roman", 13),
    text=f"Побед: {self.win}\nПроигрышей:"
    f" {self.lose}\nНичей: {self.drow}",
    bg="#FFF")
self.lbl2.place(x=5, y=5)

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



Сделаем 3 счетчика:


  1. Поражений
  2. Побед
  3. Ничей



Для этого все в тот же startUI добавим такую строку:



self.win = self.drow = self.lose = 0

Теперь в классе main создаем метод btn_click, и пишем в него следующие строки:



def btn_click(self, choise):
    comp_choise = rdm.randint(1, 3)
    print(choise)

Недолго музыка играла. Там же, в btn_click, удаляем

print(choise)
и пишем вот эти строки:



if choise == comp_choise:
    self.drow += 1
    self.lbl.configure(text="Ничья")
elif choise == 1 and comp_choise == 2     or choise == 2 and comp_choise == 3     or choise == 3 and comp_choise == 1:
    self.win += 1
    self.lbl.configure(text="Победа")
else:
    self.lose += 1
    self.lbl.configure(text="Проигрыш")

self.lbl2.configure(text=f"Побед: {self.win}\nПроигрышей:"
    f" {self.lose}\nНичей: {self.drow}")

del comp_choise

Собственно всё, на этом создание закончилось. Всё работает, можно играть.



Полный код:


from tkinter import *
import random as rdm


class Main(Frame):
    def __init__(self, root):
        super(Main, self).__init__(root)
        self.startUI()

    def startUI(self):
        btn = Button(root, text="Камень", font=("Times New Roman", 15),
                     command=lambda x=1: self.btn_click(x))
        btn2 = Button(root, text="Ножницы", font=("Times New Roman", 15),
                      command=lambda x=2: self.btn_click(x))
        btn3 = Button(root, text="Бумага", font=("Times New Roman", 15),
                      command=lambda x=3: self.btn_click(x))

        btn.place(x=10, y=100, width=120, height=50)
        btn2.place(x=155, y=100, width=120, height=50)
        btn3.place(x=300, y=100, width=120, height=50)

        self.lbl = Label(root, text="Начало игры!", bg="#FFF", font=("Times New Roman", 21, "bold"))
        self.lbl.place(x=150, y=25)

        self.win = self.drow = self.lose = 0

        self.lbl2 = Label(root, justify="left", font=("Times New Roman", 13),
                         text=f"Побед: {self.win}\nПроигрышей:"
                              f" {self.lose}\nНичей: {self.drow}",
                         bg="#FFF")
        self.lbl2.place(x=5, y=5)

    def btn_click(self, choise):
        comp_choise = rdm.randint(1, 3)

        if choise == comp_choise:
            self.drow += 1
            self.lbl.configure(text="Ничья")
        elif choise == 1 and comp_choise == 2                 or choise == 2 and comp_choise == 3                 or choise == 3 and comp_choise == 1:
            self.win += 1
            self.lbl.configure(text="Победа")
        else:
            self.lose += 1
            self.lbl.configure(text="Проигрыш")

        self.lbl2.configure(text=f"Побед: {self.win}\nПроигрышей:"
                              f" {self.lose}\nНичей: {self.drow}")

        del comp_choise


if __name__ == '__main__':
    root = Tk()
    root.geometry("430x160+200+200")
    root.title("Камень, ножницы, бумага")
    root.resizable(False, False)
    root["bg"] = "#FFF"
    app = Main(root)
    app.pack()
    root.mainloop()

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


  1. justK
    18.12.2019 12:00

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

    Вот скажите мне, человеку, не говорящему на парселтанге, зачем нужен tkinter? (нет, я прочитал, что это библиотека для рисования окошек, но можно было бы в двух словах упомянуть про это и дальше уже сослаться на статью за деталями)
    Почему координаты элементов заданы прямо в их свойствах? не разумнее ли вынести их в переменные в начале программы? и, как я понял, tkinter умеет сам располагать элементы относительно друг друга используя .pack() или .grid(), почему именно .place()? из-за задания их размера?
    Зачем нужен if __name__ == '__main__':? без него не заработает? (действительно не знаю)
    Почему логика самой игры в том же цикле, что и создание окошечка? Можно ли вынести логику игры в отдельный цикл и перерисовывать только отдельные элементы по необходимости?
    Зачем писать print(choise) для кнопки если он сразу же будет удалён? Если для отладки куда-то в консоль, то можно было бы и упомянуть об этом.
    Может быть стоит скрыть полный листинг под спойлер?

    P.S. я не пытаюсь разгромить пост, я действительно пытаюсь понять, почему сделано именно так: может быть есть веские причины, которые я не понимаю, а может быть просто этот код скорее proof of concept, показывающий, что такое вообще возможно сделать лёгким движением руки.


    1. alexyr
      18.12.2019 12:28

      Зачем нужен if __name__ == '__main__':? без него не заработает?
      без него не заработает, но и так тоже «фу-фу-фу»
      надо делать функцию main со всей этой логикой и использовать
      if __name__ == '__main__':
          main()


    1. justhabrauser
      18.12.2019 14:27

      Зачем нужен if name == 'main':? без него не заработает?

      Добавлю к предыдущему оратору.
      Таки да — лучше написать if <тратата> main().
      А просто с первой колонки не писать ничего.
      Тогда при запуске программы будет выполняться это вот main().
      А вот при импорте как модуля — ничего выполняться не будет.
      Ну такой вот стиль хорошего тона.


  1. andreyons
    18.12.2019 12:32

    Если статья рассчитана на новичков, то у меня 2 пункта:
    1. Python 2.7? Он уже все :)
    2. Вся статья это вставки кода и между ними текст вроде «и пишем вот эти строки:». Это мало что прояснит новичку.


    1. megagnom37
      19.12.2019 14:49

      Ещё не всё. В январе только прекратят работать над последним релизом, а в апреле хотят выложить этот релиз (2.7.18, если я не ошибаюсь).


      P.S.: Я за Python 3 :)


  1. alexyr
    18.12.2019 12:38

    from tkinter import *
    не надо так, используйте только необходимые и явно перечисленные импорты (Frame, Button, ...)
    del comp_choise
    зачем? никакого смысла в этой строчке не вижу
    я уж не говорю про разделение кода на более мелкие функции, например код выяснения кто победил вынести в
    def check_result(self, user_choice, comp_choice): ...


  1. GCU
    18.12.2019 23:53

    Choice, draw, ничего, ничьих.


    1. LarinSasha Автор
      19.12.2019 01:26

      Спасибо, в этой статье уже не буду исправлять, в следующих учту.