📄 gpegsol.c
字号:
/* * GNOME Peg Solitaire: The Peg Solitaire game for GNOME. * Copyright (C) 2001, 2002 Andreas T. Hagli <ahagli@online.no> * * 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 <math.h> /* For sin() and cos(). */#include <config.h>#include <gnome.h>/* Uncomment the next line to enable icon. *//* #define GPEGSOL_ICON */#ifdef GPEGSOL_ICON#include <libgnomeui/gnome-window-icon.h>#ifndef GNOME_ICONDIR#define GNOME_ICONDIR "/usr/share/pixmaps"#endif#endif#define GPEGSOL_VERSION "0.3"#define MAX_WIN_SIZE 800#define MIN_WIN_SIZE 200#define CELL_SIZE 32.0#define STILL_PEG_SIZE 18.4#define MOVING_PEG_SIZE 25.8#define PREVIEW_CELL_SIZE 16#define MAX_BOARD_SIZE 10#define N_GAMETYPES 17#define S_N_GAMETYPES "17" /* Needed for output. */static GnomeApp* app;static GnomeCanvas* canvas;static GnomeCanvasGroup* board_group;static GnomeCanvasGroup* peg_group[MAX_BOARD_SIZE][MAX_BOARD_SIZE];enum game_types { CROSS, PLUS, FIREPLACE, UP_ARROW, PYRAMID, DIAMOND, CROSSBOW, LONGBOW, SOLITARE, FRENSH, SQUARE, DRAUGHTSBOARD, THE_X, BIG_DIAMOND, TRIANGULAR, HEXAGONAL, STELLAR };enum sounds { PICK_UP, DROP_VALID, YOU_WIN, YOU_LOSE };static struct board_cell { double indent; gboolean hole; gboolean peg;} board[MAX_BOARD_SIZE][MAX_BOARD_SIZE];static gboolean hex_struct;static int board_size;static int sel_peg_x=-1, sel_peg_y=-1;static int undo_vars[MAX_BOARD_SIZE*MAX_BOARD_SIZE][4]; /* Four values for each move. */static int moves = 0;static int undo_count = 0;static enum game_types game_type;static gboolean sound;static int win_size;static gboolean restored = FALSE;/* For preferences. */static GtkWidget *pref_dialog;static GtkWidget *preview;static gboolean pref_sound;static enum game_types pref_game_type;static int pref_board_size;static struct board_cell pref_board[MAX_BOARD_SIZE][MAX_BOARD_SIZE];static int session_xpos = -1;static int session_ypos = -1;static int session_game_type = -1;static int session_win_size = -1;static int session_sound = -1;static voidplay_sound (enum sounds sound_id){ if (!sound) return; switch (sound_id) { case PICK_UP: gnome_triggers_do (NULL, NULL, "gpegsol", "pick-up", NULL); break; case DROP_VALID: gnome_triggers_do (NULL, NULL, "gpegsol", "drop", NULL); break; case YOU_WIN: gnome_triggers_do (NULL, NULL, "gpegsol", "you-win", NULL); break; case YOU_LOSE: gnome_triggers_do (NULL, NULL, "gpegsol", "you-lose", NULL); break; default: break; }}/* scale_peg: Moves the pegcenter to (0,0) scales it and moves it back. */static voidscale_peg (GnomeCanvasItem *peg, int x, int y, double value){ double affine1[6]; double affine2[6]; double x1, x2, y1, y2; gnome_canvas_item_get_bounds (peg, &x1, &y1, &x2, &y2); art_affine_translate (affine1, -x1, -y1); art_affine_scale (affine2, value, value); art_affine_multiply (affine1, affine1, affine2); art_affine_translate (affine2, x1 + (1-value)*(x2-x1)/2, y1 + (1-value)*(y2-y1)/2); art_affine_multiply (affine1, affine1, affine2); gnome_canvas_item_affine_relative (peg, affine1);}static voidpick_up_peg (int x, int y){ if (board[x][y].peg) { sel_peg_x = x; sel_peg_y = y; play_sound (PICK_UP); } else sel_peg_x = -1;}/* get_indent: Returns the needed indent value to create six-sided board. */static gbooleanget_indent (int y){ if (!hex_struct) return FALSE; if ((board_size/2)*2 == board_size && (y/2)*2 == y) return TRUE; else if ((board_size/2)*2 != board_size && (y/2)*2 != y) return TRUE; else return FALSE;}static intcount_pegs (void){ int n = 0; int x, y; for (x = 0; x < board_size; x++) for (y = 0; y < board_size; y++) if (board[x][y].peg) n++; return n;}static voidgame_over (void){ char msg[80]; int pegs; if ((pegs = count_pegs ()) == 1) { play_sound (YOU_WIN); sprintf (msg, _("Congratulations! You made it!")); } else { play_sound (YOU_LOSE); sprintf (msg, _("Sorry, you had %d pegs left."), pegs); } gnome_app_flash (GNOME_APP(app), msg);}/* check_game_over: Returns false is finds a valid move. True otherwise. */static gbooleancheck_game_over (void){ int x, y; for (x = 0; x < board_size; x++) for (y = 0; y < board_size; y++) { if (!hex_struct && ((x < board_size-2 && board[x+2][y].hole && board[x][y].peg && board[x+1][y].peg && !board[x+2][y].peg) || (x >= 2 && board[x-2][y].hole && board[x][y].peg && board[x-1][y].peg && !board[x-2][y].peg) || (y < board_size-2 && board[x][y+2].hole && board[x][y].peg && board[x][y+1].peg && !board[x][y+2].peg) || (y >= 2 && board[x][y-2].hole && board[x][y].peg && board[x][y-1].peg && !board[x][y-2].peg))) return FALSE; else if (hex_struct && ((x+2 <= board_size && board[x+2][y].hole && board[x][y].peg && board[x+1][y].peg && !board[x+2][y].peg) || (x-2 > 0 && board[x-2][y].hole && board[x][y].peg && board[x-1][y].peg && !board[x-2][y].peg) || (y+2 <= board_size && x-1 > 0 && board[x-1][y+2].hole && board[x][y].peg && board[x][y+1].peg && !board[x-1][y+2].peg && get_indent (y)) || (y+2 <= board_size && x+1 <= board_size && board[x+1][y+2].hole && board[x][y].peg && board[x+1][y+1].peg && !board[x+1][y+2].peg && get_indent (y)) || (y-2 > 0 && x-1 > 0 && board[x-1][y-2].hole && board[x][y].peg && board[x][y-1].peg && !board[x-1][y-2].peg && get_indent (y)) || (y-2 > 0 && x+1 <= board_size && board[x+1][y-2].hole && board[x][y].peg && board[x+1][y-1].peg && !board[x+1][y-2].peg && get_indent (y)) || (y+2 <= board_size && board[x-1][y+2].hole && board[x][y].peg && board[x-1][y+1].peg && !board[x-1][y+2].peg && !get_indent (y)) || (y+2 <= board_size && board[x+1][y+2].hole && board[x][y].peg && board[x][y+1].peg && !board[x+1][y+2].peg && !get_indent (y)) || (y-2 > 0 && x-1 > 0 && board[x-1][y-2].hole && board[x][y].peg && board[x-1][y-1].peg && !board[x-1][y-2].peg && !get_indent (y)) || (y-2 > 0 && x+1 <= board_size && board[x+1][y-2].hole && board[x][y].peg && board[x][y-1].peg && !board[x+1][y-2].peg && !get_indent (y)))) return FALSE; } return TRUE;}static voidremove_peg (int x, int y){ board[x][y].peg = FALSE; gnome_canvas_item_hide (GNOME_CANVAS_ITEM(peg_group[x][y]));}static voidrestore_peg (int x, int y){ board[x][y].peg = TRUE; gnome_canvas_item_show (GNOME_CANVAS_ITEM(peg_group[x][y]));}/* drop_peg: Drops the peg if valid move. If not, returns peg. */static voiddrop_peg (int x, int y){ gboolean valid = FALSE; if (!board[x][y].peg && board[x][y].hole) { if (!hex_struct && sel_peg_x == x && sel_peg_y == (y-2) && board[x][(y-1)].peg) { remove_peg (sel_peg_x, sel_peg_y); remove_peg (sel_peg_x, sel_peg_y+1); restore_peg (x, y); valid = TRUE; } else if (!hex_struct && sel_peg_x == x && sel_peg_y == y+2 && board[x][y+1].peg) { remove_peg (sel_peg_x, sel_peg_y); remove_peg (sel_peg_x, sel_peg_y-1); restore_peg (x, y); valid = TRUE; } else if (hex_struct && sel_peg_x == x+1 && sel_peg_y == y-2) { if (!get_indent (y) && board[x][y-1].peg) { remove_peg (sel_peg_x, sel_peg_y); remove_peg (sel_peg_x-1, sel_peg_y+1); restore_peg (x, y); valid = TRUE; } else if (board[x+1][y-1].peg) { remove_peg (sel_peg_x, sel_peg_y); remove_peg (sel_peg_x, sel_peg_y+1); restore_peg (x, y); valid = TRUE; } } else if (hex_struct && sel_peg_x == x-1 && sel_peg_y == y-2) { if (!get_indent (y) && board[x-1][y-1].peg) { remove_peg (sel_peg_x, sel_peg_y); remove_peg (sel_peg_x, sel_peg_y+1); restore_peg (x, y); valid = TRUE; } else if (board[x][y-1].peg) { remove_peg (sel_peg_x, sel_peg_y); remove_peg (sel_peg_x+1, sel_peg_y+1); restore_peg (x, y); valid = TRUE; } } else if (hex_struct && sel_peg_x == x+1 && sel_peg_y == y+2) { if (!get_indent (y) && board[x][y+1].peg) { remove_peg (sel_peg_x, sel_peg_y); remove_peg (sel_peg_x-1, sel_peg_y-1); restore_peg (x, y); valid = TRUE; } else if (board[x+1][y+1].peg) { remove_peg (sel_peg_x, sel_peg_y); remove_peg (sel_peg_x, sel_peg_y-1); restore_peg (x, y); valid = TRUE; } } else if (hex_struct && sel_peg_x == x-1 && sel_peg_y == y+2) { if (!get_indent (y) && board[x-1][y+1].peg) { remove_peg (sel_peg_x, sel_peg_y); remove_peg (sel_peg_x, sel_peg_y-1); restore_peg (x, y); valid = TRUE; } else if (board[x][y+1].peg) { remove_peg (sel_peg_x, sel_peg_y); remove_peg (sel_peg_x+1, sel_peg_y-1); restore_peg (x, y); valid = TRUE; } } /* Here it does not matter if it is hex structure or not. */ else if (sel_peg_x == x-2 && sel_peg_y == y && board[x-1][y].peg) { remove_peg (sel_peg_x, sel_peg_y); remove_peg (sel_peg_x+1, sel_peg_y); restore_peg (x, y); valid = TRUE; } else if (sel_peg_x == x+2 && sel_peg_y == y && board[x+1][y].peg) { remove_peg (sel_peg_x, sel_peg_y); remove_peg (sel_peg_x-1, sel_peg_y); restore_peg (x, y); valid = TRUE; } if (valid) { if (undo_count != 0) undo_count = 0; undo_vars[moves][0] = sel_peg_x; undo_vars[moves][1] = sel_peg_y; undo_vars[moves][2] = x; undo_vars[moves][3] = y; moves++; play_sound (DROP_VALID); if (check_game_over ()) game_over (); } else restore_peg (sel_peg_x, sel_peg_y); sel_peg_x = -1; sel_peg_y = -1; }}/* resize_event: Resize the canvas if the window is resized. */static gintresize_event (GtkWidget *widget, GtkAllocation *allocation, void *d){ double factor; int dominant; dominant = allocation->height < allocation->width ? allocation->height : allocation->width; win_size = dominant; factor = win_size / CELL_SIZE / board_size / 1.15; gnome_canvas_set_pixels_per_unit (canvas, factor); return TRUE;}/* item_event: Handles picking up, dropping and moving of a peg. */static gintitem_event (GnomeCanvasItem *item, GdkEvent *event, gpointer data){ GdkCursor *fleur; static gboolean dragging; static double org_x, org_y; static double x, y; double item_x, item_y; double new_x, new_y; 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: if (event->button.button == 1) { if (check_game_over ()) return FALSE; fleur = gdk_cursor_new (GDK_FLEUR); gnome_canvas_item_grab (item, GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, fleur, event->button.time); gdk_cursor_destroy (fleur); x = event->button.x; y = event->button.y; org_x = x - (int)(x + board[(int)(x/CELL_SIZE)][(int)(y/CELL_SIZE)].indent)%(int)CELL_SIZE; org_y = y - (int)y%(int)CELL_SIZE; dragging = TRUE; pick_up_peg ((x-get_indent(y/CELL_SIZE)*CELL_SIZE/2)/CELL_SIZE, y/CELL_SIZE); gnome_canvas_item_raise_to_top (item); gnome_canvas_item_move (item, x - org_x - CELL_SIZE/2, y - org_y - CELL_SIZE/2); scale_peg (item, x, y, (MOVING_PEG_SIZE/STILL_PEG_SIZE)); } case GDK_MOTION_NOTIFY: if (dragging && (event->motion.state & GDK_BUTTON1_MASK)) { int bx, by; new_x = event->button.x; new_y = event->button.y; by = (int)(new_y/CELL_SIZE); bx = (int)((new_x-(CELL_SIZE/2)*get_indent(by))/CELL_SIZE); /* If a peg is dragged over a possible move, it will signalize that by temporarily moving it to that hole. */ if (board[bx][by].hole && !board[bx][by].peg && ((bx == sel_peg_x && by == sel_peg_y+2 && board[bx][by-1].peg && !hex_struct) || (bx == sel_peg_x && by == sel_peg_y-2 && board[bx][by+1].peg && !hex_struct) || (bx == sel_peg_x-1 && by == sel_peg_y+2 && board[bx][by-1].peg && hex_struct && !get_indent (by)) || (bx == sel_peg_x-1 && by == sel_peg_y+2 && board[bx+1][by-1].peg && hex_struct && get_indent (by)) || (bx == sel_peg_x+1 && by == sel_peg_y+2 && board[bx-1][by-1].peg && hex_struct && !get_indent (by)) || (bx == sel_peg_x+1 && by == sel_peg_y+2 && board[bx][by-1].peg && hex_struct && get_indent (by)) || (bx == sel_peg_x-1 && by == sel_peg_y-2 && board[bx][by+1].peg && hex_struct && !get_indent (by)) || (bx == sel_peg_x-1 && by == sel_peg_y-2 && board[bx+1][by+1].peg && hex_struct && get_indent (by)) || (bx == sel_peg_x+1 && by == sel_peg_y-2 && board[bx-1][by+1].peg && hex_struct && !get_indent (by)) || (bx == sel_peg_x+1 && by == sel_peg_y-2 && board[bx][by+1].peg && hex_struct && get_indent (by)) ||
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -