Python 简易小项目

Prerequisite

实习的时候有个这样要求,虽然我不太想做,但毕竟是工作,于是我花了几个小时出(魔改)了几道,此贴作个记录

构建一个简易图书馆

#! /usr/bin/env python
# -*- coding: UTF-8 -*-
# @Author: Xiaotuan

"""
构建一个简易图书馆:
1. 可以存入书籍,每一本书籍的信息包含书名和作者(假设每一个信息都是不重复的)
2. 可以删除书籍,选中要删除书籍的书名或作者,即可删除
3. 可以修改书籍,选中要修改书籍的书名或作者,修改书籍的某个信息
4. 可以查询书籍,选中要查询书籍的书名或作者,查询该书籍的全部信息
5. 可以打印书籍,一次性打印出全部书籍信息

验证图书馆的可行性:
1. 存入书籍,三体 刘慈欣、白夜行 东野圭吾、意林 匿名,然后打印书籍
2. 删除书籍,作者 东野圭吾,然后打印书籍
3. 修改书籍,书名 意林,修改作者 黄帮主,然后打印书籍
4. 查询书籍,作者 刘慈欣,然后打印书籍
"""

class Library:

    def __init__(self):
        """初始化字典"""
        self.books = {}

    def get_name(self, author):
        """根据作者找书名"""
        for key, value in self.books.items():
            if value == author:
                return key

    def add(self, name, author):
        """存入书籍"""
        self.books[name] = author
        print("已存入书籍:书名:《" + name + "》 作者:" + author)

    def delete(self, judge, value):
        """删除书籍"""
        if judge == "书名":
            print("已删除书籍:书名:《" + value + "》 作者:" + self.books[value])
            del self.books[value]
        elif judge == "作者":
            print("已删除书籍:书名:《" + self.get_name(value) + "》 作者:" + value)
            del self.books[self.get_name(value)]
        else:
            print("输入错误,请重新输入")

    def change(self, judge1, value1, judge2, value2):
        """修改书籍"""
        if judge1 == "书名":
            if judge2 == "书名":
                print("已修改书籍:书名:《" + value2 + "》 作者:" + self.books[value1])
                self.add(value2, self.books[value1])
                del self.books[value1]
            elif judge2 == "作者":
                print("已修改书籍:书名:《" + value1 + "》 作者:" + value2)
                self.books[value1] = value2
            else:
                print("输入错误,请重新输入")
        elif judge1 == "作者":
            if judge2 == "书名":
                print("已修改书籍:书名:《" + value2 + "》 作者:" + value1)
                del self.books[self.get_name(value1)]
                self.add(value2, value1)
            elif judge2 == "作者":
                print("已修改书籍:书名:《" + self.get_name(value1) + "》 作者:" + value2)
                self.books[self.get_name(value1)] = value2
            else:
                print("输入错误,请重新输入")

    def find(self, judge, value):
        """查找书籍"""
        if judge == "书名":
            print("已查询书籍:", end="")
            print("书名:《" + value + "》 ", end="")
            print("作者:" + self.books[value])
        elif judge == "作者":
            print("已查询书籍:", end="")
            print("书名:《" + self.get_name(value) + "》 ", end="")
            print("作者:" + value)
        else:
            print("输入错误,请重新输入")

    def print(self):
        """加上书名"""
        print("图书馆信息如下:")
        for name, author in self.books.items():
            print("书名:《" + name + "》 ", end="")
            print("作者:" + author)
        print()


library = Library()

# 存入书籍
print("--------- 存入书籍 ---------")
library.add("三体", "刘慈欣")
library.add("白夜行", "东野圭吾")
library.add("意林", "匿名")
library.print()

# 删除书籍
print("--------- 删除书籍 ---------")
library.delete("作者", "东野圭吾")
library.print()

# 修改书籍
print("--------- 修改书籍 ---------")
library.change("书名", "意林", "作者", "黄帮主")
library.print()

