Python 函数式编程中的弹跳球游戏

Python Bounce Ball Game in functional programming

这是一款常见的街机游戏,目标是尽可能多地击中所有目标以获得最高分。每次球击中“石头”,您将获得 1 分,如果击中 2 个,您将获得 2 分。如果玩家未能接住横杆或“杆”上的球,则游戏结束。

我的任务是把这个用面向对象编程写的游戏变成函数式编程。

import time

class Ball:
    def __init__(self, cvs, pole, stones, scre):
        self.stones = stones
        self.cvs = cvs
        self.pole = pole
        self.scre = scre
        self.bottom_hit = False
        self.hit = 0
        self.id = canvas.create_oval(10, 10, 25, 25, fill="cadetblue", width=1)
        self.cvs.move(self.id, 230, 461)

        self.a = 3
        self.b = -3
        self.cvs.move(self.id, self.a, self.b)
        self.cvs_height = canvas.winfo_height()
        self.cvs_width = canvas.winfo_width()

    def stone_strike(self, push):
        for stone_line in self.stones:
            for stone in stone_line:
                stone_push = self.cvs.coords(stone.id)

                try:
                    if push[2] >= stone_push[0] and push[0] <= stone_push[2]:
                        if push[3] >= stone_push[1] and push[1] <= stone_push[3]:
                            canvas.bell()
                            self.hit += 1
                            self.scre.configure(text="Score: " + str(self.hit))
                            self.cvs.delete(stone.id)
                            return True
                except:
                    continue
        return False

    def pole_strike(self, push):
        pole_push = self.cvs.coords(self.pole.id)
        if push[2] >= pole_push[0] and push[0] <= pole_push[2]:
            if push[3] >= pole_push[1] and push[1] <= pole_push[3]:
                return True
            return False

    def draw(self):
        self.cvs.move(self.id, self.a, self.b)
        push = self.cvs.coords(self.id)
        if self.stone_strike(push):
            self.b = 3
        if push[1] <= 0:
            self.b = 3
        if push[3] >= self.cvs_height:
            self.bottom_hit = True
        if push[0] <= 0:
            self.a = 3
        if push[2] >= self.cvs_width:
            self.a = -3
        if self.pole_strike(push):
            self.b = -3


class Pole:
    def __init__(self, cvs):
        self.cvs = cvs
        self.id = canvas.create_rectangle(0, 0, 100, 10, fill="darkgoldenrod")
        self.cvs.move(self.id, 200, 485)
        self.a = 0
        self.cvs_width = canvas.winfo_width()
        self.cvs.bind_all("<Left>", self.turn_left)
        self.cvs.bind_all("<Right>", self.turn_right)

    def draw(self):
        push = self.cvs.coords(self.id)
        if push[0] + self.a <= 0:
            self.a = 0
        if push[2] + self.a >= self.cvs_width:
            self.a = 0
        self.cvs.move(self.id, self.a, 0)

    def turn_left(self, event):
        self.a = -5

    def turn_right(self, event):
        self.a = 5


class Stone:
    def __init__(self):
        self.id = canvas.create_rectangle(5, 5, 25, 25, fill="firebrick")


def start_game(event):
    score.configure(text="Score: 00")
    canvas.delete("all")
    pole = Pole(canvas)
    stones = []
    for i in range(0, 5):
        b = []
        for j in range(0, 19):
            tmp = Stone()
            b.append(tmp)
        stones.append(b)

    for i in range(0, 5):
        for j in range(0, 19):
            canvas.move(stones[i][j].id, 25 * j, 25 * i)

    ball = Ball(canvas, pole, stones, score)
    root.update()

    time.sleep(1)
    while 1:
        if not ball.bottom_hit:
            ball.draw()
            pole.draw()
            root.update()
            time.sleep(0.01)
            if ball.hit == 95:
                canvas.create_text(250, 250, text="YOU WON!!", fill="darkolivegreen", font="Calibri 24")
                break
        else:
            canvas.create_text(250, 250, text="GAME OVER!!", fill="darkolivegreen", font="Calibri 24")
            break

