chess.c 28.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
/* gcompris - chess.c
 *
 * Copyright (C) 2002 Bruno Coudoin
 *
 *   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <ctype.h>
#include <math.h>
#include <assert.h>

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>

#include "chess_notation.h"

#include "gcompris/gcompris.h"

35 36 37 38
#if defined _WIN32 || defined __WIN32__
# undef WIN32   /* avoid warning on mingw32 */
# define WIN32
#endif
39 40 41

#define SOUNDLISTFILE PACKAGE

42 43
static GcomprisBoard *gcomprisBoard = NULL;
static gboolean board_paused = TRUE;
44

45
static GPid	 gnuchess_pid;
46 47 48 49 50 51 52 53
static void	 start_board (GcomprisBoard *agcomprisBoard);
static void	 pause_board (gboolean pause);
static void	 end_board (void);
static gboolean	 is_our_board (GcomprisBoard *gcomprisBoard);
static void	 set_level (guint level);
static int	 gamewon;
static void	 game_won(void);

54
static void	 engine_local_destroy (GPid gnuchess_pid);
55 56 57 58 59 60 61 62 63 64 65

static gboolean  engine_local_cb     (GIOChannel *source,
				      GIOCondition condition,
				      gpointer data);
static gboolean  engine_local_err_cb (GIOChannel *source,
				      GIOCondition condition,
				      gpointer data);
static void	 display_white_turn  (gboolean whiteturn);
static void	 display_info	     (gchar *info);
static int	 get_square_from_coord (double x, double y);

Bruno Coudoin's avatar
Bruno Coudoin committed
66
#define CHESSBOARD_X	50
67 68 69
#define CHESSBOARD_Y	20
#define SQUARE_WIDTH	60
#define SQUARE_HEIGHT	60
70 71 72 73
#define WHITE_COLOR	0xFFFF99FF
#define BLACK_COLOR	0x9999FFFF
#define WHITE_COLOR_H	0x99FF99FF
#define BLACK_COLOR_H	0x99FF99FF
74 75 76 77 78 79 80

#define TURN_X		(BOARDWIDTH-(BOARDWIDTH-(CHESSBOARD_X+(SQUARE_WIDTH*8)))/2)
#define TURN_Y		(CHESSBOARD_Y+15)

#define INFO_X		TURN_X
#define INFO_Y		(TURN_Y+40)

Bruno Coudoin's avatar
Bruno Coudoin committed
81 82 83 84
/* Game Type */
#define COMPUTER	1
#define PARTYEND	2
#define MOVELEARN	3
85

Bruno Coudoin's avatar
Bruno Coudoin committed
86
static char gameType = COMPUTER;
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103

static GnomeCanvasGroup *boardRootItem = NULL;

static GIOChannel *read_chan;
static GIOChannel *write_chan;

static gint read_cb;
static gint err_cb;

static Position *position;

/*
 * Contains the squares structure
 */
typedef struct {
  GnomeCanvasItem	*square_item;
  GnomeCanvasItem	*piece_item;
Bruno Coudoin's avatar
Bruno Coudoin committed
104
  Square		 square;
105 106
} GSquare;

Bruno Coudoin's avatar
Bruno Coudoin committed
107 108
static GSquare  *currentHighlightedGsquare;

109 110 111
static GnomeCanvasItem	*turn_item = NULL;
static GnomeCanvasItem	*info_item = NULL;

Bruno Coudoin's avatar
Bruno Coudoin committed
112 113
/* Need more space to fit notation.h definition */
static GSquare *chessboard[100];
114 115 116 117 118

static GnomeCanvasItem	*chess_create_item(GnomeCanvasGroup *parent);
static void		 chess_destroy_all_items(void);
static void		 chess_next_level(void);
static gint		 item_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data);
Bruno Coudoin's avatar
Bruno Coudoin committed
119
static gint		 item_event_black(GnomeCanvasItem *item, GdkEvent *event, gpointer data);
120
static gboolean		 start_child (char        *cmd,
121 122
				      GIOChannel **read_chan,
				      GIOChannel **write_chan,
123
				      GPid	 *gnuchess_pid);
124 125 126 127 128 129 130

static void		 write_child (GIOChannel  *write_chan,
				      char        *format,
				      ...);


/* Description of this plugin */
131
static BoardPlugin menu_bp =
132 133 134
  {
    NULL,
    NULL,
135 136
    "Learning Chess",
    "Play chess against tux in a learning mode",
137 138 139 140 141 142 143 144 145 146 147 148 149
    "Bruno Coudoin <bruno.coudoin@free.fr>",
    NULL,
    NULL,
    NULL,
    NULL,
    start_board,
    pause_board,
    end_board,
    is_our_board,
    NULL,
    NULL,
    set_level,
    NULL,
150 151
    NULL,
    NULL,
152 153 154 155 156 157 158 159 160
    NULL
  };

/*
 * Main entry point mandatory for each Gcompris's game
 * ---------------------------------------------------
 *
 */

161
GET_BPLUGIN_INFO(chess)
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183

/*
 * in : boolean TRUE = PAUSE : FALSE = CONTINUE
 *
 */
static void pause_board (gboolean pause)
{
  if(gcomprisBoard==NULL)
    return;

  if(gamewon == TRUE && pause == FALSE) /* the game is won */
    {
      game_won();
    }

  board_paused = pause;
}

/*
 */
