[gnome-sudoku/vala-port] Implemented save game feature
- From: Michael Catanzaro <mcatanzaro src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-sudoku/vala-port] Implemented save game feature
- Date: Tue, 13 May 2014 15:55:37 +0000 (UTC)
commit 7dcf1da8bd8b9819d8b1f6ecf8d37b6297065cc4
Author: Parin Porecha <parinporecha gmail com>
Date: Fri May 9 15:08:19 2014 +0530
Implemented save game feature
Created SudokuSaver class in sudoku-saver.vala
https://bugzilla.gnome.org/show_bug.cgi?id=633464
TODO | 12 +--
configure.ac | 1 +
data/gnome-sudoku.ui | 35 ++++++++++
src/Makefile.am | 4 +-
src/gnome-sudoku.vala | 35 +++++++++-
src/sudoku-board.vala | 18 +++++
src/sudoku-game.vala | 5 ++
src/sudoku-saver.vala | 179 +++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 279 insertions(+), 10 deletions(-)
---
diff --git a/TODO b/TODO
index b3529a4..2188932 100644
--- a/TODO
+++ b/TODO
@@ -31,17 +31,13 @@ mcatanzaro: Too confusing in general:
mouse over the text to see anything, and even then there's no explanation of
what it is.
-
-No way to save a game
-
-No way to know if a game has already been played or not:
- For this SudokuTracker class needs to be ported. Once it is complete, enable the corresponding options -
+enable the options -
'MarkAsPlayedToggle' and 'includeOldGamesToggle' in data/print_games.ui (for multiple print dialog)
Currently we only read sudokus from data files:
We need to complete the port of SudokuGenerator class to be able to generate sudokus as desired.
-Port saver.py for saving games, fullscreen mode feature, highlighter feature
+Port fullscreen mode feature, highlighter feature
mcatanzaro: We're removing Fullscreen from most other games because it usually
looks pretty bad. Games maximized with the header bar usually fit
@@ -61,8 +57,6 @@ mcatanzaro: This number pad was from an earlier design; I think we can remove
- The preview window does not resize to it's original size (the size at startup) after 'New Game' is clicked.
-- No way to save a game (port saver.py)
-
- Sudoku master has total 19 menu options (8 in 'Game', 5 in 'Settings', 4 in 'Tools', 2 in 'Help')
Current vala-port design and mockups have only one panel menu and it cannot accomodate all the 19 options
Ask aday about this.
@@ -78,3 +72,5 @@ mcatanzaro: This number pad was from an earlier design; I think we can remove
Instead, they are displayed at random positions in the cells (looks random, maybe there is a bug in
display offsets)
- Print (single sudoku) should be disabled when not in-game
+
+- Message Dialogs haven't been ported. Port gtk_goodies/dialog_extras.py
diff --git a/configure.ac b/configure.ac
index 81358cd..5e66425 100644
--- a/configure.ac
+++ b/configure.ac
@@ -21,6 +21,7 @@ PKG_CHECK_MODULES(GNOME_SUDOKU, [
gio-2.0
gtk+-3.0 >= $GTK_REQUIRED
gee-0.8
+ json-glib-1.0
])
AC_PATH_PROG([APPDATA_VALIDATE], [appdata-validate], [/bin/true])
diff --git a/data/gnome-sudoku.ui b/data/gnome-sudoku.ui
index ba1bc34..55ca191 100644
--- a/data/gnome-sudoku.ui
+++ b/data/gnome-sudoku.ui
@@ -153,6 +153,41 @@
<property name="position">2</property>
</packing>
</child>
+ <child>
+ <object class="GtkBox" id="savegame_box">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkBox" id="savegame_grid">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkLabel" id="savegame_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Latest
Saved</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">20</property>
+ </packing>
+ </child>
+ </object> <!-- End of save game box -->
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ <property name="padding">20</property>
+ </packing>
+ </child>
</object> <!-- End of new start_box -->
</child>
<child>
diff --git a/src/Makefile.am b/src/Makefile.am
index a010fdd..f84ed60 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -15,6 +15,7 @@ gnome_sudoku_SOURCES = \
sudoku-view.vala \
number-picker.vala \
sudoku-printer.vala \
+ sudoku-saver.vala \
$(BUILT_SOURCES)
gnome_sudoku_CFLAGS = \
@@ -32,7 +33,8 @@ gnome_sudoku_VALAFLAGS = \
--pkg posix \
--pkg gio-2.0 \
--pkg gtk+-3.0 \
- --pkg gee-0.8
+ --pkg gee-0.8 \
+ --pkg json-glib-1.0
gnome-sudoku-resources.c: gnome-sudoku.gresource.xml $(shell $(GLIB_COMPILE_RESOURCES)
--generate-dependencies gnome-sudoku.gresource.xml)
$(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) --generate-source $<
diff --git a/src/gnome-sudoku.vala b/src/gnome-sudoku.vala
index 72da433..171b3a0 100644
--- a/src/gnome-sudoku.vala
+++ b/src/gnome-sudoku.vala
@@ -28,9 +28,12 @@ public class Sudoku : Gtk.Application
private SudokuView medium_preview;
private SudokuView hard_preview;
private SudokuView very_hard_preview;
+ private SudokuView savegame_preview;
private SudokuStore sudoku_store;
+ private SudokuSaver saver;
+
// Help Stuff
private Box naked_single_items;
private Box hidden_single_items;
@@ -108,12 +111,14 @@ public class Sudoku : Gtk.Application
});
sudoku_store = new SudokuStore ();
+ saver = new SudokuSaver ();
//SudokuGenerator gen = new SudokuGenerator();
var easy_grid = (Box) builder.get_object ("easy_grid");
var medium_grid = (Box) builder.get_object ("medium_grid");
var hard_grid = (Box) builder.get_object ("hard_grid");
var very_hard_grid = (Box) builder.get_object ("very_hard_grid");
+ var savegame_grid = (Box) builder.get_object ("savegame_grid");
var easy_board = sudoku_store.get_random_easy_board ();
//gen.make_symmetric_puzzle(Random.int_range(0, 4));
@@ -171,11 +176,35 @@ public class Sudoku : Gtk.Application
return false;
});
+ // FIXME: Remove the preview and directly start the game instead
+ var savegame = saver.get_savedgame ();
+ if (savegame == null)
+ savegame = new SudokuGame (sudoku_store.get_random_easy_board ());
+ //gen.make_symmetric_puzzle(Random.int_range(0, 4));
+ // gen.generate (DifficultyRating.medium_range);
+ savegame_preview = new SudokuView (savegame, true);
+ savegame_preview.show ();
+ savegame_grid.pack_start (savegame_preview);
+
+ savegame_grid.button_press_event.connect ((event) => {
+ if (event.button == 1)
+ start_game (savegame.board);
+
+ return false;
+ });
+
show_start ();
builder.connect_signals (this);
window.show ();
+
+ window.delete_event.connect ((event) => {
+ if (game_box.visible)
+ saver.save_game (game);
+
+ return false;
+ });
}
private void start_game (SudokuBoard board)
@@ -234,7 +263,7 @@ public class Sudoku : Gtk.Application
game.board.completed.connect (() => {
view.dance ();
- var time = game.timer.elapsed ();
+ var time = game.get_total_time_played ();
for (var i = 0; i < game.board.rows; i++)
{
@@ -244,6 +273,8 @@ public class Sudoku : Gtk.Application
}
}
+ saver.add_game_to_finished (game);
+
var dialog = new MessageDialog(null, DialogFlags.DESTROY_WITH_PARENT, MessageType.INFO,
ButtonsType.NONE, "Well done, you completed the puzzle in %f seconds", time);
dialog.add_button ("Same difficulty again", 0);
@@ -474,6 +505,8 @@ public class Sudoku : Gtk.Application
public void quit_cb ()
{
+ if (game_box.visible)
+ saver.save_game (game);
window.destroy ();
}
diff --git a/src/sudoku-board.vala b/src/sudoku-board.vala
index 0569cca..95f6ba1 100644
--- a/src/sudoku-board.vala
+++ b/src/sudoku-board.vala
@@ -12,6 +12,8 @@ public class SudokuBoard
private bool[,] possible_in_col; /* if specific value is possible in specific col */
private bool[,,] possible_in_block; /* if specific value is possible in specific block */
+ public double previous_played_time { set; get; default = 0; }
+
/* Number of rows in one block */
private int _block_rows;
public int block_rows
@@ -471,6 +473,22 @@ public class SudokuBoard
stdout.printf (" ]");
}
+ public string to_string (bool get_original_state = false)
+ {
+ var board_string = "";
+ for (var i = 0; i < _rows; i++)
+ {
+ for (var j = 0; j < _cols; j++)
+ {
+ if (is_fixed[i, j])
+ board_string += cells[i, j].to_string ();
+ else
+ board_string += get_original_state ? "0" : cells[i, j].to_string ();
+ }
+ }
+ return board_string;
+ }
+
public int[,] get_cells()
{
return cells;
diff --git a/src/sudoku-game.vala b/src/sudoku-game.vala
index 42c2473..005d1fa 100644
--- a/src/sudoku-game.vala
+++ b/src/sudoku-game.vala
@@ -155,4 +155,9 @@ public class SudokuGame
add_to_stack (ref to, -1, -1, num);
}
}
+
+ public double get_total_time_played ()
+ {
+ return board.previous_played_time + timer.elapsed ();
+ }
}
diff --git a/src/sudoku-saver.vala b/src/sudoku-saver.vala
new file mode 100644
index 0000000..19c662e
--- /dev/null
+++ b/src/sudoku-saver.vala
@@ -0,0 +1,179 @@
+/* -*- Mode: vala; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+using Gee;
+
+public class SudokuSaver
+{
+ private string savegame_file;
+ private string finishgame_dir;
+
+ public SudokuSaver() {
+ try {
+
+ var config_dir = Environment.get_user_data_dir ();
+ var sudoku_data_dir = Path.build_path (Path.DIR_SEPARATOR_S, config_dir, "gnome-sudoku");
+ savegame_file = Path.build_path (Path.DIR_SEPARATOR_S, sudoku_data_dir, "savefile");
+ finishgame_dir = Path.build_path (Path.DIR_SEPARATOR_S, sudoku_data_dir, "finished");
+
+ {
+ var file = File.new_for_path (sudoku_data_dir);
+ if (!file.query_exists ())
+ file.make_directory ();
+
+ file = File.new_for_path (finishgame_dir);
+ if (!file.query_exists ())
+ file.make_directory ();
+ }
+ } catch (Error e) {
+ warning ("%s", e.message);
+ }
+ }
+
+ public SudokuGame? get_savedgame ()
+ {
+ var file = File.new_for_path (savegame_file);
+ if (!file.query_exists ())
+ return null;
+
+ return parse_json_to_game (savegame_file);
+ }
+
+ public void save_game (SudokuGame game)
+ {
+ create_file_for_game (game, savegame_file);
+ }
+
+ public void add_game_to_finished (SudokuGame game)
+ {
+ var file_name = game.board.to_string (true) + ".save";
+ var file_path = Path.build_path (Path.DIR_SEPARATOR_S, finishgame_dir, file_name);
+ create_file_for_game (game, file_path);
+
+ // Delete savegame file
+ var file = File.new_for_path (savegame_file);
+ if (file.query_exists ())
+ file.delete ();
+ }
+
+ private void create_file_for_game (SudokuGame game, string file_name)
+ {
+ var json_str = serialize_game_to_json (game);
+ var file = File.new_for_path (file_name);
+
+ try {
+ FileUtils.set_contents (file_name, json_str);
+ } catch (Error e) {
+ warning ("%s", e.message);
+ }
+ }
+
+ private string serialize_game_to_json (SudokuGame game)
+ {
+ var board = game.board;
+ var board_cells = board.get_cells ();
+ Json.Builder builder = new Json.Builder ();
+
+ builder.begin_object ();
+ builder.set_member_name ("difficulty_rating");
+ builder.add_double_value (board.difficulty_rating);
+ builder.set_member_name ("difficulty_catagory");
+ builder.add_string_value (board.get_difficulty_catagory ().to_string ());
+ builder.set_member_name ("time_elapsed");
+ builder.add_double_value (game.get_total_time_played ());
+
+ builder.set_member_name ("cells");
+ builder.begin_array ();
+
+ for (var i = 0; i < board.rows; i++)
+ {
+ for (var j = 0; j < board.cols; j++)
+ {
+ if (board_cells[i, j] == 0)
+ continue;
+
+ builder.begin_object ();
+
+ builder.set_member_name ("position");
+ builder.begin_array ();
+ builder.add_int_value (i);
+ builder.add_int_value (j);
+ builder.end_array ();
+ builder.set_member_name ("value");
+ builder.add_int_value (board_cells[i, j]);
+ builder.set_member_name ("fixed");
+ builder.add_boolean_value (board.is_fixed[i, j]);
+ builder.set_member_name ("notes");
+ builder.add_string_value ("");
+
+ builder.end_object ();
+ }
+ }
+
+ builder.end_array ();
+ builder.end_object ();
+
+ Json.Generator generator = new Json.Generator ();
+ generator.set_pretty (true);
+ Json.Node root = builder.get_root ();
+ generator.set_root (root);
+
+ return generator.to_data (null);
+ }
+
+ private SudokuGame? parse_json_to_game (string file_path)
+ {
+ Json.Parser parser = new Json.Parser ();
+ try {
+ parser.load_from_file (file_path);
+ } catch (Error e) {
+ return null;
+ }
+
+ var board = new SudokuBoard ();
+ Json.Node node = parser.get_root ();
+ Json.Reader reader = new Json.Reader (node);
+ reader.read_member ("cells");
+ assert (reader.is_array ());
+
+ for (var i = 0; i < reader.count_elements (); i++)
+ {
+ reader.read_element (i); // Reading a cell
+
+ reader.read_member ("position");
+ return_val_if_fail (reader.is_array (), null);
+ return_val_if_fail (reader.count_elements () == 2, null);
+ reader.read_element (0);
+ return_val_if_fail (reader.is_value (), null);
+ var row = (int) reader.get_int_value ();
+ reader.end_element ();
+
+ reader.read_element (1);
+ return_val_if_fail (reader.is_value (), null);
+ var col = (int) reader.get_int_value ();
+ reader.end_element ();
+ reader.end_member ();
+
+ reader.read_member ("value");
+ return_val_if_fail (reader.is_value (), null);
+ var val = (int) reader.get_int_value ();
+ reader.end_member ();
+
+ reader.read_member ("fixed");
+ return_val_if_fail (reader.is_value (), null);
+ var is_fixed = reader.get_boolean_value ();
+ reader.end_member ();
+
+ reader.end_element ();
+
+ board.insert (row, col, val, is_fixed);
+ }
+ reader.end_member ();
+
+ reader.read_member ("time_elapsed");
+ return_val_if_fail (reader.is_value (), null);
+ board.previous_played_time = reader.get_double_value ();
+ reader.end_member ();
+
+ return new SudokuGame (board);
+ }
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]