-->
当前位置:首页 > 题库 > 正文内容

编程题:井字棋(Tic-Tac-Toe)

Luz3年前 (2022-04-06)题库817
封校期间老师的作业居然跳票,无聊到跟舍友玩起了井字棋。一定是昨晚看数分过久,居然小负了几十局。舍友仰天大笑出门去,留下我对着棋盘**反思**:

- 总共9个格子,从左往右、从上往下依次编号:

txt
0 1 2
3 4 5
6 7 8


- 每格有3种可能状态:. X O,数点表示空白。
- **先手为X**、双方轮流下。所以当局面X与O个数相同时,轮到X下,否则O下。
- 谁先在**同行、或同列、或对角线**下出三连就胜利。比如下列棋局分别是:X胜、O胜、X胜、平局:

txt
X X X O X X O X X O X X
O O . O X . X X O X O O
. . . O . . O X O O X X


- 判断胜负的话,把三行、三列、两对角线共8种情况检查一遍就行。
- 怎样评估优势呢?设计成“**胜利为正分、失败为负分、平局为0分**”是个思路。
- **胜得越早优势越大**。“越早”就是下的棋子少,空白位置多。空白的数量代表着分数。
- 但是上面的第3、4个例子都没有空白,却一胜一平。那就规定**胜负分是空白数量+1,平局为0分**。

任务:对棋局进行评估,找出双方都采取最优策略时、**当前棋手最优得分**。
参考:Minimax策略。

#### 输入规格

- 每行一个表示局面的字符串,3行拼起来不留空格即9个字符。读取到EOF为止。
- 局面保证合理。

#### 输出规格

- 每行输出当前棋手、空格、最优得分。

#### 样例输入

in
XXXOO....
XX.OO....
OXXOX.O..
OXXXXOOXO
OXXXOOOXX


#### 样例输出

out
O -5
X 5
X -4
O -1
O 0


#### 样例解释

- 第1组:X多,该O下。X已经三连,O得负分。剩4个空,-5分。
- 第2组:XO一样多,该X下。下到第1行第3列就赢,之后剩4个空,+5分。
- 第3组:XO一样多,该X下。但O已三连,X输了,得负分。剩3个空,-4分。
- 第4组:X多,该O下。X已经三连,O得负分。剩0个空,-1分。
- 第5组:X多,该O下。无空可下,双方都没三连、平局。

#### 代码框架

cpp
#include <iostream>
using namespace std;

bool win(string& g, char u){
// TODO: 检查三行、三列、两对角线是否出现棋手u的三连,返回true表示u获胜。
return false;
}

int eval(string& g, char a, char b, int r){
// 参数:局面、当前棋手、对方、剩余空格。
if(win(g, a)){ return (r + 1); }
if(win(g, b)){ return -(r + 1); } // 当前局面如果b胜利,对a来说就是负分。
int best = -9; // 初始化成不可能的分数(获胜时双方至少要下5子,剩4格空,不会-9分)
for(int i = 0; i < 9; ++i){
/* TODO:
尝试每个空白
按当前棋手a落子,
交换角色,以对手角色评估棋局得分(递归调用eval)。
那么该下法对手得分取负就是我方得分。
如果得分更高,更新best。
恢复空白
*/
}
// 如果best没被修改,说明无空可下,平局。
return best == -9 ? 0 : best;
}

int main(){
for(string g; cin >> g;){
// TODO: 统计X、O数量,确定当前棋手a、对手b所执的字符。
char a = '?'; // TODO: 当前棋手。XO数量相同时轮到X下,否则O下。
char b = '?'; // TODO: 对方棋手。与a相反。
int r = 0; // TODO: 统计空白数量。
cout << a << ' ' << eval(g, a, b, r) << endl;
}
}






答案:若无答案欢迎评论

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。