目录

  • 概述
    • 特色
    • 功能演示
  • 详细说明
    • CardSuit(纸牌花色枚举)
    • Card(纸牌类)
    • SpiderSolitaire(蜘蛛纸牌类)
      • 常量
      • 抽象数据结构(ADT)
      • 翻牌的实现
      • 纸牌移动的实现
        • buttone1Press()
        • button1Motion()
        • button1Release()

概述

使用Python语言开发的蜘蛛纸牌游戏, 与蜘蛛纸牌游戏的功能基本一致(目前缺少"撤销"操作, 后续可能会添加).

代码可从我的资源下载, 该资源为付费资源(!!创作不易, 敬请谅解!!)

特色

  • 界面基于 tkinter 模块;
  • 支持游戏的保存和导入;
  • 纸牌的移动和发牌通过鼠标操作.

功能演示

  • 移动纸牌演示:

  • 发牌演示

  • 保存与导入演示.

详细说明

CardSuit(纸牌花色枚举)

CardSuit枚举类用于表示纸牌的花色, 分别为: 红桃(Hearts), 黑桃(Spades), 方片(Diamonds), 梅花(Clubs). 目前游戏只用到了黑桃. 后续可以扩充为使用 两种花色 和 四种花色.

class CardSuit(Enum):Hearts = 0Spades = 1Diamonds = 2,Clubs = 3

Card(纸牌类)

Card类用于表示纸牌. 纸牌的属性包括:

  • 点数(points: int),
  • 花色(suit: CardSuit),
  • 是否正面朝上(isfront: bool),
  • canvas中对应的项目ID
  • 正面图像(_imfront: ImageTk.PhotoImage),
  • 反面图像(_imback: ImageTk.PhotoImage)

image() 方法用于返回要显示的图像. isfront==True时, 返回正面图像, 否则返回反面图像.

class Card:def __init__(self, points: int, suit: CardSuit, imfront: ImageTk.PhotoImage, imback: ImageTk.PhotoImage,isfront: bool = False) -> None:self.points = pointsself.suit = suitself._imfront = imfrontself._imback = imbackself.isfront = isfrontself.itemId = Nonedef image(self):if self.isfront:return self._imfrontelse:return self._imback...

SpiderSolitaire(蜘蛛纸牌类)

SpiderSolitaire为蜘蛛纸牌类, 包含整个游戏的界面的显示, 事件响应, 游戏逻辑等.

SpiderSolitaire类继承自Mainwindow.

class SpiderSolitaire(Mainwindow):"""蜘蛛纸牌"""def __init__(self, master: Tk, **kw):super().__init__(master, **kw)self.createMenu()self.canvas = Canvas(self.mainframe, width=CANVAS_WIDTH, height=CANVAS_HEIGHT, background='gray55')self.canvas.grid(row=0, column=0, sticky=NS)...       # set window's title.self.master.title('Spider Solitaire')# set window's geometry sizeself.master.geometry(f'{CANVAS_WIDTH+10}x{CANVAS_HEIGHT+10}')self.showmessage('Ready.')

常量

以下常量在SpiderSolitaire类中被用到.

# 纸牌的宽度和高度
CARD_WIDTH = 68
CARD_HEIGHT = 103# 列宽. COL_WIDTH > CARD_WIDTH
COL_WIDTH = 90
# 行宽
ROW_WIDTH = 25# 左侧空白宽度
COL_PAD = (COL_WIDTH - CARD_WIDTH) // 2# 列数
COL_NUM = 10
# 待发牌堆数, 每堆 COL_NUM 张牌
HEAP_NUM = 5# 牌的组数. 牌的总数为: PACK_NUM * 13
PACK_NUM = 8# 右下角牌堆显示的宽度
HEAP_WIDTH = 20
# 待发牌堆的水平偏移和垂直偏移
HEAP_OS_H = COL_WIDTH*COL_NUM - HEAP_WIDTH * HEAP_NUM - CARD_WIDTH
HEAP_OS_V = ROW_WIDTH * 25# 显示文字的水平偏移和垂直偏移
TEXT_OS_H = COL_WIDTH*COL_NUM//2
TEXT_OS_V = ROW_WIDTH * 25 + CARD_HEIGHT//2# CANVAS的宽高
CANVAS_WIDTH = COL_WIDTH*COL_NUM
CANVAS_HEIGHT = HEAP_OS_V + CARD_HEIGHT + 25

抽象数据结构(ADT)

  • 纸牌队列
    游戏界面中每一列纸牌(card_queue), 用一个列表(list)表示. 列表元素为Card. 所有纸牌列用一个列表(list)表示.
class SpiderSolitaire(Mainwindow):...def start(self):...self.card_queues = [[] for k in range(COL_NUM)]while len(cards) > 0:for k in range(COL_NUM):self.card_queues[k].append(cards.pop())if len(cards) <= 0:break
  • 待发纸牌
    待发纸牌(card_heaps)分成5组, 每组10张(对应纸牌的列数). 每组纸牌(card_heap)用一个列表(list)表示, 所有待发的纸牌构成一个列表(list)
