1
/* ipuz-clue.c
2
 *
3
 * Copyright 2022 Jonathan Blandford <jrb@gnome.org>
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 Software
17
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18
 *
19
 * SPDX-License-Identifier: (LGPL-2.1-or-later OR MIT)
20
 */
21

            
22

            
23
#include <libipuz/libipuz.h>
24
#include "ipuz-misc.h"
25

            
26

            
27

            
28
IPuzClueId *
29
ipuz_clue_id_copy (const IPuzClueId *clue_id)
30
{
31
  IPuzClueId *new_clue_id;
32

            
33
  if (clue_id == NULL)
34
    return NULL;
35

            
36
  new_clue_id = g_new0 (IPuzClueId, 1);
37
  *new_clue_id = *clue_id;
38

            
39
  return new_clue_id;
40
}
41

            
42
void
43
ipuz_clue_id_free (IPuzClueId *clue_id)
44
{
45
  g_free (clue_id);
46
}
47

            
48
gboolean
49
ipuz_clue_id_equal (const IPuzClueId *a,
50
                    const IPuzClueId *b)
51
{
52
  g_return_val_if_fail (a != NULL, FALSE);
53
  g_return_val_if_fail (b != NULL, FALSE);
54

            
55
  return (a->direction == b->direction
56
          && a->index == b->index);
57
}
58

            
59
IPuzClue *
60
958
ipuz_clue_new ()
61
{
62
  IPuzClue *clue;
63
958
  clue = g_new0 (IPuzClue, 1);
64

            
65
958
  clue->number = -1;
66
958
  clue->cells = g_array_new (FALSE, TRUE, sizeof (IPuzCellCoord));
67

            
68
958
  return clue;
69
}
70

            
71
IPuzClue *
72
60
ipuz_clue_copy (const IPuzClue *clue)
73
{
74
  IPuzClue *new_clue;
75
60
  g_return_val_if_fail (clue != NULL, NULL);
76

            
77
60
  new_clue = ipuz_clue_new ();
78
60
  new_clue->number = clue->number;
79
60
  new_clue->label = g_strdup (clue->label);
80
60
  new_clue->clue_text = g_strdup (clue->clue_text);
81
60
  new_clue->enumeration = clue->enumeration;
82
60
  new_clue->direction = clue->direction;
83
60
  g_clear_pointer (&new_clue->cells, g_array_unref);
84
60
  new_clue->cells = g_array_copy (clue->cells);
85
60
  new_clue->cells_set = clue->cells_set;
86

            
87
60
  return new_clue;
88
}
89

            
90
void
91
959
ipuz_clue_free (IPuzClue *clue)
92
{
93
959
  if (clue == NULL)
94
1
    return;
95

            
96
958
  g_free (clue->clue_text);
97
958
  g_free (clue->label);
98
958
  ipuz_enumeration_unref (clue->enumeration);
99
958
  g_array_free (clue->cells, TRUE);
100
958
  g_free (clue);
101
}
102

            
103
gboolean
104
4307
ipuz_clue_equal (const IPuzClue    *clue1,
105
                 const IPuzClue    *clue2)
106
{
107
4307
  if (clue1 == NULL && clue2 == NULL)
108
    return TRUE;
109

            
110
4307
  if (clue1 == NULL || clue2 == NULL)
111
    return FALSE;
112

            
113
4761
  if (!((clue1->number == clue2->number) &&
114
8614
        (clue1->direction == clue2->direction) &&
115
4307
        (g_strcmp0 (clue1->label, clue2->label) == 0) &&
116
454
        (g_strcmp0 (clue1->clue_text, clue2->clue_text) == 0)))
117
3853
    return FALSE;
118

            
119
454
  if (clue1->cells->len != clue2->cells->len)
120
    return FALSE;
121

            
122
454
  if (clue1->cells_set != clue2->cells_set)
123
    return FALSE;
124

            
125
454
  return (memcmp (clue1->cells->data, clue2->cells->data, clue1->cells->len * sizeof (IPuzCellCoord)) == 0);
126
}
127

            
128
void
129
ipuz_clue_build_simple (IPuzClue    *clue,
130
                        JsonBuilder *builder)
