forked from len0rd/rockbox
Make sure that reversi doesn't enter an infinite loop when using 1 or 2 AIs.
+ some changes that will be needed for more advanced AIs (which aren't ready to commit yet) git-svn-id: svn://svn.rockbox.org/rockbox/trunk@13758 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
58f97f517a
commit
cd82964e5d
7 changed files with 51 additions and 35 deletions
|
@ -120,24 +120,9 @@ static int reversi_count_free_cells(const reversi_board_t *game) {
|
||||||
* can make a move. Note that the implementation is not correct
|
* can make a move. Note that the implementation is not correct
|
||||||
* as a game may be finished even if there are free cells
|
* as a game may be finished even if there are free cells
|
||||||
*/
|
*/
|
||||||
bool reversi_game_is_finished(const reversi_board_t *game) {
|
bool reversi_game_is_finished(const reversi_board_t *game, int player) {
|
||||||
return (reversi_count_free_cells(game) == 0);
|
return (reversi_count_free_cells(game) == 0)
|
||||||
}
|
|| (reversi_count_passes(game,player) == 2);
|
||||||
|
|
||||||
|
|
||||||
/* Finds out who should place the next stone. It's the partner
|
|
||||||
* of the last move or WHITE if the game is just started.
|
|
||||||
*
|
|
||||||
* Returns WHITE or BLACK.
|
|
||||||
*/
|
|
||||||
int reversi_get_turn(const reversi_board_t *game) {
|
|
||||||
int moves;
|
|
||||||
moves = reversi_count_moves(game);
|
|
||||||
if (moves == 0) {
|
|
||||||
return WHITE;
|
|
||||||
} else {
|
|
||||||
return reversi_flipped_color(MOVE_PLAYER(game->history[moves-1]));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -165,6 +150,7 @@ static int reversi_count_player_moves(const reversi_board_t *game,
|
||||||
return cnt;
|
return cnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Returns the number of moves available for the specified player */
|
||||||
int reversi_count_player_available_moves(const reversi_board_t *game,
|
int reversi_count_player_available_moves(const reversi_board_t *game,
|
||||||
const int player) {
|
const int player) {
|
||||||
int cnt = 0, row, col;
|
int cnt = 0, row, col;
|
||||||
|
@ -176,6 +162,17 @@ int reversi_count_player_available_moves(const reversi_board_t *game,
|
||||||
return cnt;
|
return cnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Returns the number of players who HAVE to pass (2 == game is stuck) */
|
||||||
|
int reversi_count_passes(const reversi_board_t *game, const int player) {
|
||||||
|
if(reversi_count_player_available_moves(game,player)==0) {
|
||||||
|
if(reversi_count_player_available_moves(game,
|
||||||
|
reversi_flipped_color(player))==0) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Returns the number of moves made by WHITE so far */
|
/* Returns the number of moves made by WHITE so far */
|
||||||
int reversi_count_white_moves(const reversi_board_t *game) {
|
int reversi_count_white_moves(const reversi_board_t *game) {
|
||||||
|
|
|
@ -23,11 +23,11 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
|
|
||||||
#define WHITE 1 /* WHITE constant, it always plays first (as in chess) */
|
#define WHITE 1 /* WHITE constant, it always plays first (as in chess) */
|
||||||
#define BLACK 2 /* BLACK constant */
|
#define BLACK -1 /* BLACK constant */
|
||||||
#define FREE 0 /* Free place constant */
|
#define FREE 0 /* Free place constant */
|
||||||
|
|
||||||
#define BOARD_SIZE 8
|
#define BOARD_SIZE 8
|
||||||
#define INIT_STONES 4
|
#define INIT_STONES 4
|
||||||
|
|
||||||
/* Description of a move. A move is stored as a byte in the following format:
|
/* Description of a move. A move is stored as a byte in the following format:
|
||||||
|
@ -50,7 +50,7 @@ typedef unsigned char move_t;
|
||||||
/* State of a board */
|
/* State of a board */
|
||||||
typedef struct _reversi_board_t {
|
typedef struct _reversi_board_t {
|
||||||
/* The current state of the game (BLACK/WHITE/FREE) */
|
/* The current state of the game (BLACK/WHITE/FREE) */
|
||||||
char board[BOARD_SIZE][BOARD_SIZE];
|
int board[BOARD_SIZE][BOARD_SIZE];
|
||||||
|
|
||||||
/* Game history. First move (mostly, but not necessarily, black) is stored
|
/* Game history. First move (mostly, but not necessarily, black) is stored
|
||||||
* in history[0], second move (mostly, but not necessarily, white) is
|
* in history[0], second move (mostly, but not necessarily, white) is
|
||||||
|
@ -64,8 +64,7 @@ typedef struct _reversi_board_t {
|
||||||
|
|
||||||
void reversi_init_game(reversi_board_t *game);
|
void reversi_init_game(reversi_board_t *game);
|
||||||
int reversi_flipped_color(const int color);
|
int reversi_flipped_color(const int color);
|
||||||
bool reversi_game_is_finished(const reversi_board_t *game);
|
bool reversi_game_is_finished(const reversi_board_t *game, int cur_player);
|
||||||
int reversi_get_turn(const reversi_board_t *game);
|
|
||||||
int reversi_count_occupied_cells(const reversi_board_t *game,
|
int reversi_count_occupied_cells(const reversi_board_t *game,
|
||||||
int *white_count, int *black_count);
|
int *white_count, int *black_count);
|
||||||
int reversi_count_moves(const reversi_board_t *game);
|
int reversi_count_moves(const reversi_board_t *game);
|
||||||
|
@ -77,6 +76,7 @@ int reversi_is_valid_move(const reversi_board_t *game,
|
||||||
const int row, const int col, const int player);
|
const int row, const int col, const int player);
|
||||||
int reversi_count_player_available_moves(const reversi_board_t *game,
|
int reversi_count_player_available_moves(const reversi_board_t *game,
|
||||||
const int player);
|
const int player);
|
||||||
|
int reversi_count_passes(const reversi_board_t *game, const int player);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -190,13 +190,13 @@ static int cur_player;
|
||||||
static cursor_wrap_mode_t cursor_wrap_mode;
|
static cursor_wrap_mode_t cursor_wrap_mode;
|
||||||
|
|
||||||
static bool quit_plugin;
|
static bool quit_plugin;
|
||||||
|
static bool game_finished;
|
||||||
|
|
||||||
|
|
||||||
/* Initialises the state of the game (starts a new game) */
|
/* Initialises the state of the game (starts a new game) */
|
||||||
static void reversi_gui_init(void) {
|
static void reversi_gui_init(void) {
|
||||||
reversi_init_game(&game);
|
reversi_init_game(&game);
|
||||||
white_strategy = &strategy_human;
|
game_finished = false;
|
||||||
black_strategy = &strategy_human;
|
|
||||||
cur_player = BLACK;
|
cur_player = BLACK;
|
||||||
|
|
||||||
/* Place the cursor so that WHITE can make a move */
|
/* Place the cursor so that WHITE can make a move */
|
||||||
|
@ -330,9 +330,10 @@ static struct opt_items strategy_settings[] = {
|
||||||
{ "Human", NULL },
|
{ "Human", NULL },
|
||||||
{ "Naive robot", NULL },
|
{ "Naive robot", NULL },
|
||||||
{ "Simple robot", NULL },
|
{ "Simple robot", NULL },
|
||||||
|
//{ "AB robot", NULL },
|
||||||
};
|
};
|
||||||
static const game_strategy_t * const strategy_values[] = {
|
static const game_strategy_t * const strategy_values[] = {
|
||||||
&strategy_human, &strategy_naive, &strategy_simple };
|
&strategy_human, &strategy_naive, &strategy_simple, /*&strategy_ab*/ };
|
||||||
|
|
||||||
|
|
||||||
/* Sets the strategy for the specified player. 'player' is the
|
/* Sets the strategy for the specified player. 'player' is the
|
||||||
|
@ -354,6 +355,9 @@ static bool reversi_gui_choose_strategy(
|
||||||
result = rb->set_option(prompt, &index, INT, strategy_settings, num_items, NULL);
|
result = rb->set_option(prompt, &index, INT, strategy_settings, num_items, NULL);
|
||||||
(*player) = strategy_values[index];
|
(*player) = strategy_values[index];
|
||||||
|
|
||||||
|
if((*player)->init_func)
|
||||||
|
(*player)->init_func(&game);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -557,6 +561,8 @@ enum plugin_status plugin_start(struct plugin_api *api, void *parameter) {
|
||||||
|
|
||||||
game.rb = rb;
|
game.rb = rb;
|
||||||
rb->srand(*rb->current_tick); /* Some AIs use rand() */
|
rb->srand(*rb->current_tick); /* Some AIs use rand() */
|
||||||
|
white_strategy = &strategy_human;
|
||||||
|
black_strategy = &strategy_human;
|
||||||
|
|
||||||
reversi_gui_init();
|
reversi_gui_init();
|
||||||
cursor_wrap_mode = WRAP_FLAT;
|
cursor_wrap_mode = WRAP_FLAT;
|
||||||
|
@ -580,21 +586,21 @@ enum plugin_status plugin_start(struct plugin_api *api, void *parameter) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(cur_strategy->is_robot) {
|
if(cur_strategy->is_robot && !game_finished) {
|
||||||
/* TODO: Check move validity */
|
|
||||||
move_t m = cur_strategy->move_func(&game, cur_player);
|
move_t m = cur_strategy->move_func(&game, cur_player);
|
||||||
reversi_make_move(&game, MOVE_ROW(m), MOVE_COL(m), cur_player);
|
reversi_make_move(&game, MOVE_ROW(m), MOVE_COL(m), cur_player);
|
||||||
cur_player = reversi_flipped_color(cur_player);
|
cur_player = reversi_flipped_color(cur_player);
|
||||||
draw_screen = true;
|
draw_screen = true;
|
||||||
/* TODO: Add some delay to prevent it from being too fast ? */
|
/* TODO: Add some delay to prevent it from being too fast ? */
|
||||||
/* TODO: Don't duplicate end of game check */
|
/* TODO: Don't duplicate end of game check */
|
||||||
if (reversi_game_is_finished(&game)) {
|
if (reversi_game_is_finished(&game, cur_player)) {
|
||||||
reversi_count_occupied_cells(&game, &w_cnt, &b_cnt);
|
reversi_count_occupied_cells(&game, &w_cnt, &b_cnt);
|
||||||
rb->snprintf(msg_buf, sizeof(msg_buf),
|
rb->snprintf(msg_buf, sizeof(msg_buf),
|
||||||
"Game over. %s have won.",
|
"Game over. %s have won.",
|
||||||
(w_cnt>b_cnt?"WHITE":"BLACK"));
|
(w_cnt>b_cnt?"WHITE":"BLACK"));
|
||||||
rb->splash(HZ*2, msg_buf);
|
rb->splash(HZ*2, msg_buf);
|
||||||
draw_screen = true; /* Must update screen after splash */
|
draw_screen = true; /* Must update screen after splash */
|
||||||
|
game_finished = true;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -618,17 +624,19 @@ enum plugin_status plugin_start(struct plugin_api *api, void *parameter) {
|
||||||
&& (lastbutton != REVERSI_BUTTON_MAKE_MOVE_PRE))
|
&& (lastbutton != REVERSI_BUTTON_MAKE_MOVE_PRE))
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
if (game_finished) break;
|
||||||
if (reversi_make_move(&game, cur_row, cur_col, cur_player) > 0) {
|
if (reversi_make_move(&game, cur_row, cur_col, cur_player) > 0) {
|
||||||
/* Move was made. Global changes on the board are possible */
|
/* Move was made. Global changes on the board are possible */
|
||||||
draw_screen = true; /* Redraw the screen next time */
|
draw_screen = true; /* Redraw the screen next time */
|
||||||
cur_player = reversi_flipped_color(cur_player);
|
cur_player = reversi_flipped_color(cur_player);
|
||||||
if (reversi_game_is_finished(&game)) {
|
if (reversi_game_is_finished(&game, cur_player)) {
|
||||||
reversi_count_occupied_cells(&game, &w_cnt, &b_cnt);
|
reversi_count_occupied_cells(&game, &w_cnt, &b_cnt);
|
||||||
rb->snprintf(msg_buf, sizeof(msg_buf),
|
rb->snprintf(msg_buf, sizeof(msg_buf),
|
||||||
"Game over. %s have won.",
|
"Game over. %s have won.",
|
||||||
(w_cnt>b_cnt?"WHITE":"BLACK"));
|
(w_cnt>b_cnt?"WHITE":"BLACK"));
|
||||||
rb->splash(HZ*2, msg_buf);
|
rb->splash(HZ*2, msg_buf);
|
||||||
draw_screen = true; /* Must update screen after splash */
|
draw_screen = true; /* Must update screen after splash */
|
||||||
|
game_finished = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* An attempt to make an invalid move */
|
/* An attempt to make an invalid move */
|
||||||
|
|
|
@ -26,9 +26,11 @@
|
||||||
|
|
||||||
static move_t naive_move_func(const reversi_board_t *game, int player) {
|
static move_t naive_move_func(const reversi_board_t *game, int player) {
|
||||||
int num_moves = reversi_count_player_available_moves(game, player);
|
int num_moves = reversi_count_player_available_moves(game, player);
|
||||||
int r = game->rb->rand()%num_moves;
|
int r;
|
||||||
int row = 0;
|
int row = 0;
|
||||||
int col = 0;
|
int col = 0;
|
||||||
|
if(!num_moves) return MOVE_INVALID;
|
||||||
|
r = game->rb->rand()%num_moves;
|
||||||
while(true) {
|
while(true) {
|
||||||
if(reversi_is_valid_move(game, row, col, player)) {
|
if(reversi_is_valid_move(game, row, col, player)) {
|
||||||
r--;
|
r--;
|
||||||
|
@ -49,5 +51,6 @@ static move_t naive_move_func(const reversi_board_t *game, int player) {
|
||||||
|
|
||||||
const game_strategy_t strategy_naive = {
|
const game_strategy_t strategy_naive = {
|
||||||
true,
|
true,
|
||||||
|
NULL,
|
||||||
naive_move_func
|
naive_move_func
|
||||||
};
|
};
|
||||||
|
|
|
@ -33,7 +33,7 @@ static void reversi_copy_board(reversi_board_t *dst,
|
||||||
dst->rb->memcpy(dst->history,src->history,
|
dst->rb->memcpy(dst->history,src->history,
|
||||||
(BOARD_SIZE*BOARD_SIZE - INIT_STONES)*sizeof(move_t));
|
(BOARD_SIZE*BOARD_SIZE - INIT_STONES)*sizeof(move_t));
|
||||||
for(i=0;i<BOARD_SIZE;i++) {
|
for(i=0;i<BOARD_SIZE;i++) {
|
||||||
dst->rb->memcpy(dst->board[i],src->board[i],BOARD_SIZE);
|
dst->rb->memcpy(dst->board[i],src->board[i],BOARD_SIZE*sizeof(int));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,5 +113,6 @@ static move_t simple_move_func(const reversi_board_t *game, int player) {
|
||||||
|
|
||||||
const game_strategy_t strategy_simple = {
|
const game_strategy_t strategy_simple = {
|
||||||
true,
|
true,
|
||||||
|
NULL,
|
||||||
simple_move_func
|
simple_move_func
|
||||||
};
|
};
|
||||||
|
|
|
@ -23,5 +23,6 @@
|
||||||
/* Strategy that requires interaction with the user */
|
/* Strategy that requires interaction with the user */
|
||||||
const game_strategy_t strategy_human = {
|
const game_strategy_t strategy_human = {
|
||||||
false,
|
false,
|
||||||
|
NULL,
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,11 +28,13 @@
|
||||||
a move has been considered or HISTORY_INVALID_ENTRY if no move
|
a move has been considered or HISTORY_INVALID_ENTRY if no move
|
||||||
has been considered. The board should not be modified. */
|
has been considered. The board should not be modified. */
|
||||||
typedef move_t (*move_func_t)(const reversi_board_t *game, int color);
|
typedef move_t (*move_func_t)(const reversi_board_t *game, int color);
|
||||||
|
typedef void (*init_func_t)(const reversi_board_t *game);
|
||||||
|
|
||||||
/* A playing party/strategy */
|
/* A playing party/strategy */
|
||||||
typedef struct _game_strategy_t {
|
typedef struct _game_strategy_t {
|
||||||
const bool is_robot; /* Is the player a robot or does it require user input? */
|
const bool is_robot; /* Is the player a robot or does it require user input? */
|
||||||
move_func_t move_func; /* Function for advicing a move */
|
init_func_t init_func; /* Initialise strategy */
|
||||||
|
move_func_t move_func; /* Make a move */
|
||||||
} game_strategy_t;
|
} game_strategy_t;
|
||||||
|
|
||||||
|
|
||||||
|
@ -40,5 +42,9 @@ typedef struct _game_strategy_t {
|
||||||
extern const game_strategy_t strategy_human;
|
extern const game_strategy_t strategy_human;
|
||||||
extern const game_strategy_t strategy_naive;
|
extern const game_strategy_t strategy_naive;
|
||||||
extern const game_strategy_t strategy_simple;
|
extern const game_strategy_t strategy_simple;
|
||||||
|
//extern const game_strategy_t strategy_ab;
|
||||||
|
|
||||||
|
#define CHAOSNOISE 0.05 /**< Changerate of heuristic-value */
|
||||||
|
#define CHAOSPROB 0.1 /**< Probability of change of the heuristic-value */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue