URL
https://opencores.org/ocsvn/connect-6/connect-6/trunk
Subversion Repositories connect-6
Compare Revisions
- This comparison shows the changes necessary to convert path
/connect-6/trunk/CONNECTK/connectk-2.0/src/ai
- from Rev 3 to Rev 4
- ↔ Reverse comparison
Rev 3 → Rev 4
/serial_port.c
0,0 → 1,188
#include <stdlib.h> |
#include <stdio.h> |
#include <string.h> |
#include <unistd.h> |
#include <fcntl.h> |
#include <errno.h> |
#include <termios.h> |
#include <sys/time.h> |
#include "../shared.h" |
|
|
/* All serial functions work on this board */ |
static Board *myboard = NULL; |
static port=0; |
// The AI has as much time as it wants, but moves after 1 second. Default is to wait 2 seconds |
#define AI_WAIT_TIME 0.5 |
|
// FPGA has 1 second to make its move |
#define MOVE_TIME_LIMIT 0.5 |
|
static int char_to_int(short x){ |
if(x>=48) |
return x-48; |
else |
return 0; |
} |
//void wait(double seconds){ |
// timeval tim; |
// gettimeofday(&tim, NULL); |
// double t1=tim.tv_sec+(tim.tv_usec/1000000.0); |
// while (1){ |
// gettimeofday(&tim, NULL); |
// double t2=tim.tv_sec+(tim.tv_usec/1000000.0); |
// if (t2-t1 >= seconds) |
// break; |
// } |
//} |
|
void setup_port(int fd) { |
struct termios options; |
fcntl(fd, F_SETFL, 0); |
tcgetattr(fd, &options); |
cfsetispeed(&options, B115200); |
cfsetospeed(&options, B115200); |
options.c_cflag |= (CLOCAL | CREAD); |
tcsetattr(fd, TCSANOW, &options); |
|
// set up non-blocking port, so that we can time out |
int opts; |
opts = fcntl(fd,F_GETFL); |
if (opts < 0) { |
perror("fcntl(F_GETFL)"); |
exit(EXIT_FAILURE); |
} |
opts = (opts | O_NONBLOCK); |
if (fcntl(fd,F_SETFL,opts) < 0) { |
perror("fcntl(F_SETFL)"); |
exit(EXIT_FAILURE); |
} |
return; |
} |
|
|
|
void move_to_ascii(int x,int y, char move[4]){ |
if (y >= 10){ |
move[0] = '1'; |
y -= 10; |
} else { |
move[0] = '0'; |
} |
if (y == 0) move[1] = '0'; |
else if (y == 1) move[1] = '1'; |
else if (y == 2) move[1] = '2'; |
else if (y == 3) move[1] = '3'; |
else if (y == 4) move[1] = '4'; |
else if (y == 5) move[1] = '5'; |
else if (y == 6) move[1] = '6'; |
else if (y == 7) move[1] = '7'; |
else if (y == 8) move[1] = '8'; |
else if (y == 9) move[1] = '9'; |
|
// Do same for x. |
if (x >= 10){ |
move[2] = '1'; |
x -= 10; |
} else { |
move[2] = '0'; |
} |
if (x == 0) move[3] = '0'; |
else if (x == 1) move[3] = '1'; |
else if (x == 2) move[3] = '2'; |
else if (x == 3) move[3] = '3'; |
else if (x == 4) move[3] = '4'; |
else if (x == 5) move[3] = '5'; |
else if (x == 6) move[3] = '6'; |
else if (x == 7) move[3] = '7'; |
else if (x == 8) move[3] = '8'; |
else if (x == 9) move[3] = '9'; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
AIMoves *ai_serial(const Board *b){ |
int i,j,firstmove=0; |
char move[4]; |
|
AIMoves *moves = aimoves_new(); |
AIMove fpgamove; |
|
|
|
|
for(i=0;i<board_size;i++){ |
for(j=0;j<board_size;j++){ |
if (piece_at(b,i,j)!=PIECE_NONE) firstmove++; |
} |
} |
if(firstmove<=1){ |
port = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NONBLOCK); |
setup_port(port); |
|
if(b->turn==PIECE_WHITE){ |
write(port, "L",1); |
}else{ |
write(port, "D",1); |
|
} |
printf("firstmove %d\n",firstmove); |
if(!myboard){ |
myboard=board_new(); |
}else{ |
board_free(myboard); |
myboard=board_new(); |
} |
|
} |
if(b->moves_left >1 || (firstmove<=1)){ |
for(i=0;i<board_size;i++){ |
for(j=0;j<board_size;j++){ |
if ((piece_at(myboard,i,j)==PIECE_NONE) && (piece_at(b,i,j)!=PIECE_NONE)){ |
printf("%d,%d\n",i+1,j+1); |
move_to_ascii(i+1,j+1,move); |
write(port,&move[0],1); |
write(port,&move[1],1); |
write(port,&move[2],1); |
write(port,&move[3],1); |
printf("AI move %c%c %c%c\n", move[2],move[3],move[0],move[1] ); |
} |
} |
|
} |
|
} |
sleep(1); |
//move[0] = 0; move[1] = 0; move[2] = 0; move[3] = 0; |
////////// Get Opponent's move |
read(port,&move[0],1); |
read(port,&move[1],1); |
read(port,&move[2],1); |
read(port,&move[3],1); |
fpgamove.y=char_to_int(move[0])*10 + char_to_int(move[1])-1; |
fpgamove.x=char_to_int(move[2])*10 + char_to_int(move[3])-1; |
printf("FPGA move %d %d\n", fpgamove.x, fpgamove.y); |
aimoves_append(moves, &fpgamove); |
//move[0] = 0; move[1] = 0; move[2] = 0; move[3] = 0; |
//read(port,&move[0],1); |
//read(port,&move[1],1); |
//read(port,&move[2],1); |
//read(port,&move[3],1); |
// printf("FPGA move %c%c %c%c\n", move[2],move[3],move[0],move[1] ); |
//fpgamove.x=char_to_int(move[2])*10 + char_to_int(move[3]) - 1; |
//fpgamove.y=char_to_int(move[0])*10 + char_to_int(move[1]) - 1; |
//printf("FPGA move %d %d\n", fpgamove.x, fpgamove.y); |
//aimoves_append(moves, &fpgamove); |
board_copy(b,myboard); |
place_piece_type(myboard,fpgamove.x,fpgamove.y,b->turn); |
return moves; |
} |
/montec.c
0,0 → 1,147
|
/* |
|
connectk -- a program to play the connect-k family of games |
Copyright (C) 2007 Jeff Deitch |
|
This program is free software; you can redistribute it and/or |
modify it under the terms of the GNU General Public License |
as published by the Free Software Foundation; either version 2 |
of the License, or (at your option) any later version. |
|
This program is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
GNU General Public License for more details. |
|
You should have received a copy of the GNU General Public License |
along with this program; if not, write to the Free Software |
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
*/ |
|
#include "config.h" |
#include <glib.h> |
#include "../shared.h" |
|
#define MONTE_N 10 |
#define MONTE_NUM_RUNS 1000 |
|
// sequences.c |
AIMoves *move_utilities(const Board *b); |
|
AIMoves *empty_cells(Board *b) |
/* returns an array of the empty locations on board b */ |
{ |
int i, j; |
AIMoves *empties = aimoves_new(); |
AIMove move; |
|
for (i = 0; i < board_size; i++) { |
for (j = 0; j < board_size; j++) { |
if (piece_at(b, i, j) == PIECE_NONE) { |
move.x = i; |
move.y = j; |
move.weight = 0.f; |
aimoves_add(empties, &move); |
} |
} |
} |
return empties; |
} |
|
int mc_run(Board *b) |
/* plays a random game on board b, returns 1 if the current player wins |
and 0 otherwise */ |
{ |
Board *new_board = board_new(); |
board_copy(b, new_board); |
|
AIMove move; |
AIMoves *empties; |
empties = empty_cells(new_board); |
int tries = 0; |
int i; |
|
while ( TRUE ) { |
|
/* if the board filled up, start over */ |
if (empties->len == 0) { |
board_copy(b, new_board); |
empties = empty_cells(new_board); |
tries++; |
if (tries == 10) { |
g_debug("bailing"); |
board_free(new_board); |
aimoves_free(empties); |
return 0; |
} |
} |
|
i = g_random_int_range(0, empties->len); |
move = empties->data[i]; |
aimoves_remove_index_fast(empties, i); |
|
place_piece(new_board, move.x, move.y); |
|
if (check_win(new_board, move.x, move.y)) { |
if (new_board->turn == board->turn) { |
board_free(new_board); |
aimoves_free(empties); |
return 1; |
} |
else { |
board_free(new_board); |
aimoves_free(empties); |
return 0; |
} |
} |
|
new_board->moves_left--; |
if (new_board->moves_left == 0) { |
new_board->turn = other_player(new_board->turn); |
new_board->moves_left = place_p; |
} |
} |
} |
|
AIMoves *ai_monte_carlo(const Board *b) |
/* chooses the best move based on which one wins the most random games */ |
{ |
int i, k, wins, len; |
|
Board *new_board = board_new(); |
|
AIMove move; |
AIMoves *moves = move_utilities(b); |
moves->utility = 0; |
aimoves_crop(moves, MONTE_N); |
|
len = moves->len; |
|
for (i = 0; i < len; i++) { |
|
move = moves->data[i]; |
|
board_copy(b, new_board); |
place_piece(new_board, move.x, move.y); |
|
if (check_win(new_board, move.x, move.y)) { |
move.weight = MONTE_NUM_RUNS; |
moves->data[i] = move; |
moves->utility += MONTE_NUM_RUNS; |
} else { |
/* run the monte carlo trials */ |
wins = 0; |
for (k = 0; k < MONTE_NUM_RUNS; k++) { |
wins += mc_run(new_board); |
} |
move.weight = wins; |
moves->data[i] = move; |
moves->utility += wins; |
} |
} |
|
board_free(new_board); |
return moves; |
} |
montec.c
Property changes :
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: search.c
===================================================================
--- search.c (nonexistent)
+++ search.c (revision 4)
@@ -0,0 +1,234 @@
+
+/*
+
+connectk -- UMN CSci 5512W project
+
+*/
+
+#include "config.h"
+#include
+#include
+#include "../shared.h"
+#include "../connectk.h"
+
+/* Variables required to check for cache hits */
+static int cache_id = -1, cache_depth = -1, cache_branch = -1;
+static SEARCH cache_search = -1;
+static AIMoves *cache_moves = NULL;
+static AIWEIGHT cache_best;
+static Player *cache_player;
+static AIFunc cache_func;
+static BCOORD cache_size;
+
+void dfs_cache_dump(void)
+/* Called from tests.c to print out the DFS cache */
+{
+ g_debug("DFS cache: ");
+ aimoves_print(cache_moves);
+ g_print("\n");
+}
+
+static void cache_set(int index, AIMove *move)
+{
+ if (move->weight < cache_best || index > place_p)
+ return;
+ if (cache_moves->len <= index)
+ cache_moves->len = index + 1;
+ cache_moves->data[index] = *move;
+ cache_best = move->weight;
+}
+
+static AIWEIGHT df_search(Board *b, AIMoves *moves, Player *player,
+ int depth, int cache_index,
+ PIECE searched, AIWEIGHT alpha, AIWEIGHT beta)
+/* Depth is in _moves_ */
+{
+ int i, j;
+
+ /* Halt and depth abort */
+ if (ai_stop || depth < 1)
+ return moves->utility;
+
+ /* Alpha-beta sanity check */
+ if (alpha >= beta) {
+ g_warning("DFS alpha-beta failed sanity check");
+ return moves->utility;
+ }
+
+ /* Search only the top moves beyond the minimum */
+ aimoves_sort(moves);
+ if (moves->len > player->branch) {
+ for (i = player->branch; i < moves->len; i++)
+ if (moves->data[i].weight != moves->data[0].weight)
+ break;
+ moves->len = i;
+ }
+
+ /* No moves left -- its a draw */
+ if (moves->len < 1)
+ return AIW_DRAW;
+
+ /* Search each move available in depth first order */
+ for (i = 0; i < moves->len; i++) {
+ Board *b_next;
+ AIMove *aim = moves->data + i;
+ AIMoves *moves_next;
+
+ /* Did we get a bad move? */
+ if (!piece_empty(piece_at(b, aim->x, aim->y))) {
+ g_warning("DFS utility function suggested a bad move "
+ "(%s)", bcoords_to_string(aim->x, aim->y));
+ continue;
+ }
+
+ /* Already searched here? */
+ if (piece_at(b, aim->x, aim->y) == searched)
+ continue;
+ place_piece_type(b, aim->x, aim->y, searched);
+
+ b_next = board_new();
+ board_copy(b, b_next);
+ place_piece(b_next, aim->x, aim->y);
+
+ /* Did we win? */
+ if (check_win(b_next, aim->x, aim->y))
+ aim->weight = AIW_WIN;
+
+ /* Otherwise, search deeper */
+ else {
+ int next_ci = cache_index + 1;
+ AIWEIGHT next_alpha = alpha, next_beta = beta;
+ AIFunc func;
+
+ b_next->moves_left--;
+
+ /* Player has changed */
+ if (b_next->moves_left <= 0) {
+ b_next->moves_left = place_p;
+ b_next->turn = other_player(b->turn);
+ next_ci += place_p;
+ searched++;
+ next_alpha = -beta;
+ next_beta = -alpha;
+ }
+
+ func = ai(player->ai)->func;
+ if (!func) {
+ g_warning("DFS player has no AI function");
+ return moves->utility;
+ }
+ moves_next = func(b_next);
+ aim->weight = df_search(b_next, moves_next, player,
+ depth - 1, next_ci, searched,
+ next_alpha, next_beta);
+ aimoves_free(moves_next);
+ if (b_next->turn != b->turn)
+ aim->weight = -aim->weight;
+ }
+
+ /* Debug search */
+ if (opt_debug_dfsc) {
+ for(j = MAX_DEPTH - depth; j > 0; j--)
+ g_print("-");
+ g_print("> d=%d, %s, u=%d, a=%d, b=%d %s\n",
+ depth, bcoords_to_string(aim->x, aim->y),
+ aim->weight, alpha, beta,
+ piece_to_string(b->turn));
+ }
+
+ board_free(b_next);
+ if (aim->weight > alpha) {
+ alpha = aim->weight;
+ cache_set(cache_index, aim);
+
+ /* Victory abort */
+ if (alpha >= AIW_WIN)
+ return AIW_WIN;
+
+ /* Alpha-beta pruning */
+ if (alpha >= beta)
+ return alpha;
+ }
+ }
+
+ return alpha;
+}
+
+void search(const Board *b, AIMoves *moves, Player *player)
+{
+ Board *copy;
+ AIFunc move_func = ai(player->ai)->func;
+
+ /* Player is not configured to search */
+ if (player->search == SEARCH_NONE)
+ return;
+
+ /* Moves list does not need to be searched */
+ if (moves->len <= b->moves_left) {
+ if (opt_debug_dfsc)
+ g_debug("DFS no choice abort");
+ return;
+ }
+
+ /* Board size changed, cache is invalidated */
+ if (board_size != cache_size)
+ cache_moves = NULL;
+ cache_size = board_size;
+
+ /* Cache hit, last or same board */
+ if (player->cache && cache_moves && cache_moves->len &&
+ cache_search == player->search &&
+ cache_depth == player->depth &&
+ cache_player == player &&
+ cache_func == move_func &&
+ cache_branch == player->branch) {
+ if (b->parent && cache_id == b->parent->ac.id) {
+ aimoves_remove(cache_moves, b->parent->move_x,
+ b->parent->move_y);
+ cache_id = b->ac.id;
+ }
+ if (cache_id == b->ac.id && cache_moves->len) {
+ if (cache_moves->len) {
+ aimoves_copy(cache_moves, moves);
+ if (opt_debug_dfsc)
+ g_debug("DFS cache HIT");
+ return;
+ }
+ aimoves_free(cache_moves);
+ cache_moves = NULL;
+ }
+ }
+
+ /* Cache miss */
+ if (opt_debug_dfsc)
+ g_debug("DFS cache MISS");
+ cache_id = b->ac.id;
+ if (!cache_moves)
+ cache_moves = aimoves_new();
+ cache_moves->len = 0;
+ cache_best = AIW_MIN;
+ copy = board_new();
+ board_copy(b, copy);
+ if (player->search == SEARCH_DFS) {
+ df_search(copy, moves, player, player->depth, 0,
+ PIECE_SEARCHED, AIW_LOSE, AIW_WIN);
+ if (cache_moves->len)
+ aimoves_copy(cache_moves, moves);
+ } else {
+ board_free(copy);
+ g_warning("Unsupported search type %d", player->search);
+ return;
+ }
+ board_free(copy);
+
+ /* Debug DFS search */
+ if (opt_debug_dfsc)
+ dfs_cache_dump();
+
+ /* Save params so we can check if we have a hit later */
+ cache_player = player;
+ cache_search = player->search;
+ cache_depth = player->depth;
+ cache_branch = player->branch;
+ cache_func = move_func;
+}
search.c
Property changes :
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: thread.c
===================================================================
--- thread.c (nonexistent)
+++ thread.c (revision 4)
@@ -0,0 +1,181 @@
+
+/*
+
+connectk -- a program to play the connect-k family of games
+Copyright (C) 2007 Michael Levin
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+*/
+
+#include "config.h"
+#include
+#include "../shared.h"
+#include "../connectk.h"
+
+/* FIXME: The AI thread function busy waits on Windows. For some reason, it fails to
+ deadlock itself on mutex1! */
+
+int ai_stop = 0;
+
+static GMutex *ai_mutex1, *ai_mutex2;
+static guint ai_done_source = 0;
+static AIMove ai_move;
+static GThread *ai_thread;
+static int ai_halt = 0;
+
+static gboolean ai_done(gpointer user_data)
+{
+ if (opt_debug_thread)
+ g_debug("ai_done(): making AI move");
+ make_move(ai_move.x, ai_move.y);
+ return FALSE;
+}
+
+AIMoves *run_ai(Player *player)
+{
+ AIMoves *moves;
+ AIFunc func;
+
+ if (opt_debug_thread)
+ g_debug("run_ai(): running AI function");
+ ai_stop = FALSE;
+ func = ai(player->ai)->func;
+ if (!func)
+ return NULL;
+ moves = func(board);
+ if (player->search)
+ search(board, moves, player);
+
+ ai_stop = FALSE;
+ return moves;
+}
+
+static gpointer ai_thread_func(gpointer user_data)
+{
+ for (;;) {
+ AIMoves *moves;
+
+ if (opt_debug_thread)
+ g_debug("ai_thread_func(): locking mutex1");
+ g_mutex_lock(ai_mutex1);
+ if (opt_debug_thread)
+ g_debug("ai_thread_func(): mutex1 locked");
+ if (ai_halt) {
+ ai_halt = FALSE;
+ g_mutex_unlock(ai_mutex1);
+ return NULL;
+ }
+ if (opt_debug_thread)
+ g_debug("ai_thread_func(): locking mutex2");
+ g_mutex_lock(ai_mutex2);
+ if (opt_debug_thread)
+ g_debug("ai_thread_func(): mutex2 locked");
+ if (ai(players[board->turn].ai)->func && !opt_pause_ai &&
+ !board->won) {
+ ai_move.x = -1;
+ ai_move.y = -1;
+ moves = run_ai(players + board->turn);
+
+ /* Choose an adjacent move if the AI returns nothing */
+ if (!aimoves_choose(moves, &ai_move)) {
+ aimoves_free(moves);
+ moves = ai_adjacent(board);
+ aimoves_choose(moves, &ai_move);
+ }
+
+ /* Print the move list utility */
+ if (opt_print_u)
+ g_debug("AI %s utility %d (0x%x)",
+ ai(players[board->turn].ai)->s_desc,
+ moves->utility, moves->utility);
+
+ ai_done_source = g_timeout_add(0, ai_done, NULL);
+ aimoves_free(moves);
+ }
+ g_mutex_unlock(ai_mutex2);
+ if (opt_debug_thread)
+ g_debug("ai_thread_func(): mutex2 unlocked");
+ g_thread_yield();
+ }
+ return NULL;
+}
+
+void stop_ai(void)
+{
+ ai_stop = TRUE;
+#ifndef NO_TRYLOCK
+ if (opt_debug_thread)
+ g_debug("stop_ai(): trylocking mutex1");
+ g_mutex_trylock(ai_mutex1);
+#endif
+ if (opt_debug_thread)
+ g_debug("stop_ai(): locking mutex2");
+ g_mutex_lock(ai_mutex2);
+ if (opt_debug_thread)
+ g_debug("stop_ai(): mutex2 locked");
+ if (ai_done_source) {
+ g_source_remove(ai_done_source);
+ ai_done_source = 0;
+ }
+ g_mutex_unlock(ai_mutex2);
+ if (opt_debug_thread)
+ g_debug("stop_ai(): mutex2 unlocked");
+}
+
+void start_ai(void)
+{
+ if (players[board->turn].ai == PLAYER_HUMAN)
+ return;
+
+#ifndef NO_TRYLOCK
+ if (opt_debug_thread)
+ g_debug("start_ai(): trylocking mutex1");
+ g_mutex_trylock(ai_mutex1);
+#endif
+ g_mutex_unlock(ai_mutex1);
+ if (opt_debug_thread)
+ g_debug("stop_ai(): mutex1 unlocked");
+}
+
+void halt_ai_thread(void)
+{
+ if (opt_debug_thread)
+ g_debug("halt_ai_thread(): unlocking mutex1, joining thread");
+ ai_stop = TRUE;
+ ai_halt = TRUE;
+ g_mutex_unlock(ai_mutex1);
+ g_thread_join(ai_thread);
+ if (opt_debug_thread)
+ g_debug("halt_ai_thread(): mutex1 unlocked");
+}
+
+void start_ai_thread(void)
+{
+ GError *error = NULL;
+
+ g_thread_init(NULL);
+ ai_mutex1 = g_mutex_new();
+ ai_mutex2 = g_mutex_new();
+ g_mutex_lock(ai_mutex1);
+ if (opt_debug_thread)
+ g_debug("start_ai_thread(): mutex1, mutex2 locked");
+
+ ai_thread = g_thread_create_full(ai_thread_func, NULL, 0, TRUE, TRUE,
+ G_THREAD_PRIORITY_NORMAL, &error);
+ if (error)
+ g_error("Failed to spawn AI thread");
+}
+
thread.c
Property changes :
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: window.c
===================================================================
--- window.c (nonexistent)
+++ window.c (revision 4)
@@ -0,0 +1,146 @@
+
+/*
+
+connectk -- a program to play the connect-k family of games
+Copyright (C) 2007 Michael Levin
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+*/
+
+#include "config.h"
+#include
+#include
+#include "../shared.h"
+
+static int window_dir(const Board *b, BCOORD x, BCOORD y, PIECE type,
+ int dx, int dy, int *length, int *count)
+{
+ int i, j, min_i, max_i, min_block = 0, max_block = 0, max = 0;
+ PIECE p;
+
+ if (!dx && !dy)
+ return 0;
+
+ /* Find the lowest index i along our diagonal that is valid and is
+ still a window containing (x, y) and count the number of pieces
+ inside */
+ for (i = 0; i > -connect_k; i--) {
+ p = piece_at(b, x + dx * i, y + dy * i);
+ if (p != type && p != PIECE_NONE) {
+ min_block = 1;
+ break;
+ }
+ }
+ min_i = max_i = ++i;
+ for (j = i; j < i + connect_k; j++) {
+ p = piece_at(b, x + dx * j, y + dy *j);
+ if (p == type)
+ max++;
+ else if (p != PIECE_NONE)
+ return 0;
+ }
+
+ /* Slide out window along and find the smallest and largest maximum
+ count positions */
+ j = max;
+ for (; i < 0; i++) {
+ p = piece_at(b, x + dx * i, y + dy * i);
+ if (p == type)
+ j--;
+ p = piece_at(b, x + dx * (i + connect_k),
+ y + dy * (i + connect_k));
+ if (p == type)
+ j++;
+ else if (p != PIECE_NONE) {
+ max_block = 1;
+ break;
+ }
+ if (j == max)
+ max_i = i + 1;
+ else if (j > max) {
+ max = j;
+ min_i = max_i = i + 1;
+ }
+ }
+
+ /* Check if we have blocked multiple threats with this move */
+ *count = 1;
+ if (min_block || min_i > -connect_k + 1 ||
+ ((p = piece_at(b, x - dx * connect_k, y - dy * connect_k)) !=
+ type && p != PIECE_NONE))
+ for (i = min_i; i < max_i; i++) {
+ p = piece_at(b, x + dx * i, y + dy * i);
+ if (p == PIECE_NONE)
+ (*count)++;
+ else if (p == PIECE_ERROR)
+ break;
+ }
+ if (max_block || max_i < 0 ||
+ ((p = piece_at(b, x + dx * connect_k, y + dy * connect_k)) !=
+ type && p != PIECE_NONE))
+ for (i = min_i + connect_k; i < max_i + connect_k; i++) {
+ p = piece_at(b, x + dx * i, y + dy * i);
+ if (p == PIECE_NONE)
+ (*count)++;
+ else if (p == PIECE_ERROR)
+ break;
+ }
+
+ *length = max;
+ return 1;
+}
+
+static AIWEIGHT window(const Board *b, BCOORD x, BCOORD y, PIECE turn)
+{
+ int lines[MAX_CONNECT_K], xs[] = {1, 1, 0, -1}, ys[] = {0, 1, 1, 1}, i;
+ AIWEIGHT weight;
+ PIECE type;
+
+ memset(lines, 0, sizeof (lines));
+ type = piece_at(b, x, y);
+ if (type != PIECE_NONE)
+ return AIW_NONE;
+ for (i = 0; i < 4; i++) {
+ int length, count;
+
+ if (!window_dir(b, x, y, turn, xs[i], ys[i], &length, &count))
+ continue;
+ lines[length] += count;
+ }
+
+ /* Bit pack the weight */
+ weight = AIW_NONE;
+ for (i = 1; i < connect_k; i++)
+ weight += lines[i] << ((i - 1) * 6);
+ return weight;
+}
+
+AIMoves *ai_windows(const Board *b)
+{
+ AIMoves *moves;
+ AIMove move;
+ PIECE opp = other_player(b->turn);
+
+ moves = aimoves_new();
+ moves->utility = AIW_NONE;
+ for (move.y = 0; move.y < board_size; move.y++)
+ for (move.x = 0; move.x < board_size; move.x++) {
+ move.weight = window(b, move.x, move.y, opp);
+ if (move.weight > AIW_NONE)
+ aimoves_append(moves, &move);
+ }
+ return moves;
+}
window.c
Property changes :
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: ai.c
===================================================================
--- ai.c (nonexistent)
+++ ai.c (revision 4)
@@ -0,0 +1,147 @@
+
+/*
+
+connectk -- a program to play the connect-k family of games
+Copyright (C) 2007 Michael Levin
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+*/
+
+#include "config.h"
+#include
+#include
+#include
+#include "../shared.h"
+#include "../connectk.h"
+
+/*
+ * AIs
+ */
+
+static AI ais[] = {
+ { "human", "Human", NULL },
+ { "random", "Random", ai_random },
+ { "adjacent", "Adjacent", ai_adjacent },
+ { "threats", "Threats", ai_threats },
+ /*{ "windows", "Windows", ai_windows },*/
+ /*{ "priority", "Prioritized Threats", ai_priority },*/
+ { "sequences", "Sequences", ai_sequences },
+ { "mesh", "Mesh", ai_mesh },
+ { "montecarlo", "Monte Carlo", ai_monte_carlo },
+ { "serial", "Serial Port", ai_serial },
+};
+
+static gboolean is_adjacent(const Board *b, BCOORD x, BCOORD y, int dist)
+{
+ int dx, dy, count;
+ PIECE p;
+
+ if (!piece_empty(piece_at(b, x, y)))
+ return FALSE;
+ for (dy = -1; dy < 2; dy++)
+ for (dx = -1; dx < 2; dx++) {
+ if (!dx && !dy)
+ continue;
+ count = count_pieces(b, x, y, PIECE_NONE, dx, dy, &p);
+ if (count - 1 < dist && p != PIECE_NONE)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+AIMoves *enum_adjacent(const Board *b, int dist)
+{
+ AIMoves *moves;
+ AIMove move;
+
+ move.weight = AIW_NONE;
+ moves = aimoves_new();
+ for (move.y = 0; move.y < board_size; move.y++)
+ for (move.x = 0; move.x < board_size; move.x++)
+ if (is_adjacent(b, move.x, move.y, dist))
+ aimoves_append(moves, &move);
+ aimoves_shuffle(moves);
+ return moves;
+}
+
+AIMoves *ai_marks(const Board *b, PIECE min)
+{
+ AIMoves *moves = aimoves_new();
+ AIMove move;
+ PIECE p;
+
+ for (move.y = 0; move.y < board_size; move.y++)
+ for (move.x = 0; move.x < board_size; move.x++)
+ if ((p = piece_at(b, move.x, move.y)) >= min) {
+ move.weight = p - PIECE_THREAT0;
+ aimoves_set(moves, &move);
+ }
+ return moves;
+}
+
+AIMoves *ai_random(const Board *b)
+/* Returns a list of all empty tiles */
+{
+ AIMove move;
+ AIMoves *moves;
+
+ moves = aimoves_new();
+ for (move.y = 0; move.y < board_size; move.y++)
+ for (move.x = 0; move.x < board_size; move.x++)
+ if (piece_empty(piece_at(b, move.x, move.y))) {
+ move.weight =
+ g_random_int_range(AIW_MIN, AIW_MAX);
+ aimoves_append(moves, &move);
+ }
+ return moves;
+}
+
+AIMoves *ai_adjacent(const Board *b)
+{
+ AIMove move;
+ AIMoves *moves;
+
+ /* Get all open tiles adjacent to any piece */
+ moves = enum_adjacent(b, 1);
+ printf("ai adjacen move %d %d\n", moves->data[1].x, moves->data[1].y);
+ if (moves->len)
+ return moves;
+
+ /* Play in the middle if there are no open adjacent tiles */
+ move.x = board_size / 2;
+ move.y = board_size / 2;
+ move.weight = AIW_NONE;
+ aimoves_append(moves, &move);
+ return moves;
+}
+
+const char *player_to_string(PLAYER p)
+{
+ return ais[p].l_desc;
+}
+
+int number_of_ais(void)
+{
+ return sizeof (ais) / sizeof (*ais);
+}
+
+AI *ai(int n)
+{
+ if (n >= 0 && n < number_of_ais())
+ return ais + n;
+ return NULL;
+}
+
ai.c
Property changes :
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: threats.c
===================================================================
--- threats.c (nonexistent)
+++ threats.c (revision 4)
@@ -0,0 +1,346 @@
+
+/*
+
+connectk -- a program to play the connect-k family of games
+Copyright (C) 2007 Michael Levin
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+*/
+
+#include "config.h"
+#include
+#include
+#include "../shared.h"
+
+/* Bits per threat level */
+#define BITS_PER_THREAT 6
+
+/* All threat functions work on this board */
+static Board *b = NULL;
+
+/* This is the line of threats currently being processed */
+static struct {
+ int threat[2];
+ PIECE turn[2];
+} line[MAX_BOARD_SIZE];
+
+/* Running tally of threats for both players */
+static int threat_counts[MAX_CONNECT_K + 1][2];
+
+static AIWEIGHT threat_bits(int threat, PIECE type)
+/* Bit pack the threat value */
+{
+ if (threat < 1)
+ return 0;
+
+ /* No extra value for building sequences over k - p unless it is
+ enough to win */
+ if (b->turn == type && connect_k - threat <= b->moves_left)
+ threat = connect_k - place_p + 1;
+ else if (threat >= connect_k - place_p)
+ threat = connect_k - place_p - (type == b->turn);
+
+ return 1 << ((threat - 1) * BITS_PER_THREAT);
+}
+
+static void threat_mark(int i, int threat, PIECE type)
+{
+ int j, index = 0;
+
+ if (threat <= 0)
+ return;
+
+ /* No extra value for building sequences over k - p unless it is
+ enough to win */
+ if (b->turn == type && connect_k - threat <= b->moves_left)
+ threat = connect_k - place_p + 1;
+ else if (threat >= connect_k - place_p)
+ threat = connect_k - place_p - (type == b->turn);
+
+ /* Do not mark if this threat is dominated by a preceeding threat;
+ Likewise supress any smaller threats */
+ for (j = i; j >= 0 && j > i - connect_k; j--)
+ if (line[j].threat[0] > threat)
+ return;
+ else if (line[j].threat[0] < threat) {
+ line[j].threat[0] = 0;
+ line[j].threat[1] = 0;
+ }
+
+ /* Store up to two threats per tile in the line */
+ if (line[i].threat[index])
+ index++;
+ line[i].threat[index] = threat;
+ line[i].turn[index] = type;
+}
+
+static int threat_window(int x, int y, int dx, int dy,
+ PIECE *ptype, int *pdouble)
+{
+ int min, max, count = 0;
+ PIECE p, type = PIECE_NONE;
+
+ /* Check if this tile is empty */
+ p = piece_at(b, x, y);
+ if (!piece_empty(p))
+ return 0;
+
+ /* Push forward the max and find the window type */
+ for (max = 1; max < connect_k; max++) {
+ p = piece_at(b, x + dx * max, y + dy * max);
+ if (p == PIECE_ERROR)
+ break;
+ if (!piece_empty(p)) {
+ if (type == PIECE_NONE)
+ type = p;
+ else if (type != p)
+ break;
+ count++;
+ }
+ }
+ max--;
+
+ /* Try to push the entire window back */
+ for (min = -1; min > -connect_k; min--) {
+ p = piece_at(b, x + dx * min, y + dy * min);
+ if (p == PIECE_ERROR || piece_empty(p))
+ break;
+ if (type == PIECE_NONE)
+ type = p;
+ else if (type != p)
+ break;
+ if (max - min > connect_k - 1) {
+ p = piece_at(b, x + dx * max, y + dy * max);
+ if (p == type)
+ count--;
+ max--;
+ }
+ count++;
+ }
+ min++;
+
+ /* Push back min if we haven't formed a complete window, this window
+ can't be a double */
+ if (max - min < connect_k - 1) {
+ for (min--; min > max - connect_k; min--) {
+ p = piece_at(b, x + dx * min, y + dy * min);
+ if (p == PIECE_ERROR)
+ break;
+ if (!piece_empty(p)) {
+ if (type != p)
+ break;
+ if (type == PIECE_NONE)
+ type = p;
+ count++;
+ }
+ }
+ *pdouble = 0;
+ min++;
+ }
+
+ *ptype = type;
+ if (max - min >= connect_k - 1)
+ return count;
+ return 0;
+}
+
+static AIWEIGHT threat_line(int x, int y, int dx, int dy)
+{
+ int i;
+ AIWEIGHT weight = 0;
+
+ /* Mark the maximum threat for each */
+ for (i = 0; x >= 0 && x < board_size && y >= 0 && y < board_size; i++) {
+ int count[2], tmp, double_threat = 1;
+ PIECE type[2];
+
+ count[0] = threat_window(x, y, dx, dy, type, &double_threat);
+ count[1] = threat_window(x, y, -dx, -dy, type + 1,
+ &double_threat);
+ if (count[1] > count[0]) {
+ tmp = count[1];
+ count[1] = count[0];
+ count[0] = tmp;
+ tmp = type[1];
+ type[1] = type[0];
+ type[0] = tmp;
+ }
+ line[i].threat[0] = 0;
+ line[i].threat[1] = 0;
+ threat_mark(i, count[0], type[0]);
+ if (double_threat)
+ threat_mark(i, count[1], type[1]);
+ x += dx;
+ y += dy;
+ }
+
+ /* Commit stored line values to the board */
+ x -= dx;
+ y -= dy;
+ for (i--; x >= 0 && x < board_size && y >= 0 && y < board_size; i--) {
+ AIWEIGHT bits[2];
+ PIECE p;
+
+ bits[0] = threat_bits(line[i].threat[0], line[i].turn[0]);
+ bits[1] = threat_bits(line[i].threat[1], line[i].turn[1]);
+ p = piece_at(b, x, y);
+ if (piece_empty(p) && line[i].threat[0]) {
+ threat_counts[line[i].threat[0]][line[i].turn[0] - 1]++;
+ if (line[i].threat[1])
+ threat_counts[line[i].threat[1]]
+ [line[i].turn[1] - 1]++;
+ if (p >= PIECE_THREAT0)
+ place_threat(b, x, y, p - PIECE_THREAT0 +
+ bits[0] + bits[1]);
+ else
+ place_threat(b, x, y, bits[0] + bits[1]);
+ }
+ if (b->turn != line[i].turn[0])
+ bits[0] = -bits[0];
+ if (b->turn != line[i].turn[1])
+ bits[1] = -bits[1];
+ weight += bits[0] + bits[1];
+ x -= dx;
+ y -= dy;
+ }
+
+ return weight;
+}
+
+AIMoves *ai_threats(const Board *original)
+{
+ AIMoves *moves;
+ AIWEIGHT u_sum = 0;
+ int i;
+
+ b = board_new();
+ board_copy(original, b);
+
+ /* Clear threat tallys */
+ for (i = 0; i < connect_k; i++) {
+ threat_counts[i][0] = 0;
+ threat_counts[i][1] = 0;
+ }
+
+ /* Horizontal lines */
+ for (i = 0; i < board_size; i++)
+ u_sum += threat_line(0, i, 1, 0);
+
+ /* Vertical lines */
+ for (i = 0; i < board_size; i++)
+ u_sum += threat_line(i, 0, 0, 1);
+
+ /* SE diagonals */
+ for (i = 0; i < board_size - connect_k + 1; i++)
+ u_sum += threat_line(i, 0, 1, 1);
+ for (i = 1; i < board_size - connect_k + 1; i++)
+ u_sum += threat_line(0, i, 1, 1);
+
+ /* SW diagonals */
+ for (i = connect_k - 1; i < board_size; i++)
+ u_sum += threat_line(i, 0, -1, 1);
+ for (i = 1; i < board_size - connect_k + 1; i++)
+ u_sum += threat_line(board_size - 1, i, -1, 1);
+
+ moves = ai_marks(b, PIECE_THREAT(1));
+ moves->utility = u_sum;
+ board_free(b);
+ return moves;
+}
+
+void debug_counts(void)
+{
+ int i, sum = 0;
+
+ if (!b)
+ return;
+
+ g_debug("Threat counts (black, white):");
+ for (i = 1; i < connect_k; i++) {
+ g_debug("%d: %3d %3d", i, threat_counts[i][0],
+ threat_counts[i][1]);
+ sum += threat_counts[i][0] * threat_bits(i, b->turn) -
+ threat_counts[i][1] *
+ threat_bits(i, other_player(b->turn));
+ }
+ if (sum > 0)
+ g_debug("Threat sum: %d (10^%.2f)", sum, log10((double)sum));
+ else if (sum < 0)
+ g_debug("Threat sum: %d (-10^%.2f)", sum, log10((double)-sum));
+ else
+ g_debug("Threat sum: 0");
+}
+
+static int threat_number(int player, int threat)
+{
+ return threat_counts[threat][player] / (connect_k - threat);
+}
+
+AIMoves *ai_priority(const Board *b)
+{
+ AIMoves *moves;
+ int i, j, stage[2] = {1, 1}, mask, bits;
+
+ moves = ai_threats(b);
+
+ /* Do not prioritize if we've won */
+ if (threat_counts[connect_k - place_p + 1][b->turn - 1]) {
+ moves->utility = AIW_WIN;
+ return moves;
+ }
+
+ /* Find the largest supported threat for each player */
+ for (i = 2; i < connect_k; i++) {
+ if (threat_number(0, i - 1) >= place_p &&
+ threat_number(0, i) > place_p)
+ stage[0] = i;
+ if (threat_number(1, i - 1) >= place_p &&
+ threat_number(1, i) > place_p)
+ stage[1] = i;
+ }
+
+ if (opt_debug_stage)
+ g_debug("Stages %d/%d", stage[0], stage[1]);
+
+ /* Do not prioritize if we're losing */
+ if (stage[b->turn - 1] <= stage[other_player(b->turn) - 1]) {
+ moves->utility = -stage[other_player(b->turn) - 1];
+ return moves;
+ }
+
+ /* Threats above the player's stage are no more valuable than the
+ stage */
+ bits = 1 << (stage[b->turn - 1] * BITS_PER_THREAT);
+ mask = bits - 1;
+ for (i = 0; i < moves->len; i++) {
+ AIWEIGHT w = moves->data[i].weight, w2;
+
+ if (w < AIW_THREAT_MAX && w >= bits) {
+ w2 = w & mask;
+ w = w & ~mask;
+ for (j = stage[b->turn - 1];
+ w && j < connect_k - place_p + 1; j++) {
+ w = w >> BITS_PER_THREAT;
+ w2 += w & mask;
+ }
+ moves->data[i].weight = w2;
+ }
+ }
+
+ /* Stage determines weight */
+ moves->utility = stage[b->turn - 1];
+ return moves;
+}
threats.c
Property changes :
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: sequences.c
===================================================================
--- sequences.c (nonexistent)
+++ sequences.c (revision 4)
@@ -0,0 +1,381 @@
+
+/*
+
+connectk -- a program to play the connect-k family of games
+Copyright (C) 2007 Jeff Deitch
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+*/
+
+#include "config.h"
+#include
+#include
+#include "../shared.h"
+
+int utility_upper_bound;
+
+/* arrays for holding the utility and threat values for each window */
+int white_window_sequence_array[MAX_BOARD_SIZE][MAX_BOARD_SIZE][4];
+int black_window_sequence_array[MAX_BOARD_SIZE][MAX_BOARD_SIZE][4];
+int white_window_threat_array[MAX_BOARD_SIZE][MAX_BOARD_SIZE][4];
+int black_window_threat_array[MAX_BOARD_SIZE][MAX_BOARD_SIZE][4];
+
+/* arrays for holding the marks used for counting threats */
+int whites_marks[MAX_BOARD_SIZE][MAX_BOARD_SIZE];
+int blacks_marks[MAX_BOARD_SIZE][MAX_BOARD_SIZE];
+
+/* the sum of all the windows' values for threats and utility */
+int white_window_sequence_count;
+int black_window_sequence_count;
+int white_threat_count;
+int black_threat_count;
+
+/* the max sequence on a board */
+int max_white_sequence;
+int max_black_sequence;
+
+/* Returns an integer to identify the window */
+static int window_id(BCOORD x, BCOORD y, int i)
+{
+ return x * board_size * 4 + y * 4 + i + 1;
+}
+
+static void clear_all()
+{
+ /* clears all values in the matricies that hold the window scores and threat info */
+ int x, y, d;
+ for (x = 0; x < board_size; x++) {
+ for (y = 0; y < board_size; y++) {
+
+ blacks_marks[x][y] = 0;
+ whites_marks[x][y] = 0;
+
+ for (d = 0; d < 4; d++) {
+ white_window_sequence_array[x][y][d] = 0;
+ black_window_sequence_array[x][y][d] = 0;
+ white_window_threat_array[x][y][d] = 0;
+ black_window_threat_array[x][y][d] = 0;
+ }
+ }
+ }
+
+ white_window_sequence_count = 0;
+ black_window_sequence_count = 0;
+ white_threat_count = 0;
+ black_threat_count = 0;
+
+ max_black_sequence = 0;
+ max_white_sequence = 0;
+}
+
+/* A utility function of a particular window for a given player */
+static void window_sequence(const Board *b, BCOORD x, BCOORD y, int i, PIECE player)
+{
+ int sequence, count_sequence, count_threat, dx, dy, ddx, ddy, j, len;
+ int ddxs[] = {1, 0, 1, 1};
+ int ddys[] = {0, 1, 1, -1};
+ int to_mark[MAX_CONNECT_K];
+
+ ddx = ddxs[i];
+ ddy = ddys[i];
+
+ len = 0;
+ count_sequence = 0;
+ count_threat = 0;
+ sequence = 0;
+
+ if (player == PIECE_WHITE) {
+ // We are recalculating this window's threat level.
+ // Start be taking this window's previous threat out of the sum
+ // and reset it to 0.
+ white_threat_count -= white_window_threat_array[x][y][i];
+ white_window_threat_array[x][y][i] = 0;
+
+ // Do the same for the sequence count.
+ white_window_sequence_count -= white_window_sequence_array[x][y][i];
+ white_window_sequence_array[x][y][i] = 0;
+
+ // step through the window.
+ for (dx = 0, dy = 0; dx < connect_k && dy < (int)connect_k; dx += ddx, dy += ddy) {
+ if (x + dx < 0 || y + dy < 0 || x + dx >= board_size || y + dy >= board_size) {
+ break;
+ }
+ // If this window was the one that marked this spot, unmark it so we can recheck.
+ if (whites_marks[x + dx][y + dy] == window_id(x, y, i)) {
+ whites_marks[x + dx][y + dy] = 0;
+ }
+
+ if (piece_at(b, x + dx, y + dy) == PIECE_NONE) {
+ count_sequence++;
+ if (whites_marks[x + dx][y + dy] == 0) {
+ count_threat++;
+ to_mark[len++] = x + dx;
+ to_mark[len++] = y + dy;
+ }
+ } else if (piece_at(b, x + dx, y + dy) == player) {
+ count_sequence++;
+ count_threat++;
+ sequence++;
+ }
+ }
+
+ if (count_threat == connect_k) {
+ /* if this is a threat, update the threat array */
+ if (sequence >= (connect_k - place_p)) {
+ for (j = 0; j < len; j+=2) {
+ whites_marks[to_mark[j]][to_mark[j+1]] = window_id(x, y, i);
+ }
+ white_window_threat_array[x][y][i] = 1;
+ white_threat_count += 1;
+ }
+ }
+
+ if (count_sequence == connect_k) {
+
+ /* if this is the max sequence of the board, update the max_sequence value */
+ if (sequence > max_white_sequence) {
+ max_white_sequence = sequence;
+ }
+
+ /* if the sequence is greater than connect_k - place_p, set it equal to connect_k - place_p.
+ This prevents it from giving a sequence of 5 a higher score than a sequence of 4 in the default game. */
+ if (sequence > (connect_k - place_p)) {
+ sequence = (connect_k - place_p);
+ }
+
+ /* update the utility array values */
+ white_window_sequence_array[x][y][i] = sequence * sequence;
+ white_window_sequence_count += white_window_sequence_array[x][y][i];
+ }
+
+ /* Do it all again if we are black. There must be a better way to do this */
+ } else if (player == PIECE_BLACK) {
+ // We are recalculating this window's threat level.
+ // Start be taking this window's previous threat out of the sum
+ // and reset it to 0.
+ black_threat_count -= black_window_threat_array[x][y][i];
+ black_window_threat_array[x][y][i] = 0;
+
+ // Do the same for the sequence count.
+ black_window_sequence_count -= black_window_sequence_array[x][y][i];
+ black_window_sequence_array[x][y][i] = 0;
+
+ // step through the window.
+ for (dx = 0, dy = 0; dx < connect_k && dy < (int)connect_k; dx += ddx, dy += ddy) {
+ if (x + dx < 0 || y + dy < 0 || x + dx >= board_size || y + dy >= board_size) {
+ break;
+ }
+ // If this window was the one that marked this spot, unmark it so we can recheck.
+ if (blacks_marks[x + dx][y + dy] == window_id(x, y, i)) {
+ blacks_marks[x + dx][y + dy] = 0;
+ }
+
+ if (piece_at(b, x + dx, y + dy) == PIECE_NONE) {
+ count_sequence++;
+ if (blacks_marks[x + dx][y + dy] == 0) {
+ count_threat++;
+ to_mark[len++] = x + dx;
+ to_mark[len++] = y + dy;
+ }
+ } else if (piece_at(b, x + dx, y + dy) == player) {
+ count_sequence++;
+ count_threat++;
+ sequence++;
+ }
+ }
+
+ if (count_threat == connect_k) {
+ /* if this is a threat, update the threat array */
+ if (sequence >= (connect_k - place_p)) {
+ for (j = 0; j < len; j+=2) {
+ blacks_marks[to_mark[j]][to_mark[j+1]] = window_id(x, y, i);
+ }
+ black_window_threat_array[x][y][i] = 1;
+ black_threat_count += 1;
+ }
+ }
+
+ if (count_sequence == connect_k) {
+
+ /* if this is the max sequence of the board, update the max_sequence value */
+ if (sequence > max_black_sequence) {
+ max_black_sequence = sequence;
+ }
+
+ /* if the sequence is greater than connect_k - place_p, set it equal to connect_k - place_p.
+ This prevents it from giving a sequence of 5 a higher score than a sequence of 4 in the default game. */
+ if (sequence > (connect_k - place_p)) {
+ sequence = (connect_k - place_p);
+ }
+
+ /* update the utility array values */
+ black_window_sequence_array[x][y][i] = sequence * sequence;
+ black_window_sequence_count += black_window_sequence_array[x][y][i];
+ }
+ } else {
+ g_debug("error in window_sequence(), unknown board turn.");
+ }
+}
+
+int board_utility(const Board *b, PIECE player)
+/* This returns the utility value of the board
+It looks for a few things, first if the currently player is on their way to winning
+(have enough moves left in given turn to win) it returns a high utility
+second if there is a threat on this board from the other player, it gives a very low utility.
+Third, if the board is going to force a win for the player, it gives a high utility.
+If none of these are true, it adds up the utilities of all the windows and subtracts
+the sum of the utilities of the other player times the defensive constant */
+{
+
+ int utility = 0;
+ utility_upper_bound = 2 * board_size * board_size * connect_k * connect_k;
+
+ if (b->turn == PIECE_WHITE) {
+ if ((connect_k - max_white_sequence) <= (b->moves_left)) {
+ utility = utility_upper_bound;
+ } else if (black_threat_count) {
+ utility = -utility_upper_bound - black_threat_count;
+ } else if (white_threat_count > place_p) {
+ utility = utility_upper_bound - 1;
+ } else {
+ utility = white_window_sequence_count - black_window_sequence_count;
+ }
+ } else if (b->turn == PIECE_BLACK) {
+ if ((connect_k - max_black_sequence) <= (b->moves_left)) {
+ utility = utility_upper_bound;
+ } else if (white_threat_count) {
+ utility = -utility_upper_bound - white_threat_count;
+ } else if (black_threat_count > place_p) {
+ utility = utility_upper_bound - 1;
+ } else {
+ utility = black_window_sequence_count - white_window_sequence_count;
+ }
+ } else {
+ g_debug("error in board_utility(), unknown board turn.");
+ }
+
+ if (b->turn != player)
+ utility = -utility;
+
+ return utility;
+}
+
+int board_update(const Board *b, PIECE player)
+{
+ /* scans the entire board and returns its utility */
+ BCOORD x, y;
+ int i;
+
+ clear_all();
+
+ for (y = 0; y < board_size; y++) {
+ for (x = 0; x < board_size; x++) {
+ for (i = 0; i < 4; i++) {
+ window_sequence(b, x, y, i, PIECE_WHITE);
+ window_sequence(b, x, y, i, PIECE_BLACK);
+ }
+ }
+ }
+
+ return board_utility(b, player);
+}
+
+int incremental_update(const Board *b, BCOORD x, BCOORD y, PIECE player)
+/* only scans the windows that have been impacted by a new piece and returns the board's utility */
+{
+ int dx, dy, ddx, ddy, i, counter;
+ int ddxs[] = {1, 0, 1, 1};
+ int ddys[] = {0, 1, 1, -1};
+
+ max_black_sequence = 0;
+ max_white_sequence = 0;
+
+ for (i = 0; i < 4; i++) {
+
+ ddx = ddxs[i];
+ ddy = ddys[i];
+
+ for (counter = 0, dx = ddx * -connect_k, dy = ddy * -connect_k;
+ counter < connect_k + 2;
+ counter ++, dx += ddx, dy += ddy) {
+ if (x + dx < 0 || y + dy < 0 || x + dx >= board_size || y + dy >= board_size) {
+ continue;
+ }
+
+ window_sequence(b, x + dx, y + dy, i, PIECE_WHITE);
+ window_sequence(b, x + dx, y + dy, i, PIECE_BLACK);
+ }
+ }
+
+ return board_utility(b, player);
+}
+
+AIMoves *move_utilities(const Board *b)
+{
+ /* creates a list of possible moves based on the sequences utility function */
+ AIMoves *moves = aimoves_new();
+ AIMove move;
+
+ moves->utility = board_update(b, b->turn);
+
+ /* create a new board */
+ Board *new_board;
+ new_board = board_new();
+
+ for (move.y = 0; move.y < board_size; move.y++) {
+
+ /* bails out if ai_stop is true */
+ if (ai_stop)
+ return moves;
+
+ for (move.x = 0; move.x < board_size; move.x++) {
+
+ if (piece_at(b, move.x, move.y) != PIECE_NONE)
+ continue;
+
+ /* copy the board into the new board */
+ board_copy(b, new_board);
+ /* Add the piece to the board */
+ place_piece(new_board, move.x, move.y);
+ new_board->moves_left--;
+ /* find the utility for this new board */
+ move.weight = incremental_update(new_board, move.x, move.y, new_board->turn);
+
+ aimoves_add(moves, &move);
+
+ /* undo what we did, so things are correct next time around */
+ place_piece_type(new_board, move.x, move.y, PIECE_NONE);
+ new_board->moves_left++;
+ incremental_update(new_board, move.x, move.y, new_board->turn);
+ }
+ }
+
+ if ((b->turn == PIECE_WHITE && black_threat_count) || (b->turn == PIECE_BLACK && white_threat_count)) {
+ aimoves_sort(moves);
+ aimoves_crop(moves, 1);
+ }
+
+ board_free(new_board);
+
+ return moves;
+}
+
+AIMoves *ai_sequences(const Board *b)
+{
+ AIMoves *moves = move_utilities(b);
+
+ return moves;
+}
sequences.c
Property changes :
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: mesh.c
===================================================================
--- mesh.c (nonexistent)
+++ mesh.c (revision 4)
@@ -0,0 +1,131 @@
+
+/*
+
+connectk -- a program to play the connect-k family of games
+Copyright (C) 2007 Jeff Deitch
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+*/
+
+#include "config.h"
+#include
+#include "../shared.h"
+
+int cur_player_weight = 50;
+int opp_player_weight = 500;
+int mesh_array[19][19];
+
+int get_mesh_value(int x, int y) {
+ if (x < 0 || y < 0 || x >= board_size || y >= board_size) {
+ /* out of bounds */
+ return 0;
+ } else {
+ return mesh_array[x][y];
+ }
+}
+
+int stone_weight(int x, int y) {
+ if (piece_at(board, x, y) == board->turn) {
+ return cur_player_weight;
+ } else if (piece_at(board, x, y) == other_player(board->turn)) {
+ return opp_player_weight;
+ } else {
+ return 0;
+ }
+}
+
+int surrounding_weight(int x, int y) {
+
+ int weight = 0;
+ int n = 4; // number of tiles in each of the 8 radials that contribute to the weight.
+
+ int dx, dy, ddx, ddy, i;
+ int ddxs[] = {1, 0, 1, 1};
+ int ddys[] = {0, 1, 1, -1};
+
+ for (i = 0; i < 4; i++) {
+
+ ddx = ddxs[i];
+ ddy = ddys[i];
+
+ for (dx = -(ddx * n), dy = -(ddy * n); dx <= n && dy <= n; dx += ddx, dy += ddy) {
+ if (dx == 0 && dy == 0) {
+ continue;
+ } else {
+ weight += get_mesh_value(x + dx, y + dy);
+ }
+ }
+ }
+
+ return weight / (8 * n);
+}
+
+AIMoves *ai_mesh(const Board *b)
+/* imagine the board is elastic and that each stone has a weight. The current players stones have
+a different weight than the opposite players stones. Placing a stone on the board creates a depression,
+and the more stones in an area, the deeper the depression. This ai chooses the lowest, unplayed tile for
+the next move. The idea is to create clumps of stones. */
+{
+ AIMove move;
+ AIMoves *moves = aimoves_new();
+
+ int i, x, y;
+ int iterations = 10;
+ int max_weight = 0;
+
+ /* set all values to 0 */
+ for (x = 0; x < board_size; x++) {
+ for (y = 0; y < board_size; y++) {
+ mesh_array[x][y] = 0;
+ }
+ }
+
+ /* iteratively find the depth of each tile */
+ for (i = 0; i < iterations; i++) {
+ for (x = 0; x < board_size; x++) {
+ for (y = 0; y < board_size; y++) {
+ mesh_array[x][y] = stone_weight(x, y) + surrounding_weight(x, y);
+ }
+ }
+ }
+
+ /* find the max weight (i.e. the lowest spot on the board) */
+ for (x = 0; x < board_size; x++) {
+ for (y = 0; y < board_size; y++) {
+ if (piece_at(b, x, y) == PIECE_NONE) {
+ move.weight = mesh_array[x][y];
+ if (move.weight > max_weight)
+ max_weight = move.weight;
+ move.x = x;
+ move.y = y;
+ aimoves_add(moves, &move);
+ }
+ }
+ }
+
+ /* if the board is empty, play in the middle */
+ if (max_weight == 0) {
+ move.x = board_size / 2;
+ move.y = board_size / 2;
+ move.weight = 1;
+ aimoves_add(moves, &move);
+ }
+
+ moves->utility = max_weight;
+
+ /* return the array */
+ return moves;
+}
mesh.c
Property changes :
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property