131
{
132
  g_return_if_fail (clue != NULL);
133

            
134
  json_builder_begin_array (builder);
135
  if (clue->number >=0)
136
    json_builder_add_int_value (builder, clue->number);
137
  json_builder_add_string_value (builder, clue->clue_text);
138
  json_builder_end_array (builder);
139
}
140

            
141
void
142
57
ipuz_clue_build_full (IPuzClue    *clue,
143
                      JsonBuilder *builder)
144
{
145
57
  g_return_if_fail (clue != NULL);
146

            
147
57
  json_builder_begin_object (builder);
148
57
  if (clue->number >=0)
149
    {
150
      json_builder_set_member_name (builder, "number");
151
      json_builder_add_int_value (builder, clue->number);
152
    }
153

            
154
57
  if (clue->label)
155
    {
156
57
      json_builder_set_member_name (builder, "label");
157
57
      json_builder_add_string_value (builder, clue->label);
158
    }
159

            
160
57
  if (clue->clue_text)
161
    {
162
57
      json_builder_set_member_name (builder, "clue");
163
57
      json_builder_add_string_value (builder, clue->clue_text);
164
    }
165

            
166
57
  if (clue->enumeration)
167
    {
168
      json_builder_set_member_name (builder, "enumeration");
169
      g_autofree gchar *src = ipuz_enumeration_get_src (clue->enumeration);
170
      json_builder_add_string_value (builder, src);
171
    }
172

            
173
57
  if (clue->location_set)
174
    {
175
      json_builder_set_member_name (builder, "location");
176
      json_builder_begin_array (builder);
177
      json_builder_add_int_value (builder, clue->location.column);
178
      json_builder_add_int_value (builder, clue->location.row);
179
      json_builder_end_array (builder);
180
    }
181

            
182
57
  if (clue->cells)
183
    {
184
      guint i;
185

            
186
57
      json_builder_set_member_name (builder, "cells");
187
57
      json_builder_begin_array (builder);
188
426
      for (i = 0; i < clue->cells->len; i++)
189
        {
190
          IPuzCellCoord *clue_coord;
191

            
192
369
          clue_coord = &(g_array_index (clue->cells, IPuzCellCoord, i));
193

            
194
369
          json_builder_begin_array (builder);
195
369
          json_builder_add_int_value (builder, clue_coord->column);
196
369
          json_builder_add_int_value (builder, clue_coord->row);
197
369
          json_builder_end_array (builder);
198
        }
199
57
      json_builder_end_array (builder);
200
    }
201
57
  json_builder_end_object (builder);
202
}
203

            
204
void
205
57
ipuz_clue_build (IPuzClue    *clue,
206
                 JsonBuilder *builder)
207
{
208
57
  g_return_if_fail (clue != NULL);
209

            
210
57
  if (clue->cells_set || clue->label || clue->enumeration)
211
57
    ipuz_clue_build_full (clue, builder);
212
  else
213
    ipuz_clue_build_simple (clue, builder);
214
}
215

            
216
/* Convenience function */
217
IPuzClueDirection
218
ipuz_clue_direction_switch (IPuzClueDirection direction)
219
{
220
  if (direction == IPUZ_CLUE_DIRECTION_ACROSS)
221
    return IPUZ_CLUE_DIRECTION_DOWN;
222
  if (direction == IPUZ_CLUE_DIRECTION_DOWN)
223
    return IPUZ_CLUE_DIRECTION_ACROSS;
224
  if (direction == IPUZ_CLUE_DIRECTION_DIAGONAL)
225
    return IPUZ_CLUE_DIRECTION_DIAGONAL_UP_LEFT;
226
  if (direction == IPUZ_CLUE_DIRECTION_DIAGONAL_UP_LEFT)
227
    return IPUZ_CLUE_DIRECTION_DIAGONAL;
228
  if (direction == IPUZ_CLUE_DIRECTION_DIAGONAL_UP)
229
    return IPUZ_CLUE_DIRECTION_DIAGONAL_DOWN_LEFT;
230
  if (direction == IPUZ_CLUE_DIRECTION_DIAGONAL_DOWN_LEFT)
231
    return IPUZ_CLUE_DIRECTION_DIAGONAL_UP;
232

            
233
  return direction;
234
}
235

            
236
gint
237
14165
ipuz_clue_get_number (IPuzClue *clue)
238
{
239
14165
  g_return_val_if_fail (clue != NULL, -1);
240

            
241
14165
  return clue->number;
242
}
243

            
244
void
245
149
ipuz_clue_set_number (IPuzClue *clue,
246
                      gint      number)
