博弈树-BIT
下棋属于一种博弈游戏,博弈过程可以用树(博弈树)来表示。假设游戏由两个人( A 和 B )玩,开始由某个人从根结点开始走,两个人轮流走棋,每次只能走一步, 下一步棋只能选择当前结点的孩子结点,谁先走到叶子结点为胜。例如,对于下图所示的博弈树,若 A 先走,可以选 f , B 若选 h ,则 A 选 j 胜。
编写一程序,让计算机和人下棋。当计算机走下一步时,可以根据以下情况决定下一步:
(1) 若存在可以确保取胜的一个孩子结点,则选择该结点作为下一步;
(2) 若存在多个可以确保取胜的孩子结点,则选择其中高度最小的结点作为下一步(若有多个选择,则选最左边的结点);
(3) 若不存在可以确保取胜的一个孩子结点,则选择高度最大的孩子结点作为下一步(若有多个选择,则选最左边的结点);
例: (下面的黑体为输入)
(a,(b,(x)),(c,(d),(e,(g),(h)),(f)))
a
b
x
c
d
e
g
h
f
Who play first(0: computer; 1: player )?
1
player:
c
computer: d
Sorry, you lost.
Continue(y/n)?
y
Who play first(0: computer; 1: player )?
1
player:
x
illegal move.
player:
b
computer: x
Sorry, you lost.
Continue(y/n)?
y
Who play first(0: computer; 1: player )?
0
computer: c
player:
f
Congratulate, you win.
Continue(y/n)?
n
大体思路:
先根据给出的广义表结构创立广义表,再根据建立的广义表创立树(这里采用了孩子兄弟表示法),关于建立广义表和根据广义表的步骤请参考《数据结构》教材,这里不加赘述。
下棋嘛,就是两个人的事,这里就是人和电脑的事。
人所下的位置是由输入决定的,对它的孩子节点进行遍历就能找到下一个位置,只要处理好非法输入的情况就好了。
而对于电脑,我们就需要做到题目要求的三点,
(1)对于电脑来说,非胜即败,通过预处理让电脑知道哪个点是必胜点,那个点是必败点,清楚了结果之后再 玩游戏,就可以开始“欺负”人类了。(叶子节点是必胜点,它的孩子节点全部为必败点的节点为必胜点,其他节点均视为必败点)
(2)(3)计算节点的高度,必胜点考虑最小高度,必败点考虑最大高度
#include <iostream>
#include <string>
#include <cstring>
#include <algorithm>
#define OK 1
#define YES 1
#define NO 0
#define ERROR 0
#define MAX_SIZE 10005
using namespace std;
const int INF = 0x3f3f3f3f;
typedef char ElemType;
typedef char AtomType;
typedef int Status;
typedef long long ll;
typedef enum {ATOM, LIST} ElemTag;
typedef struct GLNode {
ElemTag tag;
union {
AtomType atom;
struct {struct GLNode *hp, *tp;}ptr;
};
} * Glist;
typedef struct Tnode{
ElemType data;
int status;
struct Tnode * firstchild, * nextsibling;
} * Tree;
string emp("()");
Status CreateGlist(Glist &L, string s);
Status sever(string &str, string &hstr);
Glist GetHead(Glist L);
Glist GetTail(Glist L);
void CreateTree(Tree &T, Glist L);
Status pre_deal(Tree T);
Status play(Tree T);
int get_height(Tree T);
Status who_play_first();
Status illegal_move();
Tree computer_do(Tree T);
Tree human_do(Tree T);
int main() {
string s;
cin >> s;
int i;
for(i = 0; s[i] != '\0'; i++) {
if(s[i] >= 'a' && s[i] <= 'z') printf("%c\n", s[i]);
}
Glist L;
CreateGlist(L, s);
Tree T;
CreateTree(T, L);
pre_deal(T);
play(T);
return 0;
}
Status CreateGlist(Glist &L, string s) { //建立广义表
if(s == emp) {
L = NULL;
}
else {
L = new GLNode;
if(s.length() == 1) { //原子节点
L ->tag = ATOM;
L ->atom = s[0];
}
else { //表结点
L ->tag = LIST;
string sub, hsub;
sub = s.substr(1, s.size() - 2);
//cout << sub;
Glist p, q;
p = L;
while(!sub.empty()) {
sever(sub, hsub); //分离头尾
//cout << sub << endl;
//cout << hsub << endl;
CreateGlist(p ->ptr.hp, hsub);
q = p;
if(!sub.empty()) { //若尾表非空,创建尾表指向以便递归使用
p = new GLNode;
p ->tag = LIST;
q ->ptr.tp = p;
}
}
q ->ptr.tp = NULL;
} //else 创建表结点
} //else 表不空
return OK;
}
Status sever(string &str, string &hstr) { //头尾分离函数(用于建立广义表)
int i, k = 0;
int n = str.length();
char ch = '\0';
for(i = 0; i < n && (ch != ',' || k != 0); i++) {
ch = str[i];
if(ch == '(') {
k++;
}
if(ch == ')') {
k--;
}
}
if(i < n) {
hstr = str.substr(0, i - 1);
str = str.substr(i, n - i);
//cout << hstr << endl;
//cout << str << endl;
}
else {
hstr = str;
str.clear();
}
return OK;
}
Glist GetHead(Glist L) {
if(L) {
return L ->ptr.hp;
}
else {
return NULL;
}
}
Glist GetTail(Glist L) {
if(L) {
return L ->ptr.tp;
}
else {
return NULL;
}
}
void CreateTree(Tree &T, Glist L) { //根据广义表建立树
if(L) {
T = new Tnode;
T ->data = GetHead(L) ->atom;
T ->firstchild = NULL;
T ->nextsibling = NULL;
if(GetTail(L)) {
Glist h = GetHead(GetTail(L));
Glist t = GetTail(GetTail(L));
CreateTree(T ->firstchild, h);
Tnode * p = T ->firstchild;
while(t) {
h = GetHead(t);
t = GetTail(t);
CreateTree(p ->nextsibling, h);
p = p ->nextsibling;
}
p ->nextsibling = NULL;
}
}
else {
T = NULL;
}
return;
}
Status play(Tree T) {
while(1) {
who_play_first();
int player, flag = 0;
scanf("%d", &player);
Tree p = T;
switch(player) {
case 0:
p = computer_do(T);
flag = 0;
break;
case 1:
p = human_do(T);
flag = 1;
break;
default: exit(-1);
}
if(p ->firstchild == NULL) {// 本局结束
if(flag == 0) {
printf("Sorry,you lost.\n");
}
else {
printf("Congratulate,you win.\n");
}
printf("Continue(y/n)?\n");
char c;
getchar();
scanf("%c", &c);
if(c == 'y') {
continue;
}
else {
return OK;
}
}
if(flag == 1 && p != T) { //下一步电脑
while(1) { //循环电脑-人-电脑-人-...
p = computer_do(p);
if(p ->firstchild == NULL) {
printf("Sorry,you lost.\n");
printf("Continue(y/n)?\n");
char c;
getchar();
scanf("%c", &c);
if(c == 'y') {
break;
}
else {
return OK;
}
}
while(1) { //处理因输入错误而重来的情况
Tree q;
q = human_do(p);
if(q != p) {
p = q;
break;
}
}
if(p ->firstchild == NULL) {
printf("Congratulate,you win.\n");
printf("Continue(y/n)?\n");
char c;
getchar();
scanf("%c", &c);
if(c == 'y') {
break;
}
else {
return OK;
}
}
}
}
else { //下一步人
while(1) { //思路同上
while(1) {
Tree q;
q = human_do(p);
if(q != p) {
p = q;
break;
}
}
if(p ->firstchild == NULL) {
printf("Congratulate,you win.\n");
printf("Continue(y/n)?\n");
char c;
getchar();
scanf("%c", &c);
if(c == 'y') {
break;
}
else {
return OK;
}
}
p = computer_do(p);
if(p ->firstchild == NULL) {
printf("Sorry,you lost.\n");
printf("Continue(y/n)?\n");
char c;
getchar();
scanf("%c", &c);
if(c == 'y') {
break;
}
else {
return OK;
}
}
}
}
}
return OK;
}
Status pre_deal(Tree T) { //预处理
if(T) {
if(T ->firstchild == NULL) {
T ->status = 1;
}
pre_deal(T ->firstchild);
pre_deal(T ->nextsibling);
Tnode * p = T;
if(T ->firstchild) {
p = T ->firstchild;
int flag = 1;
while(p) {
if(p ->status == 1) {
flag = 0;
break;
}
p = p ->nextsibling;
}
if(flag) {
T ->status = 1;
}
else {
T ->status = 0;
}
}
}
return OK;
}
Status who_play_first() {
printf("Who play first(0:computer;1:player)?\n");
return OK;
}
Status illegal_move() {
printf("illegal move.\n");
return OK;
}
Tree computer_do(Tree T) { //电脑进行一轮操作
int victory_h = INF, false_h = 0;
Tree p = T, q1, q2;
if(p ->firstchild) {
p = p ->firstchild;
while(p) {
int h = get_height(p);
if(p ->status == 1) {
if(h < victory_h) {
victory_h = h;
q1 = p;
}
}
else {
if(h > false_h) {
false_h = h;
q2 = p;
}
}
p = p ->nextsibling;
}
if(victory_h != INF) {
printf("computer:%c\n", q1 ->data);
return q1;
}
else {
printf("computer:%c\n", q2 ->data);
return q2;
}
}
return T;
}
Tree human_do(Tree T) { //人进行一轮操作
printf("player:\n");
char c;
getchar();
scanf("%c", &c);
Tnode * p = T;
if(p ->firstchild) {
p = p ->firstchild;
while(p) {
if(p ->data == c) {
break;
}
p = p ->nextsibling;
}
}
if(p) {
return p;
}
else {
illegal_move();
return T;
}
}
int get_height(Tree T) { //计算树的高度
if(T == NULL) return 0;
else {
int h, h2;
h = get_height(T ->firstchild);
if(h != 0) {
T = T ->firstchild;
while(T) {
h2 = get_height(T ->nextsibling);
if(h < h2) {
h = h2;
}
T = T ->nextsibling;
}
}
return h + 1;
}
}