[iagno] Introduce Opening.



commit 31ffd883b7156531c67ba2100dfff646795bea12
Author: Arnaud Bonatti <arnaud bonatti gmail com>
Date:   Fri Sep 6 02:25:50 2019 +0200

    Introduce Opening.
    
    When randomly selecting an
    opening position, it would
    be better to allow all the
    openings, with symmetries.

 src/game.vala         |  51 +++++++---
 src/iagno.vala        |  31 ++++--
 src/reversi-view.vala | 261 +++++++++++++++++++++++++++++++++++++++++---------
 3 files changed, 278 insertions(+), 65 deletions(-)
---
diff --git a/src/game.vala b/src/game.vala
index 55945b5..0f3dcdc 100644
--- a/src/game.vala
+++ b/src/game.vala
@@ -556,8 +556,8 @@ private class Game : Object
 
     [CCode (notify = false)] public uint8           size                    { internal get; protected 
construct;     }
     [CCode (notify = false)] public bool            reverse                 { internal get; protected 
construct;     }
+    [CCode (notify = false)] public Opening         opening                 { internal get; protected 
construct;     }
     [CCode (notify = false)] public GameStateObject current_state           { internal get; protected 
construct set; }
-    [CCode (notify = false)] public bool            alternative_start       { internal get; protected 
construct;     }
     [CCode (notify = false)] public uint8           initial_number_of_tiles { internal get; protected 
construct;     }
 
     construct
@@ -566,7 +566,7 @@ private class Game : Object
         update_possible_moves ();
     }
 
-    internal Game (bool _reverse, bool _alternative_start = false, uint8 _size = 8)
+    internal Game (bool _reverse, Opening _opening = Opening.REVERSI, uint8 _size = 8)
         requires (_size >= 4)
         requires (_size <= 16)
     {
@@ -584,22 +584,34 @@ private class Game : Object
             /* setup board with four tiles by default */
             uint8 half_size = _size / 2;
             _initial_number_of_tiles = 4;
-            tiles [half_size - 1, half_size - 1] = _alternative_start ? Player.DARK : Player.LIGHT;
-            tiles [half_size - 1, half_size    ] = Player.DARK;
-            tiles [half_size    , half_size - 1] = _alternative_start ? Player.LIGHT : Player.DARK;
-            tiles [half_size    , half_size    ] = Player.LIGHT;
+            Player [,] start_position;
+            switch (_opening)
+            {
+                case Opening.REVERSI:      start_position = {{ Player.LIGHT, Player.DARK  }, { Player.DARK , 
Player.LIGHT }}; break;
+                case Opening.INVERTED:     start_position = {{ Player.DARK , Player.LIGHT }, { Player.LIGHT, 
Player.DARK  }}; break;
+                case Opening.ALTER_TOP:    start_position = {{ Player.DARK , Player.DARK  }, { Player.LIGHT, 
Player.LIGHT }}; break;
+                case Opening.ALTER_LEFT:   start_position = {{ Player.DARK , Player.LIGHT }, { Player.DARK , 
Player.LIGHT }}; break;
+                case Opening.ALTER_RIGHT:  start_position = {{ Player.LIGHT, Player.DARK  }, { Player.LIGHT, 
Player.DARK  }}; break;
+                case Opening.ALTER_BOTTOM: start_position = {{ Player.LIGHT, Player.LIGHT }, { Player.DARK , 
Player.DARK  }}; break;
+                default: assert_not_reached ();
+            }
+            tiles [half_size - 1, half_size - 1] = start_position [0, 0];
+            tiles [half_size    , half_size - 1] = start_position [0, 1];
+            tiles [half_size - 1, half_size    ] = start_position [1, 0];
+            tiles [half_size    , half_size    ] = start_position [1, 1];
         }
         else
         {
             /* logical starting position for odd board */
+            bool alternative_start = Opening.is_alter (_opening);   // TODO better
             uint8 mid_board = (_size - 1) / 2;
             _initial_number_of_tiles = 7;
             tiles [mid_board    , mid_board    ] = Player.DARK;
-            tiles [mid_board + 1, mid_board - 1] = _alternative_start ? Player.LIGHT : Player.DARK;
-            tiles [mid_board - 1, mid_board + 1] = _alternative_start ? Player.LIGHT : Player.DARK;
+            tiles [mid_board + 1, mid_board - 1] = alternative_start ? Player.LIGHT : Player.DARK;
+            tiles [mid_board - 1, mid_board + 1] = alternative_start ? Player.LIGHT : Player.DARK;
             tiles [mid_board    , mid_board - 1] = Player.LIGHT;
-            tiles [mid_board - 1, mid_board    ] = _alternative_start ? Player.DARK : Player.LIGHT;
-            tiles [mid_board + 1, mid_board    ] = _alternative_start ? Player.DARK : Player.LIGHT;
+            tiles [mid_board - 1, mid_board    ] = alternative_start ? Player.DARK : Player.LIGHT;
+            tiles [mid_board + 1, mid_board    ] = alternative_start ? Player.DARK : Player.LIGHT;
             tiles [mid_board    , mid_board + 1] = Player.LIGHT;
         }
 