247
{
248
149
  g_return_if_fail (clue != NULL);
249

            
250
149
  clue->number = number;
251
149
  if (clue->number > 0)
252
149
    g_clear_pointer (&clue->label, g_free);
253
}
254

            
255
const gchar *
256
14136
ipuz_clue_get_label (IPuzClue *clue)
257
{
258
14136
  g_return_val_if_fail (clue != NULL, NULL);
259

            
260
14136
  return clue->label;
261
}
262

            
263
void
264
ipuz_clue_set_label (IPuzClue    *clue,
265
                     const gchar *label)
266
{
267
  g_return_if_fail (clue != NULL);
268

            
269
  g_free (clue->label);
270
  clue->label = g_strdup (label);
271
  if (clue->label)
272
    clue->number = -1;
273
}
274

            
275
const gchar *
276
450
ipuz_clue_get_clue_text (IPuzClue *clue)
277
{
278
450
  g_return_val_if_fail (clue != NULL, NULL);
279

            
280
450
  return clue->clue_text;
281
}
282

            
283
void
284
75
ipuz_clue_set_clue_text (IPuzClue    *clue,
285
                         const gchar *clue_text)
286
{
287
75
  g_return_if_fail (clue != NULL);
288

            
289
75
  g_free (clue->clue_text);
290
75
  clue->clue_text = g_strdup (clue_text);
291
}
292

            
293
IPuzEnumeration *
294
259
ipuz_clue_get_enumeration (IPuzClue *clue)
295
{
296
259
  g_return_val_if_fail (clue != NULL, NULL);
297

            
298
259
  if (clue->enumeration)
299
193
    return ipuz_enumeration_ref (clue->enumeration);
300
66
  return NULL;
301
}
302

            
303
void
304
73
ipuz_clue_set_enumeration (IPuzClue        *clue,
305
                           IPuzEnumeration *enumeration)
306
{
307
73
  g_return_if_fail (clue != NULL);
308

            
309
73
  if (enumeration)
310
73
    ipuz_enumeration_ref (enumeration);
311
73
  ipuz_enumeration_unref (clue->enumeration);
312
73
  clue->enumeration = enumeration;
313
}
314

            
315
IPuzClueDirection
316
191
ipuz_clue_get_direction (IPuzClue *clue)
317
{
318
191
  g_return_val_if_fail (clue != NULL, 0);
319

            
320
191
  return clue->direction;
321
}
322

            
323
void
324
889
ipuz_clue_set_direction (IPuzClue          *clue,
325
                         IPuzClueDirection  direction)
326
{
327
889
  g_return_if_fail (clue != NULL);
328

            
329
889
  clue->direction = direction;
330
}
331

            
332
IPuzCellCoord
333
ipuz_clue_get_location(IPuzClue *clue,
334
                       gboolean *location_set)
335
{
336
  IPuzCellCoord coord = {
337
    .row = 0,
338
    .column = 0,
339
  };
340

            
341
  g_return_val_if_fail (clue != NULL, coord);
342

            
343
  if (location_set)
344
    *location_set = clue->location_set;
345

            
346
  return clue->location;
347
}
348

            
349
void
350
ipuz_clue_set_location (IPuzClue      *clue,
351
                        IPuzCellCoord  location)
352
{
353
  g_return_if_fail (clue != NULL);
354

            
355
  /* FIXME(arrowword): We need to recalculate the cells now */
356
  clue->location = location;
357
  clue->location_set = TRUE;
358
}
359

            
360
const GArray *
361
3262
ipuz_clue_get_cells (IPuzClue *clue)
362
{
363
3262
  g_return_val_if_fail (clue != NULL, NULL);
364

            
365
3262
  return clue->cells;
366
}
367

            
368
void
369
1983
ipuz_clue_append_cell (IPuzClue      *clue,
370
                       IPuzCellCoord  coord)
