📄 bot.cpp
字号:
//// Copyright (c) 2005, Wei Mingzhi <whistler@openoffice.org>// Portions copyright (c) 1994, 1995 Koji Suzuki <suz@d2.bs1.fc.nec.co.jp>// 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//// Many parts of this file is based on Netmaj by Koji Suzuki// so credit goes to him.#include "main.h"#define DRAW_LOCATION(l) \ ((l >= 13) ? 640 - (20 + TILE_WIDTH * (l + 1) + 8) : 640 - (20 + TILE_WIDTH * (l + 1)))// Uncomment this to enable debug messages//#define BOT_DEBUGCBot::CBot(){}CBot::~CBot(){}void CBot::NewRound(){ m_BotBrain.goal = -1; m_BotBrain.aggression = (float)m_iScore / (m_iScore + m_pOpponent->m_iScore); m_BotBrain.aggression -= 0.5; m_BotBrain.aggression *= 2; m_BotBrain.num_handresult = 0; CBasePlayer::NewRound();#ifdef BOT_DEBUG fprintf(stdout, "*** NEW ROUND STARTED ***\n");#endif}void CBot::DrawHand(bool shown){ int i, j, loc = 0; if (m_pOpponent->m_fReach) { shown = true; // alway show computer's hand if player reached }#ifdef BOT_DEBUG shown = true;#endif for (i = 0; i < m_Hand.m_iNumTileSets; i++) { if ((m_Hand.m_TileSets[i].type & HT_OPEN) && !(m_Hand.m_TileSets[i].type & HT_RON)) { if (m_Hand.m_TileSets[i].type & HT_OPENKONG) { for (j = 0; j < 3; j++) { gpGeneral->EraseArea(DRAW_LOCATION(loc), 10, TILE_WIDTH, 80); gpGeneral->DrawTile(m_Hand.m_TileSets[i].first, DRAW_LOCATION(loc), 25, COMPUTER_SHOWN); loc++; } // Draw another tile as it's a kong gpGeneral->DrawTile(m_Hand.m_TileSets[i].first, DRAW_LOCATION(loc - 2), 8, COMPUTER_SHOWN); } else if (m_Hand.m_TileSets[i].type & HT_CLOSEDKONG) { for (j = 0; j < 3; j++) { gpGeneral->EraseArea(DRAW_LOCATION(loc), 10, TILE_WIDTH, 80); gpGeneral->DrawTile(m_Hand.m_TileSets[i].first, DRAW_LOCATION(loc), 25, WALL_CONCEALED); loc++; } // Draw another tile as it's a kong gpGeneral->DrawTile(m_Hand.m_TileSets[i].first, DRAW_LOCATION(loc - 2), 8, COMPUTER_SHOWN); } else if (m_Hand.m_TileSets[i].type & HT_OPENPUNG) { for (j = 0; j < 3; j++) { gpGeneral->EraseArea(DRAW_LOCATION(loc), 10, TILE_WIDTH, 80); gpGeneral->DrawTile(m_Hand.m_TileSets[i].first, DRAW_LOCATION(loc), 25, COMPUTER_SHOWN); loc++; } } else if (m_Hand.m_TileSets[i].type & HT_OPENCHOW) { CTile t = m_Hand.m_TileSets[i].first; for (j = 0; j < 3; j++) { gpGeneral->EraseArea(DRAW_LOCATION(loc), 10, TILE_WIDTH, 80); gpGeneral->DrawTile(t, DRAW_LOCATION(loc), 25, COMPUTER_SHOWN); loc++; t = t() + 1; } } else { assert(false); // this should NOT happen } } } for (i = 0; i < m_Hand.m_iNumTiles; i++) { if ((m_Hand.m_Tiles[i].flags & HT_OPEN) && !(m_Hand.m_Tiles[i].flags & HT_RON)) { continue; } gpGeneral->EraseArea(DRAW_LOCATION(loc), 10, TILE_WIDTH, 80); gpGeneral->DrawTile(m_Hand.m_Tiles[i].tile, DRAW_LOCATION(loc), shown ? 18 : 10, shown ? COMPUTER_SHOWN : COMPUTER_CONCEALED); loc++; } gpGeneral->EraseArea(0, 0, DRAW_LOCATION(loc - 1), 18 + TILE_HEIGHT_CONCEALED); gpGeneral->UpdateScreen(0, 0, 640, 18 + TILE_HEIGHT_CONCEALED);}void CBot::DrawDiscarded(){ int i, x, y; for (i = 0; i < 10 && i < m_iNumDiscarded - 10; i++) { gpGeneral->DrawTile(m_Discarded[i + 10].tile, DRAW_LOCATION(i), 94, WALL_SHOWN); if (m_Discarded[i + 10].flags & DT_REACH) { bool locked = false; if (SDL_MUSTLOCK(gpScreen)) { SDL_LockSurface(gpScreen); locked = true; } for (x = DRAW_LOCATION(i); x < DRAW_LOCATION(i) + TILE_WIDTH; x++) { for (y = 94; y < 94 + TILE_HEIGHT_SHOWN; y++) { unsigned char r, g, b; UTIL_GetPixel(gpScreen, x, y, &r, &g, &b); if (g >= 255 - 35) { g = 255; } else { g += 35; } UTIL_PutPixel(gpScreen, x, y, r, g, b); } } if (locked) { SDL_UnlockSurface(gpScreen); } } } gpGeneral->EraseArea(DRAW_LOCATION(9), 94, TILE_WIDTH * (10 - i), TILE_HEIGHT_SHOWN - 22); for (i = 0; i < 10 && i < m_iNumDiscarded; i++) { gpGeneral->DrawTile(m_Discarded[i].tile, DRAW_LOCATION(i), 132, WALL_SHOWN); if (m_Discarded[i].flags & DT_REACH) { bool locked = false; if (SDL_MUSTLOCK(gpScreen)) { SDL_LockSurface(gpScreen); locked = true; } for (x = DRAW_LOCATION(i); x < DRAW_LOCATION(i) + TILE_WIDTH; x++) { for (y = 132; y < 132 + TILE_HEIGHT_SHOWN; y++) { unsigned char r, g, b; UTIL_GetPixel(gpScreen, x, y, &r, &g, &b); if (g >= 255 - 35) { g = 255; } else { g += 35; } UTIL_PutPixel(gpScreen, x, y, r, g, b); } } if (locked) { SDL_UnlockSurface(gpScreen); } } } gpGeneral->EraseArea(DRAW_LOCATION(9), 132, TILE_WIDTH * (10 - i), TILE_HEIGHT_SHOWN); if (m_fReach) { gpGeneral->DrawDotBar(DRAW_LOCATION(9) - 20, 99); } else { gpGeneral->EraseArea(DRAW_LOCATION(9) - 20, 99, 20, 85); } gpGeneral->UpdateScreen(DRAW_LOCATION(9) - 20, 94, 660 - DRAW_LOCATION(9), TILE_HEIGHT_SHOWN * 2 - 22);}playeraction CBot::Action(enum actionstate state){ playeraction ret = PA_NONE; int first = SDL_GetTicks(); if (ActionIsValid(state, PA_MAHJONG)) { // Do we still need to think more as we've already won? if (state == AS_DRAW) { m_Hand.AddTile(m_pOpponent->LastDiscardedTile()); } UTIL_Delay(800); // delay a while return PA_MAHJONG; // we've already won the round } else if ((m_iState & PS_KONGED) && state == AS_DRAW) { m_BotBrain.action = PA_DRAW; } else { BotThink(state); } int time; switch (state) { case AS_DRAW: ret = ActionDraw(); time = first + 300 - SDL_GetTicks(); if (time > 0) { UTIL_Delay(time); } break; case AS_DISCARD: ret = ActionDiscard(); time = first + 350 - SDL_GetTicks(); if (time > 0) { UTIL_Delay(time); } if (m_iLastDiscardIndex != -1) { gpGeneral->EraseArea(DRAW_LOCATION(IndexToLoc(m_iLastDiscardIndex)), 10, TILE_WIDTH, 80); gpGeneral->UpdateScreen(DRAW_LOCATION(IndexToLoc(m_iLastDiscardIndex)), 10, TILE_WIDTH, 80); UTIL_Delay(100); } break; default: assert(false); // can this ever happen? break; } return ret;}playeraction CBot::ActionDraw(){ if (IsRoundDraw()) { return PA_NONE; // round has ended } int i; switch (m_BotBrain.action) { case PA_DRAW: for (i = 0; i < m_Hand.m_iNumTiles; i++) { m_Hand.m_Tiles[i].flags &= ~HT_JUSTGOT; } m_Hand.AddRandomTile(HT_JUSTGOT); m_iState &= (PS_CALLREACH | PS_KONGED); break; case PA_CHOW: if (!m_Hand.Chow(m_pOpponent->LastDiscardedTile(), m_BotBrain.chowlocation)) { TerminateOnError("CBot::ActionDraw(): cannot chow"); } m_pOpponent->m_iNumDiscarded--; m_iState |= PS_ASKED; break; case PA_PUNG: if (!m_Hand.Pung(m_pOpponent->LastDiscardedTile())) { TerminateOnError("CBot::ActionDraw(): cannot pung"); } m_pOpponent->m_iNumDiscarded--; m_iState |= PS_ASKED; break; case PA_KONG: if (!m_Hand.Kong(m_pOpponent->LastDiscardedTile())) { TerminateOnError("CBot::ActionDraw(): cannot kong"); } m_pOpponent->m_iNumDiscarded--; m_iState |= (PS_ASKED | PS_KONGED); break; default: TerminateOnError("CBot::ActionDraw(): unknown action"); break; } return m_BotBrain.action;}playeraction CBot::ActionDiscard(){ int index; m_iLastDiscardIndex = -1; switch (m_BotBrain.action) { case PA_REACH: gpGeneral->PlayBGMusic(-1); gpGeneral->PlaySound(SND_REACH); gpGeneral->DrawDotBar(DRAW_LOCATION(9) - 20, 99, true); gpGeneral->UpdateScreen(DRAW_LOCATION(9) - 20, 99, 20, 85); UTIL_Delay(800); m_iState = PS_CALLREACH; m_fReach = true; case PA_DISCARD: index = m_BotBrain.act_index; if (index < 0 || index >= m_Hand.m_iNumTiles) { TerminateOnError("CBot::ActionDiscard(): index out of range"); } m_Discarded[m_iNumDiscarded].tile = m_Hand.m_Tiles[index].tile; m_Discarded[m_iNumDiscarded].flags = 0; m_iNumDiscarded++; if (m_BotBrain.action == PA_REACH) { m_Discarded[m_iNumDiscarded - 1].flags |= DT_REACH; if (m_pOpponent->m_iNumDiscarded > 0) { m_pOpponent->m_Discarded[m_pOpponent->m_iNumDiscarded - 1].flags |= DT_OPPONENTREACH; } } else { m_iState = 0; } m_Hand.RemoveTile(index); m_iLastDiscardIndex = index; for (index = 0; index < m_Hand.m_iNumTiles; index++) { m_Hand.m_Tiles[index].flags &= ~HT_JUSTGOT; } m_Hand.Sort(); break; case PA_KONG: if (!m_Hand.Kong(m_Hand.m_Tiles[m_BotBrain.act_index].tile, false)) { TerminateOnError("CBot::ActionDiscard(): cannot kong"); } m_iState |= PS_KONGED; break; default: TerminateOnError("CBot::ActionDiscard(): unknown bot action"); break; } return m_BotBrain.action;}void CBot::BotThink(enum actionstate state){#ifdef BOT_DEBUG fprintf(stdout, "=== CBot::BotThink() begin ===\n");#endif int i; if (m_fReach) { // we've reached, no need to think too much if (state == AS_DRAW) { // we can do nothing other than drawing a tile now m_BotBrain.action = PA_DRAW; } else if (state == AS_DISCARD) { // check if we can declare a kong, and after declaring // we still have a ready hand... for (i = m_Hand.m_iNumTiles - 1; i >= 0; i--) { if ((m_Hand.m_Tiles[i].flags & HT_JUSTGOT) == 0) { continue; } m_BotBrain.act_index = i; CHand tmp = m_Hand; if (tmp.Kong(m_Hand.m_Tiles[i].tile, false)) { if (tmp.IsReady()) { m_BotBrain.action = PA_KONG; return; } } break; } m_BotBrain.action = PA_DISCARD; } else { TerminateOnError("CBot::BotThink(): unknown action state"); } return; } UpdateStatus(); switch (state) { case AS_DRAW: m_BotBrain.action = PA_DRAW; if (WantToChow()) {#ifdef BOT_DEBUG fprintf(stdout, "Bot want to chow\n");#endif } if (WantToPung()) {#ifdef BOT_DEBUG fprintf(stdout, "Bot want to pung\n");#endif } if (WantToOpenKong()) {#ifdef BOT_DEBUG fprintf(stdout, "Bot want to open kong\n");#endif } break; case AS_DISCARD: m_BotBrain.action = PA_DISCARD; if (m_BotBrain.action == PA_DISCARD || m_BotBrain.action == PA_REACH) { AnalyzeDiscard(); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -