[gnome-games] lightsoff: Port from Javascript to Vala
- From: Robert Ancell <rancell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-games] lightsoff: Port from Javascript to Vala
- Date: Fri, 16 Dec 2011 10:31:55 +0000 (UTC)
commit af473091374e20dea00bf788a1708d29fd6963d9
Author: Robert Ancell <robert ancell canonical com>
Date: Sat Nov 19 14:11:21 2011 +1100
lightsoff: Port from Javascript to Vala
configure.in | 18 +-
lightsoff/Makefile.am | 41 +---
lightsoff/data/Makefile.am | 41 +++
lightsoff/data/{themes/tango => }/arrow.svg | 0
lightsoff/data/{themes/tango => }/backing.svg | 0
lightsoff/data/{themes/tango => }/highlight.svg | 0
lightsoff/data/{themes/tango => }/led-back.svg | 0
lightsoff/{ => data}/lightsoff.desktop.in.in | 0
lightsoff/data/lightsoff.ui | 18 +-
lightsoff/data/{themes/tango => }/off.svg | 0
lightsoff/data/{themes/tango => }/on.svg | 0
lightsoff/data/org.gnome.lightsoff.gschema.xml.in | 9 +
lightsoff/data/settings.ui | 135 ---------
lightsoff/data/themes/Makefile.am | 3 -
lightsoff/data/themes/tango/Makefile.am | 20 --
lightsoff/data/themes/tango/theme.js | 136 ---------
lightsoff/data/themes/up/Makefile.am | 20 --
lightsoff/data/themes/up/arrow.svg | 66 ----
lightsoff/data/themes/up/backing.svg | 141 ---------
lightsoff/data/themes/up/highlight.svg | 99 -------
lightsoff/data/themes/up/led-back.svg | 117 --------
lightsoff/data/themes/up/off.svg | 164 -----------
lightsoff/data/themes/up/on.svg | 198 -------------
lightsoff/data/themes/up/theme.js | 20 --
lightsoff/lightsoff.schemas.in | 43 ---
lightsoff/src/About.js | 29 --
lightsoff/src/Arrow.js | 28 --
lightsoff/src/Board.js | 214 --------------
lightsoff/src/Game.js | 327 ---------------------
lightsoff/src/LED.js | 181 ------------
lightsoff/src/Light.js | 84 ------
lightsoff/src/Makefile.am | 77 ++---
lightsoff/src/Path.js.in | 1 -
lightsoff/src/Puzzle.js | 162 ----------
lightsoff/src/Settings.js | 171 -----------
lightsoff/src/ThemeLoader.js | 44 ---
lightsoff/src/board-view.vala | 238 +++++++++++++++
lightsoff/src/config.vapi | 2 +
lightsoff/src/fixes.vapi | 9 +
lightsoff/src/game-view.vala | 276 +++++++++++++++++
lightsoff/src/led-array.vala | 133 +++++++++
lightsoff/src/lightsoff.in | 5 -
lightsoff/src/lightsoff.vala | 166 +++++++++++
lightsoff/src/main.js | 73 -----
lightsoff/src/puzzle-generator.vala | 198 +++++++++++++
po/POTFILES.in | 11 +-
46 files changed, 1125 insertions(+), 2593 deletions(-)
---
diff --git a/configure.in b/configure.in
index a5dc6bc..fa6f247 100644
--- a/configure.in
+++ b/configure.in
@@ -22,9 +22,9 @@ AM_MAINTAINER_MODE([enable])
# we support and which features to check for
# This is the canonical list of all game subdirectories.
-allgames="glchess glines gnect gnibbles gnobots2 gnomine gnotravex gnotski gtali iagno mahjongg quadrapassel gnome-sudoku"
+allgames="glchess glines gnect gnibbles gnobots2 gnomine gnotravex gnotski gtali iagno lightsoff mahjongg quadrapassel gnome-sudoku"
AC_SUBST([allgames])
-staginggames="lightsoff swell-foop"
+staginggames="swell-foop"
AC_SUBST([staginggames])
gamelist=""
@@ -126,7 +126,7 @@ for game in $gamelist; do
*) ;;
esac
case $game in
- glchess|gnomine|gnotravex|mahjongg) need_vala=yes ;;
+ glchess|gnomine|gnotravex|lightsoff|mahjongg) need_vala=yes ;;
*) ;;
esac
case $game in
@@ -878,6 +878,10 @@ glines/Makefile
glines/data/Makefile
glines/src/Makefile
glines/help/Makefile
+lightsoff/Makefile
+lightsoff/src/Makefile
+lightsoff/data/Makefile
+lightsoff/help/Makefile
quadrapassel/Makefile
quadrapassel/data/Makefile
quadrapassel/help/Makefile
@@ -921,19 +925,13 @@ gnibbles/data/gnibbles.desktop.in
gnotski/data/gnotski.desktop.in
glchess/glchess.desktop.in
glines/data/glines.desktop.in
+lightsoff/data/lightsoff.desktop.in
mahjongg/data/mahjongg.desktop.in
gtali/data/gtali.desktop.in
gnome-sudoku/gnome-sudoku.desktop.in
iagno/data/iagno.desktop.in
gnect/data/gnect.desktop.in
gnomine/data/gnomine.desktop.in
-lightsoff/Makefile
-lightsoff/help/Makefile
-lightsoff/lightsoff.desktop.in
-lightsoff/data/themes/Makefile
-lightsoff/data/themes/tango/Makefile
-lightsoff/data/themes/up/Makefile
-lightsoff/src/Makefile
])
AC_OUTPUT
diff --git a/lightsoff/Makefile.am b/lightsoff/Makefile.am
index 81c3d34..904a4c8 100644
--- a/lightsoff/Makefile.am
+++ b/lightsoff/Makefile.am
@@ -1,44 +1,7 @@
-SUBDIRS = data/themes
+SUBDIRS = src data
if BUILD_HELP
-SUBDIRS += src help
+SUBDIRS += help
endif
-lightsoffdir = $(pkgdatadir)/lightsoff
-lightsoff_DATA = \
- data/settings.ui \
- data/lightsoff.ui
-
-schema_in_files = lightsoff.schemas.in
-if HAVE_GNOME
-schemadir = $(GCONF_SCHEMA_FILE_DIR)
-schema_DATA = $(schema_in_files:.schemas.in=.schemas)
-endif
-
-desktop_in_files = lightsoff.desktop.in.in
-desktopdir = $(datadir)/applications
-desktop_DATA = $(desktop_in_files:.desktop.in.in=.desktop)
- INTLTOOL_DESKTOP_RULE@
-
-CLEANFILES = $(desktop_DATA) $(schema_DATA)
-DISTCLEANFILES = $(desktop_DATA) $(schema_DATA)
-
-EXTRA_DIST = \
- data/settings.ui \
- data/lightsoff.ui \
- $(schema_in_files)
-
-install-schemas-local: $(schema_DATA)
-if GCONF_SCHEMAS_INSTALL
- if test -z "$(DESTDIR)" ; then \
- for p in $^ ; do \
- GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-install-rule $$p 2>&1 > /dev/null; \
- done \
- fi
-endif
-
-install-data-local: install-schemas-local
-
- INTLTOOL_SCHEMAS_RULE@
-
-include $(top_srcdir)/git.mk
diff --git a/lightsoff/data/Makefile.am b/lightsoff/data/Makefile.am
new file mode 100644
index 0000000..a3596d2
--- /dev/null
+++ b/lightsoff/data/Makefile.am
@@ -0,0 +1,41 @@
+lightsoffdir = $(pkgdatadir)/lightsoff
+lightsoff_DATA = \
+ lightsoff.ui \
+ arrow.svg \
+ backing.svg \
+ led-back.svg \
+ off.svg \
+ on.svg \
+ highlight.svg
+
+gsettings_in_file = org.gnome.lightsoff.gschema.xml.in
+gsettings_SCHEMAS = $(gsettings_in_file:.xml.in=.xml)
+ INTLTOOL_XML_NOMERGE_RULE@
+ GSETTINGS_RULES@
+
+desktop_in_files = lightsoff.desktop.in.in
+desktopdir = $(datadir)/applications
+desktop_DATA = $(desktop_in_files:.desktop.in.in=.desktop)
+ INTLTOOL_DESKTOP_RULE@
+
+CLEANFILES = $(desktop_DATA) $(schema_DATA)
+DISTCLEANFILES = $(desktop_DATA) $(schema_DATA)
+
+EXTRA_DIST = \
+ $(lightsoff_DATA) \
+ $(schema_in_files)
+
+install-schemas-local: $(schema_DATA)
+if GCONF_SCHEMAS_INSTALL
+ if test -z "$(DESTDIR)" ; then \
+ for p in $^ ; do \
+ GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-install-rule $$p 2>&1 > /dev/null; \
+ done \
+ fi
+endif
+
+install-data-local: install-schemas-local
+
+ INTLTOOL_SCHEMAS_RULE@
+
+-include $(top_srcdir)/git.mk
diff --git a/lightsoff/data/themes/tango/arrow.svg b/lightsoff/data/arrow.svg
similarity index 100%
rename from lightsoff/data/themes/tango/arrow.svg
rename to lightsoff/data/arrow.svg
diff --git a/lightsoff/data/themes/tango/backing.svg b/lightsoff/data/backing.svg
similarity index 100%
rename from lightsoff/data/themes/tango/backing.svg
rename to lightsoff/data/backing.svg
diff --git a/lightsoff/data/themes/tango/highlight.svg b/lightsoff/data/highlight.svg
similarity index 100%
rename from lightsoff/data/themes/tango/highlight.svg
rename to lightsoff/data/highlight.svg
diff --git a/lightsoff/data/themes/tango/led-back.svg b/lightsoff/data/led-back.svg
similarity index 100%
rename from lightsoff/data/themes/tango/led-back.svg
rename to lightsoff/data/led-back.svg
diff --git a/lightsoff/lightsoff.desktop.in.in b/lightsoff/data/lightsoff.desktop.in.in
similarity index 100%
rename from lightsoff/lightsoff.desktop.in.in
rename to lightsoff/data/lightsoff.desktop.in.in
diff --git a/lightsoff/data/lightsoff.ui b/lightsoff/data/lightsoff.ui
index c9c8b7e..c4cfb7d 100644
--- a/lightsoff/data/lightsoff.ui
+++ b/lightsoff/data/lightsoff.ui
@@ -25,7 +25,7 @@
<property name="visible">True</property>
<child>
<object class="GtkImageMenuItem" id="new_game_item">
- <signal name="activate" handler="reset_score"/>
+ <signal name="activate" handler="new_game_cb"/>
<property name="label">games-new-game</property>
<property name="visible">True</property>
<property name="use_underline">True</property>
@@ -34,21 +34,11 @@
</object>
</child>
<child>
- <object class="GtkImageMenuItem" id="show_preferences_item">
- <signal name="activate" handler="show_settings"/>
- <property name="label">gtk-preferences</property>
- <property name="visible">True</property>
- <property name="use_underline">True</property>
- <property name="use_stock">True</property>
- <property name="accel_group">accel_group</property>
- </object>
- </child>
- <child>
<object class="GtkSeparatorMenuItem" id="separator1" />
</child>
<child>
<object class="GtkImageMenuItem" id="quit_item">
- <signal name="activate" handler="quit"/>
+ <signal name="activate" handler="quit_cb"/>
<property name="label">gtk-quit</property>
<property name="visible">True</property>
<property name="use_underline">True</property>
@@ -70,7 +60,7 @@
<property name="visible">True</property>
<child>
<object class="GtkImageMenuItem" id="show_help_item">
- <signal name="activate" handler="show_help"/>
+ <signal name="activate" handler="help_cb"/>
<property name="label">games-contents</property>
<property name="visible">True</property>
<property name="use_underline">True</property>
@@ -80,7 +70,7 @@
</child>
<child>
<object class="GtkImageMenuItem" id="show_about_item">
- <signal name="activate" handler="show_about"/>
+ <signal name="activate" handler="about_cb"/>
<property name="label">gtk-about</property>
<property name="visible">True</property>
<property name="use_underline">True</property>
diff --git a/lightsoff/data/themes/tango/off.svg b/lightsoff/data/off.svg
similarity index 100%
rename from lightsoff/data/themes/tango/off.svg
rename to lightsoff/data/off.svg
diff --git a/lightsoff/data/themes/tango/on.svg b/lightsoff/data/on.svg
similarity index 100%
rename from lightsoff/data/themes/tango/on.svg
rename to lightsoff/data/on.svg
diff --git a/lightsoff/data/org.gnome.lightsoff.gschema.xml.in b/lightsoff/data/org.gnome.lightsoff.gschema.xml.in
new file mode 100644
index 0000000..b29fe1a
--- /dev/null
+++ b/lightsoff/data/org.gnome.lightsoff.gschema.xml.in
@@ -0,0 +1,9 @@
+<schemalist>
+ <schema id="org.gnome.lightsoff" path="/org/gnome/lightsoff/">
+ <key name="level" type="i">
+ <default>1</default>
+ <_summary>The current level</_summary>
+ <_description>The users's most recent level.</_description>
+ </key>
+ </schema>
+</schemalist>
diff --git a/lightsoff/src/Makefile.am b/lightsoff/src/Makefile.am
index 6fdcb91..f906233 100644
--- a/lightsoff/src/Makefile.am
+++ b/lightsoff/src/Makefile.am
@@ -1,47 +1,34 @@
-lightsoffdir = $(pkgdatadir)/lightsoff
-
-lightsoff_DATA = \
- About.js \
- Arrow.js \
- Board.js \
- Light.js \
- main.js \
- Path.js \
- Game.js \
- LED.js \
- Puzzle.js \
- Settings.js \
- ThemeLoader.js
-
-bin_SCRIPTS = \
- lightsoff
-
-lightsoff: lightsoff.in Makefile
- $(AM_V_GEN) $(SED) -e "s|%pkglibdir%|$(pkglibdir)|" -e "s|%pkgdatadir%|$(pkgdatadir)|" $< > $@
-
-Path.js: Path.js.in
- $(AM_V_GEN) $(SED) -e "s|%pkgdatadir%|$(pkgdatadir)|" $< > $@
-
-EXTRA_DIST = \
- lightsoff.in \
- About.js \
- Arrow.js \
- Board.js \
- Light.js \
- main.js \
- Path.js.in \
- Game.js \
- LED.js \
- Puzzle.js \
- Settings.js \
- ThemeLoader.js
-
-CLEANFILES = \
- lightsoff \
- Path.js
-
-DISTCLEANFILES = \
- lightsoff \
- Path.js
+bin_PROGRAMS = lightsoff
+
+lightsoff_SOURCES = \
+ board-view.vala \
+ config.vapi \
+ fixes.vapi \
+ lightsoff.vala \
+ led-array.vala \
+ puzzle-generator.vala \
+ game-view.vala
+
+lightsoff_VALAFLAGS = \
+ --pkg posix \
+ --pkg gmodule-2.0 \
+ --pkg clutter-gtk-1.0 \
+ --vapidir $(top_srcdir)/libgames-support \
+ --pkg GnomeGamesSupport-1.0
+
+lightsoff_CFLAGS = \
+ -I$(top_srcdir)/libgames-support \
+ -DVERSION=\"$(VERSION)\" \
+ -DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\" \
+ $(GMODULE_CFLAGS) \
+ $(GTK_CFLAGS) \
+ $(CLUTTER_GTK_CFLAGS)
+
+lightsoff_LDADD = \
+ $(top_builddir)/libgames-support/libgames-support.la \
+ $(GMODULE_LIBS) \
+ $(GTK_LIBS) \
+ $(CLUTTER_GTK_LIBS) \
+ $(INTLLIBS)
-include $(top_srcdir)/git.mk
diff --git a/lightsoff/src/board-view.vala b/lightsoff/src/board-view.vala
new file mode 100644
index 0000000..9a0eedb
--- /dev/null
+++ b/lightsoff/src/board-view.vala
@@ -0,0 +1,238 @@
+private class Light : Clutter.Group
+{
+ private Clutter.Actor off;
+ private Clutter.Actor on;
+
+ private bool _is_lit;
+ public bool is_lit
+ {
+ get { return _is_lit; }
+ set
+ {
+ value = value != false;
+ if (value != _is_lit)
+ toggle ();
+ }
+ }
+
+ public Light (Clutter.Actor off_actor, Clutter.Actor on_actor)
+ {
+ set_scale (0.9, 0.9);
+
+ off = new Clutter.Clone (off_actor);
+ off.anchor_gravity = Clutter.Gravity.CENTER;
+ add_actor (off);
+
+ on = new Clutter.Clone (on_actor);
+ on.anchor_gravity = Clutter.Gravity.CENTER;
+ on.opacity = 0;
+ add_actor (on);
+
+ // Add a 2 px margin around the tile image, center tiles within it.
+ width += 4;
+ height += 4;
+ off.set_position (width / 2, height / 2);
+ on.set_position (width / 2, height / 2);
+ }
+
+ public void toggle (Clutter.Timeline? timeline = null)
+ {
+ _is_lit = !_is_lit;
+
+ if (timeline != null)
+ {
+ // Animate the opacity of the 'off' tile to match the state.
+ off.animate_with_timeline (Clutter.AnimationMode.EASE_OUT_SINE, timeline, "opacity", is_lit ? 0 : 255);
+ on.animate_with_timeline (Clutter.AnimationMode.EASE_OUT_SINE, timeline, "opacity", is_lit ? 255 : 0);
+
+ // Animate the tile to be smaller when in the 'off' state.
+ animate_with_timeline (Clutter.AnimationMode.EASE_OUT_SINE, timeline,
+ "scale-x", is_lit ? 1.0 : 0.9,
+ "scale-y", is_lit ? 1.0 : 0.9);
+ }
+ else
+ {
+ off.opacity = is_lit ? 0 : 255;
+ on.opacity = is_lit ? 255 : 0;
+ scale_x = is_lit ? 1 : 0.9;
+ scale_y = is_lit ? 1 : 0.9;
+ }
+ }
+}
+
+public class BoardView : Clutter.Group
+{
+ private const int size = 5;
+ private PuzzleGenerator puzzle_generator;
+ private Clutter.Texture off_texture;
+ private Clutter.Texture on_texture;
+ private Light[,] lights;
+
+ public bool playable = true;
+
+ public signal void game_won ();
+
+ public BoardView (Clutter.Texture off_texture, Clutter.Texture on_texture)
+ {
+ this.off_texture = off_texture;
+ this.on_texture = on_texture;
+ puzzle_generator = new PuzzleGenerator (size);
+ lights = new Light [size, size];
+ for (var x = 0; x < size; x++)
+ {
+ for (var y = 0; y < size; y++)
+ {
+ var l = new Light (off_texture, on_texture);
+
+ l.reactive = true;
+ l.button_press_event.connect (light_button_press_cb);
+
+ float xx, yy;
+ get_light_position (x, y, out xx, out yy);
+ l.anchor_gravity = Clutter.Gravity.CENTER;
+ l.set_position (xx, yy);
+
+ lights[x, y] = l;
+ add_actor (l);
+ }
+ }
+ }
+
+ public void get_light_position (int x, int y, out float xx, out float yy)
+ {
+ // All lights need to be shifted down and right by half a light,
+ // as lights have center gravity.
+ xx = (x + 0.5f) * off_texture.width + 2;
+ yy = (y + 0.5f) * off_texture.height + 2;
+ }
+
+ public void fade_in (Clutter.Timeline timeline)
+ {
+ animate_with_timeline (Clutter.AnimationMode.EASE_OUT_SINE, timeline, "opactity", 0);
+ }
+
+ public void fade_out (Clutter.Timeline timeline)
+ {
+ animate_with_timeline (Clutter.AnimationMode.EASE_OUT_SINE, timeline, "opactity", 255);
+ }
+
+ public void slide_in (int direction, int sign, Clutter.Timeline timeline)
+ {
+ /* Place offscreen */
+ x = -sign * direction * width;
+ y = -sign * (1 - direction) * height;
+
+ /* Slide onscreen */
+ animate_with_timeline (Clutter.AnimationMode.EASE_OUT_BOUNCE, timeline, "x", 0.0, "y", 0.0);
+ }
+
+ public void slide_out (int direction, int sign, Clutter.Timeline timeline)
+ {
+ /* Slide offscreen */
+ animate_with_timeline (Clutter.AnimationMode.EASE_OUT_BOUNCE, timeline,
+ "x", sign * direction * width,
+ "y", sign * (1 - direction) * height);
+ }
+
+ public void swap_in (float direction, Clutter.Timeline timeline)
+ {
+ /* Bring into foreground and make visible */
+ animate_with_timeline (Clutter.AnimationMode.EASE_IN_SINE, timeline,
+ "opacity", 255,
+ "depth", 0.0);
+ }
+
+ public void swap_out (float direction, Clutter.Timeline timeline)
+ {
+ /* Fade into background or drop down */
+ animate_with_timeline (Clutter.AnimationMode.EASE_IN_SINE, timeline,
+ "depth", 250.0 * direction,
+ "opacity", 0);
+ }
+
+ private void find_light (Light light, out int x, out int y)
+ {
+ x = y = 0;
+ for (x = 0; x < size; x++)
+ for (y = 0; y < size; y++)
+ if (lights[x, y] == light)
+ return;
+ }
+
+ private bool light_button_press_cb (Clutter.Actor actor, Clutter.ButtonEvent event)
+ {
+ int x, y;
+ find_light ((Light) actor, out x, out y);
+ toggle_light (x, y);
+ return false;
+ }
+
+ // Toggle a light and those in each cardinal direction around it.
+ public void toggle_light (int x, int y, bool animate = true)
+ {
+ if (!playable)
+ return;
+
+ Clutter.Timeline? timeline = null;
+ if (animate)
+ {
+ timeline = new Clutter.Timeline (300);
+ timeline.completed.connect (toggle_completed_cb);
+ }
+
+ if ((int) x + 1 < size)
+ lights[(int) x + 1, (int) y].toggle (timeline);
+ if ((int) x - 1 >= 0)
+ lights[(int) x - 1, (int) y].toggle (timeline);
+ if ((int) y + 1 < size)
+ lights[(int) x, (int) y + 1].toggle (timeline);
+ if ((int) y - 1 >= 0)
+ lights[(int) x, (int) y - 1].toggle (timeline);
+
+ lights[(int) x, (int) y].toggle (timeline);
+
+ if (animate)
+ timeline.start ();
+ }
+
+ private void toggle_completed_cb ()
+ {
+ var cleared = true;
+ for (var x = 0; x < size; x++)
+ for (var y = 0; y < size; y++)
+ if (lights[x, y].is_lit)
+ cleared = false;
+
+ if (cleared)
+ game_won ();
+ }
+
+ // Pseudorandomly generates and sets the state of each light based on
+ // a level number; hopefully this is stable between machines, but that
+ // depends on GLib's PRNG stability. Also, provides some semblance of
+ // symmetry for some levels.
+ public void load_level (int level)
+ {
+ /* We *must* not have level < 1, as the following assumes a nonzero, nonnegative number */
+ if (level < 1)
+ level = 1;
+
+ /* Clear level */
+ for (var x = 0; x < size; x++)
+ for (var y = 0; y < size; y++)
+ lights[x, y].is_lit = false;
+
+ /* Use the same pseudo-random levels */
+ Random.set_seed (level);
+
+ /* Levels require more and more clicks to make */
+ var solution_length = (int) Math.floor (2 * Math.log (level) + 1);
+
+ /* Do the moves the player needs to */
+ var sol = puzzle_generator.minimal_solution (solution_length);
+ for (var x = 0; x < size; x++)
+ for (var y = 0; y < size; y++)
+ if (sol[x, y])
+ toggle_light (x, y, false);
+ }
+}
diff --git a/lightsoff/src/config.vapi b/lightsoff/src/config.vapi
new file mode 100644
index 0000000..6477226
--- /dev/null
+++ b/lightsoff/src/config.vapi
@@ -0,0 +1,2 @@
+public const string VERSION;
+public const string GETTEXT_PACKAGE;
\ No newline at end of file
diff --git a/lightsoff/src/fixes.vapi b/lightsoff/src/fixes.vapi
new file mode 100644
index 0000000..7f05acb
--- /dev/null
+++ b/lightsoff/src/fixes.vapi
@@ -0,0 +1,9 @@
+namespace Clutter
+{
+ public const int KEY_Up;
+ public const int KEY_Down;
+ public const int KEY_Left;
+ public const int KEY_Right;
+ public const int KEY_Return;
+ public const int KEY_Escape;
+}
diff --git a/lightsoff/src/game-view.vala b/lightsoff/src/game-view.vala
new file mode 100644
index 0000000..fce0ee4
--- /dev/null
+++ b/lightsoff/src/game-view.vala
@@ -0,0 +1,276 @@
+public class GameView : Clutter.Group
+{
+ private Clutter.Texture backing_texture;
+ private Clutter.Texture highlight_texture;
+ private Clutter.Texture off_texture;
+ private Clutter.Texture on_texture;
+ private Clutter.Texture led_back_texture;
+ private Clutter.Texture arrow_texture;
+
+ private int current_level;
+
+ private List<Clutter.Actor> actor_remove_queue = null;
+
+ private LEDArray score_view;
+ private BoardView board_view;
+ private BoardView? new_board_view = null;
+ private Clutter.Actor backing_view;
+ private Clutter.Actor left_arrow;
+ private Clutter.Actor right_arrow;
+ private Clutter.Actor key_cursor_view;
+
+ private Clutter.Timeline timeline;
+ private int key_cursor_x = 0;
+ private int key_cursor_y = 0;
+ private bool key_cursor_ready = false;
+
+ private int last_direction = 0;
+
+ private int last_sign = 0;
+
+ public signal void level_changed (int level);
+
+ public GameView (int level)
+ {
+ try
+ {
+ backing_texture = new Clutter.Texture.from_file ("data/backing.svg");
+ highlight_texture = new Clutter.Texture.from_file ("data/highlight.svg");
+ off_texture = new Clutter.Texture.from_file ("data/off.svg");
+ on_texture = new Clutter.Texture.from_file ("data/on.svg");
+ led_back_texture = new Clutter.Texture.from_file ("data/led-back.svg");
+ arrow_texture = new Clutter.Texture.from_file ("data/arrow.svg");
+ }
+ catch (Clutter.TextureError e)
+ {
+ warning ("Failed to load textures: %s", e.message);
+ }
+
+ /* Add textures onto the scene so they can be cloned */
+ backing_texture.hide ();
+ add_actor (backing_texture);
+ highlight_texture.hide ();
+ add_actor (highlight_texture);
+ off_texture.hide ();
+ add_actor (off_texture);
+ on_texture.hide ();
+ add_actor (on_texture);
+ led_back_texture.hide ();
+ add_actor (led_back_texture);
+ arrow_texture.hide ();
+ add_actor (arrow_texture);
+
+ var real_board_width = 5 * off_texture.width + 4;
+ var real_board_height = 5 * off_texture.height + 4;
+
+ current_level = level;
+ board_view = create_board_view (current_level);
+ board_view.playable = true;
+ add_actor (board_view);
+
+ backing_view = new Clutter.Clone (backing_texture);
+ backing_view.set_position (0, real_board_height);
+ add_actor (backing_view);
+
+ score_view = new LEDArray (5, led_back_texture);
+ score_view.value = current_level;
+ score_view.set_anchor_point (score_view.width / 2, 0);
+ score_view.set_position (real_board_width / 2, real_board_height + 18);
+ add_actor (score_view);
+
+ set_size (real_board_width, score_view.y + score_view.height);
+
+ left_arrow = new Clutter.Clone (arrow_texture);
+ left_arrow.anchor_gravity = Clutter.Gravity.CENTER;
+ left_arrow.reactive = true;
+ left_arrow.button_release_event.connect (left_arrow_button_release_cb);
+ left_arrow.set_position ((score_view.x - score_view.anchor_x) / 2, score_view.y + (score_view.height / 2) - 10);
+ add_actor (left_arrow);
+
+ right_arrow = new Clutter.Clone (arrow_texture);
+ right_arrow.anchor_gravity = Clutter.Gravity.CENTER;
+ right_arrow.reactive = true;
+ right_arrow.button_release_event.connect (right_arrow_button_release_cb);
+ right_arrow.rotation_angle_y = 180;
+ right_arrow.set_position (real_board_width - left_arrow.x, score_view.y + (score_view.height / 2) - 10);
+ add_actor (right_arrow);
+
+ key_cursor_view = new Clutter.Clone (highlight_texture);
+ key_cursor_view.set_position (-100, -100);
+ key_cursor_view.anchor_gravity = Clutter.Gravity.CENTER;
+ add_actor (key_cursor_view);
+ }
+
+ private BoardView create_board_view (int level)
+ {
+ var view = new BoardView (off_texture, on_texture);
+ view.load_level (level);
+ view.game_won.connect (game_won_cb);
+ view.playable = false;
+
+ return view;
+ }
+
+ // The boards have finished transitioning; delete the old one!
+ private void transition_complete_cb ()
+ {
+ remove_actor (board_view);
+ board_view = new_board_view;
+ board_view.playable = true;
+ key_cursor_view.raise_top ();
+ new_board_view = null;
+ timeline = null;
+
+ // Remove all of the queued-for-removal actors
+ foreach (var actor in actor_remove_queue)
+ {
+ if (actor.get_parent () == null)
+ continue;
+ var group = (Clutter.Group) actor.get_parent ();
+ group.remove_actor (actor);
+ }
+ actor_remove_queue = null;
+ }
+
+ // The player won the game; create a new board, update the level count,
+ // and transition between the two boards in a random direction.
+ private void game_won_cb ()
+ {
+ if (timeline != null && timeline.is_playing ())
+ return;
+
+ current_level++;
+ score_view.value = current_level;
+
+ // Make sure the board transition is different than the previous.
+ var direction = 0;
+ var sign = 0;
+ do
+ {
+ direction = Random.int_range (0, 2); // x or y
+ sign = Random.boolean () ? 1 : -1; // left/right up/down
+ }
+ while (last_direction == direction || last_sign == sign);
+ last_direction = direction;
+ last_sign = sign;
+
+ new_board_view = create_board_view (current_level);
+ add_actor (new_board_view);
+ new_board_view.lower (board_view);
+
+ timeline = new Clutter.Timeline (1500);
+ new_board_view.slide_in (direction, sign, timeline);
+ board_view.slide_out (direction, sign, timeline);
+ timeline.completed.connect (transition_complete_cb);
+
+ level_changed (current_level);
+ }
+
+ private bool left_arrow_button_release_cb (Clutter.Actor actor, Clutter.ButtonEvent event)
+ {
+ swap_board (-1);
+ return false;
+ }
+
+ private bool right_arrow_button_release_cb (Clutter.Actor actor, Clutter.ButtonEvent event)
+ {
+ swap_board (1);
+ return false;
+ }
+
+ // The player asked to swap to a different level without completing
+ // the one in progress; this can occur either by clicking an arrow
+ // or by requesting a new game from the menu. Animate the new board
+ // in, depthwise, in the direction indicated by 'context'.
+ private void swap_board (int direction)
+ {
+ if (timeline != null && timeline.is_playing ())
+ return;
+
+ current_level += direction;
+ if (current_level <= 0)
+ {
+ current_level = 1;
+ return;
+ }
+
+ score_view.value = current_level;
+
+ timeline = new Clutter.Timeline (500);
+
+ new_board_view = create_board_view (current_level);
+ add_actor (new_board_view);
+ new_board_view.lower (board_view);
+ new_board_view.depth = -250 * direction;
+ new_board_view.opacity = 0;
+
+ new_board_view.swap_in (direction, timeline);
+ board_view.swap_out (direction, timeline);
+ timeline.completed.connect (transition_complete_cb);
+
+ level_changed (current_level);
+ }
+
+ public void hide_cursor ()
+ {
+ key_cursor_view.raise_top ();
+ key_cursor_view.animate (Clutter.AnimationMode.EASE_OUT_SINE, 250, "opacity", 0);
+ key_cursor_ready = false;
+ }
+
+ public void move_cursor (int x_step, int y_step)
+ {
+ if (key_cursor_ready)
+ {
+ key_cursor_x += x_step;
+ key_cursor_y += y_step;
+ key_cursor_x = int.max (key_cursor_x, 0);
+ key_cursor_x = int.min (key_cursor_x, 4); // FIXME: Get the size from the model
+ key_cursor_y = int.max (key_cursor_y, 0);
+ key_cursor_y = int.min (key_cursor_y, 4);
+ }
+
+ float x, y;
+ board_view.get_light_position (key_cursor_x, key_cursor_y, out x, out y);
+
+ if (key_cursor_ready)
+ key_cursor_view.animate (Clutter.AnimationMode.EASE_OUT_SINE, 250, "x", x, "y", y);
+ else
+ {
+ key_cursor_view.opacity = 0;
+ key_cursor_view.set_position (x, y);
+ key_cursor_view.animate (Clutter.AnimationMode.EASE_OUT_SINE, 250, "opacity", 255);
+ }
+
+ key_cursor_ready = true;
+ }
+
+ public void activate_cursor ()
+ {
+ if (key_cursor_ready)
+ board_view.toggle_light (key_cursor_x, key_cursor_y);
+ }
+
+ public void reset_game ()
+ {
+ if (timeline != null && timeline.is_playing ())
+ return;
+
+ current_level = 1;
+ score_view.value = current_level;
+
+ timeline = new Clutter.Timeline (500);
+
+ new_board_view = create_board_view (current_level);
+ add_actor (new_board_view);
+ new_board_view.lower (board_view);
+ new_board_view.depth = 250;
+ new_board_view.opacity = 0;
+
+ new_board_view.swap_in (-1, timeline);
+ board_view.swap_out (-1, timeline);
+ timeline.completed.connect (transition_complete_cb);
+
+ level_changed (current_level);
+ }
+}
diff --git a/lightsoff/src/led-array.vala b/lightsoff/src/led-array.vala
new file mode 100644
index 0000000..72e7951
--- /dev/null
+++ b/lightsoff/src/led-array.vala
@@ -0,0 +1,133 @@
+public class LEDDigit : Clutter.CairoTexture
+{
+ private const int scale = 23;
+
+ private int _value = 0;
+ public int value
+ {
+ get { return _value; }
+ set { _value = value; invalidate (); }
+ }
+
+ private const bool segment_states[] =
+ {
+ true, true, true, true, true, false, true,
+ false, false, false, false, true, false, true,
+ true, false, true, true, false, true, true,
+ true, false, false, true, true, true, true,
+ false, true, false, false, true, true, true,
+ true, true, false, true, true, true, false,
+ true, true, true, true, true, true, false,
+ true, false, false, false, true, false, true,
+ true, true, true, true, true, true, true,
+ true, true, false, true, true, true, true
+ };
+
+ public LEDDigit ()
+ {
+ set_surface_size (37, 65);
+ invalidate ();
+ }
+
+ public override bool draw (Cairo.Context cr)
+ {
+ cr.set_operator (Cairo.Operator.CLEAR);
+ cr.paint ();
+ cr.set_operator (Cairo.Operator.OVER);
+
+ var thickness = scale / 3;
+ var pointy = thickness / 2;
+ var margin = Math.floor (Math.log (scale));
+ var side = pointy + margin;
+
+ var offset = value * 7;
+ draw_segment (cr, side, 0, false, segment_states[offset]);
+ draw_segment (cr, 0, side, true, segment_states[offset + 1]);
+ draw_segment (cr, 0, side + scale + margin * 2, true, segment_states[offset + 2]);
+ draw_segment (cr, side, 2 * scale + 4 * margin, false, segment_states[offset + 3]);
+ draw_segment (cr, scale + 2 * margin, side + scale + margin * 2, true, segment_states[offset + 4]);
+ draw_segment (cr, side, scale + 2 * margin, false, segment_states[offset + 5]);
+ draw_segment (cr, scale + 2 * margin, side, true, segment_states[offset + 6]);
+
+ return false;
+ }
+
+ private void draw_segment (Cairo.Context cr, double x, double y, bool is_vertical, bool is_lit)
+ {
+ if (is_lit)
+ cr.set_source_rgba (0.145, 0.541, 1, 1);
+ else
+ cr.set_source_rgba (0.2, 0.2, 0.2, 1);
+
+ cr.new_path ();
+
+ var thickness = scale / 3;
+ var pointy = thickness / 2;
+
+ if (is_vertical)
+ {
+ cr.move_to (x + thickness / 2, y + 0);
+ cr.line_to (x + 0, y + pointy);
+ cr.line_to (x + 0, y + scale - pointy);
+ cr.line_to (x + thickness / 2, y + scale);
+ cr.line_to (x + thickness, y + scale - pointy);
+ cr.line_to (x + thickness, y + pointy);
+ }
+ else
+ {
+ cr.move_to (x + 0, y + thickness / 2);
+ cr.line_to (x + pointy, y + 0);
+ cr.line_to (x + scale - pointy, y + 0);
+ cr.line_to (x + scale, y + thickness / 2);
+ cr.line_to (x + scale - pointy, y + thickness);
+ cr.line_to (x + pointy, y + thickness);
+ }
+
+ cr.close_path ();
+ cr.fill ();
+ }
+}
+
+public class LEDArray : Clutter.Group
+{
+ private List<LEDDigit> digits = null;
+ private Clutter.Actor back;
+
+ private int _value = 0;
+ public int value
+ {
+ get { return _value; }
+ set
+ {
+ _value = value;
+ var d_val = value;
+ foreach (var d in digits)
+ {
+ d.value = (int) Math.floor (d_val % 10);
+ d_val /= 10;
+ }
+ }
+ }
+
+ public LEDArray (int n_digits, Clutter.Actor back_texture)
+ {
+ var margin = 4;
+ var inner_x_margin = 10;
+ var inner_y_margin = -1;
+
+ back = new Clutter.Clone (back_texture);
+ add_actor (back);
+
+ for (var i = 0; i < n_digits; i++)
+ {
+ var d = new LEDDigit ();
+ d.set_anchor_point (0, d.height / 2);
+ d.x = i * (d.width + margin) + inner_x_margin;
+ d.y = back.height / 2 + inner_y_margin;
+ add_actor (d);
+ digits.prepend (d);
+ }
+
+ back.lower_bottom ();
+ }
+}
diff --git a/lightsoff/src/lightsoff.vala b/lightsoff/src/lightsoff.vala
new file mode 100644
index 0000000..a75c81b
--- /dev/null
+++ b/lightsoff/src/lightsoff.vala
@@ -0,0 +1,166 @@
+public class LightsOff
+{
+ private Settings settings;
+ private Gtk.Builder ui;
+ private Gtk.Window window;
+ private GameView game_view;
+
+ private LightsOff () throws Error
+ {
+ settings = new Settings ("org.gnome.lightsoff");
+
+ ui = new Gtk.Builder();
+ ui.add_from_file ("data/lightsoff.ui");
+ ui.connect_signals (this);
+
+ window = (Gtk.Window) ui.get_object ("game_window");
+ window.hide.connect (Gtk.main_quit);
+
+ var box = (Gtk.Box) ui.get_object ("game_vbox");
+
+ var clutter_embed = new GtkClutter.Embed ();
+ clutter_embed.show ();
+ box.pack_start (clutter_embed, true, true);
+
+ var stage = (Clutter.Stage) clutter_embed.get_stage ();
+ stage.key_release_event.connect (key_release_event_cb);
+ stage.color = Clutter.Color.from_string ("#000000");
+ stage.use_fog = false;
+
+ game_view = new GameView (settings.get_int ("level"));
+ game_view.level_changed.connect (level_changed_cb);
+ game_view.show ();
+ stage.add_actor (game_view);
+
+ stage.set_size (game_view.width, game_view.height);
+ clutter_embed.set_size_request ((int) stage.width, (int) stage.height);
+ }
+
+ private void level_changed_cb (int level)
+ {
+ settings.set_int ("level", level);
+ }
+
+ private bool key_release_event_cb (Clutter.Actor actor, Clutter.KeyEvent event)
+ {
+ switch (event.keyval)
+ {
+ case Clutter.KEY_Escape:
+ game_view.hide_cursor ();
+ return true;
+ case Clutter.KEY_Down:
+ game_view.move_cursor (0, 1);
+ return true;
+ case Clutter.KEY_Up:
+ game_view.move_cursor (0, -1);
+ return true;
+ case Clutter.KEY_Left:
+ game_view.move_cursor (-1, 0);
+ return true;
+ case Clutter.KEY_Right:
+ game_view.move_cursor (1, 0);
+ return true;
+ case Clutter.KEY_Return:
+ game_view.activate_cursor ();
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public void show ()
+ {
+ window.show ();
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT new_game_cb", instance_pos = -1)]
+ public void new_game_cb (Gtk.Widget widget)
+ {
+ game_view.reset_game();
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT quit_cb", instance_pos = -1)]
+ public void quit_cb (Gtk.Widget widget)
+ {
+ Gtk.main_quit ();
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT help_cb", instance_pos = -1)]
+ public void help_cb (Gtk.Widget widget)
+ {
+ GnomeGamesSupport.help_display (window, "lightsoff", null);
+ }
+
+ [CCode (cname = "G_MODULE_EXPORT about_cb", instance_pos = -1)]
+ public void about_cb (Gtk.Widget widget)
+ {
+ string[] authors =
+ {
+ "Tim Horton",
+ "Robert Ancell",
+ null
+ };
+
+ string[] artists =
+ {
+ "Tim Horton",
+ "Ulisse Perusin",
+ null
+ };
+
+ string[] documenters =
+ {
+ "Eric Baudais",
+ null
+ };
+
+ Gtk.show_about_dialog (window,
+ "program-name", _("Lights Off"),
+ "version", VERSION,
+ "comments",
+ _("Turn off all the lights\n\nLights Off is a part of GNOME Games."),
+ "copyright", "Copyright \xa9 2009 Tim Horton",
+ "license", GnomeGamesSupport.get_license (_("Lights Off")),
+ "wrap-license", true,
+ "authors", authors,
+ "artists", artists,
+ "documenters", documenters,
+ "translator-credits", _("translator-credits"),
+ "logo-icon-name", "gnome-lightsoff",
+ "website", "http://www.gnome.org/projects/gnome-games",
+ "website-label", _("GNOME Games web site"),
+ null);
+ }
+
+ public static int main (string[] args)
+ {
+ Environment.set_prgname ("lightsoff");
+
+ if (GtkClutter.init (ref args) != Clutter.InitError.SUCCESS)
+ {
+ warning ("Failed to initialise Clutter");
+ return Posix.EXIT_FAILURE;
+ }
+
+ GnomeGamesSupport.runtime_init ("lightsoff");
+ GnomeGamesSupport.stock_init ();
+
+ LightsOff app;
+ try
+ {
+ app = new LightsOff ();
+ app.show ();
+ }
+ catch (Error e)
+ {
+ warning ("Failed to create application: %s", e.message);
+ return Posix.EXIT_FAILURE;
+ }
+
+ Gtk.main ();
+
+ GnomeGamesSupport.runtime_shutdown();
+
+ return Posix.EXIT_SUCCESS;
+ }
+}
diff --git a/lightsoff/src/puzzle-generator.vala b/lightsoff/src/puzzle-generator.vala
new file mode 100644
index 0000000..6103d59
--- /dev/null
+++ b/lightsoff/src/puzzle-generator.vala
@@ -0,0 +1,198 @@
+// Puzzle generation logic:
+// We want to measure a puzzle's difficulty by the number of button presses
+// needed to solve it, and have that increase in a controlled manner.
+//
+// Lights Off can be seen as a linear algebra problem over the field GF(2).
+// (See e.g. the first page of "Turning Lights Out with Linear Algebra".)
+// The linear map from button-press strategies to light configurations
+// will in general have a nullspace (of dimension n).
+// Thus, any solvable puzzle has 2^n solutions, given by a fixed solution
+// plus an element of the nullspace.
+// A solution is thus optimal if it presses at most half the buttons
+// which make up any element of the nullspace.
+//
+// A basis for the nullspace splits the board into (up to) 2^n regions,
+// defined by which basic nullspace elements include a given button.
+// (This determines which nullspace elements include the same button.)
+// Thus, a solution is optimal if it includes at most half the buttons in any
+// region (except the region lying outside all null-sets).
+// The converse is not true, but (at least if the regions are even-sized)
+// does hold for a large number of puzzles up to the highest difficulty level.
+// (Certainly, on average, at most half the lights which belong to at least
+// one null set may be used.)
+
+public class PuzzleGenerator
+{
+ private int size;
+ private int max_solution_length;
+ private int[] region_of;
+ private int[] region_size;
+
+ public PuzzleGenerator (int size)
+ {
+ this.size = size;
+ var adj_matrix = new int[size * size, size * size];
+ for (var x0 = 0; x0 < size; x0++)
+ {
+ for (var y0 = 0; y0 < size; y0++)
+ {
+ for (var x1 = 0; x1 < size; x1++)
+ {
+ for (var y1 = 0; y1 < size; y1++)
+ {
+ var dx = x0 - x1;
+ var dy = y0 - y1;
+ adj_matrix[x0 * size + y0, x1 * size + y1] = dx*dx + dy*dy <= 1 ? 1 : 0;
+ }
+ }
+ }
+ }
+
+ // Row-reduction over field with two elements
+ List<int> non_pivot_cols = null;
+ var ipiv = 0;
+ for (var jpiv = 0; jpiv < size * size; jpiv++)
+ {
+ var is_pivot_col = false;
+ for (var i = ipiv; i < size * size; i++)
+ {
+ if (adj_matrix[i, jpiv] != 0)
+ {
+ /* Swap rows */
+ if (i != ipiv)
+ {
+ for (var z = 0; z < size * size; z++)
+ {
+ var t = adj_matrix[i, z];
+ adj_matrix[i, z] = adj_matrix[ipiv, z];
+ adj_matrix[ipiv, z] = t;
+ }
+ }
+
+ for (var j = ipiv+1; j < size * size; j++)
+ {
+ if (adj_matrix[j, jpiv] != 0)
+ {
+ for (var k = 0; k < size * size; k++)
+ adj_matrix[j, k] ^= adj_matrix[ipiv, k];
+ }
+ }
+ is_pivot_col = true;
+ ipiv++;
+ break;
+ }
+ }
+
+ if (!is_pivot_col)
+ non_pivot_cols.append (jpiv);
+ }
+
+ // Use back-substitution to solve Adj*x = 0, once with each
+ // free variable set to 1 (and the others to 0).
+ var basis_for_ns = new int[non_pivot_cols.length (), size * size];
+ var n = 0;
+ foreach (var col in non_pivot_cols)
+ {
+ for (var j = 0; j < size * size; j++)
+ basis_for_ns[n, j] = 0;
+ basis_for_ns[n, col] = 1;
+
+ for (var i = size * size - 1; i >= 0; i--)
+ {
+ var jpiv = 0;
+ for (; jpiv < size * size; jpiv++)
+ if (adj_matrix[i, jpiv] != 0)
+ break;
+ if (jpiv == size * size)
+ continue;
+ for (var j = jpiv + 1; j < size * size; j++)
+ basis_for_ns[n, jpiv] ^= adj_matrix[i, j] * basis_for_ns[n, j];
+ }
+
+ n++;
+ }
+
+ // A button's region # is a binary # with 1's in a place corresponding
+ // to any null-vector which contains it.
+ region_size = new int [1 << non_pivot_cols.length ()];
+ for (var j = 0; j < region_size.length; j++)
+ region_size[j] = 0;
+ region_of = new int[size * size];
+ for (var i = 0; i < size * size; i++)
+ {
+ region_of[i] = 0;
+ for (var j = 0; j < non_pivot_cols.length (); j++)
+ {
+ if (basis_for_ns[j, i] != 0)
+ region_of[i] += 1 << j;
+ }
+ region_size[region_of[i]]++;
+ }
+
+ max_solution_length = region_size[0];
+ for (var j = 1; j < region_size.length; j++)
+ max_solution_length += (int) Math.floor (region_size[j] / 2);
+ }
+
+ public bool[,] minimal_solution (int solution_length)
+ {
+ var sol = new bool[size, size];
+ for (var x = 0; x < size; x++)
+ for (var y = 0; y < size; y++)
+ sol[x, y] = false;
+
+ var presses_in_region = new int[region_size.length];
+ for (var i = 0; i < region_size.length; i++)
+ presses_in_region[i] = 0;
+
+ /* Note this should be Random.int_range (0, 3) but it is like this to match the old behaviour */
+ var sym = (int) Math.floor (3 * Random.next_double ());
+
+ var presses_left = int.min (solution_length, max_solution_length);
+ while (presses_left > 0)
+ {
+ int x[2], y[2];
+
+ // Pick a spot (x[0], y[0]), a corner if one is needed
+ /* Note this should be Random.int_range (0, size) but it is like this to match the old behaviour */
+ x[0] = (int) Math.round ((size - 1) * Random.next_double ());
+ y[0] = (int) Math.round ((size - 1) * Random.next_double ());
+
+ // Also pick a symmetric spot, to take if possible
+ if (sym == 0)
+ {
+ x[1] = size - 1 - x[0];
+ y[1] = y[0];
+ }
+ else if (sym == 1)
+ {
+ x[1] = size - 1 - x[0];
+ y[1] = size - 1 - y[0];
+ }
+ else
+ {
+ x[1] = x[0];
+ y[1] = size - 1 - y[0];
+ }
+
+ // Make each move if it doesn't fill a region more than halfway.
+ for (var k = 0; k < 2; k++)
+ {
+ var r = region_of[x[k] * size + y[k]];
+ if (r == 0 || 2 * (presses_in_region[r] + 1) <= region_size[r])
+ {
+ if (sol[x[k], y[k]])
+ continue;
+ sol[x[k], y[k]] = true;
+ presses_in_region[r]++;
+ presses_left--;
+ }
+ if (presses_left == 0)
+ break;
+ }
+ }
+
+ return sol;
+ }
+}
+
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 365dc46..e401801 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -113,10 +113,13 @@ libgames-support/games-show.c
libgames-support/games-sound.c
libgames-support/games-stock.c
[type: gettext/glade]lightsoff/data/lightsoff.ui
-[type: gettext/glade]lightsoff/data/settings.ui
-lightsoff/lightsoff.desktop.in.in
-lightsoff/lightsoff.schemas.in
-lightsoff/src/About.js
+lightsoff/data/lightsoff.desktop.in.in
+lightsoff/data/org.gnome.lightsoff.gschema.xml.in
+lightsoff/src/board-view.vala
+lightsoff/src/game-view.vala
+lightsoff/src/led-array.vala
+lightsoff/src/lightsoff.vala
+lightsoff/src/puzzle-generator.vala
mahjongg/data/mahjongg.desktop.in.in
mahjongg/data/org.gnome.mahjongg.gschema.xml.in
mahjongg/data/translatable_game_names.h
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]