class SpiderSolitaire(Mainwindow):...def start(self):...self.card_heaps = []for k in range(HEAP_NUM):self.card_heaps.append([cards.pop() for i in range(COL_NUM)])
  • 分数
    分数(score)用int表示, 初始分数为500, 每移动一组纸牌, 分数-1. 完成一组序列(从"K"到"A"连续递减), 分数+100.
  • 操作计数
    操作计数(moves)用int表示, 初始为0, 每移动一组纸牌, 操作计数+1.

翻牌的实现

每一张在纸牌队列和待发纸牌中的纸牌, 在canvas中都对应一个image项, 该项通过调用self.canvas.create_image()创建.
翻牌动作通过删除该纸牌对应的image项( self.canvas.delete(card.itemId)), 然后创建新的image项实现.

    def flopTop(self, col: int):"""翻最上面的一张牌"""if len(self.card_queues[col]) == 0:return Falsecard: Card = self.card_queues[col][-1]card.isfront = Trueself.canvas.delete(card.itemId)card.itemId = self.canvas.create_image(self.col_from*COL_WIDTH+COL_PAD, (len(self.card_queues[self.col_from])-1)*ROW_WIDTH, image=card.image(), anchor='nw')return True

纸牌移动的实现

  • 纸牌的移动用到了鼠标左键的"按下(<ButtonPress-1>)", “拖动(<B1-Motion>)”, "释放(<ButtonRelease-1>)"三个预定义事件.分别绑定self.buttone1Press(), self.button1Motion(), self.button1Release()三个函数.
class SpiderSolitaire(Mainwindow):def __init__(self, master: Tk, **kw):...self.canvas.bind("<ButtonPress-1>", self.buttone1Press)self.canvas.bind("<ButtonRelease-1>", self.button1Release)self.canvas.bind("<B1-Motion>", self.button1Motion)

buttone1Press()

  • buttone1Press()函数中, 获取当前点击的纸牌(card)所在的行和列, 并判断该纸牌是否正面朝上(card.isfront == True), 且其上的所有要移动的纸牌(self.select_cards)是否满足连续递减的条件 (该函数同时需要判断点击区域是否为待发牌区域).
  • 因为Canvas中的所有项目是按照创建先后进行堆叠的, 如果只是单纯移动项目的位置, 可能导致意想不到的遮盖混乱. 因此需要调用self.canvas.lift(card.itemId)函数, 将要移动的纸牌的堆叠顺序,移动到最上层.
  • 因为可能出现移动失败的情况(移动到的列跟移动的纸牌不能衔接, 或者将移动头纸牌的点数不是13的纸牌放置到空列, 则移动失败), 因此需要保存移动的起始列(self.col_from)
  • 为了在button1Motion()函数中, 纸牌能够跟着鼠标移动, 需要保存移动纸牌与鼠标的相对位置(self.offset_x, self.offset_y)
    def selectCheck(self, col: int, row: int):card_queue = self.card_queues[col]if card_queue[row].isfront == False:return Falsefor i in range(row+1, len(card_queue)):if card_queue[i-1].points - card_queue[i].points != 1:return Falsereturn True....def buttone1Press(self, event):"""鼠标左键按下事件.判断选中的卡片. 或者发牌."""# 发牌if event.x >= HEAP_OS_H and event.y >= HEAP_OS_V:self.dealCard()col = (event.x - COL_PAD) // COL_WIDTHrow = event.y // ROW_WIDTHself.offset_x = event.x - col * COL_WIDTH + COL_PADself.offset_y = event.y - row * ROW_WIDTHqueue_len = len(self.card_queues[col])if row >= queue_len:if event.y < (queue_len - 1) * ROW_WIDTH + CARD_HEIGHT:row = queue_len - 1else:returnif self.selectCheck(col, row) == False:self.select_cards = []returnself.select_cards = self.card_queues[col][row:]for card in self.select_cards:card: Cardself.canvas.lift(card.itemId)self.col_from = coldel self.card_queues[col][row:]

button1Motion()

  • button1Motion()函数的功能很简单, 只要调用self.canvas.coords()修改所有要移动的纸牌(self.select_cards)的坐标值即可
    def button1Motion(self, event):col = event.x // COL_WIDTHfor row, card in enumerate(self.select_cards):self.canvas.coords(card.itemId, event.x - self.offset_x, event.y - self.offset_y + row * ROW_WIDTH)

button1Release()

  • button1Release()函数是纸牌移动的最终过程, 需要判断移动是否符合条件;
  • 如果移动成功, 将移动的纸牌添加到该列纸牌的末尾, 并翻转移动纸牌的起始列的最上面的一张牌;
  • 如果移动失败, 将移动的纸牌归还给起始列;
  • 同时, 需要判断是否构成收牌条件(从"K"到"A"依次递减), 如果符合条件, 需要删除响应的纸牌, 修改分数.
    def button1Release(self, event):if len(self.select_cards) == 0:returncol = event.x // COL_WIDTHif self.moveCheck(col) == False:col = self.col_fromfor row, card in enumerate(self.select_cards, len(self.card_queues[col])):self.canvas.coords(card.itemId, col * COL_WIDTH + COL_PAD, row * ROW_WIDTH)self.card_queues[col].extend(self.select_cards)self.select_cards.clear()self.moves += 1self.score -= 1self.canvas.itemconfig(self.text_item, text=f'分数: {self.score}\n操作: {self.moves}')ok = self.tryTakeup(col)if ok:self.flopTop(col)if col != self.col_from:# 翻转最后一张牌if len(self.card_queues[self.col_from]) > 0:self.flopTop(self.col_from)