static void start_board (GcomprisBoard *agcomprisBoard)
{
Bruno Coudoin's avatar
Bruno Coudoin committed
184
  
185
#ifndef WIN32  
186
  if (!g_file_test (GNUCHESS, G_FILE_TEST_EXISTS)) {
187
    
188
    gc_dialog(_("Error: The external program gnuchess is mandatory\nto play chess in gcompris.\nFind this program on http://www.rpmfind.net or in your\nGNU/Linux distribution\nAnd check it is located here: "GNUCHESS), gc_board_end);
Bruno Coudoin's avatar
Bruno Coudoin committed
189 190 191
    
    return;
  }
192 193
#endif

194 195
  if(agcomprisBoard!=NULL)
    {
196

197
      gcomprisBoard=agcomprisBoard;
Bruno Coudoin's avatar
Bruno Coudoin committed
198
      
Bruno Coudoin's avatar
Bruno Coudoin committed
199 200 201 202 203 204 205 206 207
      /* Default mode */
      if(!gcomprisBoard->mode)
	gameType=COMPUTER;
      else if(g_strncasecmp(gcomprisBoard->mode, "computer", 1)==0)
	gameType=COMPUTER;
      else if(g_strncasecmp(gcomprisBoard->mode, "partyend", 1)==0)
	gameType=PARTYEND;
      else if(g_strncasecmp(gcomprisBoard->mode, "movelearn", 1)==0)
	gameType=MOVELEARN;
Bruno Coudoin's avatar
Bruno Coudoin committed
208
      
209 210 211 212
      gcomprisBoard->level=1;
      gcomprisBoard->maxlevel=1;
      gcomprisBoard->sublevel=1;
      gcomprisBoard->number_of_sublevel=1; /* Go to next level after this number of 'play' */
Bruno Coudoin's avatar
Bruno Coudoin committed
213
      
Bruno Coudoin's avatar
Bruno Coudoin committed
214 215 216 217 218
      switch(gameType)
	{
	case PARTYEND:
	case MOVELEARN:
	  gcomprisBoard->maxlevel=9;
219
	  gc_bar_set(GC_BAR_LEVEL);
Bruno Coudoin's avatar
Bruno Coudoin committed
220 221
	  break;
	default:
222
	  gc_bar_set(0);
Bruno Coudoin's avatar
Bruno Coudoin committed
223
	}
224

225
      if(start_child (GNUCHESS, &read_chan,
226
		      &write_chan, &gnuchess_pid)==FALSE) {
227
	gc_dialog(_("Error: The external program gnuchess is mandatory\nto play chess in gcompris.\nFind this program on http://www.rpmfind.net or in your\nGNU/Linux distribution\nAnd check it is in "GNUCHESS), gc_board_end);
228 229
	return;
      }
Bruno Coudoin's avatar
Bruno Coudoin committed
230
      
231 232 233 234
      read_cb = g_io_add_watch (read_chan, G_IO_IN,
				engine_local_cb, NULL);
      err_cb = g_io_add_watch (read_chan, G_IO_HUP,
			       engine_local_err_cb, NULL);
Bruno Coudoin's avatar
Bruno Coudoin committed
235
      
236 237
      write_child (write_chan, "xboard\n");
      write_child (write_chan, "protover 2\n");
238 239 240
      write_child (write_chan, "post\n");
      write_child (write_chan, "easy\n");
      write_child (write_chan, "level 100 1 0\n");
241
      write_child (write_chan, "depth 1\n");
242
      
Bruno Coudoin's avatar
Bruno Coudoin committed
243
      chess_next_level();
Bruno Coudoin's avatar
Bruno Coudoin committed
244
      
Bruno Coudoin's avatar
Bruno Coudoin committed
245 246
      gamewon = FALSE;
      pause_board(FALSE);
247 248
    }
}
Bruno Coudoin's avatar
Bruno Coudoin committed
249

250 251 252 253 254 255 256 257 258 259 260 261 262 263
/* ======================================= */
static void end_board ()
{
  if(gcomprisBoard!=NULL)
    {
      pause_board(TRUE);
      chess_destroy_all_items();
    }
  gcomprisBoard = NULL;

  turn_item     = NULL;
  info_item     = NULL;


264
  engine_local_destroy(gnuchess_pid);
265 266 267 268 269 270 271 272 273 274 275 276 277 278
}

/* ======================================= */
static void set_level (guint level)
{

  if(gcomprisBoard!=NULL)
    {
      gcomprisBoard->level=level;
      gcomprisBoard->sublevel=1;
      chess_next_level();
    }
}
/* ======================================= */
279
static gboolean is_our_board (GcomprisBoard *gcomprisBoard)
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
{
  if (gcomprisBoard)
    {
      if(g_strcasecmp(gcomprisBoard->type, "chess")==0)
	{
	  /* Set the plugin entry */
	  gcomprisBoard->plugin=&menu_bp;

	  return TRUE;
	}
    }
  return FALSE;
}

