[gnome-2048] Add CLI.
- From: Arnaud B. <arnaudb src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-2048] Add CLI.
- Date: Fri, 15 Nov 2019 13:53:42 +0000 (UTC)
commit 2523b2638e588e6934baa0cf3d8443c628db6ebf
Author: Arnaud Bonatti <arnaud bonatti gmail com>
Date: Tue Oct 29 23:33:14 2019 +0100
Add CLI.
data/gnome-2048.6 | 3 +
src/application.vala | 43 ++++++++-
src/cli.vala | 257 +++++++++++++++++++++++++++++++++++++++++++++++++++
src/game.vala | 8 +-
src/grid.vala | 4 +
src/meson.build | 1 +
6 files changed, 307 insertions(+), 9 deletions(-)
---
diff --git a/data/gnome-2048.6 b/data/gnome-2048.6
index bd461a1..c5cb1ad 100644
--- a/data/gnome-2048.6
+++ b/data/gnome-2048.6
@@ -30,6 +30,9 @@ Use your keyboard’s arrow keys or gestures to slide all tiles in the desired d
Gnome 2048 only adds tiles of value 2, as opposed to the original 2048 game. Also, it allows you to play on
grids of various size.
.SH OPTIONS
.TP
+.B \-\-cli=<command>
+Play in the terminal. See “--cli=help” for documentation.
+.TP
.B \-s, \-\-size=<size>
Changes the size of the grid. Size must be between 2 and 9, or in the form 2x3.
.TP
diff --git a/src/application.vala b/src/application.vala
index b91c1ab..f402e65 100644
--- a/src/application.vala
+++ b/src/application.vala
@@ -26,22 +26,35 @@ private class TwentyFortyEight : Gtk.Application
private static bool show_version;
private static string? size = null;
+ private static string? cli = null;
private uint8 cols = 0;
private uint8 rows = 0;
private const OptionEntry [] option_entries =
{
/* Translators: command-line option description, see 'gnome-2048 --help' */
- { "size", 's', OptionFlags.NONE, OptionArg.STRING, ref size, N_("Start new game of
given size"),
+ { "cli", 0, OptionFlags.OPTIONAL_ARG, OptionArg.CALLBACK, (void*) _cli, N_("Play in the
terminal (see “--cli=help”)"),
- /* Translators: in the command-line options description, text to indicate the user should specify a
size, see 'gnome-2048 --help' */
- N_("SIZE") },
+ /* Translators: in the command-line options description, text to indicate the user should give a
command after '--cli' for playing in the terminal, see 'gnome-2048 --help' */
+ N_("COMMAND") },
/* Translators: command-line option description, see 'gnome-2048 --help' */
- { "version", 'v', OptionFlags.NONE, OptionArg.NONE, ref show_version, N_("Print release
version and exit"), null },
+ { "size", 's', OptionFlags.NONE, OptionArg.STRING, ref size, N_("Start new game
of given size"),
+
+ /* Translators: in the command-line options description, text to indicate the user should specify a
size after '--size', see 'gnome-2048 --help' */
+ N_("SIZE") },
+
+ /* Translators: command-line option description, see 'gnome-2048 --help' */
+ { "version", 'v', OptionFlags.NONE, OptionArg.NONE, ref show_version, N_("Print release
version and exit"), null },
{}
};
+ private bool _cli (string? option_name, string? val)
+ {
+ cli = option_name == null ? "" : (!) option_name; // TODO report bug: should probably be val...
+ return true;
+ }
+
private const GLib.ActionEntry [] action_entries =
{
{ "quit", quit_cb }
@@ -94,7 +107,7 @@ private class TwentyFortyEight : Gtk.Application
Object (application_id: "org.gnome.TwentyFortyEight", flags: ApplicationFlags.FLAGS_NONE);
}
- protected override int handle_local_options (GLib.VariantDict options)
+ protected override int handle_local_options (GLib.VariantDict options) // options will be empty, we
used a custom OptionContext
{
if (show_version)
{
@@ -110,6 +123,26 @@ private class TwentyFortyEight : Gtk.Application
return Posix.EXIT_FAILURE;
}
+ if (cli != null)
+ {
+ if ((!) cli == "help" || (!) cli == "HELP")
+ {
+ string help_string = ""
+ + "\n" + "To play GNOME 2048 in command-line:"
+ + "\n" + " --cli " + "Display current game. Alias: “status” or “show”."
+ + "\n" + " --cli new " + "Start a new game; for changing size, use --size."
+ + "\n"
+ + "\n" + " --cli up " + "Move tiles up. Alias: “u”."
+ + "\n" + " --cli down " + "Move tiles down. Alias: “d”."
+ + "\n" + " --cli left " + "Move tiles left. Alias: “l”."
+ + "\n" + " --cli right " + "Move tiles right. Alias: “r”."
+ + "\n\n";
+ stdout.printf (help_string);
+ return Posix.EXIT_SUCCESS;
+ }
+ return CLI.play_cli ((!) cli, "org.gnome.TwentyFortyEight", ref cols, ref rows);
+ }
+
/* Activate */
return -1;
}
diff --git a/src/cli.vala b/src/cli.vala
new file mode 100644
index 0000000..aedb527
--- /dev/null
+++ b/src/cli.vala
@@ -0,0 +1,257 @@
+/*
+ This file is part of GNOME 2048.
+
+ Copyright (C) 2019 Arnaud Bonatti <arnaud bonatti gmail com>
+
+ GNOME 2048 is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ GNOME 2048 is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNOME 2048. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+namespace CLI
+{
+ private static int play_cli (string cli, string schema_name, ref uint8 cols, ref uint8 rows)
+ {
+ if ((cols != 0 || rows != 0) && cli != "new")
+ {
+ warning ("Size can only be given for new games." + "\n");
+ return Posix.EXIT_FAILURE;
+ }
+
+ string saved_path = Path.build_filename (Environment.get_user_data_dir (), "gnome-2048", "saved");
+
+ GLib.Settings settings = new GLib.Settings (schema_name);
+
+ bool new_game;
+ Grid grid;
+ if (cols != 0 || rows != 0)
+ {
+ if (cols == 0 || rows == 0)
+ assert_not_reached ();
+
+ settings.delay ();
+ settings.set_int ("cols", cols);
+ settings.set_int ("rows", rows);
+ settings.apply ();
+ GLib.Settings.sync ();
+
+ grid = new Grid (rows, cols);
+ new_game = true;
+ }
+ else
+ {
+ cols = (uint8) settings.get_int ("cols"); // schema ranges rows
+ rows = (uint8) settings.get_int ("rows"); // and cols from 1 to 9
+
+ grid = new Grid (rows, cols);
+ if (cli == "new")
+ new_game = true;
+ else if (!grid.restore_game (saved_path))
+ new_game = true;
+ else
+ {
+ new_game = false;
+ cols = grid.cols;
+ rows = grid.rows;
+ }
+ }
+ grid.target_value = (uint) settings.get_int ("target-value");
+
+ if (new_game)
+ {
+ Tile tile;
+ grid.new_tile (out tile); // TODO clean that
+ }
+
+ switch (cli)
+ {
+ case "help":
+ case "HELP":
+ assert_not_reached (); // should be handled by the caller
+
+ case "":
+ case "show":
+ case "status":
+ if (new_game)
+ break;
+
+ print_board (cols, rows, grid, /* do congrat */ false, /* print score */ true);
+ return Posix.EXIT_SUCCESS;
+
+ case "new": // creation already handled, need saving
+ break;
+
+ case "r":
+ case "right":
+ if (!request_move (grid, MoveRequest.RIGHT))
+ return Posix.EXIT_FAILURE;
+ break;
+
+ case "l":
+ case "left":
+ if (!request_move (grid, MoveRequest.LEFT))
+ return Posix.EXIT_FAILURE;
+ break;
+
+ case "u":
+ case "up":
+ if (!request_move (grid, MoveRequest.UP))
+ return Posix.EXIT_FAILURE;
+ break;
+
+ case "d":
+ case "down":
+ if (!request_move (grid, MoveRequest.DOWN))
+ return Posix.EXIT_FAILURE;
+ break;
+
+ default:
+ warning ("Cannot parse “--cli” command, aborting." + "\n");
+ return Posix.EXIT_FAILURE;
+ }
+
+ Tile? new_tile = null;
+ if (!grid.is_finished ())
+ {
+ grid.new_tile (out new_tile);
+ if (cli == "new")
+ new_tile = null;
+ }
+
+ bool do_congrat = settings.get_boolean ("do-congrat");
+ if (do_congrat && grid.target_value_reached)
+ settings.set_boolean ("do-congrat", false);
+
+ print_board (cols, rows, grid, do_congrat, /* print score */ false, new_tile);
+
+ if (!grid.is_finished () // one more tile since previously
+ || grid.cols != grid.rows
+ || grid.cols < 3 || grid.cols > 5)
+ grid.save_game (saved_path);
+
+ return Posix.EXIT_SUCCESS;
+ }
+
+ /*\
+ * * move request
+ \*/
+
+ private static bool request_move (Grid grid, MoveRequest req)
+ {
+ if (!can_play (grid))
+ return false;
+
+ Gee.LinkedList<TileMovement?> to_move = new Gee.LinkedList<TileMovement?> ();
+ Gee.LinkedList<TileMovement?> to_hide = new Gee.LinkedList<TileMovement?> ();
+ Gee.LinkedList<Tile?> to_show = new Gee.LinkedList<Tile?> ();
+
+ grid.move (req, ref to_move, ref to_hide, ref to_show); // TODO do not request so many unused things
+ if (!has_moves (ref to_move, ref to_hide))
+ return false;
+
+ return true;
+ }
+
+ private static inline bool can_play (Grid grid)
+ {
+ if (!grid.is_finished ())
+ return true;
+
+ warning ("Grid is finished, impossible to move." + "\n");
+ return false;
+ }
+
+ private static inline bool has_moves (ref Gee.LinkedList<TileMovement?> to_move,
+ ref Gee.LinkedList<TileMovement?> to_hide)
+ {
+ if (to_move.size != 0 || to_hide.size != 0)
+ return true;
+
+ warning ("Impossible to move in that direction." + "\n");
+ return false;
+ }
+
+ /*\
+ * * print board
+ \*/
+
+ private static void print_board (uint8 cols, uint8 rows, Grid grid, bool do_congrat, bool print_score,
Tile? new_tile = null)
+ {
+ string board = "";
+
+ board += "\n ┏";
+ for (uint8 i = 0; i <= 7 * cols; i++)
+ board += "━";
+ board += "┓\n";
+
+ for (uint8 y = 0; y < rows; y++)
+ {
+ board += " ┃";
+ for (uint8 x = 0; x < cols; x++)
+ {
+ if (grid [y, x] == 0) // FIXME inverted coordinates
+ board += " ";
+ else
+ board += " ╭────╮";
+ }
+ board += " ┃\n ┃";
+ for (uint8 x = 0; x < cols; x++)
+ {
+ uint8 tile_value = grid [y, x];
+ if (tile_value == 0)
+ board += " ";
+ else
+ {
+ string tile_value_string = tile_value.to_string ();
+ if (tile_value == 1 && new_tile != null && ((!) new_tile).pos.col == x && ((!)
new_tile).pos.row == y)
+ board += " │ +1 │";
+ else if (tile_value_string.length == 1)
+ board += @" │ $tile_value_string │";
+ else if (tile_value_string.length == 2)
+ board += @" │ $tile_value_string │";
+ else assert_not_reached ();
+ }
+ }
+ board += " ┃\n ┃";
+ for (uint8 x = 0; x < cols; x++)
+ {
+ if (grid [y, x] == 0)
+ board += " ";
+ else
+ board += " ╰────╯";
+ }
+ board += " ┃\n";
+ }
+
+ board += " ┗";
+ for (uint8 i = 0; i <= 7 * cols; i++)
+ board += "━";
+ board += "┛\n\n";
+
+ if (do_congrat && grid.target_value_reached) // try to keep string as in game-window.vala
+ board += " " + "You have obtained the %u tile for the first time!".printf
(grid.target_value_simple) + "\n\n";
+
+ if (grid.is_finished ())
+ {
+ if (print_score // called from “--cli show”
+ || grid.cols != grid.rows
+ || grid.cols < 3 || grid.cols > 5)
+ board += @" Game is finished! Your score is $(grid.get_score ()).\n\n";
+ else // game was just finished and score can be saved
+ board += @" Game is finished! Your score is $(grid.get_score ()). (If you want to save it,
use GNOME 2048 graphical interface.)\n\n"; // TODO save score
+ }
+ else if (print_score)
+ board += @" Your score is $(grid.get_score ()).\n\n";
+
+ stdout.printf (board);
+ }
+}
diff --git a/src/game.vala b/src/game.vala
index 1150777..d2fca7a 100644
--- a/src/game.vala
+++ b/src/game.vala
@@ -68,8 +68,8 @@ private class Game : Object
internal Game (ref GLib.Settings settings)
{
- uint8 rows = (uint8) settings.get_int ("rows"); // schema ranges rows
- uint8 cols = (uint8) settings.get_int ("cols"); // and cols from 1 to 9
+ uint8 cols = (uint8) settings.get_int ("cols"); // schema ranges cols
+ uint8 rows = (uint8) settings.get_int ("rows"); // and rows from 1 to 9
_init_grid (rows, cols, out _grid, ref settings);
}
@@ -127,8 +127,8 @@ private class Game : Object
_grid.clear ();
_clear_history ();
- uint8 rows = (uint8) settings.get_int ("rows"); // schema ranges rows
- uint8 cols = (uint8) settings.get_int ("cols"); // and cols from 1 to 9
+ uint8 cols = (uint8) settings.get_int ("cols"); // schema ranges cols
+ uint8 rows = (uint8) settings.get_int ("rows"); // and rows from 1 to 9
if ((rows != _grid.rows) || (cols != _grid.cols))
{
diff --git a/src/grid.vala b/src/grid.vala
index 3e9356f..c726e6a 100644
--- a/src/grid.vala
+++ b/src/grid.vala
@@ -26,6 +26,7 @@ private class Grid : Object
[CCode (notify = false)] public uint8 cols { internal get; protected construct; }
[CCode (notify = false)] internal uint target_value { internal get; internal set; default = 0; }
+ [CCode (notify = false)] internal uint target_value_simple { internal get; private set; default = 0; }
[CCode (notify = false)] internal bool target_value_reached { internal get; internal set; default =
false; }
construct
@@ -91,7 +92,10 @@ private class Grid : Object
_move_right ((int8) _cols, (int8) _rows, ref max_changed, ref _grid, ref to_move, ref
to_hide, ref to_show); break;
}
if (Math.pow (2, max_changed) >= target_value)
+ {
+ target_value_simple = max_changed;
target_value_reached = true;
+ }
}
private static void _move_down (int8 cols,
diff --git a/src/meson.build b/src/meson.build
index b362553..f77b198 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -23,6 +23,7 @@ resources = gnome.compile_resources(
gnome_2048_sources = [
'application.vala',
+ 'cli.vala',
'config.vapi',
'game.vala',
'game-headerbar.vala',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]