371
{
372
1983
  g_return_if_fail (clue != NULL);
373

            
374
1983
  g_array_append_val (clue->cells, coord);
375

            
376
}
377

            
378
void
379
ipuz_clue_get_first_cell (IPuzClue      *clue,
380
                          IPuzCellCoord *coord)
381
{
382
  IPuzCellCoord *clue_coord;
383

            
384
  g_return_if_fail (clue != NULL);
385
  g_return_if_fail (coord != NULL);
386
  g_return_if_fail (clue->cells->len != 0);
387

            
388
  clue_coord = &(g_array_index (clue->cells, IPuzCellCoord, 0));
389
  coord->row = clue_coord->row;
390
  coord->column = clue_coord->column;
391
}
392

            
393
void
394
ipuz_clue_get_last_cell (IPuzClue      *clue,
395
                         IPuzCellCoord *coord)
396
{
397
  IPuzCellCoord *clue_coord;
398

            
399
  g_return_if_fail (clue != NULL);
400
  g_return_if_fail (coord != NULL);
401
  g_return_if_fail (clue->cells->len != 0);
402

            
403
  clue_coord = &(g_array_index (clue->cells, IPuzCellCoord, clue->cells->len - 1));
404
  coord->row = clue_coord->row;
405
  coord->column = clue_coord->column;
406
}
407

            
408
gboolean
409
2037
ipuz_clue_contains_cell (IPuzClue      *clue,
410
                         IPuzCellCoord  coord)
411
{
412
  /* FIXME: We should cache this per-cell in the grid.
413
   * */
414
2037
  g_return_val_if_fail (clue != NULL, FALSE);
415

            
416
13488
  for (guint i = 0; i < clue->cells->len; i++)
417
    {
418
11542
      IPuzCellCoord * clue_coord = &(g_array_index (clue->cells, IPuzCellCoord, i));
419
11542
      if (clue_coord->row == coord.row && clue_coord->column == coord.column)
420
91
        return TRUE;
421
    }
422
1946
  return FALSE;
423
}
424

            
425
void
426
441
ipuz_clue_ensure_enumeration (IPuzClue *clue)
427
{
428
441
  g_return_if_fail (clue != NULL);
429

            
430
441
  if (clue->enumeration == NULL)
431
    {
432
117
      g_autofree gchar *src = g_strdup_printf ("%u", clue->cells->len);
433

            
434
117
      clue->enumeration = ipuz_enumeration_new (src, IPUZ_VERBOSITY_STANDARD);
435
    }
436
}
437

            
438
/*
439
 * We support the following formats:
440
 * [ number, clue_text, (optional) {"cells":[[]]} ]
441
 * [ "label", clue_text, (optional) {"cells":[[]]} ]
442
 * {"number": "clue": "label": (optional) "enumeration": (optional) "cells":[[]]}
443
 */
444

            
445

            
446
IPuzCellCoord
447
3252
ipuz_clue_parse_cell (JsonNode *node,
448
                      gboolean *valid)
449
{
450
  JsonArray *coord_array;
451
3252
  IPuzCellCoord coord = {
452
    .row = 0,
453
    .column = 0,
454
  };
455

            
456
3252
  if (valid) *valid = FALSE;
457

            
458
3252
  if (! JSON_NODE_HOLDS_ARRAY (node))
459
    return coord;
460

            
461
3252
  coord_array = json_node_get_array (node);
462
3252
  if (json_array_get_length (coord_array) < 2)
463
    return coord;
464

            
465
  /* WARNING:
466
   * It seems that puzzles store their "cells" coords as [x, y], and we do
467
   * everything as [row, column] which is inverted from this. This will
468
   * Fix it, but should probably be clarified in the spec.
469
   */
470
3252
  coord.row = json_array_get_int_element (coord_array, 1);
471
3252
  coord.column = json_array_get_int_element (coord_array, 0);
472

            
473
3252
  if (valid) *valid = TRUE;
474

            
475
3252
  return coord;
476
}
477

            
478
void
479
3252
parse_cell_foreach (JsonArray *array,
480
                    guint      index,
481
                    JsonNode  *element_node,
482
                    IPuzClue  *clue)
