0

我必须为 Android 上的游戏 FloodWars 编写一个播放器。到目前为止,我成功地编写了一个 minmax 和一个材料评估器,这很简单。

我的问题是,在特定的桌子上,在大于 9 的奇数深度上,它给出了最坏的举动。这是表格:

@@**..**@@.#@@.##
@@**..**@@.#@@.##
@@**..**@@.#@@.##
@@**..**@@.#@@.##
@@**..**@@.#@@.##
@@**..**@@.#@@.##
@@**..**@@.#@@.##
@@**..**@@.#@@.##
@@**..**@@.#@@.##
@@**..**@@.#@@.##
@@**..**@@.#@@.##
@@**..**@@.#@@.##
@@**..**@@.#@@.##

颜色被替换为:'@', '#', '+', '.', '*'。这是指定深度的输出:

++**..**@@.#@@.##
++**..**@@.#@@.##
++**..**@@.#@@.##
++**..**@@.#@@.##
++**..**@@.#@@.##
++**..**@@.#@@.##
++**..**@@.#@@.##
++**..**@@.#@@.##
++**..**@@.#@@.##
++**..**@@.#@@.##
++**..**@@.#@@.##
++**..**@@.#@@.##
++**..**@@.#@@.##

在小于 11 的所有深度上,它给出了预期的输出:

****..**@@.#@@.##
****..**@@.#@@.##
****..**@@.#@@.##
****..**@@.#@@.##
****..**@@.#@@.##
****..**@@.#@@.##
****..**@@.#@@.##
****..**@@.#@@.##
****..**@@.#@@.##
****..**@@.#@@.##
****..**@@.#@@.##
****..**@@.#@@.##
****..**@@.#@@.##

这是一些代码,重要的是:

int minmaxMin(int lvl, board *b)
{
    if(lvl == 0)
    {
        return evalM(b, g_me, g_he);
    }
    board cpB;
    moves M = getMoves(b, g_me, g_he);
    int score = 1000000;
    int i;

    for(i = 0; i < 3; i++)  
    {
        copyBoard(b, &cpB);
        memset(was, 0, MAX_L * MAX_C);
        floodfill_ch(&cpB, g_he.l, g_he.c, b->d[g_he.l][g_he.c], M.m[i]);

        score = min(score, minmaxMax(lvl - 1, &cpB)); 
    }

    return score;
}

int minmaxMax(int lvl, board *b)
{
    if(lvl == 0)
    {
        return evalM(b, g_me, g_he);
    }
    board cpB;
    moves M = getMoves(b, g_me, g_he);
    int score = -1000000;

    for(int i = 0; i < 3; i++)  
    {
        copyBoard(b, &cpB);
        memset(was, 0, MAX_L * MAX_C);
        floodfill_ch(&cpB, g_me.l, g_me.c, b->d[g_me.l][g_me.c], M.m[i]);

        score = max(score, minmaxMin(lvl - 1, &cpB)); 
    }

    return score;
}
  • lvl 是深度,board 是存储为 d[MAX_L][MAX_C] 的板 - 数据和 l(行数), c(列数) - 板的实际大小。
  • 函数floodfill_ch(...) 将棋盘上的一组颜色更改为另一种颜色,getMoves(...) 获取合法移动(所有移动-角),evalM(...) 获取a 的材料分数棋盘(以“我”为主的方格 - 以“他”为主的方格)。
  • g_me 和 g_he 是玩家的位置(行和列)。

我交换了evalM@WeatherVane 建议的参数,但什么也没发生。我在标准输出上打印了根的孩子的结果,结果(分数)不同,但程序选择了最差的一个。
这是已交换参数的程序的调试屏幕:

scr: 0:
    ++**..**@@.#@@.##
    ++**..**@@.#@@.##
    ++**..**@@.#@@.##
    ++**..**@@.#@@.##
    ++**..**@@.#@@.##
    ++**..**@@.#@@.##
    ++**..**@@.#@@.##
    ++**..**@@.#@@.##
    ++**..**@@.#@@.##
    ++**..**@@.#@@.##
    ++**..**@@.#@@.##
    ++**..**@@.#@@.##
    ++**..**@@.#@@.##

scr: 0:
    ..**..**@@.#@@.##
    ..**..**@@.#@@.##
    ..**..**@@.#@@.##
    ..**..**@@.#@@.##
    ..**..**@@.#@@.##
    ..**..**@@.#@@.##
    ..**..**@@.#@@.##
    ..**..**@@.#@@.##
    ..**..**@@.#@@.##
    ..**..**@@.#@@.##
    ..**..**@@.#@@.##
    ..**..**@@.#@@.##

scr: -26:
    ****..**@@.#@@.##
    ****..**@@.#@@.##
    ****..**@@.#@@.##
    ****..**@@.#@@.##
    ****..**@@.#@@.##
    ****..**@@.#@@.##
    ****..**@@.#@@.##
    ****..**@@.#@@.##
    ****..**@@.#@@.##
    ****..**@@.#@@.##
    ****..**@@.#@@.##
    ****..**@@.#@@.##
    ****..**@@.#@@.##