root = Tk()
root.title("Bounce Ball Game")
root.geometry("500x570")
root.resizable(0, 0)

canvas = Canvas(root, width=500, height=500, bd=0, bg="black")
canvas.pack(padx=10, pady=10)
canvas.create_text(250, 250, text="Press Enter to start Game!!", fill="darkolivegreen", font="Calibri 18")

score = Label(height=50, width=80, text="Score: 00", font="Calibri 14 italic")
score.pack(side="left")

root.bind_all("<Return>", start_game)
root.mainloop()

这是我写的代码..肯定有逻辑错误,因为它不工作。有什么想法吗??

import time

def startgame(event):
    score.configure(text="score:00")
    canvas.delete("all")
    canvas_width = canvas.winfo_width()
    canvas_height = canvas.winfo_height()
    pole = canvas.create_rectangle(0, 0, 100, 10, fill="darkgoldenrod")
    canvas.move(pole, 200, 425)
    pa = 0
    
    def turn_left(event):
        pa = -5

    def turn_right(event):
        pa = 5

    def stone_strike(push):
        for stone_line in stones:
            for stone in stone_line:
                stone_push = canvas.coords(stone)

                try:
                    if push[2] >= stone_push[0] and push[0] <= stone_push[2]:
                        if push[3] >= stone_push[1] and push[1] <= stone_push[3]:
                            canvas.bell()
                            hit += 1
                            score.configure(text="score:" + str(hit))
                            stonaki.delete()
                            return True
                except:
                    continue
        return False

    def pole_strike(push_b):
        pole_push = canvas.coords(pole)
        if push_b[2] >= pole_push[0] and push_b[0] <= pole_push[2]:
            if push_b[3] >= pole_push[1] and push_b[1] <= pole_push[3]:
                return True
            return False



    canvas.bind_all("<Left>", turn_left)
    canvas.bind_all("<Right>", turn_right)
    push_p = canvas.coords(pole)
    if push_p[0] + pa <= 0:
        pa = 0
    if push_p[2] + pa >= canvas_width:
        pa = 0


    stones = []
    for i in range(0, 5):
        lista = []
        for j in range(0, 19):
            stonaki = canvas.create_rectangle(5, 5, 25, 25, fill="firebrick")
            lista.append(stonaki)
        stones.append(lista)
    for i in range(0, 5):
        for j in range(0, 19):
            canvas.move(stones[i][j], 25 * j, 25 * i)

    root.update()
    
    bottom_hit = False
    hit = 0
    ball = canvas.create_oval(10, 10, 25, 25, fill="cadetblue", width=1)
    canvas.move(ball, 230, 461)
    ba = 3
    bb = -3
    canvas.move(ball, ba, bb)
    push_b = canvas.coords(ball)

    if stone_strike(push_b):
        bb = 3
    if push_b[1] <= 0:
        bb = 3
    if push_b[3] >= canvas_height:
        bottom_hit = True
    if push_b[0] <= 0:
        ba = 3
    if push_b[2] >= canvas_width:
        ba = -3
    if pole_strike(push_b):
        bb = -3
    
    time.sleep(1)
    while 1:
        if not bottom_hit:
            root.update()
            time.sleep(0.01)
            if hit == 95:
                canvas.create_text(250, 250, text="YOU WON!!", fill="darkolivegreen", font="Calibri 24")
                break
        else:
            canvas.create_text(250, 250, text="GAME OVER!!", fill="darkolivegreen", font="Calibri 24")
            break


root = Tk()
root.title("Bounce Ball Game")
root.geometry("500x570")
root.resizable(0, 0)

canvas = Canvas(root, width=500, height=500, bd=0, bg="black")
canvas.pack(padx=10, pady=10)
canvas.create_text(250, 250, text="Press Enter to start Game!!", fill="darkolivegreen", font="Calibri 18")

score = Label(height=50, width=80, text="Score: 00", font="Calibri 14 italic")
score.pack(side="left")

root.bind_all("<Return>", startgame)
root.mainloop()

在将这个用object-oriented编程编写的游戏变成函数式编程的过程中,我们需要处理每个函数都在使用的variables/widgets。如果我们正在对局部函数中的某个变量进行更改,并希望在函数调用结束后保留​​所做的更改,那么我将 将这些 variables/widgets 设为全局 .

