用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动画

用python演绎神奇的生命游戏,在游戏中学习numpy和matplotlib动画

js实现蜘蛛纸牌

《游戏学习》Java实现蜘蛛纸牌小游戏源码

《游戏学习》Java实现蜘蛛纸牌小游戏源码

Python纸牌游戏效率