[gnome-games] iagno: Port from C to Vala
- From: Robert Ancell <rancell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-games] iagno: Port from C to Vala
- Date: Thu, 29 Dec 2011 03:58:40 +0000 (UTC)
commit e9e8ae3e5b573cba9f535b5dceaf7bf98eabd126
Author: Robert Ancell <robert ancell canonical com>
Date: Thu Dec 22 18:26:15 2011 +1100
iagno: Port from C to Vala
configure.in | 4 +-
iagno/data/org.gnome.iagno.gschema.xml.in | 7 -
iagno/src/Makefile.am | 31 +-
iagno/src/computer-player.vala | 264 ++++++++++++
iagno/src/config.vapi | 2 +
iagno/src/game-view.vala | 239 +++++++++++
iagno/src/game.vala | 282 +++++++++++++
iagno/src/iagno.vala | 591 +++++++++++++++++++++++++++
libgames-support/GnomeGamesSupport-1.0.vapi | 22 +
po/POTFILES.in | 4 +-
10 files changed, 1419 insertions(+), 27 deletions(-)
---
diff --git a/configure.in b/configure.in
index 7ea6874..8d0d9d3 100644
--- a/configure.in
+++ b/configure.in
@@ -125,7 +125,7 @@ for game in $gamelist; do
*) ;;
esac
case $game in
- glchess|gnomine|gnotravex|lightsoff|mahjongg) need_vala=yes ;;
+ glchess|gnomine|gnotravex|iagno|lightsoff|mahjongg) need_vala=yes ;;
*) ;;
esac
case $game in
@@ -153,7 +153,7 @@ for game in $gamelist; do
*) ;;
esac
case $game in
- glines|gnobots2|gnotski|iagno) allow_smclient=yes ;;
+ glines|gnobots2|gnotski) allow_smclient=yes ;;
*) ;;
esac
case $game in
diff --git a/iagno/data/org.gnome.iagno.gschema.xml.in b/iagno/data/org.gnome.iagno.gschema.xml.in
index 7ab3f2f..13a674b 100644
--- a/iagno/data/org.gnome.iagno.gschema.xml.in
+++ b/iagno/data/org.gnome.iagno.gschema.xml.in
@@ -14,13 +14,6 @@
<key name="tileset" type="s">
<default>'classic.png'</default>
</key>
- <key name="animate" type="i">
- <default>2</default>
- <range min="0" max="2" />
- </key>
- <key name="animate-stagger" type="b">
- <default>false</default>
- </key>
<key name="show-grid" type="b">
<default>false</default>
</key>
diff --git a/iagno/src/Makefile.am b/iagno/src/Makefile.am
index 9473f10..95a3133 100644
--- a/iagno/src/Makefile.am
+++ b/iagno/src/Makefile.am
@@ -1,26 +1,27 @@
bin_PROGRAMS = iagno
iagno_SOURCES = \
- gnothello.c \
- gnothello.h \
- othello.c \
- othello.h \
- properties.c \
- properties.h \
- $(NULL)
-
-iagno_CPPFLAGS = \
- -I$(top_srcdir) \
- $(AM_CPPFLAGS)
+ config.vapi \
+ computer-player.vala \
+ game.vala \
+ game-view.vala \
+ iagno.vala
iagno_CFLAGS = \
- $(GTK_CFLAGS) \
- $(AM_CFLAGS)
+ -I$(top_srcdir)/libgames-support \
+ -DVERSION=\"$(VERSION)\" \
+ -DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\" \
+ $(GTK_CFLAGS)
+
+iagno_VALAFLAGS = \
+ --pkg posix \
+ --pkg gtk+-3.0 \
+ --vapidir $(top_srcdir)/libgames-support \
+ --pkg GnomeGamesSupport-1.0
iagno_LDADD = \
$(top_builddir)/libgames-support/libgames-support.la \
- $(GTK_LIBS) \
- $(INTLLIBS)
+ $(GTK_LIBS)
if HAVE_GNOME
iagno_CFLAGS += $(GNOME_CFLAGS)
diff --git a/iagno/src/computer-player.vala b/iagno/src/computer-player.vala
new file mode 100644
index 0000000..4bfc024
--- /dev/null
+++ b/iagno/src/computer-player.vala
@@ -0,0 +1,264 @@
+private enum Strategy
+{
+ PERFECT,
+ VICTORY,
+ BEST
+}
+
+private struct PossibleMove
+{
+ int x;
+ int y;
+ int n_tiles;
+}
+
+public class ComputerPlayer
+{
+ /* Game being played */
+ private Game game;
+
+ /* Strength */
+ private int level;
+
+ /* Value of owning each location */
+ private const int[] heuristic =
+ {
+ 65, -3, 6, 4, 4, 6, -3, 65,
+ -3, -29, 3, 1, 1, 3, -29, -3,
+ 6, 3, 5, 3, 3, 5, 3, 6,
+ 4, 1, 3, 1, 1, 3, 1, 4,
+ 4, 1, 3, 1, 1, 3, 1, 4,
+ 6, 3, 5, 3, 3, 5, 3, 6,
+ -3, -29, 3, 1, 1, 3, -29, -3,
+ 65, -3, 6, 4, 4, 6, -3, 65
+ };
+
+ public ComputerPlayer (Game game, int level)
+ {
+ this.game = game;
+ this.level = level;
+ }
+
+ public void move ()
+ {
+ /* For the first two moves play randomly so the game is not always the same */
+ if (game.n_tiles < 8)
+ {
+ int x, y;
+ random_select (out x, out y);
+ game.place_tile (x, y);
+ return;
+ }
+
+ /* Choose a strategy based on how close to the end we are.
+ * At the end of the game try and maximise the number of tokens.
+ * Near the end try and push for a win.
+ * For the rest of the game try and maximise everything.
+ */
+ var depth = 64 - game.n_tiles;
+ var strategy = Strategy.BEST;
+ if (depth <= 17 - (3 - level) * 2)
+ strategy = Strategy.PERFECT;
+ else if (depth <= 19 - (3 - level) * 2)
+ strategy = Strategy.VICTORY;
+ else
+ depth = 7 - (3 - level) * 2;
+
+ /* Choose a location to place by building the tree of possible moves and
+ * using the minimax algorithm to pick the best branch with the chosen
+ * strategy. */
+ int x = 0, y = 0;
+ search (new Game.copy (game), strategy, depth, int.MIN, int.MAX, 1, ref x, ref y);
+ if (game.place_tile (x, y) == 0)
+ warning ("Computer chose an invalid move: %d,%d", x, y);
+ }
+
+ private int search (Game g, Strategy strategy, int depth, int a, int b, int p, ref int move_x, ref int move_y)
+ {
+ /* If the end of the search depth or end of the game calculate how good a result this is */
+ if (depth == 0 || g.is_complete)
+ return calculate_heuristic (g, strategy);
+
+ /* Find all possible moves and sort from most new tiles to least new tiles */
+ List<PossibleMove?> moves = null;
+ for (var x = 0; x < 8; x++)
+ {
+ for (var y = 0; y < 8; y++)
+ {
+ var n_tiles = g.place_tile (x, y);
+ if (n_tiles > 0)
+ {
+ var move = PossibleMove ();
+ move.x = x;
+ move.y = y;
+ //warning ("%d %d", x, y);
+ move.n_tiles = n_tiles;
+ moves.insert_sorted (move, compare_move);
+ g.undo ();
+ }
+ }
+ }
+
+ /* If no moves then pass */
+ if (moves == null)
+ {
+ var move = PossibleMove ();
+ move.x = 0;
+ move.y = 0;
+ move.n_tiles = 0;
+ moves.append (move);
+ }
+
+ /* Try each move using alpha-beta pruning to optimise finding the best branch */
+ foreach (var move in moves)
+ {
+ if (move.n_tiles == 0)
+ g.pass ();
+ else if (g.place_tile (move.x, move.y) == 0)
+ {
+ warning ("Computer marked move (depth %d, %d,%d, %d flips) as valid, but is invalid when checking", depth, move.x, move.y, move.n_tiles);
+ continue;
+ }
+
+ /* If our move then maximise the result */
+ if (p > 0)
+ {
+ int next_x_move = 0, next_y_move = 0;
+ var a_new = search (g, strategy, depth - 1, a, b, -p, ref next_x_move, ref next_y_move);
+ if (a_new > a)
+ {
+ a = a_new;
+ move_x = move.x;
+ move_y = move.y;
+ }
+ }
+ /* If enemy move then minimise the result */
+ else
+ {
+ int next_x_move = 0, next_y_move = 0;
+ var b_new = search (g, strategy, depth - 1, a, b, -p, ref next_x_move, ref next_y_move);
+ if (b_new < b)
+ {
+ b = b_new;
+ move_x = move.x;
+ move_y = move.y;
+ }
+ }
+
+ g.undo ();
+
+ /* This branch has worse values, so ignore it */
+ if (b <= a)
+ break;
+ }
+
+ if (p > 0)
+ return a;
+ else
+ return b;
+ }
+
+ private static int compare_move (PossibleMove? a, PossibleMove? b)
+ {
+ return b.n_tiles - a.n_tiles;
+ }
+
+ private int calculate_heuristic (Game g, Strategy strategy)
+ {
+ var tile_difference = g.n_dark_tiles - g.n_light_tiles;
+ if (g.current_color == Player.DARK)
+ tile_difference = -tile_difference;
+
+ switch (strategy)
+ {
+ /* Maximise the number of tokens */
+ case Strategy.PERFECT:
+ return tile_difference;
+
+ /* Maximise a win over a loss */
+ case Strategy.VICTORY:
+ return tile_difference.clamp (-1, 1);
+
+ /* Try to maximise a number of values */
+ default:
+ return tile_difference + around () + eval_heuristic ();
+ }
+ }
+
+ private int eval_heuristic ()
+ {
+ var count = 0;
+ for (var x = 0; x < 8; x++)
+ {
+ for (var y = 0; y < 8; y++)
+ {
+ var h = heuristic[y * 8 + x];
+ if (game.get_owner (x, y) != game.current_color)
+ h = -h;
+ count += h;
+ }
+ }
+
+ return count;
+ }
+
+ private int around ()
+ {
+ var count = 0;
+ for (var x = 0; x < 8; x++)
+ {
+ for (var y = 0; y < 8; y++)
+ {
+ var a = 0;
+ a += is_empty (x + 1, y);
+ a += is_empty (x + 1, y + 1);
+ a += is_empty (x, y + 1);
+ a += is_empty (x - 1, y + 1);
+ a += is_empty (x - 1, y);
+ a += is_empty (x - 1, y - 1);
+ a += is_empty (x, y - 1);
+ a += is_empty (x + 1, y - 1);
+
+ /* Two points for completely surrounded tiles */
+ if (a == 0)
+ a = 2;
+
+ if (game.get_owner (x, y) != game.current_color)
+ a = -a;
+ count += a;
+ }
+ }
+
+ return count;
+ }
+
+ private int is_empty (int x, int y)
+ {
+ if (x < 0 || x >= 8 || y < 0 || y >= 8 || game.get_owner (x, y) != Player.NONE)
+ return 0;
+
+ return 1;
+ }
+
+ private void random_select (out int move_x, out int move_y)
+ {
+ List<int> moves = null;
+ for (var x = 0; x < 8; x++)
+ {
+ for (var y = 0; y < 8; y++)
+ {
+ if (game.can_place (x, y))
+ moves.append (x * 8 + y);
+ }
+ }
+ if (moves != null)
+ {
+ var i = Random.int_range (0, (int) moves.length ());
+ var xy = moves.nth_data (i);
+ move_x = xy / 8;
+ move_y = xy % 8;
+ }
+ else
+ move_x = move_y = 0;
+ }
+}
diff --git a/iagno/src/config.vapi b/iagno/src/config.vapi
new file mode 100644
index 0000000..6477226
--- /dev/null
+++ b/iagno/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/iagno/src/game-view.vala b/iagno/src/game-view.vala
new file mode 100644
index 0000000..8be528d
--- /dev/null
+++ b/iagno/src/game-view.vala
@@ -0,0 +1,239 @@
+public class GameView : Gtk.DrawingArea
+{
+ private const int GRIDWIDTH = 1;
+ private const int PIXMAP_FLIP_DELAY = 20;
+
+ private uint tile_width = 80;
+ private uint tile_height = 80;
+ private uint board_width = 648;
+ private uint board_height = 648;
+ private double[] dash = {4.0};
+
+ private Cairo.Surface? tiles_surface = null;
+ private Cairo.Surface? background_surface = null;
+
+ private int[,] pixmaps;
+
+ private uint animate_timeout = 0;
+
+ public signal void move (int x, int y);
+
+ public GameView ()
+ {
+ set_events (Gdk.EventMask.EXPOSURE_MASK | Gdk.EventMask.BUTTON_PRESS_MASK);
+ pixmaps = new int[8,8];
+ }
+
+ private Game? _game = null;
+ public Game? game
+ {
+ get { return _game; }
+ set
+ {
+ if (_game != null)
+ SignalHandler.disconnect_by_func (_game, null, this);
+ _game = value;
+ if (_game != null)
+ {
+ _game.square_changed.connect (square_changed_cb);
+ for (var x = 0; x < 8; x++)
+ for (var y = 0; y < 8; y++)
+ pixmaps[x, y] = get_pixmap (_game.get_owner (x, y));
+ }
+ redraw ();
+ }
+ }
+
+ private string? _tile_set = null;
+ public string? tile_set
+ {
+ get { return _tile_set; }
+ set { _tile_set = value; tiles_surface = null; redraw (); }
+ }
+
+ private bool _show_grid;
+ public bool show_grid
+ {
+ get { return _show_grid; }
+ set { _show_grid = value; redraw (); }
+ }
+
+ public override bool draw (Cairo.Context cr)
+ {
+ if (game == null)
+ return false;
+
+ if (tiles_surface == null)
+ load_pixmaps ();
+
+ var p = new Cairo.Pattern.for_surface (background_surface);
+ p.set_extend (Cairo.Extend.REPEAT);
+ cr.set_source (p);
+ cr.move_to (0, 0);
+ cr.line_to (0, board_height);
+ cr.line_to (board_width, board_height);
+ cr.line_to (board_width, 0);
+ cr.line_to (0, 0);
+ cr.fill ();
+
+ for (var x = 0; x < 8; x++)
+ {
+ for (var y = 0; y < 8; y++)
+ {
+ var tile_surface_x = x * (int) (tile_width + GRIDWIDTH) - (pixmaps[x, y] % 8) * (int) tile_width;
+ var tile_surface_y = y * (int) (tile_height + GRIDWIDTH) - (pixmaps[x, y] / 8) * (int) tile_height;
+
+ cr.set_source_surface (tiles_surface, tile_surface_x, tile_surface_y);
+ cr.rectangle (x * (tile_width + GRIDWIDTH), y * (tile_height + GRIDWIDTH), tile_width, tile_height);
+ cr.fill ();
+ }
+ }
+
+ if (show_grid)
+ {
+ cr.set_source_rgb (1.0, 1.0, 1.0);
+ cr.set_operator (Cairo.Operator.DIFFERENCE);
+ cr.set_dash (dash, 2.5);
+ cr.set_line_width (GRIDWIDTH);
+ for (var i = 1; i < 8; i++)
+ {
+ cr.move_to (i * board_width / 8 - 0.5, 0);
+ cr.line_to (i * board_width / 8 - 0.5, board_height);
+
+ cr.move_to (0, i * board_height / 8 - 0.5);
+ cr.line_to (board_width, i * board_height / 8 - 0.5);
+ }
+
+ cr.stroke ();
+ }
+
+ return false;
+ }
+
+ private void load_pixmaps ()
+ {
+ var dname = GnomeGamesSupport.runtime_get_directory (GnomeGamesSupport.RuntimeDirectory.GAME_PIXMAP_DIRECTORY);
+ var fname = Path.build_filename (dname, tile_set);
+
+ /* fall back to default tileset "classic.png" if tile_set not found */
+ if (!FileUtils.test (fname, FileTest.EXISTS | FileTest.IS_REGULAR))
+ fname = Path.build_filename (dname, "classic.png");
+
+ if (!FileUtils.test (fname, FileTest.EXISTS | FileTest.IS_REGULAR))
+ {
+ stderr.printf (_("Could not find \'%s\' pixmap file\n"), fname);
+ Posix.exit (Posix.EXIT_FAILURE);
+ }
+
+ Gdk.Pixbuf image;
+ try
+ {
+ image = new Gdk.Pixbuf.from_file (fname);
+ }
+ catch (Error e)
+ {
+ warning ("gdk-pixbuf error %s\n", e.message);
+ return;
+ }
+
+ tile_width = image.get_width () / 8;
+ tile_height = image.get_height () / 4;
+
+ /* Make sure the dash width evenly subdivides the tile height, and is at least 4 pixels long.
+ * This makes the dash crossings always cross in the same place, which looks nicer. */
+ var dash_count = (tile_height + GRIDWIDTH) / 4;
+ if (dash_count % 2 != 0)
+ dash_count--;
+ dash[0] = ((double)(tile_height + GRIDWIDTH)) / dash_count;
+
+ board_width = (tile_width + GRIDWIDTH) * 8;
+ board_height = (tile_height + GRIDWIDTH) * 8;
+ set_size_request ((int) board_width, (int) board_height);
+
+ tiles_surface = get_window ().create_similar_surface (Cairo.Content.COLOR_ALPHA, image.get_width (), image.get_height ());
+ var cr = new Cairo.Context (tiles_surface);
+ Gdk.cairo_set_source_pixbuf (cr, image, 0, 0);
+ cr.paint ();
+
+ background_surface = get_window ().create_similar_surface (Cairo.Content.COLOR_ALPHA, 1, 1);
+ cr = new Cairo.Context (background_surface);
+ Gdk.cairo_set_source_pixbuf (cr, image, 0, 0);
+ cr.paint ();
+ }
+
+ public void redraw ()
+ {
+ queue_draw_area (0, 0, (int) board_width, (int) board_height);
+ }
+
+ private void square_changed_cb (int x, int y)
+ {
+ var target = get_pixmap (game.get_owner (x, y));
+
+ if (pixmaps[x, y] == target)
+ return;
+
+ if (target == 0 || pixmaps[x, y] == 0)
+ pixmaps[x, y] = target;
+ else
+ {
+ if (target > pixmaps[x, y])
+ pixmaps[x, y]++;
+ else
+ pixmaps[x, y]--;
+ if (animate_timeout == 0)
+ animate_timeout = Timeout.add (PIXMAP_FLIP_DELAY, animate_cb);
+ }
+ queue_draw_area (x * (int) (tile_width + GRIDWIDTH), y * (int) (tile_height + GRIDWIDTH), (int) tile_width, (int) tile_height);
+ }
+
+ private bool animate_cb ()
+ {
+ var animating = false;
+
+ for (var x = 0; x < 8; x++)
+ {
+ for (var y = 0; y < 8; y++)
+ {
+ var old = pixmaps[x, y];
+ square_changed_cb (x, y);
+ if (pixmaps[x, y] != old)
+ animating = true;
+ }
+ }
+
+ if (!animating)
+ {
+ animate_timeout = 0;
+ return false;
+ }
+
+ return true;
+ }
+
+ private int get_pixmap (Player color)
+ {
+ switch (color)
+ {
+ default:
+ case Player.NONE:
+ return 0;
+ case Player.DARK:
+ return 1;
+ case Player.LIGHT:
+ return 31;
+ }
+ }
+
+ public override bool button_press_event (Gdk.EventButton event)
+ {
+ if (event.button == 1)
+ {
+ var x = (int) event.x / (int) (tile_width + GRIDWIDTH);
+ var y = (int) event.y / (int) (tile_height + GRIDWIDTH);
+ move (x, y);
+ }
+
+ return true;
+ }
+}
diff --git a/iagno/src/game.vala b/iagno/src/game.vala
new file mode 100644
index 0000000..b38a6e1
--- /dev/null
+++ b/iagno/src/game.vala
@@ -0,0 +1,282 @@
+public enum Player
+{
+ NONE,
+ LIGHT,
+ DARK
+}
+
+public class Game
+{
+ /* Tiles on the board */
+ public Player[,] tiles;
+
+ /* Undo stack. This is a record of all the tile changes since the start of the game
+ * in the binary form ccxxxyyy where cc is the color (0-2), xxx is the x location (0-7)
+ * and yyy is the y location (0-7). Each set of changes is followed by the number of changes
+ * preceeding. This array is oversized, but big enough for the (impossible) worst case of
+ * each move flipping 20 tiles. */
+ private int undo_history[1344];
+ private int undo_index = 0;
+
+ /* Color to move next */
+ public Player current_color;
+
+ /* Indicate that a player should move */
+ public signal void move ();
+
+ /* Indicate a square has changed */
+ public signal void square_changed (int x, int y);
+
+ /* Indicate the game is complete */
+ public signal void complete ();
+
+ /* The number of tiles on the board */
+ public int n_tiles
+ {
+ get
+ {
+ var count = 0;
+ for (var x = 0; x < 8; x++)
+ {
+ for (var y = 0; y < 8; y++)
+ {
+ if (tiles[x, y] != Player.NONE)
+ count++;
+ }
+ }
+ return count;
+ }
+ }
+
+ public int n_light_tiles
+ {
+ get { return count_tiles (Player.LIGHT); }
+ }
+
+ public int n_dark_tiles
+ {
+ get { return count_tiles (Player.DARK); }
+ }
+
+ public bool can_move
+ {
+ get
+ {
+ for (var x = 0; x < 8; x++)
+ for (var y = 0; y < 8; y++)
+ if (can_place (x, y))
+ return true;
+ return false;
+ }
+ }
+
+ public bool is_complete
+ {
+ get { return n_tiles == 64 || n_light_tiles == 0 || n_dark_tiles == 0; }
+ }
+
+ public Game ()
+ {
+ /* Setup board with four tiles by default */
+ tiles = new Player[8, 8];
+ for (var x = 0; x < 8; x++)
+ for (var y = 0; y < 8; y++)
+ tiles[x, y] = Player.NONE;
+ set_tile (3, 3, Player.LIGHT, false);
+ set_tile (3, 4, Player.DARK, false);
+ set_tile (4, 3, Player.DARK, false);
+ set_tile (4, 4, Player.LIGHT, false);
+
+ /* Black plays first */
+ current_color = Player.DARK;
+ }
+
+ public void start ()
+ {
+ if (n_tiles != 4)
+ return;
+
+ move ();
+ }
+
+ public Game.copy (Game game)
+ {
+ tiles = new Player[8, 8];
+ for (var x = 0; x < 8; x++)
+ for (var y = 0; y < 8; y++)
+ tiles[x, y] = game.tiles[x, y];
+ for (var i = 0; i < game.undo_index; i++)
+ undo_history[i] = game.undo_history[i];
+ undo_index = game.undo_index;
+ current_color = game.current_color;
+ }
+
+ public Player get_owner (int x, int y)
+ {
+ if (is_valid_location (x, y))
+ return tiles[x, y];
+ else
+ return Player.NONE;
+ }
+
+ public bool can_place (int x, int y)
+ {
+ return place (x, y, current_color, false) > 0;
+ }
+
+ public int place_tile (int x, int y)
+ {
+ var n_tiles = place (x, y, current_color, true);
+ if (n_tiles == 0)
+ return 0;
+
+ /* Move to the next player */
+ if (current_color == Player.LIGHT)
+ current_color = Player.DARK;
+ else
+ current_color = Player.LIGHT;
+
+ if (is_complete)
+ complete ();
+ else
+ move ();
+
+ return n_tiles;
+ }
+
+ public void pass ()
+ {
+ undo_history[undo_index] = 0;
+ undo_index++;
+
+ if (current_color == Player.LIGHT)
+ current_color = Player.DARK;
+ else
+ current_color = Player.LIGHT;
+
+ move ();
+ }
+
+ private int place (int x, int y, Player color, bool apply)
+ {
+ /* Square needs to be empty */
+ if (!is_valid_location (x, y) || tiles[x, y] != Player.NONE)
+ return 0;
+
+ var n_flips = 0;
+ n_flips += flip_tiles (x, y, 1, 0, color, apply);
+ n_flips += flip_tiles (x, y, 1, 1, color, apply);
+ n_flips += flip_tiles (x, y, 0, 1, color, apply);
+ n_flips += flip_tiles (x, y, -1, 1, color, apply);
+ n_flips += flip_tiles (x, y, -1, 0, color, apply);
+ n_flips += flip_tiles (x, y, -1, -1, color, apply);
+ n_flips += flip_tiles (x, y, 0, -1, color, apply);
+ n_flips += flip_tiles (x, y, 1, -1, color, apply);
+
+ /* Store the number of entries in the undo history */
+ if (apply && n_flips > 0)
+ {
+ undo_history[undo_index] = n_flips + 1;
+ undo_index++;
+ }
+
+ return n_flips;
+ }
+
+ private int count_tiles (Player color)
+ {
+ var count = 0;
+ for (var x = 0; x < 8; x++)
+ for (var y = 0; y < 8; y++)
+ if (tiles[x, y] == color)
+ count++;
+ return count;
+ }
+
+ private bool is_valid_location (int x, int y)
+ {
+ return x >= 0 && x < 8 && y >= 0 && y < 8;
+ }
+
+ private int flip_tiles (int x, int y, int x_step, int y_step, Player color, bool apply)
+ {
+ var enemy = Player.LIGHT;
+ if (color == Player.LIGHT)
+ enemy = Player.DARK;
+
+ /* Count number of enemy pieces we are beside */
+ var enemy_count = 0;
+ var xt = x + x_step;
+ var yt = y + y_step;
+ while (is_valid_location (xt, yt))
+ {
+ if (tiles[xt, yt] != enemy)
+ break;
+ enemy_count++;
+ xt += x_step;
+ yt += y_step;
+ }
+
+ /* Must be a line of enemy pieces then one of ours */
+ if (enemy_count == 0 || !is_valid_location (xt, yt) || tiles[xt, yt] != color)
+ return 0;
+
+ /* Place this tile and flip the adjacent ones */
+ if (apply)
+ for (var i = 0; i <= enemy_count; i++)
+ set_tile (x + i * x_step, y + i * y_step, color, true);
+
+ return enemy_count;
+ }
+
+ public bool can_undo
+ {
+ get { return undo_index > 0; }
+ }
+
+ public void undo (int count = 1)
+ {
+ if (count < 1)
+ return;
+
+ for (var i = 0; i < count; i++)
+ {
+ var n_changes = undo_history[undo_index - 1];
+ undo_index--;
+
+ /* Undo each tile change */
+ for (var j = 0; j < n_changes; j++)
+ {
+ var n = undo_history[undo_index - 1];
+ undo_index--;
+ var c = (Player) (n >> 6);
+ var xy = n & 0x3F;
+ set_tile (xy / 8, xy % 8, c, false);
+ }
+
+ /* Previous player to move again */
+ if (current_color == Player.LIGHT)
+ current_color = Player.DARK;
+ else
+ current_color = Player.LIGHT;
+ }
+
+ move ();
+ }
+
+ private void set_tile (int x, int y, Player color, bool update_history)
+ {
+ if (tiles[x, y] == color)
+ return;
+
+ /* Store the old color in the history */
+ if (update_history)
+ {
+ undo_history[undo_index] = ((int) tiles[x, y] << 6) | (x * 8 + y);
+ undo_index++;
+ }
+
+ tiles[x, y] = color;
+ square_changed (x, y);
+ }
+}
diff --git a/iagno/src/iagno.vala b/iagno/src/iagno.vala
new file mode 100644
index 0000000..44a8c82
--- /dev/null
+++ b/iagno/src/iagno.vala
@@ -0,0 +1,591 @@
+public class Iagno
+{
+ /* Application settings */
+ private Settings settings;
+
+ /* Widgets */
+ private Gtk.Window window;
+ private Gtk.Statusbar statusbar;
+ private uint statusbar_id;
+ private GameView view;
+ private Gtk.Label dark_score_label;
+ private Gtk.Label light_score_label;
+ private Gtk.Action new_game_action;
+ private Gtk.Action undo_action;
+
+ /* Light computer player (if there is one) */
+ private ComputerPlayer? light_computer = null;
+
+ /* Dark computer player (if there is one) */
+ private ComputerPlayer? dark_computer = null;
+
+ /* Timer to delay computer moves */
+ private uint computer_timer = 0;
+
+ /* The game being played */
+ private Game? game = null;
+
+ /* true if the last move was a pass */
+ private bool was_pass = false;
+
+ /* Possible themes */
+ private GnomeGamesSupport.FileList? theme_file_list = null;
+
+ private const Gtk.ActionEntry actions[] =
+ {
+ {"GameMenu", null, N_("_Game")},
+ {"SettingsMenu", null, N_("_Settings")},
+ {"HelpMenu", null, N_("_Help")},
+ {"NewGame", GnomeGamesSupport.STOCK_NEW_GAME, null, null, null, new_game_cb},
+ {"UndoMove", GnomeGamesSupport.STOCK_UNDO_MOVE, null, null, null, undo_move_cb},
+ {"Quit", Gtk.Stock.QUIT, null, null, null, quit_game_cb},
+ {"Preferences", Gtk.Stock.PREFERENCES, null, null, null, properties_cb},
+ {"Contents", GnomeGamesSupport.STOCK_CONTENTS, null, null, null, help_cb},
+ {"About", Gtk.Stock.ABOUT, null, null, null, about_cb}
+ };
+
+ private string ui_description =
+ "<ui>" +
+ " <menubar name='MainMenu'>" +
+ " <menu action='GameMenu'>" +
+ " <menuitem action='NewGame'/>" +
+ " <separator/>" +
+ " <menuitem action='UndoMove'/>" +
+ " <separator/>" +
+ " <menuitem action='Quit'/>" +
+ " </menu>" +
+ " <menu action='SettingsMenu'>" +
+ " <menuitem action='Preferences'/>" +
+ " </menu>" +
+ " <menu action='HelpMenu'>" +
+ " <menuitem action='Contents'/>" +
+ " <menuitem action='About'/>" +
+ " </menu>" +
+ " </menubar>" +
+ "</ui>";
+
+ public Iagno ()
+ {
+ settings = new Settings ("org.gnome.iagno");
+
+ window = new Gtk.Window (Gtk.WindowType.TOPLEVEL);
+ window.set_title (_("Iagno"));
+
+ GnomeGamesSupport.settings_bind_window_state ("/org/gnome/iagno/", window);
+
+ var vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
+ vbox.show ();
+ window.add (vbox);
+
+ var ui_manager = new Gtk.UIManager ();
+ var action_group = new Gtk.ActionGroup ("group");
+
+ action_group.set_translation_domain (GETTEXT_PACKAGE);
+ action_group.add_actions (actions, this);
+
+ ui_manager.insert_action_group (action_group, 0);
+ try
+ {
+ ui_manager.add_ui_from_string (ui_description, -1);
+ }
+ catch (Error e)
+ {
+ warning ("Failed to load UI: %s", e.message);
+ }
+
+ window.add_accel_group (ui_manager.get_accel_group ());
+
+ new_game_action = action_group.get_action ("NewGame");
+ undo_action = action_group.get_action ("UndoMove");
+ undo_action.set_sensitive (false);
+ var menubar = (Gtk.MenuBar) ui_manager.get_widget ("/MainMenu");
+ vbox.pack_start (menubar, false, false, 0);
+
+ var notebook = new Gtk.Notebook ();
+ notebook.show ();
+ notebook.set_show_tabs (false);
+ notebook.set_show_border (false);
+
+ window.delete_event.connect (window_delete_event_cb);
+
+ view = new GameView ();
+ view.game = game;
+ view.move.connect (player_move_cb);
+ view.show_grid = settings.get_boolean ("show-grid");
+ view.tile_set = settings.get_string ("tileset");
+ view.show ();
+
+ notebook.append_page (view, null);
+ notebook.set_current_page (0);
+ vbox.pack_start (notebook, false, false, 0);
+
+ statusbar = new Gtk.Statusbar ();
+ statusbar.show ();
+ vbox.pack_start (statusbar, false, false, 0);
+
+ var grid = new Gtk.Grid ();
+ grid.set_column_spacing (6);
+ grid.show ();
+ statusbar.pack_start (grid, false, true, 0);
+
+ var label = new Gtk.Label (_("Dark:"));
+ label.show ();
+ grid.attach (label, 1, 0, 1, 1);
+
+ dark_score_label = new Gtk.Label ("00");
+ dark_score_label.show ();
+ grid.attach (dark_score_label, 2, 0, 1, 1);
+
+ label = new Gtk.Label (_("Light:"));
+ label.show ();
+ grid.attach (label, 4, 0, 1, 1);
+
+ light_score_label = new Gtk.Label ("00");
+ light_score_label.show ();
+ grid.attach (light_score_label, 5, 0, 1, 1);
+
+ window.set_resizable (false);
+
+ statusbar_id = statusbar.get_context_id ("iagno");
+
+ GnomeGamesSupport.sound_enable (settings.get_boolean ("sound"));
+
+ start_game ();
+ }
+
+ private void show ()
+ {
+ window.show ();
+ }
+
+ private void quit_game_cb ()
+ {
+ Gtk.main_quit ();
+ }
+
+ private bool window_delete_event_cb (Gtk.Widget widget, Gdk.EventAny event)
+ {
+ Gtk.main_quit ();
+ return true;
+ }
+
+ private void new_game_cb ()
+ {
+ start_game ();
+ }
+
+ private void start_game ()
+ {
+ /* Cancel any pending computer moves */
+ if (computer_timer != 0)
+ {
+ Source.remove (computer_timer);
+ computer_timer = 0;
+ }
+
+ if (game != null)
+ SignalHandler.disconnect_by_func (game, null, this);
+
+ game = new Game ();
+ game.move.connect (game_move_cb);
+ game.complete.connect (game_complete_cb);
+ view.game = game;
+
+ var dark_level = settings.get_int ("black-level");
+ if (dark_level > 0)
+ dark_computer = new ComputerPlayer (game, dark_level);
+ else
+ dark_computer = null;
+ var light_level = settings.get_int ("white-level");
+ if (light_level > 0)
+ light_computer = new ComputerPlayer (game, light_level);
+ else
+ light_computer = null;
+
+ update_ui ();
+
+ game.start ();
+ }
+
+ private void update_ui ()
+ {
+ /* Can't undo when running two computer players */
+ if (light_computer != null && dark_computer != null)
+ undo_action.set_sensitive (false);
+ else
+ undo_action.set_sensitive (game.can_undo);
+
+ dark_score_label.set_text (_("%.2d").printf (game.n_dark_tiles));
+ light_score_label.set_text (_("%.2d").printf (game.n_light_tiles));
+
+ if (was_pass)
+ {
+ if (game.current_color == Player.DARK)
+ show_message (_("Light must pass, Dark's move"));
+ else
+ show_message (_("Dark must pass, Light's move"));
+ }
+ else
+ {
+ if (game.current_color == Player.DARK)
+ show_message (_("Dark's move"));
+ else if (game.current_color == Player.LIGHT)
+ show_message (_("Light's move"));
+ }
+ }
+
+ private void undo_move_cb ()
+ {
+ /* Cancel any pending computer moves */
+ if (computer_timer != 0)
+ {
+ Source.remove (computer_timer);
+ computer_timer = 0;
+ }
+
+ /* Undo once if the human player just moved, otherwise undo both moves */
+ if ((game.current_color == Player.DARK && dark_computer != null) ||
+ (game.current_color == Player.LIGHT && light_computer != null))
+ game.undo (1);
+ else
+ game.undo (2);
+ }
+
+ private void about_cb (Gtk.Action action)
+ {
+ string[] authors = { "Ian Peters", "Robert Ancell", null };
+ string[] documenters = { "Eric Baudais", null };
+
+ Gtk.show_about_dialog (window,
+ "name", _("Iagno"),
+ "version", VERSION,
+ "copyright",
+ "Copyright \xc2\xa9 1998-2008 Ian Peters",
+ "license", GnomeGamesSupport.get_license (_("Iagno")),
+ "comments", _("A disk flipping game derived from Reversi.\n\nIagno is a part of GNOME Games."),
+ "authors", authors,
+ "documenters", documenters,
+ "translator-credits", _("translator-credits"),
+ "logo-icon-name", "gnome-iagno",
+ "website-label", _("GNOME Games web site"),
+ "website", "http://www.gnome.org/projects/gnome-games/",
+ "wrap-license", true,
+ null);
+ }
+
+ private void properties_cb ()
+ {
+ show_properties_dialog ();
+ }
+
+ private void show_message (string message)
+ {
+ statusbar.pop (statusbar_id);
+ statusbar.push (statusbar_id, message);
+ }
+
+ private void help_cb (Gtk.Action action)
+ {
+ GnomeGamesSupport.help_display (window, "iagno", null);
+ }
+
+ private void game_move_cb ()
+ {
+ if (!game.can_move)
+ {
+ was_pass = true;
+ game.pass ();
+ return;
+ }
+
+ update_ui ();
+ was_pass = false;
+
+ /* Get the computer to move after a delay (so it looks like it's thinking) */
+ if ((game.current_color == Player.LIGHT && light_computer != null) ||
+ (game.current_color == Player.DARK && dark_computer != null))
+ computer_timer = Timeout.add (1000, computer_move_cb);
+ }
+
+ private bool computer_move_cb ()
+ {
+ if (game.current_color == Player.LIGHT)
+ light_computer.move ();
+ else
+ dark_computer.move ();
+ computer_timer = 0;
+ return false;
+ }
+
+ private void game_complete_cb ()
+ {
+ if (game.n_light_tiles > game.n_dark_tiles)
+ show_message (_("Light player wins!"));
+ if (game.n_dark_tiles > game.n_light_tiles)
+ show_message (_("Dark player wins!"));
+ if (game.n_light_tiles == game.n_dark_tiles)
+ show_message (_("The game was a draw."));
+
+ GnomeGamesSupport.sound_play ("gameover");
+ }
+
+ private void player_move_cb (int x, int y)
+ {
+ /* Ignore if we are waiting for the AI to move */
+ if (game.current_color == Player.LIGHT && settings.get_int ("white-level") > 0)
+ return;
+ if (game.current_color == Player.DARK && settings.get_int ("black-level") > 0)
+ return;
+
+ if (game.place_tile (x, y) == 0)
+ show_message (_("Invalid move."));
+ }
+
+ private void dark_human_cb (Gtk.ToggleButton widget)
+ {
+ if (widget.get_active ())
+ settings.set_int ("black-level", 0);
+ }
+
+ private void dark_level_one_cb (Gtk.ToggleButton widget)
+ {
+ if (widget.get_active ())
+ settings.set_int ("black-level", 1);
+ }
+
+ private void dark_level_two_cb (Gtk.ToggleButton widget)
+ {
+ if (widget.get_active ())
+ settings.set_int ("black-level", 2);
+ }
+
+ private void dark_level_three_cb (Gtk.ToggleButton widget)
+ {
+ if (widget.get_active ())
+ settings.set_int ("black-level", 3);
+ }
+
+ private void light_human_cb (Gtk.ToggleButton widget)
+ {
+ if (widget.get_active ())
+ settings.set_int ("white-level", 0);
+ }
+
+ private void light_level_one_cb (Gtk.ToggleButton widget)
+ {
+ if (widget.get_active ())
+ settings.set_int ("white-level", 1);
+ }
+
+ private void light_level_two_cb (Gtk.ToggleButton widget)
+ {
+ if (widget.get_active ())
+ settings.set_int ("white-level", 2);
+ }
+
+ private void light_level_three_cb (Gtk.ToggleButton widget)
+ {
+ if (widget.get_active ())
+ settings.set_int ("white-level", 3);
+ }
+
+ private void sound_select (Gtk.ToggleButton widget)
+ {
+ var play_sounds = widget.get_active ();
+ settings.set_boolean ("sound", play_sounds);
+ GnomeGamesSupport.sound_enable (play_sounds);
+ }
+
+ private void grid_select (Gtk.ToggleButton widget)
+ {
+ view.show_grid = widget.get_active ();
+ settings.set_boolean ("show-grid", view.show_grid);
+ }
+
+ private void propbox_response_cb (Gtk.Widget widget, int response_id)
+ {
+ widget.hide ();
+ }
+
+ private bool propbox_close_cb (Gtk.Widget widget, Gdk.EventAny event)
+ {
+ widget.hide ();
+ return true;
+ }
+
+ private void set_selection (Gtk.ComboBox widget)
+ {
+ view.tile_set = theme_file_list.get_nth (widget.get_active ());
+ settings.set_string ("tileset", view.tile_set);
+ view.redraw ();
+ }
+
+ private void show_properties_dialog ()
+ {
+ var propbox = new Gtk.Dialog.with_buttons (_("Iagno Preferences"),
+ window,
+ 0,
+ Gtk.Stock.CLOSE, Gtk.ResponseType.CLOSE,
+ null);
+
+ propbox.set_border_width (5);
+ var box = (Gtk.Box) propbox.get_content_area ();
+ box.set_spacing (2);
+ propbox.resizable = false;
+ propbox.response.connect (propbox_response_cb);
+ propbox.delete_event.connect (propbox_close_cb);
+
+ var notebook = new Gtk.Notebook ();
+ notebook.set_border_width (5);
+ box.add (notebook);
+
+ var label = new Gtk.Label (_("Game"));
+
+ var vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 18);
+ vbox.set_border_width (12);
+ notebook.append_page (vbox, label);
+
+ var grid = new Gtk.Grid ();
+ grid.set_column_spacing (18);
+ vbox.pack_start (grid, false, false, 0);
+
+ var vbox2 = new Gtk.Box (Gtk.Orientation.VERTICAL, 6);
+ vbox.pack_start (vbox2, false, false, 0);
+
+ var enable_sounds_button = new Gtk.CheckButton.with_mnemonic (_("E_nable sounds"));
+ enable_sounds_button.set_active (settings.get_boolean ("sound"));
+ enable_sounds_button.toggled.connect (sound_select);
+ vbox2.pack_start (enable_sounds_button, false, false, 0);
+
+ var frame = new GnomeGamesSupport.Frame (_("Dark"));
+ grid.attach (frame, 0, 0, 1, 1);
+
+ vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 6);
+ frame.add (vbox);
+
+ var computer_button = new Gtk.RadioButton.with_label (null, _("Human"));
+ if (settings.get_int ("black-level") == 0)
+ computer_button.set_active (true);
+ computer_button.toggled.connect (dark_human_cb);
+ vbox.pack_start (computer_button, false, false, 0);
+
+ computer_button = new Gtk.RadioButton.with_label (computer_button.get_group (), _("Level one"));
+ if (settings.get_int ("black-level") == 1)
+ computer_button.set_active (true);
+ computer_button.toggled.connect (dark_level_one_cb);
+ vbox.pack_start (computer_button, false, false, 0);
+
+ computer_button = new Gtk.RadioButton.with_label (computer_button.get_group (), _("Level two"));
+ if (settings.get_int ("black-level") == 2)
+ computer_button.set_active (true);
+ computer_button.toggled.connect (dark_level_two_cb);
+ vbox.pack_start (computer_button, false, false, 0);
+
+ computer_button = new Gtk.RadioButton.with_label (computer_button.get_group (), _("Level three"));
+ if (settings.get_int ("black-level") == 3)
+ computer_button.set_active (true);
+ computer_button.toggled.connect (dark_level_three_cb);
+ vbox.pack_start (computer_button, false, false, 0);
+
+ frame = new GnomeGamesSupport.Frame (_("Light"));
+ grid.attach (frame, 1, 0, 1, 1);
+
+ vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 6);
+ frame.add (vbox);
+
+ computer_button = new Gtk.RadioButton.with_label (null, _("Human"));
+ if (settings.get_int ("white-level") == 0)
+ computer_button.set_active (true);
+ computer_button.toggled.connect (light_human_cb);
+ vbox.pack_start (computer_button, false, false, 0);
+
+ computer_button = new Gtk.RadioButton.with_label (computer_button.get_group (), _("Level one"));
+ if (settings.get_int ("white-level") == 1)
+ computer_button.set_active (true);
+ computer_button.toggled.connect (light_level_one_cb);
+ vbox.pack_start (computer_button, false, false, 0);
+
+ computer_button = new Gtk.RadioButton.with_label (computer_button.get_group (), _("Level two"));
+ if (settings.get_int ("white-level") == 2)
+ computer_button.set_active (true);
+ computer_button.toggled.connect (light_level_two_cb);
+ vbox.pack_start (computer_button, false, false, 0);
+
+ computer_button = new Gtk.RadioButton.with_label (computer_button.get_group (), _("Level three"));
+ if (settings.get_int ("white-level") == 3)
+ computer_button.set_active (true);
+ computer_button.toggled.connect (light_level_three_cb);
+ vbox.pack_start (computer_button, false, false, 0);
+
+ label = new Gtk.Label (_("Appearance"));
+
+ grid = new Gtk.Grid ();
+ grid.set_column_spacing (18);
+ grid.set_border_width (12);
+ notebook.append_page (grid, label);
+
+ frame = new GnomeGamesSupport.Frame (_("Options"));
+ grid.attach (frame, 0, 0, 1, 1);
+
+ vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 6);
+ frame.add (vbox);
+
+ var grid_button = new Gtk.CheckButton.with_mnemonic (_("S_how grid"));
+ grid_button.set_active (settings.get_boolean ("show-grid"));
+ grid_button.toggled.connect (grid_select);
+ vbox.pack_start (grid_button, false, false, 0);
+
+ var hbox = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 12);
+ vbox.pack_start (hbox, false, false, 0);
+
+ label = new Gtk.Label.with_mnemonic (_("_Tile set:"));
+ hbox.pack_start (label, false, false, 0);
+
+ var dir = GnomeGamesSupport.runtime_get_directory (GnomeGamesSupport.RuntimeDirectory.GAME_PIXMAP_DIRECTORY);
+ theme_file_list = new GnomeGamesSupport.FileList.images (dir, null);
+ theme_file_list.transform_basename ();
+ var option_menu = (Gtk.ComboBox) theme_file_list.create_widget (view.tile_set, GnomeGamesSupport.FILE_LIST_REMOVE_EXTENSION | GnomeGamesSupport.FILE_LIST_REPLACE_UNDERSCORES);
+
+ label.set_mnemonic_widget (option_menu);
+ option_menu.changed.connect (set_selection);
+ hbox.pack_start (option_menu, true, true, 0);
+
+
+ propbox.show_all ();
+ }
+
+ public static int main (string[] args)
+ {
+ if (!GnomeGamesSupport.runtime_init ("iagno"))
+ return Posix.EXIT_FAILURE;
+
+ var context = new OptionContext ("");
+ context.set_translation_domain (GETTEXT_PACKAGE);
+ context.add_group (Gtk.get_option_group (true));
+
+ try
+ {
+ context.parse (ref args);
+ }
+ catch (Error e)
+ {
+ stderr.printf ("%s\n", e.message);
+ return Posix.EXIT_FAILURE;
+ }
+
+ Environment.set_application_name (_("Iagno"));
+
+ GnomeGamesSupport.stock_init ();
+
+ Gtk.Window.set_default_icon_name ("gnome-iagno");
+
+ var app = new Iagno ();
+ app.show ();
+
+ Gtk.main ();
+
+ GnomeGamesSupport.runtime_shutdown ();
+
+ return Posix.EXIT_SUCCESS;
+ }
+}
diff --git a/libgames-support/GnomeGamesSupport-1.0.vapi b/libgames-support/GnomeGamesSupport-1.0.vapi
index 7c5aa16..a5f71c4 100644
--- a/libgames-support/GnomeGamesSupport-1.0.vapi
+++ b/libgames-support/GnomeGamesSupport-1.0.vapi
@@ -92,6 +92,23 @@ namespace GnomeGamesSupport
public static int runtime_get_gpl_version ();
[CCode (cheader_filename = "games-runtime.h")]
public static bool runtime_is_system_prefix ();
+
+ [CCode (cheader_filename = "games-sound.h")]
+ bool sound_is_available ();
+ [CCode (cheader_filename = "games-sound.h")]
+ void sound_init (Gdk.Screen screen);
+ [CCode (cheader_filename = "games-sound.h")]
+ void sound_play (string sound_name);
+ [CCode (cheader_filename = "games-sound.h")]
+ void sound_play_for_screen (string sound_name, Gdk.Screen screen);
+ [CCode (cheader_filename = "games-sound.h")]
+ void sound_play_for_event (string sound_name, Gdk.Event event);
+ [CCode (cheader_filename = "games-sound.h")]
+ void sound_play_for_widget (string sound_name, Gtk.Widget widget);
+ [CCode (cheader_filename = "games-sound.h")]
+ void sound_enable (bool enabled);
+ [CCode (cheader_filename = "games-sound.h")]
+ bool sound_is_enabled ();
[CCode (cheader_filename = "games-help.h")]
public static void help_display (Gtk.Widget window, string doc_module, string? section);
@@ -221,6 +238,11 @@ namespace GnomeGamesSupport
}
[CCode (cheader_filename = "games-files.h")]
+ public const int FILE_LIST_REMOVE_EXTENSION;
+ [CCode (cheader_filename = "games-files.h")]
+ public const int FILE_LIST_REPLACE_UNDERSCORES;
+
+ [CCode (cheader_filename = "games-files.h")]
public class FileList : GLib.Object
{
public FileList (string glob, ...);
diff --git a/po/POTFILES.in b/po/POTFILES.in
index e401801..c662d8a 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -94,9 +94,7 @@ gtali/src/setup.c
gtali/src/yahtzee.c
iagno/data/iagno.desktop.in.in
iagno/data/org.gnome.iagno.gschema.xml.in
-iagno/src/gnothello.c
-iagno/src/othello.c
-iagno/src/properties.c
+iagno/src/iagno.vala
libgames-support/eggdesktopfile.c
libgames-support/eggsmclient.c
libgames-support/eggsmclient-osx.c
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]