[four-in-a-row] Remove tabs.
- From: Michael Catanzaro <mcatanzaro src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [four-in-a-row] Remove tabs.
- Date: Sun, 5 Oct 2014 15:48:50 +0000 (UTC)
commit d11ac4a13c522b6aa3e4da9c73b40ae773001172
Author: Arnaud Bonatti <arnaud bonatti gmail com>
Date: Sun Oct 5 04:46:30 2014 +0200
Remove tabs.
https://bugzilla.gnome.org/show_bug.cgi?id=737908
src/ai.vala | 752 +++++++++++++++++++++++++++++-----------------------------
1 files changed, 376 insertions(+), 376 deletions(-)
---
diff --git a/src/ai.vala b/src/ai.vala
index 3fd3e70..d1ec09e 100644
--- a/src/ai.vala
+++ b/src/ai.vala
@@ -26,391 +26,391 @@ const int NEG_INF = -100000;
const int MAX_HEURIST_VALUE = 10000;
const int BOARD_ROWS = 6;
const int BOARD_COLUMNS = 7;
-enum Player { NONE, HUMAN, AI;}
-enum Difficulty {EASY, MEDIUM, HARD;}
+enum Player { NONE, HUMAN, AI; }
+enum Difficulty { EASY, MEDIUM, HARD; }
int playgame (string moves_until_now)
{
- var t = new DecisionTree ();
- return t.playgame (moves_until_now);
+ var t = new DecisionTree ();
+ return t.playgame (moves_until_now);
}
public class DecisionTree
{
- /* to mantain the status of the board, to be used by the heuristic function, the top left cell is [0,
0] */
- private Player[,] board = new Player [BOARD_ROWS, BOARD_COLUMNS];
- /* plies - depth of the DecisionTree */
- private int plies = 8;
- /* last_moving_player - The player who made the last move, set to Player.NONE if no one has made a
move yet */
- private Player last_moving_player = Player.NONE;
- /* next_move_in_column - stores the column number in which next move is to be made */
- private int next_move_in_column = -1;
- /* stores the difficulty level of the game */
- private Difficulty level;
-
- /* Initializes an empty board */
- public DecisionTree ()
- {
- for (int i = 0; i < BOARD_ROWS; i++)
- for (int j = 0; j < BOARD_COLUMNS; j++)
- board [i, j] = Player.NONE;
- }
-
- /* utility function for debugging purposes, prints a snapshot of current status of the board */
- public void print_board ()
- {
- for (int i = 0; i< BOARD_ROWS; i++)
- {
- for (int j = 0; j < BOARD_COLUMNS; j++)
- stdout.printf ("%d\t", board [i, j]);
- stdout.printf ("\n");
- }
- stdout.printf ("\n");
- }
-
- /* Recursively implements a negamax tree in memory with alpha-beta pruning. The function is first
called for the root node.
- It returns the value of the current node. For nodes at height == 0, the value is determined by a
heuristic function. */
- private int negamax (int height, int alpha, int beta)
- {
- /* base case of recursive function, returns if we have reached the lowest depth of
DecisionTree or the board if full */
- if (height == 0 || board_full ())
- {
- if (last_moving_player == Player.HUMAN)
- return heurist ();
- else if (last_moving_player == Player.AI)
- return -1 * heurist ();
- else
- return 0;
- }
-
- /* Local variable that stores the maximum value returned by the node's children so far.
- None of the children have returned anything so far. Hence, it is initialized with NEG_INF.
*/
- int max = NEG_INF;
-
- /* Local variable that stores the column number in which next move is to be made.
- Initialized with -1 because we do not know the column number yet. */
- int next = -1;
-
- for (int column = 0; column < BOARD_COLUMNS; column++)
- {
- /* make a move into the i'th column */
- if (!move (column))
- continue;
-
- /* victory() checks if making a move in the i'th column results in a victory for
last_moving_player.
- If so, multiply MAX_HEURIST_VALUE by a height factor to avoid closer threats first.
- Or, we need to go further down the negamax tree. */
- int temp = victory (column) ? MAX_HEURIST_VALUE * height : -1 * negamax (height - 1,
-1 * beta, -1 * alpha);
-
- unmove (column);
-
- if (temp > max)
- {
- next = column;
- max = temp;
- }
-
- if (temp > alpha)
- alpha = temp;
-
- if (alpha >= beta)
- break;
- }
-
- /* If it's the root node, asign the value of next to next_move_in_column */
- if (height == plies)
- next_move_in_column = next;
-
- return max;
- }
-
- /* all these functions return true if last_moving_player wins, or false */
- private bool victory (int column)
- {
- /* find the cell on which the last move was made */
- int row;
- for (row = 0; row < BOARD_ROWS && board [row, column] == Player.NONE; row++);
-
- return vertical_win (row, column) ||
- horizontal_win (row, column) ||
- forward_diagonal_win (row, column) ||
- backward_diagonal_win (row, column);
- }
-
- private bool forward_diagonal_win (int i, int j)
- {
- int count = 0;
-
- for (int k = i, l = j; k >= 0 && l < BOARD_COLUMNS && board [k, l] == last_moving_player;
k--, l++, count++);
- for (int k = i + 1, l = j - 1; k < BOARD_ROWS && l >= 0 && board [k, l] ==
last_moving_player; k++, l--, count++);
-
- return count >= 4;
- }
-
- private bool backward_diagonal_win (int i, int j)
- {
- int count = 0;
-
- for (int k = i, l = j; k >= 0 && l >= 0 && board [k, l] == last_moving_player; k--, l--,
count++);
- for (int k = i + 1, l = j + 1; k < BOARD_ROWS && l < BOARD_COLUMNS && board [k, l] ==
last_moving_player; k++, l++, count++);
-
- return count >= 4;
- }
-
- private bool horizontal_win (int i, int j)
- {
- int count = 0;
-
- for (int k = j; k >= 0 && board [i, k] == last_moving_player; k--, count++);
- for (int k = j + 1; k < BOARD_COLUMNS && board [i, k] == last_moving_player; k++, count++);
-
- return count >= 4;
- }
-
- private bool vertical_win (int i, int j)
- {
- int count = 0;
-
- for (int k = i; k < BOARD_ROWS && board [k, j] == last_moving_player; k++, count++);
-
- return count >= 4;
- }
-
- /* returns true if the board is full, false if not */
- private bool board_full ()
- {
- for (int i = 0 ; i < BOARD_COLUMNS ; i++)
- if (board [0, i] == Player.NONE)
- return false;
- return true;
- }
+ /* to mantain the status of the board, to be used by the heuristic function, the top left cell is [0, 0]
*/
+ private Player[,] board = new Player [BOARD_ROWS, BOARD_COLUMNS];
+ /* plies - depth of the DecisionTree */
+ private int plies = 8;
+ /* last_moving_player - The player who made the last move, set to Player.NONE if no one has made a move
yet */
+ private Player last_moving_player = Player.NONE;
+ /* next_move_in_column - stores the column number in which next move is to be made */
+ private int next_move_in_column = -1;
+ /* stores the difficulty level of the game */
+ private Difficulty level;
+
+ /* Initializes an empty board */
+ public DecisionTree ()
+ {
+ for (int i = 0; i < BOARD_ROWS; i++)
+ for (int j = 0; j < BOARD_COLUMNS; j++)
+ board [i, j] = Player.NONE;
+ }
+
+ /* utility function for debugging purposes, prints a snapshot of current status of the board */
+ public void print_board ()
+ {
+ for (int i = 0; i< BOARD_ROWS; i++)
+ {
+ for (int j = 0; j < BOARD_COLUMNS; j++)
+ stdout.printf ("%d\t", board [i, j]);
+ stdout.printf ("\n");
+ }
+ stdout.printf ("\n");
+ }
+
+ /* Recursively implements a negamax tree in memory with alpha-beta pruning. The function is first called
for the root node.
+ It returns the value of the current node. For nodes at height == 0, the value is determined by a
heuristic function. */
+ private int negamax (int height, int alpha, int beta)
+ {
+ /* base case of recursive function, returns if we have reached the lowest depth of DecisionTree or
the board if full */
+ if (height == 0 || board_full ())
+ {
+ if (last_moving_player == Player.HUMAN)
+ return heurist ();
+ else if (last_moving_player == Player.AI)
+ return -1 * heurist ();
+ else
+ return 0;
+ }
+
+ /* Local variable that stores the maximum value returned by the node's children so far.
+ None of the children have returned anything so far. Hence, it is initialized with NEG_INF. */
+ int max = NEG_INF;
+
+ /* Local variable that stores the column number in which next move is to be made.
+ Initialized with -1 because we do not know the column number yet. */
+ int next = -1;
+
+ for (int column = 0; column < BOARD_COLUMNS; column++)
+ {
+ /* make a move into the i'th column */
+ if (!move (column))
+ continue;
+
+ /* victory() checks if making a move in the i'th column results in a victory for
last_moving_player.
+ If so, multiply MAX_HEURIST_VALUE by a height factor to avoid closer threats first.
+ Or, we need to go further down the negamax tree. */
+ int temp = victory (column) ? MAX_HEURIST_VALUE * height : -1 * negamax (height - 1, -1 * beta,
-1 * alpha);
+
+ unmove (column);
+
+ if (temp > max)
+ {
+ next = column;
+ max = temp;
+ }
+
+ if (temp > alpha)
+ alpha = temp;
+
+ if (alpha >= beta)
+ break;
+ }
+
+ /* If it's the root node, asign the value of next to next_move_in_column */
+ if (height == plies)
+ next_move_in_column = next;
+
+ return max;
+ }
+
+ /* all these functions return true if last_moving_player wins, or false */
+ private bool victory (int column)
+ {
+ /* find the cell on which the last move was made */
+ int row;
+ for (row = 0; row < BOARD_ROWS && board [row, column] == Player.NONE; row++);
+
+ return vertical_win (row, column) ||
+ horizontal_win (row, column) ||
+ forward_diagonal_win (row, column) ||
+ backward_diagonal_win (row, column);
+ }
+
+ private bool forward_diagonal_win (int i, int j)
+ {
+ int count = 0;
+
+ for (int k = i, l = j; k >= 0 && l < BOARD_COLUMNS && board [k, l] == last_moving_player; k--, l++,
count++);
+ for (int k = i + 1, l = j - 1; k < BOARD_ROWS && l >= 0 && board [k, l] == last_moving_player; k++,
l--, count++);
+
+ return count >= 4;
+ }
+
+ private bool backward_diagonal_win (int i, int j)
+ {
+ int count = 0;
+
+ for (int k = i, l = j; k >= 0 && l >= 0 && board [k, l] == last_moving_player; k--, l--, count++);
+ for (int k = i + 1, l = j + 1; k < BOARD_ROWS && l < BOARD_COLUMNS && board [k, l] ==
last_moving_player; k++, l++, count++);
+
+ return count >= 4;
+ }
+
+ private bool horizontal_win (int i, int j)
+ {
+ int count = 0;
+
+ for (int k = j; k >= 0 && board [i, k] == last_moving_player; k--, count++);
+ for (int k = j + 1; k < BOARD_COLUMNS && board [i, k] == last_moving_player; k++, count++);
+
+ return count >= 4;
+ }
+
+ private bool vertical_win (int i, int j)
+ {
+ int count = 0;
+
+ for (int k = i; k < BOARD_ROWS && board [k, j] == last_moving_player; k++, count++);
+
+ return count >= 4;
+ }
+
+ /* returns true if the board is full, false if not */
+ private bool board_full ()
+ {
+ for (int i = 0 ; i < BOARD_COLUMNS ; i++)
+ if (board [0, i] == Player.NONE)
+ return false;
+ return true;
+ }
- /* makes a move into the column'th column. Returns true if the move was succesful, false if it wasn't
*/
- private bool move (int column)
- {
- /* find the cell on which to move */
- int row;
- for (row = BOARD_ROWS - 1; row >= 0 && board [row, column] != Player.NONE; row--);
-
- if (row < 0)
- return false;
+ /* makes a move into the column'th column. Returns true if the move was succesful, false if it wasn't */
+ private bool move (int column)
+ {
+ /* find the cell on which to move */
+ int row;
+ for (row = BOARD_ROWS - 1; row >= 0 && board [row, column] != Player.NONE; row--);
+
+ if (row < 0)
+ return false;
- /* don't forget AI could make the first move */
- var player = last_moving_player != Player.AI ? Player.AI : Player.HUMAN;
- board [row, column] = player;
- last_moving_player = player;
+ /* don't forget AI could make the first move */
+ var player = last_moving_player != Player.AI ? Player.AI : Player.HUMAN;
+ board [row, column] = player;
+ last_moving_player = player;
- return true;
- }
-
- /* unmove the last move made in the column'th column */
- private void unmove (int column)
- requires (last_moving_player != Player.NONE)
- {
- /* find the cell on which the last move was made */
- int row;
- for (row = 0; row < BOARD_ROWS && board [row, column] == Player.NONE; row++);
-
- board [row, column] = Player.NONE;
-
- last_moving_player = last_moving_player == Player.AI ? Player.HUMAN : Player.AI;
- }
-
- /* vstr is the sequence of moves made until now. We update DecisionTree::board to reflect these
sequence of moves. */
- public void update_board (string vstr)
- {
- next_move_in_column = -1;
-
- /* AI will make the first move, nothing to add to the board */
- if (vstr.length == 2) return;
-
- var move = vstr.length % 2 == 0 ? Player.AI : Player.HUMAN;
-
- for (int i = 1; i < vstr.length - 1; i++)
- {
- int column = int.parse (vstr [i].to_string ()) - 1;
-
- /* find the cell on which the move is made */
- int row;
- for (row = BOARD_ROWS - 1; row >= 0 && board [row, column] != Player.NONE; row--);
-
- board [row, column] = move;
-
- move = move == Player.HUMAN ? Player.AI : Player.HUMAN;
- }
-
- last_moving_player = Player.HUMAN;
- }
-
- /* Check for immediate win of AI/HUMAN. It checks the current state of the board. Returns -1 if no
immediate win for Player P.
- Otherwise returns the column number in which Player P should move to win. */
- private int immediate_win (Player p)
- {
- Player old_last_moving_player = last_moving_player;
-
- last_moving_player = p == Player.AI ? Player.HUMAN : Player.AI;
-
- bool player_wins = false;
- int i;
- for (i = 0; i < BOARD_COLUMNS; i++)
- {
- if (!move (i))
- continue;
-
- player_wins = victory (i);
- unmove (i);
-
- if (player_wins)
- break;
- }
-
- last_moving_player = old_last_moving_player;
-
- /* returns -1 if no immediate win for Player p */
- return player_wins ? i : -1;
- }
-
- /* returns the column number in which the next move has to be made. Returns -1 if the board is full.
*/
- public int playgame (string vstr)
- {
- /* set the Difficulty level */
- set_level (vstr);
-
- /* update DecisionTree::board to reflect the moves made until now */
- update_board (vstr);
-
- /* if AI can win by making a move immediately, make that move;
- main.c has indexing beginning from 1 instead of 0, hence, we add 1 */
- int temp = immediate_win (Player.AI);
- if (temp != -1)
- return temp + 1;
-
- /* if HUMAN can win by making a move immediately,
- we make AI move in that column so as avoid loss */
- temp = immediate_win (Player.HUMAN);
- if (temp != -1)
- return temp + 1;
-
- /* call negamax tree on the current state of the board */
- negamax (plies, NEG_INF, -1 * NEG_INF);
-
- /* return the column number in which next move should be made */
- return next_move_in_column + 1;
- }
-
- /* The evaluation function to be called when we have reached the maximum depth in the DecisionTree */
- private int heurist ()
- {
- if (level == Difficulty.EASY)
- return heurist_easy ();
- else if (level == Difficulty.MEDIUM)
- return heurist_medium ();
- else
- return heurist_hard ();
- }
-
- private int heurist_easy ()
- {
- return -1 * heurist_hard ();
- }
-
- private int heurist_medium ()
- {
- return Random.int_range (1, 49);
- }
-
- private int heurist_hard ()
- {
- var count = count_3_in_a_row (Player.AI) - count_3_in_a_row (Player.HUMAN);
- return count == 0 ? Random.int_range (1, 49) : count * 100;
- }
-
- /* Count the number of threes in a row for Player P. It counts all those 3 which have an empty cell
in the vicinity to make it
- four in a row. */
- private int count_3_in_a_row (Player p)
- {
- int count = 0;
-
- Player old_last_moving_player = last_moving_player;
-
- last_moving_player = p;
-
- for (int j = 0; j < BOARD_COLUMNS; j++)
- {
- for (int i = 0; i < BOARD_ROWS; i++)
- {
- if (board [i, j] != Player.NONE)
- break;
-
- if (all_adjacent_empty (i, j))
- continue;
-
- board [i, j] = p;
-
- if (victory (j))
- count++;
-
- board [i, j] = Player.NONE;
- }
- }
- last_moving_player = old_last_moving_player;
- return count;
- }
-
- /* checks if all adjacent cells to board [i, j] are empty */
- private bool all_adjacent_empty (int i, int j)
- {
- for (int k = -1 ; k <= 1; k++)
- {
- for (int l = -1; l <= 1; l++)
- {
- if (k == 0 && l == 0)
- continue;
- if (i + k >= 0 && i + k < BOARD_ROWS && j + l >= 0 && j + l < BOARD_COLUMNS
&& board [i + k, j + l] != Player.NONE)
- return false;
- }
- }
-
- return true;
- }
-
- /* set the number of plies and the difficulty level */
- private void set_level (string vstr)
- {
- if (vstr [0] == 'a')
- {
- level = Difficulty.EASY;
- plies = 4;
- }
- else if (vstr [0] == 'b')
- {
- level = Difficulty.MEDIUM;
- plies = 7;
- }
- else
- {
- level = Difficulty.HARD;
- plies = 7;
- }
- }
-
- /* utility function for testing purposes */
- public int playandcheck (string vstr)
- {
- set_level (vstr);
- update_board (vstr);
-
- int temp = immediate_win (Player.AI);
- if (temp != -1)
- return 1000;
-
- temp = immediate_win (Player.HUMAN);
- if (temp != -1)
- return temp + 1;
-
- negamax (plies, NEG_INF, -1 * NEG_INF);
-
- return next_move_in_column + 1;
- }
+ return true;
+ }
+
+ /* unmove the last move made in the column'th column */
+ private void unmove (int column)
+ requires (last_moving_player != Player.NONE)
+ {
+ /* find the cell on which the last move was made */
+ int row;
+ for (row = 0; row < BOARD_ROWS && board [row, column] == Player.NONE; row++);
+
+ board [row, column] = Player.NONE;
+
+ last_moving_player = last_moving_player == Player.AI ? Player.HUMAN : Player.AI;
+ }
+
+ /* vstr is the sequence of moves made until now. We update DecisionTree::board to reflect these sequence
of moves. */
+ public void update_board (string vstr)
+ {
+ next_move_in_column = -1;
+
+ /* AI will make the first move, nothing to add to the board */
+ if (vstr.length == 2) return;
+
+ var move = vstr.length % 2 == 0 ? Player.AI : Player.HUMAN;
+
+ for (int i = 1; i < vstr.length - 1; i++)
+ {
+ int column = int.parse (vstr [i].to_string ()) - 1;
+
+ /* find the cell on which the move is made */
+ int row;
+ for (row = BOARD_ROWS - 1; row >= 0 && board [row, column] != Player.NONE; row--);
+
+ board [row, column] = move;
+
+ move = move == Player.HUMAN ? Player.AI : Player.HUMAN;
+ }
+
+ last_moving_player = Player.HUMAN;
+ }
+
+ /* Check for immediate win of AI/HUMAN. It checks the current state of the board. Returns -1 if no
immediate win for Player P.
+ Otherwise returns the column number in which Player P should move to win. */
+ private int immediate_win (Player p)
+ {
+ Player old_last_moving_player = last_moving_player;
+
+ last_moving_player = p == Player.AI ? Player.HUMAN : Player.AI;
+
+ bool player_wins = false;
+ int i;
+ for (i = 0; i < BOARD_COLUMNS; i++)
+ {
+ if (!move (i))
+ continue;
+
+ player_wins = victory (i);
+ unmove (i);
+
+ if (player_wins)
+ break;
+ }
+
+ last_moving_player = old_last_moving_player;
+
+ /* returns -1 if no immediate win for Player p */
+ return player_wins ? i : -1;
+ }
+
+ /* returns the column number in which the next move has to be made. Returns -1 if the board is full. */
+ public int playgame (string vstr)
+ {
+ /* set the Difficulty level */
+ set_level (vstr);
+
+ /* update DecisionTree::board to reflect the moves made until now */
+ update_board (vstr);
+
+ /* if AI can win by making a move immediately, make that move;
+ main.c has indexing beginning from 1 instead of 0, hence, we add 1 */
+ int temp = immediate_win (Player.AI);
+ if (temp != -1)
+ return temp + 1;
+
+ /* if HUMAN can win by making a move immediately,
+ we make AI move in that column so as avoid loss */
+ temp = immediate_win (Player.HUMAN);
+ if (temp != -1)
+ return temp + 1;
+
+ /* call negamax tree on the current state of the board */
+ negamax (plies, NEG_INF, -1 * NEG_INF);
+
+ /* return the column number in which next move should be made */
+ return next_move_in_column + 1;
+ }
+
+ /* The evaluation function to be called when we have reached the maximum depth in the DecisionTree */
+ private int heurist ()
+ {
+ if (level == Difficulty.EASY)
+ return heurist_easy ();
+ else if (level == Difficulty.MEDIUM)
+ return heurist_medium ();
+ else
+ return heurist_hard ();
+ }
+
+ private int heurist_easy ()
+ {
+ return -1 * heurist_hard ();
+ }
+
+ private int heurist_medium ()
+ {
+ return Random.int_range (1, 49);
+ }
+
+ private int heurist_hard ()
+ {
+ var count = count_3_in_a_row (Player.AI) - count_3_in_a_row (Player.HUMAN);
+ return count == 0 ? Random.int_range (1, 49) : count * 100;
+ }
+
+ /* Count the number of threes in a row for Player P. It counts all those 3 which have an empty cell in
the vicinity to make it
+ four in a row. */
+ private int count_3_in_a_row (Player p)
+ {
+ int count = 0;
+
+ Player old_last_moving_player = last_moving_player;
+
+ last_moving_player = p;
+
+ for (int j = 0; j < BOARD_COLUMNS; j++)
+ {
+ for (int i = 0; i < BOARD_ROWS; i++)
+ {
+ if (board [i, j] != Player.NONE)
+ break;
+
+ if (all_adjacent_empty (i, j))
+ continue;
+
+ board [i, j] = p;
+
+ if (victory (j))
+ count++;
+
+ board [i, j] = Player.NONE;
+ }
+ }
+ last_moving_player = old_last_moving_player;
+ return count;
+ }
+
+ /* checks if all adjacent cells to board [i, j] are empty */
+ private bool all_adjacent_empty (int i, int j)
+ {
+ for (int k = -1 ; k <= 1; k++)
+ {
+ for (int l = -1; l <= 1; l++)
+ {
+ if (k == 0 && l == 0)
+ continue;
+ if (i + k >= 0 && i + k < BOARD_ROWS && j + l >= 0 && j + l < BOARD_COLUMNS && board [i + k,
j + l] != Player.NONE)
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /* set the number of plies and the difficulty level */
+ private void set_level (string vstr)
+ {
+ if (vstr [0] == 'a')
+ {
+ level = Difficulty.EASY;
+ plies = 4;
+ }
+ else if (vstr [0] == 'b')
+ {
+ level = Difficulty.MEDIUM;
+ plies = 7;
+ }
+ else
+ {
+ level = Difficulty.HARD;
+ plies = 7;
+ }
+ }
+
+ /* utility function for testing purposes */
+ public int playandcheck (string vstr)
+ {
+ set_level (vstr);
+ update_board (vstr);
+
+ int temp = immediate_win (Player.AI);
+ if (temp != -1)
+ return 1000;
+
+ temp = immediate_win (Player.HUMAN);
+ if (temp != -1)
+ return temp + 1;
+
+ negamax (plies, NEG_INF, -1 * NEG_INF);
+
+ return next_move_in_column + 1;
+ }
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]