Lines
89.9 %
Functions
96.3 %
Branches
69.39 %
/* ipuz-acrostic.c
*
* Copyright 2022 Jonathan Blandford <jrb@gnome.org>
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This library 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
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
* SPDX-License-Identifier: (LGPL-2.1-or-later OR MIT)
*/
#include "libipuz-config.h"
#include <libipuz/ipuz-acrostic.h>
#include <libipuz/ipuz-charset.h>
#include "acrostic-board-dimensions.h"
#include "ipuz-magic.h"
enum
{
PROP_0,
PROP_QUOTE_STR,
PROP_CHARSET,
N_PROPS
};
static GParamSpec *obj_props[N_PROPS] = { NULL, };
typedef struct _IPuzAcrosticPrivate
gchar *quote_str;
IPuzCharset *charset;
IPuzClue *quote_clue;
} IPuzAcrosticPrivate;
static void ipuz_acrostic_init (IPuzAcrostic *self);
static void ipuz_acrostic_class_init (IPuzAcrosticClass *klass);
static void ipuz_acrostic_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void ipuz_acrostic_get_property (GObject *object,
GValue *value,
static void ipuz_acrostic_finalize (GObject *object);
static void ipuz_acrostic_clone (IPuzPuzzle *src,
IPuzPuzzle *dest);
static gboolean ipuz_acrostic_equal (IPuzPuzzle *puzzle_a,
IPuzPuzzle *puzzle_b);
static void ipuz_acrostic_fixup (IPuzPuzzle *puzzle);
static void ipuz_acrostic_real_fix_all (IPuzCrossword *self,
const gchar *first_attribute_name,
va_list var_args);
static const gchar *const *ipuz_acrostic_get_kind_str (IPuzPuzzle *puzzle);
G_DEFINE_TYPE_WITH_CODE (IPuzAcrostic, ipuz_acrostic, IPUZ_TYPE_CROSSWORD,
G_ADD_PRIVATE (IPuzAcrostic));
static void
ipuz_acrostic_init (IPuzAcrostic *self)
/* Pass */
}
ipuz_acrostic_class_init (IPuzAcrosticClass *klass)
GObjectClass *object_class = G_OBJECT_CLASS (klass);
IPuzPuzzleClass *puzzle_class = IPUZ_PUZZLE_CLASS (klass);
IPuzCrosswordClass *crossword_class = IPUZ_CROSSWORD_CLASS (klass);
object_class->set_property = ipuz_acrostic_set_property;
object_class->get_property = ipuz_acrostic_get_property;
object_class->finalize = ipuz_acrostic_finalize;
puzzle_class->clone = ipuz_acrostic_clone;
puzzle_class->equal = ipuz_acrostic_equal;
puzzle_class->fixup = ipuz_acrostic_fixup;
puzzle_class->get_kind_str = ipuz_acrostic_get_kind_str;
crossword_class->fix_all = ipuz_acrostic_real_fix_all;
obj_props[PROP_QUOTE_STR] = g_param_spec_string ("quote-string",
"Quote string",
NULL,
G_PARAM_READWRITE);
obj_props[PROP_CHARSET] = g_param_spec_pointer ("lang-charset",
"Language Charset",
g_object_class_install_properties (object_class, N_PROPS, obj_props);
ipuz_acrostic_set_property (GObject *object,
GParamSpec *pspec)
IPuzAcrosticPrivate *priv;
g_return_if_fail (object != NULL);
priv = ipuz_acrostic_get_instance_private (IPUZ_ACROSTIC (object));
switch (prop_id)
case PROP_QUOTE_STR:
ipuz_acrostic_set_quote_str (IPUZ_ACROSTIC (object), g_value_get_string (value));
break;
case PROP_CHARSET:
if (priv->charset != NULL)
ipuz_charset_unref (priv->charset);
priv->charset = g_value_get_pointer (value);
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
ipuz_acrostic_get_property (GObject *object,
g_value_set_string (value, priv->quote_str);
g_value_set_pointer (value, priv->charset);
ipuz_acrostic_finalize (GObject *object)
g_clear_pointer (&priv->quote_str, g_free);
ipuz_clue_free (priv->quote_clue);
G_OBJECT_CLASS (ipuz_acrostic_parent_class)->finalize (object);
ipuz_acrostic_clone (IPuzPuzzle *src,
IPuzPuzzle *dest)
IPuzAcrosticPrivate *src_priv, *dest_priv;
g_assert (src != NULL);
g_assert (dest != NULL);
src_priv = ipuz_acrostic_get_instance_private (IPUZ_ACROSTIC (src));
dest_priv = ipuz_acrostic_get_instance_private (IPUZ_ACROSTIC (dest));
IPUZ_PUZZLE_CLASS (ipuz_acrostic_parent_class)->clone (src, dest);
dest_priv->quote_clue = ipuz_clue_copy (src_priv->quote_clue);
static IPuzClue *
calculate_quote_clue (IPuzAcrostic *self)
IPuzCrossword *xword = IPUZ_CROSSWORD (self);
IPuzClue *quote_clue = ipuz_clue_new ();
guint rows = ipuz_crossword_get_height (xword);
guint columns = ipuz_crossword_get_width (xword);
guint row, column;
for (row = 0; row < rows; row++)
for (column = 0; column < columns; column++)
IPuzCell *cell;
IPuzCellCoord coord = {
.row = row,
.column = column,
cell = ipuz_crossword_get_cell (xword, coord);
if (IPUZ_CELL_IS_GUESSABLE (cell))
g_array_append_val (quote_clue->cells, coord);
return quote_clue;
extract_quote_clue (IPuzAcrostic *self)
for (guint n = 0; n < ipuz_crossword_get_n_clue_sets (IPUZ_CROSSWORD (self)); n++)
GArray *clues;
clues = ipuz_crossword_get_clues (IPUZ_CROSSWORD (self),
ipuz_crossword_clue_set_get_dir (IPUZ_CROSSWORD (self), n));
g_assert (clues);
for (guint i = 0; i < clues->len; i++)
IPuzClue *clue = g_array_index (clues, IPuzClue *, i);
if (g_strcmp0 (ipuz_clue_get_clue_text (clue), _IPUZ_ACROSTIC_QUOTE_STR) == 0)
quote_clue = ipuz_clue_copy (clue);
ipuz_crossword_unlink_clue (IPUZ_CROSSWORD (self), clue);
ipuz_clue_set_direction (quote_clue, IPUZ_CLUE_DIRECTION_NONE);
ipuz_clue_set_clue_text (quote_clue, NULL);
return NULL;
fix_quote_clue (IPuzAcrostic *self)
priv = ipuz_acrostic_get_instance_private (self);
priv->quote_clue = extract_quote_clue (self);
if (priv->quote_clue == NULL)
priv->quote_clue = calculate_quote_clue (self);
static gboolean
ipuz_acrostic_equal (IPuzPuzzle *puzzle_a,
IPuzPuzzle *puzzle_b)
IPuzAcrosticPrivate *priv_a, *priv_b;
g_return_val_if_fail (IPUZ_IS_ACROSTIC (puzzle_b), FALSE);
priv_a = ipuz_acrostic_get_instance_private (IPUZ_ACROSTIC (puzzle_a));
priv_b = ipuz_acrostic_get_instance_private (IPUZ_ACROSTIC (puzzle_b));
if (! ipuz_clue_equal (priv_a->quote_clue, priv_b->quote_clue))
return FALSE;
return IPUZ_PUZZLE_CLASS (ipuz_acrostic_parent_class)->equal (puzzle_a,
puzzle_b);
ipuz_acrostic_fixup (IPuzPuzzle *puzzle)
IPUZ_PUZZLE_CLASS (ipuz_acrostic_parent_class) -> fixup (puzzle);
fix_quote_clue (IPUZ_ACROSTIC (puzzle));
ipuz_acrostic_real_fix_all (IPuzCrossword *self,
va_list var_args)
const gchar *attribute_name;
IPuzAcrosticSyncDirection direction;
va_list var_args_copy;
va_copy (var_args_copy, var_args);
attribute_name = first_attribute_name;
while (attribute_name)
if (! g_strcmp0 (attribute_name, "sync-direction"))
direction = va_arg (var_args_copy, IPuzAcrosticSyncDirection);
ipuz_acrostic_fix_quote_str (IPUZ_ACROSTIC (self), direction);
attribute_name = va_arg (var_args_copy, const gchar *);
va_end (var_args_copy);
/* Don't chain up. Just fix styles from the parent class as its the
* only one that makes sense. */
ipuz_crossword_fix_styles (IPUZ_CROSSWORD (self));
static const gchar *const *
ipuz_acrostic_get_kind_str (IPuzPuzzle *puzzle)
static const char *kind_str[] =
"http://ipuz.org/acrostic#1",
NULL
return kind_str;
/*
* Public Methods
IPuzPuzzle *
ipuz_acrostic_new (void)
IPuzPuzzle *acrostic;
IPuzCharsetBuilder *builder;
builder = ipuz_charset_builder_new_for_language ("C");
charset = ipuz_charset_builder_build (builder);
builder = NULL;
acrostic = g_object_new (IPUZ_TYPE_ACROSTIC,
"lang-charset", charset,
NULL);
return acrostic;
static gchar*
sanitize_quote_str (const gchar *quote_str,
IPuzAcrostic *self)
const gchar *p;
GString *string = NULL;
gchar *sanitized = NULL;
string = g_string_new (NULL);
for (p = quote_str; p[0]; p = g_utf8_next_char (p))
gunichar c;
c = g_utf8_get_char (p);
if (ipuz_charset_get_char_count (priv->charset, g_unichar_toupper (c)))
g_string_append_unichar (string, g_unichar_toupper (c));
else
g_string_append_unichar (string, ' ');
sanitized = g_string_free (string, FALSE);
/* remove leading and trailing whitespaces */
sanitized = g_strstrip (sanitized);
return sanitized;
void
ipuz_acrostic_set_quote_str (IPuzAcrostic *self,
const gchar *quote_str)
g_return_if_fail (self != NULL);
g_return_if_fail (quote_str != NULL);
priv->quote_str = sanitize_quote_str (quote_str, self);
const gchar*
ipuz_acrostic_get_quote_str (IPuzAcrostic *self)
g_return_val_if_fail (self != NULL, NULL);
return priv->quote_str;
IPuzClue *
ipuz_acrostic_get_quote_clue (IPuzAcrostic *self)
return priv->quote_clue;
update_grid_from_quote_str (IPuzAcrostic *self)
const gchar *ptr;
guint rows, columns;
IPuzCrossword *xword;
xword = IPUZ_CROSSWORD (self);
rows = ipuz_crossword_get_height (xword);
columns = ipuz_crossword_get_width (xword);
ptr = priv->quote_str;
for (guint row = 0; row < rows; row++)
for (guint column = 0; column < columns; column++)
ipuz_cell_set_cell_type (cell, IPUZ_CELL_BLOCK);
if (ptr && ptr[0])
c = g_utf8_get_char (ptr);
if (ipuz_charset_get_char_count (priv->charset, c))
g_autofree gchar *solution = NULL;
ipuz_cell_set_cell_type (cell, IPUZ_CELL_NORMAL);
solution = g_utf8_substring (ptr, 0, 1);
ipuz_cell_set_solution (cell, solution);
ptr = g_utf8_next_char (ptr);
ensure_board_fits_quote_str (IPuzAcrostic *self)
guint quote_length;
AcrosticBoardDimension dimension;
quote_length = g_utf8_strlen (priv->quote_str, -1);
dimension = acrostic_board_dimension_from_quote_length (quote_length);
/* quote_length > IPUZ_ACROSTIC_MAX_QUOTE_STR_LENGTH */
g_return_if_fail (dimension.width != 0);
ipuz_crossword_set_size (IPUZ_CROSSWORD (self), dimension.width, dimension.height);
sync_quote_str_to_grid (IPuzAcrostic *self)
g_return_if_fail (IPUZ_IS_ACROSTIC (self));
if (priv->quote_str != NULL)
ensure_board_fits_quote_str (self);
update_grid_from_quote_str (self);
g_clear_pointer (& priv->quote_clue, ipuz_clue_free);
sync_grid_to_quote_str (IPuzAcrostic *self)
GString *quote_str = NULL;
quote_str = g_string_new (NULL);
if (IPUZ_CELL_IS_NORMAL (cell))
g_string_append_unichar (quote_str,
g_utf8_get_char (ipuz_cell_get_solution (cell)));
g_string_append_unichar (quote_str, ' ');
priv->quote_str = g_strchomp (g_string_free (quote_str, FALSE));
ipuz_acrostic_fix_quote_str (IPuzAcrostic *self,
IPuzAcrosticSyncDirection sync_direction)
if (sync_direction == IPUZ_ACROSTIC_SYNC_QUOTE_STR_TO_GRID)
sync_quote_str_to_grid (self);
else if (sync_direction == IPUZ_ACROSTIC_SYNC_GRID_TO_QUOTE_STR)
sync_grid_to_quote_str (self);
g_assert_not_reached ();