@@ -609,8 +621,8 @@ private class Game : Object
 
         Object (size                    : _size,
                 reverse                 : _reverse,
+                opening                 : _opening,
                 current_state           : _current_state,
-                alternative_start       : _alternative_start,
                 initial_number_of_tiles : _initial_number_of_tiles);
         neighbor_tiles = (owned) _neighbor_tiles;
     }
@@ -637,8 +649,8 @@ private class Game : Object
 
         Object (size                    : _size,
                 reverse                 : _reverse,
+                opening                 : /* garbage */ Opening.REVERSI,
                 current_state           : _current_state,
-                alternative_start       : /* garbage */ false,
                 initial_number_of_tiles : (_size % 2 == 0) ? 4 : 7);
         neighbor_tiles = (owned) _neighbor_tiles;
 
@@ -797,3 +809,18 @@ private class Game : Object
         current_state.get_possible_moves (out possible_moves);
     }
 }
+
+private enum Opening {
+    REVERSI,
+    INVERTED,
+    ALTER_TOP,
+    ALTER_LEFT,
+    ALTER_RIGHT,
+    ALTER_BOTTOM;
+
+    internal static inline bool is_alter (Opening opening)
+    {
+        return opening != REVERSI
+            && opening != INVERTED;
+    }
+}
diff --git a/src/iagno.vala b/src/iagno.vala
index 552ecd3..8fcc605 100644
--- a/src/iagno.vala
+++ b/src/iagno.vala
@@ -615,23 +615,36 @@ private class Iagno : Gtk.Application, BaseApplication
         if (computer != null)
             ((!) computer).cancel_move ();
 
-        bool use_alternative;
+        bool two_players = settings.get_int ("num-players") == 2;
+
+        Opening opening;
         if (alternative_start)
-            use_alternative = true;
+            opening = get_locale_direction () == TextDirection.LTR ? Opening.ALTER_LEFT : 
Opening.ALTER_RIGHT;
         else if (usual_start)
-            use_alternative = false;
+            opening = Opening.REVERSI;
         else if (random_start || settings.get_boolean ("random-start-position"))
-            use_alternative = Random.boolean ();
+        {
+            switch (Random.int_range (0, 8))
+            {
+                case 0: case 1: opening = Opening.REVERSI;      break;
+                case 2: case 3: opening = Opening.INVERTED;     break;
+                case 4:         opening = Opening.ALTER_TOP;    break;
+                case 5:         opening = Opening.ALTER_LEFT;   break;
+                case 6:         opening = Opening.ALTER_RIGHT;  break;
+                case 7:         opening = Opening.ALTER_BOTTOM; break;
+                default: assert_not_reached ();
+            }
+        }
         else
-            use_alternative = false;
+            opening = Opening.REVERSI;
 
         bool reverse = settings.get_string ("type") == "reverse";
-        game = new Game (reverse, use_alternative, (uint8) size /* 4 <= size <= 16 */);
+        game = new Game (reverse, opening, (uint8) size /* 4 <= size <= 16 */);
         game_is_set = true;
         game.turn_ended.connect (turn_ended_cb);
         view.game = game;
 
