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