Tetris Programm erklären

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
darktrym
User
Beiträge: 785
Registriert: Freitag 24. April 2009, 09:26

Kann mir erklären wie das Programm funktioniert. Ich blick' irgendwie nicht durch.

Code: Alles auswählen

#!/usr/bin/env python
import curses
from random import randrange 
curses.initscr()
curses.curs_set(0)
win = curses.newwin(18,18,0,0)
win.keypad(1) 
win.nodelay(1)
f = [ [0x315,0x4cd,0x13f,0xc47],[0x31d,0x4cf,0x137,0xc45],[0x374,0x374,0x374,0x374],[0x741,0x51c,0xdc3,0xf34],[0xfc1,0x73c,0x543,0xd14],[0x311,0x4cc,0x133,0xc44],[0xc34,0x341,0x41c,0x1c3]]

def chkFig(crds, s): 
    chk = all([win.inch(c[1],c[0]) & 255 == 32 for c in crds])
    for c in crds: 
        win.addch(c[1],c[0],'X' if s == 1 else 32) if ((chk and s == 1) or s == 0) else None
    return True if s == 0 else chk

def putFig(FP, s): 
    c = lambda el,n: -1 if (n >> el & 3) == 3 else 1 if (n >> el & 3) == 1 else 0; pos = [ c(i ,f[ FP[3] ][ FP[2] ] ) for i in range(0,15,2)[::-1]]
    return chkFig([map(lambda x,y: x+y, FP[0:2]*4,pos)[i-2:i] for i in range(2,9,2)],s)

def MoveFig(FP, key, d): 
    '''figure moving function'''
    FP[0] = FP[0] - d if key == curses.KEY_LEFT else FP[0] + d if key == curses.KEY_RIGHT else FP[0]; FP[1] = FP[1] + d if key in [curses.KEY_DOWN, -1] else FP[1]  
    if key == curses.KEY_UP: 
        FP[2] = 0 if FP[2] + d > 3 else 3 if FP[2] + d < 0 else FP[2] + d 

def chkBoard(score): 
    '''kill full lines and increase score'''
    for i in range(17):
        if all([chr(win.inch(i,x)) == 'X' for x in range(1, 17)]):
            win.deleteln()
            win.move(1,1)
            win.insertln() 
            score += 1
            if score % 10 == 0: 
                win.timeout(300 - (score * 2))
    return score

FigPos = [8,3,0,randrange(0,6,1)]
score = putFig(FigPos,1) ^ 1
win.timeout(300) 

while True:
    win.border('|','|','-','-','+','+','+','+')
    win.addstr(0,2,' Score: %d ' % score)
    key = win.getch()
    if key == 27: 
        break
    putFig(FigPos,0)
    MoveFig(FigPos,key,1)
    if not putFig(FigPos,1):
        MoveFig(FigPos,key, -1)
        putFig(FigPos,1)
        if FigPos[1]==3: 
            break
        if key in [curses.KEY_DOWN, -1]:
            score = chkBoard(score)
            FigPos = [8,3,0,randrange(0,6,1)]
            putFig(FigPos,1)
curses.endwin() 
„gcc finds bugs in Linux, NetBSD finds bugs in gcc.“[Michael Dexter, Systems 2008]
Bitbucket, Github
BlackJack

@darktrym: Ich glaube Lesbarkeit und Verständlichkeit waren nicht die Topprioritäten bei diesem Quelltext. ;-)

Ich würde anfangen das lesbarer umzuschreiben um es zu verstehen.
Benutzeravatar
/me
User
Beiträge: 3561
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

BlackJack hat geschrieben:@darktrym: Ich glaube Lesbarkeit und Verständlichkeit waren nicht die Topprioritäten bei diesem Quelltext. ;-)
Für einen "Obfuscated Python Contest" reicht es noch nicht, aber es ist auf dem guten Weg ein Kandidat dafür zu werden.
Benutzeravatar
darktrym
User
Beiträge: 785
Registriert: Freitag 24. April 2009, 09:26

Eigentlich wurde der schon von mir ein wenig bearbeitet.
Die ursprüngliche Fassung hatte lediglich 28 Zeilen.

Ich hätte ja angenommen, f steht für die 4 Positionen des Steins.
Die dann jeweils binär in einem 4x4 Block von oben nach unter kodiert sind.
Passt aber irgendwie nicht ganz.
„gcc finds bugs in Linux, NetBSD finds bugs in gcc.“[Michael Dexter, Systems 2008]
Bitbucket, Github
BlackJack

@darktrym: Da ist ein Fehler drin der verhindert dass das ”T”-Stück jemals ausgewählt wird. `f` hat sieben Elemente, ``randrange(0, 6)`` wählt aber immer nur aus den ersten 6 aus weil die 6 selbst, also das siebte Element nicht zur Ergebnismenge von dem Aufruf gehört. Hast Du den Fehler „eingebaut” oder war der schon im Original?

@/me: Ich denke dafür reicht es schon, beziehungsweise vermute ich mal darktrym hat das schon ein wenig ”ent-obfuscated”.

Edit: Noch ein Programmfehler: Wenn man ein neues Teil sofort an den linken oder rechten Rand verschiebt in dem man die entsprechende Taste so schnell wiederholt drückt, dass das Teil keine Chance hat zwischendruch nach unten bewegt zu werden, dann bricht das Programm ab wenn das Teil den Rand erreicht hat, weil die Spiellogik in diesem Fall nicht berücksichtigt, dass das nicht gleichbedeutend damit ist, dass sich das Teil nicht nach unten bewegen *kann*.
Sirius3
User
Beiträge: 18335
Registriert: Sonntag 21. Oktober 2012, 17:20

Da sind weitere Fehler drin: das "I"-Stück ist nur 3 Elemente lang.
Wenn das Ziel war, Tetris in möglichst wenig Python-Zeilen unterzubringen, sind 28 recht viel:

Code: Alles auswählen