-        if (settings.get_int ("num-players") == 2)
+        if (two_players)
             computer = null;
         else
         {
@@ -659,10 +672,10 @@ private class Iagno : Gtk.Application, BaseApplication
         else
             player_one = Player.DARK;
 
-        first_player_is_human = (player_one == Player.DARK) || (computer == null);
+        first_player_is_human = two_players || (player_one == Player.DARK);
         update_ui ();
 
-        if (computer != null)
+        if (!two_players)
         {
             if (player_one == Player.DARK)
             {
diff --git a/src/reversi-view.vala b/src/reversi-view.vala
index 03e120c..c8a94c1 100644
--- a/src/reversi-view.vala
+++ b/src/reversi-view.vala
@@ -142,12 +142,28 @@ private class ReversiView : Gtk.DrawingArea
             _game.turn_ended.connect (turn_ended_cb);
 
             show_highlight = false;
-            bool odd_game = game_size % 2 != 0; // always start on center on odd games
-            highlight_set = _game.alternative_start || odd_game;
-            highlight_x = odd_game ? (uint8) (game_size / 2) : (uint8) (game_size / 2 - 1);
+            if (game_size % 2 != 0) // always start on center on odd games
+            {
+                highlight_set = true;
+                highlight_x = (uint8) (game_size / 2);
+            }
+            else
+            {
+                highlight_set = false;
+                highlight_x = (uint8) (game_size / 2 - 1);
+            }
             highlight_y = highlight_x;
+            old_highlight_x = uint8.MAX;
+            old_highlight_y = uint8.MAX;
             highlight_state = 0;
 
+            show_mouse_highlight = false;
+            mouse_position_set = false;
+            mouse_highlight_x = uint8.MAX;
+            mouse_highlight_y = uint8.MAX;
+            mouse_position_x = uint8.MAX;
+            mouse_position_y = uint8.MAX;
+
             queue_draw ();
         }
     }
@@ -1102,36 +1118,24 @@ private class ReversiView : Gtk.DrawingArea
         old_highlight_y = highlight_y;
         switch (key)
         {
+            case "Up":
+            case "KP_Up":
+                set_highlight_position_if_needed (Direction.TOP);
+                if (highlight_y > 0) highlight_y--;
+                break;
             case "Left":
             case "KP_Left":
-                if (mouse_position_set && show_mouse_highlight) { highlight_x = mouse_highlight_x; 
highlight_y = mouse_highlight_y; }
-                else if (!highlight_set && game.current_color == Player.LIGHT) highlight_y = game_size / 2;
+                set_highlight_position_if_needed (Direction.LEFT);
                 if (highlight_x > 0) highlight_x--;
                 break;
             case "Right":
             case "KP_Right":
-                if (mouse_position_set && show_mouse_highlight) { highlight_x = mouse_highlight_x; 
highlight_y = mouse_highlight_y; }
-                else if (!highlight_set)
-                {
-                    highlight_x = game_size / 2;
-                    if (game.current_color == Player.DARK) highlight_y = highlight_x;
-                }
+                set_highlight_position_if_needed (Direction.RIGHT);
                 if (highlight_x < game_size - 1) highlight_x++;
                 break;
-            case "Up":
-            case "KP_Up":
-                if (mouse_position_set && show_mouse_highlight) { highlight_x = mouse_highlight_x; 
highlight_y = mouse_highlight_y; }
-                else if (!highlight_set && game.current_color == Player.LIGHT) highlight_x = game_size / 2;
-                if (highlight_y > 0) highlight_y--;
-                break;
             case "Down":
             case "KP_Down":
-                if (mouse_position_set && show_mouse_highlight) { highlight_x = mouse_highlight_x; 
highlight_y = mouse_highlight_y; }
-                else if (!highlight_set)
-                {
-                    highlight_y = game_size / 2;
-                    if (game.current_color == Player.DARK) highlight_x = highlight_y;
-                }
+                set_highlight_position_if_needed (Direction.BOTTOM);
                 if (highlight_y < game_size - 1) highlight_y++;
                 break;
 
@@ -1148,46 +1152,51 @@ private class ReversiView : Gtk.DrawingArea
                     highlight_x = mouse_position_x;
                     highlight_y = mouse_position_y;
                 }
+                else init_highlight_on_light_tile_if_needed ();
                 break;
 
             case "Escape": break;
 
-            case "a": case "A": highlight_x = 0; break;
-            case "b": case "B": highlight_x = 1; break;
-            case "c": case "C": highlight_x = 2; break;
-            case "d": case "D": highlight_x = 3; break;
-            case "e": case "E": highlight_x = 4; break;
-            case "f": case "F": highlight_x = 5; break;
-            case "g": case "G": highlight_x = 6; break;
-            case "h": case "H": highlight_x = 7; break;
-            case "i": case "I": highlight_x = 8; break;
-            case "j": case "J": highlight_x = 9; break;
-
-            case "1": case "KP_1": highlight_y = 0; break;
-            case "2": case "KP_2": highlight_y = 1; break;
-            case "3": case "KP_3": highlight_y = 2; break;
-            case "4": case "KP_4": highlight_y = 3; break;
-            case "5": case "KP_5": highlight_y = 4; break;
-            case "6": case "KP_6": highlight_y = 5; break;
-            case "7": case "KP_7": highlight_y = 6; break;
-            case "8": case "KP_8": highlight_y = 7; break;
-            case "9": case "KP_9": highlight_y = 8; break;
-            case "0": case "KP_0": highlight_y = 9; break;
+            case "a": case "A": init_highlight_on_light_tile_if_needed (); highlight_x = 0; break;
+            case "b": case "B": init_highlight_on_light_tile_if_needed (); highlight_x = 1; break;
+            case "c": case "C": init_highlight_on_light_tile_if_needed (); highlight_x = 2; break;
+            case "d": case "D": init_highlight_on_light_tile_if_needed (); highlight_x = 3; break;
+            case "e": case "E": init_highlight_on_light_tile_if_needed (); highlight_x = 4; break;
+            case "f": case "F": init_highlight_on_light_tile_if_needed (); highlight_x = 5; break;
+            case "g": case "G": init_highlight_on_light_tile_if_needed (); highlight_x = 6; break;
+            case "h": case "H": init_highlight_on_light_tile_if_needed (); highlight_x = 7; break;
+            case "i": case "I": init_highlight_on_light_tile_if_needed (); highlight_x = 8; break;
+            case "j": case "J": init_highlight_on_light_tile_if_needed (); highlight_x = 9; break;
+
+            case "1": case "KP_1": init_highlight_on_light_tile_if_needed (); highlight_y = 0; break;
+            case "2": case "KP_2": init_highlight_on_light_tile_if_needed (); highlight_y = 1; break;
+            case "3": case "KP_3": init_highlight_on_light_tile_if_needed (); highlight_y = 2; break;
+            case "4": case "KP_4": init_highlight_on_light_tile_if_needed (); highlight_y = 3; break;
+            case "5": case "KP_5": init_highlight_on_light_tile_if_needed (); highlight_y = 4; break;
+            case "6": case "KP_6": init_highlight_on_light_tile_if_needed (); highlight_y = 5; break;
+            case "7": case "KP_7": init_highlight_on_light_tile_if_needed (); highlight_y = 6; break;
+            case "8": case "KP_8": init_highlight_on_light_tile_if_needed (); highlight_y = 7; break;
+            case "9": case "KP_9": init_highlight_on_light_tile_if_needed (); highlight_y = 8; break;
+            case "0": case "KP_0": init_highlight_on_light_tile_if_needed (); highlight_y = 9; break;
 
             case "Home":
             case "KP_Home":
+                init_highlight_on_light_tile_if_needed ();
                 highlight_x = 0;
                 break;
             case "End":
             case "KP_End":
+                init_highlight_on_light_tile_if_needed ();
                 highlight_x = game_size - 1;
                 break;
             case "Page_Up":
             case "KP_Page_Up":
+                init_highlight_on_light_tile_if_needed ();
                 highlight_y = 0;
                 break;
             case "Page_Down":
             case "KP_Next":     // TODO use KP_Page_Down instead of KP_Next, probably a gtk+ or vala bug; 
check also KP_Prior
+                init_highlight_on_light_tile_if_needed ();
                 highlight_y = game_size - 1;
                 break;
 
@@ -1227,6 +1236,170 @@ private class ReversiView : Gtk.DrawingArea
         return true;
     }
 