# 查询书籍
print("--------- 查询书籍 ---------")
library.find("作者", "刘慈欣")
library.print()

约瑟夫问题

#! /usr/bin/env python
# -*- coding: UTF-8 -*-
# @Author: Xiaotuan

"""
约瑟夫问题:
1. N 个人围成一个圈,编号从 1 开始,依次到 N
2. 编号为 M 的游戏参与者开始报数,报数从 1 开始,后面的人报数接龙,直到 K 为止,报数为 K 的人将出局
3. 出局者的下一个玩家接着从 1 开始报数,如此循环,直到剩下一个玩家时游戏结束,这个玩家就是游戏获胜者

要求:
使用下面这个函数,使得满足约瑟夫问题,total 为 N 个人,begins 为编号 M 的人,count 为报数 K
def joseph(total, begins, count):
    xxx
"""


def joseph(total, begins, count):
	# 队列为 [1, 2, 3, ... , N]
	queue = [i for i in range(1, total + 1)]
	# 第一个出局者
    # 为什么 -2,因为数组从 0 开始所以 -1,并且报数从自己开始所以 -1
	death = (begins + count - 2) % len(queue)
	# 一共 total - 1 局
	for times in range(total - 1):
		# 出局
		print('出局的编号:', queue[death])
		# 删除队列中出局的人
		del queue[death]
		# 为什么 -1,因为数组从 0 开始所以 -1,并且报数从下一个开始报数
		death = (death + count - 1) % len(queue)
	# 幸存者
	print('幸存者:', queue[0])

joseph(10, 2, 3)

石头剪刀布

#! /usr/bin/env python
# -*- coding: UTF-8 -*-
# @Author: Xiaotuan

"""
石头剪刀布:
1. 一开始询问想玩多少轮,假设是 N 轮
2. 每一轮电脑和自己都有三种结果:电脑赢、自己赢、平局
3. 赢了的一方可以得到 1 分,当电脑得的分 + 自己得的分 == N 就退出循环
4. 根据最终的总分,输出三个结果中的一个:电脑赢、自己赢、平局
"""

import random

my_dict = {'R': "石头", 'P': "布", 'S': "剪刀"}
user_count = 0
comp_count = 0

games = int(input("你想要玩多少轮呢? "))

while (comp_count + user_count < games):

    flag = 0

    user_input = input("你要出什么呢?石头是 R 剪刀是 S 布是 P: ")[0]
    user_input = user_input.upper()

    for i in my_dict.keys():
        if (user_input == i):
            flag = 1
            break
    if (flag != 1):
        print("输入错误")
        continue

    comp_input = random.choice(list(my_dict.keys()))

    print("电脑输出的是: ", my_dict[comp_input])
    if (user_input == 'R'
            and comp_input == 'P') or (user_input == 'P' and comp_input
                                       == 'S') or (user_input == 'S'
                                                   and comp_input == 'R'):
        comp_count = comp_count + 1
    elif (user_input == 'P'
          and comp_input == 'R') or (user_input == 'S' and comp_input
                                     == 'P') or (user_input == 'R'
                                                 and comp_input == 'S'):
        user_count = user_count + 1
    else:
        print("平局")

    print("\n分数:")
    print("我的分数:", user_count, "\t电脑的分数:", comp_count, "\n")


print("\n\t\t最终结果:")
print("我的分数:", user_count, "\t\t\t电脑的分数:", comp_count, "\n")
if user_count > comp_count:
    print("\n\t恭喜你赢了!")
elif user_count < comp_count:
    print("\n\t\t很可惜你输了!")
else:
    print("\n\t\tOOPS! 是平局!")

四子连珠

"""
author https://grantjenks.com/docs/freegames/connect.html
四子连珠小游戏
"""

from turtle import *
from freegames import line

turns = {'red': 'yellow', 'yellow': 'red'}
state = {'player': 'yellow', 'rows': [0] * 8}
board = [['O' for i in range(8)] for j in range(8)]

