4 Gewinnt

Code-Stücke können hier veröffentlicht werden.
Antworten
Thesty
User
Beiträge: 1
Registriert: Dienstag 3. September 2019, 11:35

Donnerstag 5. September 2019, 13:30

Hallo,
ich probiere mich gerade an einem 4 Gewinnt Spiel mit der Turtle Funktion. Leider bin ich aber noch eine ziemliche Anfängerin und scheitere momentan an dem abwechselnden Setzen und dem Zeichen der Kreise. Google hat mich auch nicht weitergebracht … habt ihr vielleicht Ideen und Ansätze mit denen ich weiterarbeiten könnte?

Code: Alles auswählen

from turtle import *

t1 = Turtle()
setup(700, 600)
screen = t1.getscreen()
screen.screensize(700, 600)
t1.speed(10)



# Vertikale Linien
# 1.Linie
t1.penup()
t1.goto(-350, -300)
t1.pendown()
t1.right(270)
t1.forward(600)
# 2.Linie
t1.penup()
t1.goto(-250, 300)
t1.pendown()
t1.right(180)
t1.forward(600)
# 3.Linie
t1.penup()
t1.goto(-150, -300)
t1.pendown()
t1.right(180)
t1.forward(600)
# 4.Linie
t1.penup()
t1.goto(-50, 300)
t1.pendown()
t1.right(180)
t1.forward(600)
# 5.Linie
t1.penup()
t1.goto(50, -300)
t1.pendown()
t1.right(180)
t1.forward(600)
# 6.Linie
t1.penup()
t1.goto(150, 300)
t1.pendown()
t1.right(180)
t1.forward(600)
# 7.Linie
t1.penup()
t1.goto(250, -300)
t1.pendown()
t1.right(180)
t1.forward(600)
# 8.Linie
t1.penup()
t1.goto(350, 300)
t1.pendown()
t1.right(180)
t1.forward(600)


# Horizontale Linien
# 1.Linie
t1.right(90)
t1.forward(700)
# 2.Linie
t1.penup()
t1.goto(-350, -200)
t1.pendown()
t1.right(180)
t1.forward(700)
# 3.Linie
t1.penup()
t1.goto(350, -100)
t1.pendown()
t1.right(180)
t1.forward(700)
# 4.Linie
t1.penup()
t1.goto(-350, 0)
t1.pendown()
t1.right(180)
t1.forward(700)
# 5. Linie
t1.penup()
t1.goto(350, 100)
t1.pendown()
t1.right(180)
t1.forward(700)
# 6.Linie
t1.penup()
t1.goto(-350, 200)
t1.pendown()
t1.right(180)
t1.forward(700)
# 7.Linie
t1.penup()
t1.goto(350, 300)
t1.pendown()
t1.right(180)
t1.forward(700)

#Klick
def klick (x, y):
    print(x,y)
    t1.penup()
    t1.goto(x,y)
    Reihe(x,y)

#Kreise
def Kreis1():
    t1.fillcolor("yellow")
    t1.speed(100)
    t1.begin_fill()
    t1.pendown()
    t1.seth(270)
    t1.circle(45)
    t1.end_fill()


def Kreis2():
    t1.fillcolor("red")
    t1.speed(100)
    t1.begin_fill()
    t1.pendown()
    t1.seth(270)
    t1.circle(45)
    t1.end_fill()

#Reihen
def Reihe (x,y):
    if x <= -250 and y >= -350 and y <= 300:
        Reihe =1
        print("1")
    elif x >= -250 and x<= -150 and y <= 300 and y >= -350:
        Reihe = 2
        print("2")
    elif x >= -150 and x <= -50 and y >= -350 and y <= 300:
        Reihe = 3
        print("3")
    elif x>= -50 and x <= 50 and y >= -350 and y <= 300:
        Reihe = 4
        print("4")
    elif x >= 50 and x <= 150 and y >= -350 and y <= 300:
        Reihe = 5
        print("5")
    elif x >= 150 and x <=250 and y >= -350 and y <= 300:
        Reihe = 6
        print("6")
    else:
        Reihe = 7
        print("7")



onscreenclick(klick)
mainloop()
Das ist der Code den ich bislang habe :)
Benutzeravatar
__blackjack__
User
Beiträge: 4010
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Donnerstag 5. September 2019, 17:57

@Thesty: Sternchen-Importe sind Böse™. Im Grunde ist das `turtle`-Modul hier ein Grenzfall, weil man sich da sowieso nicht an „best practices“ hält und das nicht für grössere Programme sinnvoll verwendbar ist. Andererseits verwendest Du von dem ganzen globalen Kram kaum etwas also könnte man hier tatsächlich nur das aus `turtle` importieren was auch tatsächlich verwendet wird.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. Daraus folgt dann auch das man auf `t1` in Funktionen nicht mehr einfach so zugreifen kann. Funktionen und Methoden sollten alles was sie ausser Konstanten verwenden als Argument(e) übergeben bekommen. Da braucht man bei GUI-Programmierung dann mindestens `functools.partial()`, in der Regel aber auch objektorientierte Programmierung (OOP).

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase).

Man sollte Namen weder kryptisch abkürzen, noch durchnummerieren. Beim `Turtle`-Exemplar macht es zudem gar keinen Sinn, weil es ja sowieso nur eines davon gibt. `turtle` wäre ein guter Name für eine `Turtle`.