/*-------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------*/
/* set initial values for the next level */
static void chess_next_level()
{
Bruno Coudoin's avatar
Bruno Coudoin committed
299 300
  register Square square;
  register gshort rank;
301
  gchar *img;
302

303 304
  img = gc_skin_image_get("gcompris-bg.jpg");
  gc_set_background(gnome_canvas_root(gcomprisBoard->canvas), 
305 306
			  img);
  g_free(img);
307

308
  gc_bar_set_level(gcomprisBoard);
309 310 311 312 313

  chess_destroy_all_items();
  gamewon = FALSE;

  /* Initial position */
Bruno Coudoin's avatar
Bruno Coudoin committed
314
  position = POSITION (position_new_initial ());
315

Bruno Coudoin's avatar
Bruno Coudoin committed
316 317 318 319 320 321 322 323 324 325 326 327 328 329
  switch(gameType)
    {
    case PARTYEND:
      position_set_initial_partyend(position, gcomprisBoard->level);
      break;
    case MOVELEARN:
      position_set_initial_movelearn(position, gcomprisBoard->level);
      break;
    }
  /* Init our internal chessboard */
  for (rank = 1; rank <= 8; rank++) { 
    for (square = A1 + ((rank - 1) * 10); 
	 square <= H1 + ((rank - 1) * 10);
	 square++) {
330

Bruno Coudoin's avatar
Bruno Coudoin committed
331
	GSquare *gsquare;
332

Bruno Coudoin's avatar
Bruno Coudoin committed
333
	gsquare = g_malloc(sizeof(GSquare));
334

Bruno Coudoin's avatar
Bruno Coudoin committed
335 336 337 338 339 340 341
	chessboard[square] = gsquare;
	chessboard[square]->piece_item = NULL;
	chessboard[square]->square = square;
	
    }
  }    
   
342 343 344 345 346 347 348 349
  /* Try the next level */
  chess_create_item(gnome_canvas_root(gcomprisBoard->canvas));
}

/* ==================================== */
/* Destroy all the items */
static void chess_destroy_all_items()
{
Bruno Coudoin's avatar
Bruno Coudoin committed
350 351
  register Square square;
  register gshort rank;
352

353 354 355 356
  if(boardRootItem!=NULL)
    gtk_object_destroy (GTK_OBJECT(boardRootItem));

  boardRootItem = NULL;
357 358
  turn_item     = NULL;
  info_item     = NULL;
359 360 361 362 363

  if(position!=NULL)
    gtk_object_destroy (GTK_OBJECT (position));

  position = NULL;
364

Bruno Coudoin's avatar
Bruno Coudoin committed
365 366 367 368 369 370 371 372 373 374 375 376
  for (rank = 1; rank <= 8; rank++) { 
    for (square = A1 + ((rank - 1) * 10); 
	 square <= H1 + ((rank - 1) * 10);
	 square++) {
      
      if(chessboard[square]!=NULL)
	{
	  g_free(chessboard[square]);
	  chessboard[square]=NULL;
	}
    }
  }
377 378 379 380 381 382 383
}