import curses;from random import choice;curses.initscr();curses.curs_set(0);win = curses.newwin(18,18,0,0);win.keypad(1);win.nodelay(1);trafo = [lambda (x,y): (x,y), lambda (x,y): (-y, x), lambda (x,y): (-x, -y), lambda (x,y): (y, -x)];f = [[(0, 0), (0, -1), (0, 1), (1, 1)],[(0, 0), (0, -1), (0, 1), (-1, 1)],[(0, 0), (0, -1), (1, -1), (1, 0)],[(0, 0), (1, -1), (1, 0), (0, 1)],[(0, 0), (-1, -1), (-1, 0), (0, 1)],[(0, 0), (0, -1), (0, 1), (0, 2)],[(0, 0), (-1, 0), (0, -1), (1, 0)]];chkFig=lambda pos,s: (s==0 or all(win.inch(c[1],c[0]) & 255 == 32 for c in pos)) and [win.addch(c[1],c[0],'X' if s == 1 else 32) for c in pos];putFig=lambda FP, s: chkFig(map(lambda(x,y):(x+FP[0],y+FP[1]),map(trafo[FP[2]],FP[3])),s);MoveFig=lambda FP, key, d: (FP[0] + {curses.KEY_LEFT: -d, curses.KEY_RIGHT: d}.get(key,0), FP[1] + {curses.KEY_DOWN: d, -1:d}.get(key,0), (FP[2] + {curses.KEY_UP: d}.get(key,0)) % 4, FP[3]);chkBoard=lambda: sum(win.deleteln() or win.move(1,1) or win.insertln() or 1 for i in range(17) if all(chr(win.inch(i,x)) == 'X' for x in range(1, 17)));FigPos = [8,3,0,choice(f)];score = 0;putFig(FigPos,1);win.timeout(300);key=0
while key!=27 or curses.endwin():
    win.border('|','|','-','-','+','+','+','+');win.addstr(0,2,' Score: %d ' % score);key = win.getch();putFig(FigPos,0);FigPos=MoveFig(FigPos,key,1)
    if key!=27 and not putFig(FigPos,1): FigPos=MoveFig(FigPos,key, -1);putFig(FigPos,1);key=27 if FigPos[1]==3 else -2 if key in [curses.KEY_DOWN, -1] else 0
    if key==-2:score += chkBoard();win.timeout(300 - (score//10 * 20));FigPos = [8,3,0,choice(f)];putFig(FigPos,1)
Ich hab man »f« dekodiert und die Drehungen in eine eigene Liste gepackt.
Benutzeravatar
darktrym
User
Beiträge: 785
Registriert: Freitag 24. April 2009, 09:26

An der Codelogik selbst hab ich keine Änderungen vorgenommen, d.h. die Fehler sind im Original ebenfalls enthalten.
Ich werd' den Code mal auseinander nehmen, mal schauen ob's lesbarer/fehlerfreier geht.
„gcc finds bugs in Linux, NetBSD finds bugs in gcc.“[Michael Dexter, Systems 2008]
Bitbucket, Github
Sirius3
User
Beiträge: 18335
Registriert: Sonntag 21. Oktober 2012, 17:20

Ich bin mit meiner Einzeilenversion nicht zufrieden. Irgendwann wird die Rekursionstiefe überschritten:

Code: Alles auswählen

import curses;from random import choice;trafo = [lambda (x,y): (x,y), lambda (x,y): (-y, x), lambda (x,y): (-x, -y), lambda (x,y): (y, -x)];f = [[(0, 0), (0, -1), (0, 1), (1, 1)],[(0, 0), (0, -1), (0, 1), (-1, 1)],[(0, 0), (0, -1), (1, -1), (1, 0)],[(0, 0), (1, -1), (1, 0), (0, 1)],[(0, 0), (-1, -1), (-1, 0), (0, 1)],[(0, 0), (0, -1), (0, 1), (0, 2)],[(0, 0), (-1, 0), (0, -1), (1, 0)]];chkFig=lambda pos,s: (s==0 or all(win.inch(c[1],c[0]) & 255 == 32 for c in pos)) and [win.addch(c[1],c[0],'X' if s == 1 else 32) for c in pos];putFig=lambda FP, s: chkFig(map(lambda(x,y):(x+FP[0],y+FP[1]),map(trafo[FP[2]],FP[3])),s);MoveFig=lambda FP, key, d: (FP[0] + {curses.KEY_LEFT: -d, curses.KEY_RIGHT: d}.get(key,0), FP[1] + {curses.KEY_DOWN: d, -1:d}.get(key,0), (FP[2] + {curses.KEY_UP: d}.get(key,0)) % 4, FP[3]);chkBoard=lambda: sum(win.deleteln() or win.move(1,1) or win.insertln() or 1 for i in range(17) if all(chr(win.inch(i,x)) == 'X' for x in range(1, 17)));move=lambda FigPos, key: (putFig(FigPos,0), MoveFig(FigPos,key,1), key)[1:];new_tile=lambda score, FigPos: (win.timeout(300 - (score//10 * 20)) or score, (putFig(FigPos,1) or 1) and FigPos);rollback=lambda score, FigPos, key: (putFig(FigPos,1), (score, None) if FigPos[1]==3 else new_tile(score+chkBoard(),[8,3,0,choice(f)]) if key in [curses.KEY_DOWN, -1] else (score,FigPos))[1];step=lambda score,FigPos,key: (score,None) if key==27 else (score,FigPos) if putFig(FigPos,1) else rollback(score,MoveFig(FigPos,key, -1), key);play=lambda score,FigPos:reduce(lambda r,x:r and r[1] and step(r[0],*move(r[1], win.border('|','|','-','-','+','+','+','+') or win.addstr(0,2,' Score: %d ' % score) or win.getch())),xrange(10000),(score,FigPos));repeat=lambda x: x and repeat(play(*x));curses.initscr();curses.curs_set(0);win = curses.newwin(18,18,0,0);win.keypad(1);win.nodelay(1);FigPos = [8,3,0,choice(f)];putFig(FigPos,1);win.timeout(300);repeat((0,FigPos));curses.endwin()
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

Es wird Zeit, dass Python TCO unterstützt ... ;)

Wobei Semikolon zu verwenden ist ja eigentlich Cheating.
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
Antworten