Seite 2 von 2

Re: Button aktiviert sich automatisch

Verfasst: Dienstag 27. März 2018, 10:44
von Sirius3
@wuf: Fensterposition und -größe läßt man am besten automatisch ermitteln. Die Shebang-Zeile ist für Python3 falsch.

Code: Alles auswählen

from itertools import cycle
from functools import partial
import tkinter as tk
 
FENSTER_TITLE = "TickTackToe Spiel"
 
class TickTackToe(object):
    LEER = "leer.png"
    KREIS = "kreis.png"
    KREUZ = "kreuz.png"
    REIHEN = [
        [(0,0), (0,1), (0,2)],
        [(1,0), (1,1), (1,2)],
        [(2,0), (2,1), (2,2)],
        [(0,0), (1,0), (2,0)],
        [(0,1), (1,1), (2,1)],
        [(0,2), (1,2), (2,2)],
        [(0,0), (1,1), (2,2)],
        [(0,2), (1,1), (2,0)],
    ]
 
    def __init__(self, fenster):
        self.fenster = fenster
        self.images = {
            None: tk.PhotoImage(file=self.LEER),
            'Kreis': tk.PhotoImage(file=self.KREIS),
            'Kreuz': tk.PhotoImage(file=self.KREUZ),
        }
        self.knoepfe = dict()
        self.runden = 0
        self.spieler = cycle(['Kreis', 'Kreuz'])
        self.feld_bauen()
       
    def feld_bauen(self):
        self.melde_text_var = tk.StringVar(self.fenster, "Neues Spiel")
        meldung = tk.Label( self.fenster, textvariable=self.melde_text_var,
            font=('Helvetica', 12, 'bold'))
        meldung.pack(pady=4)
       
        knopf_rahmen = tk.Frame(self.fenster)
        knopf_rahmen.pack()
 
        for reihe in range(3):
            for zeile in range(3):
                knopf_widget = tk.Button(knopf_rahmen, image=self.images[None],
                    highlightthickness=0, bg='white', command=partial(self.knopf_druck, (reihe, zeile)))
                knopf_widget.grid(row=reihe, column=zeile)
                knopf_widget.status = None
                self.knoepfe[reihe, zeile] = knopf_widget
 
        self.reset_button = tk.Button(self.fenster, text="Wiederholung?",
            bg="yellow", command=self.reset_command)

    def set_status(self, key, status):
        self.knoepfe[key].status = status
        self.knoepfe[key]['image'] = self.images[status]
       
    def reset_command(self):
        for key in self.knoepfe:
            self.set_status(key, None)
        self.runden = 0
        self.melde_text_var.set("Neues Spiel")
        self.reset_button.pack_forget()
       
    def knopf_druck(self, knopf):
        print("Knopf gedrückt:", knopf)
        status = next(self.spieler)
        self.set_status(knopf, status)
        if any(all(self.knoepfe[r].status == status for r in row)
                for row in self.REIHEN):
            self.melde_text_var.set("Gewinner: {}".format(status))
            self.reset_button.pack(pady=4)
        else:
            self.runden += 1
            if self.runden==9:
                self.melde_text_var.set("Unentschieden")
                self.reset_button.pack(pady=4)
       
def main():
    fenster = tk.Tk()
    fenster.title(FENSTER_TITLE)
    app = TickTackToe(fenster)
    fenster.mainloop()
 
if __name__ == '__main__':
    main()

Re: Button aktiviert sich automatisch

Verfasst: Dienstag 27. März 2018, 11:35
von wuf
Hi Sirius3

Besten Dank für deine Hinweise und deine lehrreiche Optimierung des Skripts. Gehe ich richtig in der Annahme das die Shebang-Zeile:
#!/usr/bin/env python3
und die folgende Zeile:
# -*- coding: utf-8 -*-
in einem Python3-Skript nicht mehr benötigt sind?

Gruss wuf :wink:

Re: Button aktiviert sich automatisch

Verfasst: Donnerstag 29. März 2018, 18:26
von wuf
@Sirius3: Bei deiner Variante habe ich festgestellt, dass sich ein Belegter Knopf während dem Spiel wieder belegen lässt. Steht ein Gewinner fest lassen sich Knöpfe weiterhin belegen bevor das Spiel mit der Schaltfläche Wiederholung für ein neues Spiel freigegeben wurde.

Meine optimierte Variante sieht wir folgt aus:

Code: Alles auswählen

from itertools import cycle
from functools import partial
import tkinter as tk
 
FENSTER_TITLE = "TickTackToe Spiel"