/* ==================================== */
static GnomeCanvasItem *chess_create_item(GnomeCanvasGroup *parent)
{
  guint color;
  GnomeCanvasItem *item = NULL;
Bruno Coudoin's avatar
Bruno Coudoin committed
384 385 386 387
  Square square;
  Piece piece;
  gshort rank;
  gboolean white_side = TRUE;
388
  guint empty_case = 0;
389
  gboolean need_slash = TRUE;
390 391 392 393 394 395 396 397 398

  boardRootItem = GNOME_CANVAS_GROUP(
				     gnome_canvas_item_new (gnome_canvas_root(gcomprisBoard->canvas),
							    gnome_canvas_group_get_type (),
							    "x", (double) 0,
							    "y", (double) 0,

							    NULL));

Bruno Coudoin's avatar
Bruno Coudoin committed
399 400 401 402 403 404 405 406
  for (rank = 1; rank <= 8; rank++) { 
    for (square = A1 + ((rank - 1) * 10); 
	 square <= H1 + ((rank - 1) * 10);
	 square++) {
      int x,y;
      
      x = square % 10 - 1;
      y = square / 10 - 2;
407

Bruno Coudoin's avatar
Bruno Coudoin committed
408 409 410 411 412 413 414 415 416 417
      color=((x+y)%2?BLACK_COLOR:WHITE_COLOR);
      
      item  = gnome_canvas_item_new (boardRootItem,
				     gnome_canvas_rect_get_type (),
				     "x1", (double) CHESSBOARD_X + (x * SQUARE_WIDTH),
				     "y1", (double) CHESSBOARD_Y + ((7-y) * SQUARE_HEIGHT),
				     "x2", (double) CHESSBOARD_X + (x * SQUARE_WIDTH) + SQUARE_WIDTH -1,
				     "y2", (double)  CHESSBOARD_Y + ((7-y) * SQUARE_HEIGHT) + SQUARE_HEIGHT -1,
				     "fill_color_rgba", color,
				     "outline_color", "black",
418
				     "width_units", (double)2,
Bruno Coudoin's avatar
Bruno Coudoin committed
419 420
				     NULL);
      chessboard[square]->square_item = item;
421
    }
Bruno Coudoin's avatar
Bruno Coudoin committed
422 423
  }

424
  /* Enter the gnuchess edit mode */
425
  write_child (write_chan, "force\n");
426
  write_child (write_chan, "new\n");
427 428 429
  write_child (write_chan, "setboard ");

  empty_case = 0;
430
  need_slash = FALSE;
431 432

  /* Display the pieces */
433
  for (rank = 8; rank >= 1; rank--) { 
Bruno Coudoin's avatar
Bruno Coudoin committed
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452
    for (square = A1 + ((rank - 1) * 10); 
	 square <= H1 + ((rank - 1) * 10);
	 square++) 
      {
	GdkPixbuf *pixmap = NULL;
	char *str;
	gint x, y;
	char *temp;
	char *san;

	piece = position->square[square];

	x = square % 10 - 1;
	y = square / 10 - 2;

	/* Destination square */
	san = g_new0 (char, 12);
	temp = san;
	square_to_ascii (&temp, square);
453
	//	printf ( "%c%s\n", piece_to_ascii(piece), san);
454 455 456 457 458 459 460

	if(need_slash)
	  {
	    write_child (write_chan, "/");
	    need_slash = FALSE;
	  }

Bruno Coudoin's avatar
Bruno Coudoin committed
461 462
	if(piece!=NONE)
	  {
463

464 465
	    if( (white_side && BPIECE(piece)) ||
		(!white_side && WPIECE(piece)) ) 
Bruno Coudoin's avatar
Bruno Coudoin committed
466 467
	      {
		white_side = !white_side;
468
		//		write_child (write_chan, "c\n");
Bruno Coudoin's avatar
Bruno Coudoin committed
469
	      }
470 471 472 473 474 475
	    if(empty_case>0)
	      write_child (write_chan, "%d", empty_case);

	    empty_case=0;

	    write_child (write_chan, "%c", piece_to_ascii(piece));
Bruno Coudoin's avatar
Bruno Coudoin committed
476
	  }
477 478 479 480 481 482 483 484 485 486 487 488
	else
	  {
	    empty_case++;
	  }

	if(x==7)
	  {
	    if(empty_case>0)
	      write_child (write_chan, "%d", empty_case);

	    empty_case=0;

489
	    need_slash = TRUE;
490 491
	  }

Bruno Coudoin's avatar
Bruno Coudoin committed
492 493 494 495
  	temp = san;
	san = g_strdup (temp);
	g_free (temp);

496
	//	printf("square=%d piece=%d x=%d y=%d\n", square, piece, x, y);
Bruno Coudoin's avatar
Bruno Coudoin committed
497 498
	if(piece != EMPTY)
	  {
499 500 501 502
	    if(BPIECE(piece))
	      str = g_strdup_printf("chess/B%c.png", piece_to_ascii(piece));
	    else
	      str = g_strdup_printf("chess/W%c.png", piece_to_ascii(piece));
Bruno Coudoin's avatar
Bruno Coudoin committed
503
	    
504
	    pixmap = gc_pixmap_load(str);
505
	    //	    g_warning("loading piece %s\n",   str);
Bruno Coudoin's avatar
Bruno Coudoin committed
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
	    g_free(str);
	    item = gnome_canvas_item_new (boardRootItem,
					  gnome_canvas_pixbuf_get_type (),
					  "pixbuf", pixmap, 
					  "x", (double)CHESSBOARD_X + (x * SQUARE_WIDTH) +
					  (guint)((SQUARE_WIDTH-gdk_pixbuf_get_width(pixmap))/2),
					  "y", (double) CHESSBOARD_Y + ((7-y) * SQUARE_HEIGHT) +
					  (guint)((SQUARE_HEIGHT-gdk_pixbuf_get_height(pixmap))/2),
					  NULL);
	    
	    chessboard[square]->piece_item = item;
	    if(WPIECE(piece))
	      gtk_signal_connect(GTK_OBJECT(item), "event", 
				 (GtkSignalFunc) item_event, NULL);
	    else
	      gtk_signal_connect(GTK_OBJECT(item), "event", 
				 (GtkSignalFunc) item_event_black, NULL);

	    gdk_pixbuf_unref(pixmap);
	  }
      }
  }

529
  /* Quit the gnuchess edit mode */
530
  write_child (write_chan, " w KQkq\n");
531 532 533 534 535 536 537 538 539 540 541 542 543 544 545

  display_white_turn(TRUE);

  return NULL;
}
/* ==================================== */
static void game_won()
{
  gcomprisBoard->sublevel++;

  if(gcomprisBoard->sublevel>gcomprisBoard->number_of_sublevel) {
    /* Try the next level */
    gcomprisBoard->sublevel=1;
    gcomprisBoard->level++;
    if(gcomprisBoard->level>gcomprisBoard->maxlevel) { // the current board is finished : bail out
546
      gc_bonus_end_display(BOARD_FINISHED_RANDOM);
547 548
      return;
    }
549
    gc_sound_play_ogg ("sounds/bonus.ogg", NULL);
550 551 552 553 554 555 556 557 558 559 560 561 562
  }
  chess_next_level();
}

static void display_white_turn(gboolean whiteturn)
{

  if(turn_item == NULL)
    {

      turn_item = gnome_canvas_item_new (boardRootItem,
					 gnome_canvas_text_get_type (),
					 "text",       " ",
563
					 "font",       gc_skin_font_board_big,
564 565 566
					 "x", (double) TURN_X,
					 "y", (double) TURN_Y,
					 "anchor",     GTK_ANCHOR_CENTER,
567
					 "fill_color_rgba", gc_skin_color_content,
568 569 570
					 NULL);
    }

571
  gnome_canvas_item_set(turn_item, "text", (whiteturn ? _("White's Turn") : _("Black's Turn")),
572 573 574 575 576 577 578 579 580 581 582 583
			NULL);
}


static void display_info(gchar *info)
{

  if(info_item == NULL)
    {
      info_item = gnome_canvas_item_new (boardRootItem,
					 gnome_canvas_text_get_type (),
					 "text",       " ",
584
					 "font",       gc_skin_font_board_big,
585 586 587
					 "x", (double) INFO_X,
					 "y", (double) INFO_Y,
					 "anchor",     GTK_ANCHOR_CENTER,
588
					 "fill_color_rgba", gc_skin_color_subtitle,
589 590 591 592 593 594 595 596 597
					 NULL);
    }

  gnome_canvas_item_set(info_item, "text", info,
			NULL);
}


