minimax with alpha-beta pruning
このコミットが含まれているのは:
コミット
cb4587847d
137
uttt.c
137
uttt.c
|
@ -39,14 +39,16 @@ struct ut_game
|
|||
};
|
||||
|
||||
const char HELP_TEXT[] = "\
|
||||
Usage: uttt (--local | --host | --join)\n\
|
||||
Usage: uttt (--local | --agent | --host | --join)\n\
|
||||
\n\
|
||||
--local play non-network game\n\
|
||||
--local play local game\n\
|
||||
--agent play against AI agent\n\
|
||||
--host host game at 127.0.0.1:6669\n\
|
||||
--join join game at 127.0.0.1:6669\n\
|
||||
";
|
||||
|
||||
const char arg_local[] = "--local";
|
||||
const char arg_agent[] = "--agent";
|
||||
const char arg_host[] = "--host";
|
||||
const char arg_join[] = "--join";
|
||||
|
||||
|
@ -676,6 +678,46 @@ int ut_join_game(struct ut_state *state, struct ut_game *game) {
|
|||
return ut_network_game(state, game, sock, *player, false);
|
||||
}
|
||||
|
||||
int ut_agentgetmove(const struct ut_state *state, const struct ut_game *game, struct ut_move *move);
|
||||
int ut_agent_game(struct ut_state *state, struct ut_game *game) {
|
||||
char player;
|
||||
if ((player = ut_random_player()) < 0) {return 1;}
|
||||
for(;;)
|
||||
{
|
||||
struct ut_move m = {4, 4};
|
||||
ut_curserase();
|
||||
ut_cursdraw(state, game);
|
||||
char w = ut_winner(state);
|
||||
if(w)
|
||||
{
|
||||
if(w == ' ')
|
||||
{
|
||||
ut_cursprintf("Draw!\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
ut_cursprintf("%c wins!\n", (int)w);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (state->player == player) {
|
||||
if(ut_cursgetmove(state, &m)) {continue;}
|
||||
} else {
|
||||
ut_cursprintf("Waiting for AI agent ...\n");
|
||||
refresh();
|
||||
if(ut_agentgetmove(state, game, &m))
|
||||
{
|
||||
ut_cursprintf("Agent failed - exiting\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if(ut_move(state, state, m)) {continue;}
|
||||
game->moves[game->l++] = m;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void finish(int sig)
|
||||
{
|
||||
// putchar('\n');
|
||||
|
@ -718,6 +760,9 @@ int main(int argc, char **argv) {
|
|||
} else if (strncmp(argv[1], arg_local, sizeof(arg_local)) == 0) {
|
||||
begin();
|
||||
waitfinish(ut_local_game(&state, &game));
|
||||
} else if (strncmp(argv[1], arg_agent, sizeof(arg_agent)) == 0) {
|
||||
begin();
|
||||
waitfinish(ut_agent_game(&state, &game));
|
||||
} else if (strncmp(argv[1], arg_host, sizeof(arg_host)) == 0) {
|
||||
begin();
|
||||
waitfinish(ut_host_game(&state, &game));
|
||||
|
@ -730,3 +775,91 @@ int main(int argc, char **argv) {
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int ut_boardValue(const char *tiles, int offset, int stride)
|
||||
{
|
||||
int v = 0;
|
||||
for(int r = 0; r < 3; r++)
|
||||
for(int c = 0; c < 3; c++)
|
||||
v += (T(r, c) == 'X') ? 1 : (T(r, c) == 'O') ? -1 : 0;
|
||||
return v;
|
||||
}
|
||||
#define UT_VALUEMAX 81
|
||||
#define UT_VALUEMIN (-81)
|
||||
int ut_value(const struct ut_state *state)
|
||||
{
|
||||
int v = 0;
|
||||
char winner = ut_winner(state);
|
||||
if(winner != '\0')
|
||||
{
|
||||
return (winner == 'X') ? 81 : (winner == 'O') ? -81 : 0;
|
||||
}
|
||||
for(int r = 0; r < 3; r++)
|
||||
for(int c = 0; c < 3; c++)
|
||||
v += (state->boards[r][c] == '\0') ? ut_boardValue((char *)state->tiles, 27 * r + 3 * c, 9) :
|
||||
(state->boards[r][c] == 'X') ? 9 : (state->boards[r][c] == 'O') ? -9 : 0;
|
||||
return v;
|
||||
}
|
||||
|
||||
int ut_alphabetaq(const struct ut_state *state, struct ut_move move, int depth, int a, int b)
|
||||
{
|
||||
struct ut_state next;
|
||||
if(ut_move(&next, state, move)) {return (state->player == 'X') ? UT_VALUEMIN - 1 : UT_VALUEMAX + 1;}
|
||||
|
||||
int q = ut_value(&next);
|
||||
if(depth <= 0 || q <= UT_VALUEMIN || q >= UT_VALUEMAX) {return q;}
|
||||
|
||||
|
||||
if(next.player == 'X')
|
||||
{
|
||||
q = UT_VALUEMIN - 1;
|
||||
for(int r = 0; r < 9; r++)
|
||||
for(int c = 0; c < 9; c++)
|
||||
{
|
||||
struct ut_move next_move = (struct ut_move){r, c};
|
||||
int next_q = ut_alphabetaq(&next, next_move, depth - 1, a, b);
|
||||
if(next_q > q) {q = next_q;}
|
||||
if(q > b) {return q;}
|
||||
a = q > a ? q : a;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
q = UT_VALUEMAX + 1;
|
||||
for(int r = 0; r < 9; r++)
|
||||
for(int c = 0; c < 9; c++)
|
||||
{
|
||||
struct ut_move next_move = (struct ut_move){r, c};
|
||||
int next_q = ut_alphabetaq(&next, next_move, depth - 1, a, b);
|
||||
if(next_q < q) {q = next_q;}
|
||||
if(q < a) {return q;}
|
||||
b = q < b ? q : b;
|
||||
}
|
||||
}
|
||||
return q;
|
||||
}
|
||||
#define UT_DEPTH 5
|
||||
struct ut_move ut_minimax(const struct ut_state *state)
|
||||
{
|
||||
struct ut_move best_move = {-1, -1};
|
||||
int best_q = (state->player == 'X') ? UT_VALUEMIN - 1 : UT_VALUEMAX + 1;
|
||||
for(int r = 0; r < 9; r++)
|
||||
for(int c = 0; c < 9; c++)
|
||||
{
|
||||
struct ut_move move = (struct ut_move){r, c};
|
||||
int q = ut_alphabetaq(state, move, UT_DEPTH, UT_VALUEMIN - 1, UT_VALUEMAX + 1);
|
||||
if((state->player == 'X') ? q > best_q : q < best_q)
|
||||
{
|
||||
best_q = q;
|
||||
best_move = move;
|
||||
}
|
||||
}
|
||||
return best_move;
|
||||
}
|
||||
|
||||
int ut_agentgetmove(const struct ut_state *state, const struct ut_game *game, struct ut_move *move)
|
||||
{
|
||||
*move = ut_minimax(state);
|
||||
return (move->r < 0 || move->r >= 9 || move->c < 0 || move->c >= 9);
|
||||
}
|
||||
|
|
読み込み中…
新しいイシューから参照