1
/* This file is a part of gtkboard, a board games system.
2
Copyright (C) 2003, Arvind Narayanan <arvindn@users.sourceforge.net>
4
This program is free software; you can redistribute it and/or modify
5
it under the terms of the GNU General Public License as published by
6
the Free Software Foundation; either version 2 of the License, or
7
(at your option) any later version.
9
This program is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
GNU General Public License for more details.
14
You should have received a copy of the GNU General Public License
15
along with this program; if not, write to the Free Software
16
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
25
#include "gdk/gdkkeysyms.h"
26
#include "../pixmaps/chess.xpm"
27
#include "../pixmaps/misc.xpm"
29
#define KNIGHTS_CELL_SIZE 54
30
#define KNIGHTS_NUM_PIECES 3
32
#define KNIGHTS_BOARD_WID 7
33
#define KNIGHTS_BOARD_HEIT 7
35
#define KNIGHTS_EMPTY 0
36
#define KNIGHTS_CLOSED 1
40
char knights_colors[] = {200, 200, 130, 0, 140, 0};
42
int knights_initpos [KNIGHTS_BOARD_WID*KNIGHTS_BOARD_HEIT] =
44
3 , 0 , 0 , 0 , 0 , 0 , 0 ,
45
0 , 0 , 0 , 0 , 0 , 0 , 0 ,
46
0 , 0 , 0 , 0 , 0 , 0 , 0 ,
47
0 , 0 , 0 , 0 , 0 , 0 , 0 ,
48
0 , 0 , 0 , 0 , 0 , 0 , 0 ,
49
0 , 0 , 0 , 0 , 0 , 0 , 0 ,
50
0 , 0 , 0 , 0 , 0 , 0 , 2 ,
54
char ** knights_pixmaps [] =
66
static int knights_getmove (Pos *, int, int, GtkboardEventType, Player, byte **, int **);
67
static int knights_getmove_kb (Pos *, int, byte ** , int **);
69
static ResultType knights_who_won (Pos *, Player, char **);
70
static ResultType knights_eval (Pos *, Player, float *eval);
71
static ResultType knights_eval_real (Pos *, Player, float *eval, gboolean);
72
static byte * knights_movegen (Pos *);
73
static void *knights_newstate (Pos *, byte *);
75
Game Knights = { KNIGHTS_CELL_SIZE, KNIGHTS_BOARD_WID, KNIGHTS_BOARD_HEIT,
77
knights_colors, knights_initpos, knights_pixmaps, "Balanced Joust", "Nimlike games",
82
game_getmove = knights_getmove;
83
game_getmove_kb = knights_getmove_kb;
84
game_who_won = knights_who_won;
85
game_eval = knights_eval;
86
game_movegen = knights_movegen;
88
game_state_size = sizeof (Knights_state);
89
game_newstate = knights_newstate;
90
game_draw_cell_boundaries = TRUE;
91
game_file_label = FILERANK_LABEL_TYPE_ALPHA;
92
game_rank_label = FILERANK_LABEL_TYPE_NUM | FILERANK_LABEL_DESC;
93
game_doc_about_status = STATUS_COMPLETE;
94
game_doc_rules = "Two players take turns in moving their respective knights on a 7x7 chessboard. Squares that have been visited are considered \"eaten\" and cannot be revisited. When the knights are attacking each other, the player to move can pass by hitting Space. If both players pass in the same position, the game is a draw. The goal is to be the last player to make a move.";
95
game_doc_strategy = "As the game progresses, there will eventually appear a single square, which, if eaten, will partition the board into two, such that a knight cannot move from one part to the other. The player who eats this square is often at an advantage because they can choose which part to move to.";
98
static int incx[] = { -2, -2, -1, -1, 1, 1, 2, 2};
99
static int incy[] = { -1, 1, -2, 2, -2, 2, -1, 1};
101
static void *knights_newstate (Pos *pos, byte *move)
103
static Knights_state state;
106
state.num_pauses = 0;
110
state.num_pauses = ((Knights_state *)pos->state)->num_pauses + 1;
111
else state.num_pauses = 0;
115
static void get_cur_pos (byte *board, Player player, int *x, int *y)
118
for (i=0; i<board_wid; i++)
119
for (j=0; j<board_heit; j++)
121
if ((player == WHITE && board [j * board_wid + i] == KNIGHTS_WN)
122
|| (player == BLACK && board [j * board_wid + i] == KNIGHTS_BN))
131
ResultType knights_who_won (Pos *pos, Player player, char **commp)
135
ResultType result = knights_eval_real (pos, player, &eval, TRUE);
136
if (result == RESULT_NOTYET)
138
else if (result == RESULT_TIE) *commp = "Draw";
139
else if (result == RESULT_WHITE) *commp = "White won";
140
else if (result == RESULT_BLACK) *commp = "Black won";
145
int knights_getmove_kb (Pos *pos, int key, byte ** movp, int **rmovp)
147
int i, j, wx = 0, wy = 0, bx = 0, by = 0;
148
static byte move[1] = {-1};
151
for (i=0; i<board_wid; i++)
152
for (j=0; j<board_heit; j++)
154
if (pos->board[j * board_wid + i] == KNIGHTS_WN)
156
if (pos->board[j * board_wid + i] == KNIGHTS_BN)
159
if (abs ((wx - bx) * (wy - by)) != 2)
165
int knights_getmove (Pos *pos, int x, int y, GtkboardEventType type, Player player,
166
byte **movp, int **rmovp)
168
int curx = -1, cury = -1;
169
static byte move[128];
171
if (type != GTKBOARD_BUTTON_RELEASE)
173
if (pos->board[y * board_wid + x] == (player == WHITE ? KNIGHTS_WN : KNIGHTS_BN))
175
if (pos->board[y * board_wid + x] != KNIGHTS_EMPTY)
177
get_cur_pos (pos->board, player, &curx, &cury);
178
if (abs ((curx - x) * (cury - y)) != 2)
182
*mp++ = (player == WHITE ? KNIGHTS_WN : KNIGHTS_BN);
185
*mp++ = KNIGHTS_CLOSED;
192
byte * knights_movegen (Pos *pos)
196
byte *movlist, *movp = movbuf;
197
Player player = pos->player;
198
get_cur_pos (pos->board, player, &i, &j);
201
int x = i + incx[k], y = j + incy[k];
203
if (!ISINBOARD (x, y)) continue;
204
if ((val = pos->board[y * board_wid + x]) == KNIGHTS_EMPTY)
208
*movp++ = KNIGHTS_CLOSED;
211
*movp++ = (player == WHITE ? KNIGHTS_WN : KNIGHTS_BN);
214
else if (val == KNIGHTS_WN || val == KNIGHTS_BN)
220
movlist = (byte *) (malloc (movp - movbuf));
221
memcpy (movlist, movbuf, (movp - movbuf));
225
static gboolean eval_disconnected (byte *theboard)
227
byte board[KNIGHTS_BOARD_WID * KNIGHTS_BOARD_HEIT];
228
int stack[KNIGHTS_BOARD_WID * KNIGHTS_BOARD_HEIT];
230
int i, curx, cury, x, y;
232
for (i=0; i<board_wid * board_heit; i++)
233
board[i] = theboard[i];
235
get_cur_pos (board, WHITE, &curx, &cury);
237
stack[stack_top++] = cury * board_wid + curx;
238
while (stack_top > 0)
241
curx = stack[stack_top] % board_wid;
242
cury = stack[stack_top] / board_wid;
247
if (!ISINBOARD (x, y)) continue;
248
if (board[y * board_wid + x] == KNIGHTS_BN)
250
if (board[y * board_wid + x] != KNIGHTS_EMPTY)
252
board[y * board_wid + x] = KNIGHTS_CLOSED;
253
stack[stack_top++] = y * board_wid + x;
259
// exhaustive DFS to solve the position exactly
260
static int eval_max_path_len (byte *theboard, Player player)
262
byte board[KNIGHTS_BOARD_WID * KNIGHTS_BOARD_HEIT];
263
int stack [KNIGHTS_BOARD_WID * KNIGHTS_BOARD_HEIT];
264
int current [KNIGHTS_BOARD_WID * KNIGHTS_BOARD_HEIT];
266
int i, curx, cury, x, y;
269
for (i=0; i<board_wid * board_heit; i++)
270
board[i] = theboard[i];
272
get_cur_pos (board, player, &curx, &cury);
274
current[stack_top] = 0;
275
stack[stack_top] = cury * board_wid + curx;
277
while (stack_top >= 0)
279
if (stack_top > max_len)
281
i = current[stack_top]++;
287
curx = stack[stack_top] % board_wid;
288
cury = stack[stack_top] / board_wid;
291
if (!ISINBOARD (x, y)) continue;
292
if (board[y * board_wid + x] != KNIGHTS_EMPTY)
294
board[y * board_wid + x] = KNIGHTS_CLOSED;
296
current[stack_top] = 0;
297
stack[stack_top] = y * board_wid + x;
302
// We may want to continue the game even when a result is apparent. The
303
// parameter strict is for this. who_won() sets it to TRUE and eval() to FALSE.
304
static ResultType knights_eval_real (Pos *pos, Player player, float *eval, gboolean strict)
307
int wcnt = 0, bcnt = 0;
308
static int disconn_cnt [2 * KNIGHTS_BOARD_WID * KNIGHTS_BOARD_HEIT] = {0};
309
static int total_cnt [2 * KNIGHTS_BOARD_WID * KNIGHTS_BOARD_HEIT] = {0};
311
if (pos->state && ((Knights_state *)pos->state)->num_pauses >= 2)
317
if (!strict && eval_disconnected (pos->board))
319
int wlen = 0, blen = 0;
320
disconn_cnt [pos->num_moves]++;
321
total_cnt[pos->num_moves]++;
322
wlen = eval_max_path_len (pos->board, WHITE);
323
blen = eval_max_path_len (pos->board, BLACK);
324
*eval = 2 * (wlen - blen) + (player == WHITE ? -1 : 1);
325
if (wlen > blen) return RESULT_WHITE;
326
else if (wlen < blen) return RESULT_BLACK;
327
else return player == WHITE ? RESULT_BLACK : RESULT_WHITE;
330
total_cnt[pos->num_moves]++;
331
// if (total_cnt[pos->num_moves] % 10000 == 0) printf ("Ply: %d;\t total: %d;\t disc: %d\n", pos->num_moves, total_cnt[pos->num_moves], disconn_cnt[pos->num_moves]);
333
get_cur_pos (pos->board, WHITE, &i, &j);
336
int x = i + incx[k], y = j + incy[k];
337
if (!ISINBOARD (x, y)) continue;
338
if (pos->board[y * board_wid + x] == KNIGHTS_EMPTY)
342
get_cur_pos (pos->board, BLACK, &i, &j);
345
int x = i + incx[k], y = j + incy[k];
346
if (!ISINBOARD (x, y)) continue;
347
if (pos->board[y * board_wid + x] == KNIGHTS_EMPTY)
351
if (player == WHITE && wcnt == 0)
353
if (bcnt == 0) *eval -= 1;
354
*eval *= GAME_EVAL_INFTY;
357
if (player == BLACK && bcnt == 0)
359
if (wcnt == 0) *eval += 1;
360
*eval *= GAME_EVAL_INFTY;
363
return RESULT_NOTYET;
366
ResultType knights_eval (Pos *pos, Player player, float *eval)
368
return knights_eval_real (pos, player, eval, FALSE);