/*
Bruno Coudoin's avatar
Bruno Coudoin committed
598
 * Move a piece to the given position using chess_notation notation
599 600 601 602 603 604 605 606 607
 *
 */
static void move_piece_to(Square from, Square to)
{
  GSquare *source_square, *dest_square;
  GnomeCanvasItem *item;
  guint x, y;
  double ofset_x, ofset_y;
  double x1, y1, x2, y2;
608
  Piece piece = NONE;
609 610
      

611
  g_warning("move_piece_to from=%d to=%d\n", from, to);
612

Bruno Coudoin's avatar
Bruno Coudoin committed
613
  source_square = chessboard[from];
614 615 616 617 618
  item = source_square->piece_item;
  source_square->piece_item = NULL;

  if(item == NULL)
    {
619
      g_warning("Warning: Problem in chess.c, bad move request in move_piece_to\n");
620 621 622
      return;
    }

623
  /* If we are promoting a pawn */
Bruno Coudoin's avatar
Bruno Coudoin committed
624
  if(position_get_color_to_move(position)==BLACK)
625 626 627 628
    {
      if (to & 128) {
	piece = ((to & 127) >> 3 ) + WP - 1;
	to = (to & 7) + A8;            
Bruno Coudoin's avatar
Bruno Coudoin committed
629
	printf("  Promoting white piece to %d\n", piece);
630 631 632 633 634 635 636 637 638 639 640
      }
    }
  else
    {
      if (to & 128) {
	piece = ((to & 127) >> 3) + BP - 1;
	to = (to & 7) + A1;
	printf("  Promoting black piece to %d\n", piece);
      }
    }

641 642
  /* Show the moved piece */
  gnome_canvas_item_set(source_square->square_item,
643
			"outline_color",
644
			(BPIECE(position->square[to])?"red":"blue"),
645 646
			NULL);

Bruno Coudoin's avatar
Bruno Coudoin committed
647
  display_white_turn(BPIECE(position->square[to]));
648 649 650 651

  x = to % 10;
  y = to / 10 -1;
  
652
  g_warning("   move_piece_to to    x=%d y=%d\n", x, y);
653

Bruno Coudoin's avatar
Bruno Coudoin committed
654
  dest_square = chessboard[to];
655 656 657

  /* Show the moved piece */
  gnome_canvas_item_set(dest_square->square_item,
658
			"outline_color", 
659
			(BPIECE(position->square[to])?"red":"blue"),
660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677
			NULL);

  if(dest_square->piece_item != NULL)
    /* Oups I loose a piece */
    gtk_object_destroy (GTK_OBJECT(dest_square->piece_item));

  dest_square->piece_item    = item;

  /* Find the ofset to move the piece */
  gnome_canvas_item_get_bounds  (item,
				 &x1,
				 &y1,
				 &x2,
				 &y2);


  ofset_x = (CHESSBOARD_X + SQUARE_WIDTH  * (x-1)) - x1 + (SQUARE_WIDTH  - (x2-x1))/2;
  ofset_y = (CHESSBOARD_Y + SQUARE_HEIGHT * (8-y)) - y1 + (SQUARE_HEIGHT - (y2-y1))/2;
Bruno Coudoin's avatar
Bruno Coudoin committed
678

679 680 681
  gnome_canvas_item_move(item, ofset_x, ofset_y);

  /* Manage rock */
Bruno Coudoin's avatar
Bruno Coudoin committed
682
  if(position->square[to]==WK && from==E1 && to==C1)
683
    move_piece_to(A1, D1);
Bruno Coudoin's avatar
Bruno Coudoin committed
684
  else if(position->square[to]==WK && from==E1 && to==G1)
685
    move_piece_to(H1, F1);
Bruno Coudoin's avatar
Bruno Coudoin committed
686
  else if(position->square[to]==BK && from==E8 && to==C8)
687
    move_piece_to(A8, D8);
Bruno Coudoin's avatar
Bruno Coudoin committed
688
  else if(position->square[to]==BK && from==E8 && to==G8)
689 690
    move_piece_to(H8, F8);

691 692 693 694 695
  /* Manage promotion */
  if(piece != NONE)
    {
      GdkPixbuf *pixmap = NULL;
      char *str;
696 697
      g_warning("  WARNING promoting a pawn from=%d to=%d piece=%d\n", from, to, piece);
      g_warning("  piece_to_ascii returns %c\n", piece_to_ascii(piece));
698

699 700 701 702
      if(BPIECE(piece))
	str = g_strdup_printf("chess/B%c.png", piece_to_ascii(piece));
      else
	str = g_strdup_printf("chess/W%c.png", piece_to_ascii(piece));
703
	      
704
      pixmap = gc_pixmap_load(str);
705
      g_free(str);
706
      g_warning("loading piece %c\n",  piece_to_ascii(piece));
707 708 709 710 711 712
      gnome_canvas_item_set (dest_square->piece_item,
			     "pixbuf", pixmap, 
			     NULL);

    }

713 714
  /* Display check info */
  if(position_white_king_attack(position))
715
    display_info(_("White checks"));
716
  else if(position_black_king_attack(position))
717
    display_info(_("Black checks"));
718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734
  else
    display_info(" ");

}

/*
 * Return a square suitable for position functions
 */