+    private void set_highlight_position_if_needed (Direction direction)
+    {
+        if (mouse_position_set && show_mouse_highlight)
+        {
+            /* If mouse highlight is visible, use it for the keyboard highlight. */
+
+            highlight_x = mouse_highlight_x;
+            highlight_y = mouse_highlight_y;
+            return;
+        }
+
+        if (highlight_set)
+            /* If keyboard highlight is already set (and visible), this is good. */
+            return;
+
+        if (game.current_color == Player.LIGHT)
+        {
+            /* This section is for when computer started, so then is human turn. */
+
+            init_highlight_on_light_tile ();
+            return;
+        }
+
+        if (iagno_instance.computer != null && iagno_instance.player_one == Player.LIGHT)
+        {
+            /* This section is for when computer starts but player moves before.
+               The starting [highlight_x, highlight_y] tile is the top-left one.
+               The target tile is the one that will give correct highlight after
+               a move in the given direction, not directly the one to highlight. */
+
+            if (Gtk.get_locale_direction () == Gtk.TextDirection.LTR)
+                switch (direction)
+                {
+                    case Direction.TOP:                       highlight_y++;    break;
+                    case Direction.LEFT:    highlight_x++;    highlight_y++;    break;
+                    case Direction.RIGHT:                                       break;
+                    case Direction.BOTTOM:  highlight_x++;                      break;
+                }
+            else
+                switch (direction)
+                {
+                    case Direction.TOP:     highlight_x++;    highlight_y++;    break;
+                    case Direction.LEFT:    highlight_x++;                      break;
+                    case Direction.RIGHT:                     highlight_y++;    break;
+                    case Direction.BOTTOM:                                      break;
+                }
+            return;
+        }
+
+        switch (game.opening)
+        {
+            /* This section is for when a human starts (for one or two players).
+               The starting [highlight_x, highlight_y] tile is the top-left one.
+               The target tile is the one that will give correct highlight after
+               a move in the given direction, not directly the one to highlight. */
+
+            case Opening.REVERSI:
+                switch (direction)
+                {
+                    case Direction.TOP:                                         break;
+                    case Direction.LEFT:                                        break;
+                    case Direction.RIGHT:   highlight_x++;    highlight_y++;    break;
+                    case Direction.BOTTOM:  highlight_x++;    highlight_y++;    break;
+                }
+                return;
+
+            case Opening.INVERTED:
+                switch (direction)
+                {
+                    case Direction.TOP:     highlight_x++;                      break;
+                    case Direction.LEFT:                      highlight_y++;    break;
+                    case Direction.RIGHT:   highlight_x++;                      break;
+                    case Direction.BOTTOM:                    highlight_y++;    break;
+                }
+                return;
+
+            case Opening.ALTER_TOP:
+                switch (direction)
+                {
+                    case Direction.TOP:     highlight_x++;    highlight_y += 3; break;
+                    case Direction.LEFT:                      highlight_y += 2; break;
+                    case Direction.RIGHT:   highlight_x++;    highlight_y += 2; break;
+                    case Direction.BOTTOM:                    highlight_y++;    break;
+                }
+                return;
+
+            case Opening.ALTER_LEFT:
+                switch (direction)
+                {
+                    case Direction.TOP:     highlight_x += 2;                   break;
+                    case Direction.LEFT:    highlight_x += 3; highlight_y++;    break;
+                    case Direction.RIGHT:   highlight_x++;                      break;
+                    case Direction.BOTTOM:  highlight_x += 2; highlight_y++;    break;
+                }
+                return;
+
+            case Opening.ALTER_RIGHT:
+                switch (direction)
+                {
+                    case Direction.TOP:     highlight_x--;                      break;
+                    case Direction.LEFT:                                        break;
+                    case Direction.RIGHT:   highlight_x -= 2; highlight_y++;    break;
+                    case Direction.BOTTOM:  highlight_x--;    highlight_y++;    break;
+                }
+                return;
+
+            case Opening.ALTER_BOTTOM:
+                switch (direction)
+                {
+                    case Direction.TOP:                                         break;
+                    case Direction.LEFT:                      highlight_y--;    break;
+                    case Direction.RIGHT:   highlight_x++;    highlight_y--;    break;
+                    case Direction.BOTTOM:  highlight_x++;    highlight_y -= 2; break;
+                }
+                return;
+        }
+        assert_not_reached ();
+    }
+
+    private void init_highlight_on_light_tile_if_needed ()
+    {
+        if (!highlight_set && game.current_color == Player.LIGHT)
+            init_highlight_on_light_tile ();
+    }
+
+    private void init_highlight_on_light_tile ()
+      // requires (!highlight_set)
+      // requires (game.current_color == Player.LIGHT)
+    {
+        uint8 half_size = (uint8) (game_size / 2);
+        if (game.get_owner (half_size - 1, half_size - 1) == Player.LIGHT)
+        {
+            highlight_x = half_size - 1;
+            highlight_y = highlight_x;
+            return;
+        }
+        if (game.get_owner (half_size, half_size) == Player.LIGHT)
+        {
+            highlight_x = half_size;
+            highlight_y = half_size;
+            return;
+        }
+        if (game.get_owner (half_size - 1, half_size) == Player.LIGHT)
+        {
+            highlight_x = half_size - 1;
+            highlight_y = half_size;
+            return;
+        }
+        if (game.get_owner (half_size, half_size - 1) == Player.LIGHT)
+        {
+            highlight_x = half_size;
+            highlight_y = half_size - 1;
+            return;
+        }
+        assert_not_reached ();
+    }
+
+    private enum Direction {
+        TOP,
+        LEFT,
+        RIGHT,
+        BOTTOM;
+    }
+
     /*\
     * * testing move
     \*/


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