📄 hand.cpp
字号:
//
// Copyright (c) 2005, Wei Mingzhi <whistler@openoffice.org>
// All Rights Reserved.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of
// the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
// 02110-1301, USA
//
#include "main.h"
CHand::CHand():
m_iNumTiles(0),
m_iNumTileSets(0)
{
}
CHand::~CHand()
{
}
void CHand::Sort(handtile_t *begin, handtile_t *end)
{
if (end <= begin)
return;
handtile_t pivot = *begin, *a = begin, *b = end;
while (a < b) {
while (a < b && b->tile >= pivot.tile)
b--;
*a = *b;
while (a < b && a->tile <= pivot.tile)
a++;
*b = *a;
}
*a = pivot;
Sort(begin, a - 1);
Sort(a + 1, end);
}
void CHand::Sort()
{
Sort(&m_Tiles[0], &m_Tiles[m_iNumTiles - 1]);
}
void CHand::AddRandomTile(int flag)
{
if (m_iNumTiles >= MAX_HANDTILE) {
TerminateOnError("CHand::GetRandomTile(): Max tile reached");
}
m_Tiles[m_iNumTiles].tile = CTile::RandomTile();
m_Tiles[m_iNumTiles].flags = flag;
m_Tiles[m_iNumTiles].tileset = -1;
m_iNumTiles++;
}
void CHand::AddTile(const CTile &t, int flag, int tileset)
{
if (m_iNumTiles >= MAX_HANDTILE) {
TerminateOnError("CHand::GetRandomTile(): Max tile reached");
}
m_Tiles[m_iNumTiles].tile = t;
m_Tiles[m_iNumTiles].flags = flag;
m_Tiles[m_iNumTiles].tileset = tileset;
m_iNumTiles++;
}
bool CHand::RemoveTile(const CTile &t, int filter)
{
int i, j;
for (i = 0; i < m_iNumTiles; i++) {
if (m_Tiles[i].flags & filter)
continue; // skip open tiles
if (m_Tiles[i].tile == t) {
for (j = i; j < m_iNumTiles - 1; j++) {
m_Tiles[j] = m_Tiles[j + 1];
}
m_iNumTiles--;
return true;
}
}
return false; // cannot find this tile
}
bool CHand::RemoveTile(int index, int filter)
{
if (index < 0 || index >= m_iNumTiles) {
return false;
}
if (m_Tiles[index].flags & filter) {
return false;
}
for (int j = index; j < m_iNumTiles - 1; j++) {
m_Tiles[j] = m_Tiles[j + 1];
}
m_iNumTiles--;
return true;
}
bool CHand::HasTile(const CTile &t, int filter)
{
for (int i = 0; i < m_iNumTiles; i++) {
if (m_Tiles[i].flags & filter)
continue; // skip unwanted tiles
if (m_Tiles[i].tile == t)
return true;
}
return false; // cannot find this tile
}
bool CHand::Pung(const CTile &t, bool open, int flags)
{
handtile_t *p[3] = {NULL, NULL, NULL};
int found = 0, i;
for (i = 0; i < m_iNumTiles; i++) {
if (m_Tiles[i].flags & (HT_LOCKED | HT_TOCHOW))
continue; // skip locked tiles
if (m_Tiles[i].tile == t) {
p[found++] = &m_Tiles[i];
if (found >= (open ? 2 : 3))
break;
}
}
if (found < (open ? 2 : 3))
return false; // cannot pung with this tile
if (open) {
// Add this tile into the hand
m_Tiles[m_iNumTiles].tile = t;
m_Tiles[m_iNumTiles].flags = (HT_OPENPUNG | HT_FROMOPPONENT | flags);
m_Tiles[m_iNumTiles].tileset = m_iNumTileSets;
m_iNumTiles++;
}
// mark the tiles as open pungs
p[0]->flags |= ((open ? HT_OPENPUNG : HT_CLOSEDPUNG) | flags);
p[1]->flags |= ((open ? HT_OPENPUNG : HT_CLOSEDPUNG) | flags);
p[0]->tileset = p[1]->tileset = m_iNumTileSets;
if (!open) {
p[2]->flags |= (HT_CLOSEDPUNG | flags);
p[2]->tileset = m_iNumTileSets;
}
// Add a tileset
m_TileSets[m_iNumTileSets].first = t;
m_TileSets[m_iNumTileSets].type = ((open ? HT_OPENPUNG : HT_CLOSEDPUNG) | flags);
m_iNumTileSets++;
return true;
}
bool CHand::Chow(const CTile &t, int location, bool open, int flags)
{
handtile_t *p[3] = {NULL, NULL, NULL};
CTile to_find[2];
if (t.GetSuit() & (TILESUIT_WIND | TILESUIT_DRAGON))
return false; // cannot chow wind or dragon tiles
int i, j, found = 0;
switch (location) {
case CHOW_LOWER:
if (t.GetValue() > 7)
return false;
to_find[0] = t() + 1;
to_find[1] = t() + 2;
break;
case CHOW_MIDDLE:
if (t.GetValue() == 1 || t.GetValue() == 9)
return false;
to_find[0] = t() - 1;
to_find[1] = t() + 1;
break;
case CHOW_UPPER:
if (t.GetValue() < 3)
return false;
to_find[0] = t() - 1;
to_find[1] = t() - 2;
break;
default:
TerminateOnError("CHand::Chow(): Invalid location");
return false;
}
for (i = 0; i < m_iNumTiles; i++) {
if (m_Tiles[i].flags & (HT_LOCKED | HT_TOCHOW))
continue; // skip open or closed kong tiles
for (j = 0; j < 2; j++) {
if (m_Tiles[i].tile == to_find[j]) {
if (found < 2)
p[found++] = &m_Tiles[i];
to_find[j] = 0; // already found this one
}
}
if (!open && m_Tiles[i].tile == t) {
p[2] = &m_Tiles[i];
}
}
if (found < 2 || (!open && p[2] == NULL))
return false; // cannot chow with this tile
// Add this tile into the hand
if (open) {
m_Tiles[m_iNumTiles].tile = t;
m_Tiles[m_iNumTiles].tileset = m_iNumTileSets;
m_Tiles[m_iNumTiles].flags = (HT_OPENCHOW | HT_FROMOPPONENT | flags);
m_iNumTiles++;
}
// mark the tiles as open pungs
p[0]->flags |= ((open ? HT_OPENCHOW : HT_CLOSEDCHOW) | flags);
p[1]->flags |= ((open ? HT_OPENCHOW : HT_CLOSEDCHOW) | flags);
p[0]->tileset = p[1]->tileset = m_iNumTileSets;
if (!open) {
p[2]->flags |= (HT_CLOSEDCHOW | flags);
p[2]->tileset = m_iNumTileSets;
}
// Add a tileset
switch (location) {
case CHOW_LOWER:
m_TileSets[m_iNumTileSets].first = t;
break;
case CHOW_MIDDLE:
m_TileSets[m_iNumTileSets].first = t() - 1;
break;
case CHOW_UPPER:
m_TileSets[m_iNumTileSets].first = t() - 2;
break;
}
m_TileSets[m_iNumTileSets].type = ((open ? HT_OPENCHOW : HT_CLOSEDCHOW) | flags);
m_iNumTileSets++;
return true;
}
bool CHand::Kong(const CTile &t, bool open, int flags)
{
handtile_t *p[4] = {NULL, NULL, NULL, NULL};
int found = 0, i;
for (i = 0; i < m_iNumTiles; i++) {
if (m_Tiles[i].flags & (HT_LOCKED | HT_TOCHOW))
continue; // skip open or closed kong tiles
if (m_Tiles[i].tile == t) {
p[found++] = &m_Tiles[i];
if (found >= (open ? 3 : 4))
break;
}
}
if (found < (open ? 3 : 4)) {
if (!open && found > 0) {
// if not open kong, also see if we have a pung
for (i = 0; i < m_iNumTileSets; i++) {
if (m_TileSets[i].first == t &&
(m_TileSets[i].type & HT_OPENPUNG))
{
m_TileSets[i].type = (HT_OPENKONG | flags);
p[0]->flags |= (HT_OPENKONG | flags);
for (i = 0; i < m_iNumTiles; i++) {
if (!(m_Tiles[i].flags & HT_OPENPUNG))
continue;
if (m_Tiles[i].tile == t) {
m_Tiles[i].flags |= (HT_OPENKONG | flags);
m_Tiles[i].flags &= ~HT_OPENPUNG;
}
}
return true;
}
}
}
return false; // cannot kong with this tile
}
if (open) {
// Add this tile into the hand
m_Tiles[m_iNumTiles].tile = t;
m_Tiles[m_iNumTiles].flags = ((open ? HT_OPENKONG : HT_CLOSEDKONG) | HT_FROMOPPONENT | flags);
m_Tiles[m_iNumTiles].tileset = m_iNumTileSets;
m_iNumTiles++;
}
// mark the tiles as kongs
p[0]->flags |= ((open ? HT_OPENKONG : HT_CLOSEDKONG) | flags);
p[1]->flags |= ((open ? HT_OPENKONG : HT_CLOSEDKONG) | flags);
p[2]->flags |= ((open ? HT_OPENKONG : HT_CLOSEDKONG) | flags);
p[0]->tileset = p[1]->tileset = p[2]->tileset = m_iNumTileSets;
if (!open) {
p[3]->flags |= (HT_CLOSEDKONG | flags);
p[3]->tileset = m_iNumTileSets;
}
// Add a tileset
m_TileSets[m_iNumTileSets].first = t;
m_TileSets[m_iNumTileSets].type = ((open ? HT_OPENKONG : HT_CLOSEDKONG) | flags);
m_iNumTileSets++;
return true;
}
bool CHand::CanPung(const CTile &t)
{
int count = 0;
for (int i = 0; i < m_iNumTiles; i++) {
if (m_Tiles[i].flags & (HT_LOCKED | HT_TOCHOW))
continue;
if (m_Tiles[i].tile == t) {
count++;
}
}
return (count >= 2);
}
bool CHand::CanChow(const CTile &t, int location)
{
CTile to_find[2];
if (t.GetSuit() & (TILESUIT_WIND | TILESUIT_DRAGON))
return false; // cannot chow wind or dragon tiles
switch (location) {
case CHOW_LOWER:
if (t.GetValue() > 7)
return false;
to_find[0] = t() + 1;
to_find[1] = t() + 2;
break;
case CHOW_MIDDLE:
if (t.GetValue() == 1 || t.GetValue() == 9)
return false;
to_find[0] = t() - 1;
to_find[1] = t() + 1;
break;
case CHOW_UPPER:
if (t.GetValue() < 3)
return false;
to_find[0] = t() - 1;
to_find[1] = t() - 2;
break;
default:
TerminateOnError("CHand::Chow(): Invalid location");
return false;
}
return (HasTile(to_find[0]) && HasTile(to_find[1]));
}
bool CHand::CanKong(const CTile &t, bool open)
{
int count = 0, i;
for (i = 0; i < m_iNumTiles; i++) {
if (m_Tiles[i].flags & (HT_LOCKED | HT_TOCHOW))
continue;
if (m_Tiles[i].tile == t) {
count++;
}
}
if (count >= (open ? 3 : 4))
return true;
if (!open) {
for (i = 0; i < m_iNumTileSets; i++) {
if ((m_TileSets[i].type & HT_OPENPUNG) &&
m_TileSets[i].first == t)
return true;
}
}
return false;
}
// Check if we can go mah-jong with t.
bool CHand::CanMahjong(const CTile &t)
{
CHand tmp(*this);
tmp.AddTile(t);
return tmp.GoMahjong_real();
}
// Return true if the hand in its current state of
// organization is a complete mah-jong hand.
// (That is, the hand has been organized into four sets
// and a pair, etc.)
bool CHand::GoMahjong()
{
int i;
for (i = m_iNumTiles - 1; i >= 0; i--) {
if ((m_Tiles[i].flags & HT_JUSTGOT) == 0) {
continue;
}
CHand tmp;
tmp = *this;
if (tmp.Pung(m_Tiles[i].tile, false, HT_TUMO)) {
if (tmp.GoMahjong_real()) {
*this = tmp;
return true;
}
}
tmp = *this;
if (tmp.Chow(m_Tiles[i].tile, CHOW_LOWER, false, HT_TUMO)) {
if (tmp.GoMahjong_real()) {
*this = tmp;
return true;
}
}
tmp = *this;
if (tmp.Chow(m_Tiles[i].tile, CHOW_UPPER, false, HT_TUMO)) {
if (tmp.GoMahjong_real()) {
*this = tmp;
return true;
}
}
tmp = *this;
if (tmp.Chow(m_Tiles[i].tile, CHOW_MIDDLE, false, HT_TUMO)) {
if (tmp.GoMahjong_real()) {
*this = tmp;
return true;
}
}
tmp = *this;
for (int j = 0; j < m_iNumTiles; j++) {
if (j == i) {
continue;
}
if (m_Tiles[j].tile == m_Tiles[i].tile) {
tmp.m_Tiles[i].flags |= HT_PAIR;
tmp.m_Tiles[j].flags |= HT_PAIR;
tmp.m_Tiles[i].tileset = tmp.m_Tiles[j].tileset = tmp.m_iNumTileSets;
// Add this pair to the tileset
tmp.m_TileSets[tmp.m_iNumTileSets].first = tmp.m_Tiles[i].tile;
tmp.m_TileSets[tmp.m_iNumTileSets].type = (HT_PAIR | HT_TUMO);
tmp.m_iNumTileSets++;
if (tmp.GoMahjong_real()) {
*this = tmp;
return true;
}
break;
}
}
break;
}
if (i >= m_iNumTiles) {
return GoMahjong_real();
}
// If all above fail, check if we have Thirteen Unique
// Wonders or Seven Pairs...
if (ThirteenWonders())
return true;
if (SevenPairs())
return true;
return false; // does NOT have a Mahjong
}
bool CHand::GoMahjong_real()
{
int i, concealed = 0;
handtile_t *t[2] = {NULL, NULL};
for (i = 0; i < m_iNumTiles; i++) {
if (m_Tiles[i].flags & (HT_LOCKED | HT_TOCHOW))
continue; // skip locked tiles
CHand tmp;
tmp = *this;
if (tmp.Pung(m_Tiles[i].tile, false)) {
if (tmp.GoMahjong_real()) {
*this = tmp;
return true;
}
}
tmp = *this;
if (tmp.Chow(m_Tiles[i].tile, CHOW_LOWER, false)) {
if (tmp.GoMahjong_real()) {
*this = tmp;
return true;
}
}
if (concealed < 2)
t[concealed] = &m_Tiles[i];
concealed++;
}
if (concealed == 2 && t[0]->tile == t[1]->tile) {
// only one pair is remaining; this is a mah-jong hand
t[0]->flags |= HT_PAIR;
t[1]->flags |= HT_PAIR;
t[0]->tileset = t[1]->tileset = m_iNumTileSets;
// Add this pair to the tileset
m_TileSets[m_iNumTileSets].first = t[0]->tile;
m_TileSets[m_iNumTileSets].type = HT_PAIR;
m_iNumTileSets++;
return true;
} else if (concealed == 0) {
return true;
}
// If all above fail, check if we have Thirteen Unique
// Wonders or Seven Pairs...
if (ThirteenWonders())
return true;
if (SevenPairs())
return true;
return false; // does NOT have a Mahjong
}
bool CHand::GoMahjong(const CTile &t)
{
CHand tmp;
tmp = *this;
if (tmp.Pung(t, true, HT_RON)) {
if (tmp.GoMahjong_real()) {
*this = tmp;
return true;
}
}
tmp = *this;
if (tmp.Chow(t, CHOW_LOWER, true, HT_RON)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -