web-dev-qa-db-ja.com

勝利アルゴリズムの接続4チェック

私は勝利のためのコネクト4チェックに関して多くの質問があることを知っています。問題は、他のアルゴリズムのほとんどが私の配列の外側のインデックスにアクセスしようとするため、プログラムにランタイムエラーが発生することです。私のアルゴリズムは次のようなものです。

private int checkWin(int[][] gridTable,int rowNum,int colNum, int maxRow, int maxCol) 
{
//  For checking whether any win or lose condition is reached. Returns 1 if win or lose is reached. else returns 0
//  gridTable[][] is the game matrix(can be any number of rows and columns between 4 and 40)
//  colNum is the column number where the last token was placed
//  rowNum is the row number where the last token was placed
//  maxRow is the number of rows in my grid
//  maxCol is the number of columns in my grid

int player = gridTable[rowNum][colNum]; //player ID
int count=0;

// Horizontal check
for (int i=0;i<maxCol;i++)
{
    if (gridTable[rowNum][i]==player)
        count++;
    else
        count=0;

    if (count>=4)
        return 1;
}
//Vertical check
for (int i=0;i<maxRow;i++)
{
    if (gridTable[i][colNum]==player)
        count++;
    else
        count=0;

    if (count>=4)
        return 1;
} 
count=0;
// 4 in a row diagonally
for(int i=colNum+1,j=rowNum+1;i<maxRow && j<maxCol;i++,j++) 
{ 
    if(gridTable[j][i]!=player)
    {
        count=1;
        break;        
    }
    count++;
}
// 4 in a row diagonally
for(int i=colNum-1,j=rowNum-1;i>=0 && j>=0;i--,j--) 
{ 
    if(gridTable[j][i]!=player)
    {
        count=1;
        break;        
    }
    count++;
}
// 4 in a row diagonally
for(int i=colNum+1,j=rowNum-1;i<maxRow && j>=0;i++,j--) 
{ 
    if(gridTable[j][i]!=player)
    {
        count=1;
        break;        
    }
    count++;
}

for(int i=colNum-1,j=rowNum+1;i>=0 && j<maxCol;i--,j++) 
{ // 4 in a row diagonally
    if(gridTable[j][i]!=player)
    {
        count=1;
        break;        
    }
    count++;
}

if(count>=4)
    return 1;

return 0;
}

countは、countが4以上の場合、同じプレーヤーの4つ以上の連続トークンである必要があることを意味する変数です。

問題:このメソッドは、トークンが4つ並んでいない場合に勝ちをチェックすることもあれば、トークンが4つ並んでいる場合に勝ちをチェックしないこともあります。

9
madeluccar

コードは水平および垂直の場合に正しいようです。トリッキーな部分は斜めの場合です。

写真を試してみましょう:

enter image description here

緑の線の場合、開始行の位置は0 ... maxRow-4です。列は0 ... startingRow-です。

擬似コード:

// top-left to bottom-right - green diagonals
for( rowStart = 0; rowStart < rowMax - 4; rowStart++){
    count = 0;
    int row, col;
    for( row = rowStart, col = 0; row < rowMax && col < colMax; row++, col++ ){
        if(gridTable[row][col] == player){
            count++;
            if(count >= 4) return 1;
        }
        else {
            count = 0;
        }
    }
}

// top-left to bottom-right - red diagonals
for( colStart = 1; colStart < colMax - 4; rowStart++){
    count = 0;
    int row, col;
    for( row = 0, col = colStart; row < rowMax && col < colMax; row++, col++ ){
        if(gridTable[row][col] == player){
            count++;
            if(count >= 4) return 1;
        }
        else {
            count = 0;
        }
    }
}

対角線が逆の場合(左下から右上へ)に同様のことができます。

10
GreenGiant

なんらかの理由で、私はカウンターがあまり好きではないので、このようにしました(異なるサイズのボードで動作します)。

public boolean areFourConnected(int player){

    // horizontalCheck 
    for (int j = 0; j<getHeight()-3 ; j++ ){
        for (int i = 0; i<getWidth(); i++){
            if (this.board[i][j] == player && this.board[i][j+1] == player && this.board[i][j+2] == player && this.board[i][j+3] == player){
                return true;
            }           
        }
    }
    // verticalCheck
    for (int i = 0; i<getWidth()-3 ; i++ ){
        for (int j = 0; j<this.getHeight(); j++){
            if (this.board[i][j] == player && this.board[i+1][j] == player && this.board[i+2][j] == player && this.board[i+3][j] == player){
                return true;
            }           
        }
    }
    // ascendingDiagonalCheck 
    for (int i=3; i<getWidth(); i++){
        for (int j=0; j<getHeight()-3; j++){
            if (this.board[i][j] == player && this.board[i-1][j+1] == player && this.board[i-2][j+2] == player && this.board[i-3][j+3] == player)
                return true;
        }
    }
    // descendingDiagonalCheck
    for (int i=3; i<getWidth(); i++){
        for (int j=3; j<getHeight(); j++){
            if (this.board[i][j] == player && this.board[i-1][j-1] == player && this.board[i-2][j-2] == player && this.board[i-3][j-3] == player)
                return true;
        }
    }
    return false;
}
11
ferdelOlmo

したがって、コードを掘り下げてみると、斜めのチェックは一方向にしか勝てないように見えます(最下行と最下列にトークンを追加するとどうなりますか?)

代わりに、チェックインする方向に関係なく、基本的なチェックアルゴリズムは常に同じプロセスです。

開始点(x/y)とx/yデルタ(移動方向)が必要です。これを1つの方法にまとめることができます...

public boolean didWin(int[][] grid, int check, int row, int col, int rowDelta, int colDelta) {

    boolean win = true;
    for (int count = 0; count < 4; count++) {
        if (row < ROWS && row >= 0 && col < COLUMNS && col >= 0) {
            int test = grid[row][col];
            if (test != check) {
                win = false;
                break;
            }
        }
        row += rowDelta;
        col += colDelta;
    }
    return win;

}

これにより、基本的に4方向にチェックできますが、逆方向にもチェックできます。

したがって、次のようなものを使用する場合は...

int[][] gridTable = new int[ROWS][COLUMNS];

gridTable[ROWS - 1][3] = 1;
gridTable[ROWS - 2][3] = 1;
gridTable[ROWS - 3][3] = 1;
gridTable[ROWS - 4][3] = 1;

System.out.println("Vertical");

System.out.println(didWin(gridTable, 1, ROWS - 4, 3, 1, 0) ? "Win" : "Lose");
System.out.println(didWin(gridTable, 1, ROWS - 1, 3, -1, 0) ? "Win" : "Lose");
System.out.println(didWin(gridTable, 1, 0, 3, 1, 0) ? "Win" : "Lose");

gridTable = new int[ROWS][COLUMNS];
gridTable[3][1] = 1;
gridTable[3][2] = 1;
gridTable[3][3] = 1;
gridTable[3][4] = 1;

System.out.println("");
System.out.println("Horizontal");
System.out.println(didWin(gridTable, 1, 3, 1, 0, 1) ? "Win" : "Lose");
System.out.println(didWin(gridTable, 1, 3, 4, 0, -1) ? "Win" : "Lose");
System.out.println(didWin(gridTable, 1, 3, 0, 0, 1) ? "Win" : "Lose");

gridTable = new int[ROWS][COLUMNS];
gridTable[0][1] = 1;
gridTable[1][2] = 1;
gridTable[2][3] = 1;
gridTable[3][4] = 1;

System.out.println("");
System.out.println("Diag");
System.out.println(didWin(gridTable, 1, 0, 1, 1, 1) ? "Win" : "Lose");
System.out.println(didWin(gridTable, 1, 3, 4, -1, -1) ? "Win" : "Lose");
System.out.println(didWin(gridTable, 1, 1, 2, 1, 1) ? "Win" : "Lose");

どの出力...

Vertical
Win
Win
Lose

Horizontal
Win
Win
Lose

Diag
Win
Win
Lose

今、あなたはそれを要約することができます...