static int
get_square_from_coord (double x, double y)
{

  return (A1 + (Square) ((x - CHESSBOARD_X) / SQUARE_WIDTH)
	  + 10 * (7 - (Square)((y - CHESSBOARD_Y) / SQUARE_HEIGHT)));

}

Bruno Coudoin's avatar
Bruno Coudoin committed
735
void hightlight_possible_moves(GSquare *gsquare)
736 737 738
{
  Square square_test;
  guint color;
Bruno Coudoin's avatar
Bruno Coudoin committed
739 740 741
  register Square square;
  register gshort rank;
  short    current_color;
742

Bruno Coudoin's avatar
Bruno Coudoin committed
743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760
  if(currentHighlightedGsquare == gsquare)
    return;

  /* Remember the current color to move */
  current_color = position_get_color_to_move(position);

  if(WPIECE(position->square[gsquare->square]))
    position_set_color_to_move(position, WHITE);
  else
    position_set_color_to_move(position, BLACK);

  for (rank = 1; rank <= 8; rank++) { 
    for (square = A1 + ((rank - 1) * 10); 
	 square <= H1 + ((rank - 1) * 10);
	 square++) {


	square_test = position_move_normalize (position, gsquare->square, chessboard[square]->square);
761 762 763

	if (square_test) 
	  {
Bruno Coudoin's avatar
Bruno Coudoin committed
764
	    color=((rank+square)%2?BLACK_COLOR_H:WHITE_COLOR_H);
765

Bruno Coudoin's avatar
Bruno Coudoin committed
766
	    gnome_canvas_item_set(chessboard[square]->square_item,
767 768 769 770 771 772
				  "fill_color_rgba", color,
				  "outline_color", "black",
				  NULL);
	  }
	else
	  {
Bruno Coudoin's avatar
Bruno Coudoin committed
773
	    color=((rank+square)%2?BLACK_COLOR:WHITE_COLOR);
774

Bruno Coudoin's avatar
Bruno Coudoin committed
775
	    gnome_canvas_item_set(chessboard[square]->square_item,
776 777 778 779 780
				  "fill_color_rgba", color,
				  "outline_color", "black",
				  NULL);
	  }
      }      
Bruno Coudoin's avatar
Bruno Coudoin committed
781 782 783 784
  }

  /* Set back the current color to move */
  position_set_color_to_move(position, current_color);
785 786

  /* Show the current piece */
Bruno Coudoin's avatar
Bruno Coudoin committed
787
  gnome_canvas_item_set(gsquare->square_item,
788
			"outline_color",
789
			(BPIECE(position->square[gsquare->square])?"red":"blue"),
790 791 792 793 794 795 796 797 798
			NULL);
  
}

/* ==================================== */
static gint
item_event(GnomeCanvasItem *item, GdkEvent *event, gpointer data)
{
   static double x, y;
Bruno Coudoin's avatar
Bruno Coudoin committed
799
   static GSquare *gsquare;
800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816
   double new_x, new_y;
   GdkCursor *fleur;
   static int dragging;
   double item_x, item_y;

  if(board_paused)
    return FALSE;

  item_x = event->button.x;
  item_y = event->button.y;
  gnome_canvas_item_w2i(item->parent, &item_x, &item_y);

  switch (event->type)
    {
    case GDK_BUTTON_PRESS:
      {
	guint x1, y1;
Bruno Coudoin's avatar
Bruno Coudoin committed
817
	Square square;
818

Bruno Coudoin's avatar
Bruno Coudoin committed
819 820 821 822
	square = get_square_from_coord(event->button.x, event->button.y);
	x1 = square % 10;
	y1 = square / 10 -1;
	gsquare = chessboard[square];
823 824 825 826 827 828

	x = item_x;
	y = item_y;
	
	fleur = gdk_cursor_new(GDK_FLEUR);
	gnome_canvas_item_raise_to_top(item);
829
	gc_canvas_item_grab(item,
830 831 832 833 834 835 836
			       GDK_POINTER_MOTION_MASK | 
			       GDK_BUTTON_RELEASE_MASK,
			       fleur,
			       event->button.time);
	gdk_cursor_destroy(fleur);
	dragging = TRUE;

Bruno Coudoin's avatar
Bruno Coudoin committed
837
	hightlight_possible_moves(gsquare);
838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858
      }
      break;
    case GDK_MOTION_NOTIFY:
       if (dragging && (event->motion.state & GDK_BUTTON1_MASK)) 
         {
           new_x = item_x;
           new_y = item_y;

           gnome_canvas_item_move(item, new_x - x, new_y - y);
           x = new_x;
           y = new_y;
         }
       break;
           
     case GDK_BUTTON_RELEASE:
       if(dragging) 
	 {
	   guint x, y;
	   double ofset_x, ofset_y;
	   double x1, y1, x2, y2;
	   char pos[6];
Bruno Coudoin's avatar
Bruno Coudoin committed
859 860 861
	   Square to;

	   to = get_square_from_coord(event->button.x, event->button.y);
862
	   g_warning("===== Source square = %d Destination square = %d\n", gsquare->square, 
Bruno Coudoin's avatar
Bruno Coudoin committed
863
		  to);
864

Bruno Coudoin's avatar
Bruno Coudoin committed
865 866 867
	   to = position_move_normalize (position, gsquare->square, to);
	   if (to) {
	     position_move (position, gsquare->square, to);
868 869 870

	     x = 1 + (event->button.x - CHESSBOARD_X) / SQUARE_WIDTH;
	     y = 1 + (event->button.y - CHESSBOARD_Y) / SQUARE_HEIGHT;
Bruno Coudoin's avatar
Bruno Coudoin committed
871 872
	     move_to_ascii((char *)&pos, gsquare->square, to);

873 874
	     /* Tell gnuchess what our move is */
	     write_child (write_chan, (char *)&pos);
Bruno Coudoin's avatar
Bruno Coudoin committed
875 876
	     write_child (write_chan, "\n");
	     move_piece_to(gsquare->square, to);
877 878 879
	   }
	   else
	     {
880
	       g_warning("====== MOVE from %d REFUSED\n", gsquare->square);
881 882 883 884 885 886 887 888
	   
	       /* Find the ofset to move the piece back to where it was*/
	       gnome_canvas_item_get_bounds  (item,
					      &x1,
					      &y1,
					      &x2,
					      &y2);
	       
Bruno Coudoin's avatar
Bruno Coudoin committed
889 890
	       x = gsquare->square % 10;
	       y = gsquare->square / 10 -1;
891 892
	       
	       ofset_x = (CHESSBOARD_X + SQUARE_WIDTH  * (x-1)) - x1 + (SQUARE_WIDTH  - (x2-x1))/2;
Bruno Coudoin's avatar
Bruno Coudoin committed
893
	       ofset_y = (CHESSBOARD_Y + SQUARE_HEIGHT * (8-y)) - y1 + (SQUARE_HEIGHT - (y2-y1))/2;
894
	       g_warning("ofset = x=%f y=%f\n", ofset_x, ofset_y);
895 896 897 898
	       
	       gnome_canvas_item_move(item, ofset_x, ofset_y);
	     }

899
	   gc_canvas_item_ungrab(item, event->button.time);
900
	   dragging = FALSE;
Bruno Coudoin's avatar
Bruno Coudoin committed
901 902 903

	   position_display(position);

904 905 906 907 908 909 910 911 912 913
	 }
       break;

    default:
      break;
    }

  return FALSE;
}