483
{
484
  IPuzCellCoord coord;
485
  gboolean valid;
486

            
487
3252
  coord = ipuz_clue_parse_cell (element_node, &valid);
488
3252
  if (valid)
489
3252
    g_array_append_val (clue->cells, coord);
490
3252
}
491

            
492
static void
493
547
ipuz_clue_parse_cells (IPuzClue *clue,
494
                       JsonNode *node)
495
{
496
547
  g_return_if_fail (clue != NULL);
497

            
498
547
  if (!JSON_NODE_HOLDS_ARRAY (node))
499
    return;
500

            
501
547
  json_array_foreach_element (json_node_get_array (node),
502
                              (JsonArrayForeach) parse_cell_foreach,
503
                              clue);
504
547
  clue->cells_set = TRUE;
505
}
506

            
507
IPuzClue *
508
738
ipuz_clue_new_from_json (JsonNode *node)
509
{
510
  IPuzClue *clue;
511

            
512
738
  g_return_val_if_fail (node != NULL, NULL);
513

            
514
738
  clue = ipuz_clue_new ();
515

            
516
738
  if (JSON_NODE_HOLDS_VALUE (node))
517
    {
518
      clue->clue_text = json_node_dup_string (node);
519
    }
520
738
  else if (JSON_NODE_HOLDS_ARRAY (node))
521
    {
522
51
      JsonArray *array = json_node_get_array (node);
523

            
524
      JsonNode *element;
525
51
      element = json_array_get_element (array, 0);
526
51
      if (element != NULL && JSON_NODE_HOLDS_VALUE (element))
527
        {
528
51
          GValue value = G_VALUE_INIT;
529

            
530
51
          json_node_get_value (element, &value);
531
51
          if (G_VALUE_HOLDS_STRING (&value))
532
            clue->label = g_value_dup_string (&value);
533
51
          else if (G_VALUE_HOLDS_INT (&value) || G_VALUE_HOLDS_INT64 (&value))
534
51
            clue->number = json_node_get_int (element);
535
51
          g_value_unset (&value);
536
        }
537

            
538
51
      element = json_array_get_element (array, 1);
539
51
      if (element != NULL && JSON_NODE_HOLDS_VALUE (element))
540
51
        clue->clue_text = ipuz_html_to_markup (json_node_get_string (element));
541
    }
542

            
543
687
  else if (JSON_NODE_HOLDS_OBJECT (node))
544
    {
545
      JsonObject *object;
546

            
547
687
      object = json_node_get_object (node);
548
687
      if (json_object_has_member (object, "number"))
549
324
        clue->number = json_object_get_int_member (object, "number");
550

            
551
687
      if (json_object_has_member (object, "clue"))
552
687
        clue->clue_text = ipuz_html_to_markup (json_object_get_string_member (object, "clue"));
553

            
554
687
      if (json_object_has_member (object, "label"))
555
361
        clue->label = g_strdup (json_object_get_string_member (object, "label"));
556

            
557
687
      if (json_object_has_member (object, "enumeration"))
558
        {
559
324
          const gchar *src = json_object_get_string_member (object, "enumeration");
560
324
          clue->enumeration = ipuz_enumeration_new (src, IPUZ_VERBOSITY_STANDARD);
561
        }
562

            
563
687
      if (json_object_has_member (object, "location"))
564
        {
565
          JsonNode *location = json_object_get_member (object, "location");
566

            
567
          if (location && JSON_NODE_HOLDS_ARRAY (location))
568
            clue->location = ipuz_clue_parse_cell (location, &clue->location_set);
569
        }
570

            
571
687
      if (json_object_has_member (object, "cells"))
572
        {
573
547
          JsonNode *cell_value = json_object_get_member (object, "cells");
574
547
          if (cell_value && JSON_NODE_HOLDS_ARRAY (cell_value))
575
547
            ipuz_clue_parse_cells (clue, cell_value);
576
        }
577
    }
578

            
579
738
  return clue;
580
}
581

            
582
IPuzCellCoord *
583
ipuz_cell_coord_copy (const IPuzCellCoord *coord)
584
{
585
  IPuzCellCoord *copy = g_new (IPuzCellCoord, 1);
586
  *copy = *coord;
587

            
588
  return copy;
589
}
590

            
591
gboolean
592
ipuz_cell_coord_equal (const IPuzCellCoord *a,
593
                       const IPuzCellCoord *b)
594
{
595
  g_return_val_if_fail (a != NULL, FALSE);
596
  g_return_val_if_fail (b != NULL, FALSE);
597

            
598
  return (a->row == b->row && a->column == b->column);
599
}
600

            
601
/*
602
 * Helper functions
603
 */
604

            
605
const gchar *
606
14
ipuz_clue_direction_to_string (IPuzClueDirection direction)
607
{
608
14
  switch (direction)
609
    {
610
    case IPUZ_CLUE_DIRECTION_NONE:
611
      return "None";
612
7
    case IPUZ_CLUE_DIRECTION_ACROSS:
613
7
      return "Across";
614
4
    case IPUZ_CLUE_DIRECTION_DOWN:
615
4
      return "Down";
616
    case IPUZ_CLUE_DIRECTION_DIAGONAL:
617
      return "Diagonal";
618
    case IPUZ_CLUE_DIRECTION_DIAGONAL_UP:
619
      return "Diagonal Up";
620
    case IPUZ_CLUE_DIRECTION_DIAGONAL_DOWN_LEFT:
621
      return "Diagonal Down Left";
622
    case IPUZ_CLUE_DIRECTION_DIAGONAL_UP_LEFT:
623
      return "Diagonal Up Left";
624
    case IPUZ_CLUE_DIRECTION_ZONES:
625
      return "Zones";
626
3
    case IPUZ_CLUE_DIRECTION_CLUES:
627
3
      return "Clues";
628
    case IPUZ_CLUE_DIRECTION_HIDDEN:
629
      return "Hidden";
630
    default:
631
      g_assert_not_reached ();
632
    }
633
}
634

            
635
IPuzClueDirection
636
55
ipuz_clue_direction_from_string (const gchar *str)
637
{
638
55
  g_return_val_if_fail (str != NULL, IPUZ_CLUE_DIRECTION_NONE);
639

            
640
55
  if (g_ascii_strcasecmp (str, "none") == 0)
641
    return IPUZ_CLUE_DIRECTION_NONE;
642
55
  else if (g_ascii_strcasecmp (str, "across") == 0)
643
26
    return IPUZ_CLUE_DIRECTION_ACROSS;
644
29
  else if (g_ascii_strcasecmp (str, "down") == 0)
645
20
    return IPUZ_CLUE_DIRECTION_DOWN;
646
9
  else if (g_ascii_strcasecmp (str, "diagonal") == 0)
647
    return IPUZ_CLUE_DIRECTION_DIAGONAL;
648
9
  else if (g_ascii_strcasecmp (str, "diagonal up") == 0)
649
    return IPUZ_CLUE_DIRECTION_DIAGONAL_UP;
650
9
  else if (g_ascii_strcasecmp (str, "diagonal down left") == 0)
651
    return IPUZ_CLUE_DIRECTION_DIAGONAL_DOWN_LEFT;
652
9
  else if (g_ascii_strcasecmp (str, "diagonal up left") == 0)
653
    return IPUZ_CLUE_DIRECTION_DIAGONAL_UP_LEFT;
654
9
  else if (g_ascii_strcasecmp (str, "zones") == 0)
655
2
    return IPUZ_CLUE_DIRECTION_ZONES;
656
7
  else if (g_ascii_strcasecmp (str, "clues") == 0)
657
7
    return IPUZ_CLUE_DIRECTION_CLUES;
658
  else if (g_ascii_strcasecmp (str, "hidden") == 0)
659
    return IPUZ_CLUE_DIRECTION_HIDDEN;
660

            
661
  return IPUZ_CLUE_DIRECTION_NONE;
662
}
663

            
664

            
665
G_DEFINE_BOXED_TYPE (IPuzClueId, ipuz_clue_id, ipuz_clue_id_copy, ipuz_clue_id_free)
666
G_DEFINE_BOXED_TYPE (IPuzCellCoord, ipuz_cell_coord, ipuz_cell_coord_copy, g_free);
667
G_DEFINE_BOXED_TYPE (IPuzClue, ipuz_clue, ipuz_clue_copy, ipuz_clue_free);