[iagno] Allow humans opening for non-standard boards.



commit 77fdaaa49ed486662d1a4005dbf1f01298a7c8f0
Author: Arnaud Bonatti <arnaud bonatti gmail com>
Date:   Fri Nov 29 01:11:46 2019 +0100

    Allow humans opening for non-standard boards.

 src/game.vala         | 379 ++++++++++++++++++++++++++++++++++++++------------
 src/iagno.vala        |   2 +-
 src/reversi-view.vala | 174 +++++++++++++++++------
 3 files changed, 424 insertions(+), 131 deletions(-)
---
diff --git a/src/game.vala b/src/game.vala
index c189585..298a347 100644
--- a/src/game.vala
+++ b/src/game.vala
@@ -133,7 +133,7 @@ private struct GameStateStruct
         }
     }
 
-    internal GameStateStruct.from_grid (uint8 _size, Player [,] _tiles, Player color, uint8 [,] 
_neighbor_tiles)
+    internal GameStateStruct.from_grid (uint8 _size, Player [,] _tiles, Player color, uint8 [,] 
_neighbor_tiles, bool humans_opening)
     {
         // move color
         current_color = color;
@@ -158,7 +158,13 @@ private struct GameStateStruct
         init_empty_neighbors ();
 
         // who can move
-        update_who_can_move ();
+        if (humans_opening)
+        {
+            current_player_can_move = true;
+            is_complete = false;
+        }
+        else
+            update_who_can_move ();
     }
     private inline void add_tile_of_color (Player color)
     {
@@ -230,6 +236,39 @@ private struct GameStateStruct
         y_saved = size / 2 - 2;
     }
 
+    internal GameStateStruct.copy_and_add_two (GameStateStruct game, uint8 x1, uint8 y1, uint8 x2, uint8 y2)
+    {
+        // move color
+        opponent_color = game.current_color;
+        current_color = Player.flip_color (opponent_color);
+
+        // always given
+        size = game.size;
+        neighbor_tiles = game.neighbor_tiles;
+
+        // tiles grid
+        tiles = game.tiles;
+        if (tiles [x1, y1] != Player.NONE
+         || tiles [x2, y2] != Player.NONE)
+            assert_not_reached ();
+        tiles [x1, y1] = opponent_color;
+        tiles [x2, y2] = opponent_color;
+
+        // tiles counters
+        n_current_tiles = game.n_opponent_tiles;
+        n_opponent_tiles = game.n_current_tiles + 2;
+        n_tiles = n_current_tiles + n_opponent_tiles;
+
+        // empty neighbors
+        init_empty_neighbors ();    // lazyness
+
+        // who can move
+        current_player_can_move = true;
+        is_complete = false;
+        x_saved = size / 2 - 2;
+        y_saved = size / 2 - 2;
+    }
+
     /*\
     * * public information
     \*/
@@ -565,9 +604,9 @@ private class GameStateObject : Object
         _game_state_struct = GameStateStruct.copy_and_move (game.game_state_struct, move);
     }
 
-    internal GameStateObject.from_grid (uint8 size, Player [,] tiles, Player color, uint8 [,] neighbor_tiles)
+    internal GameStateObject.from_grid (uint8 size, Player [,] tiles, Player color, uint8 [,] 
neighbor_tiles, bool humans_opening = false)
     {
-        _game_state_struct = GameStateStruct.from_grid (size, tiles, color, neighbor_tiles);
+        _game_state_struct = GameStateStruct.from_grid (size, tiles, color, neighbor_tiles, humans_opening);
     }
 
     internal GameStateObject.empty (uint8 size, uint8 [,] neighbor_tiles)
@@ -580,6 +619,11 @@ private class GameStateObject : Object
         _game_state_struct = GameStateStruct.copy_and_add (game.game_state_struct, x, y);
     }
 
+    internal GameStateObject.copy_and_add_two (GameStateObject game, uint8 x, uint8 y, uint8 x2, uint8 y2)
+    {
+        _game_state_struct = GameStateStruct.copy_and_add_two (game.game_state_struct, x, y, x2, y2);
+    }
+
     /*\
     * * public information
     \*/
