[gnome-2048] Implement persistence



commit 7003ef2219f3e56f0d644f01c4f9a72f6a3598c0
Author: Juan R. GarcĂ­a Blanco <juanrgar gmail com>
Date:   Sat Jan 10 15:21:21 2015 +0100

    Implement persistence
    
    Game state is preserved between launches.

 src/application.vala |    5 +-
 src/game.vala        |   84 ++++++++++++++++++++++++--
 src/grid.vala        |  162 ++++++++++++++++++++++++++++++++++++-------------
 3 files changed, 202 insertions(+), 49 deletions(-)
---
diff --git a/src/application.vala b/src/application.vala
index 177ae47..1a8eafe 100644
--- a/src/application.vala
+++ b/src/application.vala
@@ -70,13 +70,16 @@ public class Application : Gtk.Application
     _create_about_dialog ();
     _create_preferences_dialog (builder);
 
-    _game.new_game ();
+    if (!_game.restore_game ())
+      _game.new_game ();
   }
 
   protected override void shutdown ()
   {
     base.shutdown ();
 
+    _game.save_game ();
+
     _settings.set_int ("window-width", _window_width);
     _settings.set_int ("window-height", _window_height);
     _settings.set_boolean ("window-maximized", _window_maximized);
diff --git a/src/game.vala b/src/game.vala
index 0060256..c63dda5 100644
--- a/src/game.vala
+++ b/src/game.vala
@@ -26,7 +26,8 @@ public class Game : GLib.Object
     MOVING_RIGHT,
     MOVING_LEFT,
     SHOWING_FIRST_TILE,
-    SHOWING_SECOND_TILE
+    SHOWING_SECOND_TILE,
+    RESTORING_TILES
   }
 
   private int BLANK_ROW_HEIGHT = 10;
@@ -52,6 +53,8 @@ public class Game : GLib.Object
 
   private GLib.Settings _settings;
 
+  private string _saved_path;
+
   public signal void finished ();
 
   public Game (GLib.Settings settings)
@@ -69,6 +72,8 @@ public class Game : GLib.Object
     _to_hide = new Gee.LinkedList<TileMovement?> ();
     _to_show = new Gee.LinkedList<Tile?> ();
 
+    _saved_path = Path.build_filename (Environment.get_user_data_dir (), "gnome-2048", "saved");
+
     _state = GameState.STOPPED;
   }
 
@@ -93,6 +98,49 @@ public class Game : GLib.Object
     _create_random_tile ();
   }
 
+  public void save_game ()
+  {
+    string contents = "";
+
+    contents += _grid.save ();
+    contents += _score.to_string() + "\n";
+
+    try {
+      DirUtils.create_with_parents (Path.get_dirname (_saved_path), 0775);
+      FileUtils.set_contents (_saved_path, contents);
+      debug ("game saved successfully");
+    } catch (FileError e) {
+      warning ("Failed to save game: %s", e.message);
+    }
+  }
+
+  public bool restore_game ()
+  {
+    string contents;
+    string[] lines;
+
+    try {
+      FileUtils.get_contents (_saved_path, out contents);
+    } catch (FileError e) {
+      warning ("Failed to save game: %s", e.message);
+      return false;
+    }
+
+    if (!_grid.load (contents))
+      return false;
+
+    lines = contents.split ("\n");
+    score = (uint)int.parse (lines[lines.length-2]);
+
+    _n_rows = _grid.rows;
+    _n_cols = _grid.cols;
+    _init_background ();
+    _restore_foreground ();
+
+    debug ("game restored successfully");
+    return true;
+  }
+
   public bool key_pressed (Gdk.EventKey event)
   {
     if (_state != GameState.IDLE) {
@@ -220,6 +268,7 @@ public class Game : GLib.Object
       _create_tile (tile);
       _to_show.add (tile);
       _show_tile (tile.pos);
+      _show_hide_trans.start ();
     }
   }
 
@@ -382,8 +431,6 @@ public class Game : GLib.Object
     trans.set_remove_on_complete (true);
     trans.set_duration (50);
     view.actor.add_transition ("show", trans);
-
-    _show_hide_trans.start ();
   }
 
   private void _move_tile (GridPosition from, GridPosition to)
@@ -463,9 +510,36 @@ public class Game : GLib.Object
     }
   }
 
