+static int
+rand_within (int num_values)
+{
+ return (int) ((double) num_values * (rand() / (RAND_MAX + 1.0)));
+}
+
+static void
+shuffle (int *array, int length)
+{
+ int i, r, tmp;
+
+ for (i = 0; i < length; i++)
+ {
+ r = i + rand_within (length - i);
+ tmp = array[i];
+ array[i] = array[r];
+ array[r] = tmp;
+ }
+}
+
+static void
+rack_init (rack_t *rack, GooCanvasItem *parent, char *word)
+{
+ int i;
+
+ for (i = 0; i < MIN (MAX_TILES, strlen (word)); i++)
+ rack->tiles[i] = tile_create (parent, word[i], i);
+ rack->num_tiles = i;
+ while (i < MAX_TILES)
+ rack->tiles[i] = NULL;
+}
+
+static gboolean
+rack_shuffle (rack_t *rack)
+{
+ int indices[MAX_TILES];
+ int i, x, y;
+
+ for (i = 0; i < rack->num_tiles; i++)
+ indices[i] = i;
+
+ shuffle (indices, rack->num_tiles);
+
+ for (i = 0; i < rack->num_tiles; i++) {
+ rack->tiles[i]->rack_index = indices[i];
+ rack_tile_position (indices[i], &x, &y);
+ tile_glide_to (rack->tiles[i], x, y);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+on_key_press_event (GtkWidget *widget,
+ GdkEventKey *event,
+ gpointer user_data)
+{
+ int i, x, y;
+ char guess_letter;
+ rack_t *rack = user_data;
+
+ if (event->keyval == GDK_space) {
+ for (i = 0; i < rack->num_tiles; i++) {
+ if (rack->tiles[i]->guessed) {
+ rack_tile_position (rack->tiles[i]->rack_index, &x, &y);
+ tile_glide_to (rack->tiles[i], x, y);
+ rack->tiles[i]->guessed = FALSE;
+ }
+ }
+ the_guess_index = 0;
+ rack_shuffle (rack);
+ return TRUE;
+ }
+
+ if (event->keyval == GDK_BackSpace) {
+ gboolean found = FALSE;
+ int found_index;
+ x = 0;
+ for (i = 0; i < rack->num_tiles; i++) {
+ /* XXX: evil stuff here... please refactor a lot */
+ if (the_guess[the_guess_index-1] == rack->tiles[i]->letter &&
+ rack->tiles[i]->guessed &&
+ rack->tiles[i]->x > x)
+ {
+ found = TRUE;
+ found_index = i;
+ }
+ }
+ if (found) {
+ rack_tile_position (rack->tiles[found_index]->rack_index, &x, &y);
+ tile_glide_to (rack->tiles[found_index], x, y);
+ rack->tiles[found_index]->guessed = FALSE;
+ the_guess_index--;
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ /* XXX: event->string is deprecated, but the non-deprecated
+ * input-method stuff (GtkIMContext) is extremely non-obvious to
+ * use. */
+ guess_letter = tolower (event->string[0]);
+ for (i = 0; i < rack->num_tiles; i++) {
+ if (guess_letter == rack->tiles[i]->letter &&
+ ! rack->tiles[i]->guessed)
+ {
+ guess_tile_position (the_guess_index, &x, &y);
+ tile_glide_to (rack->tiles[i], x, y);
+ rack->tiles[i]->guessed = TRUE;
+ the_guess[the_guess_index++] = guess_letter;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+