这是没有交换参数的输出:

scr: 39:
    ++**..**@@.#@@.##
    ++**..**@@.#@@.##
    ++**..**@@.#@@.##
    ++**..**@@.#@@.##
    ++**..**@@.#@@.##
    ++**..**@@.#@@.##
    ++**..**@@.#@@.##
    ++**..**@@.#@@.##
    ++**..**@@.#@@.##
    ++**..**@@.#@@.##
    ++**..**@@.#@@.##
    ++**..**@@.#@@.##
    ++**..**@@.#@@.##

scr: 26:
    ..**..**@@.#@@.##
    ..**..**@@.#@@.##
    ..**..**@@.#@@.##
    ..**..**@@.#@@.##
    ..**..**@@.#@@.##
    ..**..**@@.#@@.##
    ..**..**@@.#@@.##
    ..**..**@@.#@@.##
    ..**..**@@.#@@.##
    ..**..**@@.#@@.##
    ..**..**@@.#@@.##
    ..**..**@@.#@@.##
    ..**..**@@.#@@.##

scr: 39:
    ****..**@@.#@@.##
    ****..**@@.#@@.##
    ****..**@@.#@@.##
    ****..**@@.#@@.##
    ****..**@@.#@@.##
    ****..**@@.#@@.##
    ****..**@@.#@@.##
    ****..**@@.#@@.##
    ****..**@@.#@@.##
    ****..**@@.#@@.##
    ****..**@@.#@@.##
    ****..**@@.#@@.##
    ****..**@@.#@@.##

分数是标记在根的孩子上的分数。

这是整个代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#define DEBUG

#ifndef DEBUG
  #define fin stdin
  #define fout stdout
#else
  FILE *fin, *fout, *tty;
#endif

#define MAX_L 50
#define MAX_C 50

typedef struct
{
  char d[MAX_L][MAX_C];
  int l, c;
}board;

typedef struct
{
  int l, c;
}player;

typedef struct
{
  char m[3];
}moves;

int max(int a, int b)
{
  return a > b ? a : b;
}

int min(int a, int b)
{
  return a < b ? a : b;
}


//utilities
void readBoard(FILE *fin, board *b, player* me, player* he);
void printBoard(FILE *fout, board *b);

//eval
int evalM(board* b, player me, player he);

//search
int minmaxMin(int lvl, board *b);
int minmaxMax(int lvl, board *b);
int negamax(int lvl, board *b, char col);


int alphabetaMin(int lvl, board *bo, int a, int b);
int alphabetaMax(int lvl, board *bo, int a, int b);

void floodfill_ch(board *b, int l, int c, char from, char to);


player g_me, g_he;
char was[MAX_L][MAX_C];

//moves
moves getMoves(board *b, player me, player he)
{
  char pM [] = {'@', '#', '+', '.', '*'};
  moves M;
  int i, j = 0;

  //printf("inside getMoves (%c, %c): ", b->d[me.l][me.c], b->d[he.l][he.c]);
  for(i = 0; i < 5; i++)
    if(pM[i] != b->d[me.l][me.c] && pM[i] != b->d[he.l][he.c])
      M.m[j++] = pM[i]/*, printf("'%c' ", pM[i])*/;
  //printf("\n");
  return M;
}

void copyBoard(board *src, board *dst)
{
  dst -> l = src -> l;
  dst -> c = src -> c;
  memcpy(dst -> d, src -> d, MAX_C * MAX_L);
}

int main()
{
  #ifdef DEBUG
  fin = fopen("in", "r");
  fout = fopen("out", "w");
  tty = fopen("debugyline", "w");
  #endif
  board currentState;
  board cpB;
  board winner;
  player me, he;
  int score;

  readBoard(fin, &currentState, &me, &he);
  g_me = me, g_he = he;
  printf("reported: (%d %d) (%d %d)\n", me.l, me.c, he.l, he.c);
  printf("reported score: %d\n", evalM(&currentState, me, he));
  printBoard(stdout, &currentState);

  moves M = getMoves(&currentState, g_me, g_he);
  printf("possible moves: '%c' '%c' '%c';\n", M.m[0], M.m[1], M.m[2]);
  score = -1000000;
  for(int i = 0; i < 3; i++)
  {
    copyBoard(&currentState, &cpB);
    memset(was, 0, MAX_L * MAX_C);
    floodfill_ch(&cpB, g_me.l, g_me.c, cpB.d[g_me.l][g_me.c], M.m[i]);

    int x = minmaxMin(10, &cpB);
    #ifdef DEBUG
    fprintf(stdout, "    scr: %d:\n", x, evalM(&cpB, g_he, g_me));
    printBoard(stdout, &cpB);
    fprintf(stdout, "\n\n\n\n\n");
    #endif

    if(x > score)
    {
      score = x;
      copyBoard(&cpB, &winner);
    }
  }

  printf("\n\n");
  printf("minimax scored at: %d\n", score);
  printBoard(stdout, &winner);


  #ifdef DEBUG
  fclose(fin);
  fclose(fout);
  fclose(tty);
  #endif
  return 0;
}
//search

