Уведомления

Группа в Telegram: @pythonsu

#1 Июль 19, 2018 18:11:18

rami
Зарегистрирован: 2018-01-08
Сообщения: 280
Репутация: +  71  -
Профиль   Отправить e-mail  

Крестики-нолики

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

Клетки выделять с помощью рельефа и бордюра (borderwidth=3, relief='raised'), padx и pady лучше убрать.





Должно быть примерно так:

Офлайн

#2 Июль 19, 2018 21:30:16

anickone
Зарегистрирован: 2018-07-17
Сообщения: 9
Репутация: +  1  -
Профиль   Отправить e-mail  

Крестики-нолики

rami
Ужас.
Полностью согласен, это уже не программирование а какая-то кросс-браузерная верстка. Короче, у меня все нормально отображается и сейчас и в первый раз. Файл прикрепил, как оно у меня выглядит. Предлагаю, если вы имеете опыт такой разработки, сделайте и разместите здесь код, если у всех он будет нормально отображаться, я просто перейму ваш стиль организации разметки. А так это вряд ли закончится.

Отредактировано anickone (Июль 19, 2018 21:31:15)

Прикреплённый файлы:
attachment img.png (20,3 KБ)

Офлайн

#3 Июль 19, 2018 22:11:25

Vigi
От: Курья, Алтай
Зарегистрирован: 2015-02-07
Сообщения: 144
Репутация: +  8  -
Профиль   Отправить e-mail  

Крестики-нолики

anickone
Переписал под grid. У кого в интерфейсе были сползания, посмотрите что сейчас?
нормуль

Офлайн

#4 Июль 22, 2018 17:43:19

anickone
Зарегистрирован: 2018-07-17
Сообщения: 9
Репутация: +  1  -
Профиль   Отправить e-mail  

Крестики-нолики

Vigi
нормуль
Спасибо за ответ, для меня это важно было услышать. Также рад, что вам по силам обойти зашитую в игру стратегию, в отличии от меня. PEHDOM это поправил) Поделился выигрышными стратегиями со мной и теперь я тоже могу выигрывать)))

rami
Вы поделитесь своим кодом? Я по вашему скриншоту вижу, что вы его написали. Это не мой дизайн. Привязывать вашу разметку к коду не обязательно. Просто хочется увидеть ваш подход к разметке. Мне ждать? Или забыть?)

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

Офлайн

#5 Июль 22, 2018 23:13:11

rami
Зарегистрирован: 2018-01-08
Сообщения: 280
Репутация: +  71  -
Профиль   Отправить e-mail  

Крестики-нолики

anickone
rami
Вы поделитесь своим кодом? Я по вашему скриншоту вижу, что вы его написали. Это не мой дизайн. Привязывать вашу разметку к коду не обязательно. Просто хочется увидеть ваш подход к разметке.
Поделюсь, но если честно, после ваших слов:
anickone
Полностью согласен, это уже не программирование а какая-то кросс-браузерная верстка. Короче, у меня все нормально отображается и сейчас и в первый раз.
расхотелось обсуждать эту тему. Если вы любитель и программируете только для себя, то получилось довольно хорошо, я рад, что у вас всё отображается как вы хотите, но если вы хотите быть профессиональным программистом, то так говорить нельзя. Проблема не в том, что у меня комп кривой, а в том, что вы программируете “для себя”, а не “для людей”. Корень проблемы в разных настройках по умолчанию: раньше более крупный шрифт не вмещался в строго ограниченную рамку, теперь чтобы вместить текст одна из рамок увеличивается сама и растягивает игровые ячейки в своём столбце, т.е. проблема как была, так и осталась, только видоизменилась. Решить эту проблему можно и нужно с помощью одного единственного фрейма, который будет включать в себя либо все рамки и кнопку (так проще), а девять игровых ячеек останутся в главном окне, либо наоборот (так правильнее, но чуть сложнее) игровые ячейки будут в отдельном фрейме, а всё остальное в главном окне. Теперь, даже если мы увеличим в несколько раз ширину какой-либо рамки это никак не отобразится на игровых ячейках, так как у них будут независимые системы столбцов.

Ваш код в целом, работает не плохо если на него не смотреть, но если попытаться его осмыслить, то это не только “ужас”, но и “кошмарный сон программиста”.

