⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 jldoc.cpp

📁 很经典的用C++编的空当接龙的程序
💻 CPP
📖 第 1 页 / 共 4 页
字号:
// JLDoc.cpp : implementation of the CJLDoc class
//

#include "stdafx.h"
#include "JL.h"

#include "JLDoc.h"
#include "DlgSelGame.h"
#include "DlgSettings.h"
#include "JLView.h"
#include "MainFrm.h"
#include "DlgAIShow.h"
#include "MsgDlg.h"
#include "DlgAICal.h"
#include "PassedDlg.h"
#include "DlgDefGame.h"
#include "DlgScore.h"

//用于支持洗牌的函数
#include <algorithm>
#include <functional>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CJLDoc

IMPLEMENT_DYNCREATE(CJLDoc, CDocument)
IMPLEMENT_SERIAL(COperation,CObject,3)
IMPLEMENT_SERIAL(COperations,CObject,3)

#define FitTrash(card,x) (TYPE(x)==TYPE(card)&&NUM(x)==NUM(card)-1)
 
//取得文档指针
CJLDoc * AfxGetDocument() {
	POSITION tmplPos = AfxGetApp()->GetFirstDocTemplatePosition();
	CDocTemplate* pTmpl = AfxGetApp()->GetNextDocTemplate(tmplPos);
	POSITION docPos = pTmpl->GetFirstDocPosition();
 	CJLDoc* pDoc = (CJLDoc*)pTmpl->GetNextDoc(docPos);

 	ASSERT(pDoc);
 	return pDoc;
}

//取得视图指针
CJLView * AfxGetView() 
{
	CJLView * pView = AfxGetDocument()->GetView();
	ASSERT(pView);
	return pView;
}


BEGIN_MESSAGE_MAP(CJLDoc, CDocument)
	//{{AFX_MSG_MAP(CJLDoc)
	ON_COMMAND(IDM_UNDO, OnUndo)
	ON_COMMAND(IDM_SETTING, OnSetting)
	ON_COMMAND(IDM_SELECTGAMENUMBER, OnSelectgamenumber)
	ON_COMMAND(IDM_SAVE, OnSave)
	ON_COMMAND(IDM_LOAD, OnLoad)
	ON_COMMAND(IDM_AI, OnAi)
	ON_COMMAND(IDM_HELP_NEXTSTEP, OnHelpNextstep)
	ON_COMMAND(IDM_RAND, OnRand)
	ON_COMMAND(IDM_PREV_GAME, OnPrevGame)
	ON_COMMAND(IDM_NEXT_GAME, OnNextGame)
	ON_COMMAND(IDM_AGAIN, OnAgain)
	ON_COMMAND(IDB_EDIT, OnEdit)
	ON_COMMAND(IDM_SCORE, OnScore)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CJLDoc construction/destruction
CJLDoc::CJLDoc() 
{
	// TODO: add one-time construction code here
	m_nSel = 0;
	m_pOps = new CObList;
	m_Hints.ClrHints();

	srand(UINT(time(NULL)));
	m_nCurGameNumber = Random();

	m_bEnableAlert    = false;
	m_bQuickMove      = false;
	m_bEnableDbClick  = true;
	m_bMaxMove        = true;
	m_bAICalRunning   = false;
	m_bRealTimeUpdate = false;
	m_nDDASpeed       = 75;
}

CJLDoc::~CJLDoc()
{
	ClrOpsRecords();
	delete m_pOps;
}
/////////////////////////////////////////////////////////////////////////////
// CJLDoc serialization

void CJLDoc::Serialize(CArchive& ar)
{
	struct SIZE_INF { UINT size, *pAddr; };
	const SIZE_INF cols[3] = {
		{ sizeof(m_iCards  ) / sizeof(UINT) , &m_iCards[0][0]   },
		{ sizeof(m_iBuffer ) / sizeof(UINT) , &m_iBuffer[0]     },
		{ sizeof(m_iRecycle) / sizeof(UINT) , &m_iRecycle[0][0] },
	};

	if (ar.IsStoring()) {
		ar<<m_nCurGameNumber;//保存本局代号
		m_pOps->Serialize(ar);//保存步骤记录
		for(UINT k = 0; k < 3 ; ++k)//保存牌局
			for(UINT i = 0; i < cols[k].size; i++)
				ar<<cols[k].pAddr[i];
	}
	else {
		ar>>m_nCurGameNumber;//读取本局代号
		ClrOpsRecords();//清除步骤记录,准备读档 
		m_pOps->Serialize(ar);//读取步骤记录
		for(UINT k = 0; k < 3 ; ++k)//读取牌局
			for(UINT i = 0; i < cols[k].size; i++)
				ar>>cols[k].pAddr[i];
	}
}