Diese vielen „magischen Zahlen“ sollte man aus Konstanten Anfangsgrössen errechnen. Stell Dir mal vor was es im Moment für einen Aufwand bedeuten würde die Grösse einer Zelle auf dem Spielfeld von 100 auf 150 zu erhöhen. Da müsstest *Du* viel Rechnen und Zahlen anpassen. Das Rechnen kann doch aber der Rechner übernehmen – dafür gibt es das Gerät ja schliesslich. Ausgehen kann man von einer Konstanze für die Zellengrösse und den Zellen pro Reihe und der Anzahl der Reihen.

Der Code zum Zeichnen der horizontalen und der vertikalen Linien wiederholt sich im Grund für jede Linie, nur mit geänderten Werten. Diese Änderungen kann man berechnen, also viel Handgeschriebenes/Kopiertes in Schleifen deutlich kürzer schreiben.

Die Kommentare vor den Funktionen sind überflüssig. Die sagen nicht mehr aus als die Funktionssignatur selbst, beziehungsweise alleine die Funktionsnamen enthalten schon die gleiche Information. Kommentare sollten in der Regel nicht sagen *was* passiert, denn das steht ja bereits als Code da, sondern *warum* das *so* gemacht wird – sofern das nicht aus dem Code schon ersichtlich ist.

Funktions- und Methodennamen beschreiben üblicherweise die Tätigkeit, die von der Funktions oder Methode ausgeführt wird. `klick()`, `kreis()`, und `reihe()` sind keine Tätigkeiten, also auch keine guten Namen für Funktionen.

`Reihe()` ist zudem inhaltlich auch falsch, weil dort die Spalte ermittelt wird und nicht die Reihe.

In der Funktion wird die Spaltennummer an den Namen `Reihe` gebunden – also der selbe Name den die Funktion schon hat. Das ist verwirrend, weshalb man Funktionen und Methoden eben nach Tätigkeiten benennt, damit namen von ”Dingen” für eher passive Werte wie die Spaltennummer frei bleiben.

Die Nummer der Spalte wird an einen Namen gebunden und dann danach noch mal ausgegeben wobei da dann die Nummer noch mal als Zeichenkette in den Quelltext geschrieben wurde, statt die eben zugewiesene Variable auszugeben. Dann kann man das `print()` auch hinter das ``if``/``elif``/``else``-Konstrukt ziehen, denn dann würde ja *jeder* Zweig mit dem gleichen ``print(Reihe)`` enden. Aber diese ganzen Vergleiche sind auch reichtlich umständlich und auch wieder mit vielen handberechneten und geschriebenen Zahlen verbunden. Die Spaltennummer kann man auch einfach *ausrechnen*, aus dem gegebenen `x`-Wert. Den `y`-Wert würde ich hier gar nicht berücksichtigen.

`Kreis1()` und `Kreis2()` sind identisch bis auf die Farbe, das sollten also nicht *zwei* Funktionen sein, sondern *eine* welche die Farbe als Argument übergeben bekommt.

Zwischenstand der (fast) das macht was Dein Code gemacht hat:

Code: Alles auswählen

#!/usr/bin/env python3
from functools import partial
from turtle import mainloop, onscreenclick, setup, Turtle

CELL_SIZE = 100
CELLS_PER_ROW = 7
ROW_COUNT = 6
WIDTH = CELLS_PER_ROW * CELL_SIZE
HEIGHT = ROW_COUNT * CELL_SIZE


def draw_circle(turtle, color):
    turtle.fillcolor(color)
    turtle.speed(100)
    turtle.begin_fill()
    turtle.pendown()
    turtle.seth(270)
    turtle.circle(45)
    turtle.end_fill()


def print_column_number(x):
    column_number = max(
        1, min(CELLS_PER_ROW, int((x + WIDTH // 2) // CELL_SIZE + 1))
    )
    print(column_number)


def put_game_piece(turtle, x, y):
    print(x, y)
    turtle.penup()
    turtle.goto(x, y)
    print_column_number(x)


def main():
    turtle = Turtle()
    setup(WIDTH + CELL_SIZE // 2, HEIGHT + CELL_SIZE // 2)
    screen = turtle.getscreen()
    screen.screensize(WIDTH, HEIGHT)
    turtle.speed(10)

    # Vertikale Linien.
    turtle.right(90)
    for i in range(CELLS_PER_ROW + 1):
        turtle.penup()
        turtle.goto(-WIDTH // 2 + i * CELL_SIZE, HEIGHT // 2 * -((-1) ** i))
        turtle.pendown()
        turtle.right(180)
        turtle.forward(HEIGHT)

    # Horizontale Linien.
    turtle.left(90)
    for i in range(ROW_COUNT + 1):
        turtle.penup()
        turtle.goto(WIDTH // 2 * ((-1) ** i), -HEIGHT // 2 + i * CELL_SIZE)
        turtle.pendown()
        turtle.right(180)
        turtle.forward(WIDTH)

    onscreenclick(partial(put_game_piece, turtle))
    mainloop()


if __name__ == "__main__":
    main()
Um da jetzt tatsächlich ein 4 Gewinnt draus zu machen wirst Du IMHO nicht um OOP herum kommen. Und Du könntest/solltest die Spiellogik auch sauber von der GUI trennen.
“Programmieren ist ein Hobby, bei dem es einen riesigen Baumarkt mit quasi jedem Bauteil und Werkzeug und fast immer kostenlos gibt. Ob man deswegen in der Lage ist einen Kölner Dom zu bauen ist eine andere Frage. Arbeit steckt auf jeden Fall drin ;).” — Greebo, forum.ubuntuusers.de
Antworten