[gnome-games/glines-vala] Add the game model classes



commit feeac7826775d16903c7b08a2c22c2ea42d689c1
Author: Thomas Hindoe Paaboel Andersen <phomes gmail com>
Date:   Sun Jun 10 17:04:33 2012 +0200

    Add the game model classes
    
    Adds board, field, piece, and preview queue.
    
    Also adds a CLI game mode to test the model

 configure.ac                         |    7 +
 glines/src/Makefile.am               |    8 +
 glines/src/glines-application.vala   |   13 +-
 glines/src/glines-board.vala         |  313 ++++++++++++++++++++++++++++++++++
 glines/src/glines-field.vala         |   31 ++++
 glines/src/glines-piece.vala         |   12 ++
 glines/src/glines-preview-queue.vala |   30 ++++
 glines/src/glines-view-cli.vala      |  144 ++++++++++++++++
 8 files changed, 553 insertions(+), 5 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 9b4f7e0..ed1e1e6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -249,6 +249,7 @@ LIBCANBERRA_GTK_REQUIRED=0.26
 GIO_REQUIRED=2.25.7
 GSTREAMER_REQUIRED=0.10.11
 CAIRO_REQUIRED=1.10.0
+GEE_REQUIRED=0.6.0
 
 if test "$need_gmenu" = "yes"; then
   GTK_REQUIRED=3.3.11