/////////////////////////////////////////////////////////////////////////////
// CJLDoc diagnostics

#ifdef _DEBUG
void CJLDoc::AssertValid() const
{
	CDocument::AssertValid();
}

void CJLDoc::Dump(CDumpContext& dc) const
{
	CDocument::Dump(dc);
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CJLDoc commands
bool CJLDoc::IsCol(UINT col)
{
	return (col<=16&&col>=1);
}


	// +-----Buf----+    +---Recycle---+
	// | 9 10 11 12 | JL | 13 14 15 16 |
	// +------------+    +-------------+
	// +------------Cards--------------+
	// | 1   2   3   4   5   6   7   8 |
	// +-------------------------------+
//核心的移牌程序:将src列的n张牌移动到des列
void CJLDoc::MoveCards(UINT des, UINT src, UINT n)
{
	ASSERT(IsCol(src) && !IsEmptyCol(src) //源列非空
		&& n <= CntSeriIn(src) //最多可移动全部序列牌
		&& IsCol(des));

	//取消当前选中
	if(!m_bAICalRunning) UnselectCardCol();

	CRect rSrc,rDes;
	UINT *pSrc,*pDes,*pTop;
	if(src <= 8) {
		pTop = &m_iCards[src-1][0];
		pSrc = &pTop[pTop[19]-1];//指向底牌
		//刷新移走的部分
		rSrc = RectOf(src,pTop[19]-n+1,n);
		//改变牌的计数
		pTop[19] -= n;
		//刷新整列
		if(pTop[19]+n > 13) {
			rSrc.UnionRect(rSrc,RectOf(src,1,pTop[19]));
		}
	} else if(src <= 12) {
		pSrc = &m_iBuffer[src-9];
		rSrc = RectOf(src,1,1);
	} else {
		pTop = &m_iRecycle[src-13][0];
		pSrc = &pTop[pTop[13]-1];//指向底牌
		pTop[13] -= n;//改变牌的计数
		ASSERT(n==1);
		rSrc = RectOf(src,1,1);
	}

	if(des <= 8) {
		pTop = &m_iCards[des-1][0];
		pDes = &pTop[pTop[19]];//指向底牌之下
		//刷新移来的部分
		rDes = RectOf(des,pTop[19]+1,n);
		//改变牌的计数
		pTop[19] += n;
		//刷新整列
		if(pTop[19] > 13) {
			rDes.UnionRect(rDes,RectOf(des,1,pTop[19]));
		}
	} else if(des <= 12) {
		pDes = &m_iBuffer[des-9];
		rDes = RectOf(des,1,1);
		ASSERT(!m_iBuffer[des-9] && n==1);
	} else {
		pTop = &m_iRecycle[des-13][0];
		pDes = &pTop[pTop[13]];//指向底牌之下
		pTop[13] += n;//改变牌的计数
		ASSERT(n==1);
		rDes = RectOf(des,1,1);
	}

	UINT *p = pSrc+1-n;//p指向最上面那张将要被移动的牌
	for(UINT i = 0; i < n; i++) {
		*pDes++ = *p;//移动到目标处
		*p++ = 0;//源牌清零
	}

	if(m_bAICalRunning && !m_bRealTimeUpdate) {
		return;
	}
	InvalidateRect(RectOfStep());//刷新步数信息
	InvalidateRect(rSrc);//刷新源列牌面
	InvalidateRect(rDes);//刷新目标列牌面
}

//按照规则f的条件判断 a可放在b下 这一论断对两张牌a,b是否成立
//规则f如下:
//    红牌可以放在黑牌下,黑牌可以放在红牌下
//    但是必须保证大点数的牌在上,小点数的牌在下
//    且点数只能相差1点
//    例如:
//        照此规则,红桃5下只可以放黑桃4或者梅花4
//    
bool CJLDoc::FitFormula(UINT b, UINT a)
{
	ASSERT(a<=52 && a>=1 && b<=52 && b>=1);
	//Type()   =  0 黑桃   1 红桃   2 梅花   3 方块
	//b,a不同花色且b的点数比a大一点
	return (TYPE(a)+TYPE(b))%2==1 && NUM(b)-NUM(a)==1;
}
//洗牌
void CJLDoc::Shuffle() 
{
/*	
	//准备一副新牌,并洗牌
	using namespace std;
	vector<int> cards(52);
	for(int i = 1, *it = cards.begin(); it != cards.end(); *it++ = i++) ;
	
	srand(m_nCurGameNumber >> 16);
	random_shuffle(cards.begin(),cards.end());
	srand(m_nCurGameNumber & 0xFFFF);
	random_shuffle(cards.begin(),cards.end());
*/
	//准备一副新牌,并洗牌
	int cards[52];
	for(int i = 1; i <= 52; ++i) cards[i-1] = i;

	using namespace std;

	srand(m_nCurGameNumber >> 16);
	random_shuffle(cards, cards + 52);
	srand(m_nCurGameNumber & 0xFFFF);
	random_shuffle(cards, cards + 52);

	//清空缓存列、回收列和牌列
	struct SIZE_INF { UINT size, *pAddr; };
	const SIZE_INF cols[3] = {
		{ sizeof(m_iCards  ) / sizeof(UINT) , &m_iCards[0][0]   },
		{ sizeof(m_iBuffer ) / sizeof(UINT) , &m_iBuffer[0]     },
		{ sizeof(m_iRecycle) / sizeof(UINT) , &m_iRecycle[0][0] },
	};
	for(UINT k = 0; k < 3 ; ++k)
		for(UINT i = 0; i < cols[k].size; i++)
			cols[k].pAddr[i] = 0;
	//发牌到牌列m_iCards
	for(int col = 0; col <= 3; col++) {
		UINT *pTop;

		pTop = &m_iCards[col][0];
		for(i=0;i<7;i++) {
			UINT *pDes = &pTop[pTop[19]];//指向底牌之下
			pDes[i] = cards[col*7 + i];
		}
		pTop[19] = 7;

		pTop = &m_iCards[col+4][0];
		for(i=0;i<6;i++) {
			UINT *pDes = &pTop[pTop[19]];//指向底牌之下
			pDes[i] = cards[28 + col*6 + i];
		}
		pTop[19] = 6;
	}
}

void CJLDoc::SelectCardCol(UINT col)
{
	ASSERT(IsCol(col) && !IsEmptyCol(col));

	//(如果有)取消当前选中
	UnselectCardCol();

	//选中另一列并刷新
	m_nSel = col;
	InvalidateRect(RectOf(col,CntCardsIn(col),1));
}
//不选中此列
void CJLDoc::UnselectCardCol()
{
	//游戏刚开始、玩家取消选中、有
	//牌移动等都会导致无任何列被选

	//如果没有选中任何列就不管
	if(!m_nSel) return;

	CRect r = RectOf(m_nSel,CntCardsIn(m_nSel),1);
	//取消选中并刷新
	m_nSel = 0;
	InvalidateRect(r);
}
//看看此列是否为空
bool CJLDoc::IsEmptyCol(UINT col)
{
	ASSERT(IsCol(col));
	if(col <= 8) {
		return !m_iCards[col-1][19];
	} else if(col <= 12) {
		return !m_iBuffer[col-9];
	}else {
		return !m_iRecycle[col-13][13];
	}
}

// 计算实际允许从被选中列移动多少张纸牌到目标列
//(计算出来之后可以利用函数MoveCards来进行实际的移动)
UINT CJLDoc::CntMaxMv(UINT desCol, UINT srcCol)
{
	ASSERT(IsCol(srcCol) && !ColInRecycle(srcCol) && !IsEmptyCol(srcCol));
	ASSERT(IsCol(desCol));

	UINT n = 0;
	//目标列是牌列
	if(desCol <= 8) {
		if(COL_IN_BUFF(srcCol)) { //源列是缓存列
			if(IsEmptyCol(desCol) || 
				FitFormula( BottCard(desCol) , BottCard(srcCol) ))
				n = 1;
		} else {
			//源列是牌列
			UINT nSeri = CntSeriIn(srcCol);//计算连续多少张牌
			if(IsEmptyCol(desCol)) { //目标列是空牌列
				UINT maxSuppliment = CntMaxSuppliment(true);
				//肯定可以移动
				n = min(maxSuppliment,nSeri);
			} else {
				UINT bottSrc = BottCard(srcCol);//源列最下面的牌
				UINT bottDes = BottCard(desCol);//目标列最下面的牌
				UINT numSrc = NUM(bottSrc);//牌点数
				UINT numDes = NUM(bottDes);//牌点数
				n = numDes - numSrc;
				UINT maxSuppliment = CntMaxSuppliment(false);
				//必须严格满足以下条件才可以移动:
				if( 	//目标牌点数介于源序列牌之上的指定区间内 且
					numDes >= numSrc + 1 && numDes <= numSrc + nSeri &&
					//它比源牌大奇数点且红黑相异或大偶数点红黑相同 且
					n%2 == (TYPE(bottSrc)+TYPE(bottDes))%2 && 
					//有足够空间来移动
					n <= maxSuppliment) 
				{ 
					;
				} else {
					n = 0;
				}
			}
		}
	} else if(desCol <= 12) { //目标列是缓存列
		if(IsEmptyCol(desCol))
			n = 1;//缓存列无牌则可移动一张
	} else { //目标列是回收列
		int s = BottCard(srcCol);
		if(!IsEmptyCol(desCol)) {
			int d = BottCard(desCol);
			if(TYPE(s)==TYPE(d) && NUM(d) == NUM(s) - 1)
				n = 1;//花色相符,点数小一,则可以回收
		} else if(NUM(s) == 1 && TYPE(s)+13 == desCol)
			n = 1;//是A且花色相符(且相应回收列中无牌)
	}

	return n;
}

//遍历各列并自动扔出1-12列中最小的牌直到无法扔出为止
void CJLDoc::AutoThrow()
{
	UINT colSrc, cardSrc, numSrc, colDes,sons[2];
	while(true) { //直到没有牌可扔为止
		for(colSrc = 1; colSrc <= 12; colSrc++) { //寻找可扔的牌所在的列
			if(IsEmptyCol(colSrc)) continue;
			cardSrc=BottCard(colSrc);
			if(!Trashable(cardSrc)) continue;
			numSrc = NUM(cardSrc);
			colDes = TYPE(cardSrc) + 13;
			if(numSrc == 1 || numSrc == 2) break;
			if(m_bAICalRunning) break;//自动解答时废牌能扔就扔

			//考虑子牌是否已经回收
			sons[0] = sons[1] = colDes;
			sons[0] -= colDes > 13 ? 1 : -3;
			sons[1] += colDes < 16 ? 1 : -3;
			if(
				m_iRecycle[sons[0]-13][13] && //子牌的回收列非空
				m_iRecycle[sons[1]-13][13] && //子牌的回收列非空
				NUM(BottCard(sons[0])) >= numSrc-1 &&
				NUM(BottCard(sons[1])) >= numSrc-1
			) break;
		}
		if(colSrc > 12) break;
		if(!m_bQuickMove && ColInCard(colSrc)) { //快速移动的时候没有动画
			CRect rs = RectOf(colSrc,CntCardsIn(colSrc),1);
			CRect rd = RectOf(colDes,1,1);
			::LineDDA(rs.left,rs.top,rd.left,rd.top,LineDDACallback,cardSrc);
		}
		MoveCards(colDes,colSrc,1);
		if(m_pOps->IsEmpty()) {
			Record(new COperations(colDes,colSrc,1));
		} else { //扔牌后的自动扔牌动作必须和扔牌动作放在一起
			((COperations*)m_pOps->GetTail())->AddOperation(colDes,colSrc,1);
		}
	}
}

//测试是否游戏结束

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -