A tecnologia da Microsoft veio para ficar. Este blog regista o evoluir do meu conhecimento sobre .NET. No entanto este blog não pretende focar apenas a tecnologia em si mas também todas as aplicações desta.

Segunda-feira, Fevereiro 07, 2005

Intersecções, Continuação

Falta agora definir a situação de vitória. Fazer um teste que coloque quatro peças em linha é um pouco mais complicado do que aqueles que fizemos até agora. Vamos tentar.

[Test] public void Victory() {
    Board board = new Board(1, 1, Cell.White);
    board.Play(2);
    board.Play(2);
    board.Play(3);
    board.Play(3);
    board.Play(4);
    board.Play(4);
}

Com esta sequência de jogadas ficamos com peças brancas em (1,1), (2, 2), (3, 3) e (4,4) e com peças negras em (2,1), (3,2) e (4,3). As brancas fazem quatro em linha na diagonal. Bem, mas como é que vamos fazer para marcar o teste como tendo sucesso ou fracasso? Para começar vamos desenvolver apenas um método que indica se ocorreu ou não vitória de alguma das partes. Mais tarde veremos se esse método continua a ser útil ou se será substituído. Vamos acrescentar no fim do teste que criámos:

[Test] public void Victory() {
    // ...
    Assert.IsTrue(board.VictoryReached());
}

Este método irá devolver um valor booleano indicando se ocorreu vitória ou não. A sua definição poderá ser escrita assim:

class Board {
    // ...
    public Boolean VictoryReached() {
        return victoryReached;
    }
    // ...
    private Boolean victoryReached;
    // ...
}

A aplicação já compila mas ainda falta definir o método que vai afectar o campo victoryReached. Esse método tem de verificar se existe alguma sequência de quatro peças da mesma cor. Não será necessário efectuar esta verificação por todo o tabuleiro. Podemos começar a contar a partir da última peça colocada pois se houver alguma alteração, a nova peça fará parte da sequência de quatro em linha. Vamos escrever o teste de vitória assim:

private void CheckVictory() {
    if(CountHorizontal() >= 4 ||
    CountVertical() >= 4 ||
    CountDiagonal() >= 4 ||
    CountInverseDiagonal() >= 4) {
        victoryReached = true;
    }
}

É efectuada a contagem nas quatro direcções possíveis e caso alguma delas resulte num valor superior a quatro é considerada a vitória. Temos agora de definir os quatro métodos de contagem. Vamos ver o primeiro.

private Int32 CountHorizontal() {
    return Count(0, -1) + 1 + Count(0, 1);
}

Estou aqui a considerar a existência de um método chamado CountEqual que recebe dois valores - um incremento em termos de linhas e um incremento em termos de colunas - e indica o número de peças iguais à peça marcada pelas coordenadas das peças originais. Assim sendo, a contagem na horizontal corresponde em contar todas as peças que estão para a esquerda, todas as que estão para a direita e a própria peça. Aqueles valores ali não explicam muito bem o objectivo do código. Podemos experimentar em acrescentar dois enumerados.

class Board {
    // ...
    private enum Line { MoveUp = -1, NoMove = 0, MoveDown = 1 };
    private enum Column { MoveLeft = -1, NoMove = 0, MoveRight = 1 };
    // ...
}

De notar que estou a considerar a origem o canto superior esquerdo. Assim, progredir ao nível da linha no sentido de baixo para cima corresponde a subtrair uma unidade. Vamos reescrever o método de contagem na horizontal e ver como fica.

private Int32 CountHorizontal() {
    return Count(Line.NoMove, Column.MoveLeft) + 1 +
    Count(Line.NoMove, Column.MoveRight);
}

Até nem parece mal. Temos agora de definir o método Count.

private Int32 Count(Line line, Column column) {
    Int32 i = originalBlack + (Int32) line;
    Int32 j = originalWhite + (Int32) column;
    Int32 count = 0;
    while(ValidCoordinate(i, j) &&
    board[i, j] == board[originalBlack, originalWhite]) {
        i += (Int32) line;
        j += (Int32) column;
        ++count;
    }
    return count;
}

private Boolean ValidCoordinate(Int32 line, Int32 column) {
return line >= 0 && line < boardSize && column >= 0 && column < boardSize;
}

O método começa nas coordenadas ao lado das coordenadas das peças originais e continua a contagem enquanto as peças forem da mesma cor. Quando encontrar uma peça diferente ou o limite do tabuleiro, é devolvida a contagem. Vamos escrever os métodos para contagem nas outras direcções e ver se o teste está correcto.
private Int32 CountVertical() {
    return Count(Line.MoveUp, Column.NoMove) +
    1 + Count(Line.MoveDown, Column.NoMove);
}

private Int32 CountDiagonal() {
    return Count(Line.MoveUp, Column.MoveRight) +
    1 + Count(Line.MoveDown, Column.MoveLeft);
}

private Int32 CountInverseDiagonal() {
    return Count(Line.MoveUp, Column.MoveLeft) +
    1 + Count(Line.MoveDown, Column.MoveRight);
}

Pronto. Vamos compilar e correr os testes. Sim, funciona. Muito bem, temos código que verifica se existe vitória. A próxima etapa será começar com o interface do utilizador.