全部代码请从我的资源下载, 该资源为付费资源(!!创作不易, 敬请谅解!!)

Python开源项目之蜘蛛纸牌相关推荐

  1. GitHub上的“金矿”(236个Python开源项目,涵盖了15个领域)

    人生苦短,越来越多的人,都开始用Python了. 但寻找好的项目资源,费时又费力,成了头号难题. 现在,好消息来了.最近,有人在GitHub上放了一座"金矿". 目前里面有236个 ...

  2. python开源库推荐_推荐5个值得关注的Python开源项目

    原标题:推荐5个值得关注的Python开源项目 今天小编看到了五个开源项目,觉得还错,推荐给大家. 1.Wagtail CMS Wagtail是一个基于Django的CMS系统 它拥有优质的用户体验 ...

  3. python开源项目贡献_通过为开源项目做贡献,我如何找到理想的工作

    python开源项目贡献 by Utsab Saha 由Utsab Saha 通过为开源项目做贡献,我如何找到理想的工作 (How I found my dream job by contributi ...

  4. 9月Python开源项目Top10

    原文链接: 9月Python开源项目Top10 - TinyMind https://www.tinymind.cn/articles/3776 声明:转载的, 就是让自己记住,以后熟练了Python ...

  5. 近万个Python开源项目中精选Top34!

    作者 | Mybridge 编译 | 仲培艺 出品 | CSDN(ID:CSDNNews) [导语]踏着人工智能.区块链的东风,近年来一路"横冲直撞"的 Python 在实现了从小 ...

  6. 2018年Python开源项目Top100!只在这里!

    整理 | Jane 出品 | Python大本营 2018 年的最后一天,营长为大家新鲜出炉了一份 2018 年 Python 开源项目 Top100 清单!这些项目都是营长每月通过收集 Mybrid ...

  7. 精选180+Python开源项目,随你选!做项目何愁没代码

    编辑 | Jane 出品 | Python大本营 每一位程序员,每天大部分时间都是在和代码打交道.但是对于广大的普通用户来说,最重要的不是代码,而是代码最终生成的应用程序.但是,每个项目都从头开始自己 ...

  8. 精选Python开源项目Top10!

    作者 | MyBridge 译者 | Linstancy 整理 | Jane 出品 | AI科技大本营 [导读]过去一个月里,我们对近 250 个 Python 开源项目进行了排名,并挑选出热度前 1 ...

  9. 8月精选Python开源项目Top10

    作者 | Mybridge 译者 | linstancy 编辑 | Jane 出品 | AI科技大本营 [导读]过去一个月里,我们对近 250 个 Python 开源项目进行了排名,并挑选出热度前 1 ...

最新文章

  1. 工作5年才有自己博客...汗...
  2. 在命令行导出MySQL数据到Excel
  3. c语言回文串判定代码_C语言编写一个程序,判断输入的一个字符串是否是回文。...
  4. bzoj 2878 [Noi2012]迷失游乐园——树上的期望dp
  5. 【整理】RPA选择器针对SAP系统的设置
  6. Python自学——python的常用模块
  7. Xml的编码和Bom
  8. 安装VCSA6.7(vCenter Server Appliance 6.7) 2019.7.9
  9. caffe 源码阅读与运行流程
  10. YUI 3:Node
  11. win7科学计算机不支持,win7“不支持的硬件,你的电脑使用的处理器专为最新版win...
  12. 04.使用元字符 (Python)
  13. Excel实战 第1章 数据处理
  14. 你今天因为 YYYY-MM-dd 被提 BUG 了吗?
  15. 无论你英语多差,只要想学,看了此文必有改变
  16. MGRE结合OSPF(超详解)
  17. 设置git使用vimdiff比较差异
  18. 6.4.1等价关系2
  19. MySQL中 (GROUP BY 用法)和(ORDER BY用法)
  20. 梦想照进现实:挣扎中的国产操作系统

热门文章

  1. YOCTO 2019
  2. 有些新闻,公众可以遗忘,但程序员不能遗忘!
  3. 投了那么久简历,终于有人理我了,看看2020年第一波企业运维面试题吧
  4. 易康_最佳分割尺度的选择——ESP2工具的使用
  5. MySQL--MHA高可用配置及故障切换实例
  6. 计算机联锁主机部分组成,第十章三取二计算机联锁系统设备使用办法.ppt
  7. 14天阅读挑战赛(学习建议)
  8. android手机 文件丢失,Android手机意外删除了文件恢复
  9. 汇编语言:简单的英文字母大小写转换
  10. <font>字体标签