用python整个活——用numpy做一个蜘蛛纸牌
Posted ¿¿¿¡¡¡
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用python整个活——用numpy做一个蜘蛛纸牌相关的知识,希望对你有一定的参考价值。
1,过程
蜘蛛纸牌大家玩过没有?之前的电脑上自带的游戏,用他来摸鱼过的举个手。
但是现在的电脑上已经没有蜘蛛纸牌了。所以……
可不可以自己做一个呢?
想法有了,实践开始。
首先,应该怎么写?首选的方案就是pygame和numpy。
最后选了numpy。一是因为作者用电脑的时间比较短,没有时间力,而手机的在线编译器可以用numpy,不能用pygame。二是因为之前了解过numpy,但是pygame当时都没安装,是昨天才安装完毕的三是因为想挑战一下自己。下次可以试试用pygame。
所以一个动都没动过numpy的小白开始努(作)力(死)。
从10月30日写到11月9日,中间提问了14个问题,查询了大约五十多个资料,修复了20多个bug,很感谢回答我的问题的人。
numpy有一个缺点就是只能输入输出,自己要移动哪些牌,移动到哪里,很耗费时间,于是把三次输入硬生生压缩成了一次,有些难理解,并且对于被盖住的牌,只能用数字99表示了。整体有些出入。
中间有一次写了四五个小时代码,结果手残点到返回主页了没保存,心态崩了
还好还是完成了。如果程序运行有bug,可以指出
游戏规则见代码里
2,思路
整体思路:
1,创造空矩阵a,在0,1,2,3行和第四行的0,1,2,3列变成99牌,定义函数qipai来起一列牌。创建列表pai,并通过询问难度来定义不同的牌。 2,定义函数shan0,为了美观和不占地方,将矩阵a的全0行删除 3,操作移动,用input询问要移动的牌,信息:移动的牌在第几列?第几行到第几行?要移动到哪一列?之后将范围里的牌存储到列表yidong里并清0,再依次赋值到移动到的那一列。期间的错误操作如移动的牌顺序不相接,移动之后的位置不相接等,通过扣得分处罚(绝对不是因为作者懒不想再返回原位置) 4,操作看规则,print无脑就行 5,操作翻99牌,循环从下往上侦测,当侦测到99牌将这个位置在pai里随机赋值,做出错误操作处理 6,操作起牌,调用函数就行 7,操作收牌,从下往上侦测是否有x.01,如果有,则开始侦测上面的牌是否按规律排列,按照规律则依次变成0,否则继续侦测x.01作出没有可收牌的处理 8,完善,修改bug,设置开始和结束语3,配置
首先要python是必不可少的,其次是numpy模块。
快捷键win+R,输入cmd进入终端。如果没有pip包给爷上网查下载(bushi,输入pip install numpy安装,安装完成之后输入:
import numpy
numpy.zeros((3,3))
如果报错则是没安装,输出了一个3x3的矩阵则成功安装了。
4,代码
import random#导入随机数模块
import numpy as np#导入numpy模块
def qipai():#定义函数qipai,用来起一行牌
xunhuan=40
jiange=1
for xunhuan1 in range(0,10):#一列一列侦测
while xunhuan == xunhuan:#死循环
xunhuan=xunhuan-1#xunhuan自减,
if a[xunhuan][xunhuan1] != 0:#如果xunhuan行不是零,侦测到其他数字
chu=a[xunhuan+1][xunhuan1]=random.choice(pai)#将他的上一行变成pai里面的任意一个并用变量储存
pai.remove(chu)#删除这一个数
break#侦测下一列
if xunhuan == 0:#如果检测到了最后还没有数字就是这一列没有数字
chu=a[0][xunhuan1]=random.choice(pai)#在0行随机发放数字并用变量储存
pai.remove(chu)#删除这一个数
break#侦测下一列
xunhuan=40#变量变换
def shan0():#删除矩阵中所有0行
o = np.sum(a == 0, axis=1)
liebiao = []
for xunhuan2 in range(len(o)):
q = o.tolist()[xunhuan2]
if q == 10:
liebiao.append(xunhuan2)#问的大佬,我也看不懂
global newa#全局变量
newa=np.delete(a,liebiao,0)#newa为删除零行后的a,仅仅是显示作用,改变在a上改变
print("正在发牌")
a=np.zeros((40,10))#40x10的全0矩阵
nandu=int(input("请选择难度,简单输入1,中等输入2,困难输入3"))#选择难度
if nandu == 1:#简单难度
pai=[1.01,1.02,1.03,1.04,1.05,1.06,1.07,1.08,1.09,1.10,1.11,1.12,1.13]#一种花色
pai=pai*8#自乘
elif nandu == 2:#中等难度
pai=[1.01,1.02,1.03,1.04,1.05,1.06,1.07,1.08,1.09,1.10,1.11,1.12,1.13,2.01,2.02,2.03,2.04,2.05,2.06,2.07,2.08,2.09,2.10,2.11,2.12,2.13]#两种花色
pai=pai*4#自乘
elif nandu == 3:#困难难度
pai=[1.01,1.02,1.03,1.04,1.05,1.06,1.07,1.08,1.09,1.10,1.11,1.12,1.13,2.01,2.02,2.03,2.04,2.05,2.06,2.07,2.08,2.09,2.10,2.11,2.12,2.13,3.01,3.02,3.03,3.04,3.05,3.06,3.07,3.08,3.09,3.10,3.11,3.12,3.13,4.01,4.02,4.03,4.04,4.05,4.06,4.07,4.08,4.09,4.10,4.11,4.12,4.13]#四种花色
pai=pai*2#自乘
print("难度设置完毕")
for xunhuan in range(0,10):
a[0][xunhuan]=99
xunhuan=0
for xunhuan in range(0,10):
a[1][xunhuan]=99
xunhuan=0
for xunhuan in range(0,10):
a[2][xunhuan]=99
xunhuan=0
for xunhuan in range(0,10):
a[3][xunhuan]=99
a[4][0]=99
a[4][1]=99
a[4][2]=99
a[4][3]=99#设置99牌站位
dh=0
s=0
chu=0
t=0
xunhuan=0
jiange=0
xunhuan1=0
zuobi=0
defen=1000
qi=5
lie3=0
ktsfwl=0
wuyong=0
zuobima=0
#初始化变量
qipai()#开局起牌
while 1 == 1:#死循环
shan0()#删除0行
if defen <= 0:#得分变成0时游戏失败
print("得分为0,游戏失败!!!")
print(3/0)#没啥好方法了,让他自己报错吧
print(newa,"\\n")#输出牌局
print(" 0 1 2 3 4 5 6 7 8 9")#输出索引间距
print("目前得分:",defen)#说目前的得分
if np.where(a!=0)[0].shape[0]==0:#如果矩阵为全0
break#游戏成功,退出死循环
caozuo,lie1,hang1,hang2,lie2 =map(int,input("输入5个数字,不知道规则请输入2 0 0 0 0(用索引形式)\\n").split())#询问操作并执行命令,规则见上
if caozuo == 1: #要移动牌
if a[hang1][lie1] != 0 and a[hang2+1][lie1] == 0:#hang1和hang2不等于零且hang2的下面没有牌才执行移动
if a[hang2][lie1] != 0:
xunhuan=hang1#将hang1到hang2的牌保存到yidong
yidong=[]#空列表
while xunhuan <= hang2:#开始侦测
yidong.append(a[xunhuan][lie1])#添加这个数
a[xunhuan][lie1]=0#将这个数变成0
xunhuan=xunhuan+1#自增
xunhuan=0
if len(yidong) != 1:#yidong的长度不是1才执行规律侦测
while xunhuan < len(yidong)-1:#-1是因为后面有yidong[xunhuan+1],为了不越界
wuyong=yidong[xunhuan]-yidong[xunhuan+1]-0.01
sxc=f'wuyong:.3f'#大佬教的浮点数四舍五入三位,前面的牌-后面的牌-0.01=x.00时
if str(sxc)[:-3]!='000':
defen=defen-50#defen减50
print("移动的牌不按规律排列,作弊次数+1")
break#侦测到作弊
xunhuan=xunhuan+1
xunhuan=0
while xunhuan == xunhuan:#死循环侦测,从上往下
if a[xunhuan][lie2] != 0:#侦测到不为0
xunhuan=xunhuan+1#侦测下一行
else:#此时xunhuan是该列最后一个数字所在行数的下一行
xunhuan=xunhuan-1#切换到上一行,该数字
if a[0][lie2] != 0:#矩阵这一列的0行不是0
ktsfwl=0
dh=a[xunhuan][lie2]#dh变成该数字在的位置
else:#若是0,则是直接移动
ktsfwl=1
xunhuan1=0
while xunhuan1 < len(yidong):#xunhuan1为yidong的长度时
geduan=yidong[xunhuan1]#赋值
a[xunhuan+1+xunhuan1][lie2]=geduan
xunhuan1=xunhuan1+1#xunhuan自增
if ktsfwl == 1:#0行是0,取消侦测,直接退出
print("移动成功")
defen=defen-5
break
wuyong=dh-a[xunhuan+1][lie2]-0.01
sxc=f'wuyong:.3f'#这一行减移动的首个数字减0.01的差为x.00
if str(sxc)[-3:] =='000':
print("移动成功")
defen=defen-5#移动成功
break
elif str(a[xunhuan+1][lie2])[:-2] =='.1' and str(dh)[:-3]=='.11':#如果是.11和.1相接也是移动成功
defen=defen-5
print("移动成功")
break
else:#两种情况都不是
print("移动的位置不相接,作弊次数加一")
defen=defen-50
zuobi=zuobi+1#判断作弊
break
continue#下一次操作
else:
print("请输入正确的范围")
defen=defen-10
continue
else:
print("请输入正确的范围")
defen=defen-10
continue
if caozuo == 2:#查看规则时
print("规则:共有104个数字,10组,数字的小数点前代表花色,小数点后代表点数,牌只能放在比他数值大0.01,花色任意 的牌上,将一样花色的牌按照13到1的顺序排列再输入5即可消除这一组牌")
print("下面的x指输入的第二个数字,y是第三个,z是第四个,w是第五个")
print("将8组牌全部消除则胜利。其中移动牌是将在第x列的第y到第z行的牌移动到第w列,翻99牌则是翻x列标注99的牌,下面没牌之后才能翻。起一行牌则是起一行备用的牌,一行牌会覆盖在下面,共5组。消除牌则是将x列13到1顺序排列并同样花色的牌全部消除")
print("第一个数字如果移动牌输入1,看规则输入2,翻99牌输入3,起牌输入4,消除牌输入5")
print("按照上面的要求输入五个数字,用空格隔开,输入行列要用索引形式,如果用不到第2,3,4,5个数字输入0")
print("得分初始为1000,正常操作-5,犯规操作-50,错误操作-10,若得分为0则游戏失败,消除一次牌+500")
print("索引是啥给爷自己查,懒得写")
continue#下一次操作
if caozuo == 3:#翻99牌时
xunhuan=6#因为只有5行上面会有99牌,节省时间
if a[0][lie1] != 99:#0行都不是99牌,错误操作
print("该列没有99牌")
defen=defen-10
continue#下一次操作
while xunhuan >= 0:#循环检测第一个99牌
print("a")
xunhuan=xunhuan-1#自减
if a[xunhuan][lie1] == 99:#检测到99牌
chu=a[xunhuan][lie1]=random.choice(pai)#将99牌的位置随机发放牌
pai.remove(chu)#删除该数字
print("已经揭牌")
defen=defen-5
break#跳出循环
elif a[xunhuan][lie1] != 0:#检测到不是0也不是99的数
print("99牌下面还有其他的牌")#下面还有其他的牌
defen=defen-10
break
continue#下一次操作
if caozuo == 4:#起牌
if qi == 0:#如果牌堆没牌了
print("已经没有牌可以起了")
defen=defen-10
else:#牌堆有牌
qipai()#执行函数qipai
print("已经为您起牌")
qi=qi-1#自减牌堆数量
defen=defen-5
continue#下一次操作
if caozuo == 5:#消除牌
xunhuan=40#从40行往下检测
jiange=2#定义
while xunhuan == xunhuan:#开始检测
jiange=0
if xunhuan==11:#已经不够13张牌时还没有x.01
print("没有可以消除的牌")
defen=defen-10
break
xunhuan=xunhuan-1#自减
wuyong=a[xunhuan][lie1]-0.01
sxc=f'wuyong:.3f'#sxc为上减下减0.01
if str(sxc)[-3:] == '000':#出现x.01
xunhuan1=xunhuan#此时xunhuan为x.01所在行数
while xunhuan1 == xunhuan1:#死循环检测上-下是否为0.01
if xunhuan1==xunhuan-12:#12次后没有发现错误
xunhuan2=0#消除成功
while xunhuan2<=12:#依次消除
a[xunhuan-xunhuan2][lie1]=0#x.01在的行数依次减1,2…后的行数为0
xunhuan2=xunhuan2+1#自增
jiange=0#jiange为0,是因为正确运行跳出循环的
break
wuyong=a[xunhuan1-1][lie1]-a[xunhuan1][lie1]-0.01
sxc=f'wuyong:.3f'
if str(sxc)[-5:]=='0.000':#循环侦测上下差值-0.01是否为0
xunhuan1=xunhuan1-1#自减
else:#这个0.01上面并不按规律排列
jiange=1#是因为不按规律排列而退出循环
break
if jiange == 0:#侦测是因为什么跳出循环,如果因为正确操作
print("已经移除")
defen=defen+500
break
#非正确操作接着检测
continue#检测结束后继续下一次操作
#退出循环,游戏胜利
print("恭喜获胜")
print("您目前的得分为",defen,"作弊了",zuobi,"次,游戏结束!")#报成绩
四,结束语
总之还是比(磕)较(磕)顺(绊)畅(绊)地完成了。如果代码有错误或者可以优化的地方,一定一定要指出,谢谢。
接下来打算去学习turtle库,出一个新的作品,谢谢智齿
---------------------------------------------------------------end-------------------------------------------------------------
Win32小游戏--蜘蛛纸牌
前一段时间完成了蜘蛛纸牌的仿写,现将过程和思路记录下来
首先,为了符合复用性,在win32的基本框架中,把可变的部分用c++封装起来成为一系列虚函数,这样如果再继续写游戏的话,只需要继承这个类就可以了
CGameApp.h
1 #pragma once 2 class CGameApp //接口类 3 4 public: 5 virtual void OnCreatGame() 6 virtual void OnGameDraw() 7 virtual void OnGameRun() 8 virtual void OnKeyDown() 9 virtual void OnKeyUp() 10 virtual void OnLButtonDown() 11 virtual void OnLButtonUp() 12 virtual void OnMouseMove() 13 ;
1 #include<windows.h> 2 #include"CGameApp.h" 3 4 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); 5 6 int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPreInstance,LPSTR pCmdLine,int nCmdShow) 7 8 // 1. 设计 9 WNDCLASSEX wndclass; 10 wndclass.cbClsExtra = 0; 11 wndclass.cbWndExtra = 0; 12 wndclass.cbSize = sizeof(wndclass); 13 wndclass.hbrBackground = (HBRUSH)COLOR_WINDOW; 14 wndclass.hCursor = 0; 15 wndclass.hIcon = 0; 16 wndclass.hIconSm = 0; // 窗口左上的小图标 17 wndclass.hInstance = hInstance; 18 wndclass.lpfnWndProc = WndProc; // 窗口的消息处理函数 19 wndclass.lpszClassName = "cyc"; // 注册窗口类的名字 20 wndclass.lpszMenuName = 0; 21 wndclass.style = CS_HREDRAW|CS_VREDRAW; 22 23 // 2. 注册 24 if( ::RegisterClassEx(&wndclass) == FALSE) 25 26 ::MessageBox(0,"注册失败","提示",MB_OK); 27 return 0; 28 29 // 3. 创建 30 HWND hwnd = ::CreateWindow("cyc","游戏壳",WS_OVERLAPPEDWINDOW,0,0,500,500,0,0,hInstance,0); 31 if(hwnd == 0) 32 33 ::MessageBox(0,"创建失败","提示",MB_OK); 34 return 0; 35 36 37 // 4. 显示窗口 38 ::ShowWindow(hwnd,SW_SHOW); 39 40 // 5. 消息循环 41 MSG msg; 42 while(::GetMessage(&msg,0,0,0)) 43 44 ::TranslateMessage(&msg); 45 ::DispatchMessage(&msg); // 分发, 调用消息的处理函数WndProc 46 47 48 49 return 0; 50 51 52 53 54 55 56 CGameApp *p = 0; 57 LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 58 59 switch (uMsg) 60 61 case WM_CREATE: 62 63 if(p == NULL) 64 p->OnCreatGame(); 65 66 67 break; 68 case WM_PAINT: 69 70 if(p == NULL) 71 p->OnGameDraw(); 72 73 74 break; 75 case WM_TIMER: 76 77 if(p == NULL) 78 p->OnGameRun(); 79 80 81 break; 82 case WM_KEYDOWN: 83 84 if(p == NULL) 85 p->OnKeyDown(); 86 87 88 break; 89 case WM_KEYUP: 90 91 if(p == NULL) 92 p->OnKeyUp(); 93 94 95 break; 96 case WM_LBUTTONDOWN: 97 98 if(p == NULL) 99 p->OnLButtonDown(); 100 101 102 break; 103 case WM_LBUTTONUP: 104 105 if(p == NULL) 106 p->OnLButtonUp(); 107 108 109 break; 110 case WM_MOUSEMOVE: 111 112 if(p == NULL) 113 p->OnMouseMove(); 114 115 116 break; 117 case WM_CLOSE: // 关闭 118 ::PostQuitMessage(0); // 发送一个退出的消息 119 break; 120 121 return DefWindowProc( hwnd, uMsg, wParam, lParam); 122
接下来就是 蜘蛛纸牌建设的过程了,先来分析一下纸牌的功能,因为蜘蛛纸牌里抛去大小王,所以1--K每副牌里有13张牌,由于我想搭建类似与纸牌类游戏框架的东西,所以分为可重写,和不可重写两个部分,不可重写的,所以类就设置为单张牌,一副牌,牌的排列,规则这些类,由于哪种游戏用几副牌,鼠标点击是否取牌,鼠标点击是否拿牌,这些有开发人员自行定义,UML如下,
在接下来分模块记录的时候也分为框架内,和框架外来进行记录
框架内:
在CCards和CPocker两个类中,均是简单的参数赋值,此处也是第一次使用STL中vector组件,在CCardsRank类中,每一张牌的属性都用结构体记录了下来,如图
并且在贴图的过程中,我多设置了一个判断来加载位图是否进入内存,为开发人员省去了加载位图的过程
1 void CCardsRank::ShowRank(HDC hdc, HINSTANCE hIns) 2 3 //1==============================显示窗口的背景图============================ 4 if(m_hBmpWndBack == 0) 5 m_hBmpWndBack = ::LoadBitmap(hIns,MAKEINTRESOURCE(IDB_WND_BACK)); 6 7 HDC hMemDC = ::CreateCompatibleDC(hdc); 8 ::SelectObject(hMemDC,m_hBmpWndBack); 9 ::BitBlt(hdc,0,0,850,600,hMemDC,0,0,SRCCOPY); 10 ::DeleteDC(hMemDC); 11 //1==============================显示窗口的背景图============================ 12 13 14 //2==============================显示牌===================================== 15 //-------------有没有牌的背景图---------- 16 if(m_hBmpCardsBack == 0) 17 m_hBmpCardsBack = ::LoadBitmap(hIns,MAKEINTRESOURCE(IDB_CARDS_BACK)); 18 //-------------有没有牌的背景图---------- 19 for(size_t i=0;i<m_vecRank.size();i++) 20 21 list<Node*>::iterator ite = m_vecRank[i].begin(); 22 while(ite != m_vecRank[i].end()) 23 24 //----------贴图------------------- 25 HDC hMemDC = ::CreateCompatibleDC(hdc); 26 27 if((*ite)->bflag == false) 28 ::SelectObject(hMemDC,m_hBmpCardsBack); 29 else 30 ::SelectObject(hMemDC,(*ite)->pCards->m_hBmpCards); 31 32 ::BitBlt(hdc,(*ite)->x,(*ite)->y,71,96,hMemDC,0,0,SRCCOPY); 33 34 ::DeleteDC(hMemDC); 35 //----------贴图------------------ 36 ++ite; 37 38 39 //2==============================显示牌===================================== 40
在CardsApp中,由于创建多少副牌是不确定的,那么就没法创建对象,在这里就使用了博客内记录的动态创建对象,只需要在CardsApp中贴上两个宏,开发人员就可以随意的创建多少副牌,在CardsApp中可以自动的去创建对象,而不用修改代码,并且重点标注的是,由于蜘蛛纸牌有松开鼠标归位的功能,所以在显示移动牌的时候,都是以牌的上一个位置为标准进行移动牌坐标的计算
1 void CCardsApp::ShowCursorCards(HDC hdc) 2 3 int X = pointMouseMove.x - pointMouseDown.x; 4 int Y = pointMouseMove.y - pointMouseDown.y; 5 6 // 在 移动的距离的位置显示牌 7 list<Node*>::iterator ite = m_lstCursorCards.begin(); 8 while(ite != m_lstCursorCards.end()) 9 10 HDC hMemDC = ::CreateCompatibleDC(hdc); 11 ::SelectObject(hMemDC,(*ite)->pCards->m_hBmpCards); 12 ::BitBlt(hdc,(*ite)->x+X,(*ite)->y+Y,850,600,hMemDC,0,0,SRCCOPY); 13 ::DeleteDC(hMemDC); 14 ++ite; 15 16
在CRule中,进行三个判断,第一个接收牌后的操作,利用vector自身的计数函数,以及遍历链表,通过是否接收牌这个规则之后,与链表结合,更新位置,翻牌,第二个是获得鼠标点击牌的坐标,在获得之前也需要进行一系列的判断,是否光标点击在牌上,牌是否是正面,是不是最后一张能否拿起来,这些都为真之后,将牌放入光标移动的链表中,在这一步值得一提的是,运用了反向迭代器,正向迭代器比反向迭代器指向少一个元素,所以在删迭代器指向元素前,反向迭代器++,或者转为正向迭代器后-- ,第三个,如果接收失败的话,将光标链表中的牌放回原先列表的尾部
1 bool CRule::ReceiveCards(POINT point, CCardsRank* pCardsRank, list<Node*>& lstCursor) 2 3 // 遍历 所有的链表 4 for(size_t i=0;i<pCardsRank->m_vecRank.size();i++) 5 6 // 判断坐标的 交给子类 7 if(this->IsReceiveCardsRule(point,i,pCardsRank,lstCursor) == true) 8 9 // 和 i这个链表结合 10 pCardsRank->m_vecRank[i].splice(pCardsRank->m_vecRank[i].end(),lstCursor); 11 // 更新位置(对齐) 12 this->UpDatePos(pCardsRank,i); 13 // 翻牌 14 if(pCardsRank->m_vecRank[m_nGetCardsListID].empty() == false) 15 pCardsRank->m_vecRank[m_nGetCardsListID].back()->bflag = true; 16 m_nGetCardsListID = -1; 17 return true; 18 19 20 return false; 21 22 void CRule::GetCards(POINT point, CCardsRank* pCardsRank, list<Node*>& lstCursor) 23 24 // 遍历 所有的链表 25 for(size_t i=0;i<pCardsRank->m_vecRank.size();i++) 26 27 // 遍历 i 个链表的所有节点 28 list<Node*>::reverse_iterator rev_ite = pCardsRank->m_vecRank[i].rbegin(); 29 while(rev_ite != pCardsRank->m_vecRank[i].rend()) 30 31 // 判断光标是否点击到这个牌上 32 if(point.x >= (*rev_ite)->x && point.x <= (*rev_ite)->x+71 33 && point.y >= (*rev_ite)->y && point.y <= (*rev_ite)->y+96) 34 35 // 判断是不是正面 36 if((*rev_ite)->bflag == true) 37 38 // 判断能不能拿起来 39 list<Node*>::iterator ite = --(rev_ite.base()); 40 if( this->IsGetCardsRule(pCardsRank,i,ite) == true) 41 42 // 记录下标 43 m_nGetCardsListID = i; 44 // 放到光标的链表上 45 lstCursor.splice(lstCursor.end(),pCardsRank->m_vecRank[i],ite,pCardsRank->m_vecRank[i].end()); 46 47 48 return; 49 50 ++rev_ite; 51 52 53 54 void CRule::RevertCards(CCardsRank* pCardsRank, list<Node*>& lstCursor) 55 56 if(m_nGetCardsListID != -1) 57 58 // 把光标的链表 放回到 m_nGetCardsListID 这个链表尾部 59 pCardsRank->m_vecRank[m_nGetCardsListID].splice(pCardsRank->m_vecRank[m_nGetCardsListID].end(),lstCursor); 60 m_nGetCardsListID = -1; 61 62
框架外:
针对于蜘蛛纸牌而言,难点在于规则的制定上,在CMyCardsRank中需要注意的点就是,这个类的构造应该使用初始化列表来写,初始化列表的作用1.初始化成员属性 2.先完成指定类的构造,也就是说没有CCardsRank这个类,哪来的CMyCardsRank呢?
CMyCardsRank::CMyCardsRank(void):CCardsRank(11)
并且在CCardsApp中的显示应该用双缓冲来完成,因为只要连续贴的图超过一张,就有可能多张图出现在两个显卡刷新周期之内,这样的话就会出现闪屏的问题,所以利用双缓冲再为一个兼容性DC在创建一个兼容性DC,多次贴图在第一个兼容性DC中,最后一次性显示到窗口HDC中,这就是解决窗口闪烁的双缓冲技术
1 void CCardsApp::OnGameDraw() // WM_PAINT 2 3 HDC dc = ::GetDC(m_hMainWnd); 4 HDC hdc = ::CreateCompatibleDC(dc); 5 HBITMAP hbitmap = ::CreateCompatibleBitmap(dc,850,600); 6 ::SelectObject(hdc,hbitmap); 7 //------------------------------------------------------------- 8 // 显示排列 9 if(m_pRank != 0) 10 m_pRank->ShowRank(hdc,m_hIns); 11 this->ShowCursorCards(hdc); 12 //------------------------------------------------------------- 13 ::BitBlt(dc,0,0,850,600,hdc,0,0,SRCCOPY); 14 ::DeleteObject(hbitmap); 15 ::DeleteDC(hdc); 16 ::ReleaseDC(m_hMainWnd,dc); 17
大部分的重写都在CRule中,第一个拿牌的规则,利用迭代器的移动和首个牌的数字--,来判断参数的一串是否连续,一旦连续就可以拿牌
1 bool CMyRule::IsGetCardsRule(CCardsRank* pCardsRank, int nlstID, list<Node*>::iterator iteCursorPos) 2 3 int num = (*iteCursorPos)->pCards->m_nCardsNum; 4 5 while(iteCursorPos != pCardsRank->m_vecRank[nlstID].end()) 6 7 if(num != (*iteCursorPos)->pCards->m_nCardsNum) 8 return false; 9 --num; 10 iteCursorPos++; 11 12 return true; 13
第二个,发牌的规则,首先判断在发牌的序列中,也就是最后一个链表中是否有牌了,通过了,再判断点到的是不是发牌序列中的最后一张牌,也就是说是否触发发牌的指令,最后一个判断前十个链表中是否有空的链表
1 bool CMyRule::IsOpenCards(POINT point, CCardsRank* pCardsRank) 2 3 //判断最后一个链表里是否有东西 4 if(pCardsRank->m_vecRank[10].empty() == false) 5 6 //判断是不是点到最后一张牌 7 if(point.x >= pCardsRank->m_vecRank[10].back()->x && point.x <= pCardsRank->m_vecRank[10].back()->x+71 8 && point.y >= pCardsRank->m_vecRank[10].back()->y && point.y <= pCardsRank->m_vecRank[10].back()->y+96) 9 10 //前十个有没有空链表 11 for(int i = 0;i < 10;i++) 12 13 if(pCardsRank->m_vecRank[i].empty() == true) 14 return false; 15 16 return true; 17 18 19 return false; 20
第三个,接收牌的规则,这个就只有两点,是否鼠标坐标在上个牌的坐标范围之内,是否鼠标选中这张牌的数字比该链表的尾结点减一
1 bool CMyRule::IsReceiveCardsRule(POINT point, int nlstID, CCardsRank* pCardsRank, list<Node*>& lstCursorCards) 2 3 if(pCardsRank->m_vecRank[nlstID].empty() == true) 4 5 if(point.x >= 10+nlstID*81 && point.x <= 10+nlstID*81+71 && point.y >= 10 && point.y <= 10+96) 6 7 return true; 8 9 10 else 11 12 if(point.x >= pCardsRank->m_vecRank[nlstID].back()->x && point.x <= pCardsRank->m_vecRank[nlstID].back()->x+71 13 && point.y >= pCardsRank->m_vecRank[nlstID].back()->y && point.y <= pCardsRank->m_vecRank[nlstID].back()->y+96) 14 15 if(lstCursorCards.front()->pCards->m_nCardsNum == pCardsRank->m_vecRank[nlstID].back()->pCards->m_nCardsNum - 1) 16 17 return true; 18 19 20 21
第四个,更新坐标,这里需要注意的就是不更新松手后复位的坐标
1 void CMyRule::UpDatePos(CCardsRank* pCardsRank, int nlstID) 2 3 int j = 0; 4 list<Node*>::iterator ite = pCardsRank->m_vecRank[nlstID].begin(); 5 while(ite != pCardsRank->m_vecRank[nlstID].end()) 6 7 (*ite)->x = 10+nlstID*81; 8 (*ite)->y = 10+j*20; 9 ++j; 10 ++ite; 11 12 this->DeleteNode(pCardsRank,nlstID); 13
第五个,当结成连续的13张牌时,进行消除,这个判断要在接收牌时,以及发牌时进行
1.链表内至少有13个结点,最后一张牌应该是A
2.反向遍历判断有没有13张连续的正面
3.不连续或者为背面时结束
4.反向迭代器删除时要转为正向
5.删除后,尾结点翻牌
1 void CMyRule::DeleteNode(CCardsRank* pCardsRank, int nlstID) 2 3 if(pCardsRank->m_vecRank[nlstID].size() >= 13 && pCardsRank->m_vecRank[nlstID].back()->pCards->m_nCardsNum == 1) 4 5 int num = 1; 6 list<Node*>::reverse_iterator rite = pCardsRank->m_vecRank[nlstID].rbegin(); 7 for(int i = 0;i < 13;i++) 8 9 if((*rite)->bflag == false) 10 return; 11 if((*rite)->pCards->m_nCardsNum != num) 12 return; 13 ++rite; 14 ++num; 15 16 list<Node*>::iterator ite = rite.base(); 17 while(ite != pCardsRank->m_vecRank[nlstID].end()) 18 19 delete (*ite); 20 ite = pCardsRank->m_vecRank[nlstID].erase(ite); 21 22 23 if(pCardsRank->m_vecRank[nlstID].empty() == false) 24 pCardsRank->m_vecRank[nlstID].back()->bflag = true; 25 26
从宏观上看,蜘蛛纸牌就是vector-List的应用,这也是我第一次尝试去写一个框架,代码我放在文件里了,希望各位能够指正一下
2019-07-08 11:47:46 编程小菜鸟自我总结,各位大佬可以提出自己的建议和意见,谢谢!!!
以上是关于用python整个活——用numpy做一个蜘蛛纸牌的主要内容,如果未能解决你的问题,请参考以下文章
用python演绎神奇的生命游戏,在游戏中学习numpy和matplotlib动画