class WufTickTackToe(object):
    LEER = "leer.png"
    KREIS = "kreis.png"
    KREUZ = "kreuz.png"
    REIHEN = [
        [1,2,3], # 1. Horizontalreihe
        [4,5,6], # 2. Horizontalreihe
        [7,8,9], # 3. Horizontalreihe
        [1,4,7], # 1. Vertikalreihe
        [2,5,8], # 2. Vertikalreihe
        [3,6,9], # 3. Vertikalreihe
        [1,5,9], # 1. Diagonalreihe
        [3,5,7]  # 2. Diagonalreihe
        ]
    KNOPF_RAHMEN_BREITE = 70
    KNOPF_RAHMEN_HOEHE = 10
    
    def __init__(self, fenster):
        self.fenster = fenster
        self.images = {
            'Leer' : tk.PhotoImage(file=self.LEER),
            'Kreis': tk.PhotoImage(file=self.KREIS),
            'Kreuz': tk.PhotoImage(file=self.KREUZ),
        }
        self.runden = 1
        self.winner = None
        self.spieler = cycle(['Kreis', 'Kreuz'])
        self.knoepfe = list()
        self.feld_bauen()
        
    def feld_bauen(self):
        self.melde_text_var = tk.StringVar(self.fenster, "Neues Spiel")
        self.meldung = tk.Label( self.fenster, textvariable=self.melde_text_var,
            font=('Helvetica', 12, 'bold'))
        self.meldung.pack(pady=4)
        
        self.knopf_rahmen = tk.Frame(self.fenster)
        self.knopf_rahmen.pack(expand=True, padx=self.KNOPF_RAHMEN_BREITE,
            pady=self.KNOPF_RAHMEN_HOEHE)

        for reihe, positions in enumerate(self.REIHEN[0:3]):
            for zeile in range(len(positions)):
                knopf = positions[zeile]
                knopf_widget = tk.Button(self.knopf_rahmen,
                    image=self.images['Leer'], highlightthickness=0,
                    command=partial(self.knopf_druck, knopf))
                knopf_widget.status = None
                knopf_widget.grid(row=reihe, column=zeile)
                self.knoepfe.append(knopf_widget)

        self.reset_button = tk.Button(self.fenster, text="Wiederholung?",
            bg="yellow", command=self.reset_command)
        
    def knopf_druck(self, knopf):
        knopf_index = knopf-1
        if self.winner != None : return
        if self.knoepfe[knopf_index].status != None: return
        status = next(self.spieler)
        self.knoepfe[knopf_index].status = status
        self.knoepfe[knopf_index]['image'] = self.images[status]
        self.auswertung()
        
    def auswertung(self):
        for reihe in self.REIHEN:
            kreis_summe = 0
            kreuz_summe = 0
            for knopf in reihe:
                knopf_index = knopf-1
                status = self.knoepfe[knopf_index].status
                if status == 'Kreis':
                    kreis_summe += 1
                elif status == 'Kreuz':
                    kreuz_summe += 1
            if kreis_summe == 3 or kreuz_summe == 3:
                self.winner = status
                self.melde_text_var.set("Gewinner: {}".format(status))
                self.reset_button.pack(pady=4)
                return
            else:
                if self.runden >= 9:
                    self.melde_text_var.set("Unentschieden")
                    self.reset_button.pack(pady=4)
                else:
                    self.melde_text_var.set("Spielrunde: {}".format(self.runden))
        self.runden += 1            
                    
    def reset_command(self):                
        for knopf_index in range(len(self.knoepfe)):
            self.knoepfe[knopf_index].configure(image=self.images['Leer'])
            self.knoepfe[knopf_index].status = None
        self.runden = 1
        self.winner = None
        self.spieler = cycle(['Kreis', 'Kreuz'])
        self.melde_text_var.set("Neues Spiel")
        self.reset_button.pack_forget()
               
def main():
    fenster = tk.Tk()
    fenster.title(FENSTER_TITLE)
    app = WufTickTackToe(fenster)
    fenster.mainloop()
 
if __name__ == '__main__':
    main()
Gruss wuf :wink:

Re: Button aktiviert sich automatisch

Verfasst: Donnerstag 29. März 2018, 21:50
von Sirius3
@wuf: an der Spielelogik wollte ich nicht ändern. Zeile 48 und 94 sind die berühmten Anti-Pattern, die Dir hier sicher schon öfter begegnet sind. Dass in Zeile 55 die Liste genau mit dem Index position-1 gefüllt wird, ist mehr als Zufall. Solche versteckten Abhängigkeiten will man nicht in Programmen haben. Die Auswerung ist deutlich umständlicher als mit any und all.

Re: Button aktiviert sich automatisch

Verfasst: Freitag 30. März 2018, 00:20
von wuf
@Sirius3: Danke für die Hinweise. Anti-Pattern ist ein neuer Begriff für mich. Habe die Zeilen wie folgt abgeändert:

Zeile 48
for zeile in range(len(positions)):
auf:
for zeile, knopf in enumerate(positions):

Zeile 94
for knopf_index in range(len(self.knoepfe)):
auf:
for knopf , knopf_widget in enumerate(self.knoepfe):

Um das position-1 zu neutralisieren habe ich:
REIHEN = [
[1,2,3], # 1. Horizontalreihe
[4,5,6], # 2. Horizontalreihe
[7,8,9], # 3. Horizontalreihe
[1,4,7], # 1. Vertikalreihe
[2,5,8], # 2. Vertikalreihe
[3,6,9], # 3. Vertikalreihe
[1,5,9], # 1. Diagonalreihe
[3,5,7] # 2. Diagonalreihe
]

auf:
REIHEN = [
[0,1,2], # 1. Horizontalreihe
[3,4,5], # 2. Horizontalreihe
[6,7,8], # 3. Horizontalreihe
[0,3,6], # 1. Vertikalreihe
[1,4,7], # 2. Vertikalreihe
[1,5,8], # 3. Vertikalreihe
[0,4,8], # 1. Diagonalreihe
[2,4,6] # 2. Diagonalreihe
]

geändert

Die Auswertung lasse ich so bestehen da mir die Zeile wie:
any(all(self.knoepfe[r].status == status for r in row) for row in self.REIHEN
mit any und all noch nicht so klar ist.

Gruss wuf :wink: