1 /* fifteen.cc
2  *
3  * Copyright (C) 2002 The libgnomecanvasmm Development Team
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free
17  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 
20 #include "fifteen.h"
21 
22 #include <stdio.h>
23 
24 
25 #define PIECE_SIZE 50
26 
Fifteen()27 Fifteen::Fifteen()
28 : Gtk::VBox(false, 4)
29 {
30   set_border_width(4);
31 
32   Gtk::Alignment* alignment
33       = Gtk::manage(new Gtk::Alignment(0.5, 0.5, 0.0, 0.0));
34   pack_start(*alignment);
35 
36   Gtk::Frame* frame = Gtk::manage(new Gtk::Frame());
37   frame->set_shadow_type(Gtk::SHADOW_IN);
38   alignment->add(*frame);
39 
40   m_canvas = Gtk::manage(new Gnome::Canvas::Canvas());
41   m_canvas->set_size_request(PIECE_SIZE * 4 + 1, PIECE_SIZE * 4 + 1);
42   m_canvas->set_scroll_region(0, 0, PIECE_SIZE * 4 + 1, PIECE_SIZE * 4 + 1);
43   frame->add(*m_canvas);
44 
45   for(int i = 0; i < 15; ++i) {
46       int y = i / 4;
47       int x = i % 4;
48 
49       m_board.push_back(Gtk::manage(new Gnome::Canvas::Group(*m_canvas
50                                                         ->root(),
51                                                         x * PIECE_SIZE,
52                                                         y * PIECE_SIZE)));
53       Gnome::Canvas::Rect* rect
54           = Gtk::manage(new Gnome::Canvas::Rect(*m_board.back(),
55                                            0.0, 0.0,
56                                            PIECE_SIZE,
57                                            PIECE_SIZE));
58       rect->property_fill_color() = get_piece_color(i);
59       rect->property_outline_color() = "black";
60       rect->property_width_pixels() = 0;
61 
62       char buf[20];
63       sprintf(buf, "%d", i + 1);
64 
65       Gnome::Canvas::Text* text
66           = Gtk::manage(new Gnome::Canvas::Text(*m_board.back(),
67                                            PIECE_SIZE / 2.0,
68                                            PIECE_SIZE / 2.0,
69                                            buf));
70       text->property_font() = "Sans bold 24";
71       text->property_anchor() = Gtk::ANCHOR_CENTER;
72       text->property_fill_color() = "black";
73 
74       m_board.back()->signal_event()
75           .connect(sigc::bind(sigc::mem_fun(*this, &Fifteen::on_piece_event),
76                         m_board.back(),
77                         text));
78   }
79   m_board.push_back(NULL);
80 
81   Gtk::Button* button = Gtk::manage(new Gtk::Button("Scramble"));
82   button->signal_clicked().connect(sigc::mem_fun(*this, &Fifteen::on_scramble));
83   pack_start(*button, Gtk::PACK_SHRINK);
84 }
85 
86 
~Fifteen()87 Fifteen::~Fifteen()
88 {
89 }
90 
91 
92 Glib::ustring
get_piece_color(int piece)93 Fifteen::get_piece_color(int piece)
94 {
95   static char buf[50];
96 
97   int y = piece / 4;
98   int x = piece % 4;
99 
100   int r =((4 - x) * 255) / 4;
101   int g =((4 - y) * 255) / 4;
102   int b = 128;
103 
104   sprintf(buf, "#%02x%02x%02x", r, g, b);
105 
106   return buf;
107 }
108 
109 
110 #define SCRAMBLE_MOVES 256
111 
112 void
on_scramble(void)113 Fifteen::on_scramble(void)
114 {
115   srand(time(NULL));
116 
117   // First, find the blank spot
118 
119   int pos;
120   for(pos = 0; pos < 16; ++pos) {
121       if(m_board[pos] == NULL) {
122           break;
123       }
124   }
125 
126   // "Move the blank spot" around in order to scramble the pieces
127 
128   for(int i = 0; i < SCRAMBLE_MOVES; ++i) {
129   retry_scramble:
130       int dir = rand() % 4;
131 
132       int x = 0;
133       int y = 0;
134 
135       if((dir == 0) &&(pos > 3)) { // up
136           y = -1;
137       } else if((dir == 1) &&(pos < 12)) { // down
138           y = 1;
139       } else if((dir == 2) &&((pos % 4) != 0)) { // left
140           x = -1;
141       } else if((dir == 3) &&((pos % 4) != 3)) { // right
142           x = 1;
143       } else {
144           goto retry_scramble;
145       }
146 
147       int oldpos = pos + y * 4 + x;
148       m_board[pos] = m_board[oldpos];
149       m_board[oldpos] = NULL;
150       m_board[pos]->move(-x * PIECE_SIZE, -y * PIECE_SIZE);
151       m_canvas->update_now();
152       pos = oldpos;
153   }
154 }
155 
156 
157 bool
on_piece_event(GdkEvent * event,Gnome::Canvas::Group * item,Gnome::Canvas::Text * text)158 Fifteen::on_piece_event(GdkEvent *event,
159                         Gnome::Canvas::Group* item,
160                         Gnome::Canvas::Text* text)
161 {
162   int pos = 0;
163   for(pos = 0; pos < 16; ++pos) {
164       if(m_board[pos] == item) {
165           break;
166       }
167   }
168 
169   switch(event->type) {
170   case GDK_ENTER_NOTIFY:
171       text->property_fill_color() = "white";
172       break;
173 
174   case GDK_LEAVE_NOTIFY:
175       text->property_fill_color() = "black";
176       break;
177 
178   case GDK_BUTTON_PRESS: {
179       int y = pos / 4;
180       int x = pos % 4;
181       double dx = 0.0;
182       double dy = 0.0;
183 
184       bool move = true;
185 
186       if((y > 0) &&(m_board[(y - 1) * 4 + x] == NULL)) {
187           dx = 0.0;
188           dy = -1.0;
189           y--;
190       } else if((y < 3) &&(m_board[(y + 1) * 4 + x] == NULL)) {
191           dx = 0.0;
192           dy = 1.0;
193           y++;
194       } else if((x > 0) &&(m_board[y * 4 + x - 1] == NULL)) {
195           dx = -1.0;
196           dy = 0.0;
197           x--;
198       } else if((x < 3) &&(m_board[y * 4 + x + 1] == NULL)) {
199           dx = 1.0;
200           dy = 0.0;
201           x++;
202       } else {
203           move = true;
204       }
205 
206       if(move == true) {
207           int newpos = y * 4 + x;
208           m_board[pos] = NULL;
209           m_board[newpos] = item;
210           item->move(dx * PIECE_SIZE, dy * PIECE_SIZE);
211       }
212 
213       break;
214 
215   }
216   default:
217       break;
218   }
219 
220   return false;
221 }
222