Bruno Coudoin's avatar
Bruno Coudoin committed
914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935
/* ==================================== */
/* The user clicked on a black piece    */
static gint
item_event_black(GnomeCanvasItem *item, GdkEvent *event, gpointer data)
{
   static GSquare *gsquare;

  if(board_paused)
    return FALSE;

  switch (event->type)
    {
    case GDK_BUTTON_PRESS:
      {
	Square square;

	square = get_square_from_coord(event->button.x, event->button.y);
	gsquare = chessboard[square];

	hightlight_possible_moves(gsquare);
      }
      break;
936 937
    default:
      break;
Bruno Coudoin's avatar
Bruno Coudoin committed
938
    }
939
  return(FALSE);
Bruno Coudoin's avatar
Bruno Coudoin committed
940 941
}

942 943 944 945 946
/*======================================================================*/
/*======================================================================*/
/*======================================================================*/
/*======================================================================*/
static void
947
engine_local_destroy (GPid gnuchess_pid) 
948 949
{

950
  g_warning("engine_local_destroy () \n");
951
  write_child (write_chan, "quit\n");
952 953 954 955 956 957 958 959 960

  g_source_remove(read_cb);
  g_source_remove(err_cb);

  g_io_channel_close (read_chan);
  g_io_channel_unref (read_chan);
  
  g_io_channel_close (write_chan);
  g_io_channel_unref (write_chan);
961 962 963

  if(gnuchess_pid)
    g_spawn_close_pid(gnuchess_pid);
964 965 966 967 968 969 970 971 972 973 974
}

static gboolean
engine_local_cb (GIOChannel *source,
		 GIOCondition condition,
		 gpointer data)
{
  static char buf[1024];
  static char *b=buf;
  
  char *p,*q;
975 976 977 978 979 980 981 982 983 984
  gsize len;
  GIOStatus status;

  status = g_io_channel_read_chars (read_chan, b, sizeof (buf) - 1 - (b - buf), &len, NULL);
  if(status != G_IO_STATUS_NORMAL)
    {
      g_warning("g_io_channel_read_chars status=%d\n", status);
      /* FIXME: Not sure what to do */
      return FALSE;
    }
985 986 987 988 989 990 991 992 993 994 995 996
  
  if (len > 0) {
    b[len] = 0;
    b += len;
  }
  
  while (1) {
    char tmp;
    
    q = strchr (buf,'\n');
    if (q == NULL) break;
    tmp = *(q+1);
997
    *(q+1) = '\0';
998
    
999
    *q='\0';
1000 1001
    *(q+1) = tmp;
    
1002
    g_warning("engine_local_cb read=%s\n", buf);
1003 1004 1005
    
    /* parse for  NUMBER ... MOVE */
    if (isdigit (*buf))
1006 1007 1008 1009
      {
	if ((p = strstr (buf, "...")))
	  {
	    Square from, to;
1010
	  
1011
	    g_warning("computer number moves to %s\n", p+4);
1012
	  
1013 1014
	    if (san_to_move (position, p+4, &from, &to))
	      ascii_to_move (position, p+4, &from, &to);
1015
	  
1016 1017 1018 1019 1020 1021 1022 1023 1024 1025
	    position_move (position, from, to);
	    move_piece_to(from , to);
	  }
	else if ((p = strstr (buf, " ")))
	  {
	    /* It's a legal move case */
	    g_warning("Legal move to %s\n", p+1);
	  }
      }

1026
    /* parse for move MOVE */
1027
    if (!strncmp ("My move is : ",buf,13))
1028 1029 1030
      {
	Square from, to;

1031 1032
	p = strstr (buf, ":");
	printf("computer moves to %s\n", p+1);
1033

1034 1035
	if (san_to_move (position, p+1, &from, &to))
	  ascii_to_move (position, p+1, &from, &to);
1036 1037 1038 1039 1040 1041 1042 1043

	position_move (position, from, to);
	move_piece_to(from , to);
      }
    
    /* parse for illegal move */
    if (!strncmp ("Illegal move",buf,12))
      {
Bruno Coudoin's avatar
Bruno Coudoin committed
1044
	g_warning("Illegal move to %s : SHOULD NOT HAPPEN", buf+31);
1045 1046
      }

1047
    if (!strncmp ("0-1",buf,3))
1048 1049 1050 1051
      {
	display_info(_("Black mates"));
      }

1052
    if (!strncmp ("1-0",buf,3))
1053 1054 1055 1056
      {
	display_info(_("White mates"));
      }

1057
    if (!strncmp ("1/2-1/2",buf,7))
1058 1059 1060 1061
      {
	display_info(_("Drawn game"));
      }

1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073
    /* parse for feature */
    if (!strncmp ("feature",buf,7))
      {
	write_child(write_chan, "accepted setboard\n");
	write_child(write_chan, "accepted analyze\n");
	write_child(write_chan, "accepted ping\n");
	write_child(write_chan, "accepted draw\n");
	write_child(write_chan, "accepted variants\n");
	write_child(write_chan, "accepted myname\n");
	write_child(write_chan, "accepted done\n");
      }

1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092
    memmove (buf, q+1, sizeof(buf) - ( q + 1 - buf));
    b -= (q + 1 - buf);
  }
  
  return TRUE;
}

static gboolean
engine_local_err_cb (GIOChannel *source,
		     GIOCondition condition,
		     gpointer data)
{
  g_error ("Local Engine connection died");
  
  return FALSE;
}

/*----------------------------------------
 * Subprocess creation
1093
 * Return TRUE if gnuchess is started, false instead
1094 1095
 *----------------------------------------*/

1096
static gboolean
1097 1098
start_child (char *cmd, 
	     GIOChannel **read_chan, 
1099 1100
	     GIOChannel **write_chan,
	     GPid *Child_Process)
1101
{
1102 1103
  gint   Child_In, Child_Out, Child_Err;
  GError *gerror = NULL;
1104

1105 1106 1107
  gchar *Child_Argv[]={ cmd, NULL };

  g_warning("Ready to start child");
1108

1109 1110
  if (!g_spawn_async_with_pipes(NULL, Child_Argv, NULL,
				G_SPAWN_SEARCH_PATH,
1111 1112 1113 1114
				NULL, NULL, Child_Process, &Child_In, &Child_Out,
				&Child_Err, &gerror)) {
    
    g_warning("Error message '%s'", gerror->message);
1115
    g_warning("Error code    '%d'", gerror->code);
1116
    g_error_free (gerror);
1117
    g_warning("In order to play chess, you need to have gnuchess installed as " GNUCHESS);
1118
    return(FALSE);
1119 1120 1121

  }

1122
  g_warning("gnuchess subprocess is started");
1123

1124 1125 1126
  *read_chan = g_io_channel_unix_new (Child_Out);
  *write_chan = g_io_channel_unix_new (Child_In);

1127 1128 1129 1130 1131 1132 1133 1134 1135
  if(g_io_channel_set_encoding(*write_chan, NULL, NULL) != G_IO_STATUS_NORMAL)
    g_warning("Failed to set NULL encoding");

  if(g_io_channel_set_flags (*read_chan, G_IO_FLAG_SET_MASK, NULL) != G_IO_STATUS_NORMAL)
    g_warning("Failed to set NON BLOCKING IO");

  if(g_io_channel_set_flags (*write_chan, G_IO_FLAG_SET_MASK, NULL) != G_IO_STATUS_NORMAL)
    g_warning("Failed to set NON BLOCKING IO");

1136
  return(TRUE);
1137 1138 1139 1140 1141 1142 1143 1144
}

static void 
write_child (GIOChannel *write_chan, char *format, ...) 
{
  GIOError err;
  va_list ap;
  char *buf;
1145
  gsize len;	
1146 1147 1148 1149 1150 1151 1152 1153

  va_start (ap, format);

  buf = g_strdup_vprintf (format, ap);

  err = g_io_channel_write (write_chan, buf, strlen (buf), &len);
  if (err != G_IO_ERROR_NONE)
    g_warning ("Writing to child process failed");
1154 1155
  else
    g_warning ("Wrote '%s' to gnuchess", buf);
1156 1157 1158 1159 1160 1161

  va_end (ap);

  g_free (buf);
}

1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188
/* FIXME: The new API bellow should be use but for an unknown reason it doesn't work,
 *        Not all data are read back using this method
 */
/*
static void 
write_child (GIOChannel *write_chan, char *format, ...) 
{
  GIOStatus err;
  va_list ap;
  gchar *buf;
  gsize len;	

  va_start (ap, format);

  buf = g_strdup_vprintf (format, ap);

  err = g_io_channel_write_chars (write_chan, buf, strlen (buf), &len, NULL);
  if (err != G_IO_STATUS_NORMAL)
    g_warning ("Writing to child process failed");
  else
    g_warning ("Wrote '%s' to gnuchess", buf);  

  va_end (ap);

  g_free (buf);
}
*/