[iagno] Introduce ComputerReversi.
- From: Arnaud B. <arnaudb src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [iagno] Introduce ComputerReversi.
- Date: Wed, 22 May 2019 12:59:57 +0000 (UTC)
commit 57940fbfb94a8446ffa6ab24cc07a00b2a8ed688
Author: Arnaud Bonatti <arnaud bonatti gmail com>
Date: Fri Apr 26 11:53:20 2019 +0200
Introduce ComputerReversi.
ComputerPlayer is generic, it might
be subclassed for another game. The
new class contains reversi-specific
things (position evaluation, etc.).
src/computer-player.vala | 310 +------------------------------------------
src/computer-reversi.vala | 325 ++++++++++++++++++++++++++++++++++++++++++++++
src/iagno.vala | 2 +-
src/meson.build | 2 +
src/test-iagno.vala | 18 +--
5 files changed, 343 insertions(+), 314 deletions(-)
---
diff --git a/src/computer-player.vala b/src/computer-player.vala
index 83dec7f..dc16d86 100644
--- a/src/computer-player.vala
+++ b/src/computer-player.vala
@@ -20,47 +20,8 @@
along with GNOME Reversi. If not, see <https://www.gnu.org/licenses/>.
*/
-private class ComputerPlayer : Object
+private abstract class ComputerPlayer : Object
{
- private struct PossibleMove
- {
- public uint8 x;
- public uint8 y;
- public uint8 n_tiles;
-
- private PossibleMove (uint8 x, uint8 y, uint8 n_tiles)
- {
- this.x = x;
- this.y = y;
- this.n_tiles = n_tiles;
- }
- }
-
- /* Big enough. Don't use int16.MIN / int16.MAX, because int16.MIN ≠ - int16.MAX */
- private const int16 POSITIVE_INFINITY = 10000;
- private const int16 NEGATIVE_INFINITY = -10000;
- private const int16 LESS_THAN_NEGATIVE_INFINITY = -10001;
-
- /* Game being played */
- private Game game;
-
- /* Strength */
- private uint8 difficulty_level;
- private uint8 initial_depth;
-
- /* Value of owning each location */
- private const int16 [,] heuristic =
- {
- { 65, -3, 6, 4, 4, 6, -3, 65 },
- { -3, -29, 3, 1, 1, 3, -29, -3 },
- { 6, 3, 5, 3, 3, 5, 3, 6 },
- { 4, 1, 3, 1, 1, 3, 1, 4 },
- { 4, 1, 3, 1, 1, 3, 1, 4 },
- { 6, 3, 5, 3, 3, 5, 3, 6 },
- { -3, -29, 3, 1, 1, 3, -29, -3 },
- { 65, -3, 6, 4, 4, 6, -3, 65 }
- };
-
/* Source ID of a pending move timeout */
private uint pending_move_id = 0;
@@ -68,9 +29,9 @@ private class ComputerPlayer : Object
* The mutex is only needed for its memory barrier. */
private bool _move_pending = false;
private RecMutex _move_pending_mutex;
- [CCode (notify = false)] private bool move_pending
+ [CCode (notify = false)] protected bool move_pending
{
- get
+ protected get
{
_move_pending_mutex.lock ();
bool result = _move_pending;
@@ -78,7 +39,7 @@ private class ComputerPlayer : Object
return result;
}
- set
+ private set
{
_move_pending_mutex.lock ();
_move_pending = value;
@@ -86,31 +47,6 @@ private class ComputerPlayer : Object
}
}
- internal ComputerPlayer (Game game, uint8 difficulty_level = 1)
- {
- this.game = game;
- this.difficulty_level = difficulty_level;
- this.initial_depth = difficulty_level * 2;
- }
-
- private void complete_move (uint8 x, uint8 y)
- {
- if (!game.place_tile (x, y))
- {
- critical ("Computer chose an invalid move: %d,%d\n%s", x, y, game.to_string ());
-
- /* Has been reached, once. So let's have a fallback. */
- uint8 new_x;
- uint8 new_y;
- random_select (game.current_state, out new_x, out new_y);
- if (!game.place_tile (new_x, new_y))
- {
- critical ("Computer chose an invalid move for the second time: %d,%d\n%s", new_x, new_y,
game.to_string ());
- assert_not_reached ();
- }
- }
- }
-
internal void move_sync (out uint8 x, out uint8 y) // for tests
{
move_pending = true;
@@ -184,240 +120,6 @@ private class ComputerPlayer : Object
move_pending = false;
}
- /*\
- * * Minimax / Negamax / alpha-beta pruning
- \*/
-
- private void run_search (out uint8 x, out uint8 y)
- requires (game.current_player_can_move)
- {
- /* For the first/first two moves play randomly so the game is not always the same */
- if (game.current_state.n_tiles < game.initial_number_of_tiles + (game.size < 6 ? 2 : 4))
- {
- random_select (game.current_state, out x, out y);
- return;
- }
-
- x = 0; // garbage
- y = 0; // idem
-
- /* Choose a location to place by building the tree of possible moves and
- * using the minimax algorithm to pick the best branch with the chosen
- * strategy. */
- GameState g = new GameState.copy (game.current_state);
- /* The search sometimes returns NEGATIVE_INFINITY. */
- int16 a = LESS_THAN_NEGATIVE_INFINITY;
-
- List<PossibleMove?> moves;
- get_possible_moves_sorted (g, out moves);
-
- /* Try each move using alpha-beta pruning to optimise finding the best branch */
- foreach (PossibleMove? move in moves)
- {
- if (move == null)
- assert_not_reached ();
-
- GameState _g = new GameState.copy_and_move (g, ((!) move).x, ((!) move).y);
-
- int16 a_new = -1 * search (_g, initial_depth, NEGATIVE_INFINITY, -a);
- if (a_new > a)
- {
- a = a_new;
- x = ((!) move).x;
- y = ((!) move).y;
- }
- }
- }
-
- private int16 search (GameState g, uint8 depth, int16 a, int16 b)
- requires (a <= b)
- {
- /* End of the game, return a near-infinite evaluation */
- if (g.is_complete)
- return g.n_current_tiles > g.n_opponent_tiles ? POSITIVE_INFINITY - (int16) g.n_opponent_tiles
- : NEGATIVE_INFINITY + (int16) g.n_current_tiles;
-
- /* Checking move_pending here is optional. It helps avoid a long unnecessary search
- * if the move has been cancelled, but is expensive because it requires taking a mutex. */
- if (!move_pending)
- return 0;
-
- /* End of the search, calculate how good a result this is. */
- if (depth == 0)
- return calculate_heuristic (g, ref difficulty_level);
-
- if (g.current_player_can_move)
- {
- List<PossibleMove?> moves;
- get_possible_moves_sorted (g, out moves);
-
- /* Try each move using alpha-beta pruning to optimise finding the best branch */
- foreach (PossibleMove? move in moves)
- {
- if (move == null)
- assert_not_reached ();
-
- GameState _g = new GameState.copy_and_move (g, ((!) move).x, ((!) move).y);
-
- int16 a_new = -1 * search (_g, depth - 1, -b, -a);
- if (a_new > a)
- a = a_new;
-
- /* This branch has worse values, so ignore it */
- if (b <= a)
- break;
- }
- }
- else // pass
- {
- GameState _g = new GameState.copy_and_pass (g);
-
- int16 a_new = -1 * search (_g, depth - 1, -b, -a);
- if (a_new > a)
- a = a_new;
- }
-
- return a;
- }
-
- private static void get_possible_moves_sorted (GameState g, out List<PossibleMove?> moves)
- {
- uint8 size = g.size;
- moves = new List<PossibleMove?> ();
-
- for (uint8 x = 0; x < size; x++)
- {
- for (uint8 y = 0; y < size; y++)
- {
- uint8 n_tiles = g.test_placing_tile (x, y);
- if (n_tiles == 0)
- continue;
-
- PossibleMove move = PossibleMove (x, y, n_tiles);
- // the g_list_insert_sorted() documentation says: "if you are adding many new elements to
- // a list, and the number of new elements is much larger than the length of the list, use
- // g_list_prepend() to add the new items and sort the list afterwards with g_list_sort()"
- // but the perfs tests on complete games disagree; so let's keep things like that for now
- moves.insert_sorted (move, compare_move);
- }
- }
- }
-
- private static int compare_move (PossibleMove? a, PossibleMove? b)
- requires (a != null)
- requires (b != null)
- {
- return ((!) b).n_tiles - ((!) a).n_tiles;
- }
-
- /*\
- * * AI
- \*/
-
- private static int16 calculate_heuristic (GameState g, ref uint8 difficulty_level)
- {
- int16 tile_difference = (int16) g.n_current_tiles - (int16) g.n_opponent_tiles;
-
- /* Try to lose */
- if (difficulty_level == 1)
- return -tile_difference;
-
- /* End of the game: just maximize the number of tokens */
- if (g.n_tiles >= (g.size * g.size) - 10)
- return tile_difference;
-
- /* Normal strategy: try to evaluate the position */
- return tile_difference + eval_heuristic (g) + around (g) ;
- }
-
- private static int16 eval_heuristic (GameState g)
- {
- uint8 size = g.size;
- if (size != 8) // TODO
- return 0;
-
- int16 count = 0;
- for (uint8 x = 0; x < size; x++)
- {
- for (uint8 y = 0; y < size; y++)
- {
- int16 h = heuristic [x, y];
- if (g.get_owner (x, y) != g.current_color)
- h = -h;
- count += h;
- }
- }
- return count;
- }
-
- private static int16 around (GameState g)
- {
- int16 count = 0;
- int8 size = (int8) g.size;
- int8 xpp;
- int8 xmm;
- int8 ypp;
- int8 ymm;
- for (int8 x = 0; x < size; x++)
- {
- xpp = x + 1;
- xmm = x - 1;
-
- for (int8 y = 0; y < size; y++)
- {
- ypp = y + 1;
- ymm = y - 1;
-
- int16 a = 0;
- a -= is_empty (g, xpp, y );
- a -= is_empty (g, xpp, ypp);
- a -= is_empty (g, x, ypp);
- a -= is_empty (g, xmm, ypp);
- a -= is_empty (g, xmm, y );
- a -= is_empty (g, xmm, ymm);
- a -= is_empty (g, x, ymm);
- a -= is_empty (g, xpp, ymm);
-
- /* Two points for completely surrounded tiles */
- if (a == 0)
- a = 2;
-
- count += (g.get_owner ((uint8) x, (uint8) y) == g.current_color) ? a : -a;
- }
- }
- return count;
- }
-
- private static int16 is_empty (GameState g, int8 x, int8 y)
- {
- if (!g.is_valid_location_signed (x, y))
- return 0;
- if (g.get_owner ((uint8) x, (uint8) y) != Player.NONE)
- return 0;
-
- return 1;
- }
-
- /*\
- * * First random moves
- \*/
-
- private static void random_select (GameState g, out uint8 move_x, out uint8 move_y)
- {
- List<uint8> moves = new List<uint8> ();
- uint8 size = g.size;
- for (uint8 x = 0; x < size; x++)
- for (uint8 y = 0; y < size; y++)
- if (g.can_place (x, y, g.current_color))
- moves.append (x * size + y);
-
- int length = (int) moves.length ();
- if (length <= 0)
- assert_not_reached ();
-
- uint8 i = (uint8) Random.int_range (0, length);
- uint8 xy = moves.nth_data (i);
- move_x = xy / size;
- move_y = xy % size;
- }
+ protected abstract void run_search (out uint8 x, out uint8 y);
+ protected abstract void complete_move (uint8 x, uint8 y);
}
diff --git a/src/computer-reversi.vala b/src/computer-reversi.vala
new file mode 100644
index 0000000..7ad3cd0
--- /dev/null
+++ b/src/computer-reversi.vala
@@ -0,0 +1,325 @@
+/* -*- Mode: vala; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ This file is part of GNOME Reversi, also known as Iagno.
+
+ Copyright 2010-2013 Robert Ancell
+ Copyright 2013-2014 Michael Catanzaro
+ Copyright 2014-2019 Arnaud Bonatti
+
+ GNOME Reversi 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 Reversi 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 Reversi. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+private class ComputerReversi : ComputerPlayer
+{
+ private struct PossibleMove
+ {
+ public uint8 x;
+ public uint8 y;
+ public uint8 n_tiles;
+
+ private PossibleMove (uint8 x, uint8 y, uint8 n_tiles)
+ {
+ this.x = x;
+ this.y = y;
+ this.n_tiles = n_tiles;
+ }
+ }
+
+ /* Game being played */
+ private Game game;
+
+ /* Big enough. Don't use int16.MIN / int16.MAX, because int16.MIN ≠ - int16.MAX */
+ private const int16 POSITIVE_INFINITY = 10000;
+ private const int16 NEGATIVE_INFINITY = -10000;
+ private const int16 LESS_THAN_NEGATIVE_INFINITY = -10001;
+
+ /* Strength */
+ private uint8 difficulty_level;
+ private uint8 initial_depth;
+
+ /* Value of owning each location */
+ private const int16 [,] heuristic =
+ {
+ { 65, -3, 6, 4, 4, 6, -3, 65 },
+ { -3, -29, 3, 1, 1, 3, -29, -3 },
+ { 6, 3, 5, 3, 3, 5, 3, 6 },
+ { 4, 1, 3, 1, 1, 3, 1, 4 },
+ { 4, 1, 3, 1, 1, 3, 1, 4 },
+ { 6, 3, 5, 3, 3, 5, 3, 6 },
+ { -3, -29, 3, 1, 1, 3, -29, -3 },
+ { 65, -3, 6, 4, 4, 6, -3, 65 }
+ };
+
+ internal ComputerReversi (Game game, uint8 difficulty_level = 1)
+ {
+ this.game = game;
+ this.difficulty_level = difficulty_level;
+ this.initial_depth = difficulty_level * 2;
+ }
+
+ protected override void complete_move (uint8 x, uint8 y)
+ {
+ if (!game.place_tile (x, y))
+ {
+ critical ("Computer chose an invalid move: %d,%d\n%s", x, y, game.to_string ());
+
+ /* Has been reached, once. So let's have a fallback. */
+ uint8 new_x;
+ uint8 new_y;
+ random_select (game.current_state, out new_x, out new_y);
+ if (!game.place_tile (new_x, new_y))
+ {
+ critical ("Computer chose an invalid move for the second time: %d,%d\n%s", new_x, new_y,
game.to_string ());
+ assert_not_reached ();
+ }
+ }
+ }
+
+ /*\
+ * * Minimax / Negamax / alpha-beta pruning
+ \*/
+
+ protected override void run_search (out uint8 x, out uint8 y)
+ requires (game.current_player_can_move)
+ {
+ /* For the first/first two moves play randomly so the game is not always the same */
+ if (game.current_state.n_tiles < game.initial_number_of_tiles + (game.size < 6 ? 2 : 4))
+ {
+ random_select (game.current_state, out x, out y);
+ return;
+ }
+
+ x = 0; // garbage
+ y = 0; // idem
+
+ /* Choose a location to place by building the tree of possible moves and
+ * using the minimax algorithm to pick the best branch with the chosen
+ * strategy. */
+ GameState g = new GameState.copy (game.current_state);
+ /* The search sometimes returns NEGATIVE_INFINITY. */
+ int16 a = LESS_THAN_NEGATIVE_INFINITY;
+
+ List<PossibleMove?> moves;
+ get_possible_moves_sorted (g, out moves);
+
+ /* Try each move using alpha-beta pruning to optimise finding the best branch */
+ foreach (PossibleMove? move in moves)
+ {
+ if (move == null)
+ assert_not_reached ();
+
+ GameState _g = new GameState.copy_and_move (g, ((!) move).x, ((!) move).y);
+
+ int16 a_new = -1 * search (_g, initial_depth, NEGATIVE_INFINITY, -a);
+ if (a_new > a)
+ {
+ a = a_new;
+ x = ((!) move).x;
+ y = ((!) move).y;
+ }
+ }
+ }
+
+ private int16 search (GameState g, uint8 depth, int16 a, int16 b)
+ requires (a <= b)
+ {
+ /* End of the game, return a near-infinite evaluation */
+ if (g.is_complete)
+ return g.n_current_tiles > g.n_opponent_tiles ? POSITIVE_INFINITY - (int16) g.n_opponent_tiles
+ : NEGATIVE_INFINITY + (int16) g.n_current_tiles;
+
+ /* Checking move_pending here is optional. It helps avoid a long unnecessary search
+ * if the move has been cancelled, but is expensive because it requires taking a mutex. */
+ if (!move_pending)
+ return 0;
+
+ /* End of the search, calculate how good a result this is. */
+ if (depth == 0)
+ return calculate_heuristic (g, ref difficulty_level);
+
+ if (g.current_player_can_move)
+ {
+ List<PossibleMove?> moves;
+ get_possible_moves_sorted (g, out moves);
+
+ /* Try each move using alpha-beta pruning to optimise finding the best branch */
+ foreach (PossibleMove? move in moves)
+ {
+ if (move == null)
+ assert_not_reached ();
+
+ GameState _g = new GameState.copy_and_move (g, ((!) move).x, ((!) move).y);
+
+ int16 a_new = -1 * search (_g, depth - 1, -b, -a);
+ if (a_new > a)
+ a = a_new;
+
+ /* This branch has worse values, so ignore it */
+ if (b <= a)
+ break;
+ }
+ }
+ else // pass
+ {
+ GameState _g = new GameState.copy_and_pass (g);
+
+ int16 a_new = -1 * search (_g, depth - 1, -b, -a);
+ if (a_new > a)
+ a = a_new;
+ }
+
+ return a;
+ }
+
+ private static void get_possible_moves_sorted (GameState g, out List<PossibleMove?> moves)
+ {
+ uint8 size = g.size;
+ moves = new List<PossibleMove?> ();
+
+ for (uint8 x = 0; x < size; x++)
+ {
+ for (uint8 y = 0; y < size; y++)
+ {
+ uint8 n_tiles = g.test_placing_tile (x, y);
+ if (n_tiles == 0)
+ continue;
+
+ PossibleMove move = PossibleMove (x, y, n_tiles);
+ // the g_list_insert_sorted() documentation says: "if you are adding many new elements to
+ // a list, and the number of new elements is much larger than the length of the list, use
+ // g_list_prepend() to add the new items and sort the list afterwards with g_list_sort()"
+ // but the perfs tests on complete games disagree; so let's keep things like that for now
+ moves.insert_sorted (move, compare_move);
+ }
+ }
+ }
+
+ private static int compare_move (PossibleMove? a, PossibleMove? b)
+ requires (a != null)
+ requires (b != null)
+ {
+ return ((!) b).n_tiles - ((!) a).n_tiles;
+ }
+
+ /*\
+ * * AI
+ \*/
+
+ private static int16 calculate_heuristic (GameState g, ref uint8 difficulty_level)
+ {
+ int16 tile_difference = (int16) g.n_current_tiles - (int16) g.n_opponent_tiles;
+
+ /* Try to lose */
+ if (difficulty_level == 1)
+ return -tile_difference;
+
+ /* End of the game: just maximize the number of tokens */
+ if (g.n_tiles >= (g.size * g.size) - 10)
+ return tile_difference;
+
+ /* Normal strategy: try to evaluate the position */
+ return tile_difference + eval_heuristic (g) + around (g) ;
+ }
+
+ private static int16 eval_heuristic (GameState g)
+ {
+ uint8 size = g.size;
+ if (size != 8) // TODO
+ return 0;
+
+ int16 count = 0;
+ for (uint8 x = 0; x < size; x++)
+ {
+ for (uint8 y = 0; y < size; y++)
+ {
+ int16 h = heuristic [x, y];
+ if (g.get_owner (x, y) != g.current_color)
+ h = -h;
+ count += h;
+ }
+ }
+ return count;
+ }
+
+ private static int16 around (GameState g)
+ {
+ int16 count = 0;
+ int8 size = (int8) g.size;
+ int8 xpp;
+ int8 xmm;
+ int8 ypp;
+ int8 ymm;
+ for (int8 x = 0; x < size; x++)
+ {
+ xpp = x + 1;
+ xmm = x - 1;
+
+ for (int8 y = 0; y < size; y++)
+ {
+ ypp = y + 1;
+ ymm = y - 1;
+
+ int16 a = 0;
+ a -= is_empty (g, xpp, y );
+ a -= is_empty (g, xpp, ypp);
+ a -= is_empty (g, x, ypp);
+ a -= is_empty (g, xmm, ypp);
+ a -= is_empty (g, xmm, y );
+ a -= is_empty (g, xmm, ymm);
+ a -= is_empty (g, x, ymm);
+ a -= is_empty (g, xpp, ymm);
+
+ /* Two points for completely surrounded tiles */
+ if (a == 0)
+ a = 2;
+
+ count += (g.get_owner ((uint8) x, (uint8) y) == g.current_color) ? a : -a;
+ }
+ }
+ return count;
+ }
+
+ private static int16 is_empty (GameState g, int8 x, int8 y)
+ {
+ if (!g.is_valid_location_signed (x, y))
+ return 0;
+ if (g.get_owner ((uint8) x, (uint8) y) != Player.NONE)
+ return 0;
+
+ return 1;
+ }
+
+ /*\
+ * * First random moves
+ \*/
+
+ private static void random_select (GameState g, out uint8 move_x, out uint8 move_y)
+ {
+ List<uint8> moves = new List<uint8> ();
+ uint8 size = g.size;
+ for (uint8 x = 0; x < size; x++)
+ for (uint8 y = 0; y < size; y++)
+ if (g.can_place (x, y, g.current_color))
+ moves.append (x * size + y);
+
+ int length = (int) moves.length ();
+ if (length <= 0)
+ assert_not_reached ();
+
+ uint8 i = (uint8) Random.int_range (0, length);
+ uint8 xy = moves.nth_data (i);
+ move_x = xy / size;
+ move_y = xy % size;
+ }
+}
diff --git a/src/iagno.vala b/src/iagno.vala
index cddfc7f..d5997ca 100644
--- a/src/iagno.vala
+++ b/src/iagno.vala
@@ -374,7 +374,7 @@ private class Iagno : Gtk.Application
if (settings.get_int ("num-players") == 2)
computer = null;
else
- computer = new ComputerPlayer (game, (uint8) settings.get_int ("computer-level"));
+ computer = new ComputerReversi (game, (uint8) settings.get_int ("computer-level"));
if (settings.get_enum ("color") == 1)
player_one = Player.LIGHT;
diff --git a/src/meson.build b/src/meson.build
index 9e9e8ae..f30803b 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -3,6 +3,7 @@ iagno_tests = executable('iagno_tests',
[
'test-iagno.vala',
'computer-player.vala',
+ 'computer-reversi.vala',
'game.vala',
'player.vala'
],
@@ -19,6 +20,7 @@ executable(meson.project_name(),
[
'vapi/config.vapi',
'computer-player.vala',
+ 'computer-reversi.vala',
'game-view.vala',
'game-window.vala',
'game.vala',
diff --git a/src/test-iagno.vala b/src/test-iagno.vala
index c6a10d4..4a00cce 100644
--- a/src/test-iagno.vala
+++ b/src/test-iagno.vala
@@ -164,7 +164,7 @@ private class TestIagno : Object
" L L L L L L L L",
" L L L L L L L L" };
Game game = new Game.from_strings (board, Player.LIGHT);
- ComputerPlayer ai = new ComputerPlayer (game, /* AI level */ 1);
+ ComputerPlayer ai = new ComputerReversi (game, /* AI level */ 1);
assert_true (ai_move (ai, 2, 0));
/* didn't crash */
}
@@ -180,7 +180,7 @@ private class TestIagno : Object
" . . . D . . . .",
" . . D . . . . ." };
Game game = new Game.from_strings (board, Player.LIGHT);
- ComputerPlayer ai = new ComputerPlayer (game, /* AI level */ 1);
+ ComputerPlayer ai = new ComputerReversi (game, /* AI level */ 1);
assert_true (ai_move (ai, 4, 6));
/* didn't crash */
}
@@ -196,7 +196,7 @@ private class TestIagno : Object
" L L L L L D D D",
" D D D D D D D D" };
Game game = new Game.from_strings (board, Player.LIGHT);
- ComputerPlayer ai = new ComputerPlayer (game, /* AI level */ 1);
+ ComputerPlayer ai = new ComputerReversi (game, /* AI level */ 1);
assert_true (ai_move (ai, 2, 0));
assert_true (game.get_owner (2, 0) == Player.LIGHT);
}
@@ -212,7 +212,7 @@ private class TestIagno : Object
" D D D L L D D D",
" D D D L D D D D" };
Game game = new Game.from_strings (board, Player.LIGHT);
- ComputerPlayer ai = new ComputerPlayer (game, /* AI level */ 1);
+ ComputerPlayer ai = new ComputerReversi (game, /* AI level */ 1);
assert_true (ai_move (ai, 1, 0));
assert_true (game.get_owner (1, 0) == Player.LIGHT);
}
@@ -228,7 +228,7 @@ private class TestIagno : Object
" . L L L D L L L",
" . . L L L L L L" };
Game game = new Game.from_strings (board, Player.LIGHT);
- ComputerPlayer ai = new ComputerPlayer (game, /* AI level */ 1);
+ ComputerPlayer ai = new ComputerReversi (game, /* AI level */ 1);
assert_true (ai_move (ai, 0, 5));
/* didn't crash */
}
@@ -250,7 +250,7 @@ private class TestIagno : Object
/* 7 */ " . . . . . . . ." };
Game game = new Game.from_strings (board, Player.DARK);
- ComputerPlayer ai = new ComputerPlayer (game, /* AI level */ 3);
+ ComputerPlayer ai = new ComputerReversi (game, /* AI level */ 3);
assert_true (game.place_tile (4, 1));
assert_true (ai_move (ai, 5, 5));
@@ -323,7 +323,7 @@ private class TestIagno : Object
/* 7 */ " . . . . . . . ." };
Game game = new Game.from_strings (board, Player.DARK);
- ComputerPlayer ai = new ComputerPlayer (game, /* AI level */ 3);
+ ComputerPlayer ai = new ComputerReversi (game, /* AI level */ 3);
assert_true (game.place_tile (4, 2));
assert_true (ai_move (ai, 5, 5));
@@ -396,7 +396,7 @@ private class TestIagno : Object
/* 7 */ " . . . . . . . ." };
Game game = new Game.from_strings (board, Player.DARK);
- ComputerPlayer ai = new ComputerPlayer (game, /* AI level */ 3);
+ ComputerPlayer ai = new ComputerReversi (game, /* AI level */ 3);
assert_true (ai_move (ai, 3, 5));
assert_true (game.place_tile (6, 3));
@@ -469,7 +469,7 @@ private class TestIagno : Object
/* 7 */ " . . . . . . . ." };
Game game = new Game.from_strings (board, Player.DARK);
- ComputerPlayer ai = new ComputerPlayer (game, /* AI level */ 3);
+ ComputerPlayer ai = new ComputerReversi (game, /* AI level */ 3);
assert_true (ai_move (ai, 5, 4));
assert_true (game.place_tile (6, 4));
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]