В моём коде я привёл в порядок “игровые” функции без изменения стратегии, хотя, эту часть нужно переписывать заново с нуля. Интерфейсную часть переписал под виджеты ttk, они мне нравятся больше. Если что будет не понятно, спрашивайте.

 import tkinter as tk
import tkinter.ttk as ttk
from random import choice,randint,sample,shuffle
    
OX = ['O', 'X']
VINS = ((0, 1, 2), (3, 4, 5), (6, 7, 8), (0, 3, 6), (1, 4, 7), (2, 5, 8), (0, 4, 8), (2, 4, 6))
PS = [[0, 2, 6, 8], [1, 3, 5, 7]]
S='Сыграно игр: {}.\nИз них:\n    побед - {};\n    поражений - {};\n    ничьих - {}.'
    
class Square(tk.Label):
    '''Создаёт ячейки для игры "Крестики - Нолики"'''
    def __init__(self, fr, app, c, r, num, **kwargs):
        self.app = app
        self.square_num = num
        kwargs.update({'font': 'Menlo 100', 'bd': 3, 'anchor': 'center', 'relief': 'raise', 'width': 2})
        tk.Label.__init__(self, fr, **kwargs)
        self.grid(column=c, row=r+2, sticky='nsew')
    
    def bind_(self):
        '''Назначает ячейке событие "Нажатие мышкой" с передачей номера ячейки в вызываемую функцию'''
        self.bind("<Button-1>", lambda x: App.turn_user(self.app, self.square_num))
    
    