@@ -274,6 +275,12 @@ PKG_CHECK_MODULES([RSVG],[
 AC_SUBST([RSVG_CFLAGS])
 AC_SUBST([RSVG_LIBS])
 
+if test "$need_vala" = "yes"; then
+  PKG_CHECK_MODULES([GEE],[gee-1.0 >= $GEE_REQUIRED])
+  AC_SUBST([GEE_CFLAGS])
+  AC_SUBST([GEE_LIBS])
+fi
+
 # GDBus
 
 PKG_CHECK_MODULES([GIO],[gio-2.0 >= $GIO_REQUIRED])
diff --git a/glines/src/Makefile.am b/glines/src/Makefile.am
index 40627bd..47aec5f 100644
--- a/glines/src/Makefile.am
+++ b/glines/src/Makefile.am
@@ -6,6 +6,11 @@ glines_SOURCES = \
 	config.vapi \
 	main.vala \
 	glines-application.vala \
+	glines-board.vala \
+	glines-field.vala \
+	glines-piece.vala \
+	glines-preview-queue.vala \
+	glines-view-cli.vala \
 	$(BUILT_SOURCES)
 
 glines_CPPFLAGS = \
@@ -20,6 +25,7 @@ glines_CFLAGS = \
 	$(GMODULE_CFLAGS) \
 	$(GTK_CFLAGS) \
 	$(RSVG_CFLAGS) \
+	$(GEE_CFLAGS) \
 	$(AM_CFLAGS)
 
 glines_LDADD =	\
@@ -27,6 +33,7 @@ glines_LDADD =	\
 	$(GMODULE_LIBS) \
 	$(GTK_LIBS) \
 	$(RSVG_LIBS) \
+	$(GEE_LIBS) \
 	$(INTLLIBS) \
 	-lm
 
@@ -34,6 +41,7 @@ glines_VALAFLAGS = \
 	--pkg posix \
 	--pkg gtk+-3.0 \
 	--pkg gmodule-2.0 \
+	--pkg gee-1.0 \
 	--vapidir $(top_srcdir)/libgames-support \
 	--pkg GnomeGamesSupport-1.0
 
diff --git a/glines/src/glines-application.vala b/glines/src/glines-application.vala
index 3d19201..cb28587 100644
--- a/glines/src/glines-application.vala
+++ b/glines/src/glines-application.vala
@@ -27,7 +27,7 @@ namespace Glines
         };
     
         private const string[] authors = { "Thomas Andersen <phomes gmail com>", "Robert Szokovacs <szo appaloosacorp hu>", "Szabolcs B\xc3\xa1n <shooby gnome hu>" };
-        private const string[] documenters = { "Tiffany Antopolski", "Lanka Rathnayaka" };
+        //private const string[] documenters = { "Tiffany Antopolski", "Lanka Rathnayaka" };
 
         public GlinesApp ()
         {
@@ -79,6 +79,9 @@ namespace Glines
 
         public override void activate ()
         {
+            var v = new ViewCli ();
+            v.run();
+
             window.present ();
         }
 
@@ -91,10 +94,10 @@ namespace Glines
         {
             stdout.printf ("FIXME: Showing scores does not currently work\n");
 
-            var scores_dialog = new GnomeGamesSupport.ScoresDialog (window, highscores, _("GNOME Five or More"));
-            scores_dialog.set_category_description (_("_Board size:"));
-            scores_dialog.run ();
-            scores_dialog.destroy ();
+            //var scores_dialog = new GnomeGamesSupport.ScoresDialog (window, highscores, _("GNOME Five or More"));
+            //scores_dialog.set_category_description (_("_Board size:"));
+            //scores_dialog.run ();
+            //scores_dialog.destroy ();
         }
 
         private void preferences_cb ()
diff --git a/glines/src/glines-board.vala b/glines/src/glines-board.vala
new file mode 100644
index 0000000..5dce374
--- /dev/null
+++ b/glines/src/glines-board.vala
@@ -0,0 +1,313 @@
+using Gee;
+
+namespace Glines
+{
+    public class GlinesBoard
+    {
+        public int cols { get; private set; }
+        public int rows { get; private set; }
+
+        public GlinesField[,] fields { get; private set; }
+        public GlinesField active_field = null;
+
+        public GlinesPreviewQueue preview_queue;
+
+        public bool show_cursor { get; set; }
+        private int _cursor_x;
+        public int cursor_x
+        {
+            get { return this._cursor_x; }
+            set { this._cursor_x = this.clamp(0, value, this.cols - 1); }
+        }
+        private int _cursor_Y;
+        public int cursor_y
+        {
+            get { return this._cursor_Y; }
+            set { this._cursor_Y = this.clamp(0, value, this.rows - 1); }
+        }
+
+        public bool move_in_progress { get; set; }
+
+        public signal void changed ();
+        public signal void move (ArrayList<GlinesField> path);
+        public signal void info (string text);
+        public signal void gameover ();
+
+        public GlinesBoard(int cols, int rows, int preview_queue_size, int n_types)
+        {
+            this.cols = cols;
+            this.rows = rows;
+
+            preview_queue = new GlinesPreviewQueue(preview_queue_size, n_types);
+
+            this.fields = new GlinesField[this.cols, this.rows];
+
+            for (int x = 0; x < this.cols; x++)
+            {
+                for (int y = 0; y < this.rows; y++)
+                {
+                    this.fields[x, y] = new GlinesField();
+                }
+            }
+
+            //Connect the neigbouring fields
+            for (int x = 0; x < this.cols; x++)
+            {
+                for (int y = 0; y < this.rows; y++)
+                {
+                    var field = this.fields[x, y];
+                    if (x > 0)
+                        field.neigbour_west = this.fields[x - 1, y];
+                    if (x < this.cols - 1)
+                        field.neigbour_east = this.fields[x + 1, y];
+                    if (y > 0)
+                        field.neigbour_north = this.fields[x, y - 1];
+                    if (y < this.rows - 1)
+                        field.neigbour_south = this.fields[x, y + 1];
+                }
+            }
+
+            this.move_preview_queue_to_board();
+        }
+
+        public void select_field(int x, int y)
+        {
+            if (this.move_in_progress)
+                return;
+
+            var clicked_field = this.fields[x, y];
+
+            if (this.active_field == null && !clicked_field.has_piece)
+            {
+                info("No piece to select");
+            }
+            else if (this.active_field == null && clicked_field.has_piece)
+            {
+                //select a field
+                clicked_field.active = true;
+                this.active_field = clicked_field;
+                info("Field selected");
+            }
+            else if (this.active_field == clicked_field)
+            {
+                //deselect the active field
+                clicked_field.active = false;
+                this.active_field = null;
+                info("Field deselected");
+            }
+            else
+            {
+                //attempt to move the piece
+                if (find_route(this.active_field, clicked_field))
+                {
+                    info("moving");
+
+                    this.move_in_progress = true;
+
+                    // Let the listener do the animation.
+                    this.move(this.backtrack_route(clicked_field));
+
+                    //actually move the piece
+                    clicked_field.piece = this.active_field.piece;
+                    this.active_field.piece = null;
+
+                    this.active_field.active = false;
+                    this.active_field = null;
+
+                    move_completed();
+                }
+                else
+                {
+                    info("no route");
+                }
+            }
+
+            this.changed();
+        }
+
+        private void move_completed()
+        {
+            //remove lines to make space for adding pieces
+            this.remove_lines();
+
+            this.move_preview_queue_to_board();
+
+            //remove lines again. New once could be formed when adding the new pieces
+            this.remove_lines();
+
+            if (this.check_for_game_over())
+            {
+                gameover();
+            }
+
+            this.move_in_progress = false;
+        }
+
+        private void remove_lines()
+        {
+            //todo: check for lines to remove
+            //      + add scoring
+        }
+
+        private void move_preview_queue_to_board()
+        {
+            foreach (var piece in preview_queue.pieces)
+            {
+                this.add_piece_at_random_location(piece);
+            }
+
+            preview_queue.refill();
+        }
+
+        private void add_piece_at_random_location(GlinesPiece piece)
+        {
+            var vert = new ArrayList<int>();
+            var hori = new ArrayList<int>();
+
+            for (int i = 0; i < this.cols; i++)
+                vert.add(i);
+            for (int i = 0; i < this.rows; i++)
+                hori.add(i);
+
+            shuffle(hori);
+            shuffle(vert);
+
+            foreach(int x in vert)
+            {
+                foreach(int y in hori)
+                {
+                    if(!fields[x,y].has_piece)
+                    {
+                        fields[x, y].piece = piece;
+                        return;
+                    }
+                }
+            }
+        }
+
+        private void shuffle(ArrayList<int> list)
+        {
+            for (int from = list.size - 1; from > 0; from--)
+            {
+                int to = Random.int_range(0, from);
+                int temp = list[to];
+                list[to] = list[from];
+                list[from] = temp;
+            }
+        }
+
+        private bool check_for_game_over()
+        {
+            for (int x = 0; x < this.cols; x++)
+                for (int y = 0; y < this.rows; y++)
+                    if (!this.fields[x, y].has_piece)
+                        return false;
+
+            return true;
+        }
+
+        private void reset_path_search()
+        {
+            for (int x = 0; x < this.cols; x++)
+                for (int y = 0; y < this.rows; y++)
+                    fields[x, y].path_search = null;
+        }
+
+        //After the search has been performed a route will (if possible)
+        //exist from the target to the origin. Use the Pathsearch to follow
+        //the path.
+        public bool find_route(GlinesField from, GlinesField target)
+        {
+            reset_path_search();
+
+            var check_now = new ArrayList<GlinesField> ();
+            check_now.add (from);
+
+            while (check_now.size != 0)
+            {
+                var check_next = new ArrayList<GlinesField>();
+
+                foreach (var current in check_now)
+                {
+                    foreach (var neighbour in this.non_searched_neighbours(current))
+                    {
+                        if (neighbour == from)
+                            continue;
+
+                        neighbour.path_search = current;
+                        check_next.add(neighbour);
+
+                        // We have reached the target so there is no use in continuing
+                        if (neighbour == target)
+                            return true;
+                    }
+                }
+
+                check_now = check_next;
+            }
+
+            return false;
+        }
+
+        private ArrayList<GlinesField> non_searched_neighbours(GlinesField field)
+        {
+            var neighbours = new ArrayList<GlinesField>();
+
+            if (field.neigbour_north != null &&
+                !field.neigbour_north.has_piece &&
+                field.neigbour_north.path_search == null)
+                neighbours.add(field.neigbour_north);
+
+            if (field.neigbour_west != null &&
+                !field.neigbour_west.has_piece &&
+                field.neigbour_west.path_search == null)
+                neighbours.add(field.neigbour_west);
+
+            if (field.neigbour_south != null &&
+                !field.neigbour_south.has_piece &&
+                field.neigbour_south.path_search == null)
+                neighbours.add(field.neigbour_south);
+
+            if (field.neigbour_east != null &&
+                !field.neigbour_east.has_piece &&
+                field.neigbour_east.path_search == null)
+                neighbours.add(field.neigbour_east);
+
+            return neighbours;
+        }
+
+        public ArrayList<GlinesField> backtrack_route(GlinesField from)
+        {
+            var route = new ArrayList<GlinesField> ();
+            route.add (from);
+
+            //the routes origin may have more than one
+            while (route[route.size -1 ].path_search != null)
+                route.add(route[route.size -1 ].path_search);
+
+            return route;
+        }
+
+        public void place_cursor(int x, int y)
+        {
+            this.cursor_x = x;
+            this.cursor_y = y;
+
+            this.changed();
+        }
+
+        public void move_cursor(int x, int y)
+        {
+            this.place_cursor(this.cursor_x + x, this.cursor_y + y);
+        }
+
+        private int clamp(int min, int val, int max)
+        {
+            if (val < min)
+                return min;
+            if (val > max)
+                return max;
+            return val;
+        }
+    }
+}
diff --git a/glines/src/glines-field.vala b/glines/src/glines-field.vala
new file mode 100644
index 0000000..7eba100
--- /dev/null
+++ b/glines/src/glines-field.vala
@@ -0,0 +1,31 @@
+namespace Glines
+{
+    public class GlinesField
+    {
+        public GlinesField()
+        {
+            this.piece = null;
+
+            this.neigbour_north = null;
+            this.neigbour_south = null;
+            this.neigbour_east = null;
+            this.neigbour_west = null;
+            this.path_search = null;
+        }
+
+        public GlinesPiece piece { get; set; }
+        public bool active { get; set; }
+
+        public GlinesField neigbour_north { get; set; }
+        public GlinesField neigbour_south { get; set; }
+        public GlinesField neigbour_east { get; set; }
+        public GlinesField neigbour_west { get; set; }
+
+        public GlinesField path_search { get; set; }
+
+        public bool has_piece
+        {
+            get { return this.piece != null; }
+        }
+    }
+}
diff --git a/glines/src/glines-piece.vala b/glines/src/glines-piece.vala
new file mode 100644
index 0000000..77d5968
--- /dev/null
+++ b/glines/src/glines-piece.vala
@@ -0,0 +1,12 @@
+namespace Glines
+{
+    public class GlinesPiece
+    {
+        public int id { get; private set; }
+
+        public GlinesPiece(int id)
+        {
+            this.id = id;
+        }
+    }
+}
diff --git a/glines/src/glines-preview-queue.vala b/glines/src/glines-preview-queue.vala
new file mode 100644
index 0000000..eac7ff1
--- /dev/null
+++ b/glines/src/glines-preview-queue.vala
@@ -0,0 +1,30 @@
+using Gee;
+
+namespace Glines
+{
+    public class GlinesPreviewQueue
+    {
+        public ArrayList<GlinesPiece> pieces { get; set; }
+        private int size { get; set; }
+        private int types { get; set; }
+
+        public GlinesPreviewQueue(int size, int types)
+        {
+            this.pieces = new ArrayList<GlinesPiece>();
+            this.size = size;
+            this.types = types; 
+            this.refill();
+        }
+
+        public void refill()
+        {
+            this.pieces.clear();
+
+            for (int i = 0; i < this.size; i++)
+            {
+                int id = Random.int_range(0, types - 1);
+                this.pieces.add(new GlinesPiece(id));
+            }
+        }
+    }
+}
diff --git a/glines/src/glines-view-cli.vala b/glines/src/glines-view-cli.vala
new file mode 100644
index 0000000..cd1efd8
--- /dev/null
+++ b/glines/src/glines-view-cli.vala
@@ -0,0 +1,144 @@
+using Gee;
+
+namespace Glines
+{
+    class ViewCli
+    {
+        private GlinesBoard board = new GlinesBoard(10, 10, 5, 2);
+
+        private bool gameover = false;
+        private string message = "";
+        private char[] id_to_char_symbol = { 'a', 'b', 'c', 'd', 'e' };
+
+        public ViewCli()
+        {
+            board.show_cursor = true;
+            board.place_cursor(5,5);
+
+            board.changed.connect ((o) => this.print(null));
+            board.move.connect ((o, path) => this.animate_move(path));
+            board.info.connect ((o, info) => message = info);
+            board.gameover.connect ((o) => this.gameover = true);
+
+            this.print(null);
+        }
+
+        public void run()
+        {
+            while (!gameover)
+            {
+                var c = stdin.getc();
+
+                if (c == 49) //southwest
+                    board.move_cursor(-1, 1);
+
+                if (c == 50) //south
+                    board.move_cursor(0, 1);
+
+                if (c == 51) //southeast
+                    board.move_cursor(1, 1);
+
+                if (c == 52) //west
+                    board.move_cursor(-1, 0);
+
+                if (c == 54) //east
+                    board.move_cursor(1, 0);
+
+                if (c == 55) //northwest
+                    board.move_cursor(-1, -1);
+
+                if (c == 56) //north
+                    board.move_cursor(0, -1);
+
+                if (c == 57) //northeast
+                    board.move_cursor(1, -1);
+
+                if (c == 53) //click
+                    board.select_field(board.cursor_x, board.cursor_y);
+            }
+
+            stdout.printf("Game over.\n");
+        }
+
+        private void print(ArrayList<GlinesField>? path)
+        {
+            var sb = new StringBuilder();
+
+            sb.append("Preview: ");
+            foreach (var preview in board.preview_queue.pieces)
+                sb.append_c(id_to_char_symbol[preview.id]);
+            sb.append("\n\n");
+
+            for (int n = 0; n < board.cols + 2; n++)
+                sb.append("#");
+            sb.append("\n");
+
+            for (int y = 0; y < board.rows; y++)
+            {
+                sb.append("#");
+                for (int x = 0; x < board.cols; x++)
+                {
+                    var field = board.fields[x,y];
+
+                    bool is_cursor = board.cursor_x == x && board.cursor_y == y;
+
+                    if (field.active)
+                    {
+                        sb.append("@");
+                        continue;
+                    }
+
+                    if (!field.has_piece)
+                    {
+                        if (is_cursor) sb.append("Â");
+                        else
+                        {
+                            if (path != null && field.path_search != null && path.contains(field))
+                            {
+                                if (field.path_search == field.neigbour_north)
+                                    sb.append("â");
+                                if (field.path_search == field.neigbour_south)
+                                    sb.append("â");
+                                if (field.path_search == field.neigbour_west)
+                                    sb.append("â");
+                                if (field.path_search == field.neigbour_east)
+                                    sb.append("â");
+                            }
+                            else
+                                sb.append(" ");
+                        }
+                    }
+                    else
+                    {
+                        char symbol = id_to_char_symbol[field.piece.id];
+
+                        if (is_cursor)
+                            sb.append_c(symbol.toupper());
+                        else
+                            sb.append_c(symbol);
+                    }
+                }
+
+                sb.append("#\n");
+            }
+
+            for (int n = 0; n < board.cols + 2; n++)
+                sb.append("#");
+
+            sb.append("\n\n");
+            sb.append(message);
+            sb.append("\n");
+
+            stdout.printf(sb.str);
+        }
+
+        private void animate_move(ArrayList<GlinesField> path)
+        {
+            //"animate" the move:
+            this.message = "'Animating' the move...";
+            this.print(path);
+            this.message = "";
+            stdin.getc();
+        }
+    }
+}



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