1 |
3 |
sumanta.ch |
|
2 |
|
|
/*
|
3 |
|
|
|
4 |
|
|
connectk -- a program to play the connect-k family of games
|
5 |
|
|
Copyright (C) 2007 Michael Levin
|
6 |
|
|
|
7 |
|
|
This program is free software; you can redistribute it and/or
|
8 |
|
|
modify it under the terms of the GNU General Public License
|
9 |
|
|
as published by the Free Software Foundation; either version 2
|
10 |
|
|
of the License, or (at your option) any later version.
|
11 |
|
|
|
12 |
|
|
This program is distributed in the hope that it will be useful,
|
13 |
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14 |
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15 |
|
|
GNU General Public License for more details.
|
16 |
|
|
|
17 |
|
|
You should have received a copy of the GNU General Public License
|
18 |
|
|
along with this program; if not, write to the Free Software
|
19 |
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
20 |
|
|
|
21 |
|
|
*/
|
22 |
|
|
|
23 |
|
|
#include "config.h"
|
24 |
|
|
#include <time.h>
|
25 |
|
|
#include <gtk/gtk.h>
|
26 |
|
|
#include <glib/gprintf.h>
|
27 |
|
|
#include "shared.h"
|
28 |
|
|
#include "connectk.h"
|
29 |
|
|
|
30 |
|
|
/*
|
31 |
|
|
* Options menu
|
32 |
|
|
*/
|
33 |
|
|
|
34 |
|
|
int opt_det_ai = 0,
|
35 |
|
|
opt_print_u = 0,
|
36 |
|
|
opt_debug_dfsc = 0,
|
37 |
|
|
opt_debug_thread = 0,
|
38 |
|
|
opt_debug_stage = 0,
|
39 |
|
|
opt_pause_ai = 0,
|
40 |
|
|
opt_mark_log = 1,
|
41 |
|
|
opt_mark_norm = 1,
|
42 |
|
|
opt_grayscale = 0;
|
43 |
|
|
|
44 |
|
|
typedef struct {
|
45 |
|
|
char *desc;
|
46 |
|
|
int *pint;
|
47 |
|
|
int redraw;
|
48 |
|
|
} Option;
|
49 |
|
|
|
50 |
|
|
/* This array contains boolean integer options which will appear as check
|
51 |
|
|
menu items in the Options menu, the format is:
|
52 |
|
|
{ "Option name", &integer, redraw_on_toggle }, */
|
53 |
|
|
static Option options[] = {
|
54 |
|
|
{ "Pause AI", &opt_pause_ai, FALSE },
|
55 |
|
|
{ "Deterministic AI", &opt_det_ai, FALSE },
|
56 |
|
|
{ "Print board utility", &opt_print_u, FALSE },
|
57 |
|
|
{ "Debug search path", &opt_debug_dfsc, FALSE },
|
58 |
|
|
{ "Debug threat stage", &opt_debug_stage, FALSE },
|
59 |
|
|
{ "Debug threads", &opt_debug_thread, FALSE },
|
60 |
|
|
{ "Logarithmic marking", &opt_mark_log, TRUE },
|
61 |
|
|
{ "Normalized marking", &opt_mark_norm, TRUE },
|
62 |
|
|
{ "Grayscale rendering", &opt_grayscale, TRUE },
|
63 |
|
|
};
|
64 |
|
|
|
65 |
|
|
#define OPTIONS (sizeof (options) / sizeof (options[0]))
|
66 |
|
|
|
67 |
|
|
static void option_menu_item_toggled(GtkCheckMenuItem *item,
|
68 |
|
|
gpointer user_data)
|
69 |
|
|
{
|
70 |
|
|
Option *option = (Option*)user_data;
|
71 |
|
|
|
72 |
|
|
*option->pint = !(*option->pint);
|
73 |
|
|
gtk_check_menu_item_set_active(item, *option->pint);
|
74 |
|
|
if (option->redraw)
|
75 |
|
|
draw_board();
|
76 |
|
|
}
|
77 |
|
|
|
78 |
|
|
static void add_options_to_menu_shell(GtkWidget *menu)
|
79 |
|
|
{
|
80 |
|
|
GtkWidget *item;
|
81 |
|
|
int i;
|
82 |
|
|
|
83 |
|
|
for(i = 0; i < OPTIONS; i++) {
|
84 |
|
|
item = gtk_check_menu_item_new_with_mnemonic(options[i].desc);
|
85 |
|
|
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item),
|
86 |
|
|
*(options[i].pint));
|
87 |
|
|
g_signal_connect(item, "toggled",
|
88 |
|
|
G_CALLBACK(option_menu_item_toggled),
|
89 |
|
|
options + i);
|
90 |
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
|
91 |
|
|
}
|
92 |
|
|
}
|
93 |
|
|
|
94 |
|
|
/*
|
95 |
|
|
* Window widgets
|
96 |
|
|
*/
|
97 |
|
|
|
98 |
|
|
typedef struct {
|
99 |
|
|
PIECE type;
|
100 |
|
|
PLAYER player;
|
101 |
|
|
} PlayerMenuItem;
|
102 |
|
|
|
103 |
|
|
static GtkWidget *statusbar = NULL, *tree_view = NULL, *window = NULL;
|
104 |
|
|
static GtkListStore *list_store;
|
105 |
|
|
|
106 |
|
|
// Change window statusbar text
|
107 |
|
|
void window_status(const char *msg)
|
108 |
|
|
{
|
109 |
|
|
static int context;
|
110 |
|
|
|
111 |
|
|
if (!statusbar)
|
112 |
|
|
return;
|
113 |
|
|
if (context)
|
114 |
|
|
gtk_statusbar_pop(GTK_STATUSBAR(statusbar), context);
|
115 |
|
|
context = gtk_statusbar_get_context_id(GTK_STATUSBAR(statusbar),
|
116 |
|
|
"default");
|
117 |
|
|
gtk_statusbar_push(GTK_STATUSBAR(statusbar), context, msg);
|
118 |
|
|
}
|
119 |
|
|
|
120 |
|
|
// Set the correct settings for a move state
|
121 |
|
|
void setup_move(void)
|
122 |
|
|
{
|
123 |
|
|
if (!tournament) {
|
124 |
|
|
draw_marks(NULL, TRUE);
|
125 |
|
|
|
126 |
|
|
/* Stop a draw game */
|
127 |
|
|
if (move_no >= board_size * board_size) {
|
128 |
|
|
window_status(va("Draw in %d moves", move_no));
|
129 |
|
|
draw_playable(FALSE);
|
130 |
|
|
return;
|
131 |
|
|
}
|
132 |
|
|
|
133 |
|
|
/* Stop a won game */
|
134 |
|
|
if (board->won) {
|
135 |
|
|
draw_playable(FALSE);
|
136 |
|
|
draw_win();
|
137 |
|
|
window_status(va("%s wins in %d moves (%s -> %s)",
|
138 |
|
|
piece_to_string(board->turn), move_no,
|
139 |
|
|
bcoords_to_string(board->win_x1,
|
140 |
|
|
board->win_y1),
|
141 |
|
|
bcoords_to_string(board->win_x2,
|
142 |
|
|
board->win_y2)));
|
143 |
|
|
return;
|
144 |
|
|
}
|
145 |
|
|
|
146 |
|
|
/* Status bar message */
|
147 |
|
|
if (!move_no)
|
148 |
|
|
window_status(va("Game start: %s to play "
|
149 |
|
|
"(%d move%s left)",
|
150 |
|
|
piece_to_string(board->turn),
|
151 |
|
|
board->moves_left,
|
152 |
|
|
board->moves_left == 1 ? "" : "s"));
|
153 |
|
|
else
|
154 |
|
|
window_status(va("Move %d: %s to play (%d move%s left)",
|
155 |
|
|
move_no, piece_to_string(board->turn),
|
156 |
|
|
board->moves_left,
|
157 |
|
|
board->moves_left == 1 ? "" : "s"));
|
158 |
|
|
|
159 |
|
|
draw_playable(players[board->turn].ai == PLAYER_HUMAN);
|
160 |
|
|
}
|
161 |
|
|
|
162 |
|
|
/* Let the AI thread run one move */
|
163 |
|
|
start_ai();
|
164 |
|
|
}
|
165 |
|
|
|
166 |
|
|
// Tree view cursor changed
|
167 |
|
|
static void cursor_changed(GtkTreeView *tree_view, gpointer user_data)
|
168 |
|
|
{
|
169 |
|
|
GtkTreeSelection *selection;
|
170 |
|
|
GtkTreeIter iter;
|
171 |
|
|
GtkTreePath *path;
|
172 |
|
|
gint *indices;
|
173 |
|
|
|
174 |
|
|
selection = gtk_tree_view_get_selection(tree_view);
|
175 |
|
|
gtk_tree_selection_get_selected(selection, NULL, &iter);
|
176 |
|
|
path = gtk_tree_model_get_path(GTK_TREE_MODEL(list_store), &iter);
|
177 |
|
|
indices = gtk_tree_path_get_indices(path);
|
178 |
|
|
indices += gtk_tree_path_get_depth(path) - 1;
|
179 |
|
|
if (*indices == move_no)
|
180 |
|
|
return;
|
181 |
|
|
go_to_move(*indices);
|
182 |
|
|
gtk_tree_path_free(path);
|
183 |
|
|
draw_board();
|
184 |
|
|
stop_ai();
|
185 |
|
|
setup_move();
|
186 |
|
|
}
|
187 |
|
|
|
188 |
|
|
// Clear moves in the history list
|
189 |
|
|
void tree_view_clear(unsigned int from)
|
190 |
|
|
{
|
191 |
|
|
GtkTreeIter iter;
|
192 |
|
|
GtkTreeSelection *selection;
|
193 |
|
|
gboolean valid;
|
194 |
|
|
|
195 |
|
|
if (!tree_view)
|
196 |
|
|
return;
|
197 |
|
|
|
198 |
|
|
g_signal_handlers_block_by_func(G_OBJECT(tree_view), cursor_changed,
|
199 |
|
|
NULL);
|
200 |
|
|
|
201 |
|
|
// Remove entries from list store
|
202 |
|
|
valid = gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(list_store),
|
203 |
|
|
&iter, NULL, from);
|
204 |
|
|
while (valid)
|
205 |
|
|
valid = gtk_list_store_remove(list_store, &iter);
|
206 |
|
|
|
207 |
|
|
// Select start entry
|
208 |
|
|
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
|
209 |
|
|
gtk_tree_model_get_iter_first(GTK_TREE_MODEL(list_store), &iter);
|
210 |
|
|
gtk_tree_selection_select_iter(selection, &iter);
|
211 |
|
|
|
212 |
|
|
g_signal_handlers_unblock_by_func(G_OBJECT(tree_view), cursor_changed,
|
213 |
|
|
NULL);
|
214 |
|
|
}
|
215 |
|
|
|
216 |
|
|
// Make a move for the current player
|
217 |
|
|
void make_move(BCOORD x, BCOORD y)
|
218 |
|
|
{
|
219 |
|
|
GtkTreeIter iter;
|
220 |
|
|
GtkTreePath *path;
|
221 |
|
|
GtkTreeSelection *selection;
|
222 |
|
|
PIECE last_turn;
|
223 |
|
|
|
224 |
|
|
if (piece_at(board, x, y) != PIECE_NONE) {
|
225 |
|
|
g_warning("%s attempted invalid move to %s",
|
226 |
|
|
piece_to_string(board->turn),
|
227 |
|
|
bcoords_to_string(x, y));
|
228 |
|
|
return;
|
229 |
|
|
}
|
230 |
|
|
|
231 |
|
|
if (!tournament) {
|
232 |
|
|
clear_last_moves();
|
233 |
|
|
|
234 |
|
|
// Remove the later moves from the list
|
235 |
|
|
if (move_no < move_last) {
|
236 |
|
|
clear_history(move_no + 1);
|
237 |
|
|
tree_view_clear(move_no + 1);
|
238 |
|
|
}
|
239 |
|
|
|
240 |
|
|
/* Add move to list */
|
241 |
|
|
gtk_list_store_append(list_store, &iter);
|
242 |
|
|
gtk_list_store_set(list_store, &iter, 0, va("%c%s",
|
243 |
|
|
piece_to_char(board->turn),
|
244 |
|
|
bcoords_to_string(x, y)), -1);
|
245 |
|
|
|
246 |
|
|
// Scroll moves list down
|
247 |
|
|
path = gtk_tree_model_get_path(GTK_TREE_MODEL(list_store),
|
248 |
|
|
&iter);
|
249 |
|
|
gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(tree_view), path,
|
250 |
|
|
NULL, FALSE, 0., 0.);
|
251 |
|
|
gtk_tree_path_free(path);
|
252 |
|
|
|
253 |
|
|
// Select last move
|
254 |
|
|
selection =
|
255 |
|
|
gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
|
256 |
|
|
g_signal_handlers_block_by_func(G_OBJECT(tree_view),
|
257 |
|
|
cursor_changed, NULL);
|
258 |
|
|
gtk_tree_selection_select_iter(selection, &iter);
|
259 |
|
|
g_signal_handlers_unblock_by_func(G_OBJECT(tree_view),
|
260 |
|
|
cursor_changed, NULL);
|
261 |
|
|
}
|
262 |
|
|
|
263 |
|
|
// Place piece
|
264 |
|
|
board->move_x = x;
|
265 |
|
|
board->move_y = y;
|
266 |
|
|
last_turn = board->turn;
|
267 |
|
|
go_to_move(++move_no);
|
268 |
|
|
place_piece(board, x, y);
|
269 |
|
|
if (!tournament)
|
270 |
|
|
draw_tile(x, y);
|
271 |
|
|
board->won = check_win_full(board, x, y,
|
272 |
|
|
&board->win_x1, &board->win_y1,
|
273 |
|
|
&board->win_x2, &board->win_y2);
|
274 |
|
|
if (!board->won) {
|
275 |
|
|
board->moves_left--;
|
276 |
|
|
if (board->moves_left <= 0) {
|
277 |
|
|
board->turn = other_player(last_turn);
|
278 |
|
|
board->moves_left = place_p;
|
279 |
|
|
}
|
280 |
|
|
}
|
281 |
|
|
|
282 |
|
|
/* Tournament win conditions */
|
283 |
|
|
if (tournament) {
|
284 |
|
|
if (move_no >= board_size * board_size) {
|
285 |
|
|
tourney_result(PIECE_NONE, move_last);
|
286 |
|
|
return;
|
287 |
|
|
}
|
288 |
|
|
if (board->won) {
|
289 |
|
|
tourney_result(board->turn, move_last);
|
290 |
|
|
return;
|
291 |
|
|
}
|
292 |
|
|
} else
|
293 |
|
|
draw_last_moves();
|
294 |
|
|
|
295 |
|
|
setup_move();
|
296 |
|
|
}
|
297 |
|
|
|
298 |
|
|
// Main window closed
|
299 |
|
|
static void window_destroy(GtkWidget *widget, gpointer data)
|
300 |
|
|
{
|
301 |
|
|
gtk_main_quit();
|
302 |
|
|
}
|
303 |
|
|
|
304 |
|
|
// Create a menu bar the old-fashioned way
|
305 |
|
|
static GtkWidget *window_menu_bar_init(GtkAccelGroup *accel)
|
306 |
|
|
{
|
307 |
|
|
GtkWidget *bar, *item, *submenu;
|
308 |
|
|
|
309 |
|
|
bar = gtk_menu_bar_new();
|
310 |
|
|
|
311 |
|
|
/* Init player dialogs */
|
312 |
|
|
player_dialog_init(player_dialog, PIECE_BLACK);
|
313 |
|
|
player_dialog_init(player_dialog + 1, PIECE_WHITE);
|
314 |
|
|
player_dialog_init(player_dialog + 2, PIECE_NONE);
|
315 |
|
|
|
316 |
|
|
// Game menu
|
317 |
|
|
item = gtk_menu_item_new_with_mnemonic("_Game");
|
318 |
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(bar), item);
|
319 |
|
|
submenu = gtk_menu_new();
|
320 |
|
|
gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
|
321 |
|
|
item = gtk_image_menu_item_new_from_stock(GTK_STOCK_NEW, accel);
|
322 |
|
|
g_signal_connect(G_OBJECT(item), "activate",
|
323 |
|
|
G_CALLBACK(open_new_game_dialog), NULL);
|
324 |
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(submenu), item);
|
325 |
|
|
item = gtk_image_menu_item_new_from_stock(GTK_STOCK_OPEN, accel);
|
326 |
|
|
g_signal_connect(G_OBJECT(item), "activate",
|
327 |
|
|
G_CALLBACK(open_file_dialog), NULL);
|
328 |
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(submenu), item);
|
329 |
|
|
item = gtk_separator_menu_item_new();
|
330 |
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(submenu), item);
|
331 |
|
|
item = gtk_image_menu_item_new_from_stock(GTK_STOCK_SAVE_AS, accel);
|
332 |
|
|
g_signal_connect(G_OBJECT(item), "activate",
|
333 |
|
|
G_CALLBACK(save_file_dialog), NULL);
|
334 |
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(submenu), item);
|
335 |
|
|
#ifndef NO_CAIRO_SVG
|
336 |
|
|
item = gtk_separator_menu_item_new();
|
337 |
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(submenu), item);
|
338 |
|
|
item = gtk_menu_item_new_with_mnemonic("Screenshot");
|
339 |
|
|
g_signal_connect(G_OBJECT(item), "activate",
|
340 |
|
|
G_CALLBACK(screenshot_dialog), NULL);
|
341 |
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(submenu), item);
|
342 |
|
|
#endif
|
343 |
|
|
item = gtk_separator_menu_item_new();
|
344 |
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(submenu), item);
|
345 |
|
|
|
346 |
|
|
// Game -> Quit
|
347 |
|
|
item = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, accel);
|
348 |
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(submenu), item);
|
349 |
|
|
g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(window_destroy),
|
350 |
|
|
NULL);
|
351 |
|
|
|
352 |
|
|
/* Setup menu */
|
353 |
|
|
item = gtk_menu_item_new_with_mnemonic("_Setup");
|
354 |
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(bar), item);
|
355 |
|
|
submenu = gtk_menu_new();
|
356 |
|
|
gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
|
357 |
|
|
item = gtk_menu_item_new_with_mnemonic("_Black Player");
|
358 |
|
|
g_signal_connect(G_OBJECT(item), "activate",
|
359 |
|
|
G_CALLBACK(open_player_dialog), player_dialog);
|
360 |
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(submenu), item);
|
361 |
|
|
item = gtk_menu_item_new_with_mnemonic("_White Player");
|
362 |
|
|
g_signal_connect(G_OBJECT(item), "activate",
|
363 |
|
|
G_CALLBACK(open_player_dialog), player_dialog + 1);
|
364 |
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(submenu), item);
|
365 |
|
|
item = gtk_menu_item_new_with_mnemonic("_Mark Player");
|
366 |
|
|
g_signal_connect(G_OBJECT(item), "activate",
|
367 |
|
|
G_CALLBACK(open_player_dialog), player_dialog + 2);
|
368 |
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(submenu), item);
|
369 |
|
|
item = gtk_separator_menu_item_new();
|
370 |
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(submenu), item);
|
371 |
|
|
item = gtk_menu_item_new_with_mnemonic("_Tournament");
|
372 |
|
|
g_signal_connect(G_OBJECT(item), "activate",
|
373 |
|
|
G_CALLBACK(open_tourney_dialog), NULL);
|
374 |
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(submenu), item);
|
375 |
|
|
item = gtk_separator_menu_item_new();
|
376 |
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(submenu), item);
|
377 |
|
|
add_options_to_menu_shell(submenu);
|
378 |
|
|
|
379 |
|
|
/* Tests menu */
|
380 |
|
|
item = gtk_menu_item_new_with_mnemonic("_Tests");
|
381 |
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(bar), item);
|
382 |
|
|
submenu = gtk_menu_new();
|
383 |
|
|
add_tests_to_menu_shell(submenu);
|
384 |
|
|
gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
|
385 |
|
|
|
386 |
|
|
return bar;
|
387 |
|
|
}
|
388 |
|
|
|
389 |
|
|
// Create a tree view to store the move history
|
390 |
|
|
static GtkWidget *tree_view_init(void)
|
391 |
|
|
{
|
392 |
|
|
GtkWidget *scrolled;
|
393 |
|
|
GtkTreeViewColumn *column;
|
394 |
|
|
GtkTreeSelection *selection;
|
395 |
|
|
GtkTreeIter iter;
|
396 |
|
|
|
397 |
|
|
if (tree_view)
|
398 |
|
|
return tree_view;
|
399 |
|
|
|
400 |
|
|
// Tree view to list move history
|
401 |
|
|
list_store = gtk_list_store_new(1, G_TYPE_STRING);
|
402 |
|
|
tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(list_store));
|
403 |
|
|
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
|
404 |
|
|
gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
|
405 |
|
|
gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree_view), TRUE);
|
406 |
|
|
column = gtk_tree_view_column_new_with_attributes("History",
|
407 |
|
|
gtk_cell_renderer_text_new(),
|
408 |
|
|
"text", 0, NULL);
|
409 |
|
|
gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
|
410 |
|
|
g_signal_connect(G_OBJECT(tree_view), "cursor-changed",
|
411 |
|
|
G_CALLBACK(cursor_changed), NULL);
|
412 |
|
|
|
413 |
|
|
// Add empty board entry to history list
|
414 |
|
|
gtk_list_store_append(list_store, &iter);
|
415 |
|
|
gtk_list_store_set(list_store, &iter, 0, "(start)", -1);
|
416 |
|
|
|
417 |
|
|
// Scrolled container for tree view
|
418 |
|
|
scrolled = gtk_scrolled_window_new(NULL, NULL);
|
419 |
|
|
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
|
420 |
|
|
GTK_POLICY_NEVER,
|
421 |
|
|
GTK_POLICY_AUTOMATIC);
|
422 |
|
|
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled),
|
423 |
|
|
GTK_SHADOW_IN);
|
424 |
|
|
gtk_container_add(GTK_CONTAINER(scrolled), tree_view);
|
425 |
|
|
return scrolled;
|
426 |
|
|
}
|
427 |
|
|
|
428 |
|
|
// Create and show main window
|
429 |
|
|
static void window_init(void)
|
430 |
|
|
{
|
431 |
|
|
GtkWidget *hbox, *vbox;
|
432 |
|
|
GtkAccelGroup *accel;
|
433 |
|
|
|
434 |
|
|
if (window)
|
435 |
|
|
return;
|
436 |
|
|
|
437 |
|
|
// Keyboard accelerator group
|
438 |
|
|
accel = gtk_accel_group_new();
|
439 |
|
|
|
440 |
|
|
// Menu, hbox, and status bar are packed vertically
|
441 |
|
|
vbox = gtk_vbox_new(FALSE, 0);
|
442 |
|
|
gtk_box_pack_start(GTK_BOX(vbox), window_menu_bar_init(accel), FALSE,
|
443 |
|
|
FALSE, 0);
|
444 |
|
|
statusbar = gtk_statusbar_new();
|
445 |
|
|
gtk_box_pack_end(GTK_BOX(vbox), statusbar, FALSE, FALSE, 0);
|
446 |
|
|
|
447 |
|
|
// Board and history are packed horizontally
|
448 |
|
|
hbox = gtk_hbox_new(FALSE, 0);
|
449 |
|
|
gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
|
450 |
|
|
gtk_box_pack_start(GTK_BOX(hbox), draw_init(), TRUE, TRUE, 0);
|
451 |
|
|
gtk_box_pack_start(GTK_BOX(hbox), tree_view_init(), FALSE, FALSE, 0);
|
452 |
|
|
|
453 |
|
|
// Create and show the window
|
454 |
|
|
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
455 |
|
|
g_signal_connect(G_OBJECT(window), "destroy",
|
456 |
|
|
G_CALLBACK(window_destroy), NULL);
|
457 |
|
|
gtk_window_set_title(GTK_WINDOW(window), "connectk");
|
458 |
|
|
gtk_window_resize(GTK_WINDOW(window), 686, 656);
|
459 |
|
|
gtk_window_add_accel_group(GTK_WINDOW(window), accel);
|
460 |
|
|
gtk_container_add(GTK_CONTAINER(window), vbox);
|
461 |
|
|
gtk_widget_show_all(window);
|
462 |
|
|
|
463 |
|
|
// Create new game dialog
|
464 |
|
|
new_game_dialog_init();
|
465 |
|
|
|
466 |
|
|
/* Initialize tournament dialog */
|
467 |
|
|
tourney_dialog_init();
|
468 |
|
|
}
|
469 |
|
|
|
470 |
|
|
/*
|
471 |
|
|
* Utility and main
|
472 |
|
|
*/
|
473 |
|
|
|
474 |
|
|
char *nvav(int *plen, const char *fmt, va_list va)
|
475 |
|
|
{
|
476 |
|
|
static char buffer[2][16000];
|
477 |
|
|
static int which;
|
478 |
|
|
int len;
|
479 |
|
|
|
480 |
|
|
which = !which;
|
481 |
|
|
len = g_vsnprintf(buffer[which], sizeof(buffer[which]), fmt, va);
|
482 |
|
|
if (plen)
|
483 |
|
|
*plen = len;
|
484 |
|
|
return buffer[which];
|
485 |
|
|
}
|
486 |
|
|
|
487 |
|
|
char *nva(int *plen, const char *fmt, ...)
|
488 |
|
|
{
|
489 |
|
|
va_list va;
|
490 |
|
|
char *string;
|
491 |
|
|
|
492 |
|
|
va_start(va, fmt);
|
493 |
|
|
string = nvav(plen, fmt, va);
|
494 |
|
|
va_end(va);
|
495 |
|
|
return string;
|
496 |
|
|
}
|
497 |
|
|
|
498 |
|
|
char *va(const char *fmt, ...)
|
499 |
|
|
{
|
500 |
|
|
va_list va;
|
501 |
|
|
char *string;
|
502 |
|
|
|
503 |
|
|
va_start(va, fmt);
|
504 |
|
|
string = nvav(NULL, fmt, va);
|
505 |
|
|
va_end(va);
|
506 |
|
|
return string;
|
507 |
|
|
}
|
508 |
|
|
|
509 |
|
|
int main(int argc, char *argv[])
|
510 |
|
|
{
|
511 |
|
|
start_ai_thread();
|
512 |
|
|
new_game(19);
|
513 |
|
|
|
514 |
|
|
/* Set the Glib random seed */
|
515 |
|
|
g_random_set_seed(time(NULL));
|
516 |
|
|
|
517 |
|
|
/* Initialize and run the GTK interface */
|
518 |
|
|
gtk_init(&argc, &argv);
|
519 |
|
|
window_init();
|
520 |
|
|
gtk_main();
|
521 |
|
|
|
522 |
|
|
return 0;
|
523 |
|
|
}
|