class App(tk.Frame):
    def __init__(self, master):
        super(App, self).__init__(master)
        # статистика игры
        self.drawn_game = 0
        self.user_win = 0
        self.pc_win = 0
        self.create_widgets()
    
    def create_widgets(self):
        # статистика
        statistics = ttk.LabelFrame(self, text='Статистика')
        statistics.grid(column=0, row=0, rowspan=2, sticky='nsew')
        self.stat_msg = tk.StringVar(value=S.format(0,0,0,0))
        ttk.Label(statistics, textvariable=self.stat_msg, justify='left').grid()
    
        # Выбор хода
        step_choice = ttk.LabelFrame(self, text='Выбрать ход')
        step_choice.grid(column=1, row=0, rowspan=2, sticky='nsew')
        self.step = tk.IntVar()
        step0 = ttk.Radiobutton(step_choice, text='Случайным', variable=self.step, value=0)
        step1 = ttk.Radiobutton(step_choice, text='Первым', variable=self.step, value=1)
        step2 = ttk.Radiobutton(step_choice, text='Вторым', variable=self.step, value=2)
        step0.grid(column=0, row=0, sticky='we')
        step1.grid(column=0, row=1, sticky='we')
        step2.grid(column=0, row=2, sticky='we')
    
        # Выбор знака
        sign_choice = ttk.LabelFrame(self, text='Выбор знака')
        sign_choice.grid(column=2, row=0, sticky='nsew')
        self.sign = tk.IntVar()
        sign0 = ttk.Radiobutton(sign_choice, text='Ваш знак "0"', variable=self.sign, value=0)
        sign1 = ttk.Radiobutton(sign_choice, text='Ваш знак "X"', variable=self.sign, value=1)
        sign0.grid(column=0, row=0, sticky='we')
        sign1.grid(column=0, row=1, sticky='we')
        # кнопка старт
        btn = ttk.Button(self, text="Старт", command=self.start)
        btn.grid(column=2, row=1, sticky='nsew')
    
        # Статус игры
        status = ttk.LabelFrame(self, text='Статус игры')
        status.grid(column=0, row=2, columnspan=3, sticky='nsew')
        self.status_msg = tk.StringVar(value='Добро пожаловать в игру "Крестики - Нолики"!')
        ttk.Label(status, textvariable=self.status_msg).grid()
    
        self.widgets = [sign0, sign1, step0, step1, step2, btn]  #все радиокнопки и кнопка для активации и деактивации
    
        fr=tk.Frame(self)
        fr.grid(column=0, row=3, columnspan=3)
        self.squares = [Square(fr, self, i % 3, i // 3, i) for i in range(9)]  #список полей
    
    def start(self):
        self.continue_ = True
        self.board = [''] * 9
        self.free_squares = [x for x in range(9)]
        self.status_msg.set('Осторожно! Игра активирована! Удачи!')
        for square in self.squares:
            square.bind_()
            square.config(background='white', text='')
        if self.step.get() == 2:
            self.turn_pc()
        elif self.step.get() == 0:
            if choice([1, 2]) == 2:
                self.turn_pc()
        self.widget_state()
    
    def widget_state(self, wst='disabled'):
        for widget in self.widgets:
            widget.config(state=wst)
    
    def find_best_turn(self, sign):
        for iturn in self.free_squares:
            iboard = self.board[:]
            iboard[iturn] = sign
            for a, b, c in VINS:
                if iboard[a] == iboard[b] == iboard[c] == sign:
                    return iturn
    
    def default_choice(self):
        choise = [4]
        for i in PS:
            shuffle(i)
            choise.extend(i)
        for iturn in choise:
            if iturn in self.free_squares:
                return iturn
    
    def turn_pc(self):
        ''' ход пк '''
        square_num = self.find_best_turn(not self.sign.get())
        if square_num == None:
            square_num = self.find_best_turn(self.sign.get())
            if square_num == None:
                square_num = self.default_choice()
        self.turn(square_num, not self.sign.get())
    
    def turn(self, square_num, sign):
        square = self.squares[square_num]
        square.unbind("<Button-1>")             #отключает ячейку
        square.config(text=OX[sign])            #пишет символ в ячейку
        self.board[square_num] = sign
        self.free_squares.remove(square_num)
        self.test_win(sign)
    
    def turn_user(self, square_num):
        ''' ход игрока '''
        self.turn(square_num, self.sign.get())
        if self.continue_:
            self.turn_pc()          #передача хода компьютеру
    
    def test_win(self, sign):
        ''' определение исхода игры '''
        for a, b, c in VINS:
            if self.board[a] == self.board[b] == self.board[c] == sign:
                if sign == self.sign.get():
                    self.status_msg.set('Поздравляю с победой!')
                    self.user_win += 1
                    color = 'green'
                else:
                    self.status_msg.set('Победа за компьютером! Ну как так?!!!')
                    self.pc_win += 1
                    color = 'red'
                for i in a, b, c:
                    self.squares[i].config(background=color)    #окраска выигравших или проигравших ячеек
                for i in self.free_squares:
                    self.squares[i].unbind("<Button-1>")        #в случае выигрыша делает оставшиеся ячейки не активными
                self.game_over()
                return
    
        if not self.free_squares:
            self.status_msg.set('Ничья. Вы точно можете лучше!')
            self.drawn_game += 1
            [square.config(background='yellow') for square in self.squares]
            self.game_over()
    
    def game_over(self):
        ''' окончание игры '''
        self.continue_ = False
        self.stat_msg.set(S.format(self.user_win + self.pc_win + self.drawn_game, self.user_win, self.pc_win, self.drawn_game))
        self.widget_state('normal')
    
    
def main():
    root = tk.Tk()
    root.title('Игра "Крестики - Нолики"')
    root.resizable(False, False)
    App(root).grid()
    root.mainloop()
    
if __name__=='__main__':
    main()

Офлайн

#6 Июль 24, 2018 23:36:52

anickone
Зарегистрирован: 2018-07-17
Сообщения: 9
Репутация: +  1  -
Профиль   Отправить e-mail  

Крестики-нолики

Спасибо, шикарный код. Еще долго буду его изучать. И возможно, когда-то смогу так писать. Понял, что я произвел гк). Ну тут как в анекдоте, главное чтобы его было как можно больше. А также были поводы и силы производить его в не ограниченном количестве. И тогда мне будет, чем поделиться))) А ведь очень хочется)))

Офлайн

#7 Июль 25, 2018 17:32:06

rami
Зарегистрирован: 2018-01-08
Сообщения: 280
Репутация: +  71  -
Профиль   Отправить e-mail  

Крестики-нолики

anickone
Спасибо, шикарный код. Еще долго буду его изучать. И возможно, когда-то смогу так писать.
До шика ещё далеко… Ваш класс Square неправильный и бессмысленный, мой класс Square правильный, но такой же бессмысленный как и ваш. Какой смысл создавать сильно урезанный по функционалу класс (как вы сделали) или класс который почти полностью совпадает с классом Label (как сделал я)? Если то, что нам нужно есть в других доступных модулях, то не стоит изобретать велосипед. Можем ли мы создать класс которого нет в других модулях? Посмотрим на ваш интерфейс — в нём есть три группы объектов состоящих из одинаковых виджетов: это блок статистики, блок статуса игры и игровые ячейки, — все они состоят из одного или нескольких Label и рамки. Для них мы сделаем класс Cells (ячейки в рамке). Ещё у вас есть два блока радиокнопок, для них мы создадим класс Radiogrup (радиокнопки в рамке). Оба эти класса поместим в отдельный документ с именем mywidgets.py, в той же папке, что и файл с игрой “Крестики — Нолики”.
 #Это код для файла mywidgets.py
import tkinter as tk
import tkinter.ttk as ttk
    
class Cells(ttk.LabelFrame):
    '''Создаёт блок ячеек по числу столбцов cols и строк rows'''
    def __init__(self, root, cols=1, rows=1, title='', text='', justify='left', font='Menlo 13', bd=0, *args, **kwargs):
        ttk.LabelFrame.__init__(self, root, text=title, *args, **kwargs)
        self.grup = []
        for i in range(cols*rows):
            cell=tk.Label(self, text=text, font=font, justify=justify, **kwargs)
            cell.grid(column=i%cols, row=i//cols, sticky='nsew')
            self.grup.append(cell)
    
    def bind_(self, event, func, n):
        '''Назначает ячейке событие event с передачей номера ячейки n в вызываемую функцию func'''
        self.grup[n].bind(event, lambda x: func(n))
    
    
class Radiogrup(ttk.LabelFrame):
    '''Создаёт именованную группу радиокнопок для каждого элемента в items'''
    grup = []
    def __init__(self, root, items=tuple(), *args, **kwargs):
        ttk.LabelFrame.__init__(self, root, *args, **kwargs)
        self.var = tk.IntVar()
        for i,text in enumerate(items):
            rb=ttk.Radiobutton(self, text=text, variable=self.var, value=i)
            rb.grid(sticky='we')
            self.grup.append(rb)

Теперь код для интерфейса игры “Крестики — Нолики” станет проще:
 import tkinter as tk
import tkinter.ttk as ttk
import mywidgets as mw      #модуль mywidgets содержит классы Cells и Radiogrup, должен находиться в той же папке, что и текущий файл
from random import choice,randint,sample,shuffle
    
OX = ['O', 'X']
VINS = ((0, 1, 2), (3, 4, 5), (6, 7, 8), (0, 3, 6), (1, 4, 7), (2, 5, 8), (0, 4, 8), (2, 4, 6))
PS = [[0, 2, 6, 8], [1, 3, 5, 7]]
S='Сыграно игр: {}.\nИз них:\n    побед - {};\n    поражений - {};\n    ничьих - {}.'
    
    
class App(tk.Frame):
    '''Создаёт интерфейс и стратегию в игре "Крестики-Нолики"'''
    user_win=pc_win=drawn=0
    def __init__(self, master):
        super(App, self).__init__(master)
        self.create_widgets()
    
    def create_widgets(self):
        '''Размещение виджетов в окне'''
        # Статистика игр
        self.info = mw.Cells(self, title='Статистика', text=S.format(0,0,0,0))
        self.info.grid(column=0, row=0, rowspan=2, sticky='nsew')
        # Radiogrup Выбор хода
        self.step = mw.Radiogrup(self, text='Выбрать ход', items=('Случайным','Первым','Вторым'))
        self.step.grid(column=1, row=0, rowspan=2, sticky='nsew')
        # Radiogrup Выбор знака
        self.sign = mw.Radiogrup(self, text='Выбор знака', items=('Ваш знак "0"','Ваш знак "X"'))
        self.sign.grid(column=2, row=0, sticky='nsew')
        # кнопка старт
        self.btn = ttk.Button(self, text="Старт", command=self.start)
        self.btn.grid(column=2, row=1, sticky='nsew')
        # Статус игры
        self.stat = mw.Cells(self, title='Статус игры', text='Добро пожаловать в игру "Крестики - Нолики"!')
        self.stat.grid(column=0, row=2, columnspan=3, sticky='nsew')
        # Игровые ячейки
        self.cells = mw.Cells(self, cols=3, rows=3, bd=3, relief='raise', width=2, font='Menlo 100')
        self.cells.grid(column=0, row=3, columnspan=3)
    
    def start(self):
        '''Запуск игры'''
        self.continue_ = True
        self.board = [''] * 9
        self.free_cells = list(range(9))
        self.stat.grup[0].config(text='Осторожно! Игра активирована! Удачи!')
        for i in range(9):
            self.cells.bind_("<Button-1>", self.turn_user, i)   #привязка ячейки к событию "<Button-1>"
            self.cells.grup[i].config(bg='white', text='')
        if self.step.var.get() == 2:
            self.turn_pc()
        elif self.step.var.get() == 0:
            if choice([1, 2]) == 2:
                self.turn_pc()
        self.widget_state()
    
    def widget_state(self, wst='disabled'):
        '''Отключение элементов управления во время игры и включение после'''
        [i.config(state=wst) for i in [*mw.Radiogrup.grup, self.btn]]
    
    def find_best_turn(self, sign):
        for iturn in self.free_cells:
            iboard = self.board[:]
            iboard[iturn] = sign
            for a, b, c in VINS:
                if iboard[a] == iboard[b] == iboard[c] == sign:
                    return iturn
    
    def default_choice(self):
        choise = [4]
        for i in PS:
            shuffle(i)
            choise.extend(i)
        for iturn in choise:
            if iturn in self.free_cells:
                return iturn
    
    def turn_user(self, cell_num):
        '''Ход игрока'''
        self.turn(cell_num, self.sign.var.get())
        if self.continue_:
            self.turn_pc()          #передача хода компьютеру
    
    def turn_pc(self):
        '''Ход компьютера'''
        cell_num = self.find_best_turn(not self.sign.var.get())
        if cell_num == None:
            cell_num = self.find_best_turn(self.sign.var.get())
            if cell_num == None:
                cell_num = self.default_choice()
        self.turn(cell_num, not self.sign.var.get())
    
    def turn(self, cell_num, sign):
        '''Обработка текущего хода и определение исхода игры'''
        self.cells.grup[cell_num].unbind("<Button-1>")  #отключает ячейку
        self.cells.grup[cell_num].config(text=OX[sign]) #пишет символ в ячейку
        self.board[cell_num] = sign                     #пишет символ в список ходов
        self.free_cells.remove(cell_num)                #удаляет номер из списка свободных ячеек
        for a, b, c in VINS:
            if self.board[a] == self.board[b] == self.board[c] == sign:
                if sign == self.sign.var.get():
                    self.stat.grup[0].config(text='Поздравляю с победой!')
                    self.user_win += 1
                    color = 'green'
                else:
                    self.stat.grup[0].config(text='Победа за компьютером! Ну как так?!!!')
                    self.pc_win += 1
                    color = 'red'
                [self.cells.grup[i].config(bg=color) for i in (a, b, c)]    #окраска выигравших или проигравших ячеек
                [self.cells.grup[i].unbind("<Button-1>") for i in self.free_cells]  #деактивирует оставшиеся ячейки
                self.game_over()
                return
        #если не было выигрышной комбинации, проверяем наличие свободных ячеек, если их нет — ничья
        if not self.free_cells:
            self.stat.grup[0].config(text='Ничья. Вы точно можете лучше!')
            self.drawn += 1
            [cell.config(background='yellow') for cell in self.cells.grup]
            self.game_over()
    
    def game_over(self):
        '''Окончание игры'''
        self.continue_ = False
        self.info.grup[0].config(text=S.format(self.user_win + self.pc_win + self.drawn, self.user_win, self.pc_win, self.drawn))
        self.widget_state('normal')
    
    
def main():
    root = tk.Tk()
    root.title('Игра "Крестики - Нолики"')
    root.resizable(False, False)
    App(root).grid()
    root.mainloop()
    
if __name__=='__main__':
    main()

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version