此外,在 OOP 代码中,每次在 canvas 中使用函数 draw() 对“球”和“杆”进行更改。因此,为相同的 drawB()drawP() 创建两个函数:

from tkinter import *
import time

def startgame(event):
    score.configure(text="score:00")
    canvas.delete("all")
    canvas_width = canvas.winfo_width()
    canvas_height = canvas.winfo_height()
    global pole,pa, ba,bb,bottom_hit, hit,ball
    pole = canvas.create_rectangle(0, 0, 100, 10, fill="darkgoldenrod")
    canvas.move(pole, 200, 425)
    pa = 0
    
    def turn_left(event):
        global pa
        pa = -5

    def turn_right(event):
        global pa
        pa = 5

    def stone_strike(push):
        global stones, hit
        for stone_line in stones:
            for stone in stone_line:
                stone_push = canvas.coords(stone)

                try:
                    if push[2] >= stone_push[0] and push[0] <= stone_push[2]:
                        if push[3] >= stone_push[1] and push[1] <= stone_push[3]:
                            canvas.bell()
                            hit += 1
                            score.configure(text="score:" + str(hit))
                            canvas.delete(stone)   #what to delete
                            return True
                except:
                    continue
        return False

    def pole_strike(push_b):
        global pole
        pole_push = canvas.coords(pole)
        if push_b[2] >= pole_push[0] and push_b[0] <= pole_push[2]:
            if push_b[3] >= pole_push[1] and push_b[1] <= pole_push[3]:
                return True
            return False



    canvas.bind_all("<Left>", turn_left) 
    canvas.bind_all("<Right>", turn_right)

    def drawP():
        global pa,pole
        push_p = canvas.coords(pole)
        if push_p[0] + pa <= 0:
            pa = 0
        if push_p[2] + pa >= canvas_width:
            pa = 0
        canvas.move(pole, pa, 0)

    global stones
    stones = []
    for i in range(0, 5):
        lista = []
        for j in range(0, 19):
            stonaki = canvas.create_rectangle(5, 5, 25, 25, fill="firebrick")
            lista.append(stonaki)
        stones.append(lista)
    for i in range(0, 5):
        for j in range(0, 19):
            canvas.move(stones[i][j], 25 * j, 25 * i)

    
    bottom_hit = False
    hit = 0
    ball = canvas.create_oval(10, 10, 25, 25, fill="cadetblue", width=1)
    canvas.move(ball, 230, 461)
    
    ba = 3
    bb = -3

    root.update()
    
    def drawB():
        global ba,bb,bottom_hit, hit,ball,pole
        canvas.move(ball, ba, bb)
        push_b = canvas.coords(ball)

        if stone_strike(push_b):
            bb = 3
        if push_b[1] <= 0:
            bb = 3
        if push_b[3] >= canvas_height:
            bottom_hit = True
        if push_b[0] <= 0:
            ba = 3
        if push_b[2] >= canvas_width:
            ba = -3
        if pole_strike(push_b):
            bb = -3
    
    time.sleep(1)
    while 1:
        if not bottom_hit:
            drawB()
            drawP()
            root.update()
            time.sleep(0.01)
            if hit == 95:
                canvas.create_text(250, 250, text="YOU WON!!", fill="darkolivegreen", font="Calibri 24")
                break
        else:
            canvas.create_text(250, 250, text="GAME OVER!!", fill="darkolivegreen", font="Calibri 24")
            break


root = Tk()
root.title("Bounce Ball Game")
root.geometry("500x570")
root.resizable(0, 0)

canvas = Canvas(root, width=500, height=500, bd=0, bg="black")
canvas.pack(padx=10, pady=10)
canvas.create_text(250, 250, text="Press Enter to start Game!!", fill="darkolivegreen", font="Calibri 18")

score = Label(height=50, width=80, text="Score: 00", font="Calibri 14 italic")
score.pack(side="left")

root.bind_all("<Return>", startgame)
root.mainloop()

您可以进一步优化此代码。