?? jldoc.cpp
字號:
// 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 + -