public boolean didWin(int[][] grid, int check, int row, int col) {
    return didWin(grid, check, row, col, 1, 0) ||
                    didWin(grid, check, row, col, -1, 0) ||
                    didWin(grid, check, row, col, 0, 1) ||
                    didWin(grid, check, row, col, 0, -1) ||
                    didWin(grid, check, row, col, 1, 1) ||
                    didWin(grid, check, row, col, -1, -1) ||
                    didWin(grid, check, row, col, -1, 1) ||
                    didWin(grid, check, row, col, 1, -1);
}

だから、のようなものを使用して...

int[][] gridTable = new int[ROWS][COLUMNS];

gridTable[ROWS - 1][3] = 1;
gridTable[ROWS - 2][3] = 1;
gridTable[ROWS - 3][3] = 1;
gridTable[ROWS - 4][3] = 1;

System.out.println("Vertical");

System.out.println(didWin(gridTable, 1, ROWS - 1, 3) ? "Win" : "Lose");
System.out.println(didWin(gridTable, 1, ROWS - 4, 3) ? "Win" : "Lose");

gridTable = new int[ROWS][COLUMNS];
gridTable[3][1] = 1;
gridTable[3][2] = 1;
gridTable[3][3] = 1;
gridTable[3][4] = 1;

System.out.println("");
System.out.println("Horizontal");
System.out.println(didWin(gridTable, 1, 3, 1) ? "Win" : "Lose");
System.out.println(didWin(gridTable, 1, 3, 4) ? "Win" : "Lose");

gridTable = new int[ROWS][COLUMNS];
gridTable[0][1] = 1;
gridTable[1][2] = 1;
gridTable[2][3] = 1;
gridTable[3][4] = 1;

System.out.println("");
System.out.println("Diag");
System.out.println(didWin(gridTable, 1, 0, 1) ? "Win" : "Lose");
System.out.println(didWin(gridTable, 1, 3, 4) ? "Win" : "Lose");

次のようなものを出力します...

Vertical
Win
Win

Horizontal
Win
Win

Diag
Win
Win

このアプローチは、連続して4つのチップの正しいスタートを提供した場合にのみ機能すると付け加えます。たとえば、didWin(gridTable、1、3、3)は、ループが一方向しかチェックできないため、水平チェックに対してtrueではなくfalseを提供します。

その意図は、「本格的な、すぐに使える」ソリューションを提供することではなく、より広範なソリューションを開発できるコンセプトです(つまり、人々が実際に考えなければならないのは嫌いです;))。また、OPは最後のピースがどこに配置されたか、つまり開始点を知っているという考えに基づいてソリューションを設計しました;)

didWinメソッドをわずかに変更することで、n by nグリッドを任意のポイントからチェックすることができます...

public boolean didWin(int[][] grid, int check, int row, int col, int rowDelta, int colDelta) {
    boolean match = false;
    int matches = 0;
    while (row < ROWS && row >= 0 && col < COLUMNS && col >= 0) {
        int test = grid[row][col];
        if (test != check && match) {
            break;
        } else if (test == check) {
            match = true;
            matches++;
        }
        row += rowDelta;
        col += colDelta;
    }
    return matches == 4;
}

だから、私は使用しました...

public static final int ROWS = 8;
public static final int COLUMNS = 8;
//...
int[][] gridTable = new int[ROWS][COLUMNS];

gridTable[ROWS - 1][3] = 1;
gridTable[ROWS - 2][3] = 1;
gridTable[ROWS - 3][3] = 1;
gridTable[ROWS - 4][3] = 1;
for (int[] row : gridTable) {
    StringJoiner sj = new StringJoiner("|", "|", "|");
    for (int col : row) {
        sj.add(Integer.toString(col));
    }
    System.out.println(sj);
}
System.out.println(didWin(gridTable, 1, 3, 3));

それを機能させることができました。時々、答えは完全な解決策ではなく、誰かを新しい場所に連れて行くアイデアの種です;)

さらなる強化には、予想される結合されたピースの数の提供が含まれますが、それは私が本当に実証する必要のない強化であると確信しています;)

6
MadProgrammer

それでも誰かが解決策を必要とする場合は、c#で関数を作成し、GitHubリポジトリに配置します。

/// <summary>
    /// WinnerCalc check if blue or red win the game.
    /// </summary>
    /// <returns>Return 1 if 1 win and 2 if 2 win and -1 if no one win.</returns>
    /// <param name="matrix">2d array</param>
    /// <param name="lastRow">The row number.</param>
    /// <param name="lastColumn">The column number.</param>
    public static int WinnerCalc(int[,] matrix, int lastRow, int lastColumn)
    {
        int lastValue = matrix[lastRow, lastColumn];
        Console.WriteLine("drop in row: " + (lastRow) + " and column: " + (lastColumn) + " , the value is: " + lastValue);
        int rows = matrix.GetLength(0); //6
        int columns = matrix.GetLength(1); //7
        Console.WriteLine("number of rows is " + rows + ", and number of colums is " + columns);

        int numToWin = 4;
        int winner = -1;//is now one win tha game
        int match;

        match = 0;
        //check Horizontal
        for (int c = 0; c < columns; c++)
        {
            int currentValue = matrix[lastRow, c];
            if (currentValue == lastValue)
                match++;
            else match = 0;
            if(match == numToWin)
            {
                winner = lastValue;
                break;
            }
        }
        if (winner != -1)
        {
            Console.WriteLine("win Horizontal !");
            return winner;
        }

        match = 0;
        //check Vertical
        for (int r = 0; r < rows; r++)
        {
            int currentValue = matrix[r, lastColumn];
            if (currentValue == lastValue)
                match++;
            else match = 0;
            if (match == numToWin)
            {
                winner = lastValue;
                break;
            }
        }
        if (winner != -1)
        {
            Console.WriteLine("win Vertical !");
            return winner;
        }

        //check diagonal top-left to bottom-right - include middle
        match = 0;
        for (int r = 0; r <= rows - 4; r++)
        {
            int rowPosition = r;
            for (int column = 0; column < columns && rowPosition < rows; column++)
            {
                int currentValue = matrix[rowPosition, column];
                if (currentValue == lastValue)
                    match++;
                else match = 0;
                if (match == numToWin)
                {
                    winner = lastValue;
                    break;
                }
                rowPosition++;
            }
            if (winner != -1) break;
        }
        if (winner != -1)
        {
            Console.WriteLine("win Diagonal Top left! - include middle");
            return winner;
        }

        //check diagonal top-left to bottom-right - after middle
        match = 0;
        for (int c = 1; c <= columns - 4; c++)
        {
            int columnPosition = c;
            for (int row = 0; row < rows && columnPosition < columns; row++)
            {
                int currentValue = matrix[row, columnPosition];
                if (currentValue == lastValue)
                    match++;
                else match = 0;
                if (match == numToWin)
                {
                    winner = lastValue;
                    break;
                }
                columnPosition++;
            }
            if (winner != -1) break;
        }
        if (winner != -1)
        {
            Console.WriteLine("win Diagonal Top left! - after middle");
            return winner;
        }


        //check diagonal bottom-left to top-right - include middle
        match = 0;
        for (int r = rows - 1; r >= rows - 4; r--)
        {
            int rowPosition = r;
            for (int column = 0; column < columns && rowPosition < rows && rowPosition >= 0; column++)
            {
                int currentValue = matrix[rowPosition, column];
                if (currentValue == lastValue)
                    match++;
                else match = 0;
                if (match == numToWin)
                {
                    winner = lastValue;
                    break;
                }
                rowPosition--;
            }
            if (winner != -1) break;
        }
        if (winner != -1)
        {
            Console.WriteLine("win Diagonal Bottom left! - include middle");
            return winner;
        }


        //check diagonal bottom-left to top-right - after middle
        match = 0;
        for (int c = 1; c < columns; c++)
        {
            int columnPosition = c;
            for (int row = rows - 1; row < rows && columnPosition < columns && columnPosition >= 1; row--)
            {
                int currentValue = matrix[row, columnPosition];
                if (currentValue == lastValue)
                    match++;
                else match = 0;
                if (match == numToWin)
                {
                    winner = lastValue;
                    break;
                }
                columnPosition++;
            }
            if (winner != -1) break;
        }
        if (winner != -1)
        {
            Console.WriteLine("win Diagonal Bottom left! - after middle");
            return winner;
        }



        return winner; // no winner return -1
    }

}

これがレポです: https://github.com/JoshK2/connect-four-winner

0
Josh