掃雷中,你需要在不點錯雷的情況下盡可能快的將所有的雷都標記出來,如果你點錯,就得重新開始,所以掃雷也有一定的運氣成分。但是在botzone中,你點錯并不重新開始,而是只是在最后的分中扣分。
掃雷中數字的含義表示九宮格內雷的數目。
這就是掃雷規則。
實際游戲中還有操作技巧問題,例如雙擊和定式。我們寫bot不考慮這一點,我們采取盲掃的方式來實現一個輕量級的掃雷bot。這很適合初學者來練習。
首先給大家推薦一下botzone,botzone平臺為很多種游戲bot對戰提供了技術支持,使用json實現交互。例如北大計算概論(A)的黑白棋就是在這個平臺上完成的。最后我給出的完整代碼可以提交在botzone上直接運行。
首先掃雷的規則大家都很清楚了。我們平時玩掃雷的時候關鍵是背定式。但是我們的bot可以直接枚舉不考慮時間代價。
那么我們先想一個粗放的框架:先計算出每個點是空白的概率,然后挑選一個概率最大的點點擊。事實上其實在游戲的大部分情況下幾乎每一步都不需要猜,甚至都不需要考慮剩余雷的問題,直到最后才需要猜一下。所以既然是掃雷的簡易bot,我們可以做以下簡化:我們僅僅挑選出一定不是雷的地點點擊,如果不存在這樣的地點,我們在可能不是雷的地點任意挑選一個點擊。
那么為了完成以上功能,我們至少要做以下幾步:1.在地圖中找到一定是雷的地方,2.在地圖中找到一定不是雷的地方。
也就是說,首先要實現在以下格式求解1.2.兩個問題的函數,數據格式為:
int fieldHeight, fieldWidth;int mineField[MAX_HEIGHT][MAX_WIDTH]; //[row][col]/* * 0-8: digits * 9: mine * 10: unrevealed */這個問題其實十分簡單,可以作為初學者的思考題,但是很有意思。下面是一個簡單的解答,但不是很美妙:
int surround_min_mines(int row, int col){ int r, c, dr, dc, count = 0; for (dr = -1; dr <= 1; dr++) for (dc = -1; dc <= 1; dc++) { r = row + dr; c = col + dc; if ((r >= 0) && (r < fieldHeight) && (c >= 0) && (c < fieldWidth) && ((dr!= 0) || (dc!= 0))) { if (mineField[r][c] == 9) count++; } } return count;}int surround_max_mines(int row, int col){ int r, c, dr, dc, count = 0; for (dr = -1; dr <= 1; dr++) for (dc = -1; dc <= 1; dc++) { r = row + dr; c = col + dc; if ((r >= 0) && (r < fieldHeight) && (c >= 0) && (c < fieldWidth) && ((dr!= 0) || (dc!= 0))) { if ((mineField[r][c] == 9) or (mineField[r][c] == 10)) count++; } } return count;}bool must_not_a_mine(int row, int col){ int r, c, dr, dc; for (dr = -1; dr <= 1; dr++) for (dc = -1; dc <= 1; dc++) { r = row + dr; c = col + dc; if ((r >= 0) && (r < fieldHeight) && (c >= 0) && (c < fieldWidth) && (mineField[r][c] < 9) && ((dr!= 0) || (dc!= 0))) { if (surround_min_mines(r, c) == mineField[r][c]) return true; } } return false;}bool must_be_a_mine(int row, int col){ int r, c, dr, dc; for (dr = -1; dr <= 1; dr++) for (dc = -1; dc <= 1; dc++) { r = row + dr; c = col + dc; if ((r >= 0) && (r < fieldHeight) && (c >= 0) && (c < fieldWidth) && (mineField[r][c] < 9) && ((dr!= 0) || (dc!= 0))) { if (surround_max_mines(r, c) == mineField[r][c]) return true; } } return false;}有了這兩個函數就很簡單了,我們可以寫出函數decide()決定點擊位置,這是decide()的一個解答:
void decide(int& decidedRow, int& decidedCol){ int unrevealedPos[MAX_HEIGHT * MAX_WIDTH][2], unrevealedScore[MAX_HEIGHT * MAX_WIDTH]; int unrevealedCount = 0; int row, col; for (row = 0; row < fieldHeight; row++) for (col = 0; col < fieldWidth; col++) if ((mineField[row][col] == 10) && must_be_a_mine(row, col)) { mineField[row][col] = 9; unrevealedPos[unrevealedCount][0] = row; unrevealedPos[unrevealedCount][1] = col; unrevealedScore[unrevealedCount] = -1; unrevealedCount++; } for (row = 0; row < fieldHeight; row++) { for (col = 0; col < fieldWidth; col++) { if (mineField[row][col] == 10) { unrevealedPos[unrevealedCount][0] = row; unrevealedPos[unrevealedCount][1] = col; unrevealedScore[unrevealedCount] = must_not_a_mine(row, col); unrevealedCount++; } } } int myChoice = 0; for (int i = 1; i < unrevealedCount; i++) if (unrevealedScore[i] > unrevealedScore[myChoice]) myChoice = i; decidedRow = unrevealedPos[myChoice][0]; decidedCol = unrevealedPos[myChoice][1]; return;}綜合上面的函數,外加調試模塊,我們可以寫出最終解答,這個解答就是我的bot版本,經過測試,效果不錯,基本只會在首雷或者最后要猜的情況下觸雷,而這是不可避免的。以下為完整的最終bot,供參考。如果調試不對可以去botzone上跑一跑,還有圖形界面。
/* * minesweeper(botzone) * naive */#define TESTx//TEST is test mode#include <iostream>#include <string>#include <cstdlib>#include <cstdio>#ifndef TEST#include "jsoncpp/json.h"#endif#define MAX_WIDTH 80#define MAX_HEIGHT 40using namespace std;int mineCount;#ifdef TESTint fieldHeight = 3, fieldWidth = 3;int mineField[MAX_HEIGHT][MAX_WIDTH] = { { 10, 10, 10, 10}, { 10, 8, 10, 4}, { 10, 10, 10, 2}, };#elseint fieldHeight, fieldWidth;int mineField[MAX_HEIGHT][MAX_WIDTH]; //[row][col]#endif/* * 0-8: digits * 9: mine * 10: unrevealed */int surround_min_mines(int row, int col){ int r, c, dr, dc, count = 0; for (dr = -1; dr <= 1; dr++) for (dc = -1; dc <= 1; dc++) { r = row + dr; c = col + dc; if ((r >= 0) && (r < fieldHeight) && (c >= 0) && (c < fieldWidth) && ((dr!= 0) || (dc!= 0))) { if (mineField[r][c] == 9) count++; } } return count;}int surround_max_mines(int row, int col){ int r, c, dr, dc, count = 0; for (dr = -1; dr <= 1; dr++) for (dc = -1; dc <= 1; dc++) { r = row + dr; c = col + dc; if ((r >= 0) && (r < fieldHeight) && (c >= 0) && (c < fieldWidth) && ((dr!= 0) || (dc!= 0))) { if ((mineField[r][c] == 9) or (mineField[r][c] == 10)) count++; } } return count;}bool must_not_a_mine(int row, int col){ int r, c, dr, dc; for (dr = -1; dr <= 1; dr++) for (dc = -1; dc <= 1; dc++) { r = row + dr; c = col + dc; if ((r >= 0) && (r < fieldHeight) && (c >= 0) && (c < fieldWidth) && (mineField[r][c] < 9) && ((dr!= 0) || (dc!= 0))) { if (surround_min_mines(r, c) == mineField[r][c]) return true; } } return false;}bool must_be_a_mine(int row, int col){ int r, c, dr, dc; for (dr = -1; dr <= 1; dr++) for (dc = -1; dc <= 1; dc++) { r = row + dr; c = col + dc; if ((r >= 0) && (r < fieldHeight) && (c >= 0) && (c < fieldWidth) && (mineField[r][c] < 9) && ((dr!= 0) || (dc!= 0))) { if (surround_max_mines(r, c) == mineField[r][c]) return true; } } return false;}void decide(int& decidedRow, int& decidedCol){ int unrevealedPos[MAX_HEIGHT * MAX_WIDTH][2], unrevealedScore[MAX_HEIGHT * MAX_WIDTH]; int unrevealedCount = 0; int row, col; for (row = 0; row < fieldHeight; row++) for (col = 0; col < fieldWidth; col++) if ((mineField[row][col] == 10) && must_be_a_mine(row, col)) { mineField[row][col] = 9; unrevealedPos[unrevealedCount][0] = row; unrevealedPos[unrevealedCount][1] = col; unrevealedScore[unrevealedCount] = -1; unrevealedCount++; } for (row = 0; row < fieldHeight; row++) { for (col = 0; col < fieldWidth; col++) { if (mineField[row][col] == 10) { unrevealedPos[unrevealedCount][0] = row; unrevealedPos[unrevealedCount][1] = col; unrevealedScore[unrevealedCount] = must_not_a_mine(row, col); unrevealedCount++; } } } int myChoice = 0; for (int i = 1; i < unrevealedCount; i++) if (unrevealedScore[i] > unrevealedScore[myChoice]) myChoice = i; decidedRow = unrevealedPos[myChoice][0]; decidedCol = unrevealedPos[myChoice][1]; return;}#ifdef TESTint main(){ int row, col;
|
新聞熱點
疑難解答