+  private void _restore_foreground ()
+  {
+    uint val;
+    GridPosition pos;
+    Tile tile;
+
+    _create_show_hide_transition ();
+
+    for (int i = 0; i < _n_rows; i++) {
+      for (int j = 0; j < _n_cols; j++) {
+        val = _grid[i,j];
+        if (val != 0) {
+          pos = { i, j };
+          tile = { pos, val };
+          _create_tile (tile);
+          _to_show.add (tile);
+          _show_tile (pos);
+        }
+      }
+    }
+
+    if (_to_show.size > 0) {
+      _state = GameState.RESTORING_TILES;
+      _show_hide_trans.start ();
+    }
+  }
+
   private void _on_move_trans_stopped (bool is_finished)
   {
-    debug ("move animation stopped");
+    debug (@"move animation stopped; finished $is_finished");
     debug (@"$_grid");
 
     uint delta_score;
@@ -493,7 +567,7 @@ public class Game : GLib.Object
 
   private void _on_show_hide_trans_stopped (bool is_finished)
   {
-    debug ("show/hide animation stopped");
+    debug (@"show/hide animation stopped; finished $is_finished");
     debug (@"$_grid");
 
     _show_hide_trans.remove_all ();
diff --git a/src/grid.vala b/src/grid.vala
index 7a0d371..52133c4 100644
--- a/src/grid.vala
+++ b/src/grid.vala
@@ -18,24 +18,22 @@
 
 public class Grid : GLib.Object
 {
-  private int _n_rows;
-  private int _n_cols;
-
   private uint[,] _grid;
 
-  public Grid (int n_rows, int n_cols)
+  public Grid (int rows, int cols)
   {
-    Object ();
+    Object (rows: rows, cols: cols);
 
-    _n_rows = n_rows;
-    _n_cols = n_cols;
+    _grid = new uint[rows, cols];
+    clear ();
+  }
 
-    _grid = new uint[_n_rows, _n_cols];
+  public int rows {
+    get; set;
   }
 
-  construct
-  {
-    clear ();
+  public int cols {
+    get; set;
   }
 
   public void clear ()
@@ -87,16 +85,16 @@ public class Grid : GLib.Object
     to_hide.clear ();
     to_show.clear ();
 
-    for (int i = 0; i < _n_cols; i++) {
-      free = { _n_rows, i };
+    for (int i = 0; i < _cols; i++) {
+      free = { _rows, i };
 
-      for (int j = 0; j < _n_rows; j++) {
-        row = _n_rows - j - 1;
+      for (int j = 0; j < _rows; j++) {
+        row = _rows - j - 1;
         cur = { row, i };
         val = _grid[cur.row,cur.col];
 
         if (val == 0) {
-          if (free.row == _n_rows) {
+          if (free.row == _rows) {
             free.row = row;
           }
           continue;
@@ -120,7 +118,7 @@ public class Grid : GLib.Object
         if (has_match) {
           debug (@"matching tile found at $match");
 
-          if (free.row == _n_rows) {
+          if (free.row == _rows) {
             free.row = row; // temporarily
           }
           mov = { cur, free };
@@ -136,7 +134,7 @@ public class Grid : GLib.Object
           _grid[free.row,free.col] = val*2;
 
           free.row--;
-        } else if (free.row != _n_rows) {
+        } else if (free.row != _rows) {
           debug (@"moving $cur to $free");
 
           mov = { cur, free };
@@ -168,10 +166,10 @@ public class Grid : GLib.Object
     to_hide.clear ();
     to_show.clear ();
 
-    for (int i = 0; i < _n_cols; i++) {
+    for (int i = 0; i < _cols; i++) {
       free = { -1, i };
 
-      for (int j = 0; j < _n_rows; j++) {
+      for (int j = 0; j < _rows; j++) {
         row = j;
         cur = { row, i };
         val = _grid[cur.row,cur.col];
@@ -186,7 +184,7 @@ public class Grid : GLib.Object
         // search for matches
         match = { 0, 0 };
         has_match = false;
-        for (int k = row + 1; k < _n_rows; k++) {
+        for (int k = row + 1; k < _rows; k++) {
           uint k_val = _grid[k,cur.col];
 
           if (k_val != 0) {
@@ -249,10 +247,10 @@ public class Grid : GLib.Object
     to_hide.clear ();
     to_show.clear ();
 
-    for (int i = 0; i < _n_rows; i++) {
+    for (int i = 0; i < _rows; i++) {
       free = { i, -1 };
 
-      for (int j = 0; j < _n_cols; j++) {
+      for (int j = 0; j < _cols; j++) {
         col = j;
         cur = { i, col };
         val = _grid[cur.row,cur.col];
@@ -267,7 +265,7 @@ public class Grid : GLib.Object
         // search for matches
         match = { 0, 0 };
         has_match = false;
-        for (int k = col + 1; k < _n_rows; k++) {
+        for (int k = col + 1; k < _rows; k++) {
           uint k_val = _grid[cur.row,k];
 
           if (k_val != 0) {
@@ -330,16 +328,16 @@ public class Grid : GLib.Object
     to_hide.clear ();
     to_show.clear ();
 
-    for (int i = 0; i < _n_rows; i++) {
-      free = { i, _n_cols };
+    for (int i = 0; i < _rows; i++) {
+      free = { i, _cols };
 
-      for (int j = 0; j < _n_cols; j++) {
-        col = _n_cols - j - 1;
+      for (int j = 0; j < _cols; j++) {
+        col = _cols - j - 1;
         cur = { i, col };
         val = _grid[cur.row,cur.col];
 
         if (val == 0) {
-          if (free.col == _n_cols) {
+          if (free.col == _cols) {
             free.col = col;
           }
           continue;
@@ -363,7 +361,7 @@ public class Grid : GLib.Object
         if (has_match) {
           debug (@"matching tile found at $match");
 
-          if (free.col == _n_cols) {
+          if (free.col == _cols) {
             free.col = col; // temporarily
           }
           mov = { cur, free };
@@ -379,7 +377,7 @@ public class Grid : GLib.Object
           _grid[free.row,free.col] = val*2;
 
           free.col--;
-        } else if (free.col != _n_cols) {
+        } else if (free.col != _cols) {
           debug (@"moving $cur to $free");
 
           mov = { cur, free };
@@ -401,15 +399,15 @@ public class Grid : GLib.Object
     if (!_grid_is_full ())
       return false;
     else {
-      for (int i = 0; i < _n_rows; i++) {
-        for (int j = 0; j < _n_cols; j++) {
+      for (int i = 0; i < _rows; i++) {
+        for (int j = 0; j < _cols; j++) {
           val = _grid[i,j];
 
-          if (i < (_n_rows - 1))
+          if (i < (_rows - 1))
             if (val == _grid[i+1,j])
               return false;
 
-          if (j < (_n_cols - 1))
+          if (j < (_cols - 1))
             if (val == _grid[i,j+1])
               return false;
         }
@@ -419,24 +417,102 @@ public class Grid : GLib.Object
     return true;
   }
 
+  public uint get (int row, int col)
+  {
+    if ((row >= _rows) || (col >= _cols))
+      return 0;
+
+    return _grid[row,col];
+  }
+
+  public string save ()
+  {
+    string ret = "";
+
+    ret += _rows.to_string () + " ";
+    ret += _cols.to_string () + "\n";
+
+    ret += _convert_to_string ();
+
+    return ret;
+  }
+
+  public bool load (string content)
+  {
+    return _load_from_string (content);
+  }
+
   public string to_string ()
   {
+    string ret = "\n";
+    ret += _convert_to_string ();
+    return ret;
+  }
+
+  private string _convert_to_string ()
+  {
     string ret = "";
 
-    for (uint i = 0; i < _n_rows; i++) {
-      ret += "\n";
-      for (uint j = 0; j < _n_cols; j++) {
-        ret += " " + _grid[i,j].to_string () + " ";
+    for (uint i = 0; i < _rows; i++) {
+      for (uint j = 0; j < _cols; j++) {
+        ret += "%u%s".printf (_grid[i,j], (j == (_cols-1)) ? "\n" : " ");
       }
     }
 
     return ret;
   }
 
+  private bool _load_from_string (string contents)
+  {
+    int rows = 0;
+    int cols = 0;
+    string[] lines;
+    string[] tokens;
+    uint[,] grid;
+
+    lines = contents.split ("\n");
+
+    // check that at least it contains 2 rows
+    if (lines.length < 3)
+      return false;
+
+    tokens = lines[0].split (" ");
+    if (tokens.length != 2)
+      return false;
+
+    rows = int.parse (tokens[0]);
+    cols = int.parse (tokens[1]);
+
+    if ((rows < 2) || (cols < 2))
+      return false;
+    // we don't need to be strict here
+    if (lines.length < (rows+1))
+      return false;
+
+    grid = new uint[rows, cols];
+
+    for (int i = 0; i < rows; i++) {
+      tokens = lines[i+1].split (" ");
+      // we do need to be strict here
+      if (tokens.length != cols)
+        return false;
+
+      for (int j = 0; j < cols; j++) {
+        grid[i,j] = int.parse (tokens[j]);
+      }
+    }
+
+    _rows = rows;
+    _cols = cols;
+    _grid = grid;
+
+    return true;
+  }
+
   private bool _grid_is_full ()
   {
-    for (uint i = 0; i < _n_rows; i++) {
-      for (uint j = 0; j < _n_cols; j++) {
+    for (uint i = 0; i < _rows; i++) {
+      for (uint j = 0; j < _cols; j++) {
         if (_grid[i,j] == 0) {
           return false;
         }
@@ -448,8 +524,8 @@ public class Grid : GLib.Object
 
   private GridPosition _random_position ()
   {
-    GridPosition ret = { Random.int_range (0, (int)_n_rows),
-                         Random.int_range (0, (int)_n_cols) };
+    GridPosition ret = { Random.int_range (0, (int)_rows),
+                         Random.int_range (0, (int)_cols) };
 
     return ret;
   }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]