# 画出背景、网格和白珠
def grid():
    """Draw Connect Four grid."""
    bgcolor('light blue')

    for x in range(-150, 200, 50):
        line(x, -200, x, 200)

    for x in range(-175, 200, 50):
        for y in range(-175, 200, 50):
            up()
            goto(x, y)
            dot(40, 'white')

    update()

# 开始下棋(白珠染色)
def tap(x, y):
    """Draw red or yellow circle in tapped row."""
    player = state['player']
    rows = state['rows']

    row = int((x + 200) // 50)
    count = rows[row]
    board[row][count] = 'A' if state['player'] == "yellow" else 'B'

    x = ((x + 200) // 50) * 50 - 200 + 25
    y = count * 50 - 200 + 25

    up()
    goto(x, y)
    dot(40, player)
    update()

    rows[row] = count + 1
    if check_winner(board, state['player']): exitonclick()
    state['player'] = turns[player]

# 检测是否满足胜利条件
def check_winner(board, player):
    winner = player
    # 纵向判断是否满足四子连珠
    for i in range(8):
        for j in range(4):
            if board[i][j] == board[i][j+1] == board[i][j+2] == board[i][j+3] and (board[i][j] in ['A', 'B']):
                print(f"游戏结束!{winner} 胜利!")
                return True
    
    # 横向判断是否满足四子连珠
    for i in range(4):
        for j in range(8):
            if board[i][j] == board[i+1][j] == board[i+2][j] == board[i+3][j] and (board[i][j] in ['A', 'B']):
                print(f"游戏结束!{winner} 胜利!")
                return True

    # 斜向判断是否满足四子连珠
    for i in range(3, 8):
        for j in range(4):
            if board[i][j] == board[i-1][j+1] == board[i-2][j+2] == board[i-3][j+3] and (board[i][j] in ['A', 'B']):
                print(f"游戏结束!{winner} 胜利!")
                return True
    for i in range(3, 8):
        for j in range(3, 8):
            if board[i][j] == board[i-1][j-1] == board[i-2][j-2] == board[i-3][j-2] and (board[i][j] in ['A', 'B']):
                print(f"游戏结束!{winner} 胜利!")
                return True

setup(420, 420, 370, 0)
hideturtle()
tracer(False)
grid()
onscreenclick(tap)
done()

数字华容道

"""
author https://grantjenks.com/docs/freegames/tiles.html
数字华容道
"""

from random import *
from turtle import *
from freegames import floor, vector

tiles = {}
neighbors = [
    vector(100, 0),
    vector(-100, 0),
    vector(0, 100),
    vector(0, -100),
]

"""
load()函数的作用是初始化一个4x4的方块游戏板,并将方块的编号随机打乱。具体来说,它执行以下几个操作:

1. 循环生成4x4的方块板,将每个方块的位置(用一个矢量表示)和它们的编号(1到15)保存在一个字典变量tiles中。
2. 将最后一个方块的编号设置为None,以表示这个位置没有方块。
3. 随机交换方块板中的两个方块位置,这个操作执行1000次,以打乱方块的位置。
"""
def load():
    """Load tiles and scramble."""
    count = 1

    for y in range(-200, 200, 100):
        for x in range(-200, 200, 100):
            mark = vector(x, y)
            tiles[mark] = count
            count += 1

    tiles[mark] = None

    for count in range(1000):
        neighbor = choice(neighbors)
        spot = mark + neighbor

        if spot in tiles:
            number = tiles[spot]
            tiles[spot] = None
            tiles[mark] = number
            mark = spot

"""
square()函数的作用是在游戏窗口上绘制一个方块。具体来说,它执行以下几个操作:

1. 将海龟的笔提起,移动到方块的左下角位置(参数mark是一个矢量,表示方块的位置)。
2. 将海龟的笔放下,用黑色的笔画一个外边框,用白色的填充色填充方块内部。
3. 如果参数number不为None,在方块的中央位置绘制方块的编号(以字符串形式),使用Arial字体,字号60,字体风格为正常(normal)。
4. 如果方块的编号小于10,将海龟向前移动20个单位,使得数字的显示位置略微偏左。
"""
def square(mark, number):
    """Draw white square with black outline and number."""
    up()
    goto(mark.x, mark.y)
    down()

    color('black', 'white')
    begin_fill()
    for count in range(4):
        forward(99)
        left(90)
    end_fill()

    if number is None:
        return
    elif number < 10:
        forward(20)

    write(number, font=('Arial', 60, 'normal'))

"""
tap()函数的作用是在用户点击游戏窗口时,响应用户的操作。具体来说,它执行以下几个操作:

1. 将用户点击的位置向下取整,以得到最接近的方块的位置。
2. 遍历方块的四个邻居位置,检查是否有一个邻居是空的(即方块编号为None)。
3. 如果找到一个空的邻居,交换这个邻居和用户点击的方块的位置,更新tiles字典中的方块编号,并在游戏窗口中重新绘制方块的位置。
4. 如果没有找到一个空的邻居,不进行任何操作。
"""
def tap(x, y):
    """Swap tile and empty square."""
    x = floor(x, 100)
    y = floor(y, 100)
    mark = vector(x, y)

    for neighbor in neighbors:
        spot = mark + neighbor

        if spot in tiles and tiles[spot] is None:
            number = tiles[mark]
            tiles[spot] = number
            square(spot, number)
            tiles[mark] = None
            square(mark, None)

"""
draw()函数的作用是在游戏窗口中绘制所有的方块
"""
def draw():
    """Draw all tiles."""
    for mark in tiles:
        square(mark, tiles[mark])
    update()

setup(420, 420, 370, 0)
hideturtle()
tracer(False)
load()
draw()
onscreenclick(tap)
done()

Next

欸,继续吧,知识点讲解、引入包来源要做的准备工作说明,最后再录个屏,难顶。。。

数字华容道

引言

目标是做一个数字华容道,我们会用到 turtle、random 和 freegames 库,其中 turtle 库提供了一个绘图窗口和一组绘图函数,使得我们可以方便地在窗口中绘制图形,freegames 库是 Al Sweigart 编写的一个 Python 游戏库,其中包含了一些有趣的、易于理解的游戏示例,适合初学者使用。

img

数字华容道中需要用到 freegames 中的 floor 和 vector 函数,floor 用于将坐标值向下取整,vector 用于创建一个二维向量。

from random import *
from turtle import *
from freegames import floor, vector

首先要用一个字典(tiles)用于存储每个拼图块所在的位置和数字,拼图块的位置由 vector 表示,数字为整数或 None,然后用一个包含四个 vector 的列表(neighbors)表示每个拼图块可以与哪些位置的拼图块交换,即上下左右四个方向。

在数字华容道中,这些变量被用于表示拼图的状态和规则, tiles 用于存储每个拼图块的位置和数字, neighbors 表示每个拼图块可以移动到哪些相邻的位置。

tiles = {}
neighbors = [
    vector(100, 0),
    vector(-100, 0),
    vector(0, 100),
    vector(0, -100),
]

第一个函数 load() 的作用是初始化一个4x4的方块游戏板(从 -200 至 100 每个间隔 100),并将方块的编号随机打乱。具体来说,它执行以下几个操作:

  1. 循环生成 4x4 的方块板,将每个方块的位置(用一个矢量表示)和它们的编号(从 1 开始)保存在一个字典变量 tiles 中。
  2. 将最后一个方块的编号设置为 None,以表示这个位置没有方块。
  3. 随机交换方块板中的两个方块位置,这个操作执行 1000 次,以打乱方块的位置。
def load():
    count = 1

    for y in range(-200, 200, 100):
        for x in range(-200, 200, 100):
            mark = vector(x, y)
            tiles[mark] = count
            count += 1

    tiles[mark] = None

    for count in range(1000):
        neighbor = choice(neighbors)
        spot = mark + neighbor

        if spot in tiles:
            number = tiles[spot]
            tiles[spot] = None
            tiles[mark] = number
            mark = spot

第二个函数 square() 的作用是在游戏窗口上绘制一个方块。具体来说,它执行以下几个操作:

  1. 将海龟的笔提起,移动到方块的左下角位置(参数 mark 是一个矢量,表示方块的位置)。
  2. 将海龟的笔放下,用黑色的笔画一个外边框,用白色的填充色填充方块内部。
  3. 如果参数 number 不为 None,在方块的中央位置绘制方块的编号(以字符串形式),使用 Arial 字体,字号 60,字体风格为正常(normal)。
  4. 如果方块的编号小于 10,将海龟向前移动 20 个单位,使得数字的显示位置略微偏左。
def square(mark, number):
    up()
    goto(mark.x, mark.y)
    down()

    color('black', 'white')
    begin_fill()
    for count in range(4):
        forward(99)
        left(90)
    end_fill()

    if number is None:
        return
    elif number < 10:
        forward(20)

    write(number, font=('Arial', 60, 'normal'))

第三个函数 tap() 的作用是在用户点击游戏窗口时,响应用户的操作。具体来说,它执行以下几个操作:

  1. 将用户点击的位置向下取整,以得到最接近的方块的位置。
  2. 遍历方块的四个邻居位置,检查是否有一个邻居是空的(即方块编号为 None)。
  3. 如果找到一个空的邻居,交换这个邻居和用户点击的方块的位置,更新 tiles 字典中的方块编号,并在游戏窗口中重新绘制方块的位置。
  4. 如果没有找到一个空的邻居,不进行任何操作。
def tap(x, y):
    x = floor(x, 100)
    y = floor(y, 100)
    mark = vector(x, y)

    for neighbor in neighbors:
        spot = mark + neighbor

        if spot in tiles and tiles[spot] is None:
            number = tiles[mark]
            tiles[spot] = number
            square(spot, number)
            tiles[mark] = None
            square(mark, None)

第四个函数 draw() 的作用是在游戏窗口中绘制所有的方块

def draw():
    for mark in tiles:
        square(mark, tiles[mark])
    update()

最后这部分代码负责设置游戏界面和调用其他函数来启动和运行游戏。

setup(420, 420, 370, 0)
hideturtle()
tracer(False)
load()
draw()
onscreenclick(tap)
done()

setup(420, 420, 370, 0):设置游戏窗口的宽度和高度为 420 像素,左上角在屏幕上的坐标为 (370, 0)。

hideturtle():隐藏小海龟图形,因为拼图游戏中不需要绘制任何小海龟图形。

tracer(False):关闭自动刷新画面,这样可以避免画面闪烁。

load():调用 load 函数来初始化拼图。

draw():调用 draw 函数来绘制游戏初始状态的拼图。

onscreenclick(tap):将 tap 函数与屏幕的鼠标点击事件绑定,使得玩家可以通过点击空白区域来交换拼图块。

done():启动游戏并进入消息循环,等待用户交互。

总体代码

from random import *
from turtle import *
from freegames import floor, vector

tiles = {}
neighbors = [
    vector(100, 0),
    vector(-100, 0),
    vector(0, 100),
    vector(0, -100),
]

def load():
    count = 1

    for y in range(-200, 200, 100):
        for x in range(-200, 200, 100):
            mark = vector(x, y)
            tiles[mark] = count
            count += 1

    tiles[mark] = None

    for count in range(1000):
        neighbor = choice(neighbors)
        spot = mark + neighbor

        if spot in tiles:
            number = tiles[spot]
            tiles[spot] = None
            tiles[mark] = number
            mark = spot

def square(mark, number):
    up()
    goto(mark.x, mark.y)
    down()

    color('black', 'white')
    begin_fill()
    for count in range(4):
        forward(99)
        left(90)
    end_fill()

    if number is None:
        return
    elif number < 10:
        forward(20)

    write(number, font=('Arial', 60, 'normal'))

def tap(x, y):
    x = floor(x, 100)
    y = floor(y, 100)
    mark = vector(x, y)

    for neighbor in neighbors:
        spot = mark + neighbor

        if spot in tiles and tiles[spot] is None:
            number = tiles[mark]
            tiles[spot] = number
            square(spot, number)
            tiles[mark] = None
            square(mark, None)

def draw():
    for mark in tiles:
        square(mark, tiles[mark])
    update()

setup(420, 420, 370, 0)
hideturtle()
tracer(False)
load()
draw()
onscreenclick(tap)
done()

四子连珠

引言

目标是做一个四子连珠,我们会用到 turtle 和 freegames 库,其中 turtle 库提供了一个绘图窗口和一组绘图函数,使得我们可以方便地在窗口中绘制图形,freegames 库是 Al Sweigart 编写的一个 Python 游戏库,其中包含了一些有趣的、易于理解的游戏示例,适合初学者使用。

img

四子连珠中需要用到 freegames 中的 line 函数,用于在绘图窗口中绘制一条线段。

from turtle import *
from freegames import line

接下来需要定义 Connect Four 游戏的初始状态,turns 是一个字典,定义了红色和黄色两个玩家的轮流顺序。它的键为一个字符串 'red' 和 'yellow',值为另一个字符串,表示下一个玩家的颜色。这样,每当一个玩家下完棋之后,就可以通过交换 state 字典中 'player' 键的值来轮到下一个玩家。state 是一个字典,表示游戏的状态。它包含两个键值对(player 和 rows),'player':表示当前玩家的颜色。初始值为 'yellow',即黄色玩家先手。'rows':表示每一列中已经下了多少个棋子。初始值为一个长度为 8 的列表,每个元素都为 0,表示每一列都没有棋子。board 是一个二维列表,表示棋盘上每个位置的状态。初始时,每个位置都为空(用字符 'O' 表示),表示没有下棋子。

turns = {'red': 'yellow', 'yellow': 'red'}
state = {'player': 'yellow', 'rows': [0] * 8}
board = [['O' for i in range(8)] for j in range(8)]

第一个函数 grid() 的作用是在绘图窗口中绘制 Connect Four 游戏的背景、网格和初始的白珠。它首先设置窗口的背景色为浅蓝色,然后在窗口中绘制横向和纵向的线条,形成一个 8 x 8 的方格网格。接着,它在每个方格的中心点上绘制一个半径为 40 像素的白色圆点,作为棋盘的初始状态。这个函数只在游戏开始时调用一次,用于初始化游戏窗口和棋盘状态。具体来说,它执行以下几个操作:

  1. 首先通过 bgcolor() 方法将背景颜色设置为浅蓝色,为整个画布设置了一个背景色。

  2. 接下来使用 for 循环和 line() 方法画出垂直的线条,通过 range() 函数的参数设置线条的位置和数量。循环中的第一个参数 -150 表示从左边起第一个线条的位置,第二个参数 200 表示线条的高度,第三个参数 50 表示两条线条之间的距离。

  3. 然后,使用嵌套的 for 循环和 goto() 方法画出棋盘上的 64 个空位,每个空位由一个半径为 20 像素的白色圆点组成,循环中的第一个参数 -175 和第二个参数 200 表示棋盘的左下角位置,循环中的第三个参数 50 表示相邻两个空位之间的距离。

  4. 最后,通过 update() 方法将图形立即更新到屏幕上。

def grid():
    bgcolor('light blue')

    for x in range(-150, 200, 50):
        line(x, -200, x, 200)

    for x in range(-175, 200, 50):
        for y in range(-175, 200, 50):
            up()
            goto(x, y)
            dot(40, 'white')

    update()

第二个函数 tap() 函数的作用是响应用户点击事件,在用户点击的行上绘制红色或黄色的圆圈,并更新游戏状态。具体来说,它通过计算用户点击的列和行,将相应的圆圈绘制在界面上,并将当前下棋的玩家的信息更新为下一个玩家。同时,它还检查是否有玩家已经赢得了游戏,如果是则结束游戏。具体来说,它执行以下几个操作:

  1. 获取当前玩家和每一行已经有的棋子数。这些信息被存储在 state 字典中

  2. 根据玩家点击的位置计算出应该下在哪一行。x 参数表示点击事件的 x 坐标,200 是棋盘的 x 中心,50 是每个格子的宽度。该表达式会把 x 限制在 (-200, 200) 的范围内,并根据格子宽度将其映射到 (0, 8) 的行号上。int() 函数用于将浮点数转换为整数。

  3. 获取该行已经下了多少颗棋子。

  4. 在棋盘数组 board 中记录新下的棋子。row 是行号,count 是该行已经下了多少颗棋子。board[row][count] 用于指定位置。根据当前玩家是红色还是黄色,分别用字母 A 或 B 表示。

  5. 根据行和列计算出该棋子的位置。x 坐标是从左到右的列编号,用来计算棋子的横向位置。y 坐标是从下到上的棋子编号,用来计算棋子的纵向位置。50 是每个格子的宽度,25 是每个棋子的半径。

  6. 画出棋子。up() 用于将画笔抬起,goto(x, y) 用于移动画笔到棋子的中心位置。dot(40, player) 用于画一个半径为 40 的圆点,颜色为当前玩家的颜色。update() 用于更新画面。

  7. 将该行已有棋子数加 1。

  8. 检查当前玩家是否已经胜利。如果是,调用 exitonclick() 函数,等待用户点击关闭窗口。

  9. 将下一次落子权交给另一个玩家。根据当前玩家的颜色,从 turns 字典中查找下一个玩家的颜色。

def tap(x, y):
    player = state['player']
    rows = state['rows']

    row = int((x + 200) // 50)
    count = rows[row]
    board[row][count] = 'A' if state['player'] == "yellow" else 'B'

    x = ((x + 200) // 50) * 50 - 200 + 25
    y = count * 50 - 200 + 25

    up()
    goto(x, y)
    dot(40, player)
    update()

    rows[row] = count + 1
    if check_winner(board, state['player']): exitonclick()
    state['player'] = turns[player]

第三个函数 check_winner() 的作用是在每次有玩家下棋后检查游戏状态,判断是否有玩家已经赢得了游戏。它对于胜利的判断分别检查了三种情况:

  1. 纵向:在每一列上检查是否有四个相同的棋子。
  2. 横向:在每一行上检查是否有四个相同的棋子。
  3. 斜向:在两条斜线上检查是否有四个相同的棋子。
  4. 如果检测到有玩家赢得了游戏,则在控制台输出游戏结束的消息。如果没有玩家赢得游戏,则返回 False。
def check_winner(board, player):
    winner = player
    # 纵向判断是否满足四子连珠
    for i in range(8):
        for j in range(4):
            if board[i][j] == board[i][j+1] == board[i][j+2] == board[i][j+3] and (board[i][j] in ['A', 'B']):
                print(f"游戏结束!{winner} 胜利!")
                return True
    
    # 横向判断是否满足四子连珠
    for i in range(4):
        for j in range(8):
            if board[i][j] == board[i+1][j] == board[i+2][j] == board[i+3][j] and (board[i][j] in ['A', 'B']):
                print(f"游戏结束!{winner} 胜利!")
                return True

    # 斜向判断是否满足四子连珠
    for i in range(3, 8):
        for j in range(4):
            if board[i][j] == board[i-1][j+1] == board[i-2][j+2] == board[i-3][j+3] and (board[i][j] in ['A', 'B']):
                print(f"游戏结束!{winner} 胜利!")
                return True
    for i in range(3, 8):
        for j in range(3, 8):
            if board[i][j] == board[i-1][j-1] == board[i-2][j-2] == board[i-3][j-2] and (board[i][j] in ['A', 'B']):
                print(f"游戏结束!{winner} 胜利!")
                return True

最后这部分代码用于设置游戏窗口的大小、绘制游戏界面、接收玩家鼠标点击事件以及启动游戏循环。

setup(420, 420, 370, 0)
hideturtle()
tracer(False)
grid()
onscreenclick(tap)
done()

setup(420, 420, 370, 0):设置游戏窗口的宽度和高度为 420 像素,左上角在屏幕上的坐标为 (370, 0)。

hideturtle():隐藏小海龟图形,因为拼图游戏中不需要绘制任何小海龟图形。

tracer(False):关闭自动刷新画面,这样可以避免画面闪烁。

grid():调用 grid() 函数绘制了游戏界面。

onscreenclick(tap):将 tap 函数与屏幕的鼠标点击事件绑定,使得玩家可以通过点击空白区域来交换拼图块。

done():启动游戏并进入消息循环,等待用户交互。

总体代码

from turtle import *
from freegames import line

turns = {'red': 'yellow', 'yellow': 'red'}
state = {'player': 'yellow', 'rows': [0] * 8}
board = [['O' for i in range(8)] for j in range(8)]

# 画出背景、网格和白珠
def grid():
    bgcolor('light blue')

    for x in range(-150, 200, 50):
        line(x, -200, x, 200)

    for x in range(-175, 200, 50):
        for y in range(-175, 200, 50):
            up()
            goto(x, y)
            dot(40, 'white')

    update()

# 开始下棋(白珠染色)
def tap(x, y):
    player = state['player']
    rows = state['rows']

    row = int((x + 200) // 50)
    count = rows[row]
    board[row][count] = 'A' if state['player'] == "yellow" else 'B'

    x = ((x + 200) // 50) * 50 - 200 + 25
    y = count * 50 - 200 + 25

    up()
    goto(x, y)
    dot(40, player)
    update()

    rows[row] = count + 1
    if check_winner(board, state['player']): exitonclick()
    state['player'] = turns[player]

# 检测是否满足胜利条件
def check_winner(board, player):
    winner = player
    # 纵向判断是否满足四子连珠
    for i in range(8):
        for j in range(4):
            if board[i][j] == board[i][j+1] == board[i][j+2] == board[i][j+3] and (board[i][j] in ['A', 'B']):
                print(f"游戏结束!{winner} 胜利!")
                return True
    
    # 横向判断是否满足四子连珠
    for i in range(4):
        for j in range(8):
            if board[i][j] == board[i+1][j] == board[i+2][j] == board[i+3][j] and (board[i][j] in ['A', 'B']):
                print(f"游戏结束!{winner} 胜利!")
                return True

    # 斜向判断是否满足四子连珠
    for i in range(3, 8):
        for j in range(4):
            if board[i][j] == board[i-1][j+1] == board[i-2][j+2] == board[i-3][j+3] and (board[i][j] in ['A', 'B']):
                print(f"游戏结束!{winner} 胜利!")
                return True
    for i in range(3, 8):
        for j in range(3, 8):
            if board[i][j] == board[i-1][j-1] == board[i-2][j-2] == board[i-3][j-2] and (board[i][j] in ['A', 'B']):
                print(f"游戏结束!{winner} 胜利!")
                return True

setup(420, 420, 370, 0)
hideturtle()
tracer(False)
grid()
onscreenclick(tap)
done()
posted @ 2023-02-24 14:52  筱团  阅读(78)  评论(0编辑  收藏  举报