@@ -641,7 +685,7 @@ private class Game : Object
         if (print_logs)
         {
             string e_or_i = reverse ? "e" : "i";
-            if (initial_number_of_tiles == 0)
+            if (initial_number_of_tiles <= 1)
                 print (@"\nnew two-player revers$e_or_i game\n");
             else
                 print (@"\nnew one-player revers$e_or_i game ($opening opening)\n");    // TODO is human 
Dark or Light?
@@ -657,7 +701,9 @@ private class Game : Object
 
         GameStateObject _current_state;
         uint8 _initial_number_of_tiles;
-        if (_opening == Opening.HUMANS)
+        bool even_board = _size % 2 == 0;
+        bool humans_opening = _opening == Opening.HUMANS;
+        if (even_board && humans_opening)
         {
             _initial_number_of_tiles = 0;
 
@@ -671,12 +717,13 @@ private class Game : Object
                 for (uint8 y = 0; y < _size; y++)
                     tiles [x, y] = Player.NONE;
 
-            if (_size % 2 == 0)
+            if (even_board)
                 setup_even_board (_size, _opening, ref tiles, out _initial_number_of_tiles);
             else
                 setup_odd_board  (_size, _opening, ref tiles, out _initial_number_of_tiles);
 
-            _current_state = new GameStateObject.from_grid (_size, tiles, /* Dark always starts */ 
Player.DARK, _neighbor_tiles);
+            Player first_player = humans_opening ? Player.LIGHT : /* Dark "always" starts */ Player.DARK;
+            _current_state = new GameStateObject.from_grid (_size, tiles, first_player, _neighbor_tiles, 
humans_opening);
         }
 
         Object (size                    : _size,
@@ -712,10 +759,13 @@ private class Game : Object
     {
         /* logical starting position for odd board */
         uint8 mid_board = (size - 1) / 2;
-        initial_number_of_tiles = 7;
+        initial_number_of_tiles = opening == Opening.HUMANS ? 1 : 7;
         Player [,] start_position;
         switch (opening)
         {
+            case Opening.HUMANS:       start_position = {{ Player.NONE , Player.NONE , Player.NONE  },
+                                                         { Player.NONE , Player.DARK , Player.NONE  },
+                                                         { Player.NONE , Player.NONE , Player.NONE  }}; 
break;
             case Opening.REVERSI:      start_position = {{ Player.NONE , Player.LIGHT, Player.DARK  },
                                                          { Player.LIGHT, Player.DARK , Player.LIGHT },
                                                          { Player.DARK , Player.LIGHT, Player.NONE  }}; 
break;
@@ -808,51 +858,7 @@ private class Game : Object
     internal /* success */ bool place_tile (uint8 x, uint8 y)
     {
         if (opening == Opening.HUMANS)
-        {
-            uint8 half_game_size = size / 2;
-            if (x < half_game_size - 1 || x > half_game_size
-             || y < half_game_size - 1 || y > half_game_size)
-                return false;
-
-            if (current_color == Player.LIGHT && n_light_tiles == 0)
-            {
-                uint8 opposite_x = x == half_game_size ? half_game_size - 1 : half_game_size;
-                uint8 opposite_y = y == half_game_size ? half_game_size - 1 : half_game_size;
-                if (get_owner (opposite_x, opposite_y) == Player.DARK)
-                    return false;
-            }
-
-            if (current_state.n_dark_tiles == 2)
-            {
-                if (get_owner (half_game_size - 1, half_game_size - 1) == Player.DARK)
-                {
-                    if (get_owner (half_game_size, half_game_size - 1) == Player.DARK)  opening = 
Opening.ALTER_TOP;
-                    else if (get_owner (half_game_size, half_game_size) == Player.DARK) opening = 
Opening.REVERSI;
-                    else                                                                opening = 
Opening.ALTER_LEFT;
-                }
-                else
-                {
-                    if (get_owner (half_game_size, half_game_size - 1) == Player.LIGHT) opening = 
Opening.ALTER_BOTTOM;
-                    else if (get_owner (half_game_size, half_game_size) == Player.DARK) opening = 
Opening.ALTER_RIGHT;
-                    else                                                                opening = 
Opening.INVERTED;
-                }
-            }
-            if (print_logs)
-            {
-                string current_color_string = current_color == Player.DARK ? "dark :" : "light:";
-                print (@"$current_color_string ($x, $y)\n");
-            }
-            current_state = new GameStateObject.copy_and_add (current_state, x, y);
-
-            if (n_light_tiles == 2)
-            {
-                undo_stack.remove (0);
-                undo_stack.append (current_state);
-            }
-
-            end_of_turn (/* undoing */ false, /* no_draw */ false);
-            return true;
-        }
+            return humans_opening_place_tile (x, y);
 
         PossibleMove move;
         if (!current_state.test_placing_tile (x, y, out move))
@@ -971,22 +977,7 @@ private class Game : Object
         {
             move = PossibleMove (x, y); // good enough
 
-            uint8 half_game_size = size / 2;
-            if (x < half_game_size - 1 || x > half_game_size
-             || y < half_game_size - 1 || y > half_game_size)
-                return false;
-
-            if (get_owner (x, y) != Player.NONE)
-                return false;
-
-            if (current_color == Player.LIGHT && n_light_tiles == 0)
-            {
-                uint8 opposite_x = x == half_game_size ? half_game_size - 1 : half_game_size;
-                uint8 opposite_y = y == half_game_size ? half_game_size - 1 : half_game_size;
-                if (get_owner (opposite_x, opposite_y) == Player.DARK)
-                    return false;
-            }
-            return true;
+            return humans_opening_test_placing_tile (x, y);
         }
 
         unowned SList<PossibleMove?>? test_move = possible_moves.nth (0);
@@ -1012,39 +1003,245 @@ private class Game : Object
     private inline void update_possible_moves ()
     {
         if (opening == Opening.HUMANS)
+            humans_opening_update_possible_moves ();
+        else
+            current_state.get_possible_moves (out possible_moves);
+    }
+
+    /*\
+    * * humans opening
+    \*/
+
+    private inline bool humans_opening_place_tile (uint8 x, uint8 y)
+    {
+        uint8 half_game_size = size / 2;
+        bool even_board = size % 2 == 0;
+
+        if (!humans_opening_test_placing_tile (x, y))
+            return false;
+
+        if (even_board)
+        {
+            if (current_state.n_dark_tiles == 2)
+                humans_opening_update_opening_even ();
+
+            if (print_logs)
+            {
+                string current_color_string = current_color == Player.DARK ? "dark :" : "light:";
+                print (@"$current_color_string ($x, $y)\n");
+            }
+            current_state = new GameStateObject.copy_and_add (current_state, x, y);
+        }
+        else
+        {
+            if (current_color == Player.LIGHT && n_light_tiles != 0)
+                humans_opening_update_opening_odd (x, y);
+
+            uint8 x2;
+            uint8 y2;
+            if (x == half_game_size)            x2 = half_game_size;
+            else if (x == half_game_size - 1)   x2 = half_game_size + 1;
+            else /*  x == half_game_size + 1 */ x2 = half_game_size - 1;
+            if (y == half_game_size)            y2 = half_game_size;
+            else if (y == half_game_size - 1)   y2 = half_game_size + 1;
+            else /*  y == half_game_size + 1 */ y2 = half_game_size - 1;
+            if (print_logs)
+            {
+                string current_color_string = current_color == Player.DARK ? "dark :" : "light:";
+                print (@"$current_color_string ($x, $y) and ($x2, $y2)\n");
+            }
+            current_state = new GameStateObject.copy_and_add_two (current_state, x, y, x2, y2);
+        }
+
+        if (n_light_tiles == (even_board ? 2 : 4))
         {
-            possible_moves = new SList<PossibleMove?> ();
+            undo_stack.remove (0);
+            undo_stack.append (current_state);
+        }
+
+        end_of_turn (/* undoing */ false, /* no_draw */ false);
+        return true;
+    }
+    private inline void humans_opening_update_opening_even ()
+    {
+        uint8 half_game_size = size / 2;
+        if (get_owner (half_game_size - 1, half_game_size - 1) == Player.DARK)
+        {
+            if (get_owner (half_game_size, half_game_size - 1) == Player.DARK)  opening = Opening.ALTER_TOP;
+            else if (get_owner (half_game_size, half_game_size) == Player.DARK) opening = Opening.REVERSI;
+            else                                                                opening = Opening.ALTER_LEFT;
+        }
+        else
+        {
+            if (get_owner (half_game_size, half_game_size - 1) == Player.LIGHT) opening = 
Opening.ALTER_BOTTOM;
+            else if (get_owner (half_game_size, half_game_size) == Player.DARK) opening = 
Opening.ALTER_RIGHT;
+            else                                                                opening = Opening.INVERTED;
+        }
+    }
+    private inline void humans_opening_update_opening_odd (uint8 x, uint8 y)
+    {
+        uint8 half_game_size = size / 2;
+        if (x == half_game_size || y == half_game_size)
+        {
+            if (get_owner (half_game_size - 1, half_game_size - 1) == Player.NONE)
+                opening = Opening.REVERSI;
+            else
+                opening = Opening.INVERTED;
+        }
+        else
+        {
+            if (x == y)
+            {
+                if (get_owner (half_game_size, half_game_size - 1) == Player.LIGHT)
+                        opening = Opening.ALTER_LEFT;
+                else    opening = Opening.ALTER_BOTTOM;
+            }
+            else
+            {
+                if (get_owner (half_game_size, half_game_size - 1) == Player.LIGHT)
+                        opening = Opening.ALTER_RIGHT;
+                else    opening = Opening.ALTER_TOP;
+            }
+        }
+    }
+
+    private bool humans_opening_test_placing_tile (uint8 x, uint8 y)
+    {
+        if (get_owner (x, y) != Player.NONE)
+            return false;
+
+        if (size % 2 == 0)
+            return humans_opening_test_placing_tile_even (x, y);
+        else
+            return humans_opening_test_placing_tile_odd (x, y);
+    }
+    private inline bool humans_opening_test_placing_tile_even (uint8 x, uint8 y)
+    {
+        uint8 half_game_size = size / 2;
 
-            uint8 half_game_size = size / 2;
+        if (x < half_game_size - 1 || x > half_game_size
+         || y < half_game_size - 1 || y > half_game_size)
+            return false;
 
-            bool top_left;
-            bool top_right;
-            bool bottom_left;
-            bool bottom_right;
+        if (current_color == Player.LIGHT && n_light_tiles == 0)
+        {
+            uint8 opposite_x = x == half_game_size ? half_game_size - 1 : half_game_size;
+            uint8 opposite_y = y == half_game_size ? half_game_size - 1 : half_game_size;
+            if (get_owner (opposite_x, opposite_y) == Player.DARK)
+                return false;
+        }
+        return true;
+    }
+    private inline bool humans_opening_test_placing_tile_odd (uint8 x, uint8 y)
+    {
+        uint8 half_game_size = size / 2;
+
+        if (x < half_game_size - 1 || x > half_game_size + 1
+         || y < half_game_size - 1 || y > half_game_size + 1)
+            return false;
 
-            if (current_color == Player.LIGHT && n_light_tiles == 0)
+        if (current_color == Player.LIGHT)
+        {
+            if (n_light_tiles == 0)
             {
-                top_left = get_owner (half_game_size - 1, half_game_size - 1) == Player.NONE
-                        && get_owner (half_game_size    , half_game_size    ) == Player.NONE;
-                top_right    = !top_left;
-                bottom_left  = !top_left;
-                bottom_right = top_left;
+                if (x != half_game_size && y != half_game_size)
+                    return false;
             }
             else
             {
-                top_left     = get_owner (half_game_size - 1, half_game_size - 1) == Player.NONE;
-                top_right    = get_owner (half_game_size    , half_game_size - 1) == Player.NONE;
-                bottom_left  = get_owner (half_game_size - 1, half_game_size    ) == Player.NONE;
-                bottom_right = get_owner (half_game_size    , half_game_size    ) == Player.NONE;
+                if (get_owner (half_game_size - 1, half_game_size - 1) != Player.NONE
+                 || get_owner (half_game_size + 1, half_game_size - 1) != Player.NONE)
+                {
+                    if (x != half_game_size && y != half_game_size)
+                        return false;
+                }
             }
+        }
+        return true;
+    }
+
+    private inline void humans_opening_update_possible_moves ()
+    {
+        if (size % 2 == 0)
+            humans_opening_update_possible_moves_even ();
+        else
+            humans_opening_update_possible_moves_odd ();
+    }
+    private inline void humans_opening_update_possible_moves_even ()
+    {
+        possible_moves = new SList<PossibleMove?> ();
+
+        uint8 half_game_size = size / 2;
 
-            if (top_left)     possible_moves.prepend (PossibleMove (half_game_size - 1, half_game_size - 1));
-            if (top_right)    possible_moves.prepend (PossibleMove (half_game_size    , half_game_size - 1));
-            if (bottom_left)  possible_moves.prepend (PossibleMove (half_game_size - 1, half_game_size    ));
-            if (bottom_right) possible_moves.prepend (PossibleMove (half_game_size    , half_game_size    ));
+        bool top_left;
+        bool top_right;
+        bool bottom_left;
+        bool bottom_right;
+
+        if (current_color == Player.LIGHT && n_light_tiles == 0)
+        {
+            top_left = get_owner (half_game_size - 1, half_game_size - 1) == Player.NONE
+                    && get_owner (half_game_size    , half_game_size    ) == Player.NONE;
+            top_right    = !top_left;
+            bottom_left  = !top_left;
+            bottom_right = top_left;
         }
         else
-            current_state.get_possible_moves (out possible_moves);
+        {
+            top_left     = get_owner (half_game_size - 1, half_game_size - 1) == Player.NONE;
+            top_right    = get_owner (half_game_size    , half_game_size - 1) == Player.NONE;
+            bottom_left  = get_owner (half_game_size - 1, half_game_size    ) == Player.NONE;
+            bottom_right = get_owner (half_game_size    , half_game_size    ) == Player.NONE;
+        }
+
+        if (top_left)     possible_moves.prepend (PossibleMove (half_game_size - 1, half_game_size - 1));
+        if (top_right)    possible_moves.prepend (PossibleMove (half_game_size    , half_game_size - 1));
+        if (bottom_left)  possible_moves.prepend (PossibleMove (half_game_size - 1, half_game_size    ));
+        if (bottom_right) possible_moves.prepend (PossibleMove (half_game_size    , half_game_size    ));
+    }
+    private inline void humans_opening_update_possible_moves_odd ()
+    {
+        possible_moves = new SList<PossibleMove?> ();
+
+        uint8 half_game_size = size / 2;
+
+        // light starts, first ply
+        if (n_light_tiles == 0)
+        {
+            possible_moves.prepend (PossibleMove (half_game_size - 1, half_game_size    ));
+            possible_moves.prepend (PossibleMove (half_game_size    , half_game_size - 1));
+            possible_moves.prepend (PossibleMove (half_game_size    , half_game_size + 1));
+            possible_moves.prepend (PossibleMove (half_game_size + 1, half_game_size    ));
+        }
+        // first dark ply
+        else if (current_color == Player.DARK)
+        {
+            for (uint8 x = half_game_size - 1; x <= half_game_size + 1; x++)
+                for (uint8 y = half_game_size - 1; y <= half_game_size + 1; y++)
+                    if (get_owner (x, y) == Player.NONE)
+                        possible_moves.prepend (PossibleMove (x, y));
+        }
+        // dark played vertically or horizontally the center of the opening zone
+        else if (get_owner (half_game_size - 1, half_game_size - 1) == Player.NONE
+              && get_owner (half_game_size - 1, half_game_size + 1) == Player.NONE)
+        {
+            possible_moves.prepend (PossibleMove (half_game_size - 1, half_game_size - 1));
+            possible_moves.prepend (PossibleMove (half_game_size - 1, half_game_size + 1));
+            possible_moves.prepend (PossibleMove (half_game_size + 1, half_game_size - 1));
+            possible_moves.prepend (PossibleMove (half_game_size + 1, half_game_size + 1));
+        }
+        // light started horizontally, dark played in a corner of the opening zone
+        else if (get_owner (half_game_size, half_game_size - 1) == Player.NONE)
+        {
+            possible_moves.prepend (PossibleMove (half_game_size    , half_game_size - 1));
+            possible_moves.prepend (PossibleMove (half_game_size    , half_game_size + 1));
+        }
+        // light started vertically, dark played in a corner of the opening zone
+        else
+        {
+            possible_moves.prepend (PossibleMove (half_game_size - 1, half_game_size    ));
+            possible_moves.prepend (PossibleMove (half_game_size + 1, half_game_size    ));
+        }
     }
 }
 
diff --git a/src/iagno.vala b/src/iagno.vala
index 3056fa5..fddb07d 100644
--- a/src/iagno.vala
+++ b/src/iagno.vala
@@ -644,7 +644,7 @@ private class Iagno : Gtk.Application, BaseApplication
         }
         else if (usual_start)
             opening = Opening.REVERSI;
-        else if (two_players && even_board && !random_start) // TODO make work on odd board
+        else if (two_players && !random_start)
             opening = Opening.HUMANS;
         else if (random_start
               || settings.get_boolean ("random-start-position")
diff --git a/src/reversi-view.vala b/src/reversi-view.vala
index 3889508..7cb5937 100644
--- a/src/reversi-view.vala
+++ b/src/reversi-view.vala
@@ -173,10 +173,16 @@ private class ReversiView : Gtk.DrawingArea
             last_state_set = false;
             if (_game.opening == Opening.HUMANS)
             {
-                if (!even_board)
-                    assert_not_reached ();
-                overture_steps  = { 0, 0, 0, 0 };
-                overture_target = { 0, 0, 0, 0 };
+                if (even_board)
+                {
+                    overture_steps  = { 0, 0, 0, 0 };
+                    overture_target = { 0, 0, 0, 0 };
+                }
+                else
+                {
+                    overture_steps  = { 0, 0, 0, 0, 0, 0 };
+                    overture_target = { 0, 0, 0, 0, 0, 0 };
+                }
                 current_overture_playable = 0;
                 if (configuration_done)
                     configure_overture_origin ();
@@ -369,11 +375,36 @@ private class ReversiView : Gtk.DrawingArea
     }
     private inline void configure_overture_origin ()
     {
-        overture_origin_xs [0] =  5 * board_size / 16 - border_width - tile_size / 2;
-        overture_origin_xs [1] =  7 * board_size / 16 - border_width - tile_size / 2;
-        overture_origin_xs [2] =  9 * board_size / 16 - border_width - tile_size / 2;
-        overture_origin_xs [3] = 11 * board_size / 16 - border_width - tile_size / 2;
-        overture_origin_y      =  3 * board_size / 4  - border_width - tile_size / 2;
+        if (game_size % 2 == 0)
+        {
+            overture_origin_xs [0] = (game_size - 3) * board_size / (2 * game_size) - border_width - 
tile_size / 2;
+            overture_origin_xs [1] = (game_size - 1) * board_size / (2 * game_size) - border_width - 
tile_size / 2;
+            overture_origin_xs [2] = (game_size + 1) * board_size / (2 * game_size) - border_width - 
tile_size / 2;
+            overture_origin_xs [3] = (game_size + 3) * board_size / (2 * game_size) - border_width - 
tile_size / 2;
+
+            if (game_size == 4)
+                // where we can
+                overture_origin_y  = (int) ((game_size + 2.6) * board_size / (2 * game_size) - border_width 
- tile_size / 2);
+            else
+                // on the line under the center zone
+                overture_origin_y  = (game_size + 4) * board_size / (2 * game_size) - border_width - 
tile_size / 2;
+        }
+        else
+        {
+            overture_origin_xs [0] = (game_size - 2) * board_size / (2 * game_size) - border_width - 
tile_size / 2;
+            overture_origin_xs [2] =  game_size      * board_size / (2 * game_size) - border_width - 
tile_size / 2;
+            overture_origin_xs [4] = (game_size + 2) * board_size / (2 * game_size) - border_width - 
tile_size / 2;
+            overture_origin_xs [1] = overture_origin_xs [0];
+            overture_origin_xs [3] = overture_origin_xs [2];
+            overture_origin_xs [5] = overture_origin_xs [4];
+
+            if (game_size == 5)
+                // where we can
+                overture_origin_y  = (int) ((game_size + 3.6) * board_size / (2 * game_size) - border_width 
- tile_size / 2);
+            else
+                // on the line under the center zone
+                overture_origin_y  = (game_size + 5) * board_size / (2 * game_size) - border_width - 
tile_size / 2;
+        }
     }
 
     protected override bool draw (Cairo.Context cr)
@@ -502,10 +533,17 @@ private class ReversiView : Gtk.DrawingArea
         for (uint8 x = 0; x < game_size; x++)
             for (uint8 y = 0; y < game_size; y++)
             {
+                // even and odd boards
                 if ((x == half_game_size || x == half_game_size - 1)
                  && (y == half_game_size || y == half_game_size - 1))
                     continue;
 
+                // odd boards only
+                if (game_size % 2 != 0
+                 && (((y == half_game_size + 1) && (x == half_game_size - 1 || x == half_game_size || x == 
half_game_size + 1))
+                  || ((x == half_game_size + 1) && (y == half_game_size - 1 || y == half_game_size))))
+                    continue;
+
                 darken_tile (cr, x, y);
             }
 
@@ -649,24 +687,35 @@ private class ReversiView : Gtk.DrawingArea
     }
 
     private const uint8 OVERTURE_STEPS_MAX = 10;
-    private int [] overture_origin_xs = new int [4];
+    private int [] overture_origin_xs = new int [6];            // 4 for even boards, 6 for odd ones
     private int overture_origin_y = 0;
-    private uint8 [] overture_steps  = { 0, 0, 0, 0 };
-    private uint8 [] overture_target = { 0, 0, 0, 0 };
+    private uint8 [] overture_steps  = { 0, 0, 0, 0, 0, 0 };    // { 0, 0, 0, 0 } for even boards
+    private uint8 [] overture_target = { 0, 0, 0, 0, 0, 0 };    // { 0, 0, 0, 0 } for even boards
     private uint8 current_overture_playable = 0;
     private inline void draw_overture_playables (Cairo.Context cr)
     {
-        if (current_overture_playable < 4)
+        bool even_board = game_size % 2 == 0;
+        if (current_overture_playable < (even_board ? 4 : 6))
             draw_overture_indicator (cr);
-        for (uint8 i = 0; i < 4; i++)
+
+        for (uint8 i = 0; i < (even_board ? 4 : 6); i++)
             draw_overture_playable (cr, i);
+
+        if (!even_board)
+        {
+            uint8 half_game_size = game_size / 2;
+            draw_playable (cr, pixmaps [half_game_size, half_game_size],
+                               tile_xs [half_game_size, half_game_size],
+                               tile_ys [half_game_size, half_game_size]);
+        }
     }
     private inline void draw_overture_indicator (Cairo.Context cr)
     {
+        double diameter_factor = game_size == 4 ? 1.95 : 1.8;
         cr.set_source_rgba (background_red, background_green, background_blue, 1.0);
         cr.arc ((double) overture_origin_xs [current_overture_playable] + (double) tile_size / 2.0,
                 (double) overture_origin_y + (double) tile_size / 2.0,
-                (double) tile_size / 1.8,
+                (double) tile_size / diameter_factor,
                 0.0,
                 Math.PI * 2.0);
         cr.fill ();
@@ -675,7 +724,7 @@ private class ReversiView : Gtk.DrawingArea
     {
         if (overture_steps [playable_id] == OVERTURE_STEPS_MAX)
         {
-            int x, y;
+            uint8 x, y;
             get_x_and_y (playable_id, out x, out y);
             draw_playable (cr, pixmaps [x, y], tile_xs [x, y], tile_ys [x, y]);
             return;
@@ -686,7 +735,7 @@ private class ReversiView : Gtk.DrawingArea
 
         if (overture_target [playable_id] != 0)
         {
-            int x, y;
+            uint8 x, y;
             get_x_and_y (playable_id, out x, out y);
             tile_x += (tile_xs [x, y] - tile_x) * overture_steps [playable_id] / OVERTURE_STEPS_MAX;
             tile_y += (tile_ys [x, y] - tile_y) * overture_steps [playable_id] / OVERTURE_STEPS_MAX;
@@ -694,7 +743,10 @@ private class ReversiView : Gtk.DrawingArea
             overture_steps [playable_id]++;
             queue_draw ();
         }
-        _draw_overture_playable (cr, tile_x, tile_y, /* pixmap */ (playable_id % 2) * 30 + 1);
+        if (game_size % 2 == 0)
+            _draw_overture_playable (cr, tile_x, tile_y, /* pixmap */ (playable_id % 2) * 30 + 1);
+        else
+            _draw_overture_playable (cr, tile_x, tile_y, /* pixmap */ ((playable_id / 2 + 1) % 2) * 30 + 1);
     }
     private void _draw_overture_playable (Cairo.Context cr, int tile_x, int tile_y, int pixmap)
     {
@@ -706,17 +758,31 @@ private class ReversiView : Gtk.DrawingArea
         cr.rectangle (tile_x, tile_y, /* width and height */ tile_size, tile_size);
         cr.fill ();
     }
-    private void get_x_and_y (uint8 playable_id, out int x, out int y)
+    private void get_x_and_y (uint8 playable_id, out uint8 x, out uint8 y)
     {
-        int half_game_size = game_size / 2;
-        switch (overture_target [playable_id])
-        {
-            case 1: x = half_game_size - 1; y = half_game_size - 1; break;
-            case 2: x = half_game_size    ; y = half_game_size - 1; break;
-            case 3: x = half_game_size - 1; y = half_game_size    ; break;
-            case 4: x = half_game_size    ; y = half_game_size    ; break;
-            default: assert_not_reached ();
-        }
+        uint8 half_game_size = game_size / 2;
+        if (game_size % 2 == 0)
+            switch (overture_target [playable_id])
+            {
+                case 1: x = half_game_size - 1; y = half_game_size - 1; break;
+                case 2: x = half_game_size    ; y = half_game_size - 1; break;
+                case 3: x = half_game_size - 1; y = half_game_size    ; break;
+                case 4: x = half_game_size    ; y = half_game_size    ; break;
+                default: assert_not_reached ();
+            }
+        else
+            switch (overture_target [playable_id])
+            {
+                case 1: x = half_game_size - 1; y = half_game_size - 1; break;
+                case 2: x = half_game_size    ; y = half_game_size - 1; break;
+                case 3: x = half_game_size + 1; y = half_game_size - 1; break;
+                case 4: x = half_game_size - 1; y = half_game_size    ; break;
+                case 6: x = half_game_size + 1; y = half_game_size    ; break;
+                case 7: x = half_game_size - 1; y = half_game_size + 1; break;
+                case 8: x = half_game_size    ; y = half_game_size + 1; break;
+                case 9: x = half_game_size + 1; y = half_game_size + 1; break;
+                default: assert_not_reached ();
+            }
     }
 
     /*\
@@ -897,19 +963,49 @@ private class ReversiView : Gtk.DrawingArea
     {
         if (humans_opening_intensity != 0)
         {
+            bool even_board = game_size % 2 == 0;
             uint8 half_game_size = game_size / 2;
             uint8 target;
-            if (     (!last_state_set || last_state.get_owner (half_game_size - 1, half_game_size - 1) == 
Player.NONE)
-                              && game.current_state.get_owner (half_game_size - 1, half_game_size - 1) != 
Player.NONE) target = 1;
-            else if ((!last_state_set || last_state.get_owner (half_game_size    , half_game_size - 1) == 
Player.NONE)
-                              && game.current_state.get_owner (half_game_size    , half_game_size - 1) != 
Player.NONE) target = 2;
-            else if ((!last_state_set || last_state.get_owner (half_game_size - 1, half_game_size    ) == 
Player.NONE)
-                              && game.current_state.get_owner (half_game_size - 1, half_game_size    ) != 
Player.NONE) target = 3;
-            else if ((!last_state_set || last_state.get_owner (half_game_size    , half_game_size    ) == 
Player.NONE)
-                              && game.current_state.get_owner (half_game_size    , half_game_size    ) != 
Player.NONE) target = 4;
-            else assert_not_reached ();
-            overture_target [current_overture_playable] = target;
-            current_overture_playable++;
+            if (even_board)
+            {
+                if (     (!last_state_set || last_state.get_owner (half_game_size - 1, half_game_size - 1) 
== Player.NONE)
+                                  && game.current_state.get_owner (half_game_size - 1, half_game_size - 1) 
!= Player.NONE) target = 1;
+                else if ((!last_state_set || last_state.get_owner (half_game_size    , half_game_size - 1) 
== Player.NONE)
+                                  && game.current_state.get_owner (half_game_size    , half_game_size - 1) 
!= Player.NONE) target = 2;
+                else if ((!last_state_set || last_state.get_owner (half_game_size - 1, half_game_size    ) 
== Player.NONE)
+                                  && game.current_state.get_owner (half_game_size - 1, half_game_size    ) 
!= Player.NONE) target = 3;
+                else if ((!last_state_set || last_state.get_owner (half_game_size    , half_game_size    ) 
== Player.NONE)
+                                  && game.current_state.get_owner (half_game_size    , half_game_size    ) 
!= Player.NONE) target = 4;
+                else assert_not_reached ();
+
+                overture_target [current_overture_playable] = target;
+                current_overture_playable++;
+            }
+            else
+            {
+                if (     (!last_state_set || last_state.get_owner (half_game_size - 1, half_game_size - 1) 
== Player.NONE)
+                                  && game.current_state.get_owner (half_game_size - 1, half_game_size - 1) 
!= Player.NONE) target = 1;
+                else if ((!last_state_set || last_state.get_owner (half_game_size    , half_game_size - 1) 
== Player.NONE)
+                                  && game.current_state.get_owner (half_game_size    , half_game_size - 1) 
!= Player.NONE) target = 2;
+                else if ((!last_state_set || last_state.get_owner (half_game_size + 1, half_game_size - 1) 
== Player.NONE)
+                                  && game.current_state.get_owner (half_game_size + 1, half_game_size - 1) 
!= Player.NONE) target = 3;
+                else if ((!last_state_set || last_state.get_owner (half_game_size - 1, half_game_size    ) 
== Player.NONE)
+                                  && game.current_state.get_owner (half_game_size - 1, half_game_size    ) 
!= Player.NONE) target = 4;
+                else if ((!last_state_set || last_state.get_owner (half_game_size + 1, half_game_size    ) 
== Player.NONE)
+                                  && game.current_state.get_owner (half_game_size + 1, half_game_size    ) 
!= Player.NONE) target = 6;
+                else if ((!last_state_set || last_state.get_owner (half_game_size - 1, half_game_size + 1) 
== Player.NONE)
+                                  && game.current_state.get_owner (half_game_size - 1, half_game_size + 1) 
!= Player.NONE) target = 7;
+                else if ((!last_state_set || last_state.get_owner (half_game_size    , half_game_size + 1) 
== Player.NONE)
+                                  && game.current_state.get_owner (half_game_size    , half_game_size + 1) 
!= Player.NONE) target = 8;
+                else if ((!last_state_set || last_state.get_owner (half_game_size + 1, half_game_size + 1) 
== Player.NONE)
+                                  && game.current_state.get_owner (half_game_size + 1, half_game_size + 1) 
!= Player.NONE) target = 9;
+                else assert_not_reached ();
+
+                overture_target [current_overture_playable] = target;
+                current_overture_playable++;
+                overture_target [current_overture_playable] = 10 - target;
+                current_overture_playable++;
+            }
         }
         if (!no_draw)
         {


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