//--
void floodfill_ch(board *b, int l, int c, char from, char to)
{
  if(l >= 0 && l < b->l && c >= 0 && c < b->c && !was[l][c] && b->d[l][c] == from)
  {
    b->d[l][c] = to;
    was[l][c] = 1;
    char ldir[] = {0,  0, -1, 1},
       cdir[] = {1, -1,  0, 0};

    for(int i = 0; i < 4; i++)
      floodfill_ch(b, l + ldir[i], c + cdir[i], from, to);
  }
}
//--

//minmax (mm123)
int minmaxMin(int lvl, board *b)
{
  if(lvl == 0)
  {
    #ifdef DEBUG
    fprintf(tty, "scr = %d: \n", evalM(b, g_he, g_me));
    //printBoard(tty, b);
    //fprintf(tty, "\n\n\n\n\n");
    #endif
    return evalM(b, g_he, g_me);
  }
  board cpB;
  moves M = getMoves(b, g_me, g_he);
  int score = 1000000;
  int i;

  for(i = 0; i < 3; i++)
  {
    copyBoard(b, &cpB);
    memset(was, 0, MAX_L * MAX_C);
    floodfill_ch(&cpB, g_he.l, g_he.c, b->d[g_he.l][g_he.c], M.m[i]);

    score = min(score, minmaxMax(lvl - 1, &cpB));
  }

  return score;
}

int minmaxMax(int lvl, board *b)
{
  if(lvl == 0)
  {
    #ifdef DEBUG
    fprintf(tty, "scr = %d: \n", evalM(b, g_he, g_me));
    printBoard(tty, b);
    fprintf(tty, "\n\n\n\n\n");
    #endif
    return evalM(b, g_me, g_he);
  }
  board cpB;
  moves M = getMoves(b, g_me, g_he);
  int score = -1000000;

  for(int i = 0; i < 3; i++)
  {
    copyBoard(b, &cpB);
    memset(was, 0, MAX_L * MAX_C);
    floodfill_ch(&cpB, g_me.l, g_me.c, b->d[g_me.l][g_me.c], M.m[i]);
    /*
    #ifdef DEBUG
    fprintf(tty, "%d:\n", lvl - 1);
    printBoard(tty, &cpB);
    fprintf(tty, "\n\n\n\n\n");
    #endif
    */
    score = max(score, minmaxMin(lvl - 1, &cpB));
  }

  return score;
}



// eval (ev456)
int sum;

void floodfill(board *b, int l, int c, char src)
{
  if(l >= 0 && l < b->l && c >= 0 && c < b->c && !was[l][c] && b->d[l][c] == src)
  {
    sum ++;
    was[l][c] = 1;
    char ldir[] = {0,  0, -1, 1},
       cdir[] = {1, -1,  0, 0};

    for(int i = 0; i < 4; i++)
      floodfill(b, l + ldir[i], c + cdir[i], src);
  }
}

int evalM(board* b, player me, player he)
{
  int s_me, s_he;

  sum = 0;
  memset(was, 0, MAX_L * MAX_C);
  floodfill(b, me.l, me.c, b->d[me.l][me.c]);
  s_me = sum;

  sum = 0;
  memset(was, 0, MAX_L * MAX_C);
  floodfill(b, he.l, he.c, b->d[he.l][he.c]);
  s_he = sum;

  return s_me - s_he;
}

// I/O => board (iob1234)
void readBoard(FILE *fin, board *b, player* me, player* he)
{
    int l, c;
    char ch, t;
  player pJ, pS;
    t = fgetc(fin);
    fscanf(fin," ");

    ch = fgetc(fin);
    l = 0;
    while(ch != EOF && ch != '\n')
    {
        c = 0;
        while(ch != '\n' && ch != ' ' && ch != EOF)
        {
            b->d[l][c] = ch;
            ch = fgetc(fin);
            c++;
        }
        ch = fgetc(fin);
        l++;
    }

  b->l = l;
  b->c = c;

  pJ.l = l-1, pJ.c = 0;
  pS.l = 0, pS.c = c-1;

  if(t == 'J')
    *me = pJ, *he = pS;
  else if(t == 'S')
    *me = pS, *he = pJ;
  #ifdef DEBUG
  else printf("debug: (error) type of player broken; exiting...\n"), exit(-1);
  #endif
}

void printBoard(FILE *fout, board *b)
{
  int l, c;
  for(l = 0; l < b->l; l++)
  {
    fprintf(fout, "        ");
    for(c = 0; c < b->c; c++)
      fprintf(fout, "%c", b->d[l][c]);
    fprintf(fout, "\n");
  }
}

minmax 算法的调用是minmaxMin